From 505c19580e0f43fe5224431459cacb7c21edd93d Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Fri, 6 Apr 2012 15:14:11 +0200 Subject: Imported Upstream version 1 --- src/pkg/Makefile | 311 - src/pkg/archive/tar/Makefile | 13 - src/pkg/archive/tar/common.go | 52 +- src/pkg/archive/tar/reader.go | 36 +- src/pkg/archive/tar/reader_test.go | 109 +- src/pkg/archive/tar/testdata/writer.tar | Bin 3072 -> 3584 bytes src/pkg/archive/tar/writer.go | 59 +- src/pkg/archive/tar/writer_test.go | 45 +- src/pkg/archive/zip/Makefile | 13 - src/pkg/archive/zip/example_test.go | 75 + src/pkg/archive/zip/reader.go | 252 +- src/pkg/archive/zip/reader_test.go | 339 +- src/pkg/archive/zip/struct.go | 230 +- .../archive/zip/testdata/crc32-not-streamed.zip | Bin 0 -> 314 bytes .../archive/zip/testdata/go-no-datadesc-sig.zip | Bin 0 -> 330 bytes .../archive/zip/testdata/go-with-datadesc-sig.zip | Bin 0 -> 242 bytes src/pkg/archive/zip/testdata/r.zip | Bin 440 -> 0 bytes src/pkg/archive/zip/testdata/symlink.zip | Bin 0 -> 173 bytes src/pkg/archive/zip/testdata/unix.zip | Bin 0 -> 620 bytes src/pkg/archive/zip/testdata/winxp.zip | Bin 0 -> 412 bytes src/pkg/archive/zip/writer.go | 202 +- src/pkg/archive/zip/writer_test.go | 82 +- src/pkg/archive/zip/zip_test.go | 50 +- src/pkg/asn1/Makefile | 13 - src/pkg/asn1/asn1.go | 840 -- src/pkg/asn1/asn1_test.go | 689 -- src/pkg/asn1/common.go | 158 - src/pkg/asn1/marshal.go | 544 -- src/pkg/asn1/marshal_test.go | 129 - src/pkg/big/Makefile | 18 - src/pkg/big/arith.go | 231 - src/pkg/big/arith_386.s | 265 - src/pkg/big/arith_amd64.s | 263 - src/pkg/big/arith_arm.s | 312 - src/pkg/big/arith_decl.go | 18 - src/pkg/big/arith_test.go | 335 - src/pkg/big/calibrate_test.go | 88 - src/pkg/big/hilbert_test.go | 160 - src/pkg/big/int.go | 868 -- src/pkg/big/int_test.go | 1391 --- src/pkg/big/nat.go | 1272 --- src/pkg/big/nat_test.go | 669 -- src/pkg/big/rat.go | 371 - src/pkg/big/rat_test.go | 332 - src/pkg/bufio/Makefile | 11 - src/pkg/bufio/bufio.go | 191 +- src/pkg/bufio/bufio_test.go | 267 +- src/pkg/builtin/builtin.go | 104 +- src/pkg/bytes/Makefile | 16 - src/pkg/bytes/asm_386.s | 16 + src/pkg/bytes/asm_amd64.s | 16 + src/pkg/bytes/asm_arm.s | 3 + src/pkg/bytes/buffer.go | 140 +- src/pkg/bytes/buffer_test.go | 36 +- src/pkg/bytes/bytes.go | 249 +- src/pkg/bytes/bytes_test.go | 342 +- src/pkg/bytes/example_test.go | 28 + src/pkg/bytes/export_test.go | 1 + src/pkg/bytes/reader.go | 125 + src/pkg/bytes/reader_test.go | 88 + src/pkg/cmath/Makefile | 25 - src/pkg/cmath/abs.go | 12 - src/pkg/cmath/asin.go | 170 - src/pkg/cmath/cmath_test.go | 853 -- src/pkg/cmath/conj.go | 8 - src/pkg/cmath/exp.go | 55 - src/pkg/cmath/isinf.go | 21 - src/pkg/cmath/isnan.go | 25 - src/pkg/cmath/log.go | 64 - src/pkg/cmath/phase.go | 11 - src/pkg/cmath/polar.go | 12 - src/pkg/cmath/pow.go | 60 - src/pkg/cmath/rect.go | 13 - src/pkg/cmath/sin.go | 132 - src/pkg/cmath/sqrt.go | 103 - src/pkg/cmath/tan.go | 184 - src/pkg/compress/bzip2/Makefile | 14 - src/pkg/compress/bzip2/bit_reader.go | 15 +- src/pkg/compress/bzip2/bzip2.go | 23 +- src/pkg/compress/bzip2/bzip2_test.go | 3 +- src/pkg/compress/bzip2/huffman.go | 9 +- src/pkg/compress/flate/Makefile | 17 - src/pkg/compress/flate/deflate.go | 165 +- src/pkg/compress/flate/deflate_test.go | 173 +- src/pkg/compress/flate/flate_test.go | 12 +- src/pkg/compress/flate/huffman_bit_writer.go | 42 +- src/pkg/compress/flate/huffman_code.go | 59 +- src/pkg/compress/flate/inflate.go | 43 +- src/pkg/compress/flate/util.go | 72 - src/pkg/compress/gzip/Makefile | 12 - src/pkg/compress/gzip/gunzip.go | 89 +- src/pkg/compress/gzip/gunzip_test.go | 9 +- src/pkg/compress/gzip/gzip.go | 100 +- src/pkg/compress/gzip/gzip_test.go | 207 +- src/pkg/compress/lzw/Makefile | 12 - src/pkg/compress/lzw/reader.go | 28 +- src/pkg/compress/lzw/reader_test.go | 9 +- src/pkg/compress/lzw/writer.go | 48 +- src/pkg/compress/lzw/writer_test.go | 8 +- .../compress/testdata/Mark.Twain-Tom.Sawyer.txt | 8858 ++++++++++++++++++++ src/pkg/compress/zlib/Makefile | 12 - src/pkg/compress/zlib/reader.go | 31 +- src/pkg/compress/zlib/reader_test.go | 9 +- src/pkg/compress/zlib/writer.go | 111 +- src/pkg/compress/zlib/writer_test.go | 12 +- src/pkg/container/heap/Makefile | 11 - src/pkg/container/heap/example_test.go | 105 + src/pkg/container/heap/heap.go | 18 +- src/pkg/container/heap/heap_test.go | 34 +- src/pkg/container/list/Makefile | 11 - src/pkg/container/ring/Makefile | 11 - src/pkg/container/vector/Makefile | 69 - src/pkg/container/vector/defs.go | 43 - src/pkg/container/vector/intvector.go | 188 - src/pkg/container/vector/intvector_test.go | 331 - src/pkg/container/vector/nogen_test.go | 67 - src/pkg/container/vector/numbers_test.go | 123 - src/pkg/container/vector/stringvector.go | 188 - src/pkg/container/vector/stringvector_test.go | 331 - src/pkg/container/vector/vector.go | 188 - src/pkg/container/vector/vector_test.go | 331 - src/pkg/crypto/Makefile | 11 - src/pkg/crypto/aes/Makefile | 13 - src/pkg/crypto/aes/aes_test.go | 16 + src/pkg/crypto/aes/block.go | 18 +- src/pkg/crypto/aes/cipher.go | 41 +- src/pkg/crypto/aes/const.go | 544 +- src/pkg/crypto/blowfish/Makefile | 13 - src/pkg/crypto/blowfish/block.go | 101 - src/pkg/crypto/blowfish/blowfish_test.go | 192 - src/pkg/crypto/blowfish/cipher.go | 79 - src/pkg/crypto/blowfish/const.go | 199 - src/pkg/crypto/cast5/Makefile | 11 - src/pkg/crypto/cast5/cast5.go | 536 -- src/pkg/crypto/cast5/cast5_test.go | 104 - src/pkg/crypto/cipher/Makefile | 17 - src/pkg/crypto/cipher/cbc.go | 2 +- src/pkg/crypto/cipher/cbc_aes_test.go | 7 +- src/pkg/crypto/cipher/cfb_test.go | 7 +- src/pkg/crypto/cipher/common_test.go | 2 +- src/pkg/crypto/cipher/ctr_aes_test.go | 7 +- src/pkg/crypto/cipher/io.go | 17 +- src/pkg/crypto/cipher/ocfb.go | 138 - src/pkg/crypto/cipher/ocfb_test.go | 44 - src/pkg/crypto/cipher/ofb_test.go | 7 +- src/pkg/crypto/crypto.go | 30 +- src/pkg/crypto/des/Makefile | 13 - src/pkg/crypto/des/block.go | 2 +- src/pkg/crypto/des/cipher.go | 68 +- src/pkg/crypto/des/des_test.go | 16 +- src/pkg/crypto/dsa/Makefile | 11 - src/pkg/crypto/dsa/dsa.go | 54 +- src/pkg/crypto/dsa/dsa_test.go | 2 +- src/pkg/crypto/ecdsa/Makefile | 11 - src/pkg/crypto/ecdsa/ecdsa.go | 36 +- src/pkg/crypto/ecdsa/ecdsa_test.go | 10 +- src/pkg/crypto/elliptic/Makefile | 11 - src/pkg/crypto/elliptic/elliptic.go | 110 +- src/pkg/crypto/elliptic/elliptic_test.go | 41 +- src/pkg/crypto/elliptic/p224.go | 718 ++ src/pkg/crypto/elliptic/p224_test.go | 47 + src/pkg/crypto/hmac/Makefile | 11 - src/pkg/crypto/hmac/hmac.go | 58 +- src/pkg/crypto/hmac/hmac_test.go | 316 +- src/pkg/crypto/md4/Makefile | 12 - src/pkg/crypto/md4/md4.go | 117 - src/pkg/crypto/md4/md4_test.go | 71 - src/pkg/crypto/md4/md4block.go | 89 - src/pkg/crypto/md5/Makefile | 12 - src/pkg/crypto/md5/md5.go | 30 +- src/pkg/crypto/md5/md5_test.go | 17 +- src/pkg/crypto/ocsp/Makefile | 11 - src/pkg/crypto/ocsp/ocsp.go | 192 - src/pkg/crypto/ocsp/ocsp_test.go | 97 - src/pkg/crypto/openpgp/Makefile | 14 - src/pkg/crypto/openpgp/armor/Makefile | 12 - src/pkg/crypto/openpgp/armor/armor.go | 220 - src/pkg/crypto/openpgp/armor/armor_test.go | 95 - src/pkg/crypto/openpgp/armor/encode.go | 161 - src/pkg/crypto/openpgp/canonical_text.go | 58 - src/pkg/crypto/openpgp/canonical_text_test.go | 49 - src/pkg/crypto/openpgp/elgamal/Makefile | 11 - src/pkg/crypto/openpgp/elgamal/elgamal.go | 122 - src/pkg/crypto/openpgp/elgamal/elgamal_test.go | 49 - src/pkg/crypto/openpgp/error/Makefile | 11 - src/pkg/crypto/openpgp/error/error.go | 64 - src/pkg/crypto/openpgp/keys.go | 545 -- src/pkg/crypto/openpgp/packet/Makefile | 22 - src/pkg/crypto/openpgp/packet/compressed.go | 39 - src/pkg/crypto/openpgp/packet/compressed_test.go | 41 - src/pkg/crypto/openpgp/packet/encrypted_key.go | 168 - .../crypto/openpgp/packet/encrypted_key_test.go | 126 - src/pkg/crypto/openpgp/packet/literal.go | 90 - .../crypto/openpgp/packet/one_pass_signature.go | 74 - src/pkg/crypto/openpgp/packet/packet.go | 483 -- src/pkg/crypto/openpgp/packet/packet_test.go | 256 - src/pkg/crypto/openpgp/packet/private_key.go | 301 - src/pkg/crypto/openpgp/packet/private_key_test.go | 57 - src/pkg/crypto/openpgp/packet/public_key.go | 393 - src/pkg/crypto/openpgp/packet/public_key_test.go | 98 - src/pkg/crypto/openpgp/packet/reader.go | 63 - src/pkg/crypto/openpgp/packet/signature.go | 558 -- src/pkg/crypto/openpgp/packet/signature_test.go | 42 - .../openpgp/packet/symmetric_key_encrypted.go | 162 - .../openpgp/packet/symmetric_key_encrypted_test.go | 101 - .../openpgp/packet/symmetrically_encrypted.go | 291 - .../openpgp/packet/symmetrically_encrypted_test.go | 124 - src/pkg/crypto/openpgp/packet/userid.go | 161 - src/pkg/crypto/openpgp/packet/userid_test.go | 87 - src/pkg/crypto/openpgp/read.go | 415 - src/pkg/crypto/openpgp/read_test.go | 361 - src/pkg/crypto/openpgp/s2k/Makefile | 11 - src/pkg/crypto/openpgp/s2k/s2k.go | 180 - src/pkg/crypto/openpgp/s2k/s2k_test.go | 118 - src/pkg/crypto/openpgp/write.go | 308 - src/pkg/crypto/openpgp/write_test.go | 233 - src/pkg/crypto/rand/Makefile | 30 - src/pkg/crypto/rand/rand.go | 7 +- src/pkg/crypto/rand/rand_test.go | 2 +- src/pkg/crypto/rand/rand_unix.go | 11 +- src/pkg/crypto/rand/rand_windows.go | 14 +- src/pkg/crypto/rand/util.go | 12 +- src/pkg/crypto/rc4/Makefile | 11 - src/pkg/crypto/rc4/rc4.go | 9 +- src/pkg/crypto/ripemd160/Makefile | 12 - src/pkg/crypto/ripemd160/ripemd160.go | 118 - src/pkg/crypto/ripemd160/ripemd160_test.go | 64 - src/pkg/crypto/ripemd160/ripemd160block.go | 161 - src/pkg/crypto/rsa/Makefile | 12 - src/pkg/crypto/rsa/pkcs1v15.go | 40 +- src/pkg/crypto/rsa/pkcs1v15_test.go | 6 +- src/pkg/crypto/rsa/rsa.go | 86 +- src/pkg/crypto/rsa/rsa_test.go | 2 +- src/pkg/crypto/sha1/Makefile | 12 - src/pkg/crypto/sha1/sha1.go | 30 +- src/pkg/crypto/sha1/sha1_test.go | 16 +- src/pkg/crypto/sha256/Makefile | 12 - src/pkg/crypto/sha256/sha256.go | 38 +- src/pkg/crypto/sha256/sha256_test.go | 8 +- src/pkg/crypto/sha512/Makefile | 12 - src/pkg/crypto/sha512/sha512.go | 43 +- src/pkg/crypto/sha512/sha512_test.go | 8 +- src/pkg/crypto/subtle/Makefile | 11 - src/pkg/crypto/tls/Makefile | 20 - src/pkg/crypto/tls/alert.go | 4 + src/pkg/crypto/tls/cipher_suites.go | 128 +- src/pkg/crypto/tls/common.go | 180 +- src/pkg/crypto/tls/conn.go | 191 +- src/pkg/crypto/tls/conn_test.go | 54 + src/pkg/crypto/tls/generate_cert.go | 12 +- src/pkg/crypto/tls/handshake_client.go | 136 +- src/pkg/crypto/tls/handshake_client_test.go | 3 +- src/pkg/crypto/tls/handshake_messages.go | 207 +- src/pkg/crypto/tls/handshake_messages_test.go | 19 +- src/pkg/crypto/tls/handshake_server.go | 157 +- src/pkg/crypto/tls/handshake_server_test.go | 1165 ++- src/pkg/crypto/tls/key_agreement.go | 82 +- src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py | 2 + src/pkg/crypto/tls/prf.go | 124 +- src/pkg/crypto/tls/prf_test.go | 21 +- src/pkg/crypto/tls/root_test.go | 61 + src/pkg/crypto/tls/tls.go | 70 +- src/pkg/crypto/twofish/Makefile | 11 - src/pkg/crypto/twofish/twofish.go | 358 - src/pkg/crypto/twofish/twofish_test.go | 129 - src/pkg/crypto/x509/Makefile | 13 - src/pkg/crypto/x509/cert_pool.go | 33 +- src/pkg/crypto/x509/pkcs1.go | 122 + src/pkg/crypto/x509/pkcs8.go | 42 + src/pkg/crypto/x509/pkcs8_test.go | 20 + src/pkg/crypto/x509/pkix/Makefile | 11 - src/pkg/crypto/x509/pkix/pkix.go | 22 +- src/pkg/crypto/x509/root.go | 17 + src/pkg/crypto/x509/root_darwin.go | 80 + src/pkg/crypto/x509/root_stub.go | 15 + src/pkg/crypto/x509/root_unix.go | 35 + src/pkg/crypto/x509/root_windows.go | 226 + src/pkg/crypto/x509/verify.go | 104 +- src/pkg/crypto/x509/verify_test.go | 84 +- src/pkg/crypto/x509/x509.go | 307 +- src/pkg/crypto/x509/x509_test.go | 62 +- src/pkg/crypto/xtea/Makefile | 12 - src/pkg/crypto/xtea/block.go | 66 - src/pkg/crypto/xtea/cipher.go | 92 - src/pkg/crypto/xtea/xtea_test.go | 246 - src/pkg/csv/Makefile | 12 - src/pkg/csv/reader.go | 372 - src/pkg/csv/reader_test.go | 265 - src/pkg/csv/writer.go | 122 - src/pkg/csv/writer_test.go | 44 - src/pkg/database/sql/convert.go | 158 + src/pkg/database/sql/convert_test.go | 250 + src/pkg/database/sql/doc.txt | 46 + src/pkg/database/sql/driver/driver.go | 215 + src/pkg/database/sql/driver/types.go | 252 + src/pkg/database/sql/driver/types_test.go | 65 + src/pkg/database/sql/fakedb_test.go | 629 ++ src/pkg/database/sql/sql.go | 1063 +++ src/pkg/database/sql/sql_test.go | 614 ++ src/pkg/debug/dwarf/Makefile | 16 - src/pkg/debug/dwarf/buf.go | 9 +- src/pkg/debug/dwarf/const.go | 4 +- src/pkg/debug/dwarf/entry.go | 10 +- src/pkg/debug/dwarf/open.go | 12 +- src/pkg/debug/dwarf/testdata/typedef.c | 8 +- src/pkg/debug/dwarf/testdata/typedef.elf | Bin 10837 -> 12448 bytes src/pkg/debug/dwarf/testdata/typedef.macho | Bin 5256 -> 5024 bytes src/pkg/debug/dwarf/type.go | 55 +- src/pkg/debug/dwarf/type_test.go | 19 +- src/pkg/debug/dwarf/unit.go | 7 +- src/pkg/debug/elf/Makefile | 12 - src/pkg/debug/elf/elf.go | 8 +- src/pkg/debug/elf/file.go | 57 +- src/pkg/debug/elf/file_test.go | 3 + src/pkg/debug/gosym/Makefile | 19 - src/pkg/debug/gosym/pclinetest.asm | 58 + src/pkg/debug/gosym/pclinetest.h | 2 + src/pkg/debug/gosym/pclinetest.s | 58 - src/pkg/debug/gosym/pclntab_test.go | 36 +- src/pkg/debug/gosym/symtab.go | 19 +- src/pkg/debug/macho/Makefile | 12 - src/pkg/debug/macho/file.go | 28 +- src/pkg/debug/macho/file_test.go | 80 +- src/pkg/debug/macho/macho.go | 6 +- src/pkg/debug/pe/Makefile | 12 - src/pkg/debug/pe/file.go | 23 +- src/pkg/debug/pe/file_test.go | 54 +- src/pkg/deps.bash | 49 - src/pkg/ebnf/Makefile | 12 - src/pkg/ebnf/ebnf.go | 245 - src/pkg/ebnf/ebnf_test.go | 87 - src/pkg/ebnf/parser.go | 197 - src/pkg/encoding/ascii85/Makefile | 11 - src/pkg/encoding/ascii85/ascii85.go | 19 +- src/pkg/encoding/ascii85/ascii85_test.go | 12 +- src/pkg/encoding/asn1/asn1.go | 846 ++ src/pkg/encoding/asn1/asn1_test.go | 706 ++ src/pkg/encoding/asn1/common.go | 161 + src/pkg/encoding/asn1/marshal.go | 557 ++ src/pkg/encoding/asn1/marshal_test.go | 139 + src/pkg/encoding/base32/Makefile | 11 - src/pkg/encoding/base32/base32.go | 78 +- src/pkg/encoding/base32/base32_test.go | 51 +- src/pkg/encoding/base64/Makefile | 11 - src/pkg/encoding/base64/base64.go | 60 +- src/pkg/encoding/base64/base64_test.go | 44 +- src/pkg/encoding/binary/Makefile | 11 - src/pkg/encoding/binary/binary.go | 71 +- src/pkg/encoding/binary/binary_test.go | 65 +- src/pkg/encoding/binary/example_test.go | 52 + src/pkg/encoding/binary/varint.go | 134 + src/pkg/encoding/binary/varint_test.go | 168 + src/pkg/encoding/csv/reader.go | 376 + src/pkg/encoding/csv/reader_test.go | 281 + src/pkg/encoding/csv/writer.go | 121 + src/pkg/encoding/csv/writer_test.go | 44 + src/pkg/encoding/git85/Makefile | 11 - src/pkg/encoding/git85/git.go | 277 - src/pkg/encoding/git85/git_test.go | 194 - src/pkg/encoding/gob/codec_test.go | 1471 ++++ src/pkg/encoding/gob/debug.go | 695 ++ src/pkg/encoding/gob/decode.go | 1300 +++ src/pkg/encoding/gob/decoder.go | 216 + src/pkg/encoding/gob/doc.go | 366 + src/pkg/encoding/gob/dump.go | 29 + src/pkg/encoding/gob/encode.go | 731 ++ src/pkg/encoding/gob/encoder.go | 253 + src/pkg/encoding/gob/encoder_test.go | 738 ++ src/pkg/encoding/gob/error.go | 43 + src/pkg/encoding/gob/gobencdec_test.go | 594 ++ src/pkg/encoding/gob/timing_test.go | 96 + src/pkg/encoding/gob/type.go | 808 ++ src/pkg/encoding/gob/type_test.go | 161 + src/pkg/encoding/hex/Makefile | 11 - src/pkg/encoding/hex/hex.go | 39 +- src/pkg/encoding/hex/hex_test.go | 171 +- src/pkg/encoding/json/bench_test.go | 157 + src/pkg/encoding/json/decode.go | 979 +++ src/pkg/encoding/json/decode_test.go | 640 ++ src/pkg/encoding/json/encode.go | 551 ++ src/pkg/encoding/json/encode_test.go | 188 + src/pkg/encoding/json/example_test.go | 83 + src/pkg/encoding/json/indent.go | 127 + src/pkg/encoding/json/scanner.go | 623 ++ src/pkg/encoding/json/scanner_test.go | 303 + src/pkg/encoding/json/stream.go | 188 + src/pkg/encoding/json/stream_test.go | 147 + src/pkg/encoding/json/tagkey_test.go | 100 + src/pkg/encoding/json/tags.go | 44 + src/pkg/encoding/json/tags_test.go | 28 + src/pkg/encoding/json/testdata/code.json.gz | Bin 0 -> 120432 bytes src/pkg/encoding/pem/Makefile | 11 - src/pkg/encoding/pem/pem.go | 11 +- src/pkg/encoding/pem/pem_test.go | 4 +- src/pkg/encoding/xml/atom_test.go | 56 + src/pkg/encoding/xml/example_test.go | 111 + src/pkg/encoding/xml/marshal.go | 447 + src/pkg/encoding/xml/marshal_test.go | 793 ++ src/pkg/encoding/xml/read.go | 531 ++ src/pkg/encoding/xml/read_test.go | 357 + src/pkg/encoding/xml/typeinfo.go | 329 + src/pkg/encoding/xml/xml.go | 1697 ++++ src/pkg/encoding/xml/xml_test.go | 578 ++ src/pkg/errors/errors.go | 20 + src/pkg/errors/errors_test.go | 53 + src/pkg/errors/example_test.go | 34 + src/pkg/exec/Makefile | 31 - src/pkg/exec/exec.go | 377 - src/pkg/exec/exec_test.go | 214 - src/pkg/exec/lp_plan9.go | 51 - src/pkg/exec/lp_test.go | 33 - src/pkg/exec/lp_unix.go | 52 - src/pkg/exec/lp_windows.go | 77 - src/pkg/exp/README | 3 - src/pkg/exp/gui/Makefile | 11 - src/pkg/exp/gui/gui.go | 58 - src/pkg/exp/gui/x11/Makefile | 12 - src/pkg/exp/gui/x11/auth.go | 93 - src/pkg/exp/gui/x11/conn.go | 627 -- src/pkg/exp/norm/Makefile | 44 - src/pkg/exp/norm/composition.go | 344 - src/pkg/exp/norm/composition_test.go | 138 - src/pkg/exp/norm/forminfo.go | 188 - src/pkg/exp/norm/maketables.go | 855 -- src/pkg/exp/norm/maketesttables.go | 42 - src/pkg/exp/norm/norm_test.go | 14 - src/pkg/exp/norm/normalize.go | 99 - src/pkg/exp/norm/tables.go | 6580 --------------- src/pkg/exp/norm/trie.go | 234 - src/pkg/exp/norm/trie_test.go | 107 - src/pkg/exp/norm/triedata_test.go | 63 - src/pkg/exp/norm/triegen.go | 211 - src/pkg/exp/regexp/Makefile | 12 - src/pkg/exp/regexp/all_test.go | 429 - src/pkg/exp/regexp/exec.go | 295 - src/pkg/exp/regexp/find_test.go | 472 -- src/pkg/exp/regexp/regexp.go | 795 -- src/pkg/exp/regexp/syntax/Makefile | 16 - src/pkg/exp/regexp/syntax/compile.go | 269 - src/pkg/exp/regexp/syntax/make_perl_groups.pl | 103 - src/pkg/exp/regexp/syntax/parse.go | 1797 ---- src/pkg/exp/regexp/syntax/parse_test.go | 350 - src/pkg/exp/regexp/syntax/perl_groups.go | 130 - src/pkg/exp/regexp/syntax/prog.go | 237 - src/pkg/exp/regexp/syntax/prog_test.go | 91 - src/pkg/exp/regexp/syntax/regexp.go | 284 - src/pkg/exp/regexp/syntax/simplify.go | 151 - src/pkg/exp/regexp/syntax/simplify_test.go | 151 - src/pkg/exp/template/html/Makefile | 11 - src/pkg/exp/template/html/context.go | 98 - src/pkg/exp/template/html/escape.go | 105 - src/pkg/exp/template/html/escape_test.go | 75 - src/pkg/exp/wingui/Makefile | 29 - src/pkg/exp/wingui/gui.go | 153 - src/pkg/exp/wingui/winapi.go | 131 - src/pkg/exp/wingui/zwinapi.go | 211 - src/pkg/expvar/Makefile | 11 - src/pkg/expvar/expvar.go | 147 +- src/pkg/expvar/expvar_test.go | 10 +- src/pkg/flag/Makefile | 11 - src/pkg/flag/example_test.go | 83 + src/pkg/flag/flag.go | 207 +- src/pkg/flag/flag_test.go | 54 +- src/pkg/fmt/Makefile | 14 - src/pkg/fmt/doc.go | 35 +- src/pkg/fmt/export_test.go | 7 + src/pkg/fmt/fmt_test.go | 185 +- src/pkg/fmt/format.go | 112 +- src/pkg/fmt/print.go | 414 +- src/pkg/fmt/scan.go | 316 +- src/pkg/fmt/scan_test.go | 83 +- src/pkg/go/ast/Makefile | 16 - src/pkg/go/ast/ast.go | 180 +- src/pkg/go/ast/example_test.go | 136 + src/pkg/go/ast/filter.go | 348 +- src/pkg/go/ast/import.go | 134 + src/pkg/go/ast/print.go | 38 +- src/pkg/go/ast/print_test.go | 17 +- src/pkg/go/ast/resolve.go | 16 +- src/pkg/go/ast/scope.go | 10 +- src/pkg/go/build/Makefile | 22 - src/pkg/go/build/build.go | 1254 ++- src/pkg/go/build/build_test.go | 100 +- src/pkg/go/build/cgotest/cgotest.c | 9 - src/pkg/go/build/cgotest/cgotest.go | 19 - src/pkg/go/build/cgotest/cgotest.h | 5 - src/pkg/go/build/cmdtest/main.go | 12 - src/pkg/go/build/deps_test.go | 424 + src/pkg/go/build/dir.go | 172 - src/pkg/go/build/doc.go | 109 + src/pkg/go/build/path.go | 182 - src/pkg/go/build/pkgtest/pkgtest.go | 9 - src/pkg/go/build/pkgtest/sqrt_386.s | 10 - src/pkg/go/build/pkgtest/sqrt_amd64.s | 9 - src/pkg/go/build/pkgtest/sqrt_arm.s | 10 - src/pkg/go/build/syslist.go | 8 + src/pkg/go/build/syslist_test.go | 4 +- src/pkg/go/build/testdata/other/file/file.go | 5 + src/pkg/go/build/testdata/other/main.go | 11 + src/pkg/go/doc/Makefile | 11 +- src/pkg/go/doc/comment.go | 376 +- src/pkg/go/doc/comment_test.go | 109 + src/pkg/go/doc/doc.go | 668 +- src/pkg/go/doc/doc_test.go | 136 + src/pkg/go/doc/example.go | 117 + src/pkg/go/doc/exports.go | 199 + src/pkg/go/doc/filter.go | 105 + src/pkg/go/doc/headscan.go | 113 + src/pkg/go/doc/reader.go | 774 ++ src/pkg/go/doc/synopsis.go | 52 + src/pkg/go/doc/synopsis_test.go | 44 + src/pkg/go/doc/testdata/a.0.golden | 13 + src/pkg/go/doc/testdata/a.1.golden | 13 + src/pkg/go/doc/testdata/a.2.golden | 13 + src/pkg/go/doc/testdata/a0.go | 8 + src/pkg/go/doc/testdata/a1.go | 8 + src/pkg/go/doc/testdata/b.0.golden | 71 + src/pkg/go/doc/testdata/b.1.golden | 83 + src/pkg/go/doc/testdata/b.2.golden | 71 + src/pkg/go/doc/testdata/b.go | 58 + src/pkg/go/doc/testdata/benchmark.go | 293 + src/pkg/go/doc/testdata/c.0.golden | 48 + src/pkg/go/doc/testdata/c.1.golden | 48 + src/pkg/go/doc/testdata/c.2.golden | 48 + src/pkg/go/doc/testdata/c.go | 62 + src/pkg/go/doc/testdata/d.0.golden | 104 + src/pkg/go/doc/testdata/d.1.golden | 104 + src/pkg/go/doc/testdata/d.2.golden | 104 + src/pkg/go/doc/testdata/d1.go | 57 + src/pkg/go/doc/testdata/d2.go | 45 + src/pkg/go/doc/testdata/e.0.golden | 109 + src/pkg/go/doc/testdata/e.1.golden | 144 + src/pkg/go/doc/testdata/e.2.golden | 130 + src/pkg/go/doc/testdata/e.go | 147 + src/pkg/go/doc/testdata/error1.0.golden | 30 + src/pkg/go/doc/testdata/error1.1.golden | 32 + src/pkg/go/doc/testdata/error1.2.golden | 30 + src/pkg/go/doc/testdata/error1.go | 24 + src/pkg/go/doc/testdata/error2.0.golden | 27 + src/pkg/go/doc/testdata/error2.1.golden | 37 + src/pkg/go/doc/testdata/error2.2.golden | 27 + src/pkg/go/doc/testdata/error2.go | 29 + src/pkg/go/doc/testdata/example.go | 81 + src/pkg/go/doc/testdata/f.0.golden | 13 + src/pkg/go/doc/testdata/f.1.golden | 16 + src/pkg/go/doc/testdata/f.2.golden | 13 + src/pkg/go/doc/testdata/f.go | 14 + src/pkg/go/doc/testdata/template.txt | 65 + src/pkg/go/doc/testdata/testing.0.golden | 156 + src/pkg/go/doc/testdata/testing.1.golden | 298 + src/pkg/go/doc/testdata/testing.2.golden | 156 + src/pkg/go/doc/testdata/testing.go | 404 + src/pkg/go/parser/Makefile | 12 - src/pkg/go/parser/error_test.go | 166 + src/pkg/go/parser/example_test.go | 34 + src/pkg/go/parser/interface.go | 183 +- src/pkg/go/parser/parser.go | 628 +- src/pkg/go/parser/parser_test.go | 193 +- src/pkg/go/parser/short_test.go | 75 + src/pkg/go/parser/testdata/commas.src | 19 + src/pkg/go/parser/testdata/issue3106.src | 46 + src/pkg/go/printer/Makefile | 12 - src/pkg/go/printer/example_test.go | 67 + src/pkg/go/printer/nodes.go | 600 +- src/pkg/go/printer/performance_test.go | 4 +- src/pkg/go/printer/printer.go | 926 +- src/pkg/go/printer/printer_test.go | 242 +- src/pkg/go/printer/testdata/comments.golden | 174 +- src/pkg/go/printer/testdata/comments.input | 163 +- src/pkg/go/printer/testdata/declarations.golden | 167 +- src/pkg/go/printer/testdata/declarations.input | 118 +- src/pkg/go/printer/testdata/expressions.golden | 28 +- src/pkg/go/printer/testdata/expressions.input | 22 + src/pkg/go/printer/testdata/expressions.raw | 28 +- src/pkg/go/printer/testdata/linebreaks.golden | 52 + src/pkg/go/printer/testdata/linebreaks.input | 48 + src/pkg/go/printer/testdata/parser.go | 8 +- src/pkg/go/printer/testdata/statements.golden | 121 +- src/pkg/go/printer/testdata/statements.input | 119 + src/pkg/go/scanner/Makefile | 12 - src/pkg/go/scanner/errors.go | 142 +- src/pkg/go/scanner/example_test.go | 46 + src/pkg/go/scanner/scanner.go | 578 +- src/pkg/go/scanner/scanner_test.go | 155 +- src/pkg/go/token/Makefile | 12 - src/pkg/go/token/position.go | 277 +- src/pkg/go/token/position_test.go | 9 +- src/pkg/go/token/serialize.go | 56 + src/pkg/go/token/serialize_test.go | 111 + src/pkg/go/token/token.go | 12 +- src/pkg/go/typechecker/Makefile | 14 - src/pkg/go/typechecker/scope.go | 69 - src/pkg/go/typechecker/testdata/test0.src | 94 - src/pkg/go/typechecker/testdata/test1.src | 13 - src/pkg/go/typechecker/testdata/test3.src | 41 - src/pkg/go/typechecker/testdata/test4.src | 11 - src/pkg/go/typechecker/type.go | 118 - src/pkg/go/typechecker/typechecker.go | 468 -- src/pkg/go/typechecker/typechecker_test.go | 163 - src/pkg/go/typechecker/universe.go | 36 - src/pkg/go/types/Makefile | 16 - src/pkg/go/types/check.go | 226 - src/pkg/go/types/check_test.go | 215 - src/pkg/go/types/const.go | 332 - src/pkg/go/types/exportdata.go | 132 - src/pkg/go/types/gcimporter.go | 799 -- src/pkg/go/types/gcimporter_test.go | 101 - src/pkg/go/types/testdata/exports.go | 84 - src/pkg/go/types/testdata/test0.src | 154 - src/pkg/go/types/types.go | 255 - src/pkg/go/types/universe.go | 108 - src/pkg/gob/Makefile | 25 - src/pkg/gob/codec_test.go | 1406 ---- src/pkg/gob/debug.go | 686 -- src/pkg/gob/decode.go | 1275 --- src/pkg/gob/decoder.go | 202 - src/pkg/gob/doc.go | 363 - src/pkg/gob/dump.go | 22 - src/pkg/gob/encode.go | 717 -- src/pkg/gob/encoder.go | 243 - src/pkg/gob/encoder_test.go | 580 -- src/pkg/gob/error.go | 42 - src/pkg/gob/gobencdec_test.go | 490 -- src/pkg/gob/timing_test.go | 94 - src/pkg/gob/type.go | 786 -- src/pkg/gob/type_test.go | 153 - src/pkg/hash/Makefile | 11 - src/pkg/hash/adler32/Makefile | 11 - src/pkg/hash/adler32/adler32.go | 22 +- src/pkg/hash/crc32/Makefile | 20 - src/pkg/hash/crc32/crc32.go | 18 +- src/pkg/hash/crc32/crc32_generic.go | 2 + src/pkg/hash/crc64/Makefile | 11 - src/pkg/hash/crc64/crc64.go | 30 +- src/pkg/hash/fnv/Makefile | 11 - src/pkg/hash/fnv/fnv.go | 67 +- src/pkg/hash/fnv/fnv_test.go | 12 +- src/pkg/hash/hash.go | 12 +- src/pkg/html/Makefile | 17 - src/pkg/html/const.go | 90 - src/pkg/html/doc.go | 110 - src/pkg/html/entity.go | 4 +- src/pkg/html/entity_test.go | 2 +- src/pkg/html/escape.go | 46 +- src/pkg/html/node.go | 147 - src/pkg/html/parse.go | 800 -- src/pkg/html/parse_test.go | 156 - src/pkg/html/template/attr.go | 175 + src/pkg/html/template/clone_test.go | 148 + src/pkg/html/template/content.go | 129 + src/pkg/html/template/content_test.go | 261 + src/pkg/html/template/context.go | 339 + src/pkg/html/template/css.go | 268 + src/pkg/html/template/css_test.go | 281 + src/pkg/html/template/doc.go | 191 + src/pkg/html/template/error.go | 197 + src/pkg/html/template/escape.go | 795 ++ src/pkg/html/template/escape_test.go | 1657 ++++ src/pkg/html/template/html.go | 257 + src/pkg/html/template/html_test.go | 94 + src/pkg/html/template/js.go | 362 + src/pkg/html/template/js_test.go | 401 + src/pkg/html/template/template.go | 359 + src/pkg/html/template/transition.go | 553 ++ src/pkg/html/template/url.go | 105 + src/pkg/html/template/url_test.go | 112 + src/pkg/html/testdata/webkit/README | 28 - src/pkg/html/testdata/webkit/adoption01.dat | 194 - src/pkg/html/testdata/webkit/adoption02.dat | 31 - src/pkg/html/testdata/webkit/comments01.dat | 135 - src/pkg/html/testdata/webkit/doctype01.dat | 370 - src/pkg/html/testdata/webkit/entities01.dat | 603 -- src/pkg/html/testdata/webkit/entities02.dat | 249 - src/pkg/html/testdata/webkit/html5test-com.dat | 246 - src/pkg/html/testdata/webkit/inbody01.dat | 43 - src/pkg/html/testdata/webkit/isindex.dat | 40 - .../pending-spec-changes-plain-text-unsafe.dat | Bin 115 -> 0 bytes .../html/testdata/webkit/pending-spec-changes.dat | 28 - src/pkg/html/testdata/webkit/plain-text-unsafe.dat | 8 - src/pkg/html/testdata/webkit/scriptdata01.dat | 308 - .../html/testdata/webkit/scripted/adoption01.dat | 15 - src/pkg/html/testdata/webkit/scripted/webkit01.dat | 28 - src/pkg/html/testdata/webkit/tables01.dat | 197 - src/pkg/html/testdata/webkit/tests1.dat | 1952 ----- src/pkg/html/testdata/webkit/tests10.dat | 799 -- src/pkg/html/testdata/webkit/tests11.dat | 482 -- src/pkg/html/testdata/webkit/tests12.dat | 62 - src/pkg/html/testdata/webkit/tests14.dat | 74 - src/pkg/html/testdata/webkit/tests15.dat | 208 - src/pkg/html/testdata/webkit/tests16.dat | 2277 ----- src/pkg/html/testdata/webkit/tests17.dat | 153 - src/pkg/html/testdata/webkit/tests18.dat | 269 - src/pkg/html/testdata/webkit/tests19.dat | 1220 --- src/pkg/html/testdata/webkit/tests2.dat | 763 -- src/pkg/html/testdata/webkit/tests20.dat | 455 - src/pkg/html/testdata/webkit/tests21.dat | 221 - src/pkg/html/testdata/webkit/tests22.dat | 157 - src/pkg/html/testdata/webkit/tests23.dat | 155 - src/pkg/html/testdata/webkit/tests24.dat | 79 - src/pkg/html/testdata/webkit/tests25.dat | 219 - src/pkg/html/testdata/webkit/tests26.dat | 195 - src/pkg/html/testdata/webkit/tests3.dat | 305 - src/pkg/html/testdata/webkit/tests4.dat | 59 - src/pkg/html/testdata/webkit/tests5.dat | 191 - src/pkg/html/testdata/webkit/tests6.dat | 663 -- src/pkg/html/testdata/webkit/tests7.dat | 390 - src/pkg/html/testdata/webkit/tests8.dat | 148 - src/pkg/html/testdata/webkit/tests9.dat | 457 - src/pkg/html/testdata/webkit/tests_innerHTML_1.dat | 733 -- src/pkg/html/testdata/webkit/tricky01.dat | 261 - src/pkg/html/testdata/webkit/webkit01.dat | 609 -- src/pkg/html/testdata/webkit/webkit02.dat | 104 - src/pkg/html/token.go | 575 -- src/pkg/html/token_test.go | 340 - src/pkg/http/Makefile | 26 - src/pkg/http/cgi/Makefile | 12 - src/pkg/http/cgi/child.go | 191 - src/pkg/http/cgi/child_test.go | 93 - src/pkg/http/cgi/host.go | 323 - src/pkg/http/cgi/host_test.go | 449 - src/pkg/http/cgi/matryoshka_test.go | 74 - src/pkg/http/cgi/testdata/test.cgi | 96 - src/pkg/http/chunked.go | 77 - src/pkg/http/client.go | 290 - src/pkg/http/client_test.go | 292 - src/pkg/http/cookie.go | 268 - src/pkg/http/cookie_test.go | 201 - src/pkg/http/dump.go | 78 - src/pkg/http/export_test.go | 41 - src/pkg/http/fcgi/Makefile | 12 - src/pkg/http/fcgi/child.go | 258 - src/pkg/http/fcgi/fcgi.go | 271 - src/pkg/http/fcgi/fcgi_test.go | 114 - src/pkg/http/fs.go | 323 - src/pkg/http/fs_test.go | 308 - src/pkg/http/header.go | 78 - src/pkg/http/header_test.go | 81 - src/pkg/http/httptest/Makefile | 12 - src/pkg/http/httptest/recorder.go | 59 - src/pkg/http/httptest/server.go | 151 - src/pkg/http/lex.go | 144 - src/pkg/http/lex_test.go | 70 - src/pkg/http/persist.go | 420 - src/pkg/http/pprof/Makefile | 11 - src/pkg/http/pprof/pprof.go | 132 - src/pkg/http/proxy_test.go | 48 - src/pkg/http/range_test.go | 57 - src/pkg/http/readrequest_test.go | 183 - src/pkg/http/request.go | 745 -- src/pkg/http/request_test.go | 336 - src/pkg/http/requestwrite_test.go | 370 - src/pkg/http/response.go | 214 - src/pkg/http/response_test.go | 397 - src/pkg/http/responsewrite_test.go | 109 - src/pkg/http/reverseproxy.go | 159 - src/pkg/http/reverseproxy_test.go | 66 - src/pkg/http/serve_test.go | 959 --- src/pkg/http/server.go | 1183 --- src/pkg/http/sniff.go | 214 - src/pkg/http/sniff_test.go | 80 - src/pkg/http/spdy/Makefile | 13 - src/pkg/http/spdy/read.go | 313 - src/pkg/http/spdy/spdy_test.go | 497 -- src/pkg/http/spdy/types.go | 370 - src/pkg/http/spdy/write.go | 286 - src/pkg/http/status.go | 106 - src/pkg/http/testdata/file | 1 - src/pkg/http/testdata/index.html | 1 - src/pkg/http/testdata/style.css | 1 - src/pkg/http/transfer.go | 520 -- src/pkg/http/transport.go | 717 -- src/pkg/http/transport_test.go | 662 -- src/pkg/http/triv.go | 149 - src/pkg/image/Makefile | 15 - src/pkg/image/bmp/Makefile | 11 - src/pkg/image/bmp/reader.go | 151 - src/pkg/image/color.go | 251 - src/pkg/image/color/color.go | 303 + src/pkg/image/color/ycbcr.go | 99 + src/pkg/image/color/ycbcr_test.go | 33 + src/pkg/image/decode_example_test.go | 79 + src/pkg/image/decode_test.go | 18 +- src/pkg/image/draw/Makefile | 11 - src/pkg/image/draw/bench_test.go | 82 +- src/pkg/image/draw/clip_test.go | 6 +- src/pkg/image/draw/draw.go | 90 +- src/pkg/image/draw/draw_test.go | 134 +- src/pkg/image/format.go | 22 +- src/pkg/image/geom.go | 2 +- src/pkg/image/gif/Makefile | 11 - src/pkg/image/gif/reader.go | 52 +- src/pkg/image/image.go | 409 +- src/pkg/image/image_test.go | 45 +- src/pkg/image/jpeg/Makefile | 15 - src/pkg/image/jpeg/huffman.go | 13 +- src/pkg/image/jpeg/reader.go | 64 +- src/pkg/image/jpeg/writer.go | 22 +- src/pkg/image/jpeg/writer_test.go | 17 +- src/pkg/image/names.go | 71 +- src/pkg/image/png/Makefile | 12 - src/pkg/image/png/reader.go | 362 +- src/pkg/image/png/reader_test.go | 70 +- src/pkg/image/png/testdata/invalid-crc32.png | Bin 0 -> 1289 bytes src/pkg/image/png/testdata/invalid-noend.png | Bin 0 -> 1277 bytes src/pkg/image/png/testdata/invalid-trunc.png | Bin 0 -> 1288 bytes src/pkg/image/png/testdata/invalid-zlib.png | Bin 0 -> 1289 bytes src/pkg/image/png/writer.go | 71 +- src/pkg/image/png/writer_test.go | 39 +- src/pkg/image/testdata/video-001.bmp | Bin 46610 -> 0 bytes src/pkg/image/testdata/video-001.tiff | Bin 30810 -> 0 bytes src/pkg/image/tiff/Makefile | 13 - src/pkg/image/tiff/buffer.go | 57 - src/pkg/image/tiff/buffer_test.go | 36 - src/pkg/image/tiff/consts.go | 103 - src/pkg/image/tiff/reader.go | 423 - src/pkg/image/tiff/reader_test.go | 25 - src/pkg/image/tiff/testdata/no_rps.tiff | Bin 1294 -> 0 bytes src/pkg/image/ycbcr.go | 144 + src/pkg/image/ycbcr/Makefile | 11 - src/pkg/image/ycbcr/ycbcr.go | 183 - src/pkg/image/ycbcr/ycbcr_test.go | 33 - src/pkg/image/ycbcr_test.go | 106 + src/pkg/index/suffixarray/Makefile | 12 - src/pkg/index/suffixarray/qsufsort.go | 10 +- src/pkg/index/suffixarray/suffixarray.go | 130 +- src/pkg/index/suffixarray/suffixarray_test.go | 76 +- src/pkg/io/Makefile | 13 - src/pkg/io/io.go | 150 +- src/pkg/io/io_test.go | 82 +- src/pkg/io/ioutil/Makefile | 12 - src/pkg/io/ioutil/ioutil.go | 70 +- src/pkg/io/ioutil/ioutil_test.go | 33 +- src/pkg/io/ioutil/tempfile.go | 12 +- src/pkg/io/multi.go | 14 +- src/pkg/io/multi_test.go | 11 +- src/pkg/io/pipe.go | 51 +- src/pkg/io/pipe_test.go | 27 +- src/pkg/json/Makefile | 16 - src/pkg/json/decode.go | 934 --- src/pkg/json/decode_test.go | 561 -- src/pkg/json/encode.go | 454 - src/pkg/json/encode_test.go | 82 - src/pkg/json/indent.go | 116 - src/pkg/json/scanner.go | 624 -- src/pkg/json/scanner_test.go | 278 - src/pkg/json/stream.go | 185 - src/pkg/json/stream_test.go | 122 - src/pkg/json/tagkey_test.go | 95 - src/pkg/json/tags.go | 44 - src/pkg/json/tags_test.go | 28 - src/pkg/log/Makefile | 11 - src/pkg/log/log.go | 81 +- src/pkg/log/syslog/syslog.go | 170 + src/pkg/log/syslog/syslog_test.go | 117 + src/pkg/log/syslog/syslog_unix.go | 33 + src/pkg/log/syslog/syslog_windows.go | 8 + src/pkg/mail/Makefile | 11 - src/pkg/mail/message.go | 524 -- src/pkg/mail/message_test.go | 278 - src/pkg/math/Makefile | 102 - src/pkg/math/abs.go | 22 + src/pkg/math/abs_386.s | 10 + src/pkg/math/abs_amd64.s | 12 + src/pkg/math/abs_arm.s | 6 + src/pkg/math/acosh.go | 5 +- src/pkg/math/all_test.go | 314 +- src/pkg/math/asin.go | 10 +- src/pkg/math/asin_amd64.s | 9 + src/pkg/math/asin_arm.s | 9 + src/pkg/math/asin_decl.go | 8 - src/pkg/math/asinh.go | 8 +- src/pkg/math/atan.go | 4 +- src/pkg/math/atan2.go | 18 +- src/pkg/math/atan2_386.s | 0 src/pkg/math/atan2_amd64.s | 6 + src/pkg/math/atan2_arm.s | 6 + src/pkg/math/atan2_decl.go | 7 - src/pkg/math/atan_amd64.s | 6 + src/pkg/math/atan_arm.s | 6 + src/pkg/math/atan_decl.go | 7 - src/pkg/math/atanh.go | 7 +- src/pkg/math/big/arith.go | 242 + src/pkg/math/big/arith_386.s | 276 + src/pkg/math/big/arith_amd64.s | 274 + src/pkg/math/big/arith_arm.s | 321 + src/pkg/math/big/arith_decl.go | 19 + src/pkg/math/big/arith_test.go | 372 + src/pkg/math/big/calibrate_test.go | 88 + src/pkg/math/big/example_test.go | 51 + src/pkg/math/big/hilbert_test.go | 160 + src/pkg/math/big/int.go | 896 ++ src/pkg/math/big/int_test.go | 1414 ++++ src/pkg/math/big/nat.go | 1422 ++++ src/pkg/math/big/nat_test.go | 663 ++ src/pkg/math/big/rat.go | 432 + src/pkg/math/big/rat_test.go | 456 + src/pkg/math/bits.go | 2 +- src/pkg/math/cbrt.go | 17 +- src/pkg/math/cmplx/abs.go | 12 + src/pkg/math/cmplx/asin.go | 170 + src/pkg/math/cmplx/cmath_test.go | 853 ++ src/pkg/math/cmplx/conj.go | 8 + src/pkg/math/cmplx/exp.go | 55 + src/pkg/math/cmplx/isinf.go | 21 + src/pkg/math/cmplx/isnan.go | 25 + src/pkg/math/cmplx/log.go | 64 + src/pkg/math/cmplx/phase.go | 11 + src/pkg/math/cmplx/polar.go | 12 + src/pkg/math/cmplx/pow.go | 60 + src/pkg/math/cmplx/rect.go | 13 + src/pkg/math/cmplx/sin.go | 132 + src/pkg/math/cmplx/sqrt.go | 103 + src/pkg/math/cmplx/tan.go | 184 + src/pkg/math/const.go | 12 +- src/pkg/math/dim.go | 72 + src/pkg/math/dim_386.s | 12 + src/pkg/math/dim_amd64.s | 142 + src/pkg/math/dim_arm.s | 12 + src/pkg/math/erf.go | 20 +- src/pkg/math/exp.go | 179 +- src/pkg/math/exp2.go | 10 - src/pkg/math/exp2_amd64.s | 6 + src/pkg/math/exp2_arm.s | 6 + src/pkg/math/exp2_decl.go | 7 - src/pkg/math/exp_arm.s | 6 + src/pkg/math/exp_decl.go | 7 - src/pkg/math/exp_port.go | 191 - src/pkg/math/exp_test.go | 10 - src/pkg/math/expm1.go | 10 +- src/pkg/math/expm1_amd64.s | 6 + src/pkg/math/expm1_arm.s | 6 + src/pkg/math/expm1_decl.go | 7 - src/pkg/math/export_test.go | 11 + src/pkg/math/fabs.go | 21 - src/pkg/math/fabs_386.s | 10 - src/pkg/math/fabs_amd64.s | 12 - src/pkg/math/fabs_decl.go | 7 - src/pkg/math/fdim.go | 29 - src/pkg/math/fdim_amd64.s | 26 - src/pkg/math/fdim_decl.go | 9 - src/pkg/math/floor.go | 34 +- src/pkg/math/floor_amd64.s | 12 + src/pkg/math/floor_arm.s | 12 + src/pkg/math/floor_decl.go | 9 - src/pkg/math/fmod.go | 47 - src/pkg/math/fmod_386.s | 15 - src/pkg/math/fmod_decl.go | 7 - src/pkg/math/frexp.go | 8 +- src/pkg/math/frexp_amd64.s | 6 + src/pkg/math/frexp_arm.s | 6 + src/pkg/math/frexp_decl.go | 7 - src/pkg/math/gamma.go | 23 +- src/pkg/math/hypot.go | 10 +- src/pkg/math/hypot_arm.s | 6 + src/pkg/math/hypot_decl.go | 7 - src/pkg/math/hypot_port.go | 63 - src/pkg/math/hypot_test.go | 9 - src/pkg/math/j0.go | 12 +- src/pkg/math/j1.go | 12 +- src/pkg/math/jn.go | 16 +- src/pkg/math/ldexp.go | 8 +- src/pkg/math/ldexp_amd64.s | 6 + src/pkg/math/ldexp_arm.s | 6 + src/pkg/math/ldexp_decl.go | 7 - src/pkg/math/lgamma.go | 165 +- src/pkg/math/log.go | 8 +- src/pkg/math/log10.go | 12 +- src/pkg/math/log10_amd64.s | 9 + src/pkg/math/log10_arm.s | 9 + src/pkg/math/log10_decl.go | 8 - src/pkg/math/log1p.go | 13 +- src/pkg/math/log1p_amd64.s | 6 + src/pkg/math/log1p_arm.s | 6 + src/pkg/math/log1p_decl.go | 7 - src/pkg/math/log_arm.s | 6 + src/pkg/math/log_decl.go | 7 - src/pkg/math/logb.go | 12 +- src/pkg/math/mod.go | 50 + src/pkg/math/mod_386.s | 15 + src/pkg/math/mod_amd64.s | 6 + src/pkg/math/mod_arm.s | 6 + src/pkg/math/modf.go | 7 +- src/pkg/math/modf_amd64.s | 6 + src/pkg/math/modf_arm.s | 6 + src/pkg/math/modf_decl.go | 7 - src/pkg/math/nextafter.go | 10 +- src/pkg/math/pow.go | 10 +- src/pkg/math/pow10.go | 10 + src/pkg/math/rand/exp.go | 223 + src/pkg/math/rand/normal.go | 158 + src/pkg/math/rand/rand.go | 190 + src/pkg/math/rand/rand_test.go | 362 + src/pkg/math/rand/rng.go | 246 + src/pkg/math/rand/zipf.go | 73 + src/pkg/math/remainder.go | 20 +- src/pkg/math/remainder_amd64.s | 6 + src/pkg/math/remainder_arm.s | 6 + src/pkg/math/remainder_decl.go | 7 - src/pkg/math/sin.go | 239 +- src/pkg/math/sin_amd64.s | 9 + src/pkg/math/sin_arm.s | 9 + src/pkg/math/sin_decl.go | 8 - src/pkg/math/sincos.go | 64 +- src/pkg/math/sincos_arm.s | 6 + src/pkg/math/sincos_decl.go | 7 - src/pkg/math/sinh.go | 10 + src/pkg/math/sqrt.go | 138 +- src/pkg/math/sqrt_decl.go | 7 - src/pkg/math/sqrt_port.go | 147 - src/pkg/math/sqrt_test.go | 9 - src/pkg/math/tan.go | 142 +- src/pkg/math/tan_amd64.s | 6 + src/pkg/math/tan_arm.s | 6 + src/pkg/math/tan_decl.go | 7 - src/pkg/math/tanh.go | 5 + src/pkg/mime/Makefile | 13 - src/pkg/mime/grammar.go | 27 +- src/pkg/mime/mediatype.go | 107 +- src/pkg/mime/mediatype_test.go | 40 +- src/pkg/mime/mime_test.go | 27 - src/pkg/mime/multipart/Makefile | 13 - src/pkg/mime/multipart/formdata.go | 35 +- src/pkg/mime/multipart/multipart.go | 60 +- src/pkg/mime/multipart/multipart_test.go | 17 +- src/pkg/mime/multipart/writer.go | 26 +- src/pkg/mime/type.go | 71 +- src/pkg/mime/type_test.go | 29 + src/pkg/mime/type_unix.go | 61 + src/pkg/mime/type_windows.go | 61 + src/pkg/net/Makefile | 157 - src/pkg/net/cgo_bsd.go | 2 + src/pkg/net/cgo_stub.go | 12 +- src/pkg/net/cgo_unix.go | 17 +- src/pkg/net/dial.go | 249 +- src/pkg/net/dial_test.go | 224 + src/pkg/net/dialgoogle_test.go | 21 +- src/pkg/net/dict/Makefile | 7 - src/pkg/net/dict/dict.go | 211 - src/pkg/net/dnsclient.go | 43 +- src/pkg/net/dnsclient_unix.go | 53 +- src/pkg/net/dnsconfig.go | 19 +- src/pkg/net/dnsmsg.go | 352 +- src/pkg/net/dnsmsg_test.go | 13 + src/pkg/net/doc.go | 59 + src/pkg/net/example_test.go | 35 + src/pkg/net/fd.go | 530 +- src/pkg/net/fd_darwin.go | 37 +- src/pkg/net/fd_freebsd.go | 36 +- src/pkg/net/fd_linux.go | 51 +- src/pkg/net/fd_netbsd.go | 116 + src/pkg/net/fd_openbsd.go | 36 +- src/pkg/net/fd_windows.go | 315 +- src/pkg/net/file.go | 41 +- src/pkg/net/file_plan9.go | 13 +- src/pkg/net/file_test.go | 130 +- src/pkg/net/file_windows.go | 6 +- src/pkg/net/hosts.go | 12 +- src/pkg/net/hosts_test.go | 2 +- src/pkg/net/hosts_testdata | 12 - src/pkg/net/http/cgi/child.go | 193 + src/pkg/net/http/cgi/child_test.go | 87 + src/pkg/net/http/cgi/host.go | 350 + src/pkg/net/http/cgi/host_test.go | 479 ++ src/pkg/net/http/cgi/matryoshka_test.go | 93 + src/pkg/net/http/cgi/testdata/test.cgi | 96 + src/pkg/net/http/chunked.go | 170 + src/pkg/net/http/chunked_test.go | 39 + src/pkg/net/http/client.go | 334 + src/pkg/net/http/client_test.go | 442 + src/pkg/net/http/cookie.go | 267 + src/pkg/net/http/cookie_test.go | 228 + src/pkg/net/http/doc.go | 80 + src/pkg/net/http/example_test.go | 51 + src/pkg/net/http/export_test.go | 43 + src/pkg/net/http/fcgi/child.go | 271 + src/pkg/net/http/fcgi/fcgi.go | 274 + src/pkg/net/http/fcgi/fcgi_test.go | 150 + src/pkg/net/http/filetransport.go | 123 + src/pkg/net/http/filetransport_test.go | 65 + src/pkg/net/http/fs.go | 367 + src/pkg/net/http/fs_test.go | 478 ++ src/pkg/net/http/header.go | 78 + src/pkg/net/http/header_test.go | 81 + src/pkg/net/http/httptest/recorder.go | 58 + src/pkg/net/http/httptest/server.go | 207 + src/pkg/net/http/httptest/server_test.go | 29 + src/pkg/net/http/httputil/chunked.go | 172 + src/pkg/net/http/httputil/chunked_test.go | 41 + src/pkg/net/http/httputil/dump.go | 229 + src/pkg/net/http/httputil/dump_test.go | 152 + src/pkg/net/http/httputil/persist.go | 422 + src/pkg/net/http/httputil/reverseproxy.go | 172 + src/pkg/net/http/httputil/reverseproxy_test.go | 109 + src/pkg/net/http/jar.go | 30 + src/pkg/net/http/lex.go | 136 + src/pkg/net/http/lex_test.go | 70 + src/pkg/net/http/pprof/pprof.go | 194 + src/pkg/net/http/proxy_test.go | 48 + src/pkg/net/http/range_test.go | 57 + src/pkg/net/http/readrequest_test.go | 283 + src/pkg/net/http/request.go | 743 ++ src/pkg/net/http/request_test.go | 302 + src/pkg/net/http/requestwrite_test.go | 438 + src/pkg/net/http/response.go | 236 + src/pkg/net/http/response_test.go | 446 + src/pkg/net/http/responsewrite_test.go | 109 + src/pkg/net/http/serve_test.go | 1186 +++ src/pkg/net/http/server.go | 1232 +++ src/pkg/net/http/sniff.go | 214 + src/pkg/net/http/sniff_test.go | 138 + src/pkg/net/http/status.go | 108 + src/pkg/net/http/testdata/file | 1 + src/pkg/net/http/testdata/index.html | 1 + src/pkg/net/http/testdata/style.css | 1 + src/pkg/net/http/transfer.go | 630 ++ src/pkg/net/http/transport.go | 749 ++ src/pkg/net/http/transport_test.go | 821 ++ src/pkg/net/http/triv.go | 141 + src/pkg/net/interface.go | 52 +- src/pkg/net/interface_bsd.go | 112 +- src/pkg/net/interface_darwin.go | 35 +- src/pkg/net/interface_freebsd.go | 35 +- src/pkg/net/interface_linux.go | 164 +- src/pkg/net/interface_linux_test.go | 54 + src/pkg/net/interface_netbsd.go | 14 + src/pkg/net/interface_openbsd.go | 4 +- src/pkg/net/interface_stub.go | 12 +- src/pkg/net/interface_test.go | 65 +- src/pkg/net/interface_windows.go | 54 +- src/pkg/net/ip.go | 259 +- src/pkg/net/ip_test.go | 243 +- src/pkg/net/ipraw_test.go | 246 +- src/pkg/net/iprawsock.go | 8 +- src/pkg/net/iprawsock_plan9.go | 94 +- src/pkg/net/iprawsock_posix.go | 193 +- src/pkg/net/ipsock.go | 41 +- src/pkg/net/ipsock_plan9.go | 92 +- src/pkg/net/ipsock_posix.go | 114 +- src/pkg/net/lookup_plan9.go | 74 +- src/pkg/net/lookup_test.go | 70 +- src/pkg/net/lookup_unix.go | 104 +- src/pkg/net/lookup_windows.go | 89 +- src/pkg/net/mac.go | 86 + src/pkg/net/mac_test.go | 66 + src/pkg/net/mail/message.go | 523 ++ src/pkg/net/mail/message_test.go | 261 + src/pkg/net/multicast_test.go | 241 +- src/pkg/net/net.go | 190 +- src/pkg/net/net_test.go | 189 +- src/pkg/net/newpollserver.go | 17 +- src/pkg/net/parse.go | 49 +- src/pkg/net/parse_test.go | 6 +- src/pkg/net/pipe.go | 21 +- src/pkg/net/pipe_test.go | 5 +- src/pkg/net/port.go | 11 +- src/pkg/net/rpc/client.go | 293 + src/pkg/net/rpc/debug.go | 90 + src/pkg/net/rpc/jsonrpc/all_test.go | 221 + src/pkg/net/rpc/jsonrpc/client.go | 123 + src/pkg/net/rpc/jsonrpc/server.go | 136 + src/pkg/net/rpc/server.go | 651 ++ src/pkg/net/rpc/server_test.go | 597 ++ src/pkg/net/sendfile_linux.go | 32 +- src/pkg/net/sendfile_stub.go | 9 +- src/pkg/net/sendfile_windows.go | 12 +- src/pkg/net/server_test.go | 556 +- src/pkg/net/smtp/auth.go | 98 + src/pkg/net/smtp/smtp.go | 292 + src/pkg/net/smtp/smtp_test.go | 271 + src/pkg/net/sock.go | 141 +- src/pkg/net/sock_bsd.go | 65 +- src/pkg/net/sock_linux.go | 59 +- src/pkg/net/sock_windows.go | 46 +- src/pkg/net/sockopt.go | 193 + src/pkg/net/sockopt_bsd.go | 61 + src/pkg/net/sockopt_linux.go | 51 + src/pkg/net/sockopt_windows.go | 49 + src/pkg/net/sockoptip.go | 219 + src/pkg/net/sockoptip_bsd.go | 62 + src/pkg/net/sockoptip_darwin.go | 90 + src/pkg/net/sockoptip_freebsd.go | 92 + src/pkg/net/sockoptip_linux.go | 140 + src/pkg/net/sockoptip_netbsd.go | 39 + src/pkg/net/sockoptip_openbsd.go | 90 + src/pkg/net/sockoptip_windows.go | 91 + src/pkg/net/tcpsock.go | 6 +- src/pkg/net/tcpsock_plan9.go | 44 +- src/pkg/net/tcpsock_posix.go | 188 +- src/pkg/net/testdata/hosts | 12 + src/pkg/net/testdata/igmp | 24 + src/pkg/net/testdata/igmp6 | 18 + src/pkg/net/textproto/Makefile | 15 - src/pkg/net/textproto/header.go | 2 +- src/pkg/net/textproto/pipeline.go | 2 +- src/pkg/net/textproto/reader.go | 172 +- src/pkg/net/textproto/reader_test.go | 109 +- src/pkg/net/textproto/textproto.go | 14 +- src/pkg/net/textproto/writer.go | 7 +- src/pkg/net/timeout_test.go | 112 +- src/pkg/net/udp_test.go | 89 + src/pkg/net/udpsock.go | 6 +- src/pkg/net/udpsock_plan9.go | 75 +- src/pkg/net/udpsock_posix.go | 246 +- src/pkg/net/unicast_test.go | 538 ++ src/pkg/net/unixsock.go | 6 +- src/pkg/net/unixsock_plan9.go | 65 +- src/pkg/net/unixsock_posix.go | 198 +- src/pkg/net/url/example_test.go | 41 + src/pkg/net/url/url.go | 657 ++ src/pkg/net/url/url_test.go | 746 ++ src/pkg/netchan/Makefile | 13 - src/pkg/netchan/common.go | 336 - src/pkg/netchan/export.go | 400 - src/pkg/netchan/import.go | 287 - src/pkg/netchan/netchan_test.go | 435 - src/pkg/old/template/Makefile | 14 - src/pkg/old/template/doc.go | 91 - src/pkg/old/template/execute.go | 346 - src/pkg/old/template/format.go | 77 - src/pkg/old/template/parse.go | 743 -- src/pkg/old/template/template_test.go | 804 -- src/pkg/os/Makefile | 100 - src/pkg/os/dir_plan9.go | 64 +- src/pkg/os/dir_unix.go | 23 +- src/pkg/os/dir_windows.go | 4 +- src/pkg/os/doc.go | 124 + src/pkg/os/env.go | 40 +- src/pkg/os/env_plan9.go | 96 - src/pkg/os/env_test.go | 11 + src/pkg/os/env_unix.go | 109 - src/pkg/os/env_windows.go | 127 - src/pkg/os/error.go | 65 +- src/pkg/os/error_plan9.go | 73 +- src/pkg/os/error_posix.go | 94 +- src/pkg/os/error_test.go | 81 + src/pkg/os/error_windows.go | 30 + src/pkg/os/exec.go | 19 +- src/pkg/os/exec/example_test.go | 75 + src/pkg/os/exec/exec.go | 401 + src/pkg/os/exec/exec_test.go | 325 + src/pkg/os/exec/lp_plan9.go | 53 + src/pkg/os/exec/lp_test.go | 33 + src/pkg/os/exec/lp_unix.go | 56 + src/pkg/os/exec/lp_windows.go | 82 + src/pkg/os/exec_plan9.go | 125 +- src/pkg/os/exec_posix.go | 122 +- src/pkg/os/exec_unix.go | 75 +- src/pkg/os/exec_windows.go | 76 +- src/pkg/os/export_test.go | 9 + src/pkg/os/file.go | 207 +- src/pkg/os/file_plan9.go | 191 +- src/pkg/os/file_posix.go | 198 +- src/pkg/os/file_unix.go | 216 +- src/pkg/os/file_windows.go | 248 +- src/pkg/os/getwd.go | 16 +- src/pkg/os/inotify/Makefile | 14 - src/pkg/os/inotify/inotify_linux.go | 288 - src/pkg/os/inotify/inotify_linux_test.go | 96 - src/pkg/os/mkunixsignals.sh | 24 - src/pkg/os/os_test.go | 328 +- src/pkg/os/os_unix_test.go | 76 + src/pkg/os/path.go | 21 +- src/pkg/os/path_test.go | 37 +- src/pkg/os/path_unix.go | 2 + src/pkg/os/proc.go | 6 +- src/pkg/os/signal/Makefile | 11 - src/pkg/os/signal/sig.s | 16 + src/pkg/os/signal/signal.go | 73 +- src/pkg/os/signal/signal_stub.go | 11 + src/pkg/os/signal/signal_test.go | 50 +- src/pkg/os/signal/signal_unix.go | 38 + src/pkg/os/signal/signal_windows_test.go | 51 + src/pkg/os/stat_darwin.go | 71 +- src/pkg/os/stat_freebsd.go | 71 +- src/pkg/os/stat_linux.go | 71 +- src/pkg/os/stat_netbsd.go | 61 + src/pkg/os/stat_openbsd.go | 71 +- src/pkg/os/stat_plan9.go | 126 +- src/pkg/os/stat_windows.go | 258 +- src/pkg/os/str.go | 2 + src/pkg/os/sys_bsd.go | 11 +- src/pkg/os/sys_linux.go | 3 +- src/pkg/os/sys_plan9.go | 2 +- src/pkg/os/sys_windows.go | 4 +- src/pkg/os/time.go | 19 - src/pkg/os/types.go | 143 +- src/pkg/os/user/Makefile | 26 - src/pkg/os/user/lookup_stubs.go | 15 +- src/pkg/os/user/lookup_unix.go | 31 +- src/pkg/os/user/lookup_windows.go | 117 + src/pkg/os/user/user.go | 14 +- src/pkg/os/user/user_test.go | 76 +- src/pkg/patch/Makefile | 14 - src/pkg/patch/apply.go | 54 - src/pkg/patch/git.go | 121 - src/pkg/patch/patch.go | 322 - src/pkg/patch/patch_test.go | 390 - src/pkg/patch/textdiff.go | 175 - src/pkg/path/Makefile | 14 - src/pkg/path/example_test.go | 63 + src/pkg/path/filepath/Makefile | 32 - src/pkg/path/filepath/match.go | 51 +- src/pkg/path/filepath/match_test.go | 57 +- src/pkg/path/filepath/path.go | 289 +- src/pkg/path/filepath/path_plan9.go | 2 +- src/pkg/path/filepath/path_test.go | 515 +- src/pkg/path/filepath/path_unix.go | 4 +- src/pkg/path/filepath/path_windows.go | 42 +- src/pkg/path/filepath/symlink.go | 67 + src/pkg/path/filepath/symlink_windows.go | 63 + src/pkg/path/match.go | 18 +- src/pkg/path/match_test.go | 7 +- src/pkg/path/path.go | 31 +- src/pkg/path/path_test.go | 39 +- src/pkg/rand/Makefile | 15 - src/pkg/rand/exp.go | 223 - src/pkg/rand/normal.go | 158 - src/pkg/rand/rand.go | 179 - src/pkg/rand/rand_test.go | 350 - src/pkg/rand/rng.go | 246 - src/pkg/rand/zipf.go | 73 - src/pkg/reflect/Makefile | 13 - src/pkg/reflect/all_test.go | 198 +- src/pkg/reflect/deepequal.go | 16 +- src/pkg/reflect/tostring_test.go | 10 +- src/pkg/reflect/type.go | 253 +- src/pkg/reflect/value.go | 1476 ++-- src/pkg/regexp/Makefile | 11 - src/pkg/regexp/all_test.go | 165 +- src/pkg/regexp/exec.go | 345 + src/pkg/regexp/exec_test.go | 709 ++ src/pkg/regexp/find_test.go | 32 +- src/pkg/regexp/regexp.go | 1343 +-- src/pkg/regexp/syntax/compile.go | 289 + src/pkg/regexp/syntax/make_perl_groups.pl | 103 + src/pkg/regexp/syntax/parse.go | 1867 +++++ src/pkg/regexp/syntax/parse_test.go | 551 ++ src/pkg/regexp/syntax/perl_groups.go | 130 + src/pkg/regexp/syntax/prog.go | 310 + src/pkg/regexp/syntax/prog_test.go | 105 + src/pkg/regexp/syntax/regexp.go | 319 + src/pkg/regexp/syntax/simplify.go | 151 + src/pkg/regexp/syntax/simplify_test.go | 151 + src/pkg/regexp/testdata/README | 23 + src/pkg/regexp/testdata/basic.dat | 221 + src/pkg/regexp/testdata/nullsubexpr.dat | 79 + src/pkg/regexp/testdata/re2-exhaustive.txt.bz2 | Bin 0 -> 394016 bytes src/pkg/regexp/testdata/re2-search.txt | 3667 ++++++++ src/pkg/regexp/testdata/repetition.dat | 163 + src/pkg/regexp/testdata/testregex.c | 2286 +++++ src/pkg/rpc/Makefile | 13 - src/pkg/rpc/client.go | 286 - src/pkg/rpc/debug.go | 90 - src/pkg/rpc/jsonrpc/Makefile | 12 - src/pkg/rpc/jsonrpc/all_test.go | 156 - src/pkg/rpc/jsonrpc/client.go | 124 - src/pkg/rpc/jsonrpc/server.go | 136 - src/pkg/rpc/server.go | 637 -- src/pkg/rpc/server_test.go | 501 -- src/pkg/runtime/386/arch.h | 3 - src/pkg/runtime/386/asm.s | 549 -- src/pkg/runtime/386/atomic.c | 19 - src/pkg/runtime/386/closure.c | 105 - src/pkg/runtime/386/memmove.s | 86 - src/pkg/runtime/386/vlop.s | 48 - src/pkg/runtime/386/vlrt.c | 815 -- src/pkg/runtime/Makefile | 165 +- src/pkg/runtime/alg.c | 484 ++ src/pkg/runtime/amd64/arch.h | 3 - src/pkg/runtime/amd64/asm.s | 577 -- src/pkg/runtime/amd64/atomic.c | 19 - src/pkg/runtime/amd64/closure.c | 123 - src/pkg/runtime/amd64/memmove.s | 88 - src/pkg/runtime/amd64/traceback.c | 295 - src/pkg/runtime/arch_386.h | 4 + src/pkg/runtime/arch_amd64.h | 4 + src/pkg/runtime/arch_arm.h | 4 + src/pkg/runtime/arm/arch.h | 3 - src/pkg/runtime/arm/asm.s | 316 - src/pkg/runtime/arm/atomic.c | 83 - src/pkg/runtime/arm/closure.c | 129 - src/pkg/runtime/arm/memmove.s | 255 - src/pkg/runtime/arm/memset.s | 94 - src/pkg/runtime/arm/softfloat.c | 525 -- src/pkg/runtime/arm/traceback.c | 213 - src/pkg/runtime/arm/vlop.s | 190 - src/pkg/runtime/arm/vlrt.c | 816 -- src/pkg/runtime/asm_386.s | 572 ++ src/pkg/runtime/asm_amd64.s | 599 ++ src/pkg/runtime/asm_arm.s | 322 + src/pkg/runtime/atomic_386.c | 19 + src/pkg/runtime/atomic_amd64.c | 19 + src/pkg/runtime/atomic_arm.c | 83 + src/pkg/runtime/callback_windows_386.c | 107 + src/pkg/runtime/callback_windows_amd64.c | 104 + src/pkg/runtime/cgo/386.S | 67 - src/pkg/runtime/cgo/Makefile | 60 - src/pkg/runtime/cgo/amd64.S | 73 - src/pkg/runtime/cgo/arm.S | 1 - src/pkg/runtime/cgo/cgo.go | 12 + src/pkg/runtime/cgo/darwin_386.c | 149 - src/pkg/runtime/cgo/darwin_amd64.c | 119 - src/pkg/runtime/cgo/freebsd_386.c | 64 - src/pkg/runtime/cgo/freebsd_amd64.c | 63 - src/pkg/runtime/cgo/gcc_386.S | 67 + src/pkg/runtime/cgo/gcc_amd64.S | 92 + src/pkg/runtime/cgo/gcc_arm.S | 1 + src/pkg/runtime/cgo/gcc_darwin_386.c | 166 + src/pkg/runtime/cgo/gcc_darwin_amd64.c | 136 + src/pkg/runtime/cgo/gcc_freebsd_386.c | 81 + src/pkg/runtime/cgo/gcc_freebsd_amd64.c | 81 + src/pkg/runtime/cgo/gcc_linux_386.c | 88 + src/pkg/runtime/cgo/gcc_linux_amd64.c | 79 + src/pkg/runtime/cgo/gcc_linux_arm.c | 20 + src/pkg/runtime/cgo/gcc_setenv.c | 18 + src/pkg/runtime/cgo/gcc_util.c | 51 + src/pkg/runtime/cgo/gcc_windows_386.c | 59 + src/pkg/runtime/cgo/gcc_windows_amd64.c | 57 + src/pkg/runtime/cgo/libcgo.h | 2 +- src/pkg/runtime/cgo/linux_386.c | 73 - src/pkg/runtime/cgo/linux_amd64.c | 63 - src/pkg/runtime/cgo/linux_arm.c | 19 - src/pkg/runtime/cgo/setenv.c | 16 - src/pkg/runtime/cgo/util.c | 51 - src/pkg/runtime/cgo/windows_386.c | 62 - src/pkg/runtime/cgo/windows_amd64.c | 60 - src/pkg/runtime/cgocall.c | 54 +- src/pkg/runtime/chan.c | 41 +- src/pkg/runtime/chan_test.go | 60 + src/pkg/runtime/closure_386.c | 105 + src/pkg/runtime/closure_amd64.c | 123 + src/pkg/runtime/closure_arm.c | 129 + src/pkg/runtime/compiler.go | 13 + src/pkg/runtime/cpuprof.c | 1 + src/pkg/runtime/darwin/386/defs.h | 289 - src/pkg/runtime/darwin/386/rt0.s | 8 - src/pkg/runtime/darwin/386/signal.c | 194 - src/pkg/runtime/darwin/386/sys.s | 311 - src/pkg/runtime/darwin/amd64/defs.h | 305 - src/pkg/runtime/darwin/amd64/rt0.s | 10 - src/pkg/runtime/darwin/amd64/signal.c | 204 - src/pkg/runtime/darwin/amd64/sys.s | 295 - src/pkg/runtime/darwin/defs.c | 159 - src/pkg/runtime/darwin/mem.c | 55 - src/pkg/runtime/darwin/os.h | 31 - src/pkg/runtime/darwin/signals.h | 51 - src/pkg/runtime/darwin/thread.c | 484 -- src/pkg/runtime/debug.go | 62 +- src/pkg/runtime/debug/Makefile | 11 - src/pkg/runtime/debug/stack_test.go | 21 +- src/pkg/runtime/defs1_linux.go | 37 + src/pkg/runtime/defs2_linux.go | 126 + src/pkg/runtime/defs_arm_linux.go | 124 + src/pkg/runtime/defs_darwin.go | 163 + src/pkg/runtime/defs_darwin_386.h | 366 + src/pkg/runtime/defs_darwin_amd64.h | 369 + src/pkg/runtime/defs_freebsd.go | 117 + src/pkg/runtime/defs_freebsd_386.h | 193 + src/pkg/runtime/defs_freebsd_amd64.h | 204 + src/pkg/runtime/defs_linux.go | 104 + src/pkg/runtime/defs_linux_386.h | 191 + src/pkg/runtime/defs_linux_amd64.h | 234 + src/pkg/runtime/defs_linux_arm.h | 148 + src/pkg/runtime/defs_netbsd.go | 111 + src/pkg/runtime/defs_netbsd_386.h | 146 + src/pkg/runtime/defs_netbsd_amd64.h | 158 + src/pkg/runtime/defs_openbsd.go | 111 + src/pkg/runtime/defs_openbsd_386.h | 146 + src/pkg/runtime/defs_openbsd_amd64.h | 158 + src/pkg/runtime/defs_plan9_386.h | 2 + src/pkg/runtime/defs_windows.go | 66 + src/pkg/runtime/defs_windows_386.h | 99 + src/pkg/runtime/defs_windows_amd64.h | 114 + src/pkg/runtime/error.go | 49 +- src/pkg/runtime/export_test.go | 4 + src/pkg/runtime/extern.go | 75 +- src/pkg/runtime/freebsd/386/defs.h | 187 - src/pkg/runtime/freebsd/386/rt0.s | 9 - src/pkg/runtime/freebsd/386/signal.c | 193 - src/pkg/runtime/freebsd/386/sys.s | 239 - src/pkg/runtime/freebsd/amd64/defs.h | 198 - src/pkg/runtime/freebsd/amd64/rt0.s | 9 - src/pkg/runtime/freebsd/amd64/signal.c | 201 - src/pkg/runtime/freebsd/amd64/sys.s | 182 - src/pkg/runtime/freebsd/defs.c | 108 - src/pkg/runtime/freebsd/mem.c | 74 - src/pkg/runtime/freebsd/os.h | 12 - src/pkg/runtime/freebsd/signals.h | 52 - src/pkg/runtime/freebsd/thread.c | 201 - src/pkg/runtime/gc_test.go | 41 + src/pkg/runtime/goc2c.c | 727 -- src/pkg/runtime/hashmap.c | 370 +- src/pkg/runtime/hashmap.h | 5 +- src/pkg/runtime/iface.c | 178 +- src/pkg/runtime/linux/386/defs.h | 191 - src/pkg/runtime/linux/386/rt0.s | 9 - src/pkg/runtime/linux/386/signal.c | 184 - src/pkg/runtime/linux/386/sys.s | 344 - src/pkg/runtime/linux/amd64/defs.h | 236 - src/pkg/runtime/linux/amd64/rt0.s | 10 - src/pkg/runtime/linux/amd64/signal.c | 194 - src/pkg/runtime/linux/amd64/sys.s | 252 - src/pkg/runtime/linux/arm/defs.h | 149 - src/pkg/runtime/linux/arm/rt0.s | 6 - src/pkg/runtime/linux/arm/signal.c | 189 - src/pkg/runtime/linux/arm/sys.s | 319 - src/pkg/runtime/linux/defs.c | 95 - src/pkg/runtime/linux/defs1.c | 24 - src/pkg/runtime/linux/defs2.c | 120 - src/pkg/runtime/linux/defs_arm.c | 122 - src/pkg/runtime/linux/mem.c | 113 - src/pkg/runtime/linux/os.h | 19 - src/pkg/runtime/linux/signals.h | 51 - src/pkg/runtime/linux/thread.c | 320 - src/pkg/runtime/lock_futex.c | 156 + src/pkg/runtime/lock_sema.c | 230 + src/pkg/runtime/malloc.goc | 125 +- src/pkg/runtime/malloc.h | 59 +- src/pkg/runtime/malloc1.go | 26 + src/pkg/runtime/mallocrand.go | 93 + src/pkg/runtime/mallocrep.go | 72 + src/pkg/runtime/mallocrep1.go | 143 + src/pkg/runtime/mcache.c | 1 + src/pkg/runtime/mcentral.c | 1 + src/pkg/runtime/mem.go | 35 +- src/pkg/runtime/mem_darwin.c | 59 + src/pkg/runtime/mem_freebsd.c | 79 + src/pkg/runtime/mem_linux.c | 131 + src/pkg/runtime/mem_netbsd.c | 85 + src/pkg/runtime/mem_openbsd.c | 85 + src/pkg/runtime/mem_plan9.c | 68 + src/pkg/runtime/mem_windows.c | 71 + src/pkg/runtime/memmove_386.s | 86 + src/pkg/runtime/memmove_amd64.s | 88 + src/pkg/runtime/memmove_arm.s | 255 + src/pkg/runtime/memset_arm.s | 94 + src/pkg/runtime/mfinal.c | 193 +- src/pkg/runtime/mfinal_test.go | 64 + src/pkg/runtime/mfixalloc.c | 1 + src/pkg/runtime/mgc0.c | 737 +- src/pkg/runtime/mheap.c | 89 +- src/pkg/runtime/mkasmh.sh | 44 +- src/pkg/runtime/mkgodefs.sh | 39 - src/pkg/runtime/mkversion.c | 8 +- src/pkg/runtime/mprof.goc | 146 +- src/pkg/runtime/msize.c | 1 + src/pkg/runtime/openbsd/amd64/defs.h | 149 - src/pkg/runtime/openbsd/amd64/rt0.s | 8 - src/pkg/runtime/openbsd/amd64/signal.c | 199 - src/pkg/runtime/openbsd/amd64/sys.s | 221 - src/pkg/runtime/openbsd/defs.c | 103 - src/pkg/runtime/openbsd/mem.c | 74 - src/pkg/runtime/openbsd/os.h | 12 - src/pkg/runtime/openbsd/signals.h | 52 - src/pkg/runtime/openbsd/thread.c | 156 - src/pkg/runtime/os_darwin.h | 44 + src/pkg/runtime/os_freebsd.h | 26 + src/pkg/runtime/os_linux.h | 43 + src/pkg/runtime/os_netbsd.h | 21 + src/pkg/runtime/os_openbsd.h | 21 + src/pkg/runtime/os_plan9.h | 69 + src/pkg/runtime/os_windows.h | 32 + src/pkg/runtime/plan9/386/defs.h | 2 - src/pkg/runtime/plan9/386/rt0.s | 32 - src/pkg/runtime/plan9/386/signal.c | 24 - src/pkg/runtime/plan9/386/sys.s | 82 - src/pkg/runtime/plan9/mem.c | 67 - src/pkg/runtime/plan9/os.h | 57 - src/pkg/runtime/plan9/signals.h | 1 - src/pkg/runtime/plan9/thread.c | 174 - src/pkg/runtime/pprof/Makefile | 11 - src/pkg/runtime/pprof/pprof.go | 507 +- src/pkg/runtime/pprof/pprof_test.go | 17 +- src/pkg/runtime/print.c | 62 +- src/pkg/runtime/proc.c | 481 +- src/pkg/runtime/rt0_darwin_386.s | 8 + src/pkg/runtime/rt0_darwin_amd64.s | 10 + src/pkg/runtime/rt0_freebsd_386.s | 9 + src/pkg/runtime/rt0_freebsd_amd64.s | 9 + src/pkg/runtime/rt0_linux_386.s | 17 + src/pkg/runtime/rt0_linux_amd64.s | 10 + src/pkg/runtime/rt0_linux_arm.s | 60 + src/pkg/runtime/rt0_netbsd_386.s | 6 + src/pkg/runtime/rt0_netbsd_amd64.s | 8 + src/pkg/runtime/rt0_openbsd_386.s | 6 + src/pkg/runtime/rt0_openbsd_amd64.s | 8 + src/pkg/runtime/rt0_plan9_386.s | 32 + src/pkg/runtime/rt0_windows_386.s | 14 + src/pkg/runtime/rt0_windows_amd64.s | 13 + src/pkg/runtime/runtime-gdb.py | 83 +- src/pkg/runtime/runtime.c | 401 +- src/pkg/runtime/runtime.h | 247 +- src/pkg/runtime/runtime1.goc | 4 + src/pkg/runtime/runtime_linux_test.go | 29 + src/pkg/runtime/runtime_test.go | 40 + src/pkg/runtime/sema.goc | 14 +- src/pkg/runtime/sema_test.go | 100 - src/pkg/runtime/sig.go | 16 - src/pkg/runtime/signal_darwin_386.c | 140 + src/pkg/runtime/signal_darwin_amd64.c | 150 + src/pkg/runtime/signal_freebsd_386.c | 136 + src/pkg/runtime/signal_freebsd_amd64.c | 144 + src/pkg/runtime/signal_linux_386.c | 158 + src/pkg/runtime/signal_linux_amd64.c | 143 + src/pkg/runtime/signal_linux_arm.c | 140 + src/pkg/runtime/signal_netbsd_386.c | 132 + src/pkg/runtime/signal_netbsd_amd64.c | 141 + src/pkg/runtime/signal_openbsd_386.c | 132 + src/pkg/runtime/signal_openbsd_amd64.c | 141 + src/pkg/runtime/signal_plan9_386.c | 19 + src/pkg/runtime/signal_unix.c | 70 + src/pkg/runtime/signal_windows_386.c | 93 + src/pkg/runtime/signal_windows_amd64.c | 100 + src/pkg/runtime/signals_darwin.h | 50 + src/pkg/runtime/signals_freebsd.h | 51 + src/pkg/runtime/signals_linux.h | 83 + src/pkg/runtime/signals_netbsd.h | 51 + src/pkg/runtime/signals_openbsd.h | 51 + src/pkg/runtime/signals_plan9.h | 1 + src/pkg/runtime/signals_windows.h | 3 + src/pkg/runtime/sigqueue.goc | 112 +- src/pkg/runtime/slice.c | 65 +- src/pkg/runtime/softfloat64.go | 2 +- src/pkg/runtime/softfloat64_test.go | 2 +- src/pkg/runtime/softfloat_arm.c | 525 ++ src/pkg/runtime/stack.h | 9 +- src/pkg/runtime/stack_test.go | 1528 ++++ src/pkg/runtime/string.goc | 58 +- src/pkg/runtime/symtab.c | 71 +- src/pkg/runtime/sys_darwin_386.s | 378 + src/pkg/runtime/sys_darwin_amd64.s | 363 + src/pkg/runtime/sys_freebsd_386.s | 324 + src/pkg/runtime/sys_freebsd_amd64.s | 250 + src/pkg/runtime/sys_linux_386.s | 425 + src/pkg/runtime/sys_linux_amd64.s | 312 + src/pkg/runtime/sys_linux_arm.s | 387 + src/pkg/runtime/sys_netbsd_386.s | 325 + src/pkg/runtime/sys_netbsd_amd64.s | 268 + src/pkg/runtime/sys_openbsd_386.s | 325 + src/pkg/runtime/sys_openbsd_amd64.s | 268 + src/pkg/runtime/sys_plan9_386.s | 97 + src/pkg/runtime/sys_windows_386.s | 298 + src/pkg/runtime/sys_windows_amd64.s | 342 + src/pkg/runtime/syscall_windows.goc | 145 + src/pkg/runtime/syscall_windows_test.go | 243 + src/pkg/runtime/thread_darwin.c | 499 ++ src/pkg/runtime/thread_freebsd.c | 217 + src/pkg/runtime/thread_linux.c | 277 + src/pkg/runtime/thread_netbsd.c | 235 + src/pkg/runtime/thread_openbsd.c | 235 + src/pkg/runtime/thread_plan9.c | 269 + src/pkg/runtime/thread_windows.c | 430 + src/pkg/runtime/time.goc | 253 + src/pkg/runtime/traceback_arm.c | 217 + src/pkg/runtime/traceback_x86.c | 303 + src/pkg/runtime/type.go | 218 +- src/pkg/runtime/type.h | 3 +- src/pkg/runtime/vlop_386.s | 48 + src/pkg/runtime/vlop_arm.s | 190 + src/pkg/runtime/vlrt_386.c | 815 ++ src/pkg/runtime/vlrt_arm.c | 816 ++ src/pkg/runtime/windows/386/defs.h | 81 - src/pkg/runtime/windows/386/rt0.s | 14 - src/pkg/runtime/windows/386/signal.c | 98 - src/pkg/runtime/windows/386/sys.s | 256 - src/pkg/runtime/windows/amd64/defs.h | 40 - src/pkg/runtime/windows/amd64/rt0.s | 13 - src/pkg/runtime/windows/amd64/signal.c | 20 - src/pkg/runtime/windows/amd64/sys.s | 130 - src/pkg/runtime/windows/defs.c | 37 - src/pkg/runtime/windows/mem.c | 70 - src/pkg/runtime/windows/os.h | 30 - src/pkg/runtime/windows/signals.h | 3 - src/pkg/runtime/windows/syscall.goc | 67 - src/pkg/runtime/windows/thread.c | 432 - src/pkg/scanner/Makefile | 11 - src/pkg/scanner/scanner.go | 660 -- src/pkg/scanner/scanner_test.go | 566 -- src/pkg/smtp/Makefile | 12 - src/pkg/smtp/auth.go | 69 - src/pkg/smtp/smtp.go | 294 - src/pkg/smtp/smtp_test.go | 182 - src/pkg/sort/Makefile | 12 - src/pkg/sort/example_interface_test.go | 77 + src/pkg/sort/example_reverse_test.go | 30 + src/pkg/sort/example_test.go | 17 + src/pkg/sort/export_test.go | 9 + src/pkg/sort/sort.go | 69 +- src/pkg/sort/sort_test.go | 70 +- src/pkg/strconv/Makefile | 17 - src/pkg/strconv/atob.go | 21 +- src/pkg/strconv/atob_test.go | 13 +- src/pkg/strconv/atof.go | 155 +- src/pkg/strconv/atof_test.go | 204 +- src/pkg/strconv/atoi.go | 151 +- src/pkg/strconv/atoi_test.go | 121 +- src/pkg/strconv/decimal.go | 71 +- src/pkg/strconv/decimal_test.go | 20 +- src/pkg/strconv/extfloat.go | 501 ++ src/pkg/strconv/fp_test.go | 22 +- src/pkg/strconv/ftoa.go | 263 +- src/pkg/strconv/ftoa_test.go | 126 +- src/pkg/strconv/internal_test.go | 6 +- src/pkg/strconv/isprint.go | 521 ++ src/pkg/strconv/itoa.go | 146 +- src/pkg/strconv/itoa_test.go | 154 +- src/pkg/strconv/makeisprint.go | 162 + src/pkg/strconv/quote.go | 293 +- src/pkg/strconv/quote_test.go | 56 +- src/pkg/strings/Makefile | 12 - src/pkg/strings/example_test.go | 182 + src/pkg/strings/export_test.go | 9 + src/pkg/strings/reader.go | 88 +- src/pkg/strings/reader_test.go | 88 + src/pkg/strings/replace.go | 312 + src/pkg/strings/replace_test.go | 174 + src/pkg/strings/strings.go | 158 +- src/pkg/strings/strings_test.go | 313 +- src/pkg/sync/Makefile | 15 - src/pkg/sync/atomic/Makefile | 18 - src/pkg/sync/atomic/asm_386.s | 55 + src/pkg/sync/atomic/asm_amd64.s | 47 + src/pkg/sync/atomic/asm_arm.s | 24 + src/pkg/sync/atomic/asm_linux_arm.s | 39 + src/pkg/sync/atomic/atomic_test.go | 639 +- src/pkg/sync/atomic/doc.go | 39 +- src/pkg/sync/cond.go | 15 +- src/pkg/sync/example_test.go | 54 + src/pkg/sync/export_test.go | 9 + src/pkg/sync/mutex.go | 11 +- src/pkg/sync/mutex_test.go | 6 +- src/pkg/sync/once_test.go | 2 +- src/pkg/sync/runtime.go | 18 + src/pkg/sync/runtime_sema_test.go | 101 + src/pkg/sync/rwmutex.go | 13 +- src/pkg/sync/rwmutex_test.go | 2 +- src/pkg/sync/waitgroup.go | 24 +- src/pkg/syscall/Makefile | 58 - src/pkg/syscall/asm_linux_386.s | 14 +- src/pkg/syscall/asm_netbsd_386.s | 137 + src/pkg/syscall/asm_netbsd_amd64.s | 130 + src/pkg/syscall/asm_openbsd_386.s | 137 + src/pkg/syscall/asm_openbsd_amd64.s | 130 + src/pkg/syscall/bpf_bsd.go | 162 +- src/pkg/syscall/dll_windows.go | 249 + src/pkg/syscall/env_plan9.go | 128 + src/pkg/syscall/env_unix.go | 109 + src/pkg/syscall/env_windows.go | 77 + src/pkg/syscall/exec_bsd.go | 223 + src/pkg/syscall/exec_linux.go | 234 + src/pkg/syscall/exec_plan9.go | 37 +- src/pkg/syscall/exec_unix.go | 252 +- src/pkg/syscall/exec_windows.go | 50 +- src/pkg/syscall/lsf_linux.go | 26 +- src/pkg/syscall/mkall.sh | 98 +- src/pkg/syscall/mkerrors.sh | 177 +- src/pkg/syscall/mkerrors_windows.sh | 6 +- src/pkg/syscall/mksyscall.pl | 51 +- src/pkg/syscall/mksyscall_windows.pl | 58 +- src/pkg/syscall/mksysctl_openbsd.pl | 257 + src/pkg/syscall/mksysnum_linux.pl | 2 +- src/pkg/syscall/mksysnum_netbsd.pl | 51 + src/pkg/syscall/mksysnum_openbsd.pl | 43 + src/pkg/syscall/netlink_linux.go | 49 +- src/pkg/syscall/route_bsd.go | 99 +- src/pkg/syscall/route_darwin.go | 2 +- src/pkg/syscall/route_freebsd.go | 2 +- src/pkg/syscall/route_netbsd.go | 35 + src/pkg/syscall/route_openbsd.go | 35 + src/pkg/syscall/security_windows.go | 359 + src/pkg/syscall/sockcmsg_linux.go | 4 +- src/pkg/syscall/sockcmsg_unix.go | 16 +- src/pkg/syscall/syscall.go | 23 +- src/pkg/syscall/syscall_386.go | 7 - src/pkg/syscall/syscall_amd64.go | 7 - src/pkg/syscall/syscall_arm.go | 7 - src/pkg/syscall/syscall_bsd.go | 329 +- src/pkg/syscall/syscall_darwin.go | 191 +- src/pkg/syscall/syscall_darwin_386.go | 6 +- src/pkg/syscall/syscall_darwin_amd64.go | 4 +- src/pkg/syscall/syscall_freebsd.go | 194 +- src/pkg/syscall/syscall_freebsd_386.go | 2 +- src/pkg/syscall/syscall_linux.go | 469 +- src/pkg/syscall/syscall_linux_386.go | 189 +- src/pkg/syscall/syscall_linux_amd64.go | 92 +- src/pkg/syscall/syscall_linux_arm.go | 129 +- src/pkg/syscall/syscall_netbsd.go | 434 + src/pkg/syscall/syscall_netbsd_386.go | 42 + src/pkg/syscall/syscall_netbsd_amd64.go | 42 + src/pkg/syscall/syscall_openbsd.go | 281 + src/pkg/syscall/syscall_openbsd_386.go | 42 + src/pkg/syscall/syscall_openbsd_amd64.go | 42 + src/pkg/syscall/syscall_plan9.go | 106 +- src/pkg/syscall/syscall_unix.go | 81 +- src/pkg/syscall/syscall_windows.go | 563 +- src/pkg/syscall/types_darwin.c | 180 - src/pkg/syscall/types_darwin.go | 228 + src/pkg/syscall/types_freebsd.c | 190 - src/pkg/syscall/types_freebsd.go | 240 + src/pkg/syscall/types_linux.c | 266 - src/pkg/syscall/types_linux.go | 452 + src/pkg/syscall/types_netbsd.go | 229 + src/pkg/syscall/types_openbsd.go | 229 + src/pkg/syscall/types_plan9.c | 2 + src/pkg/syscall/zerrors_darwin_386.go | 2289 ++--- src/pkg/syscall/zerrors_darwin_amd64.go | 2289 ++--- src/pkg/syscall/zerrors_freebsd_386.go | 393 +- src/pkg/syscall/zerrors_freebsd_amd64.go | 393 +- src/pkg/syscall/zerrors_linux_386.go | 651 +- src/pkg/syscall/zerrors_linux_amd64.go | 653 +- src/pkg/syscall/zerrors_linux_arm.go | 639 +- src/pkg/syscall/zerrors_netbsd_386.go | 1368 +++ src/pkg/syscall/zerrors_netbsd_amd64.go | 1368 +++ src/pkg/syscall/zerrors_openbsd_386.go | 1441 ++++ src/pkg/syscall/zerrors_openbsd_amd64.go | 1441 ++++ src/pkg/syscall/zerrors_plan9_386.go | 21 +- src/pkg/syscall/zerrors_windows.go | 8 +- src/pkg/syscall/zsyscall_darwin_386.go | 594 +- src/pkg/syscall/zsyscall_darwin_amd64.go | 594 +- src/pkg/syscall/zsyscall_freebsd_386.go | 582 +- src/pkg/syscall/zsyscall_freebsd_amd64.go | 582 +- src/pkg/syscall/zsyscall_linux_386.go | 710 +- src/pkg/syscall/zsyscall_linux_amd64.go | 806 +- src/pkg/syscall/zsyscall_linux_arm.go | 830 +- src/pkg/syscall/zsyscall_netbsd_386.go | 1086 +++ src/pkg/syscall/zsyscall_netbsd_amd64.go | 1086 +++ src/pkg/syscall/zsyscall_openbsd_386.go | 1133 +++ src/pkg/syscall/zsyscall_openbsd_amd64.go | 1133 +++ src/pkg/syscall/zsyscall_plan9_386.go | 96 +- src/pkg/syscall/zsyscall_windows_386.go | 1275 +-- src/pkg/syscall/zsyscall_windows_amd64.go | 1275 +-- src/pkg/syscall/zsysctl_openbsd.go | 292 + src/pkg/syscall/zsysnum_darwin_386.go | 701 +- src/pkg/syscall/zsysnum_darwin_amd64.go | 701 +- src/pkg/syscall/zsysnum_freebsd_386.go | 27 +- src/pkg/syscall/zsysnum_freebsd_amd64.go | 27 +- src/pkg/syscall/zsysnum_linux_386.go | 4 + src/pkg/syscall/zsysnum_linux_amd64.go | 4 + src/pkg/syscall/zsysnum_linux_arm.go | 680 +- src/pkg/syscall/zsysnum_netbsd_386.go | 274 + src/pkg/syscall/zsysnum_netbsd_amd64.go | 274 + src/pkg/syscall/zsysnum_openbsd_386.go | 207 + src/pkg/syscall/zsysnum_openbsd_amd64.go | 207 + src/pkg/syscall/ztypes_darwin_386.go | 216 +- src/pkg/syscall/ztypes_darwin_amd64.go | 252 +- src/pkg/syscall/ztypes_freebsd_386.go | 231 +- src/pkg/syscall/ztypes_freebsd_amd64.go | 268 +- src/pkg/syscall/ztypes_linux_386.go | 476 +- src/pkg/syscall/ztypes_linux_amd64.go | 508 +- src/pkg/syscall/ztypes_linux_arm.go | 507 +- src/pkg/syscall/ztypes_netbsd_386.go | 406 + src/pkg/syscall/ztypes_netbsd_amd64.go | 412 + src/pkg/syscall/ztypes_openbsd_386.go | 405 + src/pkg/syscall/ztypes_openbsd_amd64.go | 412 + src/pkg/syscall/ztypes_windows.go | 364 +- src/pkg/syslog/Makefile | 12 - src/pkg/syslog/syslog.go | 149 - src/pkg/syslog/syslog_test.go | 113 - src/pkg/syslog/syslog_unix.go | 31 - src/pkg/tabwriter/Makefile | 11 - src/pkg/tabwriter/tabwriter.go | 561 -- src/pkg/tabwriter/tabwriter_test.go | 615 -- src/pkg/template/Makefile | 15 - src/pkg/template/doc.go | 313 - src/pkg/template/exec.go | 674 -- src/pkg/template/exec_test.go | 613 -- src/pkg/template/funcs.go | 368 - src/pkg/template/helper.go | 238 - src/pkg/template/parse.go | 85 - src/pkg/template/parse/Makefile | 14 - src/pkg/template/parse/lex.go | 472 -- src/pkg/template/parse/lex_test.go | 223 - src/pkg/template/parse/node.go | 470 -- src/pkg/template/parse/parse.go | 436 - src/pkg/template/parse/parse_test.go | 259 - src/pkg/template/parse/set.go | 50 - src/pkg/template/set.go | 108 - src/pkg/template/set_test.go | 239 - src/pkg/template/testdata/file1.tmpl | 2 - src/pkg/template/testdata/file2.tmpl | 2 - src/pkg/template/testdata/tmpl1.tmpl | 1 - src/pkg/template/testdata/tmpl2.tmpl | 1 - src/pkg/testing/Makefile | 12 - src/pkg/testing/benchmark.go | 127 +- src/pkg/testing/example.go | 82 + src/pkg/testing/iotest/Makefile | 13 - src/pkg/testing/iotest/logger.go | 5 +- src/pkg/testing/iotest/reader.go | 14 +- src/pkg/testing/iotest/writer.go | 7 +- src/pkg/testing/quick/Makefile | 11 - src/pkg/testing/quick/quick.go | 28 +- src/pkg/testing/quick/quick_test.go | 5 +- src/pkg/testing/script/Makefile | 11 - src/pkg/testing/script/script.go | 359 - src/pkg/testing/script/script_test.go | 75 - src/pkg/testing/testing.go | 324 +- src/pkg/text/scanner/scanner.go | 669 ++ src/pkg/text/scanner/scanner_test.go | 563 ++ src/pkg/text/tabwriter/example_test.go | 38 + src/pkg/text/tabwriter/tabwriter.go | 554 ++ src/pkg/text/tabwriter/tabwriter_test.go | 615 ++ src/pkg/text/template/doc.go | 358 + src/pkg/text/template/example_test.go | 71 + src/pkg/text/template/examplefiles_test.go | 182 + src/pkg/text/template/examplefunc_test.go | 54 + src/pkg/text/template/exec.go | 722 ++ src/pkg/text/template/exec_test.go | 722 ++ src/pkg/text/template/funcs.go | 410 + src/pkg/text/template/helper.go | 108 + src/pkg/text/template/multi_test.go | 280 + src/pkg/text/template/parse/lex.go | 507 ++ src/pkg/text/template/parse/lex_test.go | 257 + src/pkg/text/template/parse/node.go | 608 ++ src/pkg/text/template/parse/parse.go | 530 ++ src/pkg/text/template/parse/parse_test.go | 318 + src/pkg/text/template/template.go | 214 + src/pkg/text/template/testdata/file1.tmpl | 2 + src/pkg/text/template/testdata/file2.tmpl | 2 + src/pkg/text/template/testdata/tmpl1.tmpl | 3 + src/pkg/text/template/testdata/tmpl2.tmpl | 3 + src/pkg/time/Makefile | 46 - src/pkg/time/example_test.go | 58 + src/pkg/time/format.go | 712 +- src/pkg/time/internal_test.go | 13 + src/pkg/time/sleep.go | 212 +- src/pkg/time/sleep_test.go | 130 +- src/pkg/time/sys.go | 51 - src/pkg/time/sys_plan9.go | 66 +- src/pkg/time/sys_posix.go | 18 - src/pkg/time/sys_unix.go | 76 + src/pkg/time/sys_windows.go | 73 + src/pkg/time/tick.go | 188 +- src/pkg/time/tick_test.go | 24 +- src/pkg/time/time.go | 1069 ++- src/pkg/time/time_test.go | 695 +- src/pkg/time/zoneinfo.go | 204 + src/pkg/time/zoneinfo_plan9.go | 145 +- src/pkg/time/zoneinfo_posix.go | 62 - src/pkg/time/zoneinfo_read.go | 341 + src/pkg/time/zoneinfo_unix.go | 227 +- src/pkg/time/zoneinfo_windows.go | 275 +- src/pkg/try/Makefile | 11 - src/pkg/try/try.go | 174 - src/pkg/try/try_test.go | 60 - src/pkg/unicode/Makefile | 24 +- src/pkg/unicode/casetables.go | 4 +- src/pkg/unicode/digit.go | 8 +- src/pkg/unicode/digit_test.go | 6 +- src/pkg/unicode/graphic.go | 77 +- src/pkg/unicode/graphic_test.go | 20 +- src/pkg/unicode/letter.go | 170 +- src/pkg/unicode/letter_test.go | 29 +- src/pkg/unicode/maketables.go | 197 +- src/pkg/unicode/script_test.go | 8 +- src/pkg/unicode/tables.go | 6981 +++++++-------- src/pkg/unicode/utf16/export_test.go | 11 + src/pkg/unicode/utf16/utf16.go | 108 + src/pkg/unicode/utf16/utf16_test.go | 101 + src/pkg/unicode/utf8/utf8.go | 397 + src/pkg/unicode/utf8/utf8_test.go | 365 + src/pkg/unsafe/unsafe.go | 34 +- src/pkg/url/Makefile | 11 - src/pkg/url/url.go | 677 -- src/pkg/url/url_test.go | 698 -- src/pkg/utf16/Makefile | 11 - src/pkg/utf16/utf16.go | 101 - src/pkg/utf16/utf16_test.go | 118 - src/pkg/utf8/Makefile | 12 - src/pkg/utf8/string.go | 211 - src/pkg/utf8/string_test.go | 114 - src/pkg/utf8/utf8.go | 356 - src/pkg/utf8/utf8_test.go | 315 - src/pkg/websocket/Makefile | 9 - src/pkg/websocket/client.go | 323 - src/pkg/websocket/server.go | 219 - src/pkg/websocket/websocket.go | 192 - src/pkg/websocket/websocket_test.go | 271 - src/pkg/xml/Makefile | 14 - src/pkg/xml/atom_test.go | 50 - src/pkg/xml/embed_test.go | 124 - src/pkg/xml/marshal.go | 228 - src/pkg/xml/marshal_test.go | 305 - src/pkg/xml/read.go | 631 -- src/pkg/xml/read_test.go | 371 - src/pkg/xml/xml.go | 1694 ---- src/pkg/xml/xml_test.go | 609 -- 1986 files changed, 197643 insertions(+), 168156 deletions(-) delete mode 100644 src/pkg/Makefile delete mode 100644 src/pkg/archive/tar/Makefile delete mode 100644 src/pkg/archive/zip/Makefile create mode 100644 src/pkg/archive/zip/example_test.go create mode 100644 src/pkg/archive/zip/testdata/crc32-not-streamed.zip create mode 100644 src/pkg/archive/zip/testdata/go-no-datadesc-sig.zip create mode 100644 src/pkg/archive/zip/testdata/go-with-datadesc-sig.zip delete mode 100644 src/pkg/archive/zip/testdata/r.zip create mode 100644 src/pkg/archive/zip/testdata/symlink.zip create mode 100644 src/pkg/archive/zip/testdata/unix.zip create mode 100644 src/pkg/archive/zip/testdata/winxp.zip delete mode 100644 src/pkg/asn1/Makefile delete mode 100644 src/pkg/asn1/asn1.go delete mode 100644 src/pkg/asn1/asn1_test.go delete mode 100644 src/pkg/asn1/common.go delete mode 100644 src/pkg/asn1/marshal.go delete mode 100644 src/pkg/asn1/marshal_test.go delete mode 100644 src/pkg/big/Makefile delete mode 100644 src/pkg/big/arith.go delete mode 100644 src/pkg/big/arith_386.s delete mode 100644 src/pkg/big/arith_amd64.s delete mode 100644 src/pkg/big/arith_arm.s delete mode 100644 src/pkg/big/arith_decl.go delete mode 100644 src/pkg/big/arith_test.go delete mode 100644 src/pkg/big/calibrate_test.go delete mode 100644 src/pkg/big/hilbert_test.go delete mode 100755 src/pkg/big/int.go delete mode 100755 src/pkg/big/int_test.go delete mode 100755 src/pkg/big/nat.go delete mode 100755 src/pkg/big/nat_test.go delete mode 100644 src/pkg/big/rat.go delete mode 100644 src/pkg/big/rat_test.go delete mode 100644 src/pkg/bufio/Makefile delete mode 100644 src/pkg/bytes/Makefile create mode 100644 src/pkg/bytes/example_test.go create mode 100644 src/pkg/bytes/reader.go create mode 100644 src/pkg/bytes/reader_test.go delete mode 100644 src/pkg/cmath/Makefile delete mode 100644 src/pkg/cmath/abs.go delete mode 100644 src/pkg/cmath/asin.go delete mode 100644 src/pkg/cmath/cmath_test.go delete mode 100644 src/pkg/cmath/conj.go delete mode 100644 src/pkg/cmath/exp.go delete mode 100644 src/pkg/cmath/isinf.go delete mode 100644 src/pkg/cmath/isnan.go delete mode 100644 src/pkg/cmath/log.go delete mode 100644 src/pkg/cmath/phase.go delete mode 100644 src/pkg/cmath/polar.go delete mode 100644 src/pkg/cmath/pow.go delete mode 100644 src/pkg/cmath/rect.go delete mode 100644 src/pkg/cmath/sin.go delete mode 100644 src/pkg/cmath/sqrt.go delete mode 100644 src/pkg/cmath/tan.go delete mode 100644 src/pkg/compress/bzip2/Makefile delete mode 100644 src/pkg/compress/flate/Makefile delete mode 100644 src/pkg/compress/flate/util.go delete mode 100644 src/pkg/compress/gzip/Makefile delete mode 100644 src/pkg/compress/lzw/Makefile create mode 100644 src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt delete mode 100644 src/pkg/compress/zlib/Makefile delete mode 100644 src/pkg/container/heap/Makefile create mode 100644 src/pkg/container/heap/example_test.go delete mode 100644 src/pkg/container/list/Makefile delete mode 100644 src/pkg/container/ring/Makefile delete mode 100644 src/pkg/container/vector/Makefile delete mode 100644 src/pkg/container/vector/defs.go delete mode 100644 src/pkg/container/vector/intvector.go delete mode 100644 src/pkg/container/vector/intvector_test.go delete mode 100644 src/pkg/container/vector/nogen_test.go delete mode 100644 src/pkg/container/vector/numbers_test.go delete mode 100644 src/pkg/container/vector/stringvector.go delete mode 100644 src/pkg/container/vector/stringvector_test.go delete mode 100644 src/pkg/container/vector/vector.go delete mode 100644 src/pkg/container/vector/vector_test.go delete mode 100644 src/pkg/crypto/Makefile delete mode 100644 src/pkg/crypto/aes/Makefile delete mode 100644 src/pkg/crypto/blowfish/Makefile delete mode 100644 src/pkg/crypto/blowfish/block.go delete mode 100644 src/pkg/crypto/blowfish/blowfish_test.go delete mode 100644 src/pkg/crypto/blowfish/cipher.go delete mode 100644 src/pkg/crypto/blowfish/const.go delete mode 100644 src/pkg/crypto/cast5/Makefile delete mode 100644 src/pkg/crypto/cast5/cast5.go delete mode 100644 src/pkg/crypto/cast5/cast5_test.go delete mode 100644 src/pkg/crypto/cipher/Makefile delete mode 100644 src/pkg/crypto/cipher/ocfb.go delete mode 100644 src/pkg/crypto/cipher/ocfb_test.go delete mode 100644 src/pkg/crypto/des/Makefile delete mode 100644 src/pkg/crypto/dsa/Makefile delete mode 100644 src/pkg/crypto/ecdsa/Makefile delete mode 100644 src/pkg/crypto/elliptic/Makefile create mode 100644 src/pkg/crypto/elliptic/p224.go create mode 100644 src/pkg/crypto/elliptic/p224_test.go delete mode 100644 src/pkg/crypto/hmac/Makefile delete mode 100644 src/pkg/crypto/md4/Makefile delete mode 100644 src/pkg/crypto/md4/md4.go delete mode 100644 src/pkg/crypto/md4/md4_test.go delete mode 100644 src/pkg/crypto/md4/md4block.go delete mode 100644 src/pkg/crypto/md5/Makefile delete mode 100644 src/pkg/crypto/ocsp/Makefile delete mode 100644 src/pkg/crypto/ocsp/ocsp.go delete mode 100644 src/pkg/crypto/ocsp/ocsp_test.go delete mode 100644 src/pkg/crypto/openpgp/Makefile delete mode 100644 src/pkg/crypto/openpgp/armor/Makefile delete mode 100644 src/pkg/crypto/openpgp/armor/armor.go delete mode 100644 src/pkg/crypto/openpgp/armor/armor_test.go delete mode 100644 src/pkg/crypto/openpgp/armor/encode.go delete mode 100644 src/pkg/crypto/openpgp/canonical_text.go delete mode 100644 src/pkg/crypto/openpgp/canonical_text_test.go delete mode 100644 src/pkg/crypto/openpgp/elgamal/Makefile delete mode 100644 src/pkg/crypto/openpgp/elgamal/elgamal.go delete mode 100644 src/pkg/crypto/openpgp/elgamal/elgamal_test.go delete mode 100644 src/pkg/crypto/openpgp/error/Makefile delete mode 100644 src/pkg/crypto/openpgp/error/error.go delete mode 100644 src/pkg/crypto/openpgp/keys.go delete mode 100644 src/pkg/crypto/openpgp/packet/Makefile delete mode 100644 src/pkg/crypto/openpgp/packet/compressed.go delete mode 100644 src/pkg/crypto/openpgp/packet/compressed_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/encrypted_key.go delete mode 100644 src/pkg/crypto/openpgp/packet/encrypted_key_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/literal.go delete mode 100644 src/pkg/crypto/openpgp/packet/one_pass_signature.go delete mode 100644 src/pkg/crypto/openpgp/packet/packet.go delete mode 100644 src/pkg/crypto/openpgp/packet/packet_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/private_key.go delete mode 100644 src/pkg/crypto/openpgp/packet/private_key_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/public_key.go delete mode 100644 src/pkg/crypto/openpgp/packet/public_key_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/reader.go delete mode 100644 src/pkg/crypto/openpgp/packet/signature.go delete mode 100644 src/pkg/crypto/openpgp/packet/signature_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go delete mode 100644 src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go delete mode 100644 src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go delete mode 100644 src/pkg/crypto/openpgp/packet/userid.go delete mode 100644 src/pkg/crypto/openpgp/packet/userid_test.go delete mode 100644 src/pkg/crypto/openpgp/read.go delete mode 100644 src/pkg/crypto/openpgp/read_test.go delete mode 100644 src/pkg/crypto/openpgp/s2k/Makefile delete mode 100644 src/pkg/crypto/openpgp/s2k/s2k.go delete mode 100644 src/pkg/crypto/openpgp/s2k/s2k_test.go delete mode 100644 src/pkg/crypto/openpgp/write.go delete mode 100644 src/pkg/crypto/openpgp/write_test.go delete mode 100644 src/pkg/crypto/rand/Makefile mode change 100755 => 100644 src/pkg/crypto/rand/rand_windows.go delete mode 100644 src/pkg/crypto/rc4/Makefile delete mode 100644 src/pkg/crypto/ripemd160/Makefile delete mode 100644 src/pkg/crypto/ripemd160/ripemd160.go delete mode 100644 src/pkg/crypto/ripemd160/ripemd160_test.go delete mode 100644 src/pkg/crypto/ripemd160/ripemd160block.go delete mode 100644 src/pkg/crypto/rsa/Makefile delete mode 100644 src/pkg/crypto/sha1/Makefile delete mode 100644 src/pkg/crypto/sha256/Makefile delete mode 100644 src/pkg/crypto/sha512/Makefile delete mode 100644 src/pkg/crypto/subtle/Makefile delete mode 100644 src/pkg/crypto/tls/Makefile create mode 100644 src/pkg/crypto/tls/root_test.go delete mode 100644 src/pkg/crypto/twofish/Makefile delete mode 100644 src/pkg/crypto/twofish/twofish.go delete mode 100644 src/pkg/crypto/twofish/twofish_test.go delete mode 100644 src/pkg/crypto/x509/Makefile create mode 100644 src/pkg/crypto/x509/pkcs1.go create mode 100644 src/pkg/crypto/x509/pkcs8.go create mode 100644 src/pkg/crypto/x509/pkcs8_test.go delete mode 100644 src/pkg/crypto/x509/pkix/Makefile create mode 100644 src/pkg/crypto/x509/root.go create mode 100644 src/pkg/crypto/x509/root_darwin.go create mode 100644 src/pkg/crypto/x509/root_stub.go create mode 100644 src/pkg/crypto/x509/root_unix.go create mode 100644 src/pkg/crypto/x509/root_windows.go delete mode 100644 src/pkg/crypto/xtea/Makefile delete mode 100644 src/pkg/crypto/xtea/block.go delete mode 100644 src/pkg/crypto/xtea/cipher.go delete mode 100644 src/pkg/crypto/xtea/xtea_test.go delete mode 100644 src/pkg/csv/Makefile delete mode 100644 src/pkg/csv/reader.go delete mode 100644 src/pkg/csv/reader_test.go delete mode 100644 src/pkg/csv/writer.go delete mode 100644 src/pkg/csv/writer_test.go create mode 100644 src/pkg/database/sql/convert.go create mode 100644 src/pkg/database/sql/convert_test.go create mode 100644 src/pkg/database/sql/doc.txt create mode 100644 src/pkg/database/sql/driver/driver.go create mode 100644 src/pkg/database/sql/driver/types.go create mode 100644 src/pkg/database/sql/driver/types_test.go create mode 100644 src/pkg/database/sql/fakedb_test.go create mode 100644 src/pkg/database/sql/sql.go create mode 100644 src/pkg/database/sql/sql_test.go delete mode 100644 src/pkg/debug/dwarf/Makefile delete mode 100644 src/pkg/debug/elf/Makefile delete mode 100644 src/pkg/debug/gosym/Makefile create mode 100644 src/pkg/debug/gosym/pclinetest.asm delete mode 100644 src/pkg/debug/gosym/pclinetest.s delete mode 100644 src/pkg/debug/macho/Makefile delete mode 100644 src/pkg/debug/pe/Makefile delete mode 100755 src/pkg/deps.bash delete mode 100644 src/pkg/ebnf/Makefile delete mode 100644 src/pkg/ebnf/ebnf.go delete mode 100644 src/pkg/ebnf/ebnf_test.go delete mode 100644 src/pkg/ebnf/parser.go delete mode 100644 src/pkg/encoding/ascii85/Makefile create mode 100644 src/pkg/encoding/asn1/asn1.go create mode 100644 src/pkg/encoding/asn1/asn1_test.go create mode 100644 src/pkg/encoding/asn1/common.go create mode 100644 src/pkg/encoding/asn1/marshal.go create mode 100644 src/pkg/encoding/asn1/marshal_test.go delete mode 100644 src/pkg/encoding/base32/Makefile delete mode 100644 src/pkg/encoding/base64/Makefile delete mode 100644 src/pkg/encoding/binary/Makefile create mode 100644 src/pkg/encoding/binary/example_test.go create mode 100644 src/pkg/encoding/binary/varint.go create mode 100644 src/pkg/encoding/binary/varint_test.go create mode 100644 src/pkg/encoding/csv/reader.go create mode 100644 src/pkg/encoding/csv/reader_test.go create mode 100644 src/pkg/encoding/csv/writer.go create mode 100644 src/pkg/encoding/csv/writer_test.go delete mode 100644 src/pkg/encoding/git85/Makefile delete mode 100644 src/pkg/encoding/git85/git.go delete mode 100644 src/pkg/encoding/git85/git_test.go create mode 100644 src/pkg/encoding/gob/codec_test.go create mode 100644 src/pkg/encoding/gob/debug.go create mode 100644 src/pkg/encoding/gob/decode.go create mode 100644 src/pkg/encoding/gob/decoder.go create mode 100644 src/pkg/encoding/gob/doc.go create mode 100644 src/pkg/encoding/gob/dump.go create mode 100644 src/pkg/encoding/gob/encode.go create mode 100644 src/pkg/encoding/gob/encoder.go create mode 100644 src/pkg/encoding/gob/encoder_test.go create mode 100644 src/pkg/encoding/gob/error.go create mode 100644 src/pkg/encoding/gob/gobencdec_test.go create mode 100644 src/pkg/encoding/gob/timing_test.go create mode 100644 src/pkg/encoding/gob/type.go create mode 100644 src/pkg/encoding/gob/type_test.go delete mode 100644 src/pkg/encoding/hex/Makefile create mode 100644 src/pkg/encoding/json/bench_test.go create mode 100644 src/pkg/encoding/json/decode.go create mode 100644 src/pkg/encoding/json/decode_test.go create mode 100644 src/pkg/encoding/json/encode.go create mode 100644 src/pkg/encoding/json/encode_test.go create mode 100644 src/pkg/encoding/json/example_test.go create mode 100644 src/pkg/encoding/json/indent.go create mode 100644 src/pkg/encoding/json/scanner.go create mode 100644 src/pkg/encoding/json/scanner_test.go create mode 100644 src/pkg/encoding/json/stream.go create mode 100644 src/pkg/encoding/json/stream_test.go create mode 100644 src/pkg/encoding/json/tagkey_test.go create mode 100644 src/pkg/encoding/json/tags.go create mode 100644 src/pkg/encoding/json/tags_test.go create mode 100644 src/pkg/encoding/json/testdata/code.json.gz delete mode 100644 src/pkg/encoding/pem/Makefile create mode 100644 src/pkg/encoding/xml/atom_test.go create mode 100644 src/pkg/encoding/xml/example_test.go create mode 100644 src/pkg/encoding/xml/marshal.go create mode 100644 src/pkg/encoding/xml/marshal_test.go create mode 100644 src/pkg/encoding/xml/read.go create mode 100644 src/pkg/encoding/xml/read_test.go create mode 100644 src/pkg/encoding/xml/typeinfo.go create mode 100644 src/pkg/encoding/xml/xml.go create mode 100644 src/pkg/encoding/xml/xml_test.go create mode 100644 src/pkg/errors/errors.go create mode 100644 src/pkg/errors/errors_test.go create mode 100644 src/pkg/errors/example_test.go delete mode 100644 src/pkg/exec/Makefile delete mode 100644 src/pkg/exec/exec.go delete mode 100644 src/pkg/exec/exec_test.go delete mode 100644 src/pkg/exec/lp_plan9.go delete mode 100644 src/pkg/exec/lp_test.go delete mode 100644 src/pkg/exec/lp_unix.go delete mode 100644 src/pkg/exec/lp_windows.go delete mode 100644 src/pkg/exp/README delete mode 100644 src/pkg/exp/gui/Makefile delete mode 100644 src/pkg/exp/gui/gui.go delete mode 100644 src/pkg/exp/gui/x11/Makefile delete mode 100644 src/pkg/exp/gui/x11/auth.go delete mode 100644 src/pkg/exp/gui/x11/conn.go delete mode 100644 src/pkg/exp/norm/Makefile delete mode 100644 src/pkg/exp/norm/composition.go delete mode 100644 src/pkg/exp/norm/composition_test.go delete mode 100644 src/pkg/exp/norm/forminfo.go delete mode 100644 src/pkg/exp/norm/maketables.go delete mode 100644 src/pkg/exp/norm/maketesttables.go delete mode 100644 src/pkg/exp/norm/norm_test.go delete mode 100644 src/pkg/exp/norm/normalize.go delete mode 100644 src/pkg/exp/norm/tables.go delete mode 100644 src/pkg/exp/norm/trie.go delete mode 100644 src/pkg/exp/norm/trie_test.go delete mode 100644 src/pkg/exp/norm/triedata_test.go delete mode 100644 src/pkg/exp/norm/triegen.go delete mode 100644 src/pkg/exp/regexp/Makefile delete mode 100644 src/pkg/exp/regexp/all_test.go delete mode 100644 src/pkg/exp/regexp/exec.go delete mode 100644 src/pkg/exp/regexp/find_test.go delete mode 100644 src/pkg/exp/regexp/regexp.go delete mode 100644 src/pkg/exp/regexp/syntax/Makefile delete mode 100644 src/pkg/exp/regexp/syntax/compile.go delete mode 100755 src/pkg/exp/regexp/syntax/make_perl_groups.pl delete mode 100644 src/pkg/exp/regexp/syntax/parse.go delete mode 100644 src/pkg/exp/regexp/syntax/parse_test.go delete mode 100644 src/pkg/exp/regexp/syntax/perl_groups.go delete mode 100644 src/pkg/exp/regexp/syntax/prog.go delete mode 100644 src/pkg/exp/regexp/syntax/prog_test.go delete mode 100644 src/pkg/exp/regexp/syntax/regexp.go delete mode 100644 src/pkg/exp/regexp/syntax/simplify.go delete mode 100644 src/pkg/exp/regexp/syntax/simplify_test.go delete mode 100644 src/pkg/exp/template/html/Makefile delete mode 100644 src/pkg/exp/template/html/context.go delete mode 100644 src/pkg/exp/template/html/escape.go delete mode 100644 src/pkg/exp/template/html/escape_test.go delete mode 100644 src/pkg/exp/wingui/Makefile delete mode 100644 src/pkg/exp/wingui/gui.go delete mode 100644 src/pkg/exp/wingui/winapi.go delete mode 100644 src/pkg/exp/wingui/zwinapi.go delete mode 100644 src/pkg/expvar/Makefile delete mode 100644 src/pkg/flag/Makefile create mode 100644 src/pkg/flag/example_test.go delete mode 100644 src/pkg/fmt/Makefile create mode 100644 src/pkg/fmt/export_test.go delete mode 100644 src/pkg/go/ast/Makefile create mode 100644 src/pkg/go/ast/example_test.go create mode 100644 src/pkg/go/ast/import.go delete mode 100644 src/pkg/go/build/Makefile delete mode 100644 src/pkg/go/build/cgotest/cgotest.c delete mode 100644 src/pkg/go/build/cgotest/cgotest.go delete mode 100644 src/pkg/go/build/cgotest/cgotest.h delete mode 100644 src/pkg/go/build/cmdtest/main.go create mode 100644 src/pkg/go/build/deps_test.go delete mode 100644 src/pkg/go/build/dir.go create mode 100644 src/pkg/go/build/doc.go delete mode 100644 src/pkg/go/build/path.go delete mode 100644 src/pkg/go/build/pkgtest/pkgtest.go delete mode 100644 src/pkg/go/build/pkgtest/sqrt_386.s delete mode 100644 src/pkg/go/build/pkgtest/sqrt_amd64.s delete mode 100644 src/pkg/go/build/pkgtest/sqrt_arm.s create mode 100644 src/pkg/go/build/syslist.go create mode 100644 src/pkg/go/build/testdata/other/file/file.go create mode 100644 src/pkg/go/build/testdata/other/main.go create mode 100644 src/pkg/go/doc/comment_test.go create mode 100644 src/pkg/go/doc/doc_test.go create mode 100644 src/pkg/go/doc/example.go create mode 100644 src/pkg/go/doc/exports.go create mode 100644 src/pkg/go/doc/filter.go create mode 100644 src/pkg/go/doc/headscan.go create mode 100644 src/pkg/go/doc/reader.go create mode 100644 src/pkg/go/doc/synopsis.go create mode 100644 src/pkg/go/doc/synopsis_test.go create mode 100644 src/pkg/go/doc/testdata/a.0.golden create mode 100644 src/pkg/go/doc/testdata/a.1.golden create mode 100644 src/pkg/go/doc/testdata/a.2.golden create mode 100644 src/pkg/go/doc/testdata/a0.go create mode 100644 src/pkg/go/doc/testdata/a1.go create mode 100644 src/pkg/go/doc/testdata/b.0.golden create mode 100644 src/pkg/go/doc/testdata/b.1.golden create mode 100644 src/pkg/go/doc/testdata/b.2.golden create mode 100644 src/pkg/go/doc/testdata/b.go create mode 100644 src/pkg/go/doc/testdata/benchmark.go create mode 100644 src/pkg/go/doc/testdata/c.0.golden create mode 100644 src/pkg/go/doc/testdata/c.1.golden create mode 100644 src/pkg/go/doc/testdata/c.2.golden create mode 100644 src/pkg/go/doc/testdata/c.go create mode 100644 src/pkg/go/doc/testdata/d.0.golden create mode 100644 src/pkg/go/doc/testdata/d.1.golden create mode 100644 src/pkg/go/doc/testdata/d.2.golden create mode 100644 src/pkg/go/doc/testdata/d1.go create mode 100644 src/pkg/go/doc/testdata/d2.go create mode 100644 src/pkg/go/doc/testdata/e.0.golden create mode 100644 src/pkg/go/doc/testdata/e.1.golden create mode 100644 src/pkg/go/doc/testdata/e.2.golden create mode 100644 src/pkg/go/doc/testdata/e.go create mode 100644 src/pkg/go/doc/testdata/error1.0.golden create mode 100644 src/pkg/go/doc/testdata/error1.1.golden create mode 100644 src/pkg/go/doc/testdata/error1.2.golden create mode 100644 src/pkg/go/doc/testdata/error1.go create mode 100644 src/pkg/go/doc/testdata/error2.0.golden create mode 100644 src/pkg/go/doc/testdata/error2.1.golden create mode 100644 src/pkg/go/doc/testdata/error2.2.golden create mode 100644 src/pkg/go/doc/testdata/error2.go create mode 100644 src/pkg/go/doc/testdata/example.go create mode 100644 src/pkg/go/doc/testdata/f.0.golden create mode 100644 src/pkg/go/doc/testdata/f.1.golden create mode 100644 src/pkg/go/doc/testdata/f.2.golden create mode 100644 src/pkg/go/doc/testdata/f.go create mode 100644 src/pkg/go/doc/testdata/template.txt create mode 100644 src/pkg/go/doc/testdata/testing.0.golden create mode 100644 src/pkg/go/doc/testdata/testing.1.golden create mode 100644 src/pkg/go/doc/testdata/testing.2.golden create mode 100644 src/pkg/go/doc/testdata/testing.go delete mode 100644 src/pkg/go/parser/Makefile create mode 100644 src/pkg/go/parser/error_test.go create mode 100644 src/pkg/go/parser/example_test.go create mode 100644 src/pkg/go/parser/short_test.go create mode 100644 src/pkg/go/parser/testdata/commas.src create mode 100644 src/pkg/go/parser/testdata/issue3106.src delete mode 100644 src/pkg/go/printer/Makefile create mode 100644 src/pkg/go/printer/example_test.go delete mode 100644 src/pkg/go/scanner/Makefile create mode 100644 src/pkg/go/scanner/example_test.go delete mode 100644 src/pkg/go/token/Makefile create mode 100644 src/pkg/go/token/serialize.go create mode 100644 src/pkg/go/token/serialize_test.go delete mode 100644 src/pkg/go/typechecker/Makefile delete mode 100644 src/pkg/go/typechecker/scope.go delete mode 100644 src/pkg/go/typechecker/testdata/test0.src delete mode 100644 src/pkg/go/typechecker/testdata/test1.src delete mode 100644 src/pkg/go/typechecker/testdata/test3.src delete mode 100644 src/pkg/go/typechecker/testdata/test4.src delete mode 100644 src/pkg/go/typechecker/type.go delete mode 100644 src/pkg/go/typechecker/typechecker.go delete mode 100644 src/pkg/go/typechecker/typechecker_test.go delete mode 100644 src/pkg/go/typechecker/universe.go delete mode 100644 src/pkg/go/types/Makefile delete mode 100644 src/pkg/go/types/check.go delete mode 100644 src/pkg/go/types/check_test.go delete mode 100644 src/pkg/go/types/const.go delete mode 100644 src/pkg/go/types/exportdata.go delete mode 100644 src/pkg/go/types/gcimporter.go delete mode 100644 src/pkg/go/types/gcimporter_test.go delete mode 100644 src/pkg/go/types/testdata/exports.go delete mode 100644 src/pkg/go/types/testdata/test0.src delete mode 100644 src/pkg/go/types/types.go delete mode 100644 src/pkg/go/types/universe.go delete mode 100644 src/pkg/gob/Makefile delete mode 100644 src/pkg/gob/codec_test.go delete mode 100644 src/pkg/gob/debug.go delete mode 100644 src/pkg/gob/decode.go delete mode 100644 src/pkg/gob/decoder.go delete mode 100644 src/pkg/gob/doc.go delete mode 100644 src/pkg/gob/dump.go delete mode 100644 src/pkg/gob/encode.go delete mode 100644 src/pkg/gob/encoder.go delete mode 100644 src/pkg/gob/encoder_test.go delete mode 100644 src/pkg/gob/error.go delete mode 100644 src/pkg/gob/gobencdec_test.go delete mode 100644 src/pkg/gob/timing_test.go delete mode 100644 src/pkg/gob/type.go delete mode 100644 src/pkg/gob/type_test.go delete mode 100644 src/pkg/hash/Makefile delete mode 100644 src/pkg/hash/adler32/Makefile delete mode 100644 src/pkg/hash/crc32/Makefile delete mode 100644 src/pkg/hash/crc64/Makefile delete mode 100644 src/pkg/hash/fnv/Makefile delete mode 100644 src/pkg/html/Makefile delete mode 100644 src/pkg/html/const.go delete mode 100644 src/pkg/html/doc.go delete mode 100644 src/pkg/html/node.go delete mode 100644 src/pkg/html/parse.go delete mode 100644 src/pkg/html/parse_test.go create mode 100644 src/pkg/html/template/attr.go create mode 100644 src/pkg/html/template/clone_test.go create mode 100644 src/pkg/html/template/content.go create mode 100644 src/pkg/html/template/content_test.go create mode 100644 src/pkg/html/template/context.go create mode 100644 src/pkg/html/template/css.go create mode 100644 src/pkg/html/template/css_test.go create mode 100644 src/pkg/html/template/doc.go create mode 100644 src/pkg/html/template/error.go create mode 100644 src/pkg/html/template/escape.go create mode 100644 src/pkg/html/template/escape_test.go create mode 100644 src/pkg/html/template/html.go create mode 100644 src/pkg/html/template/html_test.go create mode 100644 src/pkg/html/template/js.go create mode 100644 src/pkg/html/template/js_test.go create mode 100644 src/pkg/html/template/template.go create mode 100644 src/pkg/html/template/transition.go create mode 100644 src/pkg/html/template/url.go create mode 100644 src/pkg/html/template/url_test.go delete mode 100644 src/pkg/html/testdata/webkit/README delete mode 100644 src/pkg/html/testdata/webkit/adoption01.dat delete mode 100644 src/pkg/html/testdata/webkit/adoption02.dat delete mode 100644 src/pkg/html/testdata/webkit/comments01.dat delete mode 100644 src/pkg/html/testdata/webkit/doctype01.dat delete mode 100644 src/pkg/html/testdata/webkit/entities01.dat delete mode 100644 src/pkg/html/testdata/webkit/entities02.dat delete mode 100644 src/pkg/html/testdata/webkit/html5test-com.dat delete mode 100644 src/pkg/html/testdata/webkit/inbody01.dat delete mode 100644 src/pkg/html/testdata/webkit/isindex.dat delete mode 100644 src/pkg/html/testdata/webkit/pending-spec-changes-plain-text-unsafe.dat delete mode 100644 src/pkg/html/testdata/webkit/pending-spec-changes.dat delete mode 100644 src/pkg/html/testdata/webkit/plain-text-unsafe.dat delete mode 100644 src/pkg/html/testdata/webkit/scriptdata01.dat delete mode 100644 src/pkg/html/testdata/webkit/scripted/adoption01.dat delete mode 100644 src/pkg/html/testdata/webkit/scripted/webkit01.dat delete mode 100644 src/pkg/html/testdata/webkit/tables01.dat delete mode 100644 src/pkg/html/testdata/webkit/tests1.dat delete mode 100644 src/pkg/html/testdata/webkit/tests10.dat delete mode 100644 src/pkg/html/testdata/webkit/tests11.dat delete mode 100644 src/pkg/html/testdata/webkit/tests12.dat delete mode 100644 src/pkg/html/testdata/webkit/tests14.dat delete mode 100644 src/pkg/html/testdata/webkit/tests15.dat delete mode 100644 src/pkg/html/testdata/webkit/tests16.dat delete mode 100644 src/pkg/html/testdata/webkit/tests17.dat delete mode 100644 src/pkg/html/testdata/webkit/tests18.dat delete mode 100644 src/pkg/html/testdata/webkit/tests19.dat delete mode 100644 src/pkg/html/testdata/webkit/tests2.dat delete mode 100644 src/pkg/html/testdata/webkit/tests20.dat delete mode 100644 src/pkg/html/testdata/webkit/tests21.dat delete mode 100644 src/pkg/html/testdata/webkit/tests22.dat delete mode 100644 src/pkg/html/testdata/webkit/tests23.dat delete mode 100644 src/pkg/html/testdata/webkit/tests24.dat delete mode 100644 src/pkg/html/testdata/webkit/tests25.dat delete mode 100644 src/pkg/html/testdata/webkit/tests26.dat delete mode 100644 src/pkg/html/testdata/webkit/tests3.dat delete mode 100644 src/pkg/html/testdata/webkit/tests4.dat delete mode 100644 src/pkg/html/testdata/webkit/tests5.dat delete mode 100644 src/pkg/html/testdata/webkit/tests6.dat delete mode 100644 src/pkg/html/testdata/webkit/tests7.dat delete mode 100644 src/pkg/html/testdata/webkit/tests8.dat delete mode 100644 src/pkg/html/testdata/webkit/tests9.dat delete mode 100644 src/pkg/html/testdata/webkit/tests_innerHTML_1.dat delete mode 100644 src/pkg/html/testdata/webkit/tricky01.dat delete mode 100644 src/pkg/html/testdata/webkit/webkit01.dat delete mode 100644 src/pkg/html/testdata/webkit/webkit02.dat delete mode 100644 src/pkg/html/token.go delete mode 100644 src/pkg/html/token_test.go delete mode 100644 src/pkg/http/Makefile delete mode 100644 src/pkg/http/cgi/Makefile delete mode 100644 src/pkg/http/cgi/child.go delete mode 100644 src/pkg/http/cgi/child_test.go delete mode 100644 src/pkg/http/cgi/host.go delete mode 100644 src/pkg/http/cgi/host_test.go delete mode 100644 src/pkg/http/cgi/matryoshka_test.go delete mode 100755 src/pkg/http/cgi/testdata/test.cgi delete mode 100644 src/pkg/http/chunked.go delete mode 100644 src/pkg/http/client.go delete mode 100644 src/pkg/http/client_test.go delete mode 100644 src/pkg/http/cookie.go delete mode 100644 src/pkg/http/cookie_test.go delete mode 100644 src/pkg/http/dump.go delete mode 100644 src/pkg/http/export_test.go delete mode 100644 src/pkg/http/fcgi/Makefile delete mode 100644 src/pkg/http/fcgi/child.go delete mode 100644 src/pkg/http/fcgi/fcgi.go delete mode 100644 src/pkg/http/fcgi/fcgi_test.go delete mode 100644 src/pkg/http/fs.go delete mode 100644 src/pkg/http/fs_test.go delete mode 100644 src/pkg/http/header.go delete mode 100644 src/pkg/http/header_test.go delete mode 100644 src/pkg/http/httptest/Makefile delete mode 100644 src/pkg/http/httptest/recorder.go delete mode 100644 src/pkg/http/httptest/server.go delete mode 100644 src/pkg/http/lex.go delete mode 100644 src/pkg/http/lex_test.go delete mode 100644 src/pkg/http/persist.go delete mode 100644 src/pkg/http/pprof/Makefile delete mode 100644 src/pkg/http/pprof/pprof.go delete mode 100644 src/pkg/http/proxy_test.go delete mode 100644 src/pkg/http/range_test.go delete mode 100644 src/pkg/http/readrequest_test.go delete mode 100644 src/pkg/http/request.go delete mode 100644 src/pkg/http/request_test.go delete mode 100644 src/pkg/http/requestwrite_test.go delete mode 100644 src/pkg/http/response.go delete mode 100644 src/pkg/http/response_test.go delete mode 100644 src/pkg/http/responsewrite_test.go delete mode 100644 src/pkg/http/reverseproxy.go delete mode 100644 src/pkg/http/reverseproxy_test.go delete mode 100644 src/pkg/http/serve_test.go delete mode 100644 src/pkg/http/server.go delete mode 100644 src/pkg/http/sniff.go delete mode 100644 src/pkg/http/sniff_test.go delete mode 100644 src/pkg/http/spdy/Makefile delete mode 100644 src/pkg/http/spdy/read.go delete mode 100644 src/pkg/http/spdy/spdy_test.go delete mode 100644 src/pkg/http/spdy/types.go delete mode 100644 src/pkg/http/spdy/write.go delete mode 100644 src/pkg/http/status.go delete mode 100644 src/pkg/http/testdata/file delete mode 100644 src/pkg/http/testdata/index.html delete mode 100644 src/pkg/http/testdata/style.css delete mode 100644 src/pkg/http/transfer.go delete mode 100644 src/pkg/http/transport.go delete mode 100644 src/pkg/http/transport_test.go delete mode 100644 src/pkg/http/triv.go delete mode 100644 src/pkg/image/Makefile delete mode 100644 src/pkg/image/bmp/Makefile delete mode 100644 src/pkg/image/bmp/reader.go delete mode 100644 src/pkg/image/color.go create mode 100644 src/pkg/image/color/color.go create mode 100644 src/pkg/image/color/ycbcr.go create mode 100644 src/pkg/image/color/ycbcr_test.go create mode 100644 src/pkg/image/decode_example_test.go delete mode 100644 src/pkg/image/draw/Makefile delete mode 100644 src/pkg/image/gif/Makefile delete mode 100644 src/pkg/image/jpeg/Makefile delete mode 100644 src/pkg/image/png/Makefile create mode 100644 src/pkg/image/png/testdata/invalid-crc32.png create mode 100644 src/pkg/image/png/testdata/invalid-noend.png create mode 100644 src/pkg/image/png/testdata/invalid-trunc.png create mode 100644 src/pkg/image/png/testdata/invalid-zlib.png delete mode 100644 src/pkg/image/testdata/video-001.bmp delete mode 100644 src/pkg/image/testdata/video-001.tiff delete mode 100644 src/pkg/image/tiff/Makefile delete mode 100644 src/pkg/image/tiff/buffer.go delete mode 100644 src/pkg/image/tiff/buffer_test.go delete mode 100644 src/pkg/image/tiff/consts.go delete mode 100644 src/pkg/image/tiff/reader.go delete mode 100644 src/pkg/image/tiff/reader_test.go delete mode 100644 src/pkg/image/tiff/testdata/no_rps.tiff create mode 100644 src/pkg/image/ycbcr.go delete mode 100644 src/pkg/image/ycbcr/Makefile delete mode 100644 src/pkg/image/ycbcr/ycbcr.go delete mode 100644 src/pkg/image/ycbcr/ycbcr_test.go create mode 100644 src/pkg/image/ycbcr_test.go delete mode 100644 src/pkg/index/suffixarray/Makefile delete mode 100644 src/pkg/io/Makefile delete mode 100644 src/pkg/io/ioutil/Makefile delete mode 100644 src/pkg/json/Makefile delete mode 100644 src/pkg/json/decode.go delete mode 100644 src/pkg/json/decode_test.go delete mode 100644 src/pkg/json/encode.go delete mode 100644 src/pkg/json/encode_test.go delete mode 100644 src/pkg/json/indent.go delete mode 100644 src/pkg/json/scanner.go delete mode 100644 src/pkg/json/scanner_test.go delete mode 100644 src/pkg/json/stream.go delete mode 100644 src/pkg/json/stream_test.go delete mode 100644 src/pkg/json/tagkey_test.go delete mode 100644 src/pkg/json/tags.go delete mode 100644 src/pkg/json/tags_test.go delete mode 100644 src/pkg/log/Makefile create mode 100644 src/pkg/log/syslog/syslog.go create mode 100644 src/pkg/log/syslog/syslog_test.go create mode 100644 src/pkg/log/syslog/syslog_unix.go create mode 100644 src/pkg/log/syslog/syslog_windows.go delete mode 100644 src/pkg/mail/Makefile delete mode 100644 src/pkg/mail/message.go delete mode 100644 src/pkg/mail/message_test.go delete mode 100644 src/pkg/math/Makefile create mode 100644 src/pkg/math/abs.go create mode 100644 src/pkg/math/abs_386.s create mode 100644 src/pkg/math/abs_amd64.s create mode 100644 src/pkg/math/abs_arm.s create mode 100644 src/pkg/math/asin_amd64.s create mode 100644 src/pkg/math/asin_arm.s delete mode 100644 src/pkg/math/asin_decl.go mode change 100755 => 100644 src/pkg/math/atan2_386.s create mode 100644 src/pkg/math/atan2_amd64.s create mode 100644 src/pkg/math/atan2_arm.s delete mode 100755 src/pkg/math/atan2_decl.go create mode 100644 src/pkg/math/atan_amd64.s create mode 100644 src/pkg/math/atan_arm.s delete mode 100644 src/pkg/math/atan_decl.go create mode 100644 src/pkg/math/big/arith.go create mode 100644 src/pkg/math/big/arith_386.s create mode 100644 src/pkg/math/big/arith_amd64.s create mode 100644 src/pkg/math/big/arith_arm.s create mode 100644 src/pkg/math/big/arith_decl.go create mode 100644 src/pkg/math/big/arith_test.go create mode 100644 src/pkg/math/big/calibrate_test.go create mode 100644 src/pkg/math/big/example_test.go create mode 100644 src/pkg/math/big/hilbert_test.go create mode 100644 src/pkg/math/big/int.go create mode 100644 src/pkg/math/big/int_test.go create mode 100644 src/pkg/math/big/nat.go create mode 100644 src/pkg/math/big/nat_test.go create mode 100644 src/pkg/math/big/rat.go create mode 100644 src/pkg/math/big/rat_test.go create mode 100644 src/pkg/math/cmplx/abs.go create mode 100644 src/pkg/math/cmplx/asin.go create mode 100644 src/pkg/math/cmplx/cmath_test.go create mode 100644 src/pkg/math/cmplx/conj.go create mode 100644 src/pkg/math/cmplx/exp.go create mode 100644 src/pkg/math/cmplx/isinf.go create mode 100644 src/pkg/math/cmplx/isnan.go create mode 100644 src/pkg/math/cmplx/log.go create mode 100644 src/pkg/math/cmplx/phase.go create mode 100644 src/pkg/math/cmplx/polar.go create mode 100644 src/pkg/math/cmplx/pow.go create mode 100644 src/pkg/math/cmplx/rect.go create mode 100644 src/pkg/math/cmplx/sin.go create mode 100644 src/pkg/math/cmplx/sqrt.go create mode 100644 src/pkg/math/cmplx/tan.go create mode 100644 src/pkg/math/dim.go create mode 100644 src/pkg/math/dim_386.s create mode 100644 src/pkg/math/dim_amd64.s create mode 100644 src/pkg/math/dim_arm.s delete mode 100644 src/pkg/math/exp2.go create mode 100644 src/pkg/math/exp2_amd64.s create mode 100644 src/pkg/math/exp2_arm.s delete mode 100644 src/pkg/math/exp2_decl.go create mode 100644 src/pkg/math/exp_arm.s delete mode 100644 src/pkg/math/exp_decl.go delete mode 100644 src/pkg/math/exp_port.go delete mode 100644 src/pkg/math/exp_test.go create mode 100644 src/pkg/math/expm1_amd64.s create mode 100644 src/pkg/math/expm1_arm.s delete mode 100644 src/pkg/math/expm1_decl.go create mode 100644 src/pkg/math/export_test.go delete mode 100644 src/pkg/math/fabs.go delete mode 100644 src/pkg/math/fabs_386.s delete mode 100644 src/pkg/math/fabs_amd64.s delete mode 100644 src/pkg/math/fabs_decl.go delete mode 100644 src/pkg/math/fdim.go delete mode 100644 src/pkg/math/fdim_amd64.s delete mode 100644 src/pkg/math/fdim_decl.go create mode 100644 src/pkg/math/floor_amd64.s create mode 100644 src/pkg/math/floor_arm.s delete mode 100644 src/pkg/math/floor_decl.go delete mode 100644 src/pkg/math/fmod.go delete mode 100644 src/pkg/math/fmod_386.s delete mode 100644 src/pkg/math/fmod_decl.go create mode 100644 src/pkg/math/frexp_amd64.s create mode 100644 src/pkg/math/frexp_arm.s delete mode 100644 src/pkg/math/frexp_decl.go create mode 100644 src/pkg/math/hypot_arm.s delete mode 100644 src/pkg/math/hypot_decl.go delete mode 100644 src/pkg/math/hypot_port.go delete mode 100644 src/pkg/math/hypot_test.go create mode 100644 src/pkg/math/ldexp_amd64.s create mode 100644 src/pkg/math/ldexp_arm.s delete mode 100644 src/pkg/math/ldexp_decl.go create mode 100644 src/pkg/math/log10_amd64.s create mode 100644 src/pkg/math/log10_arm.s delete mode 100644 src/pkg/math/log10_decl.go create mode 100644 src/pkg/math/log1p_amd64.s create mode 100644 src/pkg/math/log1p_arm.s delete mode 100644 src/pkg/math/log1p_decl.go create mode 100644 src/pkg/math/log_arm.s delete mode 100644 src/pkg/math/log_decl.go create mode 100644 src/pkg/math/mod.go create mode 100644 src/pkg/math/mod_386.s create mode 100644 src/pkg/math/mod_amd64.s create mode 100644 src/pkg/math/mod_arm.s create mode 100644 src/pkg/math/modf_amd64.s create mode 100644 src/pkg/math/modf_arm.s delete mode 100644 src/pkg/math/modf_decl.go create mode 100644 src/pkg/math/rand/exp.go create mode 100644 src/pkg/math/rand/normal.go create mode 100644 src/pkg/math/rand/rand.go create mode 100644 src/pkg/math/rand/rand_test.go create mode 100644 src/pkg/math/rand/rng.go create mode 100644 src/pkg/math/rand/zipf.go create mode 100644 src/pkg/math/remainder_amd64.s create mode 100644 src/pkg/math/remainder_arm.s delete mode 100644 src/pkg/math/remainder_decl.go create mode 100644 src/pkg/math/sin_amd64.s create mode 100644 src/pkg/math/sin_arm.s delete mode 100644 src/pkg/math/sin_decl.go create mode 100644 src/pkg/math/sincos_arm.s delete mode 100644 src/pkg/math/sincos_decl.go delete mode 100644 src/pkg/math/sqrt_decl.go delete mode 100644 src/pkg/math/sqrt_port.go delete mode 100644 src/pkg/math/sqrt_test.go create mode 100644 src/pkg/math/tan_amd64.s create mode 100644 src/pkg/math/tan_arm.s delete mode 100644 src/pkg/math/tan_decl.go delete mode 100644 src/pkg/mime/Makefile delete mode 100644 src/pkg/mime/mime_test.go delete mode 100644 src/pkg/mime/multipart/Makefile create mode 100644 src/pkg/mime/type_test.go create mode 100644 src/pkg/mime/type_unix.go create mode 100644 src/pkg/mime/type_windows.go delete mode 100644 src/pkg/net/Makefile create mode 100644 src/pkg/net/dial_test.go delete mode 100644 src/pkg/net/dict/Makefile delete mode 100644 src/pkg/net/dict/dict.go create mode 100644 src/pkg/net/doc.go create mode 100644 src/pkg/net/example_test.go create mode 100644 src/pkg/net/fd_netbsd.go delete mode 100644 src/pkg/net/hosts_testdata create mode 100644 src/pkg/net/http/cgi/child.go create mode 100644 src/pkg/net/http/cgi/child_test.go create mode 100644 src/pkg/net/http/cgi/host.go create mode 100644 src/pkg/net/http/cgi/host_test.go create mode 100644 src/pkg/net/http/cgi/matryoshka_test.go create mode 100755 src/pkg/net/http/cgi/testdata/test.cgi create mode 100644 src/pkg/net/http/chunked.go create mode 100644 src/pkg/net/http/chunked_test.go create mode 100644 src/pkg/net/http/client.go create mode 100644 src/pkg/net/http/client_test.go create mode 100644 src/pkg/net/http/cookie.go create mode 100644 src/pkg/net/http/cookie_test.go create mode 100644 src/pkg/net/http/doc.go create mode 100644 src/pkg/net/http/example_test.go create mode 100644 src/pkg/net/http/export_test.go create mode 100644 src/pkg/net/http/fcgi/child.go create mode 100644 src/pkg/net/http/fcgi/fcgi.go create mode 100644 src/pkg/net/http/fcgi/fcgi_test.go create mode 100644 src/pkg/net/http/filetransport.go create mode 100644 src/pkg/net/http/filetransport_test.go create mode 100644 src/pkg/net/http/fs.go create mode 100644 src/pkg/net/http/fs_test.go create mode 100644 src/pkg/net/http/header.go create mode 100644 src/pkg/net/http/header_test.go create mode 100644 src/pkg/net/http/httptest/recorder.go create mode 100644 src/pkg/net/http/httptest/server.go create mode 100644 src/pkg/net/http/httptest/server_test.go create mode 100644 src/pkg/net/http/httputil/chunked.go create mode 100644 src/pkg/net/http/httputil/chunked_test.go create mode 100644 src/pkg/net/http/httputil/dump.go create mode 100644 src/pkg/net/http/httputil/dump_test.go create mode 100644 src/pkg/net/http/httputil/persist.go create mode 100644 src/pkg/net/http/httputil/reverseproxy.go create mode 100644 src/pkg/net/http/httputil/reverseproxy_test.go create mode 100644 src/pkg/net/http/jar.go create mode 100644 src/pkg/net/http/lex.go create mode 100644 src/pkg/net/http/lex_test.go create mode 100644 src/pkg/net/http/pprof/pprof.go create mode 100644 src/pkg/net/http/proxy_test.go create mode 100644 src/pkg/net/http/range_test.go create mode 100644 src/pkg/net/http/readrequest_test.go create mode 100644 src/pkg/net/http/request.go create mode 100644 src/pkg/net/http/request_test.go create mode 100644 src/pkg/net/http/requestwrite_test.go create mode 100644 src/pkg/net/http/response.go create mode 100644 src/pkg/net/http/response_test.go create mode 100644 src/pkg/net/http/responsewrite_test.go create mode 100644 src/pkg/net/http/serve_test.go create mode 100644 src/pkg/net/http/server.go create mode 100644 src/pkg/net/http/sniff.go create mode 100644 src/pkg/net/http/sniff_test.go create mode 100644 src/pkg/net/http/status.go create mode 100644 src/pkg/net/http/testdata/file create mode 100644 src/pkg/net/http/testdata/index.html create mode 100644 src/pkg/net/http/testdata/style.css create mode 100644 src/pkg/net/http/transfer.go create mode 100644 src/pkg/net/http/transport.go create mode 100644 src/pkg/net/http/transport_test.go create mode 100644 src/pkg/net/http/triv.go create mode 100644 src/pkg/net/interface_linux_test.go create mode 100644 src/pkg/net/interface_netbsd.go create mode 100644 src/pkg/net/mac.go create mode 100644 src/pkg/net/mac_test.go create mode 100644 src/pkg/net/mail/message.go create mode 100644 src/pkg/net/mail/message_test.go create mode 100644 src/pkg/net/rpc/client.go create mode 100644 src/pkg/net/rpc/debug.go create mode 100644 src/pkg/net/rpc/jsonrpc/all_test.go create mode 100644 src/pkg/net/rpc/jsonrpc/client.go create mode 100644 src/pkg/net/rpc/jsonrpc/server.go create mode 100644 src/pkg/net/rpc/server.go create mode 100644 src/pkg/net/rpc/server_test.go create mode 100644 src/pkg/net/smtp/auth.go create mode 100644 src/pkg/net/smtp/smtp.go create mode 100644 src/pkg/net/smtp/smtp_test.go create mode 100644 src/pkg/net/sockopt.go create mode 100644 src/pkg/net/sockopt_bsd.go create mode 100644 src/pkg/net/sockopt_linux.go create mode 100644 src/pkg/net/sockopt_windows.go create mode 100644 src/pkg/net/sockoptip.go create mode 100644 src/pkg/net/sockoptip_bsd.go create mode 100644 src/pkg/net/sockoptip_darwin.go create mode 100644 src/pkg/net/sockoptip_freebsd.go create mode 100644 src/pkg/net/sockoptip_linux.go create mode 100644 src/pkg/net/sockoptip_netbsd.go create mode 100644 src/pkg/net/sockoptip_openbsd.go create mode 100644 src/pkg/net/sockoptip_windows.go create mode 100644 src/pkg/net/testdata/hosts create mode 100644 src/pkg/net/testdata/igmp create mode 100644 src/pkg/net/testdata/igmp6 delete mode 100644 src/pkg/net/textproto/Makefile create mode 100644 src/pkg/net/udp_test.go create mode 100644 src/pkg/net/unicast_test.go create mode 100644 src/pkg/net/url/example_test.go create mode 100644 src/pkg/net/url/url.go create mode 100644 src/pkg/net/url/url_test.go delete mode 100644 src/pkg/netchan/Makefile delete mode 100644 src/pkg/netchan/common.go delete mode 100644 src/pkg/netchan/export.go delete mode 100644 src/pkg/netchan/import.go delete mode 100644 src/pkg/netchan/netchan_test.go delete mode 100644 src/pkg/old/template/Makefile delete mode 100644 src/pkg/old/template/doc.go delete mode 100644 src/pkg/old/template/execute.go delete mode 100644 src/pkg/old/template/format.go delete mode 100644 src/pkg/old/template/parse.go delete mode 100644 src/pkg/old/template/template_test.go delete mode 100644 src/pkg/os/Makefile create mode 100644 src/pkg/os/doc.go delete mode 100644 src/pkg/os/env_plan9.go delete mode 100644 src/pkg/os/env_unix.go delete mode 100644 src/pkg/os/env_windows.go create mode 100644 src/pkg/os/error_test.go create mode 100644 src/pkg/os/error_windows.go create mode 100644 src/pkg/os/exec/example_test.go create mode 100644 src/pkg/os/exec/exec.go create mode 100644 src/pkg/os/exec/exec_test.go create mode 100644 src/pkg/os/exec/lp_plan9.go create mode 100644 src/pkg/os/exec/lp_test.go create mode 100644 src/pkg/os/exec/lp_unix.go create mode 100644 src/pkg/os/exec/lp_windows.go create mode 100644 src/pkg/os/export_test.go delete mode 100644 src/pkg/os/inotify/Makefile delete mode 100644 src/pkg/os/inotify/inotify_linux.go delete mode 100644 src/pkg/os/inotify/inotify_linux_test.go delete mode 100755 src/pkg/os/mkunixsignals.sh create mode 100644 src/pkg/os/os_unix_test.go delete mode 100644 src/pkg/os/signal/Makefile create mode 100644 src/pkg/os/signal/sig.s create mode 100644 src/pkg/os/signal/signal_stub.go create mode 100644 src/pkg/os/signal/signal_unix.go create mode 100644 src/pkg/os/signal/signal_windows_test.go create mode 100644 src/pkg/os/stat_netbsd.go delete mode 100644 src/pkg/os/time.go delete mode 100644 src/pkg/os/user/Makefile create mode 100644 src/pkg/os/user/lookup_windows.go delete mode 100644 src/pkg/patch/Makefile delete mode 100644 src/pkg/patch/apply.go delete mode 100644 src/pkg/patch/git.go delete mode 100644 src/pkg/patch/patch.go delete mode 100644 src/pkg/patch/patch_test.go delete mode 100644 src/pkg/patch/textdiff.go delete mode 100644 src/pkg/path/Makefile create mode 100644 src/pkg/path/example_test.go delete mode 100644 src/pkg/path/filepath/Makefile create mode 100644 src/pkg/path/filepath/symlink.go create mode 100644 src/pkg/path/filepath/symlink_windows.go delete mode 100644 src/pkg/rand/Makefile delete mode 100644 src/pkg/rand/exp.go delete mode 100644 src/pkg/rand/normal.go delete mode 100644 src/pkg/rand/rand.go delete mode 100644 src/pkg/rand/rand_test.go delete mode 100644 src/pkg/rand/rng.go delete mode 100644 src/pkg/rand/zipf.go delete mode 100644 src/pkg/reflect/Makefile delete mode 100644 src/pkg/regexp/Makefile create mode 100644 src/pkg/regexp/exec.go create mode 100644 src/pkg/regexp/exec_test.go create mode 100644 src/pkg/regexp/syntax/compile.go create mode 100755 src/pkg/regexp/syntax/make_perl_groups.pl create mode 100644 src/pkg/regexp/syntax/parse.go create mode 100644 src/pkg/regexp/syntax/parse_test.go create mode 100644 src/pkg/regexp/syntax/perl_groups.go create mode 100644 src/pkg/regexp/syntax/prog.go create mode 100644 src/pkg/regexp/syntax/prog_test.go create mode 100644 src/pkg/regexp/syntax/regexp.go create mode 100644 src/pkg/regexp/syntax/simplify.go create mode 100644 src/pkg/regexp/syntax/simplify_test.go create mode 100644 src/pkg/regexp/testdata/README create mode 100644 src/pkg/regexp/testdata/basic.dat create mode 100644 src/pkg/regexp/testdata/nullsubexpr.dat create mode 100644 src/pkg/regexp/testdata/re2-exhaustive.txt.bz2 create mode 100644 src/pkg/regexp/testdata/re2-search.txt create mode 100644 src/pkg/regexp/testdata/repetition.dat create mode 100644 src/pkg/regexp/testdata/testregex.c delete mode 100644 src/pkg/rpc/Makefile delete mode 100644 src/pkg/rpc/client.go delete mode 100644 src/pkg/rpc/debug.go delete mode 100644 src/pkg/rpc/jsonrpc/Makefile delete mode 100644 src/pkg/rpc/jsonrpc/all_test.go delete mode 100644 src/pkg/rpc/jsonrpc/client.go delete mode 100644 src/pkg/rpc/jsonrpc/server.go delete mode 100644 src/pkg/rpc/server.go delete mode 100644 src/pkg/rpc/server_test.go delete mode 100644 src/pkg/runtime/386/arch.h delete mode 100644 src/pkg/runtime/386/asm.s delete mode 100644 src/pkg/runtime/386/atomic.c delete mode 100644 src/pkg/runtime/386/closure.c delete mode 100644 src/pkg/runtime/386/memmove.s delete mode 100644 src/pkg/runtime/386/vlop.s delete mode 100644 src/pkg/runtime/386/vlrt.c create mode 100644 src/pkg/runtime/alg.c delete mode 100644 src/pkg/runtime/amd64/arch.h delete mode 100644 src/pkg/runtime/amd64/asm.s delete mode 100644 src/pkg/runtime/amd64/atomic.c delete mode 100644 src/pkg/runtime/amd64/closure.c delete mode 100644 src/pkg/runtime/amd64/memmove.s delete mode 100644 src/pkg/runtime/amd64/traceback.c create mode 100644 src/pkg/runtime/arch_386.h create mode 100644 src/pkg/runtime/arch_amd64.h create mode 100644 src/pkg/runtime/arch_arm.h delete mode 100644 src/pkg/runtime/arm/arch.h delete mode 100644 src/pkg/runtime/arm/asm.s delete mode 100644 src/pkg/runtime/arm/atomic.c delete mode 100644 src/pkg/runtime/arm/closure.c delete mode 100644 src/pkg/runtime/arm/memmove.s delete mode 100644 src/pkg/runtime/arm/memset.s delete mode 100644 src/pkg/runtime/arm/softfloat.c delete mode 100644 src/pkg/runtime/arm/traceback.c delete mode 100644 src/pkg/runtime/arm/vlop.s delete mode 100644 src/pkg/runtime/arm/vlrt.c create mode 100644 src/pkg/runtime/asm_386.s create mode 100644 src/pkg/runtime/asm_amd64.s create mode 100644 src/pkg/runtime/asm_arm.s create mode 100644 src/pkg/runtime/atomic_386.c create mode 100644 src/pkg/runtime/atomic_amd64.c create mode 100644 src/pkg/runtime/atomic_arm.c create mode 100644 src/pkg/runtime/callback_windows_386.c create mode 100644 src/pkg/runtime/callback_windows_amd64.c delete mode 100755 src/pkg/runtime/cgo/386.S delete mode 100644 src/pkg/runtime/cgo/Makefile delete mode 100644 src/pkg/runtime/cgo/amd64.S delete mode 100644 src/pkg/runtime/cgo/arm.S delete mode 100644 src/pkg/runtime/cgo/darwin_386.c delete mode 100644 src/pkg/runtime/cgo/darwin_amd64.c delete mode 100644 src/pkg/runtime/cgo/freebsd_386.c delete mode 100644 src/pkg/runtime/cgo/freebsd_amd64.c create mode 100644 src/pkg/runtime/cgo/gcc_386.S create mode 100644 src/pkg/runtime/cgo/gcc_amd64.S create mode 100644 src/pkg/runtime/cgo/gcc_arm.S create mode 100644 src/pkg/runtime/cgo/gcc_darwin_386.c create mode 100644 src/pkg/runtime/cgo/gcc_darwin_amd64.c create mode 100644 src/pkg/runtime/cgo/gcc_freebsd_386.c create mode 100644 src/pkg/runtime/cgo/gcc_freebsd_amd64.c create mode 100644 src/pkg/runtime/cgo/gcc_linux_386.c create mode 100644 src/pkg/runtime/cgo/gcc_linux_amd64.c create mode 100644 src/pkg/runtime/cgo/gcc_linux_arm.c create mode 100644 src/pkg/runtime/cgo/gcc_setenv.c create mode 100644 src/pkg/runtime/cgo/gcc_util.c create mode 100644 src/pkg/runtime/cgo/gcc_windows_386.c create mode 100644 src/pkg/runtime/cgo/gcc_windows_amd64.c delete mode 100644 src/pkg/runtime/cgo/linux_386.c delete mode 100644 src/pkg/runtime/cgo/linux_amd64.c delete mode 100644 src/pkg/runtime/cgo/linux_arm.c delete mode 100644 src/pkg/runtime/cgo/setenv.c delete mode 100644 src/pkg/runtime/cgo/util.c delete mode 100755 src/pkg/runtime/cgo/windows_386.c delete mode 100755 src/pkg/runtime/cgo/windows_amd64.c create mode 100644 src/pkg/runtime/closure_386.c create mode 100644 src/pkg/runtime/closure_amd64.c create mode 100644 src/pkg/runtime/closure_arm.c create mode 100644 src/pkg/runtime/compiler.go delete mode 100644 src/pkg/runtime/darwin/386/defs.h delete mode 100644 src/pkg/runtime/darwin/386/rt0.s delete mode 100644 src/pkg/runtime/darwin/386/signal.c delete mode 100644 src/pkg/runtime/darwin/386/sys.s delete mode 100644 src/pkg/runtime/darwin/amd64/defs.h delete mode 100644 src/pkg/runtime/darwin/amd64/rt0.s delete mode 100644 src/pkg/runtime/darwin/amd64/signal.c delete mode 100644 src/pkg/runtime/darwin/amd64/sys.s delete mode 100644 src/pkg/runtime/darwin/defs.c delete mode 100644 src/pkg/runtime/darwin/mem.c delete mode 100644 src/pkg/runtime/darwin/os.h delete mode 100644 src/pkg/runtime/darwin/signals.h delete mode 100644 src/pkg/runtime/darwin/thread.c delete mode 100644 src/pkg/runtime/debug/Makefile create mode 100644 src/pkg/runtime/defs1_linux.go create mode 100644 src/pkg/runtime/defs2_linux.go create mode 100644 src/pkg/runtime/defs_arm_linux.go create mode 100644 src/pkg/runtime/defs_darwin.go create mode 100644 src/pkg/runtime/defs_darwin_386.h create mode 100644 src/pkg/runtime/defs_darwin_amd64.h create mode 100644 src/pkg/runtime/defs_freebsd.go create mode 100644 src/pkg/runtime/defs_freebsd_386.h create mode 100644 src/pkg/runtime/defs_freebsd_amd64.h create mode 100644 src/pkg/runtime/defs_linux.go create mode 100644 src/pkg/runtime/defs_linux_386.h create mode 100644 src/pkg/runtime/defs_linux_amd64.h create mode 100644 src/pkg/runtime/defs_linux_arm.h create mode 100644 src/pkg/runtime/defs_netbsd.go create mode 100644 src/pkg/runtime/defs_netbsd_386.h create mode 100644 src/pkg/runtime/defs_netbsd_amd64.h create mode 100644 src/pkg/runtime/defs_openbsd.go create mode 100644 src/pkg/runtime/defs_openbsd_386.h create mode 100644 src/pkg/runtime/defs_openbsd_amd64.h create mode 100644 src/pkg/runtime/defs_plan9_386.h create mode 100644 src/pkg/runtime/defs_windows.go create mode 100644 src/pkg/runtime/defs_windows_386.h create mode 100644 src/pkg/runtime/defs_windows_amd64.h delete mode 100644 src/pkg/runtime/freebsd/386/defs.h delete mode 100644 src/pkg/runtime/freebsd/386/rt0.s delete mode 100644 src/pkg/runtime/freebsd/386/signal.c delete mode 100644 src/pkg/runtime/freebsd/386/sys.s delete mode 100644 src/pkg/runtime/freebsd/amd64/defs.h delete mode 100644 src/pkg/runtime/freebsd/amd64/rt0.s delete mode 100644 src/pkg/runtime/freebsd/amd64/signal.c delete mode 100644 src/pkg/runtime/freebsd/amd64/sys.s delete mode 100644 src/pkg/runtime/freebsd/defs.c delete mode 100644 src/pkg/runtime/freebsd/mem.c delete mode 100644 src/pkg/runtime/freebsd/os.h delete mode 100644 src/pkg/runtime/freebsd/signals.h delete mode 100644 src/pkg/runtime/freebsd/thread.c create mode 100644 src/pkg/runtime/gc_test.go delete mode 100644 src/pkg/runtime/goc2c.c delete mode 100644 src/pkg/runtime/linux/386/defs.h delete mode 100644 src/pkg/runtime/linux/386/rt0.s delete mode 100644 src/pkg/runtime/linux/386/signal.c delete mode 100644 src/pkg/runtime/linux/386/sys.s delete mode 100644 src/pkg/runtime/linux/amd64/defs.h delete mode 100644 src/pkg/runtime/linux/amd64/rt0.s delete mode 100644 src/pkg/runtime/linux/amd64/signal.c delete mode 100644 src/pkg/runtime/linux/amd64/sys.s delete mode 100644 src/pkg/runtime/linux/arm/defs.h delete mode 100644 src/pkg/runtime/linux/arm/rt0.s delete mode 100644 src/pkg/runtime/linux/arm/signal.c delete mode 100644 src/pkg/runtime/linux/arm/sys.s delete mode 100644 src/pkg/runtime/linux/defs.c delete mode 100644 src/pkg/runtime/linux/defs1.c delete mode 100644 src/pkg/runtime/linux/defs2.c delete mode 100644 src/pkg/runtime/linux/defs_arm.c delete mode 100644 src/pkg/runtime/linux/mem.c delete mode 100644 src/pkg/runtime/linux/os.h delete mode 100644 src/pkg/runtime/linux/signals.h delete mode 100644 src/pkg/runtime/linux/thread.c create mode 100644 src/pkg/runtime/lock_futex.c create mode 100644 src/pkg/runtime/lock_sema.c create mode 100644 src/pkg/runtime/malloc1.go create mode 100644 src/pkg/runtime/mallocrand.go create mode 100644 src/pkg/runtime/mallocrep.go create mode 100644 src/pkg/runtime/mallocrep1.go create mode 100644 src/pkg/runtime/mem_darwin.c create mode 100644 src/pkg/runtime/mem_freebsd.c create mode 100644 src/pkg/runtime/mem_linux.c create mode 100644 src/pkg/runtime/mem_netbsd.c create mode 100644 src/pkg/runtime/mem_openbsd.c create mode 100644 src/pkg/runtime/mem_plan9.c create mode 100644 src/pkg/runtime/mem_windows.c create mode 100644 src/pkg/runtime/memmove_386.s create mode 100644 src/pkg/runtime/memmove_amd64.s create mode 100644 src/pkg/runtime/memmove_arm.s create mode 100644 src/pkg/runtime/memset_arm.s create mode 100644 src/pkg/runtime/mfinal_test.go delete mode 100755 src/pkg/runtime/mkgodefs.sh delete mode 100644 src/pkg/runtime/openbsd/amd64/defs.h delete mode 100644 src/pkg/runtime/openbsd/amd64/rt0.s delete mode 100644 src/pkg/runtime/openbsd/amd64/signal.c delete mode 100644 src/pkg/runtime/openbsd/amd64/sys.s delete mode 100644 src/pkg/runtime/openbsd/defs.c delete mode 100644 src/pkg/runtime/openbsd/mem.c delete mode 100644 src/pkg/runtime/openbsd/os.h delete mode 100644 src/pkg/runtime/openbsd/signals.h delete mode 100644 src/pkg/runtime/openbsd/thread.c create mode 100644 src/pkg/runtime/os_darwin.h create mode 100644 src/pkg/runtime/os_freebsd.h create mode 100644 src/pkg/runtime/os_linux.h create mode 100644 src/pkg/runtime/os_netbsd.h create mode 100644 src/pkg/runtime/os_openbsd.h create mode 100644 src/pkg/runtime/os_plan9.h create mode 100644 src/pkg/runtime/os_windows.h delete mode 100644 src/pkg/runtime/plan9/386/defs.h delete mode 100644 src/pkg/runtime/plan9/386/rt0.s delete mode 100644 src/pkg/runtime/plan9/386/signal.c delete mode 100644 src/pkg/runtime/plan9/386/sys.s delete mode 100644 src/pkg/runtime/plan9/mem.c delete mode 100644 src/pkg/runtime/plan9/os.h delete mode 100644 src/pkg/runtime/plan9/signals.h delete mode 100644 src/pkg/runtime/plan9/thread.c delete mode 100644 src/pkg/runtime/pprof/Makefile create mode 100644 src/pkg/runtime/rt0_darwin_386.s create mode 100644 src/pkg/runtime/rt0_darwin_amd64.s create mode 100644 src/pkg/runtime/rt0_freebsd_386.s create mode 100644 src/pkg/runtime/rt0_freebsd_amd64.s create mode 100644 src/pkg/runtime/rt0_linux_386.s create mode 100644 src/pkg/runtime/rt0_linux_amd64.s create mode 100644 src/pkg/runtime/rt0_linux_arm.s create mode 100644 src/pkg/runtime/rt0_netbsd_386.s create mode 100644 src/pkg/runtime/rt0_netbsd_amd64.s create mode 100644 src/pkg/runtime/rt0_openbsd_386.s create mode 100644 src/pkg/runtime/rt0_openbsd_amd64.s create mode 100644 src/pkg/runtime/rt0_plan9_386.s create mode 100644 src/pkg/runtime/rt0_windows_386.s create mode 100644 src/pkg/runtime/rt0_windows_amd64.s create mode 100644 src/pkg/runtime/runtime_linux_test.go create mode 100644 src/pkg/runtime/runtime_test.go delete mode 100644 src/pkg/runtime/sema_test.go delete mode 100644 src/pkg/runtime/sig.go create mode 100644 src/pkg/runtime/signal_darwin_386.c create mode 100644 src/pkg/runtime/signal_darwin_amd64.c create mode 100644 src/pkg/runtime/signal_freebsd_386.c create mode 100644 src/pkg/runtime/signal_freebsd_amd64.c create mode 100644 src/pkg/runtime/signal_linux_386.c create mode 100644 src/pkg/runtime/signal_linux_amd64.c create mode 100644 src/pkg/runtime/signal_linux_arm.c create mode 100644 src/pkg/runtime/signal_netbsd_386.c create mode 100644 src/pkg/runtime/signal_netbsd_amd64.c create mode 100644 src/pkg/runtime/signal_openbsd_386.c create mode 100644 src/pkg/runtime/signal_openbsd_amd64.c create mode 100644 src/pkg/runtime/signal_plan9_386.c create mode 100644 src/pkg/runtime/signal_unix.c create mode 100644 src/pkg/runtime/signal_windows_386.c create mode 100644 src/pkg/runtime/signal_windows_amd64.c create mode 100644 src/pkg/runtime/signals_darwin.h create mode 100644 src/pkg/runtime/signals_freebsd.h create mode 100644 src/pkg/runtime/signals_linux.h create mode 100644 src/pkg/runtime/signals_netbsd.h create mode 100644 src/pkg/runtime/signals_openbsd.h create mode 100644 src/pkg/runtime/signals_plan9.h create mode 100644 src/pkg/runtime/signals_windows.h create mode 100644 src/pkg/runtime/softfloat_arm.c create mode 100644 src/pkg/runtime/stack_test.go create mode 100644 src/pkg/runtime/sys_darwin_386.s create mode 100644 src/pkg/runtime/sys_darwin_amd64.s create mode 100644 src/pkg/runtime/sys_freebsd_386.s create mode 100644 src/pkg/runtime/sys_freebsd_amd64.s create mode 100644 src/pkg/runtime/sys_linux_386.s create mode 100644 src/pkg/runtime/sys_linux_amd64.s create mode 100644 src/pkg/runtime/sys_linux_arm.s create mode 100644 src/pkg/runtime/sys_netbsd_386.s create mode 100644 src/pkg/runtime/sys_netbsd_amd64.s create mode 100644 src/pkg/runtime/sys_openbsd_386.s create mode 100644 src/pkg/runtime/sys_openbsd_amd64.s create mode 100644 src/pkg/runtime/sys_plan9_386.s create mode 100644 src/pkg/runtime/sys_windows_386.s create mode 100644 src/pkg/runtime/sys_windows_amd64.s create mode 100644 src/pkg/runtime/syscall_windows.goc create mode 100644 src/pkg/runtime/syscall_windows_test.go create mode 100644 src/pkg/runtime/thread_darwin.c create mode 100644 src/pkg/runtime/thread_freebsd.c create mode 100644 src/pkg/runtime/thread_linux.c create mode 100644 src/pkg/runtime/thread_netbsd.c create mode 100644 src/pkg/runtime/thread_openbsd.c create mode 100644 src/pkg/runtime/thread_plan9.c create mode 100644 src/pkg/runtime/thread_windows.c create mode 100644 src/pkg/runtime/time.goc create mode 100644 src/pkg/runtime/traceback_arm.c create mode 100644 src/pkg/runtime/traceback_x86.c create mode 100644 src/pkg/runtime/vlop_386.s create mode 100644 src/pkg/runtime/vlop_arm.s create mode 100644 src/pkg/runtime/vlrt_386.c create mode 100644 src/pkg/runtime/vlrt_arm.c delete mode 100644 src/pkg/runtime/windows/386/defs.h delete mode 100644 src/pkg/runtime/windows/386/rt0.s delete mode 100644 src/pkg/runtime/windows/386/signal.c delete mode 100644 src/pkg/runtime/windows/386/sys.s delete mode 100644 src/pkg/runtime/windows/amd64/defs.h delete mode 100644 src/pkg/runtime/windows/amd64/rt0.s delete mode 100644 src/pkg/runtime/windows/amd64/signal.c delete mode 100644 src/pkg/runtime/windows/amd64/sys.s delete mode 100644 src/pkg/runtime/windows/defs.c delete mode 100644 src/pkg/runtime/windows/mem.c delete mode 100644 src/pkg/runtime/windows/os.h delete mode 100644 src/pkg/runtime/windows/signals.h delete mode 100644 src/pkg/runtime/windows/syscall.goc delete mode 100644 src/pkg/runtime/windows/thread.c delete mode 100644 src/pkg/scanner/Makefile delete mode 100644 src/pkg/scanner/scanner.go delete mode 100644 src/pkg/scanner/scanner_test.go delete mode 100644 src/pkg/smtp/Makefile delete mode 100644 src/pkg/smtp/auth.go delete mode 100644 src/pkg/smtp/smtp.go delete mode 100644 src/pkg/smtp/smtp_test.go delete mode 100644 src/pkg/sort/Makefile create mode 100644 src/pkg/sort/example_interface_test.go create mode 100644 src/pkg/sort/example_reverse_test.go create mode 100644 src/pkg/sort/example_test.go create mode 100644 src/pkg/sort/export_test.go delete mode 100644 src/pkg/strconv/Makefile create mode 100644 src/pkg/strconv/extfloat.go create mode 100644 src/pkg/strconv/isprint.go create mode 100644 src/pkg/strconv/makeisprint.go delete mode 100644 src/pkg/strings/Makefile create mode 100644 src/pkg/strings/example_test.go create mode 100644 src/pkg/strings/export_test.go create mode 100644 src/pkg/strings/reader_test.go create mode 100644 src/pkg/strings/replace.go create mode 100644 src/pkg/strings/replace_test.go delete mode 100644 src/pkg/sync/Makefile delete mode 100644 src/pkg/sync/atomic/Makefile create mode 100644 src/pkg/sync/example_test.go create mode 100644 src/pkg/sync/export_test.go create mode 100644 src/pkg/sync/runtime.go create mode 100644 src/pkg/sync/runtime_sema_test.go delete mode 100644 src/pkg/syscall/Makefile create mode 100644 src/pkg/syscall/asm_netbsd_386.s create mode 100644 src/pkg/syscall/asm_netbsd_amd64.s create mode 100644 src/pkg/syscall/asm_openbsd_386.s create mode 100644 src/pkg/syscall/asm_openbsd_amd64.s create mode 100644 src/pkg/syscall/dll_windows.go create mode 100644 src/pkg/syscall/env_plan9.go create mode 100644 src/pkg/syscall/env_unix.go create mode 100644 src/pkg/syscall/env_windows.go create mode 100644 src/pkg/syscall/exec_bsd.go create mode 100644 src/pkg/syscall/exec_linux.go create mode 100755 src/pkg/syscall/mksysctl_openbsd.pl create mode 100755 src/pkg/syscall/mksysnum_netbsd.pl create mode 100755 src/pkg/syscall/mksysnum_openbsd.pl create mode 100644 src/pkg/syscall/route_netbsd.go create mode 100644 src/pkg/syscall/route_openbsd.go create mode 100644 src/pkg/syscall/security_windows.go delete mode 100644 src/pkg/syscall/syscall_386.go delete mode 100644 src/pkg/syscall/syscall_amd64.go delete mode 100644 src/pkg/syscall/syscall_arm.go create mode 100644 src/pkg/syscall/syscall_netbsd.go create mode 100644 src/pkg/syscall/syscall_netbsd_386.go create mode 100644 src/pkg/syscall/syscall_netbsd_amd64.go create mode 100644 src/pkg/syscall/syscall_openbsd.go create mode 100644 src/pkg/syscall/syscall_openbsd_386.go create mode 100644 src/pkg/syscall/syscall_openbsd_amd64.go delete mode 100644 src/pkg/syscall/types_darwin.c create mode 100644 src/pkg/syscall/types_darwin.go delete mode 100644 src/pkg/syscall/types_freebsd.c create mode 100644 src/pkg/syscall/types_freebsd.go delete mode 100644 src/pkg/syscall/types_linux.c create mode 100644 src/pkg/syscall/types_linux.go create mode 100644 src/pkg/syscall/types_netbsd.go create mode 100644 src/pkg/syscall/types_openbsd.go create mode 100644 src/pkg/syscall/zerrors_netbsd_386.go create mode 100644 src/pkg/syscall/zerrors_netbsd_amd64.go create mode 100644 src/pkg/syscall/zerrors_openbsd_386.go create mode 100644 src/pkg/syscall/zerrors_openbsd_amd64.go create mode 100644 src/pkg/syscall/zsyscall_netbsd_386.go create mode 100644 src/pkg/syscall/zsyscall_netbsd_amd64.go create mode 100644 src/pkg/syscall/zsyscall_openbsd_386.go create mode 100644 src/pkg/syscall/zsyscall_openbsd_amd64.go create mode 100644 src/pkg/syscall/zsysctl_openbsd.go create mode 100644 src/pkg/syscall/zsysnum_netbsd_386.go create mode 100644 src/pkg/syscall/zsysnum_netbsd_amd64.go create mode 100644 src/pkg/syscall/zsysnum_openbsd_386.go create mode 100644 src/pkg/syscall/zsysnum_openbsd_amd64.go create mode 100644 src/pkg/syscall/ztypes_netbsd_386.go create mode 100644 src/pkg/syscall/ztypes_netbsd_amd64.go create mode 100644 src/pkg/syscall/ztypes_openbsd_386.go create mode 100644 src/pkg/syscall/ztypes_openbsd_amd64.go delete mode 100644 src/pkg/syslog/Makefile delete mode 100644 src/pkg/syslog/syslog.go delete mode 100644 src/pkg/syslog/syslog_test.go delete mode 100644 src/pkg/syslog/syslog_unix.go delete mode 100644 src/pkg/tabwriter/Makefile delete mode 100644 src/pkg/tabwriter/tabwriter.go delete mode 100644 src/pkg/tabwriter/tabwriter_test.go delete mode 100644 src/pkg/template/Makefile delete mode 100644 src/pkg/template/doc.go delete mode 100644 src/pkg/template/exec.go delete mode 100644 src/pkg/template/exec_test.go delete mode 100644 src/pkg/template/funcs.go delete mode 100644 src/pkg/template/helper.go delete mode 100644 src/pkg/template/parse.go delete mode 100644 src/pkg/template/parse/Makefile delete mode 100644 src/pkg/template/parse/lex.go delete mode 100644 src/pkg/template/parse/lex_test.go delete mode 100644 src/pkg/template/parse/node.go delete mode 100644 src/pkg/template/parse/parse.go delete mode 100644 src/pkg/template/parse/parse_test.go delete mode 100644 src/pkg/template/parse/set.go delete mode 100644 src/pkg/template/set.go delete mode 100644 src/pkg/template/set_test.go delete mode 100644 src/pkg/template/testdata/file1.tmpl delete mode 100644 src/pkg/template/testdata/file2.tmpl delete mode 100644 src/pkg/template/testdata/tmpl1.tmpl delete mode 100644 src/pkg/template/testdata/tmpl2.tmpl delete mode 100644 src/pkg/testing/Makefile create mode 100644 src/pkg/testing/example.go delete mode 100644 src/pkg/testing/iotest/Makefile delete mode 100644 src/pkg/testing/quick/Makefile delete mode 100644 src/pkg/testing/script/Makefile delete mode 100644 src/pkg/testing/script/script.go delete mode 100644 src/pkg/testing/script/script_test.go create mode 100644 src/pkg/text/scanner/scanner.go create mode 100644 src/pkg/text/scanner/scanner_test.go create mode 100644 src/pkg/text/tabwriter/example_test.go create mode 100644 src/pkg/text/tabwriter/tabwriter.go create mode 100644 src/pkg/text/tabwriter/tabwriter_test.go create mode 100644 src/pkg/text/template/doc.go create mode 100644 src/pkg/text/template/example_test.go create mode 100644 src/pkg/text/template/examplefiles_test.go create mode 100644 src/pkg/text/template/examplefunc_test.go create mode 100644 src/pkg/text/template/exec.go create mode 100644 src/pkg/text/template/exec_test.go create mode 100644 src/pkg/text/template/funcs.go create mode 100644 src/pkg/text/template/helper.go create mode 100644 src/pkg/text/template/multi_test.go create mode 100644 src/pkg/text/template/parse/lex.go create mode 100644 src/pkg/text/template/parse/lex_test.go create mode 100644 src/pkg/text/template/parse/node.go create mode 100644 src/pkg/text/template/parse/parse.go create mode 100644 src/pkg/text/template/parse/parse_test.go create mode 100644 src/pkg/text/template/template.go create mode 100644 src/pkg/text/template/testdata/file1.tmpl create mode 100644 src/pkg/text/template/testdata/file2.tmpl create mode 100644 src/pkg/text/template/testdata/tmpl1.tmpl create mode 100644 src/pkg/text/template/testdata/tmpl2.tmpl delete mode 100644 src/pkg/time/Makefile create mode 100644 src/pkg/time/example_test.go create mode 100644 src/pkg/time/internal_test.go delete mode 100644 src/pkg/time/sys.go delete mode 100644 src/pkg/time/sys_posix.go create mode 100644 src/pkg/time/sys_unix.go create mode 100644 src/pkg/time/sys_windows.go create mode 100644 src/pkg/time/zoneinfo.go delete mode 100644 src/pkg/time/zoneinfo_posix.go create mode 100644 src/pkg/time/zoneinfo_read.go delete mode 100644 src/pkg/try/Makefile delete mode 100644 src/pkg/try/try.go delete mode 100644 src/pkg/try/try_test.go create mode 100644 src/pkg/unicode/utf16/export_test.go create mode 100644 src/pkg/unicode/utf16/utf16.go create mode 100644 src/pkg/unicode/utf16/utf16_test.go create mode 100644 src/pkg/unicode/utf8/utf8.go create mode 100644 src/pkg/unicode/utf8/utf8_test.go delete mode 100644 src/pkg/url/Makefile delete mode 100644 src/pkg/url/url.go delete mode 100644 src/pkg/url/url_test.go delete mode 100644 src/pkg/utf16/Makefile delete mode 100644 src/pkg/utf16/utf16.go delete mode 100644 src/pkg/utf16/utf16_test.go delete mode 100644 src/pkg/utf8/Makefile delete mode 100644 src/pkg/utf8/string.go delete mode 100644 src/pkg/utf8/string_test.go delete mode 100644 src/pkg/utf8/utf8.go delete mode 100644 src/pkg/utf8/utf8_test.go delete mode 100644 src/pkg/websocket/Makefile delete mode 100644 src/pkg/websocket/client.go delete mode 100644 src/pkg/websocket/server.go delete mode 100644 src/pkg/websocket/websocket.go delete mode 100644 src/pkg/websocket/websocket_test.go delete mode 100644 src/pkg/xml/Makefile delete mode 100644 src/pkg/xml/atom_test.go delete mode 100644 src/pkg/xml/embed_test.go delete mode 100644 src/pkg/xml/marshal.go delete mode 100644 src/pkg/xml/marshal_test.go delete mode 100644 src/pkg/xml/read.go delete mode 100644 src/pkg/xml/read_test.go delete mode 100644 src/pkg/xml/xml.go delete mode 100644 src/pkg/xml/xml_test.go (limited to 'src/pkg') diff --git a/src/pkg/Makefile b/src/pkg/Makefile deleted file mode 100644 index 44d7f4367..000000000 --- a/src/pkg/Makefile +++ /dev/null @@ -1,311 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -# After editing the DIRS= list or adding imports to any Go files -# in any of those directories, run: -# -# ./deps.bash -# -# to rebuild the dependency information in Make.deps. - -include ../Make.inc - -all: install - -DIRS=\ - archive/tar\ - archive/zip\ - asn1\ - big\ - bufio\ - bytes\ - cmath\ - compress/bzip2\ - compress/flate\ - compress/gzip\ - compress/lzw \ - compress/zlib\ - container/heap\ - container/list\ - container/ring\ - container/vector\ - crypto\ - crypto/aes\ - crypto/blowfish\ - crypto/cast5\ - crypto/cipher\ - crypto/des\ - crypto/dsa\ - crypto/ecdsa\ - crypto/elliptic\ - crypto/hmac\ - crypto/md4\ - crypto/md5\ - crypto/ocsp\ - crypto/openpgp\ - crypto/openpgp/armor\ - crypto/openpgp/elgamal\ - crypto/openpgp/error\ - crypto/openpgp/packet\ - crypto/openpgp/s2k\ - crypto/rand\ - crypto/rc4\ - crypto/ripemd160\ - crypto/rsa\ - crypto/sha1\ - crypto/sha256\ - crypto/sha512\ - crypto/subtle\ - crypto/tls\ - crypto/twofish\ - crypto/x509\ - crypto/x509/pkix\ - crypto/xtea\ - csv\ - debug/dwarf\ - debug/macho\ - debug/elf\ - debug/gosym\ - debug/pe\ - ebnf\ - encoding/ascii85\ - encoding/base32\ - encoding/base64\ - encoding/binary\ - encoding/git85\ - encoding/hex\ - encoding/pem\ - exec\ - exp/gui\ - exp/gui/x11\ - exp/norm\ - exp/regexp/syntax\ - exp/template/html\ - expvar\ - flag\ - fmt\ - go/ast\ - go/build\ - go/doc\ - go/parser\ - go/printer\ - go/scanner\ - go/token\ - go/typechecker\ - go/types\ - gob\ - hash\ - hash/adler32\ - hash/crc32\ - hash/crc64\ - hash/fnv\ - html\ - http\ - http/cgi\ - http/fcgi\ - http/pprof\ - http/httptest\ - http/spdy\ - image\ - image/bmp\ - image/draw\ - image/gif\ - image/jpeg\ - image/png\ - image/tiff\ - image/ycbcr\ - index/suffixarray\ - io\ - io/ioutil\ - json\ - log\ - mail\ - math\ - mime\ - mime/multipart\ - net\ - net/dict\ - net/textproto\ - netchan\ - old/template\ - os\ - os/signal\ - os/user\ - patch\ - path\ - path/filepath\ - rand\ - reflect\ - regexp\ - rpc\ - rpc/jsonrpc\ - runtime\ - runtime/cgo\ - runtime/debug\ - runtime/pprof\ - scanner\ - smtp\ - sort\ - strconv\ - strings\ - sync\ - sync/atomic\ - syscall\ - syslog\ - tabwriter\ - template\ - template/parse\ - testing\ - testing/iotest\ - testing/quick\ - testing/script\ - time\ - try\ - unicode\ - url\ - utf16\ - utf8\ - websocket\ - xml\ - ../cmd/cgo\ - ../cmd/ebnflint\ - ../cmd/godoc\ - ../cmd/gofix\ - ../cmd/gofmt\ - ../cmd/goinstall\ - ../cmd/gotest\ - ../cmd/gotype\ - ../cmd/govet\ - ../cmd/goyacc\ - ../cmd/hgpatch\ - -ifeq ($(GOOS),linux) -DIRS+=\ - os/inotify\ - -endif - -ifeq ($(GOOS),plan9) -NOPLAN9BUILD=\ - crypto/tls\ - exp/gui/x11\ - expvar\ - http\ - http/cgi\ - http/fcgi\ - http/httptest\ - http/pprof\ - http/spdy\ - mail\ - mime/multipart\ - net\ - net/dict\ - net/textproto\ - netchan\ - os/signal\ - rpc\ - rpc/jsonrpc\ - smtp\ - syslog\ - websocket\ - ../cmd/godoc\ - ../cmd/goinstall\ - -DIRS:=$(filter-out $(NOPLAN9BUILD),$(DIRS)) -endif - -NOTEST+=\ - crypto\ - crypto/openpgp/error\ - crypto/x509/pkix\ - exp/gui\ - exp/gui/x11\ - go/ast\ - go/doc\ - go/token\ - hash\ - http/pprof\ - http/httptest\ - image/bmp\ - image/gif\ - net/dict\ - rand\ - runtime/cgo\ - syscall\ - testing\ - testing/iotest\ - try\ - ../cmd/cgo\ - ../cmd/ebnflint\ - ../cmd/godoc\ - ../cmd/gotest\ - ../cmd/goyacc\ - ../cmd/hgpatch\ - -NOBENCH+=\ - container/vector\ - -# Disable tests that windows cannot run yet. -ifeq ($(GOOS),windows) -NOTEST+=os/signal # no signals -NOTEST+=syslog # no network -NOTEST+=time # no syscall.Kill, syscall.SIGCHLD for sleep tests -endif - -TEST=\ - $(filter-out $(NOTEST),$(DIRS)) - -BENCH=\ - $(filter-out $(NOBENCH),$(TEST)) - -clean.dirs: $(addsuffix .clean, $(DIRS)) -install.dirs: $(addsuffix .install, $(DIRS)) -nuke.dirs: $(addsuffix .nuke, $(DIRS)) -test.dirs: $(addsuffix .test, $(TEST)) -testshort.dirs: $(addsuffix .testshort, $(TEST)) -bench.dirs: $(addsuffix .bench, $(BENCH)) - -%.clean: - +$(MAKE) -C $* clean - -%.install: - +@echo install $* - +@$(MAKE) -C $* install.clean >$*/build.out 2>&1 || (echo INSTALL FAIL $*; cat $*/build.out; exit 1) - -%.nuke: - +$(MAKE) -C $* nuke - -%.test: - +@echo test $* - +@$(MAKE) -C $* test.clean >$*/test.out 2>&1 || (echo TEST FAIL $*; cat $*/test.out; exit 1) - -%.testshort: - +@echo test $* - +@$(MAKE) -C $* testshort.clean >$*/test.out 2>&1 || (echo TEST FAIL $*; cat $*/test.out; exit 1) - -%.bench: - +$(MAKE) -C $* bench - -clean: clean.dirs - -install: install.dirs - -test: test.dirs - -testshort: testshort.dirs - -bench: bench.dirs ../../test/garbage.bench - -nuke: nuke.dirs - rm -rf "$(GOROOT)"/pkg/* - -deps: - ./deps.bash - -echo-dirs: - @echo $(DIRS) - --include Make.deps - -runtime/cgo.install: ../cmd/cgo.install diff --git a/src/pkg/archive/tar/Makefile b/src/pkg/archive/tar/Makefile deleted file mode 100644 index 8897e883e..000000000 --- a/src/pkg/archive/tar/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=archive/tar -GOFILES=\ - common.go\ - reader.go\ - writer.go\ - -include ../../../Make.pkg diff --git a/src/pkg/archive/tar/common.go b/src/pkg/archive/tar/common.go index 528858765..fc7a40923 100644 --- a/src/pkg/archive/tar/common.go +++ b/src/pkg/archive/tar/common.go @@ -11,40 +11,42 @@ // http://www.gnu.org/software/tar/manual/html_node/Standard.html package tar +import "time" + const ( blockSize = 512 // Types - TypeReg = '0' - TypeRegA = '\x00' - TypeLink = '1' - TypeSymlink = '2' - TypeChar = '3' - TypeBlock = '4' - TypeDir = '5' - TypeFifo = '6' - TypeCont = '7' - TypeXHeader = 'x' - TypeXGlobalHeader = 'g' + TypeReg = '0' // regular file + TypeRegA = '\x00' // regular file + TypeLink = '1' // hard link + TypeSymlink = '2' // symbolic link + TypeChar = '3' // character device node + TypeBlock = '4' // block device node + TypeDir = '5' // directory + TypeFifo = '6' // fifo node + TypeCont = '7' // reserved + TypeXHeader = 'x' // extended header + TypeXGlobalHeader = 'g' // global extended header ) // A Header represents a single header in a tar archive. // Some fields may not be populated. type Header struct { - Name string - Mode int64 - Uid int - Gid int - Size int64 - Mtime int64 - Typeflag byte - Linkname string - Uname string - Gname string - Devmajor int64 - Devminor int64 - Atime int64 - Ctime int64 + Name string // name of header file entry + Mode int64 // permission and mode bits + Uid int // user id of owner + Gid int // group id of owner + Size int64 // length in bytes + ModTime time.Time // modified time + Typeflag byte // type of header entry + Linkname string // target name of link + Uname string // user name of owner + Gname string // group name of owner + Devmajor int64 // major number of character or block device + Devminor int64 // minor number of character or block device + AccessTime time.Time // access time + ChangeTime time.Time // status change time } var zeroBlock = make([]byte, blockSize) diff --git a/src/pkg/archive/tar/reader.go b/src/pkg/archive/tar/reader.go index 45d95c3df..1b40af812 100644 --- a/src/pkg/archive/tar/reader.go +++ b/src/pkg/archive/tar/reader.go @@ -9,14 +9,16 @@ package tar import ( "bytes" + "errors" "io" "io/ioutil" "os" "strconv" + "time" ) var ( - HeaderError = os.NewError("invalid tar header") + ErrHeader = errors.New("archive/tar: invalid tar header") ) // A Reader provides sequential access to the contents of a tar archive. @@ -28,7 +30,7 @@ var ( // tr := tar.NewReader(r) // for { // hdr, err := tr.Next() -// if err == os.EOF { +// if err == io.EOF { // // end of tar archive // break // } @@ -39,7 +41,7 @@ var ( // } type Reader struct { r io.Reader - err os.Error + err error nb int64 // number of unread bytes for current file entry pad int64 // amount of padding (ignored) after current file entry } @@ -48,7 +50,7 @@ type Reader struct { func NewReader(r io.Reader) *Reader { return &Reader{r: r} } // Next advances to the next entry in the tar archive. -func (tr *Reader) Next() (*Header, os.Error) { +func (tr *Reader) Next() (*Header, error) { var hdr *Header if tr.err == nil { tr.skipUnread() @@ -78,7 +80,7 @@ func (tr *Reader) octal(b []byte) int64 { for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') { b = b[0 : len(b)-1] } - x, err := strconv.Btoui64(cString(b), 8) + x, err := strconv.ParseUint(cString(b), 8, 64) if err != nil { tr.err = err } @@ -94,7 +96,7 @@ func (tr *Reader) skipUnread() { return } } - _, tr.err = io.Copyn(ioutil.Discard, tr.r, nr) + _, tr.err = io.CopyN(ioutil.Discard, tr.r, nr) } func (tr *Reader) verifyChecksum(header []byte) bool { @@ -119,15 +121,15 @@ func (tr *Reader) readHeader() *Header { return nil } if bytes.Equal(header, zeroBlock[0:blockSize]) { - tr.err = os.EOF + tr.err = io.EOF } else { - tr.err = HeaderError // zero block and then non-zero block + tr.err = ErrHeader // zero block and then non-zero block } return nil } if !tr.verifyChecksum(header) { - tr.err = HeaderError + tr.err = ErrHeader return nil } @@ -140,7 +142,7 @@ func (tr *Reader) readHeader() *Header { hdr.Uid = int(tr.octal(s.next(8))) hdr.Gid = int(tr.octal(s.next(8))) hdr.Size = tr.octal(s.next(12)) - hdr.Mtime = tr.octal(s.next(12)) + hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0) s.next(8) // chksum hdr.Typeflag = s.next(1)[0] hdr.Linkname = cString(s.next(100)) @@ -177,8 +179,8 @@ func (tr *Reader) readHeader() *Header { prefix = cString(s.next(155)) case "star": prefix = cString(s.next(131)) - hdr.Atime = tr.octal(s.next(12)) - hdr.Ctime = tr.octal(s.next(12)) + hdr.AccessTime = time.Unix(tr.octal(s.next(12)), 0) + hdr.ChangeTime = time.Unix(tr.octal(s.next(12)), 0) } if len(prefix) > 0 { hdr.Name = prefix + "/" + hdr.Name @@ -186,7 +188,7 @@ func (tr *Reader) readHeader() *Header { } if tr.err != nil { - tr.err = HeaderError + tr.err = ErrHeader return nil } @@ -199,12 +201,12 @@ func (tr *Reader) readHeader() *Header { } // Read reads from the current entry in the tar archive. -// It returns 0, os.EOF when it reaches the end of that entry, +// It returns 0, io.EOF when it reaches the end of that entry, // until Next is called to advance to the next entry. -func (tr *Reader) Read(b []byte) (n int, err os.Error) { +func (tr *Reader) Read(b []byte) (n int, err error) { if tr.nb == 0 { // file consumed - return 0, os.EOF + return 0, io.EOF } if int64(len(b)) > tr.nb { @@ -213,7 +215,7 @@ func (tr *Reader) Read(b []byte) (n int, err os.Error) { n, err = tr.r.Read(b) tr.nb -= int64(n) - if err == os.EOF && tr.nb > 0 { + if err == io.EOF && tr.nb > 0 { err = io.ErrUnexpectedEOF } tr.err = err diff --git a/src/pkg/archive/tar/reader_test.go b/src/pkg/archive/tar/reader_test.go index f473c900f..0a8646c39 100644 --- a/src/pkg/archive/tar/reader_test.go +++ b/src/pkg/archive/tar/reader_test.go @@ -10,8 +10,8 @@ import ( "fmt" "io" "os" - "reflect" "testing" + "time" ) type untarTest struct { @@ -23,24 +23,24 @@ type untarTest struct { var gnuTarTest = &untarTest{ file: "testdata/gnu.tar", headers: []*Header{ - &Header{ + { Name: "small.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 5, - Mtime: 1244428340, + ModTime: time.Unix(1244428340, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", }, - &Header{ + { Name: "small2.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 11, - Mtime: 1244436044, + ModTime: time.Unix(1244436044, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", @@ -54,56 +54,56 @@ var gnuTarTest = &untarTest{ var untarTests = []*untarTest{ gnuTarTest, - &untarTest{ + { file: "testdata/star.tar", headers: []*Header{ - &Header{ - Name: "small.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 5, - Mtime: 1244592783, - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - Atime: 1244592783, - Ctime: 1244592783, + { + Name: "small.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 5, + ModTime: time.Unix(1244592783, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + AccessTime: time.Unix(1244592783, 0), + ChangeTime: time.Unix(1244592783, 0), }, - &Header{ - Name: "small2.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 11, - Mtime: 1244592783, - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - Atime: 1244592783, - Ctime: 1244592783, + { + Name: "small2.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 11, + ModTime: time.Unix(1244592783, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + AccessTime: time.Unix(1244592783, 0), + ChangeTime: time.Unix(1244592783, 0), }, }, }, - &untarTest{ + { file: "testdata/v7.tar", headers: []*Header{ - &Header{ + { Name: "small.txt", Mode: 0444, Uid: 73025, Gid: 5000, Size: 5, - Mtime: 1244593104, + ModTime: time.Unix(1244593104, 0), Typeflag: '\x00', }, - &Header{ + { Name: "small2.txt", Mode: 0444, Uid: 73025, Gid: 5000, Size: 11, - Mtime: 1244593104, + ModTime: time.Unix(1244593104, 0), Typeflag: '\x00', }, }, @@ -126,13 +126,13 @@ testLoop: f.Close() continue testLoop } - if !reflect.DeepEqual(hdr, header) { + if *hdr != *header { t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v", i, j, *hdr, *header) } } hdr, err := tr.Next() - if err == os.EOF { + if err == io.EOF { break } if hdr != nil || err != nil { @@ -195,12 +195,12 @@ func TestIncrementalRead(t *testing.T) { // loop over all files for ; ; nread++ { hdr, err := tr.Next() - if hdr == nil || err == os.EOF { + if hdr == nil || err == io.EOF { break } // check the header - if !reflect.DeepEqual(hdr, headers[nread]) { + if *hdr != *headers[nread] { t.Errorf("Incorrect header:\nhave %+v\nwant %+v", *hdr, headers[nread]) } @@ -211,7 +211,7 @@ func TestIncrementalRead(t *testing.T) { rdbuf := make([]uint8, 8) for { nr, err := tr.Read(rdbuf) - if err == os.EOF { + if err == io.EOF { break } if err != nil { @@ -221,7 +221,7 @@ func TestIncrementalRead(t *testing.T) { h.Write(rdbuf[0:nr]) } // verify checksum - have := fmt.Sprintf("%x", h.Sum()) + have := fmt.Sprintf("%x", h.Sum(nil)) want := cksums[nread] if want != have { t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want) @@ -240,31 +240,20 @@ func TestNonSeekable(t *testing.T) { } defer f.Close() - // pipe the data in - r, w, err := os.Pipe() - if err != nil { - t.Fatalf("Unexpected error %s", err) + type readerOnly struct { + io.Reader } - go func() { - rdbuf := make([]uint8, 1<<16) - for { - nr, err := f.Read(rdbuf) - w.Write(rdbuf[0:nr]) - if err == os.EOF { - break - } - } - w.Close() - }() - - tr := NewReader(r) + tr := NewReader(readerOnly{f}) nread := 0 for ; ; nread++ { - hdr, err := tr.Next() - if hdr == nil || err == os.EOF { + _, err := tr.Next() + if err == io.EOF { break } + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } } if nread != len(test.headers) { diff --git a/src/pkg/archive/tar/testdata/writer.tar b/src/pkg/archive/tar/testdata/writer.tar index 0358f91b9..e6d816ad0 100644 Binary files a/src/pkg/archive/tar/testdata/writer.tar and b/src/pkg/archive/tar/testdata/writer.tar differ diff --git a/src/pkg/archive/tar/writer.go b/src/pkg/archive/tar/writer.go index 8673bad31..b2b7a58a1 100644 --- a/src/pkg/archive/tar/writer.go +++ b/src/pkg/archive/tar/writer.go @@ -5,18 +5,19 @@ package tar // TODO(dsymonds): -// - catch more errors (no first header, write after close, etc.) +// - catch more errors (no first header, etc.) import ( + "errors" + "fmt" "io" - "os" "strconv" ) var ( - ErrWriteTooLong = os.NewError("write too long") - ErrFieldTooLong = os.NewError("header field too long") - ErrWriteAfterClose = os.NewError("write after close") + ErrWriteTooLong = errors.New("archive/tar: write too long") + ErrFieldTooLong = errors.New("archive/tar: header field too long") + ErrWriteAfterClose = errors.New("archive/tar: write after close") ) // A Writer provides sequential writing of a tar archive in POSIX.1 format. @@ -36,7 +37,7 @@ var ( // tw.Close() type Writer struct { w io.Writer - err os.Error + err error nb int64 // number of unwritten bytes for current file entry pad int64 // amount of padding to write after current file entry closed bool @@ -47,7 +48,12 @@ type Writer struct { func NewWriter(w io.Writer) *Writer { return &Writer{w: w} } // Flush finishes writing the current file (optional). -func (tw *Writer) Flush() os.Error { +func (tw *Writer) Flush() error { + if tw.nb > 0 { + tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb) + return tw.err + } + n := tw.nb + tw.pad for n > 0 && tw.err == nil { nr := n @@ -79,7 +85,7 @@ func (tw *Writer) cString(b []byte, s string) { // Encode x as an octal ASCII string and write it into b with leading zeros. func (tw *Writer) octal(b []byte, x int64) { - s := strconv.Itob64(x, 8) + s := strconv.FormatInt(x, 8) // leading zeros, but leave room for a NUL. for len(s)+1 < len(b) { s = "0" + s @@ -90,7 +96,7 @@ func (tw *Writer) octal(b []byte, x int64) { // Write x into b, either as octal or as binary (GNUtar/star extension). func (tw *Writer) numeric(b []byte, x int64) { // Try octal first. - s := strconv.Itob64(x, 8) + s := strconv.FormatInt(x, 8) if len(s) < len(b) { tw.octal(b, x) return @@ -107,7 +113,7 @@ func (tw *Writer) numeric(b []byte, x int64) { // WriteHeader writes hdr and prepares to accept the file's contents. // WriteHeader calls Flush if it is not the first header. // Calling after a Close will return ErrWriteAfterClose. -func (tw *Writer) WriteHeader(hdr *Header) os.Error { +func (tw *Writer) WriteHeader(hdr *Header) error { if tw.closed { return ErrWriteAfterClose } @@ -127,19 +133,19 @@ func (tw *Writer) WriteHeader(hdr *Header) os.Error { // TODO(dsymonds): handle names longer than 100 chars copy(s.next(100), []byte(hdr.Name)) - tw.octal(s.next(8), hdr.Mode) // 100:108 - tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 - tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 - tw.numeric(s.next(12), hdr.Size) // 124:136 - tw.numeric(s.next(12), hdr.Mtime) // 136:148 - s.next(8) // chksum (148:156) - s.next(1)[0] = hdr.Typeflag // 156:157 - s.next(100) // linkname (157:257) - copy(s.next(8), []byte("ustar\x0000")) // 257:265 - tw.cString(s.next(32), hdr.Uname) // 265:297 - tw.cString(s.next(32), hdr.Gname) // 297:329 - tw.numeric(s.next(8), hdr.Devmajor) // 329:337 - tw.numeric(s.next(8), hdr.Devminor) // 337:345 + tw.octal(s.next(8), hdr.Mode) // 100:108 + tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 + tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 + tw.numeric(s.next(12), hdr.Size) // 124:136 + tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148 + s.next(8) // chksum (148:156) + s.next(1)[0] = hdr.Typeflag // 156:157 + tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) + copy(s.next(8), []byte("ustar\x0000")) // 257:265 + tw.cString(s.next(32), hdr.Uname) // 265:297 + tw.cString(s.next(32), hdr.Gname) // 297:329 + tw.numeric(s.next(8), hdr.Devmajor) // 329:337 + tw.numeric(s.next(8), hdr.Devminor) // 337:345 // Use the GNU magic instead of POSIX magic if we used any GNU extensions. if tw.usedBinary { @@ -165,7 +171,7 @@ func (tw *Writer) WriteHeader(hdr *Header) os.Error { // Write writes to the current entry in the tar archive. // Write returns the error ErrWriteTooLong if more than // hdr.Size bytes are written after WriteHeader. -func (tw *Writer) Write(b []byte) (n int, err os.Error) { +func (tw *Writer) Write(b []byte) (n int, err error) { if tw.closed { err = ErrWriteTooLong return @@ -187,12 +193,15 @@ func (tw *Writer) Write(b []byte) (n int, err os.Error) { // Close closes the tar archive, flushing any unwritten // data to the underlying writer. -func (tw *Writer) Close() os.Error { +func (tw *Writer) Close() error { if tw.err != nil || tw.closed { return tw.err } tw.Flush() tw.closed = true + if tw.err != nil { + return tw.err + } // trailer: two zero blocks for i := 0; i < 2; i++ { diff --git a/src/pkg/archive/tar/writer_test.go b/src/pkg/archive/tar/writer_test.go index 838cb7e1f..a214e57b9 100644 --- a/src/pkg/archive/tar/writer_test.go +++ b/src/pkg/archive/tar/writer_test.go @@ -9,8 +9,10 @@ import ( "fmt" "io" "io/ioutil" + "strings" "testing" "testing/iotest" + "time" ) type writerTestEntry struct { @@ -24,58 +26,78 @@ type writerTest struct { } var writerTests = []*writerTest{ - &writerTest{ + // The writer test file was produced with this command: + // tar (GNU tar) 1.26 + // ln -s small.txt link.txt + // tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt + { file: "testdata/writer.tar", entries: []*writerTestEntry{ - &writerTestEntry{ + { header: &Header{ Name: "small.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 5, - Mtime: 1246508266, + ModTime: time.Unix(1246508266, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", }, contents: "Kilts", }, - &writerTestEntry{ + { header: &Header{ Name: "small2.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 11, - Mtime: 1245217492, + ModTime: time.Unix(1245217492, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", }, contents: "Google.com\n", }, + { + header: &Header{ + Name: "link.txt", + Mode: 0777, + Uid: 1000, + Gid: 1000, + Size: 0, + ModTime: time.Unix(1314603082, 0), + Typeflag: '2', + Linkname: "small.txt", + Uname: "strings", + Gname: "strings", + }, + // no contents + }, }, }, // The truncated test file was produced using these commands: // dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt // tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar - &writerTest{ + { file: "testdata/writer-big.tar", entries: []*writerTestEntry{ - &writerTestEntry{ + { header: &Header{ Name: "tmp/16gig.txt", Mode: 0640, Uid: 73025, Gid: 5000, Size: 16 << 30, - Mtime: 1254699560, + ModTime: time.Unix(1254699560, 0), Typeflag: '0', Uname: "dsymonds", Gname: "eng", }, - // no contents + // fake contents + contents: strings.Repeat("\x00", 4<<10), }, }, }, @@ -130,7 +152,9 @@ testLoop: buf := new(bytes.Buffer) tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB + big := false for j, entry := range test.entries { + big = big || entry.header.Size > 1<<10 if err := tw.WriteHeader(entry.header); err != nil { t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err) continue testLoop @@ -140,7 +164,8 @@ testLoop: continue testLoop } } - if err := tw.Close(); err != nil { + // Only interested in Close failures for the small tests. + if err := tw.Close(); err != nil && !big { t.Errorf("test %d: Failed closing archive: %v", i, err) continue testLoop } diff --git a/src/pkg/archive/zip/Makefile b/src/pkg/archive/zip/Makefile deleted file mode 100644 index 9071690f0..000000000 --- a/src/pkg/archive/zip/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=archive/zip -GOFILES=\ - reader.go\ - struct.go\ - writer.go\ - -include ../../../Make.pkg diff --git a/src/pkg/archive/zip/example_test.go b/src/pkg/archive/zip/example_test.go new file mode 100644 index 000000000..c2ed9e79c --- /dev/null +++ b/src/pkg/archive/zip/example_test.go @@ -0,0 +1,75 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zip_test + +import ( + "archive/zip" + "bytes" + "fmt" + "io" + "log" + "os" +) + +func ExampleWriter() { + // Create a buffer to write our archive to. + buf := new(bytes.Buffer) + + // Create a new zip archive. + w := zip.NewWriter(buf) + + // Add some files to the archive. + var files = []struct { + Name, Body string + }{ + {"readme.txt", "This archive contains some text files."}, + {"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"}, + {"todo.txt", "Get animal handling licence.\nWrite more examples."}, + } + for _, file := range files { + f, err := w.Create(file.Name) + if err != nil { + log.Fatal(err) + } + _, err = f.Write([]byte(file.Body)) + if err != nil { + log.Fatal(err) + } + } + + // Make sure to check the error on Close. + err := w.Close() + if err != nil { + log.Fatal(err) + } +} + +func ExampleReader() { + // Open a zip archive for reading. + r, err := zip.OpenReader("testdata/readme.zip") + if err != nil { + log.Fatal(err) + } + defer r.Close() + + // Iterate through the files in the archive, + // printing some of their contents. + for _, f := range r.File { + fmt.Printf("Contents of %s:\n", f.Name) + rc, err := f.Open() + if err != nil { + log.Fatal(err) + } + _, err = io.CopyN(os.Stdout, rc, 68) + if err != nil { + log.Fatal(err) + } + rc.Close() + fmt.Println() + } + // Output: + // Contents of README: + // This is the source code repository for the Go programming language. +} diff --git a/src/pkg/archive/zip/reader.go b/src/pkg/archive/zip/reader.go index f92f9297a..ddd507538 100644 --- a/src/pkg/archive/zip/reader.go +++ b/src/pkg/archive/zip/reader.go @@ -7,18 +7,19 @@ package zip import ( "bufio" "compress/flate" + "encoding/binary" + "errors" "hash" "hash/crc32" - "encoding/binary" "io" "io/ioutil" "os" ) var ( - FormatError = os.NewError("zip: not a valid zip file") - UnsupportedMethod = os.NewError("zip: unsupported compression algorithm") - ChecksumError = os.NewError("zip: checksum error") + ErrFormat = errors.New("zip: not a valid zip file") + ErrAlgorithm = errors.New("zip: unsupported compression algorithm") + ErrChecksum = errors.New("zip: checksum error") ) type Reader struct { @@ -44,7 +45,7 @@ func (f *File) hasDataDescriptor() bool { } // OpenReader will open the Zip file specified by name and return a ReadCloser. -func OpenReader(name string) (*ReadCloser, os.Error) { +func OpenReader(name string) (*ReadCloser, error) { f, err := os.Open(name) if err != nil { return nil, err @@ -55,16 +56,17 @@ func OpenReader(name string) (*ReadCloser, os.Error) { return nil, err } r := new(ReadCloser) - if err := r.init(f, fi.Size); err != nil { + if err := r.init(f, fi.Size()); err != nil { f.Close() return nil, err } + r.f = f return r, nil } // NewReader returns a new Reader reading from r, which is assumed to // have the given size in bytes. -func NewReader(r io.ReaderAt, size int64) (*Reader, os.Error) { +func NewReader(r io.ReaderAt, size int64) (*Reader, error) { zr := new(Reader) if err := zr.init(r, size); err != nil { return nil, err @@ -72,7 +74,7 @@ func NewReader(r io.ReaderAt, size int64) (*Reader, os.Error) { return zr, nil } -func (z *Reader) init(r io.ReaderAt, size int64) os.Error { +func (z *Reader) init(r io.ReaderAt, size int64) error { end, err := readDirectoryEnd(r, size) if err != nil { return err @@ -88,12 +90,12 @@ func (z *Reader) init(r io.ReaderAt, size int64) os.Error { // The count of files inside a zip is truncated to fit in a uint16. // Gloss over this by reading headers until we encounter - // a bad one, and then only report a FormatError or UnexpectedEOF if + // a bad one, and then only report a ErrFormat or UnexpectedEOF if // the file count modulo 65536 is incorrect. for { f := &File{zipr: r, zipsize: size} err = readDirectoryHeader(f, buf) - if err == FormatError || err == io.ErrUnexpectedEOF { + if err == ErrFormat || err == io.ErrUnexpectedEOF { break } if err != nil { @@ -110,22 +112,18 @@ func (z *Reader) init(r io.ReaderAt, size int64) os.Error { } // Close closes the Zip file, rendering it unusable for I/O. -func (rc *ReadCloser) Close() os.Error { +func (rc *ReadCloser) Close() error { return rc.f.Close() } // Open returns a ReadCloser that provides access to the File's contents. -// It is safe to Open and Read from files concurrently. -func (f *File) Open() (rc io.ReadCloser, err os.Error) { +// Multiple files may be read concurrently. +func (f *File) Open() (rc io.ReadCloser, err error) { bodyOffset, err := f.findBodyOffset() if err != nil { return } size := int64(f.CompressedSize) - if size == 0 && f.hasDataDescriptor() { - // permit SectionReader to see the rest of the file - size = f.zipsize - (f.headerOffset + bodyOffset) - } r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size) switch f.Method { case Store: // (no compression) @@ -133,11 +131,14 @@ func (f *File) Open() (rc io.ReadCloser, err os.Error) { case Deflate: rc = flate.NewReader(r) default: - err = UnsupportedMethod + err = ErrAlgorithm + return } - if rc != nil { - rc = &checksumReader{rc, crc32.NewIEEE(), f, r} + var desr io.Reader + if f.hasDataDescriptor() { + desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) } + rc = &checksumReader{rc, crc32.NewIEEE(), f, desr, nil} return } @@ -145,101 +146,86 @@ type checksumReader struct { rc io.ReadCloser hash hash.Hash32 f *File - zipr io.Reader // for reading the data descriptor + desr io.Reader // if non-nil, where to read the data descriptor + err error // sticky error } -func (r *checksumReader) Read(b []byte) (n int, err os.Error) { +func (r *checksumReader) Read(b []byte) (n int, err error) { + if r.err != nil { + return 0, r.err + } n, err = r.rc.Read(b) r.hash.Write(b[:n]) - if err != os.EOF { + if err == nil { return } - if r.f.hasDataDescriptor() { - if err = readDataDescriptor(r.zipr, r.f); err != nil { - return + if err == io.EOF { + if r.desr != nil { + if err1 := readDataDescriptor(r.desr, r.f); err1 != nil { + err = err1 + } else if r.hash.Sum32() != r.f.CRC32 { + err = ErrChecksum + } + } else { + // If there's not a data descriptor, we still compare + // the CRC32 of what we've read against the file header + // or TOC's CRC32, if it seems like it was set. + if r.f.CRC32 != 0 && r.hash.Sum32() != r.f.CRC32 { + err = ErrChecksum + } } } - if r.hash.Sum32() != r.f.CRC32 { - err = ChecksumError - } + r.err = err return } -func (r *checksumReader) Close() os.Error { return r.rc.Close() } - -func readFileHeader(f *File, r io.Reader) os.Error { - var b [fileHeaderLen]byte - if _, err := io.ReadFull(r, b[:]); err != nil { - return err - } - c := binary.LittleEndian - if sig := c.Uint32(b[:4]); sig != fileHeaderSignature { - return FormatError - } - f.ReaderVersion = c.Uint16(b[4:6]) - f.Flags = c.Uint16(b[6:8]) - f.Method = c.Uint16(b[8:10]) - f.ModifiedTime = c.Uint16(b[10:12]) - f.ModifiedDate = c.Uint16(b[12:14]) - f.CRC32 = c.Uint32(b[14:18]) - f.CompressedSize = c.Uint32(b[18:22]) - f.UncompressedSize = c.Uint32(b[22:26]) - filenameLen := int(c.Uint16(b[26:28])) - extraLen := int(c.Uint16(b[28:30])) - d := make([]byte, filenameLen+extraLen) - if _, err := io.ReadFull(r, d); err != nil { - return err - } - f.Name = string(d[:filenameLen]) - f.Extra = d[filenameLen:] - return nil -} +func (r *checksumReader) Close() error { return r.rc.Close() } // findBodyOffset does the minimum work to verify the file has a header // and returns the file body offset. -func (f *File) findBodyOffset() (int64, os.Error) { +func (f *File) findBodyOffset() (int64, error) { r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset) - var b [fileHeaderLen]byte - if _, err := io.ReadFull(r, b[:]); err != nil { + var buf [fileHeaderLen]byte + if _, err := io.ReadFull(r, buf[:]); err != nil { return 0, err } - c := binary.LittleEndian - if sig := c.Uint32(b[:4]); sig != fileHeaderSignature { - return 0, FormatError + b := readBuf(buf[:]) + if sig := b.uint32(); sig != fileHeaderSignature { + return 0, ErrFormat } - filenameLen := int(c.Uint16(b[26:28])) - extraLen := int(c.Uint16(b[28:30])) + b = b[22:] // skip over most of the header + filenameLen := int(b.uint16()) + extraLen := int(b.uint16()) return int64(fileHeaderLen + filenameLen + extraLen), nil } // readDirectoryHeader attempts to read a directory header from r. // It returns io.ErrUnexpectedEOF if it cannot read a complete header, -// and FormatError if it doesn't find a valid header signature. -func readDirectoryHeader(f *File, r io.Reader) os.Error { - var b [directoryHeaderLen]byte - if _, err := io.ReadFull(r, b[:]); err != nil { +// and ErrFormat if it doesn't find a valid header signature. +func readDirectoryHeader(f *File, r io.Reader) error { + var buf [directoryHeaderLen]byte + if _, err := io.ReadFull(r, buf[:]); err != nil { return err } - c := binary.LittleEndian - if sig := c.Uint32(b[:4]); sig != directoryHeaderSignature { - return FormatError + b := readBuf(buf[:]) + if sig := b.uint32(); sig != directoryHeaderSignature { + return ErrFormat } - f.CreatorVersion = c.Uint16(b[4:6]) - f.ReaderVersion = c.Uint16(b[6:8]) - f.Flags = c.Uint16(b[8:10]) - f.Method = c.Uint16(b[10:12]) - f.ModifiedTime = c.Uint16(b[12:14]) - f.ModifiedDate = c.Uint16(b[14:16]) - f.CRC32 = c.Uint32(b[16:20]) - f.CompressedSize = c.Uint32(b[20:24]) - f.UncompressedSize = c.Uint32(b[24:28]) - filenameLen := int(c.Uint16(b[28:30])) - extraLen := int(c.Uint16(b[30:32])) - commentLen := int(c.Uint16(b[32:34])) - // startDiskNumber := c.Uint16(b[34:36]) // Unused - // internalAttributes := c.Uint16(b[36:38]) // Unused - // externalAttributes := c.Uint32(b[38:42]) // Unused - f.headerOffset = int64(c.Uint32(b[42:46])) + f.CreatorVersion = b.uint16() + f.ReaderVersion = b.uint16() + f.Flags = b.uint16() + f.Method = b.uint16() + f.ModifiedTime = b.uint16() + f.ModifiedDate = b.uint16() + f.CRC32 = b.uint32() + f.CompressedSize = b.uint32() + f.UncompressedSize = b.uint32() + filenameLen := int(b.uint16()) + extraLen := int(b.uint16()) + commentLen := int(b.uint16()) + b = b[4:] // skipped start disk number and internal attributes (2x uint16) + f.ExternalAttrs = b.uint32() + f.headerOffset = int64(b.uint32()) d := make([]byte, filenameLen+extraLen+commentLen) if _, err := io.ReadFull(r, d); err != nil { return err @@ -250,49 +236,75 @@ func readDirectoryHeader(f *File, r io.Reader) os.Error { return nil } -func readDataDescriptor(r io.Reader, f *File) os.Error { - var b [dataDescriptorLen]byte - if _, err := io.ReadFull(r, b[:]); err != nil { +func readDataDescriptor(r io.Reader, f *File) error { + var buf [dataDescriptorLen]byte + + // The spec says: "Although not originally assigned a + // signature, the value 0x08074b50 has commonly been adopted + // as a signature value for the data descriptor record. + // Implementers should be aware that ZIP files may be + // encountered with or without this signature marking data + // descriptors and should account for either case when reading + // ZIP files to ensure compatibility." + // + // dataDescriptorLen includes the size of the signature but + // first read just those 4 bytes to see if it exists. + if _, err := io.ReadFull(r, buf[:4]); err != nil { + return err + } + off := 0 + maybeSig := readBuf(buf[:4]) + if maybeSig.uint32() != dataDescriptorSignature { + // No data descriptor signature. Keep these four + // bytes. + off += 4 + } + if _, err := io.ReadFull(r, buf[off:12]); err != nil { return err } - c := binary.LittleEndian - f.CRC32 = c.Uint32(b[:4]) - f.CompressedSize = c.Uint32(b[4:8]) - f.UncompressedSize = c.Uint32(b[8:12]) + b := readBuf(buf[:12]) + f.CRC32 = b.uint32() + f.CompressedSize = b.uint32() + f.UncompressedSize = b.uint32() return nil } -func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err os.Error) { +func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { // look for directoryEndSignature in the last 1k, then in the last 65k - var b []byte + var buf []byte for i, bLen := range []int64{1024, 65 * 1024} { if bLen > size { bLen = size } - b = make([]byte, int(bLen)) - if _, err := r.ReadAt(b, size-bLen); err != nil && err != os.EOF { + buf = make([]byte, int(bLen)) + if _, err := r.ReadAt(buf, size-bLen); err != nil && err != io.EOF { return nil, err } - if p := findSignatureInBlock(b); p >= 0 { - b = b[p:] + if p := findSignatureInBlock(buf); p >= 0 { + buf = buf[p:] break } if i == 1 || bLen == size { - return nil, FormatError + return nil, ErrFormat } } // read header into struct - c := binary.LittleEndian - d := new(directoryEnd) - d.diskNbr = c.Uint16(b[4:6]) - d.dirDiskNbr = c.Uint16(b[6:8]) - d.dirRecordsThisDisk = c.Uint16(b[8:10]) - d.directoryRecords = c.Uint16(b[10:12]) - d.directorySize = c.Uint32(b[12:16]) - d.directoryOffset = c.Uint32(b[16:20]) - d.commentLen = c.Uint16(b[20:22]) - d.comment = string(b[22 : 22+int(d.commentLen)]) + b := readBuf(buf[4:]) // skip signature + d := &directoryEnd{ + diskNbr: b.uint16(), + dirDiskNbr: b.uint16(), + dirRecordsThisDisk: b.uint16(), + directoryRecords: b.uint16(), + directorySize: b.uint32(), + directoryOffset: b.uint32(), + commentLen: b.uint16(), + } + l := int(d.commentLen) + if l > len(b) { + return nil, errors.New("zip: invalid comment length") + } + d.comment = string(b[:l]) return d, nil } @@ -309,3 +321,17 @@ func findSignatureInBlock(b []byte) int { } return -1 } + +type readBuf []byte + +func (b *readBuf) uint16() uint16 { + v := binary.LittleEndian.Uint16(*b) + *b = (*b)[2:] + return v +} + +func (b *readBuf) uint32() uint32 { + v := binary.LittleEndian.Uint32(*b) + *b = (*b)[4:] + return v +} diff --git a/src/pkg/archive/zip/reader_test.go b/src/pkg/archive/zip/reader_test.go index fd5fed2af..5f1d1b28a 100644 --- a/src/pkg/archive/zip/reader_test.go +++ b/src/pkg/archive/zip/reader_test.go @@ -7,25 +7,31 @@ package zip import ( "bytes" "encoding/binary" + "encoding/hex" "io" "io/ioutil" "os" + "path/filepath" + "regexp" "testing" "time" ) type ZipTest struct { Name string + Source func() (r io.ReaderAt, size int64) // if non-nil, used instead of testdata/ file Comment string File []ZipTestFile - Error os.Error // the error that Opening this file should return + Error error // the error that Opening this file should return } type ZipTestFile struct { - Name string - Content []byte // if blank, will attempt to compare against File - File string // name of file to compare to (relative to testdata/) - Mtime string // modified time in format "mm-dd-yy hh:mm:ss" + Name string + Content []byte // if blank, will attempt to compare against File + ContentErr error + File string // name of file to compare to (relative to testdata/) + Mtime string // modified time in format "mm-dd-yy hh:mm:ss" + Mode os.FileMode } // Caution: The Mtime values found for the test files should correspond to @@ -47,26 +53,45 @@ var tests = []ZipTest{ Name: "test.txt", Content: []byte("This is a test text file.\n"), Mtime: "09-05-10 12:12:02", + Mode: 0644, }, { Name: "gophercolor16x16.png", File: "gophercolor16x16.png", Mtime: "09-05-10 15:52:58", + Mode: 0644, + }, + }, + }, + { + Name: "r.zip", + Source: returnRecursiveZip, + File: []ZipTestFile{ + { + Name: "r/r.zip", + Content: rZipBytes(), + Mtime: "03-04-10 00:24:16", + Mode: 0666, }, }, }, { - Name: "r.zip", + Name: "symlink.zip", File: []ZipTestFile{ { - Name: "r/r.zip", - File: "r.zip", - Mtime: "03-04-10 00:24:16", + Name: "symlink", + Content: []byte("../target"), + Mode: 0777 | os.ModeSymlink, }, }, }, - {Name: "readme.zip"}, - {Name: "readme.notzip", Error: FormatError}, + { + Name: "readme.zip", + }, + { + Name: "readme.notzip", + Error: ErrFormat, + }, { Name: "dd.zip", File: []ZipTestFile{ @@ -74,9 +99,136 @@ var tests = []ZipTest{ Name: "filename", Content: []byte("This is a test textfile.\n"), Mtime: "02-02-11 13:06:20", + Mode: 0666, }, }, }, + { + // created in windows XP file manager. + Name: "winxp.zip", + File: crossPlatform, + }, + { + // created by Zip 3.0 under Linux + Name: "unix.zip", + File: crossPlatform, + }, + { + // created by Go, before we wrote the "optional" data + // descriptor signatures (which are required by OS X) + Name: "go-no-datadesc-sig.zip", + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mtime: "03-08-12 16:59:10", + Mode: 0644, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mtime: "03-08-12 16:59:12", + Mode: 0644, + }, + }, + }, + { + // created by Go, after we wrote the "optional" data + // descriptor signatures (which are required by OS X) + Name: "go-with-datadesc-sig.zip", + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mode: 0666, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mode: 0666, + }, + }, + }, + { + Name: "Bad-CRC32-in-data-descriptor", + Source: returnCorruptCRC32Zip, + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mode: 0666, + ContentErr: ErrChecksum, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mode: 0666, + }, + }, + }, + // Tests that we verify (and accept valid) crc32s on files + // with crc32s in their file header (not in data descriptors) + { + Name: "crc32-not-streamed.zip", + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mtime: "03-08-12 16:59:10", + Mode: 0644, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mtime: "03-08-12 16:59:12", + Mode: 0644, + }, + }, + }, + // Tests that we verify (and reject invalid) crc32s on files + // with crc32s in their file header (not in data descriptors) + { + Name: "crc32-not-streamed.zip", + Source: returnCorruptNotStreamedZip, + File: []ZipTestFile{ + { + Name: "foo.txt", + Content: []byte("foo\n"), + Mtime: "03-08-12 16:59:10", + Mode: 0644, + ContentErr: ErrChecksum, + }, + { + Name: "bar.txt", + Content: []byte("bar\n"), + Mtime: "03-08-12 16:59:12", + Mode: 0644, + }, + }, + }, +} + +var crossPlatform = []ZipTestFile{ + { + Name: "hello", + Content: []byte("world \r\n"), + Mode: 0666, + }, + { + Name: "dir/bar", + Content: []byte("foo \r\n"), + Mode: 0666, + }, + { + Name: "dir/empty/", + Content: []byte{}, + Mode: os.ModeDir | 0777, + }, + { + Name: "readonly", + Content: []byte("important \r\n"), + Mode: 0444, + }, } func TestReader(t *testing.T) { @@ -86,17 +238,27 @@ func TestReader(t *testing.T) { } func readTestZip(t *testing.T, zt ZipTest) { - z, err := OpenReader("testdata/" + zt.Name) + var z *Reader + var err error + if zt.Source != nil { + rat, size := zt.Source() + z, err = NewReader(rat, size) + } else { + var rc *ReadCloser + rc, err = OpenReader(filepath.Join("testdata", zt.Name)) + if err == nil { + z = &rc.Reader + } + } if err != zt.Error { t.Errorf("error=%v, want %v", err, zt.Error) return } // bail if file is not zip - if err == FormatError { + if err == ErrFormat { return } - defer z.Close() // bail here if no Files expected to be tested // (there may actually be files in the zip, but we don't care) @@ -108,12 +270,12 @@ func readTestZip(t *testing.T, zt ZipTest) { t.Errorf("%s: comment=%q, want %q", zt.Name, z.Comment, zt.Comment) } if len(z.File) != len(zt.File) { - t.Errorf("%s: file count=%d, want %d", zt.Name, len(z.File), len(zt.File)) + t.Fatalf("%s: file count=%d, want %d", zt.Name, len(z.File), len(zt.File)) } // test read of each file for i, ft := range zt.File { - readTestFile(t, ft, z.File[i]) + readTestFile(t, zt, ft, z.File[i]) } // test simultaneous reads @@ -121,46 +283,35 @@ func readTestZip(t *testing.T, zt ZipTest) { done := make(chan bool) for i := 0; i < 5; i++ { for j, ft := range zt.File { - go func() { - readTestFile(t, ft, z.File[j]) + go func(j int, ft ZipTestFile) { + readTestFile(t, zt, ft, z.File[j]) done <- true - }() + }(j, ft) n++ } } for ; n > 0; n-- { <-done } +} + +func readTestFile(t *testing.T, zt ZipTest, ft ZipTestFile, f *File) { + if f.Name != ft.Name { + t.Errorf("%s: name=%q, want %q", zt.Name, f.Name, ft.Name) + } - // test invalid checksum - if !z.File[0].hasDataDescriptor() { // skip test when crc32 in dd - z.File[0].CRC32++ // invalidate - r, err := z.File[0].Open() + if ft.Mtime != "" { + mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime) if err != nil { t.Error(err) return } - var b bytes.Buffer - _, err = io.Copy(&b, r) - if err != ChecksumError { - t.Errorf("%s: copy error=%v, want %v", z.File[0].Name, err, ChecksumError) + if ft := f.ModTime(); !ft.Equal(mtime) { + t.Errorf("%s: %s: mtime=%s, want %s", zt.Name, f.Name, ft, mtime) } } -} - -func readTestFile(t *testing.T, ft ZipTestFile, f *File) { - if f.Name != ft.Name { - t.Errorf("name=%q, want %q", f.Name, ft.Name) - } - mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime) - if err != nil { - t.Error(err) - return - } - if got, want := f.Mtime_ns()/1e9, mtime.Seconds(); got != want { - t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want) - } + testFileMode(t, zt.Name, f, ft.Mode) size0 := f.UncompressedSize @@ -176,14 +327,16 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { } _, err = io.Copy(&b, r) + if err != ft.ContentErr { + t.Errorf("%s: copying contents: %v (want %v)", zt.Name, err, ft.ContentErr) + } if err != nil { - t.Error(err) return } r.Close() var c []byte - if len(ft.Content) != 0 { + if ft.Content != nil { c = ft.Content } else if c, err = ioutil.ReadFile("testdata/" + ft.File); err != nil { t.Error(err) @@ -203,14 +356,23 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { } } +func testFileMode(t *testing.T, zipName string, f *File, want os.FileMode) { + mode := f.Mode() + if want == 0 { + t.Errorf("%s: %s mode: got %v, want none", zipName, f.Name, mode) + } else if mode != want { + t.Errorf("%s: %s mode: want %v, got %v", zipName, f.Name, want, mode) + } +} + func TestInvalidFiles(t *testing.T) { const size = 1024 * 70 // 70kb b := make([]byte, size) // zeroes - _, err := NewReader(sliceReaderAt(b), size) - if err != FormatError { - t.Errorf("zeroes: error=%v, want %v", err, FormatError) + _, err := NewReader(bytes.NewReader(b), size) + if err != ErrFormat { + t.Errorf("zeroes: error=%v, want %v", err, ErrFormat) } // repeated directoryEndSignatures @@ -219,15 +381,86 @@ func TestInvalidFiles(t *testing.T) { for i := 0; i < size-4; i += 4 { copy(b[i:i+4], sig) } - _, err = NewReader(sliceReaderAt(b), size) - if err != FormatError { - t.Errorf("sigs: error=%v, want %v", err, FormatError) + _, err = NewReader(bytes.NewReader(b), size) + if err != ErrFormat { + t.Errorf("sigs: error=%v, want %v", err, ErrFormat) } } -type sliceReaderAt []byte +func messWith(fileName string, corrupter func(b []byte)) (r io.ReaderAt, size int64) { + data, err := ioutil.ReadFile(filepath.Join("testdata", fileName)) + if err != nil { + panic("Error reading " + fileName + ": " + err.Error()) + } + corrupter(data) + return bytes.NewReader(data), int64(len(data)) +} + +func returnCorruptCRC32Zip() (r io.ReaderAt, size int64) { + return messWith("go-with-datadesc-sig.zip", func(b []byte) { + // Corrupt one of the CRC32s in the data descriptor: + b[0x2d]++ + }) +} + +func returnCorruptNotStreamedZip() (r io.ReaderAt, size int64) { + return messWith("crc32-not-streamed.zip", func(b []byte) { + // Corrupt foo.txt's final crc32 byte, in both + // the file header and TOC. (0x7e -> 0x7f) + b[0x11]++ + b[0x9d]++ + + // TODO(bradfitz): add a new test that only corrupts + // one of these values, and verify that that's also an + // error. Currently, the reader code doesn't verify the + // fileheader and TOC's crc32 match if they're both + // non-zero and only the second line above, the TOC, + // is what matters. + }) +} + +// rZipBytes returns the bytes of a recursive zip file, without +// putting it on disk and triggering certain virus scanners. +func rZipBytes() []byte { + s := ` +0000000 50 4b 03 04 14 00 00 00 08 00 08 03 64 3c f9 f4 +0000010 89 64 48 01 00 00 b8 01 00 00 07 00 00 00 72 2f +0000020 72 2e 7a 69 70 00 25 00 da ff 50 4b 03 04 14 00 +0000030 00 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00 +0000040 b8 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00 +0000050 2f 00 d0 ff 00 25 00 da ff 50 4b 03 04 14 00 00 +0000060 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00 b8 +0000070 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00 2f +0000080 00 d0 ff c2 54 8e 57 39 00 05 00 fa ff c2 54 8e +0000090 57 39 00 05 00 fa ff 00 05 00 fa ff 00 14 00 eb +00000a0 ff c2 54 8e 57 39 00 05 00 fa ff 00 05 00 fa ff +00000b0 00 14 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42 +00000c0 88 21 c4 00 00 14 00 eb ff 42 88 21 c4 00 00 14 +00000d0 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42 88 21 +00000e0 c4 00 00 00 00 ff ff 00 00 00 ff ff 00 34 00 cb +00000f0 ff 42 88 21 c4 00 00 00 00 ff ff 00 00 00 ff ff +0000100 00 34 00 cb ff 42 e8 21 5e 0f 00 00 00 ff ff 0a +0000110 f0 66 64 12 61 c0 15 dc e8 a0 48 bf 48 af 2a b3 +0000120 20 c0 9b 95 0d c4 67 04 42 53 06 06 06 40 00 06 +0000130 00 f9 ff 6d 01 00 00 00 00 42 e8 21 5e 0f 00 00 +0000140 00 ff ff 0a f0 66 64 12 61 c0 15 dc e8 a0 48 bf +0000150 48 af 2a b3 20 c0 9b 95 0d c4 67 04 42 53 06 06 +0000160 06 40 00 06 00 f9 ff 6d 01 00 00 00 00 50 4b 01 +0000170 02 14 00 14 00 00 00 08 00 08 03 64 3c f9 f4 89 +0000180 64 48 01 00 00 b8 01 00 00 07 00 00 00 00 00 00 +0000190 00 00 00 00 00 00 00 00 00 00 00 72 2f 72 2e 7a +00001a0 69 70 50 4b 05 06 00 00 00 00 01 00 01 00 35 00 +00001b0 00 00 6d 01 00 00 00 00` + s = regexp.MustCompile(`[0-9a-f]{7}`).ReplaceAllString(s, "") + s = regexp.MustCompile(`\s+`).ReplaceAllString(s, "") + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} -func (r sliceReaderAt) ReadAt(b []byte, off int64) (int, os.Error) { - copy(b, r[int(off):int(off)+len(b)]) - return len(b), nil +func returnRecursiveZip() (r io.ReaderAt, size int64) { + b := rZipBytes() + return bytes.NewReader(b), int64(len(b)) } diff --git a/src/pkg/archive/zip/struct.go b/src/pkg/archive/zip/struct.go index 1d6e70f10..55f3dcfb8 100644 --- a/src/pkg/archive/zip/struct.go +++ b/src/pkg/archive/zip/struct.go @@ -11,8 +11,11 @@ This package does not support ZIP64 or disk spanning. */ package zip -import "os" -import "time" +import ( + "errors" + "os" + "time" +) // Compression methods. const ( @@ -24,10 +27,18 @@ const ( fileHeaderSignature = 0x04034b50 directoryHeaderSignature = 0x02014b50 directoryEndSignature = 0x06054b50 - fileHeaderLen = 30 // + filename + extra - directoryHeaderLen = 46 // + filename + extra + comment - directoryEndLen = 22 // + comment - dataDescriptorLen = 12 + dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder + fileHeaderLen = 30 // + filename + extra + directoryHeaderLen = 46 // + filename + extra + comment + directoryEndLen = 22 // + comment + dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size + + // Constants for the first byte in CreatorVersion + creatorFAT = 0 + creatorUnix = 3 + creatorNTFS = 11 + creatorVFAT = 14 + creatorMacOSX = 19 ) type FileHeader struct { @@ -42,9 +53,43 @@ type FileHeader struct { CompressedSize uint32 UncompressedSize uint32 Extra []byte + ExternalAttrs uint32 // Meaning depends on CreatorVersion Comment string } +// FileInfo returns an os.FileInfo for the FileHeader. +func (h *FileHeader) FileInfo() os.FileInfo { + return headerFileInfo{h} +} + +// headerFileInfo implements os.FileInfo. +type headerFileInfo struct { + fh *FileHeader +} + +func (fi headerFileInfo) Name() string { return fi.fh.Name } +func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) } +func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } +func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } +func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } +func (fi headerFileInfo) Sys() interface{} { return fi.fh } + +// FileInfoHeader creates a partially-populated FileHeader from an +// os.FileInfo. +func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { + size := fi.Size() + if size > (1<<32 - 1) { + return nil, errors.New("zip: file over 4GB") + } + fh := &FileHeader{ + Name: fi.Name(), + UncompressedSize: uint32(size), + } + fh.SetModTime(fi.ModTime()) + fh.SetMode(fi.Mode()) + return fh, nil +} + type directoryEnd struct { diskNbr uint16 // unused dirDiskNbr uint16 // unused @@ -56,36 +101,165 @@ type directoryEnd struct { comment string } -func recoverError(err *os.Error) { - if e := recover(); e != nil { - if osErr, ok := e.(os.Error); ok { - *err = osErr - return - } - panic(e) - } -} - // msDosTimeToTime converts an MS-DOS date and time into a time.Time. // The resolution is 2s. // See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx func msDosTimeToTime(dosDate, dosTime uint16) time.Time { - return time.Time{ + return time.Date( // date bits 0-4: day of month; 5-8: month; 9-15: years since 1980 - Year: int64(dosDate>>9 + 1980), - Month: int(dosDate >> 5 & 0xf), - Day: int(dosDate & 0x1f), + int(dosDate>>9+1980), + time.Month(dosDate>>5&0xf), + int(dosDate&0x1f), // time bits 0-4: second/2; 5-10: minute; 11-15: hour - Hour: int(dosTime >> 11), - Minute: int(dosTime >> 5 & 0x3f), - Second: int(dosTime & 0x1f * 2), - } + int(dosTime>>11), + int(dosTime>>5&0x3f), + int(dosTime&0x1f*2), + 0, // nanoseconds + + time.UTC, + ) } -// Mtime_ns returns the modified time in ns since epoch. +// timeToMsDosTime converts a time.Time to an MS-DOS date and time. // The resolution is 2s. -func (h *FileHeader) Mtime_ns() int64 { - t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) - return t.Seconds() * 1e9 +// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx +func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) { + t = t.In(time.UTC) + fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9) + fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11) + return +} + +// ModTime returns the modification time. +// The resolution is 2s. +func (h *FileHeader) ModTime() time.Time { + return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) +} + +// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time. +// The resolution is 2s. +func (h *FileHeader) SetModTime(t time.Time) { + h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t) +} + +const ( + // Unix constants. The specification doesn't mention them, + // but these seem to be the values agreed on by tools. + s_IFMT = 0xf000 + s_IFSOCK = 0xc000 + s_IFLNK = 0xa000 + s_IFREG = 0x8000 + s_IFBLK = 0x6000 + s_IFDIR = 0x4000 + s_IFCHR = 0x2000 + s_IFIFO = 0x1000 + s_ISUID = 0x800 + s_ISGID = 0x400 + s_ISVTX = 0x200 + + msdosDir = 0x10 + msdosReadOnly = 0x01 +) + +// Mode returns the permission and mode bits for the FileHeader. +func (h *FileHeader) Mode() (mode os.FileMode) { + switch h.CreatorVersion >> 8 { + case creatorUnix, creatorMacOSX: + mode = unixModeToFileMode(h.ExternalAttrs >> 16) + case creatorNTFS, creatorVFAT, creatorFAT: + mode = msdosModeToFileMode(h.ExternalAttrs) + } + if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/' { + mode |= os.ModeDir + } + return mode +} + +// SetMode changes the permission and mode bits for the FileHeader. +func (h *FileHeader) SetMode(mode os.FileMode) { + h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8 + h.ExternalAttrs = fileModeToUnixMode(mode) << 16 + + // set MSDOS attributes too, as the original zip does. + if mode&os.ModeDir != 0 { + h.ExternalAttrs |= msdosDir + } + if mode&0200 == 0 { + h.ExternalAttrs |= msdosReadOnly + } +} + +func msdosModeToFileMode(m uint32) (mode os.FileMode) { + if m&msdosDir != 0 { + mode = os.ModeDir | 0777 + } else { + mode = 0666 + } + if m&msdosReadOnly != 0 { + mode &^= 0222 + } + return mode +} + +func fileModeToUnixMode(mode os.FileMode) uint32 { + var m uint32 + switch mode & os.ModeType { + default: + m = s_IFREG + case os.ModeDir: + m = s_IFDIR + case os.ModeSymlink: + m = s_IFLNK + case os.ModeNamedPipe: + m = s_IFIFO + case os.ModeSocket: + m = s_IFSOCK + case os.ModeDevice: + if mode&os.ModeCharDevice != 0 { + m = s_IFCHR + } else { + m = s_IFBLK + } + } + if mode&os.ModeSetuid != 0 { + m |= s_ISUID + } + if mode&os.ModeSetgid != 0 { + m |= s_ISGID + } + if mode&os.ModeSticky != 0 { + m |= s_ISVTX + } + return m | uint32(mode&0777) +} + +func unixModeToFileMode(m uint32) os.FileMode { + mode := os.FileMode(m & 0777) + switch m & s_IFMT { + case s_IFBLK: + mode |= os.ModeDevice + case s_IFCHR: + mode |= os.ModeDevice | os.ModeCharDevice + case s_IFDIR: + mode |= os.ModeDir + case s_IFIFO: + mode |= os.ModeNamedPipe + case s_IFLNK: + mode |= os.ModeSymlink + case s_IFREG: + // nothing to do + case s_IFSOCK: + mode |= os.ModeSocket + } + if m&s_ISGID != 0 { + mode |= os.ModeSetgid + } + if m&s_ISUID != 0 { + mode |= os.ModeSetuid + } + if m&s_ISVTX != 0 { + mode |= os.ModeSticky + } + return mode } diff --git a/src/pkg/archive/zip/testdata/crc32-not-streamed.zip b/src/pkg/archive/zip/testdata/crc32-not-streamed.zip new file mode 100644 index 000000000..f268d8873 Binary files /dev/null and b/src/pkg/archive/zip/testdata/crc32-not-streamed.zip differ diff --git a/src/pkg/archive/zip/testdata/go-no-datadesc-sig.zip b/src/pkg/archive/zip/testdata/go-no-datadesc-sig.zip new file mode 100644 index 000000000..c3d593f44 Binary files /dev/null and b/src/pkg/archive/zip/testdata/go-no-datadesc-sig.zip differ diff --git a/src/pkg/archive/zip/testdata/go-with-datadesc-sig.zip b/src/pkg/archive/zip/testdata/go-with-datadesc-sig.zip new file mode 100644 index 000000000..bcfe121bb Binary files /dev/null and b/src/pkg/archive/zip/testdata/go-with-datadesc-sig.zip differ diff --git a/src/pkg/archive/zip/testdata/r.zip b/src/pkg/archive/zip/testdata/r.zip deleted file mode 100644 index ea0fa2ffc..000000000 Binary files a/src/pkg/archive/zip/testdata/r.zip and /dev/null differ diff --git a/src/pkg/archive/zip/testdata/symlink.zip b/src/pkg/archive/zip/testdata/symlink.zip new file mode 100644 index 000000000..af846938c Binary files /dev/null and b/src/pkg/archive/zip/testdata/symlink.zip differ diff --git a/src/pkg/archive/zip/testdata/unix.zip b/src/pkg/archive/zip/testdata/unix.zip new file mode 100644 index 000000000..ce1a981b2 Binary files /dev/null and b/src/pkg/archive/zip/testdata/unix.zip differ diff --git a/src/pkg/archive/zip/testdata/winxp.zip b/src/pkg/archive/zip/testdata/winxp.zip new file mode 100644 index 000000000..3919322f0 Binary files /dev/null and b/src/pkg/archive/zip/testdata/winxp.zip differ diff --git a/src/pkg/archive/zip/writer.go b/src/pkg/archive/zip/writer.go index 2065b06da..45eb6bd73 100644 --- a/src/pkg/archive/zip/writer.go +++ b/src/pkg/archive/zip/writer.go @@ -8,10 +8,10 @@ import ( "bufio" "compress/flate" "encoding/binary" + "errors" "hash" "hash/crc32" "io" - "os" ) // TODO(adg): support zip file comments @@ -19,7 +19,7 @@ import ( // Writer implements a zip file writer. type Writer struct { - *countWriter + cw *countWriter dir []*header last *fileWriter closed bool @@ -32,69 +32,81 @@ type header struct { // NewWriter returns a new Writer writing a zip file to w. func NewWriter(w io.Writer) *Writer { - return &Writer{countWriter: &countWriter{w: bufio.NewWriter(w)}} + return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}} } // Close finishes writing the zip file by writing the central directory. // It does not (and can not) close the underlying writer. -func (w *Writer) Close() (err os.Error) { +func (w *Writer) Close() error { if w.last != nil && !w.last.closed { - if err = w.last.close(); err != nil { - return + if err := w.last.close(); err != nil { + return err } w.last = nil } if w.closed { - return os.NewError("zip: writer closed twice") + return errors.New("zip: writer closed twice") } w.closed = true - defer recoverError(&err) - // write central directory - start := w.count + start := w.cw.count for _, h := range w.dir { - write(w, uint32(directoryHeaderSignature)) - write(w, h.CreatorVersion) - write(w, h.ReaderVersion) - write(w, h.Flags) - write(w, h.Method) - write(w, h.ModifiedTime) - write(w, h.ModifiedDate) - write(w, h.CRC32) - write(w, h.CompressedSize) - write(w, h.UncompressedSize) - write(w, uint16(len(h.Name))) - write(w, uint16(len(h.Extra))) - write(w, uint16(len(h.Comment))) - write(w, uint16(0)) // disk number start - write(w, uint16(0)) // internal file attributes - write(w, uint32(0)) // external file attributes - write(w, h.offset) - writeBytes(w, []byte(h.Name)) - writeBytes(w, h.Extra) - writeBytes(w, []byte(h.Comment)) + var buf [directoryHeaderLen]byte + b := writeBuf(buf[:]) + b.uint32(uint32(directoryHeaderSignature)) + b.uint16(h.CreatorVersion) + b.uint16(h.ReaderVersion) + b.uint16(h.Flags) + b.uint16(h.Method) + b.uint16(h.ModifiedTime) + b.uint16(h.ModifiedDate) + b.uint32(h.CRC32) + b.uint32(h.CompressedSize) + b.uint32(h.UncompressedSize) + b.uint16(uint16(len(h.Name))) + b.uint16(uint16(len(h.Extra))) + b.uint16(uint16(len(h.Comment))) + b = b[4:] // skip disk number start and internal file attr (2x uint16) + b.uint32(h.ExternalAttrs) + b.uint32(h.offset) + if _, err := w.cw.Write(buf[:]); err != nil { + return err + } + if _, err := io.WriteString(w.cw, h.Name); err != nil { + return err + } + if _, err := w.cw.Write(h.Extra); err != nil { + return err + } + if _, err := io.WriteString(w.cw, h.Comment); err != nil { + return err + } } - end := w.count + end := w.cw.count // write end record - write(w, uint32(directoryEndSignature)) - write(w, uint16(0)) // disk number - write(w, uint16(0)) // disk number where directory starts - write(w, uint16(len(w.dir))) // number of entries this disk - write(w, uint16(len(w.dir))) // number of entries total - write(w, uint32(end-start)) // size of directory - write(w, uint32(start)) // start of directory - write(w, uint16(0)) // size of comment + var buf [directoryEndLen]byte + b := writeBuf(buf[:]) + b.uint32(uint32(directoryEndSignature)) + b = b[4:] // skip over disk number and first disk number (2x uint16) + b.uint16(uint16(len(w.dir))) // number of entries this disk + b.uint16(uint16(len(w.dir))) // number of entries total + b.uint32(uint32(end - start)) // size of directory + b.uint32(uint32(start)) // start of directory + // skipped size of comment (always zero) + if _, err := w.cw.Write(buf[:]); err != nil { + return err + } - return w.w.(*bufio.Writer).Flush() + return w.cw.w.(*bufio.Writer).Flush() } // Create adds a file to the zip file using the provided name. // It returns a Writer to which the file contents should be written. // The file's contents must be written to the io.Writer before the next // call to Create, CreateHeader, or Close. -func (w *Writer) Create(name string) (io.Writer, os.Error) { +func (w *Writer) Create(name string) (io.Writer, error) { header := &FileHeader{ Name: name, Method: Deflate, @@ -107,7 +119,7 @@ func (w *Writer) Create(name string) (io.Writer, os.Error) { // It returns a Writer to which the file contents should be written. // The file's contents must be written to the io.Writer before the next // call to Create, CreateHeader, or Close. -func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) { +func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { if w.last != nil && !w.last.closed { if err := w.last.close(); err != nil { return nil, err @@ -115,32 +127,36 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) { } fh.Flags |= 0x8 // we will write a data descriptor - fh.CreatorVersion = 0x14 + fh.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14 fh.ReaderVersion = 0x14 fw := &fileWriter{ - zipw: w, - compCount: &countWriter{w: w}, + zipw: w.cw, + compCount: &countWriter{w: w.cw}, crc32: crc32.NewIEEE(), } switch fh.Method { case Store: fw.comp = nopCloser{fw.compCount} case Deflate: - fw.comp = flate.NewWriter(fw.compCount, 5) + var err error + fw.comp, err = flate.NewWriter(fw.compCount, 5) + if err != nil { + return nil, err + } default: - return nil, UnsupportedMethod + return nil, ErrAlgorithm } fw.rawCount = &countWriter{w: fw.comp} h := &header{ FileHeader: fh, - offset: uint32(w.count), + offset: uint32(w.cw.count), } w.dir = append(w.dir, h) fw.header = h - if err := writeHeader(w, fh); err != nil { + if err := writeHeader(w.cw, fh); err != nil { return nil, err } @@ -148,22 +164,28 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) { return fw, nil } -func writeHeader(w io.Writer, h *FileHeader) (err os.Error) { - defer recoverError(&err) - write(w, uint32(fileHeaderSignature)) - write(w, h.ReaderVersion) - write(w, h.Flags) - write(w, h.Method) - write(w, h.ModifiedTime) - write(w, h.ModifiedDate) - write(w, h.CRC32) - write(w, h.CompressedSize) - write(w, h.UncompressedSize) - write(w, uint16(len(h.Name))) - write(w, uint16(len(h.Extra))) - writeBytes(w, []byte(h.Name)) - writeBytes(w, h.Extra) - return nil +func writeHeader(w io.Writer, h *FileHeader) error { + var buf [fileHeaderLen]byte + b := writeBuf(buf[:]) + b.uint32(uint32(fileHeaderSignature)) + b.uint16(h.ReaderVersion) + b.uint16(h.Flags) + b.uint16(h.Method) + b.uint16(h.ModifiedTime) + b.uint16(h.ModifiedDate) + b.uint32(h.CRC32) + b.uint32(h.CompressedSize) + b.uint32(h.UncompressedSize) + b.uint16(uint16(len(h.Name))) + b.uint16(uint16(len(h.Extra))) + if _, err := w.Write(buf[:]); err != nil { + return err + } + if _, err := io.WriteString(w, h.Name); err != nil { + return err + } + _, err := w.Write(h.Extra) + return err } type fileWriter struct { @@ -176,21 +198,21 @@ type fileWriter struct { closed bool } -func (w *fileWriter) Write(p []byte) (int, os.Error) { +func (w *fileWriter) Write(p []byte) (int, error) { if w.closed { - return 0, os.NewError("zip: write to closed file") + return 0, errors.New("zip: write to closed file") } w.crc32.Write(p) return w.rawCount.Write(p) } -func (w *fileWriter) close() (err os.Error) { +func (w *fileWriter) close() error { if w.closed { - return os.NewError("zip: file closed twice") + return errors.New("zip: file closed twice") } w.closed = true - if err = w.comp.Close(); err != nil { - return + if err := w.comp.Close(); err != nil { + return err } // update FileHeader @@ -200,12 +222,14 @@ func (w *fileWriter) close() (err os.Error) { fh.UncompressedSize = uint32(w.rawCount.count) // write data descriptor - defer recoverError(&err) - write(w.zipw, fh.CRC32) - write(w.zipw, fh.CompressedSize) - write(w.zipw, fh.UncompressedSize) - - return nil + var buf [dataDescriptorLen]byte + b := writeBuf(buf[:]) + b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X + b.uint32(fh.CRC32) + b.uint32(fh.CompressedSize) + b.uint32(fh.UncompressedSize) + _, err := w.zipw.Write(buf[:]) + return err } type countWriter struct { @@ -213,7 +237,7 @@ type countWriter struct { count int64 } -func (w *countWriter) Write(p []byte) (int, os.Error) { +func (w *countWriter) Write(p []byte) (int, error) { n, err := w.w.Write(p) w.count += int64(n) return n, err @@ -223,22 +247,18 @@ type nopCloser struct { io.Writer } -func (w nopCloser) Close() os.Error { +func (w nopCloser) Close() error { return nil } -func write(w io.Writer, data interface{}) { - if err := binary.Write(w, binary.LittleEndian, data); err != nil { - panic(err) - } +type writeBuf []byte + +func (b *writeBuf) uint16(v uint16) { + binary.LittleEndian.PutUint16(*b, v) + *b = (*b)[2:] } -func writeBytes(w io.Writer, b []byte) { - n, err := w.Write(b) - if err != nil { - panic(err) - } - if n != len(b) { - panic(io.ErrShortWrite) - } +func (b *writeBuf) uint32(v uint32) { + binary.LittleEndian.PutUint32(*b, v) + *b = (*b)[4:] } diff --git a/src/pkg/archive/zip/writer_test.go b/src/pkg/archive/zip/writer_test.go index eb2a80c3f..8b1c4dfd2 100644 --- a/src/pkg/archive/zip/writer_test.go +++ b/src/pkg/archive/zip/writer_test.go @@ -7,54 +7,108 @@ package zip import ( "bytes" "io/ioutil" - "rand" + "math/rand" + "os" "testing" ) // TODO(adg): a more sophisticated test suite -const testString = "Rabbits, guinea pigs, gophers, marsupial rats, and quolls." +type WriteTest struct { + Name string + Data []byte + Method uint16 + Mode os.FileMode +} + +var writeTests = []WriteTest{ + { + Name: "foo", + Data: []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."), + Method: Store, + Mode: 0666, + }, + { + Name: "bar", + Data: nil, // large data set in the test + Method: Deflate, + Mode: 0644, + }, + { + Name: "setuid", + Data: []byte("setuid file"), + Method: Deflate, + Mode: 0755 | os.ModeSetuid, + }, + { + Name: "setgid", + Data: []byte("setgid file"), + Method: Deflate, + Mode: 0755 | os.ModeSetgid, + }, + { + Name: "symlink", + Data: []byte("../link/target"), + Method: Deflate, + Mode: 0755 | os.ModeSymlink, + }, +} func TestWriter(t *testing.T) { largeData := make([]byte, 1<<17) for i := range largeData { largeData[i] = byte(rand.Int()) } + writeTests[1].Data = largeData + defer func() { + writeTests[1].Data = nil + }() // write a zip file buf := new(bytes.Buffer) w := NewWriter(buf) - testCreate(t, w, "foo", []byte(testString), Store) - testCreate(t, w, "bar", largeData, Deflate) + + for _, wt := range writeTests { + testCreate(t, w, &wt) + } + if err := w.Close(); err != nil { t.Fatal(err) } // read it back - r, err := NewReader(sliceReaderAt(buf.Bytes()), int64(buf.Len())) + r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())) if err != nil { t.Fatal(err) } - testReadFile(t, r.File[0], []byte(testString)) - testReadFile(t, r.File[1], largeData) + for i, wt := range writeTests { + testReadFile(t, r.File[i], &wt) + } } -func testCreate(t *testing.T, w *Writer, name string, data []byte, method uint16) { +func testCreate(t *testing.T, w *Writer, wt *WriteTest) { header := &FileHeader{ - Name: name, - Method: method, + Name: wt.Name, + Method: wt.Method, + } + if wt.Mode != 0 { + header.SetMode(wt.Mode) } f, err := w.CreateHeader(header) if err != nil { t.Fatal(err) } - _, err = f.Write(data) + _, err = f.Write(wt.Data) if err != nil { t.Fatal(err) } } -func testReadFile(t *testing.T, f *File, data []byte) { +func testReadFile(t *testing.T, f *File, wt *WriteTest) { + if f.Name != wt.Name { + t.Fatalf("File name: got %q, want %q", f.Name, wt.Name) + } + testFileMode(t, wt.Name, f, wt.Mode) rc, err := f.Open() if err != nil { t.Fatal("opening:", err) @@ -67,7 +121,7 @@ func testReadFile(t *testing.T, f *File, data []byte) { if err != nil { t.Fatal("closing:", err) } - if !bytes.Equal(b, data) { - t.Errorf("File contents %q, want %q", b, data) + if !bytes.Equal(b, wt.Data) { + t.Errorf("File contents %q, want %q", b, wt.Data) } } diff --git a/src/pkg/archive/zip/zip_test.go b/src/pkg/archive/zip/zip_test.go index 0f71fdfac..d6490c4cb 100644 --- a/src/pkg/archive/zip/zip_test.go +++ b/src/pkg/archive/zip/zip_test.go @@ -9,20 +9,12 @@ package zip import ( "bytes" "fmt" - "os" + "reflect" + "strings" "testing" + "time" ) -type stringReaderAt string - -func (s stringReaderAt) ReadAt(p []byte, off int64) (n int, err os.Error) { - if off >= int64(len(s)) { - return 0, os.EOF - } - n = copy(p, s[off:]) - return -} - func TestOver65kFiles(t *testing.T) { if testing.Short() { t.Logf("slow test; skipping") @@ -40,8 +32,8 @@ func TestOver65kFiles(t *testing.T) { if err := w.Close(); err != nil { t.Fatalf("Writer.Close: %v", err) } - rat := stringReaderAt(buf.String()) - zr, err := NewReader(rat, int64(len(rat))) + s := buf.String() + zr, err := NewReader(strings.NewReader(s), int64(len(s))) if err != nil { t.Fatalf("NewReader: %v", err) } @@ -55,3 +47,35 @@ func TestOver65kFiles(t *testing.T) { } } } + +func TestModTime(t *testing.T) { + var testTime = time.Date(2009, time.November, 10, 23, 45, 58, 0, time.UTC) + fh := new(FileHeader) + fh.SetModTime(testTime) + outTime := fh.ModTime() + if !outTime.Equal(testTime) { + t.Errorf("times don't match: got %s, want %s", outTime, testTime) + } +} + +func TestFileHeaderRoundTrip(t *testing.T) { + fh := &FileHeader{ + Name: "foo.txt", + UncompressedSize: 987654321, + ModifiedTime: 1234, + ModifiedDate: 5678, + } + fi := fh.FileInfo() + fh2, err := FileInfoHeader(fi) + + // Ignore these fields: + fh2.CreatorVersion = 0 + fh2.ExternalAttrs = 0 + + if !reflect.DeepEqual(fh, fh2) { + t.Errorf("mismatch\n input=%#v\noutput=%#v\nerr=%v", fh, fh2, err) + } + if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh { + t.Errorf("Sys didn't return original *FileHeader") + } +} diff --git a/src/pkg/asn1/Makefile b/src/pkg/asn1/Makefile deleted file mode 100644 index 6b7770e82..000000000 --- a/src/pkg/asn1/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../Make.inc - -TARG=asn1 -GOFILES=\ - asn1.go\ - common.go\ - marshal.go\ - -include ../../Make.pkg diff --git a/src/pkg/asn1/asn1.go b/src/pkg/asn1/asn1.go deleted file mode 100644 index 39b676b41..000000000 --- a/src/pkg/asn1/asn1.go +++ /dev/null @@ -1,840 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package asn1 implements parsing of DER-encoded ASN.1 data structures, -// as defined in ITU-T Rec X.690. -// -// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' -// http://luca.ntop.org/Teaching/Appunti/asn1.html. -package asn1 - -// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc -// are different encoding formats for those objects. Here, we'll be dealing -// with DER, the Distinguished Encoding Rules. DER is used in X.509 because -// it's fast to parse and, unlike BER, has a unique encoding for every object. -// When calculating hashes over objects, it's important that the resulting -// bytes be the same at both ends and DER removes this margin of error. -// -// ASN.1 is very complex and this package doesn't attempt to implement -// everything by any means. - -import ( - "big" - "fmt" - "os" - "reflect" - "time" -) - -// A StructuralError suggests that the ASN.1 data is valid, but the Go type -// which is receiving it doesn't match. -type StructuralError struct { - Msg string -} - -func (e StructuralError) String() string { return "ASN.1 structure error: " + e.Msg } - -// A SyntaxError suggests that the ASN.1 data is invalid. -type SyntaxError struct { - Msg string -} - -func (e SyntaxError) String() string { return "ASN.1 syntax error: " + e.Msg } - -// We start by dealing with each of the primitive types in turn. - -// BOOLEAN - -func parseBool(bytes []byte) (ret bool, err os.Error) { - if len(bytes) != 1 { - err = SyntaxError{"invalid boolean"} - return - } - - return bytes[0] != 0, nil -} - -// INTEGER - -// parseInt64 treats the given bytes as a big-endian, signed integer and -// returns the result. -func parseInt64(bytes []byte) (ret int64, err os.Error) { - if len(bytes) > 8 { - // We'll overflow an int64 in this case. - err = StructuralError{"integer too large"} - return - } - for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { - ret <<= 8 - ret |= int64(bytes[bytesRead]) - } - - // Shift up and down in order to sign extend the result. - ret <<= 64 - uint8(len(bytes))*8 - ret >>= 64 - uint8(len(bytes))*8 - return -} - -// parseInt treats the given bytes as a big-endian, signed integer and returns -// the result. -func parseInt(bytes []byte) (int, os.Error) { - ret64, err := parseInt64(bytes) - if err != nil { - return 0, err - } - if ret64 != int64(int(ret64)) { - return 0, StructuralError{"integer too large"} - } - return int(ret64), nil -} - -var bigOne = big.NewInt(1) - -// parseBigInt treats the given bytes as a big-endian, signed integer and returns -// the result. -func parseBigInt(bytes []byte) *big.Int { - ret := new(big.Int) - if len(bytes) > 0 && bytes[0]&0x80 == 0x80 { - // This is a negative number. - notBytes := make([]byte, len(bytes)) - for i := range notBytes { - notBytes[i] = ^bytes[i] - } - ret.SetBytes(notBytes) - ret.Add(ret, bigOne) - ret.Neg(ret) - return ret - } - ret.SetBytes(bytes) - return ret -} - -// BIT STRING - -// BitString is the structure to use when you want an ASN.1 BIT STRING type. A -// bit string is padded up to the nearest byte in memory and the number of -// valid bits is recorded. Padding bits will be zero. -type BitString struct { - Bytes []byte // bits packed into bytes. - BitLength int // length in bits. -} - -// At returns the bit at the given index. If the index is out of range it -// returns false. -func (b BitString) At(i int) int { - if i < 0 || i >= b.BitLength { - return 0 - } - x := i / 8 - y := 7 - uint(i%8) - return int(b.Bytes[x]>>y) & 1 -} - -// RightAlign returns a slice where the padding bits are at the beginning. The -// slice may share memory with the BitString. -func (b BitString) RightAlign() []byte { - shift := uint(8 - (b.BitLength % 8)) - if shift == 8 || len(b.Bytes) == 0 { - return b.Bytes - } - - a := make([]byte, len(b.Bytes)) - a[0] = b.Bytes[0] >> shift - for i := 1; i < len(b.Bytes); i++ { - a[i] = b.Bytes[i-1] << (8 - shift) - a[i] |= b.Bytes[i] >> shift - } - - return a -} - -// parseBitString parses an ASN.1 bit string from the given byte slice and returns it. -func parseBitString(bytes []byte) (ret BitString, err os.Error) { - if len(bytes) == 0 { - err = SyntaxError{"zero length BIT STRING"} - return - } - paddingBits := int(bytes[0]) - if paddingBits > 7 || - len(bytes) == 1 && paddingBits > 0 || - bytes[len(bytes)-1]&((1< 4 { - err = StructuralError{"base 128 integer too large"} - return - } - ret <<= 7 - b := bytes[offset] - ret |= int(b & 0x7f) - offset++ - if b&0x80 == 0 { - return - } - } - err = SyntaxError{"truncated base 128 integer"} - return -} - -// UTCTime - -func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { - s := string(bytes) - ret, err = time.Parse("0601021504Z0700", s) - if err == nil { - return - } - ret, err = time.Parse("060102150405Z0700", s) - return -} - -// parseGeneralizedTime parses the GeneralizedTime from the given byte slice -// and returns the resulting time. -func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { - return time.Parse("20060102150405Z0700", string(bytes)) -} - -// PrintableString - -// parsePrintableString parses a ASN.1 PrintableString from the given byte -// array and returns it. -func parsePrintableString(bytes []byte) (ret string, err os.Error) { - for _, b := range bytes { - if !isPrintable(b) { - err = SyntaxError{"PrintableString contains invalid character"} - return - } - } - ret = string(bytes) - return -} - -// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. -func isPrintable(b byte) bool { - return 'a' <= b && b <= 'z' || - 'A' <= b && b <= 'Z' || - '0' <= b && b <= '9' || - '\'' <= b && b <= ')' || - '+' <= b && b <= '/' || - b == ' ' || - b == ':' || - b == '=' || - b == '?' || - // This is technically not allowed in a PrintableString. - // However, x509 certificates with wildcard strings don't - // always use the correct string type so we permit it. - b == '*' -} - -// IA5String - -// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given -// byte slice and returns it. -func parseIA5String(bytes []byte) (ret string, err os.Error) { - for _, b := range bytes { - if b >= 0x80 { - err = SyntaxError{"IA5String contains invalid character"} - return - } - } - ret = string(bytes) - return -} - -// T61String - -// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given -// byte slice and returns it. -func parseT61String(bytes []byte) (ret string, err os.Error) { - return string(bytes), nil -} - -// UTF8String - -// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte -// array and returns it. -func parseUTF8String(bytes []byte) (ret string, err os.Error) { - return string(bytes), nil -} - -// A RawValue represents an undecoded ASN.1 object. -type RawValue struct { - Class, Tag int - IsCompound bool - Bytes []byte - FullBytes []byte // includes the tag and length -} - -// RawContent is used to signal that the undecoded, DER data needs to be -// preserved for a struct. To use it, the first field of the struct must have -// this type. It's an error for any of the other fields to have this type. -type RawContent []byte - -// Tagging - -// parseTagAndLength parses an ASN.1 tag and length pair from the given offset -// into a byte slice. It returns the parsed data and the new offset. SET and -// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we -// don't distinguish between ordered and unordered objects in this code. -func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) { - offset = initOffset - b := bytes[offset] - offset++ - ret.class = int(b >> 6) - ret.isCompound = b&0x20 == 0x20 - ret.tag = int(b & 0x1f) - - // If the bottom five bits are set, then the tag number is actually base 128 - // encoded afterwards - if ret.tag == 0x1f { - ret.tag, offset, err = parseBase128Int(bytes, offset) - if err != nil { - return - } - } - if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} - return - } - b = bytes[offset] - offset++ - if b&0x80 == 0 { - // The length is encoded in the bottom 7 bits. - ret.length = int(b & 0x7f) - } else { - // Bottom 7 bits give the number of length bytes to follow. - numBytes := int(b & 0x7f) - // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. - if numBytes > 3 { - err = StructuralError{"length too large"} - return - } - if numBytes == 0 { - err = SyntaxError{"indefinite length found (not DER)"} - return - } - ret.length = 0 - for i := 0; i < numBytes; i++ { - if offset >= len(bytes) { - err = SyntaxError{"truncated tag or length"} - return - } - b = bytes[offset] - offset++ - ret.length <<= 8 - ret.length |= int(b) - } - } - - return -} - -// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse -// a number of ASN.1 values from the given byte slice and returns them as a -// slice of Go values of the given type. -func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err os.Error) { - expectedTag, compoundType, ok := getUniversalType(elemType) - if !ok { - err = StructuralError{"unknown Go type for slice"} - return - } - - // First we iterate over the input and count the number of elements, - // checking that the types are correct in each case. - numElements := 0 - for offset := 0; offset < len(bytes); { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so - // that a sequence of them can be parsed into a []string. - if t.tag == tagGeneralString { - t.tag = tagPrintableString - } - if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { - err = StructuralError{"sequence tag mismatch"} - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"truncated sequence"} - return - } - offset += t.length - numElements++ - } - ret = reflect.MakeSlice(sliceType, numElements, numElements) - params := fieldParameters{} - offset := 0 - for i := 0; i < numElements; i++ { - offset, err = parseField(ret.Index(i), bytes, offset, params) - if err != nil { - return - } - } - return -} - -var ( - bitStringType = reflect.TypeOf(BitString{}) - objectIdentifierType = reflect.TypeOf(ObjectIdentifier{}) - enumeratedType = reflect.TypeOf(Enumerated(0)) - flagType = reflect.TypeOf(Flag(false)) - timeType = reflect.TypeOf(&time.Time{}) - rawValueType = reflect.TypeOf(RawValue{}) - rawContentsType = reflect.TypeOf(RawContent(nil)) - bigIntType = reflect.TypeOf(new(big.Int)) -) - -// invalidLength returns true iff offset + length > sliceLength, or if the -// addition would overflow. -func invalidLength(offset, length, sliceLength int) bool { - return offset+length < offset || offset+length > sliceLength -} - -// parseField is the main parsing function. Given a byte slice and an offset -// into the array, it will try to parse a suitable ASN.1 value out and store it -// in the given Value. -func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) { - offset = initOffset - fieldType := v.Type() - - // If we have run out of data, it may be that there are optional elements at the end. - if offset == len(bytes) { - if !setDefaultValue(v, params) { - err = SyntaxError{"sequence truncated"} - } - return - } - - // Deal with raw values. - if fieldType == rawValueType { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} - offset += t.length - v.Set(reflect.ValueOf(result)) - return - } - - // Deal with the ANY type. - if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 { - var t tagAndLength - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - var result interface{} - if !t.isCompound && t.class == classUniversal { - innerBytes := bytes[offset : offset+t.length] - switch t.tag { - case tagPrintableString: - result, err = parsePrintableString(innerBytes) - case tagIA5String: - result, err = parseIA5String(innerBytes) - case tagT61String: - result, err = parseT61String(innerBytes) - case tagInteger: - result, err = parseInt64(innerBytes) - case tagBitString: - result, err = parseBitString(innerBytes) - case tagOID: - result, err = parseObjectIdentifier(innerBytes) - case tagUTCTime: - result, err = parseUTCTime(innerBytes) - case tagOctetString: - result = innerBytes - default: - // If we don't know how to handle the type, we just leave Value as nil. - } - } - offset += t.length - if err != nil { - return - } - if result != nil { - v.Set(reflect.ValueOf(result)) - } - return - } - universalTag, compoundType, ok1 := getUniversalType(fieldType) - if !ok1 { - err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} - return - } - - t, offset, err := parseTagAndLength(bytes, offset) - if err != nil { - return - } - if params.explicit { - expectedClass := classContextSpecific - if params.application { - expectedClass = classApplication - } - if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { - if t.length > 0 { - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { - return - } - } else { - if fieldType != flagType { - err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} - return - } - v.SetBool(true) - return - } - } else { - // The tags didn't match, it might be an optional element. - ok := setDefaultValue(v, params) - if ok { - offset = initOffset - } else { - err = StructuralError{"explicitly tagged member didn't match"} - } - return - } - } - - // Special case for strings: all the ASN.1 string types map to the Go - // type string. getUniversalType returns the tag for PrintableString - // when it sees a string, so if we see a different string type on the - // wire, we change the universal type to match. - if universalTag == tagPrintableString { - switch t.tag { - case tagIA5String, tagGeneralString, tagT61String, tagUTF8String: - universalTag = t.tag - } - } - - // Special case for time: UTCTime and GeneralizedTime both map to the - // Go type time.Time. - if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { - universalTag = tagGeneralizedTime - } - - expectedClass := classUniversal - expectedTag := universalTag - - if !params.explicit && params.tag != nil { - expectedClass = classContextSpecific - expectedTag = *params.tag - } - - if !params.explicit && params.application && params.tag != nil { - expectedClass = classApplication - expectedTag = *params.tag - } - - // We have unwrapped any explicit tagging at this point. - if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { - // Tags don't match. Again, it could be an optional element. - ok := setDefaultValue(v, params) - if ok { - offset = initOffset - } else { - err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} - } - return - } - if invalidLength(offset, t.length, len(bytes)) { - err = SyntaxError{"data truncated"} - return - } - innerBytes := bytes[offset : offset+t.length] - offset += t.length - - // We deal with the structures defined in this package first. - switch fieldType { - case objectIdentifierType: - newSlice, err1 := parseObjectIdentifier(innerBytes) - v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice))) - if err1 == nil { - reflect.Copy(v, reflect.ValueOf(newSlice)) - } - err = err1 - return - case bitStringType: - bs, err1 := parseBitString(innerBytes) - if err1 == nil { - v.Set(reflect.ValueOf(bs)) - } - err = err1 - return - case timeType: - var time *time.Time - var err1 os.Error - if universalTag == tagUTCTime { - time, err1 = parseUTCTime(innerBytes) - } else { - time, err1 = parseGeneralizedTime(innerBytes) - } - if err1 == nil { - v.Set(reflect.ValueOf(time)) - } - err = err1 - return - case enumeratedType: - parsedInt, err1 := parseInt(innerBytes) - if err1 == nil { - v.SetInt(int64(parsedInt)) - } - err = err1 - return - case flagType: - v.SetBool(true) - return - case bigIntType: - parsedInt := parseBigInt(innerBytes) - v.Set(reflect.ValueOf(parsedInt)) - return - } - switch val := v; val.Kind() { - case reflect.Bool: - parsedBool, err1 := parseBool(innerBytes) - if err1 == nil { - val.SetBool(parsedBool) - } - err = err1 - return - case reflect.Int, reflect.Int32: - parsedInt, err1 := parseInt(innerBytes) - if err1 == nil { - val.SetInt(int64(parsedInt)) - } - err = err1 - return - case reflect.Int64: - parsedInt, err1 := parseInt64(innerBytes) - if err1 == nil { - val.SetInt(parsedInt) - } - err = err1 - return - // TODO(dfc) Add support for the remaining integer types - case reflect.Struct: - structType := fieldType - - if structType.NumField() > 0 && - structType.Field(0).Type == rawContentsType { - bytes := bytes[initOffset:offset] - val.Field(0).Set(reflect.ValueOf(RawContent(bytes))) - } - - innerOffset := 0 - for i := 0; i < structType.NumField(); i++ { - field := structType.Field(i) - if i == 0 && field.Type == rawContentsType { - continue - } - innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1"))) - if err != nil { - return - } - } - // We allow extra bytes at the end of the SEQUENCE because - // adding elements to the end has been used in X.509 as the - // version numbers have increased. - return - case reflect.Slice: - sliceType := fieldType - if sliceType.Elem().Kind() == reflect.Uint8 { - val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) - reflect.Copy(val, reflect.ValueOf(innerBytes)) - return - } - newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) - if err1 == nil { - val.Set(newSlice) - } - err = err1 - return - case reflect.String: - var v string - switch universalTag { - case tagPrintableString: - v, err = parsePrintableString(innerBytes) - case tagIA5String: - v, err = parseIA5String(innerBytes) - case tagT61String: - v, err = parseT61String(innerBytes) - case tagUTF8String: - v, err = parseUTF8String(innerBytes) - case tagGeneralString: - // GeneralString is specified in ISO-2022/ECMA-35, - // A brief review suggests that it includes structures - // that allow the encoding to change midstring and - // such. We give up and pass it as an 8-bit string. - v, err = parseT61String(innerBytes) - default: - err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} - } - if err == nil { - val.SetString(v) - } - return - } - err = StructuralError{"unsupported: " + v.Type().String()} - return -} - -// setDefaultValue is used to install a default value, from a tag string, into -// a Value. It is successful is the field was optional, even if a default value -// wasn't provided or it failed to install it into the Value. -func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { - if !params.optional { - return - } - ok = true - if params.defaultValue == nil { - return - } - switch val := v; val.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - val.SetInt(*params.defaultValue) - } - return -} - -// Unmarshal parses the DER-encoded ASN.1 data structure b -// and uses the reflect package to fill in an arbitrary value pointed at by val. -// Because Unmarshal uses the reflect package, the structs -// being written to must use upper case field names. -// -// An ASN.1 INTEGER can be written to an int, int32 or int64. -// If the encoded value does not fit in the Go type, -// Unmarshal returns a parse error. -// -// An ASN.1 BIT STRING can be written to a BitString. -// -// An ASN.1 OCTET STRING can be written to a []byte. -// -// An ASN.1 OBJECT IDENTIFIER can be written to an -// ObjectIdentifier. -// -// An ASN.1 ENUMERATED can be written to an Enumerated. -// -// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. -// -// An ASN.1 PrintableString or IA5String can be written to a string. -// -// Any of the above ASN.1 values can be written to an interface{}. -// The value stored in the interface has the corresponding Go type. -// For integers, that type is int64. -// -// An ASN.1 SEQUENCE OF x or SET OF x can be written -// to a slice if an x can be written to the slice's element type. -// -// An ASN.1 SEQUENCE or SET can be written to a struct -// if each of the elements in the sequence can be -// written to the corresponding element in the struct. -// -// The following tags on struct fields have special meaning to Unmarshal: -// -// optional marks the field as ASN.1 OPTIONAL -// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC -// default:x sets the default value for optional integer fields -// -// If the type of the first field of a structure is RawContent then the raw -// ASN1 contents of the struct will be stored in it. -// -// Other ASN.1 types are not supported; if it encounters them, -// Unmarshal returns a parse error. -func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { - return UnmarshalWithParams(b, val, "") -} - -// UnmarshalWithParams allows field parameters to be specified for the -// top-level element. The form of the params is the same as the field tags. -func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) { - v := reflect.ValueOf(val).Elem() - offset, err := parseField(v, b, 0, parseFieldParameters(params)) - if err != nil { - return nil, err - } - return b[offset:], nil -} diff --git a/src/pkg/asn1/asn1_test.go b/src/pkg/asn1/asn1_test.go deleted file mode 100644 index 9f48f7bdd..000000000 --- a/src/pkg/asn1/asn1_test.go +++ /dev/null @@ -1,689 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package asn1 - -import ( - "bytes" - "reflect" - "testing" - "time" -) - -type int64Test struct { - in []byte - ok bool - out int64 -} - -var int64TestData = []int64Test{ - {[]byte{0x00}, true, 0}, - {[]byte{0x7f}, true, 127}, - {[]byte{0x00, 0x80}, true, 128}, - {[]byte{0x01, 0x00}, true, 256}, - {[]byte{0x80}, true, -128}, - {[]byte{0xff, 0x7f}, true, -129}, - {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, true, -1}, - {[]byte{0xff}, true, -1}, - {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808}, - {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0}, -} - -func TestParseInt64(t *testing.T) { - for i, test := range int64TestData { - ret, err := parseInt64(test.in) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if test.ok && ret != test.out { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } -} - -type int32Test struct { - in []byte - ok bool - out int32 -} - -var int32TestData = []int32Test{ - {[]byte{0x00}, true, 0}, - {[]byte{0x7f}, true, 127}, - {[]byte{0x00, 0x80}, true, 128}, - {[]byte{0x01, 0x00}, true, 256}, - {[]byte{0x80}, true, -128}, - {[]byte{0xff, 0x7f}, true, -129}, - {[]byte{0xff, 0xff, 0xff, 0xff}, true, -1}, - {[]byte{0xff}, true, -1}, - {[]byte{0x80, 0x00, 0x00, 0x00}, true, -2147483648}, - {[]byte{0x80, 0x00, 0x00, 0x00, 0x00}, false, 0}, -} - -func TestParseInt32(t *testing.T) { - for i, test := range int32TestData { - ret, err := parseInt(test.in) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if test.ok && int32(ret) != test.out { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } -} - -var bigIntTests = []struct { - in []byte - base10 string -}{ - {[]byte{0xff}, "-1"}, - {[]byte{0x00}, "0"}, - {[]byte{0x01}, "1"}, - {[]byte{0x00, 0xff}, "255"}, - {[]byte{0xff, 0x00}, "-256"}, - {[]byte{0x01, 0x00}, "256"}, -} - -func TestParseBigInt(t *testing.T) { - for i, test := range bigIntTests { - ret := parseBigInt(test.in) - if ret.String() != test.base10 { - t.Errorf("#%d: bad result from %x, got %s want %s", i, test.in, ret.String(), test.base10) - } - fw := newForkableWriter() - marshalBigInt(fw, ret) - result := fw.Bytes() - if !bytes.Equal(result, test.in) { - t.Errorf("#%d: got %x from marshaling %s, want %x", i, result, ret, test.in) - } - } -} - -type bitStringTest struct { - in []byte - ok bool - out []byte - bitLength int -} - -var bitStringTestData = []bitStringTest{ - {[]byte{}, false, []byte{}, 0}, - {[]byte{0x00}, true, []byte{}, 0}, - {[]byte{0x07, 0x00}, true, []byte{0x00}, 1}, - {[]byte{0x07, 0x01}, false, []byte{}, 0}, - {[]byte{0x07, 0x40}, false, []byte{}, 0}, - {[]byte{0x08, 0x00}, false, []byte{}, 0}, -} - -func TestBitString(t *testing.T) { - for i, test := range bitStringTestData { - ret, err := parseBitString(test.in) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil { - if test.bitLength != ret.BitLength || bytes.Compare(ret.Bytes, test.out) != 0 { - t.Errorf("#%d: Bad result: %v (expected %v %v)", i, ret, test.out, test.bitLength) - } - } - } -} - -func TestBitStringAt(t *testing.T) { - bs := BitString{[]byte{0x82, 0x40}, 16} - if bs.At(0) != 1 { - t.Error("#1: Failed") - } - if bs.At(1) != 0 { - t.Error("#2: Failed") - } - if bs.At(6) != 1 { - t.Error("#3: Failed") - } - if bs.At(9) != 1 { - t.Error("#4: Failed") - } -} - -type bitStringRightAlignTest struct { - in []byte - inlen int - out []byte -} - -var bitStringRightAlignTests = []bitStringRightAlignTest{ - {[]byte{0x80}, 1, []byte{0x01}}, - {[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}}, - {[]byte{}, 0, []byte{}}, - {[]byte{0xce}, 8, []byte{0xce}}, - {[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}}, - {[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}}, -} - -func TestBitStringRightAlign(t *testing.T) { - for i, test := range bitStringRightAlignTests { - bs := BitString{test.in, test.inlen} - out := bs.RightAlign() - if bytes.Compare(out, test.out) != 0 { - t.Errorf("#%d got: %x want: %x", i, out, test.out) - } - } -} - -type objectIdentifierTest struct { - in []byte - ok bool - out []int -} - -var objectIdentifierTestData = []objectIdentifierTest{ - {[]byte{}, false, []int{}}, - {[]byte{85}, true, []int{2, 5}}, - {[]byte{85, 0x02}, true, []int{2, 5, 2}}, - {[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}}, - {[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}}, -} - -func TestObjectIdentifier(t *testing.T) { - for i, test := range objectIdentifierTestData { - ret, err := parseObjectIdentifier(test.in) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil { - if !reflect.DeepEqual(test.out, ret) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } - } -} - -type timeTest struct { - in string - ok bool - out *time.Time -} - -var utcTestData = []timeTest{ - {"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, -7 * 60 * 60, ""}}, - {"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, 7*60*60 + 30*60, ""}}, - {"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, 0, "UTC"}}, - {"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, 0, "UTC"}}, - {"a10506234540Z", false, nil}, - {"91a506234540Z", false, nil}, - {"9105a6234540Z", false, nil}, - {"910506a34540Z", false, nil}, - {"910506334a40Z", false, nil}, - {"91050633444aZ", false, nil}, - {"910506334461Z", false, nil}, - {"910506334400Za", false, nil}, -} - -func TestUTCTime(t *testing.T) { - for i, test := range utcTestData { - ret, err := parseUTCTime([]byte(test.in)) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil { - if !reflect.DeepEqual(test.out, ret) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } - } -} - -var generalizedTimeTestData = []timeTest{ - {"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 0, "UTC"}}, - {"20100102030405", false, nil}, - {"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 6*60*60 + 7*60, ""}}, - {"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, -6*60*60 - 7*60, ""}}, -} - -func TestGeneralizedTime(t *testing.T) { - for i, test := range generalizedTimeTestData { - ret, err := parseGeneralizedTime([]byte(test.in)) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil { - if !reflect.DeepEqual(test.out, ret) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) - } - } - } -} - -type tagAndLengthTest struct { - in []byte - ok bool - out tagAndLength -} - -var tagAndLengthData = []tagAndLengthTest{ - {[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}}, - {[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}}, - {[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}}, - {[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}}, - {[]byte{0x1f, 0x01, 0x00}, true, tagAndLength{0, 1, 0, false}}, - {[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}}, - {[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}}, - {[]byte{0x00, 0x81, 0x01}, true, tagAndLength{0, 0, 1, false}}, - {[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}}, - {[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}}, - {[]byte{0x1f, 0x85}, false, tagAndLength{}}, - {[]byte{0x30, 0x80}, false, tagAndLength{}}, -} - -func TestParseTagAndLength(t *testing.T) { - for i, test := range tagAndLengthData { - tagAndLength, _, err := parseTagAndLength(test.in, 0) - if (err == nil) != test.ok { - t.Errorf("#%d: Incorrect error result (did pass? %v, expected: %v)", i, err == nil, test.ok) - } - if err == nil && !reflect.DeepEqual(test.out, tagAndLength) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, tagAndLength, test.out) - } - } -} - -type parseFieldParametersTest struct { - in string - out fieldParameters -} - -func newInt(n int) *int { return &n } - -func newInt64(n int64) *int64 { return &n } - -func newString(s string) *string { return &s } - -func newBool(b bool) *bool { return &b } - -var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParametersTest{ - {"", fieldParameters{}}, - {"ia5", fieldParameters{stringType: tagIA5String}}, - {"printable", fieldParameters{stringType: tagPrintableString}}, - {"optional", fieldParameters{optional: true}}, - {"explicit", fieldParameters{explicit: true, tag: new(int)}}, - {"application", fieldParameters{application: true, tag: new(int)}}, - {"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}}, - {"default:42", fieldParameters{defaultValue: newInt64(42)}}, - {"tag:17", fieldParameters{tag: newInt(17)}}, - {"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}}, - {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false}}, - {"set", fieldParameters{set: true}}, -} - -func TestParseFieldParameters(t *testing.T) { - for i, test := range parseFieldParametersTestData { - f := parseFieldParameters(test.in) - if !reflect.DeepEqual(f, test.out) { - t.Errorf("#%d: Bad result: %v (expected %v)", i, f, test.out) - } - } -} - -type TestObjectIdentifierStruct struct { - OID ObjectIdentifier -} - -type TestContextSpecificTags struct { - A int `asn1:"tag:1"` -} - -type TestContextSpecificTags2 struct { - A int `asn1:"explicit,tag:1"` - B int -} - -type TestElementsAfterString struct { - S string - A, B int -} - -var unmarshalTestData = []struct { - in []byte - out interface{} -}{ - {[]byte{0x02, 0x01, 0x42}, newInt(0x42)}, - {[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}}, - {[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}}, - {[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}}, - {[]byte{0x02, 0x01, 0x10}, newInt(16)}, - {[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")}, - {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")}, - {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test"), []byte("\x16\x04test")}}, - {[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}}, - {[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, - {[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, - {[]byte{0x01, 0x01, 0x00}, newBool(false)}, - {[]byte{0x01, 0x01, 0x01}, newBool(true)}, - {[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}}, -} - -func TestUnmarshal(t *testing.T) { - for i, test := range unmarshalTestData { - pv := reflect.New(reflect.TypeOf(test.out).Elem()) - val := pv.Interface() - _, err := Unmarshal(test.in, val) - if err != nil { - t.Errorf("Unmarshal failed at index %d %v", i, err) - } - if !reflect.DeepEqual(val, test.out) { - t.Errorf("#%d:\nhave %#v\nwant %#v", i, val, test.out) - } - } -} - -type Certificate struct { - TBSCertificate TBSCertificate - SignatureAlgorithm AlgorithmIdentifier - SignatureValue BitString -} - -type TBSCertificate struct { - Version int `asn1:"optional,explicit,default:0,tag:0"` - SerialNumber RawValue - SignatureAlgorithm AlgorithmIdentifier - Issuer RDNSequence - Validity Validity - Subject RDNSequence - PublicKey PublicKeyInfo -} - -type AlgorithmIdentifier struct { - Algorithm ObjectIdentifier -} - -type RDNSequence []RelativeDistinguishedNameSET - -type RelativeDistinguishedNameSET []AttributeTypeAndValue - -type AttributeTypeAndValue struct { - Type ObjectIdentifier - Value interface{} -} - -type Validity struct { - NotBefore, NotAfter *time.Time -} - -type PublicKeyInfo struct { - Algorithm AlgorithmIdentifier - PublicKey BitString -} - -func TestCertificate(t *testing.T) { - // This is a minimal, self-signed certificate that should parse correctly. - var cert Certificate - if _, err := Unmarshal(derEncodedSelfSignedCertBytes, &cert); err != nil { - t.Errorf("Unmarshal failed: %v", err) - } - if !reflect.DeepEqual(cert, derEncodedSelfSignedCert) { - t.Errorf("Bad result:\ngot: %+v\nwant: %+v", cert, derEncodedSelfSignedCert) - } -} - -func TestCertificateWithNUL(t *testing.T) { - // This is the paypal NUL-hack certificate. It should fail to parse because - // NUL isn't a permitted character in a PrintableString. - - var cert Certificate - if _, err := Unmarshal(derEncodedPaypalNULCertBytes, &cert); err == nil { - t.Error("Unmarshal succeeded, should not have") - } -} - -type rawStructTest struct { - Raw RawContent - A int -} - -func TestRawStructs(t *testing.T) { - var s rawStructTest - input := []byte{0x30, 0x03, 0x02, 0x01, 0x50} - - rest, err := Unmarshal(input, &s) - if len(rest) != 0 { - t.Errorf("incomplete parse: %x", rest) - return - } - if err != nil { - t.Error(err) - return - } - if s.A != 0x50 { - t.Errorf("bad value for A: got %d want %d", s.A, 0x50) - } - if bytes.Compare([]byte(s.Raw), input) != 0 { - t.Errorf("bad value for Raw: got %x want %x", s.Raw, input) - } -} - -var derEncodedSelfSignedCert = Certificate{ - TBSCertificate: TBSCertificate{ - Version: 0, - SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, - SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, - Issuer: RDNSequence{ - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, - }, - Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}}, - Subject: RDNSequence{ - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, - RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, - }, - PublicKey: PublicKeyInfo{ - Algorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}}, - PublicKey: BitString{ - Bytes: []uint8{ - 0x30, 0x48, 0x2, 0x41, 0x0, 0xcd, 0xb7, - 0x63, 0x9c, 0x32, 0x78, 0xf0, 0x6, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, - 0x90, 0x2b, 0x59, 0x2d, 0x8c, 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, - 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, - 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, 0x96, 0x57, 0x72, 0x2a, 0x4f, - 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, 0xdc, 0x8f, 0xde, 0xec, - 0x35, 0x7d, 0x2, 0x3, 0x1, 0x0, 0x1, - }, - BitLength: 592, - }, - }, - }, - SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, - SignatureValue: BitString{ - Bytes: []uint8{ - 0xa6, 0x7b, 0x6, 0xec, 0x5e, 0xce, - 0x92, 0x77, 0x2c, 0xa4, 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, - 0x7b, 0x45, 0x11, 0xcd, 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x4, 0x2, 0xdf, 0x2b, - 0x99, 0x8b, 0xb9, 0xa4, 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, - 0xd9, 0x1e, 0xde, 0x14, 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, - 0xfa, 0x88, 0x21, 0x49, 0x4, 0x35, - }, - BitLength: 512, - }, -} - -var derEncodedSelfSignedCertBytes = []byte{ - 0x30, 0x82, 0x02, 0x18, 0x30, - 0x82, 0x01, 0xc2, 0x02, 0x09, 0x00, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, - 0x98, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, - 0x05, 0x05, 0x00, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, - 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, - 0x69, 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, - 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, - 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, - 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, - 0x73, 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, - 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, - 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, - 0x30, 0x39, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, 0x33, 0x5a, - 0x17, 0x0d, 0x31, 0x30, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, - 0x33, 0x5a, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, - 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, 0x69, - 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x1a, - 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, 0x73, - 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, - 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, - 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x5c, 0x30, 0x0d, 0x06, - 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, - 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xcd, 0xb7, 0x63, 0x9c, 0x32, 0x78, - 0xf0, 0x06, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, 0x90, 0x2b, 0x59, 0x2d, 0x8c, - 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, - 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, - 0x96, 0x57, 0x72, 0x2a, 0x4f, 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, - 0xdc, 0x8f, 0xde, 0xec, 0x35, 0x7d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, - 0x03, 0x41, 0x00, 0xa6, 0x7b, 0x06, 0xec, 0x5e, 0xce, 0x92, 0x77, 0x2c, 0xa4, - 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, 0x7b, 0x45, 0x11, 0xcd, - 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x04, 0x02, 0xdf, 0x2b, 0x99, 0x8b, 0xb9, 0xa4, - 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, 0xd9, 0x1e, 0xde, 0x14, - 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, 0xfa, 0x88, 0x21, 0x49, - 0x04, 0x35, -} - -var derEncodedPaypalNULCertBytes = []byte{ - 0x30, 0x82, 0x06, 0x44, 0x30, - 0x82, 0x05, 0xad, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x00, 0xf0, 0x9b, - 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, - 0x05, 0x00, 0x30, 0x82, 0x01, 0x12, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, - 0x04, 0x08, 0x13, 0x09, 0x42, 0x61, 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, - 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x42, 0x61, - 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, - 0x55, 0x04, 0x0a, 0x13, 0x20, 0x49, 0x50, 0x53, 0x20, 0x43, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x73, 0x2e, 0x6c, 0x2e, 0x31, 0x2e, - 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x14, 0x25, 0x67, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, - 0x20, 0x43, 0x2e, 0x49, 0x2e, 0x46, 0x2e, 0x20, 0x20, 0x42, 0x2d, 0x42, 0x36, - 0x32, 0x32, 0x31, 0x30, 0x36, 0x39, 0x35, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, - 0x55, 0x04, 0x0b, 0x13, 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, - 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, - 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, - 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, - 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, - 0x01, 0x16, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, - 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, - 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, 0x17, 0x0d, - 0x31, 0x31, 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, - 0x30, 0x81, 0x94, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, - 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x53, 0x61, 0x6e, 0x20, - 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x31, 0x11, 0x30, 0x0f, - 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0b, - 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x55, 0x6e, 0x69, 0x74, 0x31, 0x2f, - 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x77, 0x77, 0x77, 0x2e, - 0x70, 0x61, 0x79, 0x70, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x73, 0x73, - 0x6c, 0x2e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x63, 0x63, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, - 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xd2, 0x69, - 0xfa, 0x6f, 0x3a, 0x00, 0xb4, 0x21, 0x1b, 0xc8, 0xb1, 0x02, 0xd7, 0x3f, 0x19, - 0xb2, 0xc4, 0x6d, 0xb4, 0x54, 0xf8, 0x8b, 0x8a, 0xcc, 0xdb, 0x72, 0xc2, 0x9e, - 0x3c, 0x60, 0xb9, 0xc6, 0x91, 0x3d, 0x82, 0xb7, 0x7d, 0x99, 0xff, 0xd1, 0x29, - 0x84, 0xc1, 0x73, 0x53, 0x9c, 0x82, 0xdd, 0xfc, 0x24, 0x8c, 0x77, 0xd5, 0x41, - 0xf3, 0xe8, 0x1e, 0x42, 0xa1, 0xad, 0x2d, 0x9e, 0xff, 0x5b, 0x10, 0x26, 0xce, - 0x9d, 0x57, 0x17, 0x73, 0x16, 0x23, 0x38, 0xc8, 0xd6, 0xf1, 0xba, 0xa3, 0x96, - 0x5b, 0x16, 0x67, 0x4a, 0x4f, 0x73, 0x97, 0x3a, 0x4d, 0x14, 0xa4, 0xf4, 0xe2, - 0x3f, 0x8b, 0x05, 0x83, 0x42, 0xd1, 0xd0, 0xdc, 0x2f, 0x7a, 0xe5, 0xb6, 0x10, - 0xb2, 0x11, 0xc0, 0xdc, 0x21, 0x2a, 0x90, 0xff, 0xae, 0x97, 0x71, 0x5a, 0x49, - 0x81, 0xac, 0x40, 0xf3, 0x3b, 0xb8, 0x59, 0xb2, 0x4f, 0x02, 0x03, 0x01, 0x00, - 0x01, 0xa3, 0x82, 0x03, 0x21, 0x30, 0x82, 0x03, 0x1d, 0x30, 0x09, 0x06, 0x03, - 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x11, 0x06, 0x09, 0x60, 0x86, - 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x06, 0x40, - 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xf8, - 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, - 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x61, 0x8f, 0x61, 0x34, 0x43, 0x55, 0x14, - 0x7f, 0x27, 0x09, 0xce, 0x4c, 0x8b, 0xea, 0x9b, 0x7b, 0x19, 0x25, 0xbc, 0x6e, - 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, - 0x0e, 0x07, 0x60, 0xd4, 0x39, 0xc9, 0x1b, 0x5b, 0x5d, 0x90, 0x7b, 0x23, 0xc8, - 0xd2, 0x34, 0x9d, 0x4a, 0x9a, 0x46, 0x39, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, - 0x11, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, - 0x15, 0x30, 0x13, 0x81, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, - 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x72, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x65, 0x16, 0x63, - 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, - 0x4f, 0x54, 0x20, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x44, 0x2e, - 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x68, - 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, - 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x2f, 0x06, 0x09, 0x60, - 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x02, 0x04, 0x22, 0x16, 0x20, 0x68, - 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, - 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, - 0x32, 0x30, 0x30, 0x32, 0x2f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, - 0x86, 0xf8, 0x42, 0x01, 0x04, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, - 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, - 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, - 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x03, 0x04, 0x39, 0x16, 0x37, - 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, - 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, - 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, - 0x6d, 0x6c, 0x3f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, - 0x42, 0x01, 0x07, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, - 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, - 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, - 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x3f, 0x30, 0x41, 0x06, 0x09, 0x60, 0x86, - 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x08, 0x04, 0x34, 0x16, 0x32, 0x68, 0x74, - 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, - 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, - 0x30, 0x30, 0x32, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x4c, 0x41, - 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x30, 0x81, 0x83, 0x06, - 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x7c, 0x30, 0x7a, 0x30, 0x39, 0xa0, 0x37, 0xa0, - 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, - 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, - 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, - 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, - 0x72, 0x6c, 0x30, 0x3d, 0xa0, 0x3b, 0xa0, 0x39, 0x86, 0x37, 0x68, 0x74, 0x74, - 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x69, - 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, - 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, - 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, - 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, - 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, - 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, - 0x73, 0x70, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, - 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x68, 0xee, 0x79, 0x97, 0x97, 0xdd, 0x3b, - 0xef, 0x16, 0x6a, 0x06, 0xf2, 0x14, 0x9a, 0x6e, 0xcd, 0x9e, 0x12, 0xf7, 0xaa, - 0x83, 0x10, 0xbd, 0xd1, 0x7c, 0x98, 0xfa, 0xc7, 0xae, 0xd4, 0x0e, 0x2c, 0x9e, - 0x38, 0x05, 0x9d, 0x52, 0x60, 0xa9, 0x99, 0x0a, 0x81, 0xb4, 0x98, 0x90, 0x1d, - 0xae, 0xbb, 0x4a, 0xd7, 0xb9, 0xdc, 0x88, 0x9e, 0x37, 0x78, 0x41, 0x5b, 0xf7, - 0x82, 0xa5, 0xf2, 0xba, 0x41, 0x25, 0x5a, 0x90, 0x1a, 0x1e, 0x45, 0x38, 0xa1, - 0x52, 0x58, 0x75, 0x94, 0x26, 0x44, 0xfb, 0x20, 0x07, 0xba, 0x44, 0xcc, 0xe5, - 0x4a, 0x2d, 0x72, 0x3f, 0x98, 0x47, 0xf6, 0x26, 0xdc, 0x05, 0x46, 0x05, 0x07, - 0x63, 0x21, 0xab, 0x46, 0x9b, 0x9c, 0x78, 0xd5, 0x54, 0x5b, 0x3d, 0x0c, 0x1e, - 0xc8, 0x64, 0x8c, 0xb5, 0x50, 0x23, 0x82, 0x6f, 0xdb, 0xb8, 0x22, 0x1c, 0x43, - 0x96, 0x07, 0xa8, 0xbb, -} diff --git a/src/pkg/asn1/common.go b/src/pkg/asn1/common.go deleted file mode 100644 index 01f4f7b6e..000000000 --- a/src/pkg/asn1/common.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package asn1 - -import ( - "reflect" - "strconv" - "strings" -) - -// ASN.1 objects have metadata preceding them: -// the tag: the type of the object -// a flag denoting if this object is compound or not -// the class type: the namespace of the tag -// the length of the object, in bytes - -// Here are some standard tags and classes - -const ( - tagBoolean = 1 - tagInteger = 2 - tagBitString = 3 - tagOctetString = 4 - tagOID = 6 - tagEnum = 10 - tagUTF8String = 12 - tagSequence = 16 - tagSet = 17 - tagPrintableString = 19 - tagT61String = 20 - tagIA5String = 22 - tagUTCTime = 23 - tagGeneralizedTime = 24 - tagGeneralString = 27 -) - -const ( - classUniversal = 0 - classApplication = 1 - classContextSpecific = 2 - classPrivate = 3 -) - -type tagAndLength struct { - class, tag, length int - isCompound bool -} - -// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead -// of" and "in addition to". When not specified, every primitive type has a -// default tag in the UNIVERSAL class. -// -// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1 -// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT -// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another. -// -// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an -// /additional/ tag would wrap the default tag. This explicit tag will have the -// compound flag set. -// -// (This is used in order to remove ambiguity with optional elements.) -// -// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we -// don't support that here. We support a single layer of EXPLICIT or IMPLICIT -// tagging with tag strings on the fields of a structure. - -// fieldParameters is the parsed representation of tag string from a structure field. -type fieldParameters struct { - optional bool // true iff the field is OPTIONAL - explicit bool // true iff an EXPLICIT tag is in use. - application bool // true iff an APPLICATION tag is in use. - defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). - tag *int // the EXPLICIT or IMPLICIT tag (maybe nil). - stringType int // the string tag to use when marshaling. - set bool // true iff this should be encoded as a SET - - // Invariants: - // if explicit is set, tag is non-nil. -} - -// Given a tag string with the format specified in the package comment, -// parseFieldParameters will parse it into a fieldParameters structure, -// ignoring unknown parts of the string. -func parseFieldParameters(str string) (ret fieldParameters) { - for _, part := range strings.Split(str, ",") { - switch { - case part == "optional": - ret.optional = true - case part == "explicit": - ret.explicit = true - if ret.tag == nil { - ret.tag = new(int) - } - case part == "ia5": - ret.stringType = tagIA5String - case part == "printable": - ret.stringType = tagPrintableString - case strings.HasPrefix(part, "default:"): - i, err := strconv.Atoi64(part[8:]) - if err == nil { - ret.defaultValue = new(int64) - *ret.defaultValue = i - } - case strings.HasPrefix(part, "tag:"): - i, err := strconv.Atoi(part[4:]) - if err == nil { - ret.tag = new(int) - *ret.tag = i - } - case part == "set": - ret.set = true - case part == "application": - ret.application = true - if ret.tag == nil { - ret.tag = new(int) - } - } - } - return -} - -// Given a reflected Go type, getUniversalType returns the default tag number -// and expected compound flag. -func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) { - switch t { - case objectIdentifierType: - return tagOID, false, true - case bitStringType: - return tagBitString, false, true - case timeType: - return tagUTCTime, false, true - case enumeratedType: - return tagEnum, false, true - case bigIntType: - return tagInteger, false, true - } - switch t.Kind() { - case reflect.Bool: - return tagBoolean, false, true - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return tagInteger, false, true - case reflect.Struct: - return tagSequence, true, true - case reflect.Slice: - if t.Elem().Kind() == reflect.Uint8 { - return tagOctetString, false, true - } - if strings.HasSuffix(t.Name(), "SET") { - return tagSet, true, true - } - return tagSequence, true, true - case reflect.String: - return tagPrintableString, false, true - } - return 0, false, false -} diff --git a/src/pkg/asn1/marshal.go b/src/pkg/asn1/marshal.go deleted file mode 100644 index d7eb63bf8..000000000 --- a/src/pkg/asn1/marshal.go +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package asn1 - -import ( - "big" - "bytes" - "fmt" - "io" - "os" - "reflect" - "time" -) - -// A forkableWriter is an in-memory buffer that can be -// 'forked' to create new forkableWriters that bracket the -// original. After -// pre, post := w.fork(); -// the overall sequence of bytes represented is logically w+pre+post. -type forkableWriter struct { - *bytes.Buffer - pre, post *forkableWriter -} - -func newForkableWriter() *forkableWriter { - return &forkableWriter{bytes.NewBuffer(nil), nil, nil} -} - -func (f *forkableWriter) fork() (pre, post *forkableWriter) { - if f.pre != nil || f.post != nil { - panic("have already forked") - } - f.pre = newForkableWriter() - f.post = newForkableWriter() - return f.pre, f.post -} - -func (f *forkableWriter) Len() (l int) { - l += f.Buffer.Len() - if f.pre != nil { - l += f.pre.Len() - } - if f.post != nil { - l += f.post.Len() - } - return -} - -func (f *forkableWriter) writeTo(out io.Writer) (n int, err os.Error) { - n, err = out.Write(f.Bytes()) - if err != nil { - return - } - - var nn int - - if f.pre != nil { - nn, err = f.pre.writeTo(out) - n += nn - if err != nil { - return - } - } - - if f.post != nil { - nn, err = f.post.writeTo(out) - n += nn - } - return -} - -func marshalBase128Int(out *forkableWriter, n int64) (err os.Error) { - if n == 0 { - err = out.WriteByte(0) - return - } - - l := 0 - for i := n; i > 0; i >>= 7 { - l++ - } - - for i := l - 1; i >= 0; i-- { - o := byte(n >> uint(i*7)) - o &= 0x7f - if i != 0 { - o |= 0x80 - } - err = out.WriteByte(o) - if err != nil { - return - } - } - - return nil -} - -func marshalInt64(out *forkableWriter, i int64) (err os.Error) { - n := int64Length(i) - - for ; n > 0; n-- { - err = out.WriteByte(byte(i >> uint((n-1)*8))) - if err != nil { - return - } - } - - return nil -} - -func int64Length(i int64) (numBytes int) { - numBytes = 1 - - for i > 127 { - numBytes++ - i >>= 8 - } - - for i < -128 { - numBytes++ - i >>= 8 - } - - return -} - -func marshalBigInt(out *forkableWriter, n *big.Int) (err os.Error) { - if n.Sign() < 0 { - // A negative number has to be converted to two's-complement - // form. So we'll subtract 1 and invert. If the - // most-significant-bit isn't set then we'll need to pad the - // beginning with 0xff in order to keep the number negative. - nMinus1 := new(big.Int).Neg(n) - nMinus1.Sub(nMinus1, bigOne) - bytes := nMinus1.Bytes() - for i := range bytes { - bytes[i] ^= 0xff - } - if len(bytes) == 0 || bytes[0]&0x80 == 0 { - err = out.WriteByte(0xff) - if err != nil { - return - } - } - _, err = out.Write(bytes) - } else if n.Sign() == 0 { - // Zero is written as a single 0 zero rather than no bytes. - err = out.WriteByte(0x00) - } else { - bytes := n.Bytes() - if len(bytes) > 0 && bytes[0]&0x80 != 0 { - // We'll have to pad this with 0x00 in order to stop it - // looking like a negative number. - err = out.WriteByte(0) - if err != nil { - return - } - } - _, err = out.Write(bytes) - } - return -} - -func marshalLength(out *forkableWriter, i int) (err os.Error) { - n := lengthLength(i) - - for ; n > 0; n-- { - err = out.WriteByte(byte(i >> uint((n-1)*8))) - if err != nil { - return - } - } - - return nil -} - -func lengthLength(i int) (numBytes int) { - numBytes = 1 - for i > 255 { - numBytes++ - i >>= 8 - } - return -} - -func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err os.Error) { - b := uint8(t.class) << 6 - if t.isCompound { - b |= 0x20 - } - if t.tag >= 31 { - b |= 0x1f - err = out.WriteByte(b) - if err != nil { - return - } - err = marshalBase128Int(out, int64(t.tag)) - if err != nil { - return - } - } else { - b |= uint8(t.tag) - err = out.WriteByte(b) - if err != nil { - return - } - } - - if t.length >= 128 { - l := lengthLength(t.length) - err = out.WriteByte(0x80 | byte(l)) - if err != nil { - return - } - err = marshalLength(out, t.length) - if err != nil { - return - } - } else { - err = out.WriteByte(byte(t.length)) - if err != nil { - return - } - } - - return nil -} - -func marshalBitString(out *forkableWriter, b BitString) (err os.Error) { - paddingBits := byte((8 - b.BitLength%8) % 8) - err = out.WriteByte(paddingBits) - if err != nil { - return - } - _, err = out.Write(b.Bytes) - return -} - -func marshalObjectIdentifier(out *forkableWriter, oid []int) (err os.Error) { - if len(oid) < 2 || oid[0] > 6 || oid[1] >= 40 { - return StructuralError{"invalid object identifier"} - } - - err = out.WriteByte(byte(oid[0]*40 + oid[1])) - if err != nil { - return - } - for i := 2; i < len(oid); i++ { - err = marshalBase128Int(out, int64(oid[i])) - if err != nil { - return - } - } - - return -} - -func marshalPrintableString(out *forkableWriter, s string) (err os.Error) { - b := []byte(s) - for _, c := range b { - if !isPrintable(c) { - return StructuralError{"PrintableString contains invalid character"} - } - } - - _, err = out.Write(b) - return -} - -func marshalIA5String(out *forkableWriter, s string) (err os.Error) { - b := []byte(s) - for _, c := range b { - if c > 127 { - return StructuralError{"IA5String contains invalid character"} - } - } - - _, err = out.Write(b) - return -} - -func marshalTwoDigits(out *forkableWriter, v int) (err os.Error) { - err = out.WriteByte(byte('0' + (v/10)%10)) - if err != nil { - return - } - return out.WriteByte(byte('0' + v%10)) -} - -func marshalUTCTime(out *forkableWriter, t *time.Time) (err os.Error) { - switch { - case 1950 <= t.Year && t.Year < 2000: - err = marshalTwoDigits(out, int(t.Year-1900)) - case 2000 <= t.Year && t.Year < 2050: - err = marshalTwoDigits(out, int(t.Year-2000)) - default: - return StructuralError{"Cannot represent time as UTCTime"} - } - - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Month) - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Day) - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Hour) - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Minute) - if err != nil { - return - } - - err = marshalTwoDigits(out, t.Second) - if err != nil { - return - } - - switch { - case t.ZoneOffset/60 == 0: - err = out.WriteByte('Z') - return - case t.ZoneOffset > 0: - err = out.WriteByte('+') - case t.ZoneOffset < 0: - err = out.WriteByte('-') - } - - if err != nil { - return - } - - offsetMinutes := t.ZoneOffset / 60 - if offsetMinutes < 0 { - offsetMinutes = -offsetMinutes - } - - err = marshalTwoDigits(out, offsetMinutes/60) - if err != nil { - return - } - - err = marshalTwoDigits(out, offsetMinutes%60) - return -} - -func stripTagAndLength(in []byte) []byte { - _, offset, err := parseTagAndLength(in, 0) - if err != nil { - return in - } - return in[offset:] -} - -func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err os.Error) { - switch value.Type() { - case timeType: - return marshalUTCTime(out, value.Interface().(*time.Time)) - case bitStringType: - return marshalBitString(out, value.Interface().(BitString)) - case objectIdentifierType: - return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier)) - case bigIntType: - return marshalBigInt(out, value.Interface().(*big.Int)) - } - - switch v := value; v.Kind() { - case reflect.Bool: - if v.Bool() { - return out.WriteByte(255) - } else { - return out.WriteByte(0) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return marshalInt64(out, int64(v.Int())) - case reflect.Struct: - t := v.Type() - - startingField := 0 - - // If the first element of the structure is a non-empty - // RawContents, then we don't bother serializing the rest. - if t.NumField() > 0 && t.Field(0).Type == rawContentsType { - s := v.Field(0) - if s.Len() > 0 { - bytes := make([]byte, s.Len()) - for i := 0; i < s.Len(); i++ { - bytes[i] = uint8(s.Index(i).Uint()) - } - /* The RawContents will contain the tag and - * length fields but we'll also be writing - * those ourselves, so we strip them out of - * bytes */ - _, err = out.Write(stripTagAndLength(bytes)) - return - } else { - startingField = 1 - } - } - - for i := startingField; i < t.NumField(); i++ { - var pre *forkableWriter - pre, out = out.fork() - err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1"))) - if err != nil { - return - } - } - return - case reflect.Slice: - sliceType := v.Type() - if sliceType.Elem().Kind() == reflect.Uint8 { - bytes := make([]byte, v.Len()) - for i := 0; i < v.Len(); i++ { - bytes[i] = uint8(v.Index(i).Uint()) - } - _, err = out.Write(bytes) - return - } - - var params fieldParameters - for i := 0; i < v.Len(); i++ { - var pre *forkableWriter - pre, out = out.fork() - err = marshalField(pre, v.Index(i), params) - if err != nil { - return - } - } - return - case reflect.String: - if params.stringType == tagIA5String { - return marshalIA5String(out, v.String()) - } else { - return marshalPrintableString(out, v.String()) - } - return - } - - return StructuralError{"unknown Go type"} -} - -func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err os.Error) { - // If the field is an interface{} then recurse into it. - if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 { - return marshalField(out, v.Elem(), params) - } - - if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) { - return - } - - if v.Type() == rawValueType { - rv := v.Interface().(RawValue) - err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound}) - if err != nil { - return - } - _, err = out.Write(rv.Bytes) - return - } - - tag, isCompound, ok := getUniversalType(v.Type()) - if !ok { - err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())} - return - } - class := classUniversal - - if params.stringType != 0 { - if tag != tagPrintableString { - return StructuralError{"Explicit string type given to non-string member"} - } - tag = params.stringType - } - - if params.set { - if tag != tagSequence { - return StructuralError{"Non sequence tagged as set"} - } - tag = tagSet - } - - tags, body := out.fork() - - err = marshalBody(body, v, params) - if err != nil { - return - } - - bodyLen := body.Len() - - var explicitTag *forkableWriter - if params.explicit { - explicitTag, tags = tags.fork() - } - - if !params.explicit && params.tag != nil { - // implicit tag. - tag = *params.tag - class = classContextSpecific - } - - err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound}) - if err != nil { - return - } - - if params.explicit { - err = marshalTagAndLength(explicitTag, tagAndLength{ - class: classContextSpecific, - tag: *params.tag, - length: bodyLen + tags.Len(), - isCompound: true, - }) - } - - return nil -} - -// Marshal returns the ASN.1 encoding of val. -func Marshal(val interface{}) ([]byte, os.Error) { - var out bytes.Buffer - v := reflect.ValueOf(val) - f := newForkableWriter() - err := marshalField(f, v, fieldParameters{}) - if err != nil { - return nil, err - } - _, err = f.writeTo(&out) - return out.Bytes(), nil -} diff --git a/src/pkg/asn1/marshal_test.go b/src/pkg/asn1/marshal_test.go deleted file mode 100644 index 03df5f1e1..000000000 --- a/src/pkg/asn1/marshal_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package asn1 - -import ( - "bytes" - "encoding/hex" - "testing" - "time" -) - -type intStruct struct { - A int -} - -type twoIntStruct struct { - A int - B int -} - -type nestedStruct struct { - A intStruct -} - -type rawContentsStruct struct { - Raw RawContent - A int -} - -type implicitTagTest struct { - A int `asn1:"implicit,tag:5"` -} - -type explicitTagTest struct { - A int `asn1:"explicit,tag:5"` -} - -type ia5StringTest struct { - A string `asn1:"ia5"` -} - -type printableStringTest struct { - A string `asn1:"printable"` -} - -type optionalRawValueTest struct { - A RawValue `asn1:"optional"` -} - -type testSET []int - -func setPST(t *time.Time) *time.Time { - t.ZoneOffset = -28800 - return t -} - -type marshalTest struct { - in interface{} - out string // hex encoded -} - -var marshalTests = []marshalTest{ - {10, "02010a"}, - {127, "02017f"}, - {128, "02020080"}, - {-128, "020180"}, - {-129, "0202ff7f"}, - {intStruct{64}, "3003020140"}, - {twoIntStruct{64, 65}, "3006020140020141"}, - {nestedStruct{intStruct{127}}, "3005300302017f"}, - {[]byte{1, 2, 3}, "0403010203"}, - {implicitTagTest{64}, "3003850140"}, - {explicitTagTest{64}, "3005a503020140"}, - {time.SecondsToUTC(0), "170d3730303130313030303030305a"}, - {time.SecondsToUTC(1258325776), "170d3039313131353232353631365a"}, - {setPST(time.SecondsToUTC(1258325776)), "17113039313131353232353631362d30383030"}, - {BitString{[]byte{0x80}, 1}, "03020780"}, - {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, - {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, - {ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"}, - {"test", "130474657374"}, - { - "" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 127 times 'x' - "137f" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "78787878787878787878787878787878787878787878787878787878787878", - }, - { - "" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 128 times 'x' - "138180" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878" + - "7878787878787878787878787878787878787878787878787878787878787878", - }, - {ia5StringTest{"test"}, "3006160474657374"}, - {optionalRawValueTest{}, "3000"}, - {printableStringTest{"test"}, "3006130474657374"}, - {printableStringTest{"test*"}, "30071305746573742a"}, - {rawContentsStruct{nil, 64}, "3003020140"}, - {rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"}, - {RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"}, - {testSET([]int{10}), "310302010a"}, -} - -func TestMarshal(t *testing.T) { - for i, test := range marshalTests { - data, err := Marshal(test.in) - if err != nil { - t.Errorf("#%d failed: %s", i, err) - } - out, _ := hex.DecodeString(test.out) - if bytes.Compare(out, data) != 0 { - t.Errorf("#%d got: %x want %x", i, data, out) - } - } -} diff --git a/src/pkg/big/Makefile b/src/pkg/big/Makefile deleted file mode 100644 index 3d4b56d78..000000000 --- a/src/pkg/big/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../Make.inc - -TARG=big -GOFILES=\ - arith.go\ - arith_decl.go\ - int.go\ - nat.go\ - rat.go\ - -OFILES=\ - arith_$(GOARCH).$O\ - -include ../../Make.pkg diff --git a/src/pkg/big/arith.go b/src/pkg/big/arith.go deleted file mode 100644 index 0a02a4ef5..000000000 --- a/src/pkg/big/arith.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file provides Go implementations of elementary multi-precision -// arithmetic operations on word vectors. Needed for platforms without -// assembly implementations of these routines. - -package big - -// TODO(gri) Decide if Word needs to remain exported. - -type Word uintptr - -const ( - // Compute the size _S of a Word in bytes. - _m = ^Word(0) - _logS = _m>>8&1 + _m>>16&1 + _m>>32&1 - _S = 1 << _logS - - _W = _S << 3 // word size in bits - _B = 1 << _W // digit base - _M = _B - 1 // digit mask - - _W2 = _W / 2 // half word size in bits - _B2 = 1 << _W2 // half digit base - _M2 = _B2 - 1 // half digit mask -) - -// ---------------------------------------------------------------------------- -// Elementary operations on words -// -// These operations are used by the vector operations below. - -// z1<<_W + z0 = x+y+c, with c == 0 or 1 -func addWW_g(x, y, c Word) (z1, z0 Word) { - yc := y + c - z0 = x + yc - if z0 < x || yc < y { - z1 = 1 - } - return -} - -// z1<<_W + z0 = x-y-c, with c == 0 or 1 -func subWW_g(x, y, c Word) (z1, z0 Word) { - yc := y + c - z0 = x - yc - if z0 > x || yc < y { - z1 = 1 - } - return -} - -// z1<<_W + z0 = x*y -// Adapted from Warren, Hacker's Delight, p. 132. -func mulWW_g(x, y Word) (z1, z0 Word) { - x0 := x & _M2 - x1 := x >> _W2 - y0 := y & _M2 - y1 := y >> _W2 - w0 := x0 * y0 - t := x1*y0 + w0>>_W2 - w1 := t & _M2 - w2 := t >> _W2 - w1 += x0 * y1 - z1 = x1*y1 + w2 + w1>>_W2 - z0 = x * y - return -} - -// z1<<_W + z0 = x*y + c -func mulAddWWW_g(x, y, c Word) (z1, z0 Word) { - z1, zz0 := mulWW(x, y) - if z0 = zz0 + c; z0 < zz0 { - z1++ - } - return -} - -// Length of x in bits. -func bitLen(x Word) (n int) { - for ; x >= 0x100; x >>= 8 { - n += 8 - } - for ; x > 0; x >>= 1 { - n++ - } - return -} - -// log2 computes the integer binary logarithm of x. -// The result is the integer n for which 2^n <= x < 2^(n+1). -// If x == 0, the result is -1. -func log2(x Word) int { - return bitLen(x) - 1 -} - -// Number of leading zeros in x. -func leadingZeros(x Word) uint { - return uint(_W - bitLen(x)) -} - -// q = (u1<<_W + u0 - r)/y -// Adapted from Warren, Hacker's Delight, p. 152. -func divWW_g(u1, u0, v Word) (q, r Word) { - if u1 >= v { - return 1<<_W - 1, 1<<_W - 1 - } - - s := leadingZeros(v) - v <<= s - - vn1 := v >> _W2 - vn0 := v & _M2 - un32 := u1<>(_W-s) - un10 := u0 << s - un1 := un10 >> _W2 - un0 := un10 & _M2 - q1 := un32 / vn1 - rhat := un32 - q1*vn1 - -again1: - if q1 >= _B2 || q1*vn0 > _B2*rhat+un1 { - q1-- - rhat += vn1 - if rhat < _B2 { - goto again1 - } - } - - un21 := un32*_B2 + un1 - q1*v - q0 := un21 / vn1 - rhat = un21 - q0*vn1 - -again2: - if q0 >= _B2 || q0*vn0 > _B2*rhat+un0 { - q0-- - rhat += vn1 - if rhat < _B2 { - goto again2 - } - } - - return q1*_B2 + q0, (un21*_B2 + un0 - q0*v) >> s -} - -func addVV_g(z, x, y []Word) (c Word) { - for i := range z { - c, z[i] = addWW_g(x[i], y[i], c) - } - return -} - -func subVV_g(z, x, y []Word) (c Word) { - for i := range z { - c, z[i] = subWW_g(x[i], y[i], c) - } - return -} - -func addVW_g(z, x []Word, y Word) (c Word) { - c = y - for i := range z { - c, z[i] = addWW_g(x[i], c, 0) - } - return -} - -func subVW_g(z, x []Word, y Word) (c Word) { - c = y - for i := range z { - c, z[i] = subWW_g(x[i], c, 0) - } - return -} - -func shlVU_g(z, x []Word, s uint) (c Word) { - if n := len(z); n > 0 { - ŝ := _W - s - w1 := x[n-1] - c = w1 >> ŝ - for i := n - 1; i > 0; i-- { - w := w1 - w1 = x[i-1] - z[i] = w<>ŝ - } - z[0] = w1 << s - } - return -} - -func shrVU_g(z, x []Word, s uint) (c Word) { - if n := len(z); n > 0 { - ŝ := _W - s - w1 := x[0] - c = w1 << ŝ - for i := 0; i < n-1; i++ { - w := w1 - w1 = x[i+1] - z[i] = w>>s | w1<<ŝ - } - z[n-1] = w1 >> s - } - return -} - -func mulAddVWW_g(z, x []Word, y, r Word) (c Word) { - c = r - for i := range z { - c, z[i] = mulAddWWW_g(x[i], y, c) - } - return -} - -func addMulVVW_g(z, x []Word, y Word) (c Word) { - for i := range z { - z1, z0 := mulAddWWW_g(x[i], y, z[i]) - c, z[i] = addWW_g(z0, c, 0) - c += z1 - } - return -} - -func divWVW_g(z []Word, xn Word, x []Word, y Word) (r Word) { - r = xn - for i := len(z) - 1; i >= 0; i-- { - z[i], r = divWW_g(r, x[i], y) - } - return -} diff --git a/src/pkg/big/arith_386.s b/src/pkg/big/arith_386.s deleted file mode 100644 index 07c07b02c..000000000 --- a/src/pkg/big/arith_386.s +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file provides fast assembly versions for the elementary -// arithmetic operations on vectors implemented in arith.go. - -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),7,$0 - MOVL x+0(FP), AX - MULL y+4(FP) - MOVL DX, z1+8(FP) - MOVL AX, z0+12(FP) - RET - - -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),7,$0 - MOVL x1+0(FP), DX - MOVL x0+4(FP), AX - DIVL y+8(FP) - MOVL AX, q+12(FP) - MOVL DX, r+16(FP) - RET - - -// func addVV(z, x, y []Word) (c Word) -TEXT ·addVV(SB),7,$0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL y+24(FP), CX - MOVL n+4(FP), BP - MOVL $0, BX // i = 0 - MOVL $0, DX // c = 0 - JMP E1 - -L1: MOVL (SI)(BX*4), AX - RCRL $1, DX - ADCL (CX)(BX*4), AX - RCLL $1, DX - MOVL AX, (DI)(BX*4) - ADDL $1, BX // i++ - -E1: CMPL BX, BP // i < n - JL L1 - - MOVL DX, c+36(FP) - RET - - -// func subVV(z, x, y []Word) (c Word) -// (same as addVV except for SBBL instead of ADCL and label names) -TEXT ·subVV(SB),7,$0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL y+24(FP), CX - MOVL n+4(FP), BP - MOVL $0, BX // i = 0 - MOVL $0, DX // c = 0 - JMP E2 - -L2: MOVL (SI)(BX*4), AX - RCRL $1, DX - SBBL (CX)(BX*4), AX - RCLL $1, DX - MOVL AX, (DI)(BX*4) - ADDL $1, BX // i++ - -E2: CMPL BX, BP // i < n - JL L2 - - MOVL DX, c+36(FP) - RET - - -// func addVW(z, x []Word, y Word) (c Word) -TEXT ·addVW(SB),7,$0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL y+24(FP), AX // c = y - MOVL n+4(FP), BP - MOVL $0, BX // i = 0 - JMP E3 - -L3: ADDL (SI)(BX*4), AX - MOVL AX, (DI)(BX*4) - RCLL $1, AX - ANDL $1, AX - ADDL $1, BX // i++ - -E3: CMPL BX, BP // i < n - JL L3 - - MOVL AX, c+28(FP) - RET - - -// func subVW(z, x []Word, y Word) (c Word) -TEXT ·subVW(SB),7,$0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL y+24(FP), AX // c = y - MOVL n+4(FP), BP - MOVL $0, BX // i = 0 - JMP E4 - -L4: MOVL (SI)(BX*4), DX // TODO(gri) is there a reverse SUBL? - SUBL AX, DX - MOVL DX, (DI)(BX*4) - RCLL $1, AX - ANDL $1, AX - ADDL $1, BX // i++ - -E4: CMPL BX, BP // i < n - JL L4 - - MOVL AX, c+28(FP) - RET - - -// func shlVU(z, x []Word, s uint) (c Word) -TEXT ·shlVU(SB),7,$0 - MOVL n+4(FP), BX // i = n - SUBL $1, BX // i-- - JL X8b // i < 0 (n <= 0) - - // n > 0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL s+24(FP), CX - MOVL (SI)(BX*4), AX // w1 = x[n-1] - MOVL $0, DX - SHLL CX, DX:AX // w1>>ŝ - MOVL DX, c+28(FP) - - CMPL BX, $0 - JLE X8a // i <= 0 - - // i > 0 -L8: MOVL AX, DX // w = w1 - MOVL -4(SI)(BX*4), AX // w1 = x[i-1] - SHLL CX, DX:AX // w<>ŝ - MOVL DX, (DI)(BX*4) // z[i] = w<>ŝ - SUBL $1, BX // i-- - JG L8 // i > 0 - - // i <= 0 -X8a: SHLL CX, AX // w1< 0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL s+24(FP), CX - MOVL (SI), AX // w1 = x[0] - MOVL $0, DX - SHRL CX, DX:AX // w1<<ŝ - MOVL DX, c+28(FP) - - MOVL $0, BX // i = 0 - JMP E9 - - // i < n-1 -L9: MOVL AX, DX // w = w1 - MOVL 4(SI)(BX*4), AX // w1 = x[i+1] - SHRL CX, DX:AX // w>>s | w1<<ŝ - MOVL DX, (DI)(BX*4) // z[i] = w>>s | w1<<ŝ - ADDL $1, BX // i++ - -E9: CMPL BX, BP - JL L9 // i < n-1 - - // i >= n-1 -X9a: SHRL CX, AX // w1>>s - MOVL AX, (DI)(BP*4) // z[n-1] = w1>>s - RET - -X9b: MOVL $0, c+28(FP) - RET - - -// func mulAddVWW(z, x []Word, y, r Word) (c Word) -TEXT ·mulAddVWW(SB),7,$0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL y+24(FP), BP - MOVL r+28(FP), CX // c = r - MOVL n+4(FP), BX - LEAL (DI)(BX*4), DI - LEAL (SI)(BX*4), SI - NEGL BX // i = -n - JMP E5 - -L5: MOVL (SI)(BX*4), AX - MULL BP - ADDL CX, AX - ADCL $0, DX - MOVL AX, (DI)(BX*4) - MOVL DX, CX - ADDL $1, BX // i++ - -E5: CMPL BX, $0 // i < 0 - JL L5 - - MOVL CX, c+32(FP) - RET - - -// func addMulVVW(z, x []Word, y Word) (c Word) -TEXT ·addMulVVW(SB),7,$0 - MOVL z+0(FP), DI - MOVL x+12(FP), SI - MOVL y+24(FP), BP - MOVL n+4(FP), BX - LEAL (DI)(BX*4), DI - LEAL (SI)(BX*4), SI - NEGL BX // i = -n - MOVL $0, CX // c = 0 - JMP E6 - -L6: MOVL (SI)(BX*4), AX - MULL BP - ADDL CX, AX - ADCL $0, DX - ADDL AX, (DI)(BX*4) - ADCL $0, DX - MOVL DX, CX - ADDL $1, BX // i++ - -E6: CMPL BX, $0 // i < 0 - JL L6 - - MOVL CX, c+28(FP) - RET - - -// divWVW(z* Word, xn Word, x []Word, y Word) (r Word) -TEXT ·divWVW(SB),7,$0 - MOVL z+0(FP), DI - MOVL xn+12(FP), DX // r = xn - MOVL x+16(FP), SI - MOVL y+28(FP), CX - MOVL n+4(FP), BX // i = n - JMP E7 - -L7: MOVL (SI)(BX*4), AX - DIVL CX - MOVL AX, (DI)(BX*4) - -E7: SUBL $1, BX // i-- - JGE L7 // i >= 0 - - MOVL DX, r+32(FP) - RET diff --git a/src/pkg/big/arith_amd64.s b/src/pkg/big/arith_amd64.s deleted file mode 100644 index 89b65f38a..000000000 --- a/src/pkg/big/arith_amd64.s +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file provides fast assembly versions for the elementary -// arithmetic operations on vectors implemented in arith.go. - -// TODO(gri) - experiment with unrolled loops for faster execution - -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),7,$0 - MOVQ x+0(FP), AX - MULQ y+8(FP) - MOVQ DX, z1+16(FP) - MOVQ AX, z0+24(FP) - RET - - -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),7,$0 - MOVQ x1+0(FP), DX - MOVQ x0+8(FP), AX - DIVQ y+16(FP) - MOVQ AX, q+24(FP) - MOVQ DX, r+32(FP) - RET - - -// func addVV(z, x, y []Word) (c Word) -TEXT ·addVV(SB),7,$0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), R9 - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - MOVQ $0, DX // c = 0 - JMP E1 - -L1: MOVQ (R8)(BX*8), AX - RCRQ $1, DX - ADCQ (R9)(BX*8), AX - RCLQ $1, DX - MOVQ AX, (R10)(BX*8) - ADDL $1, BX // i++ - -E1: CMPQ BX, R11 // i < n - JL L1 - - MOVQ DX, c+48(FP) - RET - - -// func subVV(z, x, y []Word) (c Word) -// (same as addVV_s except for SBBQ instead of ADCQ and label names) -TEXT ·subVV(SB),7,$0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), R9 - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - MOVQ $0, DX // c = 0 - JMP E2 - -L2: MOVQ (R8)(BX*8), AX - RCRQ $1, DX - SBBQ (R9)(BX*8), AX - RCLQ $1, DX - MOVQ AX, (R10)(BX*8) - ADDL $1, BX // i++ - -E2: CMPQ BX, R11 // i < n - JL L2 - - MOVQ DX, c+48(FP) - RET - - -// func addVW(z, x []Word, y Word) (c Word) -TEXT ·addVW(SB),7,$0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), AX // c = y - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - JMP E3 - -L3: ADDQ (R8)(BX*8), AX - MOVQ AX, (R10)(BX*8) - RCLQ $1, AX - ANDQ $1, AX - ADDL $1, BX // i++ - -E3: CMPQ BX, R11 // i < n - JL L3 - - MOVQ AX, c+40(FP) - RET - - -// func subVW(z, x []Word, y Word) (c Word) -TEXT ·subVW(SB),7,$0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), AX // c = y - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - JMP E4 - -L4: MOVQ (R8)(BX*8), DX // TODO(gri) is there a reverse SUBQ? - SUBQ AX, DX - MOVQ DX, (R10)(BX*8) - RCLQ $1, AX - ANDQ $1, AX - ADDL $1, BX // i++ - -E4: CMPQ BX, R11 // i < n - JL L4 - - MOVQ AX, c+40(FP) - RET - - -// func shlVU(z, x []Word, s uint) (c Word) -TEXT ·shlVU(SB),7,$0 - MOVL n+8(FP), BX // i = n - SUBL $1, BX // i-- - JL X8b // i < 0 (n <= 0) - - // n > 0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVL s+32(FP), CX - MOVQ (R8)(BX*8), AX // w1 = x[n-1] - MOVQ $0, DX - SHLQ CX, DX:AX // w1>>ŝ - MOVQ DX, c+40(FP) - - CMPL BX, $0 - JLE X8a // i <= 0 - - // i > 0 -L8: MOVQ AX, DX // w = w1 - MOVQ -8(R8)(BX*8), AX // w1 = x[i-1] - SHLQ CX, DX:AX // w<>ŝ - MOVQ DX, (R10)(BX*8) // z[i] = w<>ŝ - SUBL $1, BX // i-- - JG L8 // i > 0 - - // i <= 0 -X8a: SHLQ CX, AX // w1< 0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVL s+32(FP), CX - MOVQ (R8), AX // w1 = x[0] - MOVQ $0, DX - SHRQ CX, DX:AX // w1<<ŝ - MOVQ DX, c+40(FP) - - MOVQ $0, BX // i = 0 - JMP E9 - - // i < n-1 -L9: MOVQ AX, DX // w = w1 - MOVQ 8(R8)(BX*8), AX // w1 = x[i+1] - SHRQ CX, DX:AX // w>>s | w1<<ŝ - MOVQ DX, (R10)(BX*8) // z[i] = w>>s | w1<<ŝ - ADDL $1, BX // i++ - -E9: CMPQ BX, R11 - JL L9 // i < n-1 - - // i >= n-1 -X9a: SHRQ CX, AX // w1>>s - MOVQ AX, (R10)(R11*8) // z[n-1] = w1>>s - RET - -X9b: MOVQ $0, c+40(FP) - RET - - -// func mulAddVWW(z, x []Word, y, r Word) (c Word) -TEXT ·mulAddVWW(SB),7,$0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), R9 - MOVQ r+40(FP), CX // c = r - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - JMP E5 - -L5: MOVQ (R8)(BX*8), AX - MULQ R9 - ADDQ CX, AX - ADCQ $0, DX - MOVQ AX, (R10)(BX*8) - MOVQ DX, CX - ADDL $1, BX // i++ - -E5: CMPQ BX, R11 // i < n - JL L5 - - MOVQ CX, c+48(FP) - RET - - -// func addMulVVW(z, x []Word, y Word) (c Word) -TEXT ·addMulVVW(SB),7,$0 - MOVQ z+0(FP), R10 - MOVQ x+16(FP), R8 - MOVQ y+32(FP), R9 - MOVL n+8(FP), R11 - MOVQ $0, BX // i = 0 - MOVQ $0, CX // c = 0 - JMP E6 - -L6: MOVQ (R8)(BX*8), AX - MULQ R9 - ADDQ CX, AX - ADCQ $0, DX - ADDQ AX, (R10)(BX*8) - ADCQ $0, DX - MOVQ DX, CX - ADDL $1, BX // i++ - -E6: CMPQ BX, R11 // i < n - JL L6 - - MOVQ CX, c+40(FP) - RET - - -// divWVW(z []Word, xn Word, x []Word, y Word) (r Word) -TEXT ·divWVW(SB),7,$0 - MOVQ z+0(FP), R10 - MOVQ xn+16(FP), DX // r = xn - MOVQ x+24(FP), R8 - MOVQ y+40(FP), R9 - MOVL n+8(FP), BX // i = n - JMP E7 - -L7: MOVQ (R8)(BX*8), AX - DIVQ R9 - MOVQ AX, (R10)(BX*8) - -E7: SUBL $1, BX // i-- - JGE L7 // i >= 0 - - MOVQ DX, r+48(FP) - RET diff --git a/src/pkg/big/arith_arm.s b/src/pkg/big/arith_arm.s deleted file mode 100644 index 60abe6eaa..000000000 --- a/src/pkg/big/arith_arm.s +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file provides fast assembly versions for the elementary -// arithmetic operations on vectors implemented in arith.go. - -#define CFLAG 29 // bit position of carry flag - -// func addVV(z, x, y []Word) (c Word) -TEXT ·addVV(SB),7,$0 - MOVW $0, R0 - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW y+24(FP), R3 - MOVW n+4(FP), R4 - MOVW R4<<2, R4 - ADD R1, R4 - B E1 -L1: - MOVW.P 4(R2), R5 - MOVW.P 4(R3), R6 - MOVW R0, CPSR - ADC.S R6, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 -E1: - CMP R1, R4 - BNE L1 - - MOVW R0>>CFLAG, R0 - AND $1, R0 - MOVW R0, c+36(FP) - RET - - -// func subVV(z, x, y []Word) (c Word) -// (same as addVV except for SBC instead of ADC and label names) -TEXT ·subVV(SB),7,$0 - MOVW $(1<>CFLAG, R0 - AND $1, R0 - EOR $1, R0 - MOVW R0, c+36(FP) - RET - - -// func addVW(z, x []Word, y Word) (c Word) -TEXT ·addVW(SB),7,$0 - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW y+24(FP), R3 - MOVW n+4(FP), R4 - MOVW R4<<2, R4 - ADD R1, R4 - CMP R1, R4 - BNE L3a - MOVW R3, c+28(FP) - RET -L3a: - MOVW.P 4(R2), R5 - ADD.S R3, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 - B E3 -L3: - MOVW.P 4(R2), R5 - MOVW R0, CPSR - ADC.S $0, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 -E3: - CMP R1, R4 - BNE L3 - - MOVW R0>>CFLAG, R0 - AND $1, R0 - MOVW R0, c+28(FP) - RET - - -// func subVW(z, x []Word, y Word) (c Word) -TEXT ·subVW(SB),7,$0 - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW y+24(FP), R3 - MOVW n+4(FP), R4 - MOVW R4<<2, R4 - ADD R1, R4 - CMP R1, R4 - BNE L4a - MOVW R3, c+28(FP) - RET -L4a: - MOVW.P 4(R2), R5 - SUB.S R3, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 - B E4 -L4: - MOVW.P 4(R2), R5 - MOVW R0, CPSR - SBC.S $0, R5 - MOVW.P R5, 4(R1) - MOVW CPSR, R0 -E4: - CMP R1, R4 - BNE L4 - - MOVW R0>>CFLAG, R0 - AND $1, R0 - EOR $1, R0 - MOVW R0, c+28(FP) - RET - - -// func shlVU(z, x []Word, s uint) (c Word) -TEXT ·shlVU(SB),7,$0 - MOVW n+4(FP), R5 - CMP $0, R5 - BEQ X7 - - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW R5<<2, R5 - ADD R5, R2 - ADD R1, R5 - MOVW s+24(FP), R3 - CMP $0, R3 // shift 0 is special - BEQ Y7 - ADD $4, R1 // stop one word early - MOVW $32, R4 - SUB R3, R4 - MOVW $0, R7 - - MOVW.W -4(R2), R6 - MOVW R6<>R4, R6 - MOVW R6, c+28(FP) - B E7 - -L7: - MOVW.W -4(R2), R6 - ORR R6>>R4, R7 - MOVW.W R7, -4(R5) - MOVW R6<>R3, R7 - MOVW R6<>R3, R7 -E6: - CMP R1, R5 - BNE L6 - - MOVW R7, 0(R1) - RET - -Y6: // copy loop, because shift 0 == shift 32 - MOVW.P 4(R2), R6 - MOVW.P R6, 4(R1) - CMP R1, R5 - BNE Y6 - -X6: - MOVW $0, R1 - MOVW R1, c+28(FP) - RET - - -// func mulAddVWW(z, x []Word, y, r Word) (c Word) -TEXT ·mulAddVWW(SB),7,$0 - MOVW $0, R0 - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW y+24(FP), R3 - MOVW r+28(FP), R4 - MOVW n+4(FP), R5 - MOVW R5<<2, R5 - ADD R1, R5 - B E8 - - // word loop -L8: - MOVW.P 4(R2), R6 - MULLU R6, R3, (R7, R6) - ADD.S R4, R6 - ADC R0, R7 - MOVW.P R6, 4(R1) - MOVW R7, R4 -E8: - CMP R1, R5 - BNE L8 - - MOVW R4, c+32(FP) - RET - - -// func addMulVVW(z, x []Word, y Word) (c Word) -TEXT ·addMulVVW(SB),7,$0 - MOVW $0, R0 - MOVW z+0(FP), R1 - MOVW x+12(FP), R2 - MOVW y+24(FP), R3 - MOVW n+4(FP), R5 - MOVW R5<<2, R5 - ADD R1, R5 - MOVW $0, R4 - B E9 - - // word loop -L9: - MOVW.P 4(R2), R6 - MULLU R6, R3, (R7, R6) - ADD.S R4, R6 - ADC R0, R7 - MOVW 0(R1), R4 - ADD.S R4, R6 - ADC R0, R7 - MOVW.P R6, 4(R1) - MOVW R7, R4 -E9: - CMP R1, R5 - BNE L9 - - MOVW R4, c+28(FP) - RET - - -// divWVW(z* Word, xn Word, x []Word, y Word) (r Word) -TEXT ·divWVW(SB),7,$0 - // ARM has no multiword division, so use portable code. - B ·divWVW_g(SB) - - -// func divWW(x1, x0, y Word) (q, r Word) -TEXT ·divWW(SB),7,$0 - // ARM has no multiword division, so use portable code. - B ·divWW_g(SB) - - -// func mulWW(x, y Word) (z1, z0 Word) -TEXT ·mulWW(SB),7,$0 - MOVW x+0(FP), R1 - MOVW y+4(FP), R2 - MULLU R1, R2, (R4, R3) - MOVW R4, z1+8(FP) - MOVW R3, z0+12(FP) - RET diff --git a/src/pkg/big/arith_decl.go b/src/pkg/big/arith_decl.go deleted file mode 100644 index 95fcd8b94..000000000 --- a/src/pkg/big/arith_decl.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package big - -// implemented in arith_$GOARCH.s -func mulWW(x, y Word) (z1, z0 Word) -func divWW(x1, x0, y Word) (q, r Word) -func addVV(z, x, y []Word) (c Word) -func subVV(z, x, y []Word) (c Word) -func addVW(z, x []Word, y Word) (c Word) -func subVW(z, x []Word, y Word) (c Word) -func shlVU(z, x []Word, s uint) (c Word) -func shrVU(z, x []Word, s uint) (c Word) -func mulAddVWW(z, x []Word, y, r Word) (c Word) -func addMulVVW(z, x []Word, y Word) (c Word) -func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) diff --git a/src/pkg/big/arith_test.go b/src/pkg/big/arith_test.go deleted file mode 100644 index b6c56c39e..000000000 --- a/src/pkg/big/arith_test.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package big - -import "testing" - -type funWW func(x, y, c Word) (z1, z0 Word) -type argWW struct { - x, y, c, z1, z0 Word -} - -var sumWW = []argWW{ - {0, 0, 0, 0, 0}, - {0, 1, 0, 0, 1}, - {0, 0, 1, 0, 1}, - {0, 1, 1, 0, 2}, - {12345, 67890, 0, 0, 80235}, - {12345, 67890, 1, 0, 80236}, - {_M, 1, 0, 1, 0}, - {_M, 0, 1, 1, 0}, - {_M, 1, 1, 1, 1}, - {_M, _M, 0, 1, _M - 1}, - {_M, _M, 1, 1, _M}, -} - -func testFunWW(t *testing.T, msg string, f funWW, a argWW) { - z1, z0 := f(a.x, a.y, a.c) - if z1 != a.z1 || z0 != a.z0 { - t.Errorf("%s%+v\n\tgot z1:z0 = %#x:%#x; want %#x:%#x", msg, a, z1, z0, a.z1, a.z0) - } -} - -func TestFunWW(t *testing.T) { - for _, a := range sumWW { - arg := a - testFunWW(t, "addWW_g", addWW_g, arg) - - arg = argWW{a.y, a.x, a.c, a.z1, a.z0} - testFunWW(t, "addWW_g symmetric", addWW_g, arg) - - arg = argWW{a.z0, a.x, a.c, a.z1, a.y} - testFunWW(t, "subWW_g", subWW_g, arg) - - arg = argWW{a.z0, a.y, a.c, a.z1, a.x} - testFunWW(t, "subWW_g symmetric", subWW_g, arg) - } -} - -type funVV func(z, x, y []Word) (c Word) -type argVV struct { - z, x, y nat - c Word -} - -var sumVV = []argVV{ - {}, - {nat{0}, nat{0}, nat{0}, 0}, - {nat{1}, nat{1}, nat{0}, 0}, - {nat{0}, nat{_M}, nat{1}, 1}, - {nat{80235}, nat{12345}, nat{67890}, 0}, - {nat{_M - 1}, nat{_M}, nat{_M}, 1}, - {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, nat{1, 0, 0, 0}, 1}, - {nat{0, 0, 0, _M}, nat{_M, _M, _M, _M - 1}, nat{1, 0, 0, 0}, 0}, - {nat{0, 0, 0, 0}, nat{_M, 0, _M, 0}, nat{1, _M, 0, _M}, 1}, -} - -func testFunVV(t *testing.T, msg string, f funVV, a argVV) { - z := make(nat, len(a.z)) - c := f(z, a.x, a.y) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if c != a.c { - t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) - } -} - -func TestFunVV(t *testing.T) { - for _, a := range sumVV { - arg := a - testFunVV(t, "addVV_g", addVV_g, arg) - testFunVV(t, "addVV", addVV, arg) - - arg = argVV{a.z, a.y, a.x, a.c} - testFunVV(t, "addVV_g symmetric", addVV_g, arg) - testFunVV(t, "addVV symmetric", addVV, arg) - - arg = argVV{a.x, a.z, a.y, a.c} - testFunVV(t, "subVV_g", subVV_g, arg) - testFunVV(t, "subVV", subVV, arg) - - arg = argVV{a.y, a.z, a.x, a.c} - testFunVV(t, "subVV_g symmetric", subVV_g, arg) - testFunVV(t, "subVV symmetric", subVV, arg) - } -} - -type funVW func(z, x []Word, y Word) (c Word) -type argVW struct { - z, x nat - y Word - c Word -} - -var sumVW = []argVW{ - {}, - {nil, nil, 2, 2}, - {nat{0}, nat{0}, 0, 0}, - {nat{1}, nat{0}, 1, 0}, - {nat{1}, nat{1}, 0, 0}, - {nat{0}, nat{_M}, 1, 1}, - {nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1}, -} - -var prodVW = []argVW{ - {}, - {nat{0}, nat{0}, 0, 0}, - {nat{0}, nat{_M}, 0, 0}, - {nat{0}, nat{0}, _M, 0}, - {nat{1}, nat{1}, 1, 0}, - {nat{22793}, nat{991}, 23, 0}, - {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0}, - {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0}, - {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0}, - {nat{_M << 1 & _M}, nat{_M}, 1 << 1, _M >> (_W - 1)}, - {nat{_M << 7 & _M}, nat{_M}, 1 << 7, _M >> (_W - 7)}, - {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, _M >> (_W - 7)}, -} - -var lshVW = []argVW{ - {}, - {nat{0}, nat{0}, 0, 0}, - {nat{0}, nat{0}, 1, 0}, - {nat{0}, nat{0}, 20, 0}, - - {nat{_M}, nat{_M}, 0, 0}, - {nat{_M << 1 & _M}, nat{_M}, 1, 1}, - {nat{_M << 20 & _M}, nat{_M}, 20, _M >> (_W - 20)}, - - {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, - {nat{_M << 1 & _M, _M, _M}, nat{_M, _M, _M}, 1, 1}, - {nat{_M << 20 & _M, _M, _M}, nat{_M, _M, _M}, 20, _M >> (_W - 20)}, -} - -var rshVW = []argVW{ - {}, - {nat{0}, nat{0}, 0, 0}, - {nat{0}, nat{0}, 1, 0}, - {nat{0}, nat{0}, 20, 0}, - - {nat{_M}, nat{_M}, 0, 0}, - {nat{_M >> 1}, nat{_M}, 1, _M << (_W - 1) & _M}, - {nat{_M >> 20}, nat{_M}, 20, _M << (_W - 20) & _M}, - - {nat{_M, _M, _M}, nat{_M, _M, _M}, 0, 0}, - {nat{_M, _M, _M >> 1}, nat{_M, _M, _M}, 1, _M << (_W - 1) & _M}, - {nat{_M, _M, _M >> 20}, nat{_M, _M, _M}, 20, _M << (_W - 20) & _M}, -} - -func testFunVW(t *testing.T, msg string, f funVW, a argVW) { - z := make(nat, len(a.z)) - c := f(z, a.x, a.y) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if c != a.c { - t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) - } -} - -func makeFunVW(f func(z, x []Word, s uint) (c Word)) funVW { - return func(z, x []Word, s Word) (c Word) { - return f(z, x, uint(s)) - } -} - -func TestFunVW(t *testing.T) { - for _, a := range sumVW { - arg := a - testFunVW(t, "addVW_g", addVW_g, arg) - testFunVW(t, "addVW", addVW, arg) - - arg = argVW{a.x, a.z, a.y, a.c} - testFunVW(t, "subVW_g", subVW_g, arg) - testFunVW(t, "subVW", subVW, arg) - } - - shlVW_g := makeFunVW(shlVU_g) - shlVW := makeFunVW(shlVU) - for _, a := range lshVW { - arg := a - testFunVW(t, "shlVU_g", shlVW_g, arg) - testFunVW(t, "shlVU", shlVW, arg) - } - - shrVW_g := makeFunVW(shrVU_g) - shrVW := makeFunVW(shrVU) - for _, a := range rshVW { - arg := a - testFunVW(t, "shrVU_g", shrVW_g, arg) - testFunVW(t, "shrVU", shrVW, arg) - } -} - -type funVWW func(z, x []Word, y, r Word) (c Word) -type argVWW struct { - z, x nat - y, r Word - c Word -} - -var prodVWW = []argVWW{ - {}, - {nat{0}, nat{0}, 0, 0, 0}, - {nat{991}, nat{0}, 0, 991, 0}, - {nat{0}, nat{_M}, 0, 0, 0}, - {nat{991}, nat{_M}, 0, 991, 0}, - {nat{0}, nat{0}, _M, 0, 0}, - {nat{991}, nat{0}, _M, 991, 0}, - {nat{1}, nat{1}, 1, 0, 0}, - {nat{992}, nat{1}, 1, 991, 0}, - {nat{22793}, nat{991}, 23, 0, 0}, - {nat{22800}, nat{991}, 23, 7, 0}, - {nat{0, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 0, 0}, - {nat{7, 0, 0, 22793}, nat{0, 0, 0, 991}, 23, 7, 0}, - {nat{0, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 0, 0}, - {nat{991, 0, 0, 0}, nat{7893475, 7395495, 798547395, 68943}, 0, 991, 0}, - {nat{0, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 0, 0}, - {nat{991, 0, 0, 0}, nat{0, 0, 0, 0}, 894375984, 991, 0}, - {nat{_M << 1 & _M}, nat{_M}, 1 << 1, 0, _M >> (_W - 1)}, - {nat{_M<<1&_M + 1}, nat{_M}, 1 << 1, 1, _M >> (_W - 1)}, - {nat{_M << 7 & _M}, nat{_M}, 1 << 7, 0, _M >> (_W - 7)}, - {nat{_M<<7&_M + 1<<6}, nat{_M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, - {nat{_M << 7 & _M, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 0, _M >> (_W - 7)}, - {nat{_M<<7&_M + 1<<6, _M, _M, _M}, nat{_M, _M, _M, _M}, 1 << 7, 1 << 6, _M >> (_W - 7)}, -} - -func testFunVWW(t *testing.T, msg string, f funVWW, a argVWW) { - z := make(nat, len(a.z)) - c := f(z, a.x, a.y, a.r) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if c != a.c { - t.Errorf("%s%+v\n\tgot c = %#x; want %#x", msg, a, c, a.c) - } -} - -// TODO(gri) mulAddVWW and divWVW are symmetric operations but -// their signature is not symmetric. Try to unify. - -type funWVW func(z []Word, xn Word, x []Word, y Word) (r Word) -type argWVW struct { - z nat - xn Word - x nat - y Word - r Word -} - -func testFunWVW(t *testing.T, msg string, f funWVW, a argWVW) { - z := make(nat, len(a.z)) - r := f(z, a.xn, a.x, a.y) - for i, zi := range z { - if zi != a.z[i] { - t.Errorf("%s%+v\n\tgot z[%d] = %#x; want %#x", msg, a, i, zi, a.z[i]) - break - } - } - if r != a.r { - t.Errorf("%s%+v\n\tgot r = %#x; want %#x", msg, a, r, a.r) - } -} - -func TestFunVWW(t *testing.T) { - for _, a := range prodVWW { - arg := a - testFunVWW(t, "mulAddVWW_g", mulAddVWW_g, arg) - testFunVWW(t, "mulAddVWW", mulAddVWW, arg) - - if a.y != 0 && a.r < a.y { - arg := argWVW{a.x, a.c, a.z, a.y, a.r} - testFunWVW(t, "divWVW_g", divWVW_g, arg) - testFunWVW(t, "divWVW", divWVW, arg) - } - } -} - -var mulWWTests = []struct { - x, y Word - q, r Word -}{ - {_M, _M, _M - 1, 1}, - // 32 bit only: {0xc47dfa8c, 50911, 0x98a4, 0x998587f4}, -} - -func TestMulWW(t *testing.T) { - for i, test := range mulWWTests { - q, r := mulWW_g(test.x, test.y) - if q != test.q || r != test.r { - t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) - } - } -} - -var mulAddWWWTests = []struct { - x, y, c Word - q, r Word -}{ - // TODO(agl): These will only work on 64-bit platforms. - // {15064310297182388543, 0xe7df04d2d35d5d80, 13537600649892366549, 13644450054494335067, 10832252001440893781}, - // {15064310297182388543, 0xdab2f18048baa68d, 13644450054494335067, 12869334219691522700, 14233854684711418382}, - {_M, _M, 0, _M - 1, 1}, - {_M, _M, _M, _M, 0}, -} - -func TestMulAddWWW(t *testing.T) { - for i, test := range mulAddWWWTests { - q, r := mulAddWWW_g(test.x, test.y, test.c) - if q != test.q || r != test.r { - t.Errorf("#%d got (%x, %x) want (%x, %x)", i, q, r, test.q, test.r) - } - } -} diff --git a/src/pkg/big/calibrate_test.go b/src/pkg/big/calibrate_test.go deleted file mode 100644 index 1cd93b105..000000000 --- a/src/pkg/big/calibrate_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file prints execution times for the Mul benchmark -// given different Karatsuba thresholds. The result may be -// used to manually fine-tune the threshold constant. The -// results are somewhat fragile; use repeated runs to get -// a clear picture. - -// Usage: gotest -calibrate - -package big - -import ( - "flag" - "fmt" - "testing" - "time" -) - -var calibrate = flag.Bool("calibrate", false, "run calibration test") - -// measure returns the time to run f -func measure(f func()) int64 { - const N = 100 - start := time.Nanoseconds() - for i := N; i > 0; i-- { - f() - } - stop := time.Nanoseconds() - return (stop - start) / N -} - -func computeThresholds() { - fmt.Printf("Multiplication times for varying Karatsuba thresholds\n") - fmt.Printf("(run repeatedly for good results)\n") - - // determine Tk, the work load execution time using basic multiplication - karatsubaThreshold = 1e9 // disable karatsuba - Tb := measure(benchmarkMulLoad) - fmt.Printf("Tb = %dns\n", Tb) - - // thresholds - n := 8 // any lower values for the threshold lead to very slow multiplies - th1 := -1 - th2 := -1 - - var deltaOld int64 - for count := -1; count != 0; count-- { - // determine Tk, the work load execution time using Karatsuba multiplication - karatsubaThreshold = n // enable karatsuba - Tk := measure(benchmarkMulLoad) - - // improvement over Tb - delta := (Tb - Tk) * 100 / Tb - - fmt.Printf("n = %3d Tk = %8dns %4d%%", n, Tk, delta) - - // determine break-even point - if Tk < Tb && th1 < 0 { - th1 = n - fmt.Print(" break-even point") - } - - // determine diminishing return - if 0 < delta && delta < deltaOld && th2 < 0 { - th2 = n - fmt.Print(" diminishing return") - } - deltaOld = delta - - fmt.Println() - - // trigger counter - if th1 >= 0 && th2 >= 0 && count < 0 { - count = 20 // this many extra measurements after we got both thresholds - } - - n++ - } -} - -func TestCalibrate(t *testing.T) { - if *calibrate { - computeThresholds() - } -} diff --git a/src/pkg/big/hilbert_test.go b/src/pkg/big/hilbert_test.go deleted file mode 100644 index 1a84341b3..000000000 --- a/src/pkg/big/hilbert_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// A little test program and benchmark for rational arithmetics. -// Computes a Hilbert matrix, its inverse, multiplies them -// and verifies that the product is the identity matrix. - -package big - -import ( - "fmt" - "testing" -) - -type matrix struct { - n, m int - a []*Rat -} - -func (a *matrix) at(i, j int) *Rat { - if !(0 <= i && i < a.n && 0 <= j && j < a.m) { - panic("index out of range") - } - return a.a[i*a.m+j] -} - -func (a *matrix) set(i, j int, x *Rat) { - if !(0 <= i && i < a.n && 0 <= j && j < a.m) { - panic("index out of range") - } - a.a[i*a.m+j] = x -} - -func newMatrix(n, m int) *matrix { - if !(0 <= n && 0 <= m) { - panic("illegal matrix") - } - a := new(matrix) - a.n = n - a.m = m - a.a = make([]*Rat, n*m) - return a -} - -func newUnit(n int) *matrix { - a := newMatrix(n, n) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - x := NewRat(0, 1) - if i == j { - x.SetInt64(1) - } - a.set(i, j, x) - } - } - return a -} - -func newHilbert(n int) *matrix { - a := newMatrix(n, n) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - a.set(i, j, NewRat(1, int64(i+j+1))) - } - } - return a -} - -func newInverseHilbert(n int) *matrix { - a := newMatrix(n, n) - for i := 0; i < n; i++ { - for j := 0; j < n; j++ { - x1 := new(Rat).SetInt64(int64(i + j + 1)) - x2 := new(Rat).SetInt(new(Int).Binomial(int64(n+i), int64(n-j-1))) - x3 := new(Rat).SetInt(new(Int).Binomial(int64(n+j), int64(n-i-1))) - x4 := new(Rat).SetInt(new(Int).Binomial(int64(i+j), int64(i))) - - x1.Mul(x1, x2) - x1.Mul(x1, x3) - x1.Mul(x1, x4) - x1.Mul(x1, x4) - - if (i+j)&1 != 0 { - x1.Neg(x1) - } - - a.set(i, j, x1) - } - } - return a -} - -func (a *matrix) mul(b *matrix) *matrix { - if a.m != b.n { - panic("illegal matrix multiply") - } - c := newMatrix(a.n, b.m) - for i := 0; i < c.n; i++ { - for j := 0; j < c.m; j++ { - x := NewRat(0, 1) - for k := 0; k < a.m; k++ { - x.Add(x, new(Rat).Mul(a.at(i, k), b.at(k, j))) - } - c.set(i, j, x) - } - } - return c -} - -func (a *matrix) eql(b *matrix) bool { - if a.n != b.n || a.m != b.m { - return false - } - for i := 0; i < a.n; i++ { - for j := 0; j < a.m; j++ { - if a.at(i, j).Cmp(b.at(i, j)) != 0 { - return false - } - } - } - return true -} - -func (a *matrix) String() string { - s := "" - for i := 0; i < a.n; i++ { - for j := 0; j < a.m; j++ { - s += fmt.Sprintf("\t%s", a.at(i, j)) - } - s += "\n" - } - return s -} - -func doHilbert(t *testing.T, n int) { - a := newHilbert(n) - b := newInverseHilbert(n) - I := newUnit(n) - ab := a.mul(b) - if !ab.eql(I) { - if t == nil { - panic("Hilbert failed") - } - t.Errorf("a = %s\n", a) - t.Errorf("b = %s\n", b) - t.Errorf("a*b = %s\n", ab) - t.Errorf("I = %s\n", I) - } -} - -func TestHilbert(t *testing.T) { - doHilbert(t, 10) -} - -func BenchmarkHilbert(b *testing.B) { - for i := 0; i < b.N; i++ { - doHilbert(nil, 10) - } -} diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go deleted file mode 100755 index 701b69715..000000000 --- a/src/pkg/big/int.go +++ /dev/null @@ -1,868 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file implements signed multi-precision integers. - -package big - -import ( - "fmt" - "io" - "os" - "rand" - "strings" -) - -// An Int represents a signed multi-precision integer. -// The zero value for an Int represents the value 0. -type Int struct { - neg bool // sign - abs nat // absolute value of the integer -} - -var intOne = &Int{false, natOne} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x == 0 -// +1 if x > 0 -// -func (x *Int) Sign() int { - if len(x.abs) == 0 { - return 0 - } - if x.neg { - return -1 - } - return 1 -} - -// SetInt64 sets z to x and returns z. -func (z *Int) SetInt64(x int64) *Int { - neg := false - if x < 0 { - neg = true - x = -x - } - z.abs = z.abs.setUint64(uint64(x)) - z.neg = neg - return z -} - -// NewInt allocates and returns a new Int set to x. -func NewInt(x int64) *Int { - return new(Int).SetInt64(x) -} - -// Set sets z to x and returns z. -func (z *Int) Set(x *Int) *Int { - z.abs = z.abs.set(x.abs) - z.neg = x.neg - return z -} - -// Abs sets z to |x| (the absolute value of x) and returns z. -func (z *Int) Abs(x *Int) *Int { - z.abs = z.abs.set(x.abs) - z.neg = false - return z -} - -// Neg sets z to -x and returns z. -func (z *Int) Neg(x *Int) *Int { - z.abs = z.abs.set(x.abs) - z.neg = len(z.abs) > 0 && !x.neg // 0 has no sign - return z -} - -// Add sets z to the sum x+y and returns z. -func (z *Int) Add(x, y *Int) *Int { - neg := x.neg - if x.neg == y.neg { - // x + y == x + y - // (-x) + (-y) == -(x + y) - z.abs = z.abs.add(x.abs, y.abs) - } else { - // x + (-y) == x - y == -(y - x) - // (-x) + y == y - x == -(x - y) - if x.abs.cmp(y.abs) >= 0 { - z.abs = z.abs.sub(x.abs, y.abs) - } else { - neg = !neg - z.abs = z.abs.sub(y.abs, x.abs) - } - } - z.neg = len(z.abs) > 0 && neg // 0 has no sign - return z -} - -// Sub sets z to the difference x-y and returns z. -func (z *Int) Sub(x, y *Int) *Int { - neg := x.neg - if x.neg != y.neg { - // x - (-y) == x + y - // (-x) - y == -(x + y) - z.abs = z.abs.add(x.abs, y.abs) - } else { - // x - y == x - y == -(y - x) - // (-x) - (-y) == y - x == -(x - y) - if x.abs.cmp(y.abs) >= 0 { - z.abs = z.abs.sub(x.abs, y.abs) - } else { - neg = !neg - z.abs = z.abs.sub(y.abs, x.abs) - } - } - z.neg = len(z.abs) > 0 && neg // 0 has no sign - return z -} - -// Mul sets z to the product x*y and returns z. -func (z *Int) Mul(x, y *Int) *Int { - // x * y == x * y - // x * (-y) == -(x * y) - // (-x) * y == -(x * y) - // (-x) * (-y) == x * y - z.abs = z.abs.mul(x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign - return z -} - -// MulRange sets z to the product of all integers -// in the range [a, b] inclusively and returns z. -// If a > b (empty range), the result is 1. -func (z *Int) MulRange(a, b int64) *Int { - switch { - case a > b: - return z.SetInt64(1) // empty range - case a <= 0 && b >= 0: - return z.SetInt64(0) // range includes 0 - } - // a <= b && (b < 0 || a > 0) - - neg := false - if a < 0 { - neg = (b-a)&1 == 0 - a, b = -b, -a - } - - z.abs = z.abs.mulRange(uint64(a), uint64(b)) - z.neg = neg - return z -} - -// Binomial sets z to the binomial coefficient of (n, k) and returns z. -func (z *Int) Binomial(n, k int64) *Int { - var a, b Int - a.MulRange(n-k+1, n) - b.MulRange(1, k) - return z.Quo(&a, &b) -} - -// Quo sets z to the quotient x/y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// See QuoRem for more details. -func (z *Int) Quo(x, y *Int) *Int { - z.abs, _ = z.abs.div(nil, x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign - return z -} - -// Rem sets z to the remainder x%y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// See QuoRem for more details. -func (z *Int) Rem(x, y *Int) *Int { - _, z.abs = nat(nil).div(z.abs, x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg // 0 has no sign - return z -} - -// QuoRem sets z to the quotient x/y and r to the remainder x%y -// and returns the pair (z, r) for y != 0. -// If y == 0, a division-by-zero run-time panic occurs. -// -// QuoRem implements T-division and modulus (like Go): -// -// q = x/y with the result truncated to zero -// r = x - y*q -// -// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.) -// -func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) { - z.abs, r.abs = z.abs.div(r.abs, x.abs, y.abs) - z.neg, r.neg = len(z.abs) > 0 && x.neg != y.neg, len(r.abs) > 0 && x.neg // 0 has no sign - return z, r -} - -// Div sets z to the quotient x/y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// See DivMod for more details. -func (z *Int) Div(x, y *Int) *Int { - y_neg := y.neg // z may be an alias for y - var r Int - z.QuoRem(x, y, &r) - if r.neg { - if y_neg { - z.Add(z, intOne) - } else { - z.Sub(z, intOne) - } - } - return z -} - -// Mod sets z to the modulus x%y for y != 0 and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -// See DivMod for more details. -func (z *Int) Mod(x, y *Int) *Int { - y0 := y // save y - if z == y || alias(z.abs, y.abs) { - y0 = new(Int).Set(y) - } - var q Int - q.QuoRem(x, y, z) - if z.neg { - if y0.neg { - z.Sub(z, y0) - } else { - z.Add(z, y0) - } - } - return z -} - -// DivMod sets z to the quotient x div y and m to the modulus x mod y -// and returns the pair (z, m) for y != 0. -// If y == 0, a division-by-zero run-time panic occurs. -// -// DivMod implements Euclidean division and modulus (unlike Go): -// -// q = x div y such that -// m = x - y*q with 0 <= m < |q| -// -// (See Raymond T. Boute, ``The Euclidean definition of the functions -// div and mod''. ACM Transactions on Programming Languages and -// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. -// ACM press.) -// -func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { - y0 := y // save y - if z == y || alias(z.abs, y.abs) { - y0 = new(Int).Set(y) - } - z.QuoRem(x, y, m) - if m.neg { - if y0.neg { - z.Add(z, intOne) - m.Sub(m, y0) - } else { - z.Sub(z, intOne) - m.Add(m, y0) - } - } - return z, m -} - -// Cmp compares x and y and returns: -// -// -1 if x < y -// 0 if x == y -// +1 if x > y -// -func (x *Int) Cmp(y *Int) (r int) { - // x cmp y == x cmp y - // x cmp (-y) == x - // (-x) cmp y == y - // (-x) cmp (-y) == -(x cmp y) - switch { - case x.neg == y.neg: - r = x.abs.cmp(y.abs) - if x.neg { - r = -r - } - case x.neg: - r = -1 - default: - r = 1 - } - return -} - -func (x *Int) String() string { - switch { - case x == nil: - return "" - case x.neg: - return "-" + x.abs.decimalString() - } - return x.abs.decimalString() -} - -func charset(ch int) string { - switch ch { - case 'b': - return lowercaseDigits[0:2] - case 'o': - return lowercaseDigits[0:8] - case 'd', 's', 'v': - return lowercaseDigits[0:10] - case 'x': - return lowercaseDigits[0:16] - case 'X': - return uppercaseDigits[0:16] - } - return "" // unknown format -} - -// write count copies of text to s -func writeMultiple(s fmt.State, text string, count int) { - if len(text) > 0 { - b := []byte(text) - for ; count > 0; count-- { - s.Write(b) - } - } -} - -// Format is a support routine for fmt.Formatter. It accepts -// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x' -// (lowercase hexadecimal), and 'X' (uppercase hexadecimal). -// Also supported are the full suite of package fmt's format -// verbs for integral types, including '+', '-', and ' ' -// for sign control, '#' for leading zero in octal and for -// hexadecimal, a leading "0x" or "0X" for "%#x" and "%#X" -// respectively, specification of minimum digits precision, -// output field width, space or zero padding, and left or -// right justification. -// -func (x *Int) Format(s fmt.State, ch int) { - cs := charset(ch) - - // special cases - switch { - case cs == "": - // unknown format - fmt.Fprintf(s, "%%!%c(big.Int=%s)", ch, x.String()) - return - case x == nil: - fmt.Fprint(s, "") - return - } - - // determine sign character - sign := "" - switch { - case x.neg: - sign = "-" - case s.Flag('+'): // supersedes ' ' when both specified - sign = "+" - case s.Flag(' '): - sign = " " - } - - // determine prefix characters for indicating output base - prefix := "" - if s.Flag('#') { - switch ch { - case 'o': // octal - prefix = "0" - case 'x': // hexadecimal - prefix = "0x" - case 'X': - prefix = "0X" - } - } - - // determine digits with base set by len(cs) and digit characters from cs - digits := x.abs.string(cs) - - // number of characters for the three classes of number padding - var left int // space characters to left of digits for right justification ("%8d") - var zeroes int // zero characters (actually cs[0]) as left-most digits ("%.8d") - var right int // space characters to right of digits for left justification ("%-8d") - - // determine number padding from precision: the least number of digits to output - precision, precisionSet := s.Precision() - if precisionSet { - switch { - case len(digits) < precision: - zeroes = precision - len(digits) // count of zero padding - case digits == "0" && precision == 0: - return // print nothing if zero value (x == 0) and zero precision ("." or ".0") - } - } - - // determine field pad from width: the least number of characters to output - length := len(sign) + len(prefix) + zeroes + len(digits) - if width, widthSet := s.Width(); widthSet && length < width { // pad as specified - switch d := width - length; { - case s.Flag('-'): - // pad on the right with spaces; supersedes '0' when both specified - right = d - case s.Flag('0') && !precisionSet: - // pad with zeroes unless precision also specified - zeroes = d - default: - // pad on the left with spaces - left = d - } - } - - // print number as [left pad][sign][prefix][zero pad][digits][right pad] - writeMultiple(s, " ", left) - writeMultiple(s, sign, 1) - writeMultiple(s, prefix, 1) - writeMultiple(s, "0", zeroes) - writeMultiple(s, digits, 1) - writeMultiple(s, " ", right) -} - -// scan sets z to the integer value corresponding to the longest possible prefix -// read from r representing a signed integer number in a given conversion base. -// It returns z, the actual conversion base used, and an error, if any. In the -// error case, the value of z is undefined. The syntax follows the syntax of -// integer literals in Go. -// -// The base argument must be 0 or a value from 2 through MaxBase. If the base -// is 0, the string prefix determines the actual conversion base. A prefix of -// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a -// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. -// -func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, os.Error) { - // determine sign - ch, _, err := r.ReadRune() - if err != nil { - return z, 0, err - } - neg := false - switch ch { - case '-': - neg = true - case '+': // nothing to do - default: - r.UnreadRune() - } - - // determine mantissa - z.abs, base, err = z.abs.scan(r, base) - if err != nil { - return z, base, err - } - z.neg = len(z.abs) > 0 && neg // 0 has no sign - - return z, base, nil -} - -// Scan is a support routine for fmt.Scanner; it sets z to the value of -// the scanned number. It accepts the formats 'b' (binary), 'o' (octal), -// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). -func (z *Int) Scan(s fmt.ScanState, ch int) os.Error { - s.SkipSpace() // skip leading space characters - base := 0 - switch ch { - case 'b': - base = 2 - case 'o': - base = 8 - case 'd': - base = 10 - case 'x', 'X': - base = 16 - case 's', 'v': - // let scan determine the base - default: - return os.NewError("Int.Scan: invalid verb") - } - _, _, err := z.scan(s, base) - return err -} - -// Int64 returns the int64 representation of x. -// If x cannot be represented in an int64, the result is undefined. -func (x *Int) Int64() int64 { - if len(x.abs) == 0 { - return 0 - } - v := int64(x.abs[0]) - if _W == 32 && len(x.abs) > 1 { - v |= int64(x.abs[1]) << 32 - } - if x.neg { - v = -v - } - return v -} - -// SetString sets z to the value of s, interpreted in the given base, -// and returns z and a boolean indicating success. If SetString fails, -// the value of z is undefined. -// -// The base argument must be 0 or a value from 2 through MaxBase. If the base -// is 0, the string prefix determines the actual conversion base. A prefix of -// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a -// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. -// -func (z *Int) SetString(s string, base int) (*Int, bool) { - r := strings.NewReader(s) - _, _, err := z.scan(r, base) - if err != nil { - return z, false - } - _, _, err = r.ReadRune() - return z, err == os.EOF // err == os.EOF => scan consumed all of s -} - -// SetBytes interprets buf as the bytes of a big-endian unsigned -// integer, sets z to that value, and returns z. -func (z *Int) SetBytes(buf []byte) *Int { - z.abs = z.abs.setBytes(buf) - z.neg = false - return z -} - -// Bytes returns the absolute value of z as a big-endian byte slice. -func (z *Int) Bytes() []byte { - buf := make([]byte, len(z.abs)*_S) - return buf[z.abs.bytes(buf):] -} - -// BitLen returns the length of the absolute value of z in bits. -// The bit length of 0 is 0. -func (z *Int) BitLen() int { - return z.abs.bitLen() -} - -// Exp sets z = x**y mod m. If m is nil, z = x**y. -// See Knuth, volume 2, section 4.6.3. -func (z *Int) Exp(x, y, m *Int) *Int { - if y.neg || len(y.abs) == 0 { - neg := x.neg - z.SetInt64(1) - z.neg = neg - return z - } - - var mWords nat - if m != nil { - mWords = m.abs - } - - z.abs = z.abs.expNN(x.abs, y.abs, mWords) - z.neg = len(z.abs) > 0 && x.neg && y.abs[0]&1 == 1 // 0 has no sign - return z -} - -// GcdInt sets d to the greatest common divisor of a and b, which must be -// positive numbers. -// If x and y are not nil, GcdInt sets x and y such that d = a*x + b*y. -// If either a or b is not positive, GcdInt sets d = x = y = 0. -func GcdInt(d, x, y, a, b *Int) { - if a.neg || b.neg { - d.SetInt64(0) - if x != nil { - x.SetInt64(0) - } - if y != nil { - y.SetInt64(0) - } - return - } - - A := new(Int).Set(a) - B := new(Int).Set(b) - - X := new(Int) - Y := new(Int).SetInt64(1) - - lastX := new(Int).SetInt64(1) - lastY := new(Int) - - q := new(Int) - temp := new(Int) - - for len(B.abs) > 0 { - r := new(Int) - q, r = q.QuoRem(A, B, r) - - A, B = B, r - - temp.Set(X) - X.Mul(X, q) - X.neg = !X.neg - X.Add(X, lastX) - lastX.Set(temp) - - temp.Set(Y) - Y.Mul(Y, q) - Y.neg = !Y.neg - Y.Add(Y, lastY) - lastY.Set(temp) - } - - if x != nil { - *x = *lastX - } - - if y != nil { - *y = *lastY - } - - *d = *A -} - -// ProbablyPrime performs n Miller-Rabin tests to check whether z is prime. -// If it returns true, z is prime with probability 1 - 1/4^n. -// If it returns false, z is not prime. -func ProbablyPrime(z *Int, n int) bool { - return !z.neg && z.abs.probablyPrime(n) -} - -// Rand sets z to a pseudo-random number in [0, n) and returns z. -func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { - z.neg = false - if n.neg == true || len(n.abs) == 0 { - z.abs = nil - return z - } - z.abs = z.abs.random(rnd, n.abs, n.abs.bitLen()) - return z -} - -// ModInverse sets z to the multiplicative inverse of g in the group ℤ/pℤ (where -// p is a prime) and returns z. -func (z *Int) ModInverse(g, p *Int) *Int { - var d Int - GcdInt(&d, z, nil, g, p) - // x and y are such that g*x + p*y = d. Since p is prime, d = 1. Taking - // that modulo p results in g*x = 1, therefore x is the inverse element. - if z.neg { - z.Add(z, p) - } - return z -} - -// Lsh sets z = x << n and returns z. -func (z *Int) Lsh(x *Int, n uint) *Int { - z.abs = z.abs.shl(x.abs, n) - z.neg = x.neg - return z -} - -// Rsh sets z = x >> n and returns z. -func (z *Int) Rsh(x *Int, n uint) *Int { - if x.neg { - // (-x) >> s == ^(x-1) >> s == ^((x-1) >> s) == -(((x-1) >> s) + 1) - t := z.abs.sub(x.abs, natOne) // no underflow because |x| > 0 - t = t.shr(t, n) - z.abs = t.add(t, natOne) - z.neg = true // z cannot be zero if x is negative - return z - } - - z.abs = z.abs.shr(x.abs, n) - z.neg = false - return z -} - -// Bit returns the value of the i'th bit of z. That is, it -// returns (z>>i)&1. The bit index i must be >= 0. -func (z *Int) Bit(i int) uint { - if i < 0 { - panic("negative bit index") - } - if z.neg { - t := nat{}.sub(z.abs, natOne) - return t.bit(uint(i)) ^ 1 - } - - return z.abs.bit(uint(i)) -} - -// SetBit sets the i'th bit of z to bit and returns z. -// That is, if bit is 1 SetBit sets z = x | (1 << i); -// if bit is 0 it sets z = x &^ (1 << i). If bit is not 0 or 1, -// SetBit will panic. -func (z *Int) SetBit(x *Int, i int, b uint) *Int { - if i < 0 { - panic("negative bit index") - } - if x.neg { - t := z.abs.sub(x.abs, natOne) - t = t.setBit(t, uint(i), b^1) - z.abs = t.add(t, natOne) - z.neg = len(z.abs) > 0 - return z - } - z.abs = z.abs.setBit(x.abs, uint(i), b) - z.neg = false - return z -} - -// And sets z = x & y and returns z. -func (z *Int) And(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.or(x1, y1), natOne) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x & y == x & y - z.abs = z.abs.and(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // & is symmetric - } - - // x & (-y) == x & ^(y-1) == x &^ (y-1) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.andNot(x.abs, y1) - z.neg = false - return z -} - -// AndNot sets z = x &^ y and returns z. -func (z *Int) AndNot(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.andNot(y1, x1) - z.neg = false - return z - } - - // x &^ y == x &^ y - z.abs = z.abs.andNot(x.abs, y.abs) - z.neg = false - return z - } - - if x.neg { - // (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1) - x1 := nat{}.sub(x.abs, natOne) - z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne) - z.neg = true // z cannot be zero if x is negative and y is positive - return z - } - - // x &^ (-y) == x &^ ^(y-1) == x & (y-1) - y1 := nat{}.add(y.abs, natOne) - z.abs = z.abs.and(x.abs, y1) - z.neg = false - return z -} - -// Or sets z = x | y and returns z. -func (z *Int) Or(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.and(x1, y1), natOne) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x | y == x | y - z.abs = z.abs.or(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // | is symmetric - } - - // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne) - z.neg = true // z cannot be zero if one of x or y is negative - return z -} - -// Xor sets z = x ^ y and returns z. -func (z *Int) Xor(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.xor(x1, y1) - z.neg = false - return z - } - - // x ^ y == x ^ y - z.abs = z.abs.xor(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // ^ is symmetric - } - - // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) - y1 := nat{}.sub(y.abs, natOne) - z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne) - z.neg = true // z cannot be zero if only one of x or y is negative - return z -} - -// Not sets z = ^x and returns z. -func (z *Int) Not(x *Int) *Int { - if x.neg { - // ^(-x) == ^(^(x-1)) == x-1 - z.abs = z.abs.sub(x.abs, natOne) - z.neg = false - return z - } - - // ^x == -x-1 == -(x+1) - z.abs = z.abs.add(x.abs, natOne) - z.neg = true // z cannot be zero if x is positive - return z -} - -// Gob codec version. Permits backward-compatible changes to the encoding. -const intGobVersion byte = 1 - -// GobEncode implements the gob.GobEncoder interface. -func (z *Int) GobEncode() ([]byte, os.Error) { - buf := make([]byte, 1+len(z.abs)*_S) // extra byte for version and sign bit - i := z.abs.bytes(buf) - 1 // i >= 0 - b := intGobVersion << 1 // make space for sign bit - if z.neg { - b |= 1 - } - buf[i] = b - return buf[i:], nil -} - -// GobDecode implements the gob.GobDecoder interface. -func (z *Int) GobDecode(buf []byte) os.Error { - if len(buf) == 0 { - return os.NewError("Int.GobDecode: no data") - } - b := buf[0] - if b>>1 != intGobVersion { - return os.NewError(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1)) - } - z.neg = b&1 != 0 - z.abs = z.abs.setBytes(buf[1:]) - return nil -} diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go deleted file mode 100755 index 03446d6ae..000000000 --- a/src/pkg/big/int_test.go +++ /dev/null @@ -1,1391 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package big - -import ( - "bytes" - "encoding/hex" - "fmt" - "gob" - "testing" - "testing/quick" -) - -func isNormalized(x *Int) bool { - if len(x.abs) == 0 { - return !x.neg - } - // len(x.abs) > 0 - return x.abs[len(x.abs)-1] != 0 -} - -type funZZ func(z, x, y *Int) *Int -type argZZ struct { - z, x, y *Int -} - -var sumZZ = []argZZ{ - {NewInt(0), NewInt(0), NewInt(0)}, - {NewInt(1), NewInt(1), NewInt(0)}, - {NewInt(1111111110), NewInt(123456789), NewInt(987654321)}, - {NewInt(-1), NewInt(-1), NewInt(0)}, - {NewInt(864197532), NewInt(-123456789), NewInt(987654321)}, - {NewInt(-1111111110), NewInt(-123456789), NewInt(-987654321)}, -} - -var prodZZ = []argZZ{ - {NewInt(0), NewInt(0), NewInt(0)}, - {NewInt(0), NewInt(1), NewInt(0)}, - {NewInt(1), NewInt(1), NewInt(1)}, - {NewInt(-991 * 991), NewInt(991), NewInt(-991)}, - // TODO(gri) add larger products -} - -func TestSignZ(t *testing.T) { - var zero Int - for _, a := range sumZZ { - s := a.z.Sign() - e := a.z.Cmp(&zero) - if s != e { - t.Errorf("got %d; want %d for z = %v", s, e, a.z) - } - } -} - -func TestSetZ(t *testing.T) { - for _, a := range sumZZ { - var z Int - z.Set(a.z) - if !isNormalized(&z) { - t.Errorf("%v is not normalized", z) - } - if (&z).Cmp(a.z) != 0 { - t.Errorf("got z = %v; want %v", z, a.z) - } - } -} - -func TestAbsZ(t *testing.T) { - var zero Int - for _, a := range sumZZ { - var z Int - z.Abs(a.z) - var e Int - e.Set(a.z) - if e.Cmp(&zero) < 0 { - e.Sub(&zero, &e) - } - if z.Cmp(&e) != 0 { - t.Errorf("got z = %v; want %v", z, e) - } - } -} - -func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) { - var z Int - f(&z, a.x, a.y) - if !isNormalized(&z) { - t.Errorf("%s%v is not normalized", z, msg) - } - if (&z).Cmp(a.z) != 0 { - t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z) - } -} - -func TestSumZZ(t *testing.T) { - AddZZ := func(z, x, y *Int) *Int { return z.Add(x, y) } - SubZZ := func(z, x, y *Int) *Int { return z.Sub(x, y) } - for _, a := range sumZZ { - arg := a - testFunZZ(t, "AddZZ", AddZZ, arg) - - arg = argZZ{a.z, a.y, a.x} - testFunZZ(t, "AddZZ symmetric", AddZZ, arg) - - arg = argZZ{a.x, a.z, a.y} - testFunZZ(t, "SubZZ", SubZZ, arg) - - arg = argZZ{a.y, a.z, a.x} - testFunZZ(t, "SubZZ symmetric", SubZZ, arg) - } -} - -func TestProdZZ(t *testing.T) { - MulZZ := func(z, x, y *Int) *Int { return z.Mul(x, y) } - for _, a := range prodZZ { - arg := a - testFunZZ(t, "MulZZ", MulZZ, arg) - - arg = argZZ{a.z, a.y, a.x} - testFunZZ(t, "MulZZ symmetric", MulZZ, arg) - } -} - -// mulBytes returns x*y via grade school multiplication. Both inputs -// and the result are assumed to be in big-endian representation (to -// match the semantics of Int.Bytes and Int.SetBytes). -func mulBytes(x, y []byte) []byte { - z := make([]byte, len(x)+len(y)) - - // multiply - k0 := len(z) - 1 - for j := len(y) - 1; j >= 0; j-- { - d := int(y[j]) - if d != 0 { - k := k0 - carry := 0 - for i := len(x) - 1; i >= 0; i-- { - t := int(z[k]) + int(x[i])*d + carry - z[k], carry = byte(t), t>>8 - k-- - } - z[k] = byte(carry) - } - k0-- - } - - // normalize (remove leading 0's) - i := 0 - for i < len(z) && z[i] == 0 { - i++ - } - - return z[i:] -} - -func checkMul(a, b []byte) bool { - var x, y, z1 Int - x.SetBytes(a) - y.SetBytes(b) - z1.Mul(&x, &y) - - var z2 Int - z2.SetBytes(mulBytes(a, b)) - - return z1.Cmp(&z2) == 0 -} - -func TestMul(t *testing.T) { - if err := quick.Check(checkMul, nil); err != nil { - t.Error(err) - } -} - -var mulRangesZ = []struct { - a, b int64 - prod string -}{ - // entirely positive ranges are covered by mulRangesN - {-1, 1, "0"}, - {-2, -1, "2"}, - {-3, -2, "6"}, - {-3, -1, "-6"}, - {1, 3, "6"}, - {-10, -10, "-10"}, - {0, -1, "1"}, // empty range - {-1, -100, "1"}, // empty range - {-1, 1, "0"}, // range includes 0 - {-1e9, 0, "0"}, // range includes 0 - {-1e9, 1e9, "0"}, // range includes 0 - {-10, -1, "3628800"}, // 10! - {-20, -2, "-2432902008176640000"}, // -20! - {-99, -1, - "-933262154439441526816992388562667004907159682643816214685929" + - "638952175999932299156089414639761565182862536979208272237582" + - "511852109168640000000000000000000000", // -99! - }, -} - -func TestMulRangeZ(t *testing.T) { - var tmp Int - // test entirely positive ranges - for i, r := range mulRangesN { - prod := tmp.MulRange(int64(r.a), int64(r.b)).String() - if prod != r.prod { - t.Errorf("#%da: got %s; want %s", i, prod, r.prod) - } - } - // test other ranges - for i, r := range mulRangesZ { - prod := tmp.MulRange(r.a, r.b).String() - if prod != r.prod { - t.Errorf("#%db: got %s; want %s", i, prod, r.prod) - } - } -} - -var stringTests = []struct { - in string - out string - base int - val int64 - ok bool -}{ - {in: "", ok: false}, - {in: "a", ok: false}, - {in: "z", ok: false}, - {in: "+", ok: false}, - {in: "-", ok: false}, - {in: "0b", ok: false}, - {in: "0x", ok: false}, - {in: "2", base: 2, ok: false}, - {in: "0b2", base: 0, ok: false}, - {in: "08", ok: false}, - {in: "8", base: 8, ok: false}, - {in: "0xg", base: 0, ok: false}, - {in: "g", base: 16, ok: false}, - {"0", "0", 0, 0, true}, - {"0", "0", 10, 0, true}, - {"0", "0", 16, 0, true}, - {"+0", "0", 0, 0, true}, - {"-0", "0", 0, 0, true}, - {"10", "10", 0, 10, true}, - {"10", "10", 10, 10, true}, - {"10", "10", 16, 16, true}, - {"-10", "-10", 16, -16, true}, - {"+10", "10", 16, 16, true}, - {"0x10", "16", 0, 16, true}, - {in: "0x10", base: 16, ok: false}, - {"-0x10", "-16", 0, -16, true}, - {"+0x10", "16", 0, 16, true}, - {"00", "0", 0, 0, true}, - {"0", "0", 8, 0, true}, - {"07", "7", 0, 7, true}, - {"7", "7", 8, 7, true}, - {"023", "19", 0, 19, true}, - {"23", "23", 8, 19, true}, - {"cafebabe", "cafebabe", 16, 0xcafebabe, true}, - {"0b0", "0", 0, 0, true}, - {"-111", "-111", 2, -7, true}, - {"-0b111", "-7", 0, -7, true}, - {"0b1001010111", "599", 0, 0x257, true}, - {"1001010111", "1001010111", 2, 0x257, true}, -} - -func format(base int) string { - switch base { - case 2: - return "%b" - case 8: - return "%o" - case 16: - return "%x" - } - return "%d" -} - -func TestGetString(t *testing.T) { - z := new(Int) - for i, test := range stringTests { - if !test.ok { - continue - } - z.SetInt64(test.val) - - if test.base == 10 { - s := z.String() - if s != test.out { - t.Errorf("#%da got %s; want %s", i, s, test.out) - } - } - - s := fmt.Sprintf(format(test.base), z) - if s != test.out { - t.Errorf("#%db got %s; want %s", i, s, test.out) - } - } -} - -func TestSetString(t *testing.T) { - tmp := new(Int) - for i, test := range stringTests { - n1, ok1 := new(Int).SetString(test.in, test.base) - n2, ok2 := tmp.SetString(test.in, test.base) - expected := NewInt(test.val) - if ok1 != test.ok || ok2 != test.ok { - t.Errorf("#%d (input '%s') ok incorrect (should be %t)", i, test.in, test.ok) - continue - } - if !ok1 || !ok2 { - continue - } - - if ok1 && !isNormalized(n1) { - t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n1) - } - if ok2 && !isNormalized(n2) { - t.Errorf("#%d (input '%s'): %v is not normalized", i, test.in, *n2) - } - - if n1.Cmp(expected) != 0 { - t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n1, test.val) - } - if n2.Cmp(expected) != 0 { - t.Errorf("#%d (input '%s') got: %s want: %d", i, test.in, n2, test.val) - } - } -} - -var formatTests = []struct { - input string - format string - output string -}{ - {"", "%x", ""}, - {"", "%#x", ""}, - {"", "%#y", "%!y(big.Int=)"}, - - {"10", "%b", "1010"}, - {"10", "%o", "12"}, - {"10", "%d", "10"}, - {"10", "%v", "10"}, - {"10", "%x", "a"}, - {"10", "%X", "A"}, - {"-10", "%X", "-A"}, - {"10", "%y", "%!y(big.Int=10)"}, - {"-10", "%y", "%!y(big.Int=-10)"}, - - {"10", "%#b", "1010"}, - {"10", "%#o", "012"}, - {"10", "%#d", "10"}, - {"10", "%#v", "10"}, - {"10", "%#x", "0xa"}, - {"10", "%#X", "0XA"}, - {"-10", "%#X", "-0XA"}, - {"10", "%#y", "%!y(big.Int=10)"}, - {"-10", "%#y", "%!y(big.Int=-10)"}, - - {"1234", "%d", "1234"}, - {"1234", "%3d", "1234"}, - {"1234", "%4d", "1234"}, - {"-1234", "%d", "-1234"}, - {"1234", "% 5d", " 1234"}, - {"1234", "%+5d", "+1234"}, - {"1234", "%-5d", "1234 "}, - {"1234", "%x", "4d2"}, - {"1234", "%X", "4D2"}, - {"-1234", "%3x", "-4d2"}, - {"-1234", "%4x", "-4d2"}, - {"-1234", "%5x", " -4d2"}, - {"-1234", "%-5x", "-4d2 "}, - {"1234", "%03d", "1234"}, - {"1234", "%04d", "1234"}, - {"1234", "%05d", "01234"}, - {"1234", "%06d", "001234"}, - {"-1234", "%06d", "-01234"}, - {"1234", "%+06d", "+01234"}, - {"1234", "% 06d", " 01234"}, - {"1234", "%-6d", "1234 "}, - {"1234", "%-06d", "1234 "}, - {"-1234", "%-06d", "-1234 "}, - - {"1234", "%.3d", "1234"}, - {"1234", "%.4d", "1234"}, - {"1234", "%.5d", "01234"}, - {"1234", "%.6d", "001234"}, - {"-1234", "%.3d", "-1234"}, - {"-1234", "%.4d", "-1234"}, - {"-1234", "%.5d", "-01234"}, - {"-1234", "%.6d", "-001234"}, - - {"1234", "%8.3d", " 1234"}, - {"1234", "%8.4d", " 1234"}, - {"1234", "%8.5d", " 01234"}, - {"1234", "%8.6d", " 001234"}, - {"-1234", "%8.3d", " -1234"}, - {"-1234", "%8.4d", " -1234"}, - {"-1234", "%8.5d", " -01234"}, - {"-1234", "%8.6d", " -001234"}, - - {"1234", "%+8.3d", " +1234"}, - {"1234", "%+8.4d", " +1234"}, - {"1234", "%+8.5d", " +01234"}, - {"1234", "%+8.6d", " +001234"}, - {"-1234", "%+8.3d", " -1234"}, - {"-1234", "%+8.4d", " -1234"}, - {"-1234", "%+8.5d", " -01234"}, - {"-1234", "%+8.6d", " -001234"}, - - {"1234", "% 8.3d", " 1234"}, - {"1234", "% 8.4d", " 1234"}, - {"1234", "% 8.5d", " 01234"}, - {"1234", "% 8.6d", " 001234"}, - {"-1234", "% 8.3d", " -1234"}, - {"-1234", "% 8.4d", " -1234"}, - {"-1234", "% 8.5d", " -01234"}, - {"-1234", "% 8.6d", " -001234"}, - - {"1234", "%.3x", "4d2"}, - {"1234", "%.4x", "04d2"}, - {"1234", "%.5x", "004d2"}, - {"1234", "%.6x", "0004d2"}, - {"-1234", "%.3x", "-4d2"}, - {"-1234", "%.4x", "-04d2"}, - {"-1234", "%.5x", "-004d2"}, - {"-1234", "%.6x", "-0004d2"}, - - {"1234", "%8.3x", " 4d2"}, - {"1234", "%8.4x", " 04d2"}, - {"1234", "%8.5x", " 004d2"}, - {"1234", "%8.6x", " 0004d2"}, - {"-1234", "%8.3x", " -4d2"}, - {"-1234", "%8.4x", " -04d2"}, - {"-1234", "%8.5x", " -004d2"}, - {"-1234", "%8.6x", " -0004d2"}, - - {"1234", "%+8.3x", " +4d2"}, - {"1234", "%+8.4x", " +04d2"}, - {"1234", "%+8.5x", " +004d2"}, - {"1234", "%+8.6x", " +0004d2"}, - {"-1234", "%+8.3x", " -4d2"}, - {"-1234", "%+8.4x", " -04d2"}, - {"-1234", "%+8.5x", " -004d2"}, - {"-1234", "%+8.6x", " -0004d2"}, - - {"1234", "% 8.3x", " 4d2"}, - {"1234", "% 8.4x", " 04d2"}, - {"1234", "% 8.5x", " 004d2"}, - {"1234", "% 8.6x", " 0004d2"}, - {"1234", "% 8.7x", " 00004d2"}, - {"1234", "% 8.8x", " 000004d2"}, - {"-1234", "% 8.3x", " -4d2"}, - {"-1234", "% 8.4x", " -04d2"}, - {"-1234", "% 8.5x", " -004d2"}, - {"-1234", "% 8.6x", " -0004d2"}, - {"-1234", "% 8.7x", "-00004d2"}, - {"-1234", "% 8.8x", "-000004d2"}, - - {"1234", "%-8.3d", "1234 "}, - {"1234", "%-8.4d", "1234 "}, - {"1234", "%-8.5d", "01234 "}, - {"1234", "%-8.6d", "001234 "}, - {"1234", "%-8.7d", "0001234 "}, - {"1234", "%-8.8d", "00001234"}, - {"-1234", "%-8.3d", "-1234 "}, - {"-1234", "%-8.4d", "-1234 "}, - {"-1234", "%-8.5d", "-01234 "}, - {"-1234", "%-8.6d", "-001234 "}, - {"-1234", "%-8.7d", "-0001234"}, - {"-1234", "%-8.8d", "-00001234"}, - - {"16777215", "%b", "111111111111111111111111"}, // 2**24 - 1 - - {"0", "%.d", ""}, - {"0", "%.0d", ""}, - {"0", "%3.d", ""}, -} - -func TestFormat(t *testing.T) { - for i, test := range formatTests { - var x *Int - if test.input != "" { - var ok bool - x, ok = new(Int).SetString(test.input, 0) - if !ok { - t.Errorf("#%d failed reading input %s", i, test.input) - } - } - output := fmt.Sprintf(test.format, x) - if output != test.output { - t.Errorf("#%d got %q; want %q, {%q, %q, %q}", i, output, test.output, test.input, test.format, test.output) - } - } -} - -var scanTests = []struct { - input string - format string - output string - remaining int -}{ - {"1010", "%b", "10", 0}, - {"0b1010", "%v", "10", 0}, - {"12", "%o", "10", 0}, - {"012", "%v", "10", 0}, - {"10", "%d", "10", 0}, - {"10", "%v", "10", 0}, - {"a", "%x", "10", 0}, - {"0xa", "%v", "10", 0}, - {"A", "%X", "10", 0}, - {"-A", "%X", "-10", 0}, - {"+0b1011001", "%v", "89", 0}, - {"0xA", "%v", "10", 0}, - {"0 ", "%v", "0", 1}, - {"2+3", "%v", "2", 2}, - {"0XABC 12", "%v", "2748", 3}, -} - -func TestScan(t *testing.T) { - var buf bytes.Buffer - for i, test := range scanTests { - x := new(Int) - buf.Reset() - buf.WriteString(test.input) - if _, err := fmt.Fscanf(&buf, test.format, x); err != nil { - t.Errorf("#%d error: %s", i, err.String()) - } - if x.String() != test.output { - t.Errorf("#%d got %s; want %s", i, x.String(), test.output) - } - if buf.Len() != test.remaining { - t.Errorf("#%d got %d bytes remaining; want %d", i, buf.Len(), test.remaining) - } - } -} - -// Examples from the Go Language Spec, section "Arithmetic operators" -var divisionSignsTests = []struct { - x, y int64 - q, r int64 // T-division - d, m int64 // Euclidian division -}{ - {5, 3, 1, 2, 1, 2}, - {-5, 3, -1, -2, -2, 1}, - {5, -3, -1, 2, -1, 2}, - {-5, -3, 1, -2, 2, 1}, - {1, 2, 0, 1, 0, 1}, - {8, 4, 2, 0, 2, 0}, -} - -func TestDivisionSigns(t *testing.T) { - for i, test := range divisionSignsTests { - x := NewInt(test.x) - y := NewInt(test.y) - q := NewInt(test.q) - r := NewInt(test.r) - d := NewInt(test.d) - m := NewInt(test.m) - - q1 := new(Int).Quo(x, y) - r1 := new(Int).Rem(x, y) - if !isNormalized(q1) { - t.Errorf("#%d Quo: %v is not normalized", i, *q1) - } - if !isNormalized(r1) { - t.Errorf("#%d Rem: %v is not normalized", i, *r1) - } - if q1.Cmp(q) != 0 || r1.Cmp(r) != 0 { - t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q1, r1, q, r) - } - - q2, r2 := new(Int).QuoRem(x, y, new(Int)) - if !isNormalized(q2) { - t.Errorf("#%d Quo: %v is not normalized", i, *q2) - } - if !isNormalized(r2) { - t.Errorf("#%d Rem: %v is not normalized", i, *r2) - } - if q2.Cmp(q) != 0 || r2.Cmp(r) != 0 { - t.Errorf("#%d QuoRem: got (%s, %s), want (%s, %s)", i, q2, r2, q, r) - } - - d1 := new(Int).Div(x, y) - m1 := new(Int).Mod(x, y) - if !isNormalized(d1) { - t.Errorf("#%d Div: %v is not normalized", i, *d1) - } - if !isNormalized(m1) { - t.Errorf("#%d Mod: %v is not normalized", i, *m1) - } - if d1.Cmp(d) != 0 || m1.Cmp(m) != 0 { - t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d1, m1, d, m) - } - - d2, m2 := new(Int).DivMod(x, y, new(Int)) - if !isNormalized(d2) { - t.Errorf("#%d Div: %v is not normalized", i, *d2) - } - if !isNormalized(m2) { - t.Errorf("#%d Mod: %v is not normalized", i, *m2) - } - if d2.Cmp(d) != 0 || m2.Cmp(m) != 0 { - t.Errorf("#%d DivMod: got (%s, %s), want (%s, %s)", i, d2, m2, d, m) - } - } -} - -func checkSetBytes(b []byte) bool { - hex1 := hex.EncodeToString(new(Int).SetBytes(b).Bytes()) - hex2 := hex.EncodeToString(b) - - for len(hex1) < len(hex2) { - hex1 = "0" + hex1 - } - - for len(hex1) > len(hex2) { - hex2 = "0" + hex2 - } - - return hex1 == hex2 -} - -func TestSetBytes(t *testing.T) { - if err := quick.Check(checkSetBytes, nil); err != nil { - t.Error(err) - } -} - -func checkBytes(b []byte) bool { - b2 := new(Int).SetBytes(b).Bytes() - return bytes.Compare(b, b2) == 0 -} - -func TestBytes(t *testing.T) { - if err := quick.Check(checkSetBytes, nil); err != nil { - t.Error(err) - } -} - -func checkQuo(x, y []byte) bool { - u := new(Int).SetBytes(x) - v := new(Int).SetBytes(y) - - if len(v.abs) == 0 { - return true - } - - r := new(Int) - q, r := new(Int).QuoRem(u, v, r) - - if r.Cmp(v) >= 0 { - return false - } - - uprime := new(Int).Set(q) - uprime.Mul(uprime, v) - uprime.Add(uprime, r) - - return uprime.Cmp(u) == 0 -} - -var quoTests = []struct { - x, y string - q, r string -}{ - { - "476217953993950760840509444250624797097991362735329973741718102894495832294430498335824897858659711275234906400899559094370964723884706254265559534144986498357", - "9353930466774385905609975137998169297361893554149986716853295022578535724979483772383667534691121982974895531435241089241440253066816724367338287092081996", - "50911", - "1", - }, - { - "11510768301994997771168", - "1328165573307167369775", - "8", - "885443715537658812968", - }, -} - -func TestQuo(t *testing.T) { - if err := quick.Check(checkQuo, nil); err != nil { - t.Error(err) - } - - for i, test := range quoTests { - x, _ := new(Int).SetString(test.x, 10) - y, _ := new(Int).SetString(test.y, 10) - expectedQ, _ := new(Int).SetString(test.q, 10) - expectedR, _ := new(Int).SetString(test.r, 10) - - r := new(Int) - q, r := new(Int).QuoRem(x, y, r) - - if q.Cmp(expectedQ) != 0 || r.Cmp(expectedR) != 0 { - t.Errorf("#%d got (%s, %s) want (%s, %s)", i, q, r, expectedQ, expectedR) - } - } -} - -func TestQuoStepD6(t *testing.T) { - // See Knuth, Volume 2, section 4.3.1, exercise 21. This code exercises - // a code path which only triggers 1 in 10^{-19} cases. - - u := &Int{false, nat{0, 0, 1 + 1<<(_W-1), _M ^ (1 << (_W - 1))}} - v := &Int{false, nat{5, 2 + 1<<(_W-1), 1 << (_W - 1)}} - - r := new(Int) - q, r := new(Int).QuoRem(u, v, r) - const expectedQ64 = "18446744073709551613" - const expectedR64 = "3138550867693340382088035895064302439801311770021610913807" - const expectedQ32 = "4294967293" - const expectedR32 = "39614081266355540837921718287" - if q.String() != expectedQ64 && q.String() != expectedQ32 || - r.String() != expectedR64 && r.String() != expectedR32 { - t.Errorf("got (%s, %s) want (%s, %s) or (%s, %s)", q, r, expectedQ64, expectedR64, expectedQ32, expectedR32) - } -} - -var bitLenTests = []struct { - in string - out int -}{ - {"-1", 1}, - {"0", 0}, - {"1", 1}, - {"2", 2}, - {"4", 3}, - {"0xabc", 12}, - {"0x8000", 16}, - {"0x80000000", 32}, - {"0x800000000000", 48}, - {"0x8000000000000000", 64}, - {"0x80000000000000000000", 80}, - {"-0x4000000000000000000000", 87}, -} - -func TestBitLen(t *testing.T) { - for i, test := range bitLenTests { - x, ok := new(Int).SetString(test.in, 0) - if !ok { - t.Errorf("#%d test input invalid: %s", i, test.in) - continue - } - - if n := x.BitLen(); n != test.out { - t.Errorf("#%d got %d want %d", i, n, test.out) - } - } -} - -var expTests = []struct { - x, y, m string - out string -}{ - {"5", "0", "", "1"}, - {"-5", "0", "", "-1"}, - {"5", "1", "", "5"}, - {"-5", "1", "", "-5"}, - {"-2", "3", "2", "0"}, - {"5", "2", "", "25"}, - {"1", "65537", "2", "1"}, - {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, - {"0x8000000000000000", "2", "6719", "4944"}, - {"0x8000000000000000", "3", "6719", "5447"}, - {"0x8000000000000000", "1000", "6719", "1603"}, - {"0x8000000000000000", "1000000", "6719", "3199"}, - { - "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", - "298472983472983471903246121093472394872319615612417471234712061", - "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", - "23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291", - }, -} - -func TestExp(t *testing.T) { - for i, test := range expTests { - x, ok1 := new(Int).SetString(test.x, 0) - y, ok2 := new(Int).SetString(test.y, 0) - out, ok3 := new(Int).SetString(test.out, 0) - - var ok4 bool - var m *Int - - if len(test.m) == 0 { - m, ok4 = nil, true - } else { - m, ok4 = new(Int).SetString(test.m, 0) - } - - if !ok1 || !ok2 || !ok3 || !ok4 { - t.Errorf("#%d: error in input", i) - continue - } - - z := y.Exp(x, y, m) - if !isNormalized(z) { - t.Errorf("#%d: %v is not normalized", i, *z) - } - if z.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, z, out) - } - } -} - -func checkGcd(aBytes, bBytes []byte) bool { - a := new(Int).SetBytes(aBytes) - b := new(Int).SetBytes(bBytes) - - x := new(Int) - y := new(Int) - d := new(Int) - - GcdInt(d, x, y, a, b) - x.Mul(x, a) - y.Mul(y, b) - x.Add(x, y) - - return x.Cmp(d) == 0 -} - -var gcdTests = []struct { - a, b int64 - d, x, y int64 -}{ - {120, 23, 1, -9, 47}, -} - -func TestGcd(t *testing.T) { - for i, test := range gcdTests { - a := NewInt(test.a) - b := NewInt(test.b) - - x := new(Int) - y := new(Int) - d := new(Int) - - expectedX := NewInt(test.x) - expectedY := NewInt(test.y) - expectedD := NewInt(test.d) - - GcdInt(d, x, y, a, b) - - if expectedX.Cmp(x) != 0 || - expectedY.Cmp(y) != 0 || - expectedD.Cmp(d) != 0 { - t.Errorf("#%d got (%s %s %s) want (%s %s %s)", i, x, y, d, expectedX, expectedY, expectedD) - } - } - - quick.Check(checkGcd, nil) -} - -var primes = []string{ - "2", - "3", - "5", - "7", - "11", - - "13756265695458089029", - "13496181268022124907", - "10953742525620032441", - "17908251027575790097", - - // http://code.google.com/p/go/issues/detail?id=638 - "18699199384836356663", - - "98920366548084643601728869055592650835572950932266967461790948584315647051443", - "94560208308847015747498523884063394671606671904944666360068158221458669711639", - - // http://primes.utm.edu/lists/small/small3.html - "449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163", - "230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593", - "5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993", - "203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123", -} - -var composites = []string{ - "21284175091214687912771199898307297748211672914763848041968395774954376176754", - "6084766654921918907427900243509372380954290099172559290432744450051395395951", - "84594350493221918389213352992032324280367711247940675652888030554255915464401", - "82793403787388584738507275144194252681", -} - -func TestProbablyPrime(t *testing.T) { - nreps := 20 - if testing.Short() { - nreps = 1 - } - for i, s := range primes { - p, _ := new(Int).SetString(s, 10) - if !ProbablyPrime(p, nreps) { - t.Errorf("#%d prime found to be non-prime (%s)", i, s) - } - } - - for i, s := range composites { - c, _ := new(Int).SetString(s, 10) - if ProbablyPrime(c, nreps) { - t.Errorf("#%d composite found to be prime (%s)", i, s) - } - if testing.Short() { - break - } - } -} - -type intShiftTest struct { - in string - shift uint - out string -} - -var rshTests = []intShiftTest{ - {"0", 0, "0"}, - {"-0", 0, "0"}, - {"0", 1, "0"}, - {"0", 2, "0"}, - {"1", 0, "1"}, - {"1", 1, "0"}, - {"1", 2, "0"}, - {"2", 0, "2"}, - {"2", 1, "1"}, - {"-1", 0, "-1"}, - {"-1", 1, "-1"}, - {"-1", 10, "-1"}, - {"-100", 2, "-25"}, - {"-100", 3, "-13"}, - {"-100", 100, "-1"}, - {"4294967296", 0, "4294967296"}, - {"4294967296", 1, "2147483648"}, - {"4294967296", 2, "1073741824"}, - {"18446744073709551616", 0, "18446744073709551616"}, - {"18446744073709551616", 1, "9223372036854775808"}, - {"18446744073709551616", 2, "4611686018427387904"}, - {"18446744073709551616", 64, "1"}, - {"340282366920938463463374607431768211456", 64, "18446744073709551616"}, - {"340282366920938463463374607431768211456", 128, "1"}, -} - -func TestRsh(t *testing.T) { - for i, test := range rshTests { - in, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - out := new(Int).Rsh(in, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if out.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, out, expected) - } - } -} - -func TestRshSelf(t *testing.T) { - for i, test := range rshTests { - z, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - z.Rsh(z, test.shift) - - if !isNormalized(z) { - t.Errorf("#%d: %v is not normalized", i, *z) - } - if z.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, z, expected) - } - } -} - -var lshTests = []intShiftTest{ - {"0", 0, "0"}, - {"0", 1, "0"}, - {"0", 2, "0"}, - {"1", 0, "1"}, - {"1", 1, "2"}, - {"1", 2, "4"}, - {"2", 0, "2"}, - {"2", 1, "4"}, - {"2", 2, "8"}, - {"-87", 1, "-174"}, - {"4294967296", 0, "4294967296"}, - {"4294967296", 1, "8589934592"}, - {"4294967296", 2, "17179869184"}, - {"18446744073709551616", 0, "18446744073709551616"}, - {"9223372036854775808", 1, "18446744073709551616"}, - {"4611686018427387904", 2, "18446744073709551616"}, - {"1", 64, "18446744073709551616"}, - {"18446744073709551616", 64, "340282366920938463463374607431768211456"}, - {"1", 128, "340282366920938463463374607431768211456"}, -} - -func TestLsh(t *testing.T) { - for i, test := range lshTests { - in, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - out := new(Int).Lsh(in, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if out.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, out, expected) - } - } -} - -func TestLshSelf(t *testing.T) { - for i, test := range lshTests { - z, _ := new(Int).SetString(test.in, 10) - expected, _ := new(Int).SetString(test.out, 10) - z.Lsh(z, test.shift) - - if !isNormalized(z) { - t.Errorf("#%d: %v is not normalized", i, *z) - } - if z.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, z, expected) - } - } -} - -func TestLshRsh(t *testing.T) { - for i, test := range rshTests { - in, _ := new(Int).SetString(test.in, 10) - out := new(Int).Lsh(in, test.shift) - out = out.Rsh(out, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if in.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, out, in) - } - } - for i, test := range lshTests { - in, _ := new(Int).SetString(test.in, 10) - out := new(Int).Lsh(in, test.shift) - out.Rsh(out, test.shift) - - if !isNormalized(out) { - t.Errorf("#%d: %v is not normalized", i, *out) - } - if in.Cmp(out) != 0 { - t.Errorf("#%d: got %s want %s", i, out, in) - } - } -} - -var int64Tests = []int64{ - 0, - 1, - -1, - 4294967295, - -4294967295, - 4294967296, - -4294967296, - 9223372036854775807, - -9223372036854775807, - -9223372036854775808, -} - -func TestInt64(t *testing.T) { - for i, testVal := range int64Tests { - in := NewInt(testVal) - out := in.Int64() - - if out != testVal { - t.Errorf("#%d got %d want %d", i, out, testVal) - } - } -} - -var bitwiseTests = []struct { - x, y string - and, or, xor, andNot string -}{ - {"0x00", "0x00", "0x00", "0x00", "0x00", "0x00"}, - {"0x00", "0x01", "0x00", "0x01", "0x01", "0x00"}, - {"0x01", "0x00", "0x00", "0x01", "0x01", "0x01"}, - {"-0x01", "0x00", "0x00", "-0x01", "-0x01", "-0x01"}, - {"-0xaf", "-0x50", "-0xf0", "-0x0f", "0xe1", "0x41"}, - {"0x00", "-0x01", "0x00", "-0x01", "-0x01", "0x00"}, - {"0x01", "0x01", "0x01", "0x01", "0x00", "0x00"}, - {"-0x01", "-0x01", "-0x01", "-0x01", "0x00", "0x00"}, - {"0x07", "0x08", "0x00", "0x0f", "0x0f", "0x07"}, - {"0x05", "0x0f", "0x05", "0x0f", "0x0a", "0x00"}, - {"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8", "0x0125b0"}, - {"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc", "-0x01c000"}, - {"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8", "0x8008"}, - { - "0x1000009dc6e3d9822cba04129bcbe3401", - "0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", - "0x1000001186210100001000009048c2001", - "0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", - "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", - "0x8c40c2d8822caa04120b8321400", - }, - { - "0x1000009dc6e3d9822cba04129bcbe3401", - "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", - "0x8c40c2d8822caa04120b8321401", - "-0xb9bd7d543685789d57ca918e82229142459020483cd2014001fd", - "-0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fe", - "0x1000001186210100001000009048c2000", - }, - { - "-0x1000009dc6e3d9822cba04129bcbe3401", - "-0xb9bd7d543685789d57cb918e833af352559021483cdb05cc21fd", - "-0xb9bd7d543685789d57cb918e8bfeff7fddb2ebe87dfbbdfe35fd", - "-0x1000001186210100001000009048c2001", - "0xb9bd7d543685789d57ca918e8ae69d6fcdb2eae87df2b97215fc", - "0xb9bd7d543685789d57ca918e82229142459020483cd2014001fc", - }, -} - -type bitFun func(z, x, y *Int) *Int - -func testBitFun(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { - expected := new(Int) - expected.SetString(exp, 0) - - out := f(new(Int), x, y) - if out.Cmp(expected) != 0 { - t.Errorf("%s: got %s want %s", msg, out, expected) - } -} - -func testBitFunSelf(t *testing.T, msg string, f bitFun, x, y *Int, exp string) { - self := new(Int) - self.Set(x) - expected := new(Int) - expected.SetString(exp, 0) - - self = f(self, self, y) - if self.Cmp(expected) != 0 { - t.Errorf("%s: got %s want %s", msg, self, expected) - } -} - -func altBit(x *Int, i int) uint { - z := new(Int).Rsh(x, uint(i)) - z = z.And(z, NewInt(1)) - if z.Cmp(new(Int)) != 0 { - return 1 - } - return 0 -} - -func altSetBit(z *Int, x *Int, i int, b uint) *Int { - one := NewInt(1) - m := one.Lsh(one, uint(i)) - switch b { - case 1: - return z.Or(x, m) - case 0: - return z.AndNot(x, m) - } - panic("set bit is not 0 or 1") -} - -func testBitset(t *testing.T, x *Int) { - n := x.BitLen() - z := new(Int).Set(x) - z1 := new(Int).Set(x) - for i := 0; i < n+10; i++ { - old := z.Bit(i) - old1 := altBit(z1, i) - if old != old1 { - t.Errorf("bitset: inconsistent value for Bit(%s, %d), got %v want %v", z1, i, old, old1) - } - z := new(Int).SetBit(z, i, 1) - z1 := altSetBit(new(Int), z1, i, 1) - if z.Bit(i) == 0 { - t.Errorf("bitset: bit %d of %s got 0 want 1", i, x) - } - if z.Cmp(z1) != 0 { - t.Errorf("bitset: inconsistent value after SetBit 1, got %s want %s", z, z1) - } - z.SetBit(z, i, 0) - altSetBit(z1, z1, i, 0) - if z.Bit(i) != 0 { - t.Errorf("bitset: bit %d of %s got 1 want 0", i, x) - } - if z.Cmp(z1) != 0 { - t.Errorf("bitset: inconsistent value after SetBit 0, got %s want %s", z, z1) - } - altSetBit(z1, z1, i, old) - z.SetBit(z, i, old) - if z.Cmp(z1) != 0 { - t.Errorf("bitset: inconsistent value after SetBit old, got %s want %s", z, z1) - } - } - if z.Cmp(x) != 0 { - t.Errorf("bitset: got %s want %s", z, x) - } -} - -var bitsetTests = []struct { - x string - i int - b uint -}{ - {"0", 0, 0}, - {"0", 200, 0}, - {"1", 0, 1}, - {"1", 1, 0}, - {"-1", 0, 1}, - {"-1", 200, 1}, - {"0x2000000000000000000000000000", 108, 0}, - {"0x2000000000000000000000000000", 109, 1}, - {"0x2000000000000000000000000000", 110, 0}, - {"-0x2000000000000000000000000001", 108, 1}, - {"-0x2000000000000000000000000001", 109, 0}, - {"-0x2000000000000000000000000001", 110, 1}, -} - -func TestBitSet(t *testing.T) { - for _, test := range bitwiseTests { - x := new(Int) - x.SetString(test.x, 0) - testBitset(t, x) - x = new(Int) - x.SetString(test.y, 0) - testBitset(t, x) - } - for i, test := range bitsetTests { - x := new(Int) - x.SetString(test.x, 0) - b := x.Bit(test.i) - if b != test.b { - - t.Errorf("#%d want %v got %v", i, test.b, b) - } - } -} - -func BenchmarkBitset(b *testing.B) { - z := new(Int) - z.SetBit(z, 512, 1) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - z.SetBit(z, i&512, 1) - } -} - -func BenchmarkBitsetNeg(b *testing.B) { - z := NewInt(-1) - z.SetBit(z, 512, 0) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - z.SetBit(z, i&512, 0) - } -} - -func BenchmarkBitsetOrig(b *testing.B) { - z := new(Int) - altSetBit(z, z, 512, 1) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - altSetBit(z, z, i&512, 1) - } -} - -func BenchmarkBitsetNegOrig(b *testing.B) { - z := NewInt(-1) - altSetBit(z, z, 512, 0) - b.ResetTimer() - b.StartTimer() - for i := b.N - 1; i >= 0; i-- { - altSetBit(z, z, i&512, 0) - } -} - -func TestBitwise(t *testing.T) { - x := new(Int) - y := new(Int) - for _, test := range bitwiseTests { - x.SetString(test.x, 0) - y.SetString(test.y, 0) - - testBitFun(t, "and", (*Int).And, x, y, test.and) - testBitFunSelf(t, "and", (*Int).And, x, y, test.and) - testBitFun(t, "andNot", (*Int).AndNot, x, y, test.andNot) - testBitFunSelf(t, "andNot", (*Int).AndNot, x, y, test.andNot) - testBitFun(t, "or", (*Int).Or, x, y, test.or) - testBitFunSelf(t, "or", (*Int).Or, x, y, test.or) - testBitFun(t, "xor", (*Int).Xor, x, y, test.xor) - testBitFunSelf(t, "xor", (*Int).Xor, x, y, test.xor) - } -} - -var notTests = []struct { - in string - out string -}{ - {"0", "-1"}, - {"1", "-2"}, - {"7", "-8"}, - {"0", "-1"}, - {"-81910", "81909"}, - { - "298472983472983471903246121093472394872319615612417471234712061", - "-298472983472983471903246121093472394872319615612417471234712062", - }, -} - -func TestNot(t *testing.T) { - in := new(Int) - out := new(Int) - expected := new(Int) - for i, test := range notTests { - in.SetString(test.in, 10) - expected.SetString(test.out, 10) - out = out.Not(in) - if out.Cmp(expected) != 0 { - t.Errorf("#%d: got %s want %s", i, out, expected) - } - out = out.Not(out) - if out.Cmp(in) != 0 { - t.Errorf("#%d: got %s want %s", i, out, in) - } - } -} - -var modInverseTests = []struct { - element string - prime string -}{ - {"1", "7"}, - {"1", "13"}, - {"239487239847", "2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919"}, -} - -func TestModInverse(t *testing.T) { - var element, prime Int - one := NewInt(1) - for i, test := range modInverseTests { - (&element).SetString(test.element, 10) - (&prime).SetString(test.prime, 10) - inverse := new(Int).ModInverse(&element, &prime) - inverse.Mul(inverse, &element) - inverse.Mod(inverse, &prime) - if inverse.Cmp(one) != 0 { - t.Errorf("#%d: failed (e·e^(-1)=%s)", i, inverse) - } - } -} - -// used by TestIntGobEncoding and TestRatGobEncoding -var gobEncodingTests = []string{ - "0", - "1", - "2", - "10", - "42", - "1234567890", - "298472983472983471903246121093472394872319615612417471234712061", -} - -func TestIntGobEncoding(t *testing.T) { - var medium bytes.Buffer - enc := gob.NewEncoder(&medium) - dec := gob.NewDecoder(&medium) - for i, test := range gobEncodingTests { - for j := 0; j < 2; j++ { - medium.Reset() // empty buffer for each test case (in case of failures) - stest := test - if j != 0 { - // negative numbers - stest = "-" + test - } - var tx Int - tx.SetString(stest, 10) - if err := enc.Encode(&tx); err != nil { - t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err) - } - var rx Int - if err := dec.Decode(&rx); err != nil { - t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err) - } - if rx.Cmp(&tx) != 0 { - t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx) - } - } - } -} diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go deleted file mode 100755 index be3aff29d..000000000 --- a/src/pkg/big/nat.go +++ /dev/null @@ -1,1272 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package big implements multi-precision arithmetic (big numbers). -// The following numeric types are supported: -// -// - Int signed integers -// - Rat rational numbers -// -// All methods on Int take the result as the receiver; if it is one -// of the operands it may be overwritten (and its memory reused). -// To enable chaining of operations, the result is also returned. -// -package big - -// This file contains operations on unsigned multi-precision integers. -// These are the building blocks for the operations on signed integers -// and rationals. - -import ( - "io" - "os" - "rand" -) - -// An unsigned integer x of the form -// -// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0] -// -// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n, -// with the digits x[i] as the slice elements. -// -// A number is normalized if the slice contains no leading 0 digits. -// During arithmetic operations, denormalized values may occur but are -// always normalized before returning the final result. The normalized -// representation of 0 is the empty or nil slice (length = 0). - -type nat []Word - -var ( - natOne = nat{1} - natTwo = nat{2} - natTen = nat{10} -) - -func (z nat) clear() { - for i := range z { - z[i] = 0 - } -} - -func (z nat) norm() nat { - i := len(z) - for i > 0 && z[i-1] == 0 { - i-- - } - return z[0:i] -} - -func (z nat) make(n int) nat { - if n <= cap(z) { - return z[0:n] // reuse z - } - // Choosing a good value for e has significant performance impact - // because it increases the chance that a value can be reused. - const e = 4 // extra capacity - return make(nat, n, n+e) -} - -func (z nat) setWord(x Word) nat { - if x == 0 { - return z.make(0) - } - z = z.make(1) - z[0] = x - return z -} - -func (z nat) setUint64(x uint64) nat { - // single-digit values - if w := Word(x); uint64(w) == x { - return z.setWord(w) - } - - // compute number of words n required to represent x - n := 0 - for t := x; t > 0; t >>= _W { - n++ - } - - // split x into n words - z = z.make(n) - for i := range z { - z[i] = Word(x & _M) - x >>= _W - } - - return z -} - -func (z nat) set(x nat) nat { - z = z.make(len(x)) - copy(z, x) - return z -} - -func (z nat) add(x, y nat) nat { - m := len(x) - n := len(y) - - switch { - case m < n: - return z.add(y, x) - case m == 0: - // n == 0 because m >= n; result is 0 - return z.make(0) - case n == 0: - // result is x - return z.set(x) - } - // m > 0 - - z = z.make(m + 1) - c := addVV(z[0:n], x, y) - if m > n { - c = addVW(z[n:m], x[n:], c) - } - z[m] = c - - return z.norm() -} - -func (z nat) sub(x, y nat) nat { - m := len(x) - n := len(y) - - switch { - case m < n: - panic("underflow") - case m == 0: - // n == 0 because m >= n; result is 0 - return z.make(0) - case n == 0: - // result is x - return z.set(x) - } - // m > 0 - - z = z.make(m) - c := subVV(z[0:n], x, y) - if m > n { - c = subVW(z[n:], x[n:], c) - } - if c != 0 { - panic("underflow") - } - - return z.norm() -} - -func (x nat) cmp(y nat) (r int) { - m := len(x) - n := len(y) - if m != n || m == 0 { - switch { - case m < n: - r = -1 - case m > n: - r = 1 - } - return - } - - i := m - 1 - for i > 0 && x[i] == y[i] { - i-- - } - - switch { - case x[i] < y[i]: - r = -1 - case x[i] > y[i]: - r = 1 - } - return -} - -func (z nat) mulAddWW(x nat, y, r Word) nat { - m := len(x) - if m == 0 || y == 0 { - return z.setWord(r) // result is r - } - // m > 0 - - z = z.make(m + 1) - z[m] = mulAddVWW(z[0:m], x, y, r) - - return z.norm() -} - -// basicMul multiplies x and y and leaves the result in z. -// The (non-normalized) result is placed in z[0 : len(x) + len(y)]. -func basicMul(z, x, y nat) { - z[0 : len(x)+len(y)].clear() // initialize z - for i, d := range y { - if d != 0 { - z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d) - } - } -} - -// Fast version of z[0:n+n>>1].add(z[0:n+n>>1], x[0:n]) w/o bounds checks. -// Factored out for readability - do not use outside karatsuba. -func karatsubaAdd(z, x nat, n int) { - if c := addVV(z[0:n], z, x); c != 0 { - addVW(z[n:n+n>>1], z[n:], c) - } -} - -// Like karatsubaAdd, but does subtract. -func karatsubaSub(z, x nat, n int) { - if c := subVV(z[0:n], z, x); c != 0 { - subVW(z[n:n+n>>1], z[n:], c) - } -} - -// Operands that are shorter than karatsubaThreshold are multiplied using -// "grade school" multiplication; for longer operands the Karatsuba algorithm -// is used. -var karatsubaThreshold int = 32 // computed by calibrate.go - -// karatsuba multiplies x and y and leaves the result in z. -// Both x and y must have the same length n and n must be a -// power of 2. The result vector z must have len(z) >= 6*n. -// The (non-normalized) result is placed in z[0 : 2*n]. -func karatsuba(z, x, y nat) { - n := len(y) - - // Switch to basic multiplication if numbers are odd or small. - // (n is always even if karatsubaThreshold is even, but be - // conservative) - if n&1 != 0 || n < karatsubaThreshold || n < 2 { - basicMul(z, x, y) - return - } - // n&1 == 0 && n >= karatsubaThreshold && n >= 2 - - // Karatsuba multiplication is based on the observation that - // for two numbers x and y with: - // - // x = x1*b + x0 - // y = y1*b + y0 - // - // the product x*y can be obtained with 3 products z2, z1, z0 - // instead of 4: - // - // x*y = x1*y1*b*b + (x1*y0 + x0*y1)*b + x0*y0 - // = z2*b*b + z1*b + z0 - // - // with: - // - // xd = x1 - x0 - // yd = y0 - y1 - // - // z1 = xd*yd + z1 + z0 - // = (x1-x0)*(y0 - y1) + z1 + z0 - // = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z1 + z0 - // = x1*y0 - z1 - z0 + x0*y1 + z1 + z0 - // = x1*y0 + x0*y1 - - // split x, y into "digits" - n2 := n >> 1 // n2 >= 1 - x1, x0 := x[n2:], x[0:n2] // x = x1*b + y0 - y1, y0 := y[n2:], y[0:n2] // y = y1*b + y0 - - // z is used for the result and temporary storage: - // - // 6*n 5*n 4*n 3*n 2*n 1*n 0*n - // z = [z2 copy|z0 copy| xd*yd | yd:xd | x1*y1 | x0*y0 ] - // - // For each recursive call of karatsuba, an unused slice of - // z is passed in that has (at least) half the length of the - // caller's z. - - // compute z0 and z2 with the result "in place" in z - karatsuba(z, x0, y0) // z0 = x0*y0 - karatsuba(z[n:], x1, y1) // z2 = x1*y1 - - // compute xd (or the negative value if underflow occurs) - s := 1 // sign of product xd*yd - xd := z[2*n : 2*n+n2] - if subVV(xd, x1, x0) != 0 { // x1-x0 - s = -s - subVV(xd, x0, x1) // x0-x1 - } - - // compute yd (or the negative value if underflow occurs) - yd := z[2*n+n2 : 3*n] - if subVV(yd, y0, y1) != 0 { // y0-y1 - s = -s - subVV(yd, y1, y0) // y1-y0 - } - - // p = (x1-x0)*(y0-y1) == x1*y0 - x1*y1 - x0*y0 + x0*y1 for s > 0 - // p = (x0-x1)*(y0-y1) == x0*y0 - x0*y1 - x1*y0 + x1*y1 for s < 0 - p := z[n*3:] - karatsuba(p, xd, yd) - - // save original z2:z0 - // (ok to use upper half of z since we're done recursing) - r := z[n*4:] - copy(r, z) - - // add up all partial products - // - // 2*n n 0 - // z = [ z2 | z0 ] - // + [ z0 ] - // + [ z2 ] - // + [ p ] - // - karatsubaAdd(z[n2:], r, n) - karatsubaAdd(z[n2:], r[n:], n) - if s > 0 { - karatsubaAdd(z[n2:], p, n) - } else { - karatsubaSub(z[n2:], p, n) - } -} - -// alias returns true if x and y share the same base array. -func alias(x, y nat) bool { - return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1] -} - -// addAt implements z += x*(1<<(_W*i)); z must be long enough. -// (we don't use nat.add because we need z to stay the same -// slice, and we don't need to normalize z after each addition) -func addAt(z, x nat, i int) { - if n := len(x); n > 0 { - if c := addVV(z[i:i+n], z[i:], x); c != 0 { - j := i + n - if j < len(z) { - addVW(z[j:], z[j:], c) - } - } - } -} - -func max(x, y int) int { - if x > y { - return x - } - return y -} - -// karatsubaLen computes an approximation to the maximum k <= n such that -// k = p<= 0. Thus, the -// result is the largest number that can be divided repeatedly by 2 before -// becoming about the value of karatsubaThreshold. -func karatsubaLen(n int) int { - i := uint(0) - for n > karatsubaThreshold { - n >>= 1 - i++ - } - return n << i -} - -func (z nat) mul(x, y nat) nat { - m := len(x) - n := len(y) - - switch { - case m < n: - return z.mul(y, x) - case m == 0 || n == 0: - return z.make(0) - case n == 1: - return z.mulAddWW(x, y[0], 0) - } - // m >= n > 1 - - // determine if z can be reused - if alias(z, x) || alias(z, y) { - z = nil // z is an alias for x or y - cannot reuse - } - - // use basic multiplication if the numbers are small - if n < karatsubaThreshold || n < 2 { - z = z.make(m + n) - basicMul(z, x, y) - return z.norm() - } - // m >= n && n >= karatsubaThreshold && n >= 2 - - // determine Karatsuba length k such that - // - // x = x1*b + x0 - // y = y1*b + y0 (and k <= len(y), which implies k <= len(x)) - // b = 1<<(_W*k) ("base" of digits xi, yi) - // - k := karatsubaLen(n) - // k <= n - - // multiply x0 and y0 via Karatsuba - x0 := x[0:k] // x0 is not normalized - y0 := y[0:k] // y0 is not normalized - z = z.make(max(6*k, m+n)) // enough space for karatsuba of x0*y0 and full result of x*y - karatsuba(z, x0, y0) - z = z[0 : m+n] // z has final length but may be incomplete, upper portion is garbage - - // If x1 and/or y1 are not 0, add missing terms to z explicitly: - // - // m+n 2*k 0 - // z = [ ... | x0*y0 ] - // + [ x1*y1 ] - // + [ x1*y0 ] - // + [ x0*y1 ] - // - if k < n || m != n { - x1 := x[k:] // x1 is normalized because x is - y1 := y[k:] // y1 is normalized because y is - var t nat - t = t.mul(x1, y1) - copy(z[2*k:], t) - z[2*k+len(t):].clear() // upper portion of z is garbage - t = t.mul(x1, y0.norm()) - addAt(z, t, k) - t = t.mul(x0.norm(), y1) - addAt(z, t, k) - } - - return z.norm() -} - -// mulRange computes the product of all the unsigned integers in the -// range [a, b] inclusively. If a > b (empty range), the result is 1. -func (z nat) mulRange(a, b uint64) nat { - switch { - case a == 0: - // cut long ranges short (optimization) - return z.setUint64(0) - case a > b: - return z.setUint64(1) - case a == b: - return z.setUint64(a) - case a+1 == b: - return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b)) - } - m := (a + b) / 2 - return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b)) -} - -// q = (x-r)/y, with 0 <= r < y -func (z nat) divW(x nat, y Word) (q nat, r Word) { - m := len(x) - switch { - case y == 0: - panic("division by zero") - case y == 1: - q = z.set(x) // result is x - return - case m == 0: - q = z.make(0) // result is 0 - return - } - // m > 0 - z = z.make(m) - r = divWVW(z, 0, x, y) - q = z.norm() - return -} - -func (z nat) div(z2, u, v nat) (q, r nat) { - if len(v) == 0 { - panic("division by zero") - } - - if u.cmp(v) < 0 { - q = z.make(0) - r = z2.set(u) - return - } - - if len(v) == 1 { - var rprime Word - q, rprime = z.divW(u, v[0]) - if rprime > 0 { - r = z2.make(1) - r[0] = rprime - } else { - r = z2.make(0) - } - return - } - - q, r = z.divLarge(z2, u, v) - return -} - -// q = (uIn-r)/v, with 0 <= r < y -// Uses z as storage for q, and u as storage for r if possible. -// See Knuth, Volume 2, section 4.3.1, Algorithm D. -// Preconditions: -// len(v) >= 2 -// len(uIn) >= len(v) -func (z nat) divLarge(u, uIn, v nat) (q, r nat) { - n := len(v) - m := len(uIn) - n - - // determine if z can be reused - // TODO(gri) should find a better solution - this if statement - // is very costly (see e.g. time pidigits -s -n 10000) - if alias(z, uIn) || alias(z, v) { - z = nil // z is an alias for uIn or v - cannot reuse - } - q = z.make(m + 1) - - qhatv := make(nat, n+1) - if alias(u, uIn) || alias(u, v) { - u = nil // u is an alias for uIn or v - cannot reuse - } - u = u.make(len(uIn) + 1) - u.clear() - - // D1. - shift := leadingZeros(v[n-1]) - if shift > 0 { - // do not modify v, it may be used by another goroutine simultaneously - v1 := make(nat, n) - shlVU(v1, v, shift) - v = v1 - } - u[len(uIn)] = shlVU(u[0:len(uIn)], uIn, shift) - - // D2. - for j := m; j >= 0; j-- { - // D3. - qhat := Word(_M) - if u[j+n] != v[n-1] { - var rhat Word - qhat, rhat = divWW(u[j+n], u[j+n-1], v[n-1]) - - // x1 | x2 = q̂v_{n-2} - x1, x2 := mulWW(qhat, v[n-2]) - // test if q̂v_{n-2} > br̂ + u_{j+n-2} - for greaterThan(x1, x2, rhat, u[j+n-2]) { - qhat-- - prevRhat := rhat - rhat += v[n-1] - // v[n-1] >= 0, so this tests for overflow. - if rhat < prevRhat { - break - } - x1, x2 = mulWW(qhat, v[n-2]) - } - } - - // D4. - qhatv[n] = mulAddVWW(qhatv[0:n], v, qhat, 0) - - c := subVV(u[j:j+len(qhatv)], u[j:], qhatv) - if c != 0 { - c := addVV(u[j:j+n], u[j:], v) - u[j+n] += c - qhat-- - } - - q[j] = qhat - } - - q = q.norm() - shrVU(u, u, shift) - r = u.norm() - - return q, r -} - -// Length of x in bits. x must be normalized. -func (x nat) bitLen() int { - if i := len(x) - 1; i >= 0 { - return i*_W + bitLen(x[i]) - } - return 0 -} - -// MaxBase is the largest number base accepted for string conversions. -const MaxBase = 'z' - 'a' + 10 + 1 // = hexValue('z') + 1 - - -func hexValue(ch int) Word { - d := MaxBase + 1 // illegal base - switch { - case '0' <= ch && ch <= '9': - d = ch - '0' - case 'a' <= ch && ch <= 'z': - d = ch - 'a' + 10 - case 'A' <= ch && ch <= 'Z': - d = ch - 'A' + 10 - } - return Word(d) -} - -// scan sets z to the natural number corresponding to the longest possible prefix -// read from r representing an unsigned integer in a given conversion base. -// It returns z, the actual conversion base used, and an error, if any. In the -// error case, the value of z is undefined. The syntax follows the syntax of -// unsigned integer literals in Go. -// -// The base argument must be 0 or a value from 2 through MaxBase. If the base -// is 0, the string prefix determines the actual conversion base. A prefix of -// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a -// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10. -// -func (z nat) scan(r io.RuneScanner, base int) (nat, int, os.Error) { - // reject illegal bases - if base < 0 || base == 1 || MaxBase < base { - return z, 0, os.NewError("illegal number base") - } - - // one char look-ahead - ch, _, err := r.ReadRune() - if err != nil { - return z, 0, err - } - - // determine base if necessary - b := Word(base) - if base == 0 { - b = 10 - if ch == '0' { - switch ch, _, err = r.ReadRune(); err { - case nil: - b = 8 - switch ch { - case 'x', 'X': - b = 16 - case 'b', 'B': - b = 2 - } - if b == 2 || b == 16 { - if ch, _, err = r.ReadRune(); err != nil { - return z, 0, err - } - } - case os.EOF: - return z, 10, nil - default: - return z, 10, err - } - } - } - - // convert string - // - group as many digits d as possible together into a "super-digit" dd with "super-base" bb - // - only when bb does not fit into a word anymore, do a full number mulAddWW using bb and dd - z = z.make(0) - bb := Word(1) - dd := Word(0) - for max := _M / b; ; { - d := hexValue(ch) - if d >= b { - r.UnreadRune() // ch does not belong to number anymore - break - } - - if bb <= max { - bb *= b - dd = dd*b + d - } else { - // bb * b would overflow - z = z.mulAddWW(z, bb, dd) - bb = b - dd = d - } - - if ch, _, err = r.ReadRune(); err != nil { - if err != os.EOF { - return z, int(b), err - } - break - } - } - - switch { - case bb > 1: - // there was at least one mantissa digit - z = z.mulAddWW(z, bb, dd) - case base == 0 && b == 8: - // there was only the octal prefix 0 (possibly followed by digits > 7); - // return base 10, not 8 - return z, 10, nil - case base != 0 || b != 8: - // there was neither a mantissa digit nor the octal prefix 0 - return z, int(b), os.NewError("syntax error scanning number") - } - - return z.norm(), int(b), nil -} - -// Character sets for string conversion. -const ( - lowercaseDigits = "0123456789abcdefghijklmnopqrstuvwxyz" - uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" -) - -// decimalString returns a decimal representation of x. -// It calls x.string with the charset "0123456789". -func (x nat) decimalString() string { - return x.string(lowercaseDigits[0:10]) -} - -// string converts x to a string using digits from a charset; a digit with -// value d is represented by charset[d]. The conversion base is determined -// by len(charset), which must be >= 2. -func (x nat) string(charset string) string { - b := Word(len(charset)) - - // special cases - switch { - case b < 2 || b > 256: - panic("illegal base") - case len(x) == 0: - return string(charset[0]) - } - - // allocate buffer for conversion - i := x.bitLen()/log2(b) + 1 // +1: round up - s := make([]byte, i) - - // special case: power of two bases can avoid divisions completely - if b == b&-b { - // shift is base-b digit size in bits - shift := uint(trailingZeroBits(b)) // shift > 0 because b >= 2 - mask := Word(1)<= shift { - i-- - s[i] = charset[w&mask] - w >>= shift - nbits -= shift - } - - // convert any partial leading digit and advance to next word - if nbits == 0 { - // no partial digit remaining, just advance - w = x[k] - nbits = _W - } else { - // partial digit in current (k-1) and next (k) word - w |= x[k] << nbits - i-- - s[i] = charset[w&mask] - - // advance - w = x[k] >> (shift - nbits) - nbits = _W - (shift - nbits) - } - } - - // convert digits of most-significant word (omit leading zeros) - for nbits >= 0 && w != 0 { - i-- - s[i] = charset[w&mask] - w >>= shift - nbits -= shift - } - - return string(s[i:]) - } - - // general case: extract groups of digits by multiprecision division - - // maximize ndigits where b**ndigits < 2^_W; bb (big base) is b**ndigits - bb := Word(1) - ndigits := 0 - for max := Word(_M / b); bb <= max; bb *= b { - ndigits++ - } - - // preserve x, create local copy for use in repeated divisions - q := nat(nil).set(x) - var r Word - - // convert - if b == 10 { // hard-coding for 10 here speeds this up by 1.25x - for len(q) > 0 { - // extract least significant, base bb "digit" - q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW - if len(q) == 0 { - // skip leading zeros in most-significant group of digits - for j := 0; j < ndigits && r != 0; j++ { - i-- - s[i] = charset[r%10] - r /= 10 - } - } else { - for j := 0; j < ndigits; j++ { - i-- - s[i] = charset[r%10] - r /= 10 - } - } - } - } else { - for len(q) > 0 { - // extract least significant group of digits - q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW - if len(q) == 0 { - // skip leading zeros in most-significant group of digits - for j := 0; j < ndigits && r != 0; j++ { - i-- - s[i] = charset[r%b] - r /= b - } - } else { - for j := 0; j < ndigits; j++ { - i-- - s[i] = charset[r%b] - r /= b - } - } - } - } - - return string(s[i:]) -} - -const deBruijn32 = 0x077CB531 - -var deBruijn32Lookup = []byte{ - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9, -} - -const deBruijn64 = 0x03f79d71b4ca8b09 - -var deBruijn64Lookup = []byte{ - 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, - 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, - 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, - 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, -} - -// trailingZeroBits returns the number of consecutive zero bits on the right -// side of the given Word. -// See Knuth, volume 4, section 7.3.1 -func trailingZeroBits(x Word) int { - // x & -x leaves only the right-most bit set in the word. Let k be the - // index of that bit. Since only a single bit is set, the value is two - // to the power of k. Multiplying by a power of two is equivalent to - // left shifting, in this case by k bits. The de Bruijn constant is - // such that all six bit, consecutive substrings are distinct. - // Therefore, if we have a left shifted version of this constant we can - // find by how many bits it was shifted by looking at which six bit - // substring ended up at the top of the word. - switch _W { - case 32: - return int(deBruijn32Lookup[((x&-x)*deBruijn32)>>27]) - case 64: - return int(deBruijn64Lookup[((x&-x)*(deBruijn64&_M))>>58]) - default: - panic("Unknown word size") - } - - return 0 -} - -// z = x << s -func (z nat) shl(x nat, s uint) nat { - m := len(x) - if m == 0 { - return z.make(0) - } - // m > 0 - - n := m + int(s/_W) - z = z.make(n + 1) - z[n] = shlVU(z[n-m:n], x, s%_W) - z[0 : n-m].clear() - - return z.norm() -} - -// z = x >> s -func (z nat) shr(x nat, s uint) nat { - m := len(x) - n := m - int(s/_W) - if n <= 0 { - return z.make(0) - } - // n > 0 - - z = z.make(n) - shrVU(z, x[m-n:], s%_W) - - return z.norm() -} - -func (z nat) setBit(x nat, i uint, b uint) nat { - j := int(i / _W) - m := Word(1) << (i % _W) - n := len(x) - switch b { - case 0: - z = z.make(n) - copy(z, x) - if j >= n { - // no need to grow - return z - } - z[j] &^= m - return z.norm() - case 1: - if j >= n { - n = j + 1 - } - z = z.make(n) - copy(z, x) - z[j] |= m - // no need to normalize - return z - } - panic("set bit is not 0 or 1") -} - -func (z nat) bit(i uint) uint { - j := int(i / _W) - if j >= len(z) { - return 0 - } - return uint(z[j] >> (i % _W) & 1) -} - -func (z nat) and(x, y nat) nat { - m := len(x) - n := len(y) - if m > n { - m = n - } - // m <= n - - z = z.make(m) - for i := 0; i < m; i++ { - z[i] = x[i] & y[i] - } - - return z.norm() -} - -func (z nat) andNot(x, y nat) nat { - m := len(x) - n := len(y) - if n > m { - n = m - } - // m >= n - - z = z.make(m) - for i := 0; i < n; i++ { - z[i] = x[i] &^ y[i] - } - copy(z[n:m], x[n:m]) - - return z.norm() -} - -func (z nat) or(x, y nat) nat { - m := len(x) - n := len(y) - s := x - if m < n { - n, m = m, n - s = y - } - // m >= n - - z = z.make(m) - for i := 0; i < n; i++ { - z[i] = x[i] | y[i] - } - copy(z[n:m], s[n:m]) - - return z.norm() -} - -func (z nat) xor(x, y nat) nat { - m := len(x) - n := len(y) - s := x - if m < n { - n, m = m, n - s = y - } - // m >= n - - z = z.make(m) - for i := 0; i < n; i++ { - z[i] = x[i] ^ y[i] - } - copy(z[n:m], s[n:m]) - - return z.norm() -} - -// greaterThan returns true iff (x1<<_W + x2) > (y1<<_W + y2) -func greaterThan(x1, x2, y1, y2 Word) bool { - return x1 > y1 || x1 == y1 && x2 > y2 -} - -// modW returns x % d. -func (x nat) modW(d Word) (r Word) { - // TODO(agl): we don't actually need to store the q value. - var q nat - q = q.make(len(x)) - return divWVW(q, 0, x, d) -} - -// powersOfTwoDecompose finds q and k with x = q * 1<= 0; i-- { - v = y[i] - - for j := 0; j < _W; j++ { - z = z.mul(z, z) - - if v&mask != 0 { - z = z.mul(z, x) - } - - if m != nil { - q, z = q.div(z, z, m) - } - - v <<= 1 - } - } - - return z -} - -// probablyPrime performs reps Miller-Rabin tests to check whether n is prime. -// If it returns true, n is prime with probability 1 - 1/4^reps. -// If it returns false, n is not prime. -func (n nat) probablyPrime(reps int) bool { - if len(n) == 0 { - return false - } - - if len(n) == 1 { - if n[0] < 2 { - return false - } - - if n[0]%2 == 0 { - return n[0] == 2 - } - - // We have to exclude these cases because we reject all - // multiples of these numbers below. - switch n[0] { - case 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53: - return true - } - } - - const primesProduct32 = 0xC0CFD797 // Π {p ∈ primes, 2 < p <= 29} - const primesProduct64 = 0xE221F97C30E94E1D // Π {p ∈ primes, 2 < p <= 53} - - var r Word - switch _W { - case 32: - r = n.modW(primesProduct32) - case 64: - r = n.modW(primesProduct64 & _M) - default: - panic("Unknown word size") - } - - if r%3 == 0 || r%5 == 0 || r%7 == 0 || r%11 == 0 || - r%13 == 0 || r%17 == 0 || r%19 == 0 || r%23 == 0 || r%29 == 0 { - return false - } - - if _W == 64 && (r%31 == 0 || r%37 == 0 || r%41 == 0 || - r%43 == 0 || r%47 == 0 || r%53 == 0) { - return false - } - - nm1 := nat(nil).sub(n, natOne) - // 1<= len(z)*_S. The value of z is encoded in the -// slice buf[i:]. The number i of unused bytes at the beginning of -// buf is returned as result. -func (z nat) bytes(buf []byte) (i int) { - i = len(buf) - for _, d := range z { - for j := 0; j < _S; j++ { - i-- - buf[i] = byte(d) - d >>= 8 - } - } - - for i < len(buf) && buf[i] == 0 { - i++ - } - - return -} - -// setBytes interprets buf as the bytes of a big-endian unsigned -// integer, sets z to that value, and returns z. -func (z nat) setBytes(buf []byte) nat { - z = z.make((len(buf) + _S - 1) / _S) - - k := 0 - s := uint(0) - var d Word - for i := len(buf); i > 0; i-- { - d |= Word(buf[i-1]) << s - if s += 8; s == _S*8 { - z[k] = d - k++ - s = 0 - d = 0 - } - } - if k < len(z) { - z[k] = d - } - - return z.norm() -} diff --git a/src/pkg/big/nat_test.go b/src/pkg/big/nat_test.go deleted file mode 100755 index 71d086087..000000000 --- a/src/pkg/big/nat_test.go +++ /dev/null @@ -1,669 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package big - -import ( - "fmt" - "os" - "strings" - "testing" -) - -var cmpTests = []struct { - x, y nat - r int -}{ - {nil, nil, 0}, - {nil, nat{}, 0}, - {nat{}, nil, 0}, - {nat{}, nat{}, 0}, - {nat{0}, nat{0}, 0}, - {nat{0}, nat{1}, -1}, - {nat{1}, nat{0}, 1}, - {nat{1}, nat{1}, 0}, - {nat{0, _M}, nat{1}, 1}, - {nat{1}, nat{0, _M}, -1}, - {nat{1, _M}, nat{0, _M}, 1}, - {nat{0, _M}, nat{1, _M}, -1}, - {nat{16, 571956, 8794, 68}, nat{837, 9146, 1, 754489}, -1}, - {nat{34986, 41, 105, 1957}, nat{56, 7458, 104, 1957}, 1}, -} - -func TestCmp(t *testing.T) { - for i, a := range cmpTests { - r := a.x.cmp(a.y) - if r != a.r { - t.Errorf("#%d got r = %v; want %v", i, r, a.r) - } - } -} - -type funNN func(z, x, y nat) nat -type argNN struct { - z, x, y nat -} - -var sumNN = []argNN{ - {}, - {nat{1}, nil, nat{1}}, - {nat{1111111110}, nat{123456789}, nat{987654321}}, - {nat{0, 0, 0, 1}, nil, nat{0, 0, 0, 1}}, - {nat{0, 0, 0, 1111111110}, nat{0, 0, 0, 123456789}, nat{0, 0, 0, 987654321}}, - {nat{0, 0, 0, 1}, nat{0, 0, _M}, nat{0, 0, 1}}, -} - -var prodNN = []argNN{ - {}, - {nil, nil, nil}, - {nil, nat{991}, nil}, - {nat{991}, nat{991}, nat{1}}, - {nat{991 * 991}, nat{991}, nat{991}}, - {nat{0, 0, 991 * 991}, nat{0, 991}, nat{0, 991}}, - {nat{1 * 991, 2 * 991, 3 * 991, 4 * 991}, nat{1, 2, 3, 4}, nat{991}}, - {nat{4, 11, 20, 30, 20, 11, 4}, nat{1, 2, 3, 4}, nat{4, 3, 2, 1}}, -} - -func TestSet(t *testing.T) { - for _, a := range sumNN { - z := nat(nil).set(a.z) - if z.cmp(a.z) != 0 { - t.Errorf("got z = %v; want %v", z, a.z) - } - } -} - -func testFunNN(t *testing.T, msg string, f funNN, a argNN) { - z := f(nil, a.x, a.y) - if z.cmp(a.z) != 0 { - t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, z, a.z) - } -} - -func TestFunNN(t *testing.T) { - for _, a := range sumNN { - arg := a - testFunNN(t, "add", nat.add, arg) - - arg = argNN{a.z, a.y, a.x} - testFunNN(t, "add symmetric", nat.add, arg) - - arg = argNN{a.x, a.z, a.y} - testFunNN(t, "sub", nat.sub, arg) - - arg = argNN{a.y, a.z, a.x} - testFunNN(t, "sub symmetric", nat.sub, arg) - } - - for _, a := range prodNN { - arg := a - testFunNN(t, "mul", nat.mul, arg) - - arg = argNN{a.z, a.y, a.x} - testFunNN(t, "mul symmetric", nat.mul, arg) - } -} - -var mulRangesN = []struct { - a, b uint64 - prod string -}{ - {0, 0, "0"}, - {1, 1, "1"}, - {1, 2, "2"}, - {1, 3, "6"}, - {10, 10, "10"}, - {0, 100, "0"}, - {0, 1e9, "0"}, - {1, 0, "1"}, // empty range - {100, 1, "1"}, // empty range - {1, 10, "3628800"}, // 10! - {1, 20, "2432902008176640000"}, // 20! - {1, 100, - "933262154439441526816992388562667004907159682643816214685929" + - "638952175999932299156089414639761565182862536979208272237582" + - "51185210916864000000000000000000000000", // 100! - }, -} - -func TestMulRangeN(t *testing.T) { - for i, r := range mulRangesN { - prod := nat(nil).mulRange(r.a, r.b).decimalString() - if prod != r.prod { - t.Errorf("#%d: got %s; want %s", i, prod, r.prod) - } - } -} - -var mulArg, mulTmp nat - -func init() { - const n = 1000 - mulArg = make(nat, n) - for i := 0; i < n; i++ { - mulArg[i] = _M - } -} - -func benchmarkMulLoad() { - for j := 1; j <= 10; j++ { - x := mulArg[0 : j*100] - mulTmp.mul(x, x) - } -} - -func BenchmarkMul(b *testing.B) { - for i := 0; i < b.N; i++ { - benchmarkMulLoad() - } -} - -func toString(x nat, charset string) string { - base := len(charset) - - // special cases - switch { - case base < 2: - panic("illegal base") - case len(x) == 0: - return string(charset[0]) - } - - // allocate buffer for conversion - i := x.bitLen()/log2(Word(base)) + 1 // +1: round up - s := make([]byte, i) - - // don't destroy x - q := nat(nil).set(x) - - // convert - for len(q) > 0 { - i-- - var r Word - q, r = q.divW(q, Word(base)) - s[i] = charset[r] - } - - return string(s[i:]) -} - -var strTests = []struct { - x nat // nat value to be converted - c string // conversion charset - s string // expected result -}{ - {nil, "01", "0"}, - {nat{1}, "01", "1"}, - {nat{0xc5}, "01", "11000101"}, - {nat{03271}, lowercaseDigits[0:8], "3271"}, - {nat{10}, lowercaseDigits[0:10], "10"}, - {nat{1234567890}, uppercaseDigits[0:10], "1234567890"}, - {nat{0xdeadbeef}, lowercaseDigits[0:16], "deadbeef"}, - {nat{0xdeadbeef}, uppercaseDigits[0:16], "DEADBEEF"}, - {nat{0x229be7}, lowercaseDigits[0:17], "1a2b3c"}, - {nat{0x309663e6}, uppercaseDigits[0:32], "O9COV6"}, -} - -func TestString(t *testing.T) { - for _, a := range strTests { - s := a.x.string(a.c) - if s != a.s { - t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s) - } - - x, b, err := nat(nil).scan(strings.NewReader(a.s), len(a.c)) - if x.cmp(a.x) != 0 { - t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) - } - if b != len(a.c) { - t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, len(a.c)) - } - if err != nil { - t.Errorf("scan%+v\n\tgot error = %s", a, err) - } - } -} - -var natScanTests = []struct { - s string // string to be scanned - base int // input base - x nat // expected nat - b int // expected base - ok bool // expected success - next int // next character (or 0, if at EOF) -}{ - // error: illegal base - {base: -1}, - {base: 1}, - {base: 37}, - - // error: no mantissa - {}, - {s: "?"}, - {base: 10}, - {base: 36}, - {s: "?", base: 10}, - {s: "0x"}, - {s: "345", base: 2}, - - // no errors - {"0", 0, nil, 10, true, 0}, - {"0", 10, nil, 10, true, 0}, - {"0", 36, nil, 36, true, 0}, - {"1", 0, nat{1}, 10, true, 0}, - {"1", 10, nat{1}, 10, true, 0}, - {"0 ", 0, nil, 10, true, ' '}, - {"08", 0, nil, 10, true, '8'}, - {"018", 0, nat{1}, 8, true, '8'}, - {"0b1", 0, nat{1}, 2, true, 0}, - {"0b11000101", 0, nat{0xc5}, 2, true, 0}, - {"03271", 0, nat{03271}, 8, true, 0}, - {"10ab", 0, nat{10}, 10, true, 'a'}, - {"1234567890", 0, nat{1234567890}, 10, true, 0}, - {"xyz", 36, nat{(33*36+34)*36 + 35}, 36, true, 0}, - {"xyz?", 36, nat{(33*36+34)*36 + 35}, 36, true, '?'}, - {"0x", 16, nil, 16, true, 'x'}, - {"0xdeadbeef", 0, nat{0xdeadbeef}, 16, true, 0}, - {"0XDEADBEEF", 0, nat{0xdeadbeef}, 16, true, 0}, -} - -func TestScanBase(t *testing.T) { - for _, a := range natScanTests { - r := strings.NewReader(a.s) - x, b, err := nat(nil).scan(r, a.base) - if err == nil && !a.ok { - t.Errorf("scan%+v\n\texpected error", a) - } - if err != nil { - if a.ok { - t.Errorf("scan%+v\n\tgot error = %s", a, err) - } - continue - } - if x.cmp(a.x) != 0 { - t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) - } - if b != a.b { - t.Errorf("scan%+v\n\tgot b = %d; want %d", a, b, a.base) - } - next, _, err := r.ReadRune() - if err == os.EOF { - next = 0 - err = nil - } - if err == nil && next != a.next { - t.Errorf("scan%+v\n\tgot next = %q; want %q", a, next, a.next) - } - } -} - -var pi = "3" + - "14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651" + - "32823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461" + - "28475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920" + - "96282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179" + - "31051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798" + - "60943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901" + - "22495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837" + - "29780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083" + - "81420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909" + - "21642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" + - "55748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035" + - "63707660104710181942955596198946767837449448255379774726847104047534646208046684259069491293313677028989152104" + - "75216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992" + - "45863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818" + - "34797753566369807426542527862551818417574672890977772793800081647060016145249192173217214772350141441973568548" + - "16136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179" + - "04946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886" + - "26945604241965285022210661186306744278622039194945047123713786960956364371917287467764657573962413890865832645" + - "99581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745" + - "53050682034962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" + - "68683868942774155991855925245953959431049972524680845987273644695848653836736222626099124608051243884390451244" + - "13654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767" + - "88952521385225499546667278239864565961163548862305774564980355936345681743241125150760694794510965960940252288" + - "79710893145669136867228748940560101503308617928680920874760917824938589009714909675985261365549781893129784821" + - "68299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610" + - "21359695362314429524849371871101457654035902799344037420073105785390621983874478084784896833214457138687519435" + - "06430218453191048481005370614680674919278191197939952061419663428754440643745123718192179998391015919561814675" + - "14269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672" + - "21825625996615014215030680384477345492026054146659252014974428507325186660021324340881907104863317346496514539" + - "05796268561005508106658796998163574736384052571459102897064140110971206280439039759515677157700420337869936007" + - "23055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816" + - "90915280173506712748583222871835209353965725121083579151369882091444210067510334671103141267111369908658516398" + - "31501970165151168517143765761835155650884909989859982387345528331635507647918535893226185489632132933089857064" + - "20467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325" + - "97463667305836041428138830320382490375898524374417029132765618093773444030707469211201913020330380197621101100" + - "44929321516084244485963766983895228684783123552658213144957685726243344189303968642624341077322697802807318915" + - "44110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201" + - "85581007293606598764861179104533488503461136576867532494416680396265797877185560845529654126654085306143444318" + - "58676975145661406800700237877659134401712749470420562230538994561314071127000407854733269939081454664645880797" + - "27082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923" + - "09907965473761255176567513575178296664547791745011299614890304639947132962107340437518957359614589019389713111" + - "79042978285647503203198691514028708085990480109412147221317947647772622414254854540332157185306142288137585043" + - "06332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120" + - "91807638327166416274888800786925602902284721040317211860820419000422966171196377921337575114959501566049631862" + - "94726547364252308177036751590673502350728354056704038674351362222477158915049530984448933309634087807693259939" + - "78054193414473774418426312986080998886874132604721569516239658645730216315981931951673538129741677294786724229" + - "24654366800980676928238280689964004824354037014163149658979409243237896907069779422362508221688957383798623001" + - "59377647165122893578601588161755782973523344604281512627203734314653197777416031990665541876397929334419521541" + - "34189948544473456738316249934191318148092777710386387734317720754565453220777092120190516609628049092636019759" + - "88281613323166636528619326686336062735676303544776280350450777235547105859548702790814356240145171806246436267" + - "94561275318134078330336254232783944975382437205835311477119926063813346776879695970309833913077109870408591337" - -// Test case for BenchmarkScanPi. -func TestScanPi(t *testing.T) { - var x nat - z, _, err := x.scan(strings.NewReader(pi), 10) - if err != nil { - t.Errorf("scanning pi: %s", err) - } - if s := z.decimalString(); s != pi { - t.Errorf("scanning pi: got %s", s) - } -} - -func BenchmarkScanPi(b *testing.B) { - for i := 0; i < b.N; i++ { - var x nat - x.scan(strings.NewReader(pi), 10) - } -} - -const ( - // 314**271 - // base 2: 2249 digits - // base 8: 751 digits - // base 10: 678 digits - // base 16: 563 digits - shortBase = 314 - shortExponent = 271 - - // 3141**2178 - // base 2: 31577 digits - // base 8: 10527 digits - // base 10: 9507 digits - // base 16: 7895 digits - mediumBase = 3141 - mediumExponent = 2718 - - // 3141**2178 - // base 2: 406078 digits - // base 8: 135360 digits - // base 10: 122243 digits - // base 16: 101521 digits - longBase = 31415 - longExponent = 27182 -) - -func BenchmarkScanShort2(b *testing.B) { - ScanHelper(b, 2, shortBase, shortExponent) -} - -func BenchmarkScanShort8(b *testing.B) { - ScanHelper(b, 8, shortBase, shortExponent) -} - -func BenchmarkScanSort10(b *testing.B) { - ScanHelper(b, 10, shortBase, shortExponent) -} - -func BenchmarkScanShort16(b *testing.B) { - ScanHelper(b, 16, shortBase, shortExponent) -} - -func BenchmarkScanMedium2(b *testing.B) { - ScanHelper(b, 2, mediumBase, mediumExponent) -} - -func BenchmarkScanMedium8(b *testing.B) { - ScanHelper(b, 8, mediumBase, mediumExponent) -} - -func BenchmarkScanMedium10(b *testing.B) { - ScanHelper(b, 10, mediumBase, mediumExponent) -} - -func BenchmarkScanMedium16(b *testing.B) { - ScanHelper(b, 16, mediumBase, mediumExponent) -} - -func BenchmarkScanLong2(b *testing.B) { - ScanHelper(b, 2, longBase, longExponent) -} - -func BenchmarkScanLong8(b *testing.B) { - ScanHelper(b, 8, longBase, longExponent) -} - -func BenchmarkScanLong10(b *testing.B) { - ScanHelper(b, 10, longBase, longExponent) -} - -func BenchmarkScanLong16(b *testing.B) { - ScanHelper(b, 16, longBase, longExponent) -} - -func ScanHelper(b *testing.B, base int, xv, yv Word) { - b.StopTimer() - var x, y, z nat - x = x.setWord(xv) - y = y.setWord(yv) - z = z.expNN(x, y, nil) - - var s string - s = z.string(lowercaseDigits[0:base]) - if t := toString(z, lowercaseDigits[0:base]); t != s { - panic(fmt.Sprintf("scanning: got %s; want %s", s, t)) - } - b.StartTimer() - - for i := 0; i < b.N; i++ { - x.scan(strings.NewReader(s), base) - } -} - -func BenchmarkStringShort2(b *testing.B) { - StringHelper(b, 2, shortBase, shortExponent) -} - -func BenchmarkStringShort8(b *testing.B) { - StringHelper(b, 8, shortBase, shortExponent) -} - -func BenchmarkStringShort10(b *testing.B) { - StringHelper(b, 10, shortBase, shortExponent) -} - -func BenchmarkStringShort16(b *testing.B) { - StringHelper(b, 16, shortBase, shortExponent) -} - -func BenchmarkStringMedium2(b *testing.B) { - StringHelper(b, 2, mediumBase, mediumExponent) -} - -func BenchmarkStringMedium8(b *testing.B) { - StringHelper(b, 8, mediumBase, mediumExponent) -} - -func BenchmarkStringMedium10(b *testing.B) { - StringHelper(b, 10, mediumBase, mediumExponent) -} - -func BenchmarkStringMedium16(b *testing.B) { - StringHelper(b, 16, mediumBase, mediumExponent) -} - -func BenchmarkStringLong2(b *testing.B) { - StringHelper(b, 2, longBase, longExponent) -} - -func BenchmarkStringLong8(b *testing.B) { - StringHelper(b, 8, longBase, longExponent) -} - -func BenchmarkStringLong10(b *testing.B) { - StringHelper(b, 10, longBase, longExponent) -} - -func BenchmarkStringLong16(b *testing.B) { - StringHelper(b, 16, longBase, longExponent) -} - -func StringHelper(b *testing.B, base int, xv, yv Word) { - b.StopTimer() - var x, y, z nat - x = x.setWord(xv) - y = y.setWord(yv) - z = z.expNN(x, y, nil) - b.StartTimer() - - for i := 0; i < b.N; i++ { - z.string(lowercaseDigits[0:base]) - } -} - -func TestLeadingZeros(t *testing.T) { - var x Word = _B >> 1 - for i := 0; i <= _W; i++ { - if int(leadingZeros(x)) != i { - t.Errorf("failed at %x: got %d want %d", x, leadingZeros(x), i) - } - x >>= 1 - } -} - -type shiftTest struct { - in nat - shift uint - out nat -} - -var leftShiftTests = []shiftTest{ - {nil, 0, nil}, - {nil, 1, nil}, - {natOne, 0, natOne}, - {natOne, 1, natTwo}, - {nat{1 << (_W - 1)}, 1, nat{0}}, - {nat{1 << (_W - 1), 0}, 1, nat{0, 1}}, -} - -func TestShiftLeft(t *testing.T) { - for i, test := range leftShiftTests { - var z nat - z = z.shl(test.in, test.shift) - for j, d := range test.out { - if j >= len(z) || z[j] != d { - t.Errorf("#%d: got: %v want: %v", i, z, test.out) - break - } - } - } -} - -var rightShiftTests = []shiftTest{ - {nil, 0, nil}, - {nil, 1, nil}, - {natOne, 0, natOne}, - {natOne, 1, nil}, - {natTwo, 1, natOne}, - {nat{0, 1}, 1, nat{1 << (_W - 1)}}, - {nat{2, 1, 1}, 1, nat{1<<(_W-1) + 1, 1 << (_W - 1)}}, -} - -func TestShiftRight(t *testing.T) { - for i, test := range rightShiftTests { - var z nat - z = z.shr(test.in, test.shift) - for j, d := range test.out { - if j >= len(z) || z[j] != d { - t.Errorf("#%d: got: %v want: %v", i, z, test.out) - break - } - } - } -} - -type modWTest struct { - in string - dividend string - out string -} - -var modWTests32 = []modWTest{ - {"23492635982634928349238759823742", "252341", "220170"}, -} - -var modWTests64 = []modWTest{ - {"6527895462947293856291561095690465243862946", "524326975699234", "375066989628668"}, -} - -func runModWTests(t *testing.T, tests []modWTest) { - for i, test := range tests { - in, _ := new(Int).SetString(test.in, 10) - d, _ := new(Int).SetString(test.dividend, 10) - out, _ := new(Int).SetString(test.out, 10) - - r := in.abs.modW(d.abs[0]) - if r != out.abs[0] { - t.Errorf("#%d failed: got %s want %s", i, r, out) - } - } -} - -func TestModW(t *testing.T) { - if _W >= 32 { - runModWTests(t, modWTests32) - } - if _W >= 64 { - runModWTests(t, modWTests64) - } -} - -func TestTrailingZeroBits(t *testing.T) { - var x Word - x-- - for i := 0; i < _W; i++ { - if trailingZeroBits(x) != i { - t.Errorf("Failed at step %d: x: %x got: %d", i, x, trailingZeroBits(x)) - } - x <<= 1 - } -} - -var expNNTests = []struct { - x, y, m string - out string -}{ - {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"}, - {"0x8000000000000000", "2", "6719", "4944"}, - {"0x8000000000000000", "3", "6719", "5447"}, - {"0x8000000000000000", "1000", "6719", "1603"}, - {"0x8000000000000000", "1000000", "6719", "3199"}, - { - "2938462938472983472983659726349017249287491026512746239764525612965293865296239471239874193284792387498274256129746192347", - "298472983472983471903246121093472394872319615612417471234712061", - "29834729834729834729347290846729561262544958723956495615629569234729836259263598127342374289365912465901365498236492183464", - "23537740700184054162508175125554701713153216681790245129157191391322321508055833908509185839069455749219131480588829346291", - }, -} - -func TestExpNN(t *testing.T) { - for i, test := range expNNTests { - x, _, _ := nat(nil).scan(strings.NewReader(test.x), 0) - y, _, _ := nat(nil).scan(strings.NewReader(test.y), 0) - out, _, _ := nat(nil).scan(strings.NewReader(test.out), 0) - - var m nat - - if len(test.m) > 0 { - m, _, _ = nat(nil).scan(strings.NewReader(test.m), 0) - } - - z := nat(nil).expNN(x, y, m) - if z.cmp(out) != 0 { - t.Errorf("#%d got %v want %v", i, z, out) - } - } -} diff --git a/src/pkg/big/rat.go b/src/pkg/big/rat.go deleted file mode 100644 index 327b9bd9c..000000000 --- a/src/pkg/big/rat.go +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file implements multi-precision rational numbers. - -package big - -import ( - "encoding/binary" - "fmt" - "os" - "strings" -) - -// A Rat represents a quotient a/b of arbitrary precision. The zero value for -// a Rat, 0/0, is not a legal Rat. -type Rat struct { - a Int - b nat -} - -// NewRat creates a new Rat with numerator a and denominator b. -func NewRat(a, b int64) *Rat { - return new(Rat).SetFrac64(a, b) -} - -// SetFrac sets z to a/b and returns z. -func (z *Rat) SetFrac(a, b *Int) *Rat { - z.a.Set(a) - z.a.neg = a.neg != b.neg - z.b = z.b.set(b.abs) - return z.norm() -} - -// SetFrac64 sets z to a/b and returns z. -func (z *Rat) SetFrac64(a, b int64) *Rat { - z.a.SetInt64(a) - if b < 0 { - b = -b - z.a.neg = !z.a.neg - } - z.b = z.b.setUint64(uint64(b)) - return z.norm() -} - -// SetInt sets z to x (by making a copy of x) and returns z. -func (z *Rat) SetInt(x *Int) *Rat { - z.a.Set(x) - z.b = z.b.setWord(1) - return z -} - -// SetInt64 sets z to x and returns z. -func (z *Rat) SetInt64(x int64) *Rat { - z.a.SetInt64(x) - z.b = z.b.setWord(1) - return z -} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x == 0 -// +1 if x > 0 -// -func (x *Rat) Sign() int { - return x.a.Sign() -} - -// IsInt returns true if the denominator of x is 1. -func (x *Rat) IsInt() bool { - return len(x.b) == 1 && x.b[0] == 1 -} - -// Num returns the numerator of z; it may be <= 0. -// The result is a reference to z's numerator; it -// may change if a new value is assigned to z. -func (z *Rat) Num() *Int { - return &z.a -} - -// Denom returns the denominator of z; it is always > 0. -// The result is a reference to z's denominator; it -// may change if a new value is assigned to z. -func (z *Rat) Denom() *Int { - return &Int{false, z.b} -} - -func gcd(x, y nat) nat { - // Euclidean algorithm. - var a, b nat - a = a.set(x) - b = b.set(y) - for len(b) != 0 { - var q, r nat - _, r = q.div(r, a, b) - a = b - b = r - } - return a -} - -func (z *Rat) norm() *Rat { - f := gcd(z.a.abs, z.b) - if len(z.a.abs) == 0 { - // z == 0 - z.a.neg = false // normalize sign - z.b = z.b.setWord(1) - return z - } - if f.cmp(natOne) != 0 { - z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f) - z.b, _ = z.b.div(nil, z.b, f) - } - return z -} - -func mulNat(x *Int, y nat) *Int { - var z Int - z.abs = z.abs.mul(x.abs, y) - z.neg = len(z.abs) > 0 && x.neg - return &z -} - -// Cmp compares x and y and returns: -// -// -1 if x < y -// 0 if x == y -// +1 if x > y -// -func (x *Rat) Cmp(y *Rat) (r int) { - return mulNat(&x.a, y.b).Cmp(mulNat(&y.a, x.b)) -} - -// Abs sets z to |x| (the absolute value of x) and returns z. -func (z *Rat) Abs(x *Rat) *Rat { - z.a.Abs(&x.a) - z.b = z.b.set(x.b) - return z -} - -// Add sets z to the sum x+y and returns z. -func (z *Rat) Add(x, y *Rat) *Rat { - a1 := mulNat(&x.a, y.b) - a2 := mulNat(&y.a, x.b) - z.a.Add(a1, a2) - z.b = z.b.mul(x.b, y.b) - return z.norm() -} - -// Sub sets z to the difference x-y and returns z. -func (z *Rat) Sub(x, y *Rat) *Rat { - a1 := mulNat(&x.a, y.b) - a2 := mulNat(&y.a, x.b) - z.a.Sub(a1, a2) - z.b = z.b.mul(x.b, y.b) - return z.norm() -} - -// Mul sets z to the product x*y and returns z. -func (z *Rat) Mul(x, y *Rat) *Rat { - z.a.Mul(&x.a, &y.a) - z.b = z.b.mul(x.b, y.b) - return z.norm() -} - -// Quo sets z to the quotient x/y and returns z. -// If y == 0, a division-by-zero run-time panic occurs. -func (z *Rat) Quo(x, y *Rat) *Rat { - if len(y.a.abs) == 0 { - panic("division by zero") - } - a := mulNat(&x.a, y.b) - b := mulNat(&y.a, x.b) - z.a.abs = a.abs - z.b = b.abs - z.a.neg = a.neg != b.neg - return z.norm() -} - -// Neg sets z to -x (by making a copy of x if necessary) and returns z. -func (z *Rat) Neg(x *Rat) *Rat { - z.a.Neg(&x.a) - z.b = z.b.set(x.b) - return z -} - -// Set sets z to x (by making a copy of x if necessary) and returns z. -func (z *Rat) Set(x *Rat) *Rat { - z.a.Set(&x.a) - z.b = z.b.set(x.b) - return z -} - -func ratTok(ch int) bool { - return strings.IndexRune("+-/0123456789.eE", ch) >= 0 -} - -// Scan is a support routine for fmt.Scanner. It accepts the formats -// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent. -func (z *Rat) Scan(s fmt.ScanState, ch int) os.Error { - tok, err := s.Token(true, ratTok) - if err != nil { - return err - } - if strings.IndexRune("efgEFGv", ch) < 0 { - return os.NewError("Rat.Scan: invalid verb") - } - if _, ok := z.SetString(string(tok)); !ok { - return os.NewError("Rat.Scan: invalid syntax") - } - return nil -} - -// SetString sets z to the value of s and returns z and a boolean indicating -// success. s can be given as a fraction "a/b" or as a floating-point number -// optionally followed by an exponent. If the operation failed, the value of z -// is undefined. -func (z *Rat) SetString(s string) (*Rat, bool) { - if len(s) == 0 { - return z, false - } - - // check for a quotient - sep := strings.Index(s, "/") - if sep >= 0 { - if _, ok := z.a.SetString(s[0:sep], 10); !ok { - return z, false - } - s = s[sep+1:] - var err os.Error - if z.b, _, err = z.b.scan(strings.NewReader(s), 10); err != nil { - return z, false - } - return z.norm(), true - } - - // check for a decimal point - sep = strings.Index(s, ".") - // check for an exponent - e := strings.IndexAny(s, "eE") - var exp Int - if e >= 0 { - if e < sep { - // The E must come after the decimal point. - return z, false - } - if _, ok := exp.SetString(s[e+1:], 10); !ok { - return z, false - } - s = s[0:e] - } - if sep >= 0 { - s = s[0:sep] + s[sep+1:] - exp.Sub(&exp, NewInt(int64(len(s)-sep))) - } - - if _, ok := z.a.SetString(s, 10); !ok { - return z, false - } - powTen := nat{}.expNN(natTen, exp.abs, nil) - if exp.neg { - z.b = powTen - z.norm() - } else { - z.a.abs = z.a.abs.mul(z.a.abs, powTen) - z.b = z.b.setWord(1) - } - - return z, true -} - -// String returns a string representation of z in the form "a/b" (even if b == 1). -func (z *Rat) String() string { - return z.a.String() + "/" + z.b.decimalString() -} - -// RatString returns a string representation of z in the form "a/b" if b != 1, -// and in the form "a" if b == 1. -func (z *Rat) RatString() string { - if z.IsInt() { - return z.a.String() - } - return z.String() -} - -// FloatString returns a string representation of z in decimal form with prec -// digits of precision after the decimal point and the last digit rounded. -func (z *Rat) FloatString(prec int) string { - if z.IsInt() { - s := z.a.String() - if prec > 0 { - s += "." + strings.Repeat("0", prec) - } - return s - } - - q, r := nat{}.div(nat{}, z.a.abs, z.b) - - p := natOne - if prec > 0 { - p = nat{}.expNN(natTen, nat{}.setUint64(uint64(prec)), nil) - } - - r = r.mul(r, p) - r, r2 := r.div(nat{}, r, z.b) - - // see if we need to round up - r2 = r2.add(r2, r2) - if z.b.cmp(r2) <= 0 { - r = r.add(r, natOne) - if r.cmp(p) >= 0 { - q = nat{}.add(q, natOne) - r = nat{}.sub(r, p) - } - } - - s := q.decimalString() - if z.a.neg { - s = "-" + s - } - - if prec > 0 { - rs := r.decimalString() - leadingZeros := prec - len(rs) - s += "." + strings.Repeat("0", leadingZeros) + rs - } - - return s -} - -// Gob codec version. Permits backward-compatible changes to the encoding. -const ratGobVersion byte = 1 - -// GobEncode implements the gob.GobEncoder interface. -func (z *Rat) GobEncode() ([]byte, os.Error) { - buf := make([]byte, 1+4+(len(z.a.abs)+len(z.b))*_S) // extra bytes for version and sign bit (1), and numerator length (4) - i := z.b.bytes(buf) - j := z.a.abs.bytes(buf[0:i]) - n := i - j - if int(uint32(n)) != n { - // this should never happen - return nil, os.NewError("Rat.GobEncode: numerator too large") - } - binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) - j -= 1 + 4 - b := ratGobVersion << 1 // make space for sign bit - if z.a.neg { - b |= 1 - } - buf[j] = b - return buf[j:], nil -} - -// GobDecode implements the gob.GobDecoder interface. -func (z *Rat) GobDecode(buf []byte) os.Error { - if len(buf) == 0 { - return os.NewError("Rat.GobDecode: no data") - } - b := buf[0] - if b>>1 != ratGobVersion { - return os.NewError(fmt.Sprintf("Rat.GobDecode: encoding version %d not supported", b>>1)) - } - const j = 1 + 4 - i := j + binary.BigEndian.Uint32(buf[j-4:j]) - z.a.neg = b&1 != 0 - z.a.abs = z.a.abs.setBytes(buf[j:i]) - z.b = z.b.setBytes(buf[i:]) - return nil -} diff --git a/src/pkg/big/rat_test.go b/src/pkg/big/rat_test.go deleted file mode 100644 index dbc5bb6cc..000000000 --- a/src/pkg/big/rat_test.go +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package big - -import ( - "bytes" - "fmt" - "gob" - "testing" -) - -var setStringTests = []struct { - in, out string - ok bool -}{ - {"0", "0", true}, - {"-0", "0", true}, - {"1", "1", true}, - {"-1", "-1", true}, - {"1.", "1", true}, - {"1e0", "1", true}, - {"1.e1", "10", true}, - {in: "1e", ok: false}, - {in: "1.e", ok: false}, - {in: "1e+14e-5", ok: false}, - {in: "1e4.5", ok: false}, - {in: "r", ok: false}, - {in: "a/b", ok: false}, - {in: "a.b", ok: false}, - {"-0.1", "-1/10", true}, - {"-.1", "-1/10", true}, - {"2/4", "1/2", true}, - {".25", "1/4", true}, - {"-1/5", "-1/5", true}, - {"8129567.7690E14", "812956776900000000000", true}, - {"78189e+4", "781890000", true}, - {"553019.8935e+8", "55301989350000", true}, - {"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true}, - {"9877861857500000E-7", "3951144743/4", true}, - {"2169378.417e-3", "2169378417/1000000", true}, - {"884243222337379604041632732738665534", "884243222337379604041632732738665534", true}, - {"53/70893980658822810696", "53/70893980658822810696", true}, - {"106/141787961317645621392", "53/70893980658822810696", true}, - {"204211327800791583.81095", "4084226556015831676219/20000", true}, -} - -func TestRatSetString(t *testing.T) { - for i, test := range setStringTests { - x, ok := new(Rat).SetString(test.in) - - if ok != test.ok || ok && x.RatString() != test.out { - t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) - } - } -} - -func TestRatScan(t *testing.T) { - var buf bytes.Buffer - for i, test := range setStringTests { - x := new(Rat) - buf.Reset() - buf.WriteString(test.in) - - _, err := fmt.Fscanf(&buf, "%v", x) - if err == nil != test.ok { - if test.ok { - t.Errorf("#%d error: %s", i, err.String()) - } else { - t.Errorf("#%d expected error", i) - } - continue - } - if err == nil && x.RatString() != test.out { - t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) - } - } -} - -var floatStringTests = []struct { - in string - prec int - out string -}{ - {"0", 0, "0"}, - {"0", 4, "0.0000"}, - {"1", 0, "1"}, - {"1", 2, "1.00"}, - {"-1", 0, "-1"}, - {".25", 2, "0.25"}, - {".25", 1, "0.3"}, - {".25", 3, "0.250"}, - {"-1/3", 3, "-0.333"}, - {"-2/3", 4, "-0.6667"}, - {"0.96", 1, "1.0"}, - {"0.999", 2, "1.00"}, - {"0.9", 0, "1"}, - {".25", -1, "0"}, - {".55", -1, "1"}, -} - -func TestFloatString(t *testing.T) { - for i, test := range floatStringTests { - x, _ := new(Rat).SetString(test.in) - - if x.FloatString(test.prec) != test.out { - t.Errorf("#%d got %s want %s", i, x.FloatString(test.prec), test.out) - } - } -} - -func TestRatSign(t *testing.T) { - zero := NewRat(0, 1) - for _, a := range setStringTests { - var x Rat - x.SetString(a.in) - s := x.Sign() - e := x.Cmp(zero) - if s != e { - t.Errorf("got %d; want %d for z = %v", s, e, &x) - } - } -} - -var ratCmpTests = []struct { - rat1, rat2 string - out int -}{ - {"0", "0/1", 0}, - {"1/1", "1", 0}, - {"-1", "-2/2", 0}, - {"1", "0", 1}, - {"0/1", "1/1", -1}, - {"-5/1434770811533343057144", "-5/1434770811533343057145", -1}, - {"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1}, - {"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1}, - {"37414950961700930/7204075375675961", "74829901923401860/14408150751351922", 0}, -} - -func TestRatCmp(t *testing.T) { - for i, test := range ratCmpTests { - x, _ := new(Rat).SetString(test.rat1) - y, _ := new(Rat).SetString(test.rat2) - - out := x.Cmp(y) - if out != test.out { - t.Errorf("#%d got out = %v; want %v", i, out, test.out) - } - } -} - -func TestIsInt(t *testing.T) { - one := NewInt(1) - for _, a := range setStringTests { - var x Rat - x.SetString(a.in) - i := x.IsInt() - e := x.Denom().Cmp(one) == 0 - if i != e { - t.Errorf("got %v; want %v for z = %v", i, e, &x) - } - } -} - -func TestRatAbs(t *testing.T) { - zero := NewRat(0, 1) - for _, a := range setStringTests { - var z Rat - z.SetString(a.in) - var e Rat - e.Set(&z) - if e.Cmp(zero) < 0 { - e.Sub(zero, &e) - } - z.Abs(&z) - if z.Cmp(&e) != 0 { - t.Errorf("got z = %v; want %v", &z, &e) - } - } -} - -type ratBinFun func(z, x, y *Rat) *Rat -type ratBinArg struct { - x, y, z string -} - -func testRatBin(t *testing.T, i int, name string, f ratBinFun, a ratBinArg) { - x, _ := NewRat(0, 1).SetString(a.x) - y, _ := NewRat(0, 1).SetString(a.y) - z, _ := NewRat(0, 1).SetString(a.z) - out := f(NewRat(0, 1), x, y) - - if out.Cmp(z) != 0 { - t.Errorf("%s #%d got %s want %s", name, i, out, z) - } -} - -var ratBinTests = []struct { - x, y string - sum, prod string -}{ - {"0", "0", "0", "0"}, - {"0", "1", "1", "0"}, - {"-1", "0", "-1", "0"}, - {"-1", "1", "0", "-1"}, - {"1", "1", "2", "1"}, - {"1/2", "1/2", "1", "1/4"}, - {"1/4", "1/3", "7/12", "1/12"}, - {"2/5", "-14/3", "-64/15", "-28/15"}, - {"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020", "-1760941/388732505247628681598037355282018369560"}, - {"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6", "950260896245257153059642991192710872711/3"}, - {"-854857841473707320655/4237645934602118692642972629634714039", "-18/31750379913563777419", "-27/133467566250814981", "15387441146526731771790/134546868362786310073779084329032722548987800600710485341"}, - {"618575745270541348005638912139/19198433543745179392300736", "-19948846211000086/637313996471", "27674141753240653/30123979153216", "-6169936206128396568797607742807090270137721977/6117715203873571641674006593837351328"}, - {"-3/26206484091896184128", "5/2848423294177090248", "15310893822118706237/9330894968229805033368778458685147968", "-5/24882386581946146755650075889827061248"}, - {"26946729/330400702820", "41563965/225583428284", "1238218672302860271/4658307703098666660055", "224002580204097/14906584649915733312176"}, - {"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "-468402123685491748914621885145127724451/396955729248131024720", "350340947706464153265156004876107029701/198477864624065512360"}, - {"575775209696864/1320203974639986246357", "29/712593081308", "410331716733912717985762465/940768218243776489278275419794956", "808/45524274987585732633"}, - {"1786597389946320496771/2066653520653241", "6269770/1992362624741777", "3559549865190272133656109052308126637/4117523232840525481453983149257", "8967230/3296219033"}, - {"-36459180403360509753/32150500941194292113930", "9381566963714/9633539", "301622077145533298008420642898530153/309723104686531919656937098270", "-3784609207827/3426986245"}, -} - -func TestRatBin(t *testing.T) { - for i, test := range ratBinTests { - arg := ratBinArg{test.x, test.y, test.sum} - testRatBin(t, i, "Add", (*Rat).Add, arg) - - arg = ratBinArg{test.y, test.x, test.sum} - testRatBin(t, i, "Add symmetric", (*Rat).Add, arg) - - arg = ratBinArg{test.sum, test.x, test.y} - testRatBin(t, i, "Sub", (*Rat).Sub, arg) - - arg = ratBinArg{test.sum, test.y, test.x} - testRatBin(t, i, "Sub symmetric", (*Rat).Sub, arg) - - arg = ratBinArg{test.x, test.y, test.prod} - testRatBin(t, i, "Mul", (*Rat).Mul, arg) - - arg = ratBinArg{test.y, test.x, test.prod} - testRatBin(t, i, "Mul symmetric", (*Rat).Mul, arg) - - if test.x != "0" { - arg = ratBinArg{test.prod, test.x, test.y} - testRatBin(t, i, "Quo", (*Rat).Quo, arg) - } - - if test.y != "0" { - arg = ratBinArg{test.prod, test.y, test.x} - testRatBin(t, i, "Quo symmetric", (*Rat).Quo, arg) - } - } -} - -func TestIssue820(t *testing.T) { - x := NewRat(3, 1) - y := NewRat(2, 1) - z := y.Quo(x, y) - q := NewRat(3, 2) - if z.Cmp(q) != 0 { - t.Errorf("got %s want %s", z, q) - } - - y = NewRat(3, 1) - x = NewRat(2, 1) - z = y.Quo(x, y) - q = NewRat(2, 3) - if z.Cmp(q) != 0 { - t.Errorf("got %s want %s", z, q) - } - - x = NewRat(3, 1) - z = x.Quo(x, x) - q = NewRat(3, 3) - if z.Cmp(q) != 0 { - t.Errorf("got %s want %s", z, q) - } -} - -var setFrac64Tests = []struct { - a, b int64 - out string -}{ - {0, 1, "0"}, - {0, -1, "0"}, - {1, 1, "1"}, - {-1, 1, "-1"}, - {1, -1, "-1"}, - {-1, -1, "1"}, - {-9223372036854775808, -9223372036854775808, "1"}, -} - -func TestRatSetFrac64Rat(t *testing.T) { - for i, test := range setFrac64Tests { - x := new(Rat).SetFrac64(test.a, test.b) - if x.RatString() != test.out { - t.Errorf("#%d got %s want %s", i, x.RatString(), test.out) - } - } -} - -func TestRatGobEncoding(t *testing.T) { - var medium bytes.Buffer - enc := gob.NewEncoder(&medium) - dec := gob.NewDecoder(&medium) - for i, test := range gobEncodingTests { - for j := 0; j < 4; j++ { - medium.Reset() // empty buffer for each test case (in case of failures) - stest := test - if j&1 != 0 { - // negative numbers - stest = "-" + test - } - if j%2 != 0 { - // fractions - stest = stest + "." + test - } - var tx Rat - tx.SetString(stest) - if err := enc.Encode(&tx); err != nil { - t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err) - } - var rx Rat - if err := dec.Decode(&rx); err != nil { - t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err) - } - if rx.Cmp(&tx) != 0 { - t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx) - } - } - } -} diff --git a/src/pkg/bufio/Makefile b/src/pkg/bufio/Makefile deleted file mode 100644 index 85430e8e8..000000000 --- a/src/pkg/bufio/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../Make.inc - -TARG=bufio -GOFILES=\ - bufio.go\ - -include ../../Make.pkg diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go index 727ebfdbb..b44d0e7d1 100644 --- a/src/pkg/bufio/bufio.go +++ b/src/pkg/bufio/bufio.go @@ -9,38 +9,22 @@ package bufio import ( "bytes" + "errors" "io" - "os" - "strconv" - "utf8" + "unicode/utf8" ) const ( defaultBufSize = 4096 ) -// Errors introduced by this package. -type Error struct { - ErrorString string -} - -func (err *Error) String() string { return err.ErrorString } - var ( - ErrInvalidUnreadByte os.Error = &Error{"bufio: invalid use of UnreadByte"} - ErrInvalidUnreadRune os.Error = &Error{"bufio: invalid use of UnreadRune"} - ErrBufferFull os.Error = &Error{"bufio: buffer full"} - ErrNegativeCount os.Error = &Error{"bufio: negative count"} - errInternal os.Error = &Error{"bufio: internal error"} + ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte") + ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune") + ErrBufferFull = errors.New("bufio: buffer full") + ErrNegativeCount = errors.New("bufio: negative count") ) -// BufSizeError is the error representing an invalid buffer size. -type BufSizeError int - -func (b BufSizeError) String() string { - return "bufio: bad buffer size " + strconv.Itoa(int(b)) -} - // Buffered input. // Reader implements buffering for an io.Reader object. @@ -48,40 +32,36 @@ type Reader struct { buf []byte rd io.Reader r, w int - err os.Error + err error lastByte int lastRuneSize int } -// NewReaderSize creates a new Reader whose buffer has the specified size, -// which must be greater than zero. If the argument io.Reader is already a -// Reader with large enough size, it returns the underlying Reader. -// It returns the Reader and any error. -func NewReaderSize(rd io.Reader, size int) (*Reader, os.Error) { - if size <= 0 { - return nil, BufSizeError(size) - } +const minReadBufferSize = 16 + +// NewReaderSize returns a new Reader whose buffer has at least the specified +// size. If the argument io.Reader is already a Reader with large enough +// size, it returns the underlying Reader. +func NewReaderSize(rd io.Reader, size int) *Reader { // Is it already a Reader? b, ok := rd.(*Reader) if ok && len(b.buf) >= size { - return b, nil + return b + } + if size < minReadBufferSize { + size = minReadBufferSize + } + return &Reader{ + buf: make([]byte, size), + rd: rd, + lastByte: -1, + lastRuneSize: -1, } - b = new(Reader) - b.buf = make([]byte, size) - b.rd = rd - b.lastByte = -1 - b.lastRuneSize = -1 - return b, nil } // NewReader returns a new Reader whose buffer has the default size. func NewReader(rd io.Reader) *Reader { - b, err := NewReaderSize(rd, defaultBufSize) - if err != nil { - // cannot happen - defaultBufSize is a valid size - panic(err) - } - return b + return NewReaderSize(rd, defaultBufSize) } // fill reads a new chunk into the buffer. @@ -101,7 +81,7 @@ func (b *Reader) fill() { } } -func (b *Reader) readErr() os.Error { +func (b *Reader) readErr() error { err := b.err b.err = nil return err @@ -111,7 +91,7 @@ func (b *Reader) readErr() os.Error { // being valid at the next read call. If Peek returns fewer than n bytes, it // also returns an error explaining why the read is short. The error is // ErrBufferFull if n is larger than b's buffer size. -func (b *Reader) Peek(n int) ([]byte, os.Error) { +func (b *Reader) Peek(n int) ([]byte, error) { if n < 0 { return nil, ErrNegativeCount } @@ -125,9 +105,12 @@ func (b *Reader) Peek(n int) ([]byte, os.Error) { if m > n { m = n } - err := b.readErr() - if m < n && err == nil { - err = ErrBufferFull + var err error + if m < n { + err = b.readErr() + if err == nil { + err = ErrBufferFull + } } return b.buf[b.r : b.r+m], err } @@ -136,8 +119,8 @@ func (b *Reader) Peek(n int) ([]byte, os.Error) { // It returns the number of bytes read into p. // It calls Read at most once on the underlying Reader, // hence n may be less than len(p). -// At EOF, the count will be zero and err will be os.EOF. -func (b *Reader) Read(p []byte) (n int, err os.Error) { +// At EOF, the count will be zero and err will be io.EOF. +func (b *Reader) Read(p []byte) (n int, err error) { n = len(p) if n == 0 { return 0, b.readErr() @@ -174,7 +157,7 @@ func (b *Reader) Read(p []byte) (n int, err os.Error) { // ReadByte reads and returns a single byte. // If no byte is available, returns an error. -func (b *Reader) ReadByte() (c byte, err os.Error) { +func (b *Reader) ReadByte() (c byte, err error) { b.lastRuneSize = -1 for b.w == b.r { if b.err != nil { @@ -189,7 +172,7 @@ func (b *Reader) ReadByte() (c byte, err os.Error) { } // UnreadByte unreads the last byte. Only the most recently read byte can be unread. -func (b *Reader) UnreadByte() os.Error { +func (b *Reader) UnreadByte() error { b.lastRuneSize = -1 if b.r == b.w && b.lastByte >= 0 { b.w = 1 @@ -207,8 +190,9 @@ func (b *Reader) UnreadByte() os.Error { } // ReadRune reads a single UTF-8 encoded Unicode character and returns the -// rune and its size in bytes. -func (b *Reader) ReadRune() (rune int, size int, err os.Error) { +// rune and its size in bytes. If the encoded rune is invalid, it consumes one byte +// and returns unicode.ReplacementChar (U+FFFD) with a size of 1. +func (b *Reader) ReadRune() (r rune, size int, err error) { for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil { b.fill() } @@ -216,21 +200,21 @@ func (b *Reader) ReadRune() (rune int, size int, err os.Error) { if b.r == b.w { return 0, 0, b.readErr() } - rune, size = int(b.buf[b.r]), 1 - if rune >= 0x80 { - rune, size = utf8.DecodeRune(b.buf[b.r:b.w]) + r, size = rune(b.buf[b.r]), 1 + if r >= 0x80 { + r, size = utf8.DecodeRune(b.buf[b.r:b.w]) } b.r += size b.lastByte = int(b.buf[b.r-1]) b.lastRuneSize = size - return rune, size, nil + return r, size, nil } // UnreadRune unreads the last rune. If the most recent read operation on // the buffer was not a ReadRune, UnreadRune returns an error. (In this // regard it is stricter than UnreadByte, which will unread the last byte // from any read operation.) -func (b *Reader) UnreadRune() os.Error { +func (b *Reader) UnreadRune() error { if b.lastRuneSize < 0 || b.r == 0 { return ErrInvalidUnreadRune } @@ -247,13 +231,13 @@ func (b *Reader) Buffered() int { return b.w - b.r } // returning a slice pointing at the bytes in the buffer. // The bytes stop being valid at the next read call. // If ReadSlice encounters an error before finding a delimiter, -// it returns all the data in the buffer and the error itself (often os.EOF). +// it returns all the data in the buffer and the error itself (often io.EOF). // ReadSlice fails with error ErrBufferFull if the buffer fills without a delim. // Because the data returned from ReadSlice will be overwritten // by the next I/O operation, most clients should use // ReadBytes or ReadString instead. // ReadSlice returns err != nil if and only if line does not end in delim. -func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) { +func (b *Reader) ReadSlice(delim byte) (line []byte, err error) { // Look in buffer. if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 { line1 := b.buf[b.r : b.r+i+1] @@ -295,22 +279,37 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) { // of the line. The returned buffer is only valid until the next call to // ReadLine. ReadLine either returns a non-nil line or it returns an error, // never both. -func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) { +func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) { line, err = b.ReadSlice('\n') if err == ErrBufferFull { + // Handle the case where "\r\n" straddles the buffer. + if len(line) > 0 && line[len(line)-1] == '\r' { + // Put the '\r' back on buf and drop it from line. + // Let the next call to ReadLine check for "\r\n". + if b.r == 0 { + // should be unreachable + panic("bufio: tried to rewind past start of buffer") + } + b.r-- + line = line[:len(line)-1] + } return line, true, nil } if len(line) == 0 { + if err != nil { + line = nil + } return } err = nil if line[len(line)-1] == '\n' { - line = line[:len(line)-1] - } - if len(line) > 0 && line[len(line)-1] == '\r' { - line = line[:len(line)-1] + drop := 1 + if len(line) > 1 && line[len(line)-2] == '\r' { + drop = 2 + } + line = line[:len(line)-drop] } return } @@ -318,10 +317,10 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) { // ReadBytes reads until the first occurrence of delim in the input, // returning a slice containing the data up to and including the delimiter. // If ReadBytes encounters an error before finding a delimiter, -// it returns the data read before the error and the error itself (often os.EOF). +// it returns the data read before the error and the error itself (often io.EOF). // ReadBytes returns err != nil if and only if the returned data does not end in // delim. -func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { +func (b *Reader) ReadBytes(delim byte) (line []byte, err error) { // Use ReadSlice to look for array, // accumulating full buffers. var frag []byte @@ -329,7 +328,7 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { err = nil for { - var e os.Error + var e error frag, e = b.ReadSlice(delim) if e == nil { // got final fragment break @@ -365,10 +364,10 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err os.Error) { // ReadString reads until the first occurrence of delim in the input, // returning a string containing the data up to and including the delimiter. // If ReadString encounters an error before finding a delimiter, -// it returns the data read before the error and the error itself (often os.EOF). +// it returns the data read before the error and the error itself (often io.EOF). // ReadString returns err != nil if and only if the returned data does not end in // delim. -func (b *Reader) ReadString(delim byte) (line string, err os.Error) { +func (b *Reader) ReadString(delim byte) (line string, err error) { bytes, e := b.ReadBytes(delim) return string(bytes), e } @@ -376,44 +375,40 @@ func (b *Reader) ReadString(delim byte) (line string, err os.Error) { // buffered output // Writer implements buffering for an io.Writer object. +// If an error occurs writing to a Writer, no more data will be +// accepted and all subsequent writes will return the error. type Writer struct { - err os.Error + err error buf []byte n int wr io.Writer } -// NewWriterSize creates a new Writer whose buffer has the specified size, -// which must be greater than zero. If the argument io.Writer is already a -// Writer with large enough size, it returns the underlying Writer. -// It returns the Writer and any error. -func NewWriterSize(wr io.Writer, size int) (*Writer, os.Error) { - if size <= 0 { - return nil, BufSizeError(size) - } +// NewWriterSize returns a new Writer whose buffer has at least the specified +// size. If the argument io.Writer is already a Writer with large enough +// size, it returns the underlying Writer. +func NewWriterSize(wr io.Writer, size int) *Writer { // Is it already a Writer? b, ok := wr.(*Writer) if ok && len(b.buf) >= size { - return b, nil + return b + } + if size <= 0 { + size = defaultBufSize } b = new(Writer) b.buf = make([]byte, size) b.wr = wr - return b, nil + return b } // NewWriter returns a new Writer whose buffer has the default size. func NewWriter(wr io.Writer) *Writer { - b, err := NewWriterSize(wr, defaultBufSize) - if err != nil { - // cannot happen - defaultBufSize is valid size - panic(err) - } - return b + return NewWriterSize(wr, defaultBufSize) } // Flush writes any buffered data to the underlying io.Writer. -func (b *Writer) Flush() os.Error { +func (b *Writer) Flush() error { if b.err != nil { return b.err } @@ -446,7 +441,7 @@ func (b *Writer) Buffered() int { return b.n } // It returns the number of bytes written. // If nn < len(p), it also returns an error explaining // why the write is short. -func (b *Writer) Write(p []byte) (nn int, err os.Error) { +func (b *Writer) Write(p []byte) (nn int, err error) { for len(p) > b.Available() && b.err == nil { var n int if b.Buffered() == 0 { @@ -471,7 +466,7 @@ func (b *Writer) Write(p []byte) (nn int, err os.Error) { } // WriteByte writes a single byte. -func (b *Writer) WriteByte(c byte) os.Error { +func (b *Writer) WriteByte(c byte) error { if b.err != nil { return b.err } @@ -485,9 +480,9 @@ func (b *Writer) WriteByte(c byte) os.Error { // WriteRune writes a single Unicode code point, returning // the number of bytes written and any error. -func (b *Writer) WriteRune(rune int) (size int, err os.Error) { - if rune < utf8.RuneSelf { - err = b.WriteByte(byte(rune)) +func (b *Writer) WriteRune(r rune) (size int, err error) { + if r < utf8.RuneSelf { + err = b.WriteByte(byte(r)) if err != nil { return 0, err } @@ -504,10 +499,10 @@ func (b *Writer) WriteRune(rune int) (size int, err os.Error) { n = b.Available() if n < utf8.UTFMax { // Can only happen if buffer is silly small. - return b.WriteString(string(rune)) + return b.WriteString(string(r)) } } - size = utf8.EncodeRune(b.buf[b.n:], rune) + size = utf8.EncodeRune(b.buf[b.n:], r) b.n += size return size, nil } @@ -516,7 +511,7 @@ func (b *Writer) WriteRune(rune int) (size int, err os.Error) { // It returns the number of bytes written. // If the count is less than len(s), it also returns an error explaining // why the write is short. -func (b *Writer) WriteString(s string) (int, os.Error) { +func (b *Writer) WriteString(s string) (int, error) { nn := 0 for len(s) > b.Available() && b.err == nil { n := copy(b.buf[b.n:], s) diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go index 82c73d36a..a43cbd23a 100644 --- a/src/pkg/bufio/bufio_test.go +++ b/src/pkg/bufio/bufio_test.go @@ -10,11 +10,10 @@ import ( "fmt" "io" "io/ioutil" - "os" "strings" "testing" "testing/iotest" - "utf8" + "unicode/utf8" ) // Reads from a reader and rot13s the result. @@ -28,7 +27,7 @@ func newRot13Reader(r io.Reader) *rot13Reader { return r13 } -func (r13 *rot13Reader) Read(p []byte) (int, os.Error) { +func (r13 *rot13Reader) Read(p []byte) (int, error) { n, e := r13.r.Read(p) if e != nil { return n, e @@ -50,14 +49,14 @@ func readBytes(buf *Reader) string { nb := 0 for { c, e := buf.ReadByte() - if e == os.EOF { + if e == io.EOF { break } if e == nil { b[nb] = c nb++ } else if e != iotest.ErrTimeout { - panic("Data: " + e.String()) + panic("Data: " + e.Error()) } } return string(b[0:nb]) @@ -95,11 +94,11 @@ func readLines(b *Reader) string { s := "" for { s1, e := b.ReadString('\n') - if e == os.EOF { + if e == io.EOF { break } if e != nil && e != iotest.ErrTimeout { - panic("GetLines: " + e.String()) + panic("GetLines: " + e.Error()) } s += s1 } @@ -113,7 +112,7 @@ func reads(buf *Reader, m int) string { for { n, e := buf.Read(b[nb : nb+m]) nb += n - if e == os.EOF { + if e == io.EOF { break } } @@ -136,9 +135,10 @@ var bufreaders = []bufReader{ {"lines", readLines}, } +const minReadBufferSize = 16 + var bufsizes = []int{ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 23, 32, 46, 64, 93, 128, 1024, 4096, + minReadBufferSize, 23, 32, 46, 64, 93, 128, 1024, 4096, } func TestReader(t *testing.T) { @@ -161,7 +161,7 @@ func TestReader(t *testing.T) { bufreader := bufreaders[j] bufsize := bufsizes[k] read := readmaker.fn(bytes.NewBufferString(text)) - buf, _ := NewReaderSize(read, bufsize) + buf := NewReaderSize(read, bufsize) s := bufreader.fn(buf) if s != text { t.Errorf("reader=%s fn=%s bufsize=%d want=%q got=%q", @@ -179,13 +179,13 @@ type StringReader struct { step int } -func (r *StringReader) Read(p []byte) (n int, err os.Error) { +func (r *StringReader) Read(p []byte) (n int, err error) { if r.step < len(r.data) { s := r.data[r.step] n = copy(p, s) r.step++ } else { - err = os.EOF + err = io.EOF } return } @@ -195,14 +195,14 @@ func readRuneSegments(t *testing.T, segments []string) { want := strings.Join(segments, "") r := NewReader(&StringReader{data: segments}) for { - rune, _, err := r.ReadRune() + r, _, err := r.ReadRune() if err != nil { - if err != os.EOF { + if err != io.EOF { return } break } - got += string(rune) + got += string(r) } if got != want { t.Errorf("segments=%v got=%s want=%s", segments, got, want) @@ -233,24 +233,24 @@ func TestUnreadRune(t *testing.T) { r := NewReader(&StringReader{data: segments}) // Normal execution. for { - rune, _, err := r.ReadRune() + r1, _, err := r.ReadRune() if err != nil { - if err != os.EOF { + if err != io.EOF { t.Error("unexpected EOF") } break } - got += string(rune) + got += string(r1) // Put it back and read it again if err = r.UnreadRune(); err != nil { t.Error("unexpected error on UnreadRune:", err) } - rune1, _, err := r.ReadRune() + r2, _, err := r.ReadRune() if err != nil { t.Error("unexpected error reading after unreading:", err) } - if rune != rune1 { - t.Errorf("incorrect rune after unread: got %c wanted %c", rune1, rune) + if r1 != r2 { + t.Errorf("incorrect rune after unread: got %c wanted %c", r1, r2) } } if got != data { @@ -328,7 +328,7 @@ func TestUnreadRuneAtEOF(t *testing.T) { _, _, err := r.ReadRune() if err == nil { t.Error("expected error at EOF") - } else if err != os.EOF { + } else if err != io.EOF { t.Error("expected EOF; got", err) } } @@ -339,25 +339,25 @@ func TestReadWriteRune(t *testing.T) { w := NewWriter(byteBuf) // Write the runes out using WriteRune buf := make([]byte, utf8.UTFMax) - for rune := 0; rune < NRune; rune++ { - size := utf8.EncodeRune(buf, rune) - nbytes, err := w.WriteRune(rune) + for r := rune(0); r < NRune; r++ { + size := utf8.EncodeRune(buf, r) + nbytes, err := w.WriteRune(r) if err != nil { - t.Fatalf("WriteRune(0x%x) error: %s", rune, err) + t.Fatalf("WriteRune(0x%x) error: %s", r, err) } if nbytes != size { - t.Fatalf("WriteRune(0x%x) expected %d, got %d", rune, size, nbytes) + t.Fatalf("WriteRune(0x%x) expected %d, got %d", r, size, nbytes) } } w.Flush() r := NewReader(byteBuf) // Read them back with ReadRune - for rune := 0; rune < NRune; rune++ { - size := utf8.EncodeRune(buf, rune) + for r1 := rune(0); r1 < NRune; r1++ { + size := utf8.EncodeRune(buf, r1) nr, nbytes, err := r.ReadRune() - if nr != rune || nbytes != size || err != nil { - t.Fatalf("ReadRune(0x%x) got 0x%x,%d not 0x%x,%d (err=%s)", r, nr, nbytes, r, size, err) + if nr != r1 || nbytes != size || err != nil { + t.Fatalf("ReadRune(0x%x) got 0x%x,%d not 0x%x,%d (err=%s)", r1, nr, nbytes, r1, size, err) } } } @@ -379,18 +379,14 @@ func TestWriter(t *testing.T) { // and that the data is correct. w.Reset() - buf, e := NewWriterSize(w, bs) + buf := NewWriterSize(w, bs) context := fmt.Sprintf("nwrite=%d bufsize=%d", nwrite, bs) - if e != nil { - t.Errorf("%s: NewWriterSize %d: %v", context, bs, e) - continue - } n, e1 := buf.Write(data[0:nwrite]) if e1 != nil || n != nwrite { t.Errorf("%s: buf.Write %d = %d, %v", context, nwrite, n, e1) continue } - if e = buf.Flush(); e != nil { + if e := buf.Flush(); e != nil { t.Errorf("%s: buf.Flush = %v", context, e) } @@ -413,11 +409,11 @@ func TestWriter(t *testing.T) { type errorWriterTest struct { n, m int - err os.Error - expect os.Error + err error + expect error } -func (w errorWriterTest) Write(p []byte) (int, os.Error) { +func (w errorWriterTest) Write(p []byte) (int, error) { return len(p) * w.n / w.m, w.err } @@ -425,9 +421,9 @@ var errorWriterTests = []errorWriterTest{ {0, 1, nil, io.ErrShortWrite}, {1, 2, nil, io.ErrShortWrite}, {1, 1, nil, nil}, - {0, 1, os.EPIPE, os.EPIPE}, - {1, 2, os.EPIPE, os.EPIPE}, - {1, 1, os.EPIPE, os.EPIPE}, + {0, 1, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 2, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 1, io.ErrClosedPipe, io.ErrClosedPipe}, } func TestWriteErrors(t *testing.T) { @@ -447,23 +443,14 @@ func TestWriteErrors(t *testing.T) { func TestNewReaderSizeIdempotent(t *testing.T) { const BufSize = 1000 - b, err := NewReaderSize(bytes.NewBufferString("hello world"), BufSize) - if err != nil { - t.Error("NewReaderSize create fail", err) - } + b := NewReaderSize(bytes.NewBufferString("hello world"), BufSize) // Does it recognize itself? - b1, err2 := NewReaderSize(b, BufSize) - if err2 != nil { - t.Error("NewReaderSize #2 create fail", err2) - } + b1 := NewReaderSize(b, BufSize) if b1 != b { t.Error("NewReaderSize did not detect underlying Reader") } // Does it wrap if existing buffer is too small? - b2, err3 := NewReaderSize(b, 2*BufSize) - if err3 != nil { - t.Error("NewReaderSize #3 create fail", err3) - } + b2 := NewReaderSize(b, 2*BufSize) if b2 == b { t.Error("NewReaderSize did not enlarge buffer") } @@ -471,23 +458,14 @@ func TestNewReaderSizeIdempotent(t *testing.T) { func TestNewWriterSizeIdempotent(t *testing.T) { const BufSize = 1000 - b, err := NewWriterSize(new(bytes.Buffer), BufSize) - if err != nil { - t.Error("NewWriterSize create fail", err) - } + b := NewWriterSize(new(bytes.Buffer), BufSize) // Does it recognize itself? - b1, err2 := NewWriterSize(b, BufSize) - if err2 != nil { - t.Error("NewWriterSize #2 create fail", err2) - } + b1 := NewWriterSize(b, BufSize) if b1 != b { t.Error("NewWriterSize did not detect underlying Writer") } // Does it wrap if existing buffer is too small? - b2, err3 := NewWriterSize(b, 2*BufSize) - if err3 != nil { - t.Error("NewWriterSize #3 create fail", err3) - } + b2 := NewWriterSize(b, 2*BufSize) if b2 == b { t.Error("NewWriterSize did not enlarge buffer") } @@ -496,10 +474,7 @@ func TestNewWriterSizeIdempotent(t *testing.T) { func TestWriteString(t *testing.T) { const BufSize = 8 buf := new(bytes.Buffer) - b, err := NewWriterSize(buf, BufSize) - if err != nil { - t.Error("NewWriterSize create fail", err) - } + b := NewWriterSize(buf, BufSize) b.WriteString("0") // easy b.WriteString("123456") // still easy b.WriteString("7890") // easy after flush @@ -515,27 +490,29 @@ func TestWriteString(t *testing.T) { } func TestBufferFull(t *testing.T) { - buf, _ := NewReaderSize(strings.NewReader("hello, world"), 5) - line, err := buf.ReadSlice(',') - if string(line) != "hello" || err != ErrBufferFull { + const longString = "And now, hello, world! It is the time for all good men to come to the aid of their party" + buf := NewReaderSize(strings.NewReader(longString), minReadBufferSize) + line, err := buf.ReadSlice('!') + if string(line) != "And now, hello, " || err != ErrBufferFull { t.Errorf("first ReadSlice(,) = %q, %v", line, err) } - line, err = buf.ReadSlice(',') - if string(line) != "," || err != nil { + line, err = buf.ReadSlice('!') + if string(line) != "world!" || err != nil { t.Errorf("second ReadSlice(,) = %q, %v", line, err) } } func TestPeek(t *testing.T) { p := make([]byte, 10) - buf, _ := NewReaderSize(strings.NewReader("abcdefghij"), 4) + // string is 16 (minReadBufferSize) long. + buf := NewReaderSize(strings.NewReader("abcdefghijklmnop"), minReadBufferSize) if s, err := buf.Peek(1); string(s) != "a" || err != nil { t.Fatalf("want %q got %q, err=%v", "a", string(s), err) } if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { t.Fatalf("want %q got %q, err=%v", "abcd", string(s), err) } - if _, err := buf.Peek(5); err != ErrBufferFull { + if _, err := buf.Peek(32); err != ErrBufferFull { t.Fatalf("want ErrBufFull got %v", err) } if _, err := buf.Read(p[0:3]); string(p[0:3]) != "abc" || err != nil { @@ -553,15 +530,36 @@ func TestPeek(t *testing.T) { if s, err := buf.Peek(4); string(s) != "ghij" || err != nil { t.Fatalf("want %q got %q, err=%v", "ghij", string(s), err) } - if _, err := buf.Read(p[0:4]); string(p[0:4]) != "ghij" || err != nil { - t.Fatalf("want %q got %q, err=%v", "ghij", string(p[0:3]), err) + if _, err := buf.Read(p[0:]); string(p[0:]) != "ghijklmnop" || err != nil { + t.Fatalf("want %q got %q, err=%v", "ghijklmnop", string(p[0:minReadBufferSize]), err) } if s, err := buf.Peek(0); string(s) != "" || err != nil { t.Fatalf("want %q got %q, err=%v", "", string(s), err) } - if _, err := buf.Peek(1); err != os.EOF { + if _, err := buf.Peek(1); err != io.EOF { t.Fatalf("want EOF got %v", err) } + + // Test for issue 3022, not exposing a reader's error on a successful Peek. + buf = NewReaderSize(dataAndEOFReader("abcd"), 32) + if s, err := buf.Peek(2); string(s) != "ab" || err != nil { + t.Errorf(`Peek(2) on "abcd", EOF = %q, %v; want "ab", nil`, string(s), err) + } + if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { + t.Errorf(`Peek(4) on "abcd", EOF = %q, %v; want "abcd", nil`, string(s), err) + } + if n, err := buf.Read(p[0:5]); string(p[0:n]) != "abcd" || err != nil { + t.Fatalf("Read after peek = %q, %v; want abcd, EOF", p[0:n], err) + } + if n, err := buf.Read(p[0:1]); string(p[0:n]) != "" || err != io.EOF { + t.Fatalf(`second Read after peek = %q, %v; want "", EOF`, p[0:n], err) + } +} + +type dataAndEOFReader string + +func (r dataAndEOFReader) Read(p []byte) (int, error) { + return copy(p, r), io.EOF } func TestPeekThenUnreadRune(t *testing.T) { @@ -583,7 +581,7 @@ type testReader struct { stride int } -func (t *testReader) Read(buf []byte) (n int, err os.Error) { +func (t *testReader) Read(buf []byte) (n int, err error) { n = t.stride if n > len(t.data) { n = len(t.data) @@ -594,7 +592,7 @@ func (t *testReader) Read(buf []byte) (n int, err os.Error) { copy(buf, t.data) t.data = t.data[n:] if len(t.data) == 0 { - err = os.EOF + err = io.EOF } return } @@ -604,7 +602,7 @@ func testReadLine(t *testing.T, input []byte) { for stride := 1; stride < 2; stride++ { done := 0 reader := testReader{input, stride} - l, _ := NewReaderSize(&reader, len(input)+1) + l := NewReaderSize(&reader, len(input)+1) for { line, isPrefix, err := l.ReadLine() if len(line) > 0 && err != nil { @@ -614,7 +612,7 @@ func testReadLine(t *testing.T, input []byte) { t.Errorf("ReadLine returned prefix") } if err != nil { - if err != os.EOF { + if err != io.EOF { t.Fatalf("Got unknown error: %s", err) } break @@ -636,19 +634,25 @@ func TestReadLine(t *testing.T) { } func TestLineTooLong(t *testing.T) { - buf := bytes.NewBuffer([]byte("aaabbbcc\n")) - l, _ := NewReaderSize(buf, 3) + data := make([]byte, 0) + for i := 0; i < minReadBufferSize*5/2; i++ { + data = append(data, '0'+byte(i%10)) + } + buf := bytes.NewBuffer(data) + l := NewReaderSize(buf, minReadBufferSize) line, isPrefix, err := l.ReadLine() - if !isPrefix || !bytes.Equal(line, []byte("aaa")) || err != nil { - t.Errorf("bad result for first line: %x %s", line, err) + if !isPrefix || !bytes.Equal(line, data[:minReadBufferSize]) || err != nil { + t.Errorf("bad result for first line: got %q want %q %v", line, data[:minReadBufferSize], err) } + data = data[len(line):] line, isPrefix, err = l.ReadLine() - if !isPrefix || !bytes.Equal(line, []byte("bbb")) || err != nil { - t.Errorf("bad result for second line: %x", line) + if !isPrefix || !bytes.Equal(line, data[:minReadBufferSize]) || err != nil { + t.Errorf("bad result for second line: got %q want %q %v", line, data[:minReadBufferSize], err) } + data = data[len(line):] line, isPrefix, err = l.ReadLine() - if isPrefix || !bytes.Equal(line, []byte("cc")) || err != nil { - t.Errorf("bad result for third line: %x", line) + if isPrefix || !bytes.Equal(line, data[:minReadBufferSize/2]) || err != nil { + t.Errorf("bad result for third line: got %q want %q %v", line, data[:minReadBufferSize/2], err) } line, isPrefix, err = l.ReadLine() if isPrefix || err == nil { @@ -657,12 +661,12 @@ func TestLineTooLong(t *testing.T) { } func TestReadAfterLines(t *testing.T) { - line1 := "line1" - restData := "line2\nline 3\n" + line1 := "this is line1" + restData := "this is line2\nthis is line 3\n" inbuf := bytes.NewBuffer([]byte(line1 + "\n" + restData)) outbuf := new(bytes.Buffer) maxLineLength := len(line1) + len(restData)/2 - l, _ := NewReaderSize(inbuf, maxLineLength) + l := NewReaderSize(inbuf, maxLineLength) line, isPrefix, err := l.ReadLine() if isPrefix || err != nil || string(line) != line1 { t.Errorf("bad result for first line: isPrefix=%v err=%v line=%q", isPrefix, err, string(line)) @@ -677,15 +681,15 @@ func TestReadAfterLines(t *testing.T) { } func TestReadEmptyBuffer(t *testing.T) { - l, _ := NewReaderSize(bytes.NewBuffer(nil), 10) + l := NewReaderSize(new(bytes.Buffer), minReadBufferSize) line, isPrefix, err := l.ReadLine() - if err != os.EOF { + if err != io.EOF { t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err) } } func TestLinesAfterRead(t *testing.T) { - l, _ := NewReaderSize(bytes.NewBuffer([]byte("foo")), 10) + l := NewReaderSize(bytes.NewBuffer([]byte("foo")), minReadBufferSize) _, err := ioutil.ReadAll(l) if err != nil { t.Error(err) @@ -693,7 +697,68 @@ func TestLinesAfterRead(t *testing.T) { } line, isPrefix, err := l.ReadLine() - if err != os.EOF { + if err != io.EOF { t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err) } } + +func TestReadLineNonNilLineOrError(t *testing.T) { + r := NewReader(strings.NewReader("line 1\n")) + for i := 0; i < 2; i++ { + l, _, err := r.ReadLine() + if l != nil && err != nil { + t.Fatalf("on line %d/2; ReadLine=%#v, %v; want non-nil line or Error, but not both", + i+1, l, err) + } + } +} + +type readLineResult struct { + line []byte + isPrefix bool + err error +} + +var readLineNewlinesTests = []struct { + input string + expect []readLineResult +}{ + {"012345678901234\r\n012345678901234\r\n", []readLineResult{ + {[]byte("012345678901234"), true, nil}, + {nil, false, nil}, + {[]byte("012345678901234"), true, nil}, + {nil, false, nil}, + {nil, false, io.EOF}, + }}, + {"0123456789012345\r012345678901234\r", []readLineResult{ + {[]byte("0123456789012345"), true, nil}, + {[]byte("\r012345678901234"), true, nil}, + {[]byte("\r"), false, nil}, + {nil, false, io.EOF}, + }}, +} + +func TestReadLineNewlines(t *testing.T) { + for _, e := range readLineNewlinesTests { + testReadLineNewlines(t, e.input, e.expect) + } +} + +func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) { + b := NewReaderSize(strings.NewReader(input), minReadBufferSize) + for i, e := range expect { + line, isPrefix, err := b.ReadLine() + if bytes.Compare(line, e.line) != 0 { + t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line) + return + } + if isPrefix != e.isPrefix { + t.Errorf("%q call %d, isPrefix == %v, want %v", input, i, isPrefix, e.isPrefix) + return + } + if err != e.err { + t.Errorf("%q call %d, err == %v, want %v", input, i, err, e.err) + return + } + } +} diff --git a/src/pkg/builtin/builtin.go b/src/pkg/builtin/builtin.go index 07acce4f7..e81616ca4 100644 --- a/src/pkg/builtin/builtin.go +++ b/src/pkg/builtin/builtin.go @@ -3,29 +3,110 @@ // license that can be found in the LICENSE file. /* - Package builtin provides documentation for Go's built-in functions. - The functions documented here are not actually in package builtin + Package builtin provides documentation for Go's predeclared identifiers. + The items documented here are not actually in package builtin but their descriptions here allow godoc to present documentation - for the language's special functions. + for the language's special identifiers. */ package builtin +// bool is the set of boolean values, true and false. +type bool bool + +// uint8 is the set of all unsigned 8-bit integers. +// Range: 0 through 255. +type uint8 uint8 + +// uint16 is the set of all unsigned 16-bit integers. +// Range: 0 through 65535. +type uint16 uint16 + +// uint32 is the set of all unsigned 32-bit integers. +// Range: 0 through 4294967295. +type uint32 uint32 + +// uint64 is the set of all unsigned 64-bit integers. +// Range: 0 through 18446744073709551615. +type uint64 uint64 + +// int8 is the set of all signed 8-bit integers. +// Range: -128 through 127. +type int8 int8 + +// int16 is the set of all signed 16-bit integers. +// Range: -32768 through 32767. +type int16 int16 + +// int32 is the set of all signed 32-bit integers. +// Range: -2147483648 through 2147483647. +type int32 int32 + +// int64 is the set of all signed 64-bit integers. +// Range: -9223372036854775808 through 9223372036854775807. +type int64 int64 + +// float32 is the set of all IEEE-754 32-bit floating-point numbers. +type float32 float32 + +// float64 is the set of all IEEE-754 64-bit floating-point numbers. +type float64 float64 + +// complex64 is the set of all complex numbers with float32 real and +// imaginary parts. +type complex64 complex64 + +// complex128 is the set of all complex numbers with float64 real and +// imaginary parts. +type complex128 complex128 + +// string is the set of all strings of 8-bit bytes, conventionally but not +// necessarily representing UTF-8-encoded text. A string may be empty, but +// not nil. Values of string type are immutable. +type string string + +// int is a signed integer type that is at least 32 bits in size. It is a +// distinct type, however, and not an alias for, say, int32. +type int int + +// uint is an unsigned integer type that is at least 32 bits in size. It is a +// distinct type, however, and not an alias for, say, uint32. +type uint uint + +// uintptr is an integer type that is large enough to hold the bit pattern of +// any pointer. +type uintptr uintptr + +// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is +// used, by convention, to distinguish byte values from 8-bit unsigned +// integer values. +type byte byte + +// rune is an alias for int and is equivalent to int in all ways. It is +// used, by convention, to distinguish character values from integer values. +// In a future version of Go, it will change to an alias of int32. +type rune rune + // Type is here for the purposes of documentation only. It is a stand-in // for any Go type, but represents the same type for any given function // invocation. type Type int +// Type1 is here for the purposes of documentation only. It is a stand-in +// for any Go type, but represents the same type for any given function +// invocation. +type Type1 int + // IntegerType is here for the purposes of documentation only. It is a stand-in // for any integer type: int, uint, int8 etc. type IntegerType int // FloatType is here for the purposes of documentation only. It is a stand-in // for either float type: float32 or float64. -type FloatType int +type FloatType float32 // ComplexType is here for the purposes of documentation only. It is a // stand-in for either complex type: complex64 or complex128. -type ComplexType int +type ComplexType complex64 // The append built-in function appends elements to the end of a slice. If // it has sufficient capacity, the destination is resliced to accommodate the @@ -43,6 +124,11 @@ func append(slice []Type, elems ...Type) []Type // len(src) and len(dst). func copy(dst, src []Type) int +// The delete built-in function deletes the element with the specified key +// (m[key]) from the map. If there is no such element, delete is a no-op. +// If m is nil, delete panics. +func delete(m map[Type]Type1, key Type) + // The len built-in function returns the length of v, according to its type: // Array: the number of elements in v. // Pointer to array: the number of elements in *v (even if v is nil). @@ -95,7 +181,7 @@ func complex(r, i FloatType) ComplexType // The return value will be floating point type corresponding to the type of c. func real(c ComplexType) FloatType -// The imaginary built-in function returns the imaginary part of the complex +// The imag built-in function returns the imaginary part of the complex // number c. The return value will be floating point type corresponding to // the type of c. func imag(c ComplexType) FloatType @@ -133,3 +219,9 @@ func panic(v interface{}) // nil. Thus the return value from recover reports whether the goroutine is // panicking. func recover() interface{} + +// The error built-in interface type is the conventional interface for +// representing an error condition, with the nil value representing no error. +type error interface { + Error() string +} diff --git a/src/pkg/bytes/Makefile b/src/pkg/bytes/Makefile deleted file mode 100644 index 03395c7a4..000000000 --- a/src/pkg/bytes/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../Make.inc - -TARG=bytes -GOFILES=\ - buffer.go\ - bytes.go\ - bytes_decl.go\ - -OFILES=\ - asm_$(GOARCH).$O\ - -include ../../Make.pkg diff --git a/src/pkg/bytes/asm_386.s b/src/pkg/bytes/asm_386.s index f3391740b..e7833de0c 100644 --- a/src/pkg/bytes/asm_386.s +++ b/src/pkg/bytes/asm_386.s @@ -15,3 +15,19 @@ TEXT ·IndexByte(SB),7,$0 SUBL $1, DI MOVL DI, ret+16(FP) RET + +TEXT ·Equal(SB),7,$0 + MOVL len+4(FP), BX + MOVL len1+16(FP), CX + MOVL $0, AX + CMPL BX, CX + JNE eqret + MOVL p+0(FP), SI + MOVL q+12(FP), DI + CLD + REP; CMPSB + JNE eqret + MOVL $1, AX +eqret: + MOVB AX, ret+24(FP) + RET diff --git a/src/pkg/bytes/asm_amd64.s b/src/pkg/bytes/asm_amd64.s index c6793cbdc..5caea5c52 100644 --- a/src/pkg/bytes/asm_amd64.s +++ b/src/pkg/bytes/asm_amd64.s @@ -90,3 +90,19 @@ success: MOVL DI, ret+24(FP) RET +TEXT ·Equal(SB),7,$0 + MOVL len+8(FP), BX + MOVL len1+24(FP), CX + MOVL $0, AX + CMPL BX, CX + JNE eqret + MOVQ p+0(FP), SI + MOVQ q+16(FP), DI + CLD + REP; CMPSB + MOVL $1, DX + CMOVLEQ DX, AX +eqret: + MOVB AX, ret+32(FP) + RET + diff --git a/src/pkg/bytes/asm_arm.s b/src/pkg/bytes/asm_arm.s index f32fca136..4ed0c1580 100644 --- a/src/pkg/bytes/asm_arm.s +++ b/src/pkg/bytes/asm_arm.s @@ -6,3 +6,6 @@ TEXT ·IndexByte(SB),7,$0 B ·indexBytePortable(SB) +// no memcmp implementation on arm yet +TEXT ·Equal(SB),7,$0 + B ·equalPortable(SB) diff --git a/src/pkg/bytes/buffer.go b/src/pkg/bytes/buffer.go index 5de86105d..afdf22055 100644 --- a/src/pkg/bytes/buffer.go +++ b/src/pkg/bytes/buffer.go @@ -7,9 +7,9 @@ package bytes // Simple byte buffer for marshaling data. import ( + "errors" "io" - "os" - "utf8" + "unicode/utf8" ) // A Buffer is a variable-sized buffer of bytes with Read and Write methods. @@ -33,6 +33,9 @@ const ( opRead // Any other read operation. ) +// ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer. +var ErrTooLarge = errors.New("bytes.Buffer: too large") + // Bytes returns a slice of the contents of the unread portion of the buffer; // len(b.Bytes()) == b.Len(). If the caller changes the contents of the // returned slice, the contents of the buffer will change provided there @@ -54,10 +57,13 @@ func (b *Buffer) String() string { func (b *Buffer) Len() int { return len(b.buf) - b.off } // Truncate discards all but the first n unread bytes from the buffer. -// It is an error to call b.Truncate(n) with n > b.Len(). +// It panics if n is negative or greater than the length of the buffer. func (b *Buffer) Truncate(n int) { b.lastRead = opInvalid - if n == 0 { + switch { + case n < 0 || n > b.Len(): + panic("bytes.Buffer: truncation out of range") + case n == 0: // Reuse buffer space. b.off = 0 } @@ -68,8 +74,9 @@ func (b *Buffer) Truncate(n int) { // b.Reset() is the same as b.Truncate(0). func (b *Buffer) Reset() { b.Truncate(0) } -// Grow buffer to guarantee space for n more bytes. -// Return index where bytes should be written. +// grow grows the buffer to guarantee space for n more bytes. +// It returns the index where bytes should be written. +// If the buffer can't grow it will panic with ErrTooLarge. func (b *Buffer) grow(n int) int { m := b.Len() // If buffer is empty, reset to recover space. @@ -82,7 +89,7 @@ func (b *Buffer) grow(n int) int { buf = b.bootstrap[0:] } else { // not enough space anywhere - buf = make([]byte, 2*cap(b.buf)+n) + buf = makeSlice(2*cap(b.buf) + n) copy(buf, b.buf[b.off:]) } b.buf = buf @@ -94,16 +101,19 @@ func (b *Buffer) grow(n int) int { // Write appends the contents of p to the buffer. The return // value n is the length of p; err is always nil. -func (b *Buffer) Write(p []byte) (n int, err os.Error) { +// If the buffer becomes too large, Write will panic with +// ErrTooLarge. +func (b *Buffer) Write(p []byte) (n int, err error) { b.lastRead = opInvalid m := b.grow(len(p)) - copy(b.buf[m:], p) - return len(p), nil + return copy(b.buf[m:], p), nil } // WriteString appends the contents of s to the buffer. The return // value n is the length of s; err is always nil. -func (b *Buffer) WriteString(s string) (n int, err os.Error) { +// If the buffer becomes too large, WriteString will panic with +// ErrTooLarge. +func (b *Buffer) WriteString(s string) (n int, err error) { b.lastRead = opInvalid m := b.grow(len(s)) return copy(b.buf[m:], s), nil @@ -117,33 +127,33 @@ const MinRead = 512 // ReadFrom reads data from r until EOF and appends it to the buffer. // The return value n is the number of bytes read. -// Any error except os.EOF encountered during the read +// Any error except io.EOF encountered during the read // is also returned. -func (b *Buffer) ReadFrom(r io.Reader) (n int64, err os.Error) { +// If the buffer becomes too large, ReadFrom will panic with +// ErrTooLarge. +func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) { b.lastRead = opInvalid // If buffer is empty, reset to recover space. if b.off >= len(b.buf) { b.Truncate(0) } for { - if cap(b.buf)-len(b.buf) < MinRead { - var newBuf []byte - // can we get space without allocation? - if b.off+cap(b.buf)-len(b.buf) >= MinRead { - // reuse beginning of buffer - newBuf = b.buf[0 : len(b.buf)-b.off] - } else { - // not enough space at end; put space on end - newBuf = make([]byte, len(b.buf)-b.off, 2*(cap(b.buf)-b.off)+MinRead) + if free := cap(b.buf) - len(b.buf); free < MinRead { + // not enough space at end + newBuf := b.buf + if b.off+free < MinRead { + // not enough space using beginning of buffer; + // double buffer capacity + newBuf = makeSlice(2*cap(b.buf) + MinRead) } copy(newBuf, b.buf[b.off:]) - b.buf = newBuf + b.buf = newBuf[:len(b.buf)-b.off] b.off = 0 } m, e := r.Read(b.buf[len(b.buf):cap(b.buf)]) b.buf = b.buf[0 : len(b.buf)+m] n += int64(m) - if e == os.EOF { + if e == io.EOF { break } if e != nil { @@ -153,21 +163,40 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err os.Error) { return n, nil // err is EOF, so return nil explicitly } +// makeSlice allocates a slice of size n. If the allocation fails, it panics +// with ErrTooLarge. +func makeSlice(n int) []byte { + // If the make fails, give a known error. + defer func() { + if recover() != nil { + panic(ErrTooLarge) + } + }() + return make([]byte, n) +} + // WriteTo writes data to w until the buffer is drained or an error // occurs. The return value n is the number of bytes written; it always // fits into an int, but it is int64 to match the io.WriterTo interface. // Any error encountered during the write is also returned. -func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) { +func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) { b.lastRead = opInvalid if b.off < len(b.buf) { + nBytes := b.Len() m, e := w.Write(b.buf[b.off:]) + if m > nBytes { + panic("bytes.Buffer.WriteTo: invalid Write count") + } b.off += m n = int64(m) if e != nil { return n, e } - // otherwise all bytes were written, by definition of + // all bytes should have been written, by definition of // Write method in io.Writer + if m != nBytes { + return n, io.ErrShortWrite + } } // Buffer is now empty; reset. b.Truncate(0) @@ -177,7 +206,9 @@ func (b *Buffer) WriteTo(w io.Writer) (n int64, err os.Error) { // WriteByte appends the byte c to the buffer. // The returned error is always nil, but is included // to match bufio.Writer's WriteByte. -func (b *Buffer) WriteByte(c byte) os.Error { +// If the buffer becomes too large, WriteByte will panic with +// ErrTooLarge. +func (b *Buffer) WriteByte(c byte) error { b.lastRead = opInvalid m := b.grow(1) b.buf[m] = c @@ -188,7 +219,9 @@ func (b *Buffer) WriteByte(c byte) os.Error { // code point r to the buffer, returning its length and // an error, which is always nil but is included // to match bufio.Writer's WriteRune. -func (b *Buffer) WriteRune(r int) (n int, err os.Error) { +// If the buffer becomes too large, WriteRune will panic with +// ErrTooLarge. +func (b *Buffer) WriteRune(r rune) (n int, err error) { if r < utf8.RuneSelf { b.WriteByte(byte(r)) return 1, nil @@ -200,14 +233,17 @@ func (b *Buffer) WriteRune(r int) (n int, err os.Error) { // Read reads the next len(p) bytes from the buffer or until the buffer // is drained. The return value n is the number of bytes read. If the -// buffer has no data to return, err is os.EOF even if len(p) is zero; +// buffer has no data to return, err is io.EOF (unless len(p) is zero); // otherwise it is nil. -func (b *Buffer) Read(p []byte) (n int, err os.Error) { +func (b *Buffer) Read(p []byte) (n int, err error) { b.lastRead = opInvalid if b.off >= len(b.buf) { // Buffer is empty, reset to recover space. b.Truncate(0) - return 0, os.EOF + if len(p) == 0 { + return + } + return 0, io.EOF } n = copy(p, b.buf[b.off:]) b.off += n @@ -236,13 +272,13 @@ func (b *Buffer) Next(n int) []byte { } // ReadByte reads and returns the next byte from the buffer. -// If no byte is available, it returns error os.EOF. -func (b *Buffer) ReadByte() (c byte, err os.Error) { +// If no byte is available, it returns error io.EOF. +func (b *Buffer) ReadByte() (c byte, err error) { b.lastRead = opInvalid if b.off >= len(b.buf) { // Buffer is empty, reset to recover space. b.Truncate(0) - return 0, os.EOF + return 0, io.EOF } c = b.buf[b.off] b.off++ @@ -252,21 +288,21 @@ func (b *Buffer) ReadByte() (c byte, err os.Error) { // ReadRune reads and returns the next UTF-8-encoded // Unicode code point from the buffer. -// If no bytes are available, the error returned is os.EOF. +// If no bytes are available, the error returned is io.EOF. // If the bytes are an erroneous UTF-8 encoding, it // consumes one byte and returns U+FFFD, 1. -func (b *Buffer) ReadRune() (r int, size int, err os.Error) { +func (b *Buffer) ReadRune() (r rune, size int, err error) { b.lastRead = opInvalid if b.off >= len(b.buf) { // Buffer is empty, reset to recover space. b.Truncate(0) - return 0, 0, os.EOF + return 0, 0, io.EOF } b.lastRead = opReadRune c := b.buf[b.off] if c < utf8.RuneSelf { b.off++ - return int(c), 1, nil + return rune(c), 1, nil } r, n := utf8.DecodeRune(b.buf[b.off:]) b.off += n @@ -278,9 +314,9 @@ func (b *Buffer) ReadRune() (r int, size int, err os.Error) { // not a ReadRune, UnreadRune returns an error. (In this regard // it is stricter than UnreadByte, which will unread the last byte // from any read operation.) -func (b *Buffer) UnreadRune() os.Error { +func (b *Buffer) UnreadRune() error { if b.lastRead != opReadRune { - return os.NewError("bytes.Buffer: UnreadRune: previous operation was not ReadRune") + return errors.New("bytes.Buffer: UnreadRune: previous operation was not ReadRune") } b.lastRead = opInvalid if b.off > 0 { @@ -293,9 +329,9 @@ func (b *Buffer) UnreadRune() os.Error { // UnreadByte unreads the last byte returned by the most recent // read operation. If write has happened since the last read, UnreadByte // returns an error. -func (b *Buffer) UnreadByte() os.Error { +func (b *Buffer) UnreadByte() error { if b.lastRead != opReadRune && b.lastRead != opRead { - return os.NewError("bytes.Buffer: UnreadByte: previous operation was not a read") + return errors.New("bytes.Buffer: UnreadByte: previous operation was not a read") } b.lastRead = opInvalid if b.off > 0 { @@ -307,15 +343,15 @@ func (b *Buffer) UnreadByte() os.Error { // ReadBytes reads until the first occurrence of delim in the input, // returning a slice containing the data up to and including the delimiter. // If ReadBytes encounters an error before finding a delimiter, -// it returns the data read before the error and the error itself (often os.EOF). +// it returns the data read before the error and the error itself (often io.EOF). // ReadBytes returns err != nil if and only if the returned data does not end in // delim. -func (b *Buffer) ReadBytes(delim byte) (line []byte, err os.Error) { +func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { i := IndexByte(b.buf[b.off:], delim) size := i + 1 if i < 0 { size = len(b.buf) - b.off - err = os.EOF + err = io.EOF } line = make([]byte, size) copy(line, b.buf[b.off:]) @@ -326,23 +362,29 @@ func (b *Buffer) ReadBytes(delim byte) (line []byte, err os.Error) { // ReadString reads until the first occurrence of delim in the input, // returning a string containing the data up to and including the delimiter. // If ReadString encounters an error before finding a delimiter, -// it returns the data read before the error and the error itself (often os.EOF). +// it returns the data read before the error and the error itself (often io.EOF). // ReadString returns err != nil if and only if the returned data does not end // in delim. -func (b *Buffer) ReadString(delim byte) (line string, err os.Error) { +func (b *Buffer) ReadString(delim byte) (line string, err error) { bytes, err := b.ReadBytes(delim) return string(bytes), err } // NewBuffer creates and initializes a new Buffer using buf as its initial // contents. It is intended to prepare a Buffer to read existing data. It -// can also be used to size the internal buffer for writing. To do that, +// can also be used to size the internal buffer for writing. To do that, // buf should have the desired capacity but a length of zero. +// +// In most cases, new(Buffer) (or just declaring a Buffer variable) is +// sufficient to initialize a Buffer. func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } // NewBufferString creates and initializes a new Buffer using string s as its -// initial contents. It is intended to prepare a buffer to read an existing +// initial contents. It is intended to prepare a buffer to read an existing // string. +// +// In most cases, new(Buffer) (or just declaring a Buffer variable) is +// sufficient to initialize a Buffer. func NewBufferString(s string) *Buffer { return &Buffer{buf: []byte(s)} } diff --git a/src/pkg/bytes/buffer_test.go b/src/pkg/bytes/buffer_test.go index 06d2a65c6..d0af11f10 100644 --- a/src/pkg/bytes/buffer_test.go +++ b/src/pkg/bytes/buffer_test.go @@ -6,17 +6,16 @@ package bytes_test import ( . "bytes" - "os" - "rand" + "io" + "math/rand" "testing" - "utf8" + "unicode/utf8" ) const N = 10000 // make this bigger for a larger (and slower) test var data string // test data for write tests var bytes []byte // test data; same as data but as a slice. - func init() { bytes = make([]byte, N) for i := 0; i < N; i++ { @@ -264,7 +263,7 @@ func TestRuneIO(t *testing.T) { b := make([]byte, utf8.UTFMax*NRune) var buf Buffer n := 0 - for r := 0; r < NRune; r++ { + for r := rune(0); r < NRune; r++ { size := utf8.EncodeRune(b[n:], r) nbytes, err := buf.WriteRune(r) if err != nil { @@ -284,7 +283,7 @@ func TestRuneIO(t *testing.T) { p := make([]byte, utf8.UTFMax) // Read it back with ReadRune - for r := 0; r < NRune; r++ { + for r := rune(0); r < NRune; r++ { size := utf8.EncodeRune(p, r) nr, nbytes, err := buf.ReadRune() if nr != r || nbytes != size || err != nil { @@ -295,7 +294,7 @@ func TestRuneIO(t *testing.T) { // Check that UnreadRune works buf.Reset() buf.Write(b) - for r := 0; r < NRune; r++ { + for r := rune(0); r < NRune; r++ { r1, size, _ := buf.ReadRune() if err := buf.UnreadRune(); err != nil { t.Fatalf("UnreadRune(%U) got error %q", r, err) @@ -344,21 +343,21 @@ var readBytesTests = []struct { buffer string delim byte expected []string - err os.Error + err error }{ - {"", 0, []string{""}, os.EOF}, + {"", 0, []string{""}, io.EOF}, {"a\x00", 0, []string{"a\x00"}, nil}, {"abbbaaaba", 'b', []string{"ab", "b", "b", "aaab"}, nil}, {"hello\x01world", 1, []string{"hello\x01"}, nil}, - {"foo\nbar", 0, []string{"foo\nbar"}, os.EOF}, + {"foo\nbar", 0, []string{"foo\nbar"}, io.EOF}, {"alpha\nbeta\ngamma\n", '\n', []string{"alpha\n", "beta\n", "gamma\n"}, nil}, - {"alpha\nbeta\ngamma", '\n', []string{"alpha\n", "beta\n", "gamma"}, os.EOF}, + {"alpha\nbeta\ngamma", '\n', []string{"alpha\n", "beta\n", "gamma"}, io.EOF}, } func TestReadBytes(t *testing.T) { for _, test := range readBytesTests { buf := NewBufferString(test.buffer) - var err os.Error + var err error for _, expected := range test.expected { var bytes []byte bytes, err = buf.ReadBytes(test.delim) @@ -374,3 +373,16 @@ func TestReadBytes(t *testing.T) { } } } + +// Was a bug: used to give EOF reading empty slice at EOF. +func TestReadEmptyAtEOF(t *testing.T) { + b := new(Buffer) + slice := make([]byte, 0) + n, err := b.Read(slice) + if err != nil { + t.Errorf("read error: %v", err) + } + if n != 0 { + t.Errorf("wrong count; got %d want 0", n) + } +} diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go index 5119fce94..7d1426fb4 100644 --- a/src/pkg/bytes/bytes.go +++ b/src/pkg/bytes/bytes.go @@ -8,11 +8,12 @@ package bytes import ( "unicode" - "utf8" + "unicode/utf8" ) // Compare returns an integer comparing the two byte arrays lexicographically. // The result will be 0 if a==b, -1 if a < b, and +1 if a > b +// A nil argument is equivalent to an empty slice. func Compare(a, b []byte) int { m := len(a) if m > len(b) { @@ -37,7 +38,10 @@ func Compare(a, b []byte) int { } // Equal returns a boolean reporting whether a == b. -func Equal(a, b []byte) bool { +// A nil argument is equivalent to an empty slice. +func Equal(a, b []byte) bool + +func equalPortable(a, b []byte) bool { if len(a) != len(b) { return false } @@ -74,18 +78,38 @@ func explode(s []byte, n int) [][]byte { // Count counts the number of non-overlapping instances of sep in s. func Count(s, sep []byte) int { - if len(sep) == 0 { + n := len(sep) + if n == 0 { return utf8.RuneCount(s) + 1 } + if n > len(s) { + return 0 + } + count := 0 c := sep[0] - n := 0 - for i := 0; i+len(sep) <= len(s); i++ { - if s[i] == c && (len(sep) == 1 || Equal(s[i:i+len(sep)], sep)) { - n++ - i += len(sep) - 1 + i := 0 + t := s[:len(s)-n+1] + for i < len(t) { + if t[i] != c { + o := IndexByte(t[i:], c) + if o < 0 { + break + } + i += o + } + if n == 1 || Equal(s[i:i+n], sep) { + count++ + i += n + continue } + i++ } - return n + return count +} + +// Contains returns whether subslice is within b. +func Contains(b, subslice []byte) bool { + return Index(b, subslice) != -1 } // Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. @@ -94,11 +118,27 @@ func Index(s, sep []byte) int { if n == 0 { return 0 } + if n > len(s) { + return -1 + } c := sep[0] - for i := 0; i+n <= len(s); i++ { - if s[i] == c && (n == 1 || Equal(s[i:i+n], sep)) { + if n == 1 { + return IndexByte(s, c) + } + i := 0 + t := s[:len(s)-n+1] + for i < len(t) { + if t[i] != c { + o := IndexByte(t[i:], c) + if o < 0 { + break + } + i += o + } + if Equal(s[i:i+n], sep) { return i } + i++ } return -1 } @@ -130,10 +170,10 @@ func LastIndex(s, sep []byte) int { // IndexRune interprets s as a sequence of UTF-8-encoded Unicode code points. // It returns the byte index of the first occurrence in s of the given rune. // It returns -1 if rune is not present in s. -func IndexRune(s []byte, rune int) int { +func IndexRune(s []byte, r rune) int { for i := 0; i < len(s); { - r, size := utf8.DecodeRune(s[i:]) - if r == rune { + r1, size := utf8.DecodeRune(s[i:]) + if r == r1 { return i } i += size @@ -147,16 +187,17 @@ func IndexRune(s []byte, rune int) int { // point in common. func IndexAny(s []byte, chars string) int { if len(chars) > 0 { - var rune, width int + var r rune + var width int for i := 0; i < len(s); i += width { - rune = int(s[i]) - if rune < utf8.RuneSelf { + r = rune(s[i]) + if r < utf8.RuneSelf { width = 1 } else { - rune, width = utf8.DecodeRune(s[i:]) + r, width = utf8.DecodeRune(s[i:]) } - for _, r := range chars { - if rune == r { + for _, ch := range chars { + if r == ch { return i } } @@ -172,10 +213,10 @@ func IndexAny(s []byte, chars string) int { func LastIndexAny(s []byte, chars string) int { if len(chars) > 0 { for i := len(s); i > 0; { - rune, size := utf8.DecodeLastRune(s[0:i]) + r, size := utf8.DecodeLastRune(s[0:i]) i -= size - for _, m := range chars { - if rune == m { + for _, ch := range chars { + if r == ch { return i } } @@ -256,13 +297,13 @@ func Fields(s []byte) [][]byte { // It splits the array s at each run of code points c satisfying f(c) and // returns a slice of subarrays of s. If no code points in s satisfy f(c), an // empty slice is returned. -func FieldsFunc(s []byte, f func(int) bool) [][]byte { +func FieldsFunc(s []byte, f func(rune) bool) [][]byte { n := 0 inField := false for i := 0; i < len(s); { - rune, size := utf8.DecodeRune(s[i:]) + r, size := utf8.DecodeRune(s[i:]) wasInField := inField - inField = !f(rune) + inField = !f(r) if inField && !wasInField { n++ } @@ -273,13 +314,13 @@ func FieldsFunc(s []byte, f func(int) bool) [][]byte { na := 0 fieldStart := -1 for i := 0; i <= len(s) && na < n; { - rune, size := utf8.DecodeRune(s[i:]) - if fieldStart < 0 && size > 0 && !f(rune) { + r, size := utf8.DecodeRune(s[i:]) + if fieldStart < 0 && size > 0 && !f(r) { fieldStart = i i += size continue } - if fieldStart >= 0 && (size == 0 || f(rune)) { + if fieldStart >= 0 && (size == 0 || f(r)) { a[na] = s[fieldStart:i] na++ fieldStart = -1 @@ -329,7 +370,7 @@ func HasSuffix(s, suffix []byte) bool { // according to the mapping function. If mapping returns a negative value, the character is // dropped from the string with no replacement. The characters in s and the // output are interpreted as UTF-8-encoded Unicode code points. -func Map(mapping func(rune int) int, s []byte) []byte { +func Map(mapping func(r rune) rune, s []byte) []byte { // In the worst case, the array can grow when mapped, making // things unpleasant. But it's so rare we barge in assuming it's // fine. It could also shrink but that falls out naturally. @@ -338,20 +379,20 @@ func Map(mapping func(rune int) int, s []byte) []byte { b := make([]byte, maxbytes) for i := 0; i < len(s); { wid := 1 - rune := int(s[i]) - if rune >= utf8.RuneSelf { - rune, wid = utf8.DecodeRune(s[i:]) + r := rune(s[i]) + if r >= utf8.RuneSelf { + r, wid = utf8.DecodeRune(s[i:]) } - rune = mapping(rune) - if rune >= 0 { - if nbytes+utf8.RuneLen(rune) > maxbytes { + r = mapping(r) + if r >= 0 { + if nbytes+utf8.RuneLen(r) > maxbytes { // Grow the buffer. maxbytes = maxbytes*2 + utf8.UTFMax nb := make([]byte, maxbytes) copy(nb, b[0:nbytes]) b = nb } - nbytes += utf8.EncodeRune(b[nbytes:maxbytes], rune) + nbytes += utf8.EncodeRune(b[nbytes:maxbytes], r) } i += wid } @@ -383,44 +424,44 @@ func ToTitle(s []byte) []byte { return Map(unicode.ToTitle, s) } // ToUpperSpecial returns a copy of the byte array s with all Unicode letters mapped to their // upper case, giving priority to the special casing rules. func ToUpperSpecial(_case unicode.SpecialCase, s []byte) []byte { - return Map(func(r int) int { return _case.ToUpper(r) }, s) + return Map(func(r rune) rune { return _case.ToUpper(r) }, s) } // ToLowerSpecial returns a copy of the byte array s with all Unicode letters mapped to their // lower case, giving priority to the special casing rules. func ToLowerSpecial(_case unicode.SpecialCase, s []byte) []byte { - return Map(func(r int) int { return _case.ToLower(r) }, s) + return Map(func(r rune) rune { return _case.ToLower(r) }, s) } // ToTitleSpecial returns a copy of the byte array s with all Unicode letters mapped to their // title case, giving priority to the special casing rules. func ToTitleSpecial(_case unicode.SpecialCase, s []byte) []byte { - return Map(func(r int) int { return _case.ToTitle(r) }, s) + return Map(func(r rune) rune { return _case.ToTitle(r) }, s) } // isSeparator reports whether the rune could mark a word boundary. // TODO: update when package unicode captures more of the properties. -func isSeparator(rune int) bool { +func isSeparator(r rune) bool { // ASCII alphanumerics and underscore are not separators - if rune <= 0x7F { + if r <= 0x7F { switch { - case '0' <= rune && rune <= '9': + case '0' <= r && r <= '9': return false - case 'a' <= rune && rune <= 'z': + case 'a' <= r && r <= 'z': return false - case 'A' <= rune && rune <= 'Z': + case 'A' <= r && r <= 'Z': return false - case rune == '_': + case r == '_': return false } return true } // Letters and digits are not separators - if unicode.IsLetter(rune) || unicode.IsDigit(rune) { + if unicode.IsLetter(r) || unicode.IsDigit(r) { return false } // Otherwise, all we can do for now is treat spaces as separators. - return unicode.IsSpace(rune) + return unicode.IsSpace(r) } // BUG(r): The rule Title uses for word boundaries does not handle Unicode punctuation properly. @@ -433,7 +474,7 @@ func Title(s []byte) []byte { // the closure once per rune. prev := ' ' return Map( - func(r int) int { + func(r rune) rune { if isSeparator(prev) { prev = r return unicode.ToTitle(r) @@ -446,7 +487,7 @@ func Title(s []byte) []byte { // TrimLeftFunc returns a subslice of s by slicing off all leading UTF-8-encoded // Unicode code points c that satisfy f(c). -func TrimLeftFunc(s []byte, f func(r int) bool) []byte { +func TrimLeftFunc(s []byte, f func(r rune) bool) []byte { i := indexFunc(s, f, false) if i == -1 { return nil @@ -456,7 +497,7 @@ func TrimLeftFunc(s []byte, f func(r int) bool) []byte { // TrimRightFunc returns a subslice of s by slicing off all trailing UTF-8 // encoded Unicode code points c that satisfy f(c). -func TrimRightFunc(s []byte, f func(r int) bool) []byte { +func TrimRightFunc(s []byte, f func(r rune) bool) []byte { i := lastIndexFunc(s, f, false) if i >= 0 && s[i] >= utf8.RuneSelf { _, wid := utf8.DecodeRune(s[i:]) @@ -469,36 +510,36 @@ func TrimRightFunc(s []byte, f func(r int) bool) []byte { // TrimFunc returns a subslice of s by slicing off all leading and trailing // UTF-8-encoded Unicode code points c that satisfy f(c). -func TrimFunc(s []byte, f func(r int) bool) []byte { +func TrimFunc(s []byte, f func(r rune) bool) []byte { return TrimRightFunc(TrimLeftFunc(s, f), f) } // IndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points. // It returns the byte index in s of the first Unicode // code point satisfying f(c), or -1 if none do. -func IndexFunc(s []byte, f func(r int) bool) int { +func IndexFunc(s []byte, f func(r rune) bool) int { return indexFunc(s, f, true) } // LastIndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points. // It returns the byte index in s of the last Unicode // code point satisfying f(c), or -1 if none do. -func LastIndexFunc(s []byte, f func(r int) bool) int { +func LastIndexFunc(s []byte, f func(r rune) bool) int { return lastIndexFunc(s, f, true) } // indexFunc is the same as IndexFunc except that if // truth==false, the sense of the predicate function is // inverted. -func indexFunc(s []byte, f func(r int) bool, truth bool) int { +func indexFunc(s []byte, f func(r rune) bool, truth bool) int { start := 0 for start < len(s) { wid := 1 - rune := int(s[start]) - if rune >= utf8.RuneSelf { - rune, wid = utf8.DecodeRune(s[start:]) + r := rune(s[start]) + if r >= utf8.RuneSelf { + r, wid = utf8.DecodeRune(s[start:]) } - if f(rune) == truth { + if f(r) == truth { return start } start += wid @@ -509,21 +550,21 @@ func indexFunc(s []byte, f func(r int) bool, truth bool) int { // lastIndexFunc is the same as LastIndexFunc except that if // truth==false, the sense of the predicate function is // inverted. -func lastIndexFunc(s []byte, f func(r int) bool, truth bool) int { +func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int { for i := len(s); i > 0; { - rune, size := utf8.DecodeLastRune(s[0:i]) + r, size := utf8.DecodeLastRune(s[0:i]) i -= size - if f(rune) == truth { + if f(r) == truth { return i } } return -1 } -func makeCutsetFunc(cutset string) func(rune int) bool { - return func(rune int) bool { +func makeCutsetFunc(cutset string) func(r rune) bool { + return func(r rune) bool { for _, c := range cutset { - if c == rune { + if c == r { return true } } @@ -556,8 +597,8 @@ func TrimSpace(s []byte) []byte { } // Runes returns a slice of runes (Unicode code points) equivalent to s. -func Runes(s []byte) []int { - t := make([]int, utf8.RuneCount(s)) +func Runes(s []byte) []rune { + t := make([]rune, utf8.RuneCount(s)) i := 0 for len(s) > 0 { r, l := utf8.DecodeRune(s) @@ -572,13 +613,18 @@ func Runes(s []byte) []int { // non-overlapping instances of old replaced by new. // If n < 0, there is no limit on the number of replacements. func Replace(s, old, new []byte, n int) []byte { - if n == 0 { - return s // avoid allocation - } - // Compute number of replacements. - if m := Count(s, old); m == 0 { - return s // avoid allocation - } else if n <= 0 || m < n { + m := 0 + if n != 0 { + // Compute number of replacements. + m = Count(s, old) + } + if m == 0 { + // Nothing to do. Just copy. + t := make([]byte, len(s)) + copy(t, s) + return t + } + if n < 0 || m < n { n = m } @@ -603,3 +649,58 @@ func Replace(s, old, new []byte, n int) []byte { w += copy(t[w:], s[start:]) return t[0:w] } + +// EqualFold reports whether s and t, interpreted as UTF-8 strings, +// are equal under Unicode case-folding. +func EqualFold(s, t []byte) bool { + for len(s) != 0 && len(t) != 0 { + // Extract first rune from each. + var sr, tr rune + if s[0] < utf8.RuneSelf { + sr, s = rune(s[0]), s[1:] + } else { + r, size := utf8.DecodeRune(s) + sr, s = r, s[size:] + } + if t[0] < utf8.RuneSelf { + tr, t = rune(t[0]), t[1:] + } else { + r, size := utf8.DecodeRune(t) + tr, t = r, t[size:] + } + + // If they match, keep going; if not, return false. + + // Easy case. + if tr == sr { + continue + } + + // Make sr < tr to simplify what follows. + if tr < sr { + tr, sr = sr, tr + } + // Fast check for ASCII. + if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' { + // ASCII, and sr is upper case. tr must be lower case. + if tr == sr+'a'-'A' { + continue + } + return false + } + + // General case. SimpleFold(x) returns the next equivalent rune > x + // or wraps around to smaller values. + r := unicode.SimpleFold(sr) + for r != sr && r < tr { + r = unicode.SimpleFold(r) + } + if r == tr { + continue + } + return false + } + + // One string is empty. Are both? + return len(s) == len(t) +} diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go index 9444358a8..000f23517 100644 --- a/src/pkg/bytes/bytes_test.go +++ b/src/pkg/bytes/bytes_test.go @@ -9,7 +9,7 @@ import ( "reflect" "testing" "unicode" - "utf8" + "unicode/utf8" ) func eq(a, b []string) bool { @@ -46,31 +46,42 @@ type BinOpTest struct { i int } -var comparetests = []BinOpTest{ - {"", "", 0}, - {"a", "", 1}, - {"", "a", -1}, - {"abc", "abc", 0}, - {"ab", "abc", -1}, - {"abc", "ab", 1}, - {"x", "ab", 1}, - {"ab", "x", -1}, - {"x", "a", 1}, - {"b", "x", -1}, +var compareTests = []struct { + a, b []byte + i int +}{ + {[]byte(""), []byte(""), 0}, + {[]byte("a"), []byte(""), 1}, + {[]byte(""), []byte("a"), -1}, + {[]byte("abc"), []byte("abc"), 0}, + {[]byte("ab"), []byte("abc"), -1}, + {[]byte("abc"), []byte("ab"), 1}, + {[]byte("x"), []byte("ab"), 1}, + {[]byte("ab"), []byte("x"), -1}, + {[]byte("x"), []byte("a"), 1}, + {[]byte("b"), []byte("x"), -1}, + // nil tests + {nil, nil, 0}, + {[]byte(""), nil, 0}, + {nil, []byte(""), 0}, + {[]byte("a"), nil, 1}, + {nil, []byte("a"), -1}, } func TestCompare(t *testing.T) { - for _, tt := range comparetests { - a := []byte(tt.a) - b := []byte(tt.b) - cmp := Compare(a, b) - eql := Equal(a, b) + for _, tt := range compareTests { + cmp := Compare(tt.a, tt.b) if cmp != tt.i { t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp) } + eql := Equal(tt.a, tt.b) if eql != (tt.i == 0) { t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql) } + eql = EqualPortable(tt.a, tt.b) + if eql != (tt.i == 0) { + t.Errorf(`EqualPortable(%q, %q) = %v`, tt.a, tt.b, eql) + } } } @@ -264,41 +275,147 @@ func TestIndexRune(t *testing.T) { } } -func BenchmarkIndexByte4K(b *testing.B) { bmIndex(b, IndexByte, 4<<10) } +var bmbuf []byte + +func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) } +func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) } +func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) } +func BenchmarkIndexByte64M(b *testing.B) { bmIndexByte(b, IndexByte, 64<<20) } +func BenchmarkIndexBytePortable32(b *testing.B) { bmIndexByte(b, IndexBytePortable, 32) } +func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) } +func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) } +func BenchmarkIndexBytePortable64M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 64<<20) } + +func bmIndexByte(b *testing.B, index func([]byte, byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + for i := 0; i < b.N; i++ { + j := index(buf, 'x') + if j != n-1 { + b.Fatal("bad index", j) + } + } + buf[n-1] = '\x00' +} + +func BenchmarkEqual32(b *testing.B) { bmEqual(b, Equal, 32) } +func BenchmarkEqual4K(b *testing.B) { bmEqual(b, Equal, 4<<10) } +func BenchmarkEqual4M(b *testing.B) { bmEqual(b, Equal, 4<<20) } +func BenchmarkEqual64M(b *testing.B) { bmEqual(b, Equal, 64<<20) } +func BenchmarkEqualPort32(b *testing.B) { bmEqual(b, EqualPortable, 32) } +func BenchmarkEqualPort4K(b *testing.B) { bmEqual(b, EqualPortable, 4<<10) } +func BenchmarkEqualPortable4M(b *testing.B) { bmEqual(b, EqualPortable, 4<<20) } +func BenchmarkEqualPortable64M(b *testing.B) { bmEqual(b, EqualPortable, 64<<20) } -func BenchmarkIndexByte4M(b *testing.B) { bmIndex(b, IndexByte, 4<<20) } +func bmEqual(b *testing.B, equal func([]byte, []byte) bool, n int) { + if len(bmbuf) < 2*n { + bmbuf = make([]byte, 2*n) + } + b.SetBytes(int64(n)) + buf1 := bmbuf[0:n] + buf2 := bmbuf[n : 2*n] + buf1[n-1] = 'x' + buf2[n-1] = 'x' + for i := 0; i < b.N; i++ { + eq := equal(buf1, buf2) + if !eq { + b.Fatal("bad equal") + } + } + buf1[n-1] = '\x00' + buf2[n-1] = '\x00' +} -func BenchmarkIndexByte64M(b *testing.B) { bmIndex(b, IndexByte, 64<<20) } +func BenchmarkIndex32(b *testing.B) { bmIndex(b, Index, 32) } +func BenchmarkIndex4K(b *testing.B) { bmIndex(b, Index, 4<<10) } +func BenchmarkIndex4M(b *testing.B) { bmIndex(b, Index, 4<<20) } +func BenchmarkIndex64M(b *testing.B) { bmIndex(b, Index, 64<<20) } -func BenchmarkIndexBytePortable4K(b *testing.B) { - bmIndex(b, IndexBytePortable, 4<<10) +func bmIndex(b *testing.B, index func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + for i := 0; i < b.N; i++ { + j := index(buf, buf[n-7:]) + if j != n-7 { + b.Fatal("bad index", j) + } + } + buf[n-1] = '\x00' } -func BenchmarkIndexBytePortable4M(b *testing.B) { - bmIndex(b, IndexBytePortable, 4<<20) +func BenchmarkIndexEasy32(b *testing.B) { bmIndexEasy(b, Index, 32) } +func BenchmarkIndexEasy4K(b *testing.B) { bmIndexEasy(b, Index, 4<<10) } +func BenchmarkIndexEasy4M(b *testing.B) { bmIndexEasy(b, Index, 4<<20) } +func BenchmarkIndexEasy64M(b *testing.B) { bmIndexEasy(b, Index, 64<<20) } + +func bmIndexEasy(b *testing.B, index func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + buf[n-7] = 'x' + for i := 0; i < b.N; i++ { + j := index(buf, buf[n-7:]) + if j != n-7 { + b.Fatal("bad index", j) + } + } + buf[n-1] = '\x00' + buf[n-7] = '\x00' } -func BenchmarkIndexBytePortable64M(b *testing.B) { - bmIndex(b, IndexBytePortable, 64<<20) +func BenchmarkCount32(b *testing.B) { bmCount(b, Count, 32) } +func BenchmarkCount4K(b *testing.B) { bmCount(b, Count, 4<<10) } +func BenchmarkCount4M(b *testing.B) { bmCount(b, Count, 4<<20) } +func BenchmarkCount64M(b *testing.B) { bmCount(b, Count, 64<<20) } + +func bmCount(b *testing.B, count func([]byte, []byte) int, n int) { + if len(bmbuf) < n { + bmbuf = make([]byte, n) + } + b.SetBytes(int64(n)) + buf := bmbuf[0:n] + buf[n-1] = 'x' + for i := 0; i < b.N; i++ { + j := count(buf, buf[n-7:]) + if j != 1 { + b.Fatal("bad count", j) + } + } + buf[n-1] = '\x00' } -var bmbuf []byte +func BenchmarkCountEasy32(b *testing.B) { bmCountEasy(b, Count, 32) } +func BenchmarkCountEasy4K(b *testing.B) { bmCountEasy(b, Count, 4<<10) } +func BenchmarkCountEasy4M(b *testing.B) { bmCountEasy(b, Count, 4<<20) } +func BenchmarkCountEasy64M(b *testing.B) { bmCountEasy(b, Count, 64<<20) } -func bmIndex(b *testing.B, index func([]byte, byte) int, n int) { +func bmCountEasy(b *testing.B, count func([]byte, []byte) int, n int) { if len(bmbuf) < n { bmbuf = make([]byte, n) } b.SetBytes(int64(n)) buf := bmbuf[0:n] buf[n-1] = 'x' + buf[n-7] = 'x' for i := 0; i < b.N; i++ { - j := index(buf, 'x') - if j != n-1 { - println("bad index", j) - panic("bad index") + j := count(buf, buf[n-7:]) + if j != 1 { + b.Fatal("bad count", j) } } - buf[n-1] = '0' + buf[n-1] = '\x00' + buf[n-7] = '\x00' } type ExplodeTest struct { @@ -444,7 +561,7 @@ func TestFields(t *testing.T) { } func TestFieldsFunc(t *testing.T) { - pred := func(c int) bool { return c == 'X' } + pred := func(c rune) bool { return c == 'X' } var fieldsFuncTests = []FieldsTest{ {"", []string{}}, {"XX", []string{}}, @@ -514,24 +631,24 @@ func runStringTests(t *testing.T, f func([]byte) []byte, funcName string, testCa } } -func tenRunes(rune int) string { - r := make([]int, 10) - for i := range r { - r[i] = rune +func tenRunes(r rune) string { + runes := make([]rune, 10) + for i := range runes { + runes[i] = r } - return string(r) + return string(runes) } // User-defined self-inverse mapping function -func rot13(rune int) int { - step := 13 - if rune >= 'a' && rune <= 'z' { - return ((rune - 'a' + step) % 26) + 'a' +func rot13(r rune) rune { + const step = 13 + if r >= 'a' && r <= 'z' { + return ((r - 'a' + step) % 26) + 'a' } - if rune >= 'A' && rune <= 'Z' { - return ((rune - 'A' + step) % 26) + 'A' + if r >= 'A' && r <= 'Z' { + return ((r - 'A' + step) % 26) + 'A' } - return rune + return r } func TestMap(t *testing.T) { @@ -539,7 +656,7 @@ func TestMap(t *testing.T) { a := tenRunes('a') // 1. Grow. This triggers two reallocations in Map. - maxRune := func(rune int) int { return unicode.MaxRune } + maxRune := func(r rune) rune { return unicode.MaxRune } m := Map(maxRune, []byte(a)) expect := tenRunes(unicode.MaxRune) if string(m) != expect { @@ -547,7 +664,7 @@ func TestMap(t *testing.T) { } // 2. Shrink - minRune := func(rune int) int { return 'a' } + minRune := func(r rune) rune { return 'a' } m = Map(minRune, []byte(tenRunes(unicode.MaxRune))) expect = a if string(m) != expect { @@ -569,9 +686,9 @@ func TestMap(t *testing.T) { } // 5. Drop - dropNotLatin := func(rune int) int { - if unicode.Is(unicode.Latin, rune) { - return rune + dropNotLatin := func(r rune) rune { + if unicode.Is(unicode.Latin, r) { + return r } return -1 } @@ -615,7 +732,7 @@ func TestRepeat(t *testing.T) { } } -func runesEqual(a, b []int) bool { +func runesEqual(a, b []rune) bool { if len(a) != len(b) { return false } @@ -629,18 +746,18 @@ func runesEqual(a, b []int) bool { type RunesTest struct { in string - out []int + out []rune lossy bool } var RunesTests = []RunesTest{ - {"", []int{}, false}, - {" ", []int{32}, false}, - {"ABC", []int{65, 66, 67}, false}, - {"abc", []int{97, 98, 99}, false}, - {"\u65e5\u672c\u8a9e", []int{26085, 26412, 35486}, false}, - {"ab\x80c", []int{97, 98, 0xFFFD, 99}, true}, - {"ab\xc0c", []int{97, 98, 0xFFFD, 99}, true}, + {"", []rune{}, false}, + {" ", []rune{32}, false}, + {"ABC", []rune{65, 66, 67}, false}, + {"abc", []rune{97, 98, 99}, false}, + {"\u65e5\u672c\u8a9e", []rune{26085, 26412, 35486}, false}, + {"ab\x80c", []rune{97, 98, 0xFFFD, 99}, true}, + {"ab\xc0c", []rune{97, 98, 0xFFFD, 99}, true}, } func TestRunes(t *testing.T) { @@ -662,48 +779,49 @@ func TestRunes(t *testing.T) { } type TrimTest struct { - f func([]byte, string) []byte + f string in, cutset, out string } var trimTests = []TrimTest{ - {Trim, "abba", "a", "bb"}, - {Trim, "abba", "ab", ""}, - {TrimLeft, "abba", "ab", ""}, - {TrimRight, "abba", "ab", ""}, - {TrimLeft, "abba", "a", "bba"}, - {TrimRight, "abba", "a", "abb"}, - {Trim, "", "<>", "tag"}, - {Trim, "* listitem", " *", "listitem"}, - {Trim, `"quote"`, `"`, "quote"}, - {Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, + {"Trim", "abba", "a", "bb"}, + {"Trim", "abba", "ab", ""}, + {"TrimLeft", "abba", "ab", ""}, + {"TrimRight", "abba", "ab", ""}, + {"TrimLeft", "abba", "a", "bba"}, + {"TrimRight", "abba", "a", "abb"}, + {"Trim", "", "<>", "tag"}, + {"Trim", "* listitem", " *", "listitem"}, + {"Trim", `"quote"`, `"`, "quote"}, + {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, //empty string tests - {Trim, "abba", "", "abba"}, - {Trim, "", "123", ""}, - {Trim, "", "", ""}, - {TrimLeft, "abba", "", "abba"}, - {TrimLeft, "", "123", ""}, - {TrimLeft, "", "", ""}, - {TrimRight, "abba", "", "abba"}, - {TrimRight, "", "123", ""}, - {TrimRight, "", "", ""}, - {TrimRight, "☺\xc0", "☺", "☺\xc0"}, + {"Trim", "abba", "", "abba"}, + {"Trim", "", "123", ""}, + {"Trim", "", "", ""}, + {"TrimLeft", "abba", "", "abba"}, + {"TrimLeft", "", "123", ""}, + {"TrimLeft", "", "", ""}, + {"TrimRight", "abba", "", "abba"}, + {"TrimRight", "", "123", ""}, + {"TrimRight", "", "", ""}, + {"TrimRight", "☺\xc0", "☺", "☺\xc0"}, } func TestTrim(t *testing.T) { for _, tc := range trimTests { - actual := string(tc.f([]byte(tc.in), tc.cutset)) - var name string - switch tc.f { - case Trim: - name = "Trim" - case TrimLeft: - name = "TrimLeft" - case TrimRight: - name = "TrimRight" + name := tc.f + var f func([]byte, string) []byte + switch name { + case "Trim": + f = Trim + case "TrimLeft": + f = TrimLeft + case "TrimRight": + f = TrimRight default: - t.Error("Undefined trim function") + t.Errorf("Undefined trim function %s", name) } + actual := string(f([]byte(tc.in), tc.cutset)) if actual != tc.out { t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out) } @@ -711,7 +829,7 @@ func TestTrim(t *testing.T) { } type predicate struct { - f func(r int) bool + f func(r rune) bool name string } @@ -719,7 +837,7 @@ var isSpace = predicate{unicode.IsSpace, "IsSpace"} var isDigit = predicate{unicode.IsDigit, "IsDigit"} var isUpper = predicate{unicode.IsUpper, "IsUpper"} var isValidRune = predicate{ - func(r int) bool { + func(r rune) bool { return r != utf8.RuneError }, "IsValidRune", @@ -732,7 +850,7 @@ type TrimFuncTest struct { func not(p predicate) predicate { return predicate{ - func(r int) bool { + func(r rune) bool { return !p.f(r) }, "not " + p.name, @@ -829,9 +947,15 @@ var ReplaceTests = []ReplaceTest{ func TestReplace(t *testing.T) { for _, tt := range ReplaceTests { - if s := string(Replace([]byte(tt.in), []byte(tt.old), []byte(tt.new), tt.n)); s != tt.out { + in := append([]byte(tt.in), ""...) + in = in[:len(tt.in)] + out := Replace(in, []byte(tt.old), []byte(tt.new), tt.n) + if s := string(out); s != tt.out { t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out) } + if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] { + t.Errorf("Replace(%q, %q, %q, %d) didn't copy", tt.in, tt.old, tt.new, tt.n) + } } } @@ -856,3 +980,31 @@ func TestTitle(t *testing.T) { } } } + +var EqualFoldTests = []struct { + s, t string + out bool +}{ + {"abc", "abc", true}, + {"ABcd", "ABcd", true}, + {"123abc", "123ABC", true}, + {"αβδ", "ΑΒΔ", true}, + {"abc", "xyz", false}, + {"abc", "XYZ", false}, + {"abcdefghijk", "abcdefghijX", false}, + {"abcdefghijk", "abcdefghij\u212A", true}, + {"abcdefghijK", "abcdefghij\u212A", true}, + {"abcdefghijkz", "abcdefghij\u212Ay", false}, + {"abcdefghijKz", "abcdefghij\u212Ay", false}, +} + +func TestEqualFold(t *testing.T) { + for _, tt := range EqualFoldTests { + if out := EqualFold([]byte(tt.s), []byte(tt.t)); out != tt.out { + t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.s, tt.t, out, tt.out) + } + if out := EqualFold([]byte(tt.t), []byte(tt.s)); out != tt.out { + t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.t, tt.s, out, tt.out) + } + } +} diff --git a/src/pkg/bytes/example_test.go b/src/pkg/bytes/example_test.go new file mode 100644 index 000000000..6fe8cd5a9 --- /dev/null +++ b/src/pkg/bytes/example_test.go @@ -0,0 +1,28 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bytes_test + +import ( + . "bytes" + "encoding/base64" + "io" + "os" +) + +func ExampleBuffer() { + var b Buffer // A Buffer needs no initialization. + b.Write([]byte("Hello ")) + b.Write([]byte("world!")) + b.WriteTo(os.Stdout) + // Output: Hello world! +} + +func ExampleBuffer_reader() { + // A Buffer can turn a string or a []byte into an io.Reader. + buf := NewBufferString("R29waGVycyBydWxlIQ==") + dec := base64.NewDecoder(base64.StdEncoding, buf) + io.Copy(os.Stdout, dec) + // Output: Gophers rule! +} diff --git a/src/pkg/bytes/export_test.go b/src/pkg/bytes/export_test.go index b65428d9c..f61523e60 100644 --- a/src/pkg/bytes/export_test.go +++ b/src/pkg/bytes/export_test.go @@ -6,3 +6,4 @@ package bytes // Export func for testing var IndexBytePortable = indexBytePortable +var EqualPortable = equalPortable diff --git a/src/pkg/bytes/reader.go b/src/pkg/bytes/reader.go new file mode 100644 index 000000000..a062e54ba --- /dev/null +++ b/src/pkg/bytes/reader.go @@ -0,0 +1,125 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bytes + +import ( + "errors" + "io" + "unicode/utf8" +) + +// A Reader implements the io.Reader, io.ReaderAt, io.Seeker, +// io.ByteScanner, and io.RuneScanner interfaces by reading from +// a byte slice. +// Unlike a Buffer, a Reader is read-only and supports seeking. +type Reader struct { + s []byte + i int // current reading index + prevRune int // index of previous rune; or < 0 +} + +// Len returns the number of bytes of the unread portion of the +// slice. +func (r *Reader) Len() int { + if r.i >= len(r.s) { + return 0 + } + return len(r.s) - r.i +} + +func (r *Reader) Read(b []byte) (n int, err error) { + if len(b) == 0 { + return 0, nil + } + if r.i >= len(r.s) { + return 0, io.EOF + } + n = copy(b, r.s[r.i:]) + r.i += n + r.prevRune = -1 + return +} + +func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) { + if off < 0 { + return 0, errors.New("bytes: invalid offset") + } + if off >= int64(len(r.s)) { + return 0, io.EOF + } + n = copy(b, r.s[int(off):]) + if n < len(b) { + err = io.EOF + } + return +} + +func (r *Reader) ReadByte() (b byte, err error) { + if r.i >= len(r.s) { + return 0, io.EOF + } + b = r.s[r.i] + r.i++ + r.prevRune = -1 + return +} + +func (r *Reader) UnreadByte() error { + if r.i <= 0 { + return errors.New("bytes.Reader: at beginning of slice") + } + r.i-- + r.prevRune = -1 + return nil +} + +func (r *Reader) ReadRune() (ch rune, size int, err error) { + if r.i >= len(r.s) { + return 0, 0, io.EOF + } + r.prevRune = r.i + if c := r.s[r.i]; c < utf8.RuneSelf { + r.i++ + return rune(c), 1, nil + } + ch, size = utf8.DecodeRune(r.s[r.i:]) + r.i += size + return +} + +func (r *Reader) UnreadRune() error { + if r.prevRune < 0 { + return errors.New("bytes.Reader: previous operation was not ReadRune") + } + r.i = r.prevRune + r.prevRune = -1 + return nil +} + +// Seek implements the io.Seeker interface. +func (r *Reader) Seek(offset int64, whence int) (int64, error) { + var abs int64 + switch whence { + case 0: + abs = offset + case 1: + abs = int64(r.i) + offset + case 2: + abs = int64(len(r.s)) + offset + default: + return 0, errors.New("bytes: invalid whence") + } + if abs < 0 { + return 0, errors.New("bytes: negative position") + } + if abs >= 1<<31 { + return 0, errors.New("bytes: position out of range") + } + r.i = int(abs) + return abs, nil +} + +// NewReader returns a new Reader reading from b. +func NewReader(b []byte) *Reader { return &Reader{b, 0, -1} } diff --git a/src/pkg/bytes/reader_test.go b/src/pkg/bytes/reader_test.go new file mode 100644 index 000000000..2e4b1f26e --- /dev/null +++ b/src/pkg/bytes/reader_test.go @@ -0,0 +1,88 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bytes_test + +import ( + . "bytes" + "fmt" + "io" + "os" + "testing" +) + +func TestReader(t *testing.T) { + r := NewReader([]byte("0123456789")) + tests := []struct { + off int64 + seek int + n int + want string + wantpos int64 + seekerr string + }{ + {seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"}, + {seek: os.SEEK_SET, off: 1, n: 1, want: "1"}, + {seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"}, + {seek: os.SEEK_SET, off: -1, seekerr: "bytes: negative position"}, + {seek: os.SEEK_SET, off: 1<<31 - 1}, + {seek: os.SEEK_CUR, off: 1, seekerr: "bytes: position out of range"}, + {seek: os.SEEK_SET, n: 5, want: "01234"}, + {seek: os.SEEK_CUR, n: 5, want: "56789"}, + {seek: os.SEEK_END, off: -1, n: 1, wantpos: 9, want: "9"}, + } + + for i, tt := range tests { + pos, err := r.Seek(tt.off, tt.seek) + if err == nil && tt.seekerr != "" { + t.Errorf("%d. want seek error %q", i, tt.seekerr) + continue + } + if err != nil && err.Error() != tt.seekerr { + t.Errorf("%d. seek error = %q; want %q", i, err.Error(), tt.seekerr) + continue + } + if tt.wantpos != 0 && tt.wantpos != pos { + t.Errorf("%d. pos = %d, want %d", i, pos, tt.wantpos) + } + buf := make([]byte, tt.n) + n, err := r.Read(buf) + if err != nil { + t.Errorf("%d. read = %v", i, err) + continue + } + got := string(buf[:n]) + if got != tt.want { + t.Errorf("%d. got %q; want %q", i, got, tt.want) + } + } +} + +func TestReaderAt(t *testing.T) { + r := NewReader([]byte("0123456789")) + tests := []struct { + off int64 + n int + want string + wanterr interface{} + }{ + {0, 10, "0123456789", nil}, + {1, 10, "123456789", io.EOF}, + {1, 9, "123456789", nil}, + {11, 10, "", io.EOF}, + {0, 0, "", nil}, + {-1, 0, "", "bytes: invalid offset"}, + } + for i, tt := range tests { + b := make([]byte, tt.n) + rn, err := r.ReadAt(b, tt.off) + got := string(b[:rn]) + if got != tt.want { + t.Errorf("%d. got %q; want %q", i, got, tt.want) + } + if fmt.Sprintf("%v", err) != fmt.Sprintf("%v", tt.wanterr) { + t.Errorf("%d. got error = %v; want %v", i, err, tt.wanterr) + } + } +} diff --git a/src/pkg/cmath/Makefile b/src/pkg/cmath/Makefile deleted file mode 100644 index 486caace4..000000000 --- a/src/pkg/cmath/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../Make.inc - -TARG=cmath - -GOFILES=\ - abs.go\ - asin.go\ - conj.go\ - exp.go\ - isinf.go\ - isnan.go\ - log.go\ - phase.go\ - polar.go\ - pow.go\ - rect.go\ - sin.go\ - sqrt.go\ - tan.go\ - -include ../../Make.pkg diff --git a/src/pkg/cmath/abs.go b/src/pkg/cmath/abs.go deleted file mode 100644 index f3199cad5..000000000 --- a/src/pkg/cmath/abs.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package cmath provides basic constants and mathematical functions for -// complex numbers. -package cmath - -import "math" - -// Abs returns the absolute value (also called the modulus) of x. -func Abs(x complex128) float64 { return math.Hypot(real(x), imag(x)) } diff --git a/src/pkg/cmath/asin.go b/src/pkg/cmath/asin.go deleted file mode 100644 index d6a3ca480..000000000 --- a/src/pkg/cmath/asin.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// Cephes Math Library Release 2.8: June, 2000 -// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier -// -// The readme file at http://netlib.sandia.gov/cephes/ says: -// Some software in this archive may be from the book _Methods and -// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster -// International, 1989) or from the Cephes Mathematical Library, a -// commercial product. In either event, it is copyrighted by the author. -// What you see here may be used freely but it comes with no support or -// guarantee. -// -// The two known misprints in the book are repaired here in the -// source listings for the gamma function and the incomplete beta -// integral. -// -// Stephen L. Moshier -// moshier@na-net.ornl.gov - -// Complex circular arc sine -// -// DESCRIPTION: -// -// Inverse complex sine: -// 2 -// w = -i clog( iz + csqrt( 1 - z ) ). -// -// casin(z) = -i casinh(iz) -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 10100 2.1e-15 3.4e-16 -// IEEE -10,+10 30000 2.2e-14 2.7e-15 -// Larger relative error can be observed for z near zero. -// Also tested by csin(casin(z)) = z. - -// Asin returns the inverse sine of x. -func Asin(x complex128) complex128 { - if imag(x) == 0 { - if math.Fabs(real(x)) > 1 { - return complex(math.Pi/2, 0) // DOMAIN error - } - return complex(math.Asin(real(x)), 0) - } - ct := complex(-imag(x), real(x)) // i * x - xx := x * x - x1 := complex(1-real(xx), -imag(xx)) // 1 - x*x - x2 := Sqrt(x1) // x2 = sqrt(1 - x*x) - w := Log(ct + x2) - return complex(imag(w), -real(w)) // -i * w -} - -// Asinh returns the inverse hyperbolic sine of x. -func Asinh(x complex128) complex128 { - // TODO check range - if imag(x) == 0 { - if math.Fabs(real(x)) > 1 { - return complex(math.Pi/2, 0) // DOMAIN error - } - return complex(math.Asinh(real(x)), 0) - } - xx := x * x - x1 := complex(1+real(xx), imag(xx)) // 1 + x*x - return Log(x + Sqrt(x1)) // log(x + sqrt(1 + x*x)) -} - -// Complex circular arc cosine -// -// DESCRIPTION: -// -// w = arccos z = PI/2 - arcsin z. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 5200 1.6e-15 2.8e-16 -// IEEE -10,+10 30000 1.8e-14 2.2e-15 - -// Acos returns the inverse cosine of x. -func Acos(x complex128) complex128 { - w := Asin(x) - return complex(math.Pi/2-real(w), -imag(w)) -} - -// Acosh returns the inverse hyperbolic cosine of x. -func Acosh(x complex128) complex128 { - w := Acos(x) - if imag(w) <= 0 { - return complex(-imag(w), real(w)) // i * w - } - return complex(imag(w), -real(w)) // -i * w -} - -// Complex circular arc tangent -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// 1 ( 2x ) -// Re w = - arctan(-----------) + k PI -// 2 ( 2 2) -// (1 - x - y ) -// -// ( 2 2) -// 1 (x + (y+1) ) -// Im w = - log(------------) -// 4 ( 2 2) -// (x + (y-1) ) -// -// Where k is an arbitrary integer. -// -// catan(z) = -i catanh(iz). -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 5900 1.3e-16 7.8e-18 -// IEEE -10,+10 30000 2.3e-15 8.5e-17 -// The check catan( ctan(z) ) = z, with |x| and |y| < PI/2, -// had peak relative error 1.5e-16, rms relative error -// 2.9e-17. See also clog(). - -// Atan returns the inverse tangent of x. -func Atan(x complex128) complex128 { - if real(x) == 0 && imag(x) > 1 { - return NaN() - } - - x2 := real(x) * real(x) - a := 1 - x2 - imag(x)*imag(x) - if a == 0 { - return NaN() - } - t := 0.5 * math.Atan2(2*real(x), a) - w := reducePi(t) - - t = imag(x) - 1 - b := x2 + t*t - if b == 0 { - return NaN() - } - t = imag(x) + 1 - c := (x2 + t*t) / b - return complex(w, 0.25*math.Log(c)) -} - -// Atanh returns the inverse hyperbolic tangent of x. -func Atanh(x complex128) complex128 { - z := complex(-imag(x), real(x)) // z = i * x - z = Atan(z) - return complex(imag(z), -real(z)) // z = -i * z -} diff --git a/src/pkg/cmath/cmath_test.go b/src/pkg/cmath/cmath_test.go deleted file mode 100644 index 6a595b0a6..000000000 --- a/src/pkg/cmath/cmath_test.go +++ /dev/null @@ -1,853 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import ( - "math" - "testing" -) - -var vc26 = []complex128{ - (4.97901192488367350108546816 + 7.73887247457810456552351752i), - (7.73887247457810456552351752 - 0.27688005719200159404635997i), - (-0.27688005719200159404635997 - 5.01060361827107492160848778i), - (-5.01060361827107492160848778 + 9.63629370719841737980004837i), - (9.63629370719841737980004837 + 2.92637723924396464525443662i), - (2.92637723924396464525443662 + 5.22908343145930665230025625i), - (5.22908343145930665230025625 + 2.72793991043601025126008608i), - (2.72793991043601025126008608 + 1.82530809168085506044576505i), - (1.82530809168085506044576505 - 8.68592476857560136238589621i), - (-8.68592476857560136238589621 + 4.97901192488367350108546816i), -} -var vc = []complex128{ - (4.9790119248836735e+00 + 7.7388724745781045e+00i), - (7.7388724745781045e+00 - 2.7688005719200159e-01i), - (-2.7688005719200159e-01 - 5.0106036182710749e+00i), - (-5.0106036182710749e+00 + 9.6362937071984173e+00i), - (9.6362937071984173e+00 + 2.9263772392439646e+00i), - (2.9263772392439646e+00 + 5.2290834314593066e+00i), - (5.2290834314593066e+00 + 2.7279399104360102e+00i), - (2.7279399104360102e+00 + 1.8253080916808550e+00i), - (1.8253080916808550e+00 - 8.6859247685756013e+00i), - (-8.6859247685756013e+00 + 4.9790119248836735e+00i), -} - -// The expected results below were computed by the high precision calculators -// at http://keisan.casio.com/. More exact input values (array vc[], above) -// were obtained by printing them with "%.26f". The answers were calculated -// to 26 digits (by using the "Digit number" drop-down control of each -// calculator). - -var abs = []float64{ - 9.2022120669932650313380972e+00, - 7.7438239742296106616261394e+00, - 5.0182478202557746902556648e+00, - 1.0861137372799545160704002e+01, - 1.0070841084922199607011905e+01, - 5.9922447613166942183705192e+00, - 5.8978784056736762299945176e+00, - 3.2822866700678709020367184e+00, - 8.8756430028990417290744307e+00, - 1.0011785496777731986390856e+01, -} - -var acos = []complex128{ - (1.0017679804707456328694569 - 2.9138232718554953784519807i), - (0.03606427612041407369636057 + 2.7358584434576260925091256i), - (1.6249365462333796703711823 + 2.3159537454335901187730929i), - (2.0485650849650740120660391 - 3.0795576791204117911123886i), - (0.29621132089073067282488147 - 3.0007392508200622519398814i), - (1.0664555914934156601503632 - 2.4872865024796011364747111i), - (0.48681307452231387690013905 - 2.463655912283054555225301i), - (0.6116977071277574248407752 - 1.8734458851737055262693056i), - (1.3649311280370181331184214 + 2.8793528632328795424123832i), - (2.6189310485682988308904501 - 2.9956543302898767795858704i), -} -var acosh = []complex128{ - (2.9138232718554953784519807 + 1.0017679804707456328694569i), - (2.7358584434576260925091256 - 0.03606427612041407369636057i), - (2.3159537454335901187730929 - 1.6249365462333796703711823i), - (3.0795576791204117911123886 + 2.0485650849650740120660391i), - (3.0007392508200622519398814 + 0.29621132089073067282488147i), - (2.4872865024796011364747111 + 1.0664555914934156601503632i), - (2.463655912283054555225301 + 0.48681307452231387690013905i), - (1.8734458851737055262693056 + 0.6116977071277574248407752i), - (2.8793528632328795424123832 - 1.3649311280370181331184214i), - (2.9956543302898767795858704 + 2.6189310485682988308904501i), -} -var asin = []complex128{ - (0.56902834632415098636186476 + 2.9138232718554953784519807i), - (1.5347320506744825455349611 - 2.7358584434576260925091256i), - (-0.054140219438483051139860579 - 2.3159537454335901187730929i), - (-0.47776875817017739283471738 + 3.0795576791204117911123886i), - (1.2745850059041659464064402 + 3.0007392508200622519398814i), - (0.50434073530148095908095852 + 2.4872865024796011364747111i), - (1.0839832522725827423311826 + 2.463655912283054555225301i), - (0.9590986196671391943905465 + 1.8734458851737055262693056i), - (0.20586519875787848611290031 - 2.8793528632328795424123832i), - (-1.0481347217734022116591284 + 2.9956543302898767795858704i), -} -var asinh = []complex128{ - (2.9113760469415295679342185 + 0.99639459545704326759805893i), - (2.7441755423994259061579029 - 0.035468308789000500601119392i), - (-2.2962136462520690506126678 - 1.5144663565690151885726707i), - (-3.0771233459295725965402455 + 1.0895577967194013849422294i), - (3.0048366100923647417557027 + 0.29346979169819220036454168i), - (2.4800059370795363157364643 + 1.0545868606049165710424232i), - (2.4718773838309585611141821 + 0.47502344364250803363708842i), - (1.8910743588080159144378396 + 0.56882925572563602341139174i), - (2.8735426423367341878069406 - 1.362376149648891420997548i), - (-2.9981750586172477217567878 + 0.5183571985225367505624207i), -} -var atan = []complex128{ - (1.5115747079332741358607654 + 0.091324403603954494382276776i), - (1.4424504323482602560806727 - 0.0045416132642803911503770933i), - (-1.5593488703630532674484026 - 0.20163295409248362456446431i), - (-1.5280619472445889867794105 + 0.081721556230672003746956324i), - (1.4759909163240799678221039 + 0.028602969320691644358773586i), - (1.4877353772046548932715555 + 0.14566877153207281663773599i), - (1.4206983927779191889826 + 0.076830486127880702249439993i), - (1.3162236060498933364869556 + 0.16031313000467530644933363i), - (1.5473450684303703578810093 - 0.11064907507939082484935782i), - (-1.4841462340185253987375812 + 0.049341850305024399493142411i), -} -var atanh = []complex128{ - (0.058375027938968509064640438 + 1.4793488495105334458167782i), - (0.12977343497790381229915667 - 1.5661009410463561327262499i), - (-0.010576456067347252072200088 - 1.3743698658402284549750563i), - (-0.042218595678688358882784918 + 1.4891433968166405606692604i), - (0.095218997991316722061828397 + 1.5416884098777110330499698i), - (0.079965459366890323857556487 + 1.4252510353873192700350435i), - (0.15051245471980726221708301 + 1.4907432533016303804884461i), - (0.25082072933993987714470373 + 1.392057665392187516442986i), - (0.022896108815797135846276662 - 1.4609224989282864208963021i), - (-0.08665624101841876130537396 + 1.5207902036935093480142159i), -} -var conj = []complex128{ - (4.9790119248836735e+00 - 7.7388724745781045e+00i), - (7.7388724745781045e+00 + 2.7688005719200159e-01i), - (-2.7688005719200159e-01 + 5.0106036182710749e+00i), - (-5.0106036182710749e+00 - 9.6362937071984173e+00i), - (9.6362937071984173e+00 - 2.9263772392439646e+00i), - (2.9263772392439646e+00 - 5.2290834314593066e+00i), - (5.2290834314593066e+00 - 2.7279399104360102e+00i), - (2.7279399104360102e+00 - 1.8253080916808550e+00i), - (1.8253080916808550e+00 + 8.6859247685756013e+00i), - (-8.6859247685756013e+00 - 4.9790119248836735e+00i), -} -var cos = []complex128{ - (3.024540920601483938336569e+02 + 1.1073797572517071650045357e+03i), - (1.192858682649064973252758e-01 + 2.7857554122333065540970207e-01i), - (7.2144394304528306603857962e+01 - 2.0500129667076044169954205e+01i), - (2.24921952538403984190541e+03 - 7.317363745602773587049329e+03i), - (-9.148222970032421760015498e+00 + 1.953124661113563541862227e+00i), - (-9.116081175857732248227078e+01 - 1.992669213569952232487371e+01i), - (3.795639179042704640002918e+00 + 6.623513350981458399309662e+00i), - (-2.9144840732498869560679084e+00 - 1.214620271628002917638748e+00i), - (-7.45123482501299743872481e+02 + 2.8641692314488080814066734e+03i), - (-5.371977967039319076416747e+01 + 4.893348341339375830564624e+01i), -} -var cosh = []complex128{ - (8.34638383523018249366948e+00 + 7.2181057886425846415112064e+01i), - (1.10421967379919366952251e+03 - 3.1379638689277575379469861e+02i), - (3.051485206773701584738512e-01 - 2.6805384730105297848044485e-01i), - (-7.33294728684187933370938e+01 + 1.574445942284918251038144e+01i), - (-7.478643293945957535757355e+03 + 1.6348382209913353929473321e+03i), - (4.622316522966235701630926e+00 - 8.088695185566375256093098e+00i), - (-8.544333183278877406197712e+01 + 3.7505836120128166455231717e+01i), - (-1.934457815021493925115198e+00 + 7.3725859611767228178358673e+00i), - (-2.352958770061749348353548e+00 - 2.034982010440878358915409e+00i), - (7.79756457532134748165069e+02 + 2.8549350716819176560377717e+03i), -} -var exp = []complex128{ - (1.669197736864670815125146e+01 + 1.4436895109507663689174096e+02i), - (2.2084389286252583447276212e+03 - 6.2759289284909211238261917e+02i), - (2.227538273122775173434327e-01 + 7.2468284028334191250470034e-01i), - (-6.5182985958153548997881627e-03 - 1.39965837915193860879044e-03i), - (-1.4957286524084015746110777e+04 + 3.269676455931135688988042e+03i), - (9.218158701983105935659273e+00 - 1.6223985291084956009304582e+01i), - (-1.7088175716853040841444505e+02 + 7.501382609870410713795546e+01i), - (-3.852461315830959613132505e+00 + 1.4808420423156073221970892e+01i), - (-4.586775503301407379786695e+00 - 4.178501081246873415144744e+00i), - (4.451337963005453491095747e-05 - 1.62977574205442915935263e-04i), -} -var log = []complex128{ - (2.2194438972179194425697051e+00 + 9.9909115046919291062461269e-01i), - (2.0468956191154167256337289e+00 - 3.5762575021856971295156489e-02i), - (1.6130808329853860438751244e+00 - 1.6259990074019058442232221e+00i), - (2.3851910394823008710032651e+00 + 2.0502936359659111755031062e+00i), - (2.3096442270679923004800651e+00 + 2.9483213155446756211881774e-01i), - (1.7904660933974656106951860e+00 + 1.0605860367252556281902109e+00i), - (1.7745926939841751666177512e+00 + 4.8084556083358307819310911e-01i), - (1.1885403350045342425648780e+00 + 5.8969634164776659423195222e-01i), - (2.1833107837679082586772505e+00 - 1.3636647724582455028314573e+00i), - (2.3037629487273259170991671e+00 + 2.6210913895386013290915234e+00i), -} -var log10 = []complex128{ - (9.6389223745559042474184943e-01 + 4.338997735671419492599631e-01i), - (8.8895547241376579493490892e-01 - 1.5531488990643548254864806e-02i), - (7.0055210462945412305244578e-01 - 7.0616239649481243222248404e-01i), - (1.0358753067322445311676952e+00 + 8.9043121238134980156490909e-01i), - (1.003065742975330237172029e+00 + 1.2804396782187887479857811e-01i), - (7.7758954439739162532085157e-01 + 4.6060666333341810869055108e-01i), - (7.7069581462315327037689152e-01 + 2.0882857371769952195512475e-01i), - (5.1617650901191156135137239e-01 + 2.5610186717615977620363299e-01i), - (9.4819982567026639742663212e-01 - 5.9223208584446952284914289e-01i), - (1.0005115362454417135973429e+00 + 1.1383255270407412817250921e+00i), -} - -type ff struct { - r, theta float64 -} - -var polar = []ff{ - {9.2022120669932650313380972e+00, 9.9909115046919291062461269e-01}, - {7.7438239742296106616261394e+00, -3.5762575021856971295156489e-02}, - {5.0182478202557746902556648e+00, -1.6259990074019058442232221e+00}, - {1.0861137372799545160704002e+01, 2.0502936359659111755031062e+00}, - {1.0070841084922199607011905e+01, 2.9483213155446756211881774e-01}, - {5.9922447613166942183705192e+00, 1.0605860367252556281902109e+00}, - {5.8978784056736762299945176e+00, 4.8084556083358307819310911e-01}, - {3.2822866700678709020367184e+00, 5.8969634164776659423195222e-01}, - {8.8756430028990417290744307e+00, -1.3636647724582455028314573e+00}, - {1.0011785496777731986390856e+01, 2.6210913895386013290915234e+00}, -} -var pow = []complex128{ - (-2.499956739197529585028819e+00 + 1.759751724335650228957144e+00i), - (7.357094338218116311191939e+04 - 5.089973412479151648145882e+04i), - (1.320777296067768517259592e+01 - 3.165621914333901498921986e+01i), - (-3.123287828297300934072149e-07 - 1.9849567521490553032502223E-7i), - (8.0622651468477229614813e+04 - 7.80028727944573092944363e+04i), - (-1.0268824572103165858577141e+00 - 4.716844738244989776610672e-01i), - (-4.35953819012244175753187e+01 + 2.2036445974645306917648585e+02i), - (8.3556092283250594950239e-01 - 1.2261571947167240272593282e+01i), - (1.582292972120769306069625e+03 + 1.273564263524278244782512e+04i), - (6.592208301642122149025369e-08 + 2.584887236651661903526389e-08i), -} -var sin = []complex128{ - (-1.1073801774240233539648544e+03 + 3.024539773002502192425231e+02i), - (1.0317037521400759359744682e+00 - 3.2208979799929570242818e-02i), - (-2.0501952097271429804261058e+01 - 7.2137981348240798841800967e+01i), - (7.3173638080346338642193078e+03 + 2.249219506193664342566248e+03i), - (-1.964375633631808177565226e+00 - 9.0958264713870404464159683e+00i), - (1.992783647158514838337674e+01 - 9.11555769410191350416942e+01i), - (-6.680335650741921444300349e+00 + 3.763353833142432513086117e+00i), - (1.2794028166657459148245993e+00 - 2.7669092099795781155109602e+00i), - (2.8641693949535259594188879e+03 + 7.451234399649871202841615e+02i), - (-4.893811726244659135553033e+01 - 5.371469305562194635957655e+01i), -} -var sinh = []complex128{ - (8.34559353341652565758198e+00 + 7.2187893208650790476628899e+01i), - (1.1042192548260646752051112e+03 - 3.1379650595631635858792056e+02i), - (-8.239469336509264113041849e-02 + 9.9273668758439489098514519e-01i), - (7.332295456982297798219401e+01 - 1.574585908122833444899023e+01i), - (-7.4786432301380582103534216e+03 + 1.63483823493980029604071e+03i), - (4.595842179016870234028347e+00 - 8.135290105518580753211484e+00i), - (-8.543842533574163435246793e+01 + 3.750798997857594068272375e+01i), - (-1.918003500809465688017307e+00 + 7.4358344619793504041350251e+00i), - (-2.233816733239658031433147e+00 - 2.143519070805995056229335e+00i), - (-7.797564130187551181105341e+02 - 2.8549352346594918614806877e+03i), -} -var sqrt = []complex128{ - (2.6628203086086130543813948e+00 + 1.4531345674282185229796902e+00i), - (2.7823278427251986247149295e+00 - 4.9756907317005224529115567e-02i), - (1.5397025302089642757361015e+00 - 1.6271336573016637535695727e+00i), - (1.7103411581506875260277898e+00 + 2.8170677122737589676157029e+00i), - (3.1390392472953103383607947e+00 + 4.6612625849858653248980849e-01i), - (2.1117080764822417640789287e+00 + 1.2381170223514273234967850e+00i), - (2.3587032281672256703926939e+00 + 5.7827111903257349935720172e-01i), - (1.7335262588873410476661577e+00 + 5.2647258220721269141550382e-01i), - (2.3131094974708716531499282e+00 - 1.8775429304303785570775490e+00i), - (8.1420535745048086240947359e-01 + 3.0575897587277248522656113e+00i), -} -var tan = []complex128{ - (-1.928757919086441129134525e-07 + 1.0000003267499169073251826e+00i), - (1.242412685364183792138948e+00 - 3.17149693883133370106696e+00i), - (-4.6745126251587795225571826e-05 - 9.9992439225263959286114298e-01i), - (4.792363401193648192887116e-09 + 1.0000000070589333451557723e+00i), - (2.345740824080089140287315e-03 + 9.947733046570988661022763e-01i), - (-2.396030789494815566088809e-05 + 9.9994781345418591429826779e-01i), - (-7.370204836644931340905303e-03 + 1.0043553413417138987717748e+00i), - (-3.691803847992048527007457e-02 + 9.6475071993469548066328894e-01i), - (-2.781955256713729368401878e-08 - 1.000000049848910609006646e+00i), - (9.4281590064030478879791249e-05 + 9.9999119340863718183758545e-01i), -} -var tanh = []complex128{ - (1.0000921981225144748819918e+00 + 2.160986245871518020231507e-05i), - (9.9999967727531993209562591e-01 - 1.9953763222959658873657676e-07i), - (-1.765485739548037260789686e+00 + 1.7024216325552852445168471e+00i), - (-9.999189442732736452807108e-01 + 3.64906070494473701938098e-05i), - (9.9999999224622333738729767e-01 - 3.560088949517914774813046e-09i), - (1.0029324933367326862499343e+00 - 4.948790309797102353137528e-03i), - (9.9996113064788012488693567e-01 - 4.226995742097032481451259e-05i), - (1.0074784189316340029873945e+00 - 4.194050814891697808029407e-03i), - (9.9385534229718327109131502e-01 + 5.144217985914355502713437e-02i), - (-1.0000000491604982429364892e+00 - 2.901873195374433112227349e-08i), -} - -// special cases -var vcAbsSC = []complex128{ - NaN(), -} -var absSC = []float64{ - math.NaN(), -} -var vcAcosSC = []complex128{ - NaN(), -} -var acosSC = []complex128{ - NaN(), -} -var vcAcoshSC = []complex128{ - NaN(), -} -var acoshSC = []complex128{ - NaN(), -} -var vcAsinSC = []complex128{ - NaN(), -} -var asinSC = []complex128{ - NaN(), -} -var vcAsinhSC = []complex128{ - NaN(), -} -var asinhSC = []complex128{ - NaN(), -} -var vcAtanSC = []complex128{ - NaN(), -} -var atanSC = []complex128{ - NaN(), -} -var vcAtanhSC = []complex128{ - NaN(), -} -var atanhSC = []complex128{ - NaN(), -} -var vcConjSC = []complex128{ - NaN(), -} -var conjSC = []complex128{ - NaN(), -} -var vcCosSC = []complex128{ - NaN(), -} -var cosSC = []complex128{ - NaN(), -} -var vcCoshSC = []complex128{ - NaN(), -} -var coshSC = []complex128{ - NaN(), -} -var vcExpSC = []complex128{ - NaN(), -} -var expSC = []complex128{ - NaN(), -} -var vcIsNaNSC = []complex128{ - complex(math.Inf(-1), math.Inf(-1)), - complex(math.Inf(-1), math.NaN()), - complex(math.NaN(), math.Inf(-1)), - complex(0, math.NaN()), - complex(math.NaN(), 0), - complex(math.Inf(1), math.Inf(1)), - complex(math.Inf(1), math.NaN()), - complex(math.NaN(), math.Inf(1)), - complex(math.NaN(), math.NaN()), -} -var isNaNSC = []bool{ - false, - false, - false, - true, - true, - false, - false, - false, - true, -} -var vcLogSC = []complex128{ - NaN(), -} -var logSC = []complex128{ - NaN(), -} -var vcLog10SC = []complex128{ - NaN(), -} -var log10SC = []complex128{ - NaN(), -} -var vcPolarSC = []complex128{ - NaN(), -} -var polarSC = []ff{ - {math.NaN(), math.NaN()}, -} -var vcPowSC = [][2]complex128{ - {NaN(), NaN()}, -} -var powSC = []complex128{ - NaN(), -} -var vcSinSC = []complex128{ - NaN(), -} -var sinSC = []complex128{ - NaN(), -} -var vcSinhSC = []complex128{ - NaN(), -} -var sinhSC = []complex128{ - NaN(), -} -var vcSqrtSC = []complex128{ - NaN(), -} -var sqrtSC = []complex128{ - NaN(), -} -var vcTanSC = []complex128{ - NaN(), -} -var tanSC = []complex128{ - NaN(), -} -var vcTanhSC = []complex128{ - NaN(), -} -var tanhSC = []complex128{ - NaN(), -} - -// functions borrowed from pkg/math/all_test.go -func tolerance(a, b, e float64) bool { - d := a - b - if d < 0 { - d = -d - } - - if a != 0 { - e = e * a - if e < 0 { - e = -e - } - } - return d < e -} -func soclose(a, b, e float64) bool { return tolerance(a, b, e) } -func veryclose(a, b float64) bool { return tolerance(a, b, 4e-16) } -func alike(a, b float64) bool { - switch { - case a != a && b != b: // math.IsNaN(a) && math.IsNaN(b): - return true - case a == b: - return math.Signbit(a) == math.Signbit(b) - } - return false -} - -func cTolerance(a, b complex128, e float64) bool { - d := Abs(a - b) - if a != 0 { - e = e * Abs(a) - if e < 0 { - e = -e - } - } - return d < e -} -func cSoclose(a, b complex128, e float64) bool { return cTolerance(a, b, e) } -func cVeryclose(a, b complex128) bool { return cTolerance(a, b, 4e-16) } -func cAlike(a, b complex128) bool { - switch { - case IsNaN(a) && IsNaN(b): - return true - case a == b: - return math.Signbit(real(a)) == math.Signbit(real(b)) && math.Signbit(imag(a)) == math.Signbit(imag(b)) - } - return false -} - -func TestAbs(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Abs(vc[i]); !veryclose(abs[i], f) { - t.Errorf("Abs(%g) = %g, want %g", vc[i], f, abs[i]) - } - } - for i := 0; i < len(vcAbsSC); i++ { - if f := Abs(vcAbsSC[i]); !alike(absSC[i], f) { - t.Errorf("Abs(%g) = %g, want %g", vcAbsSC[i], f, absSC[i]) - } - } -} -func TestAcos(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Acos(vc[i]); !cSoclose(acos[i], f, 1e-14) { - t.Errorf("Acos(%g) = %g, want %g", vc[i], f, acos[i]) - } - } - for i := 0; i < len(vcAcosSC); i++ { - if f := Acos(vcAcosSC[i]); !cAlike(acosSC[i], f) { - t.Errorf("Acos(%g) = %g, want %g", vcAcosSC[i], f, acosSC[i]) - } - } -} -func TestAcosh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Acosh(vc[i]); !cSoclose(acosh[i], f, 1e-14) { - t.Errorf("Acosh(%g) = %g, want %g", vc[i], f, acosh[i]) - } - } - for i := 0; i < len(vcAcoshSC); i++ { - if f := Acosh(vcAcoshSC[i]); !cAlike(acoshSC[i], f) { - t.Errorf("Acosh(%g) = %g, want %g", vcAcoshSC[i], f, acoshSC[i]) - } - } -} -func TestAsin(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Asin(vc[i]); !cSoclose(asin[i], f, 1e-14) { - t.Errorf("Asin(%g) = %g, want %g", vc[i], f, asin[i]) - } - } - for i := 0; i < len(vcAsinSC); i++ { - if f := Asin(vcAsinSC[i]); !cAlike(asinSC[i], f) { - t.Errorf("Asin(%g) = %g, want %g", vcAsinSC[i], f, asinSC[i]) - } - } -} -func TestAsinh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Asinh(vc[i]); !cSoclose(asinh[i], f, 4e-15) { - t.Errorf("Asinh(%g) = %g, want %g", vc[i], f, asinh[i]) - } - } - for i := 0; i < len(vcAsinhSC); i++ { - if f := Asinh(vcAsinhSC[i]); !cAlike(asinhSC[i], f) { - t.Errorf("Asinh(%g) = %g, want %g", vcAsinhSC[i], f, asinhSC[i]) - } - } -} -func TestAtan(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Atan(vc[i]); !cVeryclose(atan[i], f) { - t.Errorf("Atan(%g) = %g, want %g", vc[i], f, atan[i]) - } - } - for i := 0; i < len(vcAtanSC); i++ { - if f := Atan(vcAtanSC[i]); !cAlike(atanSC[i], f) { - t.Errorf("Atan(%g) = %g, want %g", vcAtanSC[i], f, atanSC[i]) - } - } -} -func TestAtanh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Atanh(vc[i]); !cVeryclose(atanh[i], f) { - t.Errorf("Atanh(%g) = %g, want %g", vc[i], f, atanh[i]) - } - } - for i := 0; i < len(vcAtanhSC); i++ { - if f := Atanh(vcAtanhSC[i]); !cAlike(atanhSC[i], f) { - t.Errorf("Atanh(%g) = %g, want %g", vcAtanhSC[i], f, atanhSC[i]) - } - } -} -func TestConj(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Conj(vc[i]); !cVeryclose(conj[i], f) { - t.Errorf("Conj(%g) = %g, want %g", vc[i], f, conj[i]) - } - } - for i := 0; i < len(vcConjSC); i++ { - if f := Conj(vcConjSC[i]); !cAlike(conjSC[i], f) { - t.Errorf("Conj(%g) = %g, want %g", vcConjSC[i], f, conjSC[i]) - } - } -} -func TestCos(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Cos(vc[i]); !cSoclose(cos[i], f, 3e-15) { - t.Errorf("Cos(%g) = %g, want %g", vc[i], f, cos[i]) - } - } - for i := 0; i < len(vcCosSC); i++ { - if f := Cos(vcCosSC[i]); !cAlike(cosSC[i], f) { - t.Errorf("Cos(%g) = %g, want %g", vcCosSC[i], f, cosSC[i]) - } - } -} -func TestCosh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Cosh(vc[i]); !cSoclose(cosh[i], f, 2e-15) { - t.Errorf("Cosh(%g) = %g, want %g", vc[i], f, cosh[i]) - } - } - for i := 0; i < len(vcCoshSC); i++ { - if f := Cosh(vcCoshSC[i]); !cAlike(coshSC[i], f) { - t.Errorf("Cosh(%g) = %g, want %g", vcCoshSC[i], f, coshSC[i]) - } - } -} -func TestExp(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Exp(vc[i]); !cSoclose(exp[i], f, 1e-15) { - t.Errorf("Exp(%g) = %g, want %g", vc[i], f, exp[i]) - } - } - for i := 0; i < len(vcExpSC); i++ { - if f := Exp(vcExpSC[i]); !cAlike(expSC[i], f) { - t.Errorf("Exp(%g) = %g, want %g", vcExpSC[i], f, expSC[i]) - } - } -} -func TestIsNaN(t *testing.T) { - for i := 0; i < len(vcIsNaNSC); i++ { - if f := IsNaN(vcIsNaNSC[i]); isNaNSC[i] != f { - t.Errorf("IsNaN(%v) = %v, want %v", vcIsNaNSC[i], f, isNaNSC[i]) - } - } -} -func TestLog(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Log(vc[i]); !cVeryclose(log[i], f) { - t.Errorf("Log(%g) = %g, want %g", vc[i], f, log[i]) - } - } - for i := 0; i < len(vcLogSC); i++ { - if f := Log(vcLogSC[i]); !cAlike(logSC[i], f) { - t.Errorf("Log(%g) = %g, want %g", vcLogSC[i], f, logSC[i]) - } - } -} -func TestLog10(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Log10(vc[i]); !cVeryclose(log10[i], f) { - t.Errorf("Log10(%g) = %g, want %g", vc[i], f, log10[i]) - } - } - for i := 0; i < len(vcLog10SC); i++ { - if f := Log10(vcLog10SC[i]); !cAlike(log10SC[i], f) { - t.Errorf("Log10(%g) = %g, want %g", vcLog10SC[i], f, log10SC[i]) - } - } -} -func TestPolar(t *testing.T) { - for i := 0; i < len(vc); i++ { - if r, theta := Polar(vc[i]); !veryclose(polar[i].r, r) && !veryclose(polar[i].theta, theta) { - t.Errorf("Polar(%g) = %g, %g want %g, %g", vc[i], r, theta, polar[i].r, polar[i].theta) - } - } - for i := 0; i < len(vcPolarSC); i++ { - if r, theta := Polar(vcPolarSC[i]); !alike(polarSC[i].r, r) && !alike(polarSC[i].theta, theta) { - t.Errorf("Polar(%g) = %g, %g, want %g, %g", vcPolarSC[i], r, theta, polarSC[i].r, polarSC[i].theta) - } - } -} -func TestPow(t *testing.T) { - var a = complex(3.0, 3.0) - for i := 0; i < len(vc); i++ { - if f := Pow(a, vc[i]); !cSoclose(pow[i], f, 4e-15) { - t.Errorf("Pow(%g, %g) = %g, want %g", a, vc[i], f, pow[i]) - } - } - for i := 0; i < len(vcPowSC); i++ { - if f := Pow(vcPowSC[i][0], vcPowSC[i][0]); !cAlike(powSC[i], f) { - t.Errorf("Pow(%g, %g) = %g, want %g", vcPowSC[i][0], vcPowSC[i][0], f, powSC[i]) - } - } -} -func TestRect(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Rect(polar[i].r, polar[i].theta); !cVeryclose(vc[i], f) { - t.Errorf("Rect(%g, %g) = %g want %g", polar[i].r, polar[i].theta, f, vc[i]) - } - } - for i := 0; i < len(vcPolarSC); i++ { - if f := Rect(polarSC[i].r, polarSC[i].theta); !cAlike(vcPolarSC[i], f) { - t.Errorf("Rect(%g, %g) = %g, want %g", polarSC[i].r, polarSC[i].theta, f, vcPolarSC[i]) - } - } -} -func TestSin(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Sin(vc[i]); !cSoclose(sin[i], f, 2e-15) { - t.Errorf("Sin(%g) = %g, want %g", vc[i], f, sin[i]) - } - } - for i := 0; i < len(vcSinSC); i++ { - if f := Sin(vcSinSC[i]); !cAlike(sinSC[i], f) { - t.Errorf("Sin(%g) = %g, want %g", vcSinSC[i], f, sinSC[i]) - } - } -} -func TestSinh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Sinh(vc[i]); !cSoclose(sinh[i], f, 2e-15) { - t.Errorf("Sinh(%g) = %g, want %g", vc[i], f, sinh[i]) - } - } - for i := 0; i < len(vcSinhSC); i++ { - if f := Sinh(vcSinhSC[i]); !cAlike(sinhSC[i], f) { - t.Errorf("Sinh(%g) = %g, want %g", vcSinhSC[i], f, sinhSC[i]) - } - } -} -func TestSqrt(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Sqrt(vc[i]); !cVeryclose(sqrt[i], f) { - t.Errorf("Sqrt(%g) = %g, want %g", vc[i], f, sqrt[i]) - } - } - for i := 0; i < len(vcSqrtSC); i++ { - if f := Sqrt(vcSqrtSC[i]); !cAlike(sqrtSC[i], f) { - t.Errorf("Sqrt(%g) = %g, want %g", vcSqrtSC[i], f, sqrtSC[i]) - } - } -} -func TestTan(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Tan(vc[i]); !cSoclose(tan[i], f, 3e-15) { - t.Errorf("Tan(%g) = %g, want %g", vc[i], f, tan[i]) - } - } - for i := 0; i < len(vcTanSC); i++ { - if f := Tan(vcTanSC[i]); !cAlike(tanSC[i], f) { - t.Errorf("Tan(%g) = %g, want %g", vcTanSC[i], f, tanSC[i]) - } - } -} -func TestTanh(t *testing.T) { - for i := 0; i < len(vc); i++ { - if f := Tanh(vc[i]); !cSoclose(tanh[i], f, 2e-15) { - t.Errorf("Tanh(%g) = %g, want %g", vc[i], f, tanh[i]) - } - } - for i := 0; i < len(vcTanhSC); i++ { - if f := Tanh(vcTanhSC[i]); !cAlike(tanhSC[i], f) { - t.Errorf("Tanh(%g) = %g, want %g", vcTanhSC[i], f, tanhSC[i]) - } - } -} - -func BenchmarkAbs(b *testing.B) { - for i := 0; i < b.N; i++ { - Abs(complex(2.5, 3.5)) - } -} -func BenchmarkAcos(b *testing.B) { - for i := 0; i < b.N; i++ { - Acos(complex(2.5, 3.5)) - } -} -func BenchmarkAcosh(b *testing.B) { - for i := 0; i < b.N; i++ { - Acosh(complex(2.5, 3.5)) - } -} -func BenchmarkAsin(b *testing.B) { - for i := 0; i < b.N; i++ { - Asin(complex(2.5, 3.5)) - } -} -func BenchmarkAsinh(b *testing.B) { - for i := 0; i < b.N; i++ { - Asinh(complex(2.5, 3.5)) - } -} -func BenchmarkAtan(b *testing.B) { - for i := 0; i < b.N; i++ { - Atan(complex(2.5, 3.5)) - } -} -func BenchmarkAtanh(b *testing.B) { - for i := 0; i < b.N; i++ { - Atanh(complex(2.5, 3.5)) - } -} -func BenchmarkConj(b *testing.B) { - for i := 0; i < b.N; i++ { - Conj(complex(2.5, 3.5)) - } -} -func BenchmarkCos(b *testing.B) { - for i := 0; i < b.N; i++ { - Cos(complex(2.5, 3.5)) - } -} -func BenchmarkCosh(b *testing.B) { - for i := 0; i < b.N; i++ { - Cosh(complex(2.5, 3.5)) - } -} -func BenchmarkExp(b *testing.B) { - for i := 0; i < b.N; i++ { - Exp(complex(2.5, 3.5)) - } -} -func BenchmarkLog(b *testing.B) { - for i := 0; i < b.N; i++ { - Log(complex(2.5, 3.5)) - } -} -func BenchmarkLog10(b *testing.B) { - for i := 0; i < b.N; i++ { - Log10(complex(2.5, 3.5)) - } -} -func BenchmarkPhase(b *testing.B) { - for i := 0; i < b.N; i++ { - Phase(complex(2.5, 3.5)) - } -} -func BenchmarkPolar(b *testing.B) { - for i := 0; i < b.N; i++ { - Polar(complex(2.5, 3.5)) - } -} -func BenchmarkPow(b *testing.B) { - for i := 0; i < b.N; i++ { - Pow(complex(2.5, 3.5), complex(2.5, 3.5)) - } -} -func BenchmarkRect(b *testing.B) { - for i := 0; i < b.N; i++ { - Rect(2.5, 1.5) - } -} -func BenchmarkSin(b *testing.B) { - for i := 0; i < b.N; i++ { - Sin(complex(2.5, 3.5)) - } -} -func BenchmarkSinh(b *testing.B) { - for i := 0; i < b.N; i++ { - Sinh(complex(2.5, 3.5)) - } -} -func BenchmarkSqrt(b *testing.B) { - for i := 0; i < b.N; i++ { - Sqrt(complex(2.5, 3.5)) - } -} -func BenchmarkTan(b *testing.B) { - for i := 0; i < b.N; i++ { - Tan(complex(2.5, 3.5)) - } -} -func BenchmarkTanh(b *testing.B) { - for i := 0; i < b.N; i++ { - Tanh(complex(2.5, 3.5)) - } -} diff --git a/src/pkg/cmath/conj.go b/src/pkg/cmath/conj.go deleted file mode 100644 index 776b57da7..000000000 --- a/src/pkg/cmath/conj.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -// Conj returns the complex conjugate of x. -func Conj(x complex128) complex128 { return complex(real(x), -imag(x)) } diff --git a/src/pkg/cmath/exp.go b/src/pkg/cmath/exp.go deleted file mode 100644 index 64c1ef409..000000000 --- a/src/pkg/cmath/exp.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// Cephes Math Library Release 2.8: June, 2000 -// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier -// -// The readme file at http://netlib.sandia.gov/cephes/ says: -// Some software in this archive may be from the book _Methods and -// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster -// International, 1989) or from the Cephes Mathematical Library, a -// commercial product. In either event, it is copyrighted by the author. -// What you see here may be used freely but it comes with no support or -// guarantee. -// -// The two known misprints in the book are repaired here in the -// source listings for the gamma function and the incomplete beta -// integral. -// -// Stephen L. Moshier -// moshier@na-net.ornl.gov - -// Complex exponential function -// -// DESCRIPTION: -// -// Returns the complex exponential of the complex argument z. -// -// If -// z = x + iy, -// r = exp(x), -// then -// w = r cos y + i r sin y. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 8700 3.7e-17 1.1e-17 -// IEEE -10,+10 30000 3.0e-16 8.7e-17 - -// Exp returns e**x, the base-e exponential of x. -func Exp(x complex128) complex128 { - r := math.Exp(real(x)) - s, c := math.Sincos(imag(x)) - return complex(r*c, r*s) -} diff --git a/src/pkg/cmath/isinf.go b/src/pkg/cmath/isinf.go deleted file mode 100644 index f23d2dea7..000000000 --- a/src/pkg/cmath/isinf.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// IsInf returns true if either real(x) or imag(x) is an infinity. -func IsInf(x complex128) bool { - if math.IsInf(real(x), 0) || math.IsInf(imag(x), 0) { - return true - } - return false -} - -// Inf returns a complex infinity, complex(+Inf, +Inf). -func Inf() complex128 { - inf := math.Inf(1) - return complex(inf, inf) -} diff --git a/src/pkg/cmath/isnan.go b/src/pkg/cmath/isnan.go deleted file mode 100644 index 2063bb835..000000000 --- a/src/pkg/cmath/isnan.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// IsNaN returns true if either real(x) or imag(x) is NaN -// and neither is an infinity. -func IsNaN(x complex128) bool { - switch { - case math.IsInf(real(x), 0) || math.IsInf(imag(x), 0): - return false - case math.IsNaN(real(x)) || math.IsNaN(imag(x)): - return true - } - return false -} - -// NaN returns a complex ``not-a-number'' value. -func NaN() complex128 { - nan := math.NaN() - return complex(nan, nan) -} diff --git a/src/pkg/cmath/log.go b/src/pkg/cmath/log.go deleted file mode 100644 index 8e6964fee..000000000 --- a/src/pkg/cmath/log.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// Cephes Math Library Release 2.8: June, 2000 -// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier -// -// The readme file at http://netlib.sandia.gov/cephes/ says: -// Some software in this archive may be from the book _Methods and -// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster -// International, 1989) or from the Cephes Mathematical Library, a -// commercial product. In either event, it is copyrighted by the author. -// What you see here may be used freely but it comes with no support or -// guarantee. -// -// The two known misprints in the book are repaired here in the -// source listings for the gamma function and the incomplete beta -// integral. -// -// Stephen L. Moshier -// moshier@na-net.ornl.gov - -// Complex natural logarithm -// -// DESCRIPTION: -// -// Returns complex logarithm to the base e (2.718...) of -// the complex argument z. -// -// If -// z = x + iy, r = sqrt( x**2 + y**2 ), -// then -// w = log(r) + i arctan(y/x). -// -// The arctangent ranges from -PI to +PI. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 7000 8.5e-17 1.9e-17 -// IEEE -10,+10 30000 5.0e-15 1.1e-16 -// -// Larger relative error can be observed for z near 1 +i0. -// In IEEE arithmetic the peak absolute error is 5.2e-16, rms -// absolute error 1.0e-16. - -// Log returns the natural logarithm of x. -func Log(x complex128) complex128 { - return complex(math.Log(Abs(x)), Phase(x)) -} - -// Log10 returns the decimal logarithm of x. -func Log10(x complex128) complex128 { - return math.Log10E * Log(x) -} diff --git a/src/pkg/cmath/phase.go b/src/pkg/cmath/phase.go deleted file mode 100644 index 2d67aa34c..000000000 --- a/src/pkg/cmath/phase.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// Phase returns the phase (also called the argument) of x. -// The returned value is in the range [-Pi, Pi]. -func Phase(x complex128) float64 { return math.Atan2(imag(x), real(x)) } diff --git a/src/pkg/cmath/polar.go b/src/pkg/cmath/polar.go deleted file mode 100644 index 033676acc..000000000 --- a/src/pkg/cmath/polar.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -// Polar returns the absolute value r and phase θ of x, -// such that x = r * e**θi. -// The phase is in the range [-Pi, Pi]. -func Polar(x complex128) (r, θ float64) { - return Abs(x), Phase(x) -} diff --git a/src/pkg/cmath/pow.go b/src/pkg/cmath/pow.go deleted file mode 100644 index 68e1207c6..000000000 --- a/src/pkg/cmath/pow.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// Cephes Math Library Release 2.8: June, 2000 -// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier -// -// The readme file at http://netlib.sandia.gov/cephes/ says: -// Some software in this archive may be from the book _Methods and -// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster -// International, 1989) or from the Cephes Mathematical Library, a -// commercial product. In either event, it is copyrighted by the author. -// What you see here may be used freely but it comes with no support or -// guarantee. -// -// The two known misprints in the book are repaired here in the -// source listings for the gamma function and the incomplete beta -// integral. -// -// Stephen L. Moshier -// moshier@na-net.ornl.gov - -// Complex power function -// -// DESCRIPTION: -// -// Raises complex A to the complex Zth power. -// Definition is per AMS55 # 4.2.8, -// analytically equivalent to cpow(a,z) = cexp(z clog(a)). -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// IEEE -10,+10 30000 9.4e-15 1.5e-15 - -// Pow returns x**y, the base-x exponential of y. -func Pow(x, y complex128) complex128 { - modulus := Abs(x) - if modulus == 0 { - return complex(0, 0) - } - r := math.Pow(modulus, real(y)) - arg := Phase(x) - theta := real(y) * arg - if imag(y) != 0 { - r *= math.Exp(-imag(y) * arg) - theta += imag(y) * math.Log(modulus) - } - s, c := math.Sincos(theta) - return complex(r*c, r*s) -} diff --git a/src/pkg/cmath/rect.go b/src/pkg/cmath/rect.go deleted file mode 100644 index b955f0bf7..000000000 --- a/src/pkg/cmath/rect.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// Rect returns the complex number x with polar coordinates r, θ. -func Rect(r, θ float64) complex128 { - s, c := math.Sincos(θ) - return complex(r*c, r*s) -} diff --git a/src/pkg/cmath/sin.go b/src/pkg/cmath/sin.go deleted file mode 100644 index 8900ecdde..000000000 --- a/src/pkg/cmath/sin.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// Cephes Math Library Release 2.8: June, 2000 -// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier -// -// The readme file at http://netlib.sandia.gov/cephes/ says: -// Some software in this archive may be from the book _Methods and -// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster -// International, 1989) or from the Cephes Mathematical Library, a -// commercial product. In either event, it is copyrighted by the author. -// What you see here may be used freely but it comes with no support or -// guarantee. -// -// The two known misprints in the book are repaired here in the -// source listings for the gamma function and the incomplete beta -// integral. -// -// Stephen L. Moshier -// moshier@na-net.ornl.gov - -// Complex circular sine -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// -// w = sin x cosh y + i cos x sinh y. -// -// csin(z) = -i csinh(iz). -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 8400 5.3e-17 1.3e-17 -// IEEE -10,+10 30000 3.8e-16 1.0e-16 -// Also tested by csin(casin(z)) = z. - -// Sin returns the sine of x. -func Sin(x complex128) complex128 { - s, c := math.Sincos(real(x)) - sh, ch := sinhcosh(imag(x)) - return complex(s*ch, c*sh) -} - -// Complex hyperbolic sine -// -// DESCRIPTION: -// -// csinh z = (cexp(z) - cexp(-z))/2 -// = sinh x * cos y + i cosh x * sin y . -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// IEEE -10,+10 30000 3.1e-16 8.2e-17 - -// Sinh returns the hyperbolic sine of x. -func Sinh(x complex128) complex128 { - s, c := math.Sincos(imag(x)) - sh, ch := sinhcosh(real(x)) - return complex(c*sh, s*ch) -} - -// Complex circular cosine -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// -// w = cos x cosh y - i sin x sinh y. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 8400 4.5e-17 1.3e-17 -// IEEE -10,+10 30000 3.8e-16 1.0e-16 - -// Cos returns the cosine of x. -func Cos(x complex128) complex128 { - s, c := math.Sincos(real(x)) - sh, ch := sinhcosh(imag(x)) - return complex(c*ch, -s*sh) -} - -// Complex hyperbolic cosine -// -// DESCRIPTION: -// -// ccosh(z) = cosh x cos y + i sinh x sin y . -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// IEEE -10,+10 30000 2.9e-16 8.1e-17 - -// Cosh returns the hyperbolic cosine of x. -func Cosh(x complex128) complex128 { - s, c := math.Sincos(imag(x)) - sh, ch := sinhcosh(real(x)) - return complex(c*ch, s*sh) -} - -// calculate sinh and cosh -func sinhcosh(x float64) (sh, ch float64) { - if math.Fabs(x) <= 0.5 { - return math.Sinh(x), math.Cosh(x) - } - e := math.Exp(x) - ei := 0.5 / e - e *= 0.5 - return e - ei, e + ei -} diff --git a/src/pkg/cmath/sqrt.go b/src/pkg/cmath/sqrt.go deleted file mode 100644 index e77a9b9df..000000000 --- a/src/pkg/cmath/sqrt.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// Cephes Math Library Release 2.8: June, 2000 -// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier -// -// The readme file at http://netlib.sandia.gov/cephes/ says: -// Some software in this archive may be from the book _Methods and -// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster -// International, 1989) or from the Cephes Mathematical Library, a -// commercial product. In either event, it is copyrighted by the author. -// What you see here may be used freely but it comes with no support or -// guarantee. -// -// The two known misprints in the book are repaired here in the -// source listings for the gamma function and the incomplete beta -// integral. -// -// Stephen L. Moshier -// moshier@na-net.ornl.gov - -// Complex square root -// -// DESCRIPTION: -// -// If z = x + iy, r = |z|, then -// -// 1/2 -// Re w = [ (r + x)/2 ] , -// -// 1/2 -// Im w = [ (r - x)/2 ] . -// -// Cancellation error in r-x or r+x is avoided by using the -// identity 2 Re w Im w = y. -// -// Note that -w is also a square root of z. The root chosen -// is always in the right half plane and Im w has the same sign as y. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 25000 3.2e-17 9.6e-18 -// IEEE -10,+10 1,000,000 2.9e-16 6.1e-17 - -// Sqrt returns the square root of x. -func Sqrt(x complex128) complex128 { - if imag(x) == 0 { - if real(x) == 0 { - return complex(0, 0) - } - if real(x) < 0 { - return complex(0, math.Sqrt(-real(x))) - } - return complex(math.Sqrt(real(x)), 0) - } - if real(x) == 0 { - if imag(x) < 0 { - r := math.Sqrt(-0.5 * imag(x)) - return complex(r, -r) - } - r := math.Sqrt(0.5 * imag(x)) - return complex(r, r) - } - a := real(x) - b := imag(x) - var scale float64 - // Rescale to avoid internal overflow or underflow. - if math.Fabs(a) > 4 || math.Fabs(b) > 4 { - a *= 0.25 - b *= 0.25 - scale = 2 - } else { - a *= 1.8014398509481984e16 // 2**54 - b *= 1.8014398509481984e16 - scale = 7.450580596923828125e-9 // 2**-27 - } - r := math.Hypot(a, b) - var t float64 - if a > 0 { - t = math.Sqrt(0.5*r + 0.5*a) - r = scale * math.Fabs((0.5*b)/t) - t *= scale - } else { - r = math.Sqrt(0.5*r - 0.5*a) - t = scale * math.Fabs((0.5*b)/r) - r *= scale - } - if b < 0 { - return complex(t, -r) - } - return complex(t, r) -} diff --git a/src/pkg/cmath/tan.go b/src/pkg/cmath/tan.go deleted file mode 100644 index 94b517521..000000000 --- a/src/pkg/cmath/tan.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmath - -import "math" - -// The original C code, the long comment, and the constants -// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. -// The go code is a simplified version of the original C. -// -// Cephes Math Library Release 2.8: June, 2000 -// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier -// -// The readme file at http://netlib.sandia.gov/cephes/ says: -// Some software in this archive may be from the book _Methods and -// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster -// International, 1989) or from the Cephes Mathematical Library, a -// commercial product. In either event, it is copyrighted by the author. -// What you see here may be used freely but it comes with no support or -// guarantee. -// -// The two known misprints in the book are repaired here in the -// source listings for the gamma function and the incomplete beta -// integral. -// -// Stephen L. Moshier -// moshier@na-net.ornl.gov - -// Complex circular tangent -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// -// sin 2x + i sinh 2y -// w = --------------------. -// cos 2x + cosh 2y -// -// On the real axis the denominator is zero at odd multiples -// of PI/2. The denominator is evaluated by its Taylor -// series near these points. -// -// ctan(z) = -i ctanh(iz). -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 5200 7.1e-17 1.6e-17 -// IEEE -10,+10 30000 7.2e-16 1.2e-16 -// Also tested by ctan * ccot = 1 and catan(ctan(z)) = z. - -// Tan returns the tangent of x. -func Tan(x complex128) complex128 { - d := math.Cos(2*real(x)) + math.Cosh(2*imag(x)) - if math.Fabs(d) < 0.25 { - d = tanSeries(x) - } - if d == 0 { - return Inf() - } - return complex(math.Sin(2*real(x))/d, math.Sinh(2*imag(x))/d) -} - -// Complex hyperbolic tangent -// -// DESCRIPTION: -// -// tanh z = (sinh 2x + i sin 2y) / (cosh 2x + cos 2y) . -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// IEEE -10,+10 30000 1.7e-14 2.4e-16 - -// Tanh returns the hyperbolic tangent of x. -func Tanh(x complex128) complex128 { - d := math.Cosh(2*real(x)) + math.Cos(2*imag(x)) - if d == 0 { - return Inf() - } - return complex(math.Sinh(2*real(x))/d, math.Sin(2*imag(x))/d) -} - -// Program to subtract nearest integer multiple of PI -func reducePi(x float64) float64 { - const ( - // extended precision value of PI: - DP1 = 3.14159265160560607910E0 // ?? 0x400921fb54000000 - DP2 = 1.98418714791870343106E-9 // ?? 0x3e210b4610000000 - DP3 = 1.14423774522196636802E-17 // ?? 0x3c6a62633145c06e - ) - t := x / math.Pi - if t >= 0 { - t += 0.5 - } else { - t -= 0.5 - } - t = float64(int64(t)) // int64(t) = the multiple - return ((x - t*DP1) - t*DP2) - t*DP3 -} - -// Taylor series expansion for cosh(2y) - cos(2x) -func tanSeries(z complex128) float64 { - const MACHEP = 1.0 / (1 << 53) - x := math.Fabs(2 * real(z)) - y := math.Fabs(2 * imag(z)) - x = reducePi(x) - x = x * x - y = y * y - x2 := 1.0 - y2 := 1.0 - f := 1.0 - rn := 0.0 - d := 0.0 - for { - rn += 1 - f *= rn - rn += 1 - f *= rn - x2 *= x - y2 *= y - t := y2 + x2 - t /= f - d += t - - rn += 1 - f *= rn - rn += 1 - f *= rn - x2 *= x - y2 *= y - t = y2 - x2 - t /= f - d += t - if math.Fabs(t/d) <= MACHEP { - break - } - } - return d -} - -// Complex circular cotangent -// -// DESCRIPTION: -// -// If -// z = x + iy, -// -// then -// -// sin 2x - i sinh 2y -// w = --------------------. -// cosh 2y - cos 2x -// -// On the real axis, the denominator has zeros at even -// multiples of PI/2. Near these points it is evaluated -// by a Taylor series. -// -// ACCURACY: -// -// Relative error: -// arithmetic domain # trials peak rms -// DEC -10,+10 3000 6.5e-17 1.6e-17 -// IEEE -10,+10 30000 9.2e-16 1.2e-16 -// Also tested by ctan * ccot = 1 + i0. - -// Cot returns the cotangent of x. -func Cot(x complex128) complex128 { - d := math.Cosh(2*imag(x)) - math.Cos(2*real(x)) - if math.Fabs(d) < 0.25 { - d = tanSeries(x) - } - if d == 0 { - return Inf() - } - return complex(math.Sin(2*real(x))/d, -math.Sinh(2*imag(x))/d) -} diff --git a/src/pkg/compress/bzip2/Makefile b/src/pkg/compress/bzip2/Makefile deleted file mode 100644 index a4bceef16..000000000 --- a/src/pkg/compress/bzip2/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=compress/bzip2 -GOFILES=\ - bit_reader.go\ - bzip2.go\ - huffman.go\ - move_to_front.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/bzip2/bit_reader.go b/src/pkg/compress/bzip2/bit_reader.go index 50f0ec836..b35c69a1c 100644 --- a/src/pkg/compress/bzip2/bit_reader.go +++ b/src/pkg/compress/bzip2/bit_reader.go @@ -7,25 +7,24 @@ package bzip2 import ( "bufio" "io" - "os" ) // bitReader wraps an io.Reader and provides the ability to read values, -// bit-by-bit, from it. Its Read* methods don't return the usual os.Error +// bit-by-bit, from it. Its Read* methods don't return the usual error // because the error handling was verbose. Instead, any error is kept and can // be checked afterwards. type bitReader struct { r byteReader n uint64 bits uint - err os.Error + err error } -// bitReader needs to read bytes from an io.Reader. We attempt to cast the +// bitReader needs to read bytes from an io.Reader. We attempt to convert the // given io.Reader to this interface and, if it doesn't already fit, we wrap in // a bufio.Reader. type byteReader interface { - ReadByte() (byte, os.Error) + ReadByte() (byte, error) } func newBitReader(r io.Reader) bitReader { @@ -38,11 +37,11 @@ func newBitReader(r io.Reader) bitReader { // ReadBits64 reads the given number of bits and returns them in the // least-significant part of a uint64. In the event of an error, it returns 0 -// and the error can be obtained by calling Error(). +// and the error can be obtained by calling Err(). func (br *bitReader) ReadBits64(bits uint) (n uint64) { for bits > br.bits { b, err := br.r.ReadByte() - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } if err != nil { @@ -83,6 +82,6 @@ func (br *bitReader) ReadBit() bool { return n != 0 } -func (br *bitReader) Error() os.Error { +func (br *bitReader) Err() error { return br.err } diff --git a/src/pkg/compress/bzip2/bzip2.go b/src/pkg/compress/bzip2/bzip2.go index 8b4572306..3dc8c6206 100644 --- a/src/pkg/compress/bzip2/bzip2.go +++ b/src/pkg/compress/bzip2/bzip2.go @@ -5,10 +5,7 @@ // Package bzip2 implements bzip2 decompression. package bzip2 -import ( - "io" - "os" -) +import "io" // There's no RFC for bzip2. I used the Wikipedia page for reference and a lot // of guessing: http://en.wikipedia.org/wiki/Bzip2 @@ -19,7 +16,7 @@ import ( // syntactically invalid. type StructuralError string -func (s StructuralError) String() string { +func (s StructuralError) Error() string { return "bzip2 data invalid: " + string(s) } @@ -53,7 +50,7 @@ const bzip2BlockMagic = 0x314159265359 const bzip2FinalMagic = 0x177245385090 // setup parses the bzip2 header. -func (bz2 *reader) setup() os.Error { +func (bz2 *reader) setup() error { br := &bz2.br magic := br.ReadBits(16) @@ -76,14 +73,14 @@ func (bz2 *reader) setup() os.Error { return nil } -func (bz2 *reader) Read(buf []byte) (n int, err os.Error) { +func (bz2 *reader) Read(buf []byte) (n int, err error) { if bz2.eof { - return 0, os.EOF + return 0, io.EOF } if !bz2.setupDone { err = bz2.setup() - brErr := bz2.br.Error() + brErr := bz2.br.Err() if brErr != nil { err = brErr } @@ -94,14 +91,14 @@ func (bz2 *reader) Read(buf []byte) (n int, err os.Error) { } n, err = bz2.read(buf) - brErr := bz2.br.Error() + brErr := bz2.br.Err() if brErr != nil { err = brErr } return } -func (bz2 *reader) read(buf []byte) (n int, err os.Error) { +func (bz2 *reader) read(buf []byte) (n int, err error) { // bzip2 is a block based compressor, except that it has a run-length // preprocessing step. The block based nature means that we can // preallocate fixed-size buffers and reuse them. However, the RLE @@ -162,7 +159,7 @@ func (bz2 *reader) read(buf []byte) (n int, err os.Error) { if magic == bzip2FinalMagic { br.ReadBits64(32) // ignored CRC bz2.eof = true - return 0, os.EOF + return 0, io.EOF } else if magic != bzip2BlockMagic { return 0, StructuralError("bad magic value found") } @@ -176,7 +173,7 @@ func (bz2 *reader) read(buf []byte) (n int, err os.Error) { } // readBlock reads a bzip2 block. The magic number should already have been consumed. -func (bz2 *reader) readBlock() (err os.Error) { +func (bz2 *reader) readBlock() (err error) { br := &bz2.br br.ReadBits64(32) // skip checksum. TODO: check it if we can figure out what it is. randomized := br.ReadBits(1) diff --git a/src/pkg/compress/bzip2/bzip2_test.go b/src/pkg/compress/bzip2/bzip2_test.go index 156eea83f..7b227ac9f 100644 --- a/src/pkg/compress/bzip2/bzip2_test.go +++ b/src/pkg/compress/bzip2/bzip2_test.go @@ -9,7 +9,6 @@ import ( "encoding/hex" "io" "io/ioutil" - "os" "testing" ) @@ -46,7 +45,7 @@ func readerFromHex(s string) io.Reader { return bytes.NewBuffer(data) } -func decompressHex(s string) (out []byte, err os.Error) { +func decompressHex(s string) (out []byte, err error) { r := NewReader(readerFromHex(s)) return ioutil.ReadAll(r) } diff --git a/src/pkg/compress/bzip2/huffman.go b/src/pkg/compress/bzip2/huffman.go index dc05739c7..078c1cb89 100644 --- a/src/pkg/compress/bzip2/huffman.go +++ b/src/pkg/compress/bzip2/huffman.go @@ -4,10 +4,7 @@ package bzip2 -import ( - "os" - "sort" -) +import "sort" // A huffmanTree is a binary tree which is navigated, bit-by-bit to reach a // symbol. @@ -63,7 +60,7 @@ func (t huffmanTree) Decode(br *bitReader) (v uint16) { // newHuffmanTree builds a Huffman tree from a slice containing the code // lengths of each symbol. The maximum code length is 32 bits. -func newHuffmanTree(lengths []uint8) (huffmanTree, os.Error) { +func newHuffmanTree(lengths []uint8) (huffmanTree, error) { // There are many possible trees that assign the same code length to // each symbol (consider reflecting a tree down the middle, for // example). Since the code length assignments determine the @@ -176,7 +173,7 @@ func (n huffmanCodes) Swap(i, j int) { // buildHuffmanNode takes a slice of sorted huffmanCodes and builds a node in // the Huffman tree at the given level. It returns the index of the newly // constructed node. -func buildHuffmanNode(t *huffmanTree, codes []huffmanCode, level uint32) (nodeIndex uint16, err os.Error) { +func buildHuffmanNode(t *huffmanTree, codes []huffmanCode, level uint32) (nodeIndex uint16, err error) { test := uint32(1) << (31 - level) // We have to search the list of codes to find the divide between the left and right sides. diff --git a/src/pkg/compress/flate/Makefile b/src/pkg/compress/flate/Makefile deleted file mode 100644 index 197828a92..000000000 --- a/src/pkg/compress/flate/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=compress/flate -GOFILES=\ - deflate.go\ - huffman_bit_writer.go\ - huffman_code.go\ - inflate.go\ - reverse_bits.go\ - token.go\ - util.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/flate/deflate.go b/src/pkg/compress/flate/deflate.go index b1cee0b2f..20408409c 100644 --- a/src/pkg/compress/flate/deflate.go +++ b/src/pkg/compress/flate/deflate.go @@ -5,9 +5,9 @@ package flate import ( + "fmt" "io" "math" - "os" ) const ( @@ -28,10 +28,12 @@ const ( // stop things from getting too large. maxFlateBlockTokens = 1 << 14 maxStoreBlockSize = 65535 - hashBits = 15 + hashBits = 17 hashSize = 1 << hashBits hashMask = (1 << hashBits) - 1 hashShift = (hashBits + minMatchLength - 1) / minMatchLength + + skipNever = math.MaxInt32 ) type compressionLevel struct { @@ -46,12 +48,12 @@ var levels = []compressionLevel{ {3, 0, 32, 32, 6}, // Levels 4-9 use increasingly more lazy matching // and increasingly stringent conditions for "good enough". - {4, 4, 16, 16, math.MaxInt32}, - {8, 16, 32, 32, math.MaxInt32}, - {8, 16, 128, 128, math.MaxInt32}, - {8, 32, 128, 256, math.MaxInt32}, - {32, 128, 258, 1024, math.MaxInt32}, - {32, 258, 258, 4096, math.MaxInt32}, + {4, 4, 16, 16, skipNever}, + {8, 16, 32, 32, skipNever}, + {8, 16, 128, 128, skipNever}, + {8, 32, 128, 256, skipNever}, + {32, 128, 258, 1024, skipNever}, + {32, 258, 258, 4096, skipNever}, } type compressor struct { @@ -69,9 +71,10 @@ type compressor struct { // If hashHead[hashValue] is within the current window, then // hashPrev[hashHead[hashValue] & windowMask] contains the previous index // with the same hash value. - chainHead int - hashHead []int - hashPrev []int + chainHead int + hashHead []int + hashPrev []int + hashOffset int // input window: unprocessed data is window[index:windowEnd] index int @@ -80,16 +83,15 @@ type compressor struct { blockStart int // window index where current tokens start byteAvailable bool // if true, still need to process window[index-1]. - // queued output tokens: tokens[:ti] + // queued output tokens tokens []token - ti int // deflate state length int offset int hash int maxInsertIndex int - err os.Error + err error } func (d *compressor) fillDeflate(b []byte) int { @@ -103,27 +105,14 @@ func (d *compressor) fillDeflate(b []byte) int { } else { d.blockStart = math.MaxInt32 } - for i, h := range d.hashHead { - v := h - windowSize - if v < -1 { - v = -1 - } - d.hashHead[i] = v - } - for i, h := range d.hashPrev { - v := -h - windowSize - if v < -1 { - v = -1 - } - d.hashPrev[i] = v - } + d.hashOffset += windowSize } n := copy(d.window[d.windowEnd:], b) d.windowEnd += n return n } -func (d *compressor) writeBlock(tokens []token, index int, eof bool) os.Error { +func (d *compressor) writeBlock(tokens []token, index int, eof bool) error { if index > 0 || eof { var window []byte if d.blockStart <= index { @@ -187,14 +176,14 @@ func (d *compressor) findMatch(pos int, prevHead int, prevLength int, lookahead // hashPrev[i & windowMask] has already been overwritten, so stop now. break } - if i = d.hashPrev[i&windowMask]; i < minIndex || i < 0 { + if i = d.hashPrev[i&windowMask] - d.hashOffset; i < minIndex || i < 0 { break } } return } -func (d *compressor) writeStoredBlock(buf []byte) os.Error { +func (d *compressor) writeStoredBlock(buf []byte) error { if d.w.writeStoredHeader(len(buf), false); d.w.err != nil { return d.w.err } @@ -206,13 +195,12 @@ func (d *compressor) initDeflate() { d.hashHead = make([]int, hashSize) d.hashPrev = make([]int, windowSize) d.window = make([]byte, 2*windowSize) - fillInts(d.hashHead, -1) - d.tokens = make([]token, maxFlateBlockTokens, maxFlateBlockTokens+1) + d.hashOffset = 1 + d.tokens = make([]token, 0, maxFlateBlockTokens+1) d.length = minMatchLength - 1 d.offset = 0 d.byteAvailable = false d.index = 0 - d.ti = 0 d.hash = 0 d.chainHead = -1 } @@ -244,15 +232,14 @@ Loop: // Flush current output block if any. if d.byteAvailable { // There is still one pending token that needs to be flushed - d.tokens[d.ti] = literalToken(uint32(d.window[d.index-1])) - d.ti++ + d.tokens = append(d.tokens, literalToken(uint32(d.window[d.index-1]))) d.byteAvailable = false } - if d.ti > 0 { - if d.err = d.writeBlock(d.tokens[0:d.ti], d.index, false); d.err != nil { + if len(d.tokens) > 0 { + if d.err = d.writeBlock(d.tokens, d.index, false); d.err != nil { return } - d.ti = 0 + d.tokens = d.tokens[:0] } break Loop } @@ -262,7 +249,7 @@ Loop: d.hash = (d.hash<= minIndex && - (d.fastSkipHashing != 0 && lookahead > minMatchLength-1 || - d.fastSkipHashing == 0 && lookahead > prevLength && prevLength < d.lazy) { - if newLength, newOffset, ok := d.findMatch(d.index, d.chainHead, minMatchLength-1, lookahead); ok { + if d.chainHead-d.hashOffset >= minIndex && + (d.fastSkipHashing != skipNever && lookahead > minMatchLength-1 || + d.fastSkipHashing == skipNever && lookahead > prevLength && prevLength < d.lazy) { + if newLength, newOffset, ok := d.findMatch(d.index, d.chainHead-d.hashOffset, minMatchLength-1, lookahead); ok { d.length = newLength d.offset = newOffset } } - if d.fastSkipHashing != 0 && d.length >= minMatchLength || - d.fastSkipHashing == 0 && prevLength >= minMatchLength && d.length <= prevLength { + if d.fastSkipHashing != skipNever && d.length >= minMatchLength || + d.fastSkipHashing == skipNever && prevLength >= minMatchLength && d.length <= prevLength { // There was a match at the previous step, and the current match is // not better. Output the previous match. - if d.fastSkipHashing != 0 { - d.tokens[d.ti] = matchToken(uint32(d.length-minMatchLength), uint32(d.offset-minOffsetSize)) + if d.fastSkipHashing != skipNever { + d.tokens = append(d.tokens, matchToken(uint32(d.length-minMatchLength), uint32(d.offset-minOffsetSize))) } else { - d.tokens[d.ti] = matchToken(uint32(prevLength-minMatchLength), uint32(prevOffset-minOffsetSize)) + d.tokens = append(d.tokens, matchToken(uint32(prevLength-minMatchLength), uint32(prevOffset-minOffsetSize))) } - d.ti++ // Insert in the hash table all strings up to the end of the match. // index and index-1 are already inserted. If there is not enough // lookahead, the last two strings are not inserted into the hash // table. if d.length <= d.fastSkipHashing { var newIndex int - if d.fastSkipHashing != 0 { + if d.fastSkipHashing != skipNever { newIndex = d.index + d.length } else { - newIndex = prevLength - 1 + newIndex = d.index + prevLength - 1 } for d.index++; d.index < newIndex; d.index++ { if d.index < d.maxInsertIndex { @@ -309,10 +295,10 @@ Loop: // Our chain should point to the previous value. d.hashPrev[d.index&windowMask] = d.hashHead[d.hash] // Set the head of the hash chain to us. - d.hashHead[d.hash] = d.index + d.hashHead[d.hash] = d.index + d.hashOffset } } - if d.fastSkipHashing == 0 { + if d.fastSkipHashing == skipNever { d.byteAvailable = false d.length = minMatchLength - 1 } @@ -320,32 +306,33 @@ Loop: // For matches this long, we don't bother inserting each individual // item into the table. d.index += d.length - d.hash = (int(d.window[d.index])< 0 { @@ -375,7 +362,7 @@ func (d *compressor) write(b []byte) (n int, err os.Error) { return n, d.err } -func (d *compressor) syncFlush() os.Error { +func (d *compressor) syncFlush() error { d.sync = true d.step(d) if d.err == nil { @@ -387,7 +374,7 @@ func (d *compressor) syncFlush() os.Error { return d.err } -func (d *compressor) init(w io.Writer, level int) (err os.Error) { +func (d *compressor) init(w io.Writer, level int) (err error) { d.w = newHuffmanBitWriter(w) switch { @@ -404,12 +391,12 @@ func (d *compressor) init(w io.Writer, level int) (err os.Error) { d.fill = (*compressor).fillDeflate d.step = (*compressor).deflate default: - return WrongValueError{"level", 0, 9, int32(level)} + return fmt.Errorf("flate: invalid compression level %d: want value in range [-1, 9]", level) } return nil } -func (d *compressor) close() os.Error { +func (d *compressor) close() error { d.sync = true d.step(d) if d.err != nil { @@ -422,17 +409,22 @@ func (d *compressor) close() os.Error { return d.w.err } -// NewWriter returns a new Writer compressing -// data at the given level. Following zlib, levels -// range from 1 (BestSpeed) to 9 (BestCompression); -// higher levels typically run slower but compress more. -// Level 0 (NoCompression) does not attempt any -// compression; it only adds the necessary DEFLATE framing. -func NewWriter(w io.Writer, level int) *Writer { +// NewWriter returns a new Writer compressing data at the given level. +// Following zlib, levels range from 1 (BestSpeed) to 9 (BestCompression); +// higher levels typically run slower but compress more. Level 0 +// (NoCompression) does not attempt any compression; it only adds the +// necessary DEFLATE framing. Level -1 (DefaultCompression) uses the default +// compression level. +// +// If level is in the range [-1, 9] then the error returned will be nil. +// Otherwise the error returned will be non-nil. +func NewWriter(w io.Writer, level int) (*Writer, error) { const logWindowSize = logMaxOffsetSize var dw Writer - dw.d.init(w, level) - return &dw + if err := dw.d.init(w, level); err != nil { + return nil, err + } + return &dw, nil } // NewWriterDict is like NewWriter but initializes the new @@ -441,13 +433,16 @@ func NewWriter(w io.Writer, level int) *Writer { // any compressed output. The compressed data written to w // can only be decompressed by a Reader initialized with the // same dictionary. -func NewWriterDict(w io.Writer, level int, dict []byte) *Writer { +func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) { dw := &dictWriter{w, false} - zw := NewWriter(dw, level) + zw, err := NewWriter(dw, level) + if err != nil { + return nil, err + } zw.Write(dict) zw.Flush() dw.enabled = true - return zw + return zw, err } type dictWriter struct { @@ -455,7 +450,7 @@ type dictWriter struct { enabled bool } -func (w *dictWriter) Write(b []byte) (n int, err os.Error) { +func (w *dictWriter) Write(b []byte) (n int, err error) { if w.enabled { return w.w.Write(b) } @@ -470,7 +465,7 @@ type Writer struct { // Write writes data to w, which will eventually write the // compressed form of data to its underlying writer. -func (w *Writer) Write(data []byte) (n int, err os.Error) { +func (w *Writer) Write(data []byte) (n int, err error) { return w.d.write(data) } @@ -481,13 +476,13 @@ func (w *Writer) Write(data []byte) (n int, err os.Error) { // If the underlying writer returns an error, Flush returns that error. // // In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. -func (w *Writer) Flush() os.Error { +func (w *Writer) Flush() error { // For more about flushing: // http://www.bolet.org/~pornin/deflate-flush.html return w.d.syncFlush() } // Close flushes and closes the writer. -func (w *Writer) Close() os.Error { +func (w *Writer) Close() error { return w.d.close() } diff --git a/src/pkg/compress/flate/deflate_test.go b/src/pkg/compress/flate/deflate_test.go index 930823685..543c59505 100644 --- a/src/pkg/compress/flate/deflate_test.go +++ b/src/pkg/compress/flate/deflate_test.go @@ -9,7 +9,6 @@ import ( "fmt" "io" "io/ioutil" - "os" "sync" "testing" ) @@ -31,44 +30,44 @@ type reverseBitsTest struct { } var deflateTests = []*deflateTest{ - &deflateTest{[]byte{}, 0, []byte{1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, -1, []byte{18, 4, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, DefaultCompression, []byte{18, 4, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, 4, []byte{18, 4, 4, 0, 0, 255, 255}}, - - &deflateTest{[]byte{0x11}, 0, []byte{0, 1, 0, 254, 255, 17, 1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x12}, 0, []byte{0, 2, 0, 253, 255, 17, 18, 1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 0, + {[]byte{}, 0, []byte{1, 0, 0, 255, 255}}, + {[]byte{0x11}, -1, []byte{18, 4, 4, 0, 0, 255, 255}}, + {[]byte{0x11}, DefaultCompression, []byte{18, 4, 4, 0, 0, 255, 255}}, + {[]byte{0x11}, 4, []byte{18, 4, 4, 0, 0, 255, 255}}, + + {[]byte{0x11}, 0, []byte{0, 1, 0, 254, 255, 17, 1, 0, 0, 255, 255}}, + {[]byte{0x11, 0x12}, 0, []byte{0, 2, 0, 253, 255, 17, 18, 1, 0, 0, 255, 255}}, + {[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 0, []byte{0, 8, 0, 247, 255, 17, 17, 17, 17, 17, 17, 17, 17, 1, 0, 0, 255, 255}, }, - &deflateTest{[]byte{}, 1, []byte{1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, 1, []byte{18, 4, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x12}, 1, []byte{18, 20, 2, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 1, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}}, - &deflateTest{[]byte{}, 9, []byte{1, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11}, 9, []byte{18, 4, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x12}, 9, []byte{18, 20, 2, 4, 0, 0, 255, 255}}, - &deflateTest{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 9, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}}, + {[]byte{}, 1, []byte{1, 0, 0, 255, 255}}, + {[]byte{0x11}, 1, []byte{18, 4, 4, 0, 0, 255, 255}}, + {[]byte{0x11, 0x12}, 1, []byte{18, 20, 2, 4, 0, 0, 255, 255}}, + {[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 1, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}}, + {[]byte{}, 9, []byte{1, 0, 0, 255, 255}}, + {[]byte{0x11}, 9, []byte{18, 4, 4, 0, 0, 255, 255}}, + {[]byte{0x11, 0x12}, 9, []byte{18, 20, 2, 4, 0, 0, 255, 255}}, + {[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, 9, []byte{18, 132, 2, 64, 0, 0, 0, 255, 255}}, } var deflateInflateTests = []*deflateInflateTest{ - &deflateInflateTest{[]byte{}}, - &deflateInflateTest{[]byte{0x11}}, - &deflateInflateTest{[]byte{0x11, 0x12}}, - &deflateInflateTest{[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}}, - &deflateInflateTest{[]byte{0x11, 0x10, 0x13, 0x41, 0x21, 0x21, 0x41, 0x13, 0x87, 0x78, 0x13}}, - &deflateInflateTest{largeDataChunk()}, + {[]byte{}}, + {[]byte{0x11}}, + {[]byte{0x11, 0x12}}, + {[]byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}}, + {[]byte{0x11, 0x10, 0x13, 0x41, 0x21, 0x21, 0x41, 0x13, 0x87, 0x78, 0x13}}, + {largeDataChunk()}, } var reverseBitsTests = []*reverseBitsTest{ - &reverseBitsTest{1, 1, 1}, - &reverseBitsTest{1, 2, 2}, - &reverseBitsTest{1, 3, 4}, - &reverseBitsTest{1, 4, 8}, - &reverseBitsTest{1, 5, 16}, - &reverseBitsTest{17, 5, 17}, - &reverseBitsTest{257, 9, 257}, - &reverseBitsTest{29, 5, 23}, + {1, 1, 1}, + {1, 2, 2}, + {1, 3, 4}, + {1, 4, 8}, + {1, 5, 16}, + {17, 5, 17}, + {257, 9, 257}, + {29, 5, 23}, } func largeDataChunk() []byte { @@ -82,7 +81,11 @@ func largeDataChunk() []byte { func TestDeflate(t *testing.T) { for _, h := range deflateTests { var buf bytes.Buffer - w := NewWriter(&buf, h.level) + w, err := NewWriter(&buf, h.level) + if err != nil { + t.Errorf("NewWriter: %v", err) + continue + } w.Write(h.in) w.Close() if !bytes.Equal(buf.Bytes(), h.out) { @@ -102,7 +105,7 @@ func newSyncBuffer() *syncBuffer { return &syncBuffer{ready: make(chan bool, 1)} } -func (b *syncBuffer) Read(p []byte) (n int, err os.Error) { +func (b *syncBuffer) Read(p []byte) (n int, err error) { for { b.mu.RLock() n, err = b.buf.Read(p) @@ -122,7 +125,7 @@ func (b *syncBuffer) signal() { } } -func (b *syncBuffer) Write(p []byte) (n int, err os.Error) { +func (b *syncBuffer) Write(p []byte) (n int, err error) { n, err = b.buf.Write(p) b.signal() return @@ -137,7 +140,7 @@ func (b *syncBuffer) ReadMode() { b.signal() } -func (b *syncBuffer) Close() os.Error { +func (b *syncBuffer) Close() error { b.closed = true b.signal() return nil @@ -152,7 +155,11 @@ func testSync(t *testing.T, level int, input []byte, name string) { buf := newSyncBuffer() buf1 := new(bytes.Buffer) buf.WriteMode() - w := NewWriter(io.MultiWriter(buf, buf1), level) + w, err := NewWriter(io.MultiWriter(buf, buf1), level) + if err != nil { + t.Errorf("NewWriter: %v", err) + return + } r := NewReader(buf) // Write half the input and read back. @@ -204,7 +211,7 @@ func testSync(t *testing.T, level int, input []byte, name string) { } buf.ReadMode() out := make([]byte, 10) - if n, err := r.Read(out); n > 0 || err != os.EOF { + if n, err := r.Read(out); n > 0 || err != io.EOF { t.Errorf("testSync (%d, %d, %s): final Read: %d, %v (hex: %x)", level, len(input), name, n, err, out[0:n]) } if buf.buf.Len() != 0 { @@ -214,7 +221,7 @@ func testSync(t *testing.T, level int, input []byte, name string) { // stream should work for ordinary reader too r = NewReader(buf1) - out, err := ioutil.ReadAll(r) + out, err = ioutil.ReadAll(r) if err != nil { t.Errorf("testSync: read: %s", err) return @@ -225,35 +232,42 @@ func testSync(t *testing.T, level int, input []byte, name string) { } } -func testToFromWithLevel(t *testing.T, level int, input []byte, name string) os.Error { - buffer := bytes.NewBuffer(nil) - w := NewWriter(buffer, level) +func testToFromWithLevelAndLimit(t *testing.T, level int, input []byte, name string, limit int) { + var buffer bytes.Buffer + w, err := NewWriter(&buffer, level) + if err != nil { + t.Errorf("NewWriter: %v", err) + return + } w.Write(input) w.Close() - r := NewReader(buffer) + if limit > 0 && buffer.Len() > limit { + t.Errorf("level: %d, len(compress(data)) = %d > limit = %d", level, buffer.Len(), limit) + return + } + r := NewReader(&buffer) out, err := ioutil.ReadAll(r) if err != nil { t.Errorf("read: %s", err) - return err + return } r.Close() if !bytes.Equal(input, out) { t.Errorf("decompress(compress(data)) != data: level=%d input=%s", level, name) + return } - testSync(t, level, input, name) - return nil } -func testToFrom(t *testing.T, input []byte, name string) { +func testToFromWithLimit(t *testing.T, input []byte, name string, limit [10]int) { for i := 0; i < 10; i++ { - testToFromWithLevel(t, i, input, name) + testToFromWithLevelAndLimit(t, i, input, name, limit[i]) } } func TestDeflateInflate(t *testing.T) { for i, h := range deflateInflateTests { - testToFrom(t, h.in, fmt.Sprintf("#%d", i)) + testToFromWithLimit(t, h.in, fmt.Sprintf("#%d", i), [10]int{}) } } @@ -266,12 +280,36 @@ func TestReverseBits(t *testing.T) { } } +type deflateInflateStringTest struct { + filename string + label string + limit [10]int +} + +var deflateInflateStringTests = []deflateInflateStringTest{ + { + "../testdata/e.txt", + "2.718281828...", + [...]int{10013, 5065, 5096, 5115, 5093, 5079, 5079, 5079, 5079, 5079}, + }, + { + "../testdata/Mark.Twain-Tom.Sawyer.txt", + "Mark.Twain-Tom.Sawyer", + [...]int{407330, 187598, 180361, 172974, 169160, 163476, 160936, 160506, 160295, 160295}, + }, +} + func TestDeflateInflateString(t *testing.T) { - gold, err := ioutil.ReadFile("../testdata/e.txt") - if err != nil { - t.Error(err) + for _, test := range deflateInflateStringTests { + gold, err := ioutil.ReadFile(test.filename) + if err != nil { + t.Error(err) + } + testToFromWithLimit(t, gold, test.label, test.limit) + if testing.Short() { + break + } } - testToFromWithLevel(t, 1, gold, "2.718281828...") } func TestReaderDict(t *testing.T) { @@ -280,7 +318,10 @@ func TestReaderDict(t *testing.T) { text = "hello again world" ) var b bytes.Buffer - w := NewWriter(&b, 5) + w, err := NewWriter(&b, 5) + if err != nil { + t.Fatalf("NewWriter: %v", err) + } w.Write([]byte(dict)) w.Flush() b.Reset() @@ -303,7 +344,10 @@ func TestWriterDict(t *testing.T) { text = "hello again world" ) var b bytes.Buffer - w := NewWriter(&b, 5) + w, err := NewWriter(&b, 5) + if err != nil { + t.Fatalf("NewWriter: %v", err) + } w.Write([]byte(dict)) w.Flush() b.Reset() @@ -311,7 +355,7 @@ func TestWriterDict(t *testing.T) { w.Close() var b1 bytes.Buffer - w = NewWriterDict(&b1, 5, []byte(dict)) + w, _ = NewWriterDict(&b1, 5, []byte(dict)) w.Write([]byte(text)) w.Close() @@ -319,3 +363,22 @@ func TestWriterDict(t *testing.T) { t.Fatalf("writer wrote %q want %q", b1.Bytes(), b.Bytes()) } } + +// See http://code.google.com/p/go/issues/detail?id=2508 +func TestRegression2508(t *testing.T) { + if testing.Short() { + t.Logf("test disabled with -short") + return + } + w, err := NewWriter(ioutil.Discard, 1) + if err != nil { + t.Fatalf("NewWriter: %v", err) + } + buf := make([]byte, 1024) + for i := 0; i < 131072; i++ { + if _, err := w.Write(buf); err != nil { + t.Fatalf("writer failed: %v", err) + } + } + w.Close() +} diff --git a/src/pkg/compress/flate/flate_test.go b/src/pkg/compress/flate/flate_test.go index bfd3b8381..94efc90ac 100644 --- a/src/pkg/compress/flate/flate_test.go +++ b/src/pkg/compress/flate/flate_test.go @@ -52,7 +52,7 @@ type InitDecoderTest struct { var initDecoderTests = []*InitDecoderTest{ // Example from Connell 1973, - &InitDecoderTest{ + { []int{3, 5, 2, 4, 3, 5, 5, 4, 4, 3, 4, 5}, huffmanDecoder{ 2, 5, @@ -68,7 +68,7 @@ var initDecoderTests = []*InitDecoderTest{ }, // Example from RFC 1951 section 3.2.2 - &InitDecoderTest{ + { []int{2, 1, 3, 3}, huffmanDecoder{ 1, 3, @@ -80,7 +80,7 @@ var initDecoderTests = []*InitDecoderTest{ }, // Second example from RFC 1951 section 3.2.2 - &InitDecoderTest{ + { []int{3, 3, 3, 3, 3, 2, 4, 4}, huffmanDecoder{ 2, 4, @@ -92,21 +92,21 @@ var initDecoderTests = []*InitDecoderTest{ }, // Static Huffman codes (RFC 1951 section 3.2.6) - &InitDecoderTest{ + { fixedHuffmanBits[0:], fixedHuffmanDecoder, true, }, // Illegal input. - &InitDecoderTest{ + { []int{}, huffmanDecoder{}, false, }, // Illegal input. - &InitDecoderTest{ + { []int{0, 0, 0, 0, 0, 0, 0}, huffmanDecoder{}, false, diff --git a/src/pkg/compress/flate/huffman_bit_writer.go b/src/pkg/compress/flate/huffman_bit_writer.go index 3981df5cb..25e1da336 100644 --- a/src/pkg/compress/flate/huffman_bit_writer.go +++ b/src/pkg/compress/flate/huffman_bit_writer.go @@ -7,8 +7,6 @@ package flate import ( "io" "math" - "os" - "strconv" ) const ( @@ -83,14 +81,7 @@ type huffmanBitWriter struct { literalEncoding *huffmanEncoder offsetEncoding *huffmanEncoder codegenEncoding *huffmanEncoder - err os.Error -} - -type WrongValueError struct { - name string - from int32 - to int32 - value int32 + err error } func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { @@ -106,11 +97,6 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { } } -func (err WrongValueError) String() string { - return "huffmanBitWriter: " + err.name + " should belong to [" + strconv.Itoa64(int64(err.from)) + ";" + - strconv.Itoa64(int64(err.to)) + "] but actual value is " + strconv.Itoa64(int64(err.value)) -} - func (w *huffmanBitWriter) flushBits() { if w.err != nil { w.nbits = 0 @@ -194,15 +180,17 @@ func (w *huffmanBitWriter) writeBytes(bytes []byte) { // numLiterals The number of literals in literalEncoding // numOffsets The number of offsets in offsetEncoding func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int) { - fillInt32s(w.codegenFreq, 0) + for i := range w.codegenFreq { + w.codegenFreq[i] = 0 + } // Note that we are using codegen both as a temporary variable for holding // a copy of the frequencies, and as the place where we put the result. // This is fine because the output is always shorter than the input used // so far. codegen := w.codegen // cache // Copy the concatenated code sizes to codegen. Put a marker at the end. - copyUint8s(codegen[0:numLiterals], w.literalEncoding.codeBits) - copyUint8s(codegen[numLiterals:numLiterals+numOffsets], w.offsetEncoding.codeBits) + copy(codegen[0:numLiterals], w.literalEncoding.codeBits) + copy(codegen[numLiterals:numLiterals+numOffsets], w.offsetEncoding.codeBits) codegen[numLiterals+numOffsets] = badCode size := codegen[0] @@ -223,7 +211,10 @@ func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int) { w.codegenFreq[size]++ count-- for count >= 3 { - n := min(count, 6) + n := 6 + if n > count { + n = count + } codegen[outIndex] = 16 outIndex++ codegen[outIndex] = uint8(n - 3) @@ -233,7 +224,10 @@ func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int) { } } else { for count >= 11 { - n := min(count, 138) + n := 138 + if n > count { + n = count + } codegen[outIndex] = 18 outIndex++ codegen[outIndex] = uint8(n - 11) @@ -352,8 +346,12 @@ func (w *huffmanBitWriter) writeBlock(tokens []token, eof bool, input []byte) { if w.err != nil { return } - fillInt32s(w.literalFreq, 0) - fillInt32s(w.offsetFreq, 0) + for i := range w.literalFreq { + w.literalFreq[i] = 0 + } + for i := range w.offsetFreq { + w.offsetFreq[i] = 0 + } n := len(tokens) tokens = tokens[0 : n+1] diff --git a/src/pkg/compress/flate/huffman_code.go b/src/pkg/compress/flate/huffman_code.go index 7ed603a4f..009cce626 100644 --- a/src/pkg/compress/flate/huffman_code.go +++ b/src/pkg/compress/flate/huffman_code.go @@ -121,61 +121,6 @@ func (h *huffmanEncoder) bitLength(freq []int32) int64 { return total } -// Generate elements in the chain using an iterative algorithm. -func (h *huffmanEncoder) generateChains(top *levelInfo, list []literalNode) { - n := len(list) - list = list[0 : n+1] - list[n] = maxNode() - - l := top - for { - if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 { - // We've run out of both leafs and pairs. - // End all calculations for this level. - // To m sure we never come back to this level or any lower level, - // set nextPairFreq impossibly large. - l.lastChain = nil - l.needed = 0 - l = l.up - l.nextPairFreq = math.MaxInt32 - continue - } - - prevFreq := l.lastChain.freq - if l.nextCharFreq < l.nextPairFreq { - // The next item on this row is a leaf node. - n := l.lastChain.leafCount + 1 - l.lastChain = &chain{l.nextCharFreq, n, l.lastChain.up} - l.nextCharFreq = list[n].freq - } else { - // The next item on this row is a pair from the previous row. - // nextPairFreq isn't valid until we generate two - // more values in the level below - l.lastChain = &chain{l.nextPairFreq, l.lastChain.leafCount, l.down.lastChain} - l.down.needed = 2 - } - - if l.needed--; l.needed == 0 { - // We've done everything we need to do for this level. - // Continue calculating one level up. Fill in nextPairFreq - // of that level with the sum of the two nodes we've just calculated on - // this level. - up := l.up - if up == nil { - // All done! - return - } - up.nextPairFreq = prevFreq + l.lastChain.freq - l = up - } else { - // If we stole from below, move down temporarily to replenish it. - for l.down.needed > 0 { - l = l.down - } - } - } -} - // Return the number of literals assigned to each bit size in the Huffman encoding // // This method is only called when list.length >= 3 @@ -195,7 +140,9 @@ func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { // The tree can't have greater depth than n - 1, no matter what. This // saves a little bit of work in some small cases - maxBits = minInt32(maxBits, n-1) + if maxBits > n-1 { + maxBits = n - 1 + } // Create information about each of the levels. // A bogus "Level 0" whose sole purpose is so that diff --git a/src/pkg/compress/flate/inflate.go b/src/pkg/compress/flate/inflate.go index 3845f1204..3f2042bfe 100644 --- a/src/pkg/compress/flate/inflate.go +++ b/src/pkg/compress/flate/inflate.go @@ -10,7 +10,6 @@ package flate import ( "bufio" "io" - "os" "strconv" ) @@ -25,33 +24,33 @@ const ( // A CorruptInputError reports the presence of corrupt input at a given offset. type CorruptInputError int64 -func (e CorruptInputError) String() string { - return "flate: corrupt input before offset " + strconv.Itoa64(int64(e)) +func (e CorruptInputError) Error() string { + return "flate: corrupt input before offset " + strconv.FormatInt(int64(e), 10) } // An InternalError reports an error in the flate code itself. type InternalError string -func (e InternalError) String() string { return "flate: internal error: " + string(e) } +func (e InternalError) Error() string { return "flate: internal error: " + string(e) } // A ReadError reports an error encountered while reading input. type ReadError struct { - Offset int64 // byte offset where error occurred - Error os.Error // error returned by underlying Read + Offset int64 // byte offset where error occurred + Err error // error returned by underlying Read } -func (e *ReadError) String() string { - return "flate: read error at offset " + strconv.Itoa64(e.Offset) + ": " + e.Error.String() +func (e *ReadError) Error() string { + return "flate: read error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error() } // A WriteError reports an error encountered while writing output. type WriteError struct { - Offset int64 // byte offset where error occurred - Error os.Error // error returned by underlying Write + Offset int64 // byte offset where error occurred + Err error // error returned by underlying Write } -func (e *WriteError) String() string { - return "flate: write error at offset " + strconv.Itoa64(e.Offset) + ": " + e.Error.String() +func (e *WriteError) Error() string { + return "flate: write error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error() } // Huffman decoder is based on @@ -190,7 +189,7 @@ var fixedHuffmanDecoder = huffmanDecoder{ // the NewReader will introduce its own buffering. type Reader interface { io.Reader - ReadByte() (c byte, err os.Error) + ReadByte() (c byte, err error) } // Decompress state. @@ -224,7 +223,7 @@ type decompressor struct { // and decompression state. step func(*decompressor) final bool - err os.Error + err error toRead []byte hl, hd *huffmanDecoder copyLen int @@ -237,7 +236,7 @@ func (f *decompressor) nextBlock() { f.flush((*decompressor).nextBlock) return } - f.err = os.EOF + f.err = io.EOF return } for f.nb < 1+2 { @@ -272,7 +271,7 @@ func (f *decompressor) nextBlock() { } } -func (f *decompressor) Read(b []byte) (int, os.Error) { +func (f *decompressor) Read(b []byte) (int, error) { for { if len(f.toRead) > 0 { n := copy(b, f.toRead) @@ -287,8 +286,8 @@ func (f *decompressor) Read(b []byte) (int, os.Error) { panic("unreachable") } -func (f *decompressor) Close() os.Error { - if f.err == os.EOF { +func (f *decompressor) Close() error { + if f.err == io.EOF { return nil } return f.err @@ -299,7 +298,7 @@ func (f *decompressor) Close() os.Error { var codeOrder = [...]int{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} -func (f *decompressor) readHuffman() os.Error { +func (f *decompressor) readHuffman() error { // HLIT[5], HDIST[5], HCLEN[4]. for f.nb < 5+5+4 { if err := f.moreBits(); err != nil { @@ -625,10 +624,10 @@ func (f *decompressor) setDict(dict []byte) { f.hw = f.hp } -func (f *decompressor) moreBits() os.Error { +func (f *decompressor) moreBits() error { c, err := f.r.ReadByte() if err != nil { - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } return err @@ -640,7 +639,7 @@ func (f *decompressor) moreBits() os.Error { } // Read the next Huffman-encoded symbol from f according to h. -func (f *decompressor) huffSym(h *huffmanDecoder) (int, os.Error) { +func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) { for n := uint(h.min); n <= uint(h.max); n++ { lim := h.limit[n] if lim == -1 { diff --git a/src/pkg/compress/flate/util.go b/src/pkg/compress/flate/util.go deleted file mode 100644 index aca5c78b2..000000000 --- a/src/pkg/compress/flate/util.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package flate - -func min(left int, right int) int { - if left < right { - return left - } - return right -} - -func minInt32(left int32, right int32) int32 { - if left < right { - return left - } - return right -} - -func max(left int, right int) int { - if left > right { - return left - } - return right -} - -func fillInts(a []int, value int) { - for i := range a { - a[i] = value - } -} - -func fillInt32s(a []int32, value int32) { - for i := range a { - a[i] = value - } -} - -func fillBytes(a []byte, value byte) { - for i := range a { - a[i] = value - } -} - -func fillInt8s(a []int8, value int8) { - for i := range a { - a[i] = value - } -} - -func fillUint8s(a []uint8, value uint8) { - for i := range a { - a[i] = value - } -} - -func copyInt8s(dst []int8, src []int8) int { - cnt := min(len(dst), len(src)) - for i := 0; i < cnt; i++ { - dst[i] = src[i] - } - return cnt -} - -func copyUint8s(dst []uint8, src []uint8) int { - cnt := min(len(dst), len(src)) - for i := 0; i < cnt; i++ { - dst[i] = src[i] - } - return cnt -} diff --git a/src/pkg/compress/gzip/Makefile b/src/pkg/compress/gzip/Makefile deleted file mode 100644 index b671fc72c..000000000 --- a/src/pkg/compress/gzip/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=compress/gzip -GOFILES=\ - gunzip.go\ - gzip.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/gzip/gunzip.go b/src/pkg/compress/gzip/gunzip.go index 6ac9293d7..33736f635 100644 --- a/src/pkg/compress/gzip/gunzip.go +++ b/src/pkg/compress/gzip/gunzip.go @@ -9,15 +9,13 @@ package gzip import ( "bufio" "compress/flate" + "errors" "hash" "hash/crc32" "io" - "os" + "time" ) -// BUG(nigeltao): Comments and Names don't properly map UTF-8 character codes outside of -// the 0x00-0x7f range to ISO 8859-1 (Latin-1). - const ( gzipID1 = 0x1f gzipID2 = 0x8b @@ -36,34 +34,38 @@ func makeReader(r io.Reader) flate.Reader { return bufio.NewReader(r) } -var HeaderError = os.NewError("invalid gzip header") -var ChecksumError = os.NewError("gzip checksum error") +var ( + // ErrChecksum is returned when reading GZIP data that has an invalid checksum. + ErrChecksum = errors.New("gzip: invalid checksum") + // ErrHeader is returned when reading GZIP data that has an invalid header. + ErrHeader = errors.New("gzip: invalid header") +) // The gzip file stores a header giving metadata about the compressed file. -// That header is exposed as the fields of the Compressor and Decompressor structs. +// That header is exposed as the fields of the Writer and Reader structs. type Header struct { - Comment string // comment - Extra []byte // "extra data" - Mtime uint32 // modification time (seconds since January 1, 1970) - Name string // file name - OS byte // operating system type + Comment string // comment + Extra []byte // "extra data" + ModTime time.Time // modification time + Name string // file name + OS byte // operating system type } -// An Decompressor is an io.Reader that can be read to retrieve +// A Reader is an io.Reader that can be read to retrieve // uncompressed data from a gzip-format compressed file. // // In general, a gzip file can be a concatenation of gzip files, -// each with its own header. Reads from the Decompressor +// each with its own header. Reads from the Reader // return the concatenation of the uncompressed data of each. -// Only the first header is recorded in the Decompressor fields. +// Only the first header is recorded in the Reader fields. // // Gzip files store a length and checksum of the uncompressed data. -// The Decompressor will return a ChecksumError when Read +// The Reader will return a ErrChecksum when Read // reaches the end of the uncompressed data if it does not // have the expected length or checksum. Clients should treat data -// returned by Read as tentative until they receive the successful -// (zero length, nil error) Read marking the end of the data. -type Decompressor struct { +// returned by Read as tentative until they receive the io.EOF +// marking the end of the data. +type Reader struct { Header r flate.Reader decompressor io.ReadCloser @@ -71,18 +73,17 @@ type Decompressor struct { size uint32 flg byte buf [512]byte - err os.Error + err error } -// NewReader creates a new Decompressor reading the given reader. +// NewReader creates a new Reader reading the given reader. // The implementation buffers input and may read more data than necessary from r. -// It is the caller's responsibility to call Close on the Decompressor when done. -func NewReader(r io.Reader) (*Decompressor, os.Error) { - z := new(Decompressor) +// It is the caller's responsibility to call Close on the Reader when done. +func NewReader(r io.Reader) (*Reader, error) { + z := new(Reader) z.r = makeReader(r) z.digest = crc32.NewIEEE() if err := z.readHeader(true); err != nil { - z.err = err return nil, err } return z, nil @@ -93,26 +94,36 @@ func get4(p []byte) uint32 { return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 } -func (z *Decompressor) readString() (string, os.Error) { - var err os.Error +func (z *Reader) readString() (string, error) { + var err error + needconv := false for i := 0; ; i++ { if i >= len(z.buf) { - return "", HeaderError + return "", ErrHeader } z.buf[i], err = z.r.ReadByte() if err != nil { return "", err } + if z.buf[i] > 0x7f { + needconv = true + } if z.buf[i] == 0 { // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). - // TODO(nigeltao): Convert from ISO 8859-1 (Latin-1) to UTF-8. + if needconv { + s := make([]rune, 0, i) + for _, v := range z.buf[0:i] { + s = append(s, rune(v)) + } + return string(s), nil + } return string(z.buf[0:i]), nil } } panic("not reached") } -func (z *Decompressor) read2() (uint32, os.Error) { +func (z *Reader) read2() (uint32, error) { _, err := io.ReadFull(z.r, z.buf[0:2]) if err != nil { return 0, err @@ -120,17 +131,17 @@ func (z *Decompressor) read2() (uint32, os.Error) { return uint32(z.buf[0]) | uint32(z.buf[1])<<8, nil } -func (z *Decompressor) readHeader(save bool) os.Error { +func (z *Reader) readHeader(save bool) error { _, err := io.ReadFull(z.r, z.buf[0:10]) if err != nil { return err } if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate { - return HeaderError + return ErrHeader } z.flg = z.buf[3] if save { - z.Mtime = get4(z.buf[4:8]) + z.ModTime = time.Unix(int64(get4(z.buf[4:8])), 0) // z.buf[8] is xfl, ignored z.OS = z.buf[9] } @@ -177,7 +188,7 @@ func (z *Decompressor) readHeader(save bool) os.Error { } sum := z.digest.Sum32() & 0xFFFF if n != sum { - return HeaderError + return ErrHeader } } @@ -186,7 +197,7 @@ func (z *Decompressor) readHeader(save bool) os.Error { return nil } -func (z *Decompressor) Read(p []byte) (n int, err os.Error) { +func (z *Reader) Read(p []byte) (n int, err error) { if z.err != nil { return 0, z.err } @@ -197,7 +208,7 @@ func (z *Decompressor) Read(p []byte) (n int, err os.Error) { n, err = z.decompressor.Read(p) z.digest.Write(p[0:n]) z.size += uint32(n) - if n != 0 || err != os.EOF { + if n != 0 || err != io.EOF { z.err = err return } @@ -210,7 +221,7 @@ func (z *Decompressor) Read(p []byte) (n int, err os.Error) { crc32, isize := get4(z.buf[0:4]), get4(z.buf[4:8]) sum := z.digest.Sum32() if sum != crc32 || isize != z.size { - z.err = ChecksumError + z.err = ErrChecksum return 0, z.err } @@ -226,5 +237,5 @@ func (z *Decompressor) Read(p []byte) (n int, err os.Error) { return z.Read(p) } -// Calling Close does not close the wrapped io.Reader originally passed to NewReader. -func (z *Decompressor) Close() os.Error { return z.decompressor.Close() } +// Close closes the Reader. It does not close the underlying io.Reader. +func (z *Reader) Close() error { return z.decompressor.Close() } diff --git a/src/pkg/compress/gzip/gunzip_test.go b/src/pkg/compress/gzip/gunzip_test.go index 1c08c7374..a1333580d 100644 --- a/src/pkg/compress/gzip/gunzip_test.go +++ b/src/pkg/compress/gzip/gunzip_test.go @@ -7,7 +7,6 @@ package gzip import ( "bytes" "io" - "os" "testing" ) @@ -16,7 +15,7 @@ type gunzipTest struct { desc string raw string gzip []byte - err os.Error + err error } var gunzipTests = []gunzipTest{ @@ -233,7 +232,7 @@ var gunzipTests = []gunzipTest{ 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0x0c, 0x00, 0x00, 0x00, 'g', 'a', 'r', 'b', 'a', 'g', 'e', '!', '!', '!', }, - HeaderError, + ErrHeader, }, { // has 1 non-empty fixed huffman block not enough header "hello.txt", @@ -261,7 +260,7 @@ var gunzipTests = []gunzipTest{ 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, }, - ChecksumError, + ErrChecksum, }, { // has 1 non-empty fixed huffman block but corrupt size "hello.txt", @@ -275,7 +274,7 @@ var gunzipTests = []gunzipTest{ 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0xff, 0x00, 0x00, 0x00, }, - ChecksumError, + ErrChecksum, }, } diff --git a/src/pkg/compress/gzip/gzip.go b/src/pkg/compress/gzip/gzip.go index 8860d10af..3035dfffc 100644 --- a/src/pkg/compress/gzip/gzip.go +++ b/src/pkg/compress/gzip/gzip.go @@ -6,10 +6,11 @@ package gzip import ( "compress/flate" + "errors" + "fmt" "hash" "hash/crc32" "io" - "os" ) // These constants are copied from the flate package, so that code that imports @@ -21,9 +22,9 @@ const ( DefaultCompression = flate.DefaultCompression ) -// A Compressor is an io.WriteCloser that satisfies writes by compressing data written +// A Writer is an io.WriteCloser that satisfies writes by compressing data written // to its wrapped io.Writer. -type Compressor struct { +type Writer struct { Header w io.Writer level int @@ -32,28 +33,43 @@ type Compressor struct { size uint32 closed bool buf [10]byte - err os.Error + err error } -// NewWriter calls NewWriterLevel with the default compression level. -func NewWriter(w io.Writer) (*Compressor, os.Error) { - return NewWriterLevel(w, DefaultCompression) +// NewWriter creates a new Writer that satisfies writes by compressing data +// written to w. +// +// It is the caller's responsibility to call Close on the WriteCloser when done. +// Writes may be buffered and not flushed until Close. +// +// Callers that wish to set the fields in Writer.Header must do so before +// the first call to Write or Close. The Comment and Name header fields are +// UTF-8 strings in Go, but the underlying format requires NUL-terminated ISO +// 8859-1 (Latin-1). NUL or non-Latin-1 runes in those strings will lead to an +// error on Write. +func NewWriter(w io.Writer) *Writer { + z, _ := NewWriterLevel(w, DefaultCompression) + return z } -// NewWriterLevel creates a new Compressor writing to the given writer. -// Writes may be buffered and not flushed until Close. -// Callers that wish to set the fields in Compressor.Header must -// do so before the first call to Write or Close. -// It is the caller's responsibility to call Close on the WriteCloser when done. -// level is the compression level, which can be DefaultCompression, NoCompression, -// or any integer value between BestSpeed and BestCompression (inclusive). -func NewWriterLevel(w io.Writer, level int) (*Compressor, os.Error) { - z := new(Compressor) - z.OS = 255 // unknown - z.w = w - z.level = level - z.digest = crc32.NewIEEE() - return z, nil +// NewWriterLevel is like NewWriter but specifies the compression level instead +// of assuming DefaultCompression. +// +// The compression level can be DefaultCompression, NoCompression, or any +// integer value between BestSpeed and BestCompression inclusive. The error +// returned will be nil if the level is valid. +func NewWriterLevel(w io.Writer, level int) (*Writer, error) { + if level < DefaultCompression || level > BestCompression { + return nil, fmt.Errorf("gzip: invalid compression level: %d", level) + } + return &Writer{ + Header: Header{ + OS: 255, // unknown + }, + w: w, + level: level, + digest: crc32.NewIEEE(), + }, nil } // GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950). @@ -70,9 +86,9 @@ func put4(p []byte, v uint32) { } // writeBytes writes a length-prefixed byte slice to z.w. -func (z *Compressor) writeBytes(b []byte) os.Error { +func (z *Writer) writeBytes(b []byte) error { if len(b) > 0xffff { - return os.NewError("gzip.Write: Extra data is too large") + return errors.New("gzip.Write: Extra data is too large") } put2(z.buf[0:2], uint16(len(b))) _, err := z.w.Write(z.buf[0:2]) @@ -83,16 +99,28 @@ func (z *Compressor) writeBytes(b []byte) os.Error { return err } -// writeString writes a string (in ISO 8859-1 (Latin-1) format) to z.w. -func (z *Compressor) writeString(s string) os.Error { - // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). - // TODO(nigeltao): Convert from UTF-8 to ISO 8859-1 (Latin-1). +// writeString writes a UTF-8 string s in GZIP's format to z.w. +// GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). +func (z *Writer) writeString(s string) (err error) { + // GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII. + needconv := false for _, v := range s { - if v == 0 || v > 0x7f { - return os.NewError("gzip.Write: non-ASCII header string") + if v == 0 || v > 0xff { + return errors.New("gzip.Write: non-Latin-1 header string") + } + if v > 0x7f { + needconv = true + } + } + if needconv { + b := make([]byte, 0, len(s)) + for _, v := range s { + b = append(b, byte(v)) } + _, err = z.w.Write(b) + } else { + _, err = io.WriteString(z.w, s) } - _, err := io.WriteString(z.w, s) if err != nil { return err } @@ -102,7 +130,9 @@ func (z *Compressor) writeString(s string) os.Error { return err } -func (z *Compressor) Write(p []byte) (int, os.Error) { +// Write writes a compressed form of p to the underlying io.Writer. The +// compressed bytes are not necessarily flushed until the Writer is closed. +func (z *Writer) Write(p []byte) (int, error) { if z.err != nil { return 0, z.err } @@ -122,7 +152,7 @@ func (z *Compressor) Write(p []byte) (int, os.Error) { if z.Comment != "" { z.buf[3] |= 0x10 } - put4(z.buf[4:8], z.Mtime) + put4(z.buf[4:8], uint32(z.ModTime.Unix())) if z.level == BestCompression { z.buf[8] = 2 } else if z.level == BestSpeed { @@ -153,7 +183,7 @@ func (z *Compressor) Write(p []byte) (int, os.Error) { return n, z.err } } - z.compressor = flate.NewWriter(z.w, z.level) + z.compressor, _ = flate.NewWriter(z.w, z.level) } z.size += uint32(len(p)) z.digest.Write(p) @@ -161,8 +191,8 @@ func (z *Compressor) Write(p []byte) (int, os.Error) { return n, z.err } -// Calling Close does not close the wrapped io.Writer originally passed to NewWriter. -func (z *Compressor) Close() os.Error { +// Close closes the Writer. It does not close the underlying io.Writer. +func (z *Writer) Close() error { if z.err != nil { return z.err } diff --git a/src/pkg/compress/gzip/gzip_test.go b/src/pkg/compress/gzip/gzip_test.go index 121e627e6..6f7b59364 100644 --- a/src/pkg/compress/gzip/gzip_test.go +++ b/src/pkg/compress/gzip/gzip_test.go @@ -5,80 +5,155 @@ package gzip import ( - "io" + "bufio" + "bytes" "io/ioutil" "testing" + "time" ) -// pipe creates two ends of a pipe that gzip and gunzip, and runs dfunc at the -// writer end and cfunc at the reader end. -func pipe(t *testing.T, dfunc func(*Compressor), cfunc func(*Decompressor)) { - piper, pipew := io.Pipe() - defer piper.Close() - go func() { - defer pipew.Close() - compressor, err := NewWriter(pipew) - if err != nil { - t.Fatalf("%v", err) - } - defer compressor.Close() - dfunc(compressor) - }() - decompressor, err := NewReader(piper) +// TestEmpty tests that an empty payload still forms a valid GZIP stream. +func TestEmpty(t *testing.T) { + buf := new(bytes.Buffer) + + if err := NewWriter(buf).Close(); err != nil { + t.Fatalf("Writer.Close: %v", err) + } + + r, err := NewReader(buf) if err != nil { - t.Fatalf("%v", err) + t.Fatalf("NewReader: %v", err) + } + b, err := ioutil.ReadAll(r) + if err != nil { + t.Fatalf("ReadAll: %v", err) + } + if len(b) != 0 { + t.Fatalf("got %d bytes, want 0", len(b)) + } + if err := r.Close(); err != nil { + t.Fatalf("Reader.Close: %v", err) } - defer decompressor.Close() - cfunc(decompressor) } -// Tests that an empty payload still forms a valid GZIP stream. -func TestEmpty(t *testing.T) { - pipe(t, - func(compressor *Compressor) {}, - func(decompressor *Decompressor) { - b, err := ioutil.ReadAll(decompressor) - if err != nil { - t.Fatalf("%v", err) - } - if len(b) != 0 { - t.Fatalf("did not read an empty slice") - } - }) +// TestRoundTrip tests that gzipping and then gunzipping is the identity +// function. +func TestRoundTrip(t *testing.T) { + buf := new(bytes.Buffer) + + w := NewWriter(buf) + w.Comment = "comment" + w.Extra = []byte("extra") + w.ModTime = time.Unix(1e8, 0) + w.Name = "name" + if _, err := w.Write([]byte("payload")); err != nil { + t.Fatalf("Write: %v", err) + } + if err := w.Close(); err != nil { + t.Fatalf("Writer.Close: %v", err) + } + + r, err := NewReader(buf) + if err != nil { + t.Fatalf("NewReader: %v", err) + } + b, err := ioutil.ReadAll(r) + if err != nil { + t.Fatalf("ReadAll: %v", err) + } + if string(b) != "payload" { + t.Fatalf("payload is %q, want %q", string(b), "payload") + } + if r.Comment != "comment" { + t.Fatalf("comment is %q, want %q", r.Comment, "comment") + } + if string(r.Extra) != "extra" { + t.Fatalf("extra is %q, want %q", r.Extra, "extra") + } + if r.ModTime.Unix() != 1e8 { + t.Fatalf("mtime is %d, want %d", r.ModTime.Unix(), uint32(1e8)) + } + if r.Name != "name" { + t.Fatalf("name is %q, want %q", r.Name, "name") + } + if err := r.Close(); err != nil { + t.Fatalf("Reader.Close: %v", err) + } } -// Tests that gzipping and then gunzipping is the identity function. -func TestWriter(t *testing.T) { - pipe(t, - func(compressor *Compressor) { - compressor.Comment = "comment" - compressor.Extra = []byte("extra") - compressor.Mtime = 1e8 - compressor.Name = "name" - _, err := compressor.Write([]byte("payload")) - if err != nil { - t.Fatalf("%v", err) - } - }, - func(decompressor *Decompressor) { - b, err := ioutil.ReadAll(decompressor) - if err != nil { - t.Fatalf("%v", err) - } - if string(b) != "payload" { - t.Fatalf("payload is %q, want %q", string(b), "payload") - } - if decompressor.Comment != "comment" { - t.Fatalf("comment is %q, want %q", decompressor.Comment, "comment") - } - if string(decompressor.Extra) != "extra" { - t.Fatalf("extra is %q, want %q", decompressor.Extra, "extra") - } - if decompressor.Mtime != 1e8 { - t.Fatalf("mtime is %d, want %d", decompressor.Mtime, uint32(1e8)) - } - if decompressor.Name != "name" { - t.Fatalf("name is %q, want %q", decompressor.Name, "name") - } - }) +// TestLatin1 tests the internal functions for converting to and from Latin-1. +func TestLatin1(t *testing.T) { + latin1 := []byte{0xc4, 'u', 0xdf, 'e', 'r', 'u', 'n', 'g', 0} + utf8 := "Äußerung" + z := Reader{r: bufio.NewReader(bytes.NewBuffer(latin1))} + s, err := z.readString() + if err != nil { + t.Fatalf("readString: %v", err) + } + if s != utf8 { + t.Fatalf("read latin-1: got %q, want %q", s, utf8) + } + + buf := bytes.NewBuffer(make([]byte, 0, len(latin1))) + c := Writer{w: buf} + if err = c.writeString(utf8); err != nil { + t.Fatalf("writeString: %v", err) + } + s = buf.String() + if s != string(latin1) { + t.Fatalf("write utf-8: got %q, want %q", s, string(latin1)) + } +} + +// TestLatin1RoundTrip tests that metadata that is representable in Latin-1 +// survives a round trip. +func TestLatin1RoundTrip(t *testing.T) { + testCases := []struct { + name string + ok bool + }{ + {"", true}, + {"ASCII is OK", true}, + {"unless it contains a NUL\x00", false}, + {"no matter where \x00 occurs", false}, + {"\x00\x00\x00", false}, + {"Látin-1 also passes (U+00E1)", true}, + {"but LĀtin Extended-A (U+0100) does not", false}, + {"neither does 日本語", false}, + {"invalid UTF-8 also \xffails", false}, + {"\x00 as does Látin-1 with NUL", false}, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + + w := NewWriter(buf) + w.Name = tc.name + err := w.Close() + if (err == nil) != tc.ok { + t.Errorf("Writer.Close: name = %q, err = %v", tc.name, err) + continue + } + if !tc.ok { + continue + } + + r, err := NewReader(buf) + if err != nil { + t.Errorf("NewReader: %v", err) + continue + } + _, err = ioutil.ReadAll(r) + if err != nil { + t.Errorf("ReadAll: %v", err) + continue + } + if r.Name != tc.name { + t.Errorf("name is %q, want %q", r.Name, tc.name) + continue + } + if err := r.Close(); err != nil { + t.Errorf("Reader.Close: %v", err) + continue + } + } } diff --git a/src/pkg/compress/lzw/Makefile b/src/pkg/compress/lzw/Makefile deleted file mode 100644 index 28f5e6abc..000000000 --- a/src/pkg/compress/lzw/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=compress/lzw -GOFILES=\ - reader.go\ - writer.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/lzw/reader.go b/src/pkg/compress/lzw/reader.go index 21231c8e5..0ed742c89 100644 --- a/src/pkg/compress/lzw/reader.go +++ b/src/pkg/compress/lzw/reader.go @@ -16,9 +16,9 @@ package lzw import ( "bufio" + "errors" "fmt" "io" - "os" ) // Order specifies the bit ordering in an LZW data stream. @@ -45,9 +45,9 @@ type decoder struct { bits uint32 nBits uint width uint - read func(*decoder) (uint16, os.Error) // readLSB or readMSB - litWidth int // width in bits of literal codes - err os.Error + read func(*decoder) (uint16, error) // readLSB or readMSB + litWidth int // width in bits of literal codes + err error // The first 1< 0 { n := copy(b, d.toRead) @@ -132,7 +132,7 @@ func (d *decoder) decode() { for { code, err := d.read(d) if err != nil { - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } d.err = err @@ -156,7 +156,7 @@ func (d *decoder) decode() { continue case code == d.eof: d.flush() - d.err = os.EOF + d.err = io.EOF return case code <= d.hi: c, i := code, len(d.output)-1 @@ -186,7 +186,7 @@ func (d *decoder) decode() { d.prefix[d.hi] = d.last } default: - d.err = os.NewError("lzw: invalid code") + d.err = errors.New("lzw: invalid code") return } d.last, d.hi = code, d.hi+1 @@ -211,8 +211,10 @@ func (d *decoder) flush() { d.o = 0 } -func (d *decoder) Close() os.Error { - d.err = os.EINVAL // in case any Reads come along +var errClosed = errors.New("compress/lzw: reader/writer is closed") + +func (d *decoder) Close() error { + d.err = errClosed // in case any Reads come along return nil } @@ -230,7 +232,7 @@ func NewReader(r io.Reader, order Order, litWidth int) io.ReadCloser { case MSB: d.read = (*decoder).readMSB default: - d.err = os.NewError("lzw: unknown order") + d.err = errors.New("lzw: unknown order") return d } if litWidth < 2 || 8 < litWidth { diff --git a/src/pkg/compress/lzw/reader_test.go b/src/pkg/compress/lzw/reader_test.go index f8042b0d1..e5be12f54 100644 --- a/src/pkg/compress/lzw/reader_test.go +++ b/src/pkg/compress/lzw/reader_test.go @@ -8,7 +8,6 @@ import ( "bytes" "io" "io/ioutil" - "os" "runtime" "strconv" "strings" @@ -19,7 +18,7 @@ type lzwTest struct { desc string raw string compressed string - err os.Error + err error } var lzwTests = []lzwTest{ @@ -82,7 +81,7 @@ var lzwTests = []lzwTest{ } func TestReader(t *testing.T) { - b := bytes.NewBuffer(nil) + var b bytes.Buffer for _, tt := range lzwTests { d := strings.Split(tt.desc, ";") var order Order @@ -98,7 +97,7 @@ func TestReader(t *testing.T) { rc := NewReader(strings.NewReader(tt.compressed), order, litWidth) defer rc.Close() b.Reset() - n, err := io.Copy(b, rc) + n, err := io.Copy(&b, rc) if err != nil { if err != tt.err { t.Errorf("%s: io.Copy: %v want %v", tt.desc, err, tt.err) @@ -117,7 +116,7 @@ func benchmarkDecoder(b *testing.B, n int) { b.SetBytes(int64(n)) buf0, _ := ioutil.ReadFile("../testdata/e.txt") buf0 = buf0[:10000] - compressed := bytes.NewBuffer(nil) + compressed := new(bytes.Buffer) w := NewWriter(compressed, LSB, 8) for i := 0; i < n; i += len(buf0) { io.Copy(w, bytes.NewBuffer(buf0)) diff --git a/src/pkg/compress/lzw/writer.go b/src/pkg/compress/lzw/writer.go index 87143b7aa..488ba6428 100644 --- a/src/pkg/compress/lzw/writer.go +++ b/src/pkg/compress/lzw/writer.go @@ -6,27 +6,27 @@ package lzw import ( "bufio" + "errors" "fmt" "io" - "os" ) // A writer is a buffered, flushable writer. type writer interface { - WriteByte(byte) os.Error - Flush() os.Error + WriteByte(byte) error + Flush() error } // An errWriteCloser is an io.WriteCloser that always returns a given error. type errWriteCloser struct { - err os.Error + err error } -func (e *errWriteCloser) Write([]byte) (int, os.Error) { +func (e *errWriteCloser) Write([]byte) (int, error) { return 0, e.err } -func (e *errWriteCloser) Close() os.Error { +func (e *errWriteCloser) Close() error { return e.err } @@ -48,9 +48,10 @@ const ( type encoder struct { // w is the writer that compressed bytes are written to. w writer - // write, bits, nBits and width are the state for converting a code stream - // into a byte stream. - write func(*encoder, uint32) os.Error + // order, write, bits, nBits and width are the state for + // converting a code stream into a byte stream. + order Order + write func(*encoder, uint32) error bits uint32 nBits uint width uint @@ -63,8 +64,8 @@ type encoder struct { // call. It is equal to invalidCode if there was no such call. savedCode uint32 // err is the first error encountered during writing. Closing the encoder - // will make any future Write calls return os.EINVAL. - err os.Error + // will make any future Write calls return errClosed + err error // table is the hash table from 20-bit keys to 12-bit values. Each table // entry contains key<<12|val and collisions resolve by linear probing. // The keys consist of a 12-bit code prefix and an 8-bit byte suffix. @@ -73,7 +74,7 @@ type encoder struct { } // writeLSB writes the code c for "Least Significant Bits first" data. -func (e *encoder) writeLSB(c uint32) os.Error { +func (e *encoder) writeLSB(c uint32) error { e.bits |= c << e.nBits e.nBits += e.width for e.nBits >= 8 { @@ -87,7 +88,7 @@ func (e *encoder) writeLSB(c uint32) os.Error { } // writeMSB writes the code c for "Most Significant Bits first" data. -func (e *encoder) writeMSB(c uint32) os.Error { +func (e *encoder) writeMSB(c uint32) error { e.bits |= c << (32 - e.width - e.nBits) e.nBits += e.width for e.nBits >= 8 { @@ -102,12 +103,12 @@ func (e *encoder) writeMSB(c uint32) os.Error { // errOutOfCodes is an internal error that means that the encoder has run out // of unused codes and a clear code needs to be sent next. -var errOutOfCodes = os.NewError("lzw: out of codes") +var errOutOfCodes = errors.New("lzw: out of codes") // incHi increments e.hi and checks for both overflow and running out of // unused codes. In the latter case, incHi sends a clear code, resets the // encoder state and returns errOutOfCodes. -func (e *encoder) incHi() os.Error { +func (e *encoder) incHi() error { e.hi++ if e.hi == e.overflow { e.width++ @@ -130,7 +131,7 @@ func (e *encoder) incHi() os.Error { } // Write writes a compressed representation of p to e's underlying writer. -func (e *encoder) Write(p []byte) (int, os.Error) { +func (e *encoder) Write(p []byte) (int, error) { if e.err != nil { return 0, e.err } @@ -188,15 +189,15 @@ loop: // Close closes the encoder, flushing any pending output. It does not close or // flush e's underlying writer. -func (e *encoder) Close() os.Error { +func (e *encoder) Close() error { if e.err != nil { - if e.err == os.EINVAL { + if e.err == errClosed { return nil } return e.err } - // Make any future calls to Write return os.EINVAL. - e.err = os.EINVAL + // Make any future calls to Write return errClosed. + e.err = errClosed // Write the savedCode if valid. if e.savedCode != invalidCode { if err := e.write(e, e.savedCode); err != nil { @@ -213,7 +214,7 @@ func (e *encoder) Close() os.Error { } // Write the final bits. if e.nBits > 0 { - if e.write == (*encoder).writeMSB { + if e.order == MSB { e.bits >>= 24 } if err := e.w.WriteByte(uint8(e.bits)); err != nil { @@ -230,14 +231,14 @@ func (e *encoder) Close() os.Error { // The number of bits to use for literal codes, litWidth, must be in the // range [2,8] and is typically 8. func NewWriter(w io.Writer, order Order, litWidth int) io.WriteCloser { - var write func(*encoder, uint32) os.Error + var write func(*encoder, uint32) error switch order { case LSB: write = (*encoder).writeLSB case MSB: write = (*encoder).writeMSB default: - return &errWriteCloser{os.NewError("lzw: unknown order")} + return &errWriteCloser{errors.New("lzw: unknown order")} } if litWidth < 2 || 8 < litWidth { return &errWriteCloser{fmt.Errorf("lzw: litWidth %d out of range", litWidth)} @@ -249,6 +250,7 @@ func NewWriter(w io.Writer, order Order, litWidth int) io.WriteCloser { lw := uint(litWidth) return &encoder{ w: bw, + order: order, write: write, width: 1 + lw, litWidth: lw, diff --git a/src/pkg/compress/lzw/writer_test.go b/src/pkg/compress/lzw/writer_test.go index 4c5e522f9..d249a09b2 100644 --- a/src/pkg/compress/lzw/writer_test.go +++ b/src/pkg/compress/lzw/writer_test.go @@ -45,20 +45,16 @@ func testFile(t *testing.T, fn string, order Order, litWidth int) { var b [4096]byte for { n, err0 := raw.Read(b[:]) - if err0 != nil && err0 != os.EOF { + if err0 != nil && err0 != io.EOF { t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err0) return } _, err1 := lzww.Write(b[:n]) - if err1 == os.EPIPE { - // Fail, but do not report the error, as some other (presumably reportable) error broke the pipe. - return - } if err1 != nil { t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err1) return } - if err0 == os.EOF { + if err0 == io.EOF { break } } diff --git a/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt b/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt new file mode 100644 index 000000000..8d0ff4e65 --- /dev/null +++ b/src/pkg/compress/testdata/Mark.Twain-Tom.Sawyer.txt @@ -0,0 +1,8858 @@ +The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete +by Mark Twain (Samuel Clemens) + +This eBook is for the use of anyone anywhere at no cost and with +almost no restrictions whatsoever. You may copy it, give it away or +re-use it under the terms of the Project Gutenberg License included +with this eBook or online at www.gutenberg.net + + +Title: The Adventures of Tom Sawyer, Complete + +Author: Mark Twain (Samuel Clemens) + +Release Date: August 20, 2006 [EBook #74] +[Last updated: May 3, 2011] + +Language: English + + +*** START OF THIS PROJECT GUTENBERG EBOOK TOM SAWYER *** + + + + +Produced by David Widger. The previous edition was updated by Jose +Menendez. + + + + + + THE ADVENTURES OF TOM SAWYER + BY + MARK TWAIN + (Samuel Langhorne Clemens) + + + + + P R E F A C E + +MOST of the adventures recorded in this book really occurred; one or +two were experiences of my own, the rest those of boys who were +schoolmates of mine. Huck Finn is drawn from life; Tom Sawyer also, but +not from an individual--he is a combination of the characteristics of +three boys whom I knew, and therefore belongs to the composite order of +architecture. + +The odd superstitions touched upon were all prevalent among children +and slaves in the West at the period of this story--that is to say, +thirty or forty years ago. + +Although my book is intended mainly for the entertainment of boys and +girls, I hope it will not be shunned by men and women on that account, +for part of my plan has been to try to pleasantly remind adults of what +they once were themselves, and of how they felt and thought and talked, +and what queer enterprises they sometimes engaged in. + + THE AUTHOR. + +HARTFORD, 1876. + + + + T O M S A W Y E R + + + +CHAPTER I + +"TOM!" + +No answer. + +"TOM!" + +No answer. + +"What's gone with that boy, I wonder? You TOM!" + +No answer. + +The old lady pulled her spectacles down and looked over them about the +room; then she put them up and looked out under them. She seldom or +never looked THROUGH them for so small a thing as a boy; they were her +state pair, the pride of her heart, and were built for "style," not +service--she could have seen through a pair of stove-lids just as well. +She looked perplexed for a moment, and then said, not fiercely, but +still loud enough for the furniture to hear: + +"Well, I lay if I get hold of you I'll--" + +She did not finish, for by this time she was bending down and punching +under the bed with the broom, and so she needed breath to punctuate the +punches with. She resurrected nothing but the cat. + +"I never did see the beat of that boy!" + +She went to the open door and stood in it and looked out among the +tomato vines and "jimpson" weeds that constituted the garden. No Tom. +So she lifted up her voice at an angle calculated for distance and +shouted: + +"Y-o-u-u TOM!" + +There was a slight noise behind her and she turned just in time to +seize a small boy by the slack of his roundabout and arrest his flight. + +"There! I might 'a' thought of that closet. What you been doing in +there?" + +"Nothing." + +"Nothing! Look at your hands. And look at your mouth. What IS that +truck?" + +"I don't know, aunt." + +"Well, I know. It's jam--that's what it is. Forty times I've said if +you didn't let that jam alone I'd skin you. Hand me that switch." + +The switch hovered in the air--the peril was desperate-- + +"My! Look behind you, aunt!" + +The old lady whirled round, and snatched her skirts out of danger. The +lad fled on the instant, scrambled up the high board-fence, and +disappeared over it. + +His aunt Polly stood surprised a moment, and then broke into a gentle +laugh. + +"Hang the boy, can't I never learn anything? Ain't he played me tricks +enough like that for me to be looking out for him by this time? But old +fools is the biggest fools there is. Can't learn an old dog new tricks, +as the saying is. But my goodness, he never plays them alike, two days, +and how is a body to know what's coming? He 'pears to know just how +long he can torment me before I get my dander up, and he knows if he +can make out to put me off for a minute or make me laugh, it's all down +again and I can't hit him a lick. I ain't doing my duty by that boy, +and that's the Lord's truth, goodness knows. Spare the rod and spile +the child, as the Good Book says. I'm a laying up sin and suffering for +us both, I know. He's full of the Old Scratch, but laws-a-me! he's my +own dead sister's boy, poor thing, and I ain't got the heart to lash +him, somehow. Every time I let him off, my conscience does hurt me so, +and every time I hit him my old heart most breaks. Well-a-well, man +that is born of woman is of few days and full of trouble, as the +Scripture says, and I reckon it's so. He'll play hookey this evening, * +and [* Southwestern for "afternoon"] I'll just be obleeged to make him +work, to-morrow, to punish him. It's mighty hard to make him work +Saturdays, when all the boys is having holiday, but he hates work more +than he hates anything else, and I've GOT to do some of my duty by him, +or I'll be the ruination of the child." + +Tom did play hookey, and he had a very good time. He got back home +barely in season to help Jim, the small colored boy, saw next-day's +wood and split the kindlings before supper--at least he was there in +time to tell his adventures to Jim while Jim did three-fourths of the +work. Tom's younger brother (or rather half-brother) Sid was already +through with his part of the work (picking up chips), for he was a +quiet boy, and had no adventurous, troublesome ways. + +While Tom was eating his supper, and stealing sugar as opportunity +offered, Aunt Polly asked him questions that were full of guile, and +very deep--for she wanted to trap him into damaging revealments. Like +many other simple-hearted souls, it was her pet vanity to believe she +was endowed with a talent for dark and mysterious diplomacy, and she +loved to contemplate her most transparent devices as marvels of low +cunning. Said she: + +"Tom, it was middling warm in school, warn't it?" + +"Yes'm." + +"Powerful warm, warn't it?" + +"Yes'm." + +"Didn't you want to go in a-swimming, Tom?" + +A bit of a scare shot through Tom--a touch of uncomfortable suspicion. +He searched Aunt Polly's face, but it told him nothing. So he said: + +"No'm--well, not very much." + +The old lady reached out her hand and felt Tom's shirt, and said: + +"But you ain't too warm now, though." And it flattered her to reflect +that she had discovered that the shirt was dry without anybody knowing +that that was what she had in her mind. But in spite of her, Tom knew +where the wind lay, now. So he forestalled what might be the next move: + +"Some of us pumped on our heads--mine's damp yet. See?" + +Aunt Polly was vexed to think she had overlooked that bit of +circumstantial evidence, and missed a trick. Then she had a new +inspiration: + +"Tom, you didn't have to undo your shirt collar where I sewed it, to +pump on your head, did you? Unbutton your jacket!" + +The trouble vanished out of Tom's face. He opened his jacket. His +shirt collar was securely sewed. + +"Bother! Well, go 'long with you. I'd made sure you'd played hookey +and been a-swimming. But I forgive ye, Tom. I reckon you're a kind of a +singed cat, as the saying is--better'n you look. THIS time." + +She was half sorry her sagacity had miscarried, and half glad that Tom +had stumbled into obedient conduct for once. + +But Sidney said: + +"Well, now, if I didn't think you sewed his collar with white thread, +but it's black." + +"Why, I did sew it with white! Tom!" + +But Tom did not wait for the rest. As he went out at the door he said: + +"Siddy, I'll lick you for that." + +In a safe place Tom examined two large needles which were thrust into +the lapels of his jacket, and had thread bound about them--one needle +carried white thread and the other black. He said: + +"She'd never noticed if it hadn't been for Sid. Confound it! sometimes +she sews it with white, and sometimes she sews it with black. I wish to +geeminy she'd stick to one or t'other--I can't keep the run of 'em. But +I bet you I'll lam Sid for that. I'll learn him!" + +He was not the Model Boy of the village. He knew the model boy very +well though--and loathed him. + +Within two minutes, or even less, he had forgotten all his troubles. +Not because his troubles were one whit less heavy and bitter to him +than a man's are to a man, but because a new and powerful interest bore +them down and drove them out of his mind for the time--just as men's +misfortunes are forgotten in the excitement of new enterprises. This +new interest was a valued novelty in whistling, which he had just +acquired from a negro, and he was suffering to practise it undisturbed. +It consisted in a peculiar bird-like turn, a sort of liquid warble, +produced by touching the tongue to the roof of the mouth at short +intervals in the midst of the music--the reader probably remembers how +to do it, if he has ever been a boy. Diligence and attention soon gave +him the knack of it, and he strode down the street with his mouth full +of harmony and his soul full of gratitude. He felt much as an +astronomer feels who has discovered a new planet--no doubt, as far as +strong, deep, unalloyed pleasure is concerned, the advantage was with +the boy, not the astronomer. + +The summer evenings were long. It was not dark, yet. Presently Tom +checked his whistle. A stranger was before him--a boy a shade larger +than himself. A new-comer of any age or either sex was an impressive +curiosity in the poor little shabby village of St. Petersburg. This boy +was well dressed, too--well dressed on a week-day. This was simply +astounding. His cap was a dainty thing, his close-buttoned blue cloth +roundabout was new and natty, and so were his pantaloons. He had shoes +on--and it was only Friday. He even wore a necktie, a bright bit of +ribbon. He had a citified air about him that ate into Tom's vitals. The +more Tom stared at the splendid marvel, the higher he turned up his +nose at his finery and the shabbier and shabbier his own outfit seemed +to him to grow. Neither boy spoke. If one moved, the other moved--but +only sidewise, in a circle; they kept face to face and eye to eye all +the time. Finally Tom said: + +"I can lick you!" + +"I'd like to see you try it." + +"Well, I can do it." + +"No you can't, either." + +"Yes I can." + +"No you can't." + +"I can." + +"You can't." + +"Can!" + +"Can't!" + +An uncomfortable pause. Then Tom said: + +"What's your name?" + +"'Tisn't any of your business, maybe." + +"Well I 'low I'll MAKE it my business." + +"Well why don't you?" + +"If you say much, I will." + +"Much--much--MUCH. There now." + +"Oh, you think you're mighty smart, DON'T you? I could lick you with +one hand tied behind me, if I wanted to." + +"Well why don't you DO it? You SAY you can do it." + +"Well I WILL, if you fool with me." + +"Oh yes--I've seen whole families in the same fix." + +"Smarty! You think you're SOME, now, DON'T you? Oh, what a hat!" + +"You can lump that hat if you don't like it. I dare you to knock it +off--and anybody that'll take a dare will suck eggs." + +"You're a liar!" + +"You're another." + +"You're a fighting liar and dasn't take it up." + +"Aw--take a walk!" + +"Say--if you give me much more of your sass I'll take and bounce a +rock off'n your head." + +"Oh, of COURSE you will." + +"Well I WILL." + +"Well why don't you DO it then? What do you keep SAYING you will for? +Why don't you DO it? It's because you're afraid." + +"I AIN'T afraid." + +"You are." + +"I ain't." + +"You are." + +Another pause, and more eying and sidling around each other. Presently +they were shoulder to shoulder. Tom said: + +"Get away from here!" + +"Go away yourself!" + +"I won't." + +"I won't either." + +So they stood, each with a foot placed at an angle as a brace, and +both shoving with might and main, and glowering at each other with +hate. But neither could get an advantage. After struggling till both +were hot and flushed, each relaxed his strain with watchful caution, +and Tom said: + +"You're a coward and a pup. I'll tell my big brother on you, and he +can thrash you with his little finger, and I'll make him do it, too." + +"What do I care for your big brother? I've got a brother that's bigger +than he is--and what's more, he can throw him over that fence, too." +[Both brothers were imaginary.] + +"That's a lie." + +"YOUR saying so don't make it so." + +Tom drew a line in the dust with his big toe, and said: + +"I dare you to step over that, and I'll lick you till you can't stand +up. Anybody that'll take a dare will steal sheep." + +The new boy stepped over promptly, and said: + +"Now you said you'd do it, now let's see you do it." + +"Don't you crowd me now; you better look out." + +"Well, you SAID you'd do it--why don't you do it?" + +"By jingo! for two cents I WILL do it." + +The new boy took two broad coppers out of his pocket and held them out +with derision. Tom struck them to the ground. In an instant both boys +were rolling and tumbling in the dirt, gripped together like cats; and +for the space of a minute they tugged and tore at each other's hair and +clothes, punched and scratched each other's nose, and covered +themselves with dust and glory. Presently the confusion took form, and +through the fog of battle Tom appeared, seated astride the new boy, and +pounding him with his fists. "Holler 'nuff!" said he. + +The boy only struggled to free himself. He was crying--mainly from rage. + +"Holler 'nuff!"--and the pounding went on. + +At last the stranger got out a smothered "'Nuff!" and Tom let him up +and said: + +"Now that'll learn you. Better look out who you're fooling with next +time." + +The new boy went off brushing the dust from his clothes, sobbing, +snuffling, and occasionally looking back and shaking his head and +threatening what he would do to Tom the "next time he caught him out." +To which Tom responded with jeers, and started off in high feather, and +as soon as his back was turned the new boy snatched up a stone, threw +it and hit him between the shoulders and then turned tail and ran like +an antelope. Tom chased the traitor home, and thus found out where he +lived. He then held a position at the gate for some time, daring the +enemy to come outside, but the enemy only made faces at him through the +window and declined. At last the enemy's mother appeared, and called +Tom a bad, vicious, vulgar child, and ordered him away. So he went +away; but he said he "'lowed" to "lay" for that boy. + +He got home pretty late that night, and when he climbed cautiously in +at the window, he uncovered an ambuscade, in the person of his aunt; +and when she saw the state his clothes were in her resolution to turn +his Saturday holiday into captivity at hard labor became adamantine in +its firmness. + + + +CHAPTER II + +SATURDAY morning was come, and all the summer world was bright and +fresh, and brimming with life. There was a song in every heart; and if +the heart was young the music issued at the lips. There was cheer in +every face and a spring in every step. The locust-trees were in bloom +and the fragrance of the blossoms filled the air. Cardiff Hill, beyond +the village and above it, was green with vegetation and it lay just far +enough away to seem a Delectable Land, dreamy, reposeful, and inviting. + +Tom appeared on the sidewalk with a bucket of whitewash and a +long-handled brush. He surveyed the fence, and all gladness left him and +a deep melancholy settled down upon his spirit. Thirty yards of board +fence nine feet high. Life to him seemed hollow, and existence but a +burden. Sighing, he dipped his brush and passed it along the topmost +plank; repeated the operation; did it again; compared the insignificant +whitewashed streak with the far-reaching continent of unwhitewashed +fence, and sat down on a tree-box discouraged. Jim came skipping out at +the gate with a tin pail, and singing Buffalo Gals. Bringing water from +the town pump had always been hateful work in Tom's eyes, before, but +now it did not strike him so. He remembered that there was company at +the pump. White, mulatto, and negro boys and girls were always there +waiting their turns, resting, trading playthings, quarrelling, +fighting, skylarking. And he remembered that although the pump was only +a hundred and fifty yards off, Jim never got back with a bucket of +water under an hour--and even then somebody generally had to go after +him. Tom said: + +"Say, Jim, I'll fetch the water if you'll whitewash some." + +Jim shook his head and said: + +"Can't, Mars Tom. Ole missis, she tole me I got to go an' git dis +water an' not stop foolin' roun' wid anybody. She say she spec' Mars +Tom gwine to ax me to whitewash, an' so she tole me go 'long an' 'tend +to my own business--she 'lowed SHE'D 'tend to de whitewashin'." + +"Oh, never you mind what she said, Jim. That's the way she always +talks. Gimme the bucket--I won't be gone only a a minute. SHE won't +ever know." + +"Oh, I dasn't, Mars Tom. Ole missis she'd take an' tar de head off'n +me. 'Deed she would." + +"SHE! She never licks anybody--whacks 'em over the head with her +thimble--and who cares for that, I'd like to know. She talks awful, but +talk don't hurt--anyways it don't if she don't cry. Jim, I'll give you +a marvel. I'll give you a white alley!" + +Jim began to waver. + +"White alley, Jim! And it's a bully taw." + +"My! Dat's a mighty gay marvel, I tell you! But Mars Tom I's powerful +'fraid ole missis--" + +"And besides, if you will I'll show you my sore toe." + +Jim was only human--this attraction was too much for him. He put down +his pail, took the white alley, and bent over the toe with absorbing +interest while the bandage was being unwound. In another moment he was +flying down the street with his pail and a tingling rear, Tom was +whitewashing with vigor, and Aunt Polly was retiring from the field +with a slipper in her hand and triumph in her eye. + +But Tom's energy did not last. He began to think of the fun he had +planned for this day, and his sorrows multiplied. Soon the free boys +would come tripping along on all sorts of delicious expeditions, and +they would make a world of fun of him for having to work--the very +thought of it burnt him like fire. He got out his worldly wealth and +examined it--bits of toys, marbles, and trash; enough to buy an +exchange of WORK, maybe, but not half enough to buy so much as half an +hour of pure freedom. So he returned his straitened means to his +pocket, and gave up the idea of trying to buy the boys. At this dark +and hopeless moment an inspiration burst upon him! Nothing less than a +great, magnificent inspiration. + +He took up his brush and went tranquilly to work. Ben Rogers hove in +sight presently--the very boy, of all boys, whose ridicule he had been +dreading. Ben's gait was the hop-skip-and-jump--proof enough that his +heart was light and his anticipations high. He was eating an apple, and +giving a long, melodious whoop, at intervals, followed by a deep-toned +ding-dong-dong, ding-dong-dong, for he was personating a steamboat. As +he drew near, he slackened speed, took the middle of the street, leaned +far over to starboard and rounded to ponderously and with laborious +pomp and circumstance--for he was personating the Big Missouri, and +considered himself to be drawing nine feet of water. He was boat and +captain and engine-bells combined, so he had to imagine himself +standing on his own hurricane-deck giving the orders and executing them: + +"Stop her, sir! Ting-a-ling-ling!" The headway ran almost out, and he +drew up slowly toward the sidewalk. + +"Ship up to back! Ting-a-ling-ling!" His arms straightened and +stiffened down his sides. + +"Set her back on the stabboard! Ting-a-ling-ling! Chow! ch-chow-wow! +Chow!" His right hand, meantime, describing stately circles--for it was +representing a forty-foot wheel. + +"Let her go back on the labboard! Ting-a-lingling! Chow-ch-chow-chow!" +The left hand began to describe circles. + +"Stop the stabboard! Ting-a-ling-ling! Stop the labboard! Come ahead +on the stabboard! Stop her! Let your outside turn over slow! +Ting-a-ling-ling! Chow-ow-ow! Get out that head-line! LIVELY now! +Come--out with your spring-line--what're you about there! Take a turn +round that stump with the bight of it! Stand by that stage, now--let her +go! Done with the engines, sir! Ting-a-ling-ling! SH'T! S'H'T! SH'T!" +(trying the gauge-cocks). + +Tom went on whitewashing--paid no attention to the steamboat. Ben +stared a moment and then said: "Hi-YI! YOU'RE up a stump, ain't you!" + +No answer. Tom surveyed his last touch with the eye of an artist, then +he gave his brush another gentle sweep and surveyed the result, as +before. Ben ranged up alongside of him. Tom's mouth watered for the +apple, but he stuck to his work. Ben said: + +"Hello, old chap, you got to work, hey?" + +Tom wheeled suddenly and said: + +"Why, it's you, Ben! I warn't noticing." + +"Say--I'm going in a-swimming, I am. Don't you wish you could? But of +course you'd druther WORK--wouldn't you? Course you would!" + +Tom contemplated the boy a bit, and said: + +"What do you call work?" + +"Why, ain't THAT work?" + +Tom resumed his whitewashing, and answered carelessly: + +"Well, maybe it is, and maybe it ain't. All I know, is, it suits Tom +Sawyer." + +"Oh come, now, you don't mean to let on that you LIKE it?" + +The brush continued to move. + +"Like it? Well, I don't see why I oughtn't to like it. Does a boy get +a chance to whitewash a fence every day?" + +That put the thing in a new light. Ben stopped nibbling his apple. Tom +swept his brush daintily back and forth--stepped back to note the +effect--added a touch here and there--criticised the effect again--Ben +watching every move and getting more and more interested, more and more +absorbed. Presently he said: + +"Say, Tom, let ME whitewash a little." + +Tom considered, was about to consent; but he altered his mind: + +"No--no--I reckon it wouldn't hardly do, Ben. You see, Aunt Polly's +awful particular about this fence--right here on the street, you know +--but if it was the back fence I wouldn't mind and SHE wouldn't. Yes, +she's awful particular about this fence; it's got to be done very +careful; I reckon there ain't one boy in a thousand, maybe two +thousand, that can do it the way it's got to be done." + +"No--is that so? Oh come, now--lemme just try. Only just a little--I'd +let YOU, if you was me, Tom." + +"Ben, I'd like to, honest injun; but Aunt Polly--well, Jim wanted to +do it, but she wouldn't let him; Sid wanted to do it, and she wouldn't +let Sid. Now don't you see how I'm fixed? If you was to tackle this +fence and anything was to happen to it--" + +"Oh, shucks, I'll be just as careful. Now lemme try. Say--I'll give +you the core of my apple." + +"Well, here--No, Ben, now don't. I'm afeard--" + +"I'll give you ALL of it!" + +Tom gave up the brush with reluctance in his face, but alacrity in his +heart. And while the late steamer Big Missouri worked and sweated in +the sun, the retired artist sat on a barrel in the shade close by, +dangled his legs, munched his apple, and planned the slaughter of more +innocents. There was no lack of material; boys happened along every +little while; they came to jeer, but remained to whitewash. By the time +Ben was fagged out, Tom had traded the next chance to Billy Fisher for +a kite, in good repair; and when he played out, Johnny Miller bought in +for a dead rat and a string to swing it with--and so on, and so on, +hour after hour. And when the middle of the afternoon came, from being +a poor poverty-stricken boy in the morning, Tom was literally rolling +in wealth. He had besides the things before mentioned, twelve marbles, +part of a jews-harp, a piece of blue bottle-glass to look through, a +spool cannon, a key that wouldn't unlock anything, a fragment of chalk, +a glass stopper of a decanter, a tin soldier, a couple of tadpoles, six +fire-crackers, a kitten with only one eye, a brass doorknob, a +dog-collar--but no dog--the handle of a knife, four pieces of +orange-peel, and a dilapidated old window sash. + +He had had a nice, good, idle time all the while--plenty of company +--and the fence had three coats of whitewash on it! If he hadn't run out +of whitewash he would have bankrupted every boy in the village. + +Tom said to himself that it was not such a hollow world, after all. He +had discovered a great law of human action, without knowing it--namely, +that in order to make a man or a boy covet a thing, it is only +necessary to make the thing difficult to attain. If he had been a great +and wise philosopher, like the writer of this book, he would now have +comprehended that Work consists of whatever a body is OBLIGED to do, +and that Play consists of whatever a body is not obliged to do. And +this would help him to understand why constructing artificial flowers +or performing on a tread-mill is work, while rolling ten-pins or +climbing Mont Blanc is only amusement. There are wealthy gentlemen in +England who drive four-horse passenger-coaches twenty or thirty miles +on a daily line, in the summer, because the privilege costs them +considerable money; but if they were offered wages for the service, +that would turn it into work and then they would resign. + +The boy mused awhile over the substantial change which had taken place +in his worldly circumstances, and then wended toward headquarters to +report. + + + +CHAPTER III + +TOM presented himself before Aunt Polly, who was sitting by an open +window in a pleasant rearward apartment, which was bedroom, +breakfast-room, dining-room, and library, combined. The balmy summer +air, the restful quiet, the odor of the flowers, and the drowsing murmur +of the bees had had their effect, and she was nodding over her knitting +--for she had no company but the cat, and it was asleep in her lap. Her +spectacles were propped up on her gray head for safety. She had thought +that of course Tom had deserted long ago, and she wondered at seeing him +place himself in her power again in this intrepid way. He said: "Mayn't +I go and play now, aunt?" + +"What, a'ready? How much have you done?" + +"It's all done, aunt." + +"Tom, don't lie to me--I can't bear it." + +"I ain't, aunt; it IS all done." + +Aunt Polly placed small trust in such evidence. She went out to see +for herself; and she would have been content to find twenty per cent. +of Tom's statement true. When she found the entire fence whitewashed, +and not only whitewashed but elaborately coated and recoated, and even +a streak added to the ground, her astonishment was almost unspeakable. +She said: + +"Well, I never! There's no getting round it, you can work when you're +a mind to, Tom." And then she diluted the compliment by adding, "But +it's powerful seldom you're a mind to, I'm bound to say. Well, go 'long +and play; but mind you get back some time in a week, or I'll tan you." + +She was so overcome by the splendor of his achievement that she took +him into the closet and selected a choice apple and delivered it to +him, along with an improving lecture upon the added value and flavor a +treat took to itself when it came without sin through virtuous effort. +And while she closed with a happy Scriptural flourish, he "hooked" a +doughnut. + +Then he skipped out, and saw Sid just starting up the outside stairway +that led to the back rooms on the second floor. Clods were handy and +the air was full of them in a twinkling. They raged around Sid like a +hail-storm; and before Aunt Polly could collect her surprised faculties +and sally to the rescue, six or seven clods had taken personal effect, +and Tom was over the fence and gone. There was a gate, but as a general +thing he was too crowded for time to make use of it. His soul was at +peace, now that he had settled with Sid for calling attention to his +black thread and getting him into trouble. + +Tom skirted the block, and came round into a muddy alley that led by +the back of his aunt's cow-stable. He presently got safely beyond the +reach of capture and punishment, and hastened toward the public square +of the village, where two "military" companies of boys had met for +conflict, according to previous appointment. Tom was General of one of +these armies, Joe Harper (a bosom friend) General of the other. These +two great commanders did not condescend to fight in person--that being +better suited to the still smaller fry--but sat together on an eminence +and conducted the field operations by orders delivered through +aides-de-camp. Tom's army won a great victory, after a long and +hard-fought battle. Then the dead were counted, prisoners exchanged, +the terms of the next disagreement agreed upon, and the day for the +necessary battle appointed; after which the armies fell into line and +marched away, and Tom turned homeward alone. + +As he was passing by the house where Jeff Thatcher lived, he saw a new +girl in the garden--a lovely little blue-eyed creature with yellow hair +plaited into two long-tails, white summer frock and embroidered +pantalettes. The fresh-crowned hero fell without firing a shot. A +certain Amy Lawrence vanished out of his heart and left not even a +memory of herself behind. He had thought he loved her to distraction; +he had regarded his passion as adoration; and behold it was only a poor +little evanescent partiality. He had been months winning her; she had +confessed hardly a week ago; he had been the happiest and the proudest +boy in the world only seven short days, and here in one instant of time +she had gone out of his heart like a casual stranger whose visit is +done. + +He worshipped this new angel with furtive eye, till he saw that she +had discovered him; then he pretended he did not know she was present, +and began to "show off" in all sorts of absurd boyish ways, in order to +win her admiration. He kept up this grotesque foolishness for some +time; but by-and-by, while he was in the midst of some dangerous +gymnastic performances, he glanced aside and saw that the little girl +was wending her way toward the house. Tom came up to the fence and +leaned on it, grieving, and hoping she would tarry yet awhile longer. +She halted a moment on the steps and then moved toward the door. Tom +heaved a great sigh as she put her foot on the threshold. But his face +lit up, right away, for she tossed a pansy over the fence a moment +before she disappeared. + +The boy ran around and stopped within a foot or two of the flower, and +then shaded his eyes with his hand and began to look down street as if +he had discovered something of interest going on in that direction. +Presently he picked up a straw and began trying to balance it on his +nose, with his head tilted far back; and as he moved from side to side, +in his efforts, he edged nearer and nearer toward the pansy; finally +his bare foot rested upon it, his pliant toes closed upon it, and he +hopped away with the treasure and disappeared round the corner. But +only for a minute--only while he could button the flower inside his +jacket, next his heart--or next his stomach, possibly, for he was not +much posted in anatomy, and not hypercritical, anyway. + +He returned, now, and hung about the fence till nightfall, "showing +off," as before; but the girl never exhibited herself again, though Tom +comforted himself a little with the hope that she had been near some +window, meantime, and been aware of his attentions. Finally he strode +home reluctantly, with his poor head full of visions. + +All through supper his spirits were so high that his aunt wondered +"what had got into the child." He took a good scolding about clodding +Sid, and did not seem to mind it in the least. He tried to steal sugar +under his aunt's very nose, and got his knuckles rapped for it. He said: + +"Aunt, you don't whack Sid when he takes it." + +"Well, Sid don't torment a body the way you do. You'd be always into +that sugar if I warn't watching you." + +Presently she stepped into the kitchen, and Sid, happy in his +immunity, reached for the sugar-bowl--a sort of glorying over Tom which +was wellnigh unbearable. But Sid's fingers slipped and the bowl dropped +and broke. Tom was in ecstasies. In such ecstasies that he even +controlled his tongue and was silent. He said to himself that he would +not speak a word, even when his aunt came in, but would sit perfectly +still till she asked who did the mischief; and then he would tell, and +there would be nothing so good in the world as to see that pet model +"catch it." He was so brimful of exultation that he could hardly hold +himself when the old lady came back and stood above the wreck +discharging lightnings of wrath from over her spectacles. He said to +himself, "Now it's coming!" And the next instant he was sprawling on +the floor! The potent palm was uplifted to strike again when Tom cried +out: + +"Hold on, now, what 'er you belting ME for?--Sid broke it!" + +Aunt Polly paused, perplexed, and Tom looked for healing pity. But +when she got her tongue again, she only said: + +"Umf! Well, you didn't get a lick amiss, I reckon. You been into some +other audacious mischief when I wasn't around, like enough." + +Then her conscience reproached her, and she yearned to say something +kind and loving; but she judged that this would be construed into a +confession that she had been in the wrong, and discipline forbade that. +So she kept silence, and went about her affairs with a troubled heart. +Tom sulked in a corner and exalted his woes. He knew that in her heart +his aunt was on her knees to him, and he was morosely gratified by the +consciousness of it. He would hang out no signals, he would take notice +of none. He knew that a yearning glance fell upon him, now and then, +through a film of tears, but he refused recognition of it. He pictured +himself lying sick unto death and his aunt bending over him beseeching +one little forgiving word, but he would turn his face to the wall, and +die with that word unsaid. Ah, how would she feel then? And he pictured +himself brought home from the river, dead, with his curls all wet, and +his sore heart at rest. How she would throw herself upon him, and how +her tears would fall like rain, and her lips pray God to give her back +her boy and she would never, never abuse him any more! But he would lie +there cold and white and make no sign--a poor little sufferer, whose +griefs were at an end. He so worked upon his feelings with the pathos +of these dreams, that he had to keep swallowing, he was so like to +choke; and his eyes swam in a blur of water, which overflowed when he +winked, and ran down and trickled from the end of his nose. And such a +luxury to him was this petting of his sorrows, that he could not bear +to have any worldly cheeriness or any grating delight intrude upon it; +it was too sacred for such contact; and so, presently, when his cousin +Mary danced in, all alive with the joy of seeing home again after an +age-long visit of one week to the country, he got up and moved in +clouds and darkness out at one door as she brought song and sunshine in +at the other. + +He wandered far from the accustomed haunts of boys, and sought +desolate places that were in harmony with his spirit. A log raft in the +river invited him, and he seated himself on its outer edge and +contemplated the dreary vastness of the stream, wishing, the while, +that he could only be drowned, all at once and unconsciously, without +undergoing the uncomfortable routine devised by nature. Then he thought +of his flower. He got it out, rumpled and wilted, and it mightily +increased his dismal felicity. He wondered if she would pity him if she +knew? Would she cry, and wish that she had a right to put her arms +around his neck and comfort him? Or would she turn coldly away like all +the hollow world? This picture brought such an agony of pleasurable +suffering that he worked it over and over again in his mind and set it +up in new and varied lights, till he wore it threadbare. At last he +rose up sighing and departed in the darkness. + +About half-past nine or ten o'clock he came along the deserted street +to where the Adored Unknown lived; he paused a moment; no sound fell +upon his listening ear; a candle was casting a dull glow upon the +curtain of a second-story window. Was the sacred presence there? He +climbed the fence, threaded his stealthy way through the plants, till +he stood under that window; he looked up at it long, and with emotion; +then he laid him down on the ground under it, disposing himself upon +his back, with his hands clasped upon his breast and holding his poor +wilted flower. And thus he would die--out in the cold world, with no +shelter over his homeless head, no friendly hand to wipe the +death-damps from his brow, no loving face to bend pityingly over him +when the great agony came. And thus SHE would see him when she looked +out upon the glad morning, and oh! would she drop one little tear upon +his poor, lifeless form, would she heave one little sigh to see a bright +young life so rudely blighted, so untimely cut down? + +The window went up, a maid-servant's discordant voice profaned the +holy calm, and a deluge of water drenched the prone martyr's remains! + +The strangling hero sprang up with a relieving snort. There was a whiz +as of a missile in the air, mingled with the murmur of a curse, a sound +as of shivering glass followed, and a small, vague form went over the +fence and shot away in the gloom. + +Not long after, as Tom, all undressed for bed, was surveying his +drenched garments by the light of a tallow dip, Sid woke up; but if he +had any dim idea of making any "references to allusions," he thought +better of it and held his peace, for there was danger in Tom's eye. + +Tom turned in without the added vexation of prayers, and Sid made +mental note of the omission. + + + +CHAPTER IV + +THE sun rose upon a tranquil world, and beamed down upon the peaceful +village like a benediction. Breakfast over, Aunt Polly had family +worship: it began with a prayer built from the ground up of solid +courses of Scriptural quotations, welded together with a thin mortar of +originality; and from the summit of this she delivered a grim chapter +of the Mosaic Law, as from Sinai. + +Then Tom girded up his loins, so to speak, and went to work to "get +his verses." Sid had learned his lesson days before. Tom bent all his +energies to the memorizing of five verses, and he chose part of the +Sermon on the Mount, because he could find no verses that were shorter. +At the end of half an hour Tom had a vague general idea of his lesson, +but no more, for his mind was traversing the whole field of human +thought, and his hands were busy with distracting recreations. Mary +took his book to hear him recite, and he tried to find his way through +the fog: + +"Blessed are the--a--a--" + +"Poor"-- + +"Yes--poor; blessed are the poor--a--a--" + +"In spirit--" + +"In spirit; blessed are the poor in spirit, for they--they--" + +"THEIRS--" + +"For THEIRS. Blessed are the poor in spirit, for theirs is the kingdom +of heaven. Blessed are they that mourn, for they--they--" + +"Sh--" + +"For they--a--" + +"S, H, A--" + +"For they S, H--Oh, I don't know what it is!" + +"SHALL!" + +"Oh, SHALL! for they shall--for they shall--a--a--shall mourn--a--a-- +blessed are they that shall--they that--a--they that shall mourn, for +they shall--a--shall WHAT? Why don't you tell me, Mary?--what do you +want to be so mean for?" + +"Oh, Tom, you poor thick-headed thing, I'm not teasing you. I wouldn't +do that. You must go and learn it again. Don't you be discouraged, Tom, +you'll manage it--and if you do, I'll give you something ever so nice. +There, now, that's a good boy." + +"All right! What is it, Mary, tell me what it is." + +"Never you mind, Tom. You know if I say it's nice, it is nice." + +"You bet you that's so, Mary. All right, I'll tackle it again." + +And he did "tackle it again"--and under the double pressure of +curiosity and prospective gain he did it with such spirit that he +accomplished a shining success. Mary gave him a brand-new "Barlow" +knife worth twelve and a half cents; and the convulsion of delight that +swept his system shook him to his foundations. True, the knife would +not cut anything, but it was a "sure-enough" Barlow, and there was +inconceivable grandeur in that--though where the Western boys ever got +the idea that such a weapon could possibly be counterfeited to its +injury is an imposing mystery and will always remain so, perhaps. Tom +contrived to scarify the cupboard with it, and was arranging to begin +on the bureau, when he was called off to dress for Sunday-school. + +Mary gave him a tin basin of water and a piece of soap, and he went +outside the door and set the basin on a little bench there; then he +dipped the soap in the water and laid it down; turned up his sleeves; +poured out the water on the ground, gently, and then entered the +kitchen and began to wipe his face diligently on the towel behind the +door. But Mary removed the towel and said: + +"Now ain't you ashamed, Tom. You mustn't be so bad. Water won't hurt +you." + +Tom was a trifle disconcerted. The basin was refilled, and this time +he stood over it a little while, gathering resolution; took in a big +breath and began. When he entered the kitchen presently, with both eyes +shut and groping for the towel with his hands, an honorable testimony +of suds and water was dripping from his face. But when he emerged from +the towel, he was not yet satisfactory, for the clean territory stopped +short at his chin and his jaws, like a mask; below and beyond this line +there was a dark expanse of unirrigated soil that spread downward in +front and backward around his neck. Mary took him in hand, and when she +was done with him he was a man and a brother, without distinction of +color, and his saturated hair was neatly brushed, and its short curls +wrought into a dainty and symmetrical general effect. [He privately +smoothed out the curls, with labor and difficulty, and plastered his +hair close down to his head; for he held curls to be effeminate, and +his own filled his life with bitterness.] Then Mary got out a suit of +his clothing that had been used only on Sundays during two years--they +were simply called his "other clothes"--and so by that we know the +size of his wardrobe. The girl "put him to rights" after he had dressed +himself; she buttoned his neat roundabout up to his chin, turned his +vast shirt collar down over his shoulders, brushed him off and crowned +him with his speckled straw hat. He now looked exceedingly improved and +uncomfortable. He was fully as uncomfortable as he looked; for there +was a restraint about whole clothes and cleanliness that galled him. He +hoped that Mary would forget his shoes, but the hope was blighted; she +coated them thoroughly with tallow, as was the custom, and brought them +out. He lost his temper and said he was always being made to do +everything he didn't want to do. But Mary said, persuasively: + +"Please, Tom--that's a good boy." + +So he got into the shoes snarling. Mary was soon ready, and the three +children set out for Sunday-school--a place that Tom hated with his +whole heart; but Sid and Mary were fond of it. + +Sabbath-school hours were from nine to half-past ten; and then church +service. Two of the children always remained for the sermon +voluntarily, and the other always remained too--for stronger reasons. +The church's high-backed, uncushioned pews would seat about three +hundred persons; the edifice was but a small, plain affair, with a sort +of pine board tree-box on top of it for a steeple. At the door Tom +dropped back a step and accosted a Sunday-dressed comrade: + +"Say, Billy, got a yaller ticket?" + +"Yes." + +"What'll you take for her?" + +"What'll you give?" + +"Piece of lickrish and a fish-hook." + +"Less see 'em." + +Tom exhibited. They were satisfactory, and the property changed hands. +Then Tom traded a couple of white alleys for three red tickets, and +some small trifle or other for a couple of blue ones. He waylaid other +boys as they came, and went on buying tickets of various colors ten or +fifteen minutes longer. He entered the church, now, with a swarm of +clean and noisy boys and girls, proceeded to his seat and started a +quarrel with the first boy that came handy. The teacher, a grave, +elderly man, interfered; then turned his back a moment and Tom pulled a +boy's hair in the next bench, and was absorbed in his book when the boy +turned around; stuck a pin in another boy, presently, in order to hear +him say "Ouch!" and got a new reprimand from his teacher. Tom's whole +class were of a pattern--restless, noisy, and troublesome. When they +came to recite their lessons, not one of them knew his verses +perfectly, but had to be prompted all along. However, they worried +through, and each got his reward--in small blue tickets, each with a +passage of Scripture on it; each blue ticket was pay for two verses of +the recitation. Ten blue tickets equalled a red one, and could be +exchanged for it; ten red tickets equalled a yellow one; for ten yellow +tickets the superintendent gave a very plainly bound Bible (worth forty +cents in those easy times) to the pupil. How many of my readers would +have the industry and application to memorize two thousand verses, even +for a Dore Bible? And yet Mary had acquired two Bibles in this way--it +was the patient work of two years--and a boy of German parentage had +won four or five. He once recited three thousand verses without +stopping; but the strain upon his mental faculties was too great, and +he was little better than an idiot from that day forth--a grievous +misfortune for the school, for on great occasions, before company, the +superintendent (as Tom expressed it) had always made this boy come out +and "spread himself." Only the older pupils managed to keep their +tickets and stick to their tedious work long enough to get a Bible, and +so the delivery of one of these prizes was a rare and noteworthy +circumstance; the successful pupil was so great and conspicuous for +that day that on the spot every scholar's heart was fired with a fresh +ambition that often lasted a couple of weeks. It is possible that Tom's +mental stomach had never really hungered for one of those prizes, but +unquestionably his entire being had for many a day longed for the glory +and the eclat that came with it. + +In due course the superintendent stood up in front of the pulpit, with +a closed hymn-book in his hand and his forefinger inserted between its +leaves, and commanded attention. When a Sunday-school superintendent +makes his customary little speech, a hymn-book in the hand is as +necessary as is the inevitable sheet of music in the hand of a singer +who stands forward on the platform and sings a solo at a concert +--though why, is a mystery: for neither the hymn-book nor the sheet of +music is ever referred to by the sufferer. This superintendent was a +slim creature of thirty-five, with a sandy goatee and short sandy hair; +he wore a stiff standing-collar whose upper edge almost reached his +ears and whose sharp points curved forward abreast the corners of his +mouth--a fence that compelled a straight lookout ahead, and a turning +of the whole body when a side view was required; his chin was propped +on a spreading cravat which was as broad and as long as a bank-note, +and had fringed ends; his boot toes were turned sharply up, in the +fashion of the day, like sleigh-runners--an effect patiently and +laboriously produced by the young men by sitting with their toes +pressed against a wall for hours together. Mr. Walters was very earnest +of mien, and very sincere and honest at heart; and he held sacred +things and places in such reverence, and so separated them from worldly +matters, that unconsciously to himself his Sunday-school voice had +acquired a peculiar intonation which was wholly absent on week-days. He +began after this fashion: + +"Now, children, I want you all to sit up just as straight and pretty +as you can and give me all your attention for a minute or two. There +--that is it. That is the way good little boys and girls should do. I see +one little girl who is looking out of the window--I am afraid she +thinks I am out there somewhere--perhaps up in one of the trees making +a speech to the little birds. [Applausive titter.] I want to tell you +how good it makes me feel to see so many bright, clean little faces +assembled in a place like this, learning to do right and be good." And +so forth and so on. It is not necessary to set down the rest of the +oration. It was of a pattern which does not vary, and so it is familiar +to us all. + +The latter third of the speech was marred by the resumption of fights +and other recreations among certain of the bad boys, and by fidgetings +and whisperings that extended far and wide, washing even to the bases +of isolated and incorruptible rocks like Sid and Mary. But now every +sound ceased suddenly, with the subsidence of Mr. Walters' voice, and +the conclusion of the speech was received with a burst of silent +gratitude. + +A good part of the whispering had been occasioned by an event which +was more or less rare--the entrance of visitors: lawyer Thatcher, +accompanied by a very feeble and aged man; a fine, portly, middle-aged +gentleman with iron-gray hair; and a dignified lady who was doubtless +the latter's wife. The lady was leading a child. Tom had been restless +and full of chafings and repinings; conscience-smitten, too--he could +not meet Amy Lawrence's eye, he could not brook her loving gaze. But +when he saw this small new-comer his soul was all ablaze with bliss in +a moment. The next moment he was "showing off" with all his might +--cuffing boys, pulling hair, making faces--in a word, using every art +that seemed likely to fascinate a girl and win her applause. His +exaltation had but one alloy--the memory of his humiliation in this +angel's garden--and that record in sand was fast washing out, under +the waves of happiness that were sweeping over it now. + +The visitors were given the highest seat of honor, and as soon as Mr. +Walters' speech was finished, he introduced them to the school. The +middle-aged man turned out to be a prodigious personage--no less a one +than the county judge--altogether the most august creation these +children had ever looked upon--and they wondered what kind of material +he was made of--and they half wanted to hear him roar, and were half +afraid he might, too. He was from Constantinople, twelve miles away--so +he had travelled, and seen the world--these very eyes had looked upon +the county court-house--which was said to have a tin roof. The awe +which these reflections inspired was attested by the impressive silence +and the ranks of staring eyes. This was the great Judge Thatcher, +brother of their own lawyer. Jeff Thatcher immediately went forward, to +be familiar with the great man and be envied by the school. It would +have been music to his soul to hear the whisperings: + +"Look at him, Jim! He's a going up there. Say--look! he's a going to +shake hands with him--he IS shaking hands with him! By jings, don't you +wish you was Jeff?" + +Mr. Walters fell to "showing off," with all sorts of official +bustlings and activities, giving orders, delivering judgments, +discharging directions here, there, everywhere that he could find a +target. The librarian "showed off"--running hither and thither with his +arms full of books and making a deal of the splutter and fuss that +insect authority delights in. The young lady teachers "showed off" +--bending sweetly over pupils that were lately being boxed, lifting +pretty warning fingers at bad little boys and patting good ones +lovingly. The young gentlemen teachers "showed off" with small +scoldings and other little displays of authority and fine attention to +discipline--and most of the teachers, of both sexes, found business up +at the library, by the pulpit; and it was business that frequently had +to be done over again two or three times (with much seeming vexation). +The little girls "showed off" in various ways, and the little boys +"showed off" with such diligence that the air was thick with paper wads +and the murmur of scufflings. And above it all the great man sat and +beamed a majestic judicial smile upon all the house, and warmed himself +in the sun of his own grandeur--for he was "showing off," too. + +There was only one thing wanting to make Mr. Walters' ecstasy +complete, and that was a chance to deliver a Bible-prize and exhibit a +prodigy. Several pupils had a few yellow tickets, but none had enough +--he had been around among the star pupils inquiring. He would have given +worlds, now, to have that German lad back again with a sound mind. + +And now at this moment, when hope was dead, Tom Sawyer came forward +with nine yellow tickets, nine red tickets, and ten blue ones, and +demanded a Bible. This was a thunderbolt out of a clear sky. Walters +was not expecting an application from this source for the next ten +years. But there was no getting around it--here were the certified +checks, and they were good for their face. Tom was therefore elevated +to a place with the Judge and the other elect, and the great news was +announced from headquarters. It was the most stunning surprise of the +decade, and so profound was the sensation that it lifted the new hero +up to the judicial one's altitude, and the school had two marvels to +gaze upon in place of one. The boys were all eaten up with envy--but +those that suffered the bitterest pangs were those who perceived too +late that they themselves had contributed to this hated splendor by +trading tickets to Tom for the wealth he had amassed in selling +whitewashing privileges. These despised themselves, as being the dupes +of a wily fraud, a guileful snake in the grass. + +The prize was delivered to Tom with as much effusion as the +superintendent could pump up under the circumstances; but it lacked +somewhat of the true gush, for the poor fellow's instinct taught him +that there was a mystery here that could not well bear the light, +perhaps; it was simply preposterous that this boy had warehoused two +thousand sheaves of Scriptural wisdom on his premises--a dozen would +strain his capacity, without a doubt. + +Amy Lawrence was proud and glad, and she tried to make Tom see it in +her face--but he wouldn't look. She wondered; then she was just a grain +troubled; next a dim suspicion came and went--came again; she watched; +a furtive glance told her worlds--and then her heart broke, and she was +jealous, and angry, and the tears came and she hated everybody. Tom +most of all (she thought). + +Tom was introduced to the Judge; but his tongue was tied, his breath +would hardly come, his heart quaked--partly because of the awful +greatness of the man, but mainly because he was her parent. He would +have liked to fall down and worship him, if it were in the dark. The +Judge put his hand on Tom's head and called him a fine little man, and +asked him what his name was. The boy stammered, gasped, and got it out: + +"Tom." + +"Oh, no, not Tom--it is--" + +"Thomas." + +"Ah, that's it. I thought there was more to it, maybe. That's very +well. But you've another one I daresay, and you'll tell it to me, won't +you?" + +"Tell the gentleman your other name, Thomas," said Walters, "and say +sir. You mustn't forget your manners." + +"Thomas Sawyer--sir." + +"That's it! That's a good boy. Fine boy. Fine, manly little fellow. +Two thousand verses is a great many--very, very great many. And you +never can be sorry for the trouble you took to learn them; for +knowledge is worth more than anything there is in the world; it's what +makes great men and good men; you'll be a great man and a good man +yourself, some day, Thomas, and then you'll look back and say, It's all +owing to the precious Sunday-school privileges of my boyhood--it's all +owing to my dear teachers that taught me to learn--it's all owing to +the good superintendent, who encouraged me, and watched over me, and +gave me a beautiful Bible--a splendid elegant Bible--to keep and have +it all for my own, always--it's all owing to right bringing up! That is +what you will say, Thomas--and you wouldn't take any money for those +two thousand verses--no indeed you wouldn't. And now you wouldn't mind +telling me and this lady some of the things you've learned--no, I know +you wouldn't--for we are proud of little boys that learn. Now, no +doubt you know the names of all the twelve disciples. Won't you tell us +the names of the first two that were appointed?" + +Tom was tugging at a button-hole and looking sheepish. He blushed, +now, and his eyes fell. Mr. Walters' heart sank within him. He said to +himself, it is not possible that the boy can answer the simplest +question--why DID the Judge ask him? Yet he felt obliged to speak up +and say: + +"Answer the gentleman, Thomas--don't be afraid." + +Tom still hung fire. + +"Now I know you'll tell me," said the lady. "The names of the first +two disciples were--" + +"DAVID AND GOLIAH!" + +Let us draw the curtain of charity over the rest of the scene. + + + +CHAPTER V + +ABOUT half-past ten the cracked bell of the small church began to +ring, and presently the people began to gather for the morning sermon. +The Sunday-school children distributed themselves about the house and +occupied pews with their parents, so as to be under supervision. Aunt +Polly came, and Tom and Sid and Mary sat with her--Tom being placed +next the aisle, in order that he might be as far away from the open +window and the seductive outside summer scenes as possible. The crowd +filed up the aisles: the aged and needy postmaster, who had seen better +days; the mayor and his wife--for they had a mayor there, among other +unnecessaries; the justice of the peace; the widow Douglass, fair, +smart, and forty, a generous, good-hearted soul and well-to-do, her +hill mansion the only palace in the town, and the most hospitable and +much the most lavish in the matter of festivities that St. Petersburg +could boast; the bent and venerable Major and Mrs. Ward; lawyer +Riverson, the new notable from a distance; next the belle of the +village, followed by a troop of lawn-clad and ribbon-decked young +heart-breakers; then all the young clerks in town in a body--for they +had stood in the vestibule sucking their cane-heads, a circling wall of +oiled and simpering admirers, till the last girl had run their gantlet; +and last of all came the Model Boy, Willie Mufferson, taking as heedful +care of his mother as if she were cut glass. He always brought his +mother to church, and was the pride of all the matrons. The boys all +hated him, he was so good. And besides, he had been "thrown up to them" +so much. His white handkerchief was hanging out of his pocket behind, as +usual on Sundays--accidentally. Tom had no handkerchief, and he looked +upon boys who had as snobs. + +The congregation being fully assembled, now, the bell rang once more, +to warn laggards and stragglers, and then a solemn hush fell upon the +church which was only broken by the tittering and whispering of the +choir in the gallery. The choir always tittered and whispered all +through service. There was once a church choir that was not ill-bred, +but I have forgotten where it was, now. It was a great many years ago, +and I can scarcely remember anything about it, but I think it was in +some foreign country. + +The minister gave out the hymn, and read it through with a relish, in +a peculiar style which was much admired in that part of the country. +His voice began on a medium key and climbed steadily up till it reached +a certain point, where it bore with strong emphasis upon the topmost +word and then plunged down as if from a spring-board: + + Shall I be car-ri-ed toe the skies, on flow'ry BEDS of ease, + + Whilst others fight to win the prize, and sail thro' BLOODY seas? + +He was regarded as a wonderful reader. At church "sociables" he was +always called upon to read poetry; and when he was through, the ladies +would lift up their hands and let them fall helplessly in their laps, +and "wall" their eyes, and shake their heads, as much as to say, "Words +cannot express it; it is too beautiful, TOO beautiful for this mortal +earth." + +After the hymn had been sung, the Rev. Mr. Sprague turned himself into +a bulletin-board, and read off "notices" of meetings and societies and +things till it seemed that the list would stretch out to the crack of +doom--a queer custom which is still kept up in America, even in cities, +away here in this age of abundant newspapers. Often, the less there is +to justify a traditional custom, the harder it is to get rid of it. + +And now the minister prayed. A good, generous prayer it was, and went +into details: it pleaded for the church, and the little children of the +church; for the other churches of the village; for the village itself; +for the county; for the State; for the State officers; for the United +States; for the churches of the United States; for Congress; for the +President; for the officers of the Government; for poor sailors, tossed +by stormy seas; for the oppressed millions groaning under the heel of +European monarchies and Oriental despotisms; for such as have the light +and the good tidings, and yet have not eyes to see nor ears to hear +withal; for the heathen in the far islands of the sea; and closed with +a supplication that the words he was about to speak might find grace +and favor, and be as seed sown in fertile ground, yielding in time a +grateful harvest of good. Amen. + +There was a rustling of dresses, and the standing congregation sat +down. The boy whose history this book relates did not enjoy the prayer, +he only endured it--if he even did that much. He was restive all +through it; he kept tally of the details of the prayer, unconsciously +--for he was not listening, but he knew the ground of old, and the +clergyman's regular route over it--and when a little trifle of new +matter was interlarded, his ear detected it and his whole nature +resented it; he considered additions unfair, and scoundrelly. In the +midst of the prayer a fly had lit on the back of the pew in front of +him and tortured his spirit by calmly rubbing its hands together, +embracing its head with its arms, and polishing it so vigorously that +it seemed to almost part company with the body, and the slender thread +of a neck was exposed to view; scraping its wings with its hind legs +and smoothing them to its body as if they had been coat-tails; going +through its whole toilet as tranquilly as if it knew it was perfectly +safe. As indeed it was; for as sorely as Tom's hands itched to grab for +it they did not dare--he believed his soul would be instantly destroyed +if he did such a thing while the prayer was going on. But with the +closing sentence his hand began to curve and steal forward; and the +instant the "Amen" was out the fly was a prisoner of war. His aunt +detected the act and made him let it go. + +The minister gave out his text and droned along monotonously through +an argument that was so prosy that many a head by and by began to nod +--and yet it was an argument that dealt in limitless fire and brimstone +and thinned the predestined elect down to a company so small as to be +hardly worth the saving. Tom counted the pages of the sermon; after +church he always knew how many pages there had been, but he seldom knew +anything else about the discourse. However, this time he was really +interested for a little while. The minister made a grand and moving +picture of the assembling together of the world's hosts at the +millennium when the lion and the lamb should lie down together and a +little child should lead them. But the pathos, the lesson, the moral of +the great spectacle were lost upon the boy; he only thought of the +conspicuousness of the principal character before the on-looking +nations; his face lit with the thought, and he said to himself that he +wished he could be that child, if it was a tame lion. + +Now he lapsed into suffering again, as the dry argument was resumed. +Presently he bethought him of a treasure he had and got it out. It was +a large black beetle with formidable jaws--a "pinchbug," he called it. +It was in a percussion-cap box. The first thing the beetle did was to +take him by the finger. A natural fillip followed, the beetle went +floundering into the aisle and lit on its back, and the hurt finger +went into the boy's mouth. The beetle lay there working its helpless +legs, unable to turn over. Tom eyed it, and longed for it; but it was +safe out of his reach. Other people uninterested in the sermon found +relief in the beetle, and they eyed it too. Presently a vagrant poodle +dog came idling along, sad at heart, lazy with the summer softness and +the quiet, weary of captivity, sighing for change. He spied the beetle; +the drooping tail lifted and wagged. He surveyed the prize; walked +around it; smelt at it from a safe distance; walked around it again; +grew bolder, and took a closer smell; then lifted his lip and made a +gingerly snatch at it, just missing it; made another, and another; +began to enjoy the diversion; subsided to his stomach with the beetle +between his paws, and continued his experiments; grew weary at last, +and then indifferent and absent-minded. His head nodded, and little by +little his chin descended and touched the enemy, who seized it. There +was a sharp yelp, a flirt of the poodle's head, and the beetle fell a +couple of yards away, and lit on its back once more. The neighboring +spectators shook with a gentle inward joy, several faces went behind +fans and handkerchiefs, and Tom was entirely happy. The dog looked +foolish, and probably felt so; but there was resentment in his heart, +too, and a craving for revenge. So he went to the beetle and began a +wary attack on it again; jumping at it from every point of a circle, +lighting with his fore-paws within an inch of the creature, making even +closer snatches at it with his teeth, and jerking his head till his +ears flapped again. But he grew tired once more, after a while; tried +to amuse himself with a fly but found no relief; followed an ant +around, with his nose close to the floor, and quickly wearied of that; +yawned, sighed, forgot the beetle entirely, and sat down on it. Then +there was a wild yelp of agony and the poodle went sailing up the +aisle; the yelps continued, and so did the dog; he crossed the house in +front of the altar; he flew down the other aisle; he crossed before the +doors; he clamored up the home-stretch; his anguish grew with his +progress, till presently he was but a woolly comet moving in its orbit +with the gleam and the speed of light. At last the frantic sufferer +sheered from its course, and sprang into its master's lap; he flung it +out of the window, and the voice of distress quickly thinned away and +died in the distance. + +By this time the whole church was red-faced and suffocating with +suppressed laughter, and the sermon had come to a dead standstill. The +discourse was resumed presently, but it went lame and halting, all +possibility of impressiveness being at an end; for even the gravest +sentiments were constantly being received with a smothered burst of +unholy mirth, under cover of some remote pew-back, as if the poor +parson had said a rarely facetious thing. It was a genuine relief to +the whole congregation when the ordeal was over and the benediction +pronounced. + +Tom Sawyer went home quite cheerful, thinking to himself that there +was some satisfaction about divine service when there was a bit of +variety in it. He had but one marring thought; he was willing that the +dog should play with his pinchbug, but he did not think it was upright +in him to carry it off. + + + +CHAPTER VI + +MONDAY morning found Tom Sawyer miserable. Monday morning always found +him so--because it began another week's slow suffering in school. He +generally began that day with wishing he had had no intervening +holiday, it made the going into captivity and fetters again so much +more odious. + +Tom lay thinking. Presently it occurred to him that he wished he was +sick; then he could stay home from school. Here was a vague +possibility. He canvassed his system. No ailment was found, and he +investigated again. This time he thought he could detect colicky +symptoms, and he began to encourage them with considerable hope. But +they soon grew feeble, and presently died wholly away. He reflected +further. Suddenly he discovered something. One of his upper front teeth +was loose. This was lucky; he was about to begin to groan, as a +"starter," as he called it, when it occurred to him that if he came +into court with that argument, his aunt would pull it out, and that +would hurt. So he thought he would hold the tooth in reserve for the +present, and seek further. Nothing offered for some little time, and +then he remembered hearing the doctor tell about a certain thing that +laid up a patient for two or three weeks and threatened to make him +lose a finger. So the boy eagerly drew his sore toe from under the +sheet and held it up for inspection. But now he did not know the +necessary symptoms. However, it seemed well worth while to chance it, +so he fell to groaning with considerable spirit. + +But Sid slept on unconscious. + +Tom groaned louder, and fancied that he began to feel pain in the toe. + +No result from Sid. + +Tom was panting with his exertions by this time. He took a rest and +then swelled himself up and fetched a succession of admirable groans. + +Sid snored on. + +Tom was aggravated. He said, "Sid, Sid!" and shook him. This course +worked well, and Tom began to groan again. Sid yawned, stretched, then +brought himself up on his elbow with a snort, and began to stare at +Tom. Tom went on groaning. Sid said: + +"Tom! Say, Tom!" [No response.] "Here, Tom! TOM! What is the matter, +Tom?" And he shook him and looked in his face anxiously. + +Tom moaned out: + +"Oh, don't, Sid. Don't joggle me." + +"Why, what's the matter, Tom? I must call auntie." + +"No--never mind. It'll be over by and by, maybe. Don't call anybody." + +"But I must! DON'T groan so, Tom, it's awful. How long you been this +way?" + +"Hours. Ouch! Oh, don't stir so, Sid, you'll kill me." + +"Tom, why didn't you wake me sooner? Oh, Tom, DON'T! It makes my +flesh crawl to hear you. Tom, what is the matter?" + +"I forgive you everything, Sid. [Groan.] Everything you've ever done +to me. When I'm gone--" + +"Oh, Tom, you ain't dying, are you? Don't, Tom--oh, don't. Maybe--" + +"I forgive everybody, Sid. [Groan.] Tell 'em so, Sid. And Sid, you +give my window-sash and my cat with one eye to that new girl that's +come to town, and tell her--" + +But Sid had snatched his clothes and gone. Tom was suffering in +reality, now, so handsomely was his imagination working, and so his +groans had gathered quite a genuine tone. + +Sid flew down-stairs and said: + +"Oh, Aunt Polly, come! Tom's dying!" + +"Dying!" + +"Yes'm. Don't wait--come quick!" + +"Rubbage! I don't believe it!" + +But she fled up-stairs, nevertheless, with Sid and Mary at her heels. +And her face grew white, too, and her lip trembled. When she reached +the bedside she gasped out: + +"You, Tom! Tom, what's the matter with you?" + +"Oh, auntie, I'm--" + +"What's the matter with you--what is the matter with you, child?" + +"Oh, auntie, my sore toe's mortified!" + +The old lady sank down into a chair and laughed a little, then cried a +little, then did both together. This restored her and she said: + +"Tom, what a turn you did give me. Now you shut up that nonsense and +climb out of this." + +The groans ceased and the pain vanished from the toe. The boy felt a +little foolish, and he said: + +"Aunt Polly, it SEEMED mortified, and it hurt so I never minded my +tooth at all." + +"Your tooth, indeed! What's the matter with your tooth?" + +"One of them's loose, and it aches perfectly awful." + +"There, there, now, don't begin that groaning again. Open your mouth. +Well--your tooth IS loose, but you're not going to die about that. +Mary, get me a silk thread, and a chunk of fire out of the kitchen." + +Tom said: + +"Oh, please, auntie, don't pull it out. It don't hurt any more. I wish +I may never stir if it does. Please don't, auntie. I don't want to stay +home from school." + +"Oh, you don't, don't you? So all this row was because you thought +you'd get to stay home from school and go a-fishing? Tom, Tom, I love +you so, and you seem to try every way you can to break my old heart +with your outrageousness." By this time the dental instruments were +ready. The old lady made one end of the silk thread fast to Tom's tooth +with a loop and tied the other to the bedpost. Then she seized the +chunk of fire and suddenly thrust it almost into the boy's face. The +tooth hung dangling by the bedpost, now. + +But all trials bring their compensations. As Tom wended to school +after breakfast, he was the envy of every boy he met because the gap in +his upper row of teeth enabled him to expectorate in a new and +admirable way. He gathered quite a following of lads interested in the +exhibition; and one that had cut his finger and had been a centre of +fascination and homage up to this time, now found himself suddenly +without an adherent, and shorn of his glory. His heart was heavy, and +he said with a disdain which he did not feel that it wasn't anything to +spit like Tom Sawyer; but another boy said, "Sour grapes!" and he +wandered away a dismantled hero. + +Shortly Tom came upon the juvenile pariah of the village, Huckleberry +Finn, son of the town drunkard. Huckleberry was cordially hated and +dreaded by all the mothers of the town, because he was idle and lawless +and vulgar and bad--and because all their children admired him so, and +delighted in his forbidden society, and wished they dared to be like +him. Tom was like the rest of the respectable boys, in that he envied +Huckleberry his gaudy outcast condition, and was under strict orders +not to play with him. So he played with him every time he got a chance. +Huckleberry was always dressed in the cast-off clothes of full-grown +men, and they were in perennial bloom and fluttering with rags. His hat +was a vast ruin with a wide crescent lopped out of its brim; his coat, +when he wore one, hung nearly to his heels and had the rearward buttons +far down the back; but one suspender supported his trousers; the seat +of the trousers bagged low and contained nothing, the fringed legs +dragged in the dirt when not rolled up. + +Huckleberry came and went, at his own free will. He slept on doorsteps +in fine weather and in empty hogsheads in wet; he did not have to go to +school or to church, or call any being master or obey anybody; he could +go fishing or swimming when and where he chose, and stay as long as it +suited him; nobody forbade him to fight; he could sit up as late as he +pleased; he was always the first boy that went barefoot in the spring +and the last to resume leather in the fall; he never had to wash, nor +put on clean clothes; he could swear wonderfully. In a word, everything +that goes to make life precious that boy had. So thought every +harassed, hampered, respectable boy in St. Petersburg. + +Tom hailed the romantic outcast: + +"Hello, Huckleberry!" + +"Hello yourself, and see how you like it." + +"What's that you got?" + +"Dead cat." + +"Lemme see him, Huck. My, he's pretty stiff. Where'd you get him?" + +"Bought him off'n a boy." + +"What did you give?" + +"I give a blue ticket and a bladder that I got at the slaughter-house." + +"Where'd you get the blue ticket?" + +"Bought it off'n Ben Rogers two weeks ago for a hoop-stick." + +"Say--what is dead cats good for, Huck?" + +"Good for? Cure warts with." + +"No! Is that so? I know something that's better." + +"I bet you don't. What is it?" + +"Why, spunk-water." + +"Spunk-water! I wouldn't give a dern for spunk-water." + +"You wouldn't, wouldn't you? D'you ever try it?" + +"No, I hain't. But Bob Tanner did." + +"Who told you so!" + +"Why, he told Jeff Thatcher, and Jeff told Johnny Baker, and Johnny +told Jim Hollis, and Jim told Ben Rogers, and Ben told a nigger, and +the nigger told me. There now!" + +"Well, what of it? They'll all lie. Leastways all but the nigger. I +don't know HIM. But I never see a nigger that WOULDN'T lie. Shucks! Now +you tell me how Bob Tanner done it, Huck." + +"Why, he took and dipped his hand in a rotten stump where the +rain-water was." + +"In the daytime?" + +"Certainly." + +"With his face to the stump?" + +"Yes. Least I reckon so." + +"Did he say anything?" + +"I don't reckon he did. I don't know." + +"Aha! Talk about trying to cure warts with spunk-water such a blame +fool way as that! Why, that ain't a-going to do any good. You got to go +all by yourself, to the middle of the woods, where you know there's a +spunk-water stump, and just as it's midnight you back up against the +stump and jam your hand in and say: + + 'Barley-corn, barley-corn, injun-meal shorts, + Spunk-water, spunk-water, swaller these warts,' + +and then walk away quick, eleven steps, with your eyes shut, and then +turn around three times and walk home without speaking to anybody. +Because if you speak the charm's busted." + +"Well, that sounds like a good way; but that ain't the way Bob Tanner +done." + +"No, sir, you can bet he didn't, becuz he's the wartiest boy in this +town; and he wouldn't have a wart on him if he'd knowed how to work +spunk-water. I've took off thousands of warts off of my hands that way, +Huck. I play with frogs so much that I've always got considerable many +warts. Sometimes I take 'em off with a bean." + +"Yes, bean's good. I've done that." + +"Have you? What's your way?" + +"You take and split the bean, and cut the wart so as to get some +blood, and then you put the blood on one piece of the bean and take and +dig a hole and bury it 'bout midnight at the crossroads in the dark of +the moon, and then you burn up the rest of the bean. You see that piece +that's got the blood on it will keep drawing and drawing, trying to +fetch the other piece to it, and so that helps the blood to draw the +wart, and pretty soon off she comes." + +"Yes, that's it, Huck--that's it; though when you're burying it if you +say 'Down bean; off wart; come no more to bother me!' it's better. +That's the way Joe Harper does, and he's been nearly to Coonville and +most everywheres. But say--how do you cure 'em with dead cats?" + +"Why, you take your cat and go and get in the graveyard 'long about +midnight when somebody that was wicked has been buried; and when it's +midnight a devil will come, or maybe two or three, but you can't see +'em, you can only hear something like the wind, or maybe hear 'em talk; +and when they're taking that feller away, you heave your cat after 'em +and say, 'Devil follow corpse, cat follow devil, warts follow cat, I'm +done with ye!' That'll fetch ANY wart." + +"Sounds right. D'you ever try it, Huck?" + +"No, but old Mother Hopkins told me." + +"Well, I reckon it's so, then. Becuz they say she's a witch." + +"Say! Why, Tom, I KNOW she is. She witched pap. Pap says so his own +self. He come along one day, and he see she was a-witching him, so he +took up a rock, and if she hadn't dodged, he'd a got her. Well, that +very night he rolled off'n a shed wher' he was a layin drunk, and broke +his arm." + +"Why, that's awful. How did he know she was a-witching him?" + +"Lord, pap can tell, easy. Pap says when they keep looking at you +right stiddy, they're a-witching you. Specially if they mumble. Becuz +when they mumble they're saying the Lord's Prayer backards." + +"Say, Hucky, when you going to try the cat?" + +"To-night. I reckon they'll come after old Hoss Williams to-night." + +"But they buried him Saturday. Didn't they get him Saturday night?" + +"Why, how you talk! How could their charms work till midnight?--and +THEN it's Sunday. Devils don't slosh around much of a Sunday, I don't +reckon." + +"I never thought of that. That's so. Lemme go with you?" + +"Of course--if you ain't afeard." + +"Afeard! 'Tain't likely. Will you meow?" + +"Yes--and you meow back, if you get a chance. Last time, you kep' me +a-meowing around till old Hays went to throwing rocks at me and says +'Dern that cat!' and so I hove a brick through his window--but don't +you tell." + +"I won't. I couldn't meow that night, becuz auntie was watching me, +but I'll meow this time. Say--what's that?" + +"Nothing but a tick." + +"Where'd you get him?" + +"Out in the woods." + +"What'll you take for him?" + +"I don't know. I don't want to sell him." + +"All right. It's a mighty small tick, anyway." + +"Oh, anybody can run a tick down that don't belong to them. I'm +satisfied with it. It's a good enough tick for me." + +"Sho, there's ticks a plenty. I could have a thousand of 'em if I +wanted to." + +"Well, why don't you? Becuz you know mighty well you can't. This is a +pretty early tick, I reckon. It's the first one I've seen this year." + +"Say, Huck--I'll give you my tooth for him." + +"Less see it." + +Tom got out a bit of paper and carefully unrolled it. Huckleberry +viewed it wistfully. The temptation was very strong. At last he said: + +"Is it genuwyne?" + +Tom lifted his lip and showed the vacancy. + +"Well, all right," said Huckleberry, "it's a trade." + +Tom enclosed the tick in the percussion-cap box that had lately been +the pinchbug's prison, and the boys separated, each feeling wealthier +than before. + +When Tom reached the little isolated frame schoolhouse, he strode in +briskly, with the manner of one who had come with all honest speed. +He hung his hat on a peg and flung himself into his seat with +business-like alacrity. The master, throned on high in his great +splint-bottom arm-chair, was dozing, lulled by the drowsy hum of study. +The interruption roused him. + +"Thomas Sawyer!" + +Tom knew that when his name was pronounced in full, it meant trouble. + +"Sir!" + +"Come up here. Now, sir, why are you late again, as usual?" + +Tom was about to take refuge in a lie, when he saw two long tails of +yellow hair hanging down a back that he recognized by the electric +sympathy of love; and by that form was THE ONLY VACANT PLACE on the +girls' side of the schoolhouse. He instantly said: + +"I STOPPED TO TALK WITH HUCKLEBERRY FINN!" + +The master's pulse stood still, and he stared helplessly. The buzz of +study ceased. The pupils wondered if this foolhardy boy had lost his +mind. The master said: + +"You--you did what?" + +"Stopped to talk with Huckleberry Finn." + +There was no mistaking the words. + +"Thomas Sawyer, this is the most astounding confession I have ever +listened to. No mere ferule will answer for this offence. Take off your +jacket." + +The master's arm performed until it was tired and the stock of +switches notably diminished. Then the order followed: + +"Now, sir, go and sit with the girls! And let this be a warning to you." + +The titter that rippled around the room appeared to abash the boy, but +in reality that result was caused rather more by his worshipful awe of +his unknown idol and the dread pleasure that lay in his high good +fortune. He sat down upon the end of the pine bench and the girl +hitched herself away from him with a toss of her head. Nudges and winks +and whispers traversed the room, but Tom sat still, with his arms upon +the long, low desk before him, and seemed to study his book. + +By and by attention ceased from him, and the accustomed school murmur +rose upon the dull air once more. Presently the boy began to steal +furtive glances at the girl. She observed it, "made a mouth" at him and +gave him the back of her head for the space of a minute. When she +cautiously faced around again, a peach lay before her. She thrust it +away. Tom gently put it back. She thrust it away again, but with less +animosity. Tom patiently returned it to its place. Then she let it +remain. Tom scrawled on his slate, "Please take it--I got more." The +girl glanced at the words, but made no sign. Now the boy began to draw +something on the slate, hiding his work with his left hand. For a time +the girl refused to notice; but her human curiosity presently began to +manifest itself by hardly perceptible signs. The boy worked on, +apparently unconscious. The girl made a sort of noncommittal attempt to +see, but the boy did not betray that he was aware of it. At last she +gave in and hesitatingly whispered: + +"Let me see it." + +Tom partly uncovered a dismal caricature of a house with two gable +ends to it and a corkscrew of smoke issuing from the chimney. Then the +girl's interest began to fasten itself upon the work and she forgot +everything else. When it was finished, she gazed a moment, then +whispered: + +"It's nice--make a man." + +The artist erected a man in the front yard, that resembled a derrick. +He could have stepped over the house; but the girl was not +hypercritical; she was satisfied with the monster, and whispered: + +"It's a beautiful man--now make me coming along." + +Tom drew an hour-glass with a full moon and straw limbs to it and +armed the spreading fingers with a portentous fan. The girl said: + +"It's ever so nice--I wish I could draw." + +"It's easy," whispered Tom, "I'll learn you." + +"Oh, will you? When?" + +"At noon. Do you go home to dinner?" + +"I'll stay if you will." + +"Good--that's a whack. What's your name?" + +"Becky Thatcher. What's yours? Oh, I know. It's Thomas Sawyer." + +"That's the name they lick me by. I'm Tom when I'm good. You call me +Tom, will you?" + +"Yes." + +Now Tom began to scrawl something on the slate, hiding the words from +the girl. But she was not backward this time. She begged to see. Tom +said: + +"Oh, it ain't anything." + +"Yes it is." + +"No it ain't. You don't want to see." + +"Yes I do, indeed I do. Please let me." + +"You'll tell." + +"No I won't--deed and deed and double deed won't." + +"You won't tell anybody at all? Ever, as long as you live?" + +"No, I won't ever tell ANYbody. Now let me." + +"Oh, YOU don't want to see!" + +"Now that you treat me so, I WILL see." And she put her small hand +upon his and a little scuffle ensued, Tom pretending to resist in +earnest but letting his hand slip by degrees till these words were +revealed: "I LOVE YOU." + +"Oh, you bad thing!" And she hit his hand a smart rap, but reddened +and looked pleased, nevertheless. + +Just at this juncture the boy felt a slow, fateful grip closing on his +ear, and a steady lifting impulse. In that wise he was borne across the +house and deposited in his own seat, under a peppering fire of giggles +from the whole school. Then the master stood over him during a few +awful moments, and finally moved away to his throne without saying a +word. But although Tom's ear tingled, his heart was jubilant. + +As the school quieted down Tom made an honest effort to study, but the +turmoil within him was too great. In turn he took his place in the +reading class and made a botch of it; then in the geography class and +turned lakes into mountains, mountains into rivers, and rivers into +continents, till chaos was come again; then in the spelling class, and +got "turned down," by a succession of mere baby words, till he brought +up at the foot and yielded up the pewter medal which he had worn with +ostentation for months. + + + +CHAPTER VII + +THE harder Tom tried to fasten his mind on his book, the more his +ideas wandered. So at last, with a sigh and a yawn, he gave it up. It +seemed to him that the noon recess would never come. The air was +utterly dead. There was not a breath stirring. It was the sleepiest of +sleepy days. The drowsing murmur of the five and twenty studying +scholars soothed the soul like the spell that is in the murmur of bees. +Away off in the flaming sunshine, Cardiff Hill lifted its soft green +sides through a shimmering veil of heat, tinted with the purple of +distance; a few birds floated on lazy wing high in the air; no other +living thing was visible but some cows, and they were asleep. Tom's +heart ached to be free, or else to have something of interest to do to +pass the dreary time. His hand wandered into his pocket and his face +lit up with a glow of gratitude that was prayer, though he did not know +it. Then furtively the percussion-cap box came out. He released the +tick and put him on the long flat desk. The creature probably glowed +with a gratitude that amounted to prayer, too, at this moment, but it +was premature: for when he started thankfully to travel off, Tom turned +him aside with a pin and made him take a new direction. + +Tom's bosom friend sat next him, suffering just as Tom had been, and +now he was deeply and gratefully interested in this entertainment in an +instant. This bosom friend was Joe Harper. The two boys were sworn +friends all the week, and embattled enemies on Saturdays. Joe took a +pin out of his lapel and began to assist in exercising the prisoner. +The sport grew in interest momently. Soon Tom said that they were +interfering with each other, and neither getting the fullest benefit of +the tick. So he put Joe's slate on the desk and drew a line down the +middle of it from top to bottom. + +"Now," said he, "as long as he is on your side you can stir him up and +I'll let him alone; but if you let him get away and get on my side, +you're to leave him alone as long as I can keep him from crossing over." + +"All right, go ahead; start him up." + +The tick escaped from Tom, presently, and crossed the equator. Joe +harassed him awhile, and then he got away and crossed back again. This +change of base occurred often. While one boy was worrying the tick with +absorbing interest, the other would look on with interest as strong, +the two heads bowed together over the slate, and the two souls dead to +all things else. At last luck seemed to settle and abide with Joe. The +tick tried this, that, and the other course, and got as excited and as +anxious as the boys themselves, but time and again just as he would +have victory in his very grasp, so to speak, and Tom's fingers would be +twitching to begin, Joe's pin would deftly head him off, and keep +possession. At last Tom could stand it no longer. The temptation was +too strong. So he reached out and lent a hand with his pin. Joe was +angry in a moment. Said he: + +"Tom, you let him alone." + +"I only just want to stir him up a little, Joe." + +"No, sir, it ain't fair; you just let him alone." + +"Blame it, I ain't going to stir him much." + +"Let him alone, I tell you." + +"I won't!" + +"You shall--he's on my side of the line." + +"Look here, Joe Harper, whose is that tick?" + +"I don't care whose tick he is--he's on my side of the line, and you +sha'n't touch him." + +"Well, I'll just bet I will, though. He's my tick and I'll do what I +blame please with him, or die!" + +A tremendous whack came down on Tom's shoulders, and its duplicate on +Joe's; and for the space of two minutes the dust continued to fly from +the two jackets and the whole school to enjoy it. The boys had been too +absorbed to notice the hush that had stolen upon the school awhile +before when the master came tiptoeing down the room and stood over +them. He had contemplated a good part of the performance before he +contributed his bit of variety to it. + +When school broke up at noon, Tom flew to Becky Thatcher, and +whispered in her ear: + +"Put on your bonnet and let on you're going home; and when you get to +the corner, give the rest of 'em the slip, and turn down through the +lane and come back. I'll go the other way and come it over 'em the same +way." + +So the one went off with one group of scholars, and the other with +another. In a little while the two met at the bottom of the lane, and +when they reached the school they had it all to themselves. Then they +sat together, with a slate before them, and Tom gave Becky the pencil +and held her hand in his, guiding it, and so created another surprising +house. When the interest in art began to wane, the two fell to talking. +Tom was swimming in bliss. He said: + +"Do you love rats?" + +"No! I hate them!" + +"Well, I do, too--LIVE ones. But I mean dead ones, to swing round your +head with a string." + +"No, I don't care for rats much, anyway. What I like is chewing-gum." + +"Oh, I should say so! I wish I had some now." + +"Do you? I've got some. I'll let you chew it awhile, but you must give +it back to me." + +That was agreeable, so they chewed it turn about, and dangled their +legs against the bench in excess of contentment. + +"Was you ever at a circus?" said Tom. + +"Yes, and my pa's going to take me again some time, if I'm good." + +"I been to the circus three or four times--lots of times. Church ain't +shucks to a circus. There's things going on at a circus all the time. +I'm going to be a clown in a circus when I grow up." + +"Oh, are you! That will be nice. They're so lovely, all spotted up." + +"Yes, that's so. And they get slathers of money--most a dollar a day, +Ben Rogers says. Say, Becky, was you ever engaged?" + +"What's that?" + +"Why, engaged to be married." + +"No." + +"Would you like to?" + +"I reckon so. I don't know. What is it like?" + +"Like? Why it ain't like anything. You only just tell a boy you won't +ever have anybody but him, ever ever ever, and then you kiss and that's +all. Anybody can do it." + +"Kiss? What do you kiss for?" + +"Why, that, you know, is to--well, they always do that." + +"Everybody?" + +"Why, yes, everybody that's in love with each other. Do you remember +what I wrote on the slate?" + +"Ye--yes." + +"What was it?" + +"I sha'n't tell you." + +"Shall I tell YOU?" + +"Ye--yes--but some other time." + +"No, now." + +"No, not now--to-morrow." + +"Oh, no, NOW. Please, Becky--I'll whisper it, I'll whisper it ever so +easy." + +Becky hesitating, Tom took silence for consent, and passed his arm +about her waist and whispered the tale ever so softly, with his mouth +close to her ear. And then he added: + +"Now you whisper it to me--just the same." + +She resisted, for a while, and then said: + +"You turn your face away so you can't see, and then I will. But you +mustn't ever tell anybody--WILL you, Tom? Now you won't, WILL you?" + +"No, indeed, indeed I won't. Now, Becky." + +He turned his face away. She bent timidly around till her breath +stirred his curls and whispered, "I--love--you!" + +Then she sprang away and ran around and around the desks and benches, +with Tom after her, and took refuge in a corner at last, with her +little white apron to her face. Tom clasped her about her neck and +pleaded: + +"Now, Becky, it's all done--all over but the kiss. Don't you be afraid +of that--it ain't anything at all. Please, Becky." And he tugged at her +apron and the hands. + +By and by she gave up, and let her hands drop; her face, all glowing +with the struggle, came up and submitted. Tom kissed the red lips and +said: + +"Now it's all done, Becky. And always after this, you know, you ain't +ever to love anybody but me, and you ain't ever to marry anybody but +me, ever never and forever. Will you?" + +"No, I'll never love anybody but you, Tom, and I'll never marry +anybody but you--and you ain't to ever marry anybody but me, either." + +"Certainly. Of course. That's PART of it. And always coming to school +or when we're going home, you're to walk with me, when there ain't +anybody looking--and you choose me and I choose you at parties, because +that's the way you do when you're engaged." + +"It's so nice. I never heard of it before." + +"Oh, it's ever so gay! Why, me and Amy Lawrence--" + +The big eyes told Tom his blunder and he stopped, confused. + +"Oh, Tom! Then I ain't the first you've ever been engaged to!" + +The child began to cry. Tom said: + +"Oh, don't cry, Becky, I don't care for her any more." + +"Yes, you do, Tom--you know you do." + +Tom tried to put his arm about her neck, but she pushed him away and +turned her face to the wall, and went on crying. Tom tried again, with +soothing words in his mouth, and was repulsed again. Then his pride was +up, and he strode away and went outside. He stood about, restless and +uneasy, for a while, glancing at the door, every now and then, hoping +she would repent and come to find him. But she did not. Then he began +to feel badly and fear that he was in the wrong. It was a hard struggle +with him to make new advances, now, but he nerved himself to it and +entered. She was still standing back there in the corner, sobbing, with +her face to the wall. Tom's heart smote him. He went to her and stood a +moment, not knowing exactly how to proceed. Then he said hesitatingly: + +"Becky, I--I don't care for anybody but you." + +No reply--but sobs. + +"Becky"--pleadingly. "Becky, won't you say something?" + +More sobs. + +Tom got out his chiefest jewel, a brass knob from the top of an +andiron, and passed it around her so that she could see it, and said: + +"Please, Becky, won't you take it?" + +She struck it to the floor. Then Tom marched out of the house and over +the hills and far away, to return to school no more that day. Presently +Becky began to suspect. She ran to the door; he was not in sight; she +flew around to the play-yard; he was not there. Then she called: + +"Tom! Come back, Tom!" + +She listened intently, but there was no answer. She had no companions +but silence and loneliness. So she sat down to cry again and upbraid +herself; and by this time the scholars began to gather again, and she +had to hide her griefs and still her broken heart and take up the cross +of a long, dreary, aching afternoon, with none among the strangers +about her to exchange sorrows with. + + + +CHAPTER VIII + +TOM dodged hither and thither through lanes until he was well out of +the track of returning scholars, and then fell into a moody jog. He +crossed a small "branch" two or three times, because of a prevailing +juvenile superstition that to cross water baffled pursuit. Half an hour +later he was disappearing behind the Douglas mansion on the summit of +Cardiff Hill, and the schoolhouse was hardly distinguishable away off +in the valley behind him. He entered a dense wood, picked his pathless +way to the centre of it, and sat down on a mossy spot under a spreading +oak. There was not even a zephyr stirring; the dead noonday heat had +even stilled the songs of the birds; nature lay in a trance that was +broken by no sound but the occasional far-off hammering of a +woodpecker, and this seemed to render the pervading silence and sense +of loneliness the more profound. The boy's soul was steeped in +melancholy; his feelings were in happy accord with his surroundings. He +sat long with his elbows on his knees and his chin in his hands, +meditating. It seemed to him that life was but a trouble, at best, and +he more than half envied Jimmy Hodges, so lately released; it must be +very peaceful, he thought, to lie and slumber and dream forever and +ever, with the wind whispering through the trees and caressing the +grass and the flowers over the grave, and nothing to bother and grieve +about, ever any more. If he only had a clean Sunday-school record he +could be willing to go, and be done with it all. Now as to this girl. +What had he done? Nothing. He had meant the best in the world, and been +treated like a dog--like a very dog. She would be sorry some day--maybe +when it was too late. Ah, if he could only die TEMPORARILY! + +But the elastic heart of youth cannot be compressed into one +constrained shape long at a time. Tom presently began to drift +insensibly back into the concerns of this life again. What if he turned +his back, now, and disappeared mysteriously? What if he went away--ever +so far away, into unknown countries beyond the seas--and never came +back any more! How would she feel then! The idea of being a clown +recurred to him now, only to fill him with disgust. For frivolity and +jokes and spotted tights were an offense, when they intruded themselves +upon a spirit that was exalted into the vague august realm of the +romantic. No, he would be a soldier, and return after long years, all +war-worn and illustrious. No--better still, he would join the Indians, +and hunt buffaloes and go on the warpath in the mountain ranges and the +trackless great plains of the Far West, and away in the future come +back a great chief, bristling with feathers, hideous with paint, and +prance into Sunday-school, some drowsy summer morning, with a +bloodcurdling war-whoop, and sear the eyeballs of all his companions +with unappeasable envy. But no, there was something gaudier even than +this. He would be a pirate! That was it! NOW his future lay plain +before him, and glowing with unimaginable splendor. How his name would +fill the world, and make people shudder! How gloriously he would go +plowing the dancing seas, in his long, low, black-hulled racer, the +Spirit of the Storm, with his grisly flag flying at the fore! And at +the zenith of his fame, how he would suddenly appear at the old village +and stalk into church, brown and weather-beaten, in his black velvet +doublet and trunks, his great jack-boots, his crimson sash, his belt +bristling with horse-pistols, his crime-rusted cutlass at his side, his +slouch hat with waving plumes, his black flag unfurled, with the skull +and crossbones on it, and hear with swelling ecstasy the whisperings, +"It's Tom Sawyer the Pirate!--the Black Avenger of the Spanish Main!" + +Yes, it was settled; his career was determined. He would run away from +home and enter upon it. He would start the very next morning. Therefore +he must now begin to get ready. He would collect his resources +together. He went to a rotten log near at hand and began to dig under +one end of it with his Barlow knife. He soon struck wood that sounded +hollow. He put his hand there and uttered this incantation impressively: + +"What hasn't come here, come! What's here, stay here!" + +Then he scraped away the dirt, and exposed a pine shingle. He took it +up and disclosed a shapely little treasure-house whose bottom and sides +were of shingles. In it lay a marble. Tom's astonishment was boundless! +He scratched his head with a perplexed air, and said: + +"Well, that beats anything!" + +Then he tossed the marble away pettishly, and stood cogitating. The +truth was, that a superstition of his had failed, here, which he and +all his comrades had always looked upon as infallible. If you buried a +marble with certain necessary incantations, and left it alone a +fortnight, and then opened the place with the incantation he had just +used, you would find that all the marbles you had ever lost had +gathered themselves together there, meantime, no matter how widely they +had been separated. But now, this thing had actually and unquestionably +failed. Tom's whole structure of faith was shaken to its foundations. +He had many a time heard of this thing succeeding but never of its +failing before. It did not occur to him that he had tried it several +times before, himself, but could never find the hiding-places +afterward. He puzzled over the matter some time, and finally decided +that some witch had interfered and broken the charm. He thought he +would satisfy himself on that point; so he searched around till he +found a small sandy spot with a little funnel-shaped depression in it. +He laid himself down and put his mouth close to this depression and +called-- + +"Doodle-bug, doodle-bug, tell me what I want to know! Doodle-bug, +doodle-bug, tell me what I want to know!" + +The sand began to work, and presently a small black bug appeared for a +second and then darted under again in a fright. + +"He dasn't tell! So it WAS a witch that done it. I just knowed it." + +He well knew the futility of trying to contend against witches, so he +gave up discouraged. But it occurred to him that he might as well have +the marble he had just thrown away, and therefore he went and made a +patient search for it. But he could not find it. Now he went back to +his treasure-house and carefully placed himself just as he had been +standing when he tossed the marble away; then he took another marble +from his pocket and tossed it in the same way, saying: + +"Brother, go find your brother!" + +He watched where it stopped, and went there and looked. But it must +have fallen short or gone too far; so he tried twice more. The last +repetition was successful. The two marbles lay within a foot of each +other. + +Just here the blast of a toy tin trumpet came faintly down the green +aisles of the forest. Tom flung off his jacket and trousers, turned a +suspender into a belt, raked away some brush behind the rotten log, +disclosing a rude bow and arrow, a lath sword and a tin trumpet, and in +a moment had seized these things and bounded away, barelegged, with +fluttering shirt. He presently halted under a great elm, blew an +answering blast, and then began to tiptoe and look warily out, this way +and that. He said cautiously--to an imaginary company: + +"Hold, my merry men! Keep hid till I blow." + +Now appeared Joe Harper, as airily clad and elaborately armed as Tom. +Tom called: + +"Hold! Who comes here into Sherwood Forest without my pass?" + +"Guy of Guisborne wants no man's pass. Who art thou that--that--" + +"Dares to hold such language," said Tom, prompting--for they talked +"by the book," from memory. + +"Who art thou that dares to hold such language?" + +"I, indeed! I am Robin Hood, as thy caitiff carcase soon shall know." + +"Then art thou indeed that famous outlaw? Right gladly will I dispute +with thee the passes of the merry wood. Have at thee!" + +They took their lath swords, dumped their other traps on the ground, +struck a fencing attitude, foot to foot, and began a grave, careful +combat, "two up and two down." Presently Tom said: + +"Now, if you've got the hang, go it lively!" + +So they "went it lively," panting and perspiring with the work. By and +by Tom shouted: + +"Fall! fall! Why don't you fall?" + +"I sha'n't! Why don't you fall yourself? You're getting the worst of +it." + +"Why, that ain't anything. I can't fall; that ain't the way it is in +the book. The book says, 'Then with one back-handed stroke he slew poor +Guy of Guisborne.' You're to turn around and let me hit you in the +back." + +There was no getting around the authorities, so Joe turned, received +the whack and fell. + +"Now," said Joe, getting up, "you got to let me kill YOU. That's fair." + +"Why, I can't do that, it ain't in the book." + +"Well, it's blamed mean--that's all." + +"Well, say, Joe, you can be Friar Tuck or Much the miller's son, and +lam me with a quarter-staff; or I'll be the Sheriff of Nottingham and +you be Robin Hood a little while and kill me." + +This was satisfactory, and so these adventures were carried out. Then +Tom became Robin Hood again, and was allowed by the treacherous nun to +bleed his strength away through his neglected wound. And at last Joe, +representing a whole tribe of weeping outlaws, dragged him sadly forth, +gave his bow into his feeble hands, and Tom said, "Where this arrow +falls, there bury poor Robin Hood under the greenwood tree." Then he +shot the arrow and fell back and would have died, but he lit on a +nettle and sprang up too gaily for a corpse. + +The boys dressed themselves, hid their accoutrements, and went off +grieving that there were no outlaws any more, and wondering what modern +civilization could claim to have done to compensate for their loss. +They said they would rather be outlaws a year in Sherwood Forest than +President of the United States forever. + + + +CHAPTER IX + +AT half-past nine, that night, Tom and Sid were sent to bed, as usual. +They said their prayers, and Sid was soon asleep. Tom lay awake and +waited, in restless impatience. When it seemed to him that it must be +nearly daylight, he heard the clock strike ten! This was despair. He +would have tossed and fidgeted, as his nerves demanded, but he was +afraid he might wake Sid. So he lay still, and stared up into the dark. +Everything was dismally still. By and by, out of the stillness, little, +scarcely perceptible noises began to emphasize themselves. The ticking +of the clock began to bring itself into notice. Old beams began to +crack mysteriously. The stairs creaked faintly. Evidently spirits were +abroad. A measured, muffled snore issued from Aunt Polly's chamber. And +now the tiresome chirping of a cricket that no human ingenuity could +locate, began. Next the ghastly ticking of a deathwatch in the wall at +the bed's head made Tom shudder--it meant that somebody's days were +numbered. Then the howl of a far-off dog rose on the night air, and was +answered by a fainter howl from a remoter distance. Tom was in an +agony. At last he was satisfied that time had ceased and eternity +begun; he began to doze, in spite of himself; the clock chimed eleven, +but he did not hear it. And then there came, mingling with his +half-formed dreams, a most melancholy caterwauling. The raising of a +neighboring window disturbed him. A cry of "Scat! you devil!" and the +crash of an empty bottle against the back of his aunt's woodshed +brought him wide awake, and a single minute later he was dressed and +out of the window and creeping along the roof of the "ell" on all +fours. He "meow'd" with caution once or twice, as he went; then jumped +to the roof of the woodshed and thence to the ground. Huckleberry Finn +was there, with his dead cat. The boys moved off and disappeared in the +gloom. At the end of half an hour they were wading through the tall +grass of the graveyard. + +It was a graveyard of the old-fashioned Western kind. It was on a +hill, about a mile and a half from the village. It had a crazy board +fence around it, which leaned inward in places, and outward the rest of +the time, but stood upright nowhere. Grass and weeds grew rank over the +whole cemetery. All the old graves were sunken in, there was not a +tombstone on the place; round-topped, worm-eaten boards staggered over +the graves, leaning for support and finding none. "Sacred to the memory +of" So-and-So had been painted on them once, but it could no longer +have been read, on the most of them, now, even if there had been light. + +A faint wind moaned through the trees, and Tom feared it might be the +spirits of the dead, complaining at being disturbed. The boys talked +little, and only under their breath, for the time and the place and the +pervading solemnity and silence oppressed their spirits. They found the +sharp new heap they were seeking, and ensconced themselves within the +protection of three great elms that grew in a bunch within a few feet +of the grave. + +Then they waited in silence for what seemed a long time. The hooting +of a distant owl was all the sound that troubled the dead stillness. +Tom's reflections grew oppressive. He must force some talk. So he said +in a whisper: + +"Hucky, do you believe the dead people like it for us to be here?" + +Huckleberry whispered: + +"I wisht I knowed. It's awful solemn like, AIN'T it?" + +"I bet it is." + +There was a considerable pause, while the boys canvassed this matter +inwardly. Then Tom whispered: + +"Say, Hucky--do you reckon Hoss Williams hears us talking?" + +"O' course he does. Least his sperrit does." + +Tom, after a pause: + +"I wish I'd said Mister Williams. But I never meant any harm. +Everybody calls him Hoss." + +"A body can't be too partic'lar how they talk 'bout these-yer dead +people, Tom." + +This was a damper, and conversation died again. + +Presently Tom seized his comrade's arm and said: + +"Sh!" + +"What is it, Tom?" And the two clung together with beating hearts. + +"Sh! There 'tis again! Didn't you hear it?" + +"I--" + +"There! Now you hear it." + +"Lord, Tom, they're coming! They're coming, sure. What'll we do?" + +"I dono. Think they'll see us?" + +"Oh, Tom, they can see in the dark, same as cats. I wisht I hadn't +come." + +"Oh, don't be afeard. I don't believe they'll bother us. We ain't +doing any harm. If we keep perfectly still, maybe they won't notice us +at all." + +"I'll try to, Tom, but, Lord, I'm all of a shiver." + +"Listen!" + +The boys bent their heads together and scarcely breathed. A muffled +sound of voices floated up from the far end of the graveyard. + +"Look! See there!" whispered Tom. "What is it?" + +"It's devil-fire. Oh, Tom, this is awful." + +Some vague figures approached through the gloom, swinging an +old-fashioned tin lantern that freckled the ground with innumerable +little spangles of light. Presently Huckleberry whispered with a +shudder: + +"It's the devils sure enough. Three of 'em! Lordy, Tom, we're goners! +Can you pray?" + +"I'll try, but don't you be afeard. They ain't going to hurt us. 'Now +I lay me down to sleep, I--'" + +"Sh!" + +"What is it, Huck?" + +"They're HUMANS! One of 'em is, anyway. One of 'em's old Muff Potter's +voice." + +"No--'tain't so, is it?" + +"I bet I know it. Don't you stir nor budge. He ain't sharp enough to +notice us. Drunk, the same as usual, likely--blamed old rip!" + +"All right, I'll keep still. Now they're stuck. Can't find it. Here +they come again. Now they're hot. Cold again. Hot again. Red hot! +They're p'inted right, this time. Say, Huck, I know another o' them +voices; it's Injun Joe." + +"That's so--that murderin' half-breed! I'd druther they was devils a +dern sight. What kin they be up to?" + +The whisper died wholly out, now, for the three men had reached the +grave and stood within a few feet of the boys' hiding-place. + +"Here it is," said the third voice; and the owner of it held the +lantern up and revealed the face of young Doctor Robinson. + +Potter and Injun Joe were carrying a handbarrow with a rope and a +couple of shovels on it. They cast down their load and began to open +the grave. The doctor put the lantern at the head of the grave and came +and sat down with his back against one of the elm trees. He was so +close the boys could have touched him. + +"Hurry, men!" he said, in a low voice; "the moon might come out at any +moment." + +They growled a response and went on digging. For some time there was +no noise but the grating sound of the spades discharging their freight +of mould and gravel. It was very monotonous. Finally a spade struck +upon the coffin with a dull woody accent, and within another minute or +two the men had hoisted it out on the ground. They pried off the lid +with their shovels, got out the body and dumped it rudely on the +ground. The moon drifted from behind the clouds and exposed the pallid +face. The barrow was got ready and the corpse placed on it, covered +with a blanket, and bound to its place with the rope. Potter took out a +large spring-knife and cut off the dangling end of the rope and then +said: + +"Now the cussed thing's ready, Sawbones, and you'll just out with +another five, or here she stays." + +"That's the talk!" said Injun Joe. + +"Look here, what does this mean?" said the doctor. "You required your +pay in advance, and I've paid you." + +"Yes, and you done more than that," said Injun Joe, approaching the +doctor, who was now standing. "Five years ago you drove me away from +your father's kitchen one night, when I come to ask for something to +eat, and you said I warn't there for any good; and when I swore I'd get +even with you if it took a hundred years, your father had me jailed for +a vagrant. Did you think I'd forget? The Injun blood ain't in me for +nothing. And now I've GOT you, and you got to SETTLE, you know!" + +He was threatening the doctor, with his fist in his face, by this +time. The doctor struck out suddenly and stretched the ruffian on the +ground. Potter dropped his knife, and exclaimed: + +"Here, now, don't you hit my pard!" and the next moment he had +grappled with the doctor and the two were struggling with might and +main, trampling the grass and tearing the ground with their heels. +Injun Joe sprang to his feet, his eyes flaming with passion, snatched +up Potter's knife, and went creeping, catlike and stooping, round and +round about the combatants, seeking an opportunity. All at once the +doctor flung himself free, seized the heavy headboard of Williams' +grave and felled Potter to the earth with it--and in the same instant +the half-breed saw his chance and drove the knife to the hilt in the +young man's breast. He reeled and fell partly upon Potter, flooding him +with his blood, and in the same moment the clouds blotted out the +dreadful spectacle and the two frightened boys went speeding away in +the dark. + +Presently, when the moon emerged again, Injun Joe was standing over +the two forms, contemplating them. The doctor murmured inarticulately, +gave a long gasp or two and was still. The half-breed muttered: + +"THAT score is settled--damn you." + +Then he robbed the body. After which he put the fatal knife in +Potter's open right hand, and sat down on the dismantled coffin. Three +--four--five minutes passed, and then Potter began to stir and moan. His +hand closed upon the knife; he raised it, glanced at it, and let it +fall, with a shudder. Then he sat up, pushing the body from him, and +gazed at it, and then around him, confusedly. His eyes met Joe's. + +"Lord, how is this, Joe?" he said. + +"It's a dirty business," said Joe, without moving. + +"What did you do it for?" + +"I! I never done it!" + +"Look here! That kind of talk won't wash." + +Potter trembled and grew white. + +"I thought I'd got sober. I'd no business to drink to-night. But it's +in my head yet--worse'n when we started here. I'm all in a muddle; +can't recollect anything of it, hardly. Tell me, Joe--HONEST, now, old +feller--did I do it? Joe, I never meant to--'pon my soul and honor, I +never meant to, Joe. Tell me how it was, Joe. Oh, it's awful--and him +so young and promising." + +"Why, you two was scuffling, and he fetched you one with the headboard +and you fell flat; and then up you come, all reeling and staggering +like, and snatched the knife and jammed it into him, just as he fetched +you another awful clip--and here you've laid, as dead as a wedge til +now." + +"Oh, I didn't know what I was a-doing. I wish I may die this minute if +I did. It was all on account of the whiskey and the excitement, I +reckon. I never used a weepon in my life before, Joe. I've fought, but +never with weepons. They'll all say that. Joe, don't tell! Say you +won't tell, Joe--that's a good feller. I always liked you, Joe, and +stood up for you, too. Don't you remember? You WON'T tell, WILL you, +Joe?" And the poor creature dropped on his knees before the stolid +murderer, and clasped his appealing hands. + +"No, you've always been fair and square with me, Muff Potter, and I +won't go back on you. There, now, that's as fair as a man can say." + +"Oh, Joe, you're an angel. I'll bless you for this the longest day I +live." And Potter began to cry. + +"Come, now, that's enough of that. This ain't any time for blubbering. +You be off yonder way and I'll go this. Move, now, and don't leave any +tracks behind you." + +Potter started on a trot that quickly increased to a run. The +half-breed stood looking after him. He muttered: + +"If he's as much stunned with the lick and fuddled with the rum as he +had the look of being, he won't think of the knife till he's gone so +far he'll be afraid to come back after it to such a place by himself +--chicken-heart!" + +Two or three minutes later the murdered man, the blanketed corpse, the +lidless coffin, and the open grave were under no inspection but the +moon's. The stillness was complete again, too. + + + +CHAPTER X + +THE two boys flew on and on, toward the village, speechless with +horror. They glanced backward over their shoulders from time to time, +apprehensively, as if they feared they might be followed. Every stump +that started up in their path seemed a man and an enemy, and made them +catch their breath; and as they sped by some outlying cottages that lay +near the village, the barking of the aroused watch-dogs seemed to give +wings to their feet. + +"If we can only get to the old tannery before we break down!" +whispered Tom, in short catches between breaths. "I can't stand it much +longer." + +Huckleberry's hard pantings were his only reply, and the boys fixed +their eyes on the goal of their hopes and bent to their work to win it. +They gained steadily on it, and at last, breast to breast, they burst +through the open door and fell grateful and exhausted in the sheltering +shadows beyond. By and by their pulses slowed down, and Tom whispered: + +"Huckleberry, what do you reckon'll come of this?" + +"If Doctor Robinson dies, I reckon hanging'll come of it." + +"Do you though?" + +"Why, I KNOW it, Tom." + +Tom thought a while, then he said: + +"Who'll tell? We?" + +"What are you talking about? S'pose something happened and Injun Joe +DIDN'T hang? Why, he'd kill us some time or other, just as dead sure as +we're a laying here." + +"That's just what I was thinking to myself, Huck." + +"If anybody tells, let Muff Potter do it, if he's fool enough. He's +generally drunk enough." + +Tom said nothing--went on thinking. Presently he whispered: + +"Huck, Muff Potter don't know it. How can he tell?" + +"What's the reason he don't know it?" + +"Because he'd just got that whack when Injun Joe done it. D'you reckon +he could see anything? D'you reckon he knowed anything?" + +"By hokey, that's so, Tom!" + +"And besides, look-a-here--maybe that whack done for HIM!" + +"No, 'taint likely, Tom. He had liquor in him; I could see that; and +besides, he always has. Well, when pap's full, you might take and belt +him over the head with a church and you couldn't phase him. He says so, +his own self. So it's the same with Muff Potter, of course. But if a +man was dead sober, I reckon maybe that whack might fetch him; I dono." + +After another reflective silence, Tom said: + +"Hucky, you sure you can keep mum?" + +"Tom, we GOT to keep mum. You know that. That Injun devil wouldn't +make any more of drownding us than a couple of cats, if we was to +squeak 'bout this and they didn't hang him. Now, look-a-here, Tom, less +take and swear to one another--that's what we got to do--swear to keep +mum." + +"I'm agreed. It's the best thing. Would you just hold hands and swear +that we--" + +"Oh no, that wouldn't do for this. That's good enough for little +rubbishy common things--specially with gals, cuz THEY go back on you +anyway, and blab if they get in a huff--but there orter be writing +'bout a big thing like this. And blood." + +Tom's whole being applauded this idea. It was deep, and dark, and +awful; the hour, the circumstances, the surroundings, were in keeping +with it. He picked up a clean pine shingle that lay in the moonlight, +took a little fragment of "red keel" out of his pocket, got the moon on +his work, and painfully scrawled these lines, emphasizing each slow +down-stroke by clamping his tongue between his teeth, and letting up +the pressure on the up-strokes. [See next page.] + + "Huck Finn and + Tom Sawyer swears + they will keep mum + about This and They + wish They may Drop + down dead in Their + Tracks if They ever + Tell and Rot." + +Huckleberry was filled with admiration of Tom's facility in writing, +and the sublimity of his language. He at once took a pin from his lapel +and was going to prick his flesh, but Tom said: + +"Hold on! Don't do that. A pin's brass. It might have verdigrease on +it." + +"What's verdigrease?" + +"It's p'ison. That's what it is. You just swaller some of it once +--you'll see." + +So Tom unwound the thread from one of his needles, and each boy +pricked the ball of his thumb and squeezed out a drop of blood. In +time, after many squeezes, Tom managed to sign his initials, using the +ball of his little finger for a pen. Then he showed Huckleberry how to +make an H and an F, and the oath was complete. They buried the shingle +close to the wall, with some dismal ceremonies and incantations, and +the fetters that bound their tongues were considered to be locked and +the key thrown away. + +A figure crept stealthily through a break in the other end of the +ruined building, now, but they did not notice it. + +"Tom," whispered Huckleberry, "does this keep us from EVER telling +--ALWAYS?" + +"Of course it does. It don't make any difference WHAT happens, we got +to keep mum. We'd drop down dead--don't YOU know that?" + +"Yes, I reckon that's so." + +They continued to whisper for some little time. Presently a dog set up +a long, lugubrious howl just outside--within ten feet of them. The boys +clasped each other suddenly, in an agony of fright. + +"Which of us does he mean?" gasped Huckleberry. + +"I dono--peep through the crack. Quick!" + +"No, YOU, Tom!" + +"I can't--I can't DO it, Huck!" + +"Please, Tom. There 'tis again!" + +"Oh, lordy, I'm thankful!" whispered Tom. "I know his voice. It's Bull +Harbison." * + +[* If Mr. Harbison owned a slave named Bull, Tom would have spoken of +him as "Harbison's Bull," but a son or a dog of that name was "Bull +Harbison."] + +"Oh, that's good--I tell you, Tom, I was most scared to death; I'd a +bet anything it was a STRAY dog." + +The dog howled again. The boys' hearts sank once more. + +"Oh, my! that ain't no Bull Harbison!" whispered Huckleberry. "DO, Tom!" + +Tom, quaking with fear, yielded, and put his eye to the crack. His +whisper was hardly audible when he said: + +"Oh, Huck, IT S A STRAY DOG!" + +"Quick, Tom, quick! Who does he mean?" + +"Huck, he must mean us both--we're right together." + +"Oh, Tom, I reckon we're goners. I reckon there ain't no mistake 'bout +where I'LL go to. I been so wicked." + +"Dad fetch it! This comes of playing hookey and doing everything a +feller's told NOT to do. I might a been good, like Sid, if I'd a tried +--but no, I wouldn't, of course. But if ever I get off this time, I lay +I'll just WALLER in Sunday-schools!" And Tom began to snuffle a little. + +"YOU bad!" and Huckleberry began to snuffle too. "Consound it, Tom +Sawyer, you're just old pie, 'longside o' what I am. Oh, LORDY, lordy, +lordy, I wisht I only had half your chance." + +Tom choked off and whispered: + +"Look, Hucky, look! He's got his BACK to us!" + +Hucky looked, with joy in his heart. + +"Well, he has, by jingoes! Did he before?" + +"Yes, he did. But I, like a fool, never thought. Oh, this is bully, +you know. NOW who can he mean?" + +The howling stopped. Tom pricked up his ears. + +"Sh! What's that?" he whispered. + +"Sounds like--like hogs grunting. No--it's somebody snoring, Tom." + +"That IS it! Where 'bouts is it, Huck?" + +"I bleeve it's down at 'tother end. Sounds so, anyway. Pap used to +sleep there, sometimes, 'long with the hogs, but laws bless you, he +just lifts things when HE snores. Besides, I reckon he ain't ever +coming back to this town any more." + +The spirit of adventure rose in the boys' souls once more. + +"Hucky, do you das't to go if I lead?" + +"I don't like to, much. Tom, s'pose it's Injun Joe!" + +Tom quailed. But presently the temptation rose up strong again and the +boys agreed to try, with the understanding that they would take to +their heels if the snoring stopped. So they went tiptoeing stealthily +down, the one behind the other. When they had got to within five steps +of the snorer, Tom stepped on a stick, and it broke with a sharp snap. +The man moaned, writhed a little, and his face came into the moonlight. +It was Muff Potter. The boys' hearts had stood still, and their hopes +too, when the man moved, but their fears passed away now. They tiptoed +out, through the broken weather-boarding, and stopped at a little +distance to exchange a parting word. That long, lugubrious howl rose on +the night air again! They turned and saw the strange dog standing +within a few feet of where Potter was lying, and FACING Potter, with +his nose pointing heavenward. + +"Oh, geeminy, it's HIM!" exclaimed both boys, in a breath. + +"Say, Tom--they say a stray dog come howling around Johnny Miller's +house, 'bout midnight, as much as two weeks ago; and a whippoorwill +come in and lit on the banisters and sung, the very same evening; and +there ain't anybody dead there yet." + +"Well, I know that. And suppose there ain't. Didn't Gracie Miller fall +in the kitchen fire and burn herself terrible the very next Saturday?" + +"Yes, but she ain't DEAD. And what's more, she's getting better, too." + +"All right, you wait and see. She's a goner, just as dead sure as Muff +Potter's a goner. That's what the niggers say, and they know all about +these kind of things, Huck." + +Then they separated, cogitating. When Tom crept in at his bedroom +window the night was almost spent. He undressed with excessive caution, +and fell asleep congratulating himself that nobody knew of his +escapade. He was not aware that the gently-snoring Sid was awake, and +had been so for an hour. + +When Tom awoke, Sid was dressed and gone. There was a late look in the +light, a late sense in the atmosphere. He was startled. Why had he not +been called--persecuted till he was up, as usual? The thought filled +him with bodings. Within five minutes he was dressed and down-stairs, +feeling sore and drowsy. The family were still at table, but they had +finished breakfast. There was no voice of rebuke; but there were +averted eyes; there was a silence and an air of solemnity that struck a +chill to the culprit's heart. He sat down and tried to seem gay, but it +was up-hill work; it roused no smile, no response, and he lapsed into +silence and let his heart sink down to the depths. + +After breakfast his aunt took him aside, and Tom almost brightened in +the hope that he was going to be flogged; but it was not so. His aunt +wept over him and asked him how he could go and break her old heart so; +and finally told him to go on, and ruin himself and bring her gray +hairs with sorrow to the grave, for it was no use for her to try any +more. This was worse than a thousand whippings, and Tom's heart was +sorer now than his body. He cried, he pleaded for forgiveness, promised +to reform over and over again, and then received his dismissal, feeling +that he had won but an imperfect forgiveness and established but a +feeble confidence. + +He left the presence too miserable to even feel revengeful toward Sid; +and so the latter's prompt retreat through the back gate was +unnecessary. He moped to school gloomy and sad, and took his flogging, +along with Joe Harper, for playing hookey the day before, with the air +of one whose heart was busy with heavier woes and wholly dead to +trifles. Then he betook himself to his seat, rested his elbows on his +desk and his jaws in his hands, and stared at the wall with the stony +stare of suffering that has reached the limit and can no further go. +His elbow was pressing against some hard substance. After a long time +he slowly and sadly changed his position, and took up this object with +a sigh. It was in a paper. He unrolled it. A long, lingering, colossal +sigh followed, and his heart broke. It was his brass andiron knob! + +This final feather broke the camel's back. + + + +CHAPTER XI + +CLOSE upon the hour of noon the whole village was suddenly electrified +with the ghastly news. No need of the as yet undreamed-of telegraph; +the tale flew from man to man, from group to group, from house to +house, with little less than telegraphic speed. Of course the +schoolmaster gave holiday for that afternoon; the town would have +thought strangely of him if he had not. + +A gory knife had been found close to the murdered man, and it had been +recognized by somebody as belonging to Muff Potter--so the story ran. +And it was said that a belated citizen had come upon Potter washing +himself in the "branch" about one or two o'clock in the morning, and +that Potter had at once sneaked off--suspicious circumstances, +especially the washing which was not a habit with Potter. It was also +said that the town had been ransacked for this "murderer" (the public +are not slow in the matter of sifting evidence and arriving at a +verdict), but that he could not be found. Horsemen had departed down +all the roads in every direction, and the Sheriff "was confident" that +he would be captured before night. + +All the town was drifting toward the graveyard. Tom's heartbreak +vanished and he joined the procession, not because he would not a +thousand times rather go anywhere else, but because an awful, +unaccountable fascination drew him on. Arrived at the dreadful place, +he wormed his small body through the crowd and saw the dismal +spectacle. It seemed to him an age since he was there before. Somebody +pinched his arm. He turned, and his eyes met Huckleberry's. Then both +looked elsewhere at once, and wondered if anybody had noticed anything +in their mutual glance. But everybody was talking, and intent upon the +grisly spectacle before them. + +"Poor fellow!" "Poor young fellow!" "This ought to be a lesson to +grave robbers!" "Muff Potter'll hang for this if they catch him!" This +was the drift of remark; and the minister said, "It was a judgment; His +hand is here." + +Now Tom shivered from head to heel; for his eye fell upon the stolid +face of Injun Joe. At this moment the crowd began to sway and struggle, +and voices shouted, "It's him! it's him! he's coming himself!" + +"Who? Who?" from twenty voices. + +"Muff Potter!" + +"Hallo, he's stopped!--Look out, he's turning! Don't let him get away!" + +People in the branches of the trees over Tom's head said he wasn't +trying to get away--he only looked doubtful and perplexed. + +"Infernal impudence!" said a bystander; "wanted to come and take a +quiet look at his work, I reckon--didn't expect any company." + +The crowd fell apart, now, and the Sheriff came through, +ostentatiously leading Potter by the arm. The poor fellow's face was +haggard, and his eyes showed the fear that was upon him. When he stood +before the murdered man, he shook as with a palsy, and he put his face +in his hands and burst into tears. + +"I didn't do it, friends," he sobbed; "'pon my word and honor I never +done it." + +"Who's accused you?" shouted a voice. + +This shot seemed to carry home. Potter lifted his face and looked +around him with a pathetic hopelessness in his eyes. He saw Injun Joe, +and exclaimed: + +"Oh, Injun Joe, you promised me you'd never--" + +"Is that your knife?" and it was thrust before him by the Sheriff. + +Potter would have fallen if they had not caught him and eased him to +the ground. Then he said: + +"Something told me 't if I didn't come back and get--" He shuddered; +then waved his nerveless hand with a vanquished gesture and said, "Tell +'em, Joe, tell 'em--it ain't any use any more." + +Then Huckleberry and Tom stood dumb and staring, and heard the +stony-hearted liar reel off his serene statement, they expecting every +moment that the clear sky would deliver God's lightnings upon his head, +and wondering to see how long the stroke was delayed. And when he had +finished and still stood alive and whole, their wavering impulse to +break their oath and save the poor betrayed prisoner's life faded and +vanished away, for plainly this miscreant had sold himself to Satan and +it would be fatal to meddle with the property of such a power as that. + +"Why didn't you leave? What did you want to come here for?" somebody +said. + +"I couldn't help it--I couldn't help it," Potter moaned. "I wanted to +run away, but I couldn't seem to come anywhere but here." And he fell +to sobbing again. + +Injun Joe repeated his statement, just as calmly, a few minutes +afterward on the inquest, under oath; and the boys, seeing that the +lightnings were still withheld, were confirmed in their belief that Joe +had sold himself to the devil. He was now become, to them, the most +balefully interesting object they had ever looked upon, and they could +not take their fascinated eyes from his face. + +They inwardly resolved to watch him nights, when opportunity should +offer, in the hope of getting a glimpse of his dread master. + +Injun Joe helped to raise the body of the murdered man and put it in a +wagon for removal; and it was whispered through the shuddering crowd +that the wound bled a little! The boys thought that this happy +circumstance would turn suspicion in the right direction; but they were +disappointed, for more than one villager remarked: + +"It was within three feet of Muff Potter when it done it." + +Tom's fearful secret and gnawing conscience disturbed his sleep for as +much as a week after this; and at breakfast one morning Sid said: + +"Tom, you pitch around and talk in your sleep so much that you keep me +awake half the time." + +Tom blanched and dropped his eyes. + +"It's a bad sign," said Aunt Polly, gravely. "What you got on your +mind, Tom?" + +"Nothing. Nothing 't I know of." But the boy's hand shook so that he +spilled his coffee. + +"And you do talk such stuff," Sid said. "Last night you said, 'It's +blood, it's blood, that's what it is!' You said that over and over. And +you said, 'Don't torment me so--I'll tell!' Tell WHAT? What is it +you'll tell?" + +Everything was swimming before Tom. There is no telling what might +have happened, now, but luckily the concern passed out of Aunt Polly's +face and she came to Tom's relief without knowing it. She said: + +"Sho! It's that dreadful murder. I dream about it most every night +myself. Sometimes I dream it's me that done it." + +Mary said she had been affected much the same way. Sid seemed +satisfied. Tom got out of the presence as quick as he plausibly could, +and after that he complained of toothache for a week, and tied up his +jaws every night. He never knew that Sid lay nightly watching, and +frequently slipped the bandage free and then leaned on his elbow +listening a good while at a time, and afterward slipped the bandage +back to its place again. Tom's distress of mind wore off gradually and +the toothache grew irksome and was discarded. If Sid really managed to +make anything out of Tom's disjointed mutterings, he kept it to himself. + +It seemed to Tom that his schoolmates never would get done holding +inquests on dead cats, and thus keeping his trouble present to his +mind. Sid noticed that Tom never was coroner at one of these inquiries, +though it had been his habit to take the lead in all new enterprises; +he noticed, too, that Tom never acted as a witness--and that was +strange; and Sid did not overlook the fact that Tom even showed a +marked aversion to these inquests, and always avoided them when he +could. Sid marvelled, but said nothing. However, even inquests went out +of vogue at last, and ceased to torture Tom's conscience. + +Every day or two, during this time of sorrow, Tom watched his +opportunity and went to the little grated jail-window and smuggled such +small comforts through to the "murderer" as he could get hold of. The +jail was a trifling little brick den that stood in a marsh at the edge +of the village, and no guards were afforded for it; indeed, it was +seldom occupied. These offerings greatly helped to ease Tom's +conscience. + +The villagers had a strong desire to tar-and-feather Injun Joe and +ride him on a rail, for body-snatching, but so formidable was his +character that nobody could be found who was willing to take the lead +in the matter, so it was dropped. He had been careful to begin both of +his inquest-statements with the fight, without confessing the +grave-robbery that preceded it; therefore it was deemed wisest not +to try the case in the courts at present. + + + +CHAPTER XII + +ONE of the reasons why Tom's mind had drifted away from its secret +troubles was, that it had found a new and weighty matter to interest +itself about. Becky Thatcher had stopped coming to school. Tom had +struggled with his pride a few days, and tried to "whistle her down the +wind," but failed. He began to find himself hanging around her father's +house, nights, and feeling very miserable. She was ill. What if she +should die! There was distraction in the thought. He no longer took an +interest in war, nor even in piracy. The charm of life was gone; there +was nothing but dreariness left. He put his hoop away, and his bat; +there was no joy in them any more. His aunt was concerned. She began to +try all manner of remedies on him. She was one of those people who are +infatuated with patent medicines and all new-fangled methods of +producing health or mending it. She was an inveterate experimenter in +these things. When something fresh in this line came out she was in a +fever, right away, to try it; not on herself, for she was never ailing, +but on anybody else that came handy. She was a subscriber for all the +"Health" periodicals and phrenological frauds; and the solemn ignorance +they were inflated with was breath to her nostrils. All the "rot" they +contained about ventilation, and how to go to bed, and how to get up, +and what to eat, and what to drink, and how much exercise to take, and +what frame of mind to keep one's self in, and what sort of clothing to +wear, was all gospel to her, and she never observed that her +health-journals of the current month customarily upset everything they +had recommended the month before. She was as simple-hearted and honest +as the day was long, and so she was an easy victim. She gathered +together her quack periodicals and her quack medicines, and thus armed +with death, went about on her pale horse, metaphorically speaking, with +"hell following after." But she never suspected that she was not an +angel of healing and the balm of Gilead in disguise, to the suffering +neighbors. + +The water treatment was new, now, and Tom's low condition was a +windfall to her. She had him out at daylight every morning, stood him +up in the woodshed and drowned him with a deluge of cold water; then +she scrubbed him down with a towel like a file, and so brought him to; +then she rolled him up in a wet sheet and put him away under blankets +till she sweated his soul clean and "the yellow stains of it came +through his pores"--as Tom said. + +Yet notwithstanding all this, the boy grew more and more melancholy +and pale and dejected. She added hot baths, sitz baths, shower baths, +and plunges. The boy remained as dismal as a hearse. She began to +assist the water with a slim oatmeal diet and blister-plasters. She +calculated his capacity as she would a jug's, and filled him up every +day with quack cure-alls. + +Tom had become indifferent to persecution by this time. This phase +filled the old lady's heart with consternation. This indifference must +be broken up at any cost. Now she heard of Pain-killer for the first +time. She ordered a lot at once. She tasted it and was filled with +gratitude. It was simply fire in a liquid form. She dropped the water +treatment and everything else, and pinned her faith to Pain-killer. She +gave Tom a teaspoonful and watched with the deepest anxiety for the +result. Her troubles were instantly at rest, her soul at peace again; +for the "indifference" was broken up. The boy could not have shown a +wilder, heartier interest, if she had built a fire under him. + +Tom felt that it was time to wake up; this sort of life might be +romantic enough, in his blighted condition, but it was getting to have +too little sentiment and too much distracting variety about it. So he +thought over various plans for relief, and finally hit pon that of +professing to be fond of Pain-killer. He asked for it so often that he +became a nuisance, and his aunt ended by telling him to help himself +and quit bothering her. If it had been Sid, she would have had no +misgivings to alloy her delight; but since it was Tom, she watched the +bottle clandestinely. She found that the medicine did really diminish, +but it did not occur to her that the boy was mending the health of a +crack in the sitting-room floor with it. + +One day Tom was in the act of dosing the crack when his aunt's yellow +cat came along, purring, eying the teaspoon avariciously, and begging +for a taste. Tom said: + +"Don't ask for it unless you want it, Peter." + +But Peter signified that he did want it. + +"You better make sure." + +Peter was sure. + +"Now you've asked for it, and I'll give it to you, because there ain't +anything mean about me; but if you find you don't like it, you mustn't +blame anybody but your own self." + +Peter was agreeable. So Tom pried his mouth open and poured down the +Pain-killer. Peter sprang a couple of yards in the air, and then +delivered a war-whoop and set off round and round the room, banging +against furniture, upsetting flower-pots, and making general havoc. +Next he rose on his hind feet and pranced around, in a frenzy of +enjoyment, with his head over his shoulder and his voice proclaiming +his unappeasable happiness. Then he went tearing around the house again +spreading chaos and destruction in his path. Aunt Polly entered in time +to see him throw a few double summersets, deliver a final mighty +hurrah, and sail through the open window, carrying the rest of the +flower-pots with him. The old lady stood petrified with astonishment, +peering over her glasses; Tom lay on the floor expiring with laughter. + +"Tom, what on earth ails that cat?" + +"I don't know, aunt," gasped the boy. + +"Why, I never see anything like it. What did make him act so?" + +"Deed I don't know, Aunt Polly; cats always act so when they're having +a good time." + +"They do, do they?" There was something in the tone that made Tom +apprehensive. + +"Yes'm. That is, I believe they do." + +"You DO?" + +"Yes'm." + +The old lady was bending down, Tom watching, with interest emphasized +by anxiety. Too late he divined her "drift." The handle of the telltale +teaspoon was visible under the bed-valance. Aunt Polly took it, held it +up. Tom winced, and dropped his eyes. Aunt Polly raised him by the +usual handle--his ear--and cracked his head soundly with her thimble. + +"Now, sir, what did you want to treat that poor dumb beast so, for?" + +"I done it out of pity for him--because he hadn't any aunt." + +"Hadn't any aunt!--you numskull. What has that got to do with it?" + +"Heaps. Because if he'd had one she'd a burnt him out herself! She'd a +roasted his bowels out of him 'thout any more feeling than if he was a +human!" + +Aunt Polly felt a sudden pang of remorse. This was putting the thing +in a new light; what was cruelty to a cat MIGHT be cruelty to a boy, +too. She began to soften; she felt sorry. Her eyes watered a little, +and she put her hand on Tom's head and said gently: + +"I was meaning for the best, Tom. And, Tom, it DID do you good." + +Tom looked up in her face with just a perceptible twinkle peeping +through his gravity. + +"I know you was meaning for the best, aunty, and so was I with Peter. +It done HIM good, too. I never see him get around so since--" + +"Oh, go 'long with you, Tom, before you aggravate me again. And you +try and see if you can't be a good boy, for once, and you needn't take +any more medicine." + +Tom reached school ahead of time. It was noticed that this strange +thing had been occurring every day latterly. And now, as usual of late, +he hung about the gate of the schoolyard instead of playing with his +comrades. He was sick, he said, and he looked it. He tried to seem to +be looking everywhere but whither he really was looking--down the road. +Presently Jeff Thatcher hove in sight, and Tom's face lighted; he gazed +a moment, and then turned sorrowfully away. When Jeff arrived, Tom +accosted him; and "led up" warily to opportunities for remark about +Becky, but the giddy lad never could see the bait. Tom watched and +watched, hoping whenever a frisking frock came in sight, and hating the +owner of it as soon as he saw she was not the right one. At last frocks +ceased to appear, and he dropped hopelessly into the dumps; he entered +the empty schoolhouse and sat down to suffer. Then one more frock +passed in at the gate, and Tom's heart gave a great bound. The next +instant he was out, and "going on" like an Indian; yelling, laughing, +chasing boys, jumping over the fence at risk of life and limb, throwing +handsprings, standing on his head--doing all the heroic things he could +conceive of, and keeping a furtive eye out, all the while, to see if +Becky Thatcher was noticing. But she seemed to be unconscious of it +all; she never looked. Could it be possible that she was not aware that +he was there? He carried his exploits to her immediate vicinity; came +war-whooping around, snatched a boy's cap, hurled it to the roof of the +schoolhouse, broke through a group of boys, tumbling them in every +direction, and fell sprawling, himself, under Becky's nose, almost +upsetting her--and she turned, with her nose in the air, and he heard +her say: "Mf! some people think they're mighty smart--always showing +off!" + +Tom's cheeks burned. He gathered himself up and sneaked off, crushed +and crestfallen. + + + +CHAPTER XIII + +TOM'S mind was made up now. He was gloomy and desperate. He was a +forsaken, friendless boy, he said; nobody loved him; when they found +out what they had driven him to, perhaps they would be sorry; he had +tried to do right and get along, but they would not let him; since +nothing would do them but to be rid of him, let it be so; and let them +blame HIM for the consequences--why shouldn't they? What right had the +friendless to complain? Yes, they had forced him to it at last: he +would lead a life of crime. There was no choice. + +By this time he was far down Meadow Lane, and the bell for school to +"take up" tinkled faintly upon his ear. He sobbed, now, to think he +should never, never hear that old familiar sound any more--it was very +hard, but it was forced on him; since he was driven out into the cold +world, he must submit--but he forgave them. Then the sobs came thick +and fast. + +Just at this point he met his soul's sworn comrade, Joe Harper +--hard-eyed, and with evidently a great and dismal purpose in his heart. +Plainly here were "two souls with but a single thought." Tom, wiping +his eyes with his sleeve, began to blubber out something about a +resolution to escape from hard usage and lack of sympathy at home by +roaming abroad into the great world never to return; and ended by +hoping that Joe would not forget him. + +But it transpired that this was a request which Joe had just been +going to make of Tom, and had come to hunt him up for that purpose. His +mother had whipped him for drinking some cream which he had never +tasted and knew nothing about; it was plain that she was tired of him +and wished him to go; if she felt that way, there was nothing for him +to do but succumb; he hoped she would be happy, and never regret having +driven her poor boy out into the unfeeling world to suffer and die. + +As the two boys walked sorrowing along, they made a new compact to +stand by each other and be brothers and never separate till death +relieved them of their troubles. Then they began to lay their plans. +Joe was for being a hermit, and living on crusts in a remote cave, and +dying, some time, of cold and want and grief; but after listening to +Tom, he conceded that there were some conspicuous advantages about a +life of crime, and so he consented to be a pirate. + +Three miles below St. Petersburg, at a point where the Mississippi +River was a trifle over a mile wide, there was a long, narrow, wooded +island, with a shallow bar at the head of it, and this offered well as +a rendezvous. It was not inhabited; it lay far over toward the further +shore, abreast a dense and almost wholly unpeopled forest. So Jackson's +Island was chosen. Who were to be the subjects of their piracies was a +matter that did not occur to them. Then they hunted up Huckleberry +Finn, and he joined them promptly, for all careers were one to him; he +was indifferent. They presently separated to meet at a lonely spot on +the river-bank two miles above the village at the favorite hour--which +was midnight. There was a small log raft there which they meant to +capture. Each would bring hooks and lines, and such provision as he +could steal in the most dark and mysterious way--as became outlaws. And +before the afternoon was done, they had all managed to enjoy the sweet +glory of spreading the fact that pretty soon the town would "hear +something." All who got this vague hint were cautioned to "be mum and +wait." + +About midnight Tom arrived with a boiled ham and a few trifles, +and stopped in a dense undergrowth on a small bluff overlooking the +meeting-place. It was starlight, and very still. The mighty river lay +like an ocean at rest. Tom listened a moment, but no sound disturbed the +quiet. Then he gave a low, distinct whistle. It was answered from under +the bluff. Tom whistled twice more; these signals were answered in the +same way. Then a guarded voice said: + +"Who goes there?" + +"Tom Sawyer, the Black Avenger of the Spanish Main. Name your names." + +"Huck Finn the Red-Handed, and Joe Harper the Terror of the Seas." Tom +had furnished these titles, from his favorite literature. + +"'Tis well. Give the countersign." + +Two hoarse whispers delivered the same awful word simultaneously to +the brooding night: + +"BLOOD!" + +Then Tom tumbled his ham over the bluff and let himself down after it, +tearing both skin and clothes to some extent in the effort. There was +an easy, comfortable path along the shore under the bluff, but it +lacked the advantages of difficulty and danger so valued by a pirate. + +The Terror of the Seas had brought a side of bacon, and had about worn +himself out with getting it there. Finn the Red-Handed had stolen a +skillet and a quantity of half-cured leaf tobacco, and had also brought +a few corn-cobs to make pipes with. But none of the pirates smoked or +"chewed" but himself. The Black Avenger of the Spanish Main said it +would never do to start without some fire. That was a wise thought; +matches were hardly known there in that day. They saw a fire +smouldering upon a great raft a hundred yards above, and they went +stealthily thither and helped themselves to a chunk. They made an +imposing adventure of it, saying, "Hist!" every now and then, and +suddenly halting with finger on lip; moving with hands on imaginary +dagger-hilts; and giving orders in dismal whispers that if "the foe" +stirred, to "let him have it to the hilt," because "dead men tell no +tales." They knew well enough that the raftsmen were all down at the +village laying in stores or having a spree, but still that was no +excuse for their conducting this thing in an unpiratical way. + +They shoved off, presently, Tom in command, Huck at the after oar and +Joe at the forward. Tom stood amidships, gloomy-browed, and with folded +arms, and gave his orders in a low, stern whisper: + +"Luff, and bring her to the wind!" + +"Aye-aye, sir!" + +"Steady, steady-y-y-y!" + +"Steady it is, sir!" + +"Let her go off a point!" + +"Point it is, sir!" + +As the boys steadily and monotonously drove the raft toward mid-stream +it was no doubt understood that these orders were given only for +"style," and were not intended to mean anything in particular. + +"What sail's she carrying?" + +"Courses, tops'ls, and flying-jib, sir." + +"Send the r'yals up! Lay out aloft, there, half a dozen of ye +--foretopmaststuns'l! Lively, now!" + +"Aye-aye, sir!" + +"Shake out that maintogalans'l! Sheets and braces! NOW my hearties!" + +"Aye-aye, sir!" + +"Hellum-a-lee--hard a port! Stand by to meet her when she comes! Port, +port! NOW, men! With a will! Stead-y-y-y!" + +"Steady it is, sir!" + +The raft drew beyond the middle of the river; the boys pointed her +head right, and then lay on their oars. The river was not high, so +there was not more than a two or three mile current. Hardly a word was +said during the next three-quarters of an hour. Now the raft was +passing before the distant town. Two or three glimmering lights showed +where it lay, peacefully sleeping, beyond the vague vast sweep of +star-gemmed water, unconscious of the tremendous event that was happening. +The Black Avenger stood still with folded arms, "looking his last" upon +the scene of his former joys and his later sufferings, and wishing +"she" could see him now, abroad on the wild sea, facing peril and death +with dauntless heart, going to his doom with a grim smile on his lips. +It was but a small strain on his imagination to remove Jackson's Island +beyond eyeshot of the village, and so he "looked his last" with a +broken and satisfied heart. The other pirates were looking their last, +too; and they all looked so long that they came near letting the +current drift them out of the range of the island. But they discovered +the danger in time, and made shift to avert it. About two o'clock in +the morning the raft grounded on the bar two hundred yards above the +head of the island, and they waded back and forth until they had landed +their freight. Part of the little raft's belongings consisted of an old +sail, and this they spread over a nook in the bushes for a tent to +shelter their provisions; but they themselves would sleep in the open +air in good weather, as became outlaws. + +They built a fire against the side of a great log twenty or thirty +steps within the sombre depths of the forest, and then cooked some +bacon in the frying-pan for supper, and used up half of the corn "pone" +stock they had brought. It seemed glorious sport to be feasting in that +wild, free way in the virgin forest of an unexplored and uninhabited +island, far from the haunts of men, and they said they never would +return to civilization. The climbing fire lit up their faces and threw +its ruddy glare upon the pillared tree-trunks of their forest temple, +and upon the varnished foliage and festooning vines. + +When the last crisp slice of bacon was gone, and the last allowance of +corn pone devoured, the boys stretched themselves out on the grass, +filled with contentment. They could have found a cooler place, but they +would not deny themselves such a romantic feature as the roasting +camp-fire. + +"AIN'T it gay?" said Joe. + +"It's NUTS!" said Tom. "What would the boys say if they could see us?" + +"Say? Well, they'd just die to be here--hey, Hucky!" + +"I reckon so," said Huckleberry; "anyways, I'm suited. I don't want +nothing better'n this. I don't ever get enough to eat, gen'ally--and +here they can't come and pick at a feller and bullyrag him so." + +"It's just the life for me," said Tom. "You don't have to get up, +mornings, and you don't have to go to school, and wash, and all that +blame foolishness. You see a pirate don't have to do ANYTHING, Joe, +when he's ashore, but a hermit HE has to be praying considerable, and +then he don't have any fun, anyway, all by himself that way." + +"Oh yes, that's so," said Joe, "but I hadn't thought much about it, +you know. I'd a good deal rather be a pirate, now that I've tried it." + +"You see," said Tom, "people don't go much on hermits, nowadays, like +they used to in old times, but a pirate's always respected. And a +hermit's got to sleep on the hardest place he can find, and put +sackcloth and ashes on his head, and stand out in the rain, and--" + +"What does he put sackcloth and ashes on his head for?" inquired Huck. + +"I dono. But they've GOT to do it. Hermits always do. You'd have to do +that if you was a hermit." + +"Dern'd if I would," said Huck. + +"Well, what would you do?" + +"I dono. But I wouldn't do that." + +"Why, Huck, you'd HAVE to. How'd you get around it?" + +"Why, I just wouldn't stand it. I'd run away." + +"Run away! Well, you WOULD be a nice old slouch of a hermit. You'd be +a disgrace." + +The Red-Handed made no response, being better employed. He had +finished gouging out a cob, and now he fitted a weed stem to it, loaded +it with tobacco, and was pressing a coal to the charge and blowing a +cloud of fragrant smoke--he was in the full bloom of luxurious +contentment. The other pirates envied him this majestic vice, and +secretly resolved to acquire it shortly. Presently Huck said: + +"What does pirates have to do?" + +Tom said: + +"Oh, they have just a bully time--take ships and burn them, and get +the money and bury it in awful places in their island where there's +ghosts and things to watch it, and kill everybody in the ships--make +'em walk a plank." + +"And they carry the women to the island," said Joe; "they don't kill +the women." + +"No," assented Tom, "they don't kill the women--they're too noble. And +the women's always beautiful, too. + +"And don't they wear the bulliest clothes! Oh no! All gold and silver +and di'monds," said Joe, with enthusiasm. + +"Who?" said Huck. + +"Why, the pirates." + +Huck scanned his own clothing forlornly. + +"I reckon I ain't dressed fitten for a pirate," said he, with a +regretful pathos in his voice; "but I ain't got none but these." + +But the other boys told him the fine clothes would come fast enough, +after they should have begun their adventures. They made him understand +that his poor rags would do to begin with, though it was customary for +wealthy pirates to start with a proper wardrobe. + +Gradually their talk died out and drowsiness began to steal upon the +eyelids of the little waifs. The pipe dropped from the fingers of the +Red-Handed, and he slept the sleep of the conscience-free and the +weary. The Terror of the Seas and the Black Avenger of the Spanish Main +had more difficulty in getting to sleep. They said their prayers +inwardly, and lying down, since there was nobody there with authority +to make them kneel and recite aloud; in truth, they had a mind not to +say them at all, but they were afraid to proceed to such lengths as +that, lest they might call down a sudden and special thunderbolt from +heaven. Then at once they reached and hovered upon the imminent verge +of sleep--but an intruder came, now, that would not "down." It was +conscience. They began to feel a vague fear that they had been doing +wrong to run away; and next they thought of the stolen meat, and then +the real torture came. They tried to argue it away by reminding +conscience that they had purloined sweetmeats and apples scores of +times; but conscience was not to be appeased by such thin +plausibilities; it seemed to them, in the end, that there was no +getting around the stubborn fact that taking sweetmeats was only +"hooking," while taking bacon and hams and such valuables was plain +simple stealing--and there was a command against that in the Bible. So +they inwardly resolved that so long as they remained in the business, +their piracies should not again be sullied with the crime of stealing. +Then conscience granted a truce, and these curiously inconsistent +pirates fell peacefully to sleep. + + + +CHAPTER XIV + +WHEN Tom awoke in the morning, he wondered where he was. He sat up and +rubbed his eyes and looked around. Then he comprehended. It was the +cool gray dawn, and there was a delicious sense of repose and peace in +the deep pervading calm and silence of the woods. Not a leaf stirred; +not a sound obtruded upon great Nature's meditation. Beaded dewdrops +stood upon the leaves and grasses. A white layer of ashes covered the +fire, and a thin blue breath of smoke rose straight into the air. Joe +and Huck still slept. + +Now, far away in the woods a bird called; another answered; presently +the hammering of a woodpecker was heard. Gradually the cool dim gray of +the morning whitened, and as gradually sounds multiplied and life +manifested itself. The marvel of Nature shaking off sleep and going to +work unfolded itself to the musing boy. A little green worm came +crawling over a dewy leaf, lifting two-thirds of his body into the air +from time to time and "sniffing around," then proceeding again--for he +was measuring, Tom said; and when the worm approached him, of its own +accord, he sat as still as a stone, with his hopes rising and falling, +by turns, as the creature still came toward him or seemed inclined to +go elsewhere; and when at last it considered a painful moment with its +curved body in the air and then came decisively down upon Tom's leg and +began a journey over him, his whole heart was glad--for that meant that +he was going to have a new suit of clothes--without the shadow of a +doubt a gaudy piratical uniform. Now a procession of ants appeared, +from nowhere in particular, and went about their labors; one struggled +manfully by with a dead spider five times as big as itself in its arms, +and lugged it straight up a tree-trunk. A brown spotted lady-bug +climbed the dizzy height of a grass blade, and Tom bent down close to +it and said, "Lady-bug, lady-bug, fly away home, your house is on fire, +your children's alone," and she took wing and went off to see about it +--which did not surprise the boy, for he knew of old that this insect was +credulous about conflagrations, and he had practised upon its +simplicity more than once. A tumblebug came next, heaving sturdily at +its ball, and Tom touched the creature, to see it shut its legs against +its body and pretend to be dead. The birds were fairly rioting by this +time. A catbird, the Northern mocker, lit in a tree over Tom's head, +and trilled out her imitations of her neighbors in a rapture of +enjoyment; then a shrill jay swept down, a flash of blue flame, and +stopped on a twig almost within the boy's reach, cocked his head to one +side and eyed the strangers with a consuming curiosity; a gray squirrel +and a big fellow of the "fox" kind came skurrying along, sitting up at +intervals to inspect and chatter at the boys, for the wild things had +probably never seen a human being before and scarcely knew whether to +be afraid or not. All Nature was wide awake and stirring, now; long +lances of sunlight pierced down through the dense foliage far and near, +and a few butterflies came fluttering upon the scene. + +Tom stirred up the other pirates and they all clattered away with a +shout, and in a minute or two were stripped and chasing after and +tumbling over each other in the shallow limpid water of the white +sandbar. They felt no longing for the little village sleeping in the +distance beyond the majestic waste of water. A vagrant current or a +slight rise in the river had carried off their raft, but this only +gratified them, since its going was something like burning the bridge +between them and civilization. + +They came back to camp wonderfully refreshed, glad-hearted, and +ravenous; and they soon had the camp-fire blazing up again. Huck found +a spring of clear cold water close by, and the boys made cups of broad +oak or hickory leaves, and felt that water, sweetened with such a +wildwood charm as that, would be a good enough substitute for coffee. +While Joe was slicing bacon for breakfast, Tom and Huck asked him to +hold on a minute; they stepped to a promising nook in the river-bank +and threw in their lines; almost immediately they had reward. Joe had +not had time to get impatient before they were back again with some +handsome bass, a couple of sun-perch and a small catfish--provisions +enough for quite a family. They fried the fish with the bacon, and were +astonished; for no fish had ever seemed so delicious before. They did +not know that the quicker a fresh-water fish is on the fire after he is +caught the better he is; and they reflected little upon what a sauce +open-air sleeping, open-air exercise, bathing, and a large ingredient +of hunger make, too. + +They lay around in the shade, after breakfast, while Huck had a smoke, +and then went off through the woods on an exploring expedition. They +tramped gayly along, over decaying logs, through tangled underbrush, +among solemn monarchs of the forest, hung from their crowns to the +ground with a drooping regalia of grape-vines. Now and then they came +upon snug nooks carpeted with grass and jeweled with flowers. + +They found plenty of things to be delighted with, but nothing to be +astonished at. They discovered that the island was about three miles +long and a quarter of a mile wide, and that the shore it lay closest to +was only separated from it by a narrow channel hardly two hundred yards +wide. They took a swim about every hour, so it was close upon the +middle of the afternoon when they got back to camp. They were too +hungry to stop to fish, but they fared sumptuously upon cold ham, and +then threw themselves down in the shade to talk. But the talk soon +began to drag, and then died. The stillness, the solemnity that brooded +in the woods, and the sense of loneliness, began to tell upon the +spirits of the boys. They fell to thinking. A sort of undefined longing +crept upon them. This took dim shape, presently--it was budding +homesickness. Even Finn the Red-Handed was dreaming of his doorsteps +and empty hogsheads. But they were all ashamed of their weakness, and +none was brave enough to speak his thought. + +For some time, now, the boys had been dully conscious of a peculiar +sound in the distance, just as one sometimes is of the ticking of a +clock which he takes no distinct note of. But now this mysterious sound +became more pronounced, and forced a recognition. The boys started, +glanced at each other, and then each assumed a listening attitude. +There was a long silence, profound and unbroken; then a deep, sullen +boom came floating down out of the distance. + +"What is it!" exclaimed Joe, under his breath. + +"I wonder," said Tom in a whisper. + +"'Tain't thunder," said Huckleberry, in an awed tone, "becuz thunder--" + +"Hark!" said Tom. "Listen--don't talk." + +They waited a time that seemed an age, and then the same muffled boom +troubled the solemn hush. + +"Let's go and see." + +They sprang to their feet and hurried to the shore toward the town. +They parted the bushes on the bank and peered out over the water. The +little steam ferryboat was about a mile below the village, drifting +with the current. Her broad deck seemed crowded with people. There were +a great many skiffs rowing about or floating with the stream in the +neighborhood of the ferryboat, but the boys could not determine what +the men in them were doing. Presently a great jet of white smoke burst +from the ferryboat's side, and as it expanded and rose in a lazy cloud, +that same dull throb of sound was borne to the listeners again. + +"I know now!" exclaimed Tom; "somebody's drownded!" + +"That's it!" said Huck; "they done that last summer, when Bill Turner +got drownded; they shoot a cannon over the water, and that makes him +come up to the top. Yes, and they take loaves of bread and put +quicksilver in 'em and set 'em afloat, and wherever there's anybody +that's drownded, they'll float right there and stop." + +"Yes, I've heard about that," said Joe. "I wonder what makes the bread +do that." + +"Oh, it ain't the bread, so much," said Tom; "I reckon it's mostly +what they SAY over it before they start it out." + +"But they don't say anything over it," said Huck. "I've seen 'em and +they don't." + +"Well, that's funny," said Tom. "But maybe they say it to themselves. +Of COURSE they do. Anybody might know that." + +The other boys agreed that there was reason in what Tom said, because +an ignorant lump of bread, uninstructed by an incantation, could not be +expected to act very intelligently when set upon an errand of such +gravity. + +"By jings, I wish I was over there, now," said Joe. + +"I do too" said Huck "I'd give heaps to know who it is." + +The boys still listened and watched. Presently a revealing thought +flashed through Tom's mind, and he exclaimed: + +"Boys, I know who's drownded--it's us!" + +They felt like heroes in an instant. Here was a gorgeous triumph; they +were missed; they were mourned; hearts were breaking on their account; +tears were being shed; accusing memories of unkindness to these poor +lost lads were rising up, and unavailing regrets and remorse were being +indulged; and best of all, the departed were the talk of the whole +town, and the envy of all the boys, as far as this dazzling notoriety +was concerned. This was fine. It was worth while to be a pirate, after +all. + +As twilight drew on, the ferryboat went back to her accustomed +business and the skiffs disappeared. The pirates returned to camp. They +were jubilant with vanity over their new grandeur and the illustrious +trouble they were making. They caught fish, cooked supper and ate it, +and then fell to guessing at what the village was thinking and saying +about them; and the pictures they drew of the public distress on their +account were gratifying to look upon--from their point of view. But +when the shadows of night closed them in, they gradually ceased to +talk, and sat gazing into the fire, with their minds evidently +wandering elsewhere. The excitement was gone, now, and Tom and Joe +could not keep back thoughts of certain persons at home who were not +enjoying this fine frolic as much as they were. Misgivings came; they +grew troubled and unhappy; a sigh or two escaped, unawares. By and by +Joe timidly ventured upon a roundabout "feeler" as to how the others +might look upon a return to civilization--not right now, but-- + +Tom withered him with derision! Huck, being uncommitted as yet, joined +in with Tom, and the waverer quickly "explained," and was glad to get +out of the scrape with as little taint of chicken-hearted homesickness +clinging to his garments as he could. Mutiny was effectually laid to +rest for the moment. + +As the night deepened, Huck began to nod, and presently to snore. Joe +followed next. Tom lay upon his elbow motionless, for some time, +watching the two intently. At last he got up cautiously, on his knees, +and went searching among the grass and the flickering reflections flung +by the camp-fire. He picked up and inspected several large +semi-cylinders of the thin white bark of a sycamore, and finally chose +two which seemed to suit him. Then he knelt by the fire and painfully +wrote something upon each of these with his "red keel"; one he rolled up +and put in his jacket pocket, and the other he put in Joe's hat and +removed it to a little distance from the owner. And he also put into the +hat certain schoolboy treasures of almost inestimable value--among them +a lump of chalk, an India-rubber ball, three fishhooks, and one of that +kind of marbles known as a "sure 'nough crystal." Then he tiptoed his +way cautiously among the trees till he felt that he was out of hearing, +and straightway broke into a keen run in the direction of the sandbar. + + + +CHAPTER XV + +A FEW minutes later Tom was in the shoal water of the bar, wading +toward the Illinois shore. Before the depth reached his middle he was +half-way over; the current would permit no more wading, now, so he +struck out confidently to swim the remaining hundred yards. He swam +quartering upstream, but still was swept downward rather faster than he +had expected. However, he reached the shore finally, and drifted along +till he found a low place and drew himself out. He put his hand on his +jacket pocket, found his piece of bark safe, and then struck through +the woods, following the shore, with streaming garments. Shortly before +ten o'clock he came out into an open place opposite the village, and +saw the ferryboat lying in the shadow of the trees and the high bank. +Everything was quiet under the blinking stars. He crept down the bank, +watching with all his eyes, slipped into the water, swam three or four +strokes and climbed into the skiff that did "yawl" duty at the boat's +stern. He laid himself down under the thwarts and waited, panting. + +Presently the cracked bell tapped and a voice gave the order to "cast +off." A minute or two later the skiff's head was standing high up, +against the boat's swell, and the voyage was begun. Tom felt happy in +his success, for he knew it was the boat's last trip for the night. At +the end of a long twelve or fifteen minutes the wheels stopped, and Tom +slipped overboard and swam ashore in the dusk, landing fifty yards +downstream, out of danger of possible stragglers. + +He flew along unfrequented alleys, and shortly found himself at his +aunt's back fence. He climbed over, approached the "ell," and looked in +at the sitting-room window, for a light was burning there. There sat +Aunt Polly, Sid, Mary, and Joe Harper's mother, grouped together, +talking. They were by the bed, and the bed was between them and the +door. Tom went to the door and began to softly lift the latch; then he +pressed gently and the door yielded a crack; he continued pushing +cautiously, and quaking every time it creaked, till he judged he might +squeeze through on his knees; so he put his head through and began, +warily. + +"What makes the candle blow so?" said Aunt Polly. Tom hurried up. +"Why, that door's open, I believe. Why, of course it is. No end of +strange things now. Go 'long and shut it, Sid." + +Tom disappeared under the bed just in time. He lay and "breathed" +himself for a time, and then crept to where he could almost touch his +aunt's foot. + +"But as I was saying," said Aunt Polly, "he warn't BAD, so to say +--only mischEEvous. Only just giddy, and harum-scarum, you know. He +warn't any more responsible than a colt. HE never meant any harm, and +he was the best-hearted boy that ever was"--and she began to cry. + +"It was just so with my Joe--always full of his devilment, and up to +every kind of mischief, but he was just as unselfish and kind as he +could be--and laws bless me, to think I went and whipped him for taking +that cream, never once recollecting that I throwed it out myself +because it was sour, and I never to see him again in this world, never, +never, never, poor abused boy!" And Mrs. Harper sobbed as if her heart +would break. + +"I hope Tom's better off where he is," said Sid, "but if he'd been +better in some ways--" + +"SID!" Tom felt the glare of the old lady's eye, though he could not +see it. "Not a word against my Tom, now that he's gone! God'll take +care of HIM--never you trouble YOURself, sir! Oh, Mrs. Harper, I don't +know how to give him up! I don't know how to give him up! He was such a +comfort to me, although he tormented my old heart out of me, 'most." + +"The Lord giveth and the Lord hath taken away--Blessed be the name of +the Lord! But it's so hard--Oh, it's so hard! Only last Saturday my +Joe busted a firecracker right under my nose and I knocked him +sprawling. Little did I know then, how soon--Oh, if it was to do over +again I'd hug him and bless him for it." + +"Yes, yes, yes, I know just how you feel, Mrs. Harper, I know just +exactly how you feel. No longer ago than yesterday noon, my Tom took +and filled the cat full of Pain-killer, and I did think the cretur +would tear the house down. And God forgive me, I cracked Tom's head +with my thimble, poor boy, poor dead boy. But he's out of all his +troubles now. And the last words I ever heard him say was to reproach--" + +But this memory was too much for the old lady, and she broke entirely +down. Tom was snuffling, now, himself--and more in pity of himself than +anybody else. He could hear Mary crying, and putting in a kindly word +for him from time to time. He began to have a nobler opinion of himself +than ever before. Still, he was sufficiently touched by his aunt's +grief to long to rush out from under the bed and overwhelm her with +joy--and the theatrical gorgeousness of the thing appealed strongly to +his nature, too, but he resisted and lay still. + +He went on listening, and gathered by odds and ends that it was +conjectured at first that the boys had got drowned while taking a swim; +then the small raft had been missed; next, certain boys said the +missing lads had promised that the village should "hear something" +soon; the wise-heads had "put this and that together" and decided that +the lads had gone off on that raft and would turn up at the next town +below, presently; but toward noon the raft had been found, lodged +against the Missouri shore some five or six miles below the village +--and then hope perished; they must be drowned, else hunger would have +driven them home by nightfall if not sooner. It was believed that the +search for the bodies had been a fruitless effort merely because the +drowning must have occurred in mid-channel, since the boys, being good +swimmers, would otherwise have escaped to shore. This was Wednesday +night. If the bodies continued missing until Sunday, all hope would be +given over, and the funerals would be preached on that morning. Tom +shuddered. + +Mrs. Harper gave a sobbing good-night and turned to go. Then with a +mutual impulse the two bereaved women flung themselves into each +other's arms and had a good, consoling cry, and then parted. Aunt Polly +was tender far beyond her wont, in her good-night to Sid and Mary. Sid +snuffled a bit and Mary went off crying with all her heart. + +Aunt Polly knelt down and prayed for Tom so touchingly, so +appealingly, and with such measureless love in her words and her old +trembling voice, that he was weltering in tears again, long before she +was through. + +He had to keep still long after she went to bed, for she kept making +broken-hearted ejaculations from time to time, tossing unrestfully, and +turning over. But at last she was still, only moaning a little in her +sleep. Now the boy stole out, rose gradually by the bedside, shaded the +candle-light with his hand, and stood regarding her. His heart was full +of pity for her. He took out his sycamore scroll and placed it by the +candle. But something occurred to him, and he lingered considering. His +face lighted with a happy solution of his thought; he put the bark +hastily in his pocket. Then he bent over and kissed the faded lips, and +straightway made his stealthy exit, latching the door behind him. + +He threaded his way back to the ferry landing, found nobody at large +there, and walked boldly on board the boat, for he knew she was +tenantless except that there was a watchman, who always turned in and +slept like a graven image. He untied the skiff at the stern, slipped +into it, and was soon rowing cautiously upstream. When he had pulled a +mile above the village, he started quartering across and bent himself +stoutly to his work. He hit the landing on the other side neatly, for +this was a familiar bit of work to him. He was moved to capture the +skiff, arguing that it might be considered a ship and therefore +legitimate prey for a pirate, but he knew a thorough search would be +made for it and that might end in revelations. So he stepped ashore and +entered the woods. + +He sat down and took a long rest, torturing himself meanwhile to keep +awake, and then started warily down the home-stretch. The night was far +spent. It was broad daylight before he found himself fairly abreast the +island bar. He rested again until the sun was well up and gilding the +great river with its splendor, and then he plunged into the stream. A +little later he paused, dripping, upon the threshold of the camp, and +heard Joe say: + +"No, Tom's true-blue, Huck, and he'll come back. He won't desert. He +knows that would be a disgrace to a pirate, and Tom's too proud for +that sort of thing. He's up to something or other. Now I wonder what?" + +"Well, the things is ours, anyway, ain't they?" + +"Pretty near, but not yet, Huck. The writing says they are if he ain't +back here to breakfast." + +"Which he is!" exclaimed Tom, with fine dramatic effect, stepping +grandly into camp. + +A sumptuous breakfast of bacon and fish was shortly provided, and as +the boys set to work upon it, Tom recounted (and adorned) his +adventures. They were a vain and boastful company of heroes when the +tale was done. Then Tom hid himself away in a shady nook to sleep till +noon, and the other pirates got ready to fish and explore. + + + +CHAPTER XVI + +AFTER dinner all the gang turned out to hunt for turtle eggs on the +bar. They went about poking sticks into the sand, and when they found a +soft place they went down on their knees and dug with their hands. +Sometimes they would take fifty or sixty eggs out of one hole. They +were perfectly round white things a trifle smaller than an English +walnut. They had a famous fried-egg feast that night, and another on +Friday morning. + +After breakfast they went whooping and prancing out on the bar, and +chased each other round and round, shedding clothes as they went, until +they were naked, and then continued the frolic far away up the shoal +water of the bar, against the stiff current, which latter tripped their +legs from under them from time to time and greatly increased the fun. +And now and then they stooped in a group and splashed water in each +other's faces with their palms, gradually approaching each other, with +averted faces to avoid the strangling sprays, and finally gripping and +struggling till the best man ducked his neighbor, and then they all +went under in a tangle of white legs and arms and came up blowing, +sputtering, laughing, and gasping for breath at one and the same time. + +When they were well exhausted, they would run out and sprawl on the +dry, hot sand, and lie there and cover themselves up with it, and by +and by break for the water again and go through the original +performance once more. Finally it occurred to them that their naked +skin represented flesh-colored "tights" very fairly; so they drew a +ring in the sand and had a circus--with three clowns in it, for none +would yield this proudest post to his neighbor. + +Next they got their marbles and played "knucks" and "ring-taw" and +"keeps" till that amusement grew stale. Then Joe and Huck had another +swim, but Tom would not venture, because he found that in kicking off +his trousers he had kicked his string of rattlesnake rattles off his +ankle, and he wondered how he had escaped cramp so long without the +protection of this mysterious charm. He did not venture again until he +had found it, and by that time the other boys were tired and ready to +rest. They gradually wandered apart, dropped into the "dumps," and fell +to gazing longingly across the wide river to where the village lay +drowsing in the sun. Tom found himself writing "BECKY" in the sand with +his big toe; he scratched it out, and was angry with himself for his +weakness. But he wrote it again, nevertheless; he could not help it. He +erased it once more and then took himself out of temptation by driving +the other boys together and joining them. + +But Joe's spirits had gone down almost beyond resurrection. He was so +homesick that he could hardly endure the misery of it. The tears lay +very near the surface. Huck was melancholy, too. Tom was downhearted, +but tried hard not to show it. He had a secret which he was not ready +to tell, yet, but if this mutinous depression was not broken up soon, +he would have to bring it out. He said, with a great show of +cheerfulness: + +"I bet there's been pirates on this island before, boys. We'll explore +it again. They've hid treasures here somewhere. How'd you feel to light +on a rotten chest full of gold and silver--hey?" + +But it roused only faint enthusiasm, which faded out, with no reply. +Tom tried one or two other seductions; but they failed, too. It was +discouraging work. Joe sat poking up the sand with a stick and looking +very gloomy. Finally he said: + +"Oh, boys, let's give it up. I want to go home. It's so lonesome." + +"Oh no, Joe, you'll feel better by and by," said Tom. "Just think of +the fishing that's here." + +"I don't care for fishing. I want to go home." + +"But, Joe, there ain't such another swimming-place anywhere." + +"Swimming's no good. I don't seem to care for it, somehow, when there +ain't anybody to say I sha'n't go in. I mean to go home." + +"Oh, shucks! Baby! You want to see your mother, I reckon." + +"Yes, I DO want to see my mother--and you would, too, if you had one. +I ain't any more baby than you are." And Joe snuffled a little. + +"Well, we'll let the cry-baby go home to his mother, won't we, Huck? +Poor thing--does it want to see its mother? And so it shall. You like +it here, don't you, Huck? We'll stay, won't we?" + +Huck said, "Y-e-s"--without any heart in it. + +"I'll never speak to you again as long as I live," said Joe, rising. +"There now!" And he moved moodily away and began to dress himself. + +"Who cares!" said Tom. "Nobody wants you to. Go 'long home and get +laughed at. Oh, you're a nice pirate. Huck and me ain't cry-babies. +We'll stay, won't we, Huck? Let him go if he wants to. I reckon we can +get along without him, per'aps." + +But Tom was uneasy, nevertheless, and was alarmed to see Joe go +sullenly on with his dressing. And then it was discomforting to see +Huck eying Joe's preparations so wistfully, and keeping up such an +ominous silence. Presently, without a parting word, Joe began to wade +off toward the Illinois shore. Tom's heart began to sink. He glanced at +Huck. Huck could not bear the look, and dropped his eyes. Then he said: + +"I want to go, too, Tom. It was getting so lonesome anyway, and now +it'll be worse. Let's us go, too, Tom." + +"I won't! You can all go, if you want to. I mean to stay." + +"Tom, I better go." + +"Well, go 'long--who's hendering you." + +Huck began to pick up his scattered clothes. He said: + +"Tom, I wisht you'd come, too. Now you think it over. We'll wait for +you when we get to shore." + +"Well, you'll wait a blame long time, that's all." + +Huck started sorrowfully away, and Tom stood looking after him, with a +strong desire tugging at his heart to yield his pride and go along too. +He hoped the boys would stop, but they still waded slowly on. It +suddenly dawned on Tom that it was become very lonely and still. He +made one final struggle with his pride, and then darted after his +comrades, yelling: + +"Wait! Wait! I want to tell you something!" + +They presently stopped and turned around. When he got to where they +were, he began unfolding his secret, and they listened moodily till at +last they saw the "point" he was driving at, and then they set up a +war-whoop of applause and said it was "splendid!" and said if he had +told them at first, they wouldn't have started away. He made a plausible +excuse; but his real reason had been the fear that not even the secret +would keep them with him any very great length of time, and so he had +meant to hold it in reserve as a last seduction. + +The lads came gayly back and went at their sports again with a will, +chattering all the time about Tom's stupendous plan and admiring the +genius of it. After a dainty egg and fish dinner, Tom said he wanted to +learn to smoke, now. Joe caught at the idea and said he would like to +try, too. So Huck made pipes and filled them. These novices had never +smoked anything before but cigars made of grape-vine, and they "bit" +the tongue, and were not considered manly anyway. + +Now they stretched themselves out on their elbows and began to puff, +charily, and with slender confidence. The smoke had an unpleasant +taste, and they gagged a little, but Tom said: + +"Why, it's just as easy! If I'd a knowed this was all, I'd a learnt +long ago." + +"So would I," said Joe. "It's just nothing." + +"Why, many a time I've looked at people smoking, and thought well I +wish I could do that; but I never thought I could," said Tom. + +"That's just the way with me, hain't it, Huck? You've heard me talk +just that way--haven't you, Huck? I'll leave it to Huck if I haven't." + +"Yes--heaps of times," said Huck. + +"Well, I have too," said Tom; "oh, hundreds of times. Once down by the +slaughter-house. Don't you remember, Huck? Bob Tanner was there, and +Johnny Miller, and Jeff Thatcher, when I said it. Don't you remember, +Huck, 'bout me saying that?" + +"Yes, that's so," said Huck. "That was the day after I lost a white +alley. No, 'twas the day before." + +"There--I told you so," said Tom. "Huck recollects it." + +"I bleeve I could smoke this pipe all day," said Joe. "I don't feel +sick." + +"Neither do I," said Tom. "I could smoke it all day. But I bet you +Jeff Thatcher couldn't." + +"Jeff Thatcher! Why, he'd keel over just with two draws. Just let him +try it once. HE'D see!" + +"I bet he would. And Johnny Miller--I wish could see Johnny Miller +tackle it once." + +"Oh, don't I!" said Joe. "Why, I bet you Johnny Miller couldn't any +more do this than nothing. Just one little snifter would fetch HIM." + +"'Deed it would, Joe. Say--I wish the boys could see us now." + +"So do I." + +"Say--boys, don't say anything about it, and some time when they're +around, I'll come up to you and say, 'Joe, got a pipe? I want a smoke.' +And you'll say, kind of careless like, as if it warn't anything, you'll +say, 'Yes, I got my OLD pipe, and another one, but my tobacker ain't +very good.' And I'll say, 'Oh, that's all right, if it's STRONG +enough.' And then you'll out with the pipes, and we'll light up just as +ca'm, and then just see 'em look!" + +"By jings, that'll be gay, Tom! I wish it was NOW!" + +"So do I! And when we tell 'em we learned when we was off pirating, +won't they wish they'd been along?" + +"Oh, I reckon not! I'll just BET they will!" + +So the talk ran on. But presently it began to flag a trifle, and grow +disjointed. The silences widened; the expectoration marvellously +increased. Every pore inside the boys' cheeks became a spouting +fountain; they could scarcely bail out the cellars under their tongues +fast enough to prevent an inundation; little overflowings down their +throats occurred in spite of all they could do, and sudden retchings +followed every time. Both boys were looking very pale and miserable, +now. Joe's pipe dropped from his nerveless fingers. Tom's followed. +Both fountains were going furiously and both pumps bailing with might +and main. Joe said feebly: + +"I've lost my knife. I reckon I better go and find it." + +Tom said, with quivering lips and halting utterance: + +"I'll help you. You go over that way and I'll hunt around by the +spring. No, you needn't come, Huck--we can find it." + +So Huck sat down again, and waited an hour. Then he found it lonesome, +and went to find his comrades. They were wide apart in the woods, both +very pale, both fast asleep. But something informed him that if they +had had any trouble they had got rid of it. + +They were not talkative at supper that night. They had a humble look, +and when Huck prepared his pipe after the meal and was going to prepare +theirs, they said no, they were not feeling very well--something they +ate at dinner had disagreed with them. + +About midnight Joe awoke, and called the boys. There was a brooding +oppressiveness in the air that seemed to bode something. The boys +huddled themselves together and sought the friendly companionship of +the fire, though the dull dead heat of the breathless atmosphere was +stifling. They sat still, intent and waiting. The solemn hush +continued. Beyond the light of the fire everything was swallowed up in +the blackness of darkness. Presently there came a quivering glow that +vaguely revealed the foliage for a moment and then vanished. By and by +another came, a little stronger. Then another. Then a faint moan came +sighing through the branches of the forest and the boys felt a fleeting +breath upon their cheeks, and shuddered with the fancy that the Spirit +of the Night had gone by. There was a pause. Now a weird flash turned +night into day and showed every little grass-blade, separate and +distinct, that grew about their feet. And it showed three white, +startled faces, too. A deep peal of thunder went rolling and tumbling +down the heavens and lost itself in sullen rumblings in the distance. A +sweep of chilly air passed by, rustling all the leaves and snowing the +flaky ashes broadcast about the fire. Another fierce glare lit up the +forest and an instant crash followed that seemed to rend the tree-tops +right over the boys' heads. They clung together in terror, in the thick +gloom that followed. A few big rain-drops fell pattering upon the +leaves. + +"Quick! boys, go for the tent!" exclaimed Tom. + +They sprang away, stumbling over roots and among vines in the dark, no +two plunging in the same direction. A furious blast roared through the +trees, making everything sing as it went. One blinding flash after +another came, and peal on peal of deafening thunder. And now a +drenching rain poured down and the rising hurricane drove it in sheets +along the ground. The boys cried out to each other, but the roaring +wind and the booming thunder-blasts drowned their voices utterly. +However, one by one they straggled in at last and took shelter under +the tent, cold, scared, and streaming with water; but to have company +in misery seemed something to be grateful for. They could not talk, the +old sail flapped so furiously, even if the other noises would have +allowed them. The tempest rose higher and higher, and presently the +sail tore loose from its fastenings and went winging away on the blast. +The boys seized each others' hands and fled, with many tumblings and +bruises, to the shelter of a great oak that stood upon the river-bank. +Now the battle was at its highest. Under the ceaseless conflagration of +lightning that flamed in the skies, everything below stood out in +clean-cut and shadowless distinctness: the bending trees, the billowy +river, white with foam, the driving spray of spume-flakes, the dim +outlines of the high bluffs on the other side, glimpsed through the +drifting cloud-rack and the slanting veil of rain. Every little while +some giant tree yielded the fight and fell crashing through the younger +growth; and the unflagging thunder-peals came now in ear-splitting +explosive bursts, keen and sharp, and unspeakably appalling. The storm +culminated in one matchless effort that seemed likely to tear the island +to pieces, burn it up, drown it to the tree-tops, blow it away, and +deafen every creature in it, all at one and the same moment. It was a +wild night for homeless young heads to be out in. + +But at last the battle was done, and the forces retired with weaker +and weaker threatenings and grumblings, and peace resumed her sway. The +boys went back to camp, a good deal awed; but they found there was +still something to be thankful for, because the great sycamore, the +shelter of their beds, was a ruin, now, blasted by the lightnings, and +they were not under it when the catastrophe happened. + +Everything in camp was drenched, the camp-fire as well; for they were +but heedless lads, like their generation, and had made no provision +against rain. Here was matter for dismay, for they were soaked through +and chilled. They were eloquent in their distress; but they presently +discovered that the fire had eaten so far up under the great log it had +been built against (where it curved upward and separated itself from +the ground), that a handbreadth or so of it had escaped wetting; so +they patiently wrought until, with shreds and bark gathered from the +under sides of sheltered logs, they coaxed the fire to burn again. Then +they piled on great dead boughs till they had a roaring furnace, and +were glad-hearted once more. They dried their boiled ham and had a +feast, and after that they sat by the fire and expanded and glorified +their midnight adventure until morning, for there was not a dry spot to +sleep on, anywhere around. + +As the sun began to steal in upon the boys, drowsiness came over them, +and they went out on the sandbar and lay down to sleep. They got +scorched out by and by, and drearily set about getting breakfast. After +the meal they felt rusty, and stiff-jointed, and a little homesick once +more. Tom saw the signs, and fell to cheering up the pirates as well as +he could. But they cared nothing for marbles, or circus, or swimming, +or anything. He reminded them of the imposing secret, and raised a ray +of cheer. While it lasted, he got them interested in a new device. This +was to knock off being pirates, for a while, and be Indians for a +change. They were attracted by this idea; so it was not long before +they were stripped, and striped from head to heel with black mud, like +so many zebras--all of them chiefs, of course--and then they went +tearing through the woods to attack an English settlement. + +By and by they separated into three hostile tribes, and darted upon +each other from ambush with dreadful war-whoops, and killed and scalped +each other by thousands. It was a gory day. Consequently it was an +extremely satisfactory one. + +They assembled in camp toward supper-time, hungry and happy; but now a +difficulty arose--hostile Indians could not break the bread of +hospitality together without first making peace, and this was a simple +impossibility without smoking a pipe of peace. There was no other +process that ever they had heard of. Two of the savages almost wished +they had remained pirates. However, there was no other way; so with +such show of cheerfulness as they could muster they called for the pipe +and took their whiff as it passed, in due form. + +And behold, they were glad they had gone into savagery, for they had +gained something; they found that they could now smoke a little without +having to go and hunt for a lost knife; they did not get sick enough to +be seriously uncomfortable. They were not likely to fool away this high +promise for lack of effort. No, they practised cautiously, after +supper, with right fair success, and so they spent a jubilant evening. +They were prouder and happier in their new acquirement than they would +have been in the scalping and skinning of the Six Nations. We will +leave them to smoke and chatter and brag, since we have no further use +for them at present. + + + +CHAPTER XVII + +BUT there was no hilarity in the little town that same tranquil +Saturday afternoon. The Harpers, and Aunt Polly's family, were being +put into mourning, with great grief and many tears. An unusual quiet +possessed the village, although it was ordinarily quiet enough, in all +conscience. The villagers conducted their concerns with an absent air, +and talked little; but they sighed often. The Saturday holiday seemed a +burden to the children. They had no heart in their sports, and +gradually gave them up. + +In the afternoon Becky Thatcher found herself moping about the +deserted schoolhouse yard, and feeling very melancholy. But she found +nothing there to comfort her. She soliloquized: + +"Oh, if I only had a brass andiron-knob again! But I haven't got +anything now to remember him by." And she choked back a little sob. + +Presently she stopped, and said to herself: + +"It was right here. Oh, if it was to do over again, I wouldn't say +that--I wouldn't say it for the whole world. But he's gone now; I'll +never, never, never see him any more." + +This thought broke her down, and she wandered away, with tears rolling +down her cheeks. Then quite a group of boys and girls--playmates of +Tom's and Joe's--came by, and stood looking over the paling fence and +talking in reverent tones of how Tom did so-and-so the last time they +saw him, and how Joe said this and that small trifle (pregnant with +awful prophecy, as they could easily see now!)--and each speaker +pointed out the exact spot where the lost lads stood at the time, and +then added something like "and I was a-standing just so--just as I am +now, and as if you was him--I was as close as that--and he smiled, just +this way--and then something seemed to go all over me, like--awful, you +know--and I never thought what it meant, of course, but I can see now!" + +Then there was a dispute about who saw the dead boys last in life, and +many claimed that dismal distinction, and offered evidences, more or +less tampered with by the witness; and when it was ultimately decided +who DID see the departed last, and exchanged the last words with them, +the lucky parties took upon themselves a sort of sacred importance, and +were gaped at and envied by all the rest. One poor chap, who had no +other grandeur to offer, said with tolerably manifest pride in the +remembrance: + +"Well, Tom Sawyer he licked me once." + +But that bid for glory was a failure. Most of the boys could say that, +and so that cheapened the distinction too much. The group loitered +away, still recalling memories of the lost heroes, in awed voices. + +When the Sunday-school hour was finished, the next morning, the bell +began to toll, instead of ringing in the usual way. It was a very still +Sabbath, and the mournful sound seemed in keeping with the musing hush +that lay upon nature. The villagers began to gather, loitering a moment +in the vestibule to converse in whispers about the sad event. But there +was no whispering in the house; only the funereal rustling of dresses +as the women gathered to their seats disturbed the silence there. None +could remember when the little church had been so full before. There +was finally a waiting pause, an expectant dumbness, and then Aunt Polly +entered, followed by Sid and Mary, and they by the Harper family, all +in deep black, and the whole congregation, the old minister as well, +rose reverently and stood until the mourners were seated in the front +pew. There was another communing silence, broken at intervals by +muffled sobs, and then the minister spread his hands abroad and prayed. +A moving hymn was sung, and the text followed: "I am the Resurrection +and the Life." + +As the service proceeded, the clergyman drew such pictures of the +graces, the winning ways, and the rare promise of the lost lads that +every soul there, thinking he recognized these pictures, felt a pang in +remembering that he had persistently blinded himself to them always +before, and had as persistently seen only faults and flaws in the poor +boys. The minister related many a touching incident in the lives of the +departed, too, which illustrated their sweet, generous natures, and the +people could easily see, now, how noble and beautiful those episodes +were, and remembered with grief that at the time they occurred they had +seemed rank rascalities, well deserving of the cowhide. The +congregation became more and more moved, as the pathetic tale went on, +till at last the whole company broke down and joined the weeping +mourners in a chorus of anguished sobs, the preacher himself giving way +to his feelings, and crying in the pulpit. + +There was a rustle in the gallery, which nobody noticed; a moment +later the church door creaked; the minister raised his streaming eyes +above his handkerchief, and stood transfixed! First one and then +another pair of eyes followed the minister's, and then almost with one +impulse the congregation rose and stared while the three dead boys came +marching up the aisle, Tom in the lead, Joe next, and Huck, a ruin of +drooping rags, sneaking sheepishly in the rear! They had been hid in +the unused gallery listening to their own funeral sermon! + +Aunt Polly, Mary, and the Harpers threw themselves upon their restored +ones, smothered them with kisses and poured out thanksgivings, while +poor Huck stood abashed and uncomfortable, not knowing exactly what to +do or where to hide from so many unwelcoming eyes. He wavered, and +started to slink away, but Tom seized him and said: + +"Aunt Polly, it ain't fair. Somebody's got to be glad to see Huck." + +"And so they shall. I'm glad to see him, poor motherless thing!" And +the loving attentions Aunt Polly lavished upon him were the one thing +capable of making him more uncomfortable than he was before. + +Suddenly the minister shouted at the top of his voice: "Praise God +from whom all blessings flow--SING!--and put your hearts in it!" + +And they did. Old Hundred swelled up with a triumphant burst, and +while it shook the rafters Tom Sawyer the Pirate looked around upon the +envying juveniles about him and confessed in his heart that this was +the proudest moment of his life. + +As the "sold" congregation trooped out they said they would almost be +willing to be made ridiculous again to hear Old Hundred sung like that +once more. + +Tom got more cuffs and kisses that day--according to Aunt Polly's +varying moods--than he had earned before in a year; and he hardly knew +which expressed the most gratefulness to God and affection for himself. + + + +CHAPTER XVIII + +THAT was Tom's great secret--the scheme to return home with his +brother pirates and attend their own funerals. They had paddled over to +the Missouri shore on a log, at dusk on Saturday, landing five or six +miles below the village; they had slept in the woods at the edge of the +town till nearly daylight, and had then crept through back lanes and +alleys and finished their sleep in the gallery of the church among a +chaos of invalided benches. + +At breakfast, Monday morning, Aunt Polly and Mary were very loving to +Tom, and very attentive to his wants. There was an unusual amount of +talk. In the course of it Aunt Polly said: + +"Well, I don't say it wasn't a fine joke, Tom, to keep everybody +suffering 'most a week so you boys had a good time, but it is a pity +you could be so hard-hearted as to let me suffer so. If you could come +over on a log to go to your funeral, you could have come over and give +me a hint some way that you warn't dead, but only run off." + +"Yes, you could have done that, Tom," said Mary; "and I believe you +would if you had thought of it." + +"Would you, Tom?" said Aunt Polly, her face lighting wistfully. "Say, +now, would you, if you'd thought of it?" + +"I--well, I don't know. 'Twould 'a' spoiled everything." + +"Tom, I hoped you loved me that much," said Aunt Polly, with a grieved +tone that discomforted the boy. "It would have been something if you'd +cared enough to THINK of it, even if you didn't DO it." + +"Now, auntie, that ain't any harm," pleaded Mary; "it's only Tom's +giddy way--he is always in such a rush that he never thinks of +anything." + +"More's the pity. Sid would have thought. And Sid would have come and +DONE it, too. Tom, you'll look back, some day, when it's too late, and +wish you'd cared a little more for me when it would have cost you so +little." + +"Now, auntie, you know I do care for you," said Tom. + +"I'd know it better if you acted more like it." + +"I wish now I'd thought," said Tom, with a repentant tone; "but I +dreamt about you, anyway. That's something, ain't it?" + +"It ain't much--a cat does that much--but it's better than nothing. +What did you dream?" + +"Why, Wednesday night I dreamt that you was sitting over there by the +bed, and Sid was sitting by the woodbox, and Mary next to him." + +"Well, so we did. So we always do. I'm glad your dreams could take +even that much trouble about us." + +"And I dreamt that Joe Harper's mother was here." + +"Why, she was here! Did you dream any more?" + +"Oh, lots. But it's so dim, now." + +"Well, try to recollect--can't you?" + +"Somehow it seems to me that the wind--the wind blowed the--the--" + +"Try harder, Tom! The wind did blow something. Come!" + +Tom pressed his fingers on his forehead an anxious minute, and then +said: + +"I've got it now! I've got it now! It blowed the candle!" + +"Mercy on us! Go on, Tom--go on!" + +"And it seems to me that you said, 'Why, I believe that that door--'" + +"Go ON, Tom!" + +"Just let me study a moment--just a moment. Oh, yes--you said you +believed the door was open." + +"As I'm sitting here, I did! Didn't I, Mary! Go on!" + +"And then--and then--well I won't be certain, but it seems like as if +you made Sid go and--and--" + +"Well? Well? What did I make him do, Tom? What did I make him do?" + +"You made him--you--Oh, you made him shut it." + +"Well, for the land's sake! I never heard the beat of that in all my +days! Don't tell ME there ain't anything in dreams, any more. Sereny +Harper shall know of this before I'm an hour older. I'd like to see her +get around THIS with her rubbage 'bout superstition. Go on, Tom!" + +"Oh, it's all getting just as bright as day, now. Next you said I +warn't BAD, only mischeevous and harum-scarum, and not any more +responsible than--than--I think it was a colt, or something." + +"And so it was! Well, goodness gracious! Go on, Tom!" + +"And then you began to cry." + +"So I did. So I did. Not the first time, neither. And then--" + +"Then Mrs. Harper she began to cry, and said Joe was just the same, +and she wished she hadn't whipped him for taking cream when she'd +throwed it out her own self--" + +"Tom! The sperrit was upon you! You was a prophesying--that's what you +was doing! Land alive, go on, Tom!" + +"Then Sid he said--he said--" + +"I don't think I said anything," said Sid. + +"Yes you did, Sid," said Mary. + +"Shut your heads and let Tom go on! What did he say, Tom?" + +"He said--I THINK he said he hoped I was better off where I was gone +to, but if I'd been better sometimes--" + +"THERE, d'you hear that! It was his very words!" + +"And you shut him up sharp." + +"I lay I did! There must 'a' been an angel there. There WAS an angel +there, somewheres!" + +"And Mrs. Harper told about Joe scaring her with a firecracker, and +you told about Peter and the Painkiller--" + +"Just as true as I live!" + +"And then there was a whole lot of talk 'bout dragging the river for +us, and 'bout having the funeral Sunday, and then you and old Miss +Harper hugged and cried, and she went." + +"It happened just so! It happened just so, as sure as I'm a-sitting in +these very tracks. Tom, you couldn't told it more like if you'd 'a' +seen it! And then what? Go on, Tom!" + +"Then I thought you prayed for me--and I could see you and hear every +word you said. And you went to bed, and I was so sorry that I took and +wrote on a piece of sycamore bark, 'We ain't dead--we are only off +being pirates,' and put it on the table by the candle; and then you +looked so good, laying there asleep, that I thought I went and leaned +over and kissed you on the lips." + +"Did you, Tom, DID you! I just forgive you everything for that!" And +she seized the boy in a crushing embrace that made him feel like the +guiltiest of villains. + +"It was very kind, even though it was only a--dream," Sid soliloquized +just audibly. + +"Shut up, Sid! A body does just the same in a dream as he'd do if he +was awake. Here's a big Milum apple I've been saving for you, Tom, if +you was ever found again--now go 'long to school. I'm thankful to the +good God and Father of us all I've got you back, that's long-suffering +and merciful to them that believe on Him and keep His word, though +goodness knows I'm unworthy of it, but if only the worthy ones got His +blessings and had His hand to help them over the rough places, there's +few enough would smile here or ever enter into His rest when the long +night comes. Go 'long Sid, Mary, Tom--take yourselves off--you've +hendered me long enough." + +The children left for school, and the old lady to call on Mrs. Harper +and vanquish her realism with Tom's marvellous dream. Sid had better +judgment than to utter the thought that was in his mind as he left the +house. It was this: "Pretty thin--as long a dream as that, without any +mistakes in it!" + +What a hero Tom was become, now! He did not go skipping and prancing, +but moved with a dignified swagger as became a pirate who felt that the +public eye was on him. And indeed it was; he tried not to seem to see +the looks or hear the remarks as he passed along, but they were food +and drink to him. Smaller boys than himself flocked at his heels, as +proud to be seen with him, and tolerated by him, as if he had been the +drummer at the head of a procession or the elephant leading a menagerie +into town. Boys of his own size pretended not to know he had been away +at all; but they were consuming with envy, nevertheless. They would +have given anything to have that swarthy suntanned skin of his, and his +glittering notoriety; and Tom would not have parted with either for a +circus. + +At school the children made so much of him and of Joe, and delivered +such eloquent admiration from their eyes, that the two heroes were not +long in becoming insufferably "stuck-up." They began to tell their +adventures to hungry listeners--but they only began; it was not a thing +likely to have an end, with imaginations like theirs to furnish +material. And finally, when they got out their pipes and went serenely +puffing around, the very summit of glory was reached. + +Tom decided that he could be independent of Becky Thatcher now. Glory +was sufficient. He would live for glory. Now that he was distinguished, +maybe she would be wanting to "make up." Well, let her--she should see +that he could be as indifferent as some other people. Presently she +arrived. Tom pretended not to see her. He moved away and joined a group +of boys and girls and began to talk. Soon he observed that she was +tripping gayly back and forth with flushed face and dancing eyes, +pretending to be busy chasing schoolmates, and screaming with laughter +when she made a capture; but he noticed that she always made her +captures in his vicinity, and that she seemed to cast a conscious eye +in his direction at such times, too. It gratified all the vicious +vanity that was in him; and so, instead of winning him, it only "set +him up" the more and made him the more diligent to avoid betraying that +he knew she was about. Presently she gave over skylarking, and moved +irresolutely about, sighing once or twice and glancing furtively and +wistfully toward Tom. Then she observed that now Tom was talking more +particularly to Amy Lawrence than to any one else. She felt a sharp +pang and grew disturbed and uneasy at once. She tried to go away, but +her feet were treacherous, and carried her to the group instead. She +said to a girl almost at Tom's elbow--with sham vivacity: + +"Why, Mary Austin! you bad girl, why didn't you come to Sunday-school?" + +"I did come--didn't you see me?" + +"Why, no! Did you? Where did you sit?" + +"I was in Miss Peters' class, where I always go. I saw YOU." + +"Did you? Why, it's funny I didn't see you. I wanted to tell you about +the picnic." + +"Oh, that's jolly. Who's going to give it?" + +"My ma's going to let me have one." + +"Oh, goody; I hope she'll let ME come." + +"Well, she will. The picnic's for me. She'll let anybody come that I +want, and I want you." + +"That's ever so nice. When is it going to be?" + +"By and by. Maybe about vacation." + +"Oh, won't it be fun! You going to have all the girls and boys?" + +"Yes, every one that's friends to me--or wants to be"; and she glanced +ever so furtively at Tom, but he talked right along to Amy Lawrence +about the terrible storm on the island, and how the lightning tore the +great sycamore tree "all to flinders" while he was "standing within +three feet of it." + +"Oh, may I come?" said Grace Miller. + +"Yes." + +"And me?" said Sally Rogers. + +"Yes." + +"And me, too?" said Susy Harper. "And Joe?" + +"Yes." + +And so on, with clapping of joyful hands till all the group had begged +for invitations but Tom and Amy. Then Tom turned coolly away, still +talking, and took Amy with him. Becky's lips trembled and the tears +came to her eyes; she hid these signs with a forced gayety and went on +chattering, but the life had gone out of the picnic, now, and out of +everything else; she got away as soon as she could and hid herself and +had what her sex call "a good cry." Then she sat moody, with wounded +pride, till the bell rang. She roused up, now, with a vindictive cast +in her eye, and gave her plaited tails a shake and said she knew what +SHE'D do. + +At recess Tom continued his flirtation with Amy with jubilant +self-satisfaction. And he kept drifting about to find Becky and lacerate +her with the performance. At last he spied her, but there was a sudden +falling of his mercury. She was sitting cosily on a little bench behind +the schoolhouse looking at a picture-book with Alfred Temple--and so +absorbed were they, and their heads so close together over the book, +that they did not seem to be conscious of anything in the world besides. +Jealousy ran red-hot through Tom's veins. He began to hate himself for +throwing away the chance Becky had offered for a reconciliation. He +called himself a fool, and all the hard names he could think of. He +wanted to cry with vexation. Amy chatted happily along, as they walked, +for her heart was singing, but Tom's tongue had lost its function. He +did not hear what Amy was saying, and whenever she paused expectantly he +could only stammer an awkward assent, which was as often misplaced as +otherwise. He kept drifting to the rear of the schoolhouse, again and +again, to sear his eyeballs with the hateful spectacle there. He could +not help it. And it maddened him to see, as he thought he saw, that +Becky Thatcher never once suspected that he was even in the land of the +living. But she did see, nevertheless; and she knew she was winning her +fight, too, and was glad to see him suffer as she had suffered. + +Amy's happy prattle became intolerable. Tom hinted at things he had to +attend to; things that must be done; and time was fleeting. But in +vain--the girl chirped on. Tom thought, "Oh, hang her, ain't I ever +going to get rid of her?" At last he must be attending to those +things--and she said artlessly that she would be "around" when school +let out. And he hastened away, hating her for it. + +"Any other boy!" Tom thought, grating his teeth. "Any boy in the whole +town but that Saint Louis smarty that thinks he dresses so fine and is +aristocracy! Oh, all right, I licked you the first day you ever saw +this town, mister, and I'll lick you again! You just wait till I catch +you out! I'll just take and--" + +And he went through the motions of thrashing an imaginary boy +--pummelling the air, and kicking and gouging. "Oh, you do, do you? You +holler 'nough, do you? Now, then, let that learn you!" And so the +imaginary flogging was finished to his satisfaction. + +Tom fled home at noon. His conscience could not endure any more of +Amy's grateful happiness, and his jealousy could bear no more of the +other distress. Becky resumed her picture inspections with Alfred, but +as the minutes dragged along and no Tom came to suffer, her triumph +began to cloud and she lost interest; gravity and absent-mindedness +followed, and then melancholy; two or three times she pricked up her +ear at a footstep, but it was a false hope; no Tom came. At last she +grew entirely miserable and wished she hadn't carried it so far. When +poor Alfred, seeing that he was losing her, he did not know how, kept +exclaiming: "Oh, here's a jolly one! look at this!" she lost patience +at last, and said, "Oh, don't bother me! I don't care for them!" and +burst into tears, and got up and walked away. + +Alfred dropped alongside and was going to try to comfort her, but she +said: + +"Go away and leave me alone, can't you! I hate you!" + +So the boy halted, wondering what he could have done--for she had said +she would look at pictures all through the nooning--and she walked on, +crying. Then Alfred went musing into the deserted schoolhouse. He was +humiliated and angry. He easily guessed his way to the truth--the girl +had simply made a convenience of him to vent her spite upon Tom Sawyer. +He was far from hating Tom the less when this thought occurred to him. +He wished there was some way to get that boy into trouble without much +risk to himself. Tom's spelling-book fell under his eye. Here was his +opportunity. He gratefully opened to the lesson for the afternoon and +poured ink upon the page. + +Becky, glancing in at a window behind him at the moment, saw the act, +and moved on, without discovering herself. She started homeward, now, +intending to find Tom and tell him; Tom would be thankful and their +troubles would be healed. Before she was half way home, however, she +had changed her mind. The thought of Tom's treatment of her when she +was talking about her picnic came scorching back and filled her with +shame. She resolved to let him get whipped on the damaged +spelling-book's account, and to hate him forever, into the bargain. + + + +CHAPTER XIX + +TOM arrived at home in a dreary mood, and the first thing his aunt +said to him showed him that he had brought his sorrows to an +unpromising market: + +"Tom, I've a notion to skin you alive!" + +"Auntie, what have I done?" + +"Well, you've done enough. Here I go over to Sereny Harper, like an +old softy, expecting I'm going to make her believe all that rubbage +about that dream, when lo and behold you she'd found out from Joe that +you was over here and heard all the talk we had that night. Tom, I +don't know what is to become of a boy that will act like that. It makes +me feel so bad to think you could let me go to Sereny Harper and make +such a fool of myself and never say a word." + +This was a new aspect of the thing. His smartness of the morning had +seemed to Tom a good joke before, and very ingenious. It merely looked +mean and shabby now. He hung his head and could not think of anything +to say for a moment. Then he said: + +"Auntie, I wish I hadn't done it--but I didn't think." + +"Oh, child, you never think. You never think of anything but your own +selfishness. You could think to come all the way over here from +Jackson's Island in the night to laugh at our troubles, and you could +think to fool me with a lie about a dream; but you couldn't ever think +to pity us and save us from sorrow." + +"Auntie, I know now it was mean, but I didn't mean to be mean. I +didn't, honest. And besides, I didn't come over here to laugh at you +that night." + +"What did you come for, then?" + +"It was to tell you not to be uneasy about us, because we hadn't got +drownded." + +"Tom, Tom, I would be the thankfullest soul in this world if I could +believe you ever had as good a thought as that, but you know you never +did--and I know it, Tom." + +"Indeed and 'deed I did, auntie--I wish I may never stir if I didn't." + +"Oh, Tom, don't lie--don't do it. It only makes things a hundred times +worse." + +"It ain't a lie, auntie; it's the truth. I wanted to keep you from +grieving--that was all that made me come." + +"I'd give the whole world to believe that--it would cover up a power +of sins, Tom. I'd 'most be glad you'd run off and acted so bad. But it +ain't reasonable; because, why didn't you tell me, child?" + +"Why, you see, when you got to talking about the funeral, I just got +all full of the idea of our coming and hiding in the church, and I +couldn't somehow bear to spoil it. So I just put the bark back in my +pocket and kept mum." + +"What bark?" + +"The bark I had wrote on to tell you we'd gone pirating. I wish, now, +you'd waked up when I kissed you--I do, honest." + +The hard lines in his aunt's face relaxed and a sudden tenderness +dawned in her eyes. + +"DID you kiss me, Tom?" + +"Why, yes, I did." + +"Are you sure you did, Tom?" + +"Why, yes, I did, auntie--certain sure." + +"What did you kiss me for, Tom?" + +"Because I loved you so, and you laid there moaning and I was so sorry." + +The words sounded like truth. The old lady could not hide a tremor in +her voice when she said: + +"Kiss me again, Tom!--and be off with you to school, now, and don't +bother me any more." + +The moment he was gone, she ran to a closet and got out the ruin of a +jacket which Tom had gone pirating in. Then she stopped, with it in her +hand, and said to herself: + +"No, I don't dare. Poor boy, I reckon he's lied about it--but it's a +blessed, blessed lie, there's such a comfort come from it. I hope the +Lord--I KNOW the Lord will forgive him, because it was such +goodheartedness in him to tell it. But I don't want to find out it's a +lie. I won't look." + +She put the jacket away, and stood by musing a minute. Twice she put +out her hand to take the garment again, and twice she refrained. Once +more she ventured, and this time she fortified herself with the +thought: "It's a good lie--it's a good lie--I won't let it grieve me." +So she sought the jacket pocket. A moment later she was reading Tom's +piece of bark through flowing tears and saying: "I could forgive the +boy, now, if he'd committed a million sins!" + + + +CHAPTER XX + +THERE was something about Aunt Polly's manner, when she kissed Tom, +that swept away his low spirits and made him lighthearted and happy +again. He started to school and had the luck of coming upon Becky +Thatcher at the head of Meadow Lane. His mood always determined his +manner. Without a moment's hesitation he ran to her and said: + +"I acted mighty mean to-day, Becky, and I'm so sorry. I won't ever, +ever do that way again, as long as ever I live--please make up, won't +you?" + +The girl stopped and looked him scornfully in the face: + +"I'll thank you to keep yourself TO yourself, Mr. Thomas Sawyer. I'll +never speak to you again." + +She tossed her head and passed on. Tom was so stunned that he had not +even presence of mind enough to say "Who cares, Miss Smarty?" until the +right time to say it had gone by. So he said nothing. But he was in a +fine rage, nevertheless. He moped into the schoolyard wishing she were +a boy, and imagining how he would trounce her if she were. He presently +encountered her and delivered a stinging remark as he passed. She +hurled one in return, and the angry breach was complete. It seemed to +Becky, in her hot resentment, that she could hardly wait for school to +"take in," she was so impatient to see Tom flogged for the injured +spelling-book. If she had had any lingering notion of exposing Alfred +Temple, Tom's offensive fling had driven it entirely away. + +Poor girl, she did not know how fast she was nearing trouble herself. +The master, Mr. Dobbins, had reached middle age with an unsatisfied +ambition. The darling of his desires was, to be a doctor, but poverty +had decreed that he should be nothing higher than a village +schoolmaster. Every day he took a mysterious book out of his desk and +absorbed himself in it at times when no classes were reciting. He kept +that book under lock and key. There was not an urchin in school but was +perishing to have a glimpse of it, but the chance never came. Every boy +and girl had a theory about the nature of that book; but no two +theories were alike, and there was no way of getting at the facts in +the case. Now, as Becky was passing by the desk, which stood near the +door, she noticed that the key was in the lock! It was a precious +moment. She glanced around; found herself alone, and the next instant +she had the book in her hands. The title-page--Professor Somebody's +ANATOMY--carried no information to her mind; so she began to turn the +leaves. She came at once upon a handsomely engraved and colored +frontispiece--a human figure, stark naked. At that moment a shadow fell +on the page and Tom Sawyer stepped in at the door and caught a glimpse +of the picture. Becky snatched at the book to close it, and had the +hard luck to tear the pictured page half down the middle. She thrust +the volume into the desk, turned the key, and burst out crying with +shame and vexation. + +"Tom Sawyer, you are just as mean as you can be, to sneak up on a +person and look at what they're looking at." + +"How could I know you was looking at anything?" + +"You ought to be ashamed of yourself, Tom Sawyer; you know you're +going to tell on me, and oh, what shall I do, what shall I do! I'll be +whipped, and I never was whipped in school." + +Then she stamped her little foot and said: + +"BE so mean if you want to! I know something that's going to happen. +You just wait and you'll see! Hateful, hateful, hateful!"--and she +flung out of the house with a new explosion of crying. + +Tom stood still, rather flustered by this onslaught. Presently he said +to himself: + +"What a curious kind of a fool a girl is! Never been licked in school! +Shucks! What's a licking! That's just like a girl--they're so +thin-skinned and chicken-hearted. Well, of course I ain't going to tell +old Dobbins on this little fool, because there's other ways of getting +even on her, that ain't so mean; but what of it? Old Dobbins will ask +who it was tore his book. Nobody'll answer. Then he'll do just the way +he always does--ask first one and then t'other, and when he comes to the +right girl he'll know it, without any telling. Girls' faces always tell +on them. They ain't got any backbone. She'll get licked. Well, it's a +kind of a tight place for Becky Thatcher, because there ain't any way +out of it." Tom conned the thing a moment longer, and then added: "All +right, though; she'd like to see me in just such a fix--let her sweat it +out!" + +Tom joined the mob of skylarking scholars outside. In a few moments +the master arrived and school "took in." Tom did not feel a strong +interest in his studies. Every time he stole a glance at the girls' +side of the room Becky's face troubled him. Considering all things, he +did not want to pity her, and yet it was all he could do to help it. He +could get up no exultation that was really worthy the name. Presently +the spelling-book discovery was made, and Tom's mind was entirely full +of his own matters for a while after that. Becky roused up from her +lethargy of distress and showed good interest in the proceedings. She +did not expect that Tom could get out of his trouble by denying that he +spilt the ink on the book himself; and she was right. The denial only +seemed to make the thing worse for Tom. Becky supposed she would be +glad of that, and she tried to believe she was glad of it, but she +found she was not certain. When the worst came to the worst, she had an +impulse to get up and tell on Alfred Temple, but she made an effort and +forced herself to keep still--because, said she to herself, "he'll tell +about me tearing the picture sure. I wouldn't say a word, not to save +his life!" + +Tom took his whipping and went back to his seat not at all +broken-hearted, for he thought it was possible that he had unknowingly +upset the ink on the spelling-book himself, in some skylarking bout--he +had denied it for form's sake and because it was custom, and had stuck +to the denial from principle. + +A whole hour drifted by, the master sat nodding in his throne, the air +was drowsy with the hum of study. By and by, Mr. Dobbins straightened +himself up, yawned, then unlocked his desk, and reached for his book, +but seemed undecided whether to take it out or leave it. Most of the +pupils glanced up languidly, but there were two among them that watched +his movements with intent eyes. Mr. Dobbins fingered his book absently +for a while, then took it out and settled himself in his chair to read! +Tom shot a glance at Becky. He had seen a hunted and helpless rabbit +look as she did, with a gun levelled at its head. Instantly he forgot +his quarrel with her. Quick--something must be done! done in a flash, +too! But the very imminence of the emergency paralyzed his invention. +Good!--he had an inspiration! He would run and snatch the book, spring +through the door and fly. But his resolution shook for one little +instant, and the chance was lost--the master opened the volume. If Tom +only had the wasted opportunity back again! Too late. There was no help +for Becky now, he said. The next moment the master faced the school. +Every eye sank under his gaze. There was that in it which smote even +the innocent with fear. There was silence while one might count ten +--the master was gathering his wrath. Then he spoke: "Who tore this book?" + +There was not a sound. One could have heard a pin drop. The stillness +continued; the master searched face after face for signs of guilt. + +"Benjamin Rogers, did you tear this book?" + +A denial. Another pause. + +"Joseph Harper, did you?" + +Another denial. Tom's uneasiness grew more and more intense under the +slow torture of these proceedings. The master scanned the ranks of +boys--considered a while, then turned to the girls: + +"Amy Lawrence?" + +A shake of the head. + +"Gracie Miller?" + +The same sign. + +"Susan Harper, did you do this?" + +Another negative. The next girl was Becky Thatcher. Tom was trembling +from head to foot with excitement and a sense of the hopelessness of +the situation. + +"Rebecca Thatcher" [Tom glanced at her face--it was white with terror] +--"did you tear--no, look me in the face" [her hands rose in appeal] +--"did you tear this book?" + +A thought shot like lightning through Tom's brain. He sprang to his +feet and shouted--"I done it!" + +The school stared in perplexity at this incredible folly. Tom stood a +moment, to gather his dismembered faculties; and when he stepped +forward to go to his punishment the surprise, the gratitude, the +adoration that shone upon him out of poor Becky's eyes seemed pay +enough for a hundred floggings. Inspired by the splendor of his own +act, he took without an outcry the most merciless flaying that even Mr. +Dobbins had ever administered; and also received with indifference the +added cruelty of a command to remain two hours after school should be +dismissed--for he knew who would wait for him outside till his +captivity was done, and not count the tedious time as loss, either. + +Tom went to bed that night planning vengeance against Alfred Temple; +for with shame and repentance Becky had told him all, not forgetting +her own treachery; but even the longing for vengeance had to give way, +soon, to pleasanter musings, and he fell asleep at last with Becky's +latest words lingering dreamily in his ear-- + +"Tom, how COULD you be so noble!" + + + +CHAPTER XXI + +VACATION was approaching. The schoolmaster, always severe, grew +severer and more exacting than ever, for he wanted the school to make a +good showing on "Examination" day. His rod and his ferule were seldom +idle now--at least among the smaller pupils. Only the biggest boys, and +young ladies of eighteen and twenty, escaped lashing. Mr. Dobbins' +lashings were very vigorous ones, too; for although he carried, under +his wig, a perfectly bald and shiny head, he had only reached middle +age, and there was no sign of feebleness in his muscle. As the great +day approached, all the tyranny that was in him came to the surface; he +seemed to take a vindictive pleasure in punishing the least +shortcomings. The consequence was, that the smaller boys spent their +days in terror and suffering and their nights in plotting revenge. They +threw away no opportunity to do the master a mischief. But he kept +ahead all the time. The retribution that followed every vengeful +success was so sweeping and majestic that the boys always retired from +the field badly worsted. At last they conspired together and hit upon a +plan that promised a dazzling victory. They swore in the sign-painter's +boy, told him the scheme, and asked his help. He had his own reasons +for being delighted, for the master boarded in his father's family and +had given the boy ample cause to hate him. The master's wife would go +on a visit to the country in a few days, and there would be nothing to +interfere with the plan; the master always prepared himself for great +occasions by getting pretty well fuddled, and the sign-painter's boy +said that when the dominie had reached the proper condition on +Examination Evening he would "manage the thing" while he napped in his +chair; then he would have him awakened at the right time and hurried +away to school. + +In the fulness of time the interesting occasion arrived. At eight in +the evening the schoolhouse was brilliantly lighted, and adorned with +wreaths and festoons of foliage and flowers. The master sat throned in +his great chair upon a raised platform, with his blackboard behind him. +He was looking tolerably mellow. Three rows of benches on each side and +six rows in front of him were occupied by the dignitaries of the town +and by the parents of the pupils. To his left, back of the rows of +citizens, was a spacious temporary platform upon which were seated the +scholars who were to take part in the exercises of the evening; rows of +small boys, washed and dressed to an intolerable state of discomfort; +rows of gawky big boys; snowbanks of girls and young ladies clad in +lawn and muslin and conspicuously conscious of their bare arms, their +grandmothers' ancient trinkets, their bits of pink and blue ribbon and +the flowers in their hair. All the rest of the house was filled with +non-participating scholars. + +The exercises began. A very little boy stood up and sheepishly +recited, "You'd scarce expect one of my age to speak in public on the +stage," etc.--accompanying himself with the painfully exact and +spasmodic gestures which a machine might have used--supposing the +machine to be a trifle out of order. But he got through safely, though +cruelly scared, and got a fine round of applause when he made his +manufactured bow and retired. + +A little shamefaced girl lisped, "Mary had a little lamb," etc., +performed a compassion-inspiring curtsy, got her meed of applause, and +sat down flushed and happy. + +Tom Sawyer stepped forward with conceited confidence and soared into +the unquenchable and indestructible "Give me liberty or give me death" +speech, with fine fury and frantic gesticulation, and broke down in the +middle of it. A ghastly stage-fright seized him, his legs quaked under +him and he was like to choke. True, he had the manifest sympathy of the +house but he had the house's silence, too, which was even worse than +its sympathy. The master frowned, and this completed the disaster. Tom +struggled awhile and then retired, utterly defeated. There was a weak +attempt at applause, but it died early. + +"The Boy Stood on the Burning Deck" followed; also "The Assyrian Came +Down," and other declamatory gems. Then there were reading exercises, +and a spelling fight. The meagre Latin class recited with honor. The +prime feature of the evening was in order, now--original "compositions" +by the young ladies. Each in her turn stepped forward to the edge of +the platform, cleared her throat, held up her manuscript (tied with +dainty ribbon), and proceeded to read, with labored attention to +"expression" and punctuation. The themes were the same that had been +illuminated upon similar occasions by their mothers before them, their +grandmothers, and doubtless all their ancestors in the female line +clear back to the Crusades. "Friendship" was one; "Memories of Other +Days"; "Religion in History"; "Dream Land"; "The Advantages of +Culture"; "Forms of Political Government Compared and Contrasted"; +"Melancholy"; "Filial Love"; "Heart Longings," etc., etc. + +A prevalent feature in these compositions was a nursed and petted +melancholy; another was a wasteful and opulent gush of "fine language"; +another was a tendency to lug in by the ears particularly prized words +and phrases until they were worn entirely out; and a peculiarity that +conspicuously marked and marred them was the inveterate and intolerable +sermon that wagged its crippled tail at the end of each and every one +of them. No matter what the subject might be, a brain-racking effort +was made to squirm it into some aspect or other that the moral and +religious mind could contemplate with edification. The glaring +insincerity of these sermons was not sufficient to compass the +banishment of the fashion from the schools, and it is not sufficient +to-day; it never will be sufficient while the world stands, perhaps. +There is no school in all our land where the young ladies do not feel +obliged to close their compositions with a sermon; and you will find +that the sermon of the most frivolous and the least religious girl in +the school is always the longest and the most relentlessly pious. But +enough of this. Homely truth is unpalatable. + +Let us return to the "Examination." The first composition that was +read was one entitled "Is this, then, Life?" Perhaps the reader can +endure an extract from it: + + "In the common walks of life, with what delightful + emotions does the youthful mind look forward to some + anticipated scene of festivity! Imagination is busy + sketching rose-tinted pictures of joy. In fancy, the + voluptuous votary of fashion sees herself amid the + festive throng, 'the observed of all observers.' Her + graceful form, arrayed in snowy robes, is whirling + through the mazes of the joyous dance; her eye is + brightest, her step is lightest in the gay assembly. + + "In such delicious fancies time quickly glides by, + and the welcome hour arrives for her entrance into + the Elysian world, of which she has had such bright + dreams. How fairy-like does everything appear to + her enchanted vision! Each new scene is more charming + than the last. But after a while she finds that + beneath this goodly exterior, all is vanity, the + flattery which once charmed her soul, now grates + harshly upon her ear; the ball-room has lost its + charms; and with wasted health and imbittered heart, + she turns away with the conviction that earthly + pleasures cannot satisfy the longings of the soul!" + +And so forth and so on. There was a buzz of gratification from time to +time during the reading, accompanied by whispered ejaculations of "How +sweet!" "How eloquent!" "So true!" etc., and after the thing had closed +with a peculiarly afflicting sermon the applause was enthusiastic. + +Then arose a slim, melancholy girl, whose face had the "interesting" +paleness that comes of pills and indigestion, and read a "poem." Two +stanzas of it will do: + + "A MISSOURI MAIDEN'S FAREWELL TO ALABAMA + + "Alabama, good-bye! I love thee well! + But yet for a while do I leave thee now! + Sad, yes, sad thoughts of thee my heart doth swell, + And burning recollections throng my brow! + For I have wandered through thy flowery woods; + Have roamed and read near Tallapoosa's stream; + Have listened to Tallassee's warring floods, + And wooed on Coosa's side Aurora's beam. + + "Yet shame I not to bear an o'er-full heart, + Nor blush to turn behind my tearful eyes; + 'Tis from no stranger land I now must part, + 'Tis to no strangers left I yield these sighs. + Welcome and home were mine within this State, + Whose vales I leave--whose spires fade fast from me + And cold must be mine eyes, and heart, and tete, + When, dear Alabama! they turn cold on thee!" + +There were very few there who knew what "tete" meant, but the poem was +very satisfactory, nevertheless. + +Next appeared a dark-complexioned, black-eyed, black-haired young +lady, who paused an impressive moment, assumed a tragic expression, and +began to read in a measured, solemn tone: + + "A VISION + + "Dark and tempestuous was night. Around the + throne on high not a single star quivered; but + the deep intonations of the heavy thunder + constantly vibrated upon the ear; whilst the + terrific lightning revelled in angry mood + through the cloudy chambers of heaven, seeming + to scorn the power exerted over its terror by + the illustrious Franklin! Even the boisterous + winds unanimously came forth from their mystic + homes, and blustered about as if to enhance by + their aid the wildness of the scene. + + "At such a time, so dark, so dreary, for human + sympathy my very spirit sighed; but instead thereof, + + "'My dearest friend, my counsellor, my comforter + and guide--My joy in grief, my second bliss + in joy,' came to my side. She moved like one of + those bright beings pictured in the sunny walks + of fancy's Eden by the romantic and young, a + queen of beauty unadorned save by her own + transcendent loveliness. So soft was her step, it + failed to make even a sound, and but for the + magical thrill imparted by her genial touch, as + other unobtrusive beauties, she would have glided + away un-perceived--unsought. A strange sadness + rested upon her features, like icy tears upon + the robe of December, as she pointed to the + contending elements without, and bade me contemplate + the two beings presented." + +This nightmare occupied some ten pages of manuscript and wound up with +a sermon so destructive of all hope to non-Presbyterians that it took +the first prize. This composition was considered to be the very finest +effort of the evening. The mayor of the village, in delivering the +prize to the author of it, made a warm speech in which he said that it +was by far the most "eloquent" thing he had ever listened to, and that +Daniel Webster himself might well be proud of it. + +It may be remarked, in passing, that the number of compositions in +which the word "beauteous" was over-fondled, and human experience +referred to as "life's page," was up to the usual average. + +Now the master, mellow almost to the verge of geniality, put his chair +aside, turned his back to the audience, and began to draw a map of +America on the blackboard, to exercise the geography class upon. But he +made a sad business of it with his unsteady hand, and a smothered +titter rippled over the house. He knew what the matter was, and set +himself to right it. He sponged out lines and remade them; but he only +distorted them more than ever, and the tittering was more pronounced. +He threw his entire attention upon his work, now, as if determined not +to be put down by the mirth. He felt that all eyes were fastened upon +him; he imagined he was succeeding, and yet the tittering continued; it +even manifestly increased. And well it might. There was a garret above, +pierced with a scuttle over his head; and down through this scuttle +came a cat, suspended around the haunches by a string; she had a rag +tied about her head and jaws to keep her from mewing; as she slowly +descended she curved upward and clawed at the string, she swung +downward and clawed at the intangible air. The tittering rose higher +and higher--the cat was within six inches of the absorbed teacher's +head--down, down, a little lower, and she grabbed his wig with her +desperate claws, clung to it, and was snatched up into the garret in an +instant with her trophy still in her possession! And how the light did +blaze abroad from the master's bald pate--for the sign-painter's boy +had GILDED it! + +That broke up the meeting. The boys were avenged. Vacation had come. + + NOTE:--The pretended "compositions" quoted in + this chapter are taken without alteration from a + volume entitled "Prose and Poetry, by a Western + Lady"--but they are exactly and precisely after + the schoolgirl pattern, and hence are much + happier than any mere imitations could be. + + + +CHAPTER XXII + +TOM joined the new order of Cadets of Temperance, being attracted by +the showy character of their "regalia." He promised to abstain from +smoking, chewing, and profanity as long as he remained a member. Now he +found out a new thing--namely, that to promise not to do a thing is the +surest way in the world to make a body want to go and do that very +thing. Tom soon found himself tormented with a desire to drink and +swear; the desire grew to be so intense that nothing but the hope of a +chance to display himself in his red sash kept him from withdrawing +from the order. Fourth of July was coming; but he soon gave that up +--gave it up before he had worn his shackles over forty-eight hours--and +fixed his hopes upon old Judge Frazer, justice of the peace, who was +apparently on his deathbed and would have a big public funeral, since +he was so high an official. During three days Tom was deeply concerned +about the Judge's condition and hungry for news of it. Sometimes his +hopes ran high--so high that he would venture to get out his regalia +and practise before the looking-glass. But the Judge had a most +discouraging way of fluctuating. At last he was pronounced upon the +mend--and then convalescent. Tom was disgusted; and felt a sense of +injury, too. He handed in his resignation at once--and that night the +Judge suffered a relapse and died. Tom resolved that he would never +trust a man like that again. + +The funeral was a fine thing. The Cadets paraded in a style calculated +to kill the late member with envy. Tom was a free boy again, however +--there was something in that. He could drink and swear, now--but found +to his surprise that he did not want to. The simple fact that he could, +took the desire away, and the charm of it. + +Tom presently wondered to find that his coveted vacation was beginning +to hang a little heavily on his hands. + +He attempted a diary--but nothing happened during three days, and so +he abandoned it. + +The first of all the negro minstrel shows came to town, and made a +sensation. Tom and Joe Harper got up a band of performers and were +happy for two days. + +Even the Glorious Fourth was in some sense a failure, for it rained +hard, there was no procession in consequence, and the greatest man in +the world (as Tom supposed), Mr. Benton, an actual United States +Senator, proved an overwhelming disappointment--for he was not +twenty-five feet high, nor even anywhere in the neighborhood of it. + +A circus came. The boys played circus for three days afterward in +tents made of rag carpeting--admission, three pins for boys, two for +girls--and then circusing was abandoned. + +A phrenologist and a mesmerizer came--and went again and left the +village duller and drearier than ever. + +There were some boys-and-girls' parties, but they were so few and so +delightful that they only made the aching voids between ache the harder. + +Becky Thatcher was gone to her Constantinople home to stay with her +parents during vacation--so there was no bright side to life anywhere. + +The dreadful secret of the murder was a chronic misery. It was a very +cancer for permanency and pain. + +Then came the measles. + +During two long weeks Tom lay a prisoner, dead to the world and its +happenings. He was very ill, he was interested in nothing. When he got +upon his feet at last and moved feebly down-town, a melancholy change +had come over everything and every creature. There had been a +"revival," and everybody had "got religion," not only the adults, but +even the boys and girls. Tom went about, hoping against hope for the +sight of one blessed sinful face, but disappointment crossed him +everywhere. He found Joe Harper studying a Testament, and turned sadly +away from the depressing spectacle. He sought Ben Rogers, and found him +visiting the poor with a basket of tracts. He hunted up Jim Hollis, who +called his attention to the precious blessing of his late measles as a +warning. Every boy he encountered added another ton to his depression; +and when, in desperation, he flew for refuge at last to the bosom of +Huckleberry Finn and was received with a Scriptural quotation, his +heart broke and he crept home and to bed realizing that he alone of all +the town was lost, forever and forever. + +And that night there came on a terrific storm, with driving rain, +awful claps of thunder and blinding sheets of lightning. He covered his +head with the bedclothes and waited in a horror of suspense for his +doom; for he had not the shadow of a doubt that all this hubbub was +about him. He believed he had taxed the forbearance of the powers above +to the extremity of endurance and that this was the result. It might +have seemed to him a waste of pomp and ammunition to kill a bug with a +battery of artillery, but there seemed nothing incongruous about the +getting up such an expensive thunderstorm as this to knock the turf +from under an insect like himself. + +By and by the tempest spent itself and died without accomplishing its +object. The boy's first impulse was to be grateful, and reform. His +second was to wait--for there might not be any more storms. + +The next day the doctors were back; Tom had relapsed. The three weeks +he spent on his back this time seemed an entire age. When he got abroad +at last he was hardly grateful that he had been spared, remembering how +lonely was his estate, how companionless and forlorn he was. He drifted +listlessly down the street and found Jim Hollis acting as judge in a +juvenile court that was trying a cat for murder, in the presence of her +victim, a bird. He found Joe Harper and Huck Finn up an alley eating a +stolen melon. Poor lads! they--like Tom--had suffered a relapse. + + + +CHAPTER XXIII + +AT last the sleepy atmosphere was stirred--and vigorously: the murder +trial came on in the court. It became the absorbing topic of village +talk immediately. Tom could not get away from it. Every reference to +the murder sent a shudder to his heart, for his troubled conscience and +fears almost persuaded him that these remarks were put forth in his +hearing as "feelers"; he did not see how he could be suspected of +knowing anything about the murder, but still he could not be +comfortable in the midst of this gossip. It kept him in a cold shiver +all the time. He took Huck to a lonely place to have a talk with him. +It would be some relief to unseal his tongue for a little while; to +divide his burden of distress with another sufferer. Moreover, he +wanted to assure himself that Huck had remained discreet. + +"Huck, have you ever told anybody about--that?" + +"'Bout what?" + +"You know what." + +"Oh--'course I haven't." + +"Never a word?" + +"Never a solitary word, so help me. What makes you ask?" + +"Well, I was afeard." + +"Why, Tom Sawyer, we wouldn't be alive two days if that got found out. +YOU know that." + +Tom felt more comfortable. After a pause: + +"Huck, they couldn't anybody get you to tell, could they?" + +"Get me to tell? Why, if I wanted that half-breed devil to drownd me +they could get me to tell. They ain't no different way." + +"Well, that's all right, then. I reckon we're safe as long as we keep +mum. But let's swear again, anyway. It's more surer." + +"I'm agreed." + +So they swore again with dread solemnities. + +"What is the talk around, Huck? I've heard a power of it." + +"Talk? Well, it's just Muff Potter, Muff Potter, Muff Potter all the +time. It keeps me in a sweat, constant, so's I want to hide som'ers." + +"That's just the same way they go on round me. I reckon he's a goner. +Don't you feel sorry for him, sometimes?" + +"Most always--most always. He ain't no account; but then he hain't +ever done anything to hurt anybody. Just fishes a little, to get money +to get drunk on--and loafs around considerable; but lord, we all do +that--leastways most of us--preachers and such like. But he's kind of +good--he give me half a fish, once, when there warn't enough for two; +and lots of times he's kind of stood by me when I was out of luck." + +"Well, he's mended kites for me, Huck, and knitted hooks on to my +line. I wish we could get him out of there." + +"My! we couldn't get him out, Tom. And besides, 'twouldn't do any +good; they'd ketch him again." + +"Yes--so they would. But I hate to hear 'em abuse him so like the +dickens when he never done--that." + +"I do too, Tom. Lord, I hear 'em say he's the bloodiest looking +villain in this country, and they wonder he wasn't ever hung before." + +"Yes, they talk like that, all the time. I've heard 'em say that if he +was to get free they'd lynch him." + +"And they'd do it, too." + +The boys had a long talk, but it brought them little comfort. As the +twilight drew on, they found themselves hanging about the neighborhood +of the little isolated jail, perhaps with an undefined hope that +something would happen that might clear away their difficulties. But +nothing happened; there seemed to be no angels or fairies interested in +this luckless captive. + +The boys did as they had often done before--went to the cell grating +and gave Potter some tobacco and matches. He was on the ground floor +and there were no guards. + +His gratitude for their gifts had always smote their consciences +before--it cut deeper than ever, this time. They felt cowardly and +treacherous to the last degree when Potter said: + +"You've been mighty good to me, boys--better'n anybody else in this +town. And I don't forget it, I don't. Often I says to myself, says I, +'I used to mend all the boys' kites and things, and show 'em where the +good fishin' places was, and befriend 'em what I could, and now they've +all forgot old Muff when he's in trouble; but Tom don't, and Huck +don't--THEY don't forget him, says I, 'and I don't forget them.' Well, +boys, I done an awful thing--drunk and crazy at the time--that's the +only way I account for it--and now I got to swing for it, and it's +right. Right, and BEST, too, I reckon--hope so, anyway. Well, we won't +talk about that. I don't want to make YOU feel bad; you've befriended +me. But what I want to say, is, don't YOU ever get drunk--then you won't +ever get here. Stand a litter furder west--so--that's it; it's a prime +comfort to see faces that's friendly when a body's in such a muck of +trouble, and there don't none come here but yourn. Good friendly +faces--good friendly faces. Git up on one another's backs and let me +touch 'em. That's it. Shake hands--yourn'll come through the bars, but +mine's too big. Little hands, and weak--but they've helped Muff Potter +a power, and they'd help him more if they could." + +Tom went home miserable, and his dreams that night were full of +horrors. The next day and the day after, he hung about the court-room, +drawn by an almost irresistible impulse to go in, but forcing himself +to stay out. Huck was having the same experience. They studiously +avoided each other. Each wandered away, from time to time, but the same +dismal fascination always brought them back presently. Tom kept his +ears open when idlers sauntered out of the court-room, but invariably +heard distressing news--the toils were closing more and more +relentlessly around poor Potter. At the end of the second day the +village talk was to the effect that Injun Joe's evidence stood firm and +unshaken, and that there was not the slightest question as to what the +jury's verdict would be. + +Tom was out late, that night, and came to bed through the window. He +was in a tremendous state of excitement. It was hours before he got to +sleep. All the village flocked to the court-house the next morning, for +this was to be the great day. Both sexes were about equally represented +in the packed audience. After a long wait the jury filed in and took +their places; shortly afterward, Potter, pale and haggard, timid and +hopeless, was brought in, with chains upon him, and seated where all +the curious eyes could stare at him; no less conspicuous was Injun Joe, +stolid as ever. There was another pause, and then the judge arrived and +the sheriff proclaimed the opening of the court. The usual whisperings +among the lawyers and gathering together of papers followed. These +details and accompanying delays worked up an atmosphere of preparation +that was as impressive as it was fascinating. + +Now a witness was called who testified that he found Muff Potter +washing in the brook, at an early hour of the morning that the murder +was discovered, and that he immediately sneaked away. After some +further questioning, counsel for the prosecution said: + +"Take the witness." + +The prisoner raised his eyes for a moment, but dropped them again when +his own counsel said: + +"I have no questions to ask him." + +The next witness proved the finding of the knife near the corpse. +Counsel for the prosecution said: + +"Take the witness." + +"I have no questions to ask him," Potter's lawyer replied. + +A third witness swore he had often seen the knife in Potter's +possession. + +"Take the witness." + +Counsel for Potter declined to question him. The faces of the audience +began to betray annoyance. Did this attorney mean to throw away his +client's life without an effort? + +Several witnesses deposed concerning Potter's guilty behavior when +brought to the scene of the murder. They were allowed to leave the +stand without being cross-questioned. + +Every detail of the damaging circumstances that occurred in the +graveyard upon that morning which all present remembered so well was +brought out by credible witnesses, but none of them were cross-examined +by Potter's lawyer. The perplexity and dissatisfaction of the house +expressed itself in murmurs and provoked a reproof from the bench. +Counsel for the prosecution now said: + +"By the oaths of citizens whose simple word is above suspicion, we +have fastened this awful crime, beyond all possibility of question, +upon the unhappy prisoner at the bar. We rest our case here." + +A groan escaped from poor Potter, and he put his face in his hands and +rocked his body softly to and fro, while a painful silence reigned in +the court-room. Many men were moved, and many women's compassion +testified itself in tears. Counsel for the defence rose and said: + +"Your honor, in our remarks at the opening of this trial, we +foreshadowed our purpose to prove that our client did this fearful deed +while under the influence of a blind and irresponsible delirium +produced by drink. We have changed our mind. We shall not offer that +plea." [Then to the clerk:] "Call Thomas Sawyer!" + +A puzzled amazement awoke in every face in the house, not even +excepting Potter's. Every eye fastened itself with wondering interest +upon Tom as he rose and took his place upon the stand. The boy looked +wild enough, for he was badly scared. The oath was administered. + +"Thomas Sawyer, where were you on the seventeenth of June, about the +hour of midnight?" + +Tom glanced at Injun Joe's iron face and his tongue failed him. The +audience listened breathless, but the words refused to come. After a +few moments, however, the boy got a little of his strength back, and +managed to put enough of it into his voice to make part of the house +hear: + +"In the graveyard!" + +"A little bit louder, please. Don't be afraid. You were--" + +"In the graveyard." + +A contemptuous smile flitted across Injun Joe's face. + +"Were you anywhere near Horse Williams' grave?" + +"Yes, sir." + +"Speak up--just a trifle louder. How near were you?" + +"Near as I am to you." + +"Were you hidden, or not?" + +"I was hid." + +"Where?" + +"Behind the elms that's on the edge of the grave." + +Injun Joe gave a barely perceptible start. + +"Any one with you?" + +"Yes, sir. I went there with--" + +"Wait--wait a moment. Never mind mentioning your companion's name. We +will produce him at the proper time. Did you carry anything there with +you." + +Tom hesitated and looked confused. + +"Speak out, my boy--don't be diffident. The truth is always +respectable. What did you take there?" + +"Only a--a--dead cat." + +There was a ripple of mirth, which the court checked. + +"We will produce the skeleton of that cat. Now, my boy, tell us +everything that occurred--tell it in your own way--don't skip anything, +and don't be afraid." + +Tom began--hesitatingly at first, but as he warmed to his subject his +words flowed more and more easily; in a little while every sound ceased +but his own voice; every eye fixed itself upon him; with parted lips +and bated breath the audience hung upon his words, taking no note of +time, rapt in the ghastly fascinations of the tale. The strain upon +pent emotion reached its climax when the boy said: + +"--and as the doctor fetched the board around and Muff Potter fell, +Injun Joe jumped with the knife and--" + +Crash! Quick as lightning the half-breed sprang for a window, tore his +way through all opposers, and was gone! + + + +CHAPTER XXIV + +TOM was a glittering hero once more--the pet of the old, the envy of +the young. His name even went into immortal print, for the village +paper magnified him. There were some that believed he would be +President, yet, if he escaped hanging. + +As usual, the fickle, unreasoning world took Muff Potter to its bosom +and fondled him as lavishly as it had abused him before. But that sort +of conduct is to the world's credit; therefore it is not well to find +fault with it. + +Tom's days were days of splendor and exultation to him, but his nights +were seasons of horror. Injun Joe infested all his dreams, and always +with doom in his eye. Hardly any temptation could persuade the boy to +stir abroad after nightfall. Poor Huck was in the same state of +wretchedness and terror, for Tom had told the whole story to the lawyer +the night before the great day of the trial, and Huck was sore afraid +that his share in the business might leak out, yet, notwithstanding +Injun Joe's flight had saved him the suffering of testifying in court. +The poor fellow had got the attorney to promise secrecy, but what of +that? Since Tom's harassed conscience had managed to drive him to the +lawyer's house by night and wring a dread tale from lips that had been +sealed with the dismalest and most formidable of oaths, Huck's +confidence in the human race was well-nigh obliterated. + +Daily Muff Potter's gratitude made Tom glad he had spoken; but nightly +he wished he had sealed up his tongue. + +Half the time Tom was afraid Injun Joe would never be captured; the +other half he was afraid he would be. He felt sure he never could draw +a safe breath again until that man was dead and he had seen the corpse. + +Rewards had been offered, the country had been scoured, but no Injun +Joe was found. One of those omniscient and awe-inspiring marvels, a +detective, came up from St. Louis, moused around, shook his head, +looked wise, and made that sort of astounding success which members of +that craft usually achieve. That is to say, he "found a clew." But you +can't hang a "clew" for murder, and so after that detective had got +through and gone home, Tom felt just as insecure as he was before. + +The slow days drifted on, and each left behind it a slightly lightened +weight of apprehension. + + + +CHAPTER XXV + +THERE comes a time in every rightly-constructed boy's life when he has +a raging desire to go somewhere and dig for hidden treasure. This +desire suddenly came upon Tom one day. He sallied out to find Joe +Harper, but failed of success. Next he sought Ben Rogers; he had gone +fishing. Presently he stumbled upon Huck Finn the Red-Handed. Huck +would answer. Tom took him to a private place and opened the matter to +him confidentially. Huck was willing. Huck was always willing to take a +hand in any enterprise that offered entertainment and required no +capital, for he had a troublesome superabundance of that sort of time +which is not money. "Where'll we dig?" said Huck. + +"Oh, most anywhere." + +"Why, is it hid all around?" + +"No, indeed it ain't. It's hid in mighty particular places, Huck +--sometimes on islands, sometimes in rotten chests under the end of a +limb of an old dead tree, just where the shadow falls at midnight; but +mostly under the floor in ha'nted houses." + +"Who hides it?" + +"Why, robbers, of course--who'd you reckon? Sunday-school +sup'rintendents?" + +"I don't know. If 'twas mine I wouldn't hide it; I'd spend it and have +a good time." + +"So would I. But robbers don't do that way. They always hide it and +leave it there." + +"Don't they come after it any more?" + +"No, they think they will, but they generally forget the marks, or +else they die. Anyway, it lays there a long time and gets rusty; and by +and by somebody finds an old yellow paper that tells how to find the +marks--a paper that's got to be ciphered over about a week because it's +mostly signs and hy'roglyphics." + +"Hyro--which?" + +"Hy'roglyphics--pictures and things, you know, that don't seem to mean +anything." + +"Have you got one of them papers, Tom?" + +"No." + +"Well then, how you going to find the marks?" + +"I don't want any marks. They always bury it under a ha'nted house or +on an island, or under a dead tree that's got one limb sticking out. +Well, we've tried Jackson's Island a little, and we can try it again +some time; and there's the old ha'nted house up the Still-House branch, +and there's lots of dead-limb trees--dead loads of 'em." + +"Is it under all of them?" + +"How you talk! No!" + +"Then how you going to know which one to go for?" + +"Go for all of 'em!" + +"Why, Tom, it'll take all summer." + +"Well, what of that? Suppose you find a brass pot with a hundred +dollars in it, all rusty and gray, or rotten chest full of di'monds. +How's that?" + +Huck's eyes glowed. + +"That's bully. Plenty bully enough for me. Just you gimme the hundred +dollars and I don't want no di'monds." + +"All right. But I bet you I ain't going to throw off on di'monds. Some +of 'em's worth twenty dollars apiece--there ain't any, hardly, but's +worth six bits or a dollar." + +"No! Is that so?" + +"Cert'nly--anybody'll tell you so. Hain't you ever seen one, Huck?" + +"Not as I remember." + +"Oh, kings have slathers of them." + +"Well, I don' know no kings, Tom." + +"I reckon you don't. But if you was to go to Europe you'd see a raft +of 'em hopping around." + +"Do they hop?" + +"Hop?--your granny! No!" + +"Well, what did you say they did, for?" + +"Shucks, I only meant you'd SEE 'em--not hopping, of course--what do +they want to hop for?--but I mean you'd just see 'em--scattered around, +you know, in a kind of a general way. Like that old humpbacked Richard." + +"Richard? What's his other name?" + +"He didn't have any other name. Kings don't have any but a given name." + +"No?" + +"But they don't." + +"Well, if they like it, Tom, all right; but I don't want to be a king +and have only just a given name, like a nigger. But say--where you +going to dig first?" + +"Well, I don't know. S'pose we tackle that old dead-limb tree on the +hill t'other side of Still-House branch?" + +"I'm agreed." + +So they got a crippled pick and a shovel, and set out on their +three-mile tramp. They arrived hot and panting, and threw themselves +down in the shade of a neighboring elm to rest and have a smoke. + +"I like this," said Tom. + +"So do I." + +"Say, Huck, if we find a treasure here, what you going to do with your +share?" + +"Well, I'll have pie and a glass of soda every day, and I'll go to +every circus that comes along. I bet I'll have a gay time." + +"Well, ain't you going to save any of it?" + +"Save it? What for?" + +"Why, so as to have something to live on, by and by." + +"Oh, that ain't any use. Pap would come back to thish-yer town some +day and get his claws on it if I didn't hurry up, and I tell you he'd +clean it out pretty quick. What you going to do with yourn, Tom?" + +"I'm going to buy a new drum, and a sure-'nough sword, and a red +necktie and a bull pup, and get married." + +"Married!" + +"That's it." + +"Tom, you--why, you ain't in your right mind." + +"Wait--you'll see." + +"Well, that's the foolishest thing you could do. Look at pap and my +mother. Fight! Why, they used to fight all the time. I remember, mighty +well." + +"That ain't anything. The girl I'm going to marry won't fight." + +"Tom, I reckon they're all alike. They'll all comb a body. Now you +better think 'bout this awhile. I tell you you better. What's the name +of the gal?" + +"It ain't a gal at all--it's a girl." + +"It's all the same, I reckon; some says gal, some says girl--both's +right, like enough. Anyway, what's her name, Tom?" + +"I'll tell you some time--not now." + +"All right--that'll do. Only if you get married I'll be more lonesomer +than ever." + +"No you won't. You'll come and live with me. Now stir out of this and +we'll go to digging." + +They worked and sweated for half an hour. No result. They toiled +another half-hour. Still no result. Huck said: + +"Do they always bury it as deep as this?" + +"Sometimes--not always. Not generally. I reckon we haven't got the +right place." + +So they chose a new spot and began again. The labor dragged a little, +but still they made progress. They pegged away in silence for some +time. Finally Huck leaned on his shovel, swabbed the beaded drops from +his brow with his sleeve, and said: + +"Where you going to dig next, after we get this one?" + +"I reckon maybe we'll tackle the old tree that's over yonder on +Cardiff Hill back of the widow's." + +"I reckon that'll be a good one. But won't the widow take it away from +us, Tom? It's on her land." + +"SHE take it away! Maybe she'd like to try it once. Whoever finds one +of these hid treasures, it belongs to him. It don't make any difference +whose land it's on." + +That was satisfactory. The work went on. By and by Huck said: + +"Blame it, we must be in the wrong place again. What do you think?" + +"It is mighty curious, Huck. I don't understand it. Sometimes witches +interfere. I reckon maybe that's what's the trouble now." + +"Shucks! Witches ain't got no power in the daytime." + +"Well, that's so. I didn't think of that. Oh, I know what the matter +is! What a blamed lot of fools we are! You got to find out where the +shadow of the limb falls at midnight, and that's where you dig!" + +"Then consound it, we've fooled away all this work for nothing. Now +hang it all, we got to come back in the night. It's an awful long way. +Can you get out?" + +"I bet I will. We've got to do it to-night, too, because if somebody +sees these holes they'll know in a minute what's here and they'll go +for it." + +"Well, I'll come around and maow to-night." + +"All right. Let's hide the tools in the bushes." + +The boys were there that night, about the appointed time. They sat in +the shadow waiting. It was a lonely place, and an hour made solemn by +old traditions. Spirits whispered in the rustling leaves, ghosts lurked +in the murky nooks, the deep baying of a hound floated up out of the +distance, an owl answered with his sepulchral note. The boys were +subdued by these solemnities, and talked little. By and by they judged +that twelve had come; they marked where the shadow fell, and began to +dig. Their hopes commenced to rise. Their interest grew stronger, and +their industry kept pace with it. The hole deepened and still deepened, +but every time their hearts jumped to hear the pick strike upon +something, they only suffered a new disappointment. It was only a stone +or a chunk. At last Tom said: + +"It ain't any use, Huck, we're wrong again." + +"Well, but we CAN'T be wrong. We spotted the shadder to a dot." + +"I know it, but then there's another thing." + +"What's that?". + +"Why, we only guessed at the time. Like enough it was too late or too +early." + +Huck dropped his shovel. + +"That's it," said he. "That's the very trouble. We got to give this +one up. We can't ever tell the right time, and besides this kind of +thing's too awful, here this time of night with witches and ghosts +a-fluttering around so. I feel as if something's behind me all the time; +and I'm afeard to turn around, becuz maybe there's others in front +a-waiting for a chance. I been creeping all over, ever since I got here." + +"Well, I've been pretty much so, too, Huck. They most always put in a +dead man when they bury a treasure under a tree, to look out for it." + +"Lordy!" + +"Yes, they do. I've always heard that." + +"Tom, I don't like to fool around much where there's dead people. A +body's bound to get into trouble with 'em, sure." + +"I don't like to stir 'em up, either. S'pose this one here was to +stick his skull out and say something!" + +"Don't Tom! It's awful." + +"Well, it just is. Huck, I don't feel comfortable a bit." + +"Say, Tom, let's give this place up, and try somewheres else." + +"All right, I reckon we better." + +"What'll it be?" + +Tom considered awhile; and then said: + +"The ha'nted house. That's it!" + +"Blame it, I don't like ha'nted houses, Tom. Why, they're a dern sight +worse'n dead people. Dead people might talk, maybe, but they don't come +sliding around in a shroud, when you ain't noticing, and peep over your +shoulder all of a sudden and grit their teeth, the way a ghost does. I +couldn't stand such a thing as that, Tom--nobody could." + +"Yes, but, Huck, ghosts don't travel around only at night. They won't +hender us from digging there in the daytime." + +"Well, that's so. But you know mighty well people don't go about that +ha'nted house in the day nor the night." + +"Well, that's mostly because they don't like to go where a man's been +murdered, anyway--but nothing's ever been seen around that house except +in the night--just some blue lights slipping by the windows--no regular +ghosts." + +"Well, where you see one of them blue lights flickering around, Tom, +you can bet there's a ghost mighty close behind it. It stands to +reason. Becuz you know that they don't anybody but ghosts use 'em." + +"Yes, that's so. But anyway they don't come around in the daytime, so +what's the use of our being afeard?" + +"Well, all right. We'll tackle the ha'nted house if you say so--but I +reckon it's taking chances." + +They had started down the hill by this time. There in the middle of +the moonlit valley below them stood the "ha'nted" house, utterly +isolated, its fences gone long ago, rank weeds smothering the very +doorsteps, the chimney crumbled to ruin, the window-sashes vacant, a +corner of the roof caved in. The boys gazed awhile, half expecting to +see a blue light flit past a window; then talking in a low tone, as +befitted the time and the circumstances, they struck far off to the +right, to give the haunted house a wide berth, and took their way +homeward through the woods that adorned the rearward side of Cardiff +Hill. + + + +CHAPTER XXVI + +ABOUT noon the next day the boys arrived at the dead tree; they had +come for their tools. Tom was impatient to go to the haunted house; +Huck was measurably so, also--but suddenly said: + +"Lookyhere, Tom, do you know what day it is?" + +Tom mentally ran over the days of the week, and then quickly lifted +his eyes with a startled look in them-- + +"My! I never once thought of it, Huck!" + +"Well, I didn't neither, but all at once it popped onto me that it was +Friday." + +"Blame it, a body can't be too careful, Huck. We might 'a' got into an +awful scrape, tackling such a thing on a Friday." + +"MIGHT! Better say we WOULD! There's some lucky days, maybe, but +Friday ain't." + +"Any fool knows that. I don't reckon YOU was the first that found it +out, Huck." + +"Well, I never said I was, did I? And Friday ain't all, neither. I had +a rotten bad dream last night--dreampt about rats." + +"No! Sure sign of trouble. Did they fight?" + +"No." + +"Well, that's good, Huck. When they don't fight it's only a sign that +there's trouble around, you know. All we got to do is to look mighty +sharp and keep out of it. We'll drop this thing for to-day, and play. +Do you know Robin Hood, Huck?" + +"No. Who's Robin Hood?" + +"Why, he was one of the greatest men that was ever in England--and the +best. He was a robber." + +"Cracky, I wisht I was. Who did he rob?" + +"Only sheriffs and bishops and rich people and kings, and such like. +But he never bothered the poor. He loved 'em. He always divided up with +'em perfectly square." + +"Well, he must 'a' been a brick." + +"I bet you he was, Huck. Oh, he was the noblest man that ever was. +They ain't any such men now, I can tell you. He could lick any man in +England, with one hand tied behind him; and he could take his yew bow +and plug a ten-cent piece every time, a mile and a half." + +"What's a YEW bow?" + +"I don't know. It's some kind of a bow, of course. And if he hit that +dime only on the edge he would set down and cry--and curse. But we'll +play Robin Hood--it's nobby fun. I'll learn you." + +"I'm agreed." + +So they played Robin Hood all the afternoon, now and then casting a +yearning eye down upon the haunted house and passing a remark about the +morrow's prospects and possibilities there. As the sun began to sink +into the west they took their way homeward athwart the long shadows of +the trees and soon were buried from sight in the forests of Cardiff +Hill. + +On Saturday, shortly after noon, the boys were at the dead tree again. +They had a smoke and a chat in the shade, and then dug a little in +their last hole, not with great hope, but merely because Tom said there +were so many cases where people had given up a treasure after getting +down within six inches of it, and then somebody else had come along and +turned it up with a single thrust of a shovel. The thing failed this +time, however, so the boys shouldered their tools and went away feeling +that they had not trifled with fortune, but had fulfilled all the +requirements that belong to the business of treasure-hunting. + +When they reached the haunted house there was something so weird and +grisly about the dead silence that reigned there under the baking sun, +and something so depressing about the loneliness and desolation of the +place, that they were afraid, for a moment, to venture in. Then they +crept to the door and took a trembling peep. They saw a weed-grown, +floorless room, unplastered, an ancient fireplace, vacant windows, a +ruinous staircase; and here, there, and everywhere hung ragged and +abandoned cobwebs. They presently entered, softly, with quickened +pulses, talking in whispers, ears alert to catch the slightest sound, +and muscles tense and ready for instant retreat. + +In a little while familiarity modified their fears and they gave the +place a critical and interested examination, rather admiring their own +boldness, and wondering at it, too. Next they wanted to look up-stairs. +This was something like cutting off retreat, but they got to daring +each other, and of course there could be but one result--they threw +their tools into a corner and made the ascent. Up there were the same +signs of decay. In one corner they found a closet that promised +mystery, but the promise was a fraud--there was nothing in it. Their +courage was up now and well in hand. They were about to go down and +begin work when-- + +"Sh!" said Tom. + +"What is it?" whispered Huck, blanching with fright. + +"Sh!... There!... Hear it?" + +"Yes!... Oh, my! Let's run!" + +"Keep still! Don't you budge! They're coming right toward the door." + +The boys stretched themselves upon the floor with their eyes to +knot-holes in the planking, and lay waiting, in a misery of fear. + +"They've stopped.... No--coming.... Here they are. Don't whisper +another word, Huck. My goodness, I wish I was out of this!" + +Two men entered. Each boy said to himself: "There's the old deaf and +dumb Spaniard that's been about town once or twice lately--never saw +t'other man before." + +"T'other" was a ragged, unkempt creature, with nothing very pleasant +in his face. The Spaniard was wrapped in a serape; he had bushy white +whiskers; long white hair flowed from under his sombrero, and he wore +green goggles. When they came in, "t'other" was talking in a low voice; +they sat down on the ground, facing the door, with their backs to the +wall, and the speaker continued his remarks. His manner became less +guarded and his words more distinct as he proceeded: + +"No," said he, "I've thought it all over, and I don't like it. It's +dangerous." + +"Dangerous!" grunted the "deaf and dumb" Spaniard--to the vast +surprise of the boys. "Milksop!" + +This voice made the boys gasp and quake. It was Injun Joe's! There was +silence for some time. Then Joe said: + +"What's any more dangerous than that job up yonder--but nothing's come +of it." + +"That's different. Away up the river so, and not another house about. +'Twon't ever be known that we tried, anyway, long as we didn't succeed." + +"Well, what's more dangerous than coming here in the daytime!--anybody +would suspicion us that saw us." + +"I know that. But there warn't any other place as handy after that +fool of a job. I want to quit this shanty. I wanted to yesterday, only +it warn't any use trying to stir out of here, with those infernal boys +playing over there on the hill right in full view." + +"Those infernal boys" quaked again under the inspiration of this +remark, and thought how lucky it was that they had remembered it was +Friday and concluded to wait a day. They wished in their hearts they +had waited a year. + +The two men got out some food and made a luncheon. After a long and +thoughtful silence, Injun Joe said: + +"Look here, lad--you go back up the river where you belong. Wait there +till you hear from me. I'll take the chances on dropping into this town +just once more, for a look. We'll do that 'dangerous' job after I've +spied around a little and think things look well for it. Then for +Texas! We'll leg it together!" + +This was satisfactory. Both men presently fell to yawning, and Injun +Joe said: + +"I'm dead for sleep! It's your turn to watch." + +He curled down in the weeds and soon began to snore. His comrade +stirred him once or twice and he became quiet. Presently the watcher +began to nod; his head drooped lower and lower, both men began to snore +now. + +The boys drew a long, grateful breath. Tom whispered: + +"Now's our chance--come!" + +Huck said: + +"I can't--I'd die if they was to wake." + +Tom urged--Huck held back. At last Tom rose slowly and softly, and +started alone. But the first step he made wrung such a hideous creak +from the crazy floor that he sank down almost dead with fright. He +never made a second attempt. The boys lay there counting the dragging +moments till it seemed to them that time must be done and eternity +growing gray; and then they were grateful to note that at last the sun +was setting. + +Now one snore ceased. Injun Joe sat up, stared around--smiled grimly +upon his comrade, whose head was drooping upon his knees--stirred him +up with his foot and said: + +"Here! YOU'RE a watchman, ain't you! All right, though--nothing's +happened." + +"My! have I been asleep?" + +"Oh, partly, partly. Nearly time for us to be moving, pard. What'll we +do with what little swag we've got left?" + +"I don't know--leave it here as we've always done, I reckon. No use to +take it away till we start south. Six hundred and fifty in silver's +something to carry." + +"Well--all right--it won't matter to come here once more." + +"No--but I'd say come in the night as we used to do--it's better." + +"Yes: but look here; it may be a good while before I get the right +chance at that job; accidents might happen; 'tain't in such a very good +place; we'll just regularly bury it--and bury it deep." + +"Good idea," said the comrade, who walked across the room, knelt down, +raised one of the rearward hearth-stones and took out a bag that +jingled pleasantly. He subtracted from it twenty or thirty dollars for +himself and as much for Injun Joe, and passed the bag to the latter, +who was on his knees in the corner, now, digging with his bowie-knife. + +The boys forgot all their fears, all their miseries in an instant. +With gloating eyes they watched every movement. Luck!--the splendor of +it was beyond all imagination! Six hundred dollars was money enough to +make half a dozen boys rich! Here was treasure-hunting under the +happiest auspices--there would not be any bothersome uncertainty as to +where to dig. They nudged each other every moment--eloquent nudges and +easily understood, for they simply meant--"Oh, but ain't you glad NOW +we're here!" + +Joe's knife struck upon something. + +"Hello!" said he. + +"What is it?" said his comrade. + +"Half-rotten plank--no, it's a box, I believe. Here--bear a hand and +we'll see what it's here for. Never mind, I've broke a hole." + +He reached his hand in and drew it out-- + +"Man, it's money!" + +The two men examined the handful of coins. They were gold. The boys +above were as excited as themselves, and as delighted. + +Joe's comrade said: + +"We'll make quick work of this. There's an old rusty pick over amongst +the weeds in the corner the other side of the fireplace--I saw it a +minute ago." + +He ran and brought the boys' pick and shovel. Injun Joe took the pick, +looked it over critically, shook his head, muttered something to +himself, and then began to use it. The box was soon unearthed. It was +not very large; it was iron bound and had been very strong before the +slow years had injured it. The men contemplated the treasure awhile in +blissful silence. + +"Pard, there's thousands of dollars here," said Injun Joe. + +"'Twas always said that Murrel's gang used to be around here one +summer," the stranger observed. + +"I know it," said Injun Joe; "and this looks like it, I should say." + +"Now you won't need to do that job." + +The half-breed frowned. Said he: + +"You don't know me. Least you don't know all about that thing. 'Tain't +robbery altogether--it's REVENGE!" and a wicked light flamed in his +eyes. "I'll need your help in it. When it's finished--then Texas. Go +home to your Nance and your kids, and stand by till you hear from me." + +"Well--if you say so; what'll we do with this--bury it again?" + +"Yes. [Ravishing delight overhead.] NO! by the great Sachem, no! +[Profound distress overhead.] I'd nearly forgot. That pick had fresh +earth on it! [The boys were sick with terror in a moment.] What +business has a pick and a shovel here? What business with fresh earth +on them? Who brought them here--and where are they gone? Have you heard +anybody?--seen anybody? What! bury it again and leave them to come and +see the ground disturbed? Not exactly--not exactly. We'll take it to my +den." + +"Why, of course! Might have thought of that before. You mean Number +One?" + +"No--Number Two--under the cross. The other place is bad--too common." + +"All right. It's nearly dark enough to start." + +Injun Joe got up and went about from window to window cautiously +peeping out. Presently he said: + +"Who could have brought those tools here? Do you reckon they can be +up-stairs?" + +The boys' breath forsook them. Injun Joe put his hand on his knife, +halted a moment, undecided, and then turned toward the stairway. The +boys thought of the closet, but their strength was gone. The steps came +creaking up the stairs--the intolerable distress of the situation woke +the stricken resolution of the lads--they were about to spring for the +closet, when there was a crash of rotten timbers and Injun Joe landed +on the ground amid the debris of the ruined stairway. He gathered +himself up cursing, and his comrade said: + +"Now what's the use of all that? If it's anybody, and they're up +there, let them STAY there--who cares? If they want to jump down, now, +and get into trouble, who objects? It will be dark in fifteen minutes +--and then let them follow us if they want to. I'm willing. In my +opinion, whoever hove those things in here caught a sight of us and +took us for ghosts or devils or something. I'll bet they're running +yet." + +Joe grumbled awhile; then he agreed with his friend that what daylight +was left ought to be economized in getting things ready for leaving. +Shortly afterward they slipped out of the house in the deepening +twilight, and moved toward the river with their precious box. + +Tom and Huck rose up, weak but vastly relieved, and stared after them +through the chinks between the logs of the house. Follow? Not they. +They were content to reach ground again without broken necks, and take +the townward track over the hill. They did not talk much. They were too +much absorbed in hating themselves--hating the ill luck that made them +take the spade and the pick there. But for that, Injun Joe never would +have suspected. He would have hidden the silver with the gold to wait +there till his "revenge" was satisfied, and then he would have had the +misfortune to find that money turn up missing. Bitter, bitter luck that +the tools were ever brought there! + +They resolved to keep a lookout for that Spaniard when he should come +to town spying out for chances to do his revengeful job, and follow him +to "Number Two," wherever that might be. Then a ghastly thought +occurred to Tom. + +"Revenge? What if he means US, Huck!" + +"Oh, don't!" said Huck, nearly fainting. + +They talked it all over, and as they entered town they agreed to +believe that he might possibly mean somebody else--at least that he +might at least mean nobody but Tom, since only Tom had testified. + +Very, very small comfort it was to Tom to be alone in danger! Company +would be a palpable improvement, he thought. + + + +CHAPTER XXVII + +THE adventure of the day mightily tormented Tom's dreams that night. +Four times he had his hands on that rich treasure and four times it +wasted to nothingness in his fingers as sleep forsook him and +wakefulness brought back the hard reality of his misfortune. As he lay +in the early morning recalling the incidents of his great adventure, he +noticed that they seemed curiously subdued and far away--somewhat as if +they had happened in another world, or in a time long gone by. Then it +occurred to him that the great adventure itself must be a dream! There +was one very strong argument in favor of this idea--namely, that the +quantity of coin he had seen was too vast to be real. He had never seen +as much as fifty dollars in one mass before, and he was like all boys +of his age and station in life, in that he imagined that all references +to "hundreds" and "thousands" were mere fanciful forms of speech, and +that no such sums really existed in the world. He never had supposed +for a moment that so large a sum as a hundred dollars was to be found +in actual money in any one's possession. If his notions of hidden +treasure had been analyzed, they would have been found to consist of a +handful of real dimes and a bushel of vague, splendid, ungraspable +dollars. + +But the incidents of his adventure grew sensibly sharper and clearer +under the attrition of thinking them over, and so he presently found +himself leaning to the impression that the thing might not have been a +dream, after all. This uncertainty must be swept away. He would snatch +a hurried breakfast and go and find Huck. Huck was sitting on the +gunwale of a flatboat, listlessly dangling his feet in the water and +looking very melancholy. Tom concluded to let Huck lead up to the +subject. If he did not do it, then the adventure would be proved to +have been only a dream. + +"Hello, Huck!" + +"Hello, yourself." + +Silence, for a minute. + +"Tom, if we'd 'a' left the blame tools at the dead tree, we'd 'a' got +the money. Oh, ain't it awful!" + +"'Tain't a dream, then, 'tain't a dream! Somehow I most wish it was. +Dog'd if I don't, Huck." + +"What ain't a dream?" + +"Oh, that thing yesterday. I been half thinking it was." + +"Dream! If them stairs hadn't broke down you'd 'a' seen how much dream +it was! I've had dreams enough all night--with that patch-eyed Spanish +devil going for me all through 'em--rot him!" + +"No, not rot him. FIND him! Track the money!" + +"Tom, we'll never find him. A feller don't have only one chance for +such a pile--and that one's lost. I'd feel mighty shaky if I was to see +him, anyway." + +"Well, so'd I; but I'd like to see him, anyway--and track him out--to +his Number Two." + +"Number Two--yes, that's it. I been thinking 'bout that. But I can't +make nothing out of it. What do you reckon it is?" + +"I dono. It's too deep. Say, Huck--maybe it's the number of a house!" + +"Goody!... No, Tom, that ain't it. If it is, it ain't in this +one-horse town. They ain't no numbers here." + +"Well, that's so. Lemme think a minute. Here--it's the number of a +room--in a tavern, you know!" + +"Oh, that's the trick! They ain't only two taverns. We can find out +quick." + +"You stay here, Huck, till I come." + +Tom was off at once. He did not care to have Huck's company in public +places. He was gone half an hour. He found that in the best tavern, No. +2 had long been occupied by a young lawyer, and was still so occupied. +In the less ostentatious house, No. 2 was a mystery. The +tavern-keeper's young son said it was kept locked all the time, and he +never saw anybody go into it or come out of it except at night; he did +not know any particular reason for this state of things; had had some +little curiosity, but it was rather feeble; had made the most of the +mystery by entertaining himself with the idea that that room was +"ha'nted"; had noticed that there was a light in there the night before. + +"That's what I've found out, Huck. I reckon that's the very No. 2 +we're after." + +"I reckon it is, Tom. Now what you going to do?" + +"Lemme think." + +Tom thought a long time. Then he said: + +"I'll tell you. The back door of that No. 2 is the door that comes out +into that little close alley between the tavern and the old rattle trap +of a brick store. Now you get hold of all the door-keys you can find, +and I'll nip all of auntie's, and the first dark night we'll go there +and try 'em. And mind you, keep a lookout for Injun Joe, because he +said he was going to drop into town and spy around once more for a +chance to get his revenge. If you see him, you just follow him; and if +he don't go to that No. 2, that ain't the place." + +"Lordy, I don't want to foller him by myself!" + +"Why, it'll be night, sure. He mightn't ever see you--and if he did, +maybe he'd never think anything." + +"Well, if it's pretty dark I reckon I'll track him. I dono--I dono. +I'll try." + +"You bet I'll follow him, if it's dark, Huck. Why, he might 'a' found +out he couldn't get his revenge, and be going right after that money." + +"It's so, Tom, it's so. I'll foller him; I will, by jingoes!" + +"Now you're TALKING! Don't you ever weaken, Huck, and I won't." + + + +CHAPTER XXVIII + +THAT night Tom and Huck were ready for their adventure. They hung +about the neighborhood of the tavern until after nine, one watching the +alley at a distance and the other the tavern door. Nobody entered the +alley or left it; nobody resembling the Spaniard entered or left the +tavern door. The night promised to be a fair one; so Tom went home with +the understanding that if a considerable degree of darkness came on, +Huck was to come and "maow," whereupon he would slip out and try the +keys. But the night remained clear, and Huck closed his watch and +retired to bed in an empty sugar hogshead about twelve. + +Tuesday the boys had the same ill luck. Also Wednesday. But Thursday +night promised better. Tom slipped out in good season with his aunt's +old tin lantern, and a large towel to blindfold it with. He hid the +lantern in Huck's sugar hogshead and the watch began. An hour before +midnight the tavern closed up and its lights (the only ones +thereabouts) were put out. No Spaniard had been seen. Nobody had +entered or left the alley. Everything was auspicious. The blackness of +darkness reigned, the perfect stillness was interrupted only by +occasional mutterings of distant thunder. + +Tom got his lantern, lit it in the hogshead, wrapped it closely in the +towel, and the two adventurers crept in the gloom toward the tavern. +Huck stood sentry and Tom felt his way into the alley. Then there was a +season of waiting anxiety that weighed upon Huck's spirits like a +mountain. He began to wish he could see a flash from the lantern--it +would frighten him, but it would at least tell him that Tom was alive +yet. It seemed hours since Tom had disappeared. Surely he must have +fainted; maybe he was dead; maybe his heart had burst under terror and +excitement. In his uneasiness Huck found himself drawing closer and +closer to the alley; fearing all sorts of dreadful things, and +momentarily expecting some catastrophe to happen that would take away +his breath. There was not much to take away, for he seemed only able to +inhale it by thimblefuls, and his heart would soon wear itself out, the +way it was beating. Suddenly there was a flash of light and Tom came +tearing by him: "Run!" said he; "run, for your life!" + +He needn't have repeated it; once was enough; Huck was making thirty +or forty miles an hour before the repetition was uttered. The boys +never stopped till they reached the shed of a deserted slaughter-house +at the lower end of the village. Just as they got within its shelter +the storm burst and the rain poured down. As soon as Tom got his breath +he said: + +"Huck, it was awful! I tried two of the keys, just as soft as I could; +but they seemed to make such a power of racket that I couldn't hardly +get my breath I was so scared. They wouldn't turn in the lock, either. +Well, without noticing what I was doing, I took hold of the knob, and +open comes the door! It warn't locked! I hopped in, and shook off the +towel, and, GREAT CAESAR'S GHOST!" + +"What!--what'd you see, Tom?" + +"Huck, I most stepped onto Injun Joe's hand!" + +"No!" + +"Yes! He was lying there, sound asleep on the floor, with his old +patch on his eye and his arms spread out." + +"Lordy, what did you do? Did he wake up?" + +"No, never budged. Drunk, I reckon. I just grabbed that towel and +started!" + +"I'd never 'a' thought of the towel, I bet!" + +"Well, I would. My aunt would make me mighty sick if I lost it." + +"Say, Tom, did you see that box?" + +"Huck, I didn't wait to look around. I didn't see the box, I didn't +see the cross. I didn't see anything but a bottle and a tin cup on the +floor by Injun Joe; yes, I saw two barrels and lots more bottles in the +room. Don't you see, now, what's the matter with that ha'nted room?" + +"How?" + +"Why, it's ha'nted with whiskey! Maybe ALL the Temperance Taverns have +got a ha'nted room, hey, Huck?" + +"Well, I reckon maybe that's so. Who'd 'a' thought such a thing? But +say, Tom, now's a mighty good time to get that box, if Injun Joe's +drunk." + +"It is, that! You try it!" + +Huck shuddered. + +"Well, no--I reckon not." + +"And I reckon not, Huck. Only one bottle alongside of Injun Joe ain't +enough. If there'd been three, he'd be drunk enough and I'd do it." + +There was a long pause for reflection, and then Tom said: + +"Lookyhere, Huck, less not try that thing any more till we know Injun +Joe's not in there. It's too scary. Now, if we watch every night, we'll +be dead sure to see him go out, some time or other, and then we'll +snatch that box quicker'n lightning." + +"Well, I'm agreed. I'll watch the whole night long, and I'll do it +every night, too, if you'll do the other part of the job." + +"All right, I will. All you got to do is to trot up Hooper Street a +block and maow--and if I'm asleep, you throw some gravel at the window +and that'll fetch me." + +"Agreed, and good as wheat!" + +"Now, Huck, the storm's over, and I'll go home. It'll begin to be +daylight in a couple of hours. You go back and watch that long, will +you?" + +"I said I would, Tom, and I will. I'll ha'nt that tavern every night +for a year! I'll sleep all day and I'll stand watch all night." + +"That's all right. Now, where you going to sleep?" + +"In Ben Rogers' hayloft. He lets me, and so does his pap's nigger man, +Uncle Jake. I tote water for Uncle Jake whenever he wants me to, and +any time I ask him he gives me a little something to eat if he can +spare it. That's a mighty good nigger, Tom. He likes me, becuz I don't +ever act as if I was above him. Sometime I've set right down and eat +WITH him. But you needn't tell that. A body's got to do things when +he's awful hungry he wouldn't want to do as a steady thing." + +"Well, if I don't want you in the daytime, I'll let you sleep. I won't +come bothering around. Any time you see something's up, in the night, +just skip right around and maow." + + + +CHAPTER XXIX + +THE first thing Tom heard on Friday morning was a glad piece of news +--Judge Thatcher's family had come back to town the night before. Both +Injun Joe and the treasure sunk into secondary importance for a moment, +and Becky took the chief place in the boy's interest. He saw her and +they had an exhausting good time playing "hi-spy" and "gully-keeper" +with a crowd of their school-mates. The day was completed and crowned +in a peculiarly satisfactory way: Becky teased her mother to appoint +the next day for the long-promised and long-delayed picnic, and she +consented. The child's delight was boundless; and Tom's not more +moderate. The invitations were sent out before sunset, and straightway +the young folks of the village were thrown into a fever of preparation +and pleasurable anticipation. Tom's excitement enabled him to keep +awake until a pretty late hour, and he had good hopes of hearing Huck's +"maow," and of having his treasure to astonish Becky and the picnickers +with, next day; but he was disappointed. No signal came that night. + +Morning came, eventually, and by ten or eleven o'clock a giddy and +rollicking company were gathered at Judge Thatcher's, and everything +was ready for a start. It was not the custom for elderly people to mar +the picnics with their presence. The children were considered safe +enough under the wings of a few young ladies of eighteen and a few +young gentlemen of twenty-three or thereabouts. The old steam ferryboat +was chartered for the occasion; presently the gay throng filed up the +main street laden with provision-baskets. Sid was sick and had to miss +the fun; Mary remained at home to entertain him. The last thing Mrs. +Thatcher said to Becky, was: + +"You'll not get back till late. Perhaps you'd better stay all night +with some of the girls that live near the ferry-landing, child." + +"Then I'll stay with Susy Harper, mamma." + +"Very well. And mind and behave yourself and don't be any trouble." + +Presently, as they tripped along, Tom said to Becky: + +"Say--I'll tell you what we'll do. 'Stead of going to Joe Harper's +we'll climb right up the hill and stop at the Widow Douglas'. She'll +have ice-cream! She has it most every day--dead loads of it. And she'll +be awful glad to have us." + +"Oh, that will be fun!" + +Then Becky reflected a moment and said: + +"But what will mamma say?" + +"How'll she ever know?" + +The girl turned the idea over in her mind, and said reluctantly: + +"I reckon it's wrong--but--" + +"But shucks! Your mother won't know, and so what's the harm? All she +wants is that you'll be safe; and I bet you she'd 'a' said go there if +she'd 'a' thought of it. I know she would!" + +The Widow Douglas' splendid hospitality was a tempting bait. It and +Tom's persuasions presently carried the day. So it was decided to say +nothing anybody about the night's programme. Presently it occurred to +Tom that maybe Huck might come this very night and give the signal. The +thought took a deal of the spirit out of his anticipations. Still he +could not bear to give up the fun at Widow Douglas'. And why should he +give it up, he reasoned--the signal did not come the night before, so +why should it be any more likely to come to-night? The sure fun of the +evening outweighed the uncertain treasure; and, boy-like, he determined +to yield to the stronger inclination and not allow himself to think of +the box of money another time that day. + +Three miles below town the ferryboat stopped at the mouth of a woody +hollow and tied up. The crowd swarmed ashore and soon the forest +distances and craggy heights echoed far and near with shoutings and +laughter. All the different ways of getting hot and tired were gone +through with, and by-and-by the rovers straggled back to camp fortified +with responsible appetites, and then the destruction of the good things +began. After the feast there was a refreshing season of rest and chat +in the shade of spreading oaks. By-and-by somebody shouted: + +"Who's ready for the cave?" + +Everybody was. Bundles of candles were procured, and straightway there +was a general scamper up the hill. The mouth of the cave was up the +hillside--an opening shaped like a letter A. Its massive oaken door +stood unbarred. Within was a small chamber, chilly as an ice-house, and +walled by Nature with solid limestone that was dewy with a cold sweat. +It was romantic and mysterious to stand here in the deep gloom and look +out upon the green valley shining in the sun. But the impressiveness of +the situation quickly wore off, and the romping began again. The moment +a candle was lighted there was a general rush upon the owner of it; a +struggle and a gallant defence followed, but the candle was soon +knocked down or blown out, and then there was a glad clamor of laughter +and a new chase. But all things have an end. By-and-by the procession +went filing down the steep descent of the main avenue, the flickering +rank of lights dimly revealing the lofty walls of rock almost to their +point of junction sixty feet overhead. This main avenue was not more +than eight or ten feet wide. Every few steps other lofty and still +narrower crevices branched from it on either hand--for McDougal's cave +was but a vast labyrinth of crooked aisles that ran into each other and +out again and led nowhere. It was said that one might wander days and +nights together through its intricate tangle of rifts and chasms, and +never find the end of the cave; and that he might go down, and down, +and still down, into the earth, and it was just the same--labyrinth +under labyrinth, and no end to any of them. No man "knew" the cave. +That was an impossible thing. Most of the young men knew a portion of +it, and it was not customary to venture much beyond this known portion. +Tom Sawyer knew as much of the cave as any one. + +The procession moved along the main avenue some three-quarters of a +mile, and then groups and couples began to slip aside into branch +avenues, fly along the dismal corridors, and take each other by +surprise at points where the corridors joined again. Parties were able +to elude each other for the space of half an hour without going beyond +the "known" ground. + +By-and-by, one group after another came straggling back to the mouth +of the cave, panting, hilarious, smeared from head to foot with tallow +drippings, daubed with clay, and entirely delighted with the success of +the day. Then they were astonished to find that they had been taking no +note of time and that night was about at hand. The clanging bell had +been calling for half an hour. However, this sort of close to the day's +adventures was romantic and therefore satisfactory. When the ferryboat +with her wild freight pushed into the stream, nobody cared sixpence for +the wasted time but the captain of the craft. + +Huck was already upon his watch when the ferryboat's lights went +glinting past the wharf. He heard no noise on board, for the young +people were as subdued and still as people usually are who are nearly +tired to death. He wondered what boat it was, and why she did not stop +at the wharf--and then he dropped her out of his mind and put his +attention upon his business. The night was growing cloudy and dark. Ten +o'clock came, and the noise of vehicles ceased, scattered lights began +to wink out, all straggling foot-passengers disappeared, the village +betook itself to its slumbers and left the small watcher alone with the +silence and the ghosts. Eleven o'clock came, and the tavern lights were +put out; darkness everywhere, now. Huck waited what seemed a weary long +time, but nothing happened. His faith was weakening. Was there any use? +Was there really any use? Why not give it up and turn in? + +A noise fell upon his ear. He was all attention in an instant. The +alley door closed softly. He sprang to the corner of the brick store. +The next moment two men brushed by him, and one seemed to have +something under his arm. It must be that box! So they were going to +remove the treasure. Why call Tom now? It would be absurd--the men +would get away with the box and never be found again. No, he would +stick to their wake and follow them; he would trust to the darkness for +security from discovery. So communing with himself, Huck stepped out +and glided along behind the men, cat-like, with bare feet, allowing +them to keep just far enough ahead not to be invisible. + +They moved up the river street three blocks, then turned to the left +up a cross-street. They went straight ahead, then, until they came to +the path that led up Cardiff Hill; this they took. They passed by the +old Welshman's house, half-way up the hill, without hesitating, and +still climbed upward. Good, thought Huck, they will bury it in the old +quarry. But they never stopped at the quarry. They passed on, up the +summit. They plunged into the narrow path between the tall sumach +bushes, and were at once hidden in the gloom. Huck closed up and +shortened his distance, now, for they would never be able to see him. +He trotted along awhile; then slackened his pace, fearing he was +gaining too fast; moved on a piece, then stopped altogether; listened; +no sound; none, save that he seemed to hear the beating of his own +heart. The hooting of an owl came over the hill--ominous sound! But no +footsteps. Heavens, was everything lost! He was about to spring with +winged feet, when a man cleared his throat not four feet from him! +Huck's heart shot into his throat, but he swallowed it again; and then +he stood there shaking as if a dozen agues had taken charge of him at +once, and so weak that he thought he must surely fall to the ground. He +knew where he was. He knew he was within five steps of the stile +leading into Widow Douglas' grounds. Very well, he thought, let them +bury it there; it won't be hard to find. + +Now there was a voice--a very low voice--Injun Joe's: + +"Damn her, maybe she's got company--there's lights, late as it is." + +"I can't see any." + +This was that stranger's voice--the stranger of the haunted house. A +deadly chill went to Huck's heart--this, then, was the "revenge" job! +His thought was, to fly. Then he remembered that the Widow Douglas had +been kind to him more than once, and maybe these men were going to +murder her. He wished he dared venture to warn her; but he knew he +didn't dare--they might come and catch him. He thought all this and +more in the moment that elapsed between the stranger's remark and Injun +Joe's next--which was-- + +"Because the bush is in your way. Now--this way--now you see, don't +you?" + +"Yes. Well, there IS company there, I reckon. Better give it up." + +"Give it up, and I just leaving this country forever! Give it up and +maybe never have another chance. I tell you again, as I've told you +before, I don't care for her swag--you may have it. But her husband was +rough on me--many times he was rough on me--and mainly he was the +justice of the peace that jugged me for a vagrant. And that ain't all. +It ain't a millionth part of it! He had me HORSEWHIPPED!--horsewhipped +in front of the jail, like a nigger!--with all the town looking on! +HORSEWHIPPED!--do you understand? He took advantage of me and died. But +I'll take it out of HER." + +"Oh, don't kill her! Don't do that!" + +"Kill? Who said anything about killing? I would kill HIM if he was +here; but not her. When you want to get revenge on a woman you don't +kill her--bosh! you go for her looks. You slit her nostrils--you notch +her ears like a sow!" + +"By God, that's--" + +"Keep your opinion to yourself! It will be safest for you. I'll tie +her to the bed. If she bleeds to death, is that my fault? I'll not cry, +if she does. My friend, you'll help me in this thing--for MY sake +--that's why you're here--I mightn't be able alone. If you flinch, I'll +kill you. Do you understand that? And if I have to kill you, I'll kill +her--and then I reckon nobody'll ever know much about who done this +business." + +"Well, if it's got to be done, let's get at it. The quicker the +better--I'm all in a shiver." + +"Do it NOW? And company there? Look here--I'll get suspicious of you, +first thing you know. No--we'll wait till the lights are out--there's +no hurry." + +Huck felt that a silence was going to ensue--a thing still more awful +than any amount of murderous talk; so he held his breath and stepped +gingerly back; planted his foot carefully and firmly, after balancing, +one-legged, in a precarious way and almost toppling over, first on one +side and then on the other. He took another step back, with the same +elaboration and the same risks; then another and another, and--a twig +snapped under his foot! His breath stopped and he listened. There was +no sound--the stillness was perfect. His gratitude was measureless. Now +he turned in his tracks, between the walls of sumach bushes--turned +himself as carefully as if he were a ship--and then stepped quickly but +cautiously along. When he emerged at the quarry he felt secure, and so +he picked up his nimble heels and flew. Down, down he sped, till he +reached the Welshman's. He banged at the door, and presently the heads +of the old man and his two stalwart sons were thrust from windows. + +"What's the row there? Who's banging? What do you want?" + +"Let me in--quick! I'll tell everything." + +"Why, who are you?" + +"Huckleberry Finn--quick, let me in!" + +"Huckleberry Finn, indeed! It ain't a name to open many doors, I +judge! But let him in, lads, and let's see what's the trouble." + +"Please don't ever tell I told you," were Huck's first words when he +got in. "Please don't--I'd be killed, sure--but the widow's been good +friends to me sometimes, and I want to tell--I WILL tell if you'll +promise you won't ever say it was me." + +"By George, he HAS got something to tell, or he wouldn't act so!" +exclaimed the old man; "out with it and nobody here'll ever tell, lad." + +Three minutes later the old man and his sons, well armed, were up the +hill, and just entering the sumach path on tiptoe, their weapons in +their hands. Huck accompanied them no further. He hid behind a great +bowlder and fell to listening. There was a lagging, anxious silence, +and then all of a sudden there was an explosion of firearms and a cry. + +Huck waited for no particulars. He sprang away and sped down the hill +as fast as his legs could carry him. + + + +CHAPTER XXX + +AS the earliest suspicion of dawn appeared on Sunday morning, Huck +came groping up the hill and rapped gently at the old Welshman's door. +The inmates were asleep, but it was a sleep that was set on a +hair-trigger, on account of the exciting episode of the night. A call +came from a window: + +"Who's there!" + +Huck's scared voice answered in a low tone: + +"Please let me in! It's only Huck Finn!" + +"It's a name that can open this door night or day, lad!--and welcome!" + +These were strange words to the vagabond boy's ears, and the +pleasantest he had ever heard. He could not recollect that the closing +word had ever been applied in his case before. The door was quickly +unlocked, and he entered. Huck was given a seat and the old man and his +brace of tall sons speedily dressed themselves. + +"Now, my boy, I hope you're good and hungry, because breakfast will be +ready as soon as the sun's up, and we'll have a piping hot one, too +--make yourself easy about that! I and the boys hoped you'd turn up and +stop here last night." + +"I was awful scared," said Huck, "and I run. I took out when the +pistols went off, and I didn't stop for three mile. I've come now becuz +I wanted to know about it, you know; and I come before daylight becuz I +didn't want to run across them devils, even if they was dead." + +"Well, poor chap, you do look as if you'd had a hard night of it--but +there's a bed here for you when you've had your breakfast. No, they +ain't dead, lad--we are sorry enough for that. You see we knew right +where to put our hands on them, by your description; so we crept along +on tiptoe till we got within fifteen feet of them--dark as a cellar +that sumach path was--and just then I found I was going to sneeze. It +was the meanest kind of luck! I tried to keep it back, but no use +--'twas bound to come, and it did come! I was in the lead with my pistol +raised, and when the sneeze started those scoundrels a-rustling to get +out of the path, I sung out, 'Fire boys!' and blazed away at the place +where the rustling was. So did the boys. But they were off in a jiffy, +those villains, and we after them, down through the woods. I judge we +never touched them. They fired a shot apiece as they started, but their +bullets whizzed by and didn't do us any harm. As soon as we lost the +sound of their feet we quit chasing, and went down and stirred up the +constables. They got a posse together, and went off to guard the river +bank, and as soon as it is light the sheriff and a gang are going to +beat up the woods. My boys will be with them presently. I wish we had +some sort of description of those rascals--'twould help a good deal. +But you couldn't see what they were like, in the dark, lad, I suppose?" + +"Oh yes; I saw them down-town and follered them." + +"Splendid! Describe them--describe them, my boy!" + +"One's the old deaf and dumb Spaniard that's ben around here once or +twice, and t'other's a mean-looking, ragged--" + +"That's enough, lad, we know the men! Happened on them in the woods +back of the widow's one day, and they slunk away. Off with you, boys, +and tell the sheriff--get your breakfast to-morrow morning!" + +The Welshman's sons departed at once. As they were leaving the room +Huck sprang up and exclaimed: + +"Oh, please don't tell ANYbody it was me that blowed on them! Oh, +please!" + +"All right if you say it, Huck, but you ought to have the credit of +what you did." + +"Oh no, no! Please don't tell!" + +When the young men were gone, the old Welshman said: + +"They won't tell--and I won't. But why don't you want it known?" + +Huck would not explain, further than to say that he already knew too +much about one of those men and would not have the man know that he +knew anything against him for the whole world--he would be killed for +knowing it, sure. + +The old man promised secrecy once more, and said: + +"How did you come to follow these fellows, lad? Were they looking +suspicious?" + +Huck was silent while he framed a duly cautious reply. Then he said: + +"Well, you see, I'm a kind of a hard lot,--least everybody says so, +and I don't see nothing agin it--and sometimes I can't sleep much, on +account of thinking about it and sort of trying to strike out a new way +of doing. That was the way of it last night. I couldn't sleep, and so I +come along up-street 'bout midnight, a-turning it all over, and when I +got to that old shackly brick store by the Temperance Tavern, I backed +up agin the wall to have another think. Well, just then along comes +these two chaps slipping along close by me, with something under their +arm, and I reckoned they'd stole it. One was a-smoking, and t'other one +wanted a light; so they stopped right before me and the cigars lit up +their faces and I see that the big one was the deaf and dumb Spaniard, +by his white whiskers and the patch on his eye, and t'other one was a +rusty, ragged-looking devil." + +"Could you see the rags by the light of the cigars?" + +This staggered Huck for a moment. Then he said: + +"Well, I don't know--but somehow it seems as if I did." + +"Then they went on, and you--" + +"Follered 'em--yes. That was it. I wanted to see what was up--they +sneaked along so. I dogged 'em to the widder's stile, and stood in the +dark and heard the ragged one beg for the widder, and the Spaniard +swear he'd spile her looks just as I told you and your two--" + +"What! The DEAF AND DUMB man said all that!" + +Huck had made another terrible mistake! He was trying his best to keep +the old man from getting the faintest hint of who the Spaniard might +be, and yet his tongue seemed determined to get him into trouble in +spite of all he could do. He made several efforts to creep out of his +scrape, but the old man's eye was upon him and he made blunder after +blunder. Presently the Welshman said: + +"My boy, don't be afraid of me. I wouldn't hurt a hair of your head +for all the world. No--I'd protect you--I'd protect you. This Spaniard +is not deaf and dumb; you've let that slip without intending it; you +can't cover that up now. You know something about that Spaniard that +you want to keep dark. Now trust me--tell me what it is, and trust me +--I won't betray you." + +Huck looked into the old man's honest eyes a moment, then bent over +and whispered in his ear: + +"'Tain't a Spaniard--it's Injun Joe!" + +The Welshman almost jumped out of his chair. In a moment he said: + +"It's all plain enough, now. When you talked about notching ears and +slitting noses I judged that that was your own embellishment, because +white men don't take that sort of revenge. But an Injun! That's a +different matter altogether." + +During breakfast the talk went on, and in the course of it the old man +said that the last thing which he and his sons had done, before going +to bed, was to get a lantern and examine the stile and its vicinity for +marks of blood. They found none, but captured a bulky bundle of-- + +"Of WHAT?" + +If the words had been lightning they could not have leaped with a more +stunning suddenness from Huck's blanched lips. His eyes were staring +wide, now, and his breath suspended--waiting for the answer. The +Welshman started--stared in return--three seconds--five seconds--ten +--then replied: + +"Of burglar's tools. Why, what's the MATTER with you?" + +Huck sank back, panting gently, but deeply, unutterably grateful. The +Welshman eyed him gravely, curiously--and presently said: + +"Yes, burglar's tools. That appears to relieve you a good deal. But +what did give you that turn? What were YOU expecting we'd found?" + +Huck was in a close place--the inquiring eye was upon him--he would +have given anything for material for a plausible answer--nothing +suggested itself--the inquiring eye was boring deeper and deeper--a +senseless reply offered--there was no time to weigh it, so at a venture +he uttered it--feebly: + +"Sunday-school books, maybe." + +Poor Huck was too distressed to smile, but the old man laughed loud +and joyously, shook up the details of his anatomy from head to foot, +and ended by saying that such a laugh was money in a-man's pocket, +because it cut down the doctor's bill like everything. Then he added: + +"Poor old chap, you're white and jaded--you ain't well a bit--no +wonder you're a little flighty and off your balance. But you'll come +out of it. Rest and sleep will fetch you out all right, I hope." + +Huck was irritated to think he had been such a goose and betrayed such +a suspicious excitement, for he had dropped the idea that the parcel +brought from the tavern was the treasure, as soon as he had heard the +talk at the widow's stile. He had only thought it was not the treasure, +however--he had not known that it wasn't--and so the suggestion of a +captured bundle was too much for his self-possession. But on the whole +he felt glad the little episode had happened, for now he knew beyond +all question that that bundle was not THE bundle, and so his mind was +at rest and exceedingly comfortable. In fact, everything seemed to be +drifting just in the right direction, now; the treasure must be still +in No. 2, the men would be captured and jailed that day, and he and Tom +could seize the gold that night without any trouble or any fear of +interruption. + +Just as breakfast was completed there was a knock at the door. Huck +jumped for a hiding-place, for he had no mind to be connected even +remotely with the late event. The Welshman admitted several ladies and +gentlemen, among them the Widow Douglas, and noticed that groups of +citizens were climbing up the hill--to stare at the stile. So the news +had spread. The Welshman had to tell the story of the night to the +visitors. The widow's gratitude for her preservation was outspoken. + +"Don't say a word about it, madam. There's another that you're more +beholden to than you are to me and my boys, maybe, but he don't allow +me to tell his name. We wouldn't have been there but for him." + +Of course this excited a curiosity so vast that it almost belittled +the main matter--but the Welshman allowed it to eat into the vitals of +his visitors, and through them be transmitted to the whole town, for he +refused to part with his secret. When all else had been learned, the +widow said: + +"I went to sleep reading in bed and slept straight through all that +noise. Why didn't you come and wake me?" + +"We judged it warn't worth while. Those fellows warn't likely to come +again--they hadn't any tools left to work with, and what was the use of +waking you up and scaring you to death? My three negro men stood guard +at your house all the rest of the night. They've just come back." + +More visitors came, and the story had to be told and retold for a +couple of hours more. + +There was no Sabbath-school during day-school vacation, but everybody +was early at church. The stirring event was well canvassed. News came +that not a sign of the two villains had been yet discovered. When the +sermon was finished, Judge Thatcher's wife dropped alongside of Mrs. +Harper as she moved down the aisle with the crowd and said: + +"Is my Becky going to sleep all day? I just expected she would be +tired to death." + +"Your Becky?" + +"Yes," with a startled look--"didn't she stay with you last night?" + +"Why, no." + +Mrs. Thatcher turned pale, and sank into a pew, just as Aunt Polly, +talking briskly with a friend, passed by. Aunt Polly said: + +"Good-morning, Mrs. Thatcher. Good-morning, Mrs. Harper. I've got a +boy that's turned up missing. I reckon my Tom stayed at your house last +night--one of you. And now he's afraid to come to church. I've got to +settle with him." + +Mrs. Thatcher shook her head feebly and turned paler than ever. + +"He didn't stay with us," said Mrs. Harper, beginning to look uneasy. +A marked anxiety came into Aunt Polly's face. + +"Joe Harper, have you seen my Tom this morning?" + +"No'm." + +"When did you see him last?" + +Joe tried to remember, but was not sure he could say. The people had +stopped moving out of church. Whispers passed along, and a boding +uneasiness took possession of every countenance. Children were +anxiously questioned, and young teachers. They all said they had not +noticed whether Tom and Becky were on board the ferryboat on the +homeward trip; it was dark; no one thought of inquiring if any one was +missing. One young man finally blurted out his fear that they were +still in the cave! Mrs. Thatcher swooned away. Aunt Polly fell to +crying and wringing her hands. + +The alarm swept from lip to lip, from group to group, from street to +street, and within five minutes the bells were wildly clanging and the +whole town was up! The Cardiff Hill episode sank into instant +insignificance, the burglars were forgotten, horses were saddled, +skiffs were manned, the ferryboat ordered out, and before the horror +was half an hour old, two hundred men were pouring down highroad and +river toward the cave. + +All the long afternoon the village seemed empty and dead. Many women +visited Aunt Polly and Mrs. Thatcher and tried to comfort them. They +cried with them, too, and that was still better than words. All the +tedious night the town waited for news; but when the morning dawned at +last, all the word that came was, "Send more candles--and send food." +Mrs. Thatcher was almost crazed; and Aunt Polly, also. Judge Thatcher +sent messages of hope and encouragement from the cave, but they +conveyed no real cheer. + +The old Welshman came home toward daylight, spattered with +candle-grease, smeared with clay, and almost worn out. He found Huck +still in the bed that had been provided for him, and delirious with +fever. The physicians were all at the cave, so the Widow Douglas came +and took charge of the patient. She said she would do her best by him, +because, whether he was good, bad, or indifferent, he was the Lord's, +and nothing that was the Lord's was a thing to be neglected. The +Welshman said Huck had good spots in him, and the widow said: + +"You can depend on it. That's the Lord's mark. He don't leave it off. +He never does. Puts it somewhere on every creature that comes from his +hands." + +Early in the forenoon parties of jaded men began to straggle into the +village, but the strongest of the citizens continued searching. All the +news that could be gained was that remotenesses of the cavern were +being ransacked that had never been visited before; that every corner +and crevice was going to be thoroughly searched; that wherever one +wandered through the maze of passages, lights were to be seen flitting +hither and thither in the distance, and shoutings and pistol-shots sent +their hollow reverberations to the ear down the sombre aisles. In one +place, far from the section usually traversed by tourists, the names +"BECKY & TOM" had been found traced upon the rocky wall with +candle-smoke, and near at hand a grease-soiled bit of ribbon. Mrs. +Thatcher recognized the ribbon and cried over it. She said it was the +last relic she should ever have of her child; and that no other memorial +of her could ever be so precious, because this one parted latest from +the living body before the awful death came. Some said that now and +then, in the cave, a far-away speck of light would glimmer, and then a +glorious shout would burst forth and a score of men go trooping down the +echoing aisle--and then a sickening disappointment always followed; the +children were not there; it was only a searcher's light. + +Three dreadful days and nights dragged their tedious hours along, and +the village sank into a hopeless stupor. No one had heart for anything. +The accidental discovery, just made, that the proprietor of the +Temperance Tavern kept liquor on his premises, scarcely fluttered the +public pulse, tremendous as the fact was. In a lucid interval, Huck +feebly led up to the subject of taverns, and finally asked--dimly +dreading the worst--if anything had been discovered at the Temperance +Tavern since he had been ill. + +"Yes," said the widow. + +Huck started up in bed, wild-eyed: + +"What? What was it?" + +"Liquor!--and the place has been shut up. Lie down, child--what a turn +you did give me!" + +"Only tell me just one thing--only just one--please! Was it Tom Sawyer +that found it?" + +The widow burst into tears. "Hush, hush, child, hush! I've told you +before, you must NOT talk. You are very, very sick!" + +Then nothing but liquor had been found; there would have been a great +powwow if it had been the gold. So the treasure was gone forever--gone +forever! But what could she be crying about? Curious that she should +cry. + +These thoughts worked their dim way through Huck's mind, and under the +weariness they gave him he fell asleep. The widow said to herself: + +"There--he's asleep, poor wreck. Tom Sawyer find it! Pity but somebody +could find Tom Sawyer! Ah, there ain't many left, now, that's got hope +enough, or strength enough, either, to go on searching." + + + +CHAPTER XXXI + +NOW to return to Tom and Becky's share in the picnic. They tripped +along the murky aisles with the rest of the company, visiting the +familiar wonders of the cave--wonders dubbed with rather +over-descriptive names, such as "The Drawing-Room," "The Cathedral," +"Aladdin's Palace," and so on. Presently the hide-and-seek frolicking +began, and Tom and Becky engaged in it with zeal until the exertion +began to grow a trifle wearisome; then they wandered down a sinuous +avenue holding their candles aloft and reading the tangled web-work of +names, dates, post-office addresses, and mottoes with which the rocky +walls had been frescoed (in candle-smoke). Still drifting along and +talking, they scarcely noticed that they were now in a part of the cave +whose walls were not frescoed. They smoked their own names under an +overhanging shelf and moved on. Presently they came to a place where a +little stream of water, trickling over a ledge and carrying a limestone +sediment with it, had, in the slow-dragging ages, formed a laced and +ruffled Niagara in gleaming and imperishable stone. Tom squeezed his +small body behind it in order to illuminate it for Becky's +gratification. He found that it curtained a sort of steep natural +stairway which was enclosed between narrow walls, and at once the +ambition to be a discoverer seized him. Becky responded to his call, +and they made a smoke-mark for future guidance, and started upon their +quest. They wound this way and that, far down into the secret depths of +the cave, made another mark, and branched off in search of novelties to +tell the upper world about. In one place they found a spacious cavern, +from whose ceiling depended a multitude of shining stalactites of the +length and circumference of a man's leg; they walked all about it, +wondering and admiring, and presently left it by one of the numerous +passages that opened into it. This shortly brought them to a bewitching +spring, whose basin was incrusted with a frostwork of glittering +crystals; it was in the midst of a cavern whose walls were supported by +many fantastic pillars which had been formed by the joining of great +stalactites and stalagmites together, the result of the ceaseless +water-drip of centuries. Under the roof vast knots of bats had packed +themselves together, thousands in a bunch; the lights disturbed the +creatures and they came flocking down by hundreds, squeaking and +darting furiously at the candles. Tom knew their ways and the danger of +this sort of conduct. He seized Becky's hand and hurried her into the +first corridor that offered; and none too soon, for a bat struck +Becky's light out with its wing while she was passing out of the +cavern. The bats chased the children a good distance; but the fugitives +plunged into every new passage that offered, and at last got rid of the +perilous things. Tom found a subterranean lake, shortly, which +stretched its dim length away until its shape was lost in the shadows. +He wanted to explore its borders, but concluded that it would be best +to sit down and rest awhile, first. Now, for the first time, the deep +stillness of the place laid a clammy hand upon the spirits of the +children. Becky said: + +"Why, I didn't notice, but it seems ever so long since I heard any of +the others." + +"Come to think, Becky, we are away down below them--and I don't know +how far away north, or south, or east, or whichever it is. We couldn't +hear them here." + +Becky grew apprehensive. + +"I wonder how long we've been down here, Tom? We better start back." + +"Yes, I reckon we better. P'raps we better." + +"Can you find the way, Tom? It's all a mixed-up crookedness to me." + +"I reckon I could find it--but then the bats. If they put our candles +out it will be an awful fix. Let's try some other way, so as not to go +through there." + +"Well. But I hope we won't get lost. It would be so awful!" and the +girl shuddered at the thought of the dreadful possibilities. + +They started through a corridor, and traversed it in silence a long +way, glancing at each new opening, to see if there was anything +familiar about the look of it; but they were all strange. Every time +Tom made an examination, Becky would watch his face for an encouraging +sign, and he would say cheerily: + +"Oh, it's all right. This ain't the one, but we'll come to it right +away!" + +But he felt less and less hopeful with each failure, and presently +began to turn off into diverging avenues at sheer random, in desperate +hope of finding the one that was wanted. He still said it was "all +right," but there was such a leaden dread at his heart that the words +had lost their ring and sounded just as if he had said, "All is lost!" +Becky clung to his side in an anguish of fear, and tried hard to keep +back the tears, but they would come. At last she said: + +"Oh, Tom, never mind the bats, let's go back that way! We seem to get +worse and worse off all the time." + +"Listen!" said he. + +Profound silence; silence so deep that even their breathings were +conspicuous in the hush. Tom shouted. The call went echoing down the +empty aisles and died out in the distance in a faint sound that +resembled a ripple of mocking laughter. + +"Oh, don't do it again, Tom, it is too horrid," said Becky. + +"It is horrid, but I better, Becky; they might hear us, you know," and +he shouted again. + +The "might" was even a chillier horror than the ghostly laughter, it +so confessed a perishing hope. The children stood still and listened; +but there was no result. Tom turned upon the back track at once, and +hurried his steps. It was but a little while before a certain +indecision in his manner revealed another fearful fact to Becky--he +could not find his way back! + +"Oh, Tom, you didn't make any marks!" + +"Becky, I was such a fool! Such a fool! I never thought we might want +to come back! No--I can't find the way. It's all mixed up." + +"Tom, Tom, we're lost! we're lost! We never can get out of this awful +place! Oh, why DID we ever leave the others!" + +She sank to the ground and burst into such a frenzy of crying that Tom +was appalled with the idea that she might die, or lose her reason. He +sat down by her and put his arms around her; she buried her face in his +bosom, she clung to him, she poured out her terrors, her unavailing +regrets, and the far echoes turned them all to jeering laughter. Tom +begged her to pluck up hope again, and she said she could not. He fell +to blaming and abusing himself for getting her into this miserable +situation; this had a better effect. She said she would try to hope +again, she would get up and follow wherever he might lead if only he +would not talk like that any more. For he was no more to blame than +she, she said. + +So they moved on again--aimlessly--simply at random--all they could do +was to move, keep moving. For a little while, hope made a show of +reviving--not with any reason to back it, but only because it is its +nature to revive when the spring has not been taken out of it by age +and familiarity with failure. + +By-and-by Tom took Becky's candle and blew it out. This economy meant +so much! Words were not needed. Becky understood, and her hope died +again. She knew that Tom had a whole candle and three or four pieces in +his pockets--yet he must economize. + +By-and-by, fatigue began to assert its claims; the children tried to +pay attention, for it was dreadful to think of sitting down when time +was grown to be so precious, moving, in some direction, in any +direction, was at least progress and might bear fruit; but to sit down +was to invite death and shorten its pursuit. + +At last Becky's frail limbs refused to carry her farther. She sat +down. Tom rested with her, and they talked of home, and the friends +there, and the comfortable beds and, above all, the light! Becky cried, +and Tom tried to think of some way of comforting her, but all his +encouragements were grown threadbare with use, and sounded like +sarcasms. Fatigue bore so heavily upon Becky that she drowsed off to +sleep. Tom was grateful. He sat looking into her drawn face and saw it +grow smooth and natural under the influence of pleasant dreams; and +by-and-by a smile dawned and rested there. The peaceful face reflected +somewhat of peace and healing into his own spirit, and his thoughts +wandered away to bygone times and dreamy memories. While he was deep in +his musings, Becky woke up with a breezy little laugh--but it was +stricken dead upon her lips, and a groan followed it. + +"Oh, how COULD I sleep! I wish I never, never had waked! No! No, I +don't, Tom! Don't look so! I won't say it again." + +"I'm glad you've slept, Becky; you'll feel rested, now, and we'll find +the way out." + +"We can try, Tom; but I've seen such a beautiful country in my dream. +I reckon we are going there." + +"Maybe not, maybe not. Cheer up, Becky, and let's go on trying." + +They rose up and wandered along, hand in hand and hopeless. They tried +to estimate how long they had been in the cave, but all they knew was +that it seemed days and weeks, and yet it was plain that this could not +be, for their candles were not gone yet. A long time after this--they +could not tell how long--Tom said they must go softly and listen for +dripping water--they must find a spring. They found one presently, and +Tom said it was time to rest again. Both were cruelly tired, yet Becky +said she thought she could go a little farther. She was surprised to +hear Tom dissent. She could not understand it. They sat down, and Tom +fastened his candle to the wall in front of them with some clay. +Thought was soon busy; nothing was said for some time. Then Becky broke +the silence: + +"Tom, I am so hungry!" + +Tom took something out of his pocket. + +"Do you remember this?" said he. + +Becky almost smiled. + +"It's our wedding-cake, Tom." + +"Yes--I wish it was as big as a barrel, for it's all we've got." + +"I saved it from the picnic for us to dream on, Tom, the way grown-up +people do with wedding-cake--but it'll be our--" + +She dropped the sentence where it was. Tom divided the cake and Becky +ate with good appetite, while Tom nibbled at his moiety. There was +abundance of cold water to finish the feast with. By-and-by Becky +suggested that they move on again. Tom was silent a moment. Then he +said: + +"Becky, can you bear it if I tell you something?" + +Becky's face paled, but she thought she could. + +"Well, then, Becky, we must stay here, where there's water to drink. +That little piece is our last candle!" + +Becky gave loose to tears and wailings. Tom did what he could to +comfort her, but with little effect. At length Becky said: + +"Tom!" + +"Well, Becky?" + +"They'll miss us and hunt for us!" + +"Yes, they will! Certainly they will!" + +"Maybe they're hunting for us now, Tom." + +"Why, I reckon maybe they are. I hope they are." + +"When would they miss us, Tom?" + +"When they get back to the boat, I reckon." + +"Tom, it might be dark then--would they notice we hadn't come?" + +"I don't know. But anyway, your mother would miss you as soon as they +got home." + +A frightened look in Becky's face brought Tom to his senses and he saw +that he had made a blunder. Becky was not to have gone home that night! +The children became silent and thoughtful. In a moment a new burst of +grief from Becky showed Tom that the thing in his mind had struck hers +also--that the Sabbath morning might be half spent before Mrs. Thatcher +discovered that Becky was not at Mrs. Harper's. + +The children fastened their eyes upon their bit of candle and watched +it melt slowly and pitilessly away; saw the half inch of wick stand +alone at last; saw the feeble flame rise and fall, climb the thin +column of smoke, linger at its top a moment, and then--the horror of +utter darkness reigned! + +How long afterward it was that Becky came to a slow consciousness that +she was crying in Tom's arms, neither could tell. All that they knew +was, that after what seemed a mighty stretch of time, both awoke out of +a dead stupor of sleep and resumed their miseries once more. Tom said +it might be Sunday, now--maybe Monday. He tried to get Becky to talk, +but her sorrows were too oppressive, all her hopes were gone. Tom said +that they must have been missed long ago, and no doubt the search was +going on. He would shout and maybe some one would come. He tried it; +but in the darkness the distant echoes sounded so hideously that he +tried it no more. + +The hours wasted away, and hunger came to torment the captives again. +A portion of Tom's half of the cake was left; they divided and ate it. +But they seemed hungrier than before. The poor morsel of food only +whetted desire. + +By-and-by Tom said: + +"SH! Did you hear that?" + +Both held their breath and listened. There was a sound like the +faintest, far-off shout. Instantly Tom answered it, and leading Becky +by the hand, started groping down the corridor in its direction. +Presently he listened again; again the sound was heard, and apparently +a little nearer. + +"It's them!" said Tom; "they're coming! Come along, Becky--we're all +right now!" + +The joy of the prisoners was almost overwhelming. Their speed was +slow, however, because pitfalls were somewhat common, and had to be +guarded against. They shortly came to one and had to stop. It might be +three feet deep, it might be a hundred--there was no passing it at any +rate. Tom got down on his breast and reached as far down as he could. +No bottom. They must stay there and wait until the searchers came. They +listened; evidently the distant shoutings were growing more distant! a +moment or two more and they had gone altogether. The heart-sinking +misery of it! Tom whooped until he was hoarse, but it was of no use. He +talked hopefully to Becky; but an age of anxious waiting passed and no +sounds came again. + +The children groped their way back to the spring. The weary time +dragged on; they slept again, and awoke famished and woe-stricken. Tom +believed it must be Tuesday by this time. + +Now an idea struck him. There were some side passages near at hand. It +would be better to explore some of these than bear the weight of the +heavy time in idleness. He took a kite-line from his pocket, tied it to +a projection, and he and Becky started, Tom in the lead, unwinding the +line as he groped along. At the end of twenty steps the corridor ended +in a "jumping-off place." Tom got down on his knees and felt below, and +then as far around the corner as he could reach with his hands +conveniently; he made an effort to stretch yet a little farther to the +right, and at that moment, not twenty yards away, a human hand, holding +a candle, appeared from behind a rock! Tom lifted up a glorious shout, +and instantly that hand was followed by the body it belonged to--Injun +Joe's! Tom was paralyzed; he could not move. He was vastly gratified +the next moment, to see the "Spaniard" take to his heels and get +himself out of sight. Tom wondered that Joe had not recognized his +voice and come over and killed him for testifying in court. But the +echoes must have disguised the voice. Without doubt, that was it, he +reasoned. Tom's fright weakened every muscle in his body. He said to +himself that if he had strength enough to get back to the spring he +would stay there, and nothing should tempt him to run the risk of +meeting Injun Joe again. He was careful to keep from Becky what it was +he had seen. He told her he had only shouted "for luck." + +But hunger and wretchedness rise superior to fears in the long run. +Another tedious wait at the spring and another long sleep brought +changes. The children awoke tortured with a raging hunger. Tom believed +that it must be Wednesday or Thursday or even Friday or Saturday, now, +and that the search had been given over. He proposed to explore another +passage. He felt willing to risk Injun Joe and all other terrors. But +Becky was very weak. She had sunk into a dreary apathy and would not be +roused. She said she would wait, now, where she was, and die--it would +not be long. She told Tom to go with the kite-line and explore if he +chose; but she implored him to come back every little while and speak +to her; and she made him promise that when the awful time came, he +would stay by her and hold her hand until all was over. + +Tom kissed her, with a choking sensation in his throat, and made a +show of being confident of finding the searchers or an escape from the +cave; then he took the kite-line in his hand and went groping down one +of the passages on his hands and knees, distressed with hunger and sick +with bodings of coming doom. + + + +CHAPTER XXXII + +TUESDAY afternoon came, and waned to the twilight. The village of St. +Petersburg still mourned. The lost children had not been found. Public +prayers had been offered up for them, and many and many a private +prayer that had the petitioner's whole heart in it; but still no good +news came from the cave. The majority of the searchers had given up the +quest and gone back to their daily avocations, saying that it was plain +the children could never be found. Mrs. Thatcher was very ill, and a +great part of the time delirious. People said it was heartbreaking to +hear her call her child, and raise her head and listen a whole minute +at a time, then lay it wearily down again with a moan. Aunt Polly had +drooped into a settled melancholy, and her gray hair had grown almost +white. The village went to its rest on Tuesday night, sad and forlorn. + +Away in the middle of the night a wild peal burst from the village +bells, and in a moment the streets were swarming with frantic half-clad +people, who shouted, "Turn out! turn out! they're found! they're +found!" Tin pans and horns were added to the din, the population massed +itself and moved toward the river, met the children coming in an open +carriage drawn by shouting citizens, thronged around it, joined its +homeward march, and swept magnificently up the main street roaring +huzzah after huzzah! + +The village was illuminated; nobody went to bed again; it was the +greatest night the little town had ever seen. During the first half-hour +a procession of villagers filed through Judge Thatcher's house, seized +the saved ones and kissed them, squeezed Mrs. Thatcher's hand, tried to +speak but couldn't--and drifted out raining tears all over the place. + +Aunt Polly's happiness was complete, and Mrs. Thatcher's nearly so. It +would be complete, however, as soon as the messenger dispatched with +the great news to the cave should get the word to her husband. Tom lay +upon a sofa with an eager auditory about him and told the history of +the wonderful adventure, putting in many striking additions to adorn it +withal; and closed with a description of how he left Becky and went on +an exploring expedition; how he followed two avenues as far as his +kite-line would reach; how he followed a third to the fullest stretch of +the kite-line, and was about to turn back when he glimpsed a far-off +speck that looked like daylight; dropped the line and groped toward it, +pushed his head and shoulders through a small hole, and saw the broad +Mississippi rolling by! And if it had only happened to be night he would +not have seen that speck of daylight and would not have explored that +passage any more! He told how he went back for Becky and broke the good +news and she told him not to fret her with such stuff, for she was +tired, and knew she was going to die, and wanted to. He described how he +labored with her and convinced her; and how she almost died for joy when +she had groped to where she actually saw the blue speck of daylight; how +he pushed his way out at the hole and then helped her out; how they sat +there and cried for gladness; how some men came along in a skiff and Tom +hailed them and told them their situation and their famished condition; +how the men didn't believe the wild tale at first, "because," said they, +"you are five miles down the river below the valley the cave is in" +--then took them aboard, rowed to a house, gave them supper, made them +rest till two or three hours after dark and then brought them home. + +Before day-dawn, Judge Thatcher and the handful of searchers with him +were tracked out, in the cave, by the twine clews they had strung +behind them, and informed of the great news. + +Three days and nights of toil and hunger in the cave were not to be +shaken off at once, as Tom and Becky soon discovered. They were +bedridden all of Wednesday and Thursday, and seemed to grow more and +more tired and worn, all the time. Tom got about, a little, on +Thursday, was down-town Friday, and nearly as whole as ever Saturday; +but Becky did not leave her room until Sunday, and then she looked as +if she had passed through a wasting illness. + +Tom learned of Huck's sickness and went to see him on Friday, but +could not be admitted to the bedroom; neither could he on Saturday or +Sunday. He was admitted daily after that, but was warned to keep still +about his adventure and introduce no exciting topic. The Widow Douglas +stayed by to see that he obeyed. At home Tom learned of the Cardiff +Hill event; also that the "ragged man's" body had eventually been found +in the river near the ferry-landing; he had been drowned while trying +to escape, perhaps. + +About a fortnight after Tom's rescue from the cave, he started off to +visit Huck, who had grown plenty strong enough, now, to hear exciting +talk, and Tom had some that would interest him, he thought. Judge +Thatcher's house was on Tom's way, and he stopped to see Becky. The +Judge and some friends set Tom to talking, and some one asked him +ironically if he wouldn't like to go to the cave again. Tom said he +thought he wouldn't mind it. The Judge said: + +"Well, there are others just like you, Tom, I've not the least doubt. +But we have taken care of that. Nobody will get lost in that cave any +more." + +"Why?" + +"Because I had its big door sheathed with boiler iron two weeks ago, +and triple-locked--and I've got the keys." + +Tom turned as white as a sheet. + +"What's the matter, boy! Here, run, somebody! Fetch a glass of water!" + +The water was brought and thrown into Tom's face. + +"Ah, now you're all right. What was the matter with you, Tom?" + +"Oh, Judge, Injun Joe's in the cave!" + + + +CHAPTER XXXIII + +WITHIN a few minutes the news had spread, and a dozen skiff-loads of +men were on their way to McDougal's cave, and the ferryboat, well +filled with passengers, soon followed. Tom Sawyer was in the skiff that +bore Judge Thatcher. + +When the cave door was unlocked, a sorrowful sight presented itself in +the dim twilight of the place. Injun Joe lay stretched upon the ground, +dead, with his face close to the crack of the door, as if his longing +eyes had been fixed, to the latest moment, upon the light and the cheer +of the free world outside. Tom was touched, for he knew by his own +experience how this wretch had suffered. His pity was moved, but +nevertheless he felt an abounding sense of relief and security, now, +which revealed to him in a degree which he had not fully appreciated +before how vast a weight of dread had been lying upon him since the day +he lifted his voice against this bloody-minded outcast. + +Injun Joe's bowie-knife lay close by, its blade broken in two. The +great foundation-beam of the door had been chipped and hacked through, +with tedious labor; useless labor, too, it was, for the native rock +formed a sill outside it, and upon that stubborn material the knife had +wrought no effect; the only damage done was to the knife itself. But if +there had been no stony obstruction there the labor would have been +useless still, for if the beam had been wholly cut away Injun Joe could +not have squeezed his body under the door, and he knew it. So he had +only hacked that place in order to be doing something--in order to pass +the weary time--in order to employ his tortured faculties. Ordinarily +one could find half a dozen bits of candle stuck around in the crevices +of this vestibule, left there by tourists; but there were none now. The +prisoner had searched them out and eaten them. He had also contrived to +catch a few bats, and these, also, he had eaten, leaving only their +claws. The poor unfortunate had starved to death. In one place, near at +hand, a stalagmite had been slowly growing up from the ground for ages, +builded by the water-drip from a stalactite overhead. The captive had +broken off the stalagmite, and upon the stump had placed a stone, +wherein he had scooped a shallow hollow to catch the precious drop +that fell once in every three minutes with the dreary regularity of a +clock-tick--a dessertspoonful once in four and twenty hours. That drop +was falling when the Pyramids were new; when Troy fell; when the +foundations of Rome were laid; when Christ was crucified; when the +Conqueror created the British empire; when Columbus sailed; when the +massacre at Lexington was "news." It is falling now; it will still be +falling when all these things shall have sunk down the afternoon of +history, and the twilight of tradition, and been swallowed up in the +thick night of oblivion. Has everything a purpose and a mission? Did +this drop fall patiently during five thousand years to be ready for +this flitting human insect's need? and has it another important object +to accomplish ten thousand years to come? No matter. It is many and +many a year since the hapless half-breed scooped out the stone to catch +the priceless drops, but to this day the tourist stares longest at that +pathetic stone and that slow-dropping water when he comes to see the +wonders of McDougal's cave. Injun Joe's cup stands first in the list of +the cavern's marvels; even "Aladdin's Palace" cannot rival it. + +Injun Joe was buried near the mouth of the cave; and people flocked +there in boats and wagons from the towns and from all the farms and +hamlets for seven miles around; they brought their children, and all +sorts of provisions, and confessed that they had had almost as +satisfactory a time at the funeral as they could have had at the +hanging. + +This funeral stopped the further growth of one thing--the petition to +the governor for Injun Joe's pardon. The petition had been largely +signed; many tearful and eloquent meetings had been held, and a +committee of sappy women been appointed to go in deep mourning and wail +around the governor, and implore him to be a merciful ass and trample +his duty under foot. Injun Joe was believed to have killed five +citizens of the village, but what of that? If he had been Satan himself +there would have been plenty of weaklings ready to scribble their names +to a pardon-petition, and drip a tear on it from their permanently +impaired and leaky water-works. + +The morning after the funeral Tom took Huck to a private place to have +an important talk. Huck had learned all about Tom's adventure from the +Welshman and the Widow Douglas, by this time, but Tom said he reckoned +there was one thing they had not told him; that thing was what he +wanted to talk about now. Huck's face saddened. He said: + +"I know what it is. You got into No. 2 and never found anything but +whiskey. Nobody told me it was you; but I just knowed it must 'a' ben +you, soon as I heard 'bout that whiskey business; and I knowed you +hadn't got the money becuz you'd 'a' got at me some way or other and +told me even if you was mum to everybody else. Tom, something's always +told me we'd never get holt of that swag." + +"Why, Huck, I never told on that tavern-keeper. YOU know his tavern +was all right the Saturday I went to the picnic. Don't you remember you +was to watch there that night?" + +"Oh yes! Why, it seems 'bout a year ago. It was that very night that I +follered Injun Joe to the widder's." + +"YOU followed him?" + +"Yes--but you keep mum. I reckon Injun Joe's left friends behind him, +and I don't want 'em souring on me and doing me mean tricks. If it +hadn't ben for me he'd be down in Texas now, all right." + +Then Huck told his entire adventure in confidence to Tom, who had only +heard of the Welshman's part of it before. + +"Well," said Huck, presently, coming back to the main question, +"whoever nipped the whiskey in No. 2, nipped the money, too, I reckon +--anyways it's a goner for us, Tom." + +"Huck, that money wasn't ever in No. 2!" + +"What!" Huck searched his comrade's face keenly. "Tom, have you got on +the track of that money again?" + +"Huck, it's in the cave!" + +Huck's eyes blazed. + +"Say it again, Tom." + +"The money's in the cave!" + +"Tom--honest injun, now--is it fun, or earnest?" + +"Earnest, Huck--just as earnest as ever I was in my life. Will you go +in there with me and help get it out?" + +"I bet I will! I will if it's where we can blaze our way to it and not +get lost." + +"Huck, we can do that without the least little bit of trouble in the +world." + +"Good as wheat! What makes you think the money's--" + +"Huck, you just wait till we get in there. If we don't find it I'll +agree to give you my drum and every thing I've got in the world. I +will, by jings." + +"All right--it's a whiz. When do you say?" + +"Right now, if you say it. Are you strong enough?" + +"Is it far in the cave? I ben on my pins a little, three or four days, +now, but I can't walk more'n a mile, Tom--least I don't think I could." + +"It's about five mile into there the way anybody but me would go, +Huck, but there's a mighty short cut that they don't anybody but me +know about. Huck, I'll take you right to it in a skiff. I'll float the +skiff down there, and I'll pull it back again all by myself. You +needn't ever turn your hand over." + +"Less start right off, Tom." + +"All right. We want some bread and meat, and our pipes, and a little +bag or two, and two or three kite-strings, and some of these +new-fangled things they call lucifer matches. I tell you, many's +the time I wished I had some when I was in there before." + +A trifle after noon the boys borrowed a small skiff from a citizen who +was absent, and got under way at once. When they were several miles +below "Cave Hollow," Tom said: + +"Now you see this bluff here looks all alike all the way down from the +cave hollow--no houses, no wood-yards, bushes all alike. But do you see +that white place up yonder where there's been a landslide? Well, that's +one of my marks. We'll get ashore, now." + +They landed. + +"Now, Huck, where we're a-standing you could touch that hole I got out +of with a fishing-pole. See if you can find it." + +Huck searched all the place about, and found nothing. Tom proudly +marched into a thick clump of sumach bushes and said: + +"Here you are! Look at it, Huck; it's the snuggest hole in this +country. You just keep mum about it. All along I've been wanting to be +a robber, but I knew I'd got to have a thing like this, and where to +run across it was the bother. We've got it now, and we'll keep it +quiet, only we'll let Joe Harper and Ben Rogers in--because of course +there's got to be a Gang, or else there wouldn't be any style about it. +Tom Sawyer's Gang--it sounds splendid, don't it, Huck?" + +"Well, it just does, Tom. And who'll we rob?" + +"Oh, most anybody. Waylay people--that's mostly the way." + +"And kill them?" + +"No, not always. Hive them in the cave till they raise a ransom." + +"What's a ransom?" + +"Money. You make them raise all they can, off'n their friends; and +after you've kept them a year, if it ain't raised then you kill them. +That's the general way. Only you don't kill the women. You shut up the +women, but you don't kill them. They're always beautiful and rich, and +awfully scared. You take their watches and things, but you always take +your hat off and talk polite. They ain't anybody as polite as robbers +--you'll see that in any book. Well, the women get to loving you, and +after they've been in the cave a week or two weeks they stop crying and +after that you couldn't get them to leave. If you drove them out they'd +turn right around and come back. It's so in all the books." + +"Why, it's real bully, Tom. I believe it's better'n to be a pirate." + +"Yes, it's better in some ways, because it's close to home and +circuses and all that." + +By this time everything was ready and the boys entered the hole, Tom +in the lead. They toiled their way to the farther end of the tunnel, +then made their spliced kite-strings fast and moved on. A few steps +brought them to the spring, and Tom felt a shudder quiver all through +him. He showed Huck the fragment of candle-wick perched on a lump of +clay against the wall, and described how he and Becky had watched the +flame struggle and expire. + +The boys began to quiet down to whispers, now, for the stillness and +gloom of the place oppressed their spirits. They went on, and presently +entered and followed Tom's other corridor until they reached the +"jumping-off place." The candles revealed the fact that it was not +really a precipice, but only a steep clay hill twenty or thirty feet +high. Tom whispered: + +"Now I'll show you something, Huck." + +He held his candle aloft and said: + +"Look as far around the corner as you can. Do you see that? There--on +the big rock over yonder--done with candle-smoke." + +"Tom, it's a CROSS!" + +"NOW where's your Number Two? 'UNDER THE CROSS,' hey? Right yonder's +where I saw Injun Joe poke up his candle, Huck!" + +Huck stared at the mystic sign awhile, and then said with a shaky voice: + +"Tom, less git out of here!" + +"What! and leave the treasure?" + +"Yes--leave it. Injun Joe's ghost is round about there, certain." + +"No it ain't, Huck, no it ain't. It would ha'nt the place where he +died--away out at the mouth of the cave--five mile from here." + +"No, Tom, it wouldn't. It would hang round the money. I know the ways +of ghosts, and so do you." + +Tom began to fear that Huck was right. Misgivings gathered in his +mind. But presently an idea occurred to him-- + +"Lookyhere, Huck, what fools we're making of ourselves! Injun Joe's +ghost ain't a going to come around where there's a cross!" + +The point was well taken. It had its effect. + +"Tom, I didn't think of that. But that's so. It's luck for us, that +cross is. I reckon we'll climb down there and have a hunt for that box." + +Tom went first, cutting rude steps in the clay hill as he descended. +Huck followed. Four avenues opened out of the small cavern which the +great rock stood in. The boys examined three of them with no result. +They found a small recess in the one nearest the base of the rock, with +a pallet of blankets spread down in it; also an old suspender, some +bacon rind, and the well-gnawed bones of two or three fowls. But there +was no money-box. The lads searched and researched this place, but in +vain. Tom said: + +"He said UNDER the cross. Well, this comes nearest to being under the +cross. It can't be under the rock itself, because that sets solid on +the ground." + +They searched everywhere once more, and then sat down discouraged. +Huck could suggest nothing. By-and-by Tom said: + +"Lookyhere, Huck, there's footprints and some candle-grease on the +clay about one side of this rock, but not on the other sides. Now, +what's that for? I bet you the money IS under the rock. I'm going to +dig in the clay." + +"That ain't no bad notion, Tom!" said Huck with animation. + +Tom's "real Barlow" was out at once, and he had not dug four inches +before he struck wood. + +"Hey, Huck!--you hear that?" + +Huck began to dig and scratch now. Some boards were soon uncovered and +removed. They had concealed a natural chasm which led under the rock. +Tom got into this and held his candle as far under the rock as he +could, but said he could not see to the end of the rift. He proposed to +explore. He stooped and passed under; the narrow way descended +gradually. He followed its winding course, first to the right, then to +the left, Huck at his heels. Tom turned a short curve, by-and-by, and +exclaimed: + +"My goodness, Huck, lookyhere!" + +It was the treasure-box, sure enough, occupying a snug little cavern, +along with an empty powder-keg, a couple of guns in leather cases, two +or three pairs of old moccasins, a leather belt, and some other rubbish +well soaked with the water-drip. + +"Got it at last!" said Huck, ploughing among the tarnished coins with +his hand. "My, but we're rich, Tom!" + +"Huck, I always reckoned we'd get it. It's just too good to believe, +but we HAVE got it, sure! Say--let's not fool around here. Let's snake +it out. Lemme see if I can lift the box." + +It weighed about fifty pounds. Tom could lift it, after an awkward +fashion, but could not carry it conveniently. + +"I thought so," he said; "THEY carried it like it was heavy, that day +at the ha'nted house. I noticed that. I reckon I was right to think of +fetching the little bags along." + +The money was soon in the bags and the boys took it up to the cross +rock. + +"Now less fetch the guns and things," said Huck. + +"No, Huck--leave them there. They're just the tricks to have when we +go to robbing. We'll keep them there all the time, and we'll hold our +orgies there, too. It's an awful snug place for orgies." + +"What orgies?" + +"I dono. But robbers always have orgies, and of course we've got to +have them, too. Come along, Huck, we've been in here a long time. It's +getting late, I reckon. I'm hungry, too. We'll eat and smoke when we +get to the skiff." + +They presently emerged into the clump of sumach bushes, looked warily +out, found the coast clear, and were soon lunching and smoking in the +skiff. As the sun dipped toward the horizon they pushed out and got +under way. Tom skimmed up the shore through the long twilight, chatting +cheerily with Huck, and landed shortly after dark. + +"Now, Huck," said Tom, "we'll hide the money in the loft of the +widow's woodshed, and I'll come up in the morning and we'll count it +and divide, and then we'll hunt up a place out in the woods for it +where it will be safe. Just you lay quiet here and watch the stuff till +I run and hook Benny Taylor's little wagon; I won't be gone a minute." + +He disappeared, and presently returned with the wagon, put the two +small sacks into it, threw some old rags on top of them, and started +off, dragging his cargo behind him. When the boys reached the +Welshman's house, they stopped to rest. Just as they were about to move +on, the Welshman stepped out and said: + +"Hallo, who's that?" + +"Huck and Tom Sawyer." + +"Good! Come along with me, boys, you are keeping everybody waiting. +Here--hurry up, trot ahead--I'll haul the wagon for you. Why, it's not +as light as it might be. Got bricks in it?--or old metal?" + +"Old metal," said Tom. + +"I judged so; the boys in this town will take more trouble and fool +away more time hunting up six bits' worth of old iron to sell to the +foundry than they would to make twice the money at regular work. But +that's human nature--hurry along, hurry along!" + +The boys wanted to know what the hurry was about. + +"Never mind; you'll see, when we get to the Widow Douglas'." + +Huck said with some apprehension--for he was long used to being +falsely accused: + +"Mr. Jones, we haven't been doing nothing." + +The Welshman laughed. + +"Well, I don't know, Huck, my boy. I don't know about that. Ain't you +and the widow good friends?" + +"Yes. Well, she's ben good friends to me, anyway." + +"All right, then. What do you want to be afraid for?" + +This question was not entirely answered in Huck's slow mind before he +found himself pushed, along with Tom, into Mrs. Douglas' drawing-room. +Mr. Jones left the wagon near the door and followed. + +The place was grandly lighted, and everybody that was of any +consequence in the village was there. The Thatchers were there, the +Harpers, the Rogerses, Aunt Polly, Sid, Mary, the minister, the editor, +and a great many more, and all dressed in their best. The widow +received the boys as heartily as any one could well receive two such +looking beings. They were covered with clay and candle-grease. Aunt +Polly blushed crimson with humiliation, and frowned and shook her head +at Tom. Nobody suffered half as much as the two boys did, however. Mr. +Jones said: + +"Tom wasn't at home, yet, so I gave him up; but I stumbled on him and +Huck right at my door, and so I just brought them along in a hurry." + +"And you did just right," said the widow. "Come with me, boys." + +She took them to a bedchamber and said: + +"Now wash and dress yourselves. Here are two new suits of clothes +--shirts, socks, everything complete. They're Huck's--no, no thanks, +Huck--Mr. Jones bought one and I the other. But they'll fit both of you. +Get into them. We'll wait--come down when you are slicked up enough." + +Then she left. + + + +CHAPTER XXXIV + +HUCK said: "Tom, we can slope, if we can find a rope. The window ain't +high from the ground." + +"Shucks! what do you want to slope for?" + +"Well, I ain't used to that kind of a crowd. I can't stand it. I ain't +going down there, Tom." + +"Oh, bother! It ain't anything. I don't mind it a bit. I'll take care +of you." + +Sid appeared. + +"Tom," said he, "auntie has been waiting for you all the afternoon. +Mary got your Sunday clothes ready, and everybody's been fretting about +you. Say--ain't this grease and clay, on your clothes?" + +"Now, Mr. Siddy, you jist 'tend to your own business. What's all this +blow-out about, anyway?" + +"It's one of the widow's parties that she's always having. This time +it's for the Welshman and his sons, on account of that scrape they +helped her out of the other night. And say--I can tell you something, +if you want to know." + +"Well, what?" + +"Why, old Mr. Jones is going to try to spring something on the people +here to-night, but I overheard him tell auntie to-day about it, as a +secret, but I reckon it's not much of a secret now. Everybody knows +--the widow, too, for all she tries to let on she don't. Mr. Jones was +bound Huck should be here--couldn't get along with his grand secret +without Huck, you know!" + +"Secret about what, Sid?" + +"About Huck tracking the robbers to the widow's. I reckon Mr. Jones +was going to make a grand time over his surprise, but I bet you it will +drop pretty flat." + +Sid chuckled in a very contented and satisfied way. + +"Sid, was it you that told?" + +"Oh, never mind who it was. SOMEBODY told--that's enough." + +"Sid, there's only one person in this town mean enough to do that, and +that's you. If you had been in Huck's place you'd 'a' sneaked down the +hill and never told anybody on the robbers. You can't do any but mean +things, and you can't bear to see anybody praised for doing good ones. +There--no thanks, as the widow says"--and Tom cuffed Sid's ears and +helped him to the door with several kicks. "Now go and tell auntie if +you dare--and to-morrow you'll catch it!" + +Some minutes later the widow's guests were at the supper-table, and a +dozen children were propped up at little side-tables in the same room, +after the fashion of that country and that day. At the proper time Mr. +Jones made his little speech, in which he thanked the widow for the +honor she was doing himself and his sons, but said that there was +another person whose modesty-- + +And so forth and so on. He sprung his secret about Huck's share in the +adventure in the finest dramatic manner he was master of, but the +surprise it occasioned was largely counterfeit and not as clamorous and +effusive as it might have been under happier circumstances. However, +the widow made a pretty fair show of astonishment, and heaped so many +compliments and so much gratitude upon Huck that he almost forgot the +nearly intolerable discomfort of his new clothes in the entirely +intolerable discomfort of being set up as a target for everybody's gaze +and everybody's laudations. + +The widow said she meant to give Huck a home under her roof and have +him educated; and that when she could spare the money she would start +him in business in a modest way. Tom's chance was come. He said: + +"Huck don't need it. Huck's rich." + +Nothing but a heavy strain upon the good manners of the company kept +back the due and proper complimentary laugh at this pleasant joke. But +the silence was a little awkward. Tom broke it: + +"Huck's got money. Maybe you don't believe it, but he's got lots of +it. Oh, you needn't smile--I reckon I can show you. You just wait a +minute." + +Tom ran out of doors. The company looked at each other with a +perplexed interest--and inquiringly at Huck, who was tongue-tied. + +"Sid, what ails Tom?" said Aunt Polly. "He--well, there ain't ever any +making of that boy out. I never--" + +Tom entered, struggling with the weight of his sacks, and Aunt Polly +did not finish her sentence. Tom poured the mass of yellow coin upon +the table and said: + +"There--what did I tell you? Half of it's Huck's and half of it's mine!" + +The spectacle took the general breath away. All gazed, nobody spoke +for a moment. Then there was a unanimous call for an explanation. Tom +said he could furnish it, and he did. The tale was long, but brimful of +interest. There was scarcely an interruption from any one to break the +charm of its flow. When he had finished, Mr. Jones said: + +"I thought I had fixed up a little surprise for this occasion, but it +don't amount to anything now. This one makes it sing mighty small, I'm +willing to allow." + +The money was counted. The sum amounted to a little over twelve +thousand dollars. It was more than any one present had ever seen at one +time before, though several persons were there who were worth +considerably more than that in property. + + + +CHAPTER XXXV + +THE reader may rest satisfied that Tom's and Huck's windfall made a +mighty stir in the poor little village of St. Petersburg. So vast a +sum, all in actual cash, seemed next to incredible. It was talked +about, gloated over, glorified, until the reason of many of the +citizens tottered under the strain of the unhealthy excitement. Every +"haunted" house in St. Petersburg and the neighboring villages was +dissected, plank by plank, and its foundations dug up and ransacked for +hidden treasure--and not by boys, but men--pretty grave, unromantic +men, too, some of them. Wherever Tom and Huck appeared they were +courted, admired, stared at. The boys were not able to remember that +their remarks had possessed weight before; but now their sayings were +treasured and repeated; everything they did seemed somehow to be +regarded as remarkable; they had evidently lost the power of doing and +saying commonplace things; moreover, their past history was raked up +and discovered to bear marks of conspicuous originality. The village +paper published biographical sketches of the boys. + +The Widow Douglas put Huck's money out at six per cent., and Judge +Thatcher did the same with Tom's at Aunt Polly's request. Each lad had +an income, now, that was simply prodigious--a dollar for every week-day +in the year and half of the Sundays. It was just what the minister got +--no, it was what he was promised--he generally couldn't collect it. A +dollar and a quarter a week would board, lodge, and school a boy in +those old simple days--and clothe him and wash him, too, for that +matter. + +Judge Thatcher had conceived a great opinion of Tom. He said that no +commonplace boy would ever have got his daughter out of the cave. When +Becky told her father, in strict confidence, how Tom had taken her +whipping at school, the Judge was visibly moved; and when she pleaded +grace for the mighty lie which Tom had told in order to shift that +whipping from her shoulders to his own, the Judge said with a fine +outburst that it was a noble, a generous, a magnanimous lie--a lie that +was worthy to hold up its head and march down through history breast to +breast with George Washington's lauded Truth about the hatchet! Becky +thought her father had never looked so tall and so superb as when he +walked the floor and stamped his foot and said that. She went straight +off and told Tom about it. + +Judge Thatcher hoped to see Tom a great lawyer or a great soldier some +day. He said he meant to look to it that Tom should be admitted to the +National Military Academy and afterward trained in the best law school +in the country, in order that he might be ready for either career or +both. + +Huck Finn's wealth and the fact that he was now under the Widow +Douglas' protection introduced him into society--no, dragged him into +it, hurled him into it--and his sufferings were almost more than he +could bear. The widow's servants kept him clean and neat, combed and +brushed, and they bedded him nightly in unsympathetic sheets that had +not one little spot or stain which he could press to his heart and know +for a friend. He had to eat with a knife and fork; he had to use +napkin, cup, and plate; he had to learn his book, he had to go to +church; he had to talk so properly that speech was become insipid in +his mouth; whithersoever he turned, the bars and shackles of +civilization shut him in and bound him hand and foot. + +He bravely bore his miseries three weeks, and then one day turned up +missing. For forty-eight hours the widow hunted for him everywhere in +great distress. The public were profoundly concerned; they searched +high and low, they dragged the river for his body. Early the third +morning Tom Sawyer wisely went poking among some old empty hogsheads +down behind the abandoned slaughter-house, and in one of them he found +the refugee. Huck had slept there; he had just breakfasted upon some +stolen odds and ends of food, and was lying off, now, in comfort, with +his pipe. He was unkempt, uncombed, and clad in the same old ruin of +rags that had made him picturesque in the days when he was free and +happy. Tom routed him out, told him the trouble he had been causing, +and urged him to go home. Huck's face lost its tranquil content, and +took a melancholy cast. He said: + +"Don't talk about it, Tom. I've tried it, and it don't work; it don't +work, Tom. It ain't for me; I ain't used to it. The widder's good to +me, and friendly; but I can't stand them ways. She makes me get up just +at the same time every morning; she makes me wash, they comb me all to +thunder; she won't let me sleep in the woodshed; I got to wear them +blamed clothes that just smothers me, Tom; they don't seem to any air +git through 'em, somehow; and they're so rotten nice that I can't set +down, nor lay down, nor roll around anywher's; I hain't slid on a +cellar-door for--well, it 'pears to be years; I got to go to church and +sweat and sweat--I hate them ornery sermons! I can't ketch a fly in +there, I can't chaw. I got to wear shoes all Sunday. The widder eats by +a bell; she goes to bed by a bell; she gits up by a bell--everything's +so awful reg'lar a body can't stand it." + +"Well, everybody does that way, Huck." + +"Tom, it don't make no difference. I ain't everybody, and I can't +STAND it. It's awful to be tied up so. And grub comes too easy--I don't +take no interest in vittles, that way. I got to ask to go a-fishing; I +got to ask to go in a-swimming--dern'd if I hain't got to ask to do +everything. Well, I'd got to talk so nice it wasn't no comfort--I'd got +to go up in the attic and rip out awhile, every day, to git a taste in +my mouth, or I'd a died, Tom. The widder wouldn't let me smoke; she +wouldn't let me yell, she wouldn't let me gape, nor stretch, nor +scratch, before folks--" [Then with a spasm of special irritation and +injury]--"And dad fetch it, she prayed all the time! I never see such a +woman! I HAD to shove, Tom--I just had to. And besides, that school's +going to open, and I'd a had to go to it--well, I wouldn't stand THAT, +Tom. Looky here, Tom, being rich ain't what it's cracked up to be. It's +just worry and worry, and sweat and sweat, and a-wishing you was dead +all the time. Now these clothes suits me, and this bar'l suits me, and +I ain't ever going to shake 'em any more. Tom, I wouldn't ever got into +all this trouble if it hadn't 'a' ben for that money; now you just take +my sheer of it along with your'n, and gimme a ten-center sometimes--not +many times, becuz I don't give a dern for a thing 'thout it's tollable +hard to git--and you go and beg off for me with the widder." + +"Oh, Huck, you know I can't do that. 'Tain't fair; and besides if +you'll try this thing just a while longer you'll come to like it." + +"Like it! Yes--the way I'd like a hot stove if I was to set on it long +enough. No, Tom, I won't be rich, and I won't live in them cussed +smothery houses. I like the woods, and the river, and hogsheads, and +I'll stick to 'em, too. Blame it all! just as we'd got guns, and a +cave, and all just fixed to rob, here this dern foolishness has got to +come up and spile it all!" + +Tom saw his opportunity-- + +"Lookyhere, Huck, being rich ain't going to keep me back from turning +robber." + +"No! Oh, good-licks; are you in real dead-wood earnest, Tom?" + +"Just as dead earnest as I'm sitting here. But Huck, we can't let you +into the gang if you ain't respectable, you know." + +Huck's joy was quenched. + +"Can't let me in, Tom? Didn't you let me go for a pirate?" + +"Yes, but that's different. A robber is more high-toned than what a +pirate is--as a general thing. In most countries they're awful high up +in the nobility--dukes and such." + +"Now, Tom, hain't you always ben friendly to me? You wouldn't shet me +out, would you, Tom? You wouldn't do that, now, WOULD you, Tom?" + +"Huck, I wouldn't want to, and I DON'T want to--but what would people +say? Why, they'd say, 'Mph! Tom Sawyer's Gang! pretty low characters in +it!' They'd mean you, Huck. You wouldn't like that, and I wouldn't." + +Huck was silent for some time, engaged in a mental struggle. Finally +he said: + +"Well, I'll go back to the widder for a month and tackle it and see if +I can come to stand it, if you'll let me b'long to the gang, Tom." + +"All right, Huck, it's a whiz! Come along, old chap, and I'll ask the +widow to let up on you a little, Huck." + +"Will you, Tom--now will you? That's good. If she'll let up on some of +the roughest things, I'll smoke private and cuss private, and crowd +through or bust. When you going to start the gang and turn robbers?" + +"Oh, right off. We'll get the boys together and have the initiation +to-night, maybe." + +"Have the which?" + +"Have the initiation." + +"What's that?" + +"It's to swear to stand by one another, and never tell the gang's +secrets, even if you're chopped all to flinders, and kill anybody and +all his family that hurts one of the gang." + +"That's gay--that's mighty gay, Tom, I tell you." + +"Well, I bet it is. And all that swearing's got to be done at +midnight, in the lonesomest, awfulest place you can find--a ha'nted +house is the best, but they're all ripped up now." + +"Well, midnight's good, anyway, Tom." + +"Yes, so it is. And you've got to swear on a coffin, and sign it with +blood." + +"Now, that's something LIKE! Why, it's a million times bullier than +pirating. I'll stick to the widder till I rot, Tom; and if I git to be +a reg'lar ripper of a robber, and everybody talking 'bout it, I reckon +she'll be proud she snaked me in out of the wet." + + + +CONCLUSION + +SO endeth this chronicle. It being strictly a history of a BOY, it +must stop here; the story could not go much further without becoming +the history of a MAN. When one writes a novel about grown people, he +knows exactly where to stop--that is, with a marriage; but when he +writes of juveniles, he must stop where he best can. + +Most of the characters that perform in this book still live, and are +prosperous and happy. Some day it may seem worth while to take up the +story of the younger ones again and see what sort of men and women they +turned out to be; therefore it will be wisest not to reveal any of that +part of their lives at present. + + + + + +End of the Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete +by Mark Twain (Samuel Clemens) + +*** END OF THIS PROJECT GUTENBERG EBOOK TOM SAWYER *** + +***** This file should be named 74.txt or 74.zip ***** +This and all associated files of various formats will be found in: + http://www.gutenberg.net/7/74/ + +Produced by David Widger. The previous edition was update by Jose +Menendez. + + +Updated editions will replace the previous one--the old editions +will be renamed. + +Creating the works from public domain print editions means that no +one owns a United States copyright in these works, so the Foundation +(and you!) can copy and distribute it in the United States without +permission and without paying copyright royalties. Special rules, +set forth in the General Terms of Use part of this license, apply to +copying and distributing Project Gutenberg-tm electronic works to +protect the PROJECT GUTENBERG-tm concept and trademark. Project +Gutenberg is a registered trademark, and may not be used if you +charge for the eBooks, unless you receive specific permission. If you +do not charge anything for copies of this eBook, complying with the +rules is very easy. You may use this eBook for nearly any purpose +such as creation of derivative works, reports, performances and +research. They may be modified and printed and given away--you may do +practically ANYTHING with public domain eBooks. Redistribution is +subject to the trademark license, especially commercial +redistribution. + + + +*** START: FULL LICENSE *** + +THE FULL PROJECT GUTENBERG LICENSE +PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK + +To protect the Project Gutenberg-tm mission of promoting the free +distribution of electronic works, by using or distributing this work +(or any other work associated in any way with the phrase "Project +Gutenberg"), you agree to comply with all the terms of the Full Project +Gutenberg-tm License (available with this file or online at +http://gutenberg.net/license). + + +Section 1. General Terms of Use and Redistributing Project Gutenberg-tm +electronic works + +1.A. By reading or using any part of this Project Gutenberg-tm +electronic work, you indicate that you have read, understand, agree to +and accept all the terms of this license and intellectual property +(trademark/copyright) agreement. If you do not agree to abide by all +the terms of this agreement, you must cease using and return or destroy +all copies of Project Gutenberg-tm electronic works in your possession. +If you paid a fee for obtaining a copy of or access to a Project +Gutenberg-tm electronic work and you do not agree to be bound by the +terms of this agreement, you may obtain a refund from the person or +entity to whom you paid the fee as set forth in paragraph 1.E.8. + +1.B. "Project Gutenberg" is a registered trademark. It may only be +used on or associated in any way with an electronic work by people who +agree to be bound by the terms of this agreement. There are a few +things that you can do with most Project Gutenberg-tm electronic works +even without complying with the full terms of this agreement. See +paragraph 1.C below. There are a lot of things you can do with Project +Gutenberg-tm electronic works if you follow the terms of this agreement +and help preserve free future access to Project Gutenberg-tm electronic +works. See paragraph 1.E below. + +1.C. The Project Gutenberg Literary Archive Foundation ("the Foundation" +or PGLAF), owns a compilation copyright in the collection of Project +Gutenberg-tm electronic works. Nearly all the individual works in the +collection are in the public domain in the United States. If an +individual work is in the public domain in the United States and you are +located in the United States, we do not claim a right to prevent you from +copying, distributing, performing, displaying or creating derivative +works based on the work as long as all references to Project Gutenberg +are removed. Of course, we hope that you will support the Project +Gutenberg-tm mission of promoting free access to electronic works by +freely sharing Project Gutenberg-tm works in compliance with the terms of +this agreement for keeping the Project Gutenberg-tm name associated with +the work. You can easily comply with the terms of this agreement by +keeping this work in the same format with its attached full Project +Gutenberg-tm License when you share it without charge with others. + +1.D. The copyright laws of the place where you are located also govern +what you can do with this work. Copyright laws in most countries are in +a constant state of change. If you are outside the United States, check +the laws of your country in addition to the terms of this agreement +before downloading, copying, displaying, performing, distributing or +creating derivative works based on this work or any other Project +Gutenberg-tm work. The Foundation makes no representations concerning +the copyright status of any work in any country outside the United +States. + +1.E. Unless you have removed all references to Project Gutenberg: + +1.E.1. The following sentence, with active links to, or other immediate +access to, the full Project Gutenberg-tm License must appear prominently +whenever any copy of a Project Gutenberg-tm work (any work on which the +phrase "Project Gutenberg" appears, or with which the phrase "Project +Gutenberg" is associated) is accessed, displayed, performed, viewed, +copied or distributed: + +This eBook is for the use of anyone anywhere at no cost and with +almost no restrictions whatsoever. You may copy it, give it away or +re-use it under the terms of the Project Gutenberg License included +with this eBook or online at www.gutenberg.net + +1.E.2. If an individual Project Gutenberg-tm electronic work is derived +from the public domain (does not contain a notice indicating that it is +posted with permission of the copyright holder), the work can be copied +and distributed to anyone in the United States without paying any fees +or charges. If you are redistributing or providing access to a work +with the phrase "Project Gutenberg" associated with or appearing on the +work, you must comply either with the requirements of paragraphs 1.E.1 +through 1.E.7 or obtain permission for the use of the work and the +Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or +1.E.9. + +1.E.3. If an individual Project Gutenberg-tm electronic work is posted +with the permission of the copyright holder, your use and distribution +must comply with both paragraphs 1.E.1 through 1.E.7 and any additional +terms imposed by the copyright holder. Additional terms will be linked +to the Project Gutenberg-tm License for all works posted with the +permission of the copyright holder found at the beginning of this work. + +1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm +License terms from this work, or any files containing a part of this +work or any other work associated with Project Gutenberg-tm. + +1.E.5. Do not copy, display, perform, distribute or redistribute this +electronic work, or any part of this electronic work, without +prominently displaying the sentence set forth in paragraph 1.E.1 with +active links or immediate access to the full terms of the Project +Gutenberg-tm License. + +1.E.6. You may convert to and distribute this work in any binary, +compressed, marked up, nonproprietary or proprietary form, including any +word processing or hypertext form. However, if you provide access to or +distribute copies of a Project Gutenberg-tm work in a format other than +"Plain Vanilla ASCII" or other format used in the official version +posted on the official Project Gutenberg-tm web site (www.gutenberg.net), +you must, at no additional cost, fee or expense to the user, provide a +copy, a means of exporting a copy, or a means of obtaining a copy upon +request, of the work in its original "Plain Vanilla ASCII" or other +form. Any alternate format must include the full Project Gutenberg-tm +License as specified in paragraph 1.E.1. + +1.E.7. Do not charge a fee for access to, viewing, displaying, +performing, copying or distributing any Project Gutenberg-tm works +unless you comply with paragraph 1.E.8 or 1.E.9. + +1.E.8. You may charge a reasonable fee for copies of or providing +access to or distributing Project Gutenberg-tm electronic works provided +that + +- You pay a royalty fee of 20% of the gross profits you derive from + the use of Project Gutenberg-tm works calculated using the method + you already use to calculate your applicable taxes. The fee is + owed to the owner of the Project Gutenberg-tm trademark, but he + has agreed to donate royalties under this paragraph to the + Project Gutenberg Literary Archive Foundation. Royalty payments + must be paid within 60 days following each date on which you + prepare (or are legally required to prepare) your periodic tax + returns. Royalty payments should be clearly marked as such and + sent to the Project Gutenberg Literary Archive Foundation at the + address specified in Section 4, "Information about donations to + the Project Gutenberg Literary Archive Foundation." + +- You provide a full refund of any money paid by a user who notifies + you in writing (or by e-mail) within 30 days of receipt that s/he + does not agree to the terms of the full Project Gutenberg-tm + License. You must require such a user to return or + destroy all copies of the works possessed in a physical medium + and discontinue all use of and all access to other copies of + Project Gutenberg-tm works. + +- You provide, in accordance with paragraph 1.F.3, a full refund of any + money paid for a work or a replacement copy, if a defect in the + electronic work is discovered and reported to you within 90 days + of receipt of the work. + +- You comply with all other terms of this agreement for free + distribution of Project Gutenberg-tm works. + +1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm +electronic work or group of works on different terms than are set +forth in this agreement, you must obtain permission in writing from +both the Project Gutenberg Literary Archive Foundation and Michael +Hart, the owner of the Project Gutenberg-tm trademark. Contact the +Foundation as set forth in Section 3 below. + +1.F. + +1.F.1. Project Gutenberg volunteers and employees expend considerable +effort to identify, do copyright research on, transcribe and proofread +public domain works in creating the Project Gutenberg-tm +collection. Despite these efforts, Project Gutenberg-tm electronic +works, and the medium on which they may be stored, may contain +"Defects," such as, but not limited to, incomplete, inaccurate or +corrupt data, transcription errors, a copyright or other intellectual +property infringement, a defective or damaged disk or other medium, a +computer virus, or computer codes that damage or cannot be read by +your equipment. + +1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right +of Replacement or Refund" described in paragraph 1.F.3, the Project +Gutenberg Literary Archive Foundation, the owner of the Project +Gutenberg-tm trademark, and any other party distributing a Project +Gutenberg-tm electronic work under this agreement, disclaim all +liability to you for damages, costs and expenses, including legal +fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT +LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE +PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE +TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE +LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR +INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH +DAMAGE. + +1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a +defect in this electronic work within 90 days of receiving it, you can +receive a refund of the money (if any) you paid for it by sending a +written explanation to the person you received the work from. If you +received the work on a physical medium, you must return the medium with +your written explanation. The person or entity that provided you with +the defective work may elect to provide a replacement copy in lieu of a +refund. If you received the work electronically, the person or entity +providing it to you may choose to give you a second opportunity to +receive the work electronically in lieu of a refund. If the second copy +is also defective, you may demand a refund in writing without further +opportunities to fix the problem. + +1.F.4. Except for the limited right of replacement or refund set forth +in paragraph 1.F.3, this work is provided to you 'AS-IS' WITH NO OTHER +WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE. + +1.F.5. Some states do not allow disclaimers of certain implied +warranties or the exclusion or limitation of certain types of damages. +If any disclaimer or limitation set forth in this agreement violates the +law of the state applicable to this agreement, the agreement shall be +interpreted to make the maximum disclaimer or limitation permitted by +the applicable state law. The invalidity or unenforceability of any +provision of this agreement shall not void the remaining provisions. + +1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the +trademark owner, any agent or employee of the Foundation, anyone +providing copies of Project Gutenberg-tm electronic works in accordance +with this agreement, and any volunteers associated with the production, +promotion and distribution of Project Gutenberg-tm electronic works, +harmless from all liability, costs and expenses, including legal fees, +that arise directly or indirectly from any of the following which you do +or cause to occur: (a) distribution of this or any Project Gutenberg-tm +work, (b) alteration, modification, or additions or deletions to any +Project Gutenberg-tm work, and (c) any Defect you cause. + + +Section 2. Information about the Mission of Project Gutenberg-tm + +Project Gutenberg-tm is synonymous with the free distribution of +electronic works in formats readable by the widest variety of computers +including obsolete, old, middle-aged and new computers. It exists +because of the efforts of hundreds of volunteers and donations from +people in all walks of life. + +Volunteers and financial support to provide volunteers with the +assistance they need, is critical to reaching Project Gutenberg-tm's +goals and ensuring that the Project Gutenberg-tm collection will +remain freely available for generations to come. In 2001, the Project +Gutenberg Literary Archive Foundation was created to provide a secure +and permanent future for Project Gutenberg-tm and future generations. +To learn more about the Project Gutenberg Literary Archive Foundation +and how your efforts and donations can help, see Sections 3 and 4 +and the Foundation web page at http://www.pglaf.org. + + +Section 3. Information about the Project Gutenberg Literary Archive +Foundation + +The Project Gutenberg Literary Archive Foundation is a non profit +501(c)(3) educational corporation organized under the laws of the +state of Mississippi and granted tax exempt status by the Internal +Revenue Service. The Foundation's EIN or federal tax identification +number is 64-6221541. Its 501(c)(3) letter is posted at +http://pglaf.org/fundraising. Contributions to the Project Gutenberg +Literary Archive Foundation are tax deductible to the full extent +permitted by U.S. federal laws and your state's laws. + +The Foundation's principal office is located at 4557 Melan Dr. S. +Fairbanks, AK, 99712., but its volunteers and employees are scattered +throughout numerous locations. Its business office is located at +809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email +business@pglaf.org. Email contact links and up to date contact +information can be found at the Foundation's web site and official +page at http://pglaf.org + +For additional contact information: + Dr. Gregory B. Newby + Chief Executive and Director + gbnewby@pglaf.org + + +Section 4. Information about Donations to the Project Gutenberg +Literary Archive Foundation + +Project Gutenberg-tm depends upon and cannot survive without wide +spread public support and donations to carry out its mission of +increasing the number of public domain and licensed works that can be +freely distributed in machine readable form accessible by the widest +array of equipment including outdated equipment. Many small donations +($1 to $5,000) are particularly important to maintaining tax exempt +status with the IRS. + +The Foundation is committed to complying with the laws regulating +charities and charitable donations in all 50 states of the United +States. Compliance requirements are not uniform and it takes a +considerable effort, much paperwork and many fees to meet and keep up +with these requirements. We do not solicit donations in locations +where we have not received written confirmation of compliance. To +SEND DONATIONS or determine the status of compliance for any +particular state visit http://pglaf.org + +While we cannot and do not solicit contributions from states where we +have not met the solicitation requirements, we know of no prohibition +against accepting unsolicited donations from donors in such states who +approach us with offers to donate. + +International donations are gratefully accepted, but we cannot make +any statements concerning tax treatment of donations received from +outside the United States. U.S. laws alone swamp our small staff. + +Please check the Project Gutenberg Web pages for current donation +methods and addresses. Donations are accepted in a number of other +ways including including checks, online payments and credit card +donations. To donate, please visit: http://pglaf.org/donate + + +Section 5. General Information About Project Gutenberg-tm electronic +works. + +Professor Michael S. Hart is the originator of the Project Gutenberg-tm +concept of a library of electronic works that could be freely shared +with anyone. For thirty years, he produced and distributed Project +Gutenberg-tm eBooks with only a loose network of volunteer support. + + +Project Gutenberg-tm eBooks are often created from several printed +editions, all of which are confirmed as Public Domain in the U.S. +unless a copyright notice is included. Thus, we do not necessarily +keep eBooks in compliance with any particular paper edition. + + +Most people start at our Web site which has the main PG search facility: + + http://www.gutenberg.net + +This Web site includes information about Project Gutenberg-tm, +including how to make donations to the Project Gutenberg Literary +Archive Foundation, how to help produce our new eBooks, and how to +subscribe to our email newsletter to hear about new eBooks. diff --git a/src/pkg/compress/zlib/Makefile b/src/pkg/compress/zlib/Makefile deleted file mode 100644 index 791072d34..000000000 --- a/src/pkg/compress/zlib/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=compress/zlib -GOFILES=\ - reader.go\ - writer.go\ - -include ../../../Make.pkg diff --git a/src/pkg/compress/zlib/reader.go b/src/pkg/compress/zlib/reader.go index 78dabdf4d..f38ef5a88 100644 --- a/src/pkg/compress/zlib/reader.go +++ b/src/pkg/compress/zlib/reader.go @@ -26,36 +26,41 @@ package zlib import ( "bufio" "compress/flate" + "errors" "hash" "hash/adler32" "io" - "os" ) const zlibDeflate = 8 -var ChecksumError = os.NewError("zlib checksum error") -var HeaderError = os.NewError("invalid zlib header") -var DictionaryError = os.NewError("invalid zlib dictionary") +var ( + // ErrChecksum is returned when reading ZLIB data that has an invalid checksum. + ErrChecksum = errors.New("zlib: invalid checksum") + // ErrDictionary is returned when reading ZLIB data that has an invalid dictionary. + ErrDictionary = errors.New("zlib: invalid dictionary") + // ErrHeader is returned when reading ZLIB data that has an invalid header. + ErrHeader = errors.New("zlib: invalid header") +) type reader struct { r flate.Reader decompressor io.ReadCloser digest hash.Hash32 - err os.Error + err error scratch [4]byte } // NewReader creates a new io.ReadCloser that satisfies reads by decompressing data read from r. // The implementation buffers input and may read more data than necessary from r. // It is the caller's responsibility to call Close on the ReadCloser when done. -func NewReader(r io.Reader) (io.ReadCloser, os.Error) { +func NewReader(r io.Reader) (io.ReadCloser, error) { return NewReaderDict(r, nil) } // NewReaderDict is like NewReader but uses a preset dictionary. // NewReaderDict ignores the dictionary if the compressed data does not refer to it. -func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, os.Error) { +func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, error) { z := new(reader) if fr, ok := r.(flate.Reader); ok { z.r = fr @@ -68,7 +73,7 @@ func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, os.Error) { } h := uint(z.scratch[0])<<8 | uint(z.scratch[1]) if (z.scratch[0]&0x0f != zlibDeflate) || (h%31 != 0) { - return nil, HeaderError + return nil, ErrHeader } if z.scratch[1]&0x20 != 0 { _, err = io.ReadFull(z.r, z.scratch[0:4]) @@ -77,7 +82,7 @@ func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, os.Error) { } checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3]) if checksum != adler32.Checksum(dict) { - return nil, DictionaryError + return nil, ErrDictionary } z.decompressor = flate.NewReaderDict(z.r, dict) } else { @@ -87,7 +92,7 @@ func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, os.Error) { return z, nil } -func (z *reader) Read(p []byte) (n int, err os.Error) { +func (z *reader) Read(p []byte) (n int, err error) { if z.err != nil { return 0, z.err } @@ -97,7 +102,7 @@ func (z *reader) Read(p []byte) (n int, err os.Error) { n, err = z.decompressor.Read(p) z.digest.Write(p[0:n]) - if n != 0 || err != os.EOF { + if n != 0 || err != io.EOF { z.err = err return } @@ -110,14 +115,14 @@ func (z *reader) Read(p []byte) (n int, err os.Error) { // ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952). checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3]) if checksum != z.digest.Sum32() { - z.err = ChecksumError + z.err = ErrChecksum return 0, z.err } return } // Calling Close does not close the wrapped io.Reader originally passed to NewReader. -func (z *reader) Close() os.Error { +func (z *reader) Close() error { if z.err != nil { return z.err } diff --git a/src/pkg/compress/zlib/reader_test.go b/src/pkg/compress/zlib/reader_test.go index 195db446c..3b02a0868 100644 --- a/src/pkg/compress/zlib/reader_test.go +++ b/src/pkg/compress/zlib/reader_test.go @@ -7,7 +7,6 @@ package zlib import ( "bytes" "io" - "os" "testing" ) @@ -16,7 +15,7 @@ type zlibTest struct { raw string compressed []byte dict []byte - err os.Error + err error } // Compare-to-golden test data was generated by the ZLIB example program at @@ -46,14 +45,14 @@ var zlibTests = []zlibTest{ "", []byte{0x78, 0x9f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01}, nil, - HeaderError, + ErrHeader, }, { "bad checksum", "", []byte{0x78, 0x9c, 0x03, 0x00, 0x00, 0x00, 0x00, 0xff}, nil, - ChecksumError, + ErrChecksum, }, { "not enough data", @@ -96,7 +95,7 @@ var zlibTests = []zlibTest{ []byte{ 0x48, 0x65, 0x6c, 0x6c, }, - DictionaryError, + ErrDictionary, }, } diff --git a/src/pkg/compress/zlib/writer.go b/src/pkg/compress/zlib/writer.go index 8f86e9c4c..cd8dea460 100644 --- a/src/pkg/compress/zlib/writer.go +++ b/src/pkg/compress/zlib/writer.go @@ -6,10 +6,10 @@ package zlib import ( "compress/flate" + "fmt" "hash" "hash/adler32" "io" - "os" ) // These constants are copied from the flate package, so that code that imports @@ -24,30 +24,55 @@ const ( // A Writer takes data written to it and writes the compressed // form of that data to an underlying writer (see NewWriter). type Writer struct { - w io.Writer - compressor *flate.Writer - digest hash.Hash32 - err os.Error - scratch [4]byte + w io.Writer + level int + dict []byte + compressor *flate.Writer + digest hash.Hash32 + err error + scratch [4]byte + wroteHeader bool } -// NewWriter calls NewWriterLevel with the default compression level. -func NewWriter(w io.Writer) (*Writer, os.Error) { - return NewWriterLevel(w, DefaultCompression) +// NewWriter creates a new Writer that satisfies writes by compressing data +// written to w. +// +// It is the caller's responsibility to call Close on the WriteCloser when done. +// Writes may be buffered and not flushed until Close. +func NewWriter(w io.Writer) *Writer { + z, _ := NewWriterLevelDict(w, DefaultCompression, nil) + return z } -// NewWriterLevel calls NewWriterDict with no dictionary. -func NewWriterLevel(w io.Writer, level int) (*Writer, os.Error) { - return NewWriterDict(w, level, nil) +// NewWriterLevel is like NewWriter but specifies the compression level instead +// of assuming DefaultCompression. +// +// The compression level can be DefaultCompression, NoCompression, or any +// integer value between BestSpeed and BestCompression inclusive. The error +// returned will be nil if the level is valid. +func NewWriterLevel(w io.Writer, level int) (*Writer, error) { + return NewWriterLevelDict(w, level, nil) } -// NewWriterDict creates a new io.WriteCloser that satisfies writes by compressing data written to w. -// It is the caller's responsibility to call Close on the WriteCloser when done. -// level is the compression level, which can be DefaultCompression, NoCompression, -// or any integer value between BestSpeed and BestCompression (inclusive). -// dict is the preset dictionary to compress with, or nil to use no dictionary. -func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, os.Error) { - z := new(Writer) +// NewWriterLevelDict is like NewWriterLevel but specifies a dictionary to +// compress with. +// +// The dictionary may be nil. If not, its contents should not be modified until +// the Writer is closed. +func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) { + if level < DefaultCompression || level > BestCompression { + return nil, fmt.Errorf("zlib: invalid compression level: %d", level) + } + return &Writer{ + w: w, + level: level, + dict: dict, + }, nil +} + +// writeHeader writes the ZLIB header. +func (z *Writer) writeHeader() (err error) { + z.wroteHeader = true // ZLIB has a two-byte header (as documented in RFC 1950). // The first four bits is the CINFO (compression info), which is 7 for the default deflate window size. // The next four bits is the CM (compression method), which is 8 for deflate. @@ -56,7 +81,7 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, os.Error) { // 0=fastest, 1=fast, 2=default, 3=best. // The next bit, FDICT, is set if a dictionary is given. // The final five FCHECK bits form a mod-31 checksum. - switch level { + switch z.level { case 0, 1: z.scratch[1] = 0 << 6 case 2, 3, 4, 5: @@ -66,35 +91,41 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, os.Error) { case 7, 8, 9: z.scratch[1] = 3 << 6 default: - return nil, os.NewError("level out of range") + panic("unreachable") } - if dict != nil { + if z.dict != nil { z.scratch[1] |= 1 << 5 } z.scratch[1] += uint8(31 - (uint16(z.scratch[0])<<8+uint16(z.scratch[1]))%31) - _, err := w.Write(z.scratch[0:2]) - if err != nil { - return nil, err + if _, err = z.w.Write(z.scratch[0:2]); err != nil { + return err } - if dict != nil { + if z.dict != nil { // The next four bytes are the Adler-32 checksum of the dictionary. - checksum := adler32.Checksum(dict) + checksum := adler32.Checksum(z.dict) z.scratch[0] = uint8(checksum >> 24) z.scratch[1] = uint8(checksum >> 16) z.scratch[2] = uint8(checksum >> 8) z.scratch[3] = uint8(checksum >> 0) - _, err = w.Write(z.scratch[0:4]) - if err != nil { - return nil, err + if _, err = z.w.Write(z.scratch[0:4]); err != nil { + return err } } - z.w = w - z.compressor = flate.NewWriterDict(w, level, dict) + z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict) + if err != nil { + return err + } z.digest = adler32.New() - return z, nil + return nil } -func (z *Writer) Write(p []byte) (n int, err os.Error) { +// Write writes a compressed form of p to the underlying io.Writer. The +// compressed bytes are not necessarily flushed until the Writer is closed or +// explicitly flushed. +func (z *Writer) Write(p []byte) (n int, err error) { + if !z.wroteHeader { + z.err = z.writeHeader() + } if z.err != nil { return 0, z.err } @@ -110,8 +141,11 @@ func (z *Writer) Write(p []byte) (n int, err os.Error) { return } -// Flush flushes the underlying compressor. -func (z *Writer) Flush() os.Error { +// Flush flushes the Writer to its underlying io.Writer. +func (z *Writer) Flush() error { + if !z.wroteHeader { + z.err = z.writeHeader() + } if z.err != nil { return z.err } @@ -120,7 +154,10 @@ func (z *Writer) Flush() os.Error { } // Calling Close does not close the wrapped io.Writer originally passed to NewWriter. -func (z *Writer) Close() os.Error { +func (z *Writer) Close() error { + if !z.wroteHeader { + z.err = z.writeHeader() + } if z.err != nil { return z.err } diff --git a/src/pkg/compress/zlib/writer_test.go b/src/pkg/compress/zlib/writer_test.go index 32f05ab68..aee1a5c2f 100644 --- a/src/pkg/compress/zlib/writer_test.go +++ b/src/pkg/compress/zlib/writer_test.go @@ -52,17 +52,13 @@ func testLevelDict(t *testing.T, fn string, b0 []byte, level int, d string) { defer piper.Close() go func() { defer pipew.Close() - zlibw, err := NewWriterDict(pipew, level, dict) + zlibw, err := NewWriterLevelDict(pipew, level, dict) if err != nil { t.Errorf("%s (level=%d, dict=%q): %v", fn, level, d, err) return } defer zlibw.Close() _, err = zlibw.Write(b0) - if err == os.EPIPE { - // Fail, but do not report the error, as some other (presumably reported) error broke the pipe. - return - } if err != nil { t.Errorf("%s (level=%d, dict=%q): %v", fn, level, d, err) return @@ -128,10 +124,10 @@ func TestWriterDict(t *testing.T) { func TestWriterDictIsUsed(t *testing.T) { var input = []byte("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") - buf := bytes.NewBuffer(nil) - compressor, err := NewWriterDict(buf, BestCompression, input) + var buf bytes.Buffer + compressor, err := NewWriterLevelDict(&buf, BestCompression, input) if err != nil { - t.Errorf("error in NewWriterDict: %s", err) + t.Errorf("error in NewWriterLevelDict: %s", err) return } compressor.Write(input) diff --git a/src/pkg/container/heap/Makefile b/src/pkg/container/heap/Makefile deleted file mode 100644 index 4291d1122..000000000 --- a/src/pkg/container/heap/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=container/heap -GOFILES=\ - heap.go\ - -include ../../../Make.pkg diff --git a/src/pkg/container/heap/example_test.go b/src/pkg/container/heap/example_test.go new file mode 100644 index 000000000..2050bc835 --- /dev/null +++ b/src/pkg/container/heap/example_test.go @@ -0,0 +1,105 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This example demonstrates a priority queue built using the heap interface. +package heap_test + +import ( + "container/heap" + "fmt" +) + +// An Item is something we manage in a priority queue. +type Item struct { + value string // The value of the item; arbitrary. + priority int // The priority of the item in the queue. + // The index is needed by changePriority and is maintained by the heap.Interface methods. + index int // The index of the item in the heap. +} + +// A PriorityQueue implements heap.Interface and holds Items. +type PriorityQueue []*Item + +func (pq PriorityQueue) Len() int { return len(pq) } + +func (pq PriorityQueue) Less(i, j int) bool { + // We want Pop to give us the highest, not lowest, priority so we use greater than here. + return pq[i].priority > pq[j].priority +} + +func (pq PriorityQueue) Swap(i, j int) { + pq[i], pq[j] = pq[j], pq[i] + pq[i].index = i + pq[j].index = j +} + +func (pq *PriorityQueue) Push(x interface{}) { + // Push and Pop use pointer receivers because they modify the slice's length, + // not just its contents. + // To simplify indexing expressions in these methods, we save a copy of the + // slice object. We could instead write (*pq)[i]. + a := *pq + n := len(a) + a = a[0 : n+1] + item := x.(*Item) + item.index = n + a[n] = item + *pq = a +} + +func (pq *PriorityQueue) Pop() interface{} { + a := *pq + n := len(a) + item := a[n-1] + item.index = -1 // for safety + *pq = a[0 : n-1] + return item +} + +// update is not used by the example but shows how to take the top item from +// the queue, update its priority and value, and put it back. +func (pq *PriorityQueue) update(value string, priority int) { + item := heap.Pop(pq).(*Item) + item.value = value + item.priority = priority + heap.Push(pq, item) +} + +// changePriority is not used by the example but shows how to change the +// priority of an arbitrary item. +func (pq *PriorityQueue) changePriority(item *Item, priority int) { + heap.Remove(pq, item.index) + item.priority = priority + heap.Push(pq, item) +} + +// This example pushes 10 items into a PriorityQueue and takes them out in +// order of priority. +func Example() { + const nItem = 10 + // Random priorities for the items (a permutation of 0..9, times 11)). + priorities := [nItem]int{ + 77, 22, 44, 55, 11, 88, 33, 99, 00, 66, + } + values := [nItem]string{ + "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", + } + // Create a priority queue and put some items in it. + pq := make(PriorityQueue, 0, nItem) + for i := 0; i < cap(pq); i++ { + item := &Item{ + value: values[i], + priority: priorities[i], + } + heap.Push(&pq, item) + } + // Take the items out; should arrive in decreasing priority order. + // For example, the highest priority (99) is the seventh item, so output starts with 99:"seven". + for i := 0; i < nItem; i++ { + item := heap.Pop(&pq).(*Item) + fmt.Printf("%.2d:%s ", item.priority, item.value) + } + // Output: + // 99:seven 88:five 77:zero 66:nine 55:three 44:two 33:six 22:one 11:four 00:eight +} diff --git a/src/pkg/container/heap/heap.go b/src/pkg/container/heap/heap.go index 2dfe5b43c..67018e6ba 100644 --- a/src/pkg/container/heap/heap.go +++ b/src/pkg/container/heap/heap.go @@ -3,7 +3,14 @@ // license that can be found in the LICENSE file. // Package heap provides heap operations for any type that implements -// heap.Interface. +// heap.Interface. A heap is a tree with the property that each node is the +// highest-valued node in its subtree. +// +// A heap is a common way to implement a priority queue. To build a priority +// queue, implement the Heap interface with the (negative) priority as the +// ordering for the Less method, so Push adds items while Pop removes the +// highest-priority item from the queue. The Examples include such an +// implementation; the file example_test.go has the complete source. // package heap @@ -11,14 +18,17 @@ import "sort" // Any type that implements heap.Interface may be used as a // min-heap with the following invariants (established after -// Init has been called): +// Init has been called or if the data is empty or sorted): // // !h.Less(j, i) for 0 <= i < h.Len() and j = 2*i+1 or 2*i+2 and j < h.Len() // +// Note that Push and Pop in this interface are for package heap's +// implementation to call. To add and remove things from the heap, +// use heap.Push and heap.Pop. type Interface interface { sort.Interface - Push(x interface{}) - Pop() interface{} + Push(x interface{}) // add x as element Len() + Pop() interface{} // remove and return element Len() - 1. } // A heap must be initialized before any of the heap operations diff --git a/src/pkg/container/heap/heap_test.go b/src/pkg/container/heap/heap_test.go index c5c1f76e1..cb31ef6d3 100644 --- a/src/pkg/container/heap/heap_test.go +++ b/src/pkg/container/heap/heap_test.go @@ -5,33 +5,47 @@ package heap_test import ( - "testing" - "container/vector" . "container/heap" + "testing" ) -type myHeap struct { - // A vector.Vector implements sort.Interface except for Less, - // and it implements Push and Pop as required for heap.Interface. - vector.Vector +type myHeap []int + +func (h *myHeap) Less(i, j int) bool { + return (*h)[i] < (*h)[j] +} + +func (h *myHeap) Swap(i, j int) { + (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } -func (h *myHeap) Less(i, j int) bool { return h.At(i).(int) < h.At(j).(int) } +func (h *myHeap) Len() int { + return len(*h) +} + +func (h *myHeap) Pop() (v interface{}) { + *h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1] + return +} + +func (h *myHeap) Push(v interface{}) { + *h = append(*h, v.(int)) +} -func (h *myHeap) verify(t *testing.T, i int) { +func (h myHeap) verify(t *testing.T, i int) { n := h.Len() j1 := 2*i + 1 j2 := 2*i + 2 if j1 < n { if h.Less(j1, i) { - t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j1)) + t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j1]) return } h.verify(t, j1) } if j2 < n { if h.Less(j2, i) { - t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j2)) + t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j2]) return } h.verify(t, j2) diff --git a/src/pkg/container/list/Makefile b/src/pkg/container/list/Makefile deleted file mode 100644 index 7fcd5f99a..000000000 --- a/src/pkg/container/list/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=container/list -GOFILES=\ - list.go\ - -include ../../../Make.pkg diff --git a/src/pkg/container/ring/Makefile b/src/pkg/container/ring/Makefile deleted file mode 100644 index fb0900774..000000000 --- a/src/pkg/container/ring/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=container/ring -GOFILES=\ - ring.go\ - -include ../../../Make.pkg diff --git a/src/pkg/container/vector/Makefile b/src/pkg/container/vector/Makefile deleted file mode 100644 index f6b50156f..000000000 --- a/src/pkg/container/vector/Makefile +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=container/vector -GOFILES=\ - defs.go\ - intvector.go\ - stringvector.go\ - vector.go\ - -include ../../../Make.pkg - -generate: vector.go vector_test.go - < vector.go cat\ - | gofmt -r='Vector -> IntVector'\ - | gofmt -r='interface{} -> int'\ - > intvector.go\ - - < vector.go cat\ - | gofmt -r='Vector -> StringVector'\ - | gofmt -r='interface{} -> string'\ - > stringvector.go\ - - < vector_test.go cat\ - | gofmt -r='Vector -> IntVector'\ - | gofmt -r='zero -> intzero'\ - | gofmt -r='elem2Value -> elem2IntValue'\ - | gofmt -r='intf2Value -> intf2IntValue'\ - | gofmt -r='int2Value -> int2IntValue'\ - | gofmt -r='TestZeroLen -> TestIntZeroLen'\ - | gofmt -r='TestResize -> TestIntResize'\ - | gofmt -r='TestResize2 -> TestIntResize2'\ - | gofmt -r='checkZero -> checkIntZero'\ - | gofmt -r='TestTrailingElements -> TestIntTrailingElements'\ - | gofmt -r='TestAccess -> TestIntAccess'\ - | gofmt -r='TestInsertDeleteClear -> TestIntInsertDeleteClear'\ - | gofmt -r='verify_slice -> verify_sliceInt'\ - | gofmt -r='verify_pattern -> verify_patternInt'\ - | gofmt -r='make_vector -> make_vectorInt'\ - | gofmt -r='TestInsertVector -> TestIntInsertVector'\ - | gofmt -r='TestDo -> TestIntDo'\ - | gofmt -r='TestVectorCopy -> TestIntVectorCopy'\ - | gofmt -r='interface{} -> int'\ - > intvector_test.go\ - - < vector_test.go cat\ - | gofmt -r='Vector -> StringVector'\ - | gofmt -r='zero -> strzero'\ - | gofmt -r='int2Value -> int2StrValue'\ - | gofmt -r='intf2Value -> intf2StrValue'\ - | gofmt -r='elem2Value -> elem2StrValue'\ - | gofmt -r='TestZeroLen -> TestStrZeroLen'\ - | gofmt -r='TestResize -> TestStrResize'\ - | gofmt -r='TestResize2 -> TestStrResize2'\ - | gofmt -r='checkZero -> checkStrZero'\ - | gofmt -r='TestTrailingElements -> TestStrTrailingElements'\ - | gofmt -r='TestAccess -> TestStrAccess'\ - | gofmt -r='TestInsertDeleteClear -> TestStrInsertDeleteClear'\ - | gofmt -r='verify_slice -> verify_sliceStr'\ - | gofmt -r='verify_pattern -> verify_patternStr'\ - | gofmt -r='make_vector -> make_vectorStr'\ - | gofmt -r='TestInsertVector -> TestStrInsertVector'\ - | gofmt -r='TestDo -> TestStrDo'\ - | gofmt -r='TestVectorCopy -> TestStrVectorCopy'\ - | gofmt -r='interface{} -> string'\ - > stringvector_test.go diff --git a/src/pkg/container/vector/defs.go b/src/pkg/container/vector/defs.go deleted file mode 100644 index 6d6b2ac81..000000000 --- a/src/pkg/container/vector/defs.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package vector implements containers for managing sequences of elements. -// Vectors grow and shrink dynamically as necessary. -package vector - -// Vector is a container for numbered sequences of elements of type interface{}. -// A vector's length and capacity adjusts automatically as necessary. -// The zero value for Vector is an empty vector ready to use. -type Vector []interface{} - -// IntVector is a container for numbered sequences of elements of type int. -// A vector's length and capacity adjusts automatically as necessary. -// The zero value for IntVector is an empty vector ready to use. -type IntVector []int - -// StringVector is a container for numbered sequences of elements of type string. -// A vector's length and capacity adjusts automatically as necessary. -// The zero value for StringVector is an empty vector ready to use. -type StringVector []string - -// Initial underlying array size -const initialSize = 8 - -// Partial sort.Interface support - -// LessInterface provides partial support of the sort.Interface. -type LessInterface interface { - Less(y interface{}) bool -} - -// Less returns a boolean denoting whether the i'th element is less than the j'th element. -func (p *Vector) Less(i, j int) bool { return (*p)[i].(LessInterface).Less((*p)[j]) } - -// sort.Interface support - -// Less returns a boolean denoting whether the i'th element is less than the j'th element. -func (p *IntVector) Less(i, j int) bool { return (*p)[i] < (*p)[j] } - -// Less returns a boolean denoting whether the i'th element is less than the j'th element. -func (p *StringVector) Less(i, j int) bool { return (*p)[i] < (*p)[j] } diff --git a/src/pkg/container/vector/intvector.go b/src/pkg/container/vector/intvector.go deleted file mode 100644 index aa88cfeb3..000000000 --- a/src/pkg/container/vector/intvector.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// CAUTION: If this file is not vector.go, it was generated -// automatically from vector.go - DO NOT EDIT in that case! - -package vector - -func (p *IntVector) realloc(length, capacity int) (b []int) { - if capacity < initialSize { - capacity = initialSize - } - if capacity < length { - capacity = length - } - b = make(IntVector, length, capacity) - copy(b, *p) - *p = b - return -} - -// Insert n elements at position i. -func (p *IntVector) Expand(i, n int) { - a := *p - - // make sure we have enough space - len0 := len(a) - len1 := len0 + n - if len1 <= cap(a) { - // enough space - just expand - a = a[0:len1] - } else { - // not enough space - double capacity - capb := cap(a) * 2 - if capb < len1 { - // still not enough - use required length - capb = len1 - } - // capb >= len1 - a = p.realloc(len1, capb) - } - - // make a hole - for j := len0 - 1; j >= i; j-- { - a[j+n] = a[j] - } - - *p = a -} - -// Insert n elements at the end of a vector. -func (p *IntVector) Extend(n int) { p.Expand(len(*p), n) } - -// Resize changes the length and capacity of a vector. -// If the new length is shorter than the current length, Resize discards -// trailing elements. If the new length is longer than the current length, -// Resize adds the respective zero values for the additional elements. The capacity -// parameter is ignored unless the new length or capacity is longer than the current -// capacity. The resized vector's capacity may be larger than the requested capacity. -func (p *IntVector) Resize(length, capacity int) *IntVector { - a := *p - - if length > cap(a) || capacity > cap(a) { - // not enough space or larger capacity requested explicitly - a = p.realloc(length, capacity) - } else if length < len(a) { - // clear trailing elements - for i := range a[length:] { - var zero int - a[length+i] = zero - } - } - - *p = a[0:length] - return p -} - -// Len returns the number of elements in the vector. -// Same as len(*p). -func (p *IntVector) Len() int { return len(*p) } - -// Cap returns the capacity of the vector; that is, the -// maximum length the vector can grow without resizing. -// Same as cap(*p). -func (p *IntVector) Cap() int { return cap(*p) } - -// At returns the i'th element of the vector. -func (p *IntVector) At(i int) int { return (*p)[i] } - -// Set sets the i'th element of the vector to value x. -func (p *IntVector) Set(i int, x int) { (*p)[i] = x } - -// Last returns the element in the vector of highest index. -func (p *IntVector) Last() int { return (*p)[len(*p)-1] } - -// Copy makes a copy of the vector and returns it. -func (p *IntVector) Copy() IntVector { - arr := make(IntVector, len(*p)) - copy(arr, *p) - return arr -} - -// Insert inserts into the vector an element of value x before -// the current element at index i. -func (p *IntVector) Insert(i int, x int) { - p.Expand(i, 1) - (*p)[i] = x -} - -// Delete deletes the i'th element of the vector. The gap is closed so the old -// element at index i+1 has index i afterwards. -func (p *IntVector) Delete(i int) { - a := *p - n := len(a) - - copy(a[i:n-1], a[i+1:n]) - var zero int - a[n-1] = zero // support GC, zero out entry - *p = a[0 : n-1] -} - -// InsertVector inserts into the vector the contents of the vector -// x such that the 0th element of x appears at index i after insertion. -func (p *IntVector) InsertVector(i int, x *IntVector) { - b := *x - - p.Expand(i, len(b)) - copy((*p)[i:i+len(b)], b) -} - -// Cut deletes elements i through j-1, inclusive. -func (p *IntVector) Cut(i, j int) { - a := *p - n := len(a) - m := n - (j - i) - - copy(a[i:m], a[j:n]) - for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector. - var zero int - a[k] = zero // support GC, zero out entries - } - - *p = a[0:m] -} - -// Slice returns a new sub-vector by slicing the old one to extract slice [i:j]. -// The elements are copied. The original vector is unchanged. -func (p *IntVector) Slice(i, j int) *IntVector { - var s IntVector - s.realloc(j-i, 0) // will fail in Init() if j < i - copy(s, (*p)[i:j]) - return &s -} - -// Convenience wrappers - -// Push appends x to the end of the vector. -func (p *IntVector) Push(x int) { p.Insert(len(*p), x) } - -// Pop deletes the last element of the vector. -func (p *IntVector) Pop() int { - a := *p - - i := len(a) - 1 - x := a[i] - var zero int - a[i] = zero // support GC, zero out entry - *p = a[0:i] - return x -} - -// AppendVector appends the entire vector x to the end of this vector. -func (p *IntVector) AppendVector(x *IntVector) { p.InsertVector(len(*p), x) } - -// Swap exchanges the elements at indexes i and j. -func (p *IntVector) Swap(i, j int) { - a := *p - a[i], a[j] = a[j], a[i] -} - -// Do calls function f for each element of the vector, in order. -// The behavior of Do is undefined if f changes *p. -func (p *IntVector) Do(f func(elem int)) { - for _, e := range *p { - f(e) - } -} diff --git a/src/pkg/container/vector/intvector_test.go b/src/pkg/container/vector/intvector_test.go deleted file mode 100644 index b825af912..000000000 --- a/src/pkg/container/vector/intvector_test.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// CAUTION: If this file is not vector_test.go, it was generated -// automatically from vector_test.go - DO NOT EDIT in that case! - -package vector - -import "testing" - -func TestIntZeroLen(t *testing.T) { - a := new(IntVector) - if a.Len() != 0 { - t.Errorf("%T: B1) expected 0, got %d", a, a.Len()) - } - if len(*a) != 0 { - t.Errorf("%T: B2) expected 0, got %d", a, len(*a)) - } - var b IntVector - if b.Len() != 0 { - t.Errorf("%T: B3) expected 0, got %d", b, b.Len()) - } - if len(b) != 0 { - t.Errorf("%T: B4) expected 0, got %d", b, len(b)) - } -} - -func TestIntResize(t *testing.T) { - var a IntVector - checkSize(t, &a, 0, 0) - checkSize(t, a.Resize(0, 5), 0, 5) - checkSize(t, a.Resize(1, 0), 1, 5) - checkSize(t, a.Resize(10, 0), 10, 10) - checkSize(t, a.Resize(5, 0), 5, 10) - checkSize(t, a.Resize(3, 8), 3, 10) - checkSize(t, a.Resize(0, 100), 0, 100) - checkSize(t, a.Resize(11, 100), 11, 100) -} - -func TestIntResize2(t *testing.T) { - var a IntVector - checkSize(t, &a, 0, 0) - a.Push(int2IntValue(1)) - a.Push(int2IntValue(2)) - a.Push(int2IntValue(3)) - a.Push(int2IntValue(4)) - checkSize(t, &a, 4, 4) - checkSize(t, a.Resize(10, 0), 10, 10) - for i := 4; i < a.Len(); i++ { - if a.At(i) != intzero { - t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, intzero, a.At(i)) - } - } - for i := 4; i < len(a); i++ { - if a[i] != intzero { - t.Errorf("%T: expected a[%d] == %v; found %v", a, i, intzero, a[i]) - } - } -} - -func checkIntZero(t *testing.T, a *IntVector, i int) { - for j := 0; j < i; j++ { - if a.At(j) == intzero { - t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j)) - } - if (*a)[j] == intzero { - t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j]) - } - } - for ; i < a.Len(); i++ { - if a.At(i) != intzero { - t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, intzero, a.At(i)) - } - if (*a)[i] != intzero { - t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, intzero, (*a)[i]) - } - } -} - -func TestIntTrailingElements(t *testing.T) { - var a IntVector - for i := 0; i < 10; i++ { - a.Push(int2IntValue(i + 1)) - } - checkIntZero(t, &a, 10) - checkSize(t, &a, 10, 16) - checkSize(t, a.Resize(5, 0), 5, 16) - checkSize(t, a.Resize(10, 0), 10, 16) - checkIntZero(t, &a, 5) -} - -func TestIntAccess(t *testing.T) { - const n = 100 - var a IntVector - a.Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2IntValue(val(i))) - } - for i := 0; i < n; i++ { - if elem2IntValue(a.At(i)) != int2IntValue(val(i)) { - t.Error(i) - } - } - var b IntVector - b.Resize(n, 0) - for i := 0; i < n; i++ { - b[i] = int2IntValue(val(i)) - } - for i := 0; i < n; i++ { - if elem2IntValue(b[i]) != int2IntValue(val(i)) { - t.Error(i) - } - } -} - -func TestIntInsertDeleteClear(t *testing.T) { - const n = 100 - var a IntVector - - for i := 0; i < n; i++ { - if a.Len() != i { - t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i) - } - a.Insert(0, int2IntValue(val(i))) - if elem2IntValue(a.Last()) != int2IntValue(val(0)) { - t.Errorf("%T: B", a) - } - } - for i := n - 1; i >= 0; i-- { - if elem2IntValue(a.Last()) != int2IntValue(val(0)) { - t.Errorf("%T: C", a) - } - if elem2IntValue(a.At(0)) != int2IntValue(val(i)) { - t.Errorf("%T: D", a) - } - if elem2IntValue(a[0]) != int2IntValue(val(i)) { - t.Errorf("%T: D2", a) - } - a.Delete(0) - if a.Len() != i { - t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i) - } - } - - if a.Len() != 0 { - t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a)) - } - for i := 0; i < n; i++ { - a.Push(int2IntValue(val(i))) - if a.Len() != i+1 { - t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) - } - if len(a) != i+1 { - t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1) - } - if elem2IntValue(a.Last()) != int2IntValue(val(i)) { - t.Errorf("%T: H", a) - } - } - a.Resize(0, 0) - if a.Len() != 0 { - t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a)) - } - - const m = 5 - for j := 0; j < m; j++ { - a.Push(int2IntValue(j)) - for i := 0; i < n; i++ { - x := val(i) - a.Push(int2IntValue(x)) - if elem2IntValue(a.Pop()) != int2IntValue(x) { - t.Errorf("%T: J", a) - } - if a.Len() != j+1 { - t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) - } - if len(a) != j+1 { - t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1) - } - } - } - if a.Len() != m { - t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m) - } - if len(a) != m { - t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m) - } -} - -func verify_sliceInt(t *testing.T, x *IntVector, elt, i, j int) { - for k := i; k < j; k++ { - if elem2IntValue(x.At(k)) != int2IntValue(elt) { - t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt)) - } - } - - s := x.Slice(i, j) - for k, n := 0, j-i; k < n; k++ { - if elem2IntValue(s.At(k)) != int2IntValue(elt) { - t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt)) - } - } -} - -func verify_patternInt(t *testing.T, x *IntVector, a, b, c int) { - n := a + b + c - if x.Len() != n { - t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n) - } - if len(*x) != n { - t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n) - } - verify_sliceInt(t, x, 0, 0, a) - verify_sliceInt(t, x, 1, a, a+b) - verify_sliceInt(t, x, 0, a+b, n) -} - -func make_vectorInt(elt, len int) *IntVector { - x := new(IntVector).Resize(len, 0) - for i := 0; i < len; i++ { - x.Set(i, int2IntValue(elt)) - } - return x -} - -func TestIntInsertVector(t *testing.T) { - // 1 - a := make_vectorInt(0, 0) - b := make_vectorInt(1, 10) - a.InsertVector(0, b) - verify_patternInt(t, a, 0, 10, 0) - // 2 - a = make_vectorInt(0, 10) - b = make_vectorInt(1, 0) - a.InsertVector(5, b) - verify_patternInt(t, a, 5, 0, 5) - // 3 - a = make_vectorInt(0, 10) - b = make_vectorInt(1, 3) - a.InsertVector(3, b) - verify_patternInt(t, a, 3, 3, 7) - // 4 - a = make_vectorInt(0, 10) - b = make_vectorInt(1, 1000) - a.InsertVector(8, b) - verify_patternInt(t, a, 8, 1000, 2) -} - -func TestIntDo(t *testing.T) { - const n = 25 - const salt = 17 - a := new(IntVector).Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2IntValue(salt*i)) - } - count := 0 - a.Do(func(e int) { - i := intf2IntValue(e) - if i != int2IntValue(count*salt) { - t.Error(tname(a), "value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(a), "should visit", n, "values; did visit", count) - } - - b := new(IntVector).Resize(n, 0) - for i := 0; i < n; i++ { - (*b)[i] = int2IntValue(salt * i) - } - count = 0 - b.Do(func(e int) { - i := intf2IntValue(e) - if i != int2IntValue(count*salt) { - t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(b), "b) should visit", n, "values; did visit", count) - } - - var c IntVector - c.Resize(n, 0) - for i := 0; i < n; i++ { - c[i] = int2IntValue(salt * i) - } - count = 0 - c.Do(func(e int) { - i := intf2IntValue(e) - if i != int2IntValue(count*salt) { - t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(c), "c) should visit", n, "values; did visit", count) - } - -} - -func TestIntVectorCopy(t *testing.T) { - // verify Copy() returns a copy, not simply a slice of the original vector - const Len = 10 - var src IntVector - for i := 0; i < Len; i++ { - src.Push(int2IntValue(i * i)) - } - dest := src.Copy() - for i := 0; i < Len; i++ { - src[i] = int2IntValue(-1) - v := elem2IntValue(dest[i]) - if v != int2IntValue(i*i) { - t.Error(tname(src), "expected", i*i, "got", v) - } - } -} diff --git a/src/pkg/container/vector/nogen_test.go b/src/pkg/container/vector/nogen_test.go deleted file mode 100644 index 7b6a25952..000000000 --- a/src/pkg/container/vector/nogen_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package vector - -import ( - "fmt" - "sort" - "testing" -) - -var ( - zero interface{} - intzero int - strzero string -) - -func int2Value(x int) int { return x } -func int2IntValue(x int) int { return x } -func int2StrValue(x int) string { return string(x) } - -func elem2Value(x interface{}) int { return x.(int) } -func elem2IntValue(x int) int { return x } -func elem2StrValue(x string) string { return x } - -func intf2Value(x interface{}) int { return x.(int) } -func intf2IntValue(x interface{}) int { return x.(int) } -func intf2StrValue(x interface{}) string { return x.(string) } - -type VectorInterface interface { - Len() int - Cap() int -} - -func checkSize(t *testing.T, v VectorInterface, len, cap int) { - if v.Len() != len { - t.Errorf("%T expected len = %d; found %d", v, len, v.Len()) - } - if v.Cap() < cap { - t.Errorf("%T expected cap >= %d; found %d", v, cap, v.Cap()) - } -} - -func val(i int) int { return i*991 - 1234 } - -func TestSorting(t *testing.T) { - const n = 100 - - a := new(IntVector).Resize(n, 0) - for i := n - 1; i >= 0; i-- { - a.Set(i, n-1-i) - } - if sort.IsSorted(a) { - t.Error("int vector not sorted") - } - - b := new(StringVector).Resize(n, 0) - for i := n - 1; i >= 0; i-- { - b.Set(i, fmt.Sprint(n-1-i)) - } - if sort.IsSorted(b) { - t.Error("string vector not sorted") - } -} - -func tname(x interface{}) string { return fmt.Sprintf("%T: ", x) } diff --git a/src/pkg/container/vector/numbers_test.go b/src/pkg/container/vector/numbers_test.go deleted file mode 100644 index abe01a8fb..000000000 --- a/src/pkg/container/vector/numbers_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package vector - -import ( - "fmt" - "runtime" - "strings" - "testing" -) - -const memTestN = 1000000 - -func s(n uint64) string { - str := fmt.Sprintf("%d", n) - lens := len(str) - a := make([]string, (lens+2)/3) - start := lens - for i := range a { - start -= 3 - if start < 0 { - start = 0 - } - a[len(a)-i-1] = str[start:lens] - lens -= 3 - } - return strings.Join(a, " ") -} - -func TestVectorNums(t *testing.T) { - if testing.Short() { - return - } - var v Vector - c := int(0) - runtime.GC() - m0 := runtime.MemStats - v.Resize(memTestN, memTestN) - for i := 0; i < memTestN; i++ { - v.Set(i, c) - } - runtime.GC() - m := runtime.MemStats - v.Resize(0, 0) - runtime.GC() - n := m.Alloc - m0.Alloc - t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN) -} - -func TestIntVectorNums(t *testing.T) { - if testing.Short() { - return - } - var v IntVector - c := int(0) - runtime.GC() - m0 := runtime.MemStats - v.Resize(memTestN, memTestN) - for i := 0; i < memTestN; i++ { - v.Set(i, c) - } - runtime.GC() - m := runtime.MemStats - v.Resize(0, 0) - runtime.GC() - n := m.Alloc - m0.Alloc - t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN) -} - -func TestStringVectorNums(t *testing.T) { - if testing.Short() { - return - } - var v StringVector - c := "" - runtime.GC() - m0 := runtime.MemStats - v.Resize(memTestN, memTestN) - for i := 0; i < memTestN; i++ { - v.Set(i, c) - } - runtime.GC() - m := runtime.MemStats - v.Resize(0, 0) - runtime.GC() - n := m.Alloc - m0.Alloc - t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN) -} - -func BenchmarkVectorNums(b *testing.B) { - c := int(0) - var v Vector - b.StopTimer() - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - v.Push(c) - } -} - -func BenchmarkIntVectorNums(b *testing.B) { - c := int(0) - var v IntVector - b.StopTimer() - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - v.Push(c) - } -} - -func BenchmarkStringVectorNums(b *testing.B) { - c := "" - var v StringVector - b.StopTimer() - runtime.GC() - b.StartTimer() - for i := 0; i < b.N; i++ { - v.Push(c) - } -} diff --git a/src/pkg/container/vector/stringvector.go b/src/pkg/container/vector/stringvector.go deleted file mode 100644 index dc81f06b7..000000000 --- a/src/pkg/container/vector/stringvector.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// CAUTION: If this file is not vector.go, it was generated -// automatically from vector.go - DO NOT EDIT in that case! - -package vector - -func (p *StringVector) realloc(length, capacity int) (b []string) { - if capacity < initialSize { - capacity = initialSize - } - if capacity < length { - capacity = length - } - b = make(StringVector, length, capacity) - copy(b, *p) - *p = b - return -} - -// Insert n elements at position i. -func (p *StringVector) Expand(i, n int) { - a := *p - - // make sure we have enough space - len0 := len(a) - len1 := len0 + n - if len1 <= cap(a) { - // enough space - just expand - a = a[0:len1] - } else { - // not enough space - double capacity - capb := cap(a) * 2 - if capb < len1 { - // still not enough - use required length - capb = len1 - } - // capb >= len1 - a = p.realloc(len1, capb) - } - - // make a hole - for j := len0 - 1; j >= i; j-- { - a[j+n] = a[j] - } - - *p = a -} - -// Insert n elements at the end of a vector. -func (p *StringVector) Extend(n int) { p.Expand(len(*p), n) } - -// Resize changes the length and capacity of a vector. -// If the new length is shorter than the current length, Resize discards -// trailing elements. If the new length is longer than the current length, -// Resize adds the respective zero values for the additional elements. The capacity -// parameter is ignored unless the new length or capacity is longer than the current -// capacity. The resized vector's capacity may be larger than the requested capacity. -func (p *StringVector) Resize(length, capacity int) *StringVector { - a := *p - - if length > cap(a) || capacity > cap(a) { - // not enough space or larger capacity requested explicitly - a = p.realloc(length, capacity) - } else if length < len(a) { - // clear trailing elements - for i := range a[length:] { - var zero string - a[length+i] = zero - } - } - - *p = a[0:length] - return p -} - -// Len returns the number of elements in the vector. -// Same as len(*p). -func (p *StringVector) Len() int { return len(*p) } - -// Cap returns the capacity of the vector; that is, the -// maximum length the vector can grow without resizing. -// Same as cap(*p). -func (p *StringVector) Cap() int { return cap(*p) } - -// At returns the i'th element of the vector. -func (p *StringVector) At(i int) string { return (*p)[i] } - -// Set sets the i'th element of the vector to value x. -func (p *StringVector) Set(i int, x string) { (*p)[i] = x } - -// Last returns the element in the vector of highest index. -func (p *StringVector) Last() string { return (*p)[len(*p)-1] } - -// Copy makes a copy of the vector and returns it. -func (p *StringVector) Copy() StringVector { - arr := make(StringVector, len(*p)) - copy(arr, *p) - return arr -} - -// Insert inserts into the vector an element of value x before -// the current element at index i. -func (p *StringVector) Insert(i int, x string) { - p.Expand(i, 1) - (*p)[i] = x -} - -// Delete deletes the i'th element of the vector. The gap is closed so the old -// element at index i+1 has index i afterwards. -func (p *StringVector) Delete(i int) { - a := *p - n := len(a) - - copy(a[i:n-1], a[i+1:n]) - var zero string - a[n-1] = zero // support GC, zero out entry - *p = a[0 : n-1] -} - -// InsertVector inserts into the vector the contents of the vector -// x such that the 0th element of x appears at index i after insertion. -func (p *StringVector) InsertVector(i int, x *StringVector) { - b := *x - - p.Expand(i, len(b)) - copy((*p)[i:i+len(b)], b) -} - -// Cut deletes elements i through j-1, inclusive. -func (p *StringVector) Cut(i, j int) { - a := *p - n := len(a) - m := n - (j - i) - - copy(a[i:m], a[j:n]) - for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector. - var zero string - a[k] = zero // support GC, zero out entries - } - - *p = a[0:m] -} - -// Slice returns a new sub-vector by slicing the old one to extract slice [i:j]. -// The elements are copied. The original vector is unchanged. -func (p *StringVector) Slice(i, j int) *StringVector { - var s StringVector - s.realloc(j-i, 0) // will fail in Init() if j < i - copy(s, (*p)[i:j]) - return &s -} - -// Convenience wrappers - -// Push appends x to the end of the vector. -func (p *StringVector) Push(x string) { p.Insert(len(*p), x) } - -// Pop deletes the last element of the vector. -func (p *StringVector) Pop() string { - a := *p - - i := len(a) - 1 - x := a[i] - var zero string - a[i] = zero // support GC, zero out entry - *p = a[0:i] - return x -} - -// AppendVector appends the entire vector x to the end of this vector. -func (p *StringVector) AppendVector(x *StringVector) { p.InsertVector(len(*p), x) } - -// Swap exchanges the elements at indexes i and j. -func (p *StringVector) Swap(i, j int) { - a := *p - a[i], a[j] = a[j], a[i] -} - -// Do calls function f for each element of the vector, in order. -// The behavior of Do is undefined if f changes *p. -func (p *StringVector) Do(f func(elem string)) { - for _, e := range *p { - f(e) - } -} diff --git a/src/pkg/container/vector/stringvector_test.go b/src/pkg/container/vector/stringvector_test.go deleted file mode 100644 index c75676f78..000000000 --- a/src/pkg/container/vector/stringvector_test.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// CAUTION: If this file is not vector_test.go, it was generated -// automatically from vector_test.go - DO NOT EDIT in that case! - -package vector - -import "testing" - -func TestStrZeroLen(t *testing.T) { - a := new(StringVector) - if a.Len() != 0 { - t.Errorf("%T: B1) expected 0, got %d", a, a.Len()) - } - if len(*a) != 0 { - t.Errorf("%T: B2) expected 0, got %d", a, len(*a)) - } - var b StringVector - if b.Len() != 0 { - t.Errorf("%T: B3) expected 0, got %d", b, b.Len()) - } - if len(b) != 0 { - t.Errorf("%T: B4) expected 0, got %d", b, len(b)) - } -} - -func TestStrResize(t *testing.T) { - var a StringVector - checkSize(t, &a, 0, 0) - checkSize(t, a.Resize(0, 5), 0, 5) - checkSize(t, a.Resize(1, 0), 1, 5) - checkSize(t, a.Resize(10, 0), 10, 10) - checkSize(t, a.Resize(5, 0), 5, 10) - checkSize(t, a.Resize(3, 8), 3, 10) - checkSize(t, a.Resize(0, 100), 0, 100) - checkSize(t, a.Resize(11, 100), 11, 100) -} - -func TestStrResize2(t *testing.T) { - var a StringVector - checkSize(t, &a, 0, 0) - a.Push(int2StrValue(1)) - a.Push(int2StrValue(2)) - a.Push(int2StrValue(3)) - a.Push(int2StrValue(4)) - checkSize(t, &a, 4, 4) - checkSize(t, a.Resize(10, 0), 10, 10) - for i := 4; i < a.Len(); i++ { - if a.At(i) != strzero { - t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, strzero, a.At(i)) - } - } - for i := 4; i < len(a); i++ { - if a[i] != strzero { - t.Errorf("%T: expected a[%d] == %v; found %v", a, i, strzero, a[i]) - } - } -} - -func checkStrZero(t *testing.T, a *StringVector, i int) { - for j := 0; j < i; j++ { - if a.At(j) == strzero { - t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j)) - } - if (*a)[j] == strzero { - t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j]) - } - } - for ; i < a.Len(); i++ { - if a.At(i) != strzero { - t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, strzero, a.At(i)) - } - if (*a)[i] != strzero { - t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, strzero, (*a)[i]) - } - } -} - -func TestStrTrailingElements(t *testing.T) { - var a StringVector - for i := 0; i < 10; i++ { - a.Push(int2StrValue(i + 1)) - } - checkStrZero(t, &a, 10) - checkSize(t, &a, 10, 16) - checkSize(t, a.Resize(5, 0), 5, 16) - checkSize(t, a.Resize(10, 0), 10, 16) - checkStrZero(t, &a, 5) -} - -func TestStrAccess(t *testing.T) { - const n = 100 - var a StringVector - a.Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2StrValue(val(i))) - } - for i := 0; i < n; i++ { - if elem2StrValue(a.At(i)) != int2StrValue(val(i)) { - t.Error(i) - } - } - var b StringVector - b.Resize(n, 0) - for i := 0; i < n; i++ { - b[i] = int2StrValue(val(i)) - } - for i := 0; i < n; i++ { - if elem2StrValue(b[i]) != int2StrValue(val(i)) { - t.Error(i) - } - } -} - -func TestStrInsertDeleteClear(t *testing.T) { - const n = 100 - var a StringVector - - for i := 0; i < n; i++ { - if a.Len() != i { - t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i) - } - a.Insert(0, int2StrValue(val(i))) - if elem2StrValue(a.Last()) != int2StrValue(val(0)) { - t.Errorf("%T: B", a) - } - } - for i := n - 1; i >= 0; i-- { - if elem2StrValue(a.Last()) != int2StrValue(val(0)) { - t.Errorf("%T: C", a) - } - if elem2StrValue(a.At(0)) != int2StrValue(val(i)) { - t.Errorf("%T: D", a) - } - if elem2StrValue(a[0]) != int2StrValue(val(i)) { - t.Errorf("%T: D2", a) - } - a.Delete(0) - if a.Len() != i { - t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i) - } - } - - if a.Len() != 0 { - t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a)) - } - for i := 0; i < n; i++ { - a.Push(int2StrValue(val(i))) - if a.Len() != i+1 { - t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) - } - if len(a) != i+1 { - t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1) - } - if elem2StrValue(a.Last()) != int2StrValue(val(i)) { - t.Errorf("%T: H", a) - } - } - a.Resize(0, 0) - if a.Len() != 0 { - t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a)) - } - - const m = 5 - for j := 0; j < m; j++ { - a.Push(int2StrValue(j)) - for i := 0; i < n; i++ { - x := val(i) - a.Push(int2StrValue(x)) - if elem2StrValue(a.Pop()) != int2StrValue(x) { - t.Errorf("%T: J", a) - } - if a.Len() != j+1 { - t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) - } - if len(a) != j+1 { - t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1) - } - } - } - if a.Len() != m { - t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m) - } - if len(a) != m { - t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m) - } -} - -func verify_sliceStr(t *testing.T, x *StringVector, elt, i, j int) { - for k := i; k < j; k++ { - if elem2StrValue(x.At(k)) != int2StrValue(elt) { - t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt)) - } - } - - s := x.Slice(i, j) - for k, n := 0, j-i; k < n; k++ { - if elem2StrValue(s.At(k)) != int2StrValue(elt) { - t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt)) - } - } -} - -func verify_patternStr(t *testing.T, x *StringVector, a, b, c int) { - n := a + b + c - if x.Len() != n { - t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n) - } - if len(*x) != n { - t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n) - } - verify_sliceStr(t, x, 0, 0, a) - verify_sliceStr(t, x, 1, a, a+b) - verify_sliceStr(t, x, 0, a+b, n) -} - -func make_vectorStr(elt, len int) *StringVector { - x := new(StringVector).Resize(len, 0) - for i := 0; i < len; i++ { - x.Set(i, int2StrValue(elt)) - } - return x -} - -func TestStrInsertVector(t *testing.T) { - // 1 - a := make_vectorStr(0, 0) - b := make_vectorStr(1, 10) - a.InsertVector(0, b) - verify_patternStr(t, a, 0, 10, 0) - // 2 - a = make_vectorStr(0, 10) - b = make_vectorStr(1, 0) - a.InsertVector(5, b) - verify_patternStr(t, a, 5, 0, 5) - // 3 - a = make_vectorStr(0, 10) - b = make_vectorStr(1, 3) - a.InsertVector(3, b) - verify_patternStr(t, a, 3, 3, 7) - // 4 - a = make_vectorStr(0, 10) - b = make_vectorStr(1, 1000) - a.InsertVector(8, b) - verify_patternStr(t, a, 8, 1000, 2) -} - -func TestStrDo(t *testing.T) { - const n = 25 - const salt = 17 - a := new(StringVector).Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2StrValue(salt*i)) - } - count := 0 - a.Do(func(e string) { - i := intf2StrValue(e) - if i != int2StrValue(count*salt) { - t.Error(tname(a), "value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(a), "should visit", n, "values; did visit", count) - } - - b := new(StringVector).Resize(n, 0) - for i := 0; i < n; i++ { - (*b)[i] = int2StrValue(salt * i) - } - count = 0 - b.Do(func(e string) { - i := intf2StrValue(e) - if i != int2StrValue(count*salt) { - t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(b), "b) should visit", n, "values; did visit", count) - } - - var c StringVector - c.Resize(n, 0) - for i := 0; i < n; i++ { - c[i] = int2StrValue(salt * i) - } - count = 0 - c.Do(func(e string) { - i := intf2StrValue(e) - if i != int2StrValue(count*salt) { - t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(c), "c) should visit", n, "values; did visit", count) - } - -} - -func TestStrVectorCopy(t *testing.T) { - // verify Copy() returns a copy, not simply a slice of the original vector - const Len = 10 - var src StringVector - for i := 0; i < Len; i++ { - src.Push(int2StrValue(i * i)) - } - dest := src.Copy() - for i := 0; i < Len; i++ { - src[i] = int2StrValue(-1) - v := elem2StrValue(dest[i]) - if v != int2StrValue(i*i) { - t.Error(tname(src), "expected", i*i, "got", v) - } - } -} diff --git a/src/pkg/container/vector/vector.go b/src/pkg/container/vector/vector.go deleted file mode 100644 index 8470ec067..000000000 --- a/src/pkg/container/vector/vector.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// CAUTION: If this file is not vector.go, it was generated -// automatically from vector.go - DO NOT EDIT in that case! - -package vector - -func (p *Vector) realloc(length, capacity int) (b []interface{}) { - if capacity < initialSize { - capacity = initialSize - } - if capacity < length { - capacity = length - } - b = make(Vector, length, capacity) - copy(b, *p) - *p = b - return -} - -// Insert n elements at position i. -func (p *Vector) Expand(i, n int) { - a := *p - - // make sure we have enough space - len0 := len(a) - len1 := len0 + n - if len1 <= cap(a) { - // enough space - just expand - a = a[0:len1] - } else { - // not enough space - double capacity - capb := cap(a) * 2 - if capb < len1 { - // still not enough - use required length - capb = len1 - } - // capb >= len1 - a = p.realloc(len1, capb) - } - - // make a hole - for j := len0 - 1; j >= i; j-- { - a[j+n] = a[j] - } - - *p = a -} - -// Insert n elements at the end of a vector. -func (p *Vector) Extend(n int) { p.Expand(len(*p), n) } - -// Resize changes the length and capacity of a vector. -// If the new length is shorter than the current length, Resize discards -// trailing elements. If the new length is longer than the current length, -// Resize adds the respective zero values for the additional elements. The capacity -// parameter is ignored unless the new length or capacity is longer than the current -// capacity. The resized vector's capacity may be larger than the requested capacity. -func (p *Vector) Resize(length, capacity int) *Vector { - a := *p - - if length > cap(a) || capacity > cap(a) { - // not enough space or larger capacity requested explicitly - a = p.realloc(length, capacity) - } else if length < len(a) { - // clear trailing elements - for i := range a[length:] { - var zero interface{} - a[length+i] = zero - } - } - - *p = a[0:length] - return p -} - -// Len returns the number of elements in the vector. -// Same as len(*p). -func (p *Vector) Len() int { return len(*p) } - -// Cap returns the capacity of the vector; that is, the -// maximum length the vector can grow without resizing. -// Same as cap(*p). -func (p *Vector) Cap() int { return cap(*p) } - -// At returns the i'th element of the vector. -func (p *Vector) At(i int) interface{} { return (*p)[i] } - -// Set sets the i'th element of the vector to value x. -func (p *Vector) Set(i int, x interface{}) { (*p)[i] = x } - -// Last returns the element in the vector of highest index. -func (p *Vector) Last() interface{} { return (*p)[len(*p)-1] } - -// Copy makes a copy of the vector and returns it. -func (p *Vector) Copy() Vector { - arr := make(Vector, len(*p)) - copy(arr, *p) - return arr -} - -// Insert inserts into the vector an element of value x before -// the current element at index i. -func (p *Vector) Insert(i int, x interface{}) { - p.Expand(i, 1) - (*p)[i] = x -} - -// Delete deletes the i'th element of the vector. The gap is closed so the old -// element at index i+1 has index i afterwards. -func (p *Vector) Delete(i int) { - a := *p - n := len(a) - - copy(a[i:n-1], a[i+1:n]) - var zero interface{} - a[n-1] = zero // support GC, zero out entry - *p = a[0 : n-1] -} - -// InsertVector inserts into the vector the contents of the vector -// x such that the 0th element of x appears at index i after insertion. -func (p *Vector) InsertVector(i int, x *Vector) { - b := *x - - p.Expand(i, len(b)) - copy((*p)[i:i+len(b)], b) -} - -// Cut deletes elements i through j-1, inclusive. -func (p *Vector) Cut(i, j int) { - a := *p - n := len(a) - m := n - (j - i) - - copy(a[i:m], a[j:n]) - for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector. - var zero interface{} - a[k] = zero // support GC, zero out entries - } - - *p = a[0:m] -} - -// Slice returns a new sub-vector by slicing the old one to extract slice [i:j]. -// The elements are copied. The original vector is unchanged. -func (p *Vector) Slice(i, j int) *Vector { - var s Vector - s.realloc(j-i, 0) // will fail in Init() if j < i - copy(s, (*p)[i:j]) - return &s -} - -// Convenience wrappers - -// Push appends x to the end of the vector. -func (p *Vector) Push(x interface{}) { p.Insert(len(*p), x) } - -// Pop deletes the last element of the vector. -func (p *Vector) Pop() interface{} { - a := *p - - i := len(a) - 1 - x := a[i] - var zero interface{} - a[i] = zero // support GC, zero out entry - *p = a[0:i] - return x -} - -// AppendVector appends the entire vector x to the end of this vector. -func (p *Vector) AppendVector(x *Vector) { p.InsertVector(len(*p), x) } - -// Swap exchanges the elements at indexes i and j. -func (p *Vector) Swap(i, j int) { - a := *p - a[i], a[j] = a[j], a[i] -} - -// Do calls function f for each element of the vector, in order. -// The behavior of Do is undefined if f changes *p. -func (p *Vector) Do(f func(elem interface{})) { - for _, e := range *p { - f(e) - } -} diff --git a/src/pkg/container/vector/vector_test.go b/src/pkg/container/vector/vector_test.go deleted file mode 100644 index a7f47b8c2..000000000 --- a/src/pkg/container/vector/vector_test.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// CAUTION: If this file is not vector_test.go, it was generated -// automatically from vector_test.go - DO NOT EDIT in that case! - -package vector - -import "testing" - -func TestZeroLen(t *testing.T) { - a := new(Vector) - if a.Len() != 0 { - t.Errorf("%T: B1) expected 0, got %d", a, a.Len()) - } - if len(*a) != 0 { - t.Errorf("%T: B2) expected 0, got %d", a, len(*a)) - } - var b Vector - if b.Len() != 0 { - t.Errorf("%T: B3) expected 0, got %d", b, b.Len()) - } - if len(b) != 0 { - t.Errorf("%T: B4) expected 0, got %d", b, len(b)) - } -} - -func TestResize(t *testing.T) { - var a Vector - checkSize(t, &a, 0, 0) - checkSize(t, a.Resize(0, 5), 0, 5) - checkSize(t, a.Resize(1, 0), 1, 5) - checkSize(t, a.Resize(10, 0), 10, 10) - checkSize(t, a.Resize(5, 0), 5, 10) - checkSize(t, a.Resize(3, 8), 3, 10) - checkSize(t, a.Resize(0, 100), 0, 100) - checkSize(t, a.Resize(11, 100), 11, 100) -} - -func TestResize2(t *testing.T) { - var a Vector - checkSize(t, &a, 0, 0) - a.Push(int2Value(1)) - a.Push(int2Value(2)) - a.Push(int2Value(3)) - a.Push(int2Value(4)) - checkSize(t, &a, 4, 4) - checkSize(t, a.Resize(10, 0), 10, 10) - for i := 4; i < a.Len(); i++ { - if a.At(i) != zero { - t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, zero, a.At(i)) - } - } - for i := 4; i < len(a); i++ { - if a[i] != zero { - t.Errorf("%T: expected a[%d] == %v; found %v", a, i, zero, a[i]) - } - } -} - -func checkZero(t *testing.T, a *Vector, i int) { - for j := 0; j < i; j++ { - if a.At(j) == zero { - t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j)) - } - if (*a)[j] == zero { - t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j]) - } - } - for ; i < a.Len(); i++ { - if a.At(i) != zero { - t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, zero, a.At(i)) - } - if (*a)[i] != zero { - t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, zero, (*a)[i]) - } - } -} - -func TestTrailingElements(t *testing.T) { - var a Vector - for i := 0; i < 10; i++ { - a.Push(int2Value(i + 1)) - } - checkZero(t, &a, 10) - checkSize(t, &a, 10, 16) - checkSize(t, a.Resize(5, 0), 5, 16) - checkSize(t, a.Resize(10, 0), 10, 16) - checkZero(t, &a, 5) -} - -func TestAccess(t *testing.T) { - const n = 100 - var a Vector - a.Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2Value(val(i))) - } - for i := 0; i < n; i++ { - if elem2Value(a.At(i)) != int2Value(val(i)) { - t.Error(i) - } - } - var b Vector - b.Resize(n, 0) - for i := 0; i < n; i++ { - b[i] = int2Value(val(i)) - } - for i := 0; i < n; i++ { - if elem2Value(b[i]) != int2Value(val(i)) { - t.Error(i) - } - } -} - -func TestInsertDeleteClear(t *testing.T) { - const n = 100 - var a Vector - - for i := 0; i < n; i++ { - if a.Len() != i { - t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i) - } - a.Insert(0, int2Value(val(i))) - if elem2Value(a.Last()) != int2Value(val(0)) { - t.Errorf("%T: B", a) - } - } - for i := n - 1; i >= 0; i-- { - if elem2Value(a.Last()) != int2Value(val(0)) { - t.Errorf("%T: C", a) - } - if elem2Value(a.At(0)) != int2Value(val(i)) { - t.Errorf("%T: D", a) - } - if elem2Value(a[0]) != int2Value(val(i)) { - t.Errorf("%T: D2", a) - } - a.Delete(0) - if a.Len() != i { - t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i) - } - if len(a) != i { - t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i) - } - } - - if a.Len() != 0 { - t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a)) - } - for i := 0; i < n; i++ { - a.Push(int2Value(val(i))) - if a.Len() != i+1 { - t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1) - } - if len(a) != i+1 { - t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1) - } - if elem2Value(a.Last()) != int2Value(val(i)) { - t.Errorf("%T: H", a) - } - } - a.Resize(0, 0) - if a.Len() != 0 { - t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len()) - } - if len(a) != 0 { - t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a)) - } - - const m = 5 - for j := 0; j < m; j++ { - a.Push(int2Value(j)) - for i := 0; i < n; i++ { - x := val(i) - a.Push(int2Value(x)) - if elem2Value(a.Pop()) != int2Value(x) { - t.Errorf("%T: J", a) - } - if a.Len() != j+1 { - t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1) - } - if len(a) != j+1 { - t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1) - } - } - } - if a.Len() != m { - t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m) - } - if len(a) != m { - t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m) - } -} - -func verify_slice(t *testing.T, x *Vector, elt, i, j int) { - for k := i; k < j; k++ { - if elem2Value(x.At(k)) != int2Value(elt) { - t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt)) - } - } - - s := x.Slice(i, j) - for k, n := 0, j-i; k < n; k++ { - if elem2Value(s.At(k)) != int2Value(elt) { - t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt)) - } - } -} - -func verify_pattern(t *testing.T, x *Vector, a, b, c int) { - n := a + b + c - if x.Len() != n { - t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n) - } - if len(*x) != n { - t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n) - } - verify_slice(t, x, 0, 0, a) - verify_slice(t, x, 1, a, a+b) - verify_slice(t, x, 0, a+b, n) -} - -func make_vector(elt, len int) *Vector { - x := new(Vector).Resize(len, 0) - for i := 0; i < len; i++ { - x.Set(i, int2Value(elt)) - } - return x -} - -func TestInsertVector(t *testing.T) { - // 1 - a := make_vector(0, 0) - b := make_vector(1, 10) - a.InsertVector(0, b) - verify_pattern(t, a, 0, 10, 0) - // 2 - a = make_vector(0, 10) - b = make_vector(1, 0) - a.InsertVector(5, b) - verify_pattern(t, a, 5, 0, 5) - // 3 - a = make_vector(0, 10) - b = make_vector(1, 3) - a.InsertVector(3, b) - verify_pattern(t, a, 3, 3, 7) - // 4 - a = make_vector(0, 10) - b = make_vector(1, 1000) - a.InsertVector(8, b) - verify_pattern(t, a, 8, 1000, 2) -} - -func TestDo(t *testing.T) { - const n = 25 - const salt = 17 - a := new(Vector).Resize(n, 0) - for i := 0; i < n; i++ { - a.Set(i, int2Value(salt*i)) - } - count := 0 - a.Do(func(e interface{}) { - i := intf2Value(e) - if i != int2Value(count*salt) { - t.Error(tname(a), "value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(a), "should visit", n, "values; did visit", count) - } - - b := new(Vector).Resize(n, 0) - for i := 0; i < n; i++ { - (*b)[i] = int2Value(salt * i) - } - count = 0 - b.Do(func(e interface{}) { - i := intf2Value(e) - if i != int2Value(count*salt) { - t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(b), "b) should visit", n, "values; did visit", count) - } - - var c Vector - c.Resize(n, 0) - for i := 0; i < n; i++ { - c[i] = int2Value(salt * i) - } - count = 0 - c.Do(func(e interface{}) { - i := intf2Value(e) - if i != int2Value(count*salt) { - t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i) - } - count++ - }) - if count != n { - t.Error(tname(c), "c) should visit", n, "values; did visit", count) - } - -} - -func TestVectorCopy(t *testing.T) { - // verify Copy() returns a copy, not simply a slice of the original vector - const Len = 10 - var src Vector - for i := 0; i < Len; i++ { - src.Push(int2Value(i * i)) - } - dest := src.Copy() - for i := 0; i < Len; i++ { - src[i] = int2Value(-1) - v := elem2Value(dest[i]) - if v != int2Value(i*i) { - t.Error(tname(src), "expected", i*i, "got", v) - } - } -} diff --git a/src/pkg/crypto/Makefile b/src/pkg/crypto/Makefile deleted file mode 100644 index 738a52062..000000000 --- a/src/pkg/crypto/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../Make.inc - -TARG=crypto -GOFILES=\ - crypto.go\ - -include ../../Make.pkg diff --git a/src/pkg/crypto/aes/Makefile b/src/pkg/crypto/aes/Makefile deleted file mode 100644 index 9dc846ee3..000000000 --- a/src/pkg/crypto/aes/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/aes -GOFILES=\ - block.go\ - cipher.go\ - const.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/aes/aes_test.go b/src/pkg/crypto/aes/aes_test.go index 2136d447d..e500c666d 100644 --- a/src/pkg/crypto/aes/aes_test.go +++ b/src/pkg/crypto/aes/aes_test.go @@ -91,6 +91,7 @@ func TestTe(t *testing.T) { s2 := mul(s, 2) s3 := mul(s, 3) w := s2<<24 | s<<16 | s<<8 | s3 + te := [][256]uint32{te0, te1, te2, te3} for j := 0; j < 4; j++ { if x := te[j][i]; x != w { t.Fatalf("te[%d][%d] = %#x, want %#x", j, i, x, w) @@ -110,6 +111,7 @@ func TestTd(t *testing.T) { sd := mul(s, 0xd) se := mul(s, 0xe) w := se<<24 | s9<<16 | sd<<8 | sb + td := [][256]uint32{td0, td1, td2, td3} for j := 0; j < 4; j++ { if x := td[j][i]; x != w { t.Fatalf("td[%d][%d] = %#x, want %#x", j, i, x, w) @@ -348,3 +350,17 @@ func TestCipherDecrypt(t *testing.T) { } } } + +func BenchmarkEncrypt(b *testing.B) { + b.StopTimer() + tt := encryptTests[0] + c, err := NewCipher(tt.key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.in)) + b.StartTimer() + for i := 0; i < b.N; i++ { + c.Encrypt(out, tt.in) + } +} diff --git a/src/pkg/crypto/aes/block.go b/src/pkg/crypto/aes/block.go index 130cd011c..b930787ce 100644 --- a/src/pkg/crypto/aes/block.go +++ b/src/pkg/crypto/aes/block.go @@ -56,10 +56,10 @@ func encryptBlock(xk []uint32, dst, src []byte) { nr := len(xk)/4 - 2 // - 2: one above, one more below k := 4 for r := 0; r < nr; r++ { - t0 = xk[k+0] ^ te[0][s0>>24] ^ te[1][s1>>16&0xff] ^ te[2][s2>>8&0xff] ^ te[3][s3&0xff] - t1 = xk[k+1] ^ te[0][s1>>24] ^ te[1][s2>>16&0xff] ^ te[2][s3>>8&0xff] ^ te[3][s0&0xff] - t2 = xk[k+2] ^ te[0][s2>>24] ^ te[1][s3>>16&0xff] ^ te[2][s0>>8&0xff] ^ te[3][s1&0xff] - t3 = xk[k+3] ^ te[0][s3>>24] ^ te[1][s0>>16&0xff] ^ te[2][s1>>8&0xff] ^ te[3][s2&0xff] + t0 = xk[k+0] ^ te0[uint8(s0>>24)] ^ te1[uint8(s1>>16)] ^ te2[uint8(s2>>8)] ^ te3[uint8(s3)] + t1 = xk[k+1] ^ te0[uint8(s1>>24)] ^ te1[uint8(s2>>16)] ^ te2[uint8(s3>>8)] ^ te3[uint8(s0)] + t2 = xk[k+2] ^ te0[uint8(s2>>24)] ^ te1[uint8(s3>>16)] ^ te2[uint8(s0>>8)] ^ te3[uint8(s1)] + t3 = xk[k+3] ^ te0[uint8(s3>>24)] ^ te1[uint8(s0>>16)] ^ te2[uint8(s1>>8)] ^ te3[uint8(s2)] k += 4 s0, s1, s2, s3 = t0, t1, t2, t3 } @@ -101,10 +101,10 @@ func decryptBlock(xk []uint32, dst, src []byte) { nr := len(xk)/4 - 2 // - 2: one above, one more below k := 4 for r := 0; r < nr; r++ { - t0 = xk[k+0] ^ td[0][s0>>24] ^ td[1][s3>>16&0xff] ^ td[2][s2>>8&0xff] ^ td[3][s1&0xff] - t1 = xk[k+1] ^ td[0][s1>>24] ^ td[1][s0>>16&0xff] ^ td[2][s3>>8&0xff] ^ td[3][s2&0xff] - t2 = xk[k+2] ^ td[0][s2>>24] ^ td[1][s1>>16&0xff] ^ td[2][s0>>8&0xff] ^ td[3][s3&0xff] - t3 = xk[k+3] ^ td[0][s3>>24] ^ td[1][s2>>16&0xff] ^ td[2][s1>>8&0xff] ^ td[3][s0&0xff] + t0 = xk[k+0] ^ td0[uint8(s0>>24)] ^ td1[uint8(s3>>16)] ^ td2[uint8(s2>>8)] ^ td3[uint8(s1)] + t1 = xk[k+1] ^ td0[uint8(s1>>24)] ^ td1[uint8(s0>>16)] ^ td2[uint8(s3>>8)] ^ td3[uint8(s2)] + t2 = xk[k+2] ^ td0[uint8(s2>>24)] ^ td1[uint8(s1>>16)] ^ td2[uint8(s0>>8)] ^ td3[uint8(s3)] + t3 = xk[k+3] ^ td0[uint8(s3>>24)] ^ td1[uint8(s2>>16)] ^ td2[uint8(s1>>8)] ^ td3[uint8(s0)] k += 4 s0, s1, s2, s3 = t0, t1, t2, t3 } @@ -168,7 +168,7 @@ func expandKey(key []byte, enc, dec []uint32) { for j := 0; j < 4; j++ { x := enc[ei+j] if i > 0 && i+4 < n { - x = td[0][sbox0[x>>24]] ^ td[1][sbox0[x>>16&0xff]] ^ td[2][sbox0[x>>8&0xff]] ^ td[3][sbox0[x&0xff]] + x = td0[sbox0[x>>24]] ^ td1[sbox0[x>>16&0xff]] ^ td2[sbox0[x>>8&0xff]] ^ td3[sbox0[x&0xff]] } dec[i+j] = x } diff --git a/src/pkg/crypto/aes/cipher.go b/src/pkg/crypto/aes/cipher.go index 73223531e..7d307c93a 100644 --- a/src/pkg/crypto/aes/cipher.go +++ b/src/pkg/crypto/aes/cipher.go @@ -5,30 +5,30 @@ package aes import ( - "os" + "crypto/cipher" "strconv" ) // The AES block size in bytes. const BlockSize = 16 -// A Cipher is an instance of AES encryption using a particular key. -type Cipher struct { +// A cipher is an instance of AES encryption using a particular key. +type aesCipher struct { enc []uint32 dec []uint32 } type KeySizeError int -func (k KeySizeError) String() string { +func (k KeySizeError) Error() string { return "crypto/aes: invalid key size " + strconv.Itoa(int(k)) } -// NewCipher creates and returns a new Cipher. +// NewCipher creates and returns a new cipher.Block. // The key argument should be the AES key, // either 16, 24, or 32 bytes to select // AES-128, AES-192, or AES-256. -func NewCipher(key []byte) (*Cipher, os.Error) { +func NewCipher(key []byte) (cipher.Block, error) { k := len(key) switch k { default: @@ -38,34 +38,13 @@ func NewCipher(key []byte) (*Cipher, os.Error) { } n := k + 28 - c := &Cipher{make([]uint32, n), make([]uint32, n)} + c := &aesCipher{make([]uint32, n), make([]uint32, n)} expandKey(key, c.enc, c.dec) return c, nil } -// BlockSize returns the AES block size, 16 bytes. -// It is necessary to satisfy the Cipher interface in the -// package "crypto/cipher". -func (c *Cipher) BlockSize() int { return BlockSize } +func (c *aesCipher) BlockSize() int { return BlockSize } -// Encrypt encrypts the 16-byte buffer src using the key k -// and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) } +func (c *aesCipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) } -// Decrypt decrypts the 16-byte buffer src using the key k -// and stores the result in dst. -func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c.dec, dst, src) } - -// Reset zeros the key data, so that it will no longer -// appear in the process's memory. -func (c *Cipher) Reset() { - for i := 0; i < len(c.enc); i++ { - c.enc[i] = 0 - } - for i := 0; i < len(c.dec); i++ { - c.dec[i] = 0 - } -} +func (c *aesCipher) Decrypt(dst, src []byte) { decryptBlock(c.dec, dst, src) } diff --git a/src/pkg/crypto/aes/const.go b/src/pkg/crypto/aes/const.go index 25acd0d17..f0b4eabf6 100644 --- a/src/pkg/crypto/aes/const.go +++ b/src/pkg/crypto/aes/const.go @@ -80,283 +80,279 @@ var sbox1 = [256]byte{ // Lookup tables for encryption. // These can be recomputed by adapting the tests in aes_test.go. -var te = [4][256]uint32{ - { - 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, - 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, - 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, - 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, - 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, - 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, - 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, - 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, - 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, - 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, - 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, - 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, - 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, - 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, - 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, - 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, - 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, - 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, - 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, - 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, - 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, - 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, - 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, - 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, - 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, - 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, - 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, - 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, - 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, - 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, - 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, - 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, - }, - { - 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, - 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, - 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, - 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, - 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, - 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, - 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, - 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, - 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, - 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, - 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, - 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, - 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, - 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, - 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, - 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, - 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, - 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, - 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, - 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, - 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, - 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, - 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, - 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, - 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, - 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, - 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, - 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, - 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, - 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, - 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, - 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, - }, - { - 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, - 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, - 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, - 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, - 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, - 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, - 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, - 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, - 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, - 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, - 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, - 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, - 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, - 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, - 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, - 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, - 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, - 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, - 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, - 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, - 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, - 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, - 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, - 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, - 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, - 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, - 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, - 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, - 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, - 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, - 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, - 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, - }, - { - 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, - 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, - 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, - 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, - 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, - 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, - 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, - 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, - 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, - 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, - 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, - 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, - 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, - 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, - 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, - 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, - 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, - 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, - 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, - 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, - 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, - 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, - 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, - 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, - 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, - 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, - 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, - 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, - 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, - 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, - 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, - 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, - }, +var te0 = [256]uint32{ + 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, + 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, + 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, + 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, + 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, + 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, + 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, + 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, + 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, + 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, + 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, + 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, + 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, + 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, + 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, + 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, + 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, + 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, + 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, + 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, + 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, + 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, + 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, + 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, + 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, + 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, + 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, + 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, + 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, + 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, + 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, + 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, +} +var te1 = [256]uint32{ + 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, + 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, + 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, + 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, + 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, + 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, + 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, + 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, + 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, + 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, + 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, + 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, + 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, + 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, + 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, + 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, + 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, + 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, + 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, + 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, + 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, + 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, + 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, + 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, + 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, + 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, + 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, + 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, + 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, + 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, + 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, + 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, +} +var te2 = [256]uint32{ + 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, + 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, + 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, + 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, + 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, + 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, + 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, + 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, + 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, + 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, + 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, + 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, + 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, + 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, + 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, + 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, + 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, + 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, + 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, + 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, + 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, + 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, + 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, + 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, + 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, + 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, + 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, + 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, + 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, + 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, + 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, + 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, +} +var te3 = [256]uint32{ + 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, + 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, + 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, + 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, + 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, + 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, + 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, + 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, + 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, + 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, + 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, + 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, + 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, + 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, + 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, + 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, + 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, + 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, + 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, + 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, + 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, + 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, + 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, + 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, + 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, + 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, + 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, + 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, + 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, + 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, + 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, + 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, } // Lookup tables for decryption. // These can be recomputed by adapting the tests in aes_test.go. -var td = [4][256]uint32{ - { - 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, - 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, - 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, - 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, - 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, - 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, - 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, - 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, - 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, - 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, - 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, - 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, - 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, - 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, - 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, - 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, - 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, - 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, - 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, - 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, - 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, - 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, - 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, - 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, - 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, - 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, - 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, - 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, - 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, - 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, - 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, - 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, - }, - { - 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, - 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, - 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, - 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, - 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, - 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, - 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, - 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, - 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, - 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, - 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, - 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, - 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, - 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, - 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, - 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, - 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, - 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, - 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, - 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, - 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, - 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, - 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, - 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, - 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, - 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, - 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, - 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, - 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, - 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, - 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, - 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, - }, - { - 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, - 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, - 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, - 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, - 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, - 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, - 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, - 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, - 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, - 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, - 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, - 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, - 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, - 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, - 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, - 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, - 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, - 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, - 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, - 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, - 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, - 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, - 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, - 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, - 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, - 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, - 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, - 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, - 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, - 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, - 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, - 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, - }, - { - 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, - 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, - 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, - 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, - 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, - 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, - 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, - 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, - 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, - 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, - 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, - 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, - 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, - 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, - 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, - 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, - 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, - 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, - 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, - 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, - 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, - 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, - 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, - 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, - 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, - 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, - 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, - 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, - 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, - 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, - 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, - 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, - }, +var td0 = [256]uint32{ + 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, + 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, + 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, + 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, + 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, + 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, + 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, + 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, + 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, + 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, + 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, + 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, + 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, + 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, + 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, + 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, + 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, + 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, + 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, + 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, + 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, + 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, + 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, + 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, + 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, + 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, + 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, + 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, + 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, + 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, + 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, + 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, +} +var td1 = [256]uint32{ + 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, + 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, + 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, + 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, + 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, + 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, + 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, + 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, + 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, + 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, + 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, + 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, + 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, + 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, + 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, + 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, + 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, + 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, + 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, + 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, + 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, + 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, + 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, + 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, + 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, + 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, + 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, + 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, + 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, + 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, + 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, + 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, +} +var td2 = [256]uint32{ + 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, + 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, + 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, + 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, + 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, + 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, + 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, + 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, + 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, + 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, + 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, + 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, + 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, + 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, + 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, + 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, + 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, + 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, + 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, + 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, + 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, + 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, + 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, + 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, + 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, + 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, + 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, + 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, + 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, + 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, + 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, + 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, +} +var td3 = [256]uint32{ + 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, + 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, + 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, + 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, + 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, + 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, + 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, + 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, + 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, + 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, + 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, + 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, + 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, + 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, + 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, + 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, + 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, + 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, + 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, + 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, + 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, + 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, + 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, + 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, + 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, + 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, + 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, + 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, + 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, + 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, + 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, + 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, } diff --git a/src/pkg/crypto/blowfish/Makefile b/src/pkg/crypto/blowfish/Makefile deleted file mode 100644 index f370ab28b..000000000 --- a/src/pkg/crypto/blowfish/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/blowfish -GOFILES=\ - block.go\ - cipher.go\ - const.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/blowfish/block.go b/src/pkg/crypto/blowfish/block.go deleted file mode 100644 index 7fbe7eefb..000000000 --- a/src/pkg/crypto/blowfish/block.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package blowfish - -func expandKey(key []byte, c *Cipher) { - copy(c.p[0:], p[0:]) - copy(c.s0[0:], s0[0:]) - copy(c.s1[0:], s1[0:]) - copy(c.s2[0:], s2[0:]) - copy(c.s3[0:], s3[0:]) - - j := 0 - for i := 0; i < 18; i++ { - var d uint32 - for k := 0; k < 4; k++ { - d = d<<8 | uint32(key[j])&0x000000FF - j++ - if j >= len(key) { - j = 0 - } - } - c.p[i] ^= d - } - - var l, r uint32 - for i := 0; i < 18; i += 2 { - l, r = encryptBlock(l, r, c) - c.p[i], c.p[i+1] = l, r - } - - for i := 0; i < 256; i += 2 { - l, r = encryptBlock(l, r, c) - c.s0[i], c.s0[i+1] = l, r - } - for i := 0; i < 256; i += 2 { - l, r = encryptBlock(l, r, c) - c.s1[i], c.s1[i+1] = l, r - } - for i := 0; i < 256; i += 2 { - l, r = encryptBlock(l, r, c) - c.s2[i], c.s2[i+1] = l, r - } - for i := 0; i < 256; i += 2 { - l, r = encryptBlock(l, r, c) - c.s3[i], c.s3[i+1] = l, r - } -} - -func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { - xl, xr := l, r - xl ^= c.p[0] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16] - xr ^= c.p[17] - return xr, xl -} - -func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { - xl, xr := l, r - xl ^= c.p[17] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3] - xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2] - xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1] - xr ^= c.p[0] - return xr, xl -} - -func zero(x []uint32) { - for i := range x { - x[i] = 0 - } -} diff --git a/src/pkg/crypto/blowfish/blowfish_test.go b/src/pkg/crypto/blowfish/blowfish_test.go deleted file mode 100644 index 3a7ab6c2a..000000000 --- a/src/pkg/crypto/blowfish/blowfish_test.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package blowfish - -import ( - "testing" -) - -type CryptTest struct { - key []byte - in []byte - out []byte -} - -// Test vector values are from http://www.schneier.com/code/vectors.txt. -var encryptTests = []CryptTest{ - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}}, - { - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}}, - { - []byte{0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, - []byte{0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}}, - { - []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, - []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, - []byte{0x24, 0x66, 0xDD, 0x87, 0x8B, 0x96, 0x3C, 0x9D}}, - - { - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, - []byte{0x61, 0xF9, 0xC3, 0x80, 0x22, 0x81, 0xB0, 0x96}}, - { - []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0x7D, 0x0C, 0xC6, 0x30, 0xAF, 0xDA, 0x1E, 0xC7}}, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}}, - { - []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0x0A, 0xCE, 0xAB, 0x0F, 0xC6, 0xA0, 0xA2, 0x8D}}, - { - []byte{0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57}, - []byte{0x01, 0xA1, 0xD6, 0xD0, 0x39, 0x77, 0x67, 0x42}, - []byte{0x59, 0xC6, 0x82, 0x45, 0xEB, 0x05, 0x28, 0x2B}}, - { - []byte{0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E}, - []byte{0x5C, 0xD5, 0x4C, 0xA8, 0x3D, 0xEF, 0x57, 0xDA}, - []byte{0xB1, 0xB8, 0xCC, 0x0B, 0x25, 0x0F, 0x09, 0xA0}}, - { - []byte{0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86}, - []byte{0x02, 0x48, 0xD4, 0x38, 0x06, 0xF6, 0x71, 0x72}, - []byte{0x17, 0x30, 0xE5, 0x77, 0x8B, 0xEA, 0x1D, 0xA4}}, - { - []byte{0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E}, - []byte{0x51, 0x45, 0x4B, 0x58, 0x2D, 0xDF, 0x44, 0x0A}, - []byte{0xA2, 0x5E, 0x78, 0x56, 0xCF, 0x26, 0x51, 0xEB}}, - { - []byte{0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6}, - []byte{0x42, 0xFD, 0x44, 0x30, 0x59, 0x57, 0x7F, 0xA2}, - []byte{0x35, 0x38, 0x82, 0xB1, 0x09, 0xCE, 0x8F, 0x1A}}, - { - []byte{0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE}, - []byte{0x05, 0x9B, 0x5E, 0x08, 0x51, 0xCF, 0x14, 0x3A}, - []byte{0x48, 0xF4, 0xD0, 0x88, 0x4C, 0x37, 0x99, 0x18}}, - { - []byte{0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6}, - []byte{0x07, 0x56, 0xD8, 0xE0, 0x77, 0x47, 0x61, 0xD2}, - []byte{0x43, 0x21, 0x93, 0xB7, 0x89, 0x51, 0xFC, 0x98}}, - { - []byte{0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE}, - []byte{0x76, 0x25, 0x14, 0xB8, 0x29, 0xBF, 0x48, 0x6A}, - []byte{0x13, 0xF0, 0x41, 0x54, 0xD6, 0x9D, 0x1A, 0xE5}}, - { - []byte{0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16}, - []byte{0x3B, 0xDD, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02}, - []byte{0x2E, 0xED, 0xDA, 0x93, 0xFF, 0xD3, 0x9C, 0x79}}, - { - []byte{0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F}, - []byte{0x26, 0x95, 0x5F, 0x68, 0x35, 0xAF, 0x60, 0x9A}, - []byte{0xD8, 0x87, 0xE0, 0x39, 0x3C, 0x2D, 0xA6, 0xE3}}, - { - []byte{0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46}, - []byte{0x16, 0x4D, 0x5E, 0x40, 0x4F, 0x27, 0x52, 0x32}, - []byte{0x5F, 0x99, 0xD0, 0x4F, 0x5B, 0x16, 0x39, 0x69}}, - { - []byte{0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E}, - []byte{0x6B, 0x05, 0x6E, 0x18, 0x75, 0x9F, 0x5C, 0xCA}, - []byte{0x4A, 0x05, 0x7A, 0x3B, 0x24, 0xD3, 0x97, 0x7B}}, - { - []byte{0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76}, - []byte{0x00, 0x4B, 0xD6, 0xEF, 0x09, 0x17, 0x60, 0x62}, - []byte{0x45, 0x20, 0x31, 0xC1, 0xE4, 0xFA, 0xDA, 0x8E}}, - { - []byte{0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07}, - []byte{0x48, 0x0D, 0x39, 0x00, 0x6E, 0xE7, 0x62, 0xF2}, - []byte{0x75, 0x55, 0xAE, 0x39, 0xF5, 0x9B, 0x87, 0xBD}}, - { - []byte{0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F}, - []byte{0x43, 0x75, 0x40, 0xC8, 0x69, 0x8F, 0x3C, 0xFA}, - []byte{0x53, 0xC5, 0x5F, 0x9C, 0xB4, 0x9F, 0xC0, 0x19}}, - { - []byte{0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7}, - []byte{0x07, 0x2D, 0x43, 0xA0, 0x77, 0x07, 0x52, 0x92}, - []byte{0x7A, 0x8E, 0x7B, 0xFA, 0x93, 0x7E, 0x89, 0xA3}}, - { - []byte{0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF}, - []byte{0x02, 0xFE, 0x55, 0x77, 0x81, 0x17, 0xF1, 0x2A}, - []byte{0xCF, 0x9C, 0x5D, 0x7A, 0x49, 0x86, 0xAD, 0xB5}}, - { - []byte{0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6}, - []byte{0x1D, 0x9D, 0x5C, 0x50, 0x18, 0xF7, 0x28, 0xC2}, - []byte{0xD1, 0xAB, 0xB2, 0x90, 0x65, 0x8B, 0xC7, 0x78}}, - { - []byte{0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF}, - []byte{0x30, 0x55, 0x32, 0x28, 0x6D, 0x6F, 0x29, 0x5A}, - []byte{0x55, 0xCB, 0x37, 0x74, 0xD1, 0x3E, 0xF2, 0x01}}, - { - []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0xFA, 0x34, 0xEC, 0x48, 0x47, 0xB2, 0x68, 0xB2}}, - { - []byte{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0xA7, 0x90, 0x79, 0x51, 0x08, 0xEA, 0x3C, 0xAE}}, - { - []byte{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE}, - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0xC3, 0x9E, 0x07, 0x2D, 0x9F, 0xAC, 0x63, 0x1D}}, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0x01, 0x49, 0x33, 0xE0, 0xCD, 0xAF, 0xF6, 0xE4}}, - { - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0xF2, 0x1E, 0x9A, 0x77, 0xB7, 0x1C, 0x49, 0xBC}}, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9A}}, - { - []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, - []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - []byte{0x6B, 0x5C, 0x5A, 0x9C, 0x5D, 0x9E, 0x0A, 0x5A}}, -} - -func TestCipherEncrypt(t *testing.T) { - for i, tt := range encryptTests { - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err) - continue - } - ct := make([]byte, len(tt.out)) - c.Encrypt(ct, tt.in) - for j, v := range ct { - if v != tt.out[j] { - t.Errorf("Cipher.Encrypt, test vector #%d: cipher-text[%d] = %#x, expected %#x", i, j, v, tt.out[j]) - break - } - } - } -} - -func TestCipherDecrypt(t *testing.T) { - for i, tt := range encryptTests { - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err) - continue - } - pt := make([]byte, len(tt.in)) - c.Decrypt(pt, tt.out) - for j, v := range pt { - if v != tt.in[j] { - t.Errorf("Cipher.Decrypt, test vector #%d: plain-text[%d] = %#x, expected %#x", i, j, v, tt.in[j]) - break - } - } - } -} diff --git a/src/pkg/crypto/blowfish/cipher.go b/src/pkg/crypto/blowfish/cipher.go deleted file mode 100644 index 6c37dfe94..000000000 --- a/src/pkg/crypto/blowfish/cipher.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm. -package blowfish - -// The code is a port of Bruce Schneier's C implementation. -// See http://www.schneier.com/blowfish.html. - -import ( - "os" - "strconv" -) - -// The Blowfish block size in bytes. -const BlockSize = 8 - -// A Cipher is an instance of Blowfish encryption using a particular key. -type Cipher struct { - p [18]uint32 - s0, s1, s2, s3 [256]uint32 -} - -type KeySizeError int - -func (k KeySizeError) String() string { - return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k)) -} - -// NewCipher creates and returns a Cipher. -// The key argument should be the Blowfish key, 4 to 56 bytes. -func NewCipher(key []byte) (*Cipher, os.Error) { - k := len(key) - if k < 4 || k > 56 { - return nil, KeySizeError(k) - } - var result Cipher - expandKey(key, &result) - return &result, nil -} - -// BlockSize returns the Blowfish block size, 8 bytes. -// It is necessary to satisfy the Cipher interface in the -// package "crypto/cipher". -func (c *Cipher) BlockSize() int { return BlockSize } - -// Encrypt encrypts the 8-byte buffer src using the key k -// and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { - l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - l, r = encryptBlock(l, r, c) - dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) - dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) -} - -// Decrypt decrypts the 8-byte buffer src using the key k -// and stores the result in dst. -func (c *Cipher) Decrypt(dst, src []byte) { - l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - l, r = decryptBlock(l, r, c) - dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) - dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) -} - -// Reset zeros the key data, so that it will no longer -// appear in the process's memory. -func (c *Cipher) Reset() { - zero(c.p[0:]) - zero(c.s0[0:]) - zero(c.s1[0:]) - zero(c.s2[0:]) - zero(c.s3[0:]) -} diff --git a/src/pkg/crypto/blowfish/const.go b/src/pkg/crypto/blowfish/const.go deleted file mode 100644 index 8c5ee4cb0..000000000 --- a/src/pkg/crypto/blowfish/const.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// The startup permutation array and substitution boxes. -// They are the hexadecimal digits of PI; see: -// http://www.schneier.com/code/constants.txt. - -package blowfish - -var s0 = [256]uint32{ - 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, - 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, - 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, - 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, - 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, - 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, - 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, - 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, - 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, - 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, - 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, - 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, - 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, - 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, - 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, - 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, - 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, - 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, - 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, - 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, - 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, - 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, - 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, - 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, - 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, - 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, - 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, - 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, - 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, - 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, - 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, - 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, - 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, - 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, - 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, - 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, - 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, - 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, - 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, - 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, - 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, - 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, - 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, -} - -var s1 = [256]uint32{ - 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, - 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, - 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, - 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, - 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, - 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, - 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, - 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, - 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, - 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, - 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, - 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, - 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, - 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, - 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, - 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, - 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, - 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, - 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, - 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, - 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, - 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, - 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, - 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, - 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, - 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, - 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, - 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, - 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, - 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, - 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, - 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, - 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, - 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, - 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, - 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, - 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, - 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, - 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, - 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, - 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, - 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, - 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, -} - -var s2 = [256]uint32{ - 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, - 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, - 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, - 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, - 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, - 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, - 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, - 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, - 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, - 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, - 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, - 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, - 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, - 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, - 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, - 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, - 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, - 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, - 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, - 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, - 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, - 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, - 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, - 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, - 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, - 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, - 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, - 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, - 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, - 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, - 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, - 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, - 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, - 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, - 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, - 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, - 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, - 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, - 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, - 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, - 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, - 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, - 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, -} - -var s3 = [256]uint32{ - 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, - 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, - 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, - 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, - 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, - 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, - 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, - 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, - 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, - 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, - 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, - 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, - 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, - 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, - 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, - 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, - 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, - 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, - 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, - 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, - 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, - 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, - 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, - 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, - 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, - 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, - 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, - 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, - 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, - 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, - 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, - 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, - 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, - 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, - 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, - 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, - 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, - 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, - 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, - 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, - 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, - 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, - 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, -} - -var p = [18]uint32{ - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, - 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, - 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, -} diff --git a/src/pkg/crypto/cast5/Makefile b/src/pkg/crypto/cast5/Makefile deleted file mode 100644 index 346fadd94..000000000 --- a/src/pkg/crypto/cast5/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/cast5 -GOFILES=\ - cast5.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/cast5/cast5.go b/src/pkg/crypto/cast5/cast5.go deleted file mode 100644 index e9d4a24e2..000000000 --- a/src/pkg/crypto/cast5/cast5.go +++ /dev/null @@ -1,536 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common -// OpenPGP cipher. -package cast5 - -import ( - "os" -) - -const BlockSize = 8 -const KeySize = 16 - -type Cipher struct { - masking [16]uint32 - rotate [16]uint8 -} - -func NewCipher(key []byte) (c *Cipher, err os.Error) { - if len(key) != KeySize { - return nil, os.NewError("CAST5: keys must be 16 bytes") - } - - c = new(Cipher) - c.keySchedule(key) - return -} - -func (c *Cipher) BlockSize() int { - return BlockSize -} - -// Reset zeros the key material in memory. -func (c *Cipher) Reset() { - for i := 0; i < 16; i++ { - c.masking[i] = 0 - c.rotate[i] = 0 - } -} - -func (c *Cipher) Encrypt(dst, src []byte) { - l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - - l, r = r, l^f1(r, c.masking[0], c.rotate[0]) - l, r = r, l^f2(r, c.masking[1], c.rotate[1]) - l, r = r, l^f3(r, c.masking[2], c.rotate[2]) - l, r = r, l^f1(r, c.masking[3], c.rotate[3]) - - l, r = r, l^f2(r, c.masking[4], c.rotate[4]) - l, r = r, l^f3(r, c.masking[5], c.rotate[5]) - l, r = r, l^f1(r, c.masking[6], c.rotate[6]) - l, r = r, l^f2(r, c.masking[7], c.rotate[7]) - - l, r = r, l^f3(r, c.masking[8], c.rotate[8]) - l, r = r, l^f1(r, c.masking[9], c.rotate[9]) - l, r = r, l^f2(r, c.masking[10], c.rotate[10]) - l, r = r, l^f3(r, c.masking[11], c.rotate[11]) - - l, r = r, l^f1(r, c.masking[12], c.rotate[12]) - l, r = r, l^f2(r, c.masking[13], c.rotate[13]) - l, r = r, l^f3(r, c.masking[14], c.rotate[14]) - l, r = r, l^f1(r, c.masking[15], c.rotate[15]) - - dst[0] = uint8(r >> 24) - dst[1] = uint8(r >> 16) - dst[2] = uint8(r >> 8) - dst[3] = uint8(r) - dst[4] = uint8(l >> 24) - dst[5] = uint8(l >> 16) - dst[6] = uint8(l >> 8) - dst[7] = uint8(l) -} - -func (c *Cipher) Decrypt(dst, src []byte) { - l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - - l, r = r, l^f1(r, c.masking[15], c.rotate[15]) - l, r = r, l^f3(r, c.masking[14], c.rotate[14]) - l, r = r, l^f2(r, c.masking[13], c.rotate[13]) - l, r = r, l^f1(r, c.masking[12], c.rotate[12]) - - l, r = r, l^f3(r, c.masking[11], c.rotate[11]) - l, r = r, l^f2(r, c.masking[10], c.rotate[10]) - l, r = r, l^f1(r, c.masking[9], c.rotate[9]) - l, r = r, l^f3(r, c.masking[8], c.rotate[8]) - - l, r = r, l^f2(r, c.masking[7], c.rotate[7]) - l, r = r, l^f1(r, c.masking[6], c.rotate[6]) - l, r = r, l^f3(r, c.masking[5], c.rotate[5]) - l, r = r, l^f2(r, c.masking[4], c.rotate[4]) - - l, r = r, l^f1(r, c.masking[3], c.rotate[3]) - l, r = r, l^f3(r, c.masking[2], c.rotate[2]) - l, r = r, l^f2(r, c.masking[1], c.rotate[1]) - l, r = r, l^f1(r, c.masking[0], c.rotate[0]) - - dst[0] = uint8(r >> 24) - dst[1] = uint8(r >> 16) - dst[2] = uint8(r >> 8) - dst[3] = uint8(r) - dst[4] = uint8(l >> 24) - dst[5] = uint8(l >> 16) - dst[6] = uint8(l >> 8) - dst[7] = uint8(l) -} - -type keyScheduleA [4][7]uint8 -type keyScheduleB [4][5]uint8 - -// keyScheduleRound contains the magic values for a round of the key schedule. -// The keyScheduleA deals with the lines like: -// z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8] -// Conceptually, both x and z are in the same array, x first. The first -// element describes which word of this array gets written to and the -// second, which word gets read. So, for the line above, it's "4, 0", because -// it's writing to the first word of z, which, being after x, is word 4, and -// reading from the first word of x: word 0. -// -// Next are the indexes into the S-boxes. Now the array is treated as bytes. So -// "xD" is 0xd. The first byte of z is written as "16 + 0", just to be clear -// that it's z that we're indexing. -// -// keyScheduleB deals with lines like: -// K1 = S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2] -// "K1" is ignored because key words are always written in order. So the five -// elements are the S-box indexes. They use the same form as in keyScheduleA, -// above. - -type keyScheduleRound struct{} -type keySchedule []keyScheduleRound - -var schedule = []struct { - a keyScheduleA - b keyScheduleB -}{ - { - keyScheduleA{ - {4, 0, 0xd, 0xf, 0xc, 0xe, 0x8}, - {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, - {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, - {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, - }, - keyScheduleB{ - {16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2}, - {16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6}, - {16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9}, - {16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc}, - }, - }, - { - keyScheduleA{ - {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, - {1, 4, 0, 2, 1, 3, 16 + 2}, - {2, 5, 7, 6, 5, 4, 16 + 1}, - {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, - }, - keyScheduleB{ - {3, 2, 0xc, 0xd, 8}, - {1, 0, 0xe, 0xf, 0xd}, - {7, 6, 8, 9, 3}, - {5, 4, 0xa, 0xb, 7}, - }, - }, - { - keyScheduleA{ - {4, 0, 0xd, 0xf, 0xc, 0xe, 8}, - {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, - {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, - {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, - }, - keyScheduleB{ - {16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9}, - {16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc}, - {16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2}, - {16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6}, - }, - }, - { - keyScheduleA{ - {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, - {1, 4, 0, 2, 1, 3, 16 + 2}, - {2, 5, 7, 6, 5, 4, 16 + 1}, - {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, - }, - keyScheduleB{ - {8, 9, 7, 6, 3}, - {0xa, 0xb, 5, 4, 7}, - {0xc, 0xd, 3, 2, 8}, - {0xe, 0xf, 1, 0, 0xd}, - }, - }, -} - -func (c *Cipher) keySchedule(in []byte) { - var t [8]uint32 - var k [32]uint32 - - for i := 0; i < 4; i++ { - j := i * 4 - t[i] = uint32(in[j])<<24 | uint32(in[j+1])<<16 | uint32(in[j+2])<<8 | uint32(in[j+3]) - } - - x := []byte{6, 7, 4, 5} - ki := 0 - - for half := 0; half < 2; half++ { - for _, round := range schedule { - for j := 0; j < 4; j++ { - var a [7]uint8 - copy(a[:], round.a[j][:]) - w := t[a[1]] - w ^= sBox[4][(t[a[2]>>2]>>(24-8*(a[2]&3)))&0xff] - w ^= sBox[5][(t[a[3]>>2]>>(24-8*(a[3]&3)))&0xff] - w ^= sBox[6][(t[a[4]>>2]>>(24-8*(a[4]&3)))&0xff] - w ^= sBox[7][(t[a[5]>>2]>>(24-8*(a[5]&3)))&0xff] - w ^= sBox[x[j]][(t[a[6]>>2]>>(24-8*(a[6]&3)))&0xff] - t[a[0]] = w - } - - for j := 0; j < 4; j++ { - var b [5]uint8 - copy(b[:], round.b[j][:]) - w := sBox[4][(t[b[0]>>2]>>(24-8*(b[0]&3)))&0xff] - w ^= sBox[5][(t[b[1]>>2]>>(24-8*(b[1]&3)))&0xff] - w ^= sBox[6][(t[b[2]>>2]>>(24-8*(b[2]&3)))&0xff] - w ^= sBox[7][(t[b[3]>>2]>>(24-8*(b[3]&3)))&0xff] - w ^= sBox[4+j][(t[b[4]>>2]>>(24-8*(b[4]&3)))&0xff] - k[ki] = w - ki++ - } - } - } - - for i := 0; i < 16; i++ { - c.masking[i] = k[i] - c.rotate[i] = uint8(k[16+i] & 0x1f) - } -} - -// These are the three 'f' functions. See RFC 2144, section 2.2. -func f1(d, m uint32, r uint8) uint32 { - t := m + d - I := (t << r) | (t >> (32 - r)) - return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff] -} - -func f2(d, m uint32, r uint8) uint32 { - t := m ^ d - I := (t << r) | (t >> (32 - r)) - return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff] -} - -func f3(d, m uint32, r uint8) uint32 { - t := m - d - I := (t << r) | (t >> (32 - r)) - return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff] -} - -var sBox = [8][256]uint32{ - { - 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, - 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, - 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, - 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, - 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, - 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, - 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, - 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, - 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, - 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, - 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, - 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, - 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, - 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, - 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, - 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, - 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, - 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, - 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, - 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, - 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, - 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, - 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, - 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, - 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, - 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, - 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, - 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, - 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, - 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, - 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, - 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf, - }, - { - 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, - 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, - 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, - 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, - 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, - 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, - 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, - 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, - 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, - 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, - 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, - 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, - 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, - 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, - 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, - 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, - 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, - 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, - 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, - 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, - 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, - 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, - 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, - 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, - 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, - 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, - 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, - 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, - 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, - 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, - 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, - 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1, - }, - { - 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, - 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, - 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, - 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, - 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, - 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, - 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, - 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, - 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, - 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, - 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, - 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, - 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, - 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, - 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, - 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, - 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, - 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, - 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, - 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, - 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, - 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, - 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, - 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, - 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, - 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, - 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, - 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, - 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, - 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, - 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, - 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783, - }, - { - 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, - 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, - 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, - 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, - 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, - 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, - 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, - 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, - 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, - 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, - 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, - 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, - 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, - 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, - 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, - 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, - 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, - 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, - 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, - 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, - 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, - 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, - 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, - 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, - 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, - 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, - 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, - 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, - 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, - 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, - 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, - 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2, - }, - { - 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, - 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, - 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, - 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, - 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, - 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, - 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, - 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, - 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, - 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, - 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, - 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, - 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, - 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, - 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, - 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, - 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, - 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, - 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, - 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, - 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, - 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, - 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, - 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, - 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, - 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, - 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, - 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, - 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, - 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, - 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, - 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4, - }, - { - 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, - 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, - 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, - 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, - 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, - 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, - 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, - 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, - 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, - 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, - 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, - 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, - 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, - 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, - 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, - 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, - 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, - 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, - 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, - 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, - 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, - 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, - 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, - 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, - 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, - 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, - 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, - 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, - 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, - 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, - 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, - 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f, - }, - { - 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, - 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, - 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, - 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, - 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, - 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, - 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, - 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, - 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, - 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, - 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, - 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, - 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, - 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, - 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, - 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, - 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, - 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, - 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, - 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, - 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, - 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, - 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, - 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, - 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, - 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, - 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, - 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, - 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, - 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, - 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, - 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3, - }, - { - 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, - 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, - 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, - 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, - 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, - 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, - 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, - 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, - 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, - 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, - 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, - 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, - 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, - 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, - 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, - 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, - 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, - 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, - 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, - 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, - 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, - 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, - 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, - 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, - 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, - 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, - 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, - 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, - 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, - 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, - 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, - 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e, - }, -} diff --git a/src/pkg/crypto/cast5/cast5_test.go b/src/pkg/crypto/cast5/cast5_test.go deleted file mode 100644 index 5f7025ff2..000000000 --- a/src/pkg/crypto/cast5/cast5_test.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cast5 - -import ( - "bytes" - "encoding/hex" - "testing" -) - -// This test vector is taken from RFC 2144, App B.1. -// Since the other two test vectors are for reduced-round variants, we can't -// use them. -var basicTests = []struct { - key, plainText, cipherText string -}{ - { - "0123456712345678234567893456789a", - "0123456789abcdef", - "238b4fe5847e44b2", - }, -} - -func TestBasic(t *testing.T) { - for i, test := range basicTests { - key, _ := hex.DecodeString(test.key) - plainText, _ := hex.DecodeString(test.plainText) - expected, _ := hex.DecodeString(test.cipherText) - - c, err := NewCipher(key) - if err != nil { - t.Errorf("#%d: failed to create Cipher: %s", i, err) - continue - } - var cipherText [BlockSize]byte - c.Encrypt(cipherText[:], plainText) - if !bytes.Equal(cipherText[:], expected) { - t.Errorf("#%d: got:%x want:%x", i, cipherText, expected) - } - - var plainTextAgain [BlockSize]byte - c.Decrypt(plainTextAgain[:], cipherText[:]) - if !bytes.Equal(plainTextAgain[:], plainText) { - t.Errorf("#%d: got:%x want:%x", i, plainTextAgain, plainText) - } - } -} - -// TestFull performs the test specified in RFC 2144, App B.2. -// However, due to the length of time taken, it's disabled here and a more -// limited version is included, below. -func TestFull(t *testing.T) { - // This is too slow for normal testing - return - - a, b := iterate(1000000) - - const expectedA = "eea9d0a249fd3ba6b3436fb89d6dca92" - const expectedB = "b2c95eb00c31ad7180ac05b8e83d696e" - - if hex.EncodeToString(a) != expectedA { - t.Errorf("a: got:%x want:%s", a, expectedA) - } - if hex.EncodeToString(b) != expectedB { - t.Errorf("b: got:%x want:%s", b, expectedB) - } -} - -func iterate(iterations int) ([]byte, []byte) { - const initValueHex = "0123456712345678234567893456789a" - - initValue, _ := hex.DecodeString(initValueHex) - - var a, b [16]byte - copy(a[:], initValue) - copy(b[:], initValue) - - for i := 0; i < iterations; i++ { - c, _ := NewCipher(b[:]) - c.Encrypt(a[:8], a[:8]) - c.Encrypt(a[8:], a[8:]) - c, _ = NewCipher(a[:]) - c.Encrypt(b[:8], b[:8]) - c.Encrypt(b[8:], b[8:]) - } - - return a[:], b[:] -} - -func TestLimited(t *testing.T) { - a, b := iterate(1000) - - const expectedA = "23f73b14b02a2ad7dfb9f2c35644798d" - const expectedB = "e5bf37eff14c456a40b21ce369370a9f" - - if hex.EncodeToString(a) != expectedA { - t.Errorf("a: got:%x want:%s", a, expectedA) - } - if hex.EncodeToString(b) != expectedB { - t.Errorf("b: got:%x want:%s", b, expectedB) - } -} diff --git a/src/pkg/crypto/cipher/Makefile b/src/pkg/crypto/cipher/Makefile deleted file mode 100644 index 8f61cf20b..000000000 --- a/src/pkg/crypto/cipher/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/cipher -GOFILES=\ - cbc.go\ - cfb.go\ - cipher.go\ - ctr.go\ - io.go\ - ocfb.go\ - ofb.go - -include ../../../Make.pkg diff --git a/src/pkg/crypto/cipher/cbc.go b/src/pkg/crypto/cipher/cbc.go index 4632f882a..a48929cf5 100644 --- a/src/pkg/crypto/cipher/cbc.go +++ b/src/pkg/crypto/cipher/cbc.go @@ -56,7 +56,7 @@ type cbcDecrypter cbc // NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining // mode, using the given Block. The length of iv must be the same as the -// Block's block size as must match the iv used to encrypt the data. +// Block's block size and must match the iv used to encrypt the data. func NewCBCDecrypter(b Block, iv []byte) BlockMode { return (*cbcDecrypter)(newCBC(b, iv)) } diff --git a/src/pkg/crypto/cipher/cbc_aes_test.go b/src/pkg/crypto/cipher/cbc_aes_test.go index 944ca1ba8..cee3a784b 100644 --- a/src/pkg/crypto/cipher/cbc_aes_test.go +++ b/src/pkg/crypto/cipher/cbc_aes_test.go @@ -8,11 +8,12 @@ // Special Publication 800-38A, ``Recommendation for Block Cipher // Modes of Operation,'' 2001 Edition, pp. 24-29. -package cipher +package cipher_test import ( "bytes" "crypto/aes" + "crypto/cipher" "testing" ) @@ -72,14 +73,14 @@ func TestCBC_AES(t *testing.T) { continue } - encrypter := NewCBCEncrypter(c, tt.iv) + encrypter := cipher.NewCBCEncrypter(c, tt.iv) d := make([]byte, len(tt.in)) encrypter.CryptBlocks(d, tt.in) if !bytes.Equal(tt.out, d) { t.Errorf("%s: CBCEncrypter\nhave %x\nwant %x", test, d, tt.out) } - decrypter := NewCBCDecrypter(c, tt.iv) + decrypter := cipher.NewCBCDecrypter(c, tt.iv) p := make([]byte, len(d)) decrypter.CryptBlocks(p, d) if !bytes.Equal(tt.in, p) { diff --git a/src/pkg/crypto/cipher/cfb_test.go b/src/pkg/crypto/cipher/cfb_test.go index 9547bfceb..f704b337e 100644 --- a/src/pkg/crypto/cipher/cfb_test.go +++ b/src/pkg/crypto/cipher/cfb_test.go @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package cipher +package cipher_test import ( "bytes" "crypto/aes" + "crypto/cipher" "crypto/rand" "testing" ) @@ -21,11 +22,11 @@ func TestCFB(t *testing.T) { plaintext := []byte("this is the plaintext") iv := make([]byte, block.BlockSize()) rand.Reader.Read(iv) - cfb := NewCFBEncrypter(block, iv) + cfb := cipher.NewCFBEncrypter(block, iv) ciphertext := make([]byte, len(plaintext)) cfb.XORKeyStream(ciphertext, plaintext) - cfbdec := NewCFBDecrypter(block, iv) + cfbdec := cipher.NewCFBDecrypter(block, iv) plaintextCopy := make([]byte, len(plaintext)) cfbdec.XORKeyStream(plaintextCopy, ciphertext) diff --git a/src/pkg/crypto/cipher/common_test.go b/src/pkg/crypto/cipher/common_test.go index fb755757c..c75c919d1 100644 --- a/src/pkg/crypto/cipher/common_test.go +++ b/src/pkg/crypto/cipher/common_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package cipher +package cipher_test // Common values for tests. diff --git a/src/pkg/crypto/cipher/ctr_aes_test.go b/src/pkg/crypto/cipher/ctr_aes_test.go index 8dca9968c..d019ae0d0 100644 --- a/src/pkg/crypto/cipher/ctr_aes_test.go +++ b/src/pkg/crypto/cipher/ctr_aes_test.go @@ -8,11 +8,12 @@ // Special Publication 800-38A, ``Recommendation for Block Cipher // Modes of Operation,'' 2001 Edition, pp. 55-58. -package cipher +package cipher_test import ( "bytes" "crypto/aes" + "crypto/cipher" "testing" ) @@ -76,7 +77,7 @@ func TestCTR_AES(t *testing.T) { for j := 0; j <= 5; j += 5 { in := tt.in[0 : len(tt.in)-j] - ctr := NewCTR(c, tt.iv) + ctr := cipher.NewCTR(c, tt.iv) encrypted := make([]byte, len(in)) ctr.XORKeyStream(encrypted, in) if out := tt.out[0:len(in)]; !bytes.Equal(out, encrypted) { @@ -86,7 +87,7 @@ func TestCTR_AES(t *testing.T) { for j := 0; j <= 7; j += 7 { in := tt.out[0 : len(tt.out)-j] - ctr := NewCTR(c, tt.iv) + ctr := cipher.NewCTR(c, tt.iv) plain := make([]byte, len(in)) ctr.XORKeyStream(plain, in) if out := tt.in[0:len(in)]; !bytes.Equal(out, plain) { diff --git a/src/pkg/crypto/cipher/io.go b/src/pkg/crypto/cipher/io.go index 97f40b8e7..76048fbf3 100644 --- a/src/pkg/crypto/cipher/io.go +++ b/src/pkg/crypto/cipher/io.go @@ -4,37 +4,34 @@ package cipher -import ( - "os" - "io" -) +import "io" // The Stream* objects are so simple that all their members are public. Users // can create them themselves. -// StreamReader wraps a Stream into an io.Reader. It simply calls XORKeyStream +// StreamReader wraps a Stream into an io.Reader. It calls XORKeyStream // to process each slice of data which passes through. type StreamReader struct { S Stream R io.Reader } -func (r StreamReader) Read(dst []byte) (n int, err os.Error) { +func (r StreamReader) Read(dst []byte) (n int, err error) { n, err = r.R.Read(dst) r.S.XORKeyStream(dst[:n], dst[:n]) return } -// StreamWriter wraps a Stream into an io.Writer. It simply calls XORKeyStream +// StreamWriter wraps a Stream into an io.Writer. It calls XORKeyStream // to process each slice of data which passes through. If any Write call // returns short then the StreamWriter is out of sync and must be discarded. type StreamWriter struct { S Stream W io.Writer - Err os.Error + Err error } -func (w StreamWriter) Write(src []byte) (n int, err os.Error) { +func (w StreamWriter) Write(src []byte) (n int, err error) { if w.Err != nil { return 0, w.Err } @@ -50,7 +47,7 @@ func (w StreamWriter) Write(src []byte) (n int, err os.Error) { return } -func (w StreamWriter) Close() os.Error { +func (w StreamWriter) Close() error { // This saves us from either requiring a WriteCloser or having a // StreamWriterCloser. return w.W.(io.Closer).Close() diff --git a/src/pkg/crypto/cipher/ocfb.go b/src/pkg/crypto/cipher/ocfb.go deleted file mode 100644 index 031e74a9d..000000000 --- a/src/pkg/crypto/cipher/ocfb.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9 - -package cipher - -type ocfbEncrypter struct { - b Block - fre []byte - outUsed int -} - -// An OCFBResyncOption determines if the "resynchronization step" of OCFB is -// performed. -type OCFBResyncOption bool - -const ( - OCFBResync OCFBResyncOption = true - OCFBNoResync OCFBResyncOption = false -) - -// NewOCFBEncrypter returns a Stream which encrypts data with OpenPGP's cipher -// feedback mode using the given Block, and an initial amount of ciphertext. -// randData must be random bytes and be the same length as the Block's block -// size. Resync determines if the "resynchronization step" from RFC 4880, 13.9 -// step 7 is performed. Different parts of OpenPGP vary on this point. -func NewOCFBEncrypter(block Block, randData []byte, resync OCFBResyncOption) (Stream, []byte) { - blockSize := block.BlockSize() - if len(randData) != blockSize { - return nil, nil - } - - x := &ocfbEncrypter{ - b: block, - fre: make([]byte, blockSize), - outUsed: 0, - } - prefix := make([]byte, blockSize+2) - - block.Encrypt(x.fre, x.fre) - for i := 0; i < blockSize; i++ { - prefix[i] = randData[i] ^ x.fre[i] - } - - block.Encrypt(x.fre, prefix[:blockSize]) - prefix[blockSize] = x.fre[0] ^ randData[blockSize-2] - prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1] - - if resync { - block.Encrypt(x.fre, prefix[2:]) - } else { - x.fre[0] = prefix[blockSize] - x.fre[1] = prefix[blockSize+1] - x.outUsed = 2 - } - return x, prefix -} - -func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) { - for i := 0; i < len(src); i++ { - if x.outUsed == len(x.fre) { - x.b.Encrypt(x.fre, x.fre) - x.outUsed = 0 - } - - x.fre[x.outUsed] ^= src[i] - dst[i] = x.fre[x.outUsed] - x.outUsed++ - } -} - -type ocfbDecrypter struct { - b Block - fre []byte - outUsed int -} - -// NewOCFBDecrypter returns a Stream which decrypts data with OpenPGP's cipher -// feedback mode using the given Block. Prefix must be the first blockSize + 2 -// bytes of the ciphertext, where blockSize is the Block's block size. If an -// incorrect key is detected then nil is returned. On successful exit, -// blockSize+2 bytes of decrypted data are written into prefix. Resync -// determines if the "resynchronization step" from RFC 4880, 13.9 step 7 is -// performed. Different parts of OpenPGP vary on this point. -func NewOCFBDecrypter(block Block, prefix []byte, resync OCFBResyncOption) Stream { - blockSize := block.BlockSize() - if len(prefix) != blockSize+2 { - return nil - } - - x := &ocfbDecrypter{ - b: block, - fre: make([]byte, blockSize), - outUsed: 0, - } - prefixCopy := make([]byte, len(prefix)) - copy(prefixCopy, prefix) - - block.Encrypt(x.fre, x.fre) - for i := 0; i < blockSize; i++ { - prefixCopy[i] ^= x.fre[i] - } - - block.Encrypt(x.fre, prefix[:blockSize]) - prefixCopy[blockSize] ^= x.fre[0] - prefixCopy[blockSize+1] ^= x.fre[1] - - if prefixCopy[blockSize-2] != prefixCopy[blockSize] || - prefixCopy[blockSize-1] != prefixCopy[blockSize+1] { - return nil - } - - if resync { - block.Encrypt(x.fre, prefix[2:]) - } else { - x.fre[0] = prefix[blockSize] - x.fre[1] = prefix[blockSize+1] - x.outUsed = 2 - } - copy(prefix, prefixCopy) - return x -} - -func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) { - for i := 0; i < len(src); i++ { - if x.outUsed == len(x.fre) { - x.b.Encrypt(x.fre, x.fre) - x.outUsed = 0 - } - - c := src[i] - dst[i] = x.fre[x.outUsed] ^ src[i] - x.fre[x.outUsed] = c - x.outUsed++ - } -} diff --git a/src/pkg/crypto/cipher/ocfb_test.go b/src/pkg/crypto/cipher/ocfb_test.go deleted file mode 100644 index 40938b589..000000000 --- a/src/pkg/crypto/cipher/ocfb_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cipher - -import ( - "bytes" - "crypto/aes" - "crypto/rand" - "testing" -) - -func testOCFB(t *testing.T, resync OCFBResyncOption) { - block, err := aes.NewCipher(commonKey128) - if err != nil { - t.Error(err) - return - } - - plaintext := []byte("this is the plaintext, which is long enough to span several blocks.") - randData := make([]byte, block.BlockSize()) - rand.Reader.Read(randData) - ocfb, prefix := NewOCFBEncrypter(block, randData, resync) - ciphertext := make([]byte, len(plaintext)) - ocfb.XORKeyStream(ciphertext, plaintext) - - ocfbdec := NewOCFBDecrypter(block, prefix, resync) - if ocfbdec == nil { - t.Errorf("NewOCFBDecrypter failed (resync: %t)", resync) - return - } - plaintextCopy := make([]byte, len(plaintext)) - ocfbdec.XORKeyStream(plaintextCopy, ciphertext) - - if !bytes.Equal(plaintextCopy, plaintext) { - t.Errorf("got: %x, want: %x (resync: %t)", plaintextCopy, plaintext, resync) - } -} - -func TestOCFB(t *testing.T) { - testOCFB(t, OCFBNoResync) - testOCFB(t, OCFBResync) -} diff --git a/src/pkg/crypto/cipher/ofb_test.go b/src/pkg/crypto/cipher/ofb_test.go index 9b4495c88..8d3c5d3a3 100644 --- a/src/pkg/crypto/cipher/ofb_test.go +++ b/src/pkg/crypto/cipher/ofb_test.go @@ -8,11 +8,12 @@ // Special Publication 800-38A, ``Recommendation for Block Cipher // Modes of Operation,'' 2001 Edition, pp. 52-55. -package cipher +package cipher_test import ( "bytes" "crypto/aes" + "crypto/cipher" "testing" ) @@ -76,7 +77,7 @@ func TestOFB(t *testing.T) { for j := 0; j <= 5; j += 5 { plaintext := tt.in[0 : len(tt.in)-j] - ofb := NewOFB(c, tt.iv) + ofb := cipher.NewOFB(c, tt.iv) ciphertext := make([]byte, len(plaintext)) ofb.XORKeyStream(ciphertext, plaintext) if !bytes.Equal(ciphertext, tt.out[:len(plaintext)]) { @@ -86,7 +87,7 @@ func TestOFB(t *testing.T) { for j := 0; j <= 5; j += 5 { ciphertext := tt.out[0 : len(tt.in)-j] - ofb := NewOFB(c, tt.iv) + ofb := cipher.NewOFB(c, tt.iv) plaintext := make([]byte, len(ciphertext)) ofb.XORKeyStream(plaintext, ciphertext) if !bytes.Equal(plaintext, tt.in[:len(ciphertext)]) { diff --git a/src/pkg/crypto/crypto.go b/src/pkg/crypto/crypto.go index 53672a4da..ecefc6572 100644 --- a/src/pkg/crypto/crypto.go +++ b/src/pkg/crypto/crypto.go @@ -14,15 +14,15 @@ import ( type Hash uint const ( - MD4 Hash = 1 + iota // in package crypto/md4 - MD5 // in package crypto/md5 - SHA1 // in package crypto/sha1 - SHA224 // in package crypto/sha256 - SHA256 // in package crypto/sha256 - SHA384 // in package crypto/sha512 - SHA512 // in package crypto/sha512 + MD4 Hash = 1 + iota // import code.google.com/p/go.crypto/md4 + MD5 // import crypto/md5 + SHA1 // import crypto/sha1 + SHA224 // import crypto/sha256 + SHA256 // import crypto/sha256 + SHA384 // import crypto/sha512 + SHA512 // import crypto/sha512 MD5SHA1 // no implementation; MD5+SHA1 used for TLS RSA - RIPEMD160 // in package crypto/ripemd160 + RIPEMD160 // import code.google.com/p/go.crypto/ripemd160 maxHash ) @@ -50,8 +50,8 @@ func (h Hash) Size() int { var hashes = make([]func() hash.Hash, maxHash) -// New returns a new hash.Hash calculating the given hash function. If the -// hash function is not linked into the binary, New returns nil. +// New returns a new hash.Hash calculating the given hash function. New panics +// if the hash function is not linked into the binary. func (h Hash) New() hash.Hash { if h > 0 && h < maxHash { f := hashes[h] @@ -59,7 +59,12 @@ func (h Hash) New() hash.Hash { return f() } } - return nil + panic("crypto: requested hash function is unavailable") +} + +// Available reports whether the given hash function is linked into the binary. +func (h Hash) Available() bool { + return h < maxHash && hashes[h] != nil } // RegisterHash registers a function that returns a new instance of the given @@ -71,3 +76,6 @@ func RegisterHash(h Hash, f func() hash.Hash) { } hashes[h] = f } + +// PrivateKey represents a private key using an unspecified algorithm. +type PrivateKey interface{} diff --git a/src/pkg/crypto/des/Makefile b/src/pkg/crypto/des/Makefile deleted file mode 100644 index 94b0fc0fa..000000000 --- a/src/pkg/crypto/des/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/des -GOFILES=\ - block.go\ - cipher.go\ - const.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/des/block.go b/src/pkg/crypto/des/block.go index e18eaedf5..c11c62cd7 100644 --- a/src/pkg/crypto/des/block.go +++ b/src/pkg/crypto/des/block.go @@ -79,7 +79,7 @@ func ksRotate(in uint32) (out []uint32) { } // creates 16 56-bit subkeys from the original key -func (c *Cipher) generateSubkeys(keyBytes []byte) { +func (c *desCipher) generateSubkeys(keyBytes []byte) { // apply PC1 permutation to key key := binary.BigEndian.Uint64(keyBytes) permutedKey := permuteBlock(key, permutedChoice1[:]) diff --git a/src/pkg/crypto/des/cipher.go b/src/pkg/crypto/des/cipher.go index d17a1a783..2f929ca7b 100644 --- a/src/pkg/crypto/des/cipher.go +++ b/src/pkg/crypto/des/cipher.go @@ -5,7 +5,7 @@ package des import ( - "os" + "crypto/cipher" "strconv" ) @@ -14,90 +14,60 @@ const BlockSize = 8 type KeySizeError int -func (k KeySizeError) String() string { +func (k KeySizeError) Error() string { return "crypto/des: invalid key size " + strconv.Itoa(int(k)) } -// Cipher is an instance of DES encryption. -type Cipher struct { +// desCipher is an instance of DES encryption. +type desCipher struct { subkeys [16]uint64 } -// NewCipher creates and returns a new Cipher. -func NewCipher(key []byte) (*Cipher, os.Error) { +// NewCipher creates and returns a new cipher.Block. +func NewCipher(key []byte) (cipher.Block, error) { if len(key) != 8 { return nil, KeySizeError(len(key)) } - c := new(Cipher) + c := new(desCipher) c.generateSubkeys(key) return c, nil } -// BlockSize returns the DES block size, 8 bytes. -func (c *Cipher) BlockSize() int { return BlockSize } +func (c *desCipher) BlockSize() int { return BlockSize } -// Encrypts the 8-byte buffer src and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c.subkeys[:], dst, src) } +func (c *desCipher) Encrypt(dst, src []byte) { encryptBlock(c.subkeys[:], dst, src) } -// Decrypts the 8-byte buffer src and stores the result in dst. -func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c.subkeys[:], dst, src) } +func (c *desCipher) Decrypt(dst, src []byte) { decryptBlock(c.subkeys[:], dst, src) } -// Reset zeros the key data, so that it will no longer -// appear in the process's memory. -func (c *Cipher) Reset() { - for i := 0; i < len(c.subkeys); i++ { - c.subkeys[i] = 0 - } -} - -// A TripleDESCipher is an instance of TripleDES encryption. -type TripleDESCipher struct { - cipher1, cipher2, cipher3 Cipher +// A tripleDESCipher is an instance of TripleDES encryption. +type tripleDESCipher struct { + cipher1, cipher2, cipher3 desCipher } -// NewCipher creates and returns a new Cipher. -func NewTripleDESCipher(key []byte) (*TripleDESCipher, os.Error) { +// NewTripleDESCipher creates and returns a new cipher.Block. +func NewTripleDESCipher(key []byte) (cipher.Block, error) { if len(key) != 24 { return nil, KeySizeError(len(key)) } - c := new(TripleDESCipher) + c := new(tripleDESCipher) c.cipher1.generateSubkeys(key[:8]) c.cipher2.generateSubkeys(key[8:16]) c.cipher3.generateSubkeys(key[16:]) return c, nil } -// BlockSize returns the TripleDES block size, 8 bytes. -// It is necessary to satisfy the Block interface in the -// package "crypto/cipher". -func (c *TripleDESCipher) BlockSize() int { return BlockSize } +func (c *tripleDESCipher) BlockSize() int { return BlockSize } -// Encrypts the 8-byte buffer src and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *TripleDESCipher) Encrypt(dst, src []byte) { +func (c *tripleDESCipher) Encrypt(dst, src []byte) { c.cipher1.Encrypt(dst, src) c.cipher2.Decrypt(dst, dst) c.cipher3.Encrypt(dst, dst) } -// Decrypts the 8-byte buffer src and stores the result in dst. -func (c *TripleDESCipher) Decrypt(dst, src []byte) { +func (c *tripleDESCipher) Decrypt(dst, src []byte) { c.cipher3.Decrypt(dst, src) c.cipher2.Encrypt(dst, dst) c.cipher1.Decrypt(dst, dst) } - -// Reset zeros the key data, so that it will no longer -// appear in the process's memory. -func (c *TripleDESCipher) Reset() { - c.cipher1.Reset() - c.cipher2.Reset() - c.cipher3.Reset() -} diff --git a/src/pkg/crypto/des/des_test.go b/src/pkg/crypto/des/des_test.go index d1f3aa71a..e9fc23629 100644 --- a/src/pkg/crypto/des/des_test.go +++ b/src/pkg/crypto/des/des_test.go @@ -1260,11 +1260,19 @@ var tableA4Tests = []CryptTest{ []byte{0x63, 0xfa, 0xc0, 0xd0, 0x34, 0xd9, 0xf7, 0x93}}, } +func newCipher(key []byte) *desCipher { + c, err := NewCipher(key) + if err != nil { + panic("NewCipher failed: " + err.Error()) + } + return c.(*desCipher) +} + // Use the known weak keys to test DES implementation func TestWeakKeys(t *testing.T) { for i, tt := range weakKeyTests { var encrypt = func(in []byte) (out []byte) { - c, _ := NewCipher(tt.key) + c := newCipher(tt.key) out = make([]byte, len(in)) encryptBlock(c.subkeys[:], out, in) return @@ -1285,7 +1293,7 @@ func TestWeakKeys(t *testing.T) { func TestSemiWeakKeyPairs(t *testing.T) { for i, tt := range semiWeakKeyTests { var encrypt = func(key, in []byte) (out []byte) { - c, _ := NewCipher(key) + c := newCipher(key) out = make([]byte, len(in)) encryptBlock(c.subkeys[:], out, in) return @@ -1305,7 +1313,7 @@ func TestSemiWeakKeyPairs(t *testing.T) { func TestDESEncryptBlock(t *testing.T) { for i, tt := range encryptDESTests { - c, _ := NewCipher(tt.key) + c := newCipher(tt.key) out := make([]byte, len(tt.in)) encryptBlock(c.subkeys[:], out, tt.in) @@ -1317,7 +1325,7 @@ func TestDESEncryptBlock(t *testing.T) { func TestDESDecryptBlock(t *testing.T) { for i, tt := range encryptDESTests { - c, _ := NewCipher(tt.key) + c := newCipher(tt.key) plain := make([]byte, len(tt.in)) decryptBlock(c.subkeys[:], plain, tt.out) diff --git a/src/pkg/crypto/dsa/Makefile b/src/pkg/crypto/dsa/Makefile deleted file mode 100644 index fa89d4ab2..000000000 --- a/src/pkg/crypto/dsa/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/dsa -GOFILES=\ - dsa.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/dsa/dsa.go b/src/pkg/crypto/dsa/dsa.go index a5f96fe94..05766a2f1 100644 --- a/src/pkg/crypto/dsa/dsa.go +++ b/src/pkg/crypto/dsa/dsa.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3 +// Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3. package dsa import ( - "big" + "errors" "io" - "os" + "math/big" ) // Parameters represents the domain parameters for a key. These parameters can @@ -29,17 +29,11 @@ type PrivateKey struct { X *big.Int } -type invalidPublicKeyError int - -func (invalidPublicKeyError) String() string { - return "crypto/dsa: invalid public key" -} - -// InvalidPublicKeyError results when a public key is not usable by this code. +// ErrInvalidPublicKey results when a public key is not usable by this code. // FIPS is quite strict about the format of DSA keys, but other code may be // less so. Thus, when using keys which may have been generated by other code, // this error must be handled. -var InvalidPublicKeyError = invalidPublicKeyError(0) +var ErrInvalidPublicKey = errors.New("crypto/dsa: invalid public key") // ParameterSizes is a enumeration of the acceptable bit lengths of the primes // in a set of DSA parameters. See FIPS 186-3, section 4.2. @@ -58,7 +52,7 @@ const numMRTests = 64 // GenerateParameters puts a random, valid set of DSA parameters into params. // This function takes many seconds, even on fast machines. -func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes) (err os.Error) { +func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes) (err error) { // This function doesn't follow FIPS 186-3 exactly in that it doesn't // use a verification seed to generate the primes. The verification // seed doesn't appear to be exported or used by other code and @@ -79,7 +73,7 @@ func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes L = 3072 N = 256 default: - return os.NewError("crypto/dsa: invalid ParameterSizes") + return errors.New("crypto/dsa: invalid ParameterSizes") } qBytes := make([]byte, N/8) @@ -102,7 +96,7 @@ GeneratePrimes: qBytes[0] |= 0x80 q.SetBytes(qBytes) - if !big.ProbablyPrime(q, numMRTests) { + if !q.ProbablyPrime(numMRTests) { continue } @@ -123,7 +117,7 @@ GeneratePrimes: continue } - if !big.ProbablyPrime(p, numMRTests) { + if !p.ProbablyPrime(numMRTests) { continue } @@ -156,9 +150,9 @@ GeneratePrimes: // GenerateKey generates a public&private key pair. The Parameters of the // PrivateKey must already be valid (see GenerateParameters). -func GenerateKey(priv *PrivateKey, rand io.Reader) os.Error { +func GenerateKey(priv *PrivateKey, rand io.Reader) error { if priv.P == nil || priv.Q == nil || priv.G == nil { - return os.NewError("crypto/dsa: parameters not set up before generating key") + return errors.New("crypto/dsa: parameters not set up before generating key") } x := new(big.Int) @@ -185,12 +179,16 @@ func GenerateKey(priv *PrivateKey, rand io.Reader) os.Error { // larger message) using the private key, priv. It returns the signature as a // pair of integers. The security of the private key depends on the entropy of // rand. -func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os.Error) { +// +// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated +// to the byte-length of the subgroup. This function does not perform that +// truncation itself. +func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { // FIPS 186-3, section 4.6 n := priv.Q.BitLen() if n&7 != 0 { - err = InvalidPublicKeyError + err = ErrInvalidPublicKey return } n >>= 3 @@ -218,10 +216,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. continue } - if n > len(hash) { - n = len(hash) - } - z := k.SetBytes(hash[:n]) + z := k.SetBytes(hash) s = new(big.Int).Mul(priv.X, r) s.Add(s, z) @@ -238,7 +233,11 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. } // Verify verifies the signature in r, s of hash using the public key, pub. It -// returns true iff the signature is valid. +// reports whether the signature is valid. +// +// Note that FIPS 186-3 section 4.6 specifies that the hash should be truncated +// to the byte-length of the subgroup. This function does not perform that +// truncation itself. func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { // FIPS 186-3, section 4.7 @@ -255,12 +254,7 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { if n&7 != 0 { return false } - n >>= 3 - - if n > len(hash) { - n = len(hash) - } - z := new(big.Int).SetBytes(hash[:n]) + z := new(big.Int).SetBytes(hash) u1 := new(big.Int).Mul(z, w) u1.Mod(u1, pub.Q) diff --git a/src/pkg/crypto/dsa/dsa_test.go b/src/pkg/crypto/dsa/dsa_test.go index deec08dfd..177aa444d 100644 --- a/src/pkg/crypto/dsa/dsa_test.go +++ b/src/pkg/crypto/dsa/dsa_test.go @@ -5,8 +5,8 @@ package dsa import ( - "big" "crypto/rand" + "math/big" "testing" ) diff --git a/src/pkg/crypto/ecdsa/Makefile b/src/pkg/crypto/ecdsa/Makefile deleted file mode 100644 index 0af24c2f2..000000000 --- a/src/pkg/crypto/ecdsa/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/ecdsa -GOFILES=\ - ecdsa.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/ecdsa/ecdsa.go b/src/pkg/crypto/ecdsa/ecdsa.go index 7bce1bc96..b28239b78 100644 --- a/src/pkg/crypto/ecdsa/ecdsa.go +++ b/src/pkg/crypto/ecdsa/ecdsa.go @@ -7,21 +7,20 @@ package ecdsa // References: -// [NSA]: Suite B implementor's guide to FIPS 186-3, +// [NSA]: Suite B implementer's guide to FIPS 186-3, // http://www.nsa.gov/ia/_files/ecdsa.pdf // [SECG]: SECG, SEC1 // http://www.secg.org/download/aid-780/sec1-v2.pdf import ( - "big" "crypto/elliptic" "io" - "os" + "math/big" ) // PublicKey represents an ECDSA public key. type PublicKey struct { - *elliptic.Curve + elliptic.Curve X, Y *big.Int } @@ -35,22 +34,23 @@ var one = new(big.Int).SetInt64(1) // randFieldElement returns a random element of the field underlying the given // curve using the procedure given in [NSA] A.2.1. -func randFieldElement(c *elliptic.Curve, rand io.Reader) (k *big.Int, err os.Error) { - b := make([]byte, c.BitSize/8+8) +func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) { + params := c.Params() + b := make([]byte, params.BitSize/8+8) _, err = io.ReadFull(rand, b) if err != nil { return } k = new(big.Int).SetBytes(b) - n := new(big.Int).Sub(c.N, one) + n := new(big.Int).Sub(params.N, one) k.Mod(k, n) k.Add(k, one) return } // GenerateKey generates a public&private key pair. -func GenerateKey(c *elliptic.Curve, rand io.Reader) (priv *PrivateKey, err os.Error) { +func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error) { k, err := randFieldElement(c, rand) if err != nil { return @@ -67,8 +67,8 @@ func GenerateKey(c *elliptic.Curve, rand io.Reader) (priv *PrivateKey, err os.Er // about how this is done. [NSA] suggests that this is done in the obvious // manner, but [SECG] truncates the hash to the bit-length of the curve order // first. We follow [SECG] because that's what OpenSSL does. -func hashToInt(hash []byte, c *elliptic.Curve) *big.Int { - orderBits := c.N.BitLen() +func hashToInt(hash []byte, c elliptic.Curve) *big.Int { + orderBits := c.Params().N.BitLen() orderBytes := (orderBits + 7) / 8 if len(hash) > orderBytes { hash = hash[:orderBytes] @@ -86,9 +86,10 @@ func hashToInt(hash []byte, c *elliptic.Curve) *big.Int { // larger message) using the private key, priv. It returns the signature as a // pair of integers. The security of the private key depends on the entropy of // rand. -func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os.Error) { +func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { // See [NSA] 3.4.1 c := priv.PublicKey.Curve + N := c.Params().N var k, kInv *big.Int for { @@ -99,9 +100,9 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. return } - kInv = new(big.Int).ModInverse(k, c.N) + kInv = new(big.Int).ModInverse(k, N) r, _ = priv.Curve.ScalarBaseMult(k.Bytes()) - r.Mod(r, priv.Curve.N) + r.Mod(r, N) if r.Sign() != 0 { break } @@ -111,7 +112,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. s = new(big.Int).Mul(priv.D, r) s.Add(s, e) s.Mul(s, kInv) - s.Mod(s, priv.PublicKey.Curve.N) + s.Mod(s, N) if s.Sign() != 0 { break } @@ -125,15 +126,16 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err os. func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { // See [NSA] 3.4.2 c := pub.Curve + N := c.Params().N if r.Sign() == 0 || s.Sign() == 0 { return false } - if r.Cmp(c.N) >= 0 || s.Cmp(c.N) >= 0 { + if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 { return false } e := hashToInt(hash, c) - w := new(big.Int).ModInverse(s, c.N) + w := new(big.Int).ModInverse(s, N) u1 := e.Mul(e, w) u2 := w.Mul(r, w) @@ -144,6 +146,6 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { return false } x, _ := c.Add(x1, y1, x2, y2) - x.Mod(x, c.N) + x.Mod(x, N) return x.Cmp(r) == 0 } diff --git a/src/pkg/crypto/ecdsa/ecdsa_test.go b/src/pkg/crypto/ecdsa/ecdsa_test.go index d6b403914..3a2b3efba 100644 --- a/src/pkg/crypto/ecdsa/ecdsa_test.go +++ b/src/pkg/crypto/ecdsa/ecdsa_test.go @@ -5,15 +5,15 @@ package ecdsa import ( - "big" "crypto/elliptic" - "crypto/sha1" "crypto/rand" + "crypto/sha1" "encoding/hex" + "math/big" "testing" ) -func testKeyGeneration(t *testing.T, c *elliptic.Curve, tag string) { +func testKeyGeneration(t *testing.T, c elliptic.Curve, tag string) { priv, err := GenerateKey(c, rand.Reader) if err != nil { t.Errorf("%s: error: %s", tag, err) @@ -34,7 +34,7 @@ func TestKeyGeneration(t *testing.T) { testKeyGeneration(t, elliptic.P521(), "p521") } -func testSignAndVerify(t *testing.T, c *elliptic.Curve, tag string) { +func testSignAndVerify(t *testing.T, c elliptic.Curve, tag string) { priv, _ := GenerateKey(c, rand.Reader) hashed := []byte("testing") @@ -214,7 +214,7 @@ func TestVectors(t *testing.T) { msg, _ := hex.DecodeString(test.msg) sha.Reset() sha.Write(msg) - hashed := sha.Sum() + hashed := sha.Sum(nil) r := fromHex(test.r) s := fromHex(test.s) if Verify(&pub, hashed, r, s) != test.ok { diff --git a/src/pkg/crypto/elliptic/Makefile b/src/pkg/crypto/elliptic/Makefile deleted file mode 100644 index 4db5d7de5..000000000 --- a/src/pkg/crypto/elliptic/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/elliptic -GOFILES=\ - elliptic.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/elliptic/elliptic.go b/src/pkg/crypto/elliptic/elliptic.go index 41835f1a9..30835a90b 100644 --- a/src/pkg/crypto/elliptic/elliptic.go +++ b/src/pkg/crypto/elliptic/elliptic.go @@ -14,15 +14,32 @@ package elliptic // reverse the transform than to operate in affine coordinates. import ( - "big" "io" - "os" + "math/big" "sync" ) // A Curve represents a short-form Weierstrass curve with a=-3. // See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html -type Curve struct { +type Curve interface { + // Params returns the parameters for the curve. + Params() *CurveParams + // IsOnCurve returns true if the given (x,y) lies on the curve. + IsOnCurve(x, y *big.Int) bool + // Add returns the sum of (x1,y1) and (x2,y2) + Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) + // Double returns 2*(x,y) + Double(x1, y1 *big.Int) (x, y *big.Int) + // ScalarMult returns k*(Bx,By) where k is a number in big-endian form. + ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) + // ScalarBaseMult returns k*G, where G is the base point of the group and k + // is an integer in big-endian form. + ScalarBaseMult(scalar []byte) (x, y *big.Int) +} + +// CurveParams contains the parameters of an elliptic curve and also provides +// a generic, non-constant time implementation of Curve. +type CurveParams struct { P *big.Int // the order of the underlying field N *big.Int // the order of the base point B *big.Int // the constant of the curve equation @@ -30,8 +47,11 @@ type Curve struct { BitSize int // the size of the underlying field } -// IsOnCurve returns true if the given (x,y) lies on the curve. -func (curve *Curve) IsOnCurve(x, y *big.Int) bool { +func (curve *CurveParams) Params() *CurveParams { + return curve +} + +func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool { // y² = x³ - 3x + b y2 := new(big.Int).Mul(y, y) y2.Mod(y2, curve.P) @@ -51,7 +71,7 @@ func (curve *Curve) IsOnCurve(x, y *big.Int) bool { // affineFromJacobian reverses the Jacobian transform. See the comment at the // top of the file. -func (curve *Curve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { +func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { zinv := new(big.Int).ModInverse(z, curve.P) zinvsq := new(big.Int).Mul(zinv, zinv) @@ -63,15 +83,14 @@ func (curve *Curve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { return } -// Add returns the sum of (x1,y1) and (x2,y2) -func (curve *Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { +func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { z := new(big.Int).SetInt64(1) return curve.affineFromJacobian(curve.addJacobian(x1, y1, z, x2, y2, z)) } // addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and // (x2, y2, z2) and returns their sum, also in Jacobian form. -func (curve *Curve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { +func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl z1z1 := new(big.Int).Mul(z1, z1) z1z1.Mod(z1z1, curve.P) @@ -134,15 +153,14 @@ func (curve *Curve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big return x3, y3, z3 } -// Double returns 2*(x,y) -func (curve *Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { +func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { z1 := new(big.Int).SetInt64(1) return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1)) } // doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and // returns its double, also in Jacobian form. -func (curve *Curve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { +func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b delta := new(big.Int).Mul(z, z) delta.Mod(delta, curve.P) @@ -200,8 +218,7 @@ func (curve *Curve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.I return x3, y3, z3 } -// ScalarMult returns k*(Bx,By) where k is a number in big-endian form. -func (curve *Curve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { +func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { // We have a slight problem in that the identity of the group (the // point at infinity) cannot be represented in (x, y) form on a finite // machine. Thus the standard add/double algorithm has to be tweaked @@ -239,18 +256,17 @@ func (curve *Curve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { return curve.affineFromJacobian(x, y, z) } -// ScalarBaseMult returns k*G, where G is the base point of the group and k is -// an integer in big-endian form. -func (curve *Curve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { +func (curve *CurveParams) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { return curve.ScalarMult(curve.Gx, curve.Gy, k) } var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f} -// GenerateKey returns a public/private key pair. The private key is generated -// using the given reader, which must return random data. -func (curve *Curve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err os.Error) { - byteLen := (curve.BitSize + 7) >> 3 +// GenerateKey returns a public/private key pair. The private key is +// generated using the given reader, which must return random data. +func GenerateKey(curve Curve, rand io.Reader) (priv []byte, x, y *big.Int, err error) { + bitSize := curve.Params().BitSize + byteLen := (bitSize + 7) >> 3 priv = make([]byte, byteLen) for x == nil { @@ -260,7 +276,7 @@ func (curve *Curve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err } // We have to mask off any excess bits in the case that the size of the // underlying field is not a whole number of bytes. - priv[0] &= mask[curve.BitSize%8] + priv[0] &= mask[bitSize%8] // This is because, in tests, rand will return all zeros and we don't // want to get the point at infinity and loop forever. priv[1] ^= 0x42 @@ -269,10 +285,9 @@ func (curve *Curve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err return } -// Marshal converts a point into the form specified in section 4.3.6 of ANSI -// X9.62. -func (curve *Curve) Marshal(x, y *big.Int) []byte { - byteLen := (curve.BitSize + 7) >> 3 +// Marshal converts a point into the form specified in section 4.3.6 of ANSI X9.62. +func Marshal(curve Curve, x, y *big.Int) []byte { + byteLen := (curve.Params().BitSize + 7) >> 3 ret := make([]byte, 1+2*byteLen) ret[0] = 4 // uncompressed point @@ -284,10 +299,9 @@ func (curve *Curve) Marshal(x, y *big.Int) []byte { return ret } -// Unmarshal converts a point, serialized by Marshal, into an x, y pair. On -// error, x = nil. -func (curve *Curve) Unmarshal(data []byte) (x, y *big.Int) { - byteLen := (curve.BitSize + 7) >> 3 +// Unmarshal converts a point, serialized by Marshal, into an x, y pair. On error, x = nil. +func Unmarshal(curve Curve, data []byte) (x, y *big.Int) { + byteLen := (curve.Params().BitSize + 7) >> 3 if len(data) != 1+2*byteLen { return } @@ -300,10 +314,9 @@ func (curve *Curve) Unmarshal(data []byte) (x, y *big.Int) { } var initonce sync.Once -var p224 *Curve -var p256 *Curve -var p384 *Curve -var p521 *Curve +var p256 *CurveParams +var p384 *CurveParams +var p521 *CurveParams func initAll() { initP224() @@ -312,20 +325,9 @@ func initAll() { initP521() } -func initP224() { - // See FIPS 186-3, section D.2.2 - p224 = new(Curve) - p224.P, _ = new(big.Int).SetString("26959946667150639794667015087019630673557916260026308143510066298881", 10) - p224.N, _ = new(big.Int).SetString("26959946667150639794667015087019625940457807714424391721682722368061", 10) - p224.B, _ = new(big.Int).SetString("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16) - p224.Gx, _ = new(big.Int).SetString("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16) - p224.Gy, _ = new(big.Int).SetString("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16) - p224.BitSize = 224 -} - func initP256() { // See FIPS 186-3, section D.2.3 - p256 = new(Curve) + p256 = new(CurveParams) p256.P, _ = new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10) p256.N, _ = new(big.Int).SetString("115792089210356248762697446949407573529996955224135760342422259061068512044369", 10) p256.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16) @@ -336,7 +338,7 @@ func initP256() { func initP384() { // See FIPS 186-3, section D.2.4 - p384 = new(Curve) + p384 = new(CurveParams) p384.P, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319", 10) p384.N, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643", 10) p384.B, _ = new(big.Int).SetString("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 16) @@ -347,7 +349,7 @@ func initP384() { func initP521() { // See FIPS 186-3, section D.2.5 - p521 = new(Curve) + p521 = new(CurveParams) p521.P, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", 10) p521.N, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449", 10) p521.B, _ = new(big.Int).SetString("051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", 16) @@ -356,26 +358,20 @@ func initP521() { p521.BitSize = 521 } -// P224 returns a Curve which implements P-224 (see FIPS 186-3, section D.2.2) -func P224() *Curve { - initonce.Do(initAll) - return p224 -} - // P256 returns a Curve which implements P-256 (see FIPS 186-3, section D.2.3) -func P256() *Curve { +func P256() Curve { initonce.Do(initAll) return p256 } // P384 returns a Curve which implements P-384 (see FIPS 186-3, section D.2.4) -func P384() *Curve { +func P384() Curve { initonce.Do(initAll) return p384 } // P256 returns a Curve which implements P-521 (see FIPS 186-3, section D.2.5) -func P521() *Curve { +func P521() Curve { initonce.Do(initAll) return p521 } diff --git a/src/pkg/crypto/elliptic/elliptic_test.go b/src/pkg/crypto/elliptic/elliptic_test.go index b7e7f035f..1e3407ee0 100644 --- a/src/pkg/crypto/elliptic/elliptic_test.go +++ b/src/pkg/crypto/elliptic/elliptic_test.go @@ -5,15 +5,16 @@ package elliptic import ( - "big" "crypto/rand" + "encoding/hex" "fmt" + "math/big" "testing" ) func TestOnCurve(t *testing.T) { p224 := P224() - if !p224.IsOnCurve(p224.Gx, p224.Gy) { + if !p224.IsOnCurve(p224.Params().Gx, p224.Params().Gy) { t.Errorf("FAIL") } } @@ -295,7 +296,25 @@ func TestBaseMult(t *testing.T) { } x, y := p224.ScalarBaseMult(k.Bytes()) if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y { - t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%s, %s)", i, e.k, x, y, e.x, e.y) + t.Errorf("%d: bad output for k=%s: got (%x, %x), want (%s, %s)", i, e.k, x, y, e.x, e.y) + } + if testing.Short() && i > 5 { + break + } + } +} + +func TestGenericBaseMult(t *testing.T) { + // We use the P224 CurveParams directly in order to test the generic implementation. + p224 := P224().Params() + for i, e := range p224BaseMultTests { + k, ok := new(big.Int).SetString(e.k, 10) + if !ok { + t.Errorf("%d: bad value for k: %s", i, e.k) + } + x, y := p224.ScalarBaseMult(k.Bytes()) + if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y { + t.Errorf("%d: bad output for k=%s: got (%x, %x), want (%s, %s)", i, e.k, x, y, e.x, e.y) } if testing.Short() && i > 5 { break @@ -316,13 +335,13 @@ func BenchmarkBaseMult(b *testing.B) { func TestMarshal(t *testing.T) { p224 := P224() - _, x, y, err := p224.GenerateKey(rand.Reader) + _, x, y, err := GenerateKey(p224, rand.Reader) if err != nil { t.Error(err) return } - serialized := p224.Marshal(x, y) - xx, yy := p224.Unmarshal(serialized) + serialized := Marshal(p224, x, y) + xx, yy := Unmarshal(p224, serialized) if xx == nil { t.Error("failed to unmarshal") return @@ -332,3 +351,13 @@ func TestMarshal(t *testing.T) { return } } + +func TestP224Overflow(t *testing.T) { + // This tests for a specific bug in the P224 implementation. + p224 := P224() + pointData, _ := hex.DecodeString("049B535B45FB0A2072398A6831834624C7E32CCFD5A4B933BCEAF77F1DD945E08BBE5178F5EDF5E733388F196D2A631D2E075BB16CBFEEA15B") + x, y := Unmarshal(p224, pointData) + if !p224.IsOnCurve(x, y) { + t.Error("P224 failed to validate a correct point") + } +} diff --git a/src/pkg/crypto/elliptic/p224.go b/src/pkg/crypto/elliptic/p224.go new file mode 100644 index 000000000..17571c252 --- /dev/null +++ b/src/pkg/crypto/elliptic/p224.go @@ -0,0 +1,718 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package elliptic + +// This is a constant-time, 32-bit implementation of P224. See FIPS 186-3, +// section D.2.2. +// +// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background. + +import ( + "math/big" +) + +var p224 p224Curve + +type p224Curve struct { + *CurveParams + gx, gy, b p224FieldElement +} + +func initP224() { + // See FIPS 186-3, section D.2.2 + p224.CurveParams = new(CurveParams) + p224.P, _ = new(big.Int).SetString("26959946667150639794667015087019630673557916260026308143510066298881", 10) + p224.N, _ = new(big.Int).SetString("26959946667150639794667015087019625940457807714424391721682722368061", 10) + p224.B, _ = new(big.Int).SetString("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16) + p224.Gx, _ = new(big.Int).SetString("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16) + p224.Gy, _ = new(big.Int).SetString("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16) + p224.BitSize = 224 + + p224FromBig(&p224.gx, p224.Gx) + p224FromBig(&p224.gy, p224.Gy) + p224FromBig(&p224.b, p224.B) +} + +// P224 returns a Curve which implements P-224 (see FIPS 186-3, section D.2.2) +func P224() Curve { + initonce.Do(initAll) + return p224 +} + +func (curve p224Curve) Params() *CurveParams { + return curve.CurveParams +} + +func (curve p224Curve) IsOnCurve(bigX, bigY *big.Int) bool { + var x, y p224FieldElement + p224FromBig(&x, bigX) + p224FromBig(&y, bigY) + + // y² = x³ - 3x + b + var tmp p224LargeFieldElement + var x3 p224FieldElement + p224Square(&x3, &x, &tmp) + p224Mul(&x3, &x3, &x, &tmp) + + for i := 0; i < 8; i++ { + x[i] *= 3 + } + p224Sub(&x3, &x3, &x) + p224Reduce(&x3) + p224Add(&x3, &x3, &curve.b) + p224Contract(&x3, &x3) + + p224Square(&y, &y, &tmp) + p224Contract(&y, &y) + + for i := 0; i < 8; i++ { + if y[i] != x3[i] { + return false + } + } + return true +} + +func (p224Curve) Add(bigX1, bigY1, bigX2, bigY2 *big.Int) (x, y *big.Int) { + var x1, y1, z1, x2, y2, z2, x3, y3, z3 p224FieldElement + + p224FromBig(&x1, bigX1) + p224FromBig(&y1, bigY1) + z1[0] = 1 + p224FromBig(&x2, bigX2) + p224FromBig(&y2, bigY2) + z2[0] = 1 + + p224AddJacobian(&x3, &y3, &z3, &x1, &y1, &z1, &x2, &y2, &z2) + return p224ToAffine(&x3, &y3, &z3) +} + +func (p224Curve) Double(bigX1, bigY1 *big.Int) (x, y *big.Int) { + var x1, y1, z1, x2, y2, z2 p224FieldElement + + p224FromBig(&x1, bigX1) + p224FromBig(&y1, bigY1) + z1[0] = 1 + + p224DoubleJacobian(&x2, &y2, &z2, &x1, &y1, &z1) + return p224ToAffine(&x2, &y2, &z2) +} + +func (p224Curve) ScalarMult(bigX1, bigY1 *big.Int, scalar []byte) (x, y *big.Int) { + var x1, y1, z1, x2, y2, z2 p224FieldElement + + p224FromBig(&x1, bigX1) + p224FromBig(&y1, bigY1) + z1[0] = 1 + + p224ScalarMult(&x2, &y2, &z2, &x1, &y1, &z1, scalar) + return p224ToAffine(&x2, &y2, &z2) +} + +func (curve p224Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) { + var z1, x2, y2, z2 p224FieldElement + + z1[0] = 1 + p224ScalarMult(&x2, &y2, &z2, &curve.gx, &curve.gy, &z1, scalar) + return p224ToAffine(&x2, &y2, &z2) +} + +// Field element functions. +// +// The field that we're dealing with is ℤ/pℤ where p = 2**224 - 2**96 + 1. +// +// Field elements are represented by a FieldElement, which is a typedef to an +// array of 8 uint32's. The value of a FieldElement, a, is: +// a[0] + 2**28·a[1] + 2**56·a[1] + ... + 2**196·a[7] +// +// Using 28-bit limbs means that there's only 4 bits of headroom, which is less +// than we would really like. But it has the useful feature that we hit 2**224 +// exactly, making the reflections during a reduce much nicer. +type p224FieldElement [8]uint32 + +// p224Add computes *out = a+b +// +// a[i] + b[i] < 2**32 +func p224Add(out, a, b *p224FieldElement) { + for i := 0; i < 8; i++ { + out[i] = a[i] + b[i] + } +} + +const two31p3 = 1<<31 + 1<<3 +const two31m3 = 1<<31 - 1<<3 +const two31m15m3 = 1<<31 - 1<<15 - 1<<3 + +// p224ZeroModP31 is 0 mod p where bit 31 is set in all limbs so that we can +// subtract smaller amounts without underflow. See the section "Subtraction" in +// [1] for reasoning. +var p224ZeroModP31 = []uint32{two31p3, two31m3, two31m3, two31m15m3, two31m3, two31m3, two31m3, two31m3} + +// p224Sub computes *out = a-b +// +// a[i], b[i] < 2**30 +// out[i] < 2**32 +func p224Sub(out, a, b *p224FieldElement) { + for i := 0; i < 8; i++ { + out[i] = a[i] + p224ZeroModP31[i] - b[i] + } +} + +// LargeFieldElement also represents an element of the field. The limbs are +// still spaced 28-bits apart and in little-endian order. So the limbs are at +// 0, 28, 56, ..., 392 bits, each 64-bits wide. +type p224LargeFieldElement [15]uint64 + +const two63p35 = 1<<63 + 1<<35 +const two63m35 = 1<<63 - 1<<35 +const two63m35m19 = 1<<63 - 1<<35 - 1<<19 + +// p224ZeroModP63 is 0 mod p where bit 63 is set in all limbs. See the section +// "Subtraction" in [1] for why. +var p224ZeroModP63 = [8]uint64{two63p35, two63m35, two63m35, two63m35, two63m35m19, two63m35, two63m35, two63m35} + +const bottom12Bits = 0xfff +const bottom28Bits = 0xfffffff + +// p224Mul computes *out = a*b +// +// a[i] < 2**29, b[i] < 2**30 (or vice versa) +// out[i] < 2**29 +func p224Mul(out, a, b *p224FieldElement, tmp *p224LargeFieldElement) { + for i := 0; i < 15; i++ { + tmp[i] = 0 + } + + for i := 0; i < 8; i++ { + for j := 0; j < 8; j++ { + tmp[i+j] += uint64(a[i]) * uint64(b[j]) + } + } + + p224ReduceLarge(out, tmp) +} + +// Square computes *out = a*a +// +// a[i] < 2**29 +// out[i] < 2**29 +func p224Square(out, a *p224FieldElement, tmp *p224LargeFieldElement) { + for i := 0; i < 15; i++ { + tmp[i] = 0 + } + + for i := 0; i < 8; i++ { + for j := 0; j <= i; j++ { + r := uint64(a[i]) * uint64(a[j]) + if i == j { + tmp[i+j] += r + } else { + tmp[i+j] += r << 1 + } + } + } + + p224ReduceLarge(out, tmp) +} + +// ReduceLarge converts a p224LargeFieldElement to a p224FieldElement. +// +// in[i] < 2**62 +func p224ReduceLarge(out *p224FieldElement, in *p224LargeFieldElement) { + for i := 0; i < 8; i++ { + in[i] += p224ZeroModP63[i] + } + + // Eliminate the coefficients at 2**224 and greater. + for i := 14; i >= 8; i-- { + in[i-8] -= in[i] + in[i-5] += (in[i] & 0xffff) << 12 + in[i-4] += in[i] >> 16 + } + in[8] = 0 + // in[0..8] < 2**64 + + // As the values become small enough, we start to store them in |out| + // and use 32-bit operations. + for i := 1; i < 8; i++ { + in[i+1] += in[i] >> 28 + out[i] = uint32(in[i] & bottom28Bits) + } + in[0] -= in[8] + out[3] += uint32(in[8]&0xffff) << 12 + out[4] += uint32(in[8] >> 16) + // in[0] < 2**64 + // out[3] < 2**29 + // out[4] < 2**29 + // out[1,2,5..7] < 2**28 + + out[0] = uint32(in[0] & bottom28Bits) + out[1] += uint32((in[0] >> 28) & bottom28Bits) + out[2] += uint32(in[0] >> 56) + // out[0] < 2**28 + // out[1..4] < 2**29 + // out[5..7] < 2**28 +} + +// Reduce reduces the coefficients of a to smaller bounds. +// +// On entry: a[i] < 2**31 + 2**30 +// On exit: a[i] < 2**29 +func p224Reduce(a *p224FieldElement) { + for i := 0; i < 7; i++ { + a[i+1] += a[i] >> 28 + a[i] &= bottom28Bits + } + top := a[7] >> 28 + a[7] &= bottom28Bits + + // top < 2**4 + mask := top + mask |= mask >> 2 + mask |= mask >> 1 + mask <<= 31 + mask = uint32(int32(mask) >> 31) + // Mask is all ones if top != 0, all zero otherwise + + a[0] -= top + a[3] += top << 12 + + // We may have just made a[0] negative but, if we did, then we must + // have added something to a[3], this it's > 2**12. Therefore we can + // carry down to a[0]. + a[3] -= 1 & mask + a[2] += mask & (1<<28 - 1) + a[1] += mask & (1<<28 - 1) + a[0] += mask & (1 << 28) +} + +// p224Invert calculates *out = in**-1 by computing in**(2**224 - 2**96 - 1), +// i.e. Fermat's little theorem. +func p224Invert(out, in *p224FieldElement) { + var f1, f2, f3, f4 p224FieldElement + var c p224LargeFieldElement + + p224Square(&f1, in, &c) // 2 + p224Mul(&f1, &f1, in, &c) // 2**2 - 1 + p224Square(&f1, &f1, &c) // 2**3 - 2 + p224Mul(&f1, &f1, in, &c) // 2**3 - 1 + p224Square(&f2, &f1, &c) // 2**4 - 2 + p224Square(&f2, &f2, &c) // 2**5 - 4 + p224Square(&f2, &f2, &c) // 2**6 - 8 + p224Mul(&f1, &f1, &f2, &c) // 2**6 - 1 + p224Square(&f2, &f1, &c) // 2**7 - 2 + for i := 0; i < 5; i++ { // 2**12 - 2**6 + p224Square(&f2, &f2, &c) + } + p224Mul(&f2, &f2, &f1, &c) // 2**12 - 1 + p224Square(&f3, &f2, &c) // 2**13 - 2 + for i := 0; i < 11; i++ { // 2**24 - 2**12 + p224Square(&f3, &f3, &c) + } + p224Mul(&f2, &f3, &f2, &c) // 2**24 - 1 + p224Square(&f3, &f2, &c) // 2**25 - 2 + for i := 0; i < 23; i++ { // 2**48 - 2**24 + p224Square(&f3, &f3, &c) + } + p224Mul(&f3, &f3, &f2, &c) // 2**48 - 1 + p224Square(&f4, &f3, &c) // 2**49 - 2 + for i := 0; i < 47; i++ { // 2**96 - 2**48 + p224Square(&f4, &f4, &c) + } + p224Mul(&f3, &f3, &f4, &c) // 2**96 - 1 + p224Square(&f4, &f3, &c) // 2**97 - 2 + for i := 0; i < 23; i++ { // 2**120 - 2**24 + p224Square(&f4, &f4, &c) + } + p224Mul(&f2, &f4, &f2, &c) // 2**120 - 1 + for i := 0; i < 6; i++ { // 2**126 - 2**6 + p224Square(&f2, &f2, &c) + } + p224Mul(&f1, &f1, &f2, &c) // 2**126 - 1 + p224Square(&f1, &f1, &c) // 2**127 - 2 + p224Mul(&f1, &f1, in, &c) // 2**127 - 1 + for i := 0; i < 97; i++ { // 2**224 - 2**97 + p224Square(&f1, &f1, &c) + } + p224Mul(out, &f1, &f3, &c) // 2**224 - 2**96 - 1 +} + +// p224Contract converts a FieldElement to its unique, minimal form. +// +// On entry, in[i] < 2**29 +// On exit, in[i] < 2**28 +func p224Contract(out, in *p224FieldElement) { + copy(out[:], in[:]) + + for i := 0; i < 7; i++ { + out[i+1] += out[i] >> 28 + out[i] &= bottom28Bits + } + top := out[7] >> 28 + out[7] &= bottom28Bits + + out[0] -= top + out[3] += top << 12 + + // We may just have made out[i] negative. So we carry down. If we made + // out[0] negative then we know that out[3] is sufficiently positive + // because we just added to it. + for i := 0; i < 3; i++ { + mask := uint32(int32(out[i]) >> 31) + out[i] += (1 << 28) & mask + out[i+1] -= 1 & mask + } + + // We might have pushed out[3] over 2**28 so we perform another, partial, + // carry chain. + for i := 3; i < 7; i++ { + out[i+1] += out[i] >> 28 + out[i] &= bottom28Bits + } + top = out[7] >> 28 + out[7] &= bottom28Bits + + // Eliminate top while maintaining the same value mod p. + out[0] -= top + out[3] += top << 12 + + // There are two cases to consider for out[3]: + // 1) The first time that we eliminated top, we didn't push out[3] over + // 2**28. In this case, the partial carry chain didn't change any values + // and top is zero. + // 2) We did push out[3] over 2**28 the first time that we eliminated top. + // The first value of top was in [0..16), therefore, prior to eliminating + // the first top, 0xfff1000 <= out[3] <= 0xfffffff. Therefore, after + // overflowing and being reduced by the second carry chain, out[3] <= + // 0xf000. Thus it cannot have overflowed when we eliminated top for the + // second time. + + // Again, we may just have made out[0] negative, so do the same carry down. + // As before, if we made out[0] negative then we know that out[3] is + // sufficiently positive. + for i := 0; i < 3; i++ { + mask := uint32(int32(out[i]) >> 31) + out[i] += (1 << 28) & mask + out[i+1] -= 1 & mask + } + + // Now we see if the value is >= p and, if so, subtract p. + + // First we build a mask from the top four limbs, which must all be + // equal to bottom28Bits if the whole value is >= p. If top4AllOnes + // ends up with any zero bits in the bottom 28 bits, then this wasn't + // true. + top4AllOnes := uint32(0xffffffff) + for i := 4; i < 8; i++ { + top4AllOnes &= (out[i] & bottom28Bits) - 1 + } + top4AllOnes |= 0xf0000000 + // Now we replicate any zero bits to all the bits in top4AllOnes. + top4AllOnes &= top4AllOnes >> 16 + top4AllOnes &= top4AllOnes >> 8 + top4AllOnes &= top4AllOnes >> 4 + top4AllOnes &= top4AllOnes >> 2 + top4AllOnes &= top4AllOnes >> 1 + top4AllOnes = uint32(int32(top4AllOnes<<31) >> 31) + + // Now we test whether the bottom three limbs are non-zero. + bottom3NonZero := out[0] | out[1] | out[2] + bottom3NonZero |= bottom3NonZero >> 16 + bottom3NonZero |= bottom3NonZero >> 8 + bottom3NonZero |= bottom3NonZero >> 4 + bottom3NonZero |= bottom3NonZero >> 2 + bottom3NonZero |= bottom3NonZero >> 1 + bottom3NonZero = uint32(int32(bottom3NonZero<<31) >> 31) + + // Everything depends on the value of out[3]. + // If it's > 0xffff000 and top4AllOnes != 0 then the whole value is >= p + // If it's = 0xffff000 and top4AllOnes != 0 and bottom3NonZero != 0, + // then the whole value is >= p + // If it's < 0xffff000, then the whole value is < p + n := out[3] - 0xffff000 + out3Equal := n + out3Equal |= out3Equal >> 16 + out3Equal |= out3Equal >> 8 + out3Equal |= out3Equal >> 4 + out3Equal |= out3Equal >> 2 + out3Equal |= out3Equal >> 1 + out3Equal = ^uint32(int32(out3Equal<<31) >> 31) + + // If out[3] > 0xffff000 then n's MSB will be zero. + out3GT := ^uint32(int32(n<<31) >> 31) + + mask := top4AllOnes & ((out3Equal & bottom3NonZero) | out3GT) + out[0] -= 1 & mask + out[3] -= 0xffff000 & mask + out[4] -= 0xfffffff & mask + out[5] -= 0xfffffff & mask + out[6] -= 0xfffffff & mask + out[7] -= 0xfffffff & mask +} + +// Group element functions. +// +// These functions deal with group elements. The group is an elliptic curve +// group with a = -3 defined in FIPS 186-3, section D.2.2. + +// p224AddJacobian computes *out = a+b where a != b. +func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-p224Add-2007-bl + var z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v p224FieldElement + var c p224LargeFieldElement + + // Z1Z1 = Z1² + p224Square(&z1z1, z1, &c) + // Z2Z2 = Z2² + p224Square(&z2z2, z2, &c) + // U1 = X1*Z2Z2 + p224Mul(&u1, x1, &z2z2, &c) + // U2 = X2*Z1Z1 + p224Mul(&u2, x2, &z1z1, &c) + // S1 = Y1*Z2*Z2Z2 + p224Mul(&s1, z2, &z2z2, &c) + p224Mul(&s1, y1, &s1, &c) + // S2 = Y2*Z1*Z1Z1 + p224Mul(&s2, z1, &z1z1, &c) + p224Mul(&s2, y2, &s2, &c) + // H = U2-U1 + p224Sub(&h, &u2, &u1) + p224Reduce(&h) + // I = (2*H)² + for j := 0; j < 8; j++ { + i[j] = h[j] << 1 + } + p224Reduce(&i) + p224Square(&i, &i, &c) + // J = H*I + p224Mul(&j, &h, &i, &c) + // r = 2*(S2-S1) + p224Sub(&r, &s2, &s1) + p224Reduce(&r) + for i := 0; i < 8; i++ { + r[i] <<= 1 + } + p224Reduce(&r) + // V = U1*I + p224Mul(&v, &u1, &i, &c) + // Z3 = ((Z1+Z2)²-Z1Z1-Z2Z2)*H + p224Add(&z1z1, &z1z1, &z2z2) + p224Add(&z2z2, z1, z2) + p224Reduce(&z2z2) + p224Square(&z2z2, &z2z2, &c) + p224Sub(z3, &z2z2, &z1z1) + p224Reduce(z3) + p224Mul(z3, z3, &h, &c) + // X3 = r²-J-2*V + for i := 0; i < 8; i++ { + z1z1[i] = v[i] << 1 + } + p224Add(&z1z1, &j, &z1z1) + p224Reduce(&z1z1) + p224Square(x3, &r, &c) + p224Sub(x3, x3, &z1z1) + p224Reduce(x3) + // Y3 = r*(V-X3)-2*S1*J + for i := 0; i < 8; i++ { + s1[i] <<= 1 + } + p224Mul(&s1, &s1, &j, &c) + p224Sub(&z1z1, &v, x3) + p224Reduce(&z1z1) + p224Mul(&z1z1, &z1z1, &r, &c) + p224Sub(y3, &z1z1, &s1) + p224Reduce(y3) +} + +// p224DoubleJacobian computes *out = a+a. +func p224DoubleJacobian(x3, y3, z3, x1, y1, z1 *p224FieldElement) { + var delta, gamma, beta, alpha, t p224FieldElement + var c p224LargeFieldElement + + p224Square(&delta, z1, &c) + p224Square(&gamma, y1, &c) + p224Mul(&beta, x1, &gamma, &c) + + // alpha = 3*(X1-delta)*(X1+delta) + p224Add(&t, x1, &delta) + for i := 0; i < 8; i++ { + t[i] += t[i] << 1 + } + p224Reduce(&t) + p224Sub(&alpha, x1, &delta) + p224Reduce(&alpha) + p224Mul(&alpha, &alpha, &t, &c) + + // Z3 = (Y1+Z1)²-gamma-delta + p224Add(z3, y1, z1) + p224Reduce(z3) + p224Square(z3, z3, &c) + p224Sub(z3, z3, &gamma) + p224Reduce(z3) + p224Sub(z3, z3, &delta) + p224Reduce(z3) + + // X3 = alpha²-8*beta + for i := 0; i < 8; i++ { + delta[i] = beta[i] << 3 + } + p224Reduce(&delta) + p224Square(x3, &alpha, &c) + p224Sub(x3, x3, &delta) + p224Reduce(x3) + + // Y3 = alpha*(4*beta-X3)-8*gamma² + for i := 0; i < 8; i++ { + beta[i] <<= 2 + } + p224Sub(&beta, &beta, x3) + p224Reduce(&beta) + p224Square(&gamma, &gamma, &c) + for i := 0; i < 8; i++ { + gamma[i] <<= 3 + } + p224Reduce(&gamma) + p224Mul(y3, &alpha, &beta, &c) + p224Sub(y3, y3, &gamma) + p224Reduce(y3) +} + +// p224CopyConditional sets *out = *in iff the least-significant-bit of control +// is true, and it runs in constant time. +func p224CopyConditional(out, in *p224FieldElement, control uint32) { + control <<= 31 + control = uint32(int32(control) >> 31) + + for i := 0; i < 8; i++ { + out[i] ^= (out[i] ^ in[i]) & control + } +} + +func p224ScalarMult(outX, outY, outZ, inX, inY, inZ *p224FieldElement, scalar []byte) { + var xx, yy, zz p224FieldElement + for i := 0; i < 8; i++ { + outZ[i] = 0 + } + + firstBit := uint32(1) + for _, byte := range scalar { + for bitNum := uint(0); bitNum < 8; bitNum++ { + p224DoubleJacobian(outX, outY, outZ, outX, outY, outZ) + bit := uint32((byte >> (7 - bitNum)) & 1) + p224AddJacobian(&xx, &yy, &zz, inX, inY, inZ, outX, outY, outZ) + p224CopyConditional(outX, inX, firstBit&bit) + p224CopyConditional(outY, inY, firstBit&bit) + p224CopyConditional(outZ, inZ, firstBit&bit) + p224CopyConditional(outX, &xx, ^firstBit&bit) + p224CopyConditional(outY, &yy, ^firstBit&bit) + p224CopyConditional(outZ, &zz, ^firstBit&bit) + firstBit = firstBit & ^bit + } + } +} + +// p224ToAffine converts from Jacobian to affine form. +func p224ToAffine(x, y, z *p224FieldElement) (*big.Int, *big.Int) { + var zinv, zinvsq, outx, outy p224FieldElement + var tmp p224LargeFieldElement + + isPointAtInfinity := true + for i := 0; i < 8; i++ { + if z[i] != 0 { + isPointAtInfinity = false + break + } + } + + if isPointAtInfinity { + return nil, nil + } + + p224Invert(&zinv, z) + p224Square(&zinvsq, &zinv, &tmp) + p224Mul(x, x, &zinvsq, &tmp) + p224Mul(&zinvsq, &zinvsq, &zinv, &tmp) + p224Mul(y, y, &zinvsq, &tmp) + + p224Contract(&outx, x) + p224Contract(&outy, y) + return p224ToBig(&outx), p224ToBig(&outy) +} + +// get28BitsFromEnd returns the least-significant 28 bits from buf>>shift, +// where buf is interpreted as a big-endian number. +func get28BitsFromEnd(buf []byte, shift uint) (uint32, []byte) { + var ret uint32 + + for i := uint(0); i < 4; i++ { + var b byte + if l := len(buf); l > 0 { + b = buf[l-1] + // We don't remove the byte if we're about to return and we're not + // reading all of it. + if i != 3 || shift == 4 { + buf = buf[:l-1] + } + } + ret |= uint32(b) << (8 * i) >> shift + } + ret &= bottom28Bits + return ret, buf +} + +// p224FromBig sets *out = *in. +func p224FromBig(out *p224FieldElement, in *big.Int) { + bytes := in.Bytes() + out[0], bytes = get28BitsFromEnd(bytes, 0) + out[1], bytes = get28BitsFromEnd(bytes, 4) + out[2], bytes = get28BitsFromEnd(bytes, 0) + out[3], bytes = get28BitsFromEnd(bytes, 4) + out[4], bytes = get28BitsFromEnd(bytes, 0) + out[5], bytes = get28BitsFromEnd(bytes, 4) + out[6], bytes = get28BitsFromEnd(bytes, 0) + out[7], bytes = get28BitsFromEnd(bytes, 4) +} + +// p224ToBig returns in as a big.Int. +func p224ToBig(in *p224FieldElement) *big.Int { + var buf [28]byte + buf[27] = byte(in[0]) + buf[26] = byte(in[0] >> 8) + buf[25] = byte(in[0] >> 16) + buf[24] = byte(((in[0] >> 24) & 0x0f) | (in[1]<<4)&0xf0) + + buf[23] = byte(in[1] >> 4) + buf[22] = byte(in[1] >> 12) + buf[21] = byte(in[1] >> 20) + + buf[20] = byte(in[2]) + buf[19] = byte(in[2] >> 8) + buf[18] = byte(in[2] >> 16) + buf[17] = byte(((in[2] >> 24) & 0x0f) | (in[3]<<4)&0xf0) + + buf[16] = byte(in[3] >> 4) + buf[15] = byte(in[3] >> 12) + buf[14] = byte(in[3] >> 20) + + buf[13] = byte(in[4]) + buf[12] = byte(in[4] >> 8) + buf[11] = byte(in[4] >> 16) + buf[10] = byte(((in[4] >> 24) & 0x0f) | (in[5]<<4)&0xf0) + + buf[9] = byte(in[5] >> 4) + buf[8] = byte(in[5] >> 12) + buf[7] = byte(in[5] >> 20) + + buf[6] = byte(in[6]) + buf[5] = byte(in[6] >> 8) + buf[4] = byte(in[6] >> 16) + buf[3] = byte(((in[6] >> 24) & 0x0f) | (in[7]<<4)&0xf0) + + buf[2] = byte(in[7] >> 4) + buf[1] = byte(in[7] >> 12) + buf[0] = byte(in[7] >> 20) + + return new(big.Int).SetBytes(buf[:]) +} diff --git a/src/pkg/crypto/elliptic/p224_test.go b/src/pkg/crypto/elliptic/p224_test.go new file mode 100644 index 000000000..4b26d1610 --- /dev/null +++ b/src/pkg/crypto/elliptic/p224_test.go @@ -0,0 +1,47 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package elliptic + +import ( + "math/big" + "testing" +) + +var toFromBigTests = []string{ + "0", + "1", + "23", + "b70e0cb46bb4bf7f321390b94a03c1d356c01122343280d6105c1d21", + "706a46d476dcb76798e6046d89474788d164c18032d268fd10704fa6", +} + +func p224AlternativeToBig(in *p224FieldElement) *big.Int { + ret := new(big.Int) + tmp := new(big.Int) + + for i := uint(0); i < 8; i++ { + tmp.SetInt64(int64(in[i])) + tmp.Lsh(tmp, 28*i) + ret.Add(ret, tmp) + } + ret.Mod(ret, p224.P) + return ret +} + +func TestToFromBig(t *testing.T) { + for i, test := range toFromBigTests { + n, _ := new(big.Int).SetString(test, 16) + var x p224FieldElement + p224FromBig(&x, n) + m := p224ToBig(&x) + if n.Cmp(m) != 0 { + t.Errorf("#%d: %x != %x", i, n, m) + } + q := p224AlternativeToBig(&x) + if n.Cmp(q) != 0 { + t.Errorf("#%d: %x != %x (alternative)", i, n, m) + } + } +} diff --git a/src/pkg/crypto/hmac/Makefile b/src/pkg/crypto/hmac/Makefile deleted file mode 100644 index cc69abf60..000000000 --- a/src/pkg/crypto/hmac/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/hmac -GOFILES=\ - hmac.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/hmac/hmac.go b/src/pkg/crypto/hmac/hmac.go index 04ec86e9a..a97ce0972 100644 --- a/src/pkg/crypto/hmac/hmac.go +++ b/src/pkg/crypto/hmac/hmac.go @@ -9,33 +9,20 @@ package hmac import ( - "crypto/md5" - "crypto/sha1" - "crypto/sha256" "hash" - "os" ) // FIPS 198: // http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf -// key is zero padded to 64 bytes -// ipad = 0x36 byte repeated to 64 bytes -// opad = 0x5c byte repeated to 64 bytes +// key is zero padded to the block size of the hash function +// ipad = 0x36 byte repeated for key length +// opad = 0x5c byte repeated for key length // hmac = H([key ^ opad] H([key ^ ipad] text)) -const ( - // NOTE(rsc): This constant is actually the - // underlying hash function's block size. - // HMAC is only conventionally used with - // MD5 and SHA1, and both use 64-byte blocks. - // The hash.Hash interface doesn't provide a - // way to find out the block size. - padSize = 64 -) - type hmac struct { size int + blocksize int key, tmp []byte outer, inner hash.Hash } @@ -44,57 +31,50 @@ func (h *hmac) tmpPad(xor byte) { for i, k := range h.key { h.tmp[i] = xor ^ k } - for i := len(h.key); i < padSize; i++ { + for i := len(h.key); i < h.blocksize; i++ { h.tmp[i] = xor } } -func (h *hmac) Sum() []byte { - sum := h.inner.Sum() +func (h *hmac) Sum(in []byte) []byte { + origLen := len(in) + in = h.inner.Sum(in) h.tmpPad(0x5c) - for i, b := range sum { - h.tmp[padSize+i] = b - } + copy(h.tmp[h.blocksize:], in[origLen:]) h.outer.Reset() h.outer.Write(h.tmp) - return h.outer.Sum() + return h.outer.Sum(in[:origLen]) } -func (h *hmac) Write(p []byte) (n int, err os.Error) { +func (h *hmac) Write(p []byte) (n int, err error) { return h.inner.Write(p) } func (h *hmac) Size() int { return h.size } +func (h *hmac) BlockSize() int { return h.blocksize } + func (h *hmac) Reset() { h.inner.Reset() h.tmpPad(0x36) - h.inner.Write(h.tmp[0:padSize]) + h.inner.Write(h.tmp[0:h.blocksize]) } -// New returns a new HMAC hash using the given hash generator and key. +// New returns a new HMAC hash using the given hash.Hash type and key. func New(h func() hash.Hash, key []byte) hash.Hash { hm := new(hmac) hm.outer = h() hm.inner = h() hm.size = hm.inner.Size() - hm.tmp = make([]byte, padSize+hm.size) - if len(key) > padSize { + hm.blocksize = hm.inner.BlockSize() + hm.tmp = make([]byte, hm.blocksize+hm.size) + if len(key) > hm.blocksize { // If key is too big, hash it. hm.outer.Write(key) - key = hm.outer.Sum() + key = hm.outer.Sum(nil) } hm.key = make([]byte, len(key)) copy(hm.key, key) hm.Reset() return hm } - -// NewMD5 returns a new HMAC-MD5 hash using the given key. -func NewMD5(key []byte) hash.Hash { return New(md5.New, key) } - -// NewSHA1 returns a new HMAC-SHA1 hash using the given key. -func NewSHA1(key []byte) hash.Hash { return New(sha1.New, key) } - -// NewSHA256 returns a new HMAC-SHA256 hash using the given key. -func NewSHA256(key []byte) hash.Hash { return New(sha256.New, key) } diff --git a/src/pkg/crypto/hmac/hmac_test.go b/src/pkg/crypto/hmac/hmac_test.go index bcae63b8a..07957414c 100644 --- a/src/pkg/crypto/hmac/hmac_test.go +++ b/src/pkg/crypto/hmac/hmac_test.go @@ -5,13 +5,17 @@ package hmac import ( - "hash" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" "fmt" + "hash" "testing" ) type hmacTest struct { - hash func([]byte) hash.Hash + hash func() hash.Hash key []byte in []byte out string @@ -21,7 +25,7 @@ var hmacTests = []hmacTest{ // Tests from US FIPS 198 // http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf { - NewSHA1, + sha1.New, []byte{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, @@ -36,7 +40,7 @@ var hmacTests = []hmacTest{ "4f4ca3d5d68ba7cc0a1208c9c61e9c5da0403c0a", }, { - NewSHA1, + sha1.New, []byte{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, @@ -46,7 +50,7 @@ var hmacTests = []hmacTest{ "0922d3405faa3d194f82a45830737d5cc6c75d24", }, { - NewSHA1, + sha1.New, []byte{ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, @@ -68,7 +72,7 @@ var hmacTests = []hmacTest{ // Test from Plan 9. { - NewMD5, + md5.New, []byte("Jefe"), []byte("what do ya want for nothing?"), "750c783e6ab0b503eaa86e310a5db738", @@ -76,7 +80,7 @@ var hmacTests = []hmacTest{ // Tests from RFC 4231 { - NewSHA256, + sha256.New, []byte{ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, @@ -86,13 +90,13 @@ var hmacTests = []hmacTest{ "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", }, { - NewSHA256, + sha256.New, []byte("Jefe"), []byte("what do ya want for nothing?"), "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", }, { - NewSHA256, + sha256.New, []byte{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, @@ -110,7 +114,7 @@ var hmacTests = []hmacTest{ "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe", }, { - NewSHA256, + sha256.New, []byte{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, @@ -129,7 +133,7 @@ var hmacTests = []hmacTest{ "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b", }, { - NewSHA256, + sha256.New, []byte{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, @@ -153,7 +157,7 @@ var hmacTests = []hmacTest{ "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54", }, { - NewSHA256, + sha256.New, []byte{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, @@ -178,11 +182,295 @@ var hmacTests = []hmacTest{ "be hashed before being used by the HMAC algorithm."), "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2", }, + + // Tests from http://csrc.nist.gov/groups/ST/toolkit/examples.html + // (truncated tag tests are left out) + { + sha1.New, + []byte{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + []byte("Sample message for keylen=blocklen"), + "5fd596ee78d5553c8ff4e72d266dfd192366da29", + }, + { + sha1.New, + []byte{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + }, + []byte("Sample message for keylen 0 { - n := len(p) - if n > _Chunk-d.nx { - n = _Chunk - d.nx - } - for i := 0; i < n; i++ { - d.x[d.nx+i] = p[i] - } - d.nx += n - if d.nx == _Chunk { - _Block(d, d.x[0:]) - d.nx = 0 - } - p = p[n:] - } - n := _Block(d, p) - p = p[n:] - if len(p) > 0 { - d.nx = copy(d.x[:], p) - } - return -} - -func (d0 *digest) Sum() []byte { - // Make a copy of d0, so that caller can keep writing and summing. - d := new(digest) - *d = *d0 - - // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. - len := d.len - var tmp [64]byte - tmp[0] = 0x80 - if len%64 < 56 { - d.Write(tmp[0 : 56-len%64]) - } else { - d.Write(tmp[0 : 64+56-len%64]) - } - - // Length in bits. - len <<= 3 - for i := uint(0); i < 8; i++ { - tmp[i] = byte(len >> (8 * i)) - } - d.Write(tmp[0:8]) - - if d.nx != 0 { - panic("d.nx != 0") - } - - p := make([]byte, 16) - j := 0 - for _, s := range d.s { - p[j+0] = byte(s >> 0) - p[j+1] = byte(s >> 8) - p[j+2] = byte(s >> 16) - p[j+3] = byte(s >> 24) - j += 4 - } - return p -} diff --git a/src/pkg/crypto/md4/md4_test.go b/src/pkg/crypto/md4/md4_test.go deleted file mode 100644 index 721bd4cbc..000000000 --- a/src/pkg/crypto/md4/md4_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package md4 - -import ( - "fmt" - "io" - "testing" -) - -type md4Test struct { - out string - in string -} - -var golden = []md4Test{ - {"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, - {"bde52cb31de33e46245e05fbdbd6fb24", "a"}, - {"ec388dd78999dfc7cf4632465693b6bf", "ab"}, - {"a448017aaf21d8525fc10ae87aa6729d", "abc"}, - {"41decd8f579255c5200f86a4bb3ba740", "abcd"}, - {"9803f4a34e8eb14f96adba49064a0c41", "abcde"}, - {"804e7f1c2586e50b49ac65db5b645131", "abcdef"}, - {"752f4adfe53d1da0241b5bc216d098fc", "abcdefg"}, - {"ad9daf8d49d81988590a6f0e745d15dd", "abcdefgh"}, - {"1e4e28b05464316b56402b3815ed2dfd", "abcdefghi"}, - {"dc959c6f5d6f9e04e4380777cc964b3d", "abcdefghij"}, - {"1b5701e265778898ef7de5623bbe7cc0", "Discard medicine more than two years old."}, - {"d7f087e090fe7ad4a01cb59dacc9a572", "He who has a shady past knows that nice guys finish last."}, - {"a6f8fd6df617c72837592fc3570595c9", "I wouldn't marry him with a ten foot pole."}, - {"c92a84a9526da8abc240c05d6b1a1ce0", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, - {"f6013160c4dcb00847069fee3bb09803", "The days of the digital watch are numbered. -Tom Stoppard"}, - {"2c3bb64f50b9107ed57640fe94bec09f", "Nepal premier won't resign."}, - {"45b7d8a32c7806f2f7f897332774d6e4", "For every action there is an equal and opposite government program."}, - {"b5b4f9026b175c62d7654bdc3a1cd438", "His money is twice tainted: 'taint yours and 'taint mine."}, - {"caf44e80f2c20ce19b5ba1cab766e7bd", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, - {"191fae6707f496aa54a6bce9f2ecf74d", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, - {"9ddc753e7a4ccee6081cd1b45b23a834", "size: a.out: bad magic"}, - {"8d050f55b1cadb9323474564be08a521", "The major problem is with sendmail. -Mark Horton"}, - {"ad6e2587f74c3e3cc19146f6127fa2e3", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, - {"1d616d60a5fabe85589c3f1566ca7fca", "If the enemy is within range, then so are you."}, - {"aec3326a4f496a2ced65a1963f84577f", "It's well we cannot hear the screams/That we create in others' dreams."}, - {"77b4fd762d6b9245e61c50bf6ebf118b", "You remind me of a TV show, but that's all right: I watch it anyway."}, - {"e8f48c726bae5e516f6ddb1a4fe62438", "C is as portable as Stonehedge!!"}, - {"a3a84366e7219e887423b01f9be7166e", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, - {"a6b7aa35157e984ef5d9b7f32e5fbb52", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, - {"75661f0545955f8f9abeeb17845f3fd6", "How can you write a big system without C++? -Paul Glick"}, -} - -func TestGolden(t *testing.T) { - for i := 0; i < len(golden); i++ { - g := golden[i] - c := New() - for j := 0; j < 3; j++ { - if j < 2 { - io.WriteString(c, g.in) - } else { - io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() - io.WriteString(c, g.in[len(g.in)/2:]) - } - s := fmt.Sprintf("%x", c.Sum()) - if s != g.out { - t.Fatalf("md4[%d](%s) = %s want %s", j, g.in, s, g.out) - } - c.Reset() - } - } -} diff --git a/src/pkg/crypto/md4/md4block.go b/src/pkg/crypto/md4/md4block.go deleted file mode 100644 index 3fed475f3..000000000 --- a/src/pkg/crypto/md4/md4block.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// MD4 block step. -// In its own file so that a faster assembly or C version -// can be substituted easily. - -package md4 - -var shift1 = []uint{3, 7, 11, 19} -var shift2 = []uint{3, 5, 9, 13} -var shift3 = []uint{3, 9, 11, 15} - -var xIndex2 = []uint{0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15} -var xIndex3 = []uint{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15} - -func _Block(dig *digest, p []byte) int { - a := dig.s[0] - b := dig.s[1] - c := dig.s[2] - d := dig.s[3] - n := 0 - var X [16]uint32 - for len(p) >= _Chunk { - aa, bb, cc, dd := a, b, c, d - - j := 0 - for i := 0; i < 16; i++ { - X[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 - j += 4 - } - - // If this needs to be made faster in the future, - // the usual trick is to unroll each of these - // loops by a factor of 4; that lets you replace - // the shift[] lookups with constants and, - // with suitable variable renaming in each - // unrolled body, delete the a, b, c, d = d, a, b, c - // (or you can let the optimizer do the renaming). - // - // The index variables are uint so that % by a power - // of two can be optimized easily by a compiler. - - // Round 1. - for i := uint(0); i < 16; i++ { - x := i - s := shift1[i%4] - f := ((c ^ d) & b) ^ d - a += f + X[x] - a = a<>(32-s) - a, b, c, d = d, a, b, c - } - - // Round 2. - for i := uint(0); i < 16; i++ { - x := xIndex2[i] - s := shift2[i%4] - g := (b & c) | (b & d) | (c & d) - a += g + X[x] + 0x5a827999 - a = a<>(32-s) - a, b, c, d = d, a, b, c - } - - // Round 3. - for i := uint(0); i < 16; i++ { - x := xIndex3[i] - s := shift3[i%4] - h := b ^ c ^ d - a += h + X[x] + 0x6ed9eba1 - a = a<>(32-s) - a, b, c, d = d, a, b, c - } - - a += aa - b += bb - c += cc - d += dd - - p = p[_Chunk:] - n += _Chunk - } - - dig.s[0] = a - dig.s[1] = b - dig.s[2] = c - dig.s[3] = d - return n -} diff --git a/src/pkg/crypto/md5/Makefile b/src/pkg/crypto/md5/Makefile deleted file mode 100644 index 5cde3e6d6..000000000 --- a/src/pkg/crypto/md5/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/md5 -GOFILES=\ - md5.go\ - md5block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/md5/md5.go b/src/pkg/crypto/md5/md5.go index 378faa6ec..cfb728c94 100644 --- a/src/pkg/crypto/md5/md5.go +++ b/src/pkg/crypto/md5/md5.go @@ -8,7 +8,6 @@ package md5 import ( "crypto" "hash" - "os" ) func init() { @@ -18,6 +17,9 @@ func init() { // The size of an MD5 checksum in bytes. const Size = 16 +// The blocksize of MD5 in bytes. +const BlockSize = 64 + const ( _Chunk = 64 _Init0 = 0x67452301 @@ -52,7 +54,9 @@ func New() hash.Hash { func (d *digest) Size() int { return Size } -func (d *digest) Write(p []byte) (nn int, err os.Error) { +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -78,10 +82,9 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. - d := new(digest) - *d = *d0 + d := *d0 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. len := d.len @@ -104,14 +107,13 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 16) - j := 0 - for _, s := range d.s { - p[j+0] = byte(s >> 0) - p[j+1] = byte(s >> 8) - p[j+2] = byte(s >> 16) - p[j+3] = byte(s >> 24) - j += 4 + var digest [Size]byte + for i, s := range d.s { + digest[i*4] = byte(s) + digest[i*4+1] = byte(s >> 8) + digest[i*4+2] = byte(s >> 16) + digest[i*4+3] = byte(s >> 24) } - return p + + return append(in, digest[:]...) } diff --git a/src/pkg/crypto/md5/md5_test.go b/src/pkg/crypto/md5/md5_test.go index 857002b70..aae875464 100644 --- a/src/pkg/crypto/md5/md5_test.go +++ b/src/pkg/crypto/md5/md5_test.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package md5 +package md5_test import ( + "crypto/md5" "fmt" "io" "testing" @@ -52,16 +53,16 @@ var golden = []md5Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] - c := New() + c := md5.New() for j := 0; j < 3; j++ { if j < 2 { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("md5[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -69,3 +70,11 @@ func TestGolden(t *testing.T) { } } } + +func ExampleNew() { + h := md5.New() + io.WriteString(h, "The fog is getting thicker!") + io.WriteString(h, "And Leon's getting laaarger!") + fmt.Printf("%x", h.Sum(nil)) + // Output: e2c569be17396eca2a2e3c11578123ed +} diff --git a/src/pkg/crypto/ocsp/Makefile b/src/pkg/crypto/ocsp/Makefile deleted file mode 100644 index 6e132ff9b..000000000 --- a/src/pkg/crypto/ocsp/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/ocsp -GOFILES=\ - ocsp.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/ocsp/ocsp.go b/src/pkg/crypto/ocsp/ocsp.go deleted file mode 100644 index 7ea7a1e82..000000000 --- a/src/pkg/crypto/ocsp/ocsp.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package ocsp parses OCSP responses as specified in RFC 2560. OCSP responses -// are signed messages attesting to the validity of a certificate for a small -// period of time. This is used to manage revocation for X.509 certificates. -package ocsp - -import ( - "asn1" - "crypto" - "crypto/rsa" - _ "crypto/sha1" - "crypto/x509" - "crypto/x509/pkix" - "os" - "time" -) - -var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1}) -var idSHA1WithRSA = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 1, 5}) - -// These are internal structures that reflect the ASN.1 structure of an OCSP -// response. See RFC 2560, section 4.2. - -const ( - ocspSuccess = 0 - ocspMalformed = 1 - ocspInternalError = 2 - ocspTryLater = 3 - ocspSigRequired = 4 - ocspUnauthorized = 5 -) - -type certID struct { - HashAlgorithm pkix.AlgorithmIdentifier - NameHash []byte - IssuerKeyHash []byte - SerialNumber asn1.RawValue -} - -type responseASN1 struct { - Status asn1.Enumerated - Response responseBytes `asn1:"explicit,tag:0"` -} - -type responseBytes struct { - ResponseType asn1.ObjectIdentifier - Response []byte -} - -type basicResponse struct { - TBSResponseData responseData - SignatureAlgorithm pkix.AlgorithmIdentifier - Signature asn1.BitString - Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"` -} - -type responseData struct { - Raw asn1.RawContent - Version int `asn1:"optional,default:1,explicit,tag:0"` - RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"` - KeyHash []byte `asn1:"optional,explicit,tag:2"` - ProducedAt *time.Time - Responses []singleResponse -} - -type singleResponse struct { - CertID certID - Good asn1.Flag `asn1:"explicit,tag:0,optional"` - Revoked revokedInfo `asn1:"explicit,tag:1,optional"` - Unknown asn1.Flag `asn1:"explicit,tag:2,optional"` - ThisUpdate *time.Time - NextUpdate *time.Time `asn1:"explicit,tag:0,optional"` -} - -type revokedInfo struct { - RevocationTime *time.Time - Reason int `asn1:"explicit,tag:0,optional"` -} - -// This is the exposed reflection of the internal OCSP structures. - -const ( - // Good means that the certificate is valid. - Good = iota - // Revoked means that the certificate has been deliberately revoked. - Revoked = iota - // Unknown means that the OCSP responder doesn't know about the certificate. - Unknown = iota - // ServerFailed means that the OCSP responder failed to process the request. - ServerFailed = iota -) - -// Response represents an OCSP response. See RFC 2560. -type Response struct { - // Status is one of {Good, Revoked, Unknown, ServerFailed} - Status int - SerialNumber []byte - ProducedAt, ThisUpdate, NextUpdate, RevokedAt *time.Time - RevocationReason int - Certificate *x509.Certificate -} - -// ParseError results from an invalid OCSP response. -type ParseError string - -func (p ParseError) String() string { - return string(p) -} - -// ParseResponse parses an OCSP response in DER form. It only supports -// responses for a single certificate and only those using RSA signatures. -// Non-RSA responses will result in an x509.UnsupportedAlgorithmError. -// Signature errors or parse failures will result in a ParseError. -func ParseResponse(bytes []byte) (*Response, os.Error) { - var resp responseASN1 - rest, err := asn1.Unmarshal(bytes, &resp) - if err != nil { - return nil, err - } - if len(rest) > 0 { - return nil, ParseError("trailing data in OCSP response") - } - - ret := new(Response) - if resp.Status != ocspSuccess { - ret.Status = ServerFailed - return ret, nil - } - - if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) { - return nil, ParseError("bad OCSP response type") - } - - var basicResp basicResponse - rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp) - if err != nil { - return nil, err - } - - if len(basicResp.Certificates) != 1 { - return nil, ParseError("OCSP response contains bad number of certificates") - } - - if len(basicResp.TBSResponseData.Responses) != 1 { - return nil, ParseError("OCSP response contains bad number of responses") - } - - ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes) - if err != nil { - return nil, err - } - - if ret.Certificate.PublicKeyAlgorithm != x509.RSA || !basicResp.SignatureAlgorithm.Algorithm.Equal(idSHA1WithRSA) { - return nil, x509.UnsupportedAlgorithmError{} - } - - hashType := crypto.SHA1 - h := hashType.New() - - pub := ret.Certificate.PublicKey.(*rsa.PublicKey) - h.Write(basicResp.TBSResponseData.Raw) - digest := h.Sum() - signature := basicResp.Signature.RightAlign() - - if rsa.VerifyPKCS1v15(pub, hashType, digest, signature) != nil { - return nil, ParseError("bad OCSP signature") - } - - r := basicResp.TBSResponseData.Responses[0] - - ret.SerialNumber = r.CertID.SerialNumber.Bytes - - switch { - case bool(r.Good): - ret.Status = Good - case bool(r.Unknown): - ret.Status = Unknown - default: - ret.Status = Revoked - ret.RevokedAt = r.Revoked.RevocationTime - ret.RevocationReason = r.Revoked.Reason - } - - ret.ProducedAt = basicResp.TBSResponseData.ProducedAt - ret.ThisUpdate = r.ThisUpdate - ret.NextUpdate = r.NextUpdate - - return ret, nil -} diff --git a/src/pkg/crypto/ocsp/ocsp_test.go b/src/pkg/crypto/ocsp/ocsp_test.go deleted file mode 100644 index f9889790f..000000000 --- a/src/pkg/crypto/ocsp/ocsp_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package ocsp - -import ( - "bytes" - "encoding/hex" - "reflect" - "testing" - "time" -) - -func TestOCSPDecode(t *testing.T) { - responseBytes, _ := hex.DecodeString(ocspResponseHex) - resp, err := ParseResponse(responseBytes) - if err != nil { - t.Error(err) - } - - expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}} - - if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) { - t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate) - } - - if !reflect.DeepEqual(resp.NextUpdate, resp.NextUpdate) { - t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, expected.NextUpdate) - } - - if resp.Status != expected.Status { - t.Errorf("resp.Status: got %d, want %d", resp.Status, expected.Status) - } - - if !bytes.Equal(resp.SerialNumber, expected.SerialNumber) { - t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, expected.SerialNumber) - } - - if resp.RevocationReason != expected.RevocationReason { - t.Errorf("resp.RevocationReason: got %d, want %d", resp.RevocationReason, expected.RevocationReason) - } -} - -// This OCSP response was taken from Thawte's public OCSP responder. -// To recreate: -// $ openssl s_client -tls1 -showcerts -servername www.google.com -connect www.google.com:443 -// Copy and paste the first certificate into /tmp/cert.crt and the second into -// /tmp/intermediate.crt -// $ openssl ocsp -issuer /tmp/intermediate.crt -cert /tmp/cert.crt -url http://ocsp.thawte.com -resp_text -respout /tmp/ocsp.der -// Then hex encode the result: -// $ python -c 'print file("/tmp/ocsp.der", "r").read().encode("hex")' - -const ocspResponseHex = "308206bc0a0100a08206b5308206b106092b0601050507300101048206a23082069e3081" + - "c9a14e304c310b300906035504061302494c31163014060355040a130d5374617274436f" + - "6d204c74642e312530230603550403131c5374617274436f6d20436c6173732031204f43" + - "5350205369676e6572180f32303130303730373137333531375a30663064303c30090605" + - "2b0e03021a050004146568874f40750f016a3475625e1f5c93e5a26d580414eb4234d098" + - "b0ab9ff41b6b08f7cc642eef0e2c45020301d0fa8000180f323031303037303731353031" + - "30355aa011180f32303130303730373138333531375a300d06092a864886f70d01010505" + - "000382010100ab557ff070d1d7cebbb5f0ec91a15c3fed22eb2e1b8244f1b84545f013a4" + - "fb46214c5e3fbfbebb8a56acc2b9db19f68fd3c3201046b3824d5ba689f99864328710cb" + - "467195eb37d84f539e49f859316b32964dc3e47e36814ce94d6c56dd02733b1d0802f7ff" + - "4eebdbbd2927dcf580f16cbc290f91e81b53cb365e7223f1d6e20a88ea064104875e0145" + - "672b20fc14829d51ca122f5f5d77d3ad6c83889c55c7dc43680ba2fe3cef8b05dbcabdc0" + - "d3e09aaf9725597f8c858c2fa38c0d6aed2e6318194420dd1a1137445d13e1c97ab47896" + - "17a4e08925f46f867b72e3a4dc1f08cb870b2b0717f7207faa0ac512e628a029aba7457a" + - "e63dcf3281e2162d9349a08204ba308204b6308204b23082039aa003020102020101300d" + - "06092a864886f70d010105050030818c310b300906035504061302494c31163014060355" + - "040a130d5374617274436f6d204c74642e312b3029060355040b13225365637572652044" + - "69676974616c204365727469666963617465205369676e696e6731383036060355040313" + - "2f5374617274436f6d20436c6173732031205072696d61727920496e7465726d65646961" + - "746520536572766572204341301e170d3037313032353030323330365a170d3132313032" + - "333030323330365a304c310b300906035504061302494c31163014060355040a130d5374" + - "617274436f6d204c74642e312530230603550403131c5374617274436f6d20436c617373" + - "2031204f435350205369676e657230820122300d06092a864886f70d0101010500038201" + - "0f003082010a0282010100b9561b4c45318717178084e96e178df2255e18ed8d8ecc7c2b" + - "7b51a6c1c2e6bf0aa3603066f132fe10ae97b50e99fa24b83fc53dd2777496387d14e1c3" + - "a9b6a4933e2ac12413d085570a95b8147414a0bc007c7bcf222446ef7f1a156d7ea1c577" + - "fc5f0facdfd42eb0f5974990cb2f5cefebceef4d1bdc7ae5c1075c5a99a93171f2b0845b" + - "4ff0864e973fcfe32f9d7511ff87a3e943410c90a4493a306b6944359340a9ca96f02b66" + - "ce67f028df2980a6aaee8d5d5d452b8b0eb93f923cc1e23fcccbdbe7ffcb114d08fa7a6a" + - "3c404f825d1a0e715935cf623a8c7b59670014ed0622f6089a9447a7a19010f7fe58f841" + - "29a2765ea367824d1c3bb2fda308530203010001a382015c30820158300c0603551d1301" + - "01ff04023000300b0603551d0f0404030203a8301e0603551d250417301506082b060105" + - "0507030906092b0601050507300105301d0603551d0e0416041445e0a36695414c5dd449" + - "bc00e33cdcdbd2343e173081a80603551d230481a030819d8014eb4234d098b0ab9ff41b" + - "6b08f7cc642eef0e2c45a18181a47f307d310b300906035504061302494c311630140603" + - "55040a130d5374617274436f6d204c74642e312b3029060355040b132253656375726520" + - "4469676974616c204365727469666963617465205369676e696e67312930270603550403" + - "13205374617274436f6d2043657274696669636174696f6e20417574686f726974798201" + - "0a30230603551d12041c301a8618687474703a2f2f7777772e737461727473736c2e636f" + - "6d2f302c06096086480186f842010d041f161d5374617274436f6d205265766f63617469" + - "6f6e20417574686f72697479300d06092a864886f70d01010505000382010100182d2215" + - "8f0fc0291324fa8574c49bb8ff2835085adcbf7b7fc4191c397ab6951328253fffe1e5ec" + - "2a7da0d50fca1a404e6968481366939e666c0a6209073eca57973e2fefa9ed1718e8176f" + - "1d85527ff522c08db702e3b2b180f1cbff05d98128252cf0f450f7dd2772f4188047f19d" + - "c85317366f94bc52d60f453a550af58e308aaab00ced33040b62bf37f5b1ab2a4f7f0f80" + - "f763bf4d707bc8841d7ad9385ee2a4244469260b6f2bf085977af9074796048ecc2f9d48" + - "a1d24ce16e41a9941568fec5b42771e118f16c106a54ccc339a4b02166445a167902e75e" + - "6d8620b0825dcd18a069b90fd851d10fa8effd409deec02860d26d8d833f304b10669b42" diff --git a/src/pkg/crypto/openpgp/Makefile b/src/pkg/crypto/openpgp/Makefile deleted file mode 100644 index b46ac2bba..000000000 --- a/src/pkg/crypto/openpgp/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/openpgp -GOFILES=\ - canonical_text.go\ - keys.go\ - read.go\ - write.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/armor/Makefile b/src/pkg/crypto/openpgp/armor/Makefile deleted file mode 100644 index 138e314e9..000000000 --- a/src/pkg/crypto/openpgp/armor/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../../Make.inc - -TARG=crypto/openpgp/armor -GOFILES=\ - armor.go\ - encode.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/armor/armor.go b/src/pkg/crypto/openpgp/armor/armor.go deleted file mode 100644 index 9c4180d6d..000000000 --- a/src/pkg/crypto/openpgp/armor/armor.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is -// very similar to PEM except that it has an additional CRC checksum. -package armor - -import ( - "bufio" - "bytes" - "crypto/openpgp/error" - "encoding/base64" - "io" - "os" -) - -// A Block represents an OpenPGP armored structure. -// -// The encoded form is: -// -----BEGIN Type----- -// Headers -// -// base64-encoded Bytes -// '=' base64 encoded checksum -// -----END Type----- -// where Headers is a possibly empty sequence of Key: Value lines. -// -// Since the armored data can be very large, this package presents a streaming -// interface. -type Block struct { - Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE"). - Header map[string]string // Optional headers. - Body io.Reader // A Reader from which the contents can be read - lReader lineReader - oReader openpgpReader -} - -var ArmorCorrupt os.Error = error.StructuralError("armor invalid") - -const crc24Init = 0xb704ce -const crc24Poly = 0x1864cfb -const crc24Mask = 0xffffff - -// crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 -func crc24(crc uint32, d []byte) uint32 { - for _, b := range d { - crc ^= uint32(b) << 16 - for i := 0; i < 8; i++ { - crc <<= 1 - if crc&0x1000000 != 0 { - crc ^= crc24Poly - } - } - } - return crc -} - -var armorStart = []byte("-----BEGIN ") -var armorEnd = []byte("-----END ") -var armorEndOfLine = []byte("-----") - -// lineReader wraps a line based reader. It watches for the end of an armor -// block and records the expected CRC value. -type lineReader struct { - in *bufio.Reader - buf []byte - eof bool - crc uint32 -} - -func (l *lineReader) Read(p []byte) (n int, err os.Error) { - if l.eof { - return 0, os.EOF - } - - if len(l.buf) > 0 { - n = copy(p, l.buf) - l.buf = l.buf[n:] - return - } - - line, isPrefix, err := l.in.ReadLine() - if err != nil { - return - } - if isPrefix { - return 0, ArmorCorrupt - } - - if len(line) == 5 && line[0] == '=' { - // This is the checksum line - var expectedBytes [3]byte - var m int - m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) - if m != 3 || err != nil { - return - } - l.crc = uint32(expectedBytes[0])<<16 | - uint32(expectedBytes[1])<<8 | - uint32(expectedBytes[2]) - - line, _, err = l.in.ReadLine() - if err != nil && err != os.EOF { - return - } - if !bytes.HasPrefix(line, armorEnd) { - return 0, ArmorCorrupt - } - - l.eof = true - return 0, os.EOF - } - - if len(line) > 64 { - return 0, ArmorCorrupt - } - - n = copy(p, line) - bytesToSave := len(line) - n - if bytesToSave > 0 { - if cap(l.buf) < bytesToSave { - l.buf = make([]byte, 0, bytesToSave) - } - l.buf = l.buf[0:bytesToSave] - copy(l.buf, line[n:]) - } - - return -} - -// openpgpReader passes Read calls to the underlying base64 decoder, but keeps -// a running CRC of the resulting data and checks the CRC against the value -// found by the lineReader at EOF. -type openpgpReader struct { - lReader *lineReader - b64Reader io.Reader - currentCRC uint32 -} - -func (r *openpgpReader) Read(p []byte) (n int, err os.Error) { - n, err = r.b64Reader.Read(p) - r.currentCRC = crc24(r.currentCRC, p[:n]) - - if err == os.EOF { - if r.lReader.crc != uint32(r.currentCRC&crc24Mask) { - return 0, ArmorCorrupt - } - } - - return -} - -// Decode reads a PGP armored block from the given Reader. It will ignore -// leading garbage. If it doesn't find a block, it will return nil, os.EOF. The -// given Reader is not usable after calling this function: an arbitrary amount -// of data may have been read past the end of the block. -func Decode(in io.Reader) (p *Block, err os.Error) { - r, _ := bufio.NewReaderSize(in, 100) - var line []byte - ignoreNext := false - -TryNextBlock: - p = nil - - // Skip leading garbage - for { - ignoreThis := ignoreNext - line, ignoreNext, err = r.ReadLine() - if err != nil { - return - } - if ignoreNext || ignoreThis { - continue - } - line = bytes.TrimSpace(line) - if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) { - break - } - } - - p = new(Block) - p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)]) - p.Header = make(map[string]string) - nextIsContinuation := false - var lastKey string - - // Read headers - for { - isContinuation := nextIsContinuation - line, nextIsContinuation, err = r.ReadLine() - if err != nil { - p = nil - return - } - if isContinuation { - p.Header[lastKey] += string(line) - continue - } - line = bytes.TrimSpace(line) - if len(line) == 0 { - break - } - - i := bytes.Index(line, []byte(": ")) - if i == -1 { - goto TryNextBlock - } - lastKey = string(line[:i]) - p.Header[lastKey] = string(line[i+2:]) - } - - p.lReader.in = r - p.oReader.currentCRC = crc24Init - p.oReader.lReader = &p.lReader - p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) - p.Body = &p.oReader - - return -} diff --git a/src/pkg/crypto/openpgp/armor/armor_test.go b/src/pkg/crypto/openpgp/armor/armor_test.go deleted file mode 100644 index 9334e94e9..000000000 --- a/src/pkg/crypto/openpgp/armor/armor_test.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package armor - -import ( - "bytes" - "hash/adler32" - "io/ioutil" - "testing" -) - -func TestDecodeEncode(t *testing.T) { - buf := bytes.NewBuffer([]byte(armorExample1)) - result, err := Decode(buf) - if err != nil { - t.Error(err) - } - expectedType := "PGP SIGNATURE" - if result.Type != expectedType { - t.Errorf("result.Type: got:%s want:%s", result.Type, expectedType) - } - if len(result.Header) != 1 { - t.Errorf("len(result.Header): got:%d want:1", len(result.Header)) - } - v, ok := result.Header["Version"] - if !ok || v != "GnuPG v1.4.10 (GNU/Linux)" { - t.Errorf("result.Header: got:%#v", result.Header) - } - - contents, err := ioutil.ReadAll(result.Body) - if err != nil { - t.Error(err) - } - - if adler32.Checksum(contents) != 0x27b144be { - t.Errorf("contents: got: %x", contents) - } - - buf = bytes.NewBuffer(nil) - w, err := Encode(buf, result.Type, result.Header) - if err != nil { - t.Error(err) - } - _, err = w.Write(contents) - if err != nil { - t.Error(err) - } - w.Close() - - if !bytes.Equal(buf.Bytes(), []byte(armorExample1)) { - t.Errorf("got: %s\nwant: %s", string(buf.Bytes()), armorExample1) - } -} - -func TestLongHeader(t *testing.T) { - buf := bytes.NewBuffer([]byte(armorLongLine)) - result, err := Decode(buf) - if err != nil { - t.Error(err) - return - } - value, ok := result.Header["Version"] - if !ok { - t.Errorf("missing Version header") - } - if value != longValueExpected { - t.Errorf("got: %s want: %s", value, longValueExpected) - } -} - -const armorExample1 = `-----BEGIN PGP SIGNATURE----- -Version: GnuPG v1.4.10 (GNU/Linux) - -iJwEAAECAAYFAk1Fv/0ACgkQo01+GMIMMbsYTwQAiAw+QAaNfY6WBdplZ/uMAccm -4g+81QPmTSGHnetSb6WBiY13kVzK4HQiZH8JSkmmroMLuGeJwsRTEL4wbjRyUKEt -p1xwUZDECs234F1xiG5enc5SGlRtP7foLBz9lOsjx+LEcA4sTl5/2eZR9zyFZqWW -TxRjs+fJCIFuo71xb1g= -=/teI ------END PGP SIGNATURE-----` - -const armorLongLine = `-----BEGIN PGP SIGNATURE----- -Version: 0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz - -iQEcBAABAgAGBQJMtFESAAoJEKsQXJGvOPsVj40H/1WW6jaMXv4BW+1ueDSMDwM8 -kx1fLOXbVM5/Kn5LStZNt1jWWnpxdz7eq3uiqeCQjmqUoRde3YbB2EMnnwRbAhpp -cacnAvy9ZQ78OTxUdNW1mhX5bS6q1MTEJnl+DcyigD70HG/yNNQD7sOPMdYQw0TA -byQBwmLwmTsuZsrYqB68QyLHI+DUugn+kX6Hd2WDB62DKa2suoIUIHQQCd/ofwB3 -WfCYInXQKKOSxu2YOg2Eb4kLNhSMc1i9uKUWAH+sdgJh7NBgdoE4MaNtBFkHXRvv -okWuf3+xA9ksp1npSY/mDvgHijmjvtpRDe6iUeqfCn8N9u9CBg8geANgaG8+QA4= -=wfQG ------END PGP SIGNATURE-----` - -const longValueExpected = "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz" diff --git a/src/pkg/crypto/openpgp/armor/encode.go b/src/pkg/crypto/openpgp/armor/encode.go deleted file mode 100644 index 99dee375e..000000000 --- a/src/pkg/crypto/openpgp/armor/encode.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package armor - -import ( - "encoding/base64" - "io" - "os" -) - -var armorHeaderSep = []byte(": ") -var blockEnd = []byte("\n=") -var newline = []byte("\n") -var armorEndOfLineOut = []byte("-----\n") - -// writeSlices writes its arguments to the given Writer. -func writeSlices(out io.Writer, slices ...[]byte) (err os.Error) { - for _, s := range slices { - _, err = out.Write(s) - if err != nil { - return err - } - } - return -} - -// lineBreaker breaks data across several lines, all of the same byte length -// (except possibly the last). Lines are broken with a single '\n'. -type lineBreaker struct { - lineLength int - line []byte - used int - out io.Writer - haveWritten bool -} - -func newLineBreaker(out io.Writer, lineLength int) *lineBreaker { - return &lineBreaker{ - lineLength: lineLength, - line: make([]byte, lineLength), - used: 0, - out: out, - } -} - -func (l *lineBreaker) Write(b []byte) (n int, err os.Error) { - n = len(b) - - if n == 0 { - return - } - - if l.used == 0 && l.haveWritten { - _, err = l.out.Write([]byte{'\n'}) - if err != nil { - return - } - } - - if l.used+len(b) < l.lineLength { - l.used += copy(l.line[l.used:], b) - return - } - - l.haveWritten = true - _, err = l.out.Write(l.line[0:l.used]) - if err != nil { - return - } - excess := l.lineLength - l.used - l.used = 0 - - _, err = l.out.Write(b[0:excess]) - if err != nil { - return - } - - _, err = l.Write(b[excess:]) - return -} - -func (l *lineBreaker) Close() (err os.Error) { - if l.used > 0 { - _, err = l.out.Write(l.line[0:l.used]) - if err != nil { - return - } - } - - return -} - -// encoding keeps track of a running CRC24 over the data which has been written -// to it and outputs a OpenPGP checksum when closed, followed by an armor -// trailer. -// -// It's built into a stack of io.Writers: -// encoding -> base64 encoder -> lineBreaker -> out -type encoding struct { - out io.Writer - breaker *lineBreaker - b64 io.WriteCloser - crc uint32 - blockType []byte -} - -func (e *encoding) Write(data []byte) (n int, err os.Error) { - e.crc = crc24(e.crc, data) - return e.b64.Write(data) -} - -func (e *encoding) Close() (err os.Error) { - err = e.b64.Close() - if err != nil { - return - } - e.breaker.Close() - - var checksumBytes [3]byte - checksumBytes[0] = byte(e.crc >> 16) - checksumBytes[1] = byte(e.crc >> 8) - checksumBytes[2] = byte(e.crc) - - var b64ChecksumBytes [4]byte - base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) - - return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) -} - -// Encode returns a WriteCloser which will encode the data written to it in -// OpenPGP armor. -func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err os.Error) { - bType := []byte(blockType) - err = writeSlices(out, armorStart, bType, armorEndOfLineOut) - if err != nil { - return - } - - for k, v := range headers { - err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline) - if err != nil { - return - } - } - - _, err = out.Write(newline) - if err != nil { - return - } - - e := &encoding{ - out: out, - breaker: newLineBreaker(out, 64), - crc: crc24Init, - blockType: bType, - } - e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) - return e, nil -} diff --git a/src/pkg/crypto/openpgp/canonical_text.go b/src/pkg/crypto/openpgp/canonical_text.go deleted file mode 100644 index 293eff354..000000000 --- a/src/pkg/crypto/openpgp/canonical_text.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package openpgp - -import ( - "hash" - "os" -) - -// NewCanonicalTextHash reformats text written to it into the canonical -// form and then applies the hash h. See RFC 4880, section 5.2.1. -func NewCanonicalTextHash(h hash.Hash) hash.Hash { - return &canonicalTextHash{h, 0} -} - -type canonicalTextHash struct { - h hash.Hash - s int -} - -var newline = []byte{'\r', '\n'} - -func (cth *canonicalTextHash) Write(buf []byte) (int, os.Error) { - start := 0 - - for i, c := range buf { - switch cth.s { - case 0: - if c == '\r' { - cth.s = 1 - } else if c == '\n' { - cth.h.Write(buf[start:i]) - cth.h.Write(newline) - start = i + 1 - } - case 1: - cth.s = 0 - } - } - - cth.h.Write(buf[start:]) - return len(buf), nil -} - -func (cth *canonicalTextHash) Sum() []byte { - return cth.h.Sum() -} - -func (cth *canonicalTextHash) Reset() { - cth.h.Reset() - cth.s = 0 -} - -func (cth *canonicalTextHash) Size() int { - return cth.h.Size() -} diff --git a/src/pkg/crypto/openpgp/canonical_text_test.go b/src/pkg/crypto/openpgp/canonical_text_test.go deleted file mode 100644 index ccf2910cc..000000000 --- a/src/pkg/crypto/openpgp/canonical_text_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package openpgp - -import ( - "bytes" - "os" - "testing" -) - -type recordingHash struct { - buf *bytes.Buffer -} - -func (r recordingHash) Write(b []byte) (n int, err os.Error) { - return r.buf.Write(b) -} - -func (r recordingHash) Sum() []byte { - return r.buf.Bytes() -} - -func (r recordingHash) Reset() { - panic("shouldn't be called") -} - -func (r recordingHash) Size() int { - panic("shouldn't be called") -} - -func testCanonicalText(t *testing.T, input, expected string) { - r := recordingHash{bytes.NewBuffer(nil)} - c := NewCanonicalTextHash(r) - c.Write([]byte(input)) - result := c.Sum() - if expected != string(result) { - t.Errorf("input: %x got: %x want: %x", input, result, expected) - } -} - -func TestCanonicalText(t *testing.T) { - testCanonicalText(t, "foo\n", "foo\r\n") - testCanonicalText(t, "foo", "foo") - testCanonicalText(t, "foo\r\n", "foo\r\n") - testCanonicalText(t, "foo\r\nbar", "foo\r\nbar") - testCanonicalText(t, "foo\r\nbar\n\n", "foo\r\nbar\r\n\r\n") -} diff --git a/src/pkg/crypto/openpgp/elgamal/Makefile b/src/pkg/crypto/openpgp/elgamal/Makefile deleted file mode 100644 index f730255f8..000000000 --- a/src/pkg/crypto/openpgp/elgamal/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../../Make.inc - -TARG=crypto/openpgp/elgamal -GOFILES=\ - elgamal.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/elgamal/elgamal.go b/src/pkg/crypto/openpgp/elgamal/elgamal.go deleted file mode 100644 index 99a6e3e1f..000000000 --- a/src/pkg/crypto/openpgp/elgamal/elgamal.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package elgamal implements ElGamal encryption, suitable for OpenPGP, -// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on -// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31, -// n. 4, 1985, pp. 469-472. -// -// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it -// unsuitable for other protocols. RSA should be used in preference in any -// case. -package elgamal - -import ( - "big" - "crypto/rand" - "crypto/subtle" - "io" - "os" -) - -// PublicKey represents an ElGamal public key. -type PublicKey struct { - G, P, Y *big.Int -} - -// PrivateKey represents an ElGamal private key. -type PrivateKey struct { - PublicKey - X *big.Int -} - -// Encrypt encrypts the given message to the given public key. The result is a -// pair of integers. Errors can result from reading random, or because msg is -// too large to be encrypted to the public key. -func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err os.Error) { - pLen := (pub.P.BitLen() + 7) / 8 - if len(msg) > pLen-11 { - err = os.NewError("elgamal: message too long") - return - } - - // EM = 0x02 || PS || 0x00 || M - em := make([]byte, pLen-1) - em[0] = 2 - ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] - err = nonZeroRandomBytes(ps, random) - if err != nil { - return - } - em[len(em)-len(msg)-1] = 0 - copy(mm, msg) - - m := new(big.Int).SetBytes(em) - - k, err := rand.Int(random, pub.P) - if err != nil { - return - } - - c1 = new(big.Int).Exp(pub.G, k, pub.P) - s := new(big.Int).Exp(pub.Y, k, pub.P) - c2 = s.Mul(s, m) - c2.Mod(c2, pub.P) - - return -} - -// Decrypt takes two integers, resulting from an ElGamal encryption, and -// returns the plaintext of the message. An error can result only if the -// ciphertext is invalid. Users should keep in mind that this is a padding -// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can -// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks -// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel -// Bleichenbacher, Advances in Cryptology (Crypto '98), -func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err os.Error) { - s := new(big.Int).Exp(c1, priv.X, priv.P) - s.ModInverse(s, priv.P) - s.Mul(s, c2) - s.Mod(s, priv.P) - em := s.Bytes() - - firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2) - - // The remainder of the plaintext must be a string of non-zero random - // octets, followed by a 0, followed by the message. - // lookingForIndex: 1 iff we are still looking for the zero. - // index: the offset of the first zero byte. - var lookingForIndex, index int - lookingForIndex = 1 - - for i := 1; i < len(em); i++ { - equals0 := subtle.ConstantTimeByteEq(em[i], 0) - index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index) - lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) - } - - if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 { - return nil, os.NewError("elgamal: decryption error") - } - return em[index+1:], nil -} - -// nonZeroRandomBytes fills the given slice with non-zero random octets. -func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) { - _, err = io.ReadFull(rand, s) - if err != nil { - return - } - - for i := 0; i < len(s); i++ { - for s[i] == 0 { - _, err = io.ReadFull(rand, s[i:i+1]) - if err != nil { - return - } - } - } - - return -} diff --git a/src/pkg/crypto/openpgp/elgamal/elgamal_test.go b/src/pkg/crypto/openpgp/elgamal/elgamal_test.go deleted file mode 100644 index 101121aa6..000000000 --- a/src/pkg/crypto/openpgp/elgamal/elgamal_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package elgamal - -import ( - "big" - "bytes" - "crypto/rand" - "testing" -) - -// This is the 1024-bit MODP group from RFC 5114, section 2.1: -const primeHex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371" - -const generatorHex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5" - -func fromHex(hex string) *big.Int { - n, ok := new(big.Int).SetString(hex, 16) - if !ok { - panic("failed to parse hex number") - } - return n -} - -func TestEncryptDecrypt(t *testing.T) { - priv := &PrivateKey{ - PublicKey: PublicKey{ - G: fromHex(generatorHex), - P: fromHex(primeHex), - }, - X: fromHex("42"), - } - priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P) - - message := []byte("hello world") - c1, c2, err := Encrypt(rand.Reader, &priv.PublicKey, message) - if err != nil { - t.Errorf("error encrypting: %s", err) - } - message2, err := Decrypt(priv, c1, c2) - if err != nil { - t.Errorf("error decrypting: %s", err) - } - if !bytes.Equal(message2, message) { - t.Errorf("decryption failed, got: %x, want: %x", message2, message) - } -} diff --git a/src/pkg/crypto/openpgp/error/Makefile b/src/pkg/crypto/openpgp/error/Makefile deleted file mode 100644 index 8c370a089..000000000 --- a/src/pkg/crypto/openpgp/error/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../../Make.inc - -TARG=crypto/openpgp/error -GOFILES=\ - error.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/error/error.go b/src/pkg/crypto/openpgp/error/error.go deleted file mode 100644 index 3759ce161..000000000 --- a/src/pkg/crypto/openpgp/error/error.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package error contains common error types for the OpenPGP packages. -package error - -import ( - "strconv" -) - -// A StructuralError is returned when OpenPGP data is found to be syntactically -// invalid. -type StructuralError string - -func (s StructuralError) String() string { - return "OpenPGP data invalid: " + string(s) -} - -// UnsupportedError indicates that, although the OpenPGP data is valid, it -// makes use of currently unimplemented features. -type UnsupportedError string - -func (s UnsupportedError) String() string { - return "OpenPGP feature unsupported: " + string(s) -} - -// InvalidArgumentError indicates that the caller is in error and passed an -// incorrect value. -type InvalidArgumentError string - -func (i InvalidArgumentError) String() string { - return "OpenPGP argument invalid: " + string(i) -} - -// SignatureError indicates that a syntactically valid signature failed to -// validate. -type SignatureError string - -func (b SignatureError) String() string { - return "OpenPGP signature invalid: " + string(b) -} - -type keyIncorrect int - -func (ki keyIncorrect) String() string { - return "the given key was incorrect" -} - -var KeyIncorrectError = keyIncorrect(0) - -type unknownIssuer int - -func (unknownIssuer) String() string { - return "signature make by unknown entity" -} - -var UnknownIssuerError = unknownIssuer(0) - -type UnknownPacketTypeError uint8 - -func (upte UnknownPacketTypeError) String() string { - return "unknown OpenPGP packet type: " + strconv.Itoa(int(upte)) -} diff --git a/src/pkg/crypto/openpgp/keys.go b/src/pkg/crypto/openpgp/keys.go deleted file mode 100644 index c70fb7927..000000000 --- a/src/pkg/crypto/openpgp/keys.go +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package openpgp - -import ( - "crypto" - "crypto/openpgp/armor" - "crypto/openpgp/error" - "crypto/openpgp/packet" - "crypto/rsa" - "io" - "os" - "time" -) - -// PublicKeyType is the armor type for a PGP public key. -var PublicKeyType = "PGP PUBLIC KEY BLOCK" -// PrivateKeyType is the armor type for a PGP private key. -var PrivateKeyType = "PGP PRIVATE KEY BLOCK" - -// An Entity represents the components of an OpenPGP key: a primary public key -// (which must be a signing key), one or more identities claimed by that key, -// and zero or more subkeys, which may be encryption keys. -type Entity struct { - PrimaryKey *packet.PublicKey - PrivateKey *packet.PrivateKey - Identities map[string]*Identity // indexed by Identity.Name - Subkeys []Subkey -} - -// An Identity represents an identity claimed by an Entity and zero or more -// assertions by other entities about that claim. -type Identity struct { - Name string // by convention, has the form "Full Name (comment) " - UserId *packet.UserId - SelfSignature *packet.Signature - Signatures []*packet.Signature -} - -// A Subkey is an additional public key in an Entity. Subkeys can be used for -// encryption. -type Subkey struct { - PublicKey *packet.PublicKey - PrivateKey *packet.PrivateKey - Sig *packet.Signature -} - -// A Key identifies a specific public key in an Entity. This is either the -// Entity's primary key or a subkey. -type Key struct { - Entity *Entity - PublicKey *packet.PublicKey - PrivateKey *packet.PrivateKey - SelfSignature *packet.Signature -} - -// A KeyRing provides access to public and private keys. -type KeyRing interface { - // KeysById returns the set of keys that have the given key id. - KeysById(id uint64) []Key - // DecryptionKeys returns all private keys that are valid for - // decryption. - DecryptionKeys() []Key -} - -// primaryIdentity returns the Identity marked as primary or the first identity -// if none are so marked. -func (e *Entity) primaryIdentity() *Identity { - var firstIdentity *Identity - for _, ident := range e.Identities { - if firstIdentity == nil { - firstIdentity = ident - } - if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { - return ident - } - } - return firstIdentity -} - -// encryptionKey returns the best candidate Key for encrypting a message to the -// given Entity. -func (e *Entity) encryptionKey() Key { - candidateSubkey := -1 - - for i, subkey := range e.Subkeys { - if subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications && subkey.PublicKey.PubKeyAlgo.CanEncrypt() { - candidateSubkey = i - break - } - } - - i := e.primaryIdentity() - - if e.PrimaryKey.PubKeyAlgo.CanEncrypt() { - // If we don't have any candidate subkeys for encryption and - // the primary key doesn't have any usage metadata then we - // assume that the primary key is ok. Or, if the primary key is - // marked as ok to encrypt to, then we can obviously use it. - if candidateSubkey == -1 && !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && i.SelfSignature.FlagsValid { - return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} - } - } - - if candidateSubkey != -1 { - subkey := e.Subkeys[candidateSubkey] - return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} - } - - // This Entity appears to be signing only. - return Key{} -} - -// signingKey return the best candidate Key for signing a message with this -// Entity. -func (e *Entity) signingKey() Key { - candidateSubkey := -1 - - for i, subkey := range e.Subkeys { - if subkey.Sig.FlagsValid && subkey.Sig.FlagSign && subkey.PublicKey.PubKeyAlgo.CanSign() { - candidateSubkey = i - break - } - } - - i := e.primaryIdentity() - - // If we have no candidate subkey then we assume that it's ok to sign - // with the primary key. - if candidateSubkey == -1 || i.SelfSignature.FlagsValid && i.SelfSignature.FlagSign { - return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} - } - - subkey := e.Subkeys[candidateSubkey] - return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} -} - -// An EntityList contains one or more Entities. -type EntityList []*Entity - -// KeysById returns the set of keys that have the given key id. -func (el EntityList) KeysById(id uint64) (keys []Key) { - for _, e := range el { - if e.PrimaryKey.KeyId == id { - var selfSig *packet.Signature - for _, ident := range e.Identities { - if selfSig == nil { - selfSig = ident.SelfSignature - } else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { - selfSig = ident.SelfSignature - break - } - } - keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig}) - } - - for _, subKey := range e.Subkeys { - if subKey.PublicKey.KeyId == id { - keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) - } - } - } - return -} - -// DecryptionKeys returns all private keys that are valid for decryption. -func (el EntityList) DecryptionKeys() (keys []Key) { - for _, e := range el { - for _, subKey := range e.Subkeys { - if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) { - keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) - } - } - } - return -} - -// ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file. -func ReadArmoredKeyRing(r io.Reader) (EntityList, os.Error) { - block, err := armor.Decode(r) - if err == os.EOF { - return nil, error.InvalidArgumentError("no armored data found") - } - if err != nil { - return nil, err - } - if block.Type != PublicKeyType && block.Type != PrivateKeyType { - return nil, error.InvalidArgumentError("expected public or private key block, got: " + block.Type) - } - - return ReadKeyRing(block.Body) -} - -// ReadKeyRing reads one or more public/private keys. Unsupported keys are -// ignored as long as at least a single valid key is found. -func ReadKeyRing(r io.Reader) (el EntityList, err os.Error) { - packets := packet.NewReader(r) - var lastUnsupportedError os.Error - - for { - var e *Entity - e, err = readEntity(packets) - if err != nil { - if _, ok := err.(error.UnsupportedError); ok { - lastUnsupportedError = err - err = readToNextPublicKey(packets) - } - if err == os.EOF { - err = nil - break - } - if err != nil { - el = nil - break - } - } else { - el = append(el, e) - } - } - - if len(el) == 0 && err == nil { - err = lastUnsupportedError - } - return -} - -// readToNextPublicKey reads packets until the start of the entity and leaves -// the first packet of the new entity in the Reader. -func readToNextPublicKey(packets *packet.Reader) (err os.Error) { - var p packet.Packet - for { - p, err = packets.Next() - if err == os.EOF { - return - } else if err != nil { - if _, ok := err.(error.UnsupportedError); ok { - err = nil - continue - } - return - } - - if pk, ok := p.(*packet.PublicKey); ok && !pk.IsSubkey { - packets.Unread(p) - return - } - } - - panic("unreachable") -} - -// readEntity reads an entity (public key, identities, subkeys etc) from the -// given Reader. -func readEntity(packets *packet.Reader) (*Entity, os.Error) { - e := new(Entity) - e.Identities = make(map[string]*Identity) - - p, err := packets.Next() - if err != nil { - return nil, err - } - - var ok bool - if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok { - if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok { - packets.Unread(p) - return nil, error.StructuralError("first packet was not a public/private key") - } else { - e.PrimaryKey = &e.PrivateKey.PublicKey - } - } - - if !e.PrimaryKey.PubKeyAlgo.CanSign() { - return nil, error.StructuralError("primary key cannot be used for signatures") - } - - var current *Identity -EachPacket: - for { - p, err := packets.Next() - if err == os.EOF { - break - } else if err != nil { - return nil, err - } - - switch pkt := p.(type) { - case *packet.UserId: - current = new(Identity) - current.Name = pkt.Id - current.UserId = pkt - e.Identities[pkt.Id] = current - - for { - p, err = packets.Next() - if err == os.EOF { - return nil, io.ErrUnexpectedEOF - } else if err != nil { - return nil, err - } - - sig, ok := p.(*packet.Signature) - if !ok { - return nil, error.StructuralError("user ID packet not followed by self-signature") - } - - if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { - if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, sig); err != nil { - return nil, error.StructuralError("user ID self-signature invalid: " + err.String()) - } - current.SelfSignature = sig - break - } - current.Signatures = append(current.Signatures, sig) - } - case *packet.Signature: - if current == nil { - return nil, error.StructuralError("signature packet found before user id packet") - } - current.Signatures = append(current.Signatures, pkt) - case *packet.PrivateKey: - if pkt.IsSubkey == false { - packets.Unread(p) - break EachPacket - } - err = addSubkey(e, packets, &pkt.PublicKey, pkt) - if err != nil { - return nil, err - } - case *packet.PublicKey: - if pkt.IsSubkey == false { - packets.Unread(p) - break EachPacket - } - err = addSubkey(e, packets, pkt, nil) - if err != nil { - return nil, err - } - default: - // we ignore unknown packets - } - } - - if len(e.Identities) == 0 { - return nil, error.StructuralError("entity without any identities") - } - - return e, nil -} - -func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) os.Error { - var subKey Subkey - subKey.PublicKey = pub - subKey.PrivateKey = priv - p, err := packets.Next() - if err == os.EOF { - return io.ErrUnexpectedEOF - } - if err != nil { - return error.StructuralError("subkey signature invalid: " + err.String()) - } - var ok bool - subKey.Sig, ok = p.(*packet.Signature) - if !ok { - return error.StructuralError("subkey packet not followed by signature") - } - if subKey.Sig.SigType != packet.SigTypeSubkeyBinding { - return error.StructuralError("subkey signature with wrong type") - } - err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig) - if err != nil { - return error.StructuralError("subkey signature invalid: " + err.String()) - } - e.Subkeys = append(e.Subkeys, subKey) - return nil -} - -const defaultRSAKeyBits = 2048 - -// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a -// single identity composed of the given full name, comment and email, any of -// which may be empty but must not contain any of "()<>\x00". -func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email string) (*Entity, os.Error) { - uid := packet.NewUserId(name, comment, email) - if uid == nil { - return nil, error.InvalidArgumentError("user id field contained invalid characters") - } - signingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) - if err != nil { - return nil, err - } - encryptingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits) - if err != nil { - return nil, err - } - - t := uint32(currentTimeSecs) - - e := &Entity{ - PrimaryKey: packet.NewRSAPublicKey(t, &signingPriv.PublicKey, false /* not a subkey */ ), - PrivateKey: packet.NewRSAPrivateKey(t, signingPriv, false /* not a subkey */ ), - Identities: make(map[string]*Identity), - } - isPrimaryId := true - e.Identities[uid.Id] = &Identity{ - Name: uid.Name, - UserId: uid, - SelfSignature: &packet.Signature{ - CreationTime: t, - SigType: packet.SigTypePositiveCert, - PubKeyAlgo: packet.PubKeyAlgoRSA, - Hash: crypto.SHA256, - IsPrimaryId: &isPrimaryId, - FlagsValid: true, - FlagSign: true, - FlagCertify: true, - IssuerKeyId: &e.PrimaryKey.KeyId, - }, - } - - e.Subkeys = make([]Subkey, 1) - e.Subkeys[0] = Subkey{ - PublicKey: packet.NewRSAPublicKey(t, &encryptingPriv.PublicKey, true /* is a subkey */ ), - PrivateKey: packet.NewRSAPrivateKey(t, encryptingPriv, true /* is a subkey */ ), - Sig: &packet.Signature{ - CreationTime: t, - SigType: packet.SigTypeSubkeyBinding, - PubKeyAlgo: packet.PubKeyAlgoRSA, - Hash: crypto.SHA256, - FlagsValid: true, - FlagEncryptStorage: true, - FlagEncryptCommunications: true, - IssuerKeyId: &e.PrimaryKey.KeyId, - }, - } - - return e, nil -} - -// SerializePrivate serializes an Entity, including private key material, to -// the given Writer. For now, it must only be used on an Entity returned from -// NewEntity. -func (e *Entity) SerializePrivate(w io.Writer) (err os.Error) { - err = e.PrivateKey.Serialize(w) - if err != nil { - return - } - for _, ident := range e.Identities { - err = ident.UserId.Serialize(w) - if err != nil { - return - } - err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey) - if err != nil { - return - } - err = ident.SelfSignature.Serialize(w) - if err != nil { - return - } - } - for _, subkey := range e.Subkeys { - err = subkey.PrivateKey.Serialize(w) - if err != nil { - return - } - err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey) - if err != nil { - return - } - err = subkey.Sig.Serialize(w) - if err != nil { - return - } - } - return nil -} - -// Serialize writes the public part of the given Entity to w. (No private -// key material will be output). -func (e *Entity) Serialize(w io.Writer) os.Error { - err := e.PrimaryKey.Serialize(w) - if err != nil { - return err - } - for _, ident := range e.Identities { - err = ident.UserId.Serialize(w) - if err != nil { - return err - } - err = ident.SelfSignature.Serialize(w) - if err != nil { - return err - } - for _, sig := range ident.Signatures { - err = sig.Serialize(w) - if err != nil { - return err - } - } - } - for _, subkey := range e.Subkeys { - err = subkey.PublicKey.Serialize(w) - if err != nil { - return err - } - err = subkey.Sig.Serialize(w) - if err != nil { - return err - } - } - return nil -} - -// SignIdentity adds a signature to e, from signer, attesting that identity is -// associated with e. The provided identity must already be an element of -// e.Identities and the private key of signer must have been decrypted if -// necessary. -func (e *Entity) SignIdentity(identity string, signer *Entity) os.Error { - if signer.PrivateKey == nil { - return error.InvalidArgumentError("signing Entity must have a private key") - } - if signer.PrivateKey.Encrypted { - return error.InvalidArgumentError("signing Entity's private key must be decrypted") - } - ident, ok := e.Identities[identity] - if !ok { - return error.InvalidArgumentError("given identity string not found in Entity") - } - - sig := &packet.Signature{ - SigType: packet.SigTypeGenericCert, - PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, - Hash: crypto.SHA256, - CreationTime: uint32(time.Seconds()), - IssuerKeyId: &signer.PrivateKey.KeyId, - } - if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey); err != nil { - return err - } - ident.Signatures = append(ident.Signatures, sig) - return nil -} diff --git a/src/pkg/crypto/openpgp/packet/Makefile b/src/pkg/crypto/openpgp/packet/Makefile deleted file mode 100644 index 0f0d94eb1..000000000 --- a/src/pkg/crypto/openpgp/packet/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../../Make.inc - -TARG=crypto/openpgp/packet -GOFILES=\ - compressed.go\ - encrypted_key.go\ - literal.go\ - one_pass_signature.go\ - packet.go\ - private_key.go\ - public_key.go\ - reader.go\ - signature.go\ - symmetrically_encrypted.go\ - symmetric_key_encrypted.go\ - userid.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/packet/compressed.go b/src/pkg/crypto/openpgp/packet/compressed.go deleted file mode 100644 index 1c15c24c4..000000000 --- a/src/pkg/crypto/openpgp/packet/compressed.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "compress/flate" - "compress/zlib" - "crypto/openpgp/error" - "io" - "os" - "strconv" -) - -// Compressed represents a compressed OpenPGP packet. The decompressed contents -// will contain more OpenPGP packets. See RFC 4880, section 5.6. -type Compressed struct { - Body io.Reader -} - -func (c *Compressed) parse(r io.Reader) os.Error { - var buf [1]byte - _, err := readFull(r, buf[:]) - if err != nil { - return err - } - - switch buf[0] { - case 1: - c.Body = flate.NewReader(r) - case 2: - c.Body, err = zlib.NewReader(r) - default: - err = error.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) - } - - return err -} diff --git a/src/pkg/crypto/openpgp/packet/compressed_test.go b/src/pkg/crypto/openpgp/packet/compressed_test.go deleted file mode 100644 index 24fe501ed..000000000 --- a/src/pkg/crypto/openpgp/packet/compressed_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "bytes" - "encoding/hex" - "os" - "io/ioutil" - "testing" -) - -func TestCompressed(t *testing.T) { - packet, err := Read(readerFromHex(compressedHex)) - if err != nil { - t.Errorf("failed to read Compressed: %s", err) - return - } - - c, ok := packet.(*Compressed) - if !ok { - t.Error("didn't find Compressed packet") - return - } - - contents, err := ioutil.ReadAll(c.Body) - if err != nil && err != os.EOF { - t.Error(err) - return - } - - expected, _ := hex.DecodeString(compressedExpectedHex) - if !bytes.Equal(expected, contents) { - t.Errorf("got:%x want:%x", contents, expected) - } -} - -const compressedHex = "a3013b2d90c4e02b72e25f727e5e496a5e49b11e1700" -const compressedExpectedHex = "cb1062004d14c8fe636f6e74656e74732e0a" diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key.go b/src/pkg/crypto/openpgp/packet/encrypted_key.go deleted file mode 100644 index b4730cbc9..000000000 --- a/src/pkg/crypto/openpgp/packet/encrypted_key.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "big" - "crypto/openpgp/elgamal" - "crypto/openpgp/error" - "crypto/rand" - "crypto/rsa" - "encoding/binary" - "io" - "os" - "strconv" -) - -const encryptedKeyVersion = 3 - -// EncryptedKey represents a public-key encrypted session key. See RFC 4880, -// section 5.1. -type EncryptedKey struct { - KeyId uint64 - Algo PublicKeyAlgorithm - CipherFunc CipherFunction // only valid after a successful Decrypt - Key []byte // only valid after a successful Decrypt - - encryptedMPI1, encryptedMPI2 []byte -} - -func (e *EncryptedKey) parse(r io.Reader) (err os.Error) { - var buf [10]byte - _, err = readFull(r, buf[:]) - if err != nil { - return - } - if buf[0] != encryptedKeyVersion { - return error.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) - } - e.KeyId = binary.BigEndian.Uint64(buf[1:9]) - e.Algo = PublicKeyAlgorithm(buf[9]) - switch e.Algo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - e.encryptedMPI1, _, err = readMPI(r) - case PubKeyAlgoElGamal: - e.encryptedMPI1, _, err = readMPI(r) - if err != nil { - return - } - e.encryptedMPI2, _, err = readMPI(r) - } - _, err = consumeAll(r) - return -} - -func checksumKeyMaterial(key []byte) uint16 { - var checksum uint16 - for _, v := range key { - checksum += uint16(v) - } - return checksum -} - -// Decrypt decrypts an encrypted session key with the given private key. The -// private key must have been decrypted first. -func (e *EncryptedKey) Decrypt(priv *PrivateKey) os.Error { - var err os.Error - var b []byte - - // TODO(agl): use session key decryption routines here to avoid - // padding oracle attacks. - switch priv.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - b, err = rsa.DecryptPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1) - case PubKeyAlgoElGamal: - c1 := new(big.Int).SetBytes(e.encryptedMPI1) - c2 := new(big.Int).SetBytes(e.encryptedMPI2) - b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2) - default: - err = error.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) - } - - if err != nil { - return err - } - - e.CipherFunc = CipherFunction(b[0]) - e.Key = b[1 : len(b)-2] - expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) - checksum := checksumKeyMaterial(e.Key) - if checksum != expectedChecksum { - return error.StructuralError("EncryptedKey checksum incorrect") - } - - return nil -} - -// SerializeEncryptedKey serializes an encrypted key packet to w that contains -// key, encrypted to pub. -func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFunc CipherFunction, key []byte) os.Error { - var buf [10]byte - buf[0] = encryptedKeyVersion - binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) - buf[9] = byte(pub.PubKeyAlgo) - - keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */ ) - keyBlock[0] = byte(cipherFunc) - copy(keyBlock[1:], key) - checksum := checksumKeyMaterial(key) - keyBlock[1+len(key)] = byte(checksum >> 8) - keyBlock[1+len(key)+1] = byte(checksum) - - switch pub.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: - return serializeEncryptedKeyRSA(w, rand, buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) - case PubKeyAlgoElGamal: - return serializeEncryptedKeyElGamal(w, rand, buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) - case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: - return error.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) - } - - return error.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) -} - -func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) os.Error { - cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock) - if err != nil { - return error.InvalidArgumentError("RSA encryption failed: " + err.String()) - } - - packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText) - - err = serializeHeader(w, packetTypeEncryptedKey, packetLen) - if err != nil { - return err - } - _, err = w.Write(header[:]) - if err != nil { - return err - } - return writeMPI(w, 8*uint16(len(cipherText)), cipherText) -} - -func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) os.Error { - c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock) - if err != nil { - return error.InvalidArgumentError("ElGamal encryption failed: " + err.String()) - } - - packetLen := 10 /* header length */ - packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8 - packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8 - - err = serializeHeader(w, packetTypeEncryptedKey, packetLen) - if err != nil { - return err - } - _, err = w.Write(header[:]) - if err != nil { - return err - } - err = writeBig(w, c1) - if err != nil { - return err - } - return writeBig(w, c2) -} diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key_test.go b/src/pkg/crypto/openpgp/packet/encrypted_key_test.go deleted file mode 100644 index b402245bd..000000000 --- a/src/pkg/crypto/openpgp/packet/encrypted_key_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "big" - "bytes" - "crypto/rand" - "crypto/rsa" - "fmt" - "testing" -) - -func bigFromBase10(s string) *big.Int { - b, ok := new(big.Int).SetString(s, 10) - if !ok { - panic("bigFromBase10 failed") - } - return b -} - -var encryptedKeyPub = rsa.PublicKey{ - E: 65537, - N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"), -} - -var encryptedKeyRSAPriv = &rsa.PrivateKey{ - PublicKey: encryptedKeyPub, - D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"), -} - -var encryptedKeyPriv = &PrivateKey{ - PublicKey: PublicKey{ - PubKeyAlgo: PubKeyAlgoRSA, - }, - PrivateKey: encryptedKeyRSAPriv, -} - -func TestDecryptingEncryptedKey(t *testing.T) { - const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" - const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" - - p, err := Read(readerFromHex(encryptedKeyHex)) - if err != nil { - t.Errorf("error from Read: %s", err) - return - } - ek, ok := p.(*EncryptedKey) - if !ok { - t.Errorf("didn't parse an EncryptedKey, got %#v", p) - return - } - - if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA { - t.Errorf("unexpected EncryptedKey contents: %#v", ek) - return - } - - err = ek.Decrypt(encryptedKeyPriv) - if err != nil { - t.Errorf("error from Decrypt: %s", err) - return - } - - if ek.CipherFunc != CipherAES256 { - t.Errorf("unexpected EncryptedKey contents: %#v", ek) - return - } - - keyHex := fmt.Sprintf("%x", ek.Key) - if keyHex != expectedKeyHex { - t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) - } -} - -func TestEncryptingEncryptedKey(t *testing.T) { - key := []byte{1, 2, 3, 4} - const expectedKeyHex = "01020304" - const keyId = 42 - - pub := &PublicKey{ - PublicKey: &encryptedKeyPub, - KeyId: keyId, - PubKeyAlgo: PubKeyAlgoRSAEncryptOnly, - } - - buf := new(bytes.Buffer) - err := SerializeEncryptedKey(buf, rand.Reader, pub, CipherAES128, key) - if err != nil { - t.Errorf("error writing encrypted key packet: %s", err) - } - - p, err := Read(buf) - if err != nil { - t.Errorf("error from Read: %s", err) - return - } - ek, ok := p.(*EncryptedKey) - if !ok { - t.Errorf("didn't parse an EncryptedKey, got %#v", p) - return - } - - if ek.KeyId != keyId || ek.Algo != PubKeyAlgoRSAEncryptOnly { - t.Errorf("unexpected EncryptedKey contents: %#v", ek) - return - } - - err = ek.Decrypt(encryptedKeyPriv) - if err != nil { - t.Errorf("error from Decrypt: %s", err) - return - } - - if ek.CipherFunc != CipherAES128 { - t.Errorf("unexpected EncryptedKey contents: %#v", ek) - return - } - - keyHex := fmt.Sprintf("%x", ek.Key) - if keyHex != expectedKeyHex { - t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) - } -} diff --git a/src/pkg/crypto/openpgp/packet/literal.go b/src/pkg/crypto/openpgp/packet/literal.go deleted file mode 100644 index 9411572d7..000000000 --- a/src/pkg/crypto/openpgp/packet/literal.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "encoding/binary" - "io" - "os" -) - -// LiteralData represents an encrypted file. See RFC 4880, section 5.9. -type LiteralData struct { - IsBinary bool - FileName string - Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined. - Body io.Reader -} - -// ForEyesOnly returns whether the contents of the LiteralData have been marked -// as especially sensitive. -func (l *LiteralData) ForEyesOnly() bool { - return l.FileName == "_CONSOLE" -} - -func (l *LiteralData) parse(r io.Reader) (err os.Error) { - var buf [256]byte - - _, err = readFull(r, buf[:2]) - if err != nil { - return - } - - l.IsBinary = buf[0] == 'b' - fileNameLen := int(buf[1]) - - _, err = readFull(r, buf[:fileNameLen]) - if err != nil { - return - } - - l.FileName = string(buf[:fileNameLen]) - - _, err = readFull(r, buf[:4]) - if err != nil { - return - } - - l.Time = binary.BigEndian.Uint32(buf[:4]) - l.Body = r - return -} - -// SerializeLiteral serializes a literal data packet to w and returns a -// WriteCloser to which the data itself can be written and which MUST be closed -// on completion. The fileName is truncated to 255 bytes. -func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err os.Error) { - var buf [4]byte - buf[0] = 't' - if isBinary { - buf[0] = 'b' - } - if len(fileName) > 255 { - fileName = fileName[:255] - } - buf[1] = byte(len(fileName)) - - inner, err := serializeStreamHeader(w, packetTypeLiteralData) - if err != nil { - return - } - - _, err = inner.Write(buf[:2]) - if err != nil { - return - } - _, err = inner.Write([]byte(fileName)) - if err != nil { - return - } - binary.BigEndian.PutUint32(buf[:], time) - _, err = inner.Write(buf[:]) - if err != nil { - return - } - - plaintext = inner - return -} diff --git a/src/pkg/crypto/openpgp/packet/one_pass_signature.go b/src/pkg/crypto/openpgp/packet/one_pass_signature.go deleted file mode 100644 index ca826e4f4..000000000 --- a/src/pkg/crypto/openpgp/packet/one_pass_signature.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "crypto" - "crypto/openpgp/error" - "crypto/openpgp/s2k" - "encoding/binary" - "io" - "os" - "strconv" -) - -// OnePassSignature represents a one-pass signature packet. See RFC 4880, -// section 5.4. -type OnePassSignature struct { - SigType SignatureType - Hash crypto.Hash - PubKeyAlgo PublicKeyAlgorithm - KeyId uint64 - IsLast bool -} - -const onePassSignatureVersion = 3 - -func (ops *OnePassSignature) parse(r io.Reader) (err os.Error) { - var buf [13]byte - - _, err = readFull(r, buf[:]) - if err != nil { - return - } - if buf[0] != onePassSignatureVersion { - err = error.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) - } - - var ok bool - ops.Hash, ok = s2k.HashIdToHash(buf[2]) - if !ok { - return error.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) - } - - ops.SigType = SignatureType(buf[1]) - ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) - ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) - ops.IsLast = buf[12] != 0 - return -} - -// Serialize marshals the given OnePassSignature to w. -func (ops *OnePassSignature) Serialize(w io.Writer) os.Error { - var buf [13]byte - buf[0] = onePassSignatureVersion - buf[1] = uint8(ops.SigType) - var ok bool - buf[2], ok = s2k.HashToHashId(ops.Hash) - if !ok { - return error.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) - } - buf[3] = uint8(ops.PubKeyAlgo) - binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) - if ops.IsLast { - buf[12] = 1 - } - - if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { - return err - } - _, err := w.Write(buf[:]) - return err -} diff --git a/src/pkg/crypto/openpgp/packet/packet.go b/src/pkg/crypto/openpgp/packet/packet.go deleted file mode 100644 index 1d7297e38..000000000 --- a/src/pkg/crypto/openpgp/packet/packet.go +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package packet implements parsing and serialization of OpenPGP packets, as -// specified in RFC 4880. -package packet - -import ( - "big" - "crypto/aes" - "crypto/cast5" - "crypto/cipher" - "crypto/openpgp/error" - "io" - "os" -) - -// readFull is the same as io.ReadFull except that reading zero bytes returns -// ErrUnexpectedEOF rather than EOF. -func readFull(r io.Reader, buf []byte) (n int, err os.Error) { - n, err = io.ReadFull(r, buf) - if err == os.EOF { - err = io.ErrUnexpectedEOF - } - return -} - -// readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2. -func readLength(r io.Reader) (length int64, isPartial bool, err os.Error) { - var buf [4]byte - _, err = readFull(r, buf[:1]) - if err != nil { - return - } - switch { - case buf[0] < 192: - length = int64(buf[0]) - case buf[0] < 224: - length = int64(buf[0]-192) << 8 - _, err = readFull(r, buf[0:1]) - if err != nil { - return - } - length += int64(buf[0]) + 192 - case buf[0] < 255: - length = int64(1) << (buf[0] & 0x1f) - isPartial = true - default: - _, err = readFull(r, buf[0:4]) - if err != nil { - return - } - length = int64(buf[0])<<24 | - int64(buf[1])<<16 | - int64(buf[2])<<8 | - int64(buf[3]) - } - return -} - -// partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths. -// The continuation lengths are parsed and removed from the stream and EOF is -// returned at the end of the packet. See RFC 4880, section 4.2.2.4. -type partialLengthReader struct { - r io.Reader - remaining int64 - isPartial bool -} - -func (r *partialLengthReader) Read(p []byte) (n int, err os.Error) { - for r.remaining == 0 { - if !r.isPartial { - return 0, os.EOF - } - r.remaining, r.isPartial, err = readLength(r.r) - if err != nil { - return 0, err - } - } - - toRead := int64(len(p)) - if toRead > r.remaining { - toRead = r.remaining - } - - n, err = r.r.Read(p[:int(toRead)]) - r.remaining -= int64(n) - if n < int(toRead) && err == os.EOF { - err = io.ErrUnexpectedEOF - } - return -} - -// partialLengthWriter writes a stream of data using OpenPGP partial lengths. -// See RFC 4880, section 4.2.2.4. -type partialLengthWriter struct { - w io.WriteCloser - lengthByte [1]byte -} - -func (w *partialLengthWriter) Write(p []byte) (n int, err os.Error) { - for len(p) > 0 { - for power := uint(14); power < 32; power-- { - l := 1 << power - if len(p) >= l { - w.lengthByte[0] = 224 + uint8(power) - _, err = w.w.Write(w.lengthByte[:]) - if err != nil { - return - } - var m int - m, err = w.w.Write(p[:l]) - n += m - if err != nil { - return - } - p = p[l:] - break - } - } - } - return -} - -func (w *partialLengthWriter) Close() os.Error { - w.lengthByte[0] = 0 - _, err := w.w.Write(w.lengthByte[:]) - if err != nil { - return err - } - return w.w.Close() -} - -// A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the -// underlying Reader returns EOF before the limit has been reached. -type spanReader struct { - r io.Reader - n int64 -} - -func (l *spanReader) Read(p []byte) (n int, err os.Error) { - if l.n <= 0 { - return 0, os.EOF - } - if int64(len(p)) > l.n { - p = p[0:l.n] - } - n, err = l.r.Read(p) - l.n -= int64(n) - if l.n > 0 && err == os.EOF { - err = io.ErrUnexpectedEOF - } - return -} - -// readHeader parses a packet header and returns an io.Reader which will return -// the contents of the packet. See RFC 4880, section 4.2. -func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err os.Error) { - var buf [4]byte - _, err = io.ReadFull(r, buf[:1]) - if err != nil { - return - } - if buf[0]&0x80 == 0 { - err = error.StructuralError("tag byte does not have MSB set") - return - } - if buf[0]&0x40 == 0 { - // Old format packet - tag = packetType((buf[0] & 0x3f) >> 2) - lengthType := buf[0] & 3 - if lengthType == 3 { - length = -1 - contents = r - return - } - lengthBytes := 1 << lengthType - _, err = readFull(r, buf[0:lengthBytes]) - if err != nil { - return - } - for i := 0; i < lengthBytes; i++ { - length <<= 8 - length |= int64(buf[i]) - } - contents = &spanReader{r, length} - return - } - - // New format packet - tag = packetType(buf[0] & 0x3f) - length, isPartial, err := readLength(r) - if err != nil { - return - } - if isPartial { - contents = &partialLengthReader{ - remaining: length, - isPartial: true, - r: r, - } - length = -1 - } else { - contents = &spanReader{r, length} - } - return -} - -// serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section -// 4.2. -func serializeHeader(w io.Writer, ptype packetType, length int) (err os.Error) { - var buf [6]byte - var n int - - buf[0] = 0x80 | 0x40 | byte(ptype) - if length < 192 { - buf[1] = byte(length) - n = 2 - } else if length < 8384 { - length -= 192 - buf[1] = 192 + byte(length>>8) - buf[2] = byte(length) - n = 3 - } else { - buf[1] = 255 - buf[2] = byte(length >> 24) - buf[3] = byte(length >> 16) - buf[4] = byte(length >> 8) - buf[5] = byte(length) - n = 6 - } - - _, err = w.Write(buf[:n]) - return -} - -// serializeStreamHeader writes an OpenPGP packet header to w where the -// length of the packet is unknown. It returns a io.WriteCloser which can be -// used to write the contents of the packet. See RFC 4880, section 4.2. -func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err os.Error) { - var buf [1]byte - buf[0] = 0x80 | 0x40 | byte(ptype) - _, err = w.Write(buf[:]) - if err != nil { - return - } - out = &partialLengthWriter{w: w} - return -} - -// Packet represents an OpenPGP packet. Users are expected to try casting -// instances of this interface to specific packet types. -type Packet interface { - parse(io.Reader) os.Error -} - -// consumeAll reads from the given Reader until error, returning the number of -// bytes read. -func consumeAll(r io.Reader) (n int64, err os.Error) { - var m int - var buf [1024]byte - - for { - m, err = r.Read(buf[:]) - n += int64(m) - if err == os.EOF { - err = nil - return - } - if err != nil { - return - } - } - - panic("unreachable") -} - -// packetType represents the numeric ids of the different OpenPGP packet types. See -// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2 -type packetType uint8 - -const ( - packetTypeEncryptedKey packetType = 1 - packetTypeSignature packetType = 2 - packetTypeSymmetricKeyEncrypted packetType = 3 - packetTypeOnePassSignature packetType = 4 - packetTypePrivateKey packetType = 5 - packetTypePublicKey packetType = 6 - packetTypePrivateSubkey packetType = 7 - packetTypeCompressed packetType = 8 - packetTypeSymmetricallyEncrypted packetType = 9 - packetTypeLiteralData packetType = 11 - packetTypeUserId packetType = 13 - packetTypePublicSubkey packetType = 14 - packetTypeSymmetricallyEncryptedMDC packetType = 18 -) - -// Read reads a single OpenPGP packet from the given io.Reader. If there is an -// error parsing a packet, the whole packet is consumed from the input. -func Read(r io.Reader) (p Packet, err os.Error) { - tag, _, contents, err := readHeader(r) - if err != nil { - return - } - - switch tag { - case packetTypeEncryptedKey: - p = new(EncryptedKey) - case packetTypeSignature: - p = new(Signature) - case packetTypeSymmetricKeyEncrypted: - p = new(SymmetricKeyEncrypted) - case packetTypeOnePassSignature: - p = new(OnePassSignature) - case packetTypePrivateKey, packetTypePrivateSubkey: - pk := new(PrivateKey) - if tag == packetTypePrivateSubkey { - pk.IsSubkey = true - } - p = pk - case packetTypePublicKey, packetTypePublicSubkey: - pk := new(PublicKey) - if tag == packetTypePublicSubkey { - pk.IsSubkey = true - } - p = pk - case packetTypeCompressed: - p = new(Compressed) - case packetTypeSymmetricallyEncrypted: - p = new(SymmetricallyEncrypted) - case packetTypeLiteralData: - p = new(LiteralData) - case packetTypeUserId: - p = new(UserId) - case packetTypeSymmetricallyEncryptedMDC: - se := new(SymmetricallyEncrypted) - se.MDC = true - p = se - default: - err = error.UnknownPacketTypeError(tag) - } - if p != nil { - err = p.parse(contents) - } - if err != nil { - consumeAll(contents) - } - return -} - -// SignatureType represents the different semantic meanings of an OpenPGP -// signature. See RFC 4880, section 5.2.1. -type SignatureType uint8 - -const ( - SigTypeBinary SignatureType = 0 - SigTypeText = 1 - SigTypeGenericCert = 0x10 - SigTypePersonaCert = 0x11 - SigTypeCasualCert = 0x12 - SigTypePositiveCert = 0x13 - SigTypeSubkeyBinding = 0x18 -) - -// PublicKeyAlgorithm represents the different public key system specified for -// OpenPGP. See -// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12 -type PublicKeyAlgorithm uint8 - -const ( - PubKeyAlgoRSA PublicKeyAlgorithm = 1 - PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 - PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 - PubKeyAlgoElGamal PublicKeyAlgorithm = 16 - PubKeyAlgoDSA PublicKeyAlgorithm = 17 -) - -// CanEncrypt returns true if it's possible to encrypt a message to a public -// key of the given type. -func (pka PublicKeyAlgorithm) CanEncrypt() bool { - switch pka { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal: - return true - } - return false -} - -// CanSign returns true if it's possible for a public key of the given type to -// sign a message. -func (pka PublicKeyAlgorithm) CanSign() bool { - switch pka { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: - return true - } - return false -} - -// CipherFunction represents the different block ciphers specified for OpenPGP. See -// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 -type CipherFunction uint8 - -const ( - CipherCAST5 CipherFunction = 3 - CipherAES128 CipherFunction = 7 - CipherAES192 CipherFunction = 8 - CipherAES256 CipherFunction = 9 -) - -// KeySize returns the key size, in bytes, of cipher. -func (cipher CipherFunction) KeySize() int { - switch cipher { - case CipherCAST5: - return cast5.KeySize - case CipherAES128: - return 16 - case CipherAES192: - return 24 - case CipherAES256: - return 32 - } - return 0 -} - -// blockSize returns the block size, in bytes, of cipher. -func (cipher CipherFunction) blockSize() int { - switch cipher { - case CipherCAST5: - return 8 - case CipherAES128, CipherAES192, CipherAES256: - return 16 - } - return 0 -} - -// new returns a fresh instance of the given cipher. -func (cipher CipherFunction) new(key []byte) (block cipher.Block) { - switch cipher { - case CipherCAST5: - block, _ = cast5.NewCipher(key) - case CipherAES128, CipherAES192, CipherAES256: - block, _ = aes.NewCipher(key) - } - return -} - -// readMPI reads a big integer from r. The bit length returned is the bit -// length that was specified in r. This is preserved so that the integer can be -// reserialized exactly. -func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err os.Error) { - var buf [2]byte - _, err = readFull(r, buf[0:]) - if err != nil { - return - } - bitLength = uint16(buf[0])<<8 | uint16(buf[1]) - numBytes := (int(bitLength) + 7) / 8 - mpi = make([]byte, numBytes) - _, err = readFull(r, mpi) - return -} - -// mpiLength returns the length of the given *big.Int when serialized as an -// MPI. -func mpiLength(n *big.Int) (mpiLengthInBytes int) { - mpiLengthInBytes = 2 /* MPI length */ - mpiLengthInBytes += (n.BitLen() + 7) / 8 - return -} - -// writeMPI serializes a big integer to w. -func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err os.Error) { - _, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)}) - if err == nil { - _, err = w.Write(mpiBytes) - } - return -} - -// writeBig serializes a *big.Int to w. -func writeBig(w io.Writer, i *big.Int) os.Error { - return writeMPI(w, uint16(i.BitLen()), i.Bytes()) -} diff --git a/src/pkg/crypto/openpgp/packet/packet_test.go b/src/pkg/crypto/openpgp/packet/packet_test.go deleted file mode 100644 index 23d9978ae..000000000 --- a/src/pkg/crypto/openpgp/packet/packet_test.go +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "bytes" - "crypto/openpgp/error" - "encoding/hex" - "fmt" - "io" - "io/ioutil" - "os" - "testing" -) - -func TestReadFull(t *testing.T) { - var out [4]byte - - b := bytes.NewBufferString("foo") - n, err := readFull(b, out[:3]) - if n != 3 || err != nil { - t.Errorf("full read failed n:%d err:%s", n, err) - } - - b = bytes.NewBufferString("foo") - n, err = readFull(b, out[:4]) - if n != 3 || err != io.ErrUnexpectedEOF { - t.Errorf("partial read failed n:%d err:%s", n, err) - } - - b = bytes.NewBuffer(nil) - n, err = readFull(b, out[:3]) - if n != 0 || err != io.ErrUnexpectedEOF { - t.Errorf("empty read failed n:%d err:%s", n, err) - } -} - -func readerFromHex(s string) io.Reader { - data, err := hex.DecodeString(s) - if err != nil { - panic("readerFromHex: bad input") - } - return bytes.NewBuffer(data) -} - -var readLengthTests = []struct { - hexInput string - length int64 - isPartial bool - err os.Error -}{ - {"", 0, false, io.ErrUnexpectedEOF}, - {"1f", 31, false, nil}, - {"c0", 0, false, io.ErrUnexpectedEOF}, - {"c101", 256 + 1 + 192, false, nil}, - {"e0", 1, true, nil}, - {"e1", 2, true, nil}, - {"e2", 4, true, nil}, - {"ff", 0, false, io.ErrUnexpectedEOF}, - {"ff00", 0, false, io.ErrUnexpectedEOF}, - {"ff0000", 0, false, io.ErrUnexpectedEOF}, - {"ff000000", 0, false, io.ErrUnexpectedEOF}, - {"ff00000000", 0, false, nil}, - {"ff01020304", 16909060, false, nil}, -} - -func TestReadLength(t *testing.T) { - for i, test := range readLengthTests { - length, isPartial, err := readLength(readerFromHex(test.hexInput)) - if test.err != nil { - if err != test.err { - t.Errorf("%d: expected different error got:%s want:%s", i, err, test.err) - } - continue - } - if err != nil { - t.Errorf("%d: unexpected error: %s", i, err) - continue - } - if length != test.length || isPartial != test.isPartial { - t.Errorf("%d: bad result got:(%d,%t) want:(%d,%t)", i, length, isPartial, test.length, test.isPartial) - } - } -} - -var partialLengthReaderTests = []struct { - hexInput string - err os.Error - hexOutput string -}{ - {"e0", io.ErrUnexpectedEOF, ""}, - {"e001", io.ErrUnexpectedEOF, ""}, - {"e0010102", nil, "0102"}, - {"ff00000000", nil, ""}, - {"e10102e1030400", nil, "01020304"}, - {"e101", io.ErrUnexpectedEOF, ""}, -} - -func TestPartialLengthReader(t *testing.T) { - for i, test := range partialLengthReaderTests { - r := &partialLengthReader{readerFromHex(test.hexInput), 0, true} - out, err := ioutil.ReadAll(r) - if test.err != nil { - if err != test.err { - t.Errorf("%d: expected different error got:%s want:%s", i, err, test.err) - } - continue - } - if err != nil { - t.Errorf("%d: unexpected error: %s", i, err) - continue - } - - got := fmt.Sprintf("%x", out) - if got != test.hexOutput { - t.Errorf("%d: got:%s want:%s", i, test.hexOutput, got) - } - } -} - -var readHeaderTests = []struct { - hexInput string - structuralError bool - unexpectedEOF bool - tag int - length int64 - hexOutput string -}{ - {"", false, false, 0, 0, ""}, - {"7f", true, false, 0, 0, ""}, - - // Old format headers - {"80", false, true, 0, 0, ""}, - {"8001", false, true, 0, 1, ""}, - {"800102", false, false, 0, 1, "02"}, - {"81000102", false, false, 0, 1, "02"}, - {"820000000102", false, false, 0, 1, "02"}, - {"860000000102", false, false, 1, 1, "02"}, - {"83010203", false, false, 0, -1, "010203"}, - - // New format headers - {"c0", false, true, 0, 0, ""}, - {"c000", false, false, 0, 0, ""}, - {"c00102", false, false, 0, 1, "02"}, - {"c0020203", false, false, 0, 2, "0203"}, - {"c00202", false, true, 0, 2, ""}, - {"c3020203", false, false, 3, 2, "0203"}, -} - -func TestReadHeader(t *testing.T) { - for i, test := range readHeaderTests { - tag, length, contents, err := readHeader(readerFromHex(test.hexInput)) - if test.structuralError { - if _, ok := err.(error.StructuralError); ok { - continue - } - t.Errorf("%d: expected StructuralError, got:%s", i, err) - continue - } - if err != nil { - if len(test.hexInput) == 0 && err == os.EOF { - continue - } - if !test.unexpectedEOF || err != io.ErrUnexpectedEOF { - t.Errorf("%d: unexpected error from readHeader: %s", i, err) - } - continue - } - if int(tag) != test.tag || length != test.length { - t.Errorf("%d: got:(%d,%d) want:(%d,%d)", i, int(tag), length, test.tag, test.length) - continue - } - - body, err := ioutil.ReadAll(contents) - if err != nil { - if !test.unexpectedEOF || err != io.ErrUnexpectedEOF { - t.Errorf("%d: unexpected error from contents: %s", i, err) - } - continue - } - if test.unexpectedEOF { - t.Errorf("%d: expected ErrUnexpectedEOF from contents but got no error", i) - continue - } - got := fmt.Sprintf("%x", body) - if got != test.hexOutput { - t.Errorf("%d: got:%s want:%s", i, got, test.hexOutput) - } - } -} - -func TestSerializeHeader(t *testing.T) { - tag := packetTypePublicKey - lengths := []int{0, 1, 2, 64, 192, 193, 8000, 8384, 8385, 10000} - - for _, length := range lengths { - buf := bytes.NewBuffer(nil) - serializeHeader(buf, tag, length) - tag2, length2, _, err := readHeader(buf) - if err != nil { - t.Errorf("length %d, err: %s", length, err) - } - if tag2 != tag { - t.Errorf("length %d, tag incorrect (got %d, want %d)", length, tag2, tag) - } - if int(length2) != length { - t.Errorf("length %d, length incorrect (got %d)", length, length2) - } - } -} - -func TestPartialLengths(t *testing.T) { - buf := bytes.NewBuffer(nil) - w := new(partialLengthWriter) - w.w = noOpCloser{buf} - - const maxChunkSize = 64 - - var b [maxChunkSize]byte - var n uint8 - for l := 1; l <= maxChunkSize; l++ { - for i := 0; i < l; i++ { - b[i] = n - n++ - } - m, err := w.Write(b[:l]) - if m != l { - t.Errorf("short write got: %d want: %d", m, l) - } - if err != nil { - t.Errorf("error from write: %s", err) - } - } - w.Close() - - want := (maxChunkSize * (maxChunkSize + 1)) / 2 - copyBuf := bytes.NewBuffer(nil) - r := &partialLengthReader{buf, 0, true} - m, err := io.Copy(copyBuf, r) - if m != int64(want) { - t.Errorf("short copy got: %d want: %d", m, want) - } - if err != nil { - t.Errorf("error from copy: %s", err) - } - - copyBytes := copyBuf.Bytes() - for i := 0; i < want; i++ { - if copyBytes[i] != uint8(i) { - t.Errorf("bad pattern in copy at %d", i) - break - } - } -} diff --git a/src/pkg/crypto/openpgp/packet/private_key.go b/src/pkg/crypto/openpgp/packet/private_key.go deleted file mode 100644 index 6f8133d98..000000000 --- a/src/pkg/crypto/openpgp/packet/private_key.go +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "big" - "bytes" - "crypto/cipher" - "crypto/dsa" - "crypto/openpgp/elgamal" - "crypto/openpgp/error" - "crypto/openpgp/s2k" - "crypto/rsa" - "crypto/sha1" - "io" - "io/ioutil" - "os" - "strconv" -) - -// PrivateKey represents a possibly encrypted private key. See RFC 4880, -// section 5.5.3. -type PrivateKey struct { - PublicKey - Encrypted bool // if true then the private key is unavailable until Decrypt has been called. - encryptedData []byte - cipher CipherFunction - s2k func(out, in []byte) - PrivateKey interface{} // An *rsa.PrivateKey. - sha1Checksum bool - iv []byte -} - -func NewRSAPrivateKey(currentTimeSecs uint32, priv *rsa.PrivateKey, isSubkey bool) *PrivateKey { - pk := new(PrivateKey) - pk.PublicKey = *NewRSAPublicKey(currentTimeSecs, &priv.PublicKey, isSubkey) - pk.PrivateKey = priv - return pk -} - -func (pk *PrivateKey) parse(r io.Reader) (err os.Error) { - err = (&pk.PublicKey).parse(r) - if err != nil { - return - } - var buf [1]byte - _, err = readFull(r, buf[:]) - if err != nil { - return - } - - s2kType := buf[0] - - switch s2kType { - case 0: - pk.s2k = nil - pk.Encrypted = false - case 254, 255: - _, err = readFull(r, buf[:]) - if err != nil { - return - } - pk.cipher = CipherFunction(buf[0]) - pk.Encrypted = true - pk.s2k, err = s2k.Parse(r) - if err != nil { - return - } - if s2kType == 254 { - pk.sha1Checksum = true - } - default: - return error.UnsupportedError("deprecated s2k function in private key") - } - - if pk.Encrypted { - blockSize := pk.cipher.blockSize() - if blockSize == 0 { - return error.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) - } - pk.iv = make([]byte, blockSize) - _, err = readFull(r, pk.iv) - if err != nil { - return - } - } - - pk.encryptedData, err = ioutil.ReadAll(r) - if err != nil { - return - } - - if !pk.Encrypted { - return pk.parsePrivateKey(pk.encryptedData) - } - - return -} - -func mod64kHash(d []byte) uint16 { - h := uint16(0) - for i := 0; i < len(d); i += 2 { - v := uint16(d[i]) << 8 - if i+1 < len(d) { - v += uint16(d[i+1]) - } - h += v - } - return h -} - -func (pk *PrivateKey) Serialize(w io.Writer) (err os.Error) { - // TODO(agl): support encrypted private keys - buf := bytes.NewBuffer(nil) - err = pk.PublicKey.serializeWithoutHeaders(buf) - if err != nil { - return - } - buf.WriteByte(0 /* no encryption */ ) - - privateKeyBuf := bytes.NewBuffer(nil) - - switch priv := pk.PrivateKey.(type) { - case *rsa.PrivateKey: - err = serializeRSAPrivateKey(privateKeyBuf, priv) - default: - err = error.InvalidArgumentError("non-RSA private key") - } - if err != nil { - return - } - - ptype := packetTypePrivateKey - contents := buf.Bytes() - privateKeyBytes := privateKeyBuf.Bytes() - if pk.IsSubkey { - ptype = packetTypePrivateSubkey - } - err = serializeHeader(w, ptype, len(contents)+len(privateKeyBytes)+2) - if err != nil { - return - } - _, err = w.Write(contents) - if err != nil { - return - } - _, err = w.Write(privateKeyBytes) - if err != nil { - return - } - - checksum := mod64kHash(privateKeyBytes) - var checksumBytes [2]byte - checksumBytes[0] = byte(checksum >> 8) - checksumBytes[1] = byte(checksum) - _, err = w.Write(checksumBytes[:]) - - return -} - -func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) os.Error { - err := writeBig(w, priv.D) - if err != nil { - return err - } - err = writeBig(w, priv.Primes[1]) - if err != nil { - return err - } - err = writeBig(w, priv.Primes[0]) - if err != nil { - return err - } - return writeBig(w, priv.Precomputed.Qinv) -} - -// Decrypt decrypts an encrypted private key using a passphrase. -func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error { - if !pk.Encrypted { - return nil - } - - key := make([]byte, pk.cipher.KeySize()) - pk.s2k(key, passphrase) - block := pk.cipher.new(key) - cfb := cipher.NewCFBDecrypter(block, pk.iv) - - data := pk.encryptedData - cfb.XORKeyStream(data, data) - - if pk.sha1Checksum { - if len(data) < sha1.Size { - return error.StructuralError("truncated private key data") - } - h := sha1.New() - h.Write(data[:len(data)-sha1.Size]) - sum := h.Sum() - if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { - return error.StructuralError("private key checksum failure") - } - data = data[:len(data)-sha1.Size] - } else { - if len(data) < 2 { - return error.StructuralError("truncated private key data") - } - var sum uint16 - for i := 0; i < len(data)-2; i++ { - sum += uint16(data[i]) - } - if data[len(data)-2] != uint8(sum>>8) || - data[len(data)-1] != uint8(sum) { - return error.StructuralError("private key checksum failure") - } - data = data[:len(data)-2] - } - - return pk.parsePrivateKey(data) -} - -func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) { - switch pk.PublicKey.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly: - return pk.parseRSAPrivateKey(data) - case PubKeyAlgoDSA: - return pk.parseDSAPrivateKey(data) - case PubKeyAlgoElGamal: - return pk.parseElGamalPrivateKey(data) - } - panic("impossible") -} - -func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err os.Error) { - rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey) - rsaPriv := new(rsa.PrivateKey) - rsaPriv.PublicKey = *rsaPub - - buf := bytes.NewBuffer(data) - d, _, err := readMPI(buf) - if err != nil { - return - } - p, _, err := readMPI(buf) - if err != nil { - return - } - q, _, err := readMPI(buf) - if err != nil { - return - } - - rsaPriv.D = new(big.Int).SetBytes(d) - rsaPriv.Primes = make([]*big.Int, 2) - rsaPriv.Primes[0] = new(big.Int).SetBytes(p) - rsaPriv.Primes[1] = new(big.Int).SetBytes(q) - rsaPriv.Precompute() - pk.PrivateKey = rsaPriv - pk.Encrypted = false - pk.encryptedData = nil - - return nil -} - -func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err os.Error) { - dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey) - dsaPriv := new(dsa.PrivateKey) - dsaPriv.PublicKey = *dsaPub - - buf := bytes.NewBuffer(data) - x, _, err := readMPI(buf) - if err != nil { - return - } - - dsaPriv.X = new(big.Int).SetBytes(x) - pk.PrivateKey = dsaPriv - pk.Encrypted = false - pk.encryptedData = nil - - return nil -} - -func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err os.Error) { - pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey) - priv := new(elgamal.PrivateKey) - priv.PublicKey = *pub - - buf := bytes.NewBuffer(data) - x, _, err := readMPI(buf) - if err != nil { - return - } - - priv.X = new(big.Int).SetBytes(x) - pk.PrivateKey = priv - pk.Encrypted = false - pk.encryptedData = nil - - return nil -} diff --git a/src/pkg/crypto/openpgp/packet/private_key_test.go b/src/pkg/crypto/openpgp/packet/private_key_test.go deleted file mode 100644 index 60eebaa6b..000000000 --- a/src/pkg/crypto/openpgp/packet/private_key_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "testing" -) - -var privateKeyTests = []struct { - privateKeyHex string - creationTime uint32 -}{ - { - privKeyRSAHex, - 0x4cc349a8, - }, - { - privKeyElGamalHex, - 0x4df9ee1a, - }, -} - -func TestPrivateKeyRead(t *testing.T) { - for i, test := range privateKeyTests { - packet, err := Read(readerFromHex(test.privateKeyHex)) - if err != nil { - t.Errorf("#%d: failed to parse: %s", i, err) - continue - } - - privKey := packet.(*PrivateKey) - - if !privKey.Encrypted { - t.Errorf("#%d: private key isn't encrypted", i) - continue - } - - err = privKey.Decrypt([]byte("testing")) - if err != nil { - t.Errorf("#%d: failed to decrypt: %s", i, err) - continue - } - - if privKey.CreationTime != test.creationTime || privKey.Encrypted { - t.Errorf("#%d: bad result, got: %#v", i, privKey) - } - } -} - -// Generated with `gpg --export-secret-keys "Test Key 2"` -const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" - -// Generated by `gpg --export-secret-keys` followed by a manual extraction of -// the ElGamal subkey from the packets. -const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc" diff --git a/src/pkg/crypto/openpgp/packet/public_key.go b/src/pkg/crypto/openpgp/packet/public_key.go deleted file mode 100644 index e6b0ae5f3..000000000 --- a/src/pkg/crypto/openpgp/packet/public_key.go +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "big" - "crypto/dsa" - "crypto/openpgp/elgamal" - "crypto/openpgp/error" - "crypto/rsa" - "crypto/sha1" - "encoding/binary" - "fmt" - "hash" - "io" - "os" - "strconv" -) - -// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. -type PublicKey struct { - CreationTime uint32 // seconds since the epoch - PubKeyAlgo PublicKeyAlgorithm - PublicKey interface{} // Either a *rsa.PublicKey or *dsa.PublicKey - Fingerprint [20]byte - KeyId uint64 - IsSubkey bool - - n, e, p, q, g, y parsedMPI -} - -func fromBig(n *big.Int) parsedMPI { - return parsedMPI{ - bytes: n.Bytes(), - bitLength: uint16(n.BitLen()), - } -} - -// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey. -func NewRSAPublicKey(creationTimeSecs uint32, pub *rsa.PublicKey, isSubkey bool) *PublicKey { - pk := &PublicKey{ - CreationTime: creationTimeSecs, - PubKeyAlgo: PubKeyAlgoRSA, - PublicKey: pub, - IsSubkey: isSubkey, - n: fromBig(pub.N), - e: fromBig(big.NewInt(int64(pub.E))), - } - - pk.setFingerPrintAndKeyId() - return pk -} - -func (pk *PublicKey) parse(r io.Reader) (err os.Error) { - // RFC 4880, section 5.5.2 - var buf [6]byte - _, err = readFull(r, buf[:]) - if err != nil { - return - } - if buf[0] != 4 { - return error.UnsupportedError("public key version") - } - pk.CreationTime = uint32(buf[1])<<24 | uint32(buf[2])<<16 | uint32(buf[3])<<8 | uint32(buf[4]) - pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: - err = pk.parseRSA(r) - case PubKeyAlgoDSA: - err = pk.parseDSA(r) - case PubKeyAlgoElGamal: - err = pk.parseElGamal(r) - default: - err = error.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) - } - if err != nil { - return - } - - pk.setFingerPrintAndKeyId() - return -} - -func (pk *PublicKey) setFingerPrintAndKeyId() { - // RFC 4880, section 12.2 - fingerPrint := sha1.New() - pk.SerializeSignaturePrefix(fingerPrint) - pk.serializeWithoutHeaders(fingerPrint) - copy(pk.Fingerprint[:], fingerPrint.Sum()) - pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) -} - -// parseRSA parses RSA public key material from the given Reader. See RFC 4880, -// section 5.5.2. -func (pk *PublicKey) parseRSA(r io.Reader) (err os.Error) { - pk.n.bytes, pk.n.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.e.bytes, pk.e.bitLength, err = readMPI(r) - if err != nil { - return - } - - if len(pk.e.bytes) > 3 { - err = error.UnsupportedError("large public exponent") - return - } - rsa := &rsa.PublicKey{ - N: new(big.Int).SetBytes(pk.n.bytes), - E: 0, - } - for i := 0; i < len(pk.e.bytes); i++ { - rsa.E <<= 8 - rsa.E |= int(pk.e.bytes[i]) - } - pk.PublicKey = rsa - return -} - -// parseDSA parses DSA public key material from the given Reader. See RFC 4880, -// section 5.5.2. -func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) { - pk.p.bytes, pk.p.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.q.bytes, pk.q.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.g.bytes, pk.g.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.y.bytes, pk.y.bitLength, err = readMPI(r) - if err != nil { - return - } - - dsa := new(dsa.PublicKey) - dsa.P = new(big.Int).SetBytes(pk.p.bytes) - dsa.Q = new(big.Int).SetBytes(pk.q.bytes) - dsa.G = new(big.Int).SetBytes(pk.g.bytes) - dsa.Y = new(big.Int).SetBytes(pk.y.bytes) - pk.PublicKey = dsa - return -} - -// parseElGamal parses ElGamal public key material from the given Reader. See -// RFC 4880, section 5.5.2. -func (pk *PublicKey) parseElGamal(r io.Reader) (err os.Error) { - pk.p.bytes, pk.p.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.g.bytes, pk.g.bitLength, err = readMPI(r) - if err != nil { - return - } - pk.y.bytes, pk.y.bitLength, err = readMPI(r) - if err != nil { - return - } - - elgamal := new(elgamal.PublicKey) - elgamal.P = new(big.Int).SetBytes(pk.p.bytes) - elgamal.G = new(big.Int).SetBytes(pk.g.bytes) - elgamal.Y = new(big.Int).SetBytes(pk.y.bytes) - pk.PublicKey = elgamal - return -} - -// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. -// The prefix is used when calculating a signature over this public key. See -// RFC 4880, section 5.2.4. -func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) { - var pLength uint16 - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: - pLength += 2 + uint16(len(pk.n.bytes)) - pLength += 2 + uint16(len(pk.e.bytes)) - case PubKeyAlgoDSA: - pLength += 2 + uint16(len(pk.p.bytes)) - pLength += 2 + uint16(len(pk.q.bytes)) - pLength += 2 + uint16(len(pk.g.bytes)) - pLength += 2 + uint16(len(pk.y.bytes)) - case PubKeyAlgoElGamal: - pLength += 2 + uint16(len(pk.p.bytes)) - pLength += 2 + uint16(len(pk.g.bytes)) - pLength += 2 + uint16(len(pk.y.bytes)) - default: - panic("unknown public key algorithm") - } - pLength += 6 - h.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) - return -} - -func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) { - length := 6 // 6 byte header - - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: - length += 2 + len(pk.n.bytes) - length += 2 + len(pk.e.bytes) - case PubKeyAlgoDSA: - length += 2 + len(pk.p.bytes) - length += 2 + len(pk.q.bytes) - length += 2 + len(pk.g.bytes) - length += 2 + len(pk.y.bytes) - case PubKeyAlgoElGamal: - length += 2 + len(pk.p.bytes) - length += 2 + len(pk.g.bytes) - length += 2 + len(pk.y.bytes) - default: - panic("unknown public key algorithm") - } - - packetType := packetTypePublicKey - if pk.IsSubkey { - packetType = packetTypePublicSubkey - } - err = serializeHeader(w, packetType, length) - if err != nil { - return - } - return pk.serializeWithoutHeaders(w) -} - -// serializeWithoutHeaders marshals the PublicKey to w in the form of an -// OpenPGP public key packet, not including the packet header. -func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err os.Error) { - var buf [6]byte - buf[0] = 4 - buf[1] = byte(pk.CreationTime >> 24) - buf[2] = byte(pk.CreationTime >> 16) - buf[3] = byte(pk.CreationTime >> 8) - buf[4] = byte(pk.CreationTime) - buf[5] = byte(pk.PubKeyAlgo) - - _, err = w.Write(buf[:]) - if err != nil { - return - } - - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: - return writeMPIs(w, pk.n, pk.e) - case PubKeyAlgoDSA: - return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) - case PubKeyAlgoElGamal: - return writeMPIs(w, pk.p, pk.g, pk.y) - } - return error.InvalidArgumentError("bad public-key algorithm") -} - -// CanSign returns true iff this public key can generate signatures -func (pk *PublicKey) CanSign() bool { - return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal -} - -// VerifySignature returns nil iff sig is a valid signature, made by this -// public key, of the data hashed into signed. signed is mutated by this call. -func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.Error) { - if !pk.CanSign() { - return error.InvalidArgumentError("public key cannot generate signatures") - } - - signed.Write(sig.HashSuffix) - hashBytes := signed.Sum() - - if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { - return error.SignatureError("hash tag doesn't match") - } - - if pk.PubKeyAlgo != sig.PubKeyAlgo { - return error.InvalidArgumentError("public key and signature use different algorithms") - } - - switch pk.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) - err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes) - if err != nil { - return error.SignatureError("RSA verification failure") - } - return nil - case PubKeyAlgoDSA: - dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) - if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { - return error.SignatureError("DSA verification failure") - } - return nil - default: - panic("shouldn't happen") - } - panic("unreachable") -} - -// keySignatureHash returns a Hash of the message that needs to be signed for -// pk to assert a subkey relationship to signed. -func keySignatureHash(pk, signed *PublicKey, sig *Signature) (h hash.Hash, err os.Error) { - h = sig.Hash.New() - if h == nil { - return nil, error.UnsupportedError("hash function") - } - - // RFC 4880, section 5.2.4 - pk.SerializeSignaturePrefix(h) - pk.serializeWithoutHeaders(h) - signed.SerializeSignaturePrefix(h) - signed.serializeWithoutHeaders(h) - return -} - -// VerifyKeySignature returns nil iff sig is a valid signature, made by this -// public key, of signed. -func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err os.Error) { - h, err := keySignatureHash(pk, signed, sig) - if err != nil { - return err - } - return pk.VerifySignature(h, sig) -} - -// userIdSignatureHash returns a Hash of the message that needs to be signed -// to assert that pk is a valid key for id. -func userIdSignatureHash(id string, pk *PublicKey, sig *Signature) (h hash.Hash, err os.Error) { - h = sig.Hash.New() - if h == nil { - return nil, error.UnsupportedError("hash function") - } - - // RFC 4880, section 5.2.4 - pk.SerializeSignaturePrefix(h) - pk.serializeWithoutHeaders(h) - - var buf [5]byte - buf[0] = 0xb4 - buf[1] = byte(len(id) >> 24) - buf[2] = byte(len(id) >> 16) - buf[3] = byte(len(id) >> 8) - buf[4] = byte(len(id)) - h.Write(buf[:]) - h.Write([]byte(id)) - - return -} - -// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this -// public key, of id. -func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err os.Error) { - h, err := userIdSignatureHash(id, pk, sig) - if err != nil { - return err - } - return pk.VerifySignature(h, sig) -} - -// KeyIdString returns the public key's fingerprint in capital hex -// (e.g. "6C7EE1B8621CC013"). -func (pk *PublicKey) KeyIdString() string { - return fmt.Sprintf("%X", pk.Fingerprint[12:20]) -} - -// KeyIdShortString returns the short form of public key's fingerprint -// in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). -func (pk *PublicKey) KeyIdShortString() string { - return fmt.Sprintf("%X", pk.Fingerprint[16:20]) -} - -// A parsedMPI is used to store the contents of a big integer, along with the -// bit length that was specified in the original input. This allows the MPI to -// be reserialized exactly. -type parsedMPI struct { - bytes []byte - bitLength uint16 -} - -// writeMPIs is a utility function for serializing several big integers to the -// given Writer. -func writeMPIs(w io.Writer, mpis ...parsedMPI) (err os.Error) { - for _, mpi := range mpis { - err = writeMPI(w, mpi.bitLength, mpi.bytes) - if err != nil { - return - } - } - return -} diff --git a/src/pkg/crypto/openpgp/packet/public_key_test.go b/src/pkg/crypto/openpgp/packet/public_key_test.go deleted file mode 100644 index 6e8bfbce6..000000000 --- a/src/pkg/crypto/openpgp/packet/public_key_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "bytes" - "encoding/hex" - "testing" -) - -var pubKeyTests = []struct { - hexData string - hexFingerprint string - creationTime uint32 - pubKeyAlgo PublicKeyAlgorithm - keyId uint64 - keyIdString string - keyIdShort string -}{ - {rsaPkDataHex, rsaFingerprintHex, 0x4d3c5c10, PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"}, - {dsaPkDataHex, dsaFingerprintHex, 0x4d432f89, PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"}, -} - -func TestPublicKeyRead(t *testing.T) { - for i, test := range pubKeyTests { - packet, err := Read(readerFromHex(test.hexData)) - if err != nil { - t.Errorf("#%d: Read error: %s", i, err) - continue - } - pk, ok := packet.(*PublicKey) - if !ok { - t.Errorf("#%d: failed to parse, got: %#v", i, packet) - continue - } - if pk.PubKeyAlgo != test.pubKeyAlgo { - t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo) - } - if pk.CreationTime != test.creationTime { - t.Errorf("#%d: bad creation time got:%x want:%x", i, pk.CreationTime, test.creationTime) - } - expectedFingerprint, _ := hex.DecodeString(test.hexFingerprint) - if !bytes.Equal(expectedFingerprint, pk.Fingerprint[:]) { - t.Errorf("#%d: bad fingerprint got:%x want:%x", i, pk.Fingerprint[:], expectedFingerprint) - } - if pk.KeyId != test.keyId { - t.Errorf("#%d: bad keyid got:%x want:%x", i, pk.KeyId, test.keyId) - } - if g, e := pk.KeyIdString(), test.keyIdString; g != e { - t.Errorf("#%d: bad KeyIdString got:%q want:%q", i, g, e) - } - if g, e := pk.KeyIdShortString(), test.keyIdShort; g != e { - t.Errorf("#%d: bad KeyIdShortString got:%q want:%q", i, g, e) - } - } -} - -func TestPublicKeySerialize(t *testing.T) { - for i, test := range pubKeyTests { - packet, err := Read(readerFromHex(test.hexData)) - if err != nil { - t.Errorf("#%d: Read error: %s", i, err) - continue - } - pk, ok := packet.(*PublicKey) - if !ok { - t.Errorf("#%d: failed to parse, got: %#v", i, packet) - continue - } - serializeBuf := bytes.NewBuffer(nil) - err = pk.Serialize(serializeBuf) - if err != nil { - t.Errorf("#%d: failed to serialize: %s", i, err) - continue - } - - packet, err = Read(serializeBuf) - if err != nil { - t.Errorf("#%d: Read error (from serialized data): %s", i, err) - continue - } - pk, ok = packet.(*PublicKey) - if !ok { - t.Errorf("#%d: failed to parse serialized data, got: %#v", i, packet) - continue - } - } -} - -const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb" - -const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" - -const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed" - -const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0" diff --git a/src/pkg/crypto/openpgp/packet/reader.go b/src/pkg/crypto/openpgp/packet/reader.go deleted file mode 100644 index 5febc3bc8..000000000 --- a/src/pkg/crypto/openpgp/packet/reader.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "crypto/openpgp/error" - "io" - "os" -) - -// Reader reads packets from an io.Reader and allows packets to be 'unread' so -// that they result from the next call to Next. -type Reader struct { - q []Packet - readers []io.Reader -} - -// Next returns the most recently unread Packet, or reads another packet from -// the top-most io.Reader. Unknown packet types are skipped. -func (r *Reader) Next() (p Packet, err os.Error) { - if len(r.q) > 0 { - p = r.q[len(r.q)-1] - r.q = r.q[:len(r.q)-1] - return - } - - for len(r.readers) > 0 { - p, err = Read(r.readers[len(r.readers)-1]) - if err == nil { - return - } - if err == os.EOF { - r.readers = r.readers[:len(r.readers)-1] - continue - } - if _, ok := err.(error.UnknownPacketTypeError); !ok { - return nil, err - } - } - - return nil, os.EOF -} - -// Push causes the Reader to start reading from a new io.Reader. When an EOF -// error is seen from the new io.Reader, it is popped and the Reader continues -// to read from the next most recent io.Reader. -func (r *Reader) Push(reader io.Reader) { - r.readers = append(r.readers, reader) -} - -// Unread causes the given Packet to be returned from the next call to Next. -func (r *Reader) Unread(p Packet) { - r.q = append(r.q, p) -} - -func NewReader(r io.Reader) *Reader { - return &Reader{ - q: nil, - readers: []io.Reader{r}, - } -} diff --git a/src/pkg/crypto/openpgp/packet/signature.go b/src/pkg/crypto/openpgp/packet/signature.go deleted file mode 100644 index 7577e2875..000000000 --- a/src/pkg/crypto/openpgp/packet/signature.go +++ /dev/null @@ -1,558 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "crypto" - "crypto/dsa" - "crypto/openpgp/error" - "crypto/openpgp/s2k" - "crypto/rand" - "crypto/rsa" - "encoding/binary" - "hash" - "io" - "os" - "strconv" -) - -// Signature represents a signature. See RFC 4880, section 5.2. -type Signature struct { - SigType SignatureType - PubKeyAlgo PublicKeyAlgorithm - Hash crypto.Hash - - // HashSuffix is extra data that is hashed in after the signed data. - HashSuffix []byte - // HashTag contains the first two bytes of the hash for fast rejection - // of bad signed data. - HashTag [2]byte - CreationTime uint32 // Unix epoch time - - RSASignature parsedMPI - DSASigR, DSASigS parsedMPI - - // rawSubpackets contains the unparsed subpackets, in order. - rawSubpackets []outputSubpacket - - // The following are optional so are nil when not included in the - // signature. - - SigLifetimeSecs, KeyLifetimeSecs *uint32 - PreferredSymmetric, PreferredHash, PreferredCompression []uint8 - IssuerKeyId *uint64 - IsPrimaryId *bool - - // FlagsValid is set if any flags were given. See RFC 4880, section - // 5.2.3.21 for details. - FlagsValid bool - FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool - - outSubpackets []outputSubpacket -} - -func (sig *Signature) parse(r io.Reader) (err os.Error) { - // RFC 4880, section 5.2.3 - var buf [5]byte - _, err = readFull(r, buf[:1]) - if err != nil { - return - } - if buf[0] != 4 { - err = error.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) - return - } - - _, err = readFull(r, buf[:5]) - if err != nil { - return - } - sig.SigType = SignatureType(buf[0]) - sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) - switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: - default: - err = error.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) - return - } - - var ok bool - sig.Hash, ok = s2k.HashIdToHash(buf[2]) - if !ok { - return error.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) - } - - hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) - l := 6 + hashedSubpacketsLength - sig.HashSuffix = make([]byte, l+6) - sig.HashSuffix[0] = 4 - copy(sig.HashSuffix[1:], buf[:5]) - hashedSubpackets := sig.HashSuffix[6:l] - _, err = readFull(r, hashedSubpackets) - if err != nil { - return - } - // See RFC 4880, section 5.2.4 - trailer := sig.HashSuffix[l:] - trailer[0] = 4 - trailer[1] = 0xff - trailer[2] = uint8(l >> 24) - trailer[3] = uint8(l >> 16) - trailer[4] = uint8(l >> 8) - trailer[5] = uint8(l) - - err = parseSignatureSubpackets(sig, hashedSubpackets, true) - if err != nil { - return - } - - _, err = readFull(r, buf[:2]) - if err != nil { - return - } - unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) - unhashedSubpackets := make([]byte, unhashedSubpacketsLength) - _, err = readFull(r, unhashedSubpackets) - if err != nil { - return - } - err = parseSignatureSubpackets(sig, unhashedSubpackets, false) - if err != nil { - return - } - - _, err = readFull(r, sig.HashTag[:2]) - if err != nil { - return - } - - switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) - case PubKeyAlgoDSA: - sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) - if err == nil { - sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) - } - default: - panic("unreachable") - } - return -} - -// parseSignatureSubpackets parses subpackets of the main signature packet. See -// RFC 4880, section 5.2.3.1. -func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err os.Error) { - for len(subpackets) > 0 { - subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) - if err != nil { - return - } - } - - if sig.CreationTime == 0 { - err = error.StructuralError("no creation time in signature") - } - - return -} - -type signatureSubpacketType uint8 - -const ( - creationTimeSubpacket signatureSubpacketType = 2 - signatureExpirationSubpacket signatureSubpacketType = 3 - keyExpirySubpacket signatureSubpacketType = 9 - prefSymmetricAlgosSubpacket signatureSubpacketType = 11 - issuerSubpacket signatureSubpacketType = 16 - prefHashAlgosSubpacket signatureSubpacketType = 21 - prefCompressionSubpacket signatureSubpacketType = 22 - primaryUserIdSubpacket signatureSubpacketType = 25 - keyFlagsSubpacket signatureSubpacketType = 27 -) - -// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. -func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err os.Error) { - // RFC 4880, section 5.2.3.1 - var ( - length uint32 - packetType signatureSubpacketType - isCritical bool - ) - switch { - case subpacket[0] < 192: - length = uint32(subpacket[0]) - subpacket = subpacket[1:] - case subpacket[0] < 255: - if len(subpacket) < 2 { - goto Truncated - } - length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 - subpacket = subpacket[2:] - default: - if len(subpacket) < 5 { - goto Truncated - } - length = uint32(subpacket[1])<<24 | - uint32(subpacket[2])<<16 | - uint32(subpacket[3])<<8 | - uint32(subpacket[4]) - subpacket = subpacket[5:] - } - if length > uint32(len(subpacket)) { - goto Truncated - } - rest = subpacket[length:] - subpacket = subpacket[:length] - if len(subpacket) == 0 { - err = error.StructuralError("zero length signature subpacket") - return - } - packetType = signatureSubpacketType(subpacket[0] & 0x7f) - isCritical = subpacket[0]&0x80 == 0x80 - subpacket = subpacket[1:] - sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) - switch packetType { - case creationTimeSubpacket: - if !isHashed { - err = error.StructuralError("signature creation time in non-hashed area") - return - } - if len(subpacket) != 4 { - err = error.StructuralError("signature creation time not four bytes") - return - } - sig.CreationTime = binary.BigEndian.Uint32(subpacket) - case signatureExpirationSubpacket: - // Signature expiration time, section 5.2.3.10 - if !isHashed { - return - } - if len(subpacket) != 4 { - err = error.StructuralError("expiration subpacket with bad length") - return - } - sig.SigLifetimeSecs = new(uint32) - *sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) - case keyExpirySubpacket: - // Key expiration time, section 5.2.3.6 - if !isHashed { - return - } - if len(subpacket) != 4 { - err = error.StructuralError("key expiration subpacket with bad length") - return - } - sig.KeyLifetimeSecs = new(uint32) - *sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) - case prefSymmetricAlgosSubpacket: - // Preferred symmetric algorithms, section 5.2.3.7 - if !isHashed { - return - } - sig.PreferredSymmetric = make([]byte, len(subpacket)) - copy(sig.PreferredSymmetric, subpacket) - case issuerSubpacket: - // Issuer, section 5.2.3.5 - if len(subpacket) != 8 { - err = error.StructuralError("issuer subpacket with bad length") - return - } - sig.IssuerKeyId = new(uint64) - *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) - case prefHashAlgosSubpacket: - // Preferred hash algorithms, section 5.2.3.8 - if !isHashed { - return - } - sig.PreferredHash = make([]byte, len(subpacket)) - copy(sig.PreferredHash, subpacket) - case prefCompressionSubpacket: - // Preferred compression algorithms, section 5.2.3.9 - if !isHashed { - return - } - sig.PreferredCompression = make([]byte, len(subpacket)) - copy(sig.PreferredCompression, subpacket) - case primaryUserIdSubpacket: - // Primary User ID, section 5.2.3.19 - if !isHashed { - return - } - if len(subpacket) != 1 { - err = error.StructuralError("primary user id subpacket with bad length") - return - } - sig.IsPrimaryId = new(bool) - if subpacket[0] > 0 { - *sig.IsPrimaryId = true - } - case keyFlagsSubpacket: - // Key flags, section 5.2.3.21 - if !isHashed { - return - } - if len(subpacket) == 0 { - err = error.StructuralError("empty key flags subpacket") - return - } - sig.FlagsValid = true - if subpacket[0]&1 != 0 { - sig.FlagCertify = true - } - if subpacket[0]&2 != 0 { - sig.FlagSign = true - } - if subpacket[0]&4 != 0 { - sig.FlagEncryptCommunications = true - } - if subpacket[0]&8 != 0 { - sig.FlagEncryptStorage = true - } - - default: - if isCritical { - err = error.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) - return - } - } - return - -Truncated: - err = error.StructuralError("signature subpacket truncated") - return -} - -// subpacketLengthLength returns the length, in bytes, of an encoded length value. -func subpacketLengthLength(length int) int { - if length < 192 { - return 1 - } - if length < 16320 { - return 2 - } - return 5 -} - -// serializeSubpacketLength marshals the given length into to. -func serializeSubpacketLength(to []byte, length int) int { - if length < 192 { - to[0] = byte(length) - return 1 - } - if length < 16320 { - length -= 192 - to[0] = byte(length >> 8) - to[1] = byte(length) - return 2 - } - to[0] = 255 - to[1] = byte(length >> 24) - to[2] = byte(length >> 16) - to[3] = byte(length >> 8) - to[4] = byte(length) - return 5 -} - -// subpacketsLength returns the serialized length, in bytes, of the given -// subpackets. -func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) { - for _, subpacket := range subpackets { - if subpacket.hashed == hashed { - length += subpacketLengthLength(len(subpacket.contents) + 1) - length += 1 // type byte - length += len(subpacket.contents) - } - } - return -} - -// serializeSubpackets marshals the given subpackets into to. -func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { - for _, subpacket := range subpackets { - if subpacket.hashed == hashed { - n := serializeSubpacketLength(to, len(subpacket.contents)+1) - to[n] = byte(subpacket.subpacketType) - to = to[1+n:] - n = copy(to, subpacket.contents) - to = to[n:] - } - } - return -} - -// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. -func (sig *Signature) buildHashSuffix() (err os.Error) { - hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) - - var ok bool - l := 6 + hashedSubpacketsLen - sig.HashSuffix = make([]byte, l+6) - sig.HashSuffix[0] = 4 - sig.HashSuffix[1] = uint8(sig.SigType) - sig.HashSuffix[2] = uint8(sig.PubKeyAlgo) - sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash) - if !ok { - sig.HashSuffix = nil - return error.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) - } - sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8) - sig.HashSuffix[5] = byte(hashedSubpacketsLen) - serializeSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true) - trailer := sig.HashSuffix[l:] - trailer[0] = 4 - trailer[1] = 0xff - trailer[2] = byte(l >> 24) - trailer[3] = byte(l >> 16) - trailer[4] = byte(l >> 8) - trailer[5] = byte(l) - return -} - -func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error) { - err = sig.buildHashSuffix() - if err != nil { - return - } - - h.Write(sig.HashSuffix) - digest = h.Sum() - copy(sig.HashTag[:], digest) - return -} - -// Sign signs a message with a private key. The hash, h, must contain -// the hash of the message to be signed and will be mutated by this function. -// On success, the signature is stored in sig. Call Serialize to write it out. -func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) { - sig.outSubpackets = sig.buildSubpackets() - digest, err := sig.signPrepareHash(h) - if err != nil { - return - } - - switch priv.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature.bytes, err = rsa.SignPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) - sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) - case PubKeyAlgoDSA: - r, s, err := dsa.Sign(rand.Reader, priv.PrivateKey.(*dsa.PrivateKey), digest) - if err == nil { - sig.DSASigR.bytes = r.Bytes() - sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) - sig.DSASigS.bytes = s.Bytes() - sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) - } - default: - err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) - } - - return -} - -// SignUserId computes a signature from priv, asserting that pub is a valid -// key for the identity id. On success, the signature is stored in sig. Call -// Serialize to write it out. -func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey) os.Error { - h, err := userIdSignatureHash(id, pub, sig) - if err != nil { - return nil - } - return sig.Sign(h, priv) -} - -// SignKey computes a signature from priv, asserting that pub is a subkey. On -// success, the signature is stored in sig. Call Serialize to write it out. -func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey) os.Error { - h, err := keySignatureHash(&priv.PublicKey, pub, sig) - if err != nil { - return err - } - return sig.Sign(h, priv) -} - -// Serialize marshals sig to w. SignRSA or SignDSA must have been called first. -func (sig *Signature) Serialize(w io.Writer) (err os.Error) { - if len(sig.outSubpackets) == 0 { - sig.outSubpackets = sig.rawSubpackets - } - if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { - return error.InvalidArgumentError("Signature: need to call SignRSA or SignDSA before Serialize") - } - - sigLength := 0 - switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sigLength = 2 + len(sig.RSASignature.bytes) - case PubKeyAlgoDSA: - sigLength = 2 + len(sig.DSASigR.bytes) - sigLength += 2 + len(sig.DSASigS.bytes) - default: - panic("impossible") - } - - unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) - length := len(sig.HashSuffix) - 6 /* trailer not included */ + - 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + - 2 /* hash tag */ + sigLength - err = serializeHeader(w, packetTypeSignature, length) - if err != nil { - return - } - - _, err = w.Write(sig.HashSuffix[:len(sig.HashSuffix)-6]) - if err != nil { - return - } - - unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) - unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) - unhashedSubpackets[1] = byte(unhashedSubpacketsLen) - serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) - - _, err = w.Write(unhashedSubpackets) - if err != nil { - return - } - _, err = w.Write(sig.HashTag[:]) - if err != nil { - return - } - - switch sig.PubKeyAlgo { - case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - err = writeMPIs(w, sig.RSASignature) - case PubKeyAlgoDSA: - err = writeMPIs(w, sig.DSASigR, sig.DSASigS) - default: - panic("impossible") - } - return -} - -// outputSubpacket represents a subpacket to be marshaled. -type outputSubpacket struct { - hashed bool // true if this subpacket is in the hashed area. - subpacketType signatureSubpacketType - isCritical bool - contents []byte -} - -func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { - creationTime := make([]byte, 4) - creationTime[0] = byte(sig.CreationTime >> 24) - creationTime[1] = byte(sig.CreationTime >> 16) - creationTime[2] = byte(sig.CreationTime >> 8) - creationTime[3] = byte(sig.CreationTime) - subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) - - if sig.IssuerKeyId != nil { - keyId := make([]byte, 8) - binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) - subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) - } - - return -} diff --git a/src/pkg/crypto/openpgp/packet/signature_test.go b/src/pkg/crypto/openpgp/packet/signature_test.go deleted file mode 100644 index c1bbde8b0..000000000 --- a/src/pkg/crypto/openpgp/packet/signature_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "bytes" - "crypto" - "encoding/hex" - "testing" -) - -func TestSignatureRead(t *testing.T) { - packet, err := Read(readerFromHex(signatureDataHex)) - if err != nil { - t.Error(err) - return - } - sig, ok := packet.(*Signature) - if !ok || sig.SigType != SigTypeBinary || sig.PubKeyAlgo != PubKeyAlgoRSA || sig.Hash != crypto.SHA1 { - t.Errorf("failed to parse, got: %#v", packet) - } -} - -func TestSignatureReserialize(t *testing.T) { - packet, _ := Read(readerFromHex(signatureDataHex)) - sig := packet.(*Signature) - out := new(bytes.Buffer) - err := sig.Serialize(out) - if err != nil { - t.Errorf("error reserializing: %s", err) - return - } - - expected, _ := hex.DecodeString(signatureDataHex) - if !bytes.Equal(expected, out.Bytes()) { - t.Errorf("output doesn't match input (got vs expected):\n%s\n%s", hex.Dump(out.Bytes()), hex.Dump(expected)) - } -} - -const signatureDataHex = "c2c05c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e" diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go deleted file mode 100644 index ad4f1d621..000000000 --- a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "bytes" - "crypto/cipher" - "crypto/openpgp/error" - "crypto/openpgp/s2k" - "io" - "os" - "strconv" -) - -// This is the largest session key that we'll support. Since no 512-bit cipher -// has even been seriously used, this is comfortably large. -const maxSessionKeySizeInBytes = 64 - -// SymmetricKeyEncrypted represents a passphrase protected session key. See RFC -// 4880, section 5.3. -type SymmetricKeyEncrypted struct { - CipherFunc CipherFunction - Encrypted bool - Key []byte // Empty unless Encrypted is false. - s2k func(out, in []byte) - encryptedKey []byte -} - -const symmetricKeyEncryptedVersion = 4 - -func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err os.Error) { - // RFC 4880, section 5.3. - var buf [2]byte - _, err = readFull(r, buf[:]) - if err != nil { - return - } - if buf[0] != symmetricKeyEncryptedVersion { - return error.UnsupportedError("SymmetricKeyEncrypted version") - } - ske.CipherFunc = CipherFunction(buf[1]) - - if ske.CipherFunc.KeySize() == 0 { - return error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) - } - - ske.s2k, err = s2k.Parse(r) - if err != nil { - return - } - - encryptedKey := make([]byte, maxSessionKeySizeInBytes) - // The session key may follow. We just have to try and read to find - // out. If it exists then we limit it to maxSessionKeySizeInBytes. - n, err := readFull(r, encryptedKey) - if err != nil && err != io.ErrUnexpectedEOF { - return - } - err = nil - if n != 0 { - if n == maxSessionKeySizeInBytes { - return error.UnsupportedError("oversized encrypted session key") - } - ske.encryptedKey = encryptedKey[:n] - } - - ske.Encrypted = true - - return -} - -// Decrypt attempts to decrypt an encrypted session key. If it returns nil, -// ske.Key will contain the session key. -func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error { - if !ske.Encrypted { - return nil - } - - key := make([]byte, ske.CipherFunc.KeySize()) - ske.s2k(key, passphrase) - - if len(ske.encryptedKey) == 0 { - ske.Key = key - } else { - // the IV is all zeros - iv := make([]byte, ske.CipherFunc.blockSize()) - c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv) - c.XORKeyStream(ske.encryptedKey, ske.encryptedKey) - ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) - if ske.CipherFunc.blockSize() == 0 { - return error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(ske.CipherFunc))) - } - ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) - ske.Key = ske.encryptedKey[1:] - if len(ske.Key)%ske.CipherFunc.blockSize() != 0 { - ske.Key = nil - return error.StructuralError("length of decrypted key not a multiple of block size") - } - } - - ske.Encrypted = false - return nil -} - -// SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. The -// packet contains a random session key, encrypted by a key derived from the -// given passphrase. The session key is returned and must be passed to -// SerializeSymmetricallyEncrypted. -func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []byte, cipherFunc CipherFunction) (key []byte, err os.Error) { - keySize := cipherFunc.KeySize() - if keySize == 0 { - return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) - } - - s2kBuf := new(bytes.Buffer) - keyEncryptingKey := make([]byte, keySize) - // s2k.Serialize salts and stretches the passphrase, and writes the - // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. - err = s2k.Serialize(s2kBuf, keyEncryptingKey, rand, passphrase) - if err != nil { - return - } - s2kBytes := s2kBuf.Bytes() - - packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize - err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) - if err != nil { - return - } - - var buf [2]byte - buf[0] = symmetricKeyEncryptedVersion - buf[1] = byte(cipherFunc) - _, err = w.Write(buf[:]) - if err != nil { - return - } - _, err = w.Write(s2kBytes) - if err != nil { - return - } - - sessionKey := make([]byte, keySize) - _, err = io.ReadFull(rand, sessionKey) - if err != nil { - return - } - iv := make([]byte, cipherFunc.blockSize()) - c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv) - encryptedCipherAndKey := make([]byte, keySize+1) - c.XORKeyStream(encryptedCipherAndKey, buf[1:]) - c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey) - _, err = w.Write(encryptedCipherAndKey) - if err != nil { - return - } - - key = sessionKey - return -} diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go deleted file mode 100644 index 823ec400d..000000000 --- a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "bytes" - "crypto/rand" - "encoding/hex" - "io/ioutil" - "os" - "testing" -) - -func TestSymmetricKeyEncrypted(t *testing.T) { - buf := readerFromHex(symmetricallyEncryptedHex) - packet, err := Read(buf) - if err != nil { - t.Errorf("failed to read SymmetricKeyEncrypted: %s", err) - return - } - ske, ok := packet.(*SymmetricKeyEncrypted) - if !ok { - t.Error("didn't find SymmetricKeyEncrypted packet") - return - } - err = ske.Decrypt([]byte("password")) - if err != nil { - t.Error(err) - return - } - - packet, err = Read(buf) - if err != nil { - t.Errorf("failed to read SymmetricallyEncrypted: %s", err) - return - } - se, ok := packet.(*SymmetricallyEncrypted) - if !ok { - t.Error("didn't find SymmetricallyEncrypted packet") - return - } - r, err := se.Decrypt(ske.CipherFunc, ske.Key) - if err != nil { - t.Error(err) - return - } - - contents, err := ioutil.ReadAll(r) - if err != nil && err != os.EOF { - t.Error(err) - return - } - - expectedContents, _ := hex.DecodeString(symmetricallyEncryptedContentsHex) - if !bytes.Equal(expectedContents, contents) { - t.Errorf("bad contents got:%x want:%x", contents, expectedContents) - } -} - -const symmetricallyEncryptedHex = "8c0d04030302371a0b38d884f02060c91cf97c9973b8e58e028e9501708ccfe618fb92afef7fa2d80ddadd93cf" -const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a" - -func TestSerializeSymmetricKeyEncrypted(t *testing.T) { - buf := bytes.NewBuffer(nil) - passphrase := []byte("testing") - cipherFunc := CipherAES128 - - key, err := SerializeSymmetricKeyEncrypted(buf, rand.Reader, passphrase, cipherFunc) - if err != nil { - t.Errorf("failed to serialize: %s", err) - return - } - - p, err := Read(buf) - if err != nil { - t.Errorf("failed to reparse: %s", err) - return - } - ske, ok := p.(*SymmetricKeyEncrypted) - if !ok { - t.Errorf("parsed a different packet type: %#v", p) - return - } - - if !ske.Encrypted { - t.Errorf("SKE not encrypted but should be") - } - if ske.CipherFunc != cipherFunc { - t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, cipherFunc) - } - err = ske.Decrypt(passphrase) - if err != nil { - t.Errorf("failed to decrypt reparsed SKE: %s", err) - return - } - if !bytes.Equal(key, ske.Key) { - t.Errorf("keys don't match after Decrpyt: %x (original) vs %x (parsed)", key, ske.Key) - } -} diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go deleted file mode 100644 index e33c9f3a0..000000000 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "crypto/cipher" - "crypto/openpgp/error" - "crypto/rand" - "crypto/sha1" - "crypto/subtle" - "hash" - "io" - "os" - "strconv" -) - -// SymmetricallyEncrypted represents a symmetrically encrypted byte string. The -// encrypted contents will consist of more OpenPGP packets. See RFC 4880, -// sections 5.7 and 5.13. -type SymmetricallyEncrypted struct { - MDC bool // true iff this is a type 18 packet and thus has an embedded MAC. - contents io.Reader - prefix []byte -} - -const symmetricallyEncryptedVersion = 1 - -func (se *SymmetricallyEncrypted) parse(r io.Reader) os.Error { - if se.MDC { - // See RFC 4880, section 5.13. - var buf [1]byte - _, err := readFull(r, buf[:]) - if err != nil { - return err - } - if buf[0] != symmetricallyEncryptedVersion { - return error.UnsupportedError("unknown SymmetricallyEncrypted version") - } - } - se.contents = r - return nil -} - -// Decrypt returns a ReadCloser, from which the decrypted contents of the -// packet can be read. An incorrect key can, with high probability, be detected -// immediately and this will result in a KeyIncorrect error being returned. -func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, os.Error) { - keySize := c.KeySize() - if keySize == 0 { - return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) - } - if len(key) != keySize { - return nil, error.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length") - } - - if se.prefix == nil { - se.prefix = make([]byte, c.blockSize()+2) - _, err := readFull(se.contents, se.prefix) - if err != nil { - return nil, err - } - } else if len(se.prefix) != c.blockSize()+2 { - return nil, error.InvalidArgumentError("can't try ciphers with different block lengths") - } - - ocfbResync := cipher.OCFBResync - if se.MDC { - // MDC packets use a different form of OCFB mode. - ocfbResync = cipher.OCFBNoResync - } - - s := cipher.NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) - if s == nil { - return nil, error.KeyIncorrectError - } - - plaintext := cipher.StreamReader{S: s, R: se.contents} - - if se.MDC { - // MDC packets have an embedded hash that we need to check. - h := sha1.New() - h.Write(se.prefix) - return &seMDCReader{in: plaintext, h: h}, nil - } - - // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser. - return seReader{plaintext}, nil -} - -// seReader wraps an io.Reader with a no-op Close method. -type seReader struct { - in io.Reader -} - -func (ser seReader) Read(buf []byte) (int, os.Error) { - return ser.in.Read(buf) -} - -func (ser seReader) Close() os.Error { - return nil -} - -const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size - -// An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold -// of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an -// MDC packet containing a hash of the previous contents which is checked -// against the running hash. See RFC 4880, section 5.13. -type seMDCReader struct { - in io.Reader - h hash.Hash - trailer [mdcTrailerSize]byte - scratch [mdcTrailerSize]byte - trailerUsed int - error bool - eof bool -} - -func (ser *seMDCReader) Read(buf []byte) (n int, err os.Error) { - if ser.error { - err = io.ErrUnexpectedEOF - return - } - if ser.eof { - err = os.EOF - return - } - - // If we haven't yet filled the trailer buffer then we must do that - // first. - for ser.trailerUsed < mdcTrailerSize { - n, err = ser.in.Read(ser.trailer[ser.trailerUsed:]) - ser.trailerUsed += n - if err == os.EOF { - if ser.trailerUsed != mdcTrailerSize { - n = 0 - err = io.ErrUnexpectedEOF - ser.error = true - return - } - ser.eof = true - n = 0 - return - } - - if err != nil { - n = 0 - return - } - } - - // If it's a short read then we read into a temporary buffer and shift - // the data into the caller's buffer. - if len(buf) <= mdcTrailerSize { - n, err = readFull(ser.in, ser.scratch[:len(buf)]) - copy(buf, ser.trailer[:n]) - ser.h.Write(buf[:n]) - copy(ser.trailer[:], ser.trailer[n:]) - copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:]) - if n < len(buf) { - ser.eof = true - err = os.EOF - } - return - } - - n, err = ser.in.Read(buf[mdcTrailerSize:]) - copy(buf, ser.trailer[:]) - ser.h.Write(buf[:n]) - copy(ser.trailer[:], buf[n:]) - - if err == os.EOF { - ser.eof = true - } - return -} - -// This is a new-format packet tag byte for a type 19 (MDC) packet. -const mdcPacketTagByte = byte(0x80) | 0x40 | 19 - -func (ser *seMDCReader) Close() os.Error { - if ser.error { - return error.SignatureError("error during reading") - } - - for !ser.eof { - // We haven't seen EOF so we need to read to the end - var buf [1024]byte - _, err := ser.Read(buf[:]) - if err == os.EOF { - break - } - if err != nil { - return error.SignatureError("error during reading") - } - } - - if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size { - return error.SignatureError("MDC packet not found") - } - ser.h.Write(ser.trailer[:2]) - - final := ser.h.Sum() - if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 { - return error.SignatureError("hash mismatch") - } - return nil -} - -// An seMDCWriter writes through to an io.WriteCloser while maintains a running -// hash of the data written. On close, it emits an MDC packet containing the -// running hash. -type seMDCWriter struct { - w io.WriteCloser - h hash.Hash -} - -func (w *seMDCWriter) Write(buf []byte) (n int, err os.Error) { - w.h.Write(buf) - return w.w.Write(buf) -} - -func (w *seMDCWriter) Close() (err os.Error) { - var buf [mdcTrailerSize]byte - - buf[0] = mdcPacketTagByte - buf[1] = sha1.Size - w.h.Write(buf[:2]) - digest := w.h.Sum() - copy(buf[2:], digest) - - _, err = w.w.Write(buf[:]) - if err != nil { - return - } - return w.w.Close() -} - -// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. -type noOpCloser struct { - w io.Writer -} - -func (c noOpCloser) Write(data []byte) (n int, err os.Error) { - return c.w.Write(data) -} - -func (c noOpCloser) Close() os.Error { - return nil -} - -// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet -// to w and returns a WriteCloser to which the to-be-encrypted packets can be -// written. -func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte) (contents io.WriteCloser, err os.Error) { - if c.KeySize() != len(key) { - return nil, error.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") - } - writeCloser := noOpCloser{w} - ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC) - if err != nil { - return - } - - _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion}) - if err != nil { - return - } - - block := c.new(key) - blockSize := block.BlockSize() - iv := make([]byte, blockSize) - _, err = rand.Reader.Read(iv) - if err != nil { - return - } - s, prefix := cipher.NewOCFBEncrypter(block, iv, cipher.OCFBNoResync) - _, err = ciphertext.Write(prefix) - if err != nil { - return - } - plaintext := cipher.StreamWriter{S: s, W: ciphertext} - - h := sha1.New() - h.Write(iv) - h.Write(iv[blockSize-2:]) - contents = &seMDCWriter{w: plaintext, h: h} - return -} diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go deleted file mode 100644 index 1054fc2f9..000000000 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "bytes" - "crypto/openpgp/error" - "crypto/sha1" - "encoding/hex" - "io" - "io/ioutil" - "os" - "testing" -) - -// TestReader wraps a []byte and returns reads of a specific length. -type testReader struct { - data []byte - stride int -} - -func (t *testReader) Read(buf []byte) (n int, err os.Error) { - n = t.stride - if n > len(t.data) { - n = len(t.data) - } - if n > len(buf) { - n = len(buf) - } - copy(buf, t.data) - t.data = t.data[n:] - if len(t.data) == 0 { - err = os.EOF - } - return -} - -func testMDCReader(t *testing.T) { - mdcPlaintext, _ := hex.DecodeString(mdcPlaintextHex) - - for stride := 1; stride < len(mdcPlaintext)/2; stride++ { - r := &testReader{data: mdcPlaintext, stride: stride} - mdcReader := &seMDCReader{in: r, h: sha1.New()} - body, err := ioutil.ReadAll(mdcReader) - if err != nil { - t.Errorf("stride: %d, error: %s", stride, err) - continue - } - if !bytes.Equal(body, mdcPlaintext[:len(mdcPlaintext)-22]) { - t.Errorf("stride: %d: bad contents %x", stride, body) - continue - } - - err = mdcReader.Close() - if err != nil { - t.Errorf("stride: %d, error on Close: %s", stride, err) - } - } - - mdcPlaintext[15] ^= 80 - - r := &testReader{data: mdcPlaintext, stride: 2} - mdcReader := &seMDCReader{in: r, h: sha1.New()} - _, err := ioutil.ReadAll(mdcReader) - if err != nil { - t.Errorf("corruption test, error: %s", err) - return - } - err = mdcReader.Close() - if err == nil { - t.Error("corruption: no error") - } else if _, ok := err.(*error.SignatureError); !ok { - t.Errorf("corruption: expected SignatureError, got: %s", err) - } -} - -const mdcPlaintextHex = "a302789c3b2d93c4e0eb9aba22283539b3203335af44a134afb800c849cb4c4de10200aff40b45d31432c80cb384299a0655966d6939dfdeed1dddf980" - -func TestSerialize(t *testing.T) { - buf := bytes.NewBuffer(nil) - c := CipherAES128 - key := make([]byte, c.KeySize()) - - w, err := SerializeSymmetricallyEncrypted(buf, c, key) - if err != nil { - t.Errorf("error from SerializeSymmetricallyEncrypted: %s", err) - return - } - - contents := []byte("hello world\n") - - w.Write(contents) - w.Close() - - p, err := Read(buf) - if err != nil { - t.Errorf("error from Read: %s", err) - return - } - - se, ok := p.(*SymmetricallyEncrypted) - if !ok { - t.Errorf("didn't read a *SymmetricallyEncrypted") - return - } - - r, err := se.Decrypt(c, key) - if err != nil { - t.Errorf("error from Decrypt: %s", err) - return - } - - contentsCopy := bytes.NewBuffer(nil) - _, err = io.Copy(contentsCopy, r) - if err != nil { - t.Errorf("error from io.Copy: %s", err) - return - } - if !bytes.Equal(contentsCopy.Bytes(), contents) { - t.Errorf("contents not equal got: %x want: %x", contentsCopy.Bytes(), contents) - } -} diff --git a/src/pkg/crypto/openpgp/packet/userid.go b/src/pkg/crypto/openpgp/packet/userid.go deleted file mode 100644 index 0580ba3ed..000000000 --- a/src/pkg/crypto/openpgp/packet/userid.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "io" - "io/ioutil" - "os" - "strings" -) - -// UserId contains text that is intended to represent the name and email -// address of the key holder. See RFC 4880, section 5.11. By convention, this -// takes the form "Full Name (Comment) " -type UserId struct { - Id string // By convention, this takes the form "Full Name (Comment) " which is split out in the fields below. - - Name, Comment, Email string -} - -func hasInvalidCharacters(s string) bool { - for _, c := range s { - switch c { - case '(', ')', '<', '>', 0: - return true - } - } - return false -} - -// NewUserId returns a UserId or nil if any of the arguments contain invalid -// characters. The invalid characters are '\x00', '(', ')', '<' and '>' -func NewUserId(name, comment, email string) *UserId { - // RFC 4880 doesn't deal with the structure of userid strings; the - // name, comment and email form is just a convention. However, there's - // no convention about escaping the metacharacters and GPG just refuses - // to create user ids where, say, the name contains a '('. We mirror - // this behaviour. - - if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) { - return nil - } - - uid := new(UserId) - uid.Name, uid.Comment, uid.Email = name, comment, email - uid.Id = name - if len(comment) > 0 { - if len(uid.Id) > 0 { - uid.Id += " " - } - uid.Id += "(" - uid.Id += comment - uid.Id += ")" - } - if len(email) > 0 { - if len(uid.Id) > 0 { - uid.Id += " " - } - uid.Id += "<" - uid.Id += email - uid.Id += ">" - } - return uid -} - -func (uid *UserId) parse(r io.Reader) (err os.Error) { - // RFC 4880, section 5.11 - b, err := ioutil.ReadAll(r) - if err != nil { - return - } - uid.Id = string(b) - uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id) - return -} - -// Serialize marshals uid to w in the form of an OpenPGP packet, including -// header. -func (uid *UserId) Serialize(w io.Writer) os.Error { - err := serializeHeader(w, packetTypeUserId, len(uid.Id)) - if err != nil { - return err - } - _, err = w.Write([]byte(uid.Id)) - return err -} - -// parseUserId extracts the name, comment and email from a user id string that -// is formatted as "Full Name (Comment) ". -func parseUserId(id string) (name, comment, email string) { - var n, c, e struct { - start, end int - } - var state int - - for offset, rune := range id { - switch state { - case 0: - // Entering name - n.start = offset - state = 1 - fallthrough - case 1: - // In name - if rune == '(' { - state = 2 - n.end = offset - } else if rune == '<' { - state = 5 - n.end = offset - } - case 2: - // Entering comment - c.start = offset - state = 3 - fallthrough - case 3: - // In comment - if rune == ')' { - state = 4 - c.end = offset - } - case 4: - // Between comment and email - if rune == '<' { - state = 5 - } - case 5: - // Entering email - e.start = offset - state = 6 - fallthrough - case 6: - // In email - if rune == '>' { - state = 7 - e.end = offset - } - default: - // After email - } - } - switch state { - case 1: - // ended in the name - n.end = len(id) - case 3: - // ended in comment - c.end = len(id) - case 6: - // ended in email - e.end = len(id) - } - - name = strings.TrimSpace(id[n.start:n.end]) - comment = strings.TrimSpace(id[c.start:c.end]) - email = strings.TrimSpace(id[e.start:e.end]) - return -} diff --git a/src/pkg/crypto/openpgp/packet/userid_test.go b/src/pkg/crypto/openpgp/packet/userid_test.go deleted file mode 100644 index 296819389..000000000 --- a/src/pkg/crypto/openpgp/packet/userid_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package packet - -import ( - "testing" -) - -var userIdTests = []struct { - id string - name, comment, email string -}{ - {"", "", "", ""}, - {"John Smith", "John Smith", "", ""}, - {"John Smith ()", "John Smith", "", ""}, - {"John Smith () <>", "John Smith", "", ""}, - {"(comment", "", "comment", ""}, - {"(comment)", "", "comment", ""}, - {" sdfk", "", "", "email"}, - {" John Smith ( Comment ) asdkflj < email > lksdfj", "John Smith", "Comment", "email"}, - {" John Smith < email > lksdfj", "John Smith", "", "email"}, - {"("}, - {"foo", "bar", "", "foo (bar)"}, - {"foo", "", "baz", "foo "}, - {"", "bar", "baz", "(bar) "}, - {"foo", "bar", "baz", "foo (bar) "}, -} - -func TestNewUserId(t *testing.T) { - for i, test := range newUserIdTests { - uid := NewUserId(test.name, test.comment, test.email) - if uid == nil { - t.Errorf("#%d: returned nil", i) - continue - } - if uid.Id != test.id { - t.Errorf("#%d: got '%s', want '%s'", i, uid.Id, test.id) - } - } -} - -var invalidNewUserIdTests = []struct { - name, comment, email string -}{ - {"foo(", "", ""}, - {"foo<", "", ""}, - {"", "bar)", ""}, - {"", "bar<", ""}, - {"", "", "baz>"}, - {"", "", "baz)"}, - {"", "", "baz\x00"}, -} - -func TestNewUserIdWithInvalidInput(t *testing.T) { - for i, test := range invalidNewUserIdTests { - if uid := NewUserId(test.name, test.comment, test.email); uid != nil { - t.Errorf("#%d: returned non-nil value: %#v", i, uid) - } - } -} diff --git a/src/pkg/crypto/openpgp/read.go b/src/pkg/crypto/openpgp/read.go deleted file mode 100644 index d95f613c6..000000000 --- a/src/pkg/crypto/openpgp/read.go +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package openpgp implements high level operations on OpenPGP messages. -package openpgp - -import ( - "crypto" - "crypto/openpgp/armor" - "crypto/openpgp/error" - "crypto/openpgp/packet" - _ "crypto/sha256" - "hash" - "io" - "os" - "strconv" -) - -// SignatureType is the armor type for a PGP signature. -var SignatureType = "PGP SIGNATURE" - -// readArmored reads an armored block with the given type. -func readArmored(r io.Reader, expectedType string) (body io.Reader, err os.Error) { - block, err := armor.Decode(r) - if err != nil { - return - } - - if block.Type != expectedType { - return nil, error.InvalidArgumentError("expected '" + expectedType + "', got: " + block.Type) - } - - return block.Body, nil -} - -// MessageDetails contains the result of parsing an OpenPGP encrypted and/or -// signed message. -type MessageDetails struct { - IsEncrypted bool // true if the message was encrypted. - EncryptedToKeyIds []uint64 // the list of recipient key ids. - IsSymmetricallyEncrypted bool // true if a passphrase could have decrypted the message. - DecryptedWith Key // the private key used to decrypt the message, if any. - IsSigned bool // true if the message is signed. - SignedByKeyId uint64 // the key id of the signer, if any. - SignedBy *Key // the key of the signer, if available. - LiteralData *packet.LiteralData // the metadata of the contents - UnverifiedBody io.Reader // the contents of the message. - - // If IsSigned is true and SignedBy is non-zero then the signature will - // be verified as UnverifiedBody is read. The signature cannot be - // checked until the whole of UnverifiedBody is read so UnverifiedBody - // must be consumed until EOF before the data can trusted. Even if a - // message isn't signed (or the signer is unknown) the data may contain - // an authentication code that is only checked once UnverifiedBody has - // been consumed. Once EOF has been seen, the following fields are - // valid. (An authentication code failure is reported as a - // SignatureError error when reading from UnverifiedBody.) - SignatureError os.Error // nil if the signature is good. - Signature *packet.Signature // the signature packet itself. - - decrypted io.ReadCloser -} - -// A PromptFunction is used as a callback by functions that may need to decrypt -// a private key, or prompt for a passphrase. It is called with a list of -// acceptable, encrypted private keys and a boolean that indicates whether a -// passphrase is usable. It should either decrypt a private key or return a -// passphrase to try. If the decrypted private key or given passphrase isn't -// correct, the function will be called again, forever. Any error returned will -// be passed up. -type PromptFunction func(keys []Key, symmetric bool) ([]byte, os.Error) - -// A keyEnvelopePair is used to store a private key with the envelope that -// contains a symmetric key, encrypted with that key. -type keyEnvelopePair struct { - key Key - encryptedKey *packet.EncryptedKey -} - -// ReadMessage parses an OpenPGP message that may be signed and/or encrypted. -// The given KeyRing should contain both public keys (for signature -// verification) and, possibly encrypted, private keys for decrypting. -func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction) (md *MessageDetails, err os.Error) { - var p packet.Packet - - var symKeys []*packet.SymmetricKeyEncrypted - var pubKeys []keyEnvelopePair - var se *packet.SymmetricallyEncrypted - - packets := packet.NewReader(r) - md = new(MessageDetails) - md.IsEncrypted = true - - // The message, if encrypted, starts with a number of packets - // containing an encrypted decryption key. The decryption key is either - // encrypted to a public key, or with a passphrase. This loop - // collects these packets. -ParsePackets: - for { - p, err = packets.Next() - if err != nil { - return nil, err - } - switch p := p.(type) { - case *packet.SymmetricKeyEncrypted: - // This packet contains the decryption key encrypted with a passphrase. - md.IsSymmetricallyEncrypted = true - symKeys = append(symKeys, p) - case *packet.EncryptedKey: - // This packet contains the decryption key encrypted to a public key. - md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) - switch p.Algo { - case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal: - break - default: - continue - } - var keys []Key - if p.KeyId == 0 { - keys = keyring.DecryptionKeys() - } else { - keys = keyring.KeysById(p.KeyId) - } - for _, k := range keys { - pubKeys = append(pubKeys, keyEnvelopePair{k, p}) - } - case *packet.SymmetricallyEncrypted: - se = p - break ParsePackets - case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature: - // This message isn't encrypted. - if len(symKeys) != 0 || len(pubKeys) != 0 { - return nil, error.StructuralError("key material not followed by encrypted message") - } - packets.Unread(p) - return readSignedMessage(packets, nil, keyring) - } - } - - var candidates []Key - var decrypted io.ReadCloser - - // Now that we have the list of encrypted keys we need to decrypt at - // least one of them or, if we cannot, we need to call the prompt - // function so that it can decrypt a key or give us a passphrase. -FindKey: - for { - // See if any of the keys already have a private key available - candidates = candidates[:0] - candidateFingerprints := make(map[string]bool) - - for _, pk := range pubKeys { - if pk.key.PrivateKey == nil { - continue - } - if !pk.key.PrivateKey.Encrypted { - if len(pk.encryptedKey.Key) == 0 { - pk.encryptedKey.Decrypt(pk.key.PrivateKey) - } - if len(pk.encryptedKey.Key) == 0 { - continue - } - decrypted, err = se.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key) - if err != nil && err != error.KeyIncorrectError { - return nil, err - } - if decrypted != nil { - md.DecryptedWith = pk.key - break FindKey - } - } else { - fpr := string(pk.key.PublicKey.Fingerprint[:]) - if v := candidateFingerprints[fpr]; v { - continue - } - candidates = append(candidates, pk.key) - candidateFingerprints[fpr] = true - } - } - - if len(candidates) == 0 && len(symKeys) == 0 { - return nil, error.KeyIncorrectError - } - - if prompt == nil { - return nil, error.KeyIncorrectError - } - - passphrase, err := prompt(candidates, len(symKeys) != 0) - if err != nil { - return nil, err - } - - // Try the symmetric passphrase first - if len(symKeys) != 0 && passphrase != nil { - for _, s := range symKeys { - err = s.Decrypt(passphrase) - if err == nil && !s.Encrypted { - decrypted, err = se.Decrypt(s.CipherFunc, s.Key) - if err != nil && err != error.KeyIncorrectError { - return nil, err - } - if decrypted != nil { - break FindKey - } - } - - } - } - } - - md.decrypted = decrypted - packets.Push(decrypted) - return readSignedMessage(packets, md, keyring) -} - -// readSignedMessage reads a possibly signed message if mdin is non-zero then -// that structure is updated and returned. Otherwise a fresh MessageDetails is -// used. -func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing) (md *MessageDetails, err os.Error) { - if mdin == nil { - mdin = new(MessageDetails) - } - md = mdin - - var p packet.Packet - var h hash.Hash - var wrappedHash hash.Hash -FindLiteralData: - for { - p, err = packets.Next() - if err != nil { - return nil, err - } - switch p := p.(type) { - case *packet.Compressed: - packets.Push(p.Body) - case *packet.OnePassSignature: - if !p.IsLast { - return nil, error.UnsupportedError("nested signatures") - } - - h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) - if err != nil { - md = nil - return - } - - md.IsSigned = true - md.SignedByKeyId = p.KeyId - keys := keyring.KeysById(p.KeyId) - for i, key := range keys { - if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { - continue - } - md.SignedBy = &keys[i] - break - } - case *packet.LiteralData: - md.LiteralData = p - break FindLiteralData - } - } - - if md.SignedBy != nil { - md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md} - } else if md.decrypted != nil { - md.UnverifiedBody = checkReader{md} - } else { - md.UnverifiedBody = md.LiteralData.Body - } - - return md, nil -} - -// hashForSignature returns a pair of hashes that can be used to verify a -// signature. The signature may specify that the contents of the signed message -// should be preprocessed (i.e. to normalize line endings). Thus this function -// returns two hashes. The second should be used to hash the message itself and -// performs any needed preprocessing. -func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, os.Error) { - h := hashId.New() - if h == nil { - return nil, nil, error.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId))) - } - - switch sigType { - case packet.SigTypeBinary: - return h, h, nil - case packet.SigTypeText: - return h, NewCanonicalTextHash(h), nil - } - - return nil, nil, error.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType))) -} - -// checkReader wraps an io.Reader from a LiteralData packet. When it sees EOF -// it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger -// MDC checks. -type checkReader struct { - md *MessageDetails -} - -func (cr checkReader) Read(buf []byte) (n int, err os.Error) { - n, err = cr.md.LiteralData.Body.Read(buf) - if err == os.EOF { - mdcErr := cr.md.decrypted.Close() - if mdcErr != nil { - err = mdcErr - } - } - return -} - -// signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes -// the data as it is read. When it sees an EOF from the underlying io.Reader -// it parses and checks a trailing Signature packet and triggers any MDC checks. -type signatureCheckReader struct { - packets *packet.Reader - h, wrappedHash hash.Hash - md *MessageDetails -} - -func (scr *signatureCheckReader) Read(buf []byte) (n int, err os.Error) { - n, err = scr.md.LiteralData.Body.Read(buf) - scr.wrappedHash.Write(buf[:n]) - if err == os.EOF { - var p packet.Packet - p, scr.md.SignatureError = scr.packets.Next() - if scr.md.SignatureError != nil { - return - } - - var ok bool - if scr.md.Signature, ok = p.(*packet.Signature); !ok { - scr.md.SignatureError = error.StructuralError("LiteralData not followed by Signature") - return - } - - scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) - - // The SymmetricallyEncrypted packet, if any, might have an - // unsigned hash of its own. In order to check this we need to - // close that Reader. - if scr.md.decrypted != nil { - mdcErr := scr.md.decrypted.Close() - if mdcErr != nil { - err = mdcErr - } - } - } - return -} - -// CheckDetachedSignature takes a signed file and a detached signature and -// returns the signer if the signature is valid. If the signer isn't know, -// UnknownIssuerError is returned. -func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err os.Error) { - p, err := packet.Read(signature) - if err != nil { - return - } - - sig, ok := p.(*packet.Signature) - if !ok { - return nil, error.StructuralError("non signature packet found") - } - - if sig.IssuerKeyId == nil { - return nil, error.StructuralError("signature doesn't have an issuer") - } - - keys := keyring.KeysById(*sig.IssuerKeyId) - if len(keys) == 0 { - return nil, error.UnknownIssuerError - } - - h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) - if err != nil { - return - } - - _, err = io.Copy(wrappedHash, signed) - if err != nil && err != os.EOF { - return - } - - for _, key := range keys { - if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { - continue - } - err = key.PublicKey.VerifySignature(h, sig) - if err == nil { - return key.Entity, nil - } - } - - if err != nil { - return - } - - return nil, error.UnknownIssuerError -} - -// CheckArmoredDetachedSignature performs the same actions as -// CheckDetachedSignature but expects the signature to be armored. -func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err os.Error) { - body, err := readArmored(signature, SignatureType) - if err != nil { - return - } - - return CheckDetachedSignature(keyring, signed, body) -} diff --git a/src/pkg/crypto/openpgp/read_test.go b/src/pkg/crypto/openpgp/read_test.go deleted file mode 100644 index 4dc290ef2..000000000 --- a/src/pkg/crypto/openpgp/read_test.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package openpgp - -import ( - "bytes" - "crypto/openpgp/error" - "encoding/hex" - "io" - "io/ioutil" - "os" - "testing" -) - -func readerFromHex(s string) io.Reader { - data, err := hex.DecodeString(s) - if err != nil { - panic("readerFromHex: bad input") - } - return bytes.NewBuffer(data) -} - -func TestReadKeyRing(t *testing.T) { - kring, err := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - if err != nil { - t.Error(err) - return - } - if len(kring) != 2 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB || uint32(kring[1].PrimaryKey.KeyId) != 0x1E35246B { - t.Errorf("bad keyring: %#v", kring) - } -} - -func TestRereadKeyRing(t *testing.T) { - kring, err := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - if err != nil { - t.Errorf("error in initial parse: %s", err) - return - } - out := new(bytes.Buffer) - err = kring[0].Serialize(out) - if err != nil { - t.Errorf("error in serialization: %s", err) - return - } - kring, err = ReadKeyRing(out) - if err != nil { - t.Errorf("error in second parse: %s", err) - return - } - - if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB { - t.Errorf("bad keyring: %#v", kring) - } -} - -func TestReadPrivateKeyRing(t *testing.T) { - kring, err := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - if err != nil { - t.Error(err) - return - } - if len(kring) != 2 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB || uint32(kring[1].PrimaryKey.KeyId) != 0x1E35246B || kring[0].PrimaryKey == nil { - t.Errorf("bad keyring: %#v", kring) - } -} - -func TestReadDSAKey(t *testing.T) { - kring, err := ReadKeyRing(readerFromHex(dsaTestKeyHex)) - if err != nil { - t.Error(err) - return - } - if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0x0CCC0360 { - t.Errorf("bad parse: %#v", kring) - } -} - -func TestGetKeyById(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - - keys := kring.KeysById(0xa34d7e18c20c31bb) - if len(keys) != 1 || keys[0].Entity != kring[0] { - t.Errorf("bad result for 0xa34d7e18c20c31bb: %#v", keys) - } - - keys = kring.KeysById(0xfd94408d4543314f) - if len(keys) != 1 || keys[0].Entity != kring[0] { - t.Errorf("bad result for 0xa34d7e18c20c31bb: %#v", keys) - } -} - -func checkSignedMessage(t *testing.T, signedHex, expected string) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - - md, err := ReadMessage(readerFromHex(signedHex), kring, nil) - if err != nil { - t.Error(err) - return - } - - if !md.IsSigned || md.SignedByKeyId != 0xa34d7e18c20c31bb || md.SignedBy == nil || md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) != 0 || md.IsSymmetricallyEncrypted { - t.Errorf("bad MessageDetails: %#v", md) - } - - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("error reading UnverifiedBody: %s", err) - } - if string(contents) != expected { - t.Errorf("bad UnverifiedBody got:%s want:%s", string(contents), expected) - } - if md.SignatureError != nil || md.Signature == nil { - t.Errorf("failed to validate: %s", md.SignatureError) - } -} - -func TestSignedMessage(t *testing.T) { - checkSignedMessage(t, signedMessageHex, signedInput) -} - -func TestTextSignedMessage(t *testing.T) { - checkSignedMessage(t, signedTextMessageHex, signedTextInput) -} - -var signedEncryptedMessageTests = []struct { - keyRingHex string - messageHex string - signedByKeyId uint64 - encryptedToKeyId uint64 -}{ - { - testKeys1And2PrivateHex, - signedEncryptedMessageHex, - 0xa34d7e18c20c31bb, - 0x2a67d68660df41c7, - }, - { - dsaElGamalTestKeysHex, - signedEncryptedMessage2Hex, - 0x33af447ccd759b09, - 0xcf6a7abcd43e3673, - }, -} - -func TestSignedEncryptedMessage(t *testing.T) { - for i, test := range signedEncryptedMessageTests { - expected := "Signed and encrypted message\n" - kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) - prompt := func(keys []Key, symmetric bool) ([]byte, os.Error) { - if symmetric { - t.Errorf("prompt: message was marked as symmetrically encrypted") - return nil, error.KeyIncorrectError - } - - if len(keys) == 0 { - t.Error("prompt: no keys requested") - return nil, error.KeyIncorrectError - } - - err := keys[0].PrivateKey.Decrypt([]byte("passphrase")) - if err != nil { - t.Errorf("prompt: error decrypting key: %s", err) - return nil, error.KeyIncorrectError - } - - return nil, nil - } - - md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt) - if err != nil { - t.Errorf("#%d: error reading message: %s", i, err) - return - } - - if !md.IsSigned || md.SignedByKeyId != test.signedByKeyId || md.SignedBy == nil || !md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) == 0 || md.EncryptedToKeyIds[0] != test.encryptedToKeyId { - t.Errorf("#%d: bad MessageDetails: %#v", i, md) - } - - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("#%d: error reading UnverifiedBody: %s", i, err) - } - if string(contents) != expected { - t.Errorf("#%d: bad UnverifiedBody got:%s want:%s", i, string(contents), expected) - } - - if md.SignatureError != nil || md.Signature == nil { - t.Errorf("#%d: failed to validate: %s", i, md.SignatureError) - } - } -} - -func TestUnspecifiedRecipient(t *testing.T) { - expected := "Recipient unspecified\n" - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - - md, err := ReadMessage(readerFromHex(recipientUnspecifiedHex), kring, nil) - if err != nil { - t.Errorf("error reading message: %s", err) - return - } - - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("error reading UnverifiedBody: %s", err) - } - if string(contents) != expected { - t.Errorf("bad UnverifiedBody got:%s want:%s", string(contents), expected) - } -} - -func TestSymmetricallyEncrypted(t *testing.T) { - expected := "Symmetrically encrypted.\n" - - prompt := func(keys []Key, symmetric bool) ([]byte, os.Error) { - if len(keys) != 0 { - t.Errorf("prompt: len(keys) = %d (want 0)", len(keys)) - } - - if !symmetric { - t.Errorf("symmetric is not set") - } - - return []byte("password"), nil - } - - md, err := ReadMessage(readerFromHex(symmetricallyEncryptedCompressedHex), nil, prompt) - if err != nil { - t.Errorf("ReadMessage: %s", err) - return - } - - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("ReadAll: %s", err) - } - - expectedCreationTime := uint32(1295992998) - if md.LiteralData.Time != expectedCreationTime { - t.Errorf("LiteralData.Time is %d, want %d", md.LiteralData.Time, expectedCreationTime) - } - - if string(contents) != expected { - t.Errorf("contents got: %s want: %s", string(contents), expected) - } -} - -func testDetachedSignature(t *testing.T, kring KeyRing, signature io.Reader, sigInput, tag string, expectedSignerKeyId uint64) { - signed := bytes.NewBufferString(sigInput) - signer, err := CheckDetachedSignature(kring, signed, signature) - if err != nil { - t.Errorf("%s: signature error: %s", tag, err) - return - } - if signer == nil { - t.Errorf("%s: signer is nil", tag) - return - } - if signer.PrimaryKey.KeyId != expectedSignerKeyId { - t.Errorf("%s: wrong signer got:%x want:%x", tag, signer.PrimaryKey.KeyId, expectedSignerKeyId) - } -} - -func TestDetachedSignature(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) - testDetachedSignature(t, kring, readerFromHex(detachedSignatureHex), signedInput, "binary", testKey1KeyId) - testDetachedSignature(t, kring, readerFromHex(detachedSignatureTextHex), signedInput, "text", testKey1KeyId) -} - -func TestDetachedSignatureDSA(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyHex)) - testDetachedSignature(t, kring, readerFromHex(detachedSignatureDSAHex), signedInput, "binary", testKey3KeyId) -} - -func TestReadingArmoredPrivateKey(t *testing.T) { - el, err := ReadArmoredKeyRing(bytes.NewBufferString(armoredPrivateKeyBlock)) - if err != nil { - t.Error(err) - } - if len(el) != 1 { - t.Errorf("got %d entities, wanted 1\n", len(el)) - } -} - -func TestNoArmoredData(t *testing.T) { - _, err := ReadArmoredKeyRing(bytes.NewBufferString("foo")) - if _, ok := err.(error.InvalidArgumentError); !ok { - t.Errorf("error was not an InvalidArgumentError: %s", err) - } -} - -const testKey1KeyId = 0xA34D7E18C20C31BB -const testKey3KeyId = 0x338934250CCC0360 - -const signedInput = "Signed message\nline 2\nline 3\n" -const signedTextInput = "Signed message\r\nline 2\r\nline 3\r\n" - -const recipientUnspecifiedHex = "848c0300000000000000000103ff62d4d578d03cf40c3da998dfe216c074fa6ddec5e31c197c9666ba292830d91d18716a80f699f9d897389a90e6d62d0238f5f07a5248073c0f24920e4bc4a30c2d17ee4e0cae7c3d4aaa4e8dced50e3010a80ee692175fa0385f62ecca4b56ee6e9980aa3ec51b61b077096ac9e800edaf161268593eedb6cc7027ff5cb32745d250010d407a6221ae22ef18469b444f2822478c4d190b24d36371a95cb40087cdd42d9399c3d06a53c0673349bfb607927f20d1e122bde1e2bf3aa6cae6edf489629bcaa0689539ae3b718914d88ededc3b" - -const detachedSignatureHex = "889c04000102000605024d449cd1000a0910a34d7e18c20c31bb167603ff57718d09f28a519fdc7b5a68b6a3336da04df85e38c5cd5d5bd2092fa4629848a33d85b1729402a2aab39c3ac19f9d573f773cc62c264dc924c067a79dfd8a863ae06c7c8686120760749f5fd9b1e03a64d20a7df3446ddc8f0aeadeaeba7cbaee5c1e366d65b6a0c6cc749bcb912d2f15013f812795c2e29eb7f7b77f39ce77" - -const detachedSignatureTextHex = "889c04010102000605024d449d21000a0910a34d7e18c20c31bbc8c60400a24fbef7342603a41cb1165767bd18985d015fb72fe05db42db36cfb2f1d455967f1e491194fbf6cf88146222b23bf6ffbd50d17598d976a0417d3192ff9cc0034fd00f287b02e90418bbefe609484b09231e4e7a5f3562e199bf39909ab5276c4d37382fe088f6b5c3426fc1052865da8b3ab158672d58b6264b10823dc4b39" - -const detachedSignatureDSAHex = "884604001102000605024d6c4eac000a0910338934250ccc0360f18d00a087d743d6405ed7b87755476629600b8b694a39e900a0abff8126f46faf1547c1743c37b21b4ea15b8f83" - -const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b0020003b88d044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f0011010001889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab0020003988d044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b0020003b88d044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020003" - -const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd00110100010003ff4d91393b9a8e3430b14d6209df42f98dc927425b881f1209f319220841273a802a97c7bdb8b3a7740b3ab5866c4d1d308ad0d3a79bd1e883aacf1ac92dfe720285d10d08752a7efe3c609b1d00f17f2805b217be53999a7da7e493bfc3e9618fd17018991b8128aea70a05dbce30e4fbe626aa45775fa255dd9177aabf4df7cf0200c1ded12566e4bc2bb590455e5becfb2e2c9796482270a943343a7835de41080582c2be3caf5981aa838140e97afa40ad652a0b544f83eb1833b0957dce26e47b0200eacd6046741e9ce2ec5beb6fb5e6335457844fb09477f83b050a96be7da043e17f3a9523567ed40e7a521f818813a8b8a72209f1442844843ccc7eb9805442570200bdafe0438d97ac36e773c7162028d65844c4d463e2420aa2228c6e50dc2743c3d6c72d0d782a5173fe7be2169c8a9f4ef8a7cf3e37165e8c61b89c346cdc6c1799d2b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b00200009d01d8044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f00110100010003fd17a7490c22a79c59281fb7b20f5e6553ec0c1637ae382e8adaea295f50241037f8997cf42c1ce26417e015091451b15424b2c59eb8d4161b0975630408e394d3b00f88d4b4e18e2cc85e8251d4753a27c639c83f5ad4a571c4f19d7cd460b9b73c25ade730c99df09637bd173d8e3e981ac64432078263bb6dc30d3e974150dd0200d0ee05be3d4604d2146fb0457f31ba17c057560785aa804e8ca5530a7cd81d3440d0f4ba6851efcfd3954b7e68908fc0ba47f7ac37bf559c6c168b70d3a7c8cd0200da1c677c4bce06a068070f2b3733b0a714e88d62aa3f9a26c6f5216d48d5c2b5624144f3807c0df30be66b3268eeeca4df1fbded58faf49fc95dc3c35f134f8b01fd1396b6c0fc1b6c4f0eb8f5e44b8eace1e6073e20d0b8bc5385f86f1cf3f050f66af789f3ef1fc107b7f4421e19e0349c730c68f0a226981f4e889054fdb4dc149e8e889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab00200009501fe044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001fe030302e9030f3c783e14856063f16938530e148bc57a7aa3f3e4f90df9dceccdc779bc0835e1ad3d006e4a8d7b36d08b8e0de5a0d947254ecfbd22037e6572b426bcfdc517796b224b0036ff90bc574b5509bede85512f2eefb520fb4b02aa523ba739bff424a6fe81c5041f253f8d757e69a503d3563a104d0d49e9e890b9d0c26f96b55b743883b472caa7050c4acfd4a21f875bdf1258d88bd61224d303dc9df77f743137d51e6d5246b88c406780528fd9a3e15bab5452e5b93970d9dcc79f48b38651b9f15bfbcf6da452837e9cc70683d1bdca94507870f743e4ad902005812488dd342f836e72869afd00ce1850eea4cfa53ce10e3608e13d3c149394ee3cbd0e23d018fcbcb6e2ec5a1a22972d1d462ca05355d0d290dd2751e550d5efb38c6c89686344df64852bf4ff86638708f644e8ec6bd4af9b50d8541cb91891a431326ab2e332faa7ae86cfb6e0540aa63160c1e5cdd5a4add518b303fff0a20117c6bc77f7cfbaf36b04c865c6c2b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b00200009d01fe044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001fe030302e9030f3c783e148560f936097339ae381d63116efcf802ff8b1c9360767db5219cc987375702a4123fd8657d3e22700f23f95020d1b261eda5257e9a72f9a918e8ef22dd5b3323ae03bbc1923dd224db988cadc16acc04b120a9f8b7e84da9716c53e0334d7b66586ddb9014df604b41be1e960dcfcbc96f4ed150a1a0dd070b9eb14276b9b6be413a769a75b519a53d3ecc0c220e85cd91ca354d57e7344517e64b43b6e29823cbd87eae26e2b2e78e6dedfbb76e3e9f77bcb844f9a8932eb3db2c3f9e44316e6f5d60e9e2a56e46b72abe6b06dc9a31cc63f10023d1f5e12d2a3ee93b675c96f504af0001220991c88db759e231b3320dcedf814dcf723fd9857e3d72d66a0f2af26950b915abdf56c1596f46a325bf17ad4810d3535fb02a259b247ac3dbd4cc3ecf9c51b6c07cebb009c1506fba0a89321ec8683e3fd009a6e551d50243e2d5092fefb3321083a4bad91320dc624bd6b5dddf93553e3d53924c05bfebec1fb4bd47e89a1a889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020000" - -const dsaElGamalTestKeysHex = "9501e1044dfcb16a110400aa3e5c1a1f43dd28c2ffae8abf5cfce555ee874134d8ba0a0f7b868ce2214beddc74e5e1e21ded354a95d18acdaf69e5e342371a71fbb9093162e0c5f3427de413a7f2c157d83f5cd2f9d791256dc4f6f0e13f13c3302af27f2384075ab3021dff7a050e14854bbde0a1094174855fc02f0bae8e00a340d94a1f22b32e48485700a0cec672ac21258fb95f61de2ce1af74b2c4fa3e6703ff698edc9be22c02ae4d916e4fa223f819d46582c0516235848a77b577ea49018dcd5e9e15cff9dbb4663a1ae6dd7580fa40946d40c05f72814b0f88481207e6c0832c3bded4853ebba0a7e3bd8e8c66df33d5a537cd4acf946d1080e7a3dcea679cb2b11a72a33a2b6a9dc85f466ad2ddf4c3db6283fa645343286971e3dd700703fc0c4e290d45767f370831a90187e74e9972aae5bff488eeff7d620af0362bfb95c1a6c3413ab5d15a2e4139e5d07a54d72583914661ed6a87cce810be28a0aa8879a2dd39e52fb6fe800f4f181ac7e328f740cde3d09a05cecf9483e4cca4253e60d4429ffd679d9996a520012aad119878c941e3cf151459873bdfc2a9563472fe0303027a728f9feb3b864260a1babe83925ce794710cfd642ee4ae0e5b9d74cee49e9c67b6cd0ea5dfbb582132195a121356a1513e1bca73e5b80c58c7ccb4164453412f456c47616d616c2054657374204b65792031886204131102002205024dfcb16a021b03060b090807030206150802090a0b0416020301021e01021780000a091033af447ccd759b09fadd00a0b8fd6f5a790bad7e9f2dbb7632046dc4493588db009c087c6a9ba9f7f49fab221587a74788c00db4889ab00200009d0157044dfcb16a1004008dec3f9291205255ccff8c532318133a6840739dd68b03ba942676f9038612071447bf07d00d559c5c0875724ea16a4c774f80d8338b55fca691a0522e530e604215b467bbc9ccfd483a1da99d7bc2648b4318fdbd27766fc8bfad3fddb37c62b8ae7ccfe9577e9b8d1e77c1d417ed2c2ef02d52f4da11600d85d3229607943700030503ff506c94c87c8cab778e963b76cf63770f0a79bf48fb49d3b4e52234620fc9f7657f9f8d56c96a2b7c7826ae6b57ebb2221a3fe154b03b6637cea7e6d98e3e45d87cf8dc432f723d3d71f89c5192ac8d7290684d2c25ce55846a80c9a7823f6acd9bb29fa6cd71f20bc90eccfca20451d0c976e460e672b000df49466408d527affe0303027a728f9feb3b864260abd761730327bca2aaa4ea0525c175e92bf240682a0e83b226f97ecb2e935b62c9a133858ce31b271fa8eb41f6a1b3cd72a63025ce1a75ee4180dcc284884904181102000905024dfcb16a021b0c000a091033af447ccd759b09dd0b009e3c3e7296092c81bee5a19929462caaf2fff3ae26009e218c437a2340e7ea628149af1ec98ec091a43992b00200009501e1044dfcb1be1104009f61faa61aa43df75d128cbe53de528c4aec49ce9360c992e70c77072ad5623de0a3a6212771b66b39a30dad6781799e92608316900518ec01184a85d872365b7d2ba4bacfb5882ea3c2473d3750dc6178cc1cf82147fb58caa28b28e9f12f6d1efcb0534abed644156c91cca4ab78834268495160b2400bc422beb37d237c2300a0cac94911b6d493bda1e1fbc6feeca7cb7421d34b03fe22cec6ccb39675bb7b94a335c2b7be888fd3906a1125f33301d8aa6ec6ee6878f46f73961c8d57a3e9544d8ef2a2cbfd4d52da665b1266928cfe4cb347a58c412815f3b2d2369dec04b41ac9a71cc9547426d5ab941cccf3b18575637ccfb42df1a802df3cfe0a999f9e7109331170e3a221991bf868543960f8c816c28097e503fe319db10fb98049f3a57d7c80c420da66d56f3644371631fad3f0ff4040a19a4fedc2d07727a1b27576f75a4d28c47d8246f27071e12d7a8de62aad216ddbae6aa02efd6b8a3e2818cda48526549791ab277e447b3a36c57cefe9b592f5eab73959743fcc8e83cbefec03a329b55018b53eec196765ae40ef9e20521a603c551efe0303020950d53a146bf9c66034d00c23130cce95576a2ff78016ca471276e8227fb30b1ffbd92e61804fb0c3eff9e30b1a826ee8f3e4730b4d86273ca977b4164453412f456c47616d616c2054657374204b65792032886204131102002205024dfcb1be021b03060b090807030206150802090a0b0416020301021e01021780000a0910a86bf526325b21b22bd9009e34511620415c974750a20df5cb56b182f3b48e6600a0a9466cb1a1305a84953445f77d461593f1d42bc1b00200009d0157044dfcb1be1004009565a951da1ee87119d600c077198f1c1bceb0f7aa54552489298e41ff788fa8f0d43a69871f0f6f77ebdfb14a4260cf9fbeb65d5844b4272a1904dd95136d06c3da745dc46327dd44a0f16f60135914368c8039a34033862261806bb2c5ce1152e2840254697872c85441ccb7321431d75a747a4bfb1d2c66362b51ce76311700030503fc0ea76601c196768070b7365a200e6ddb09307f262d5f39eec467b5f5784e22abdf1aa49226f59ab37cb49969d8f5230ea65caf56015abda62604544ed526c5c522bf92bed178a078789f6c807b6d34885688024a5bed9e9f8c58d11d4b82487b44c5f470c5606806a0443b79cadb45e0f897a561a53f724e5349b9267c75ca17fe0303020950d53a146bf9c660bc5f4ce8f072465e2d2466434320c1e712272fafc20e342fe7608101580fa1a1a367e60486a7cd1246b7ef5586cf5e10b32762b710a30144f12dd17dd4884904181102000905024dfcb1be021b0c000a0910a86bf526325b21b2904c00a0b2b66b4b39ccffda1d10f3ea8d58f827e30a8b8e009f4255b2d8112a184e40cde43a34e8655ca7809370b0020000" - -const signedMessageHex = "a3019bc0cbccc0c4b8d8b74ee2108fe16ec6d3ca490cbe362d3f8333d3f352531472538b8b13d353b97232f352158c20943157c71c16064626063656269052062e4e01987e9b6fccff4b7df3a34c534b23e679cbec3bc0f8f6e64dfb4b55fe3f8efa9ce110ddb5cd79faf1d753c51aecfa669f7e7aa043436596cccc3359cb7dd6bbe9ecaa69e5989d9e57209571edc0b2fa7f57b9b79a64ee6e99ce1371395fee92fec2796f7b15a77c386ff668ee27f6d38f0baa6c438b561657377bf6acff3c5947befd7bf4c196252f1d6e5c524d0300" - -const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c178233d3f352531472538b8b13d35379b97232f352158ca0b4312f57c71c1646462606365626906a062e4e019811591798ff99bf8afee860b0d8a8c2a85c3387e3bcf0bb3b17987f2bbcfab2aa526d930cbfd3d98757184df3995c9f3e7790e36e3e9779f06089d4c64e9e47dd6202cb6e9bc73c5d11bb59fbaf89d22d8dc7cf199ddf17af96e77c5f65f9bbed56f427bd8db7af37f6c9984bf9385efaf5f184f986fb3e6adb0ecfe35bbf92d16a7aa2a344fb0bc52fb7624f0200" - -const signedEncryptedMessageHex = "848c032a67d68660df41c70103ff5789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8d2c03b018bd210b1d3791e1aba74b0f1034e122ab72e760492c192383cf5e20b5628bd043272d63df9b923f147eb6091cd897553204832aba48fec54aa447547bb16305a1024713b90e77fd0065f1918271947549205af3c74891af22ee0b56cd29bfec6d6e351901cd4ab3ece7c486f1e32a792d4e474aed98ee84b3f591c7dff37b64e0ecd68fd036d517e412dcadf85840ce184ad7921ad446c4ee28db80447aea1ca8d4f574db4d4e37688158ddd19e14ee2eab4873d46947d65d14a23e788d912cf9a19624ca7352469b72a83866b7c23cb5ace3deab3c7018061b0ba0f39ed2befe27163e5083cf9b8271e3e3d52cc7ad6e2a3bd81d4c3d7022f8d" - -const signedEncryptedMessage2Hex = "85010e03cf6a7abcd43e36731003fb057f5495b79db367e277cdbe4ab90d924ddee0c0381494112ff8c1238fb0184af35d1731573b01bc4c55ecacd2aafbe2003d36310487d1ecc9ac994f3fada7f9f7f5c3a64248ab7782906c82c6ff1303b69a84d9a9529c31ecafbcdb9ba87e05439897d87e8a2a3dec55e14df19bba7f7bd316291c002ae2efd24f83f9e3441203fc081c0c23dc3092a454ca8a082b27f631abf73aca341686982e8fbda7e0e7d863941d68f3de4a755c2964407f4b5e0477b3196b8c93d551dd23c8beef7d0f03fbb1b6066f78907faf4bf1677d8fcec72651124080e0b7feae6b476e72ab207d38d90b958759fdedfc3c6c35717c9dbfc979b3cfbbff0a76d24a5e57056bb88acbd2a901ef64bc6e4db02adc05b6250ff378de81dca18c1910ab257dff1b9771b85bb9bbe0a69f5989e6d1710a35e6dfcceb7d8fb5ccea8db3932b3d9ff3fe0d327597c68b3622aec8e3716c83a6c93f497543b459b58ba504ed6bcaa747d37d2ca746fe49ae0a6ce4a8b694234e941b5159ff8bd34b9023da2814076163b86f40eed7c9472f81b551452d5ab87004a373c0172ec87ea6ce42ccfa7dbdad66b745496c4873d8019e8c28d6b3" - -const symmetricallyEncryptedCompressedHex = "8c0d04030302eb4a03808145d0d260c92f714339e13de5a79881216431925bf67ee2898ea61815f07894cd0703c50d0a76ef64d482196f47a8bc729af9b80bb6" - -const dsaTestKeyHex = "9901a2044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" - -const dsaTestKeyPrivateHex = "9501bb044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4d00009f592e0619d823953577d4503061706843317e4fee083db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" - -const armoredPrivateKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK----- -Version: GnuPG v1.4.10 (GNU/Linux) - -lQHYBE2rFNoBBADFwqWQIW/DSqcB4yCQqnAFTJ27qS5AnB46ccAdw3u4Greeu3Bp -idpoHdjULy7zSKlwR1EA873dO/k/e11Ml3dlAFUinWeejWaK2ugFP6JjiieSsrKn -vWNicdCS4HTWn0X4sjl0ZiAygw6GNhqEQ3cpLeL0g8E9hnYzJKQ0LWJa0QARAQAB -AAP/TB81EIo2VYNmTq0pK1ZXwUpxCrvAAIG3hwKjEzHcbQznsjNvPUihZ+NZQ6+X -0HCfPAdPkGDCLCb6NavcSW+iNnLTrdDnSI6+3BbIONqWWdRDYJhqZCkqmG6zqSfL -IdkJgCw94taUg5BWP/AAeQrhzjChvpMQTVKQL5mnuZbUCeMCAN5qrYMP2S9iKdnk -VANIFj7656ARKt/nf4CBzxcpHTyB8+d2CtPDKCmlJP6vL8t58Jmih+kHJMvC0dzn -gr5f5+sCAOOe5gt9e0am7AvQWhdbHVfJU0TQJx+m2OiCJAqGTB1nvtBLHdJnfdC9 -TnXXQ6ZXibqLyBies/xeY2sCKL5qtTMCAKnX9+9d/5yQxRyrQUHt1NYhaXZnJbHx -q4ytu0eWz+5i68IYUSK69jJ1NWPM0T6SkqpB3KCAIv68VFm9PxqG1KmhSrQIVGVz -dCBLZXmIuAQTAQIAIgUCTasU2gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA -CgkQO9o98PRieSoLhgQAkLEZex02Qt7vGhZzMwuN0R22w3VwyYyjBx+fM3JFETy1 -ut4xcLJoJfIaF5ZS38UplgakHG0FQ+b49i8dMij0aZmDqGxrew1m4kBfjXw9B/v+ -eIqpODryb6cOSwyQFH0lQkXC040pjq9YqDsO5w0WYNXYKDnzRV0p4H1pweo2VDid -AdgETasU2gEEAN46UPeWRqKHvA99arOxee38fBt2CI08iiWyI8T3J6ivtFGixSqV -bRcPxYO/qLpVe5l84Nb3X71GfVXlc9hyv7CD6tcowL59hg1E/DC5ydI8K8iEpUmK -/UnHdIY5h8/kqgGxkY/T/hgp5fRQgW1ZoZxLajVlMRZ8W4tFtT0DeA+JABEBAAEA -A/0bE1jaaZKj6ndqcw86jd+QtD1SF+Cf21CWRNeLKnUds4FRRvclzTyUMuWPkUeX -TaNNsUOFqBsf6QQ2oHUBBK4VCHffHCW4ZEX2cd6umz7mpHW6XzN4DECEzOVksXtc -lUC1j4UB91DC/RNQqwX1IV2QLSwssVotPMPqhOi0ZLNY7wIA3n7DWKInxYZZ4K+6 -rQ+POsz6brEoRHwr8x6XlHenq1Oki855pSa1yXIARoTrSJkBtn5oI+f8AzrnN0BN -oyeQAwIA/7E++3HDi5aweWrViiul9cd3rcsS0dEnksPhvS0ozCJiHsq/6GFmy7J8 -QSHZPteedBnZyNp5jR+H7cIfVN3KgwH/Skq4PsuPhDq5TKK6i8Pc1WW8MA6DXTdU -nLkX7RGmMwjC0DBf7KWAlPjFaONAX3a8ndnz//fy1q7u2l9AZwrj1qa1iJ8EGAEC -AAkFAk2rFNoCGwwACgkQO9o98PRieSo2/QP/WTzr4ioINVsvN1akKuekmEMI3LAp -BfHwatufxxP1U+3Si/6YIk7kuPB9Hs+pRqCXzbvPRrI8NHZBmc8qIGthishdCYad -AHcVnXjtxrULkQFGbGvhKURLvS9WnzD/m1K2zzwxzkPTzT9/Yf06O6Mal5AdugPL -VrM0m72/jnpKo04= -=zNCn ------END PGP PRIVATE KEY BLOCK-----` diff --git a/src/pkg/crypto/openpgp/s2k/Makefile b/src/pkg/crypto/openpgp/s2k/Makefile deleted file mode 100644 index 731d53431..000000000 --- a/src/pkg/crypto/openpgp/s2k/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../../Make.inc - -TARG=crypto/openpgp/s2k -GOFILES=\ - s2k.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/s2k/s2k.go b/src/pkg/crypto/openpgp/s2k/s2k.go deleted file mode 100644 index da926a76e..000000000 --- a/src/pkg/crypto/openpgp/s2k/s2k.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package s2k implements the various OpenPGP string-to-key transforms as -// specified in RFC 4800 section 3.7.1. -package s2k - -import ( - "crypto" - "crypto/openpgp/error" - "hash" - "io" - "os" - "strconv" -) - -// Simple writes to out the result of computing the Simple S2K function (RFC -// 4880, section 3.7.1.1) using the given hash and input passphrase. -func Simple(out []byte, h hash.Hash, in []byte) { - Salted(out, h, in, nil) -} - -var zero [1]byte - -// Salted writes to out the result of computing the Salted S2K function (RFC -// 4880, section 3.7.1.2) using the given hash, input passphrase and salt. -func Salted(out []byte, h hash.Hash, in []byte, salt []byte) { - done := 0 - - for i := 0; done < len(out); i++ { - h.Reset() - for j := 0; j < i; j++ { - h.Write(zero[:]) - } - h.Write(salt) - h.Write(in) - n := copy(out[done:], h.Sum()) - done += n - } -} - -// Iterated writes to out the result of computing the Iterated and Salted S2K -// function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase, -// salt and iteration count. -func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) { - combined := make([]byte, len(in)+len(salt)) - copy(combined, salt) - copy(combined[len(salt):], in) - - if count < len(combined) { - count = len(combined) - } - - done := 0 - for i := 0; done < len(out); i++ { - h.Reset() - for j := 0; j < i; j++ { - h.Write(zero[:]) - } - written := 0 - for written < count { - if written+len(combined) > count { - todo := count - written - h.Write(combined[:todo]) - written = count - } else { - h.Write(combined) - written += len(combined) - } - } - n := copy(out[done:], h.Sum()) - done += n - } -} - -// Parse reads a binary specification for a string-to-key transformation from r -// and returns a function which performs that transform. -func Parse(r io.Reader) (f func(out, in []byte), err os.Error) { - var buf [9]byte - - _, err = io.ReadFull(r, buf[:2]) - if err != nil { - return - } - - hash, ok := HashIdToHash(buf[1]) - if !ok { - return nil, error.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) - } - h := hash.New() - if h == nil { - return nil, error.UnsupportedError("hash not available: " + strconv.Itoa(int(hash))) - } - - switch buf[0] { - case 1: - f := func(out, in []byte) { - Simple(out, h, in) - } - return f, nil - case 2: - _, err := io.ReadFull(r, buf[:8]) - if err != nil { - return - } - f := func(out, in []byte) { - Salted(out, h, in, buf[:8]) - } - return f, nil - case 3: - _, err := io.ReadFull(r, buf[:9]) - if err != nil { - return - } - count := (16 + int(buf[8]&15)) << (uint32(buf[8]>>4) + 6) - f := func(out, in []byte) { - Iterated(out, h, in, buf[:8], count) - } - return f, nil - } - - return nil, error.UnsupportedError("S2K function") -} - -// Serialize salts and stretches the given passphrase and writes the resulting -// key into key. It also serializes an S2K descriptor to w. -func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte) os.Error { - var buf [11]byte - buf[0] = 3 /* iterated and salted */ - buf[1], _ = HashToHashId(crypto.SHA1) - salt := buf[2:10] - if _, err := io.ReadFull(rand, salt); err != nil { - return err - } - const count = 65536 // this is the default in gpg - buf[10] = 96 // 65536 iterations - if _, err := w.Write(buf[:]); err != nil { - return err - } - - Iterated(key, crypto.SHA1.New(), passphrase, salt, count) - return nil -} - -// hashToHashIdMapping contains pairs relating OpenPGP's hash identifier with -// Go's crypto.Hash type. See RFC 4880, section 9.4. -var hashToHashIdMapping = []struct { - id byte - hash crypto.Hash -}{ - {1, crypto.MD5}, - {2, crypto.SHA1}, - {3, crypto.RIPEMD160}, - {8, crypto.SHA256}, - {9, crypto.SHA384}, - {10, crypto.SHA512}, - {11, crypto.SHA224}, -} - -// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP -// hash id. -func HashIdToHash(id byte) (h crypto.Hash, ok bool) { - for _, m := range hashToHashIdMapping { - if m.id == id { - return m.hash, true - } - } - return 0, false -} - -// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. -func HashToHashId(h crypto.Hash) (id byte, ok bool) { - for _, m := range hashToHashIdMapping { - if m.hash == h { - return m.id, true - } - } - return 0, false -} diff --git a/src/pkg/crypto/openpgp/s2k/s2k_test.go b/src/pkg/crypto/openpgp/s2k/s2k_test.go deleted file mode 100644 index ec4012c23..000000000 --- a/src/pkg/crypto/openpgp/s2k/s2k_test.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package s2k - -import ( - "bytes" - "crypto/sha1" - "crypto/rand" - "encoding/hex" - "testing" -) - -var saltedTests = []struct { - in, out string -}{ - {"hello", "10295ac1"}, - {"world", "ac587a5e"}, - {"foo", "4dda8077"}, - {"bar", "bd8aac6b9ea9cae04eae6a91c6133b58b5d9a61c14f355516ed9370456"}, - {"x", "f1d3f289"}, - {"xxxxxxxxxxxxxxxxxxxxxxx", "e00d7b45"}, -} - -func TestSalted(t *testing.T) { - h := sha1.New() - salt := [4]byte{1, 2, 3, 4} - - for i, test := range saltedTests { - expected, _ := hex.DecodeString(test.out) - out := make([]byte, len(expected)) - Salted(out, h, []byte(test.in), salt[:]) - if !bytes.Equal(expected, out) { - t.Errorf("#%d, got: %x want: %x", i, out, expected) - } - } -} - -var iteratedTests = []struct { - in, out string -}{ - {"hello", "83126105"}, - {"world", "6fa317f9"}, - {"foo", "8fbc35b9"}, - {"bar", "2af5a99b54f093789fd657f19bd245af7604d0f6ae06f66602a46a08ae"}, - {"x", "5a684dfe"}, - {"xxxxxxxxxxxxxxxxxxxxxxx", "18955174"}, -} - -func TestIterated(t *testing.T) { - h := sha1.New() - salt := [4]byte{4, 3, 2, 1} - - for i, test := range iteratedTests { - expected, _ := hex.DecodeString(test.out) - out := make([]byte, len(expected)) - Iterated(out, h, []byte(test.in), salt[:], 31) - if !bytes.Equal(expected, out) { - t.Errorf("#%d, got: %x want: %x", i, out, expected) - } - } -} - -var parseTests = []struct { - spec, in, out string -}{ - /* Simple with SHA1 */ - {"0102", "hello", "aaf4c61d"}, - /* Salted with SHA1 */ - {"02020102030405060708", "hello", "f4f7d67e"}, - /* Iterated with SHA1 */ - {"03020102030405060708f1", "hello", "f2a57b7c"}, -} - -func TestParse(t *testing.T) { - for i, test := range parseTests { - spec, _ := hex.DecodeString(test.spec) - buf := bytes.NewBuffer(spec) - f, err := Parse(buf) - if err != nil { - t.Errorf("%d: Parse returned error: %s", i, err) - continue - } - - expected, _ := hex.DecodeString(test.out) - out := make([]byte, len(expected)) - f(out, []byte(test.in)) - if !bytes.Equal(out, expected) { - t.Errorf("%d: output got: %x want: %x", i, out, expected) - } - if testing.Short() { - break - } - } -} - -func TestSerialize(t *testing.T) { - buf := bytes.NewBuffer(nil) - key := make([]byte, 16) - passphrase := []byte("testing") - err := Serialize(buf, key, rand.Reader, passphrase) - if err != nil { - t.Errorf("failed to serialize: %s", err) - return - } - - f, err := Parse(buf) - if err != nil { - t.Errorf("failed to reparse: %s", err) - return - } - key2 := make([]byte, len(key)) - f(key2, passphrase) - if !bytes.Equal(key2, key) { - t.Errorf("keys don't match: %x (serialied) vs %x (parsed)", key, key2) - } -} diff --git a/src/pkg/crypto/openpgp/write.go b/src/pkg/crypto/openpgp/write.go deleted file mode 100644 index 9884472ce..000000000 --- a/src/pkg/crypto/openpgp/write.go +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package openpgp - -import ( - "crypto" - "crypto/openpgp/armor" - "crypto/openpgp/error" - "crypto/openpgp/packet" - "crypto/openpgp/s2k" - "crypto/rand" - _ "crypto/sha256" - "hash" - "io" - "os" - "strconv" - "time" -) - -// DetachSign signs message with the private key from signer (which must -// already have been decrypted) and writes the signature to w. -func DetachSign(w io.Writer, signer *Entity, message io.Reader) os.Error { - return detachSign(w, signer, message, packet.SigTypeBinary) -} - -// ArmoredDetachSign signs message with the private key from signer (which -// must already have been decrypted) and writes an armored signature to w. -func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader) (err os.Error) { - return armoredDetachSign(w, signer, message, packet.SigTypeBinary) -} - -// DetachSignText signs message (after canonicalising the line endings) with -// the private key from signer (which must already have been decrypted) and -// writes the signature to w. -func DetachSignText(w io.Writer, signer *Entity, message io.Reader) os.Error { - return detachSign(w, signer, message, packet.SigTypeText) -} - -// ArmoredDetachSignText signs message (after canonicalising the line endings) -// with the private key from signer (which must already have been decrypted) -// and writes an armored signature to w. -func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader) os.Error { - return armoredDetachSign(w, signer, message, packet.SigTypeText) -} - -func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType) (err os.Error) { - out, err := armor.Encode(w, SignatureType, nil) - if err != nil { - return - } - err = detachSign(out, signer, message, sigType) - if err != nil { - return - } - return out.Close() -} - -func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType) (err os.Error) { - if signer.PrivateKey == nil { - return error.InvalidArgumentError("signing key doesn't have a private key") - } - if signer.PrivateKey.Encrypted { - return error.InvalidArgumentError("signing key is encrypted") - } - - sig := new(packet.Signature) - sig.SigType = sigType - sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo - sig.Hash = crypto.SHA256 - sig.CreationTime = uint32(time.Seconds()) - sig.IssuerKeyId = &signer.PrivateKey.KeyId - - h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) - if err != nil { - return - } - io.Copy(wrappedHash, message) - - err = sig.Sign(h, signer.PrivateKey) - if err != nil { - return - } - - return sig.Serialize(w) -} - -// FileHints contains metadata about encrypted files. This metadata is, itself, -// encrypted. -type FileHints struct { - // IsBinary can be set to hint that the contents are binary data. - IsBinary bool - // FileName hints at the name of the file that should be written. It's - // truncated to 255 bytes if longer. It may be empty to suggest that the - // file should not be written to disk. It may be equal to "_CONSOLE" to - // suggest the data should not be written to disk. - FileName string - // EpochSeconds contains the modification time of the file, or 0 if not applicable. - EpochSeconds uint32 -} - -// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. -// The resulting WriteCloser must be closed after the contents of the file have -// been written. -func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints) (plaintext io.WriteCloser, err os.Error) { - if hints == nil { - hints = &FileHints{} - } - - key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, rand.Reader, passphrase, packet.CipherAES128) - if err != nil { - return - } - w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, packet.CipherAES128, key) - if err != nil { - return - } - return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) -} - -// intersectPreferences mutates and returns a prefix of a that contains only -// the values in the intersection of a and b. The order of a is preserved. -func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { - var j int - for _, v := range a { - for _, v2 := range b { - if v == v2 { - a[j] = v - j++ - break - } - } - } - - return a[:j] -} - -func hashToHashId(h crypto.Hash) uint8 { - v, ok := s2k.HashToHashId(h) - if !ok { - panic("tried to convert unknown hash") - } - return v -} - -// Encrypt encrypts a message to a number of recipients and, optionally, signs -// it. hints contains optional information, that is also encrypted, that aids -// the recipients in processing the message. The resulting WriteCloser must -// be closed after the contents of the file have been written. -func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints) (plaintext io.WriteCloser, err os.Error) { - var signer *packet.PrivateKey - if signed != nil { - signer = signed.signingKey().PrivateKey - if signer == nil || signer.Encrypted { - return nil, error.InvalidArgumentError("signing key must be decrypted") - } - } - - // These are the possible ciphers that we'll use for the message. - candidateCiphers := []uint8{ - uint8(packet.CipherAES128), - uint8(packet.CipherAES256), - uint8(packet.CipherCAST5), - } - // These are the possible hash functions that we'll use for the signature. - candidateHashes := []uint8{ - hashToHashId(crypto.SHA256), - hashToHashId(crypto.SHA512), - hashToHashId(crypto.SHA1), - hashToHashId(crypto.RIPEMD160), - } - // In the event that a recipient doesn't specify any supported ciphers - // or hash functions, these are the ones that we assume that every - // implementation supports. - defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] - defaultHashes := candidateHashes[len(candidateHashes)-1:] - - encryptKeys := make([]Key, len(to)) - for i := range to { - encryptKeys[i] = to[i].encryptionKey() - if encryptKeys[i].PublicKey == nil { - return nil, error.InvalidArgumentError("cannot encrypt a message to key id " + strconv.Uitob64(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") - } - - sig := to[i].primaryIdentity().SelfSignature - - preferredSymmetric := sig.PreferredSymmetric - if len(preferredSymmetric) == 0 { - preferredSymmetric = defaultCiphers - } - preferredHashes := sig.PreferredHash - if len(preferredHashes) == 0 { - preferredHashes = defaultHashes - } - candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) - candidateHashes = intersectPreferences(candidateHashes, preferredHashes) - } - - if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { - return nil, error.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") - } - - cipher := packet.CipherFunction(candidateCiphers[0]) - hash, _ := s2k.HashIdToHash(candidateHashes[0]) - symKey := make([]byte, cipher.KeySize()) - if _, err := io.ReadFull(rand.Reader, symKey); err != nil { - return nil, err - } - - for _, key := range encryptKeys { - if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil { - return nil, err - } - } - - encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey) - if err != nil { - return - } - - if signer != nil { - ops := &packet.OnePassSignature{ - SigType: packet.SigTypeBinary, - Hash: hash, - PubKeyAlgo: signer.PubKeyAlgo, - KeyId: signer.KeyId, - IsLast: true, - } - if err := ops.Serialize(encryptedData); err != nil { - return nil, err - } - } - - if hints == nil { - hints = &FileHints{} - } - - w := encryptedData - if signer != nil { - // If we need to write a signature packet after the literal - // data then we need to stop literalData from closing - // encryptedData. - w = noOpCloser{encryptedData} - - } - literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) - if err != nil { - return nil, err - } - - if signer != nil { - return signatureWriter{encryptedData, literalData, hash, hash.New(), signer}, nil - } - return literalData, nil -} - -// signatureWriter hashes the contents of a message while passing it along to -// literalData. When closed, it closes literalData, writes a signature packet -// to encryptedData and then also closes encryptedData. -type signatureWriter struct { - encryptedData io.WriteCloser - literalData io.WriteCloser - hashType crypto.Hash - h hash.Hash - signer *packet.PrivateKey -} - -func (s signatureWriter) Write(data []byte) (int, os.Error) { - s.h.Write(data) - return s.literalData.Write(data) -} - -func (s signatureWriter) Close() os.Error { - sig := &packet.Signature{ - SigType: packet.SigTypeBinary, - PubKeyAlgo: s.signer.PubKeyAlgo, - Hash: s.hashType, - CreationTime: uint32(time.Seconds()), - IssuerKeyId: &s.signer.KeyId, - } - - if err := sig.Sign(s.h, s.signer); err != nil { - return err - } - if err := s.literalData.Close(); err != nil { - return err - } - if err := sig.Serialize(s.encryptedData); err != nil { - return err - } - return s.encryptedData.Close() -} - -// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. -// TODO: we have two of these in OpenPGP packages alone. This probably needs -// to be promoted somewhere more common. -type noOpCloser struct { - w io.Writer -} - -func (c noOpCloser) Write(data []byte) (n int, err os.Error) { - return c.w.Write(data) -} - -func (c noOpCloser) Close() os.Error { - return nil -} diff --git a/src/pkg/crypto/openpgp/write_test.go b/src/pkg/crypto/openpgp/write_test.go deleted file mode 100644 index c542dfa45..000000000 --- a/src/pkg/crypto/openpgp/write_test.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package openpgp - -import ( - "bytes" - "crypto/rand" - "os" - "io" - "io/ioutil" - "testing" - "time" -) - -func TestSignDetached(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - out := bytes.NewBuffer(nil) - message := bytes.NewBufferString(signedInput) - err := DetachSign(out, kring[0], message) - if err != nil { - t.Error(err) - } - - testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId) -} - -func TestSignTextDetached(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - out := bytes.NewBuffer(nil) - message := bytes.NewBufferString(signedInput) - err := DetachSignText(out, kring[0], message) - if err != nil { - t.Error(err) - } - - testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId) -} - -func TestSignDetachedDSA(t *testing.T) { - kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyPrivateHex)) - out := bytes.NewBuffer(nil) - message := bytes.NewBufferString(signedInput) - err := DetachSign(out, kring[0], message) - if err != nil { - t.Error(err) - } - - testDetachedSignature(t, kring, out, signedInput, "check", testKey3KeyId) -} - -func TestNewEntity(t *testing.T) { - if testing.Short() { - return - } - - e, err := NewEntity(rand.Reader, time.Seconds(), "Test User", "test", "test@example.com") - if err != nil { - t.Errorf("failed to create entity: %s", err) - return - } - - w := bytes.NewBuffer(nil) - if err := e.SerializePrivate(w); err != nil { - t.Errorf("failed to serialize entity: %s", err) - return - } - serialized := w.Bytes() - - el, err := ReadKeyRing(w) - if err != nil { - t.Errorf("failed to reparse entity: %s", err) - return - } - - if len(el) != 1 { - t.Errorf("wrong number of entities found, got %d, want 1", len(el)) - } - - w = bytes.NewBuffer(nil) - if err := e.SerializePrivate(w); err != nil { - t.Errorf("failed to serialize entity second time: %s", err) - return - } - - if !bytes.Equal(w.Bytes(), serialized) { - t.Errorf("results differed") - } -} - -func TestSymmetricEncryption(t *testing.T) { - buf := new(bytes.Buffer) - plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil) - if err != nil { - t.Errorf("error writing headers: %s", err) - return - } - message := []byte("hello world\n") - _, err = plaintext.Write(message) - if err != nil { - t.Errorf("error writing to plaintext writer: %s", err) - } - err = plaintext.Close() - if err != nil { - t.Errorf("error closing plaintext writer: %s", err) - } - - md, err := ReadMessage(buf, nil, func(keys []Key, symmetric bool) ([]byte, os.Error) { - return []byte("testing"), nil - }) - if err != nil { - t.Errorf("error rereading message: %s", err) - } - messageBuf := bytes.NewBuffer(nil) - _, err = io.Copy(messageBuf, md.UnverifiedBody) - if err != nil { - t.Errorf("error rereading message: %s", err) - } - if !bytes.Equal(message, messageBuf.Bytes()) { - t.Errorf("recovered message incorrect got '%s', want '%s'", messageBuf.Bytes(), message) - } -} - -var testEncryptionTests = []struct { - keyRingHex string - isSigned bool -}{ - { - testKeys1And2PrivateHex, - false, - }, - { - testKeys1And2PrivateHex, - true, - }, - { - dsaElGamalTestKeysHex, - false, - }, - { - dsaElGamalTestKeysHex, - true, - }, -} - -func TestEncryption(t *testing.T) { - for i, test := range testEncryptionTests { - kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) - - passphrase := []byte("passphrase") - for _, entity := range kring { - if entity.PrivateKey != nil && entity.PrivateKey.Encrypted { - err := entity.PrivateKey.Decrypt(passphrase) - if err != nil { - t.Errorf("#%d: failed to decrypt key", i) - } - } - for _, subkey := range entity.Subkeys { - if subkey.PrivateKey != nil && subkey.PrivateKey.Encrypted { - err := subkey.PrivateKey.Decrypt(passphrase) - if err != nil { - t.Errorf("#%d: failed to decrypt subkey", i) - } - } - } - } - - var signed *Entity - if test.isSigned { - signed = kring[0] - } - - buf := new(bytes.Buffer) - w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */ ) - if err != nil { - t.Errorf("#%d: error in Encrypt: %s", i, err) - continue - } - - const message = "testing" - _, err = w.Write([]byte(message)) - if err != nil { - t.Errorf("#%d: error writing plaintext: %s", i, err) - continue - } - err = w.Close() - if err != nil { - t.Errorf("#%d: error closing WriteCloser: %s", i, err) - continue - } - - md, err := ReadMessage(buf, kring, nil /* no prompt */ ) - if err != nil { - t.Errorf("#%d: error reading message: %s", i, err) - continue - } - - if test.isSigned { - expectedKeyId := kring[0].signingKey().PublicKey.KeyId - if md.SignedByKeyId != expectedKeyId { - t.Errorf("#%d: message signed by wrong key id, got: %d, want: %d", i, *md.SignedBy, expectedKeyId) - } - if md.SignedBy == nil { - t.Errorf("#%d: failed to find the signing Entity", i) - } - } - - plaintext, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("#%d: error reading encrypted contents: %s", i, err) - continue - } - - expectedKeyId := kring[0].encryptionKey().PublicKey.KeyId - if len(md.EncryptedToKeyIds) != 1 || md.EncryptedToKeyIds[0] != expectedKeyId { - t.Errorf("#%d: expected message to be encrypted to %v, but got %#v", i, expectedKeyId, md.EncryptedToKeyIds) - } - - if string(plaintext) != message { - t.Errorf("#%d: got: %s, want: %s", i, string(plaintext), message) - } - - if test.isSigned { - if md.SignatureError != nil { - t.Errorf("#%d: signature error: %s", i, err) - } - if md.Signature == nil { - t.Error("signature missing") - } - } - } -} diff --git a/src/pkg/crypto/rand/Makefile b/src/pkg/crypto/rand/Makefile deleted file mode 100644 index d1a3d45e8..000000000 --- a/src/pkg/crypto/rand/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/rand - -GOFILES=\ - rand.go\ - util.go\ - -GOFILES_freebsd=\ - rand_unix.go\ - -GOFILES_darwin=\ - rand_unix.go\ - -GOFILES_linux=\ - rand_unix.go\ - -GOFILES_openbsd=\ - rand_unix.go\ - -GOFILES_windows=\ - rand_windows.go\ - -GOFILES+=$(GOFILES_$(GOOS)) - -include ../../../Make.pkg diff --git a/src/pkg/crypto/rand/rand.go b/src/pkg/crypto/rand/rand.go index 42d9da0ef..59759038e 100644 --- a/src/pkg/crypto/rand/rand.go +++ b/src/pkg/crypto/rand/rand.go @@ -6,10 +6,7 @@ // pseudorandom number generator. package rand -import ( - "io" - "os" -) +import "io" // Reader is a global, shared instance of a cryptographically // strong pseudo-random generator. @@ -18,4 +15,4 @@ import ( var Reader io.Reader // Read is a helper function that calls Reader.Read. -func Read(b []byte) (n int, err os.Error) { return Reader.Read(b) } +func Read(b []byte) (n int, err error) { return Reader.Read(b) } diff --git a/src/pkg/crypto/rand/rand_test.go b/src/pkg/crypto/rand/rand_test.go index bfae7ce4f..be3a5a221 100644 --- a/src/pkg/crypto/rand/rand_test.go +++ b/src/pkg/crypto/rand/rand_test.go @@ -22,7 +22,7 @@ func TestRead(t *testing.T) { } var z bytes.Buffer - f := flate.NewWriter(&z, 5) + f, _ := flate.NewWriter(&z, 5) f.Write(b) f.Close() if z.Len() < len(b)*99/100 { diff --git a/src/pkg/crypto/rand/rand_unix.go b/src/pkg/crypto/rand/rand_unix.go index 3a06aa8b1..5eb4cda2b 100644 --- a/src/pkg/crypto/rand/rand_unix.go +++ b/src/pkg/crypto/rand/rand_unix.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build darwin freebsd linux netbsd openbsd + // Unix cryptographically secure pseudorandom number // generator. @@ -10,6 +12,7 @@ package rand import ( "bufio" "crypto/aes" + "crypto/cipher" "io" "os" "sync" @@ -28,7 +31,7 @@ type devReader struct { mu sync.Mutex } -func (r *devReader) Read(b []byte) (n int, err os.Error) { +func (r *devReader) Read(b []byte) (n int, err error) { r.mu.Lock() defer r.mu.Unlock() if r.f == nil { @@ -64,12 +67,12 @@ func newReader(entropy io.Reader) io.Reader { type reader struct { mu sync.Mutex budget int // number of bytes that can be generated - cipher *aes.Cipher + cipher cipher.Block entropy io.Reader time, seed, dst, key [aes.BlockSize]byte } -func (r *reader) Read(b []byte) (n int, err os.Error) { +func (r *reader) Read(b []byte) (n int, err error) { r.mu.Lock() defer r.mu.Unlock() n = len(b) @@ -98,7 +101,7 @@ func (r *reader) Read(b []byte) (n int, err os.Error) { // t = encrypt(time) // dst = encrypt(t^seed) // seed = encrypt(t^dst) - ns := time.Nanoseconds() + ns := time.Now().UnixNano() r.time[0] = byte(ns >> 56) r.time[1] = byte(ns >> 48) r.time[2] = byte(ns >> 40) diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go old mode 100755 new mode 100644 index 0eab6b213..2b2bd4bba --- a/src/pkg/crypto/rand/rand_windows.go +++ b/src/pkg/crypto/rand/rand_windows.go @@ -23,21 +23,21 @@ type rngReader struct { mu sync.Mutex } -func (r *rngReader) Read(b []byte) (n int, err os.Error) { +func (r *rngReader) Read(b []byte) (n int, err error) { r.mu.Lock() if r.prov == 0 { const provType = syscall.PROV_RSA_FULL const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT - errno := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) - if errno != 0 { + err := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) + if err != nil { r.mu.Unlock() - return 0, os.NewSyscallError("CryptAcquireContext", errno) + return 0, os.NewSyscallError("CryptAcquireContext", err) } } r.mu.Unlock() - errno := syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) - if errno != 0 { - return 0, os.NewSyscallError("CryptGenRandom", errno) + err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) + if err != nil { + return 0, os.NewSyscallError("CryptGenRandom", err) } return len(b), nil } diff --git a/src/pkg/crypto/rand/util.go b/src/pkg/crypto/rand/util.go index 77028476e..5391c1829 100644 --- a/src/pkg/crypto/rand/util.go +++ b/src/pkg/crypto/rand/util.go @@ -5,16 +5,16 @@ package rand import ( - "big" + "errors" "io" - "os" + "math/big" ) // Prime returns a number, p, of the given size, such that p is prime // with high probability. -func Prime(rand io.Reader, bits int) (p *big.Int, err os.Error) { +func Prime(rand io.Reader, bits int) (p *big.Int, err error) { if bits < 1 { - err = os.EINVAL + err = errors.New("crypto/rand: prime size must be positive") } b := uint(bits % 8) @@ -39,7 +39,7 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err os.Error) { bytes[len(bytes)-1] |= 1 p.SetBytes(bytes) - if big.ProbablyPrime(p, 20) { + if p.ProbablyPrime(20) { return } } @@ -48,7 +48,7 @@ func Prime(rand io.Reader, bits int) (p *big.Int, err os.Error) { } // Int returns a uniform random value in [0, max). -func Int(rand io.Reader, max *big.Int) (n *big.Int, err os.Error) { +func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) { k := (max.BitLen() + 7) / 8 // b is the number of bits in the most significant byte of max. diff --git a/src/pkg/crypto/rc4/Makefile b/src/pkg/crypto/rc4/Makefile deleted file mode 100644 index 50a3b7972..000000000 --- a/src/pkg/crypto/rc4/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/rc4 -GOFILES=\ - rc4.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/rc4/rc4.go b/src/pkg/crypto/rc4/rc4.go index 7ee471093..1bb278f74 100644 --- a/src/pkg/crypto/rc4/rc4.go +++ b/src/pkg/crypto/rc4/rc4.go @@ -9,10 +9,7 @@ package rc4 // BUG(agl): RC4 is in common use but has design weaknesses that make // it a poor choice for new protocols. -import ( - "os" - "strconv" -) +import "strconv" // A Cipher is an instance of RC4 using a particular key. type Cipher struct { @@ -22,13 +19,13 @@ type Cipher struct { type KeySizeError int -func (k KeySizeError) String() string { +func (k KeySizeError) Error() string { return "crypto/rc4: invalid key size " + strconv.Itoa(int(k)) } // NewCipher creates and returns a new Cipher. The key argument should be the // RC4 key, at least 1 byte and at most 256 bytes. -func NewCipher(key []byte) (*Cipher, os.Error) { +func NewCipher(key []byte) (*Cipher, error) { k := len(key) if k < 1 || k > 256 { return nil, KeySizeError(k) diff --git a/src/pkg/crypto/ripemd160/Makefile b/src/pkg/crypto/ripemd160/Makefile deleted file mode 100644 index 7e529457d..000000000 --- a/src/pkg/crypto/ripemd160/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2010 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/ripemd160 -GOFILES=\ - ripemd160.go\ - ripemd160block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/ripemd160/ripemd160.go b/src/pkg/crypto/ripemd160/ripemd160.go deleted file mode 100644 index 5aaca59a3..000000000 --- a/src/pkg/crypto/ripemd160/ripemd160.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package ripemd160 implements the RIPEMD-160 hash algorithm. -package ripemd160 - -// RIPEMD-160 is designed by by Hans Dobbertin, Antoon Bosselaers, and Bart -// Preneel with specifications available at: -// http://homes.esat.kuleuven.be/~cosicart/pdf/AB-9601/AB-9601.pdf. - -import ( - "crypto" - "hash" - "os" -) - -func init() { - crypto.RegisterHash(crypto.RIPEMD160, New) -} - -// The size of the checksum in bytes. -const Size = 20 - -// The block size of the hash algorithm in bytes. -const BlockSize = 64 - -const ( - _s0 = 0x67452301 - _s1 = 0xefcdab89 - _s2 = 0x98badcfe - _s3 = 0x10325476 - _s4 = 0xc3d2e1f0 -) - -// digest represents the partial evaluation of a checksum. -type digest struct { - s [5]uint32 // running context - x [BlockSize]byte // temporary buffer - nx int // index into x - tc uint64 // total count of bytes processed -} - -func (d *digest) Reset() { - d.s[0], d.s[1], d.s[2], d.s[3], d.s[4] = _s0, _s1, _s2, _s3, _s4 - d.nx = 0 - d.tc = 0 -} - -// New returns a new hash.Hash computing the checksum. -func New() hash.Hash { - result := new(digest) - result.Reset() - return result -} - -func (d *digest) Size() int { return Size } - -func (d *digest) Write(p []byte) (nn int, err os.Error) { - nn = len(p) - d.tc += uint64(nn) - if d.nx > 0 { - n := len(p) - if n > BlockSize-d.nx { - n = BlockSize - d.nx - } - for i := 0; i < n; i++ { - d.x[d.nx+i] = p[i] - } - d.nx += n - if d.nx == BlockSize { - _Block(d, d.x[0:]) - d.nx = 0 - } - p = p[n:] - } - n := _Block(d, p) - p = p[n:] - if len(p) > 0 { - d.nx = copy(d.x[:], p) - } - return -} - -func (d0 *digest) Sum() []byte { - // Make a copy of d0 so that caller can keep writing and summing. - d := new(digest) - *d = *d0 - - // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. - tc := d.tc - var tmp [64]byte - tmp[0] = 0x80 - if tc%64 < 56 { - d.Write(tmp[0 : 56-tc%64]) - } else { - d.Write(tmp[0 : 64+56-tc%64]) - } - - // Length in bits. - tc <<= 3 - for i := uint(0); i < 8; i++ { - tmp[i] = byte(tc >> (8 * i)) - } - d.Write(tmp[0:8]) - - if d.nx != 0 { - panic("d.nx != 0") - } - - p := make([]byte, 20) - j := 0 - for _, s := range d.s { - p[j], p[j+1], p[j+2], p[j+3] = byte(s), byte(s>>8), byte(s>>16), byte(s>>24) - j += 4 - } - return p -} diff --git a/src/pkg/crypto/ripemd160/ripemd160_test.go b/src/pkg/crypto/ripemd160/ripemd160_test.go deleted file mode 100644 index f4135f5cf..000000000 --- a/src/pkg/crypto/ripemd160/ripemd160_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ripemd160 - -// Test vectors are from: -// http://homes.esat.kuleuven.be/~bosselae/ripemd160.html - -import ( - "fmt" - "io" - "testing" -) - -type mdTest struct { - out string - in string -} - -var vectors = [...]mdTest{ - {"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""}, - {"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"}, - {"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"}, - {"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"}, - {"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"}, - {"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, - {"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, - {"9b752e45573d4b39f4dbd3323cab82bf63326bfb", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, -} - -func TestVectors(t *testing.T) { - for i := 0; i < len(vectors); i++ { - tv := vectors[i] - md := New() - for j := 0; j < 3; j++ { - if j < 2 { - io.WriteString(md, tv.in) - } else { - io.WriteString(md, tv.in[0:len(tv.in)/2]) - md.Sum() - io.WriteString(md, tv.in[len(tv.in)/2:]) - } - s := fmt.Sprintf("%x", md.Sum()) - if s != tv.out { - t.Fatalf("RIPEMD-160[%d](%s) = %s, expected %s", j, tv.in, s, tv.out) - } - md.Reset() - } - } -} - -func TestMillionA(t *testing.T) { - md := New() - for i := 0; i < 100000; i++ { - io.WriteString(md, "aaaaaaaaaa") - } - out := "52783243c1697bdbe16d37f97f68f08325dc1528" - s := fmt.Sprintf("%x", md.Sum()) - if s != out { - t.Fatalf("RIPEMD-160 (1 million 'a') = %s, expected %s", s, out) - } - md.Reset() -} diff --git a/src/pkg/crypto/ripemd160/ripemd160block.go b/src/pkg/crypto/ripemd160/ripemd160block.go deleted file mode 100644 index 7bc8e6c48..000000000 --- a/src/pkg/crypto/ripemd160/ripemd160block.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// RIPEMD-160 block step. -// In its own file so that a faster assembly or C version -// can be substituted easily. - -package ripemd160 - -// work buffer indices and roll amounts for one line -var _n = [80]uint{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, - 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, - 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, - 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13, -} - -var _r = [80]uint{ - 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, - 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, - 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, - 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, - 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6, -} - -// same for the other parallel one -var n_ = [80]uint{ - 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, - 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, - 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, - 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, - 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11, -} - -var r_ = [80]uint{ - 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, - 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, - 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, - 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, - 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, -} - -func _Block(md *digest, p []byte) int { - n := 0 - var x [16]uint32 - var alpha, beta uint32 - for len(p) >= BlockSize { - a, b, c, d, e := md.s[0], md.s[1], md.s[2], md.s[3], md.s[4] - aa, bb, cc, dd, ee := a, b, c, d, e - j := 0 - for i := 0; i < 16; i++ { - x[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 - j += 4 - } - - // round 1 - i := 0 - for i < 16 { - alpha = a + (b ^ c ^ d) + x[_n[i]] - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb ^ (cc | ^dd)) + x[n_[i]] + 0x50a28be6 - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // round 2 - for i < 32 { - alpha = a + (b&c | ^b&d) + x[_n[i]] + 0x5a827999 - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb&dd | cc&^dd) + x[n_[i]] + 0x5c4dd124 - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // round 3 - for i < 48 { - alpha = a + (b | ^c ^ d) + x[_n[i]] + 0x6ed9eba1 - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb | ^cc ^ dd) + x[n_[i]] + 0x6d703ef3 - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // round 4 - for i < 64 { - alpha = a + (b&d | c&^d) + x[_n[i]] + 0x8f1bbcdc - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb&cc | ^bb&dd) + x[n_[i]] + 0x7a6d76e9 - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // round 5 - for i < 80 { - alpha = a + (b ^ (c | ^d)) + x[_n[i]] + 0xa953fd4e - s := _r[i] - alpha = (alpha<>(32-s)) + e - beta = c<<10 | c>>22 - a, b, c, d, e = e, alpha, b, beta, d - - // parallel line - alpha = aa + (bb ^ cc ^ dd) + x[n_[i]] - s = r_[i] - alpha = (alpha<>(32-s)) + ee - beta = cc<<10 | cc>>22 - aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd - - i++ - } - - // combine results - dd += c + md.s[1] - md.s[1] = md.s[2] + d + ee - md.s[2] = md.s[3] + e + aa - md.s[3] = md.s[4] + a + bb - md.s[4] = md.s[0] + b + cc - md.s[0] = dd - - p = p[BlockSize:] - n += BlockSize - } - return n -} diff --git a/src/pkg/crypto/rsa/Makefile b/src/pkg/crypto/rsa/Makefile deleted file mode 100644 index ff26ca6f2..000000000 --- a/src/pkg/crypto/rsa/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/rsa -GOFILES=\ - rsa.go\ - pkcs1v15.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go index 600623114..254f4a3da 100644 --- a/src/pkg/crypto/rsa/pkcs1v15.go +++ b/src/pkg/crypto/rsa/pkcs1v15.go @@ -5,11 +5,11 @@ package rsa import ( - "big" "crypto" "crypto/subtle" + "errors" "io" - "os" + "math/big" ) // This file implements encryption and decryption using PKCS#1 v1.5 padding. @@ -18,10 +18,10 @@ import ( // The message must be no longer than the length of the public modulus minus 11 bytes. // WARNING: use of this function to encrypt plaintexts other than session keys // is dangerous. Use RSA OAEP in new protocols. -func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err os.Error) { +func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error) { k := (pub.N.BitLen() + 7) / 8 if len(msg) > k-11 { - err = MessageTooLongError{} + err = ErrMessageTooLong return } @@ -44,10 +44,10 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, er // DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5. // If rand != nil, it uses RSA blinding to avoid timing side-channel attacks. -func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err os.Error) { +func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error) { valid, out, err := decryptPKCS1v15(rand, priv, ciphertext) if err == nil && valid == 0 { - err = DecryptionError{} + err = ErrDecryption } return @@ -65,11 +65,11 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out [ // about the plaintext. // See ``Chosen Ciphertext Attacks Against Protocols Based on the RSA // Encryption Standard PKCS #1'', Daniel Bleichenbacher, Advances in Cryptology -// (Crypto '98), -func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) (err os.Error) { +// (Crypto '98). +func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) (err error) { k := (priv.N.BitLen() + 7) / 8 if k-(len(key)+3+8) < 0 { - err = DecryptionError{} + err = ErrDecryption return } @@ -83,10 +83,10 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by return } -func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, msg []byte, err os.Error) { +func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, msg []byte, err error) { k := (priv.N.BitLen() + 7) / 8 if k < 11 { - err = DecryptionError{} + err = ErrDecryption return } @@ -119,7 +119,7 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid } // nonZeroRandomBytes fills the given slice with non-zero random octets. -func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) { +func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { _, err = io.ReadFull(rand, s) if err != nil { return @@ -161,7 +161,7 @@ var hashPrefixes = map[crypto.Hash][]byte{ // SignPKCS1v15 calculates the signature of hashed using RSASSA-PKCS1-V1_5-SIGN from RSA PKCS#1 v1.5. // Note that hashed must be the result of hashing the input message using the // given hash function. -func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) (s []byte, err os.Error) { +func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) (s []byte, err error) { hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed)) if err != nil { return @@ -170,7 +170,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b tLen := len(prefix) + hashLen k := (priv.N.BitLen() + 7) / 8 if k < tLen+11 { - return nil, MessageTooLongError{} + return nil, ErrMessageTooLong } // EM = 0x00 || 0x01 || PS || 0x00 || T @@ -194,7 +194,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b // hashed is the result of hashing the input message using the given hash // function and sig is the signature. A valid signature is indicated by // returning a nil error. -func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err os.Error) { +func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err error) { hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed)) if err != nil { return @@ -203,7 +203,7 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) tLen := len(prefix) + hashLen k := (pub.N.BitLen() + 7) / 8 if k < tLen+11 { - err = VerificationError{} + err = ErrVerification return } @@ -223,20 +223,20 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) } if ok != 1 { - return VerificationError{} + return ErrVerification } return nil } -func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err os.Error) { +func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) { hashLen = hash.Size() if inLen != hashLen { - return 0, nil, os.NewError("input must be hashed message") + return 0, nil, errors.New("input must be hashed message") } prefix, ok := hashPrefixes[hash] if !ok { - return 0, nil, os.NewError("unsupported hash function") + return 0, nil, errors.New("unsupported hash function") } return } diff --git a/src/pkg/crypto/rsa/pkcs1v15_test.go b/src/pkg/crypto/rsa/pkcs1v15_test.go index d69bacfd6..58d5fda19 100644 --- a/src/pkg/crypto/rsa/pkcs1v15_test.go +++ b/src/pkg/crypto/rsa/pkcs1v15_test.go @@ -5,7 +5,6 @@ package rsa import ( - "big" "bytes" "crypto" "crypto/rand" @@ -13,6 +12,7 @@ import ( "encoding/base64" "encoding/hex" "io" + "math/big" "testing" "testing/quick" ) @@ -168,7 +168,7 @@ func TestSignPKCS1v15(t *testing.T) { for i, test := range signPKCS1v15Tests { h := sha1.New() h.Write([]byte(test.in)) - digest := h.Sum() + digest := h.Sum(nil) s, err := SignPKCS1v15(nil, rsaPrivateKey, crypto.SHA1, digest) if err != nil { @@ -186,7 +186,7 @@ func TestVerifyPKCS1v15(t *testing.T) { for i, test := range signPKCS1v15Tests { h := sha1.New() h.Write([]byte(test.in)) - digest := h.Sum() + digest := h.Sum(nil) sig, _ := hex.DecodeString(test.out) diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index 6957659f2..ec77e6869 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -8,12 +8,12 @@ package rsa // TODO(agl): Add support for PSS padding. import ( - "big" "crypto/rand" "crypto/subtle" + "errors" "hash" "io" - "os" + "math/big" ) var bigZero = big.NewInt(0) @@ -55,16 +55,15 @@ type CRTValue struct { } // Validate performs basic sanity checks on the key. -// It returns nil if the key is valid, or else an os.Error describing a problem. - -func (priv *PrivateKey) Validate() os.Error { +// It returns nil if the key is valid, or else an error describing a problem. +func (priv *PrivateKey) Validate() error { // Check that the prime factors are actually prime. Note that this is // just a sanity check. Since the random witnesses chosen by // ProbablyPrime are deterministic, given the candidate number, it's // easy for an attack to generate composites that pass this test. for _, prime := range priv.Primes { - if !big.ProbablyPrime(prime, 20) { - return os.NewError("prime factor is composite") + if !prime.ProbablyPrime(20) { + return errors.New("prime factor is composite") } } @@ -74,7 +73,7 @@ func (priv *PrivateKey) Validate() os.Error { modulus.Mul(modulus, prime) } if modulus.Cmp(priv.N) != 0 { - return os.NewError("invalid modulus") + return errors.New("invalid modulus") } // Check that e and totient(Πprimes) are coprime. totient := new(big.Int).Set(bigOne) @@ -86,21 +85,21 @@ func (priv *PrivateKey) Validate() os.Error { gcd := new(big.Int) x := new(big.Int) y := new(big.Int) - big.GcdInt(gcd, x, y, totient, e) + gcd.GCD(x, y, totient, e) if gcd.Cmp(bigOne) != 0 { - return os.NewError("invalid public exponent E") + return errors.New("invalid public exponent E") } // Check that de ≡ 1 (mod totient(Πprimes)) de := new(big.Int).Mul(priv.D, e) de.Mod(de, totient) if de.Cmp(bigOne) != 0 { - return os.NewError("invalid private exponent D") + return errors.New("invalid private exponent D") } return nil } // GenerateKey generates an RSA keypair of the given bit size. -func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err os.Error) { +func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err error) { return GenerateMultiPrimeKey(random, 2, bits) } @@ -114,20 +113,12 @@ func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err os.Error) { // // [1] US patent 4405829 (1972, expired) // [2] http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf -func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *PrivateKey, err os.Error) { +func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *PrivateKey, err error) { priv = new(PrivateKey) - // Smaller public exponents lead to faster public key - // operations. Since the exponent must be coprime to - // (p-1)(q-1), the smallest possible value is 3. Some have - // suggested that a larger exponent (often 2**16+1) be used - // since previous implementation bugs[1] were avoided when this - // was the case. However, there are no current reasons not to use - // small exponents. - // [1] http://marc.info/?l=cryptography&m=115694833312008&w=2 - priv.E = 3 + priv.E = 65537 if nprimes < 2 { - return nil, os.NewError("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") + return nil, errors.New("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") } primes := make([]*big.Int, nprimes) @@ -165,7 +156,7 @@ NextSetOfPrimes: priv.D = new(big.Int) y := new(big.Int) e := big.NewInt(int64(priv.E)) - big.GcdInt(g, priv.D, y, e, totient) + g.GCD(priv.D, y, e, totient) if g.Cmp(bigOne) == 0 { priv.D.Add(priv.D, totient) @@ -198,12 +189,13 @@ func incCounter(c *[4]byte) { // specified in PKCS#1 v2.1. func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { var counter [4]byte + var digest []byte done := 0 for done < len(out) { hash.Write(seed) hash.Write(counter[0:4]) - digest := hash.Sum() + digest = hash.Sum(digest[:0]) hash.Reset() for i := 0; i < len(digest) && done < len(out); i++ { @@ -214,13 +206,9 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { } } -// MessageTooLongError is returned when attempting to encrypt a message which -// is too large for the size of the public key. -type MessageTooLongError struct{} - -func (MessageTooLongError) String() string { - return "message too long for RSA public key size" -} +// ErrMessageTooLong is returned when attempting to encrypt a message which is +// too large for the size of the public key. +var ErrMessageTooLong = errors.New("crypto/rsa: message too long for RSA public key size") func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int { e := big.NewInt(int64(pub.E)) @@ -231,16 +219,16 @@ func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int { // EncryptOAEP encrypts the given message with RSA-OAEP. // The message must be no longer than the length of the public modulus less // twice the hash length plus 2. -func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err os.Error) { +func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err error) { hash.Reset() k := (pub.N.BitLen() + 7) / 8 if len(msg) > k-2*hash.Size()-2 { - err = MessageTooLongError{} + err = ErrMessageTooLong return } hash.Write(label) - lHash := hash.Sum() + lHash := hash.Sum(nil) hash.Reset() em := make([]byte, k) @@ -274,17 +262,13 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l return } -// A DecryptionError represents a failure to decrypt a message. +// ErrDecryption represents a failure to decrypt a message. // It is deliberately vague to avoid adaptive attacks. -type DecryptionError struct{} +var ErrDecryption = errors.New("crypto/rsa: decryption error") -func (DecryptionError) String() string { return "RSA decryption error" } - -// A VerificationError represents a failure to verify a signature. +// ErrVerification represents a failure to verify a signature. // It is deliberately vague to avoid adaptive attacks. -type VerificationError struct{} - -func (VerificationError) String() string { return "RSA verification error" } +var ErrVerification = errors.New("crypto/rsa: verification error") // modInverse returns ia, the inverse of a in the multiplicative group of prime // order n. It requires that a be a member of the group (i.e. less than n). @@ -292,7 +276,7 @@ func modInverse(a, n *big.Int) (ia *big.Int, ok bool) { g := new(big.Int) x := new(big.Int) y := new(big.Int) - big.GcdInt(g, x, y, a, n) + g.GCD(x, y, a, n) if g.Cmp(bigOne) != 0 { // In this case, a and n aren't coprime and we cannot calculate // the inverse. This happens because the values of n are nearly @@ -343,10 +327,10 @@ func (priv *PrivateKey) Precompute() { // decrypt performs an RSA decryption, resulting in a plaintext integer. If a // random source is given, RSA blinding is used. -func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.Error) { +func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) { // TODO(agl): can we get away with reusing blinds? if c.Cmp(priv.N) > 0 { - err = DecryptionError{} + err = ErrDecryption return } @@ -420,12 +404,12 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os } // DecryptOAEP decrypts ciphertext using RSA-OAEP. -// If rand != nil, DecryptOAEP uses RSA blinding to avoid timing side-channel attacks. -func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err os.Error) { +// If random != nil, DecryptOAEP uses RSA blinding to avoid timing side-channel attacks. +func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err error) { k := (priv.N.BitLen() + 7) / 8 if len(ciphertext) > k || k < hash.Size()*2+2 { - err = DecryptionError{} + err = ErrDecryption return } @@ -437,7 +421,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext } hash.Write(label) - lHash := hash.Sum() + lHash := hash.Sum(nil) hash.Reset() // Converting the plaintext number to bytes will strip any @@ -481,7 +465,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext } if firstByteIsZero&lHash2Good&^invalid&^lookingForIndex != 1 { - err = DecryptionError{} + err = ErrDecryption return } diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go index c36bca1cd..0fb9875d0 100644 --- a/src/pkg/crypto/rsa/rsa_test.go +++ b/src/pkg/crypto/rsa/rsa_test.go @@ -5,10 +5,10 @@ package rsa import ( - "big" "bytes" "crypto/rand" "crypto/sha1" + "math/big" "testing" ) diff --git a/src/pkg/crypto/sha1/Makefile b/src/pkg/crypto/sha1/Makefile deleted file mode 100644 index 81ac38c0b..000000000 --- a/src/pkg/crypto/sha1/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/sha1 -GOFILES=\ - sha1.go\ - sha1block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/sha1/sha1.go b/src/pkg/crypto/sha1/sha1.go index 788d1ff55..876e7992a 100644 --- a/src/pkg/crypto/sha1/sha1.go +++ b/src/pkg/crypto/sha1/sha1.go @@ -8,7 +8,6 @@ package sha1 import ( "crypto" "hash" - "os" ) func init() { @@ -18,6 +17,9 @@ func init() { // The size of a SHA1 checksum in bytes. const Size = 20 +// The blocksize of SHA1 in bytes. +const BlockSize = 64 + const ( _Chunk = 64 _Init0 = 0x67452301 @@ -54,7 +56,9 @@ func New() hash.Hash { func (d *digest) Size() int { return Size } -func (d *digest) Write(p []byte) (nn int, err os.Error) { +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -80,10 +84,9 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. - d := new(digest) - *d = *d0 + d := *d0 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. len := d.len @@ -106,14 +109,13 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 20) - j := 0 - for _, s := range d.h { - p[j+0] = byte(s >> 24) - p[j+1] = byte(s >> 16) - p[j+2] = byte(s >> 8) - p[j+3] = byte(s >> 0) - j += 4 + var digest [Size]byte + for i, s := range d.h { + digest[i*4] = byte(s >> 24) + digest[i*4+1] = byte(s >> 16) + digest[i*4+2] = byte(s >> 8) + digest[i*4+3] = byte(s) } - return p + + return append(in, digest[:]...) } diff --git a/src/pkg/crypto/sha1/sha1_test.go b/src/pkg/crypto/sha1/sha1_test.go index 2712fe35e..2dc14ac98 100644 --- a/src/pkg/crypto/sha1/sha1_test.go +++ b/src/pkg/crypto/sha1/sha1_test.go @@ -4,9 +4,10 @@ // SHA1 hash algorithm. See RFC 3174. -package sha1 +package sha1_test import ( + "crypto/sha1" "fmt" "io" "testing" @@ -54,16 +55,16 @@ var golden = []sha1Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] - c := New() + c := sha1.New() for j := 0; j < 3; j++ { if j < 2 { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha1[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -71,3 +72,10 @@ func TestGolden(t *testing.T) { } } } + +func ExampleNew() { + h := sha1.New() + io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.") + fmt.Printf("% x", h.Sum(nil)) + // Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd +} diff --git a/src/pkg/crypto/sha256/Makefile b/src/pkg/crypto/sha256/Makefile deleted file mode 100644 index 97fe4d8e6..000000000 --- a/src/pkg/crypto/sha256/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/sha256 -GOFILES=\ - sha256.go\ - sha256block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/sha256/sha256.go b/src/pkg/crypto/sha256/sha256.go index a2c058d18..a61e30b42 100644 --- a/src/pkg/crypto/sha256/sha256.go +++ b/src/pkg/crypto/sha256/sha256.go @@ -9,7 +9,6 @@ package sha256 import ( "crypto" "hash" - "os" ) func init() { @@ -23,6 +22,9 @@ const Size = 32 // The size of a SHA224 checksum in bytes. const Size224 = 28 +// The blocksize of SHA256 and SHA224 in bytes. +const BlockSize = 64 + const ( _Chunk = 64 _Init0 = 0x6A09E667 @@ -98,7 +100,9 @@ func (d *digest) Size() int { return Size224 } -func (d *digest) Write(p []byte) (nn int, err os.Error) { +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -124,10 +128,9 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. - d := new(digest) - *d = *d0 + d := *d0 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. len := d.len @@ -150,17 +153,20 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 32) - j := 0 - for _, s := range d.h { - p[j+0] = byte(s >> 24) - p[j+1] = byte(s >> 16) - p[j+2] = byte(s >> 8) - p[j+3] = byte(s >> 0) - j += 4 - } + h := d.h[:] + size := Size if d.is224 { - return p[0:28] + h = d.h[:7] + size = Size224 } - return p + + var digest [Size]byte + for i, s := range h { + digest[i*4] = byte(s >> 24) + digest[i*4+1] = byte(s >> 16) + digest[i*4+2] = byte(s >> 8) + digest[i*4+3] = byte(s) + } + + return append(in, digest[:size]...) } diff --git a/src/pkg/crypto/sha256/sha256_test.go b/src/pkg/crypto/sha256/sha256_test.go index 42a3fa7a0..a6efb3754 100644 --- a/src/pkg/crypto/sha256/sha256_test.go +++ b/src/pkg/crypto/sha256/sha256_test.go @@ -94,10 +94,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha256[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -112,10 +112,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha224[%d](%s) = %s want %s", j, g.in, s, g.out) } diff --git a/src/pkg/crypto/sha512/Makefile b/src/pkg/crypto/sha512/Makefile deleted file mode 100644 index 2f7633fa3..000000000 --- a/src/pkg/crypto/sha512/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/sha512 -GOFILES=\ - sha512.go\ - sha512block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/sha512/sha512.go b/src/pkg/crypto/sha512/sha512.go index 78f5fe26f..a245fd68e 100644 --- a/src/pkg/crypto/sha512/sha512.go +++ b/src/pkg/crypto/sha512/sha512.go @@ -9,7 +9,6 @@ package sha512 import ( "crypto" "hash" - "os" ) func init() { @@ -23,6 +22,9 @@ const Size = 64 // The size of a SHA384 checksum in bytes. const Size384 = 48 +// The blocksize of SHA512 and SHA384 in bytes. +const BlockSize = 128 + const ( _Chunk = 128 _Init0 = 0x6a09e667f3bcc908 @@ -98,7 +100,9 @@ func (d *digest) Size() int { return Size384 } -func (d *digest) Write(p []byte) (nn int, err os.Error) { +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -124,7 +128,7 @@ func (d *digest) Write(p []byte) (nn int, err os.Error) { return } -func (d0 *digest) Sum() []byte { +func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := new(digest) *d = *d0 @@ -150,21 +154,24 @@ func (d0 *digest) Sum() []byte { panic("d.nx != 0") } - p := make([]byte, 64) - j := 0 - for _, s := range d.h { - p[j+0] = byte(s >> 56) - p[j+1] = byte(s >> 48) - p[j+2] = byte(s >> 40) - p[j+3] = byte(s >> 32) - p[j+4] = byte(s >> 24) - p[j+5] = byte(s >> 16) - p[j+6] = byte(s >> 8) - p[j+7] = byte(s >> 0) - j += 8 - } + h := d.h[:] + size := Size if d.is384 { - return p[0:48] + h = d.h[:6] + size = Size384 } - return p + + var digest [Size]byte + for i, s := range h { + digest[i*8] = byte(s >> 56) + digest[i*8+1] = byte(s >> 48) + digest[i*8+2] = byte(s >> 40) + digest[i*8+3] = byte(s >> 32) + digest[i*8+4] = byte(s >> 24) + digest[i*8+5] = byte(s >> 16) + digest[i*8+6] = byte(s >> 8) + digest[i*8+7] = byte(s) + } + + return append(in, digest[:size]...) } diff --git a/src/pkg/crypto/sha512/sha512_test.go b/src/pkg/crypto/sha512/sha512_test.go index dd116dc17..a70f7c54e 100644 --- a/src/pkg/crypto/sha512/sha512_test.go +++ b/src/pkg/crypto/sha512/sha512_test.go @@ -94,10 +94,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha512[%d](%s) = %s want %s", j, g.in, s, g.out) } @@ -112,10 +112,10 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) - c.Sum() + c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } - s := fmt.Sprintf("%x", c.Sum()) + s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("sha384[%d](%s) = %s want %s", j, g.in, s, g.out) } diff --git a/src/pkg/crypto/subtle/Makefile b/src/pkg/crypto/subtle/Makefile deleted file mode 100644 index 08d8bbfa0..000000000 --- a/src/pkg/crypto/subtle/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/subtle -GOFILES=\ - constant_time.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/tls/Makefile b/src/pkg/crypto/tls/Makefile deleted file mode 100644 index 000314be5..000000000 --- a/src/pkg/crypto/tls/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/tls -GOFILES=\ - alert.go\ - cipher_suites.go\ - common.go\ - conn.go\ - handshake_client.go\ - handshake_messages.go\ - handshake_server.go\ - key_agreement.go\ - prf.go\ - tls.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/tls/alert.go b/src/pkg/crypto/tls/alert.go index 3b9e0e241..0856311e4 100644 --- a/src/pkg/crypto/tls/alert.go +++ b/src/pkg/crypto/tls/alert.go @@ -71,3 +71,7 @@ func (e alert) String() string { } return "alert(" + strconv.Itoa(int(e)) + ")" } + +func (e alert) Error() string { + return e.String() +} diff --git a/src/pkg/crypto/tls/cipher_suites.go b/src/pkg/crypto/tls/cipher_suites.go index bc7b0d32f..00695e7d1 100644 --- a/src/pkg/crypto/tls/cipher_suites.go +++ b/src/pkg/crypto/tls/cipher_suites.go @@ -7,11 +7,12 @@ package tls import ( "crypto/aes" "crypto/cipher" + "crypto/des" "crypto/hmac" "crypto/rc4" + "crypto/sha1" "crypto/x509" "hash" - "os" ) // a keyAgreement implements the client and server side of a TLS key agreement @@ -22,20 +23,21 @@ type keyAgreement interface { // In the case that the key agreement protocol doesn't use a // ServerKeyExchange message, generateServerKeyExchange can return nil, // nil. - generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) - processClientKeyExchange(*Config, *clientKeyExchangeMsg) ([]byte, os.Error) + generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error) + processClientKeyExchange(*Config, *clientKeyExchangeMsg, uint16) ([]byte, error) // On the client side, the next two methods are called in order. // This method may not be called if the server doesn't send a // ServerKeyExchange message. - processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) os.Error - generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) + processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error + generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) } // A cipherSuite is a specific combination of key agreement, cipher and MAC // function. All cipher suites currently assume RSA key agreement. type cipherSuite struct { + id uint16 // the lengths, in bytes, of the key material needed for each component. keyLen int macLen int @@ -46,14 +48,16 @@ type cipherSuite struct { // and point format that we can handle. elliptic bool cipher func(key, iv []byte, isRead bool) interface{} - mac func(macKey []byte) hash.Hash + mac func(version uint16, macKey []byte) macFunction } -var cipherSuites = map[uint16]*cipherSuite{ - TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, hmacSHA1}, - TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, hmacSHA1}, - TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, hmacSHA1}, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, hmacSHA1}, +var cipherSuites = []*cipherSuite{ + {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1}, + {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1}, + {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1}, + {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1}, + {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, } func cipherRC4(key, iv []byte, isRead bool) interface{} { @@ -61,6 +65,14 @@ func cipherRC4(key, iv []byte, isRead bool) interface{} { return cipher } +func cipher3DES(key, iv []byte, isRead bool) interface{} { + block, _ := des.NewTripleDESCipher(key) + if isRead { + return cipher.NewCBCDecrypter(block, iv) + } + return cipher.NewCBCEncrypter(block, iv) +} + func cipherAES(key, iv []byte, isRead bool) interface{} { block, _ := aes.NewCipher(key) if isRead { @@ -69,8 +81,75 @@ func cipherAES(key, iv []byte, isRead bool) interface{} { return cipher.NewCBCEncrypter(block, iv) } -func hmacSHA1(key []byte) hash.Hash { - return hmac.NewSHA1(key) +// macSHA1 returns a macFunction for the given protocol version. +func macSHA1(version uint16, key []byte) macFunction { + if version == versionSSL30 { + mac := ssl30MAC{ + h: sha1.New(), + key: make([]byte, len(key)), + } + copy(mac.key, key) + return mac + } + return tls10MAC{hmac.New(sha1.New, key)} +} + +type macFunction interface { + Size() int + MAC(digestBuf, seq, data []byte) []byte +} + +// ssl30MAC implements the SSLv3 MAC function, as defined in +// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1 +type ssl30MAC struct { + h hash.Hash + key []byte +} + +func (s ssl30MAC) Size() int { + return s.h.Size() +} + +var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36} + +var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c} + +func (s ssl30MAC) MAC(digestBuf, seq, record []byte) []byte { + padLength := 48 + if s.h.Size() == 20 { + padLength = 40 + } + + s.h.Reset() + s.h.Write(s.key) + s.h.Write(ssl30Pad1[:padLength]) + s.h.Write(seq) + s.h.Write(record[:1]) + s.h.Write(record[3:5]) + s.h.Write(record[recordHeaderLen:]) + digestBuf = s.h.Sum(digestBuf[:0]) + + s.h.Reset() + s.h.Write(s.key) + s.h.Write(ssl30Pad2[:padLength]) + s.h.Write(digestBuf) + return s.h.Sum(digestBuf[:0]) +} + +// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3. +type tls10MAC struct { + h hash.Hash +} + +func (s tls10MAC) Size() int { + return s.h.Size() +} + +func (s tls10MAC) MAC(digestBuf, seq, record []byte) []byte { + s.h.Reset() + s.h.Write(seq) + s.h.Write(record) + return s.h.Sum(digestBuf[:0]) } func rsaKA() keyAgreement { @@ -81,22 +160,29 @@ func ecdheRSAKA() keyAgreement { return new(ecdheRSAKeyAgreement) } -// mutualCipherSuite returns a cipherSuite and its id given a list of supported +// mutualCipherSuite returns a cipherSuite given a list of supported // ciphersuites and the id requested by the peer. -func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint16) { +func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { for _, id := range have { if id == want { - return cipherSuites[id], id + for _, suite := range cipherSuites { + if suite.id == want { + return suite + } + } + return nil } } - return + return nil } // A list of the possible cipher suite ids. Taken from // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml const ( - TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 - TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f - TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 + TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 + TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a + TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f + TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 ) diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go index 3efac9c13..4ba0bf874 100644 --- a/src/pkg/crypto/tls/common.go +++ b/src/pkg/crypto/tls/common.go @@ -5,11 +5,11 @@ package tls import ( + "crypto" "crypto/rand" - "crypto/rsa" "crypto/x509" "io" - "io/ioutil" + "strings" "sync" "time" ) @@ -20,8 +20,11 @@ const ( recordHeaderLen = 5 // record header length maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) - minVersion = 0x0301 // minimum supported version - TLS 1.0 - maxVersion = 0x0301 // maximum supported version - TLS 1.0 + versionSSL30 = 0x0300 + versionTLS10 = 0x0301 + + minVersion = versionSSL30 + maxVersion = versionTLS10 ) // TLS record types. @@ -98,12 +101,28 @@ type ConnectionState struct { NegotiatedProtocol string NegotiatedProtocolIsMutual bool + // ServerName contains the server name indicated by the client, if any. + // (Only valid for server connections.) + ServerName string + // the certificate chain that was presented by the other side PeerCertificates []*x509.Certificate // the verified certificate chains built from PeerCertificates. VerifiedChains [][]*x509.Certificate } +// ClientAuthType declares the policy the server will follow for +// TLS Client Authentication. +type ClientAuthType int + +const ( + NoClientCert ClientAuthType = iota + RequestClientCert + RequireAnyClientCert + VerifyClientCertIfGiven + RequireAndVerifyClientCert +) + // A Config structure is used to configure a TLS client or server. After one // has been passed to a TLS function it must not be modified. type Config struct { @@ -113,14 +132,22 @@ type Config struct { Rand io.Reader // Time returns the current time as the number of seconds since the epoch. - // If Time is nil, TLS uses the system time.Seconds. - Time func() int64 + // If Time is nil, TLS uses time.Now. + Time func() time.Time // Certificates contains one or more certificate chains // to present to the other side of the connection. // Server configurations must include at least one certificate. Certificates []Certificate + // NameToCertificate maps from a certificate name to an element of + // Certificates. Note that a certificate name can be of the form + // '*.example.com' and so doesn't have to be a domain name as such. + // See Config.BuildNameToCertificate + // The nil value causes the first element of Certificates to be used + // for all connections. + NameToCertificate map[string]*Certificate + // RootCAs defines the set of root certificate authorities // that clients use when verifying server certificates. // If RootCAs is nil, TLS uses the host's root CA set. @@ -133,11 +160,22 @@ type Config struct { // hosting. ServerName string - // AuthenticateClient controls whether a server will request a certificate - // from the client. It does not require that the client send a - // certificate nor does it require that the certificate sent be - // anything more than self-signed. - AuthenticateClient bool + // ClientAuth determines the server's policy for + // TLS Client Authentication. The default is NoClientCert. + ClientAuth ClientAuthType + + // ClientCAs defines the set of root certificate authorities + // that servers use if required to verify a client certificate + // by the policy in ClientAuth. + ClientCAs *x509.CertPool + + // InsecureSkipVerify controls whether a client verifies the + // server's certificate chain and host name. + // If InsecureSkipVerify is true, TLS accepts any certificate + // presented by the server and any host name in that certificate. + // In this mode, TLS is susceptible to man-in-the-middle attacks. + // This should be used only for testing. + InsecureSkipVerify bool // CipherSuites is a list of supported cipher suites. If CipherSuites // is nil, TLS uses a list of suites supported by the implementation. @@ -152,22 +190,14 @@ func (c *Config) rand() io.Reader { return r } -func (c *Config) time() int64 { +func (c *Config) time() time.Time { t := c.Time if t == nil { - t = time.Seconds + t = time.Now } return t() } -func (c *Config) rootCAs() *x509.CertPool { - s := c.RootCAs - if s == nil { - s = defaultRoots() - } - return s -} - func (c *Config) cipherSuites() []uint16 { s := c.CipherSuites if s == nil { @@ -176,13 +206,71 @@ func (c *Config) cipherSuites() []uint16 { return s } +// getCertificateForName returns the best certificate for the given name, +// defaulting to the first element of c.Certificates if there are no good +// options. +func (c *Config) getCertificateForName(name string) *Certificate { + if len(c.Certificates) == 1 || c.NameToCertificate == nil { + // There's only one choice, so no point doing any work. + return &c.Certificates[0] + } + + name = strings.ToLower(name) + for len(name) > 0 && name[len(name)-1] == '.' { + name = name[:len(name)-1] + } + + if cert, ok := c.NameToCertificate[name]; ok { + return cert + } + + // try replacing labels in the name with wildcards until we get a + // match. + labels := strings.Split(name, ".") + for i := range labels { + labels[i] = "*" + candidate := strings.Join(labels, ".") + if cert, ok := c.NameToCertificate[candidate]; ok { + return cert + } + } + + // If nothing matches, return the first certificate. + return &c.Certificates[0] +} + +// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate +// from the CommonName and SubjectAlternateName fields of each of the leaf +// certificates. +func (c *Config) BuildNameToCertificate() { + c.NameToCertificate = make(map[string]*Certificate) + for i := range c.Certificates { + cert := &c.Certificates[i] + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + continue + } + if len(x509Cert.Subject.CommonName) > 0 { + c.NameToCertificate[x509Cert.Subject.CommonName] = cert + } + for _, san := range x509Cert.DNSNames { + c.NameToCertificate[san] = cert + } + } +} + // A Certificate is a chain of one or more certificates, leaf first. type Certificate struct { Certificate [][]byte - PrivateKey *rsa.PrivateKey + PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey // OCSPStaple contains an optional OCSP response which will be served // to clients that request it. OCSPStaple []byte + // Leaf is the parsed form of the leaf certificate, which may be + // initialized using x509.ParseCertificate to reduce per-handshake + // processing for TLS clients doing client authentication. If nil, the + // leaf certificate will be parsed as needed. + Leaf *x509.Certificate } // A TLS record. @@ -215,53 +303,19 @@ func defaultConfig() *Config { return &emptyConfig } -// Possible certificate files; stop after finding one. -// On OS X we should really be using the Directory Services keychain -// but that requires a lot of Mach goo to get at. Instead we use -// the same root set that curl uses. -var certFiles = []string{ - "/etc/ssl/certs/ca-certificates.crt", // Linux etc - "/usr/share/curl/curl-ca-bundle.crt", // OS X -} - -var once sync.Once - -func defaultRoots() *x509.CertPool { - once.Do(initDefaults) - return varDefaultRoots -} +var ( + once sync.Once + varDefaultCipherSuites []uint16 +) func defaultCipherSuites() []uint16 { - once.Do(initDefaults) + once.Do(initDefaultCipherSuites) return varDefaultCipherSuites } -func initDefaults() { - initDefaultRoots() - initDefaultCipherSuites() -} - -var varDefaultRoots *x509.CertPool - -func initDefaultRoots() { - roots := x509.NewCertPool() - for _, file := range certFiles { - data, err := ioutil.ReadFile(file) - if err == nil { - roots.AppendCertsFromPEM(data) - break - } - } - varDefaultRoots = roots -} - -var varDefaultCipherSuites []uint16 - func initDefaultCipherSuites() { varDefaultCipherSuites = make([]uint16, len(cipherSuites)) - i := 0 - for id := range cipherSuites { - varDefaultCipherSuites[i] = id - i++ + for i, suite := range cipherSuites { + varDefaultCipherSuites[i] = suite.id } } diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go index fac65afd9..2a5115dc6 100644 --- a/src/pkg/crypto/tls/conn.go +++ b/src/pkg/crypto/tls/conn.go @@ -11,11 +11,11 @@ import ( "crypto/cipher" "crypto/subtle" "crypto/x509" - "hash" + "errors" "io" "net" - "os" "sync" + "time" ) // A Conn represents a secured connection. @@ -37,13 +37,15 @@ type Conn struct { // verifiedChains contains the certificate chains that we built, as // opposed to the ones presented by the server. verifiedChains [][]*x509.Certificate + // serverName contains the server name indicated by the client, if any. + serverName string clientProtocol string clientProtocolFallback bool // first permanent error errMutex sync.Mutex - err os.Error + err error // input/output in, out halfConn // in.Mutex < out.Mutex @@ -54,7 +56,7 @@ type Conn struct { tmp [16]byte } -func (c *Conn) setError(err os.Error) os.Error { +func (c *Conn) setError(err error) error { c.errMutex.Lock() defer c.errMutex.Unlock() @@ -64,7 +66,7 @@ func (c *Conn) setError(err os.Error) os.Error { return err } -func (c *Conn) error() os.Error { +func (c *Conn) error() error { c.errMutex.Lock() defer c.errMutex.Unlock() @@ -85,48 +87,54 @@ func (c *Conn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } -// SetTimeout sets the read deadline associated with the connection. -// There is no write deadline. -func (c *Conn) SetTimeout(nsec int64) os.Error { - return c.conn.SetTimeout(nsec) +// SetDeadline sets the read and write deadlines associated with the connection. +// A zero value for t means Read and Write will not time out. +// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. +func (c *Conn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) } -// SetReadTimeout sets the time (in nanoseconds) that -// Read will wait for data before returning os.EAGAIN. -// Setting nsec == 0 (the default) disables the deadline. -func (c *Conn) SetReadTimeout(nsec int64) os.Error { - return c.conn.SetReadTimeout(nsec) +// SetReadDeadline sets the read deadline on the underlying connection. +// A zero value for t means Read will not time out. +func (c *Conn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) } -// SetWriteTimeout exists to satisfy the net.Conn interface -// but is not implemented by TLS. It always returns an error. -func (c *Conn) SetWriteTimeout(nsec int64) os.Error { - return os.NewError("TLS does not support SetWriteTimeout") +// SetWriteDeadline sets the write deadline on the underlying conneciton. +// A zero value for t means Write will not time out. +// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. +func (c *Conn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) } // A halfConn represents one direction of the record layer // connection, either sending or receiving. type halfConn struct { sync.Mutex - cipher interface{} // cipher algorithm - mac hash.Hash // MAC algorithm - seq [8]byte // 64-bit sequence number - bfree *block // list of free blocks + version uint16 // protocol version + cipher interface{} // cipher algorithm + mac macFunction + seq [8]byte // 64-bit sequence number + bfree *block // list of free blocks nextCipher interface{} // next encryption state - nextMac hash.Hash // next MAC algorithm + nextMac macFunction // next MAC algorithm + + // used to save allocating a new buffer for each MAC. + inDigestBuf, outDigestBuf []byte } // prepareCipherSpec sets the encryption and MAC states // that a subsequent changeCipherSpec will use. -func (hc *halfConn) prepareCipherSpec(cipher interface{}, mac hash.Hash) { +func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) { + hc.version = version hc.nextCipher = cipher hc.nextMac = mac } // changeCipherSpec changes the encryption and MAC states // to the ones previously passed to prepareCipherSpec. -func (hc *halfConn) changeCipherSpec() os.Error { +func (hc *halfConn) changeCipherSpec() error { if hc.nextCipher == nil { return alertInternalError } @@ -197,6 +205,22 @@ func removePadding(payload []byte) ([]byte, byte) { return payload[:len(payload)-int(toRemove)], good } +// removePaddingSSL30 is a replacement for removePadding in the case that the +// protocol version is SSLv3. In this version, the contents of the padding +// are random and cannot be checked. +func removePaddingSSL30(payload []byte) ([]byte, byte) { + if len(payload) < 1 { + return payload, 0 + } + + paddingLen := int(payload[len(payload)-1]) + 1 + if paddingLen > len(payload) { + return payload, 0 + } + + return payload[:len(payload)-paddingLen], 255 +} + func roundUp(a, b int) int { return a + (b-a%b)%b } @@ -226,7 +250,11 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { } c.CryptBlocks(payload, payload) - payload, paddingGood = removePadding(payload) + if hc.version == versionSSL30 { + payload, paddingGood = removePaddingSSL30(payload) + } else { + payload, paddingGood = removePadding(payload) + } b.resize(recordHeaderLen + len(payload)) // note that we still have a timing side-channel in the @@ -256,15 +284,13 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { b.data[4] = byte(n) b.resize(recordHeaderLen + n) remoteMAC := payload[n:] - - hc.mac.Reset() - hc.mac.Write(hc.seq[0:]) + localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data) hc.incSeq() - hc.mac.Write(b.data) - if subtle.ConstantTimeCompare(hc.mac.Sum(), remoteMAC) != 1 || paddingGood != 255 { + if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { return false, alertBadRecordMAC } + hc.inDigestBuf = localMAC } return true, 0 @@ -291,14 +317,13 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) { func (hc *halfConn) encrypt(b *block) (bool, alert) { // mac if hc.mac != nil { - hc.mac.Reset() - hc.mac.Write(hc.seq[0:]) + mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data) hc.incSeq() - hc.mac.Write(b.data) - mac := hc.mac.Sum() + n := len(b.data) b.resize(n + len(mac)) copy(b.data[n:], mac) + hc.outDigestBuf = mac } payload := b.data[recordHeaderLen:] @@ -360,7 +385,7 @@ func (b *block) reserve(n int) { // readFromUntil reads from r into b until b contains at least n bytes // or else returns an error. -func (b *block) readFromUntil(r io.Reader, n int) os.Error { +func (b *block) readFromUntil(r io.Reader, n int) error { // quick case if len(b.data) >= n { return nil @@ -381,7 +406,7 @@ func (b *block) readFromUntil(r io.Reader, n int) os.Error { return nil } -func (b *block) Read(p []byte) (n int, err os.Error) { +func (b *block) Read(p []byte) (n int, err error) { n = copy(p, b.data[b.off:]) b.off += n return @@ -425,7 +450,7 @@ func (hc *halfConn) splitBlock(b *block, n int) (*block, *block) { // readRecord reads the next TLS record from the connection // and updates the record layer state. // c.in.Mutex <= L; c.input == nil. -func (c *Conn) readRecord(want recordType) os.Error { +func (c *Conn) readRecord(want recordType) error { // Caller must be in sync with connection: // handshake data if handshake not yet completed, // else application data. (We don't support renegotiation.) @@ -453,7 +478,7 @@ Again: // RFC suggests that EOF without an alertCloseNotify is // an error, but popular web sites seem to do this, // so we can't make it an error. - // if err == os.EOF { + // if err == io.EOF { // err = io.ErrUnexpectedEOF // } if e, ok := err.(net.Error); !ok || !e.Temporary() { @@ -470,8 +495,21 @@ Again: if n > maxCiphertext { return c.sendAlert(alertRecordOverflow) } + if !c.haveVers { + // First message, be extra suspicious: + // this might not be a TLS client. + // Bail out before reading a full 'body', if possible. + // The current max version is 3.1. + // If the version is >= 16.0, it's probably not real. + // Similarly, a clientHello message encodes in + // well under a kilobyte. If the length is >= 12 kB, + // it's probably not real. + if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 { + return c.sendAlert(alertUnexpectedMessage) + } + } if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil { - if err == os.EOF { + if err == io.EOF { err = io.ErrUnexpectedEOF } if e, ok := err.(net.Error); !ok || !e.Temporary() { @@ -503,7 +541,7 @@ Again: break } if alert(data[1]) == alertCloseNotify { - c.setError(os.EOF) + c.setError(io.EOF) break } switch data[0] { @@ -512,7 +550,7 @@ Again: c.in.freeBlock(b) goto Again case alertLevelError: - c.setError(&net.OpError{Op: "remote error", Error: alert(data[1])}) + c.setError(&net.OpError{Op: "remote error", Err: alert(data[1])}) default: c.sendAlert(alertUnexpectedMessage) } @@ -551,7 +589,7 @@ Again: // sendAlert sends a TLS alert message. // c.out.Mutex <= L. -func (c *Conn) sendAlertLocked(err alert) os.Error { +func (c *Conn) sendAlertLocked(err alert) error { c.tmp[0] = alertLevelError if err == alertNoRenegotiation { c.tmp[0] = alertLevelWarning @@ -560,14 +598,14 @@ func (c *Conn) sendAlertLocked(err alert) os.Error { c.writeRecord(recordTypeAlert, c.tmp[0:2]) // closeNotify is a special case in that it isn't an error: if err != alertCloseNotify { - return c.setError(&net.OpError{Op: "local error", Error: err}) + return c.setError(&net.OpError{Op: "local error", Err: err}) } return nil } // sendAlert sends a TLS alert message. // L < c.out.Mutex. -func (c *Conn) sendAlert(err alert) os.Error { +func (c *Conn) sendAlert(err alert) error { c.out.Lock() defer c.out.Unlock() return c.sendAlertLocked(err) @@ -576,7 +614,7 @@ func (c *Conn) sendAlert(err alert) os.Error { // writeRecord writes a TLS record with the given type and payload // to the connection and updates the record layer state. // c.out.Mutex <= L. -func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err os.Error) { +func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { b := c.out.newBlock() for len(data) > 0 { m := len(data) @@ -612,7 +650,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err os.Error) { c.tmp[0] = alertLevelError c.tmp[1] = byte(err.(alert)) c.writeRecord(recordTypeAlert, c.tmp[0:2]) - c.err = &net.OpError{Op: "local error", Error: err} + c.err = &net.OpError{Op: "local error", Err: err} return n, c.err } } @@ -622,12 +660,14 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err os.Error) { // readHandshake reads the next handshake message from // the record layer. // c.in.Mutex < L; c.out.Mutex < L. -func (c *Conn) readHandshake() (interface{}, os.Error) { +func (c *Conn) readHandshake() (interface{}, error) { for c.hand.Len() < 4 { if c.err != nil { return nil, c.err } - c.readRecord(recordTypeHandshake) + if err := c.readRecord(recordTypeHandshake); err != nil { + return nil, err + } } data := c.hand.Bytes() @@ -640,7 +680,9 @@ func (c *Conn) readHandshake() (interface{}, os.Error) { if c.err != nil { return nil, c.err } - c.readRecord(recordTypeHandshake) + if err := c.readRecord(recordTypeHandshake); err != nil { + return nil, err + } } data = c.hand.Next(4 + n) var m handshakeMessage @@ -685,9 +727,13 @@ func (c *Conn) readHandshake() (interface{}, os.Error) { } // Write writes data to the connection. -func (c *Conn) Write(b []byte) (n int, err os.Error) { - if err = c.Handshake(); err != nil { - return +func (c *Conn) Write(b []byte) (int, error) { + if c.err != nil { + return 0, c.err + } + + if c.err = c.Handshake(); c.err != nil { + return 0, c.err } c.out.Lock() @@ -696,15 +742,15 @@ func (c *Conn) Write(b []byte) (n int, err os.Error) { if !c.handshakeComplete { return 0, alertInternalError } - if c.err != nil { - return 0, c.err - } - return c.writeRecord(recordTypeApplicationData, b) + + var n int + n, c.err = c.writeRecord(recordTypeApplicationData, b) + return n, c.err } -// Read can be made to time out and return err == os.EAGAIN -// after a fixed time limit; see SetTimeout and SetReadTimeout. -func (c *Conn) Read(b []byte) (n int, err os.Error) { +// Read can be made to time out and return a net.Error with Timeout() == true +// after a fixed time limit; see SetDeadline and SetReadDeadline. +func (c *Conn) Read(b []byte) (n int, err error) { if err = c.Handshake(); err != nil { return } @@ -730,18 +776,26 @@ func (c *Conn) Read(b []byte) (n int, err os.Error) { } // Close closes the connection. -func (c *Conn) Close() os.Error { - if err := c.Handshake(); err != nil { +func (c *Conn) Close() error { + var alertErr error + + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + if c.handshakeComplete { + alertErr = c.sendAlert(alertCloseNotify) + } + + if err := c.conn.Close(); err != nil { return err } - return c.sendAlert(alertCloseNotify) + return alertErr } // Handshake runs the client or server handshake // protocol if it has not yet been run. // Most uses of this package need not call Handshake // explicitly: the first Read or Write will call it automatically. -func (c *Conn) Handshake() os.Error { +func (c *Conn) Handshake() error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if err := c.error(); err != nil { @@ -769,6 +823,7 @@ func (c *Conn) ConnectionState() ConnectionState { state.CipherSuite = c.cipherSuite state.PeerCertificates = c.peerCertificates state.VerifiedChains = c.verifiedChains + state.ServerName = c.serverName } return state @@ -784,16 +839,16 @@ func (c *Conn) OCSPResponse() []byte { } // VerifyHostname checks that the peer certificate chain is valid for -// connecting to host. If so, it returns nil; if not, it returns an os.Error +// connecting to host. If so, it returns nil; if not, it returns an error // describing the problem. -func (c *Conn) VerifyHostname(host string) os.Error { +func (c *Conn) VerifyHostname(host string) error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if !c.isClient { - return os.NewError("VerifyHostname called on TLS server connection") + return errors.New("VerifyHostname called on TLS server connection") } if !c.handshakeComplete { - return os.NewError("TLS handshake has not yet been performed") + return errors.New("TLS handshake has not yet been performed") } return c.peerCertificates[0].VerifyHostname(host) } diff --git a/src/pkg/crypto/tls/conn_test.go b/src/pkg/crypto/tls/conn_test.go index f44a50bed..5c555147c 100644 --- a/src/pkg/crypto/tls/conn_test.go +++ b/src/pkg/crypto/tls/conn_test.go @@ -50,3 +50,57 @@ func TestRemovePadding(t *testing.T) { } } } + +var certExampleCom = `308201403081eda003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313138353835325a170d3132303933303138353835325a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31a301830160603551d11040f300d820b6578616d706c652e636f6d300b06092a864886f70d0101050341001a0b419d2c74474c6450654e5f10b32bf426ffdf55cad1c52602e7a9151513a3424c70f5960dcd682db0c33769cc1daa3fcdd3db10809d2392ed4a1bf50ced18` + +var certWildcardExampleCom = `308201423081efa003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303034365a170d3132303933303139303034365a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31c301a30180603551d110411300f820d2a2e6578616d706c652e636f6d300b06092a864886f70d0101050341001676f0c9e7c33c1b656ed5a6476c4e2ee9ec8e62df7407accb1875272b2edd0a22096cb2c22598d11604104d604f810eb4b5987ca6bb319c7e6ce48725c54059` + +var certFooExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303131345a170d3132303933303139303131345a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104133011820f666f6f2e6578616d706c652e636f6d300b06092a864886f70d010105034100646a2a51f2aa2477add854b462cf5207ba16d3213ffb5d3d0eed473fbf09935019192d1d5b8ca6a2407b424cf04d97c4cd9197c83ecf81f0eab9464a1109d09f` + +var certDoubleWildcardExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303134315a170d3132303933303139303134315a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104133011820f2a2e2a2e6578616d706c652e636f6d300b06092a864886f70d0101050341001c3de267975f56ef57771c6218ef95ecc65102e57bd1defe6f7efea90d9b26cf40de5bd7ad75e46201c7f2a92aaa3e907451e9409f65e28ddb6db80d726290f6` + +func TestCertificateSelection(t *testing.T) { + config := Config{ + Certificates: []Certificate{ + { + Certificate: [][]byte{fromHex(certExampleCom)}, + }, + { + Certificate: [][]byte{fromHex(certWildcardExampleCom)}, + }, + { + Certificate: [][]byte{fromHex(certFooExampleCom)}, + }, + { + Certificate: [][]byte{fromHex(certDoubleWildcardExampleCom)}, + }, + }, + } + + config.BuildNameToCertificate() + + pointerToIndex := func(c *Certificate) int { + for i := range config.Certificates { + if c == &config.Certificates[i] { + return i + } + } + return -1 + } + + if n := pointerToIndex(config.getCertificateForName("example.com")); n != 0 { + t.Errorf("example.com returned certificate %d, not 0", n) + } + if n := pointerToIndex(config.getCertificateForName("bar.example.com")); n != 1 { + t.Errorf("bar.example.com returned certificate %d, not 1", n) + } + if n := pointerToIndex(config.getCertificateForName("foo.example.com")); n != 2 { + t.Errorf("foo.example.com returned certificate %d, not 2", n) + } + if n := pointerToIndex(config.getCertificateForName("foo.bar.example.com")); n != 3 { + t.Errorf("foo.bar.example.com returned certificate %d, not 3", n) + } + if n := pointerToIndex(config.getCertificateForName("foo.bar.baz.example.com")); n != 0 { + t.Errorf("foo.bar.baz.example.com returned certificate %d, not 0", n) + } +} diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go index 41206e276..84be5bfd8 100644 --- a/src/pkg/crypto/tls/generate_cert.go +++ b/src/pkg/crypto/tls/generate_cert.go @@ -2,20 +2,22 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + // Generate a self-signed X.509 certificate for a TLS server. Outputs to // 'cert.pem' and 'key.pem' and will overwrite existing files. package main import ( - "big" - "crypto/x509/pkix" "crypto/rand" "crypto/rsa" "crypto/x509" + "crypto/x509/pkix" "encoding/pem" "flag" "log" + "math/big" "os" "time" ) @@ -31,7 +33,7 @@ func main() { return } - now := time.Seconds() + now := time.Now() template := x509.Certificate{ SerialNumber: new(big.Int).SetInt64(0), @@ -39,8 +41,8 @@ func main() { CommonName: *hostName, Organization: []string{"Acme Co"}, }, - NotBefore: time.SecondsToUTC(now - 300), - NotAfter: time.SecondsToUTC(now + 60*60*24*365), // valid for 1 year. + NotBefore: now.Add(-5 * time.Minute).UTC(), + NotAfter: now.AddDate(1, 0, 0).UTC(), // valid for 1 year. SubjectKeyId: []byte{1, 2, 3, 4}, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go index 15604cea7..2877f1738 100644 --- a/src/pkg/crypto/tls/handshake_client.go +++ b/src/pkg/crypto/tls/handshake_client.go @@ -5,16 +5,18 @@ package tls import ( + "bytes" "crypto" "crypto/rsa" "crypto/subtle" "crypto/x509" + "errors" "io" - "os" + "strconv" ) -func (c *Conn) clientHandshake() os.Error { - finishedHash := newFinishedHash() +func (c *Conn) clientHandshake() error { + finishedHash := newFinishedHash(versionTLS10) if c.config == nil { c.config = defaultConfig() @@ -32,7 +34,7 @@ func (c *Conn) clientHandshake() os.Error { nextProtoNeg: len(c.config.NextProtos) > 0, } - t := uint32(c.config.time()) + t := uint32(c.config.time().Unix()) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) hello.random[2] = byte(t >> 8) @@ -40,7 +42,7 @@ func (c *Conn) clientHandshake() os.Error { _, err := io.ReadFull(c.config.rand(), hello.random[4:]) if err != nil { c.sendAlert(alertInternalError) - return os.NewError("short read from Rand") + return errors.New("short read from Rand") } finishedHash.Write(hello.marshal()) @@ -57,7 +59,8 @@ func (c *Conn) clientHandshake() os.Error { finishedHash.Write(serverHello.marshal()) vers, ok := mutualVersion(serverHello.vers) - if !ok { + if !ok || vers < versionTLS10 { + // TLS 1.0 is the minimum version supported as a client. return c.sendAlert(alertProtocolVersion) } c.vers = vers @@ -69,10 +72,10 @@ func (c *Conn) clientHandshake() os.Error { if !hello.nextProtoNeg && serverHello.nextProtoNeg { c.sendAlert(alertHandshakeFailure) - return os.NewError("server advertised unrequested NPN") + return errors.New("server advertised unrequested NPN") } - suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) + suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) if suite == nil { return c.sendAlert(alertHandshakeFailure) } @@ -92,14 +95,12 @@ func (c *Conn) clientHandshake() os.Error { cert, err := x509.ParseCertificate(asn1Data) if err != nil { c.sendAlert(alertBadCertificate) - return os.NewError("failed to parse certificate from server: " + err.String()) + return errors.New("failed to parse certificate from server: " + err.Error()) } certs[i] = cert } - // If we don't have a root CA set configured then anything is accepted. - // TODO(rsc): Find certificates for OS X 10.6. - if c.config.RootCAs != nil { + if !c.config.InsecureSkipVerify { opts := x509.VerifyOptions{ Roots: c.config.RootCAs, CurrentTime: c.config.time(), @@ -164,10 +165,26 @@ func (c *Conn) clientHandshake() os.Error { } } - transmitCert := false + var certToSend *Certificate + var certRequested bool certReq, ok := msg.(*certificateRequestMsg) if ok { - // We only accept certificates with RSA keys. + certRequested = true + + // RFC 4346 on the certificateAuthorities field: + // A list of the distinguished names of acceptable certificate + // authorities. These distinguished names may specify a desired + // distinguished name for a root CA or for a subordinate CA; + // thus, this message can be used to describe both known roots + // and a desired authorization space. If the + // certificate_authorities list is empty then the client MAY + // send any certificate of the appropriate + // ClientCertificateType, unless there is some external + // arrangement to the contrary. + + finishedHash.Write(certReq.marshal()) + + // For now, we only know how to sign challenges with RSA rsaAvail := false for _, certType := range certReq.certificateTypes { if certType == certTypeRSASign { @@ -176,23 +193,41 @@ func (c *Conn) clientHandshake() os.Error { } } - // For now, only send a certificate back if the server gives us an - // empty list of certificateAuthorities. - // - // RFC 4346 on the certificateAuthorities field: - // A list of the distinguished names of acceptable certificate - // authorities. These distinguished names may specify a desired - // distinguished name for a root CA or for a subordinate CA; thus, - // this message can be used to describe both known roots and a - // desired authorization space. If the certificate_authorities - // list is empty then the client MAY send any certificate of the - // appropriate ClientCertificateType, unless there is some - // external arrangement to the contrary. - if rsaAvail && len(certReq.certificateAuthorities) == 0 { - transmitCert = true - } + // We need to search our list of client certs for one + // where SignatureAlgorithm is RSA and the Issuer is in + // certReq.certificateAuthorities + findCert: + for i, cert := range c.config.Certificates { + if !rsaAvail { + continue + } - finishedHash.Write(certReq.marshal()) + leaf := cert.Leaf + if leaf == nil { + if leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) + } + } + + if leaf.PublicKeyAlgorithm != x509.RSA { + continue + } + + if len(certReq.certificateAuthorities) == 0 { + // they gave us an empty list, so just take the + // first RSA cert from c.config.Certificates + certToSend = &cert + break + } + + for _, ca := range certReq.certificateAuthorities { + if bytes.Equal(leaf.RawIssuer, ca) { + certToSend = &cert + break findCert + } + } + } msg, err = c.readHandshake() if err != nil { @@ -206,16 +241,13 @@ func (c *Conn) clientHandshake() os.Error { } finishedHash.Write(shd.marshal()) - var cert *x509.Certificate - if transmitCert { + // If the server requested a certificate then we have to send a + // Certificate message, even if it's empty because we don't have a + // certificate to send. + if certRequested { certMsg = new(certificateMsg) - if len(c.config.Certificates) > 0 { - cert, err = x509.ParseCertificate(c.config.Certificates[0].Certificate[0]) - if err == nil && cert.PublicKeyAlgorithm == x509.RSA { - certMsg.certificates = c.config.Certificates[0].Certificate - } else { - cert = nil - } + if certToSend != nil { + certMsg.certificates = certToSend.Certificate } finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) @@ -231,12 +263,12 @@ func (c *Conn) clientHandshake() os.Error { c.writeRecord(recordTypeHandshake, ckx.marshal()) } - if cert != nil { + if certToSend != nil { certVerify := new(certificateVerifyMsg) - var digest [36]byte - copy(digest[0:16], finishedHash.serverMD5.Sum()) - copy(digest[16:36], finishedHash.serverSHA1.Sum()) - signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey, crypto.MD5SHA1, digest[0:]) + digest := make([]byte, 0, 36) + digest = finishedHash.serverMD5.Sum(digest) + digest = finishedHash.serverSHA1.Sum(digest) + signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest) if err != nil { return c.sendAlert(alertInternalError) } @@ -247,11 +279,11 @@ func (c *Conn) clientHandshake() os.Error { } masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromPreMasterSecret10(preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) + keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) - clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */ ) - clientHash := suite.mac(clientMAC) - c.out.prepareCipherSpec(clientCipher, clientHash) + clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */) + clientHash := suite.mac(c.vers, clientMAC) + c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) if serverHello.nextProtoNeg { @@ -270,9 +302,9 @@ func (c *Conn) clientHandshake() os.Error { finishedHash.Write(finished.marshal()) c.writeRecord(recordTypeHandshake, finished.marshal()) - serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */ ) - serverHash := suite.mac(serverMAC) - c.in.prepareCipherSpec(serverCipher, serverHash) + serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */) + serverHash := suite.mac(c.vers, serverMAC) + c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) c.readRecord(recordTypeChangeCipherSpec) if c.err != nil { return c.err @@ -294,7 +326,7 @@ func (c *Conn) clientHandshake() os.Error { } c.handshakeComplete = true - c.cipherSuite = suiteId + c.cipherSuite = suite.id return nil } diff --git a/src/pkg/crypto/tls/handshake_client_test.go b/src/pkg/crypto/tls/handshake_client_test.go index 3f91c7acf..8c56daaf6 100644 --- a/src/pkg/crypto/tls/handshake_client_test.go +++ b/src/pkg/crypto/tls/handshake_client_test.go @@ -18,6 +18,7 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config * go func() { cli.Write([]byte("hello\n")) cli.Close() + c.Close() }() defer c.Close() @@ -61,7 +62,7 @@ func TestRunClient(t *testing.T) { // Script of interaction with gnutls implementation. // The values for this test are obtained by building and running in client mode: -// % gotest -test.run "TestRunClient" -connect +// % go test -run "TestRunClient" -connect // and then: // % gnutls-serv -p 10443 --debug 100 --x509keyfile key.pem --x509certfile cert.pem -a > /tmp/log 2>&1 // % python parse-gnutls-cli-debug-log.py < /tmp/log diff --git a/src/pkg/crypto/tls/handshake_messages.go b/src/pkg/crypto/tls/handshake_messages.go index 6645adce4..e1517cc79 100644 --- a/src/pkg/crypto/tls/handshake_messages.go +++ b/src/pkg/crypto/tls/handshake_messages.go @@ -4,6 +4,8 @@ package tls +import "bytes" + type clientHelloMsg struct { raw []byte vers uint16 @@ -18,6 +20,25 @@ type clientHelloMsg struct { supportedPoints []uint8 } +func (m *clientHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*clientHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + bytes.Equal(m.random, m1.random) && + bytes.Equal(m.sessionId, m1.sessionId) && + eqUint16s(m.cipherSuites, m1.cipherSuites) && + bytes.Equal(m.compressionMethods, m1.compressionMethods) && + m.nextProtoNeg == m1.nextProtoNeg && + m.serverName == m1.serverName && + m.ocspStapling == m1.ocspStapling && + eqUint16s(m.supportedCurves, m1.supportedCurves) && + bytes.Equal(m.supportedPoints, m1.supportedPoints) +} + func (m *clientHelloMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -309,6 +330,23 @@ type serverHelloMsg struct { ocspStapling bool } +func (m *serverHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*serverHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + bytes.Equal(m.random, m1.random) && + bytes.Equal(m.sessionId, m1.sessionId) && + m.cipherSuite == m1.cipherSuite && + m.compressionMethod == m1.compressionMethod && + m.nextProtoNeg == m1.nextProtoNeg && + eqStrings(m.nextProtos, m1.nextProtos) && + m.ocspStapling == m1.ocspStapling +} + func (m *serverHelloMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -463,6 +501,16 @@ type certificateMsg struct { certificates [][]byte } +func (m *certificateMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + eqByteSlices(m.certificates, m1.certificates) +} + func (m *certificateMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -540,6 +588,16 @@ type serverKeyExchangeMsg struct { key []byte } +func (m *serverKeyExchangeMsg) equal(i interface{}) bool { + m1, ok := i.(*serverKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.key, m1.key) +} + func (m *serverKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -571,6 +629,17 @@ type certificateStatusMsg struct { response []byte } +func (m *certificateStatusMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateStatusMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.statusType == m1.statusType && + bytes.Equal(m.response, m1.response) +} + func (m *certificateStatusMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -622,6 +691,11 @@ func (m *certificateStatusMsg) unmarshal(data []byte) bool { type serverHelloDoneMsg struct{} +func (m *serverHelloDoneMsg) equal(i interface{}) bool { + _, ok := i.(*serverHelloDoneMsg) + return ok +} + func (m *serverHelloDoneMsg) marshal() []byte { x := make([]byte, 4) x[0] = typeServerHelloDone @@ -637,6 +711,16 @@ type clientKeyExchangeMsg struct { ciphertext []byte } +func (m *clientKeyExchangeMsg) equal(i interface{}) bool { + m1, ok := i.(*clientKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.ciphertext, m1.ciphertext) +} + func (m *clientKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -671,14 +755,24 @@ type finishedMsg struct { verifyData []byte } +func (m *finishedMsg) equal(i interface{}) bool { + m1, ok := i.(*finishedMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.verifyData, m1.verifyData) +} + func (m *finishedMsg) marshal() (x []byte) { if m.raw != nil { return m.raw } - x = make([]byte, 16) + x = make([]byte, 4+len(m.verifyData)) x[0] = typeFinished - x[3] = 12 + x[3] = byte(len(m.verifyData)) copy(x[4:], m.verifyData) m.raw = x return @@ -686,7 +780,7 @@ func (m *finishedMsg) marshal() (x []byte) { func (m *finishedMsg) unmarshal(data []byte) bool { m.raw = data - if len(data) != 4+12 { + if len(data) < 4 { return false } m.verifyData = data[4:] @@ -698,6 +792,16 @@ type nextProtoMsg struct { proto string } +func (m *nextProtoMsg) equal(i interface{}) bool { + m1, ok := i.(*nextProtoMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.proto == m1.proto +} + func (m *nextProtoMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -759,6 +863,17 @@ type certificateRequestMsg struct { certificateAuthorities [][]byte } +func (m *certificateRequestMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateRequestMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.certificateTypes, m1.certificateTypes) && + eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) +} + func (m *certificateRequestMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -766,9 +881,11 @@ func (m *certificateRequestMsg) marshal() (x []byte) { // See http://tools.ietf.org/html/rfc4346#section-7.4.4 length := 1 + len(m.certificateTypes) + 2 + casLength := 0 for _, ca := range m.certificateAuthorities { - length += 2 + len(ca) + casLength += 2 + len(ca) } + length += casLength x = make([]byte, 4+length) x[0] = typeCertificateRequest @@ -780,10 +897,8 @@ func (m *certificateRequestMsg) marshal() (x []byte) { copy(x[5:], m.certificateTypes) y := x[5+len(m.certificateTypes):] - - numCA := len(m.certificateAuthorities) - y[0] = uint8(numCA >> 8) - y[1] = uint8(numCA) + y[0] = uint8(casLength >> 8) + y[1] = uint8(casLength) y = y[2:] for _, ca := range m.certificateAuthorities { y[0] = uint8(len(ca) >> 8) @@ -794,7 +909,6 @@ func (m *certificateRequestMsg) marshal() (x []byte) { } m.raw = x - return } @@ -822,31 +936,34 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool { } data = data[numCertTypes:] + if len(data) < 2 { return false } - - numCAs := uint16(data[0])<<16 | uint16(data[1]) + casLength := uint16(data[0])<<8 | uint16(data[1]) data = data[2:] + if len(data) < int(casLength) { + return false + } + cas := make([]byte, casLength) + copy(cas, data) + data = data[casLength:] - m.certificateAuthorities = make([][]byte, numCAs) - for i := uint16(0); i < numCAs; i++ { - if len(data) < 2 { + m.certificateAuthorities = nil + for len(cas) > 0 { + if len(cas) < 2 { return false } - caLen := uint16(data[0])<<16 | uint16(data[1]) + caLen := uint16(cas[0])<<8 | uint16(cas[1]) + cas = cas[2:] - data = data[2:] - if len(data) < int(caLen) { + if len(cas) < int(caLen) { return false } - ca := make([]byte, caLen) - copy(ca, data) - m.certificateAuthorities[i] = ca - data = data[caLen:] + m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) + cas = cas[caLen:] } - if len(data) > 0 { return false } @@ -859,6 +976,16 @@ type certificateVerifyMsg struct { signature []byte } +func (m *certificateVerifyMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateVerifyMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.signature, m1.signature) +} + func (m *certificateVerifyMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -902,3 +1029,39 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool { return true } + +func eqUint16s(x, y []uint16) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqStrings(x, y []string) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqByteSlices(x, y [][]byte) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if !bytes.Equal(v, y[i]) { + return false + } + } + return true +} diff --git a/src/pkg/crypto/tls/handshake_messages_test.go b/src/pkg/crypto/tls/handshake_messages_test.go index 23f729dd9..e62a9d581 100644 --- a/src/pkg/crypto/tls/handshake_messages_test.go +++ b/src/pkg/crypto/tls/handshake_messages_test.go @@ -5,7 +5,7 @@ package tls import ( - "rand" + "math/rand" "reflect" "testing" "testing/quick" @@ -14,23 +14,25 @@ import ( var tests = []interface{}{ &clientHelloMsg{}, &serverHelloMsg{}, + &finishedMsg{}, &certificateMsg{}, &certificateRequestMsg{}, &certificateVerifyMsg{}, &certificateStatusMsg{}, &clientKeyExchangeMsg{}, - &finishedMsg{}, &nextProtoMsg{}, } type testMessage interface { marshal() []byte unmarshal([]byte) bool + equal(interface{}) bool } func TestMarshalUnmarshal(t *testing.T) { rand := rand.New(rand.NewSource(0)) + for i, iface := range tests { ty := reflect.ValueOf(iface).Type() @@ -54,16 +56,17 @@ func TestMarshalUnmarshal(t *testing.T) { } m2.marshal() // to fill any marshal cache in the message - if !reflect.DeepEqual(m1, m2) { + if !m1.equal(m2) { t.Errorf("#%d got:%#v want:%#v %x", i, m2, m1, marshaled) break } - if i >= 2 { - // The first two message types (ClientHello and - // ServerHello) are allowed to have parsable - // prefixes because the extension data is - // optional. + if i >= 3 { + // The first three message types (ClientHello, + // ServerHello and Finished) are allowed to + // have parsable prefixes because the extension + // data is optional and the length of the + // Finished varies across versions. for j := 0; j < len(marshaled); j++ { if m2.unmarshal(marshaled[0:j]) { t.Errorf("#%d unmarshaled a prefix of length %d of %#v", i, j, m1) diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index 44a324041..77e56a754 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -9,11 +9,11 @@ import ( "crypto/rsa" "crypto/subtle" "crypto/x509" + "errors" "io" - "os" ) -func (c *Conn) serverHandshake() os.Error { +func (c *Conn) serverHandshake() error { config := c.config msg, err := c.readHandshake() if err != nil { @@ -30,7 +30,7 @@ func (c *Conn) serverHandshake() os.Error { c.vers = vers c.haveVers = true - finishedHash := newFinishedHash() + finishedHash := newFinishedHash(vers) finishedHash.Write(clientHello.marshal()) hello := new(serverHelloMsg) @@ -56,18 +56,27 @@ Curves: ellipticOk := supportedCurve && supportedPointFormat var suite *cipherSuite - var suiteId uint16 FindCipherSuite: for _, id := range clientHello.cipherSuites { for _, supported := range config.cipherSuites() { if id == supported { - suite = cipherSuites[id] + var candidate *cipherSuite + + for _, s := range cipherSuites { + if s.id == id { + candidate = s + break + } + } + if candidate == nil { + continue + } // Don't select a ciphersuite which we can't // support for this client. - if suite.elliptic && !ellipticOk { + if candidate.elliptic && !ellipticOk { continue } - suiteId = id + suite = candidate break FindCipherSuite } } @@ -87,8 +96,8 @@ FindCipherSuite: } hello.vers = vers - hello.cipherSuite = suiteId - t := uint32(config.time()) + hello.cipherSuite = suite.id + t := uint32(config.time().Unix()) hello.random = make([]byte, 32) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) @@ -115,7 +124,12 @@ FindCipherSuite: } certMsg := new(certificateMsg) - certMsg.certificates = config.Certificates[0].Certificate + if len(clientHello.serverName) > 0 { + c.serverName = clientHello.serverName + certMsg.certificates = config.getCertificateForName(clientHello.serverName).Certificate + } else { + certMsg.certificates = config.Certificates[0].Certificate + } finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) @@ -128,7 +142,6 @@ FindCipherSuite: } keyAgreement := suite.ka() - skx, err := keyAgreement.generateServerKeyExchange(config, clientHello, hello) if err != nil { c.sendAlert(alertHandshakeFailure) @@ -139,14 +152,19 @@ FindCipherSuite: c.writeRecord(recordTypeHandshake, skx.marshal()) } - if config.AuthenticateClient { + if config.ClientAuth >= RequestClientCert { // Request a client certificate certReq := new(certificateRequestMsg) certReq.certificateTypes = []byte{certTypeRSASign} + // An empty list of certificateAuthorities signals to // the client that it may send any certificate in response - // to our request. - + // to our request. When we know the CAs we trust, then + // we can send them down, so that the client can choose + // an appropriate certificate to give to us. + if config.ClientCAs != nil { + certReq.certificateAuthorities = config.ClientCAs.Subjects() + } finishedHash.Write(certReq.marshal()) c.writeRecord(recordTypeHandshake, certReq.marshal()) } @@ -155,52 +173,87 @@ FindCipherSuite: finishedHash.Write(helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal()) - var pub *rsa.PublicKey - if config.AuthenticateClient { - // Get client certificate - msg, err = c.readHandshake() - if err != nil { - return err - } - certMsg, ok = msg.(*certificateMsg) - if !ok { - return c.sendAlert(alertUnexpectedMessage) + var pub *rsa.PublicKey // public key for client auth, if any + + msg, err = c.readHandshake() + if err != nil { + return err + } + + // If we requested a client certificate, then the client must send a + // certificate message, even if it's empty. + if config.ClientAuth >= RequestClientCert { + if certMsg, ok = msg.(*certificateMsg); !ok { + return c.sendAlert(alertHandshakeFailure) } finishedHash.Write(certMsg.marshal()) + if len(certMsg.certificates) == 0 { + // The client didn't actually send a certificate + switch config.ClientAuth { + case RequireAnyClientCert, RequireAndVerifyClientCert: + c.sendAlert(alertBadCertificate) + return errors.New("tls: client didn't provide a certificate") + } + } + certs := make([]*x509.Certificate, len(certMsg.certificates)) for i, asn1Data := range certMsg.certificates { - cert, err := x509.ParseCertificate(asn1Data) - if err != nil { + if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { c.sendAlert(alertBadCertificate) - return os.NewError("could not parse client's certificate: " + err.String()) + return errors.New("tls: failed to parse client certificate: " + err.Error()) } - certs[i] = cert } - // TODO(agl): do better validation of certs: max path length, name restrictions etc. - for i := 1; i < len(certs); i++ { - if err := certs[i-1].CheckSignatureFrom(certs[i]); err != nil { + if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { + opts := x509.VerifyOptions{ + Roots: c.config.ClientCAs, + CurrentTime: c.config.time(), + Intermediates: x509.NewCertPool(), + } + + for i, cert := range certs { + if i == 0 { + continue + } + opts.Intermediates.AddCert(cert) + } + + chains, err := certs[0].Verify(opts) + if err != nil { c.sendAlert(alertBadCertificate) - return os.NewError("could not validate certificate signature: " + err.String()) + return errors.New("tls: failed to verify client's certificate: " + err.Error()) + } + + ok := false + for _, ku := range certs[0].ExtKeyUsage { + if ku == x509.ExtKeyUsageClientAuth { + ok = true + break + } + } + if !ok { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication") } + + c.verifiedChains = chains } if len(certs) > 0 { - key, ok := certs[0].PublicKey.(*rsa.PublicKey) - if !ok { + if pub, ok = certs[0].PublicKey.(*rsa.PublicKey); !ok { return c.sendAlert(alertUnsupportedCertificate) } - pub = key c.peerCertificates = certs } + + msg, err = c.readHandshake() + if err != nil { + return err + } } // Get client key exchange - msg, err = c.readHandshake() - if err != nil { - return err - } ckx, ok := msg.(*clientKeyExchangeMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) @@ -223,30 +276,30 @@ FindCipherSuite: return c.sendAlert(alertUnexpectedMessage) } - digest := make([]byte, 36) - copy(digest[0:16], finishedHash.serverMD5.Sum()) - copy(digest[16:36], finishedHash.serverSHA1.Sum()) + digest := make([]byte, 0, 36) + digest = finishedHash.serverMD5.Sum(digest) + digest = finishedHash.serverSHA1.Sum(digest) err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) if err != nil { c.sendAlert(alertBadCertificate) - return os.NewError("could not validate signature of connection nonces: " + err.String()) + return errors.New("could not validate signature of connection nonces: " + err.Error()) } finishedHash.Write(certVerify.marshal()) } - preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx) + preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx, c.vers) if err != nil { c.sendAlert(alertHandshakeFailure) return err } masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromPreMasterSecret10(preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) + keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) - clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */ ) - clientHash := suite.mac(clientMAC) - c.in.prepareCipherSpec(clientCipher, clientHash) + clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */) + clientHash := suite.mac(c.vers, clientMAC) + c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) c.readRecord(recordTypeChangeCipherSpec) if err := c.error(); err != nil { return err @@ -282,9 +335,9 @@ FindCipherSuite: finishedHash.Write(clientFinished.marshal()) - serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */ ) - serverHash := suite.mac(serverMAC) - c.out.prepareCipherSpec(serverCipher, serverHash) + serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */) + serverHash := suite.mac(c.vers, serverMAC) + c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) finished := new(finishedMsg) @@ -292,7 +345,7 @@ FindCipherSuite: c.writeRecord(recordTypeHandshake, finished.marshal()) c.handshakeComplete = true - c.cipherSuite = suiteId + c.cipherSuite = suite.id return nil } diff --git a/src/pkg/crypto/tls/handshake_server_test.go b/src/pkg/crypto/tls/handshake_server_test.go index b77646e43..08a0ccb09 100644 --- a/src/pkg/crypto/tls/handshake_server_test.go +++ b/src/pkg/crypto/tls/handshake_server_test.go @@ -5,20 +5,25 @@ package tls import ( - "big" "bytes" "crypto/rsa" + "crypto/x509" "encoding/hex" + "encoding/pem" "flag" "io" + "log" + "math/big" "net" - "os" + "strconv" + "strings" "testing" + "time" ) type zeroSource struct{} -func (zeroSource) Read(b []byte) (n int, err os.Error) { +func (zeroSource) Read(b []byte) (n int, err error) { for i := range b { b[i] = 0 } @@ -30,15 +35,16 @@ var testConfig *Config func init() { testConfig = new(Config) - testConfig.Time = func() int64 { return 0 } + testConfig.Time = func() time.Time { return time.Unix(0, 0) } testConfig.Rand = zeroSource{} testConfig.Certificates = make([]Certificate, 1) testConfig.Certificates[0].Certificate = [][]byte{testCertificate} testConfig.Certificates[0].PrivateKey = testPrivateKey testConfig.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA} + testConfig.InsecureSkipVerify = true } -func testClientHelloFailure(t *testing.T, m handshakeMessage, expected os.Error) { +func testClientHelloFailure(t *testing.T, m handshakeMessage, expected error) { // Create in-memory network connection, // send message to server. Should return // expected error. @@ -53,7 +59,7 @@ func testClientHelloFailure(t *testing.T, m handshakeMessage, expected os.Error) }() err := Server(s, testConfig).Handshake() s.Close() - if e, ok := err.(*net.OpError); !ok || e.Error != expected { + if e, ok := err.(*net.OpError); !ok || e.Err != expected { t.Errorf("Got error: %s; expected: %s", err, expected) } } @@ -62,7 +68,7 @@ func TestSimpleError(t *testing.T) { testClientHelloFailure(t, &serverHelloDoneMsg{}, alertUnexpectedMessage) } -var badProtocolVersions = []uint16{0x0000, 0x0005, 0x0100, 0x0105, 0x0200, 0x0205, 0x0300} +var badProtocolVersions = []uint16{0x0000, 0x0005, 0x0100, 0x0105, 0x0200, 0x0205} func TestRejectBadProtocolVersion(t *testing.T) { for _, v := range badProtocolVersions { @@ -90,7 +96,7 @@ func TestAlertForwarding(t *testing.T) { err := Server(s, testConfig).Handshake() s.Close() - if e, ok := err.(*net.OpError); !ok || e.Error != os.Error(alertUnknownCA) { + if e, ok := err.(*net.OpError); !ok || e.Err != error(alertUnknownCA) { t.Errorf("Got error: %s; expected: %s", err, alertUnknownCA) } } @@ -101,54 +107,115 @@ func TestClose(t *testing.T) { err := Server(s, testConfig).Handshake() s.Close() - if err != os.EOF { - t.Errorf("Got error: %s; expected: %s", err, os.EOF) + if err != io.EOF { + t.Errorf("Got error: %s; expected: %s", err, io.EOF) } } -func testServerScript(t *testing.T, name string, serverScript [][]byte, config *Config) { +func testServerScript(t *testing.T, name string, serverScript [][]byte, config *Config, peers []*x509.Certificate) { c, s := net.Pipe() srv := Server(s, config) + pchan := make(chan []*x509.Certificate, 1) go func() { srv.Write([]byte("hello, world\n")) srv.Close() + s.Close() + st := srv.ConnectionState() + pchan <- st.PeerCertificates }() - defer c.Close() for i, b := range serverScript { if i%2 == 0 { c.Write(b) continue } bb := make([]byte, len(b)) - _, err := io.ReadFull(c, bb) + n, err := io.ReadFull(c, bb) if err != nil { - t.Fatalf("%s #%d: %s", name, i, err) + t.Fatalf("%s #%d: %s\nRead %d, wanted %d, got %x, wanted %x\n", name, i, err, n, len(bb), bb[:n], b) } if !bytes.Equal(b, bb) { t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", name, i, bb, b) } } + c.Close() + + if peers != nil { + gotpeers := <-pchan + if len(peers) == len(gotpeers) { + for i := range peers { + if !peers[i].Equal(gotpeers[i]) { + t.Fatalf("%s: mismatch on peer cert %d", name, i) + } + } + } else { + t.Fatalf("%s: mismatch on peer list length: %d (wanted) != %d (got)", name, len(peers), len(gotpeers)) + } + } } func TestHandshakeServerRC4(t *testing.T) { - testServerScript(t, "RC4", rc4ServerScript, testConfig) + testServerScript(t, "RC4", rc4ServerScript, testConfig, nil) +} + +func TestHandshakeServer3DES(t *testing.T) { + des3Config := new(Config) + *des3Config = *testConfig + des3Config.CipherSuites = []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA} + testServerScript(t, "3DES", des3ServerScript, des3Config, nil) } func TestHandshakeServerAES(t *testing.T) { aesConfig := new(Config) *aesConfig = *testConfig aesConfig.CipherSuites = []uint16{TLS_RSA_WITH_AES_128_CBC_SHA} - testServerScript(t, "AES", aesServerScript, aesConfig) + testServerScript(t, "AES", aesServerScript, aesConfig, nil) +} + +func TestHandshakeServerSSLv3(t *testing.T) { + testServerScript(t, "SSLv3", sslv3ServerScript, testConfig, nil) +} + +type clientauthTest struct { + name string + clientauth ClientAuthType + peers []*x509.Certificate + script [][]byte +} + +func TestClientAuth(t *testing.T) { + for _, cat := range clientauthTests { + t.Log("running", cat.name) + cfg := new(Config) + *cfg = *testConfig + cfg.ClientAuth = cat.clientauth + testServerScript(t, cat.name, cat.script, cfg, cat.peers) + } } var serve = flag.Bool("serve", false, "run a TLS server on :10443") +var testCipherSuites = flag.String("ciphersuites", + "0x"+strconv.FormatInt(int64(TLS_RSA_WITH_RC4_128_SHA), 16), + "cipher suites to accept in serving mode") +var testClientAuth = flag.Int("clientauth", 0, "value for tls.Config.ClientAuth") func TestRunServer(t *testing.T) { if !*serve { return } + suites := strings.Split(*testCipherSuites, ",") + testConfig.CipherSuites = make([]uint16, len(suites)) + for i := range suites { + suite, err := strconv.ParseUint(suites[i], 0, 64) + if err != nil { + panic(err) + } + testConfig.CipherSuites[i] = uint16(suite) + } + + testConfig.ClientAuth = ClientAuthType(*testClientAuth) + l, err := Listen("tcp", ":10443", testConfig) if err != nil { t.Fatal(err) @@ -157,13 +224,23 @@ func TestRunServer(t *testing.T) { for { c, err := l.Accept() if err != nil { + log.Printf("error from TLS handshake: %s", err) break } + _, err = c.Write([]byte("hello, world\n")) if err != nil { - t.Errorf("error from TLS: %s", err) - break + log.Printf("error from TLS: %s", err) + continue + } + + st := c.(*Conn).ConnectionState() + if len(st.PeerCertificates) > 0 { + log.Print("Handling request from client ", st.PeerCertificates[0].Subject.CommonName) + } else { + log.Print("Handling request from anon client") } + c.Close() } } @@ -193,31 +270,42 @@ var testPrivateKey = &rsa.PrivateKey{ }, } +func loadPEMCert(in string) *x509.Certificate { + block, _ := pem.Decode([]byte(in)) + if block.Type == "CERTIFICATE" && len(block.Headers) == 0 { + cert, err := x509.ParseCertificate(block.Bytes) + if err == nil { + return cert + } + panic("error parsing cert") + } + panic("error parsing PEM") +} + // Script of interaction with gnutls implementation. // The values for this test are obtained by building and running in server mode: -// % gotest -test.run "TestRunServer" -serve +// % go test -run "TestRunServer" -serve // and then: // % gnutls-cli --insecure --debug 100 -p 10443 localhost > /tmp/log 2>&1 // % python parse-gnutls-cli-debug-log.py < /tmp/log var rc4ServerScript = [][]byte{ { - 0x16, 0x03, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00, - 0x7b, 0x03, 0x02, 0x4d, 0x08, 0x1f, 0x5a, 0x7a, - 0x0a, 0x92, 0x2f, 0xf0, 0x73, 0x16, 0x3a, 0x88, - 0x14, 0x85, 0x4c, 0x98, 0x15, 0x7b, 0x65, 0xe0, - 0x78, 0xd0, 0xed, 0xd0, 0xf3, 0x65, 0x20, 0xeb, - 0x80, 0xd1, 0x0b, 0x00, 0x00, 0x34, 0x00, 0x33, + 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, + 0x76, 0x03, 0x02, 0x4e, 0xdd, 0xe6, 0xa5, 0xf7, + 0x00, 0x36, 0xf7, 0x83, 0xec, 0x93, 0x7c, 0xd2, + 0x4d, 0xe7, 0x7b, 0xf5, 0x4c, 0xf7, 0xe3, 0x86, + 0xe8, 0xec, 0x3b, 0xbd, 0x2c, 0x9a, 0x3f, 0x57, + 0xf0, 0xa4, 0xd4, 0x00, 0x00, 0x34, 0x00, 0x33, 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, - 0x00, 0x8a, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x09, + 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0xff, - 0x01, 0x00, 0x01, 0x00, + 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, }, { @@ -321,38 +409,219 @@ var rc4ServerScript = [][]byte{ { 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0x3c, 0x13, 0xd7, 0x12, 0xc1, - 0x6a, 0xf0, 0x3f, 0x8c, 0xa1, 0x35, 0x5d, 0xc5, - 0x89, 0x1e, 0x9e, 0xcd, 0x32, 0xc7, 0x9e, 0xe6, - 0xae, 0xd5, 0xf1, 0xbf, 0x70, 0xd7, 0xa9, 0xef, - 0x2c, 0x4c, 0xf4, 0x22, 0xbc, 0x17, 0x17, 0xaa, - 0x05, 0xf3, 0x9f, 0x80, 0xf2, 0xe9, 0x82, 0x2f, - 0x2a, 0x15, 0x54, 0x0d, 0x16, 0x0e, 0x77, 0x4c, - 0x28, 0x3c, 0x03, 0x2d, 0x2d, 0xd7, 0xc8, 0x64, - 0xd9, 0x59, 0x4b, 0x1c, 0xf4, 0xde, 0xff, 0x2f, - 0xbc, 0x94, 0xaf, 0x18, 0x26, 0x37, 0xce, 0x4f, - 0x84, 0x74, 0x2e, 0x45, 0x66, 0x7c, 0x0c, 0x54, - 0x46, 0x36, 0x5f, 0x65, 0x21, 0x7b, 0x83, 0x8c, - 0x6d, 0x76, 0xcd, 0x0d, 0x9f, 0xda, 0x1c, 0xa4, - 0x6e, 0xfe, 0xb1, 0xf7, 0x09, 0x0d, 0xfb, 0x74, - 0x66, 0x34, 0x99, 0x89, 0x7f, 0x5f, 0x77, 0x87, - 0x4a, 0x66, 0x4b, 0xa9, 0x59, 0x57, 0xe3, 0x56, - 0x0d, 0xdd, 0xd8, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xc0, 0x4e, - 0xd3, 0x0f, 0xb5, 0xc0, 0x57, 0xa6, 0x18, 0x80, - 0x80, 0x6b, 0x49, 0xfe, 0xbd, 0x3a, 0x7a, 0x2c, - 0xef, 0x70, 0xb5, 0x1c, 0xd2, 0xdf, 0x5f, 0x78, - 0x5a, 0xd8, 0x4f, 0xa0, 0x95, 0xb4, 0xb3, 0xb5, - 0xaa, 0x3b, + 0x82, 0x00, 0x80, 0x39, 0xe2, 0x0f, 0x49, 0xa0, + 0xe6, 0xe4, 0x3b, 0x0c, 0x5f, 0xce, 0x39, 0x97, + 0x6c, 0xb6, 0x41, 0xd9, 0xe1, 0x52, 0x8f, 0x43, + 0xb3, 0xc6, 0x4f, 0x9a, 0xe2, 0x1e, 0xb9, 0x3b, + 0xe3, 0x72, 0x17, 0x68, 0xb2, 0x0d, 0x7b, 0x71, + 0x33, 0x96, 0x5c, 0xf9, 0xfe, 0x18, 0x8f, 0x2f, + 0x2b, 0x82, 0xec, 0x03, 0xf2, 0x16, 0xa8, 0xf8, + 0x39, 0xf9, 0xbb, 0x5a, 0xd3, 0x0c, 0xc1, 0x2a, + 0x52, 0xa1, 0x90, 0x20, 0x6b, 0x24, 0xc9, 0x55, + 0xee, 0x05, 0xd8, 0xb3, 0x43, 0x58, 0xf6, 0x7f, + 0x68, 0x2d, 0xb3, 0xd1, 0x1b, 0x30, 0xaa, 0xdf, + 0xfc, 0x85, 0xf1, 0xab, 0x14, 0x51, 0x91, 0x78, + 0x29, 0x35, 0x65, 0xe0, 0x9c, 0xf6, 0xb7, 0x35, + 0x33, 0xdb, 0x28, 0x93, 0x4d, 0x86, 0xbc, 0xfe, + 0xaa, 0xd1, 0xc0, 0x2e, 0x4d, 0xec, 0xa2, 0x98, + 0xca, 0x08, 0xb2, 0x91, 0x14, 0xde, 0x97, 0x3a, + 0xc4, 0x6b, 0x49, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0x7a, 0xcb, + 0x3b, 0x0e, 0xbb, 0x7a, 0x56, 0x39, 0xaf, 0x83, + 0xae, 0xfd, 0x25, 0xfd, 0x64, 0xb4, 0x0c, 0x0c, + 0x17, 0x46, 0x54, 0x2c, 0x6a, 0x07, 0x83, 0xc6, + 0x46, 0x08, 0x0b, 0xcd, 0x15, 0x53, 0xef, 0x40, + 0x4e, 0x56, + }, + + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0xd3, 0x72, 0xeb, 0x29, 0xb9, + 0x15, 0x29, 0xb5, 0xe5, 0xb7, 0xef, 0x5c, 0xb2, + 0x9d, 0xf6, 0xc8, 0x47, 0xd6, 0xa0, 0x84, 0xf0, + 0x8c, 0xcb, 0xe6, 0xbe, 0xbc, 0xfb, 0x38, 0x90, + 0x89, 0x60, 0xa2, 0xe8, 0xaa, 0xb3, 0x12, 0x17, + 0x03, 0x01, 0x00, 0x21, 0x67, 0x4a, 0x3d, 0x31, + 0x6c, 0x5a, 0x1c, 0xf9, 0x6e, 0xf1, 0xd8, 0x12, + 0x0e, 0xb9, 0xfd, 0xfc, 0x66, 0x91, 0xd1, 0x1d, + 0x6e, 0xe4, 0x55, 0xdd, 0x11, 0xb9, 0xb8, 0xa2, + 0x65, 0xa1, 0x95, 0x64, 0x1c, 0x15, 0x03, 0x01, + 0x00, 0x16, 0x9b, 0xa0, 0x24, 0xe3, 0xcb, 0xae, + 0xad, 0x51, 0xb3, 0x63, 0x59, 0x78, 0x49, 0x24, + 0x06, 0x6e, 0xee, 0x7a, 0xd7, 0x74, 0x53, 0x04, + }, +} + +var des3ServerScript = [][]byte{ + { + 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, + 0x76, 0x03, 0x02, 0x4e, 0x84, 0xf4, 0x3c, 0xe4, + 0xb8, 0xc7, 0xa0, 0x30, 0x55, 0x2a, 0xbc, 0xb7, + 0x04, 0x6b, 0x6f, 0x87, 0x93, 0x96, 0xbd, 0x1a, + 0x7a, 0x1e, 0xce, 0xd2, 0x0d, 0xf3, 0x01, 0x03, + 0xbe, 0x7b, 0x17, 0x00, 0x00, 0x34, 0x00, 0x33, + 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, + 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, + 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, + 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, + 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, + 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, + 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + }, + + { + 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x16, + 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, + 0x00, 0x00, 0x00, + }, + + { + 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x80, 0xae, 0xcf, 0x4f, 0x70, 0x0e, + 0xe5, 0xe7, 0xba, 0xef, 0x0c, 0x66, 0xe9, 0xae, + 0x76, 0xf4, 0xe0, 0xbc, 0x1c, 0x22, 0x5b, 0x72, + 0xc9, 0x68, 0x63, 0x44, 0xec, 0x72, 0xc2, 0xca, + 0xac, 0xc2, 0xf5, 0x5c, 0x28, 0xa1, 0xaf, 0xd0, + 0xc2, 0xf7, 0x79, 0x71, 0x32, 0x73, 0x86, 0xea, + 0x39, 0xf6, 0x04, 0x26, 0x19, 0x84, 0x1d, 0x7d, + 0xa1, 0x21, 0xa6, 0x88, 0xbf, 0x33, 0x5a, 0x64, + 0xb0, 0xc2, 0xcc, 0x19, 0x7a, 0x8b, 0x6e, 0x94, + 0x9e, 0x2e, 0x20, 0xbe, 0xdc, 0xe9, 0x8e, 0xae, + 0x5c, 0x39, 0xc8, 0xcd, 0x0e, 0x19, 0x9a, 0xa2, + 0xfc, 0x3f, 0x61, 0x9a, 0xca, 0x58, 0x69, 0x0d, + 0xa8, 0x7b, 0xbe, 0x98, 0x8f, 0xb9, 0x9d, 0x8b, + 0x68, 0x65, 0xa9, 0x74, 0xcc, 0x8d, 0x0c, 0xb2, + 0xc4, 0x0f, 0xdc, 0x56, 0x3e, 0x44, 0x61, 0x0a, + 0x26, 0x93, 0x99, 0xef, 0x67, 0xff, 0x6e, 0x73, + 0x01, 0xa1, 0x90, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x60, 0x49, 0x36, + 0xc8, 0x38, 0x95, 0xe4, 0x5d, 0x8e, 0x80, 0x10, + 0x26, 0x9f, 0x87, 0x7d, 0xcd, 0xb9, 0x32, 0x6c, + 0xff, 0xaa, 0xe0, 0x07, 0xec, 0x33, 0xe2, 0x36, + 0x9d, 0xd5, 0x83, 0x2c, 0xf0, 0x0a, 0xa0, 0xa8, + 0x12, 0x9f, 0xca, 0x72, 0xda, 0x70, 0x7d, 0x76, + 0x80, 0x12, 0x88, 0x07, 0xaa, 0x27, 0x62, 0x33, + 0xab, 0x55, 0xad, 0x3c, 0x2b, 0x54, 0xc4, 0x1c, + 0x91, 0xfd, 0x8f, 0x9c, 0xa7, 0x8b, 0x75, 0x10, + 0xa8, 0x6e, 0xfc, 0x30, 0x52, 0x8a, 0x61, 0x02, + 0xdb, 0x9c, 0x6f, 0xc8, 0x19, 0x93, 0x5d, 0x41, + 0x1d, 0x36, 0x68, 0x0b, 0xec, 0x30, 0xae, 0xfb, + 0x90, 0xdb, 0x6d, 0x83, 0xb0, 0xf2, }, { 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x24, 0x9d, 0xc9, 0xda, 0xdf, 0xeb, - 0xc8, 0xdb, 0xf8, 0x94, 0xa5, 0xef, 0xd5, 0xfc, - 0x89, 0x01, 0x64, 0x30, 0x77, 0x5a, 0x18, 0x4b, - 0x16, 0x79, 0x9c, 0xf6, 0xf5, 0x09, 0x22, 0x12, - 0x4c, 0x3e, 0xa8, 0x8e, 0x91, 0xa5, 0x24, + 0x01, 0x00, 0x28, 0x07, 0xf3, 0x33, 0x84, 0xb1, + 0x5d, 0x2b, 0x52, 0xa4, 0x63, 0x3c, 0x32, 0xe0, + 0x0d, 0x22, 0xf5, 0x23, 0xec, 0xf9, 0xa6, 0xec, + 0xc0, 0x12, 0x69, 0x88, 0xf6, 0x7d, 0x37, 0xcd, + 0xc2, 0x74, 0x2f, 0xef, 0xf6, 0x49, 0x15, 0xea, + 0x88, 0x3f, 0x55, 0x17, 0x03, 0x01, 0x00, 0x28, + 0xaf, 0x00, 0x84, 0xff, 0x11, 0x01, 0x6d, 0xba, + 0x39, 0x5e, 0x45, 0xe1, 0x52, 0x5e, 0xc1, 0xab, + 0xde, 0x5b, 0x16, 0xdd, 0xd6, 0x61, 0x57, 0xb8, + 0x66, 0x8b, 0x2d, 0xde, 0x51, 0x41, 0xc5, 0x09, + 0xb3, 0x6a, 0x06, 0x43, 0xb4, 0x73, 0x5c, 0xf1, + 0x15, 0x03, 0x01, 0x00, 0x18, 0xbd, 0x65, 0xb2, + 0xce, 0x77, 0x2e, 0xf9, 0x11, 0xc4, 0x80, 0x43, + 0x5a, 0x73, 0x8b, 0x73, 0xdd, 0xf0, 0x54, 0x44, + 0x7c, 0x56, 0x19, 0x54, 0xda, }, } @@ -515,3 +784,787 @@ var aesServerScript = [][]byte{ 0xcd, 0x84, 0xf0, }, } + +var sslv3ServerScript = [][]byte{ + { + 0x16, 0x03, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, + 0x3d, 0x03, 0x00, 0x4e, 0x70, 0xe2, 0x18, 0x86, + 0xd6, 0xc6, 0x6f, 0xf3, 0xc8, 0xf4, 0x02, 0xd6, + 0x4d, 0xee, 0x17, 0x32, 0x4b, 0xd2, 0x78, 0xd8, + 0xa1, 0x03, 0x5d, 0x68, 0x82, 0x89, 0xbe, 0xfd, + 0x12, 0xb9, 0x06, 0x00, 0x00, 0x16, 0x00, 0x33, + 0x00, 0x39, 0x00, 0x16, 0x00, 0x32, 0x00, 0x38, + 0x00, 0x13, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, + 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, + }, + + { + 0x16, 0x03, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x00, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x00, 0x00, 0x04, 0x0e, + 0x00, 0x00, 0x00, + }, + + { + 0x16, 0x03, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00, + 0x80, 0x74, 0x0e, 0x3a, 0xcf, 0xba, 0x9f, 0x1a, + 0x9b, 0xb2, 0xa4, 0xc7, 0x5d, 0xf3, 0x0c, 0x80, + 0x06, 0x80, 0xf3, 0x57, 0xb2, 0xd9, 0x36, 0x24, + 0x6a, 0x06, 0x13, 0x40, 0xf9, 0x7c, 0xb9, 0x3e, + 0x4b, 0x68, 0x4f, 0x21, 0x90, 0x2d, 0xbd, 0xca, + 0xd4, 0x83, 0xf0, 0x7a, 0xeb, 0x7a, 0x74, 0x1b, + 0xcd, 0xfe, 0x69, 0xef, 0xc0, 0x86, 0xa0, 0x24, + 0x31, 0x65, 0x40, 0xd2, 0xdd, 0x6f, 0xb9, 0xd7, + 0x8d, 0xc1, 0x69, 0x60, 0x44, 0x7a, 0x75, 0xfb, + 0x42, 0x6a, 0x0f, 0x66, 0x45, 0x10, 0x73, 0xee, + 0x87, 0x28, 0x37, 0x83, 0x86, 0xd8, 0x5a, 0xc8, + 0x60, 0x87, 0xda, 0x33, 0x87, 0xaf, 0x34, 0x8b, + 0xf5, 0x61, 0x63, 0x7a, 0x5c, 0x60, 0x26, 0xb9, + 0xdb, 0xa1, 0xb7, 0xe3, 0x60, 0x38, 0x94, 0x5c, + 0x83, 0x23, 0xd6, 0x8d, 0xc2, 0x14, 0x4a, 0x0f, + 0x0e, 0x4f, 0xf9, 0x4e, 0x7b, 0x15, 0xcd, 0x18, + 0x04, 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, + 0x03, 0x00, 0x00, 0x3c, 0xbd, 0xbc, 0xec, 0xdc, + 0x79, 0xb1, 0xae, 0x16, 0xc9, 0x26, 0x9a, 0xc0, + 0xc0, 0x2c, 0x33, 0x36, 0x13, 0x91, 0x58, 0x5d, + 0x7d, 0xee, 0x4e, 0xd8, 0x7e, 0xac, 0x88, 0x87, + 0x0a, 0x75, 0x66, 0xb1, 0x44, 0x79, 0x2f, 0x42, + 0xe8, 0x92, 0x74, 0x4c, 0xab, 0x36, 0xc8, 0x17, + 0x5f, 0x02, 0x8a, 0x20, 0x53, 0xe9, 0x1d, 0xb4, + 0xfe, 0x5c, 0x2b, 0xd9, 0x0a, 0xfb, 0xc6, 0x63, + }, + + { + 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x00, 0x00, 0x3c, 0xaa, 0xa1, 0x98, 0xc4, 0x6b, + 0x5a, 0x16, 0x3f, 0x5f, 0xa4, 0x96, 0x3e, 0x78, + 0xe4, 0x6f, 0x49, 0x05, 0x47, 0xc4, 0x05, 0x60, + 0xeb, 0x0b, 0x45, 0xe3, 0xbc, 0x50, 0x11, 0x24, + 0x5f, 0x01, 0xd7, 0xb8, 0x8f, 0x60, 0x63, 0x66, + 0xbd, 0x3e, 0xd9, 0xa8, 0x80, 0x43, 0x9f, 0x0b, + 0x51, 0x61, 0xed, 0x13, 0xc6, 0x21, 0xd0, 0xfe, + 0xbc, 0x17, 0x3c, 0x36, 0xb0, 0x82, 0x7f, 0x17, + 0x03, 0x00, 0x00, 0x21, 0xee, 0x44, 0xf3, 0xa6, + 0x88, 0x9d, 0x78, 0x44, 0xde, 0xdf, 0xeb, 0xc5, + 0xad, 0xc4, 0xcc, 0x56, 0x5c, 0x54, 0x96, 0x52, + 0x3f, 0xd9, 0x40, 0x6e, 0x79, 0xd8, 0x58, 0x78, + 0x4f, 0x5a, 0xe9, 0x06, 0xef, 0x15, 0x03, 0x00, + 0x00, 0x16, 0xd3, 0xc2, 0x52, 0x99, 0x2a, 0x84, + 0xc4, 0x52, 0x5f, 0x3b, 0x19, 0xe7, 0xfc, 0x65, + 0xaf, 0xd3, 0xb7, 0xa3, 0xcc, 0x4a, 0x1d, 0x2e, + }, +} + +var clientauthTests = []clientauthTest{ + // Server doesn't asks for cert + // go test -run "TestRunServer" -serve -clientauth 0 + // gnutls-cli --insecure --debug 100 -p 10443 localhost 2>&1 | + // python parse-gnutls-cli-debug-log.py + {"NoClientCert", NoClientCert, nil, + [][]byte{{ + 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, + 0x76, 0x03, 0x02, 0x4e, 0xe0, 0x92, 0x5d, 0xcd, + 0xfe, 0x0c, 0x69, 0xd4, 0x7d, 0x8e, 0xa6, 0x88, + 0xde, 0x72, 0x04, 0x29, 0x6a, 0x4a, 0x16, 0x23, + 0xd7, 0x8f, 0xbc, 0xfa, 0x80, 0x73, 0x2e, 0x12, + 0xb7, 0x0b, 0x39, 0x00, 0x00, 0x34, 0x00, 0x33, + 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, + 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, + 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, + 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, + 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, + 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, + 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + }, + + { + 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, + 0x00, 0x00, 0x00, + }, + + { + 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x80, 0x10, 0xe1, 0x00, 0x3d, 0x0a, + 0x6b, 0x02, 0x7f, 0x97, 0xde, 0xfb, 0x65, 0x46, + 0x1a, 0x50, 0x4e, 0x34, 0x9a, 0xae, 0x14, 0x7e, + 0xec, 0xef, 0x85, 0x15, 0x3b, 0x39, 0xc2, 0x45, + 0x04, 0x40, 0x92, 0x71, 0xd6, 0x7e, 0xf6, 0xfd, + 0x4d, 0x84, 0xf7, 0xc4, 0x77, 0x99, 0x3d, 0xe2, + 0xc3, 0x8d, 0xb0, 0x4c, 0x74, 0xc8, 0x51, 0xec, + 0xb2, 0xe8, 0x6b, 0xa1, 0xd2, 0x4d, 0xd8, 0x61, + 0x92, 0x7a, 0x24, 0x57, 0x44, 0x4f, 0xa2, 0x1e, + 0x74, 0x0b, 0x06, 0x4b, 0x80, 0x34, 0x8b, 0xfe, + 0xc2, 0x0e, 0xc1, 0xcd, 0xab, 0x0c, 0x3f, 0x54, + 0xe2, 0x44, 0xe9, 0x6c, 0x2b, 0xba, 0x7b, 0x64, + 0xf1, 0x93, 0x65, 0x75, 0xf2, 0x35, 0xff, 0x27, + 0x03, 0xd5, 0x64, 0xe6, 0x8e, 0xe7, 0x7b, 0x56, + 0xb6, 0x61, 0x73, 0xeb, 0xa2, 0xdc, 0xa4, 0x6e, + 0x52, 0xac, 0xbc, 0xba, 0x11, 0xa3, 0xd2, 0x61, + 0x4a, 0xe0, 0xbb, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xd2, 0x5a, + 0x0c, 0x2a, 0x27, 0x96, 0xba, 0xa9, 0x67, 0xd2, + 0x51, 0x68, 0x32, 0x68, 0x22, 0x1f, 0xb9, 0x27, + 0x79, 0x59, 0x28, 0xdf, 0x38, 0x1f, 0x92, 0x21, + 0x5d, 0x0f, 0xf4, 0xc0, 0xee, 0xb7, 0x10, 0x5a, + 0xa9, 0x45, + }, + + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0x13, 0x6f, 0x6c, 0x71, 0x83, + 0x59, 0xcf, 0x32, 0x72, 0xe9, 0xce, 0xcc, 0x7a, + 0x6c, 0xf0, 0x72, 0x39, 0x16, 0xae, 0x40, 0x61, + 0xfa, 0x92, 0x4c, 0xe7, 0xf2, 0x1a, 0xd7, 0x0c, + 0x84, 0x76, 0x6c, 0xe9, 0x11, 0x43, 0x19, 0x17, + 0x03, 0x01, 0x00, 0x21, 0xc0, 0xa2, 0x13, 0x28, + 0x94, 0x8c, 0x5c, 0xd6, 0x79, 0xb9, 0xfe, 0xae, + 0x45, 0x4b, 0xc0, 0x7c, 0xae, 0x2d, 0xb4, 0x0d, + 0x31, 0xc4, 0xad, 0x22, 0xd7, 0x1e, 0x99, 0x1c, + 0x4c, 0x69, 0xab, 0x42, 0x61, 0x15, 0x03, 0x01, + 0x00, 0x16, 0xe1, 0x0c, 0x67, 0xf3, 0xf4, 0xb9, + 0x8e, 0x81, 0x8e, 0x01, 0xb8, 0xa0, 0x69, 0x8c, + 0x03, 0x11, 0x43, 0x3e, 0xee, 0xb7, 0x4d, 0x69, + }}}, + // Server asks for cert with empty CA list, client doesn't give it. + // go test -run "TestRunServer" -serve -clientauth 1 + // gnutls-cli --insecure --debug 100 -p 10443 localhost + {"RequestClientCert, none given", RequestClientCert, nil, + [][]byte{{ + 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, + 0x76, 0x03, 0x02, 0x4e, 0xe0, 0x93, 0xe2, 0x47, + 0x06, 0xa0, 0x61, 0x0c, 0x51, 0xdd, 0xf0, 0xef, + 0xf4, 0x30, 0x72, 0xe1, 0xa6, 0x50, 0x68, 0x82, + 0x3c, 0xfb, 0xcb, 0x72, 0x5e, 0x73, 0x9d, 0xda, + 0x27, 0x35, 0x72, 0x00, 0x00, 0x34, 0x00, 0x33, + 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, + 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, + 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, + 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, + 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, + 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, + 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + }, + + { + 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d, + 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16, + 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + + { + 0x16, 0x03, 0x01, 0x00, 0x07, 0x0b, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, + 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x64, + 0x28, 0xb9, 0x3f, 0x48, 0xaf, 0x06, 0x22, 0x39, + 0x56, 0xd8, 0x6f, 0x63, 0x5d, 0x03, 0x48, 0x63, + 0x01, 0x13, 0xa2, 0xd6, 0x76, 0xc0, 0xab, 0xda, + 0x25, 0x30, 0x75, 0x6c, 0xaa, 0xb4, 0xdc, 0x35, + 0x72, 0xdc, 0xf2, 0x43, 0xe4, 0x1d, 0x82, 0xfb, + 0x6c, 0x64, 0xe2, 0xa7, 0x8f, 0x32, 0x67, 0x6b, + 0xcd, 0xd2, 0xb2, 0x36, 0x94, 0xbc, 0x6f, 0x46, + 0x79, 0x29, 0x42, 0xe3, 0x1a, 0xbf, 0xfb, 0x41, + 0xd5, 0xe3, 0xb4, 0x2a, 0xf6, 0x95, 0x6f, 0x0c, + 0x87, 0xb9, 0x03, 0x18, 0xa1, 0xea, 0x4a, 0xe2, + 0x2e, 0x0f, 0x50, 0x00, 0xc1, 0xe8, 0x8c, 0xc8, + 0xa2, 0xf6, 0xa4, 0x05, 0xf4, 0x38, 0x3e, 0xd9, + 0x6e, 0x63, 0x96, 0x0c, 0x34, 0x73, 0x90, 0x03, + 0x55, 0xa6, 0x34, 0xb0, 0x5e, 0x8c, 0x48, 0x40, + 0x25, 0x45, 0x84, 0xa6, 0x21, 0x3f, 0x81, 0x97, + 0xa7, 0x11, 0x09, 0x14, 0x95, 0xa5, 0xe5, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x24, 0x16, 0xaa, 0x01, 0x2c, 0xa8, 0xc1, + 0x28, 0xaf, 0x35, 0xc1, 0xc1, 0xf3, 0x0a, 0x25, + 0x66, 0x6e, 0x27, 0x11, 0xa3, 0xa4, 0xd9, 0xe9, + 0xea, 0x15, 0x09, 0x9d, 0x28, 0xe3, 0x5b, 0x2b, + 0xa6, 0x25, 0xa7, 0x14, 0x24, 0x3a, + }, + + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0x9a, 0xa8, 0xd6, 0x77, 0x46, + 0x45, 0x68, 0x9d, 0x5d, 0xa9, 0x68, 0x03, 0xe5, + 0xaf, 0xe8, 0xc8, 0x21, 0xc5, 0xc6, 0xc1, 0x50, + 0xe0, 0xd8, 0x52, 0xce, 0xa3, 0x4f, 0x2d, 0xf4, + 0xe3, 0xa7, 0x7d, 0x35, 0x80, 0x84, 0x12, 0x17, + 0x03, 0x01, 0x00, 0x21, 0x8a, 0x82, 0x0c, 0x54, + 0x1b, 0xeb, 0x77, 0x90, 0x2c, 0x3e, 0xbc, 0xf0, + 0x23, 0xcc, 0xa8, 0x9f, 0x25, 0x08, 0x12, 0xed, + 0x43, 0xf1, 0xf9, 0x06, 0xad, 0xa9, 0x4b, 0x97, + 0x82, 0xb7, 0xc4, 0x0b, 0x4c, 0x15, 0x03, 0x01, + 0x00, 0x16, 0x05, 0x2d, 0x9d, 0x45, 0x03, 0xb7, + 0xc2, 0xd1, 0xb5, 0x1a, 0x43, 0xcf, 0x1a, 0x37, + 0xf4, 0x70, 0xcc, 0xb4, 0xed, 0x07, 0x76, 0x3a, + }}}, + // Server asks for cert with empty CA list, client gives one + // go test -run "TestRunServer" -serve -clientauth 1 + // gnutls-cli --insecure --debug 100 -p 10443 localhost + {"RequestClientCert, client gives it", RequestClientCert, + []*x509.Certificate{clicert}, + [][]byte{{ + 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, + 0x76, 0x03, 0x02, 0x4e, 0xe7, 0x44, 0xda, 0x58, + 0x7d, 0x46, 0x4a, 0x48, 0x97, 0x9f, 0xe5, 0x91, + 0x11, 0x64, 0xa7, 0x1e, 0x4d, 0xb7, 0xfe, 0x9b, + 0xc6, 0x63, 0xf8, 0xa4, 0xb5, 0x0b, 0x18, 0xb5, + 0xbd, 0x19, 0xb3, 0x00, 0x00, 0x34, 0x00, 0x33, + 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, + 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, + 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, + 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, + 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, + 0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09, + 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, + }, + + { + 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, + 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, + 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, + 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, + 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, + 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, + 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, + 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, + 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, + 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, + 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, + 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, + 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, + 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, + 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, + 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, + 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, + 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, + 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, + 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, + 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, + 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, + 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, + 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, + 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, + 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, + 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, + 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, + 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, + 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, + 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, + 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, + 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, + 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, + 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, + 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, + 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, + 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, + 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, + 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, + 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, + 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, + 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, + 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, + 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, + 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, + 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d, + 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16, + 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + + { + 0x16, 0x03, 0x01, 0x01, 0xfb, 0x0b, 0x00, 0x01, + 0xf7, 0x00, 0x01, 0xf4, 0x00, 0x01, 0xf1, 0x30, + 0x82, 0x01, 0xed, 0x30, 0x82, 0x01, 0x58, 0xa0, + 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30, + 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x30, 0x26, 0x31, 0x10, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, + 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x31, 0x31, 0x32, 0x30, 0x38, 0x30, 0x37, + 0x35, 0x35, 0x31, 0x32, 0x5a, 0x17, 0x0d, 0x31, + 0x32, 0x31, 0x32, 0x30, 0x37, 0x30, 0x38, 0x30, + 0x30, 0x31, 0x32, 0x5a, 0x30, 0x26, 0x31, 0x10, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, + 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9c, 0x30, + 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x03, 0x81, 0x8c, 0x00, + 0x30, 0x81, 0x88, 0x02, 0x81, 0x80, 0x4e, 0xd0, + 0x7b, 0x31, 0xe3, 0x82, 0x64, 0xd9, 0x59, 0xc0, + 0xc2, 0x87, 0xa4, 0x5e, 0x1e, 0x8b, 0x73, 0x33, + 0xc7, 0x63, 0x53, 0xdf, 0x66, 0x92, 0x06, 0x84, + 0xf6, 0x64, 0xd5, 0x8f, 0xe4, 0x36, 0xa7, 0x1d, + 0x2b, 0xe8, 0xb3, 0x20, 0x36, 0x45, 0x23, 0xb5, + 0xe3, 0x95, 0xae, 0xed, 0xe0, 0xf5, 0x20, 0x9c, + 0x8d, 0x95, 0xdf, 0x7f, 0x5a, 0x12, 0xef, 0x87, + 0xe4, 0x5b, 0x68, 0xe4, 0xe9, 0x0e, 0x74, 0xec, + 0x04, 0x8a, 0x7f, 0xde, 0x93, 0x27, 0xc4, 0x01, + 0x19, 0x7a, 0xbd, 0xf2, 0xdc, 0x3d, 0x14, 0xab, + 0xd0, 0x54, 0xca, 0x21, 0x0c, 0xd0, 0x4d, 0x6e, + 0x87, 0x2e, 0x5c, 0xc5, 0xd2, 0xbb, 0x4d, 0x4b, + 0x4f, 0xce, 0xb6, 0x2c, 0xf7, 0x7e, 0x88, 0xec, + 0x7c, 0xd7, 0x02, 0x91, 0x74, 0xa6, 0x1e, 0x0c, + 0x1a, 0xda, 0xe3, 0x4a, 0x5a, 0x2e, 0xde, 0x13, + 0x9c, 0x4c, 0x40, 0x88, 0x59, 0x93, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x32, 0x30, 0x30, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x00, 0xa0, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x06, + 0x04, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x08, 0x30, + 0x06, 0x80, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, + 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x03, 0x81, 0x81, 0x00, + 0x36, 0x1f, 0xb3, 0x7a, 0x0c, 0x75, 0xc9, 0x6e, + 0x37, 0x46, 0x61, 0x2b, 0xd5, 0xbd, 0xc0, 0xa7, + 0x4b, 0xcc, 0x46, 0x9a, 0x81, 0x58, 0x7c, 0x85, + 0x79, 0x29, 0xc8, 0xc8, 0xc6, 0x67, 0xdd, 0x32, + 0x56, 0x45, 0x2b, 0x75, 0xb6, 0xe9, 0x24, 0xa9, + 0x50, 0x9a, 0xbe, 0x1f, 0x5a, 0xfa, 0x1a, 0x15, + 0xd9, 0xcc, 0x55, 0x95, 0x72, 0x16, 0x83, 0xb9, + 0xc2, 0xb6, 0x8f, 0xfd, 0x88, 0x8c, 0x38, 0x84, + 0x1d, 0xab, 0x5d, 0x92, 0x31, 0x13, 0x4f, 0xfd, + 0x83, 0x3b, 0xc6, 0x9d, 0xf1, 0x11, 0x62, 0xb6, + 0x8b, 0xec, 0xab, 0x67, 0xbe, 0xc8, 0x64, 0xb0, + 0x11, 0x50, 0x46, 0x58, 0x17, 0x6b, 0x99, 0x1c, + 0xd3, 0x1d, 0xfc, 0x06, 0xf1, 0x0e, 0xe5, 0x96, + 0xa8, 0x0c, 0xf9, 0x78, 0x20, 0xb7, 0x44, 0x18, + 0x51, 0x8d, 0x10, 0x7e, 0x4f, 0x94, 0x67, 0xdf, + 0xa3, 0x4e, 0x70, 0x73, 0x8e, 0x90, 0x91, 0x85, + 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, + 0x82, 0x00, 0x80, 0xa7, 0x2f, 0xed, 0xfa, 0xc2, + 0xbd, 0x46, 0xa1, 0xf2, 0x69, 0xc5, 0x1d, 0xa1, + 0x34, 0xd6, 0xd0, 0x84, 0xf5, 0x5d, 0x8c, 0x82, + 0x8d, 0x98, 0x82, 0x9c, 0xd9, 0x07, 0xe0, 0xf7, + 0x55, 0x49, 0x4d, 0xa1, 0x48, 0x59, 0x02, 0xd3, + 0x84, 0x37, 0xaf, 0x01, 0xb3, 0x3a, 0xf4, 0xed, + 0x99, 0xbe, 0x67, 0x36, 0x19, 0x55, 0xf3, 0xf9, + 0xcb, 0x94, 0xe5, 0x7b, 0x8b, 0x77, 0xf2, 0x5f, + 0x4c, 0xfe, 0x01, 0x1f, 0x7b, 0xd7, 0x23, 0x49, + 0x0c, 0xcb, 0x6c, 0xb0, 0xe7, 0x77, 0xd6, 0xcf, + 0xa8, 0x7d, 0xdb, 0xa7, 0x14, 0xe2, 0xf5, 0xf3, + 0xff, 0xba, 0x23, 0xd2, 0x9a, 0x36, 0x14, 0x60, + 0x2a, 0x91, 0x5d, 0x2b, 0x35, 0x3b, 0xb6, 0xdd, + 0xcb, 0x6b, 0xdc, 0x18, 0xdc, 0x33, 0xb8, 0xb3, + 0xc7, 0x27, 0x7e, 0xfc, 0xd2, 0xf7, 0x97, 0x90, + 0x5e, 0x17, 0xac, 0x14, 0x8e, 0x0f, 0xca, 0xb5, + 0x6f, 0xc9, 0x2d, 0x16, 0x03, 0x01, 0x00, 0x86, + 0x0f, 0x00, 0x00, 0x82, 0x00, 0x80, 0x44, 0x7f, + 0xa2, 0x59, 0x60, 0x0b, 0x5a, 0xc4, 0xaf, 0x1e, + 0x60, 0xa5, 0x24, 0xea, 0xc1, 0xc3, 0x22, 0x21, + 0x6b, 0x22, 0x8b, 0x2a, 0x11, 0x82, 0x68, 0x7d, + 0xb9, 0xdd, 0x9c, 0x27, 0x4c, 0xc2, 0xc8, 0xa2, + 0x8b, 0x6b, 0x77, 0x8d, 0x3a, 0x2b, 0x8d, 0x2f, + 0x6a, 0x2b, 0x43, 0xd2, 0xd1, 0xc6, 0x41, 0x79, + 0xa2, 0x4f, 0x2b, 0xc2, 0xf7, 0xb2, 0x10, 0xad, + 0xa6, 0x01, 0x51, 0x51, 0x25, 0xe7, 0x58, 0x7a, + 0xcf, 0x3b, 0xc4, 0x29, 0xb5, 0xe5, 0xa7, 0x83, + 0xe6, 0xcb, 0x1e, 0xf3, 0x02, 0x0f, 0x53, 0x3b, + 0xb5, 0x39, 0xef, 0x9c, 0x42, 0xe0, 0xa6, 0x9b, + 0x2b, 0xdd, 0x60, 0xae, 0x0a, 0x73, 0x35, 0xbe, + 0x26, 0x10, 0x1b, 0xe9, 0xe9, 0x61, 0xab, 0x20, + 0xa5, 0x48, 0xc6, 0x60, 0xa6, 0x50, 0x3c, 0xfb, + 0xa7, 0xca, 0xb0, 0x80, 0x95, 0x1e, 0xce, 0xc7, + 0xbb, 0x68, 0x44, 0xdc, 0x0e, 0x0e, 0x14, 0x03, + 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, + 0x24, 0xb6, 0xcd, 0x0c, 0x78, 0xfd, 0xd6, 0xff, + 0xbe, 0x97, 0xd5, 0x0a, 0x7d, 0x4f, 0xa1, 0x03, + 0x78, 0xc8, 0x61, 0x6f, 0xf2, 0x4b, 0xa8, 0x56, + 0x4f, 0x3c, 0xa2, 0xd9, 0xd0, 0x20, 0x13, 0x1b, + 0x8b, 0x36, 0xb7, 0x33, 0x9c, + }, + + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0xa3, 0x43, 0x94, 0xe7, 0xdf, + 0xb6, 0xc3, 0x03, 0x9f, 0xc1, 0x59, 0x0c, 0xc3, + 0x13, 0xae, 0xed, 0xcf, 0xff, 0xf1, 0x80, 0xf3, + 0x13, 0x63, 0x1c, 0xf0, 0xca, 0xad, 0x9e, 0x71, + 0x46, 0x5f, 0x6b, 0xeb, 0x10, 0x3f, 0xe3, 0x17, + 0x03, 0x01, 0x00, 0x21, 0xe9, 0x80, 0x95, 0x6e, + 0x05, 0x55, 0x2f, 0xed, 0x4d, 0xde, 0x17, 0x3a, + 0x32, 0x9b, 0x2a, 0x74, 0x30, 0x4f, 0xe0, 0x9f, + 0x4e, 0xd3, 0x06, 0xbd, 0x3a, 0x43, 0x75, 0x8b, + 0x5b, 0x9a, 0xd8, 0x2e, 0x56, 0x15, 0x03, 0x01, + 0x00, 0x16, 0x53, 0xf5, 0xff, 0xe0, 0xa1, 0x6c, + 0x33, 0xf4, 0x4e, 0x89, 0x68, 0xe1, 0xf7, 0x61, + 0x13, 0xb3, 0x12, 0xa1, 0x8e, 0x5a, 0x7a, 0x02, + }}}, +} + +// cert.pem and key.pem were generated with generate_cert.go +// Thus, they have no ExtKeyUsage fields and trigger an error +// when verification is turned on. + +var clicert = loadPEMCert(` +-----BEGIN CERTIFICATE----- +MIIB7TCCAVigAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD +bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTExMTIwODA3NTUxMloXDTEyMTIwNzA4 +MDAxMlowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGc +MAsGCSqGSIb3DQEBAQOBjAAwgYgCgYBO0Hsx44Jk2VnAwoekXh6LczPHY1PfZpIG +hPZk1Y/kNqcdK+izIDZFI7Xjla7t4PUgnI2V339aEu+H5Fto5OkOdOwEin/ekyfE +ARl6vfLcPRSr0FTKIQzQTW6HLlzF0rtNS0/Otiz3fojsfNcCkXSmHgwa2uNKWi7e +E5xMQIhZkwIDAQABozIwMDAOBgNVHQ8BAf8EBAMCAKAwDQYDVR0OBAYEBAECAwQw +DwYDVR0jBAgwBoAEAQIDBDALBgkqhkiG9w0BAQUDgYEANh+zegx1yW43RmEr1b3A +p0vMRpqBWHyFeSnIyMZn3TJWRSt1tukkqVCavh9a+hoV2cxVlXIWg7nCto/9iIw4 +hB2rXZIxE0/9gzvGnfERYraL7KtnvshksBFQRlgXa5kc0x38BvEO5ZaoDPl4ILdE +GFGNEH5PlGffo05wc46QkYU= +-----END CERTIFICATE----- +`) + +/* corresponding key.pem for cert.pem is: +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgE7QezHjgmTZWcDCh6ReHotzM8djU99mkgaE9mTVj+Q2px0r6LMg +NkUjteOVru3g9SCcjZXff1oS74fkW2jk6Q507ASKf96TJ8QBGXq98tw9FKvQVMoh +DNBNbocuXMXSu01LT862LPd+iOx81wKRdKYeDBra40paLt4TnExAiFmTAgMBAAEC +gYBxvXd8yNteFTns8A/2yomEMC4yeosJJSpp1CsN3BJ7g8/qTnrVPxBy+RU+qr63 +t2WquaOu/cr5P8iEsa6lk20tf8pjKLNXeX0b1RTzK8rJLbS7nGzP3tvOhL096VtQ +dAo4ROEaro0TzYpHmpciSvxVIeEIAAdFDObDJPKqcJAxyQJBAJizfYgK8Gzx9fsx +hxp+VteCbVPg2euASH5Yv3K5LukRdKoSzHE2grUVQgN/LafC0eZibRanxHegYSr7 +7qaswKUCQQCEIWor/X4XTMdVj3Oj+vpiw75y/S9gh682+myZL+d/02IEkwnB098P +RkKVpenBHyrGg0oeN5La7URILWKj7CPXAkBKo6F+d+phNjwIFoN1Xb/RA32w/D1I +saG9sF+UEhRt9AxUfW/U/tIQ9V0ZHHcSg1XaCM5Nvp934brdKdvTOKnJAkBD5h/3 +Rybatlvg/fzBEaJFyq09zhngkxlZOUtBVTqzl17RVvY2orgH02U4HbCHy4phxOn7 +qTdQRYlHRftgnWK1AkANibn9PRYJ7mJyJ9Dyj2QeNcSkSTzrt0tPvUMf4+meJymN +1Ntu5+S1DLLzfxlaljWG6ylW6DNxujCyuXIV2rvAMAA= +-----END RSA PRIVATE KEY----- +*/ diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go index a40d18fd9..a931d8fb5 100644 --- a/src/pkg/crypto/tls/key_agreement.go +++ b/src/pkg/crypto/tls/key_agreement.go @@ -5,26 +5,26 @@ package tls import ( - "big" "crypto" "crypto/elliptic" "crypto/md5" "crypto/rsa" "crypto/sha1" "crypto/x509" + "errors" "io" - "os" + "math/big" ) // rsaKeyAgreement implements the standard TLS key agreement where the client // encrypts the pre-master secret to the server's public key. type rsaKeyAgreement struct{} -func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) { +func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { return nil, nil } -func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { +func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { preMasterSecret := make([]byte, 48) _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) if err != nil { @@ -32,15 +32,19 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe } if len(ckx.ciphertext) < 2 { - return nil, os.NewError("bad ClientKeyExchange") + return nil, errors.New("bad ClientKeyExchange") } - ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) - if ciphertextLen != len(ckx.ciphertext)-2 { - return nil, os.NewError("bad ClientKeyExchange") + + ciphertext := ckx.ciphertext + if version != versionSSL30 { + ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) + if ciphertextLen != len(ckx.ciphertext)-2 { + return nil, errors.New("bad ClientKeyExchange") + } + ciphertext = ckx.ciphertext[2:] } - ciphertext := ckx.ciphertext[2:] - err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret) + err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret) if err != nil { return nil, err } @@ -53,11 +57,11 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe return preMasterSecret, nil } -func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { - return os.NewError("unexpected ServerKeyExchange") +func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + return errors.New("unexpected ServerKeyExchange") } -func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { +func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { preMasterSecret := make([]byte, 48) preMasterSecret[0] = byte(clientHello.vers >> 8) preMasterSecret[1] = byte(clientHello.vers) @@ -86,13 +90,13 @@ func md5SHA1Hash(slices ...[]byte) []byte { for _, slice := range slices { hmd5.Write(slice) } - copy(md5sha1, hmd5.Sum()) + copy(md5sha1, hmd5.Sum(nil)) hsha1 := sha1.New() for _, slice := range slices { hsha1.Write(slice) } - copy(md5sha1[md5.Size:], hsha1.Sum()) + copy(md5sha1[md5.Size:], hsha1.Sum(nil)) return md5sha1 } @@ -101,11 +105,11 @@ func md5SHA1Hash(slices ...[]byte) []byte { // pre-master secret is then calculated using ECDH. type ecdheRSAKeyAgreement struct { privateKey []byte - curve *elliptic.Curve + curve elliptic.Curve x, y *big.Int } -func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) { +func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { var curveid uint16 Curve: @@ -126,13 +130,17 @@ Curve: } } + if curveid == 0 { + return nil, errors.New("tls: no supported elliptic curves offered") + } + var x, y *big.Int - var err os.Error - ka.privateKey, x, y, err = ka.curve.GenerateKey(config.rand()) + var err error + ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand()) if err != nil { return nil, err } - ecdhePublic := ka.curve.Marshal(x, y) + ecdhePublic := elliptic.Marshal(ka.curve, x, y) // http://tools.ietf.org/html/rfc4492#section-5.4 serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic)) @@ -143,9 +151,9 @@ Curve: copy(serverECDHParams[4:], ecdhePublic) md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams) - sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, crypto.MD5SHA1, md5sha1) + sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1) if err != nil { - return nil, os.NewError("failed to sign ECDHE parameters: " + err.String()) + return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) } skx := new(serverKeyExchangeMsg) @@ -159,30 +167,30 @@ Curve: return skx, nil } -func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { +func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { - return nil, os.NewError("bad ClientKeyExchange") + return nil, errors.New("bad ClientKeyExchange") } - x, y := ka.curve.Unmarshal(ckx.ciphertext[1:]) + x, y := elliptic.Unmarshal(ka.curve, ckx.ciphertext[1:]) if x == nil { - return nil, os.NewError("bad ClientKeyExchange") + return nil, errors.New("bad ClientKeyExchange") } x, _ = ka.curve.ScalarMult(x, y, ka.privateKey) - preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) + preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3) xBytes := x.Bytes() copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) return preMasterSecret, nil } -var errServerKeyExchange = os.NewError("invalid ServerKeyExchange") +var errServerKeyExchange = errors.New("invalid ServerKeyExchange") -func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { +func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { if len(skx.key) < 4 { return errServerKeyExchange } if skx.key[0] != 3 { // named curve - return os.NewError("server selected unsupported curve") + return errors.New("server selected unsupported curve") } curveid := uint16(skx.key[1])<<8 | uint16(skx.key[2]) @@ -194,14 +202,14 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH case curveP521: ka.curve = elliptic.P521() default: - return os.NewError("server selected unsupported curve") + return errors.New("server selected unsupported curve") } publicLen := int(skx.key[3]) if publicLen+4 > len(skx.key) { return errServerKeyExchange } - ka.x, ka.y = ka.curve.Unmarshal(skx.key[4 : 4+publicLen]) + ka.x, ka.y = elliptic.Unmarshal(ka.curve, skx.key[4:4+publicLen]) if ka.x == nil { return errServerKeyExchange } @@ -221,20 +229,20 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), crypto.MD5SHA1, md5sha1, sig) } -func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { +func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { if ka.curve == nil { - return nil, nil, os.NewError("missing ServerKeyExchange message") + return nil, nil, errors.New("missing ServerKeyExchange message") } - priv, mx, my, err := ka.curve.GenerateKey(config.rand()) + priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand()) if err != nil { return nil, nil, err } x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv) - preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) + preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3) xBytes := x.Bytes() copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) - serialized := ka.curve.Marshal(mx, my) + serialized := elliptic.Marshal(ka.curve, mx, my) ckx := new(clientKeyExchangeMsg) ckx.ciphertext = make([]byte, 1+len(serialized)) diff --git a/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py b/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py index c03eaa6ea..5692bd32f 100644 --- a/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py +++ b/src/pkg/crypto/tls/parse-gnutls-cli-debug-log.py @@ -35,6 +35,8 @@ for line in sys.stdin.readlines(): bs = line.split() for b in bs: currentBlock.append(int(b, 16)) + elif line.startswith("|<7>| RB-PEEK: Read 1 bytes"): + currentBlock = currentBlock[:-1] if len(currentBlock) > 0: blocks.append(currentBlock) diff --git a/src/pkg/crypto/tls/prf.go b/src/pkg/crypto/tls/prf.go index 478cf65f9..637ef03e2 100644 --- a/src/pkg/crypto/tls/prf.go +++ b/src/pkg/crypto/tls/prf.go @@ -9,7 +9,6 @@ import ( "crypto/md5" "crypto/sha1" "hash" - "os" ) // Split a premaster secret in two as specified in RFC 4346, section 5. @@ -23,14 +22,14 @@ func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { func pHash(result, secret, seed []byte, hash func() hash.Hash) { h := hmac.New(hash, secret) h.Write(seed) - a := h.Sum() + a := h.Sum(nil) j := 0 for j < len(result) { h.Reset() h.Write(a) h.Write(seed) - b := h.Sum() + b := h.Sum(nil) todo := len(b) if j+todo > len(result) { todo = len(result) - j @@ -40,7 +39,7 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) { h.Reset() h.Write(a) - a = h.Sum() + a = h.Sum(nil) } } @@ -63,6 +62,39 @@ func pRF10(result, secret, label, seed []byte) { } } +// pRF30 implements the SSL 3.0 pseudo-random function, as defined in +// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6. +func pRF30(result, secret, label, seed []byte) { + hashSHA1 := sha1.New() + hashMD5 := md5.New() + + done := 0 + i := 0 + // RFC5246 section 6.3 says that the largest PRF output needed is 128 + // bytes. Since no more ciphersuites will be added to SSLv3, this will + // remain true. Each iteration gives us 16 bytes so 10 iterations will + // be sufficient. + var b [11]byte + for done < len(result) { + for j := 0; j <= i; j++ { + b[j] = 'A' + byte(i) + } + + hashSHA1.Reset() + hashSHA1.Write(b[:i+1]) + hashSHA1.Write(secret) + hashSHA1.Write(seed) + digest := hashSHA1.Sum(nil) + + hashMD5.Reset() + hashMD5.Write(secret) + hashMD5.Write(digest) + + done += copy(result[done:], hashMD5.Sum(nil)) + i++ + } +} + const ( tlsRandomLength = 32 // Length of a random nonce in TLS 1.1. masterSecretLength = 48 // Length of a master secret in TLS 1.1. @@ -77,19 +109,24 @@ var serverFinishedLabel = []byte("server finished") // keysFromPreMasterSecret generates the connection keys from the pre master // secret, given the lengths of the MAC key, cipher key and IV, as defined in // RFC 2246, section 6.3. -func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { +func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { + prf := pRF10 + if version == versionSSL30 { + prf = pRF30 + } + var seed [tlsRandomLength * 2]byte copy(seed[0:len(clientRandom)], clientRandom) copy(seed[len(clientRandom):], serverRandom) masterSecret = make([]byte, masterSecretLength) - pRF10(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) + prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) copy(seed[0:len(clientRandom)], serverRandom) copy(seed[len(serverRandom):], clientRandom) n := 2*macLen + 2*keyLen + 2*ivLen keyMaterial := make([]byte, n) - pRF10(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) + prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) clientMAC = keyMaterial[:macLen] keyMaterial = keyMaterial[macLen:] serverMAC = keyMaterial[:macLen] @@ -104,6 +141,10 @@ func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byt return } +func newFinishedHash(version uint16) finishedHash { + return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version} +} + // A finishedHash calculates the hash of a set of handshake messages suitable // for including in a Finished message. type finishedHash struct { @@ -111,13 +152,10 @@ type finishedHash struct { clientSHA1 hash.Hash serverMD5 hash.Hash serverSHA1 hash.Hash + version uint16 } -func newFinishedHash() finishedHash { - return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New()} -} - -func (h finishedHash) Write(msg []byte) (n int, err os.Error) { +func (h finishedHash) Write(msg []byte) (n int, err error) { h.clientMD5.Write(msg) h.clientSHA1.Write(msg) h.serverMD5.Write(msg) @@ -125,9 +163,10 @@ func (h finishedHash) Write(msg []byte) (n int, err os.Error) { return len(msg), nil } -// finishedSum calculates the contents of the verify_data member of a Finished -// message given the MD5 and SHA1 hashes of a set of handshake messages. -func finishedSum(md5, sha1, label, masterSecret []byte) []byte { +// finishedSum10 calculates the contents of the verify_data member of a TLSv1 +// Finished message given the MD5 and SHA1 hashes of a set of handshake +// messages. +func finishedSum10(md5, sha1, label, masterSecret []byte) []byte { seed := make([]byte, len(md5)+len(sha1)) copy(seed, md5) copy(seed[len(md5):], sha1) @@ -136,18 +175,61 @@ func finishedSum(md5, sha1, label, masterSecret []byte) []byte { return out } +// finishedSum30 calculates the contents of the verify_data member of a SSLv3 +// Finished message given the MD5 and SHA1 hashes of a set of handshake +// messages. +func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []byte { + md5.Write(magic[:]) + md5.Write(masterSecret) + md5.Write(ssl30Pad1[:]) + md5Digest := md5.Sum(nil) + + md5.Reset() + md5.Write(masterSecret) + md5.Write(ssl30Pad2[:]) + md5.Write(md5Digest) + md5Digest = md5.Sum(nil) + + sha1.Write(magic[:]) + sha1.Write(masterSecret) + sha1.Write(ssl30Pad1[:40]) + sha1Digest := sha1.Sum(nil) + + sha1.Reset() + sha1.Write(masterSecret) + sha1.Write(ssl30Pad2[:40]) + sha1.Write(sha1Digest) + sha1Digest = sha1.Sum(nil) + + ret := make([]byte, len(md5Digest)+len(sha1Digest)) + copy(ret, md5Digest) + copy(ret[len(md5Digest):], sha1Digest) + return ret +} + +var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54} +var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52} + // clientSum returns the contents of the verify_data member of a client's // Finished message. func (h finishedHash) clientSum(masterSecret []byte) []byte { - md5 := h.clientMD5.Sum() - sha1 := h.clientSHA1.Sum() - return finishedSum(md5, sha1, clientFinishedLabel, masterSecret) + if h.version == versionSSL30 { + return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic) + } + + md5 := h.clientMD5.Sum(nil) + sha1 := h.clientSHA1.Sum(nil) + return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret) } // serverSum returns the contents of the verify_data member of a server's // Finished message. func (h finishedHash) serverSum(masterSecret []byte) []byte { - md5 := h.serverMD5.Sum() - sha1 := h.serverSHA1.Sum() - return finishedSum(md5, sha1, serverFinishedLabel, masterSecret) + if h.version == versionSSL30 { + return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic) + } + + md5 := h.serverMD5.Sum(nil) + sha1 := h.serverSHA1.Sum(nil) + return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret) } diff --git a/src/pkg/crypto/tls/prf_test.go b/src/pkg/crypto/tls/prf_test.go index f8c4acb9d..a32392cef 100644 --- a/src/pkg/crypto/tls/prf_test.go +++ b/src/pkg/crypto/tls/prf_test.go @@ -34,6 +34,7 @@ func TestSplitPreMasterSecret(t *testing.T) { } type testKeysFromTest struct { + version uint16 preMasterSecret string clientRandom, serverRandom string masterSecret string @@ -47,7 +48,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { in, _ := hex.DecodeString(test.preMasterSecret) clientRandom, _ := hex.DecodeString(test.clientRandom) serverRandom, _ := hex.DecodeString(test.serverRandom) - master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret10(in, clientRandom, serverRandom, test.macLen, test.keyLen, 0) + master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret(test.version, in, clientRandom, serverRandom, test.macLen, test.keyLen, 0) masterString := hex.EncodeToString(master) clientMACString := hex.EncodeToString(clientMAC) serverMACString := hex.EncodeToString(serverMAC) @@ -58,7 +59,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { serverMACString != test.serverMAC || clientKeyString != test.clientKey || serverKeyString != test.serverKey { - t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverMACString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) + t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s, %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverKeyString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) } } } @@ -66,6 +67,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { // These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 ` var testKeysFromTests = []testKeysFromTest{ { + versionTLS10, "0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5", "4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558", "4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db", @@ -78,6 +80,7 @@ var testKeysFromTests = []testKeysFromTest{ 16, }, { + versionTLS10, "03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890", "4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106", "4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c", @@ -90,6 +93,7 @@ var testKeysFromTests = []testKeysFromTest{ 16, }, { + versionTLS10, "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", @@ -101,4 +105,17 @@ var testKeysFromTests = []testKeysFromTest{ 20, 16, }, + { + versionSSL30, + "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", + "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", + "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", + "a614863e56299dcffeea2938f22c2ba023768dbe4b3f6877bc9c346c6ae529b51d9cb87ff9695ea4d01f2205584405b2", + "2c450d5b6f6e2013ac6bea6a0b32200d4e1ffb94", + "7a7a7438769536f2fb1ae49a61f0703b79b2dc53", + "f8f6b26c10f12855c9aafb1e0e839ccf", + "2b9d4b4a60cb7f396780ebff50650419", + 20, + 16, + }, } diff --git a/src/pkg/crypto/tls/root_test.go b/src/pkg/crypto/tls/root_test.go new file mode 100644 index 000000000..e61c21851 --- /dev/null +++ b/src/pkg/crypto/tls/root_test.go @@ -0,0 +1,61 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +import ( + "crypto/x509" + "runtime" + "testing" +) + +var tlsServers = []string{ + "google.com", + "github.com", + "twitter.com", +} + +func TestOSCertBundles(t *testing.T) { + if testing.Short() { + t.Logf("skipping certificate tests in short mode") + return + } + + for _, addr := range tlsServers { + conn, err := Dial("tcp", addr+":443", &Config{ServerName: addr}) + if err != nil { + t.Errorf("unable to verify %v: %v", addr, err) + continue + } + err = conn.Close() + if err != nil { + t.Error(err) + } + } +} + +func TestCertHostnameVerifyWindows(t *testing.T) { + if runtime.GOOS != "windows" { + return + } + + if testing.Short() { + t.Logf("skipping certificate tests in short mode") + return + } + + for _, addr := range tlsServers { + cfg := &Config{ServerName: "example.com"} + conn, err := Dial("tcp", addr+":443", cfg) + if err == nil { + conn.Close() + t.Errorf("should fail to verify for example.com: %v", addr) + continue + } + _, ok := err.(x509.HostnameError) + if !ok { + t.Errorf("error type mismatch, got: %v", err) + } + } +} diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go index 4f0859fee..09df5ad44 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -2,17 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package tls partially implements the TLS 1.1 protocol, as specified in RFC -// 4346. +// Package tls partially implements TLS 1.0, as specified in RFC 2246. package tls import ( "crypto/rsa" "crypto/x509" "encoding/pem" + "errors" "io/ioutil" "net" - "os" "strings" ) @@ -33,16 +32,16 @@ func Client(conn net.Conn, config *Config) *Conn { return &Conn{conn: conn, config: config, isClient: true} } -// A Listener implements a network listener (net.Listener) for TLS connections. -type Listener struct { - listener net.Listener - config *Config +// A listener implements a network listener (net.Listener) for TLS connections. +type listener struct { + net.Listener + config *Config } // Accept waits for and returns the next incoming TLS connection. // The returned connection c is a *tls.Conn. -func (l *Listener) Accept() (c net.Conn, err os.Error) { - c, err = l.listener.Accept() +func (l *listener) Accept() (c net.Conn, err error) { + c, err = l.Listener.Accept() if err != nil { return } @@ -50,30 +49,24 @@ func (l *Listener) Accept() (c net.Conn, err os.Error) { return } -// Close closes the listener. -func (l *Listener) Close() os.Error { return l.listener.Close() } - -// Addr returns the listener's network address. -func (l *Listener) Addr() net.Addr { return l.listener.Addr() } - // NewListener creates a Listener which accepts connections from an inner // Listener and wraps each connection with Server. // The configuration config must be non-nil and must have // at least one certificate. -func NewListener(listener net.Listener, config *Config) (l *Listener) { - l = new(Listener) - l.listener = listener +func NewListener(inner net.Listener, config *Config) net.Listener { + l := new(listener) + l.Listener = inner l.config = config - return + return l } // Listen creates a TLS listener accepting connections on the // given network address using net.Listen. // The configuration config must be non-nil and must have // at least one certificate. -func Listen(network, laddr string, config *Config) (*Listener, os.Error) { +func Listen(network, laddr string, config *Config) (net.Listener, error) { if config == nil || len(config.Certificates) == 0 { - return nil, os.NewError("tls.Listen: no certificates in configuration") + return nil, errors.New("tls.Listen: no certificates in configuration") } l, err := net.Listen(network, laddr) if err != nil { @@ -88,7 +81,7 @@ func Listen(network, laddr string, config *Config) (*Listener, os.Error) { // Dial interprets a nil configuration as equivalent to // the zero configuration; see the documentation of Config // for the defaults. -func Dial(network, addr string, config *Config) (*Conn, os.Error) { +func Dial(network, addr string, config *Config) (*Conn, error) { raddr := addr c, err := net.Dial(network, raddr) if err != nil { @@ -104,7 +97,9 @@ func Dial(network, addr string, config *Config) (*Conn, os.Error) { if config == nil { config = defaultConfig() } - if config.ServerName != "" { + // If no ServerName is set, infer the ServerName + // from the hostname we're connecting to. + if config.ServerName == "" { // Make a copy to avoid polluting argument or default. c := *config c.ServerName = hostname @@ -120,7 +115,7 @@ func Dial(network, addr string, config *Config) (*Conn, os.Error) { // LoadX509KeyPair reads and parses a public/private key pair from a pair of // files. The files must contain PEM encoded data. -func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os.Error) { +func LoadX509KeyPair(certFile, keyFile string) (cert Certificate, err error) { certPEMBlock, err := ioutil.ReadFile(certFile) if err != nil { return @@ -134,7 +129,7 @@ func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os. // X509KeyPair parses a public/private key pair from a pair of // PEM encoded data. -func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Error) { +func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) { var certDERBlock *pem.Block for { certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) @@ -147,20 +142,31 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err } if len(cert.Certificate) == 0 { - err = os.NewError("crypto/tls: failed to parse certificate PEM data") + err = errors.New("crypto/tls: failed to parse certificate PEM data") return } keyDERBlock, _ := pem.Decode(keyPEMBlock) if keyDERBlock == nil { - err = os.NewError("crypto/tls: failed to parse key PEM data") + err = errors.New("crypto/tls: failed to parse key PEM data") return } - key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) - if err != nil { - err = os.NewError("crypto/tls: failed to parse key: " + err.String()) - return + // OpenSSL 0.9.8 generates PKCS#1 private keys by default, while + // OpenSSL 1.0.0 generates PKCS#8 keys. We try both. + var key *rsa.PrivateKey + if key, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes); err != nil { + var privKey interface{} + if privKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes); err != nil { + err = errors.New("crypto/tls: failed to parse key: " + err.Error()) + return + } + + var ok bool + if key, ok = privKey.(*rsa.PrivateKey); !ok { + err = errors.New("crypto/tls: found non-RSA private key in PKCS#8 wrapping") + return + } } cert.PrivateKey = key @@ -173,7 +179,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err } if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 { - err = os.NewError("crypto/tls: private key does not match public key") + err = errors.New("crypto/tls: private key does not match public key") return } diff --git a/src/pkg/crypto/twofish/Makefile b/src/pkg/crypto/twofish/Makefile deleted file mode 100644 index aec61659d..000000000 --- a/src/pkg/crypto/twofish/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/twofish -GOFILES=\ - twofish.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/twofish/twofish.go b/src/pkg/crypto/twofish/twofish.go deleted file mode 100644 index 2e537c606..000000000 --- a/src/pkg/crypto/twofish/twofish.go +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package twofish implements Bruce Schneier's Twofish encryption algorithm. -package twofish - -// Twofish is defined in http://www.schneier.com/paper-twofish-paper.pdf [TWOFISH] - -// This code is a port of the LibTom C implementation. -// See http://libtom.org/?page=features&newsitems=5&whatfile=crypt. -// LibTomCrypt is free for all purposes under the public domain. -// It was heavily inspired by the go blowfish package. - -import ( - "os" - "strconv" -) - -// BlockSize is the constant block size of Twofish. -const BlockSize = 16 - -const mdsPolynomial = 0x169 // x^8 + x^6 + x^5 + x^3 + 1, see [TWOFISH] 4.2 -const rsPolynomial = 0x14d // x^8 + x^6 + x^3 + x^2 + 1, see [TWOFISH] 4.3 - -// A Cipher is an instance of Twofish encryption using a particular key. -type Cipher struct { - s [4][256]uint32 - k [40]uint32 -} - -type KeySizeError int - -func (k KeySizeError) String() string { - return "crypto/twofish: invalid key size " + strconv.Itoa(int(k)) -} - -// NewCipher creates and returns a Cipher. -// The key argument should be the Twofish key, 16, 24 or 32 bytes. -func NewCipher(key []byte) (*Cipher, os.Error) { - keylen := len(key) - - if keylen != 16 && keylen != 24 && keylen != 32 { - return nil, KeySizeError(keylen) - } - - // k is the number of 64 bit words in key - k := keylen / 8 - - // Create the S[..] words - var S [4 * 4]byte - for i := 0; i < k; i++ { - // Computes [y0 y1 y2 y3] = rs . [x0 x1 x2 x3 x4 x5 x6 x7] - for j, rsRow := range rs { - for k, rsVal := range rsRow { - S[4*i+j] ^= gfMult(key[8*i+k], rsVal, rsPolynomial) - } - } - } - - // Calculate subkeys - c := new(Cipher) - var tmp [4]byte - for i := byte(0); i < 20; i++ { - // A = h(p * 2x, Me) - for j := range tmp { - tmp[j] = 2 * i - } - A := h(tmp[:], key, 0) - - // B = rolc(h(p * (2x + 1), Mo), 8) - for j := range tmp { - tmp[j] = 2*i + 1 - } - B := h(tmp[:], key, 1) - B = rol(B, 8) - - c.k[2*i] = A + B - - // K[2i+1] = (A + 2B) <<< 9 - c.k[2*i+1] = rol(2*B+A, 9) - } - - // Calculate sboxes - switch k { - case 2: - for i := range c.s[0] { - c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][byte(i)]^S[0]]^S[4]], 0) - c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][byte(i)]^S[1]]^S[5]], 1) - c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][byte(i)]^S[2]]^S[6]], 2) - c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][byte(i)]^S[3]]^S[7]], 3) - } - case 3: - for i := range c.s[0] { - c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]], 0) - c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[1]]^S[5]]^S[9]], 1) - c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]], 2) - c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[3]]^S[7]]^S[11]], 3) - } - default: - for i := range c.s[0] { - c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]]^S[12]], 0) - c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[1]]^S[5]]^S[9]]^S[13]], 1) - c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]]^S[14]], 2) - c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][sbox[1][byte(i)]^S[3]]^S[7]]^S[11]]^S[15]], 3) - } - } - - return c, nil -} - -// Reset zeros the key data, so that it will no longer appear in the process's -// memory. -func (c *Cipher) Reset() { - for i := range c.k { - c.k[i] = 0 - } - for i := range c.s { - for j := 0; j < 256; j++ { - c.s[i][j] = 0 - } - } -} - -// BlockSize returns the Twofish block size, 16 bytes. -func (c *Cipher) BlockSize() int { return BlockSize } - -// store32l stores src in dst in little-endian form. -func store32l(dst []byte, src uint32) { - dst[0] = byte(src) - dst[1] = byte(src >> 8) - dst[2] = byte(src >> 16) - dst[3] = byte(src >> 24) - return -} - -// load32l reads a little-endian uint32 from src. -func load32l(src []byte) uint32 { - return uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24 -} - -// rol returns x after a left circular rotation of y bits. -func rol(x, y uint32) uint32 { - return (x << (y & 31)) | (x >> (32 - (y & 31))) -} - -// ror returns x after a right circular rotation of y bits. -func ror(x, y uint32) uint32 { - return (x >> (y & 31)) | (x << (32 - (y & 31))) -} - -// The RS matrix. See [TWOFISH] 4.3 -var rs = [4][8]byte{ - {0x01, 0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E}, - {0xA4, 0x56, 0x82, 0xF3, 0x1E, 0xC6, 0x68, 0xE5}, - {0x02, 0xA1, 0xFC, 0xC1, 0x47, 0xAE, 0x3D, 0x19}, - {0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E, 0x03}, -} - -// sbox tables -var sbox = [2][256]byte{ - { - 0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38, - 0x0d, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48, - 0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82, - 0x63, 0x01, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0x0c, 0xe3, 0x61, - 0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0x0b, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1, - 0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x03, 0x56, 0xd4, 0x1c, 0x1e, 0xd7, - 0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71, - 0x81, 0x79, 0x09, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x08, 0x86, 0xe7, - 0xa1, 0x1d, 0xaa, 0xed, 0x06, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90, - 0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0x0a, 0xef, - 0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64, - 0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x05, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0x0e, 0xa7, 0x5a, - 0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x02, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d, - 0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, - 0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4, - 0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0x0f, 0x00, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0, - }, - { - 0x75, 0xf3, 0xc6, 0xf4, 0xdb, 0x7b, 0xfb, 0xc8, 0x4a, 0xd3, 0xe6, 0x6b, 0x45, 0x7d, 0xe8, 0x4b, - 0xd6, 0x32, 0xd8, 0xfd, 0x37, 0x71, 0xf1, 0xe1, 0x30, 0x0f, 0xf8, 0x1b, 0x87, 0xfa, 0x06, 0x3f, - 0x5e, 0xba, 0xae, 0x5b, 0x8a, 0x00, 0xbc, 0x9d, 0x6d, 0xc1, 0xb1, 0x0e, 0x80, 0x5d, 0xd2, 0xd5, - 0xa0, 0x84, 0x07, 0x14, 0xb5, 0x90, 0x2c, 0xa3, 0xb2, 0x73, 0x4c, 0x54, 0x92, 0x74, 0x36, 0x51, - 0x38, 0xb0, 0xbd, 0x5a, 0xfc, 0x60, 0x62, 0x96, 0x6c, 0x42, 0xf7, 0x10, 0x7c, 0x28, 0x27, 0x8c, - 0x13, 0x95, 0x9c, 0xc7, 0x24, 0x46, 0x3b, 0x70, 0xca, 0xe3, 0x85, 0xcb, 0x11, 0xd0, 0x93, 0xb8, - 0xa6, 0x83, 0x20, 0xff, 0x9f, 0x77, 0xc3, 0xcc, 0x03, 0x6f, 0x08, 0xbf, 0x40, 0xe7, 0x2b, 0xe2, - 0x79, 0x0c, 0xaa, 0x82, 0x41, 0x3a, 0xea, 0xb9, 0xe4, 0x9a, 0xa4, 0x97, 0x7e, 0xda, 0x7a, 0x17, - 0x66, 0x94, 0xa1, 0x1d, 0x3d, 0xf0, 0xde, 0xb3, 0x0b, 0x72, 0xa7, 0x1c, 0xef, 0xd1, 0x53, 0x3e, - 0x8f, 0x33, 0x26, 0x5f, 0xec, 0x76, 0x2a, 0x49, 0x81, 0x88, 0xee, 0x21, 0xc4, 0x1a, 0xeb, 0xd9, - 0xc5, 0x39, 0x99, 0xcd, 0xad, 0x31, 0x8b, 0x01, 0x18, 0x23, 0xdd, 0x1f, 0x4e, 0x2d, 0xf9, 0x48, - 0x4f, 0xf2, 0x65, 0x8e, 0x78, 0x5c, 0x58, 0x19, 0x8d, 0xe5, 0x98, 0x57, 0x67, 0x7f, 0x05, 0x64, - 0xaf, 0x63, 0xb6, 0xfe, 0xf5, 0xb7, 0x3c, 0xa5, 0xce, 0xe9, 0x68, 0x44, 0xe0, 0x4d, 0x43, 0x69, - 0x29, 0x2e, 0xac, 0x15, 0x59, 0xa8, 0x0a, 0x9e, 0x6e, 0x47, 0xdf, 0x34, 0x35, 0x6a, 0xcf, 0xdc, - 0x22, 0xc9, 0xc0, 0x9b, 0x89, 0xd4, 0xed, 0xab, 0x12, 0xa2, 0x0d, 0x52, 0xbb, 0x02, 0x2f, 0xa9, - 0xd7, 0x61, 0x1e, 0xb4, 0x50, 0x04, 0xf6, 0xc2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xbe, 0x91, - }, -} - -// gfMult returns a·b in GF(2^8)/p -func gfMult(a, b byte, p uint32) byte { - B := [2]uint32{0, uint32(b)} - P := [2]uint32{0, p} - var result uint32 - - // branchless GF multiplier - for i := 0; i < 7; i++ { - result ^= B[a&1] - a >>= 1 - B[1] = P[B[1]>>7] ^ (B[1] << 1) - } - result ^= B[a&1] - return byte(result) -} - -// mdsColumnMult calculates y{col} where [y0 y1 y2 y3] = MDS · [x0] -func mdsColumnMult(in byte, col int) uint32 { - mul01 := in - mul5B := gfMult(in, 0x5B, mdsPolynomial) - mulEF := gfMult(in, 0xEF, mdsPolynomial) - - switch col { - case 0: - return uint32(mul01) | uint32(mul5B)<<8 | uint32(mulEF)<<16 | uint32(mulEF)<<24 - case 1: - return uint32(mulEF) | uint32(mulEF)<<8 | uint32(mul5B)<<16 | uint32(mul01)<<24 - case 2: - return uint32(mul5B) | uint32(mulEF)<<8 | uint32(mul01)<<16 | uint32(mulEF)<<24 - case 3: - return uint32(mul5B) | uint32(mul01)<<8 | uint32(mulEF)<<16 | uint32(mul5B)<<24 - } - - panic("unreachable") -} - -// h implements the S-box generation function. See [TWOFISH] 4.3.5 -func h(in, key []byte, offset int) uint32 { - var y [4]byte - for x := range y { - y[x] = in[x] - } - switch len(key) / 8 { - case 4: - y[0] = sbox[1][y[0]] ^ key[4*(6+offset)+0] - y[1] = sbox[0][y[1]] ^ key[4*(6+offset)+1] - y[2] = sbox[0][y[2]] ^ key[4*(6+offset)+2] - y[3] = sbox[1][y[3]] ^ key[4*(6+offset)+3] - fallthrough - case 3: - y[0] = sbox[1][y[0]] ^ key[4*(4+offset)+0] - y[1] = sbox[1][y[1]] ^ key[4*(4+offset)+1] - y[2] = sbox[0][y[2]] ^ key[4*(4+offset)+2] - y[3] = sbox[0][y[3]] ^ key[4*(4+offset)+3] - fallthrough - case 2: - y[0] = sbox[1][sbox[0][sbox[0][y[0]]^key[4*(2+offset)+0]]^key[4*(0+offset)+0]] - y[1] = sbox[0][sbox[0][sbox[1][y[1]]^key[4*(2+offset)+1]]^key[4*(0+offset)+1]] - y[2] = sbox[1][sbox[1][sbox[0][y[2]]^key[4*(2+offset)+2]]^key[4*(0+offset)+2]] - y[3] = sbox[0][sbox[1][sbox[1][y[3]]^key[4*(2+offset)+3]]^key[4*(0+offset)+3]] - } - // [y0 y1 y2 y3] = MDS . [x0 x1 x2 x3] - var mdsMult uint32 - for i := range y { - mdsMult ^= mdsColumnMult(y[i], i) - } - return mdsMult -} - -// Encrypt encrypts a 16-byte block from src to dst, which may overlap. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { - S1 := c.s[0] - S2 := c.s[1] - S3 := c.s[2] - S4 := c.s[3] - - // Load input - ia := load32l(src[0:4]) - ib := load32l(src[4:8]) - ic := load32l(src[8:12]) - id := load32l(src[12:16]) - - // Pre-whitening - ia ^= c.k[0] - ib ^= c.k[1] - ic ^= c.k[2] - id ^= c.k[3] - - for i := 0; i < 8; i++ { - k := c.k[8+i*4 : 12+i*4] - t2 := S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)] - t1 := S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2 - ic = ror(ic^(t1+k[0]), 1) - id = rol(id, 1) ^ (t2 + t1 + k[1]) - - t2 = S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)] - t1 = S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2 - ia = ror(ia^(t1+k[2]), 1) - ib = rol(ib, 1) ^ (t2 + t1 + k[3]) - } - - // Output with "undo last swap" - ta := ic ^ c.k[4] - tb := id ^ c.k[5] - tc := ia ^ c.k[6] - td := ib ^ c.k[7] - - store32l(dst[0:4], ta) - store32l(dst[4:8], tb) - store32l(dst[8:12], tc) - store32l(dst[12:16], td) -} - -// Decrypt decrypts a 16-byte block from src to dst, which may overlap. -func (c *Cipher) Decrypt(dst, src []byte) { - S1 := c.s[0] - S2 := c.s[1] - S3 := c.s[2] - S4 := c.s[3] - - // Load input - ta := load32l(src[0:4]) - tb := load32l(src[4:8]) - tc := load32l(src[8:12]) - td := load32l(src[12:16]) - - // Undo undo final swap - ia := tc ^ c.k[6] - ib := td ^ c.k[7] - ic := ta ^ c.k[4] - id := tb ^ c.k[5] - - for i := 8; i > 0; i-- { - k := c.k[4+i*4 : 8+i*4] - t2 := S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)] - t1 := S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2 - ia = rol(ia, 1) ^ (t1 + k[2]) - ib = ror(ib^(t2+t1+k[3]), 1) - - t2 = S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)] - t1 = S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2 - ic = rol(ic, 1) ^ (t1 + k[0]) - id = ror(id^(t2+t1+k[1]), 1) - } - - // Undo pre-whitening - ia ^= c.k[0] - ib ^= c.k[1] - ic ^= c.k[2] - id ^= c.k[3] - - store32l(dst[0:4], ia) - store32l(dst[4:8], ib) - store32l(dst[8:12], ic) - store32l(dst[12:16], id) -} diff --git a/src/pkg/crypto/twofish/twofish_test.go b/src/pkg/crypto/twofish/twofish_test.go deleted file mode 100644 index 303081f3f..000000000 --- a/src/pkg/crypto/twofish/twofish_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package twofish - -import ( - "bytes" - "testing" -) - -var qbox = [2][4][16]byte{ - { - {0x8, 0x1, 0x7, 0xD, 0x6, 0xF, 0x3, 0x2, 0x0, 0xB, 0x5, 0x9, 0xE, 0xC, 0xA, 0x4}, - {0xE, 0xC, 0xB, 0x8, 0x1, 0x2, 0x3, 0x5, 0xF, 0x4, 0xA, 0x6, 0x7, 0x0, 0x9, 0xD}, - {0xB, 0xA, 0x5, 0xE, 0x6, 0xD, 0x9, 0x0, 0xC, 0x8, 0xF, 0x3, 0x2, 0x4, 0x7, 0x1}, - {0xD, 0x7, 0xF, 0x4, 0x1, 0x2, 0x6, 0xE, 0x9, 0xB, 0x3, 0x0, 0x8, 0x5, 0xC, 0xA}, - }, - { - {0x2, 0x8, 0xB, 0xD, 0xF, 0x7, 0x6, 0xE, 0x3, 0x1, 0x9, 0x4, 0x0, 0xA, 0xC, 0x5}, - {0x1, 0xE, 0x2, 0xB, 0x4, 0xC, 0x3, 0x7, 0x6, 0xD, 0xA, 0x5, 0xF, 0x9, 0x0, 0x8}, - {0x4, 0xC, 0x7, 0x5, 0x1, 0x6, 0x9, 0xA, 0x0, 0xE, 0xD, 0x8, 0x2, 0xB, 0x3, 0xF}, - {0xB, 0x9, 0x5, 0x1, 0xC, 0x3, 0xD, 0xE, 0x6, 0x4, 0x7, 0xF, 0x2, 0x0, 0x8, 0xA}, - }, -} - -// genSbox generates the variable sbox -func genSbox(qi int, x byte) byte { - a0, b0 := x/16, x%16 - for i := 0; i < 2; i++ { - a1 := a0 ^ b0 - b1 := (a0 ^ ((b0 << 3) | (b0 >> 1)) ^ (a0 << 3)) & 15 - a0 = qbox[qi][2*i][a1] - b0 = qbox[qi][2*i+1][b1] - } - return (b0 << 4) + a0 -} - -func TestSbox(t *testing.T) { - for n := range sbox { - for m := range sbox[n] { - if genSbox(n, byte(m)) != sbox[n][m] { - t.Errorf("#%d|%d: sbox value = %d want %d", n, m, sbox[n][m], genSbox(n, byte(m))) - } - } - } -} - -var testVectors = []struct { - key []byte - dec []byte - enc []byte -}{ - // These tests are extracted from LibTom - { - []byte{0x9F, 0x58, 0x9F, 0x5C, 0xF6, 0x12, 0x2C, 0x32, 0xB6, 0xBF, 0xEC, 0x2F, 0x2A, 0xE8, 0xC3, 0x5A}, - []byte{0xD4, 0x91, 0xDB, 0x16, 0xE7, 0xB1, 0xC3, 0x9E, 0x86, 0xCB, 0x08, 0x6B, 0x78, 0x9F, 0x54, 0x19}, - []byte{0x01, 0x9F, 0x98, 0x09, 0xDE, 0x17, 0x11, 0x85, 0x8F, 0xAA, 0xC3, 0xA3, 0xBA, 0x20, 0xFB, 0xC3}, - }, - { - []byte{0x88, 0xB2, 0xB2, 0x70, 0x6B, 0x10, 0x5E, 0x36, 0xB4, 0x46, 0xBB, 0x6D, 0x73, 0x1A, 0x1E, 0x88, - 0xEF, 0xA7, 0x1F, 0x78, 0x89, 0x65, 0xBD, 0x44}, - []byte{0x39, 0xDA, 0x69, 0xD6, 0xBA, 0x49, 0x97, 0xD5, 0x85, 0xB6, 0xDC, 0x07, 0x3C, 0xA3, 0x41, 0xB2}, - []byte{0x18, 0x2B, 0x02, 0xD8, 0x14, 0x97, 0xEA, 0x45, 0xF9, 0xDA, 0xAC, 0xDC, 0x29, 0x19, 0x3A, 0x65}, - }, - { - []byte{0xD4, 0x3B, 0xB7, 0x55, 0x6E, 0xA3, 0x2E, 0x46, 0xF2, 0xA2, 0x82, 0xB7, 0xD4, 0x5B, 0x4E, 0x0D, - 0x57, 0xFF, 0x73, 0x9D, 0x4D, 0xC9, 0x2C, 0x1B, 0xD7, 0xFC, 0x01, 0x70, 0x0C, 0xC8, 0x21, 0x6F}, - []byte{0x90, 0xAF, 0xE9, 0x1B, 0xB2, 0x88, 0x54, 0x4F, 0x2C, 0x32, 0xDC, 0x23, 0x9B, 0x26, 0x35, 0xE6}, - []byte{0x6C, 0xB4, 0x56, 0x1C, 0x40, 0xBF, 0x0A, 0x97, 0x05, 0x93, 0x1C, 0xB6, 0xD4, 0x08, 0xE7, 0xFA}, - }, - // These test are derived from http://www.schneier.com/code/ecb_ival.txt - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x9F, 0x58, 0x9F, 0x5C, 0xF6, 0x12, 0x2C, 0x32, 0xB6, 0xBF, 0xEC, 0x2F, 0x2A, 0xE8, 0xC3, 0x5A}, - }, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - }, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0xCF, 0xD1, 0xD2, 0xE5, 0xA9, 0xBE, 0x9C, 0xDF, 0x50, 0x1F, 0x13, 0xB8, 0x92, 0xBD, 0x22, 0x48}, - }, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - }, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x37, 0x52, 0x7B, 0xE0, 0x05, 0x23, 0x34, 0xB8, 0x9F, 0x0C, 0xFC, 0xCA, 0xE8, 0x7C, 0xFA, 0x20}, - }, -} - -func TestCipher(t *testing.T) { - for n, tt := range testVectors { - // Test if the plaintext (dec) is encrypts to the given - // ciphertext (enc) using the given key. Test also if enc can - // be decrypted again into dec. - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("#%d: NewCipher: %v", n, err) - return - } - - buf := make([]byte, 16) - c.Encrypt(buf, tt.dec) - if !bytes.Equal(buf, tt.enc) { - t.Errorf("#%d: encrypt = %x want %x", n, buf, tt.enc) - } - c.Decrypt(buf, tt.enc) - if !bytes.Equal(buf, tt.dec) { - t.Errorf("#%d: decrypt = %x want %x", n, buf, tt.dec) - } - - // Test that 16 zero bytes, encrypted 1000 times then decrypted - // 1000 times results in zero bytes again. - zero := make([]byte, 16) - buf = make([]byte, 16) - for i := 0; i < 1000; i++ { - c.Encrypt(buf, buf) - } - for i := 0; i < 1000; i++ { - c.Decrypt(buf, buf) - } - if !bytes.Equal(buf, zero) { - t.Errorf("#%d: encrypt/decrypt 1000: have %x want %x", n, buf, zero) - } - } -} diff --git a/src/pkg/crypto/x509/Makefile b/src/pkg/crypto/x509/Makefile deleted file mode 100644 index 14ffd095f..000000000 --- a/src/pkg/crypto/x509/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/x509 -GOFILES=\ - cert_pool.go\ - verify.go\ - x509.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/x509/cert_pool.go b/src/pkg/crypto/x509/cert_pool.go index 16cd92efc..616a0b3c1 100644 --- a/src/pkg/crypto/x509/cert_pool.go +++ b/src/pkg/crypto/x509/cert_pool.go @@ -5,12 +5,10 @@ package x509 import ( - "crypto/x509/pkix" "encoding/pem" - "strings" ) -// Roots is a set of certificates. +// CertPool is a set of certificates. type CertPool struct { bySubjectKeyId map[string][]int byName map[string][]int @@ -26,21 +24,20 @@ func NewCertPool() *CertPool { } } -func nameToKey(name *pkix.Name) string { - return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName -} - // findVerifiedParents attempts to find certificates in s which have signed the // given certificate. If no such certificate can be found or the signature // doesn't match, it returns nil. func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) { + if s == nil { + return + } var candidates []int if len(cert.AuthorityKeyId) > 0 { candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)] } if len(candidates) == 0 { - candidates = s.byName[nameToKey(&cert.Issuer)] + candidates = s.byName[string(cert.RawIssuer)] } for _, c := range candidates { @@ -72,15 +69,15 @@ func (s *CertPool) AddCert(cert *Certificate) { keyId := string(cert.SubjectKeyId) s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n) } - name := nameToKey(&cert.Subject) + name := string(cert.RawSubject) s.byName[name] = append(s.byName[name], n) } -// AppendCertsFromPEM attempts to parse a series of PEM encoded root -// certificates. It appends any certificates found to s and returns true if any -// certificates were successfully parsed. +// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates. +// It appends any certificates found to s and returns true if any certificates +// were successfully parsed. // -// On many Linux systems, /etc/ssl/cert.pem will contains the system wide set +// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set // of root CAs in a format suitable for this function. func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) { for len(pemCerts) > 0 { @@ -104,3 +101,13 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) { return } + +// Subjects returns a list of the DER-encoded subjects of +// all of the certificates in the pool. +func (s *CertPool) Subjects() (res [][]byte) { + res = make([][]byte, len(s.certs)) + for i, c := range s.certs { + res[i] = c.RawSubject + } + return +} diff --git a/src/pkg/crypto/x509/pkcs1.go b/src/pkg/crypto/x509/pkcs1.go new file mode 100644 index 000000000..873d3966e --- /dev/null +++ b/src/pkg/crypto/x509/pkcs1.go @@ -0,0 +1,122 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto/rsa" + "encoding/asn1" + "errors" + "math/big" +) + +// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. +type pkcs1PrivateKey struct { + Version int + N *big.Int + E int + D *big.Int + P *big.Int + Q *big.Int + // We ignore these values, if present, because rsa will calculate them. + Dp *big.Int `asn1:"optional"` + Dq *big.Int `asn1:"optional"` + Qinv *big.Int `asn1:"optional"` + + AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"` +} + +type pkcs1AdditionalRSAPrime struct { + Prime *big.Int + + // We ignore these values because rsa will calculate them. + Exp *big.Int + Coeff *big.Int +} + +// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. +func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) { + var priv pkcs1PrivateKey + rest, err := asn1.Unmarshal(der, &priv) + if len(rest) > 0 { + err = asn1.SyntaxError{Msg: "trailing data"} + return + } + if err != nil { + return + } + + if priv.Version > 1 { + return nil, errors.New("x509: unsupported private key version") + } + + if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { + return nil, errors.New("private key contains zero or negative value") + } + + key = new(rsa.PrivateKey) + key.PublicKey = rsa.PublicKey{ + E: priv.E, + N: priv.N, + } + + key.D = priv.D + key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes)) + key.Primes[0] = priv.P + key.Primes[1] = priv.Q + for i, a := range priv.AdditionalPrimes { + if a.Prime.Sign() <= 0 { + return nil, errors.New("private key contains zero or negative prime") + } + key.Primes[i+2] = a.Prime + // We ignore the other two values because rsa will calculate + // them as needed. + } + + err = key.Validate() + if err != nil { + return nil, err + } + key.Precompute() + + return +} + +// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. +func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { + key.Precompute() + + version := 0 + if len(key.Primes) > 2 { + version = 1 + } + + priv := pkcs1PrivateKey{ + Version: version, + N: key.N, + E: key.PublicKey.E, + D: key.D, + P: key.Primes[0], + Q: key.Primes[1], + Dp: key.Precomputed.Dp, + Dq: key.Precomputed.Dq, + Qinv: key.Precomputed.Qinv, + } + + priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues)) + for i, values := range key.Precomputed.CRTValues { + priv.AdditionalPrimes[i].Prime = key.Primes[2+i] + priv.AdditionalPrimes[i].Exp = values.Exp + priv.AdditionalPrimes[i].Coeff = values.Coeff + } + + b, _ := asn1.Marshal(priv) + return b +} + +// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key. +type rsaPublicKey struct { + N *big.Int + E int +} diff --git a/src/pkg/crypto/x509/pkcs8.go b/src/pkg/crypto/x509/pkcs8.go new file mode 100644 index 000000000..4d8e0518e --- /dev/null +++ b/src/pkg/crypto/x509/pkcs8.go @@ -0,0 +1,42 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" +) + +// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See +// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn. +type pkcs8 struct { + Version int + Algo pkix.AlgorithmIdentifier + PrivateKey []byte + // optional attributes omitted. +} + +// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See +// http://www.rsa.com/rsalabs/node.asp?id=2130 +func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { + var privKey pkcs8 + if _, err := asn1.Unmarshal(der, &privKey); err != nil { + return nil, err + } + switch { + case privKey.Algo.Algorithm.Equal(oidRSA): + key, err = ParsePKCS1PrivateKey(privKey.PrivateKey) + if err != nil { + return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) + } + return key, nil + default: + return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) + } + + panic("unreachable") +} diff --git a/src/pkg/crypto/x509/pkcs8_test.go b/src/pkg/crypto/x509/pkcs8_test.go new file mode 100644 index 000000000..372005f90 --- /dev/null +++ b/src/pkg/crypto/x509/pkcs8_test.go @@ -0,0 +1,20 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "encoding/hex" + "testing" +) + +var pkcs8PrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031` + +func TestPKCS8(t *testing.T) { + derBytes, _ := hex.DecodeString(pkcs8PrivateKeyHex) + _, err := ParsePKCS8PrivateKey(derBytes) + if err != nil { + t.Errorf("failed to decode PKCS8 key: %s", err) + } +} diff --git a/src/pkg/crypto/x509/pkix/Makefile b/src/pkg/crypto/x509/pkix/Makefile deleted file mode 100644 index e29b74c01..000000000 --- a/src/pkg/crypto/x509/pkix/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../../Make.inc - -TARG=crypto/x509/pkix -GOFILES=\ - pkix.go\ - -include ../../../../Make.pkg diff --git a/src/pkg/crypto/x509/pkix/pkix.go b/src/pkg/crypto/x509/pkix/pkix.go index 266fd557a..738659011 100644 --- a/src/pkg/crypto/x509/pkix/pkix.go +++ b/src/pkg/crypto/x509/pkix/pkix.go @@ -7,8 +7,8 @@ package pkix import ( - "asn1" - "big" + "encoding/asn1" + "math/big" "time" ) @@ -23,6 +23,8 @@ type RDNSequence []RelativeDistinguishedNameSET type RelativeDistinguishedNameSET []AttributeTypeAndValue +// AttributeTypeAndValue mirrors the ASN.1 structure of the same name in +// http://tools.ietf.org/html/rfc5280#section-4.1.2.4 type AttributeTypeAndValue struct { Type asn1.ObjectIdentifier Value interface{} @@ -43,6 +45,8 @@ type Name struct { Locality, Province []string StreetAddress, PostalCode []string SerialNumber, CommonName string + + Names []AttributeTypeAndValue } func (n *Name) FillFromRDNSequence(rdns *RDNSequence) { @@ -51,6 +55,7 @@ func (n *Name) FillFromRDNSequence(rdns *RDNSequence) { continue } atv := rdn[0] + n.Names = append(n.Names, atv) value, ok := atv.Value.(string) if !ok { continue @@ -139,10 +144,9 @@ type CertificateList struct { SignatureValue asn1.BitString } -// HasExpired returns true iff currentTimeSeconds is past the expiry time of -// certList. -func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { - return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds +// HasExpired returns true iff now is past the expiry time of certList. +func (certList *CertificateList) HasExpired(now time.Time) bool { + return now.After(certList.TBSCertList.NextUpdate) } // TBSCertificateList represents the ASN.1 structure of the same name. See RFC @@ -152,8 +156,8 @@ type TBSCertificateList struct { Version int `asn1:"optional,default:2"` Signature AlgorithmIdentifier Issuer RDNSequence - ThisUpdate *time.Time - NextUpdate *time.Time + ThisUpdate time.Time + NextUpdate time.Time RevokedCertificates []RevokedCertificate `asn1:"optional"` Extensions []Extension `asn1:"tag:0,optional,explicit"` } @@ -162,6 +166,6 @@ type TBSCertificateList struct { // 5280, section 5.1. type RevokedCertificate struct { SerialNumber *big.Int - RevocationTime *time.Time + RevocationTime time.Time Extensions []Extension `asn1:"optional"` } diff --git a/src/pkg/crypto/x509/root.go b/src/pkg/crypto/x509/root.go new file mode 100644 index 000000000..8aae14e09 --- /dev/null +++ b/src/pkg/crypto/x509/root.go @@ -0,0 +1,17 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import "sync" + +var ( + once sync.Once + systemRoots *CertPool +) + +func systemRootsPool() *CertPool { + once.Do(initSystemRoots) + return systemRoots +} diff --git a/src/pkg/crypto/x509/root_darwin.go b/src/pkg/crypto/x509/root_darwin.go new file mode 100644 index 000000000..0f99581e8 --- /dev/null +++ b/src/pkg/crypto/x509/root_darwin.go @@ -0,0 +1,80 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +/* +#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060 +#cgo LDFLAGS: -framework CoreFoundation -framework Security + +#include +#include + +// FetchPEMRoots fetches the system's list of trusted X.509 root certificates. +// +// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root +// certificates of the system. On failure, the function returns -1. +// +// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after +// we've consumed its content. +int FetchPEMRoots(CFDataRef *pemRoots) { + if (pemRoots == NULL) { + return -1; + } + + CFArrayRef certs = NULL; + OSStatus err = SecTrustCopyAnchorCertificates(&certs); + if (err != noErr) { + return -1; + } + + CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); + int i, ncerts = CFArrayGetCount(certs); + for (i = 0; i < ncerts; i++) { + CFDataRef data = NULL; + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); + if (cert == NULL) { + continue; + } + + // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. + // Once we support weak imports via cgo we should prefer that, and fall back to this + // for older systems. + err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); + if (err != noErr) { + continue; + } + + if (data != NULL) { + CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); + CFRelease(data); + } + } + + CFRelease(certs); + + *pemRoots = combinedData; + return 0; +} +*/ +import "C" +import "unsafe" + +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + return nil, nil +} + +func initSystemRoots() { + roots := NewCertPool() + + var data C.CFDataRef = nil + err := C.FetchPEMRoots(&data) + if err != -1 { + defer C.CFRelease(C.CFTypeRef(data)) + buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data))) + roots.AppendCertsFromPEM(buf) + } + + systemRoots = roots +} diff --git a/src/pkg/crypto/x509/root_stub.go b/src/pkg/crypto/x509/root_stub.go new file mode 100644 index 000000000..568004108 --- /dev/null +++ b/src/pkg/crypto/x509/root_stub.go @@ -0,0 +1,15 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build plan9 darwin,!cgo + +package x509 + +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + return nil, nil +} + +func initSystemRoots() { + systemRoots = NewCertPool() +} diff --git a/src/pkg/crypto/x509/root_unix.go b/src/pkg/crypto/x509/root_unix.go new file mode 100644 index 000000000..76e79f494 --- /dev/null +++ b/src/pkg/crypto/x509/root_unix.go @@ -0,0 +1,35 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd linux openbsd netbsd + +package x509 + +import "io/ioutil" + +// Possible certificate files; stop after finding one. +var certFiles = []string{ + "/etc/ssl/certs/ca-certificates.crt", // Linux etc + "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL + "/etc/ssl/ca-bundle.pem", // OpenSUSE + "/etc/ssl/cert.pem", // OpenBSD + "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD +} + +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + return nil, nil +} + +func initSystemRoots() { + roots := NewCertPool() + for _, file := range certFiles { + data, err := ioutil.ReadFile(file) + if err == nil { + roots.AppendCertsFromPEM(data) + break + } + } + + systemRoots = roots +} diff --git a/src/pkg/crypto/x509/root_windows.go b/src/pkg/crypto/x509/root_windows.go new file mode 100644 index 000000000..7e8f2af4b --- /dev/null +++ b/src/pkg/crypto/x509/root_windows.go @@ -0,0 +1,226 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package x509 + +import ( + "errors" + "syscall" + "unsafe" +) + +// Creates a new *syscall.CertContext representing the leaf certificate in an in-memory +// certificate store containing itself and all of the intermediate certificates specified +// in the opts.Intermediates CertPool. +// +// A pointer to the in-memory store is available in the returned CertContext's Store field. +// The store is automatically freed when the CertContext is freed using +// syscall.CertFreeCertificateContext. +func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) { + var storeCtx *syscall.CertContext + + leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw))) + if err != nil { + return nil, err + } + defer syscall.CertFreeCertificateContext(leafCtx) + + handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0) + if err != nil { + return nil, err + } + defer syscall.CertCloseStore(handle, 0) + + err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx) + if err != nil { + return nil, err + } + + if opts.Intermediates != nil { + for _, intermediate := range opts.Intermediates.certs { + ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw))) + if err != nil { + return nil, err + } + + err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil) + syscall.CertFreeCertificateContext(ctx) + if err != nil { + return nil, err + } + } + } + + return storeCtx, nil +} + +// extractSimpleChain extracts the final certificate chain from a CertSimpleChain. +func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) { + if simpleChain == nil || count == 0 { + return nil, errors.New("x509: invalid simple chain") + } + + simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:] + lastChain := simpleChains[count-1] + elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:] + for i := 0; i < int(lastChain.NumElements); i++ { + // Copy the buf, since ParseCertificate does not create its own copy. + cert := elements[i].CertContext + encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:] + buf := make([]byte, cert.Length) + copy(buf, encodedCert[:]) + parsedCert, err := ParseCertificate(buf) + if err != nil { + return nil, err + } + chain = append(chain, parsedCert) + } + + return chain, nil +} + +// checkChainTrustStatus checks the trust status of the certificate chain, translating +// any errors it finds into Go errors in the process. +func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error { + if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR { + status := chainCtx.TrustStatus.ErrorStatus + switch status { + case syscall.CERT_TRUST_IS_NOT_TIME_VALID: + return CertificateInvalidError{c, Expired} + default: + return UnknownAuthorityError{c} + } + } + return nil +} + +// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for +// use as a certificate chain for a SSL/TLS server. +func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error { + sslPara := &syscall.SSLExtraCertChainPolicyPara{ + AuthType: syscall.AUTHTYPE_SERVER, + ServerName: syscall.StringToUTF16Ptr(opts.DNSName), + } + sslPara.Size = uint32(unsafe.Sizeof(*sslPara)) + + para := &syscall.CertChainPolicyPara{ + ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)), + } + para.Size = uint32(unsafe.Sizeof(*para)) + + status := syscall.CertChainPolicyStatus{} + err := syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status) + if err != nil { + return err + } + + // TODO(mkrautz): use the lChainIndex and lElementIndex fields + // of the CertChainPolicyStatus to provide proper context, instead + // using c. + if status.Error != 0 { + switch status.Error { + case syscall.CERT_E_EXPIRED: + return CertificateInvalidError{c, Expired} + case syscall.CERT_E_CN_NO_MATCH: + return HostnameError{c, opts.DNSName} + case syscall.CERT_E_UNTRUSTEDROOT: + return UnknownAuthorityError{c} + default: + return UnknownAuthorityError{c} + } + } + + return nil +} + +// systemVerify is like Verify, except that it uses CryptoAPI calls +// to build certificate chains and verify them. +func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { + hasDNSName := opts != nil && len(opts.DNSName) > 0 + + storeCtx, err := createStoreContext(c, opts) + if err != nil { + return nil, err + } + defer syscall.CertFreeCertificateContext(storeCtx) + + para := new(syscall.CertChainPara) + para.Size = uint32(unsafe.Sizeof(*para)) + + // If there's a DNSName set in opts, assume we're verifying + // a certificate from a TLS server. + if hasDNSName { + oids := []*byte{ + &syscall.OID_PKIX_KP_SERVER_AUTH[0], + // Both IE and Chrome allow certificates with + // Server Gated Crypto as well. Some certificates + // in the wild require them. + &syscall.OID_SERVER_GATED_CRYPTO[0], + &syscall.OID_SGC_NETSCAPE[0], + } + para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR + para.RequestedUsage.Usage.Length = uint32(len(oids)) + para.RequestedUsage.Usage.UsageIdentifiers = &oids[0] + } else { + para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND + para.RequestedUsage.Usage.Length = 0 + para.RequestedUsage.Usage.UsageIdentifiers = nil + } + + var verifyTime *syscall.Filetime + if opts != nil && !opts.CurrentTime.IsZero() { + ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano()) + verifyTime = &ft + } + + // CertGetCertificateChain will traverse Windows's root stores + // in an attempt to build a verified certificate chain. Once + // it has found a verified chain, it stops. MSDN docs on + // CERT_CHAIN_CONTEXT: + // + // When a CERT_CHAIN_CONTEXT is built, the first simple chain + // begins with an end certificate and ends with a self-signed + // certificate. If that self-signed certificate is not a root + // or otherwise trusted certificate, an attempt is made to + // build a new chain. CTLs are used to create the new chain + // beginning with the self-signed certificate from the original + // chain as the end certificate of the new chain. This process + // continues building additional simple chains until the first + // self-signed certificate is a trusted certificate or until + // an additional simple chain cannot be built. + // + // The result is that we'll only get a single trusted chain to + // return to our caller. + var chainCtx *syscall.CertChainContext + err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx) + if err != nil { + return nil, err + } + defer syscall.CertFreeCertificateChain(chainCtx) + + err = checkChainTrustStatus(c, chainCtx) + if err != nil { + return nil, err + } + + if hasDNSName { + err = checkChainSSLServerPolicy(c, chainCtx, opts) + if err != nil { + return nil, err + } + } + + chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount)) + if err != nil { + return nil, err + } + + chains = append(chains, chain) + + return chains, nil +} + +func initSystemRoots() { + systemRoots = NewCertPool() +} diff --git a/src/pkg/crypto/x509/verify.go b/src/pkg/crypto/x509/verify.go index 4c0fecccb..307c5ef03 100644 --- a/src/pkg/crypto/x509/verify.go +++ b/src/pkg/crypto/x509/verify.go @@ -5,9 +5,10 @@ package x509 import ( - "os" + "runtime" "strings" "time" + "unicode/utf8" ) type InvalidReason int @@ -23,6 +24,9 @@ const ( // certificate has a name constraint which doesn't include the name // being checked. CANotAuthorizedForThisName + // TooManyIntermediates results when a path length constraint is + // violated. + TooManyIntermediates ) // CertificateInvalidError results when an odd error occurs. Users of this @@ -32,7 +36,7 @@ type CertificateInvalidError struct { Reason InvalidReason } -func (e CertificateInvalidError) String() string { +func (e CertificateInvalidError) Error() string { switch e.Reason { case NotAuthorizedToSign: return "x509: certificate is not authorized to sign other other certificates" @@ -40,6 +44,8 @@ func (e CertificateInvalidError) String() string { return "x509: certificate has expired or is not yet valid" case CANotAuthorizedForThisName: return "x509: a root or intermediate certificate is not authorized to sign in this domain" + case TooManyIntermediates: + return "x509: too many intermediates for path length constraint" } return "x509: unknown error" } @@ -51,7 +57,7 @@ type HostnameError struct { Host string } -func (h HostnameError) String() string { +func (h HostnameError) Error() string { var valid string c := h.Certificate if len(c.DNSNames) > 0 { @@ -67,7 +73,7 @@ type UnknownAuthorityError struct { cert *Certificate } -func (e UnknownAuthorityError) String() string { +func (e UnknownAuthorityError) Error() string { return "x509: certificate signed by unknown authority" } @@ -76,8 +82,8 @@ func (e UnknownAuthorityError) String() string { type VerifyOptions struct { DNSName string Intermediates *CertPool - Roots *CertPool - CurrentTime int64 // if 0, the current system time is used. + Roots *CertPool // if nil, the system roots are used + CurrentTime time.Time // if zero, the current time is used } const ( @@ -87,9 +93,12 @@ const ( ) // isValid performs validity checks on the c. -func (c *Certificate) isValid(certType int, opts *VerifyOptions) os.Error { - if opts.CurrentTime < c.NotBefore.Seconds() || - opts.CurrentTime > c.NotAfter.Seconds() { +func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error { + now := opts.CurrentTime + if now.IsZero() { + now = time.Now() + } + if now.Before(c.NotBefore) || now.After(c.NotAfter) { return CertificateInvalidError{c, Expired} } @@ -127,29 +136,44 @@ func (c *Certificate) isValid(certType int, opts *VerifyOptions) os.Error { return CertificateInvalidError{c, NotAuthorizedToSign} } + if c.BasicConstraintsValid && c.MaxPathLen >= 0 { + numIntermediates := len(currentChain) - 1 + if numIntermediates > c.MaxPathLen { + return CertificateInvalidError{c, TooManyIntermediates} + } + } + return nil } // Verify attempts to verify c by building one or more chains from c to a -// certificate in opts.roots, using certificates in opts.Intermediates if -// needed. If successful, it returns one or chains where the first element of -// the chain is c and the last element is from opts.Roots. +// certificate in opts.Roots, using certificates in opts.Intermediates if +// needed. If successful, it returns one or more chains where the first +// element of the chain is c and the last element is from opts.Roots. // // WARNING: this doesn't do any revocation checking. -func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err os.Error) { - if opts.CurrentTime == 0 { - opts.CurrentTime = time.Seconds() +func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { + // Use Windows's own verification and chain building. + if opts.Roots == nil && runtime.GOOS == "windows" { + return c.systemVerify(&opts) + } + + if opts.Roots == nil { + opts.Roots = systemRootsPool() } - err = c.isValid(leafCertificate, &opts) + + err = c.isValid(leafCertificate, nil, &opts) if err != nil { return } + if len(opts.DNSName) > 0 { err = c.VerifyHostname(opts.DNSName) if err != nil { return } } + return c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts) } @@ -160,10 +184,10 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate return n } -func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err os.Error) { +func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) { for _, rootNum := range opts.Roots.findVerifiedParents(c) { root := opts.Roots.certs[rootNum] - err = root.isValid(rootCertificate, opts) + err = root.isValid(rootCertificate, currentChain, opts) if err != nil { continue } @@ -178,7 +202,7 @@ nextIntermediate: continue nextIntermediate } } - err = intermediate.isValid(intermediateCertificate, opts) + err = intermediate.isValid(intermediateCertificate, currentChain, opts) if err != nil { continue } @@ -226,17 +250,51 @@ func matchHostnames(pattern, host string) bool { return true } +// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use +// an explicitly ASCII function to avoid any sharp corners resulting from +// performing Unicode operations on DNS labels. +func toLowerCaseASCII(in string) string { + // If the string is already lower-case then there's nothing to do. + isAlreadyLowerCase := true + for _, c := range in { + if c == utf8.RuneError { + // If we get a UTF-8 error then there might be + // upper-case ASCII bytes in the invalid sequence. + isAlreadyLowerCase = false + break + } + if 'A' <= c && c <= 'Z' { + isAlreadyLowerCase = false + break + } + } + + if isAlreadyLowerCase { + return in + } + + out := []byte(in) + for i, c := range out { + if 'A' <= c && c <= 'Z' { + out[i] += 'a' - 'A' + } + } + return string(out) +} + // VerifyHostname returns nil if c is a valid certificate for the named host. -// Otherwise it returns an os.Error describing the mismatch. -func (c *Certificate) VerifyHostname(h string) os.Error { +// Otherwise it returns an error describing the mismatch. +func (c *Certificate) VerifyHostname(h string) error { + lowered := toLowerCaseASCII(h) + if len(c.DNSNames) > 0 { for _, match := range c.DNSNames { - if matchHostnames(match, h) { + if matchHostnames(toLowerCaseASCII(match), lowered) { return nil } } // If Subject Alt Name is given, we ignore the common name. - } else if matchHostnames(c.Subject.CommonName, h) { + } else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) { return nil } diff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go index 111f60eb1..7b171b291 100644 --- a/src/pkg/crypto/x509/verify_test.go +++ b/src/pkg/crypto/x509/verify_test.go @@ -5,10 +5,13 @@ package x509 import ( + "crypto/x509/pkix" "encoding/pem" - "os" + "errors" + "runtime" "strings" "testing" + "time" ) type verifyTest struct { @@ -17,8 +20,9 @@ type verifyTest struct { roots []string currentTime int64 dnsName string + systemSkip bool - errorCallback func(*testing.T, int, os.Error) bool + errorCallback func(*testing.T, int, error) bool expectedChains [][]string } @@ -31,7 +35,18 @@ var verifyTests = []verifyTest{ dnsName: "www.google.com", expectedChains: [][]string{ - []string{"Google", "Thawte", "VeriSign"}, + {"Google", "Thawte", "VeriSign"}, + }, + }, + { + leaf: googleLeaf, + intermediates: []string{thawteIntermediate}, + roots: []string{verisignRoot}, + currentTime: 1302726541, + dnsName: "WwW.GooGLE.coM", + + expectedChains: [][]string{ + {"Google", "Thawte", "VeriSign"}, }, }, { @@ -58,6 +73,9 @@ var verifyTests = []verifyTest{ currentTime: 1302726541, dnsName: "www.google.com", + // Skip when using systemVerify, since Windows + // *will* find the missing intermediate cert. + systemSkip: true, errorCallback: expectAuthorityUnknown, }, { @@ -68,7 +86,7 @@ var verifyTests = []verifyTest{ dnsName: "www.google.com", expectedChains: [][]string{ - []string{"Google", "Thawte", "VeriSign"}, + {"Google", "Thawte", "VeriSign"}, }, }, { @@ -78,7 +96,7 @@ var verifyTests = []verifyTest{ currentTime: 1302726541, expectedChains: [][]string{ - []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, + {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, }, }, { @@ -87,14 +105,17 @@ var verifyTests = []verifyTest{ roots: []string{startComRoot}, currentTime: 1302726541, + // Skip when using systemVerify, since Windows + // can only return a single chain to us (for now). + systemSkip: true, expectedChains: [][]string{ - []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, - []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"}, + {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, + {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"}, }, }, } -func expectHostnameError(t *testing.T, i int, err os.Error) (ok bool) { +func expectHostnameError(t *testing.T, i int, err error) (ok bool) { if _, ok := err.(HostnameError); !ok { t.Errorf("#%d: error was not a HostnameError: %s", i, err) return false @@ -102,7 +123,7 @@ func expectHostnameError(t *testing.T, i int, err os.Error) (ok bool) { return true } -func expectExpired(t *testing.T, i int, err os.Error) (ok bool) { +func expectExpired(t *testing.T, i int, err error) (ok bool) { if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != Expired { t.Errorf("#%d: error was not Expired: %s", i, err) return false @@ -110,7 +131,7 @@ func expectExpired(t *testing.T, i int, err os.Error) (ok bool) { return true } -func expectAuthorityUnknown(t *testing.T, i int, err os.Error) (ok bool) { +func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) { if _, ok := err.(UnknownAuthorityError); !ok { t.Errorf("#%d: error was not UnknownAuthorityError: %s", i, err) return false @@ -118,28 +139,34 @@ func expectAuthorityUnknown(t *testing.T, i int, err os.Error) (ok bool) { return true } -func certificateFromPEM(pemBytes string) (*Certificate, os.Error) { +func certificateFromPEM(pemBytes string) (*Certificate, error) { block, _ := pem.Decode([]byte(pemBytes)) if block == nil { - return nil, os.NewError("failed to decode PEM") + return nil, errors.New("failed to decode PEM") } return ParseCertificate(block.Bytes) } -func TestVerify(t *testing.T) { +func testVerify(t *testing.T, useSystemRoots bool) { for i, test := range verifyTests { + if useSystemRoots && test.systemSkip { + continue + } + opts := VerifyOptions{ - Roots: NewCertPool(), Intermediates: NewCertPool(), DNSName: test.dnsName, - CurrentTime: test.currentTime, + CurrentTime: time.Unix(test.currentTime, 0), } - for j, root := range test.roots { - ok := opts.Roots.AppendCertsFromPEM([]byte(root)) - if !ok { - t.Errorf("#%d: failed to parse root #%d", i, j) - return + if !useSystemRoots { + opts.Roots = NewCertPool() + for j, root := range test.roots { + ok := opts.Roots.AppendCertsFromPEM([]byte(root)) + if !ok { + t.Errorf("#%d: failed to parse root #%d", i, j) + return + } } } @@ -200,6 +227,19 @@ func TestVerify(t *testing.T) { } } +func TestGoVerify(t *testing.T) { + testVerify(t, false) +} + +func TestSystemVerify(t *testing.T) { + if runtime.GOOS != "windows" { + t.Logf("skipping verify test using system APIs on %q", runtime.GOOS) + return + } + + testVerify(t, true) +} + func chainToDebugString(chain []*Certificate) string { var chainStr string for _, cert := range chain { @@ -211,6 +251,10 @@ func chainToDebugString(chain []*Certificate) string { return chainStr } +func nameToKey(name *pkix.Name) string { + return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName +} + const verisignRoot = `-----BEGIN CERTIFICATE----- MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index 8fda47159..8dae7e7fc 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -6,122 +6,73 @@ package x509 import ( - "asn1" - "big" "bytes" "crypto" "crypto/dsa" "crypto/rsa" "crypto/sha1" "crypto/x509/pkix" + "encoding/asn1" "encoding/pem" + "errors" "io" - "os" + "math/big" "time" ) -// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. -type pkcs1PrivateKey struct { - Version int - N *big.Int - E int - D *big.Int - P *big.Int - Q *big.Int - // We ignore these values, if present, because rsa will calculate them. - Dp *big.Int `asn1:"optional"` - Dq *big.Int `asn1:"optional"` - Qinv *big.Int `asn1:"optional"` - - AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"` +// pkixPublicKey reflects a PKIX public key structure. See SubjectPublicKeyInfo +// in RFC 3280. +type pkixPublicKey struct { + Algo pkix.AlgorithmIdentifier + BitString asn1.BitString } -type pkcs1AdditionalRSAPrime struct { - Prime *big.Int - - // We ignore these values because rsa will calculate them. - Exp *big.Int - Coeff *big.Int -} - -// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. -func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { - var priv pkcs1PrivateKey - rest, err := asn1.Unmarshal(der, &priv) - if len(rest) > 0 { - err = asn1.SyntaxError{"trailing data"} +// ParsePKIXPublicKey parses a DER encoded public key. These values are +// typically found in PEM blocks with "BEGIN PUBLIC KEY". +func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) { + var pki publicKeyInfo + if _, err = asn1.Unmarshal(derBytes, &pki); err != nil { return } - if err != nil { - return - } - - if priv.Version > 1 { - return nil, os.NewError("x509: unsupported private key version") - } - - if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { - return nil, os.NewError("private key contains zero or negative value") - } - - key = new(rsa.PrivateKey) - key.PublicKey = rsa.PublicKey{ - E: priv.E, - N: priv.N, - } - - key.D = priv.D - key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes)) - key.Primes[0] = priv.P - key.Primes[1] = priv.Q - for i, a := range priv.AdditionalPrimes { - if a.Prime.Sign() <= 0 { - return nil, os.NewError("private key contains zero or negative prime") - } - key.Primes[i+2] = a.Prime - // We ignore the other two values because rsa will calculate - // them as needed. - } - - err = key.Validate() - if err != nil { - return nil, err + algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm) + if algo == UnknownPublicKeyAlgorithm { + return nil, errors.New("ParsePKIXPublicKey: unknown public key algorithm") } - key.Precompute() - - return + return parsePublicKey(algo, &pki) } -// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. -func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { - key.Precompute() - - version := 0 - if len(key.Primes) > 2 { - version = 1 - } +// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format. +func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) { + var pubBytes []byte - priv := pkcs1PrivateKey{ - Version: version, - N: key.N, - E: key.PublicKey.E, - D: key.D, - P: key.Primes[0], - Q: key.Primes[1], - Dp: key.Precomputed.Dp, - Dq: key.Precomputed.Dq, - Qinv: key.Precomputed.Qinv, + switch pub := pub.(type) { + case *rsa.PublicKey: + pubBytes, _ = asn1.Marshal(rsaPublicKey{ + N: pub.N, + E: pub.E, + }) + default: + return nil, errors.New("MarshalPKIXPublicKey: unknown public key type") } - priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues)) - for i, values := range key.Precomputed.CRTValues { - priv.AdditionalPrimes[i].Prime = key.Primes[2+i] - priv.AdditionalPrimes[i].Exp = values.Exp - priv.AdditionalPrimes[i].Coeff = values.Coeff + pkix := pkixPublicKey{ + Algo: pkix.AlgorithmIdentifier{ + Algorithm: []int{1, 2, 840, 113549, 1, 1, 1}, + // This is a NULL parameters value which is technically + // superfluous, but most other code includes it and, by + // doing this, we match their public key hashes. + Parameters: asn1.RawValue{ + Tag: 5, + }, + }, + BitString: asn1.BitString{ + Bytes: pubBytes, + BitLength: 8 * len(pubBytes), + }, } - b, _ := asn1.Marshal(priv) - return b + ret, _ := asn1.Marshal(pkix) + return ret, nil } // These structures reflect the ASN.1 structure of X.509 certificates.: @@ -138,9 +89,9 @@ type tbsCertificate struct { Version int `asn1:"optional,explicit,default:1,tag:0"` SerialNumber *big.Int SignatureAlgorithm pkix.AlgorithmIdentifier - Issuer pkix.RDNSequence + Issuer asn1.RawValue Validity validity - Subject pkix.RDNSequence + Subject asn1.RawValue PublicKey publicKeyInfo UniqueId asn1.BitString `asn1:"optional,tag:1"` SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` @@ -156,7 +107,7 @@ type dsaSignature struct { } type validity struct { - NotBefore, NotAfter *time.Time + NotBefore, NotAfter time.Time } type publicKeyInfo struct { @@ -202,7 +153,7 @@ const ( // // md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } // -// md5WithRSAEncryption OBJECT IDENTIFER ::= { pkcs-1 4 } +// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } // // sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } // @@ -221,9 +172,9 @@ const ( // // RFC 5758 3.1 DSA Signature Algorithms // -// dsaWithSha356 OBJECT IDENTIFER ::= { +// dsaWithSha256 OBJECT IDENTIFIER ::= { // joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) -// algorithms(4) id-dsa-with-sha2(3) 2} +// csor(3) algorithms(4) id-dsa-with-sha2(3) 2} // var ( oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} @@ -339,6 +290,8 @@ type Certificate struct { Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). RawTBSCertificate []byte // Certificate part of raw ASN.1 DER content. RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo. + RawSubject []byte // DER encoded Subject + RawIssuer []byte // DER encoded Issuer Signature []byte SignatureAlgorithm SignatureAlgorithm @@ -350,7 +303,7 @@ type Certificate struct { SerialNumber *big.Int Issuer pkix.Name Subject pkix.Name - NotBefore, NotAfter *time.Time // Validity bounds. + NotBefore, NotAfter time.Time // Validity bounds. KeyUsage KeyUsage ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages. @@ -374,21 +327,17 @@ type Certificate struct { PolicyIdentifiers []asn1.ObjectIdentifier } -// UnsupportedAlgorithmError results from attempting to perform an operation -// that involves algorithms that are not currently implemented. -type UnsupportedAlgorithmError struct{} - -func (UnsupportedAlgorithmError) String() string { - return "cannot verify signature: algorithm unimplemented" -} +// ErrUnsupportedAlgorithm results from attempting to perform an operation that +// involves algorithms that are not currently implemented. +var ErrUnsupportedAlgorithm = errors.New("crypto/x509: cannot verify signature: algorithm unimplemented") // ConstraintViolationError results when a requested usage is not permitted by // a certificate. For example: checking a signature when the public key isn't a // certificate signing key. type ConstraintViolationError struct{} -func (ConstraintViolationError) String() string { - return "invalid signature: parent certificate cannot sign this kind of certificate" +func (ConstraintViolationError) Error() string { + return "crypto/x509: invalid signature: parent certificate cannot sign this kind of certificate" } func (c *Certificate) Equal(other *Certificate) bool { @@ -397,7 +346,7 @@ func (c *Certificate) Equal(other *Certificate) bool { // CheckSignatureFrom verifies that the signature on c is a valid signature // from parent. -func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) { +func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) { // RFC 5280, 4.2.1.9: // "If the basic constraints extension is not present in a version 3 // certificate, or the extension is present but the cA boolean is not @@ -413,7 +362,7 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) { } if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm { - return UnsupportedAlgorithmError{} + return ErrUnsupportedAlgorithm } // TODO(agl): don't ignore the path length constraint. @@ -423,7 +372,7 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) { // CheckSignature verifies that signature is a valid signature over signed from // c's public key. -func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err os.Error) { +func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err error) { var hashType crypto.Hash switch algo { @@ -436,16 +385,16 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature case SHA512WithRSA: hashType = crypto.SHA512 default: - return UnsupportedAlgorithmError{} + return ErrUnsupportedAlgorithm } h := hashType.New() if h == nil { - return UnsupportedAlgorithmError{} + return ErrUnsupportedAlgorithm } h.Write(signed) - digest := h.Sum() + digest := h.Sum(nil) switch pub := c.PublicKey.(type) { case *rsa.PublicKey: @@ -456,36 +405,31 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature return err } if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { - return os.NewError("DSA signature contained zero or negative values") + return errors.New("DSA signature contained zero or negative values") } if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { - return os.NewError("DSA verification failure") + return errors.New("DSA verification failure") } return } - return UnsupportedAlgorithmError{} + return ErrUnsupportedAlgorithm } // CheckCRLSignature checks that the signature in crl is from c. -func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err os.Error) { +func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err error) { algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm) return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) } type UnhandledCriticalExtension struct{} -func (h UnhandledCriticalExtension) String() string { +func (h UnhandledCriticalExtension) Error() string { return "unhandled critical extension" } type basicConstraints struct { IsCA bool `asn1:"optional"` - MaxPathLen int `asn1:"optional"` -} - -type rsaPublicKey struct { - N *big.Int - E int + MaxPathLen int `asn1:"optional,default:-1"` } // RFC 5280 4.2.1.4 @@ -506,7 +450,7 @@ type generalSubtree struct { Max int `asn1:"optional,tag:1"` } -func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) { +func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) { asn1Data := keyData.PublicKey.RightAlign() switch algo { case RSA: @@ -534,7 +478,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ return nil, err } if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { - return nil, os.NewError("zero or negative DSA parameter") + return nil, errors.New("zero or negative DSA parameter") } pub := &dsa.PublicKey{ Parameters: dsa.Parameters{ @@ -551,11 +495,13 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ panic("unreachable") } -func parseCertificate(in *certificate) (*Certificate, os.Error) { +func parseCertificate(in *certificate) (*Certificate, error) { out := new(Certificate) out.Raw = in.Raw out.RawTBSCertificate = in.TBSCertificate.Raw out.RawSubjectPublicKeyInfo = in.TBSCertificate.PublicKey.Raw + out.RawSubject = in.TBSCertificate.Subject.FullBytes + out.RawIssuer = in.TBSCertificate.Issuer.FullBytes out.Signature = in.SignatureValue.RightAlign() out.SignatureAlgorithm = @@ -563,20 +509,30 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { out.PublicKeyAlgorithm = getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm) - var err os.Error + var err error out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCertificate.PublicKey) if err != nil { return nil, err } if in.TBSCertificate.SerialNumber.Sign() < 0 { - return nil, os.NewError("negative serial number") + return nil, errors.New("negative serial number") } out.Version = in.TBSCertificate.Version + 1 out.SerialNumber = in.TBSCertificate.SerialNumber - out.Issuer.FillFromRDNSequence(&in.TBSCertificate.Issuer) - out.Subject.FillFromRDNSequence(&in.TBSCertificate.Subject) + + var issuer, subject pkix.RDNSequence + if _, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil { + return nil, err + } + if _, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil { + return nil, err + } + + out.Issuer.FillFromRDNSequence(&issuer) + out.Subject.FillFromRDNSequence(&subject) + out.NotBefore = in.TBSCertificate.Validity.NotBefore out.NotAfter = in.TBSCertificate.Validity.NotAfter @@ -632,7 +588,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { return nil, err } if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { - return nil, asn1.StructuralError{"bad SAN sequence"} + return nil, asn1.StructuralError{Msg: "bad SAN sequence"} } parsedName := false @@ -777,14 +733,14 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { } // ParseCertificate parses a single certificate from the given ASN.1 DER data. -func ParseCertificate(asn1Data []byte) (*Certificate, os.Error) { +func ParseCertificate(asn1Data []byte) (*Certificate, error) { var cert certificate rest, err := asn1.Unmarshal(asn1Data, &cert) if err != nil { return nil, err } if len(rest) > 0 { - return nil, asn1.SyntaxError{"trailing data"} + return nil, asn1.SyntaxError{Msg: "trailing data"} } return parseCertificate(&cert) @@ -792,12 +748,12 @@ func ParseCertificate(asn1Data []byte) (*Certificate, os.Error) { // ParseCertificates parses one or more certificates from the given ASN.1 DER // data. The certificates must be concatenated with no intermediate padding. -func ParseCertificates(asn1Data []byte) ([]*Certificate, os.Error) { +func ParseCertificates(asn1Data []byte) ([]*Certificate, error) { var v []*certificate for len(asn1Data) > 0 { cert := new(certificate) - var err os.Error + var err error asn1Data, err = asn1.Unmarshal(asn1Data, cert) if err != nil { return nil, err @@ -834,8 +790,8 @@ var ( oidExtensionNameConstraints = []int{2, 5, 29, 30} ) -func buildExtensions(template *Certificate) (ret []pkix.Extension, err os.Error) { - ret = make([]pkix.Extension, 7 /* maximum number of elements. */ ) +func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { + ret = make([]pkix.Extension, 7 /* maximum number of elements. */) n := 0 if template.KeyUsage != 0 { @@ -939,21 +895,41 @@ var ( oidRSA = []int{1, 2, 840, 113549, 1, 1, 1} ) -// CreateSelfSignedCertificate creates a new certificate based on -// a template. The following members of template are used: SerialNumber, -// Subject, NotBefore, NotAfter, KeyUsage, BasicConstraintsValid, IsCA, -// MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical, -// PermittedDNSDomains. +func subjectBytes(cert *Certificate) ([]byte, error) { + if len(cert.RawSubject) > 0 { + return cert.RawSubject, nil + } + + return asn1.Marshal(cert.Subject.ToRDNSequence()) +} + +// CreateCertificate creates a new certificate based on a template. The +// following members of template are used: SerialNumber, Subject, NotBefore, +// NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId, +// DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains. // // The certificate is signed by parent. If parent is equal to template then the // certificate is self-signed. The parameter pub is the public key of the // signee and priv is the private key of the signer. // // The returned slice is the certificate in DER encoding. -func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err os.Error) { +// +// The only supported key type is RSA (*rsa.PublicKey for pub, *rsa.PrivateKey +// for priv). +func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) { + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, errors.New("x509: non-RSA public keys not supported") + } + + rsaPriv, ok := priv.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("x509: non-RSA private keys not supported") + } + asn1PublicKey, err := asn1.Marshal(rsaPublicKey{ - N: pub.N, - E: pub.E, + N: rsaPub.N, + E: rsaPub.E, }) if err != nil { return @@ -968,14 +944,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P return } + asn1Issuer, err := subjectBytes(parent) + if err != nil { + return + } + + asn1Subject, err := subjectBytes(template) + if err != nil { + return + } + encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} c := tbsCertificate{ Version: 2, SerialNumber: template.SerialNumber, SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, - Issuer: parent.Subject.ToRDNSequence(), + Issuer: asn1.RawValue{FullBytes: asn1Issuer}, Validity: validity{template.NotBefore, template.NotAfter}, - Subject: template.Subject.ToRDNSequence(), + Subject: asn1.RawValue{FullBytes: asn1Subject}, PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, Extensions: extensions, } @@ -989,9 +975,9 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P h := sha1.New() h.Write(tbsCertContents) - digest := h.Sum() + digest := h.Sum(nil) - signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest) + signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) if err != nil { return } @@ -1008,6 +994,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P // pemCRLPrefix is the magic string that indicates that we have a PEM encoded // CRL. var pemCRLPrefix = []byte("-----BEGIN X509 CRL") + // pemType is the type of a PEM encoded CRL. var pemType = "X509 CRL" @@ -1015,7 +1002,7 @@ var pemType = "X509 CRL" // encoded CRLs will appear where they should be DER encoded, so this function // will transparently handle PEM encoding as long as there isn't any leading // garbage. -func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err os.Error) { +func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err error) { if bytes.HasPrefix(crlBytes, pemCRLPrefix) { block, _ := pem.Decode(crlBytes) if block != nil && block.Type == pemType { @@ -1026,7 +1013,7 @@ func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err os.Error) { } // ParseDERCRL parses a DER encoded CRL from the given bytes. -func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err os.Error) { +func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err error) { certList = new(pkix.CertificateList) _, err = asn1.Unmarshal(derBytes, certList) if err != nil { @@ -1037,7 +1024,13 @@ func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err os.Error) // CreateCRL returns a DER encoded CRL, signed by this Certificate, that // contains the given list of revoked certificates. -func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry *time.Time) (crlBytes []byte, err os.Error) { +// +// The only supported key type is RSA (*rsa.PrivateKey for priv). +func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) { + rsaPriv, ok := priv.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("x509: non-RSA private keys not supported") + } tbsCertList := pkix.TBSCertificateList{ Version: 2, Signature: pkix.AlgorithmIdentifier{ @@ -1056,9 +1049,9 @@ func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCer h := sha1.New() h.Write(tbsCertListContents) - digest := h.Sum() + digest := h.Sum(nil) - signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest) + signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) if err != nil { return } diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index dc216505e..f0327b012 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -5,15 +5,16 @@ package x509 import ( - "asn1" - "big" + "bytes" "crypto/dsa" "crypto/rand" "crypto/rsa" "crypto/x509/pkix" + "encoding/asn1" "encoding/base64" "encoding/hex" "encoding/pem" + "math/big" "testing" "time" ) @@ -34,6 +35,40 @@ func TestParsePKCS1PrivateKey(t *testing.T) { } } +func TestParsePKIXPublicKey(t *testing.T) { + block, _ := pem.Decode([]byte(pemPublicKey)) + pub, err := ParsePKIXPublicKey(block.Bytes) + if err != nil { + t.Errorf("Failed to parse RSA public key: %s", err) + return + } + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + t.Errorf("Value returned from ParsePKIXPublicKey was not an RSA public key") + return + } + + pubBytes2, err := MarshalPKIXPublicKey(rsaPub) + if err != nil { + t.Errorf("Failed to marshal RSA public key for the second time: %s", err) + return + } + if !bytes.Equal(pubBytes2, block.Bytes) { + t.Errorf("Reserialization of public key didn't match. got %x, want %x", pubBytes2, block.Bytes) + } +} + +var pemPublicKey = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3VoPN9PKUjKFLMwOge6+ +wnDi8sbETGIx2FKXGgqtAKpzmem53kRGEQg8WeqRmp12wgp74TGpkEXsGae7RS1k +enJCnma4fii+noGH7R0qKgHvPrI2Bwa9hzsH8tHxpyM3qrXslOmD45EH9SxIDUBJ +FehNdaPbLP1gFyahKMsdfxFJLUvbUycuZSJ2ZnIgeVxwm4qbSvZInL9Iu4FzuPtg +fINKcbbovy1qq4KvPIrXzhbY3PWDc6btxCf3SE0JdE1MCPThntB62/bLMSQ7xdDR +FF53oIpvxe/SCOymfWq/LW849Ytv3Xwod0+wzAP8STXG4HSELS4UedPYeHJJJYcZ ++QIDAQAB +-----END PUBLIC KEY----- +` + var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu @@ -208,14 +243,15 @@ func TestCreateSelfSignedCertificate(t *testing.T) { return } + commonName := "test.example.com" template := Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ - CommonName: "test.example.com", + CommonName: commonName, Organization: []string{"Acme Co"}, }, - NotBefore: time.SecondsToUTC(1000), - NotAfter: time.SecondsToUTC(100000), + NotBefore: time.Unix(1000, 0), + NotAfter: time.Unix(100000, 0), SubjectKeyId: []byte{1, 2, 3, 4}, KeyUsage: KeyUsageCertSign, @@ -248,6 +284,14 @@ func TestCreateSelfSignedCertificate(t *testing.T) { t.Errorf("Failed to parse name constraints: %#v", cert.PermittedDNSDomains) } + if cert.Subject.CommonName != commonName { + t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName) + } + + if cert.Issuer.CommonName != commonName { + t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName) + } + err = cert.CheckSignatureFrom(cert) if err != nil { t.Errorf("Signature verification failed: %s", err) @@ -352,8 +396,8 @@ func TestCRLCreation(t *testing.T) { block, _ = pem.Decode([]byte(pemCertificate)) cert, _ := ParseCertificate(block.Bytes) - now := time.SecondsToUTC(1000) - expiry := time.SecondsToUTC(10000) + now := time.Unix(1000, 0) + expiry := time.Unix(10000, 0) revokedCerts := []pkix.RevokedCertificate{ { @@ -399,7 +443,7 @@ func TestParseDERCRL(t *testing.T) { t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) } - if certList.HasExpired(1302517272) { + if certList.HasExpired(time.Unix(1302517272, 0)) { t.Errorf("CRL has expired (but shouldn't have)") } @@ -419,7 +463,7 @@ func TestParsePEMCRL(t *testing.T) { t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) } - if certList.HasExpired(1302517272) { + if certList.HasExpired(time.Unix(1302517272, 0)) { t.Errorf("CRL has expired (but shouldn't have)") } diff --git a/src/pkg/crypto/xtea/Makefile b/src/pkg/crypto/xtea/Makefile deleted file mode 100644 index 301621168..000000000 --- a/src/pkg/crypto/xtea/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=crypto/xtea -GOFILES=\ - cipher.go\ - block.go\ - -include ../../../Make.pkg diff --git a/src/pkg/crypto/xtea/block.go b/src/pkg/crypto/xtea/block.go deleted file mode 100644 index bf5d24599..000000000 --- a/src/pkg/crypto/xtea/block.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - Implementation adapted from Needham and Wheeler's paper: - http://www.cix.co.uk/~klockstone/xtea.pdf - - A precalculated look up table is used during encryption/decryption for values that are based purely on the key. -*/ - -package xtea - -// XTEA is based on 64 rounds. -const numRounds = 64 - -// blockToUint32 reads an 8 byte slice into two uint32s. -// The block is treated as big endian. -func blockToUint32(src []byte) (uint32, uint32) { - r0 := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) - r1 := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) - return r0, r1 -} - -// uint32ToBlock writes two uint32s into an 8 byte data block. -// Values are written as big endian. -func uint32ToBlock(v0, v1 uint32, dst []byte) { - dst[0] = byte(v0 >> 24) - dst[1] = byte(v0 >> 16) - dst[2] = byte(v0 >> 8) - dst[3] = byte(v0) - dst[4] = byte(v1 >> 24) - dst[5] = byte(v1 >> 16) - dst[6] = byte(v1 >> 8) - dst[7] = byte(v1 >> 0) -} - -// encryptBlock encrypts a single 8 byte block using XTEA. -func encryptBlock(c *Cipher, dst, src []byte) { - v0, v1 := blockToUint32(src) - - // Two rounds of XTEA applied per loop - for i := 0; i < numRounds; { - v0 += ((v1<<4 ^ v1>>5) + v1) ^ c.table[i] - i++ - v1 += ((v0<<4 ^ v0>>5) + v0) ^ c.table[i] - i++ - } - - uint32ToBlock(v0, v1, dst) -} - -// decryptBlock decrypt a single 8 byte block using XTEA. -func decryptBlock(c *Cipher, dst, src []byte) { - v0, v1 := blockToUint32(src) - - // Two rounds of XTEA applied per loop - for i := numRounds; i > 0; { - i-- - v1 -= ((v0<<4 ^ v0>>5) + v0) ^ c.table[i] - i-- - v0 -= ((v1<<4 ^ v1>>5) + v1) ^ c.table[i] - } - - uint32ToBlock(v0, v1, dst) -} diff --git a/src/pkg/crypto/xtea/cipher.go b/src/pkg/crypto/xtea/cipher.go deleted file mode 100644 index b3fba3c84..000000000 --- a/src/pkg/crypto/xtea/cipher.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package xtea implements XTEA encryption, as defined in Needham and Wheeler's -// 1997 technical report, "Tea extensions." -package xtea - -// For details, see http://www.cix.co.uk/~klockstone/xtea.pdf - -import ( - "os" - "strconv" -) - -// The XTEA block size in bytes. -const BlockSize = 8 - -// A Cipher is an instance of an XTEA cipher using a particular key. -// table contains a series of precalculated values that are used each round. -type Cipher struct { - table [64]uint32 -} - -type KeySizeError int - -func (k KeySizeError) String() string { - return "crypto/xtea: invalid key size " + strconv.Itoa(int(k)) -} - -// NewCipher creates and returns a new Cipher. -// The key argument should be the XTEA key. -// XTEA only supports 128 bit (16 byte) keys. -func NewCipher(key []byte) (*Cipher, os.Error) { - k := len(key) - switch k { - default: - return nil, KeySizeError(k) - case 16: - break - } - - c := new(Cipher) - initCipher(c, key) - - return c, nil -} - -// BlockSize returns the XTEA block size, 8 bytes. -// It is necessary to satisfy the Cipher interface in the -// package "crypto/cipher". -func (c *Cipher) BlockSize() int { return BlockSize } - -// Encrypt encrypts the 8 byte buffer src using the key and stores the result in dst. -// Note that for amounts of data larger than a block, -// it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). -func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c, dst, src) } - -// Decrypt decrypts the 8 byte buffer src using the key k and stores the result in dst. -func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c, dst, src) } - -// Reset zeros the table, so that it will no longer appear in the process's memory. -func (c *Cipher) Reset() { - for i := 0; i < len(c.table); i++ { - c.table[i] = 0 - } -} - -// initCipher initializes the cipher context by creating a look up table -// of precalculated values that are based on the key. -func initCipher(c *Cipher, key []byte) { - // Load the key into four uint32s - var k [4]uint32 - for i := 0; i < len(k); i++ { - j := i << 2 // Multiply by 4 - k[i] = uint32(key[j+0])<<24 | uint32(key[j+1])<<16 | uint32(key[j+2])<<8 | uint32(key[j+3]) - } - - // Precalculate the table - const delta = 0x9E3779B9 - var sum uint32 = 0 - - // Two rounds of XTEA applied per loop - for i := 0; i < numRounds; { - c.table[i] = sum + k[sum&3] - i++ - sum += delta - c.table[i] = sum + k[(sum>>11)&3] - i++ - } -} diff --git a/src/pkg/crypto/xtea/xtea_test.go b/src/pkg/crypto/xtea/xtea_test.go deleted file mode 100644 index 217d96adc..000000000 --- a/src/pkg/crypto/xtea/xtea_test.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xtea - -import ( - "testing" -) - -// A sample test key for when we just want to initialize a cipher -var testKey = []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF} - -// Test that the block size for XTEA is correct -func TestBlocksize(t *testing.T) { - if BlockSize != 8 { - t.Errorf("BlockSize constant - expected 8, got %d", BlockSize) - return - } - - c, err := NewCipher(testKey) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) - return - } - - result := c.BlockSize() - if result != 8 { - t.Errorf("BlockSize function - expected 8, got %d", result) - return - } -} - -// A series of test values to confirm that the Cipher.table array was initialized correctly -var testTable = []uint32{ - 0x00112233, 0x6B1568B8, 0xE28CE030, 0xC5089E2D, 0xC5089E2D, 0x1EFBD3A2, 0xA7845C2A, 0x78EF0917, - 0x78EF0917, 0x172682D0, 0x5B6AC714, 0x822AC955, 0x3DE68511, 0xDC1DFECA, 0x2062430E, 0x3611343F, - 0xF1CCEFFB, 0x900469B4, 0xD448ADF8, 0x2E3BE36D, 0xB6C46BF5, 0x994029F2, 0x994029F2, 0xF3335F67, - 0x6AAAD6DF, 0x4D2694DC, 0x4D2694DC, 0xEB5E0E95, 0x2FA252D9, 0x4551440A, 0x121E10D6, 0xB0558A8F, - 0xE388BDC3, 0x0A48C004, 0xC6047BC0, 0x643BF579, 0xA88039BD, 0x02736F32, 0x8AFBF7BA, 0x5C66A4A7, - 0x5C66A4A7, 0xC76AEB2C, 0x3EE262A4, 0x215E20A1, 0x215E20A1, 0x7B515616, 0x03D9DE9E, 0x1988CFCF, - 0xD5448B8B, 0x737C0544, 0xB7C04988, 0xDE804BC9, 0x9A3C0785, 0x3873813E, 0x7CB7C582, 0xD6AAFAF7, - 0x4E22726F, 0x309E306C, 0x309E306C, 0x8A9165E1, 0x1319EE69, 0xF595AC66, 0xF595AC66, 0x4F88E1DB, -} - -// Test that the cipher context is initialized correctly -func TestCipherInit(t *testing.T) { - c, err := NewCipher(testKey) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) - return - } - - for i := 0; i < len(c.table); i++ { - if c.table[i] != testTable[i] { - t.Errorf("NewCipher() failed to initialize Cipher.table[%d] correctly. Expected %08X, got %08X", i, testTable[i], c.table[i]) - break - } - } -} - -// Test that invalid key sizes return an error -func TestInvalidKeySize(t *testing.T) { - // Test a long key - key := []byte{ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - } - - _, err := NewCipher(key) - if err == nil { - t.Errorf("Invalid key size %d didn't result in an error.", len(key)) - } - - // Test a short key - key = []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77} - - _, err = NewCipher(key) - if err == nil { - t.Errorf("Invalid key size %d didn't result in an error.", len(key)) - } -} - -// Test that we can correctly decode some bytes we have encoded -func TestEncodeDecode(t *testing.T) { - original := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF} - input := original - output := make([]byte, BlockSize) - - c, err := NewCipher(testKey) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) - return - } - - // Encrypt the input block - c.Encrypt(output, input) - - // Check that the output does not match the input - differs := false - for i := 0; i < len(input); i++ { - if output[i] != input[i] { - differs = true - break - } - } - if differs == false { - t.Error("Cipher.Encrypt: Failed to encrypt the input block.") - return - } - - // Decrypt the block we just encrypted - input = output - output = make([]byte, BlockSize) - c.Decrypt(output, input) - - // Check that the output from decrypt matches our initial input - for i := 0; i < len(input); i++ { - if output[i] != original[i] { - t.Errorf("Decrypted byte %d differed. Expected %02X, got %02X\n", i, original[i], output[i]) - return - } - } -} - -// Test Vectors -type CryptTest struct { - key []byte - plainText []byte - cipherText []byte -} - -var CryptTests = []CryptTest{ - // These were sourced from http://www.freemedialibrary.com/index.php/XTEA_test_vectors - { - []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, - []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, - []byte{0x49, 0x7d, 0xf3, 0xd0, 0x72, 0x61, 0x2c, 0xb5}, - }, - { - []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, - []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, - []byte{0xe7, 0x8f, 0x2d, 0x13, 0x74, 0x43, 0x41, 0xd8}, - }, - { - []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, - []byte{0x5a, 0x5b, 0x6e, 0x27, 0x89, 0x48, 0xd7, 0x7f}, - []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, - }, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, - []byte{0xa0, 0x39, 0x05, 0x89, 0xf8, 0xb8, 0xef, 0xa5}, - }, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, - []byte{0xed, 0x23, 0x37, 0x5a, 0x82, 0x1a, 0x8c, 0x2d}, - }, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x70, 0xe1, 0x22, 0x5d, 0x6e, 0x4e, 0x76, 0x55}, - []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, - }, - - // These vectors are from http://wiki.secondlife.com/wiki/XTEA_Strong_Encryption_Implementation#Bouncy_Castle_C.23_API - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0xDE, 0xE9, 0xD4, 0xD8, 0xF7, 0x13, 0x1E, 0xD9}, - }, - { - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, - []byte{0x06, 0x5C, 0x1B, 0x89, 0x75, 0xC6, 0xA8, 0x16}, - }, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A}, - []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - []byte{0x1F, 0xF9, 0xA0, 0x26, 0x1A, 0xC6, 0x42, 0x64}, - }, - { - []byte{0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A}, - []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, - []byte{0x8C, 0x67, 0x15, 0x5B, 0x2E, 0xF9, 0x1E, 0xAD}, - }, -} - -// Test encryption -func TestCipherEncrypt(t *testing.T) { - for i, tt := range CryptTests { - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("NewCipher(%d bytes), vector %d = %s", len(tt.key), i, err) - continue - } - - out := make([]byte, len(tt.plainText)) - c.Encrypt(out, tt.plainText) - - for j := 0; j < len(out); j++ { - if out[j] != tt.cipherText[j] { - t.Errorf("Cipher.Encrypt %d: out[%d] = %02X, expected %02X", i, j, out[j], tt.cipherText[j]) - break - } - } - } -} - -// Test decryption -func TestCipherDecrypt(t *testing.T) { - for i, tt := range CryptTests { - c, err := NewCipher(tt.key) - if err != nil { - t.Errorf("NewCipher(%d bytes), vector %d = %s", len(tt.key), i, err) - continue - } - - out := make([]byte, len(tt.cipherText)) - c.Decrypt(out, tt.cipherText) - - for j := 0; j < len(out); j++ { - if out[j] != tt.plainText[j] { - t.Errorf("Cipher.Decrypt %d: out[%d] = %02X, expected %02X", i, j, out[j], tt.plainText[j]) - break - } - } - } -} - -// Test resetting the cipher context -func TestReset(t *testing.T) { - c, err := NewCipher(testKey) - if err != nil { - t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) - return - } - - c.Reset() - for i := 0; i < len(c.table); i++ { - if c.table[i] != 0 { - t.Errorf("Cipher.Reset: Failed to clear Cipher.table[%d]. expected 0, got %08X", i, c.table[i]) - return - } - } -} diff --git a/src/pkg/csv/Makefile b/src/pkg/csv/Makefile deleted file mode 100644 index e364d51d2..000000000 --- a/src/pkg/csv/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../Make.inc - -TARG=csv -GOFILES=\ - reader.go\ - writer.go\ - -include ../../Make.pkg diff --git a/src/pkg/csv/reader.go b/src/pkg/csv/reader.go deleted file mode 100644 index ea2c266a4..000000000 --- a/src/pkg/csv/reader.go +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package csv reads and writes comma-separated values (CSV) files. -// -// A csv file contains zero or more records of one or more fields per record. -// Each record is separated by the newline character. The final record may -// optionally be followed by a newline character. -// -// field1,field2,field3 -// -// White space is considered part of a field. -// -// Carriage returns before newline characters are silently removed. -// -// Blank lines are ignored. A line with only whitespace characters (excluding -// the ending newline character) is not considered a blank line. -// -// Fields which start and stop with the quote character " are called -// quoted-fields. The beginning and ending quote are not part of the -// field. -// -// The source: -// -// normal string,"quoted-field" -// -// results in the fields -// -// {`normal string`, `quoted-field`} -// -// Within a quoted-field a quote character followed by a second quote -// character is considered a single quote. -// -// "the ""word"" is true","a ""quoted-field""" -// -// results in -// -// {`the "word" is true`, `a "quoted-field"`} -// -// Newlines and commas may be included in a quoted-field -// -// "Multi-line -// field","comma is ," -// -// results in -// -// {`Multi-line -// field`, `comma is ,`} -package csv - -import ( - "bufio" - "bytes" - "fmt" - "io" - "os" - "unicode" -) - -// A ParseError is returned for parsing errors. -// The first line is 1. The first column is 0. -type ParseError struct { - Line int // Line where the error occurred - Column int // Column (rune index) where the error occurred - Error os.Error // The actual error -} - -func (e *ParseError) String() string { - return fmt.Sprintf("line %d, column %d: %s", e.Line, e.Column, e.Error) -} - -// These are the errors that can be returned in ParseError.Error -var ( - ErrTrailingComma = os.NewError("extra delimiter at end of line") - ErrBareQuote = os.NewError("bare \" in non-quoted-field") - ErrQuote = os.NewError("extraneous \" in field") - ErrFieldCount = os.NewError("wrong number of fields in line") -) - -// A Reader reads records from a CSV-encoded file. -// -// As returned by NewReader, a Reader expects input conforming to RFC 4180. -// The exported fields can be changed to customize the details before the -// first call to Read or ReadAll. -// -// Comma is the field delimiter. It defaults to ','. -// -// Comment, if not 0, is the comment character. Lines beginning with the -// Comment character are ignored. -// -// If FieldsPerRecord is positive, Read requires each record to -// have the given number of fields. If FieldsPerRecord is 0, Read sets it to -// the number of fields in the first record, so that future records must -// have the same field count. -// -// If LazyQuotes is true, a quote may appear in an unquoted field and a -// non-doubled quote may appear in a quoted field. -// -// If TrailingComma is true, the last field may be an unquoted empty field. -// -// If TrimLeadingSpace is true, leading white space in a field is ignored. -type Reader struct { - Comma int // Field delimiter (set to ',' by NewReader) - Comment int // Comment character for start of line - FieldsPerRecord int // Number of expected fields per record - LazyQuotes bool // Allow lazy quotes - TrailingComma bool // Allow trailing comma - TrimLeadingSpace bool // Trim leading space - line int - column int - r *bufio.Reader - field bytes.Buffer -} - -// NewReader returns a new Reader that reads from r. -func NewReader(r io.Reader) *Reader { - return &Reader{ - Comma: ',', - r: bufio.NewReader(r), - } -} - -// error creates a new ParseError based on err. -func (r *Reader) error(err os.Error) os.Error { - return &ParseError{ - Line: r.line, - Column: r.column, - Error: err, - } -} - -// Read reads one record from r. The record is a slice of strings with each -// string representing one field. -func (r *Reader) Read() (record []string, err os.Error) { - for { - record, err = r.parseRecord() - if record != nil { - break - } - if err != nil { - return nil, err - } - } - - if r.FieldsPerRecord > 0 { - if len(record) != r.FieldsPerRecord { - r.column = 0 // report at start of record - return record, r.error(ErrFieldCount) - } - } else if r.FieldsPerRecord == 0 { - r.FieldsPerRecord = len(record) - } - return record, nil -} - -// ReadAll reads all the remaining records from r. -// Each record is a slice of fields. -func (r *Reader) ReadAll() (records [][]string, err os.Error) { - for { - record, err := r.Read() - if err == os.EOF { - return records, nil - } - if err != nil { - return nil, err - } - records = append(records, record) - } - panic("unreachable") -} - -// readRune reads one rune from r, folding \r\n to \n and keeping track -// of how far into the line we have read. r.column will point to the start -// of this rune, not the end of this rune. -func (r *Reader) readRune() (int, os.Error) { - rune, _, err := r.r.ReadRune() - - // Handle \r\n here. We make the simplifying assumption that - // anytime \r is followed by \n that it can be folded to \n. - // We will not detect files which contain both \r\n and bare \n. - if rune == '\r' { - rune, _, err = r.r.ReadRune() - if err == nil { - if rune != '\n' { - r.r.UnreadRune() - rune = '\r' - } - } - } - r.column++ - return rune, err -} - -// unreadRune puts the last rune read from r back. -func (r *Reader) unreadRune() { - r.r.UnreadRune() - r.column-- -} - -// skip reads runes up to and including the rune delim or until error. -func (r *Reader) skip(delim int) os.Error { - for { - rune, err := r.readRune() - if err != nil { - return err - } - if rune == delim { - return nil - } - } - panic("unreachable") -} - -// parseRecord reads and parses a single csv record from r. -func (r *Reader) parseRecord() (fields []string, err os.Error) { - // Each record starts on a new line. We increment our line - // number (lines start at 1, not 0) and set column to -1 - // so as we increment in readRune it points to the character we read. - r.line++ - r.column = -1 - - // Peek at the first rune. If it is an error we are done. - // If we are support comments and it is the comment character - // then skip to the end of line. - - rune, _, err := r.r.ReadRune() - if err != nil { - return nil, err - } - - if r.Comment != 0 && rune == r.Comment { - return nil, r.skip('\n') - } - r.r.UnreadRune() - - // At this point we have at least one field. - for { - haveField, delim, err := r.parseField() - if haveField { - fields = append(fields, r.field.String()) - } - if delim == '\n' || err == os.EOF { - return fields, err - } else if err != nil { - return nil, err - } - } - panic("unreachable") -} - -// parseField parses the next field in the record. The read field is -// located in r.field. Delim is the first character not part of the field -// (r.Comma or '\n'). -func (r *Reader) parseField() (haveField bool, delim int, err os.Error) { - r.field.Reset() - - rune, err := r.readRune() - if err != nil { - // If we have EOF and are not at the start of a line - // then we return the empty field. We have already - // checked for trailing commas if needed. - if err == os.EOF && r.column != 0 { - return true, 0, err - } - return false, 0, err - } - - if r.TrimLeadingSpace { - for unicode.IsSpace(rune) { - rune, err = r.readRune() - if err != nil { - return false, 0, err - } - } - } - - switch rune { - case r.Comma: - // will check below - - case '\n': - // We are a trailing empty field or a blank line - if r.column == 0 { - return false, rune, nil - } - return true, rune, nil - - case '"': - // quoted field - Quoted: - for { - rune, err = r.readRune() - if err != nil { - if err == os.EOF { - if r.LazyQuotes { - return true, 0, err - } - return false, 0, r.error(ErrQuote) - } - return false, 0, err - } - switch rune { - case '"': - rune, err = r.readRune() - if err != nil || rune == r.Comma { - break Quoted - } - if rune == '\n' { - return true, rune, nil - } - if rune != '"' { - if !r.LazyQuotes { - r.column-- - return false, 0, r.error(ErrQuote) - } - // accept the bare quote - r.field.WriteRune('"') - } - case '\n': - r.line++ - r.column = -1 - } - r.field.WriteRune(rune) - } - - default: - // unquoted field - for { - r.field.WriteRune(rune) - rune, err = r.readRune() - if err != nil || rune == r.Comma { - break - } - if rune == '\n' { - return true, rune, nil - } - if !r.LazyQuotes && rune == '"' { - return false, 0, r.error(ErrBareQuote) - } - } - } - - if err != nil { - if err == os.EOF { - return true, 0, err - } - return false, 0, err - } - - if !r.TrailingComma { - // We don't allow trailing commas. See if we - // are at the end of the line (being mindful - // of trimming spaces). - c := r.column - rune, err = r.readRune() - if r.TrimLeadingSpace { - for unicode.IsSpace(rune) { - rune, err = r.readRune() - if err != nil { - break - } - } - } - if err == os.EOF || rune == '\n' { - r.column = c // report the comma - return false, 0, r.error(ErrTrailingComma) - } - r.unreadRune() - } - return true, rune, nil -} diff --git a/src/pkg/csv/reader_test.go b/src/pkg/csv/reader_test.go deleted file mode 100644 index 0068bad1d..000000000 --- a/src/pkg/csv/reader_test.go +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package csv - -import ( - "reflect" - "strings" - "testing" -) - -var readTests = []struct { - Name string - Input string - Output [][]string - UseFieldsPerRecord bool // false (default) means FieldsPerRecord is -1 - - // These fields are copied into the Reader - Comma int - Comment int - FieldsPerRecord int - LazyQuotes bool - TrailingComma bool - TrimLeadingSpace bool - - Error string - Line int // Expected error line if != 0 - Column int // Expected error column if line != 0 -}{ - { - Name: "Simple", - Input: "a,b,c\n", - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "CRLF", - Input: "a,b\r\nc,d\r\n", - Output: [][]string{{"a", "b"}, {"c", "d"}}, - }, - { - Name: "BareCR", - Input: "a,b\rc,d\r\n", - Output: [][]string{{"a", "b\rc", "d"}}, - }, - { - Name: "RFC4180test", - UseFieldsPerRecord: true, - Input: `#field1,field2,field3 -"aaa","bb -b","ccc" -"a,a","b""bb","ccc" -zzz,yyy,xxx -`, - Output: [][]string{ - {"#field1", "field2", "field3"}, - {"aaa", "bb\nb", "ccc"}, - {"a,a", `b"bb`, "ccc"}, - {"zzz", "yyy", "xxx"}, - }, - }, - { - Name: "NoEOLTest", - Input: "a,b,c", - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "Semicolon", - Comma: ';', - Input: "a;b;c\n", - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "MultiLine", - Input: `"two -line","one line","three -line -field"`, - Output: [][]string{{"two\nline", "one line", "three\nline\nfield"}}, - }, - { - Name: "BlankLine", - Input: "a,b,c\n\nd,e,f\n\n", - Output: [][]string{ - {"a", "b", "c"}, - {"d", "e", "f"}, - }, - }, - { - Name: "TrimSpace", - Input: " a, b, c\n", - TrimLeadingSpace: true, - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "LeadingSpace", - Input: " a, b, c\n", - Output: [][]string{{" a", " b", " c"}}, - }, - { - Name: "Comment", - Comment: '#', - Input: "#1,2,3\na,b,c\n#comment", - Output: [][]string{{"a", "b", "c"}}, - }, - { - Name: "NoComment", - Input: "#1,2,3\na,b,c", - Output: [][]string{{"#1", "2", "3"}, {"a", "b", "c"}}, - }, - { - Name: "LazyQuotes", - LazyQuotes: true, - Input: `a "word","1"2",a","b`, - Output: [][]string{{`a "word"`, `1"2`, `a"`, `b`}}, - }, - { - Name: "BareQuotes", - LazyQuotes: true, - Input: `a "word","1"2",a"`, - Output: [][]string{{`a "word"`, `1"2`, `a"`}}, - }, - { - Name: "BareDoubleQuotes", - LazyQuotes: true, - Input: `a""b,c`, - Output: [][]string{{`a""b`, `c`}}, - }, - { - Name: "BadDoubleQuotes", - Input: `a""b,c`, - Output: [][]string{{`a""b`, `c`}}, - Error: `bare " in non-quoted-field`, Line: 1, Column: 1, - }, - { - Name: "TrimQuote", - Input: ` "a"," b",c`, - TrimLeadingSpace: true, - Output: [][]string{{"a", " b", "c"}}, - }, - { - Name: "BadBareQuote", - Input: `a "word","b"`, - Error: `bare " in non-quoted-field`, Line: 1, Column: 2, - }, - { - Name: "BadTrailingQuote", - Input: `"a word",b"`, - Error: `bare " in non-quoted-field`, Line: 1, Column: 10, - }, - { - Name: "ExtraneousQuote", - Input: `"a "word","b"`, - Error: `extraneous " in field`, Line: 1, Column: 3, - }, - { - Name: "BadFieldCount", - UseFieldsPerRecord: true, - Input: "a,b,c\nd,e", - Error: "wrong number of fields", Line: 2, - }, - { - Name: "BadFieldCount1", - UseFieldsPerRecord: true, - FieldsPerRecord: 2, - Input: `a,b,c`, - Error: "wrong number of fields", Line: 1, - }, - { - Name: "FieldCount", - Input: "a,b,c\nd,e", - Output: [][]string{{"a", "b", "c"}, {"d", "e"}}, - }, - { - Name: "BadTrailingCommaEOF", - Input: "a,b,c,", - Error: "extra delimiter at end of line", Line: 1, Column: 5, - }, - { - Name: "BadTrailingCommaEOL", - Input: "a,b,c,\n", - Error: "extra delimiter at end of line", Line: 1, Column: 5, - }, - { - Name: "BadTrailingCommaSpaceEOF", - TrimLeadingSpace: true, - Input: "a,b,c, ", - Error: "extra delimiter at end of line", Line: 1, Column: 5, - }, - { - Name: "BadTrailingCommaSpaceEOL", - TrimLeadingSpace: true, - Input: "a,b,c, \n", - Error: "extra delimiter at end of line", Line: 1, Column: 5, - }, - { - Name: "BadTrailingCommaLine3", - TrimLeadingSpace: true, - Input: "a,b,c\nd,e,f\ng,hi,", - Error: "extra delimiter at end of line", Line: 3, Column: 4, - }, - { - Name: "NotTrailingComma3", - Input: "a,b,c, \n", - Output: [][]string{{"a", "b", "c", " "}}, - }, - { - Name: "CommaFieldTest", - TrailingComma: true, - Input: `x,y,z,w -x,y,z, -x,y,, -x,,, -,,, -"x","y","z","w" -"x","y","z","" -"x","y","","" -"x","","","" -"","","","" -`, - Output: [][]string{ - {"x", "y", "z", "w"}, - {"x", "y", "z", ""}, - {"x", "y", "", ""}, - {"x", "", "", ""}, - {"", "", "", ""}, - {"x", "y", "z", "w"}, - {"x", "y", "z", ""}, - {"x", "y", "", ""}, - {"x", "", "", ""}, - {"", "", "", ""}, - }, - }, -} - -func TestRead(t *testing.T) { - for _, tt := range readTests { - r := NewReader(strings.NewReader(tt.Input)) - r.Comment = tt.Comment - if tt.UseFieldsPerRecord { - r.FieldsPerRecord = tt.FieldsPerRecord - } else { - r.FieldsPerRecord = -1 - } - r.LazyQuotes = tt.LazyQuotes - r.TrailingComma = tt.TrailingComma - r.TrimLeadingSpace = tt.TrimLeadingSpace - if tt.Comma != 0 { - r.Comma = tt.Comma - } - out, err := r.ReadAll() - perr, _ := err.(*ParseError) - if tt.Error != "" { - if err == nil || !strings.Contains(err.String(), tt.Error) { - t.Errorf("%s: error %v, want error %q", tt.Name, err, tt.Error) - } else if tt.Line != 0 && (tt.Line != perr.Line || tt.Column != perr.Column) { - t.Errorf("%s: error at %d:%d expected %d:%d", tt.Name, perr.Line, perr.Column, tt.Line, tt.Column) - } - } else if err != nil { - t.Errorf("%s: unexpected error %v", tt.Name, err) - } else if !reflect.DeepEqual(out, tt.Output) { - t.Errorf("%s: out=%q want %q", tt.Name, out, tt.Output) - } - } -} diff --git a/src/pkg/csv/writer.go b/src/pkg/csv/writer.go deleted file mode 100644 index ccf703f0f..000000000 --- a/src/pkg/csv/writer.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package csv - -import ( - "bufio" - "io" - "os" - "strings" - "unicode" - "utf8" -) - -// A Writer writes records to a CSV encoded file. -// -// As returned by NewWriter, a Writer writes records terminated by a -// newline and uses ',' as the field delimiter. The exported fields can be -// changed to customize the details before the first call to Write or WriteAll. -// -// Comma is the field delimiter. -// -// If UseCRLF is true, the Writer ends each record with \r\n instead of \n. -type Writer struct { - Comma int // Field delimiter (set to to ',' by NewWriter) - UseCRLF bool // True to use \r\n as the line terminator - w *bufio.Writer -} - -// NewWriter returns a new Writer that writes to w. -func NewWriter(w io.Writer) *Writer { - return &Writer{ - Comma: ',', - w: bufio.NewWriter(w), - } -} - -// Writer writes a single CSV record to w along with any necessary quoting. -// A record is a slice of strings with each string being one field. -func (w *Writer) Write(record []string) (err os.Error) { - for n, field := range record { - if n > 0 { - if _, err = w.w.WriteRune(w.Comma); err != nil { - return - } - } - - // If we don't have to have a quoted field then just - // write out the field and continue to the next field. - if !w.fieldNeedsQuotes(field) { - if _, err = w.w.WriteString(field); err != nil { - return - } - continue - } - if err = w.w.WriteByte('"'); err != nil { - return - } - - for _, rune := range field { - switch rune { - case '"': - _, err = w.w.WriteString(`""`) - case '\r': - if !w.UseCRLF { - err = w.w.WriteByte('\r') - } - case '\n': - if w.UseCRLF { - _, err = w.w.WriteString("\r\n") - } else { - err = w.w.WriteByte('\n') - } - default: - _, err = w.w.WriteRune(rune) - } - if err != nil { - return - } - } - - if err = w.w.WriteByte('"'); err != nil { - return - } - } - if w.UseCRLF { - _, err = w.w.WriteString("\r\n") - } else { - err = w.w.WriteByte('\n') - } - return -} - -// Flush writes any buffered data to the underlying io.Writer. -func (w *Writer) Flush() { - w.w.Flush() -} - -// WriteAll writes multiple CSV records to w using Write and then calls Flush. -func (w *Writer) WriteAll(records [][]string) (err os.Error) { - for _, record := range records { - err = w.Write(record) - if err != nil { - break - } - } - w.Flush() - return nil -} - -// fieldNeedsQuotes returns true if our field must be enclosed in quotes. -// Empty fields, files with a Comma, fields with a quote or newline, and -// fields which start with a space must be enclosed in quotes. -func (w *Writer) fieldNeedsQuotes(field string) bool { - if len(field) == 0 || strings.IndexRune(field, w.Comma) >= 0 || strings.IndexAny(field, "\"\r\n") >= 0 { - return true - } - - rune, _ := utf8.DecodeRuneInString(field) - return unicode.IsSpace(rune) -} diff --git a/src/pkg/csv/writer_test.go b/src/pkg/csv/writer_test.go deleted file mode 100644 index 578959007..000000000 --- a/src/pkg/csv/writer_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package csv - -import ( - "bytes" - "testing" -) - -var writeTests = []struct { - Input [][]string - Output string - UseCRLF bool -}{ - {Input: [][]string{{"abc"}}, Output: "abc\n"}, - {Input: [][]string{{"abc"}}, Output: "abc\r\n", UseCRLF: true}, - {Input: [][]string{{`"abc"`}}, Output: `"""abc"""` + "\n"}, - {Input: [][]string{{`a"b`}}, Output: `"a""b"` + "\n"}, - {Input: [][]string{{`"a"b"`}}, Output: `"""a""b"""` + "\n"}, - {Input: [][]string{{" abc"}}, Output: `" abc"` + "\n"}, - {Input: [][]string{{"abc,def"}}, Output: `"abc,def"` + "\n"}, - {Input: [][]string{{"abc", "def"}}, Output: "abc,def\n"}, - {Input: [][]string{{"abc"}, {"def"}}, Output: "abc\ndef\n"}, - {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\ndef\"\n"}, - {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\r\ndef\"\r\n", UseCRLF: true}, -} - -func TestWrite(t *testing.T) { - for n, tt := range writeTests { - b := &bytes.Buffer{} - f := NewWriter(b) - f.UseCRLF = tt.UseCRLF - err := f.WriteAll(tt.Input) - if err != nil { - t.Errorf("Unexpected error: %s\n", err) - } - out := b.String() - if out != tt.Output { - t.Errorf("#%d: out=%q want %q", n, out, tt.Output) - } - } -} diff --git a/src/pkg/database/sql/convert.go b/src/pkg/database/sql/convert.go new file mode 100644 index 000000000..bfcb03ccf --- /dev/null +++ b/src/pkg/database/sql/convert.go @@ -0,0 +1,158 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Type conversions for Scan. + +package sql + +import ( + "database/sql/driver" + "errors" + "fmt" + "reflect" + "strconv" +) + +// subsetTypeArgs takes a slice of arguments from callers of the sql +// package and converts them into a slice of the driver package's +// "subset types". +func subsetTypeArgs(args []interface{}) ([]driver.Value, error) { + out := make([]driver.Value, len(args)) + for n, arg := range args { + var err error + out[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err) + } + } + return out, nil +} + +// convertAssign copies to dest the value in src, converting it if possible. +// An error is returned if the copy would result in loss of information. +// dest should be a pointer type. +func convertAssign(dest, src interface{}) error { + // Common cases, without reflect. Fall through. + switch s := src.(type) { + case string: + switch d := dest.(type) { + case *string: + *d = s + return nil + case *[]byte: + *d = []byte(s) + return nil + } + case []byte: + switch d := dest.(type) { + case *string: + *d = string(s) + return nil + case *interface{}: + bcopy := make([]byte, len(s)) + copy(bcopy, s) + *d = bcopy + return nil + case *[]byte: + *d = s + return nil + } + case nil: + switch d := dest.(type) { + case *[]byte: + *d = nil + return nil + } + } + + var sv reflect.Value + + switch d := dest.(type) { + case *string: + sv = reflect.ValueOf(src) + switch sv.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + *d = fmt.Sprintf("%v", src) + return nil + } + case *bool: + bv, err := driver.Bool.ConvertValue(src) + if err == nil { + *d = bv.(bool) + } + return err + case *interface{}: + *d = src + return nil + } + + if scanner, ok := dest.(Scanner); ok { + return scanner.Scan(src) + } + + dpv := reflect.ValueOf(dest) + if dpv.Kind() != reflect.Ptr { + return errors.New("destination not a pointer") + } + + if !sv.IsValid() { + sv = reflect.ValueOf(src) + } + + dv := reflect.Indirect(dpv) + if dv.Kind() == sv.Kind() { + dv.Set(sv) + return nil + } + + switch dv.Kind() { + case reflect.Ptr: + if src == nil { + dv.Set(reflect.Zero(dv.Type())) + return nil + } else { + dv.Set(reflect.New(dv.Type().Elem())) + return convertAssign(dv.Interface(), src) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s := asString(src) + i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) + if err != nil { + return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) + } + dv.SetInt(i64) + return nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + s := asString(src) + u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) + if err != nil { + return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) + } + dv.SetUint(u64) + return nil + case reflect.Float32, reflect.Float64: + s := asString(src) + f64, err := strconv.ParseFloat(s, dv.Type().Bits()) + if err != nil { + return fmt.Errorf("converting string %q to a %s: %v", s, dv.Kind(), err) + } + dv.SetFloat(f64) + return nil + } + + return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest) +} + +func asString(src interface{}) string { + switch v := src.(type) { + case string: + return v + case []byte: + return string(v) + } + return fmt.Sprintf("%v", src) +} diff --git a/src/pkg/database/sql/convert_test.go b/src/pkg/database/sql/convert_test.go new file mode 100644 index 000000000..9c362d732 --- /dev/null +++ b/src/pkg/database/sql/convert_test.go @@ -0,0 +1,250 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sql + +import ( + "database/sql/driver" + "fmt" + "reflect" + "testing" + "time" +) + +var someTime = time.Unix(123, 0) +var answer int64 = 42 + +type conversionTest struct { + s, d interface{} // source and destination + + // following are used if they're non-zero + wantint int64 + wantuint uint64 + wantstr string + wantf32 float32 + wantf64 float64 + wanttime time.Time + wantbool bool // used if d is of type *bool + wanterr string + wantiface interface{} + wantptr *int64 // if non-nil, *d's pointed value must be equal to *wantptr + wantnil bool // if true, *d must be *int64(nil) +} + +// Target variables for scanning into. +var ( + scanstr string + scanint int + scanint8 int8 + scanint16 int16 + scanint32 int32 + scanuint8 uint8 + scanuint16 uint16 + scanbool bool + scanf32 float32 + scanf64 float64 + scantime time.Time + scanptr *int64 + scaniface interface{} +) + +var conversionTests = []conversionTest{ + // Exact conversions (destination pointer type matches source type) + {s: "foo", d: &scanstr, wantstr: "foo"}, + {s: 123, d: &scanint, wantint: 123}, + {s: someTime, d: &scantime, wanttime: someTime}, + + // To strings + {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"}, + {s: 123, d: &scanstr, wantstr: "123"}, + {s: int8(123), d: &scanstr, wantstr: "123"}, + {s: int64(123), d: &scanstr, wantstr: "123"}, + {s: uint8(123), d: &scanstr, wantstr: "123"}, + {s: uint16(123), d: &scanstr, wantstr: "123"}, + {s: uint32(123), d: &scanstr, wantstr: "123"}, + {s: uint64(123), d: &scanstr, wantstr: "123"}, + {s: 1.5, d: &scanstr, wantstr: "1.5"}, + + // Strings to integers + {s: "255", d: &scanuint8, wantuint: 255}, + {s: "256", d: &scanuint8, wanterr: `converting string "256" to a uint8: strconv.ParseUint: parsing "256": value out of range`}, + {s: "256", d: &scanuint16, wantuint: 256}, + {s: "-1", d: &scanint, wantint: -1}, + {s: "foo", d: &scanint, wanterr: `converting string "foo" to a int: strconv.ParseInt: parsing "foo": invalid syntax`}, + + // True bools + {s: true, d: &scanbool, wantbool: true}, + {s: "True", d: &scanbool, wantbool: true}, + {s: "TRUE", d: &scanbool, wantbool: true}, + {s: "1", d: &scanbool, wantbool: true}, + {s: 1, d: &scanbool, wantbool: true}, + {s: int64(1), d: &scanbool, wantbool: true}, + {s: uint16(1), d: &scanbool, wantbool: true}, + + // False bools + {s: false, d: &scanbool, wantbool: false}, + {s: "false", d: &scanbool, wantbool: false}, + {s: "FALSE", d: &scanbool, wantbool: false}, + {s: "0", d: &scanbool, wantbool: false}, + {s: 0, d: &scanbool, wantbool: false}, + {s: int64(0), d: &scanbool, wantbool: false}, + {s: uint16(0), d: &scanbool, wantbool: false}, + + // Not bools + {s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`}, + {s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`}, + + // Floats + {s: float64(1.5), d: &scanf64, wantf64: float64(1.5)}, + {s: int64(1), d: &scanf64, wantf64: float64(1)}, + {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)}, + {s: "1.5", d: &scanf32, wantf32: float32(1.5)}, + {s: "1.5", d: &scanf64, wantf64: float64(1.5)}, + + // Pointers + {s: interface{}(nil), d: &scanptr, wantnil: true}, + {s: int64(42), d: &scanptr, wantptr: &answer}, + + // To interface{} + {s: float64(1.5), d: &scaniface, wantiface: float64(1.5)}, + {s: int64(1), d: &scaniface, wantiface: int64(1)}, + {s: "str", d: &scaniface, wantiface: "str"}, + {s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")}, + {s: true, d: &scaniface, wantiface: true}, + {s: nil, d: &scaniface}, +} + +func intPtrValue(intptr interface{}) interface{} { + return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int() +} + +func intValue(intptr interface{}) int64 { + return reflect.Indirect(reflect.ValueOf(intptr)).Int() +} + +func uintValue(intptr interface{}) uint64 { + return reflect.Indirect(reflect.ValueOf(intptr)).Uint() +} + +func float64Value(ptr interface{}) float64 { + return *(ptr.(*float64)) +} + +func float32Value(ptr interface{}) float32 { + return *(ptr.(*float32)) +} + +func timeValue(ptr interface{}) time.Time { + return *(ptr.(*time.Time)) +} + +func TestConversions(t *testing.T) { + for n, ct := range conversionTests { + err := convertAssign(ct.d, ct.s) + errstr := "" + if err != nil { + errstr = err.Error() + } + errf := func(format string, args ...interface{}) { + base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d) + t.Errorf(base+format, args...) + } + if errstr != ct.wanterr { + errf("got error %q, want error %q", errstr, ct.wanterr) + } + if ct.wantstr != "" && ct.wantstr != scanstr { + errf("want string %q, got %q", ct.wantstr, scanstr) + } + if ct.wantint != 0 && ct.wantint != intValue(ct.d) { + errf("want int %d, got %d", ct.wantint, intValue(ct.d)) + } + if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) { + errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d)) + } + if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) { + errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d)) + } + if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) { + errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d)) + } + if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" { + errf("want bool %v, got %v", ct.wantbool, *bp) + } + if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) { + errf("want time %v, got %v", ct.wanttime, timeValue(ct.d)) + } + if ct.wantnil && *ct.d.(**int64) != nil { + errf("want nil, got %v", intPtrValue(ct.d)) + } + if ct.wantptr != nil { + if *ct.d.(**int64) == nil { + errf("want pointer to %v, got nil", *ct.wantptr) + } else if *ct.wantptr != intPtrValue(ct.d) { + errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d)) + } + } + if ifptr, ok := ct.d.(*interface{}); ok { + if !reflect.DeepEqual(ct.wantiface, scaniface) { + errf("want interface %#v, got %#v", ct.wantiface, scaniface) + continue + } + if srcBytes, ok := ct.s.([]byte); ok { + dstBytes := (*ifptr).([]byte) + if &dstBytes[0] == &srcBytes[0] { + errf("copy into interface{} didn't copy []byte data") + } + } + } + } +} + +func TestNullString(t *testing.T) { + var ns NullString + convertAssign(&ns, []byte("foo")) + if !ns.Valid { + t.Errorf("expecting not null") + } + if ns.String != "foo" { + t.Errorf("expecting foo; got %q", ns.String) + } + convertAssign(&ns, nil) + if ns.Valid { + t.Errorf("expecting null on nil") + } + if ns.String != "" { + t.Errorf("expecting blank on nil; got %q", ns.String) + } +} + +type valueConverterTest struct { + c driver.ValueConverter + in, out interface{} + err string +} + +var valueConverterTests = []valueConverterTest{ + {driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""}, + {driver.DefaultParameterConverter, NullString{"", false}, nil, ""}, +} + +func TestValueConverters(t *testing.T) { + for i, tt := range valueConverterTests { + out, err := tt.c.ConvertValue(tt.in) + goterr := "" + if err != nil { + goterr = err.Error() + } + if goterr != tt.err { + t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q", + i, tt.c, tt.in, tt.in, goterr, tt.err) + } + if tt.err != "" { + continue + } + if !reflect.DeepEqual(out, tt.out) { + t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)", + i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) + } + } +} diff --git a/src/pkg/database/sql/doc.txt b/src/pkg/database/sql/doc.txt new file mode 100644 index 000000000..fb1659548 --- /dev/null +++ b/src/pkg/database/sql/doc.txt @@ -0,0 +1,46 @@ +Goals of the sql and sql/driver packages: + +* Provide a generic database API for a variety of SQL or SQL-like + databases. There currently exist Go libraries for SQLite, MySQL, + and Postgres, but all with a very different feel, and often + a non-Go-like feel. + +* Feel like Go. + +* Care mostly about the common cases. Common SQL should be portable. + SQL edge cases or db-specific extensions can be detected and + conditionally used by the application. It is a non-goal to care + about every particular db's extension or quirk. + +* Separate out the basic implementation of a database driver + (implementing the sql/driver interfaces) vs the implementation + of all the user-level types and convenience methods. + In a nutshell: + + User Code ---> sql package (concrete types) ---> sql/driver (interfaces) + Database Driver -> sql (to register) + sql/driver (implement interfaces) + +* Make type casting/conversions consistent between all drivers. To + achieve this, most of the conversions are done in the db package, + not in each driver. The drivers then only have to deal with a + smaller set of types. + +* Be flexible with type conversions, but be paranoid about silent + truncation or other loss of precision. + +* Handle concurrency well. Users shouldn't need to care about the + database's per-connection thread safety issues (or lack thereof), + and shouldn't have to maintain their own free pools of connections. + The 'db' package should deal with that bookkeeping as needed. Given + an *sql.DB, it should be possible to share that instance between + multiple goroutines, without any extra synchronization. + +* Push complexity, where necessary, down into the sql+driver packages, + rather than exposing it to users. Said otherwise, the sql package + should expose an ideal database that's not finnicky about how it's + accessed, even if that's not true. + +* Provide optional interfaces in sql/driver for drivers to implement + for special cases or fastpaths. But the only party that knows about + those is the sql package. To user code, some stuff just might start + working or start working slightly faster. diff --git a/src/pkg/database/sql/driver/driver.go b/src/pkg/database/sql/driver/driver.go new file mode 100644 index 000000000..2f5280db8 --- /dev/null +++ b/src/pkg/database/sql/driver/driver.go @@ -0,0 +1,215 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package driver defines interfaces to be implemented by database +// drivers as used by package sql. +// +// Most code should use package sql. +package driver + +import "errors" + +// A driver Value is a value that drivers must be able to handle. +// A Value is either nil or an instance of one of these types: +// +// int64 +// float64 +// bool +// []byte +// string [*] everywhere except from Rows.Next. +// time.Time +type Value interface{} + +// Driver is the interface that must be implemented by a database +// driver. +type Driver interface { + // Open returns a new connection to the database. + // The name is a string in a driver-specific format. + // + // Open may return a cached connection (one previously + // closed), but doing so is unnecessary; the sql package + // maintains a pool of idle connections for efficient re-use. + // + // The returned connection is only used by one goroutine at a + // time. + Open(name string) (Conn, error) +} + +// ErrSkip may be returned by some optional interfaces' methods to +// indicate at runtime that the fast path is unavailable and the sql +// package should continue as if the optional interface was not +// implemented. ErrSkip is only supported where explicitly +// documented. +var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented") + +// ErrBadConn should be returned by a driver to signal to the sql +// package that a driver.Conn is in a bad state (such as the server +// having earlier closed the connection) and the sql package should +// retry on a new connection. +// +// To prevent duplicate operations, ErrBadConn should NOT be returned +// if there's a possibility that the database server might have +// performed the operation. Even if the server sends back an error, +// you shouldn't return ErrBadConn. +var ErrBadConn = errors.New("driver: bad connection") + +// Execer is an optional interface that may be implemented by a Conn. +// +// If a Conn does not implement Execer, the db package's DB.Exec will +// first prepare a query, execute the statement, and then close the +// statement. +// +// Exec may return ErrSkip. +type Execer interface { + Exec(query string, args []Value) (Result, error) +} + +// Conn is a connection to a database. It is not used concurrently +// by multiple goroutines. +// +// Conn is assumed to be stateful. +type Conn interface { + // Prepare returns a prepared statement, bound to this connection. + Prepare(query string) (Stmt, error) + + // Close invalidates and potentially stops any current + // prepared statements and transactions, marking this + // connection as no longer in use. + // + // Because the sql package maintains a free pool of + // connections and only calls Close when there's a surplus of + // idle connections, it shouldn't be necessary for drivers to + // do their own connection caching. + Close() error + + // Begin starts and returns a new transaction. + Begin() (Tx, error) +} + +// Result is the result of a query execution. +type Result interface { + // LastInsertId returns the database's auto-generated ID + // after, for example, an INSERT into a table with primary + // key. + LastInsertId() (int64, error) + + // RowsAffected returns the number of rows affected by the + // query. + RowsAffected() (int64, error) +} + +// Stmt is a prepared statement. It is bound to a Conn and not +// used by multiple goroutines concurrently. +type Stmt interface { + // Close closes the statement. + // + // Closing a statement should not interrupt any outstanding + // query created from that statement. That is, the following + // order of operations is valid: + // + // * create a driver statement + // * call Query on statement, returning Rows + // * close the statement + // * read from Rows + // + // If closing a statement invalidates currently-running + // queries, the final step above will incorrectly fail. + // + // TODO(bradfitz): possibly remove the restriction above, if + // enough driver authors object and find it complicates their + // code too much. The sql package could be smarter about + // refcounting the statement and closing it at the appropriate + // time. + Close() error + + // NumInput returns the number of placeholder parameters. + // + // If NumInput returns >= 0, the sql package will sanity check + // argument counts from callers and return errors to the caller + // before the statement's Exec or Query methods are called. + // + // NumInput may also return -1, if the driver doesn't know + // its number of placeholders. In that case, the sql package + // will not sanity check Exec or Query argument counts. + NumInput() int + + // Exec executes a query that doesn't return rows, such + // as an INSERT or UPDATE. + Exec(args []Value) (Result, error) + + // Exec executes a query that may return rows, such as a + // SELECT. + Query(args []Value) (Rows, error) +} + +// ColumnConverter may be optionally implemented by Stmt if the +// the statement is aware of its own columns' types and can +// convert from any type to a driver Value. +type ColumnConverter interface { + // ColumnConverter returns a ValueConverter for the provided + // column index. If the type of a specific column isn't known + // or shouldn't be handled specially, DefaultValueConverter + // can be returned. + ColumnConverter(idx int) ValueConverter +} + +// Rows is an iterator over an executed query's results. +type Rows interface { + // Columns returns the names of the columns. The number of + // columns of the result is inferred from the length of the + // slice. If a particular column name isn't known, an empty + // string should be returned for that entry. + Columns() []string + + // Close closes the rows iterator. + Close() error + + // Next is called to populate the next row of data into + // the provided slice. The provided slice will be the same + // size as the Columns() are wide. + // + // The dest slice may be populated only with + // a driver Value type, but excluding string. + // All string values must be converted to []byte. + // + // Next should return io.EOF when there are no more rows. + Next(dest []Value) error +} + +// Tx is a transaction. +type Tx interface { + Commit() error + Rollback() error +} + +// RowsAffected implements Result for an INSERT or UPDATE operation +// which mutates a number of rows. +type RowsAffected int64 + +var _ Result = RowsAffected(0) + +func (RowsAffected) LastInsertId() (int64, error) { + return 0, errors.New("no LastInsertId available") +} + +func (v RowsAffected) RowsAffected() (int64, error) { + return int64(v), nil +} + +// ResultNoRows is a pre-defined Result for drivers to return when a DDL +// command (such as a CREATE TABLE) succeeds. It returns an error for both +// LastInsertId and RowsAffected. +var ResultNoRows noRows + +type noRows struct{} + +var _ Result = noRows{} + +func (noRows) LastInsertId() (int64, error) { + return 0, errors.New("no LastInsertId available after DDL statement") +} + +func (noRows) RowsAffected() (int64, error) { + return 0, errors.New("no RowsAffected available after DDL statement") +} diff --git a/src/pkg/database/sql/driver/types.go b/src/pkg/database/sql/driver/types.go new file mode 100644 index 000000000..3305354df --- /dev/null +++ b/src/pkg/database/sql/driver/types.go @@ -0,0 +1,252 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package driver + +import ( + "fmt" + "reflect" + "strconv" + "time" +) + +// ValueConverter is the interface providing the ConvertValue method. +// +// Various implementations of ValueConverter are provided by the +// driver package to provide consistent implementations of conversions +// between drivers. The ValueConverters have several uses: +// +// * converting from the Value types as provided by the sql package +// into a database table's specific column type and making sure it +// fits, such as making sure a particular int64 fits in a +// table's uint16 column. +// +// * converting a value as given from the database into one of the +// driver Value types. +// +// * by the sql package, for converting from a driver's Value type +// to a user's type in a scan. +type ValueConverter interface { + // ConvertValue converts a value to a driver Value. + ConvertValue(v interface{}) (Value, error) +} + +// Valuer is the interface providing the Value method. +// +// Types implementing Valuer interface are able to convert +// themselves to a driver Value. +type Valuer interface { + // Value returns a driver Value. + Value() (Value, error) +} + +// Bool is a ValueConverter that converts input values to bools. +// +// The conversion rules are: +// - booleans are returned unchanged +// - for integer types, +// 1 is true +// 0 is false, +// other integers are an error +// - for strings and []byte, same rules as strconv.ParseBool +// - all other types are an error +var Bool boolType + +type boolType struct{} + +var _ ValueConverter = boolType{} + +func (boolType) String() string { return "Bool" } + +func (boolType) ConvertValue(src interface{}) (Value, error) { + switch s := src.(type) { + case bool: + return s, nil + case string: + b, err := strconv.ParseBool(s) + if err != nil { + return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s) + } + return b, nil + case []byte: + b, err := strconv.ParseBool(string(s)) + if err != nil { + return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s) + } + return b, nil + } + + sv := reflect.ValueOf(src) + switch sv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + iv := sv.Int() + if iv == 1 || iv == 0 { + return iv == 1, nil + } + return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", iv) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + uv := sv.Uint() + if uv == 1 || uv == 0 { + return uv == 1, nil + } + return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", uv) + } + + return nil, fmt.Errorf("sql/driver: couldn't convert %v (%T) into type bool", src, src) +} + +// Int32 is a ValueConverter that converts input values to int64, +// respecting the limits of an int32 value. +var Int32 int32Type + +type int32Type struct{} + +var _ ValueConverter = int32Type{} + +func (int32Type) ConvertValue(v interface{}) (Value, error) { + rv := reflect.ValueOf(v) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i64 := rv.Int() + if i64 > (1<<31)-1 || i64 < -(1<<31) { + return nil, fmt.Errorf("sql/driver: value %d overflows int32", v) + } + return i64, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + u64 := rv.Uint() + if u64 > (1<<31)-1 { + return nil, fmt.Errorf("sql/driver: value %d overflows int32", v) + } + return int64(u64), nil + case reflect.String: + i, err := strconv.Atoi(rv.String()) + if err != nil { + return nil, fmt.Errorf("sql/driver: value %q can't be converted to int32", v) + } + return int64(i), nil + } + return nil, fmt.Errorf("sql/driver: unsupported value %v (type %T) converting to int32", v, v) +} + +// String is a ValueConverter that converts its input to a string. +// If the value is already a string or []byte, it's unchanged. +// If the value is of another type, conversion to string is done +// with fmt.Sprintf("%v", v). +var String stringType + +type stringType struct{} + +func (stringType) ConvertValue(v interface{}) (Value, error) { + switch v.(type) { + case string, []byte: + return v, nil + } + return fmt.Sprintf("%v", v), nil +} + +// Null is a type that implements ValueConverter by allowing nil +// values but otherwise delegating to another ValueConverter. +type Null struct { + Converter ValueConverter +} + +func (n Null) ConvertValue(v interface{}) (Value, error) { + if v == nil { + return nil, nil + } + return n.Converter.ConvertValue(v) +} + +// NotNull is a type that implements ValueConverter by disallowing nil +// values but otherwise delegating to another ValueConverter. +type NotNull struct { + Converter ValueConverter +} + +func (n NotNull) ConvertValue(v interface{}) (Value, error) { + if v == nil { + return nil, fmt.Errorf("nil value not allowed") + } + return n.Converter.ConvertValue(v) +} + +// IsValue reports whether v is a valid Value parameter type. +// Unlike IsScanValue, IsValue permits the string type. +func IsValue(v interface{}) bool { + if IsScanValue(v) { + return true + } + if _, ok := v.(string); ok { + return true + } + return false +} + +// IsScanValue reports whether v is a valid Value scan type. +// Unlike IsValue, IsScanValue does not permit the string type. +func IsScanValue(v interface{}) bool { + if v == nil { + return true + } + switch v.(type) { + case int64, float64, []byte, bool, time.Time: + return true + } + return false +} + +// DefaultParameterConverter is the default implementation of +// ValueConverter that's used when a Stmt doesn't implement +// ColumnConverter. +// +// DefaultParameterConverter returns the given value directly if +// IsValue(value). Otherwise integer type are converted to +// int64, floats to float64, and strings to []byte. Other types are +// an error. +var DefaultParameterConverter defaultConverter + +type defaultConverter struct{} + +var _ ValueConverter = defaultConverter{} + +func (defaultConverter) ConvertValue(v interface{}) (Value, error) { + if IsValue(v) { + return v, nil + } + + if svi, ok := v.(Valuer); ok { + sv, err := svi.Value() + if err != nil { + return nil, err + } + if !IsValue(sv) { + return nil, fmt.Errorf("non-Value type %T returned from Value", sv) + } + return sv, nil + } + + rv := reflect.ValueOf(v) + switch rv.Kind() { + case reflect.Ptr: + // indirect pointers + if rv.IsNil() { + return nil, nil + } else { + return defaultConverter{}.ConvertValue(rv.Elem().Interface()) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int(), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: + return int64(rv.Uint()), nil + case reflect.Uint64: + u64 := rv.Uint() + if u64 >= 1<<63 { + return nil, fmt.Errorf("uint64 values with high bit set are not supported") + } + return int64(u64), nil + case reflect.Float32, reflect.Float64: + return rv.Float(), nil + } + return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind()) +} diff --git a/src/pkg/database/sql/driver/types_test.go b/src/pkg/database/sql/driver/types_test.go new file mode 100644 index 000000000..ab82bca71 --- /dev/null +++ b/src/pkg/database/sql/driver/types_test.go @@ -0,0 +1,65 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package driver + +import ( + "reflect" + "testing" + "time" +) + +type valueConverterTest struct { + c ValueConverter + in interface{} + out interface{} + err string +} + +var now = time.Now() +var answer int64 = 42 + +var valueConverterTests = []valueConverterTest{ + {Bool, "true", true, ""}, + {Bool, "True", true, ""}, + {Bool, []byte("t"), true, ""}, + {Bool, true, true, ""}, + {Bool, "1", true, ""}, + {Bool, 1, true, ""}, + {Bool, int64(1), true, ""}, + {Bool, uint16(1), true, ""}, + {Bool, "false", false, ""}, + {Bool, false, false, ""}, + {Bool, "0", false, ""}, + {Bool, 0, false, ""}, + {Bool, int64(0), false, ""}, + {Bool, uint16(0), false, ""}, + {c: Bool, in: "foo", err: "sql/driver: couldn't convert \"foo\" into type bool"}, + {c: Bool, in: 2, err: "sql/driver: couldn't convert 2 into type bool"}, + {DefaultParameterConverter, now, now, ""}, + {DefaultParameterConverter, (*int64)(nil), nil, ""}, + {DefaultParameterConverter, &answer, answer, ""}, + {DefaultParameterConverter, &now, now, ""}, +} + +func TestValueConverters(t *testing.T) { + for i, tt := range valueConverterTests { + out, err := tt.c.ConvertValue(tt.in) + goterr := "" + if err != nil { + goterr = err.Error() + } + if goterr != tt.err { + t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q", + i, tt.c, tt.in, tt.in, goterr, tt.err) + } + if tt.err != "" { + continue + } + if !reflect.DeepEqual(out, tt.out) { + t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)", + i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) + } + } +} diff --git a/src/pkg/database/sql/fakedb_test.go b/src/pkg/database/sql/fakedb_test.go new file mode 100644 index 000000000..184e7756c --- /dev/null +++ b/src/pkg/database/sql/fakedb_test.go @@ -0,0 +1,629 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sql + +import ( + "database/sql/driver" + "errors" + "fmt" + "io" + "log" + "strconv" + "strings" + "sync" + "time" +) + +var _ = log.Printf + +// fakeDriver is a fake database that implements Go's driver.Driver +// interface, just for testing. +// +// It speaks a query language that's semantically similar to but +// syntantically different and simpler than SQL. The syntax is as +// follows: +// +// WIPE +// CREATE||=,=,... +// where types are: "string", [u]int{8,16,32,64}, "bool" +// INSERT||col=val,col2=val2,col3=? +// SELECT||projectcol1,projectcol2|filtercol=?,filtercol2=? +// +// When opening a a fakeDriver's database, it starts empty with no +// tables. All tables and data are stored in memory only. +type fakeDriver struct { + mu sync.Mutex + openCount int + dbs map[string]*fakeDB +} + +type fakeDB struct { + name string + + mu sync.Mutex + free []*fakeConn + tables map[string]*table +} + +type table struct { + mu sync.Mutex + colname []string + coltype []string + rows []*row +} + +func (t *table) columnIndex(name string) int { + for n, nname := range t.colname { + if name == nname { + return n + } + } + return -1 +} + +type row struct { + cols []interface{} // must be same size as its table colname + coltype +} + +func (r *row) clone() *row { + nrow := &row{cols: make([]interface{}, len(r.cols))} + copy(nrow.cols, r.cols) + return nrow +} + +type fakeConn struct { + db *fakeDB // where to return ourselves to + + currTx *fakeTx + + // Stats for tests: + mu sync.Mutex + stmtsMade int + stmtsClosed int + numPrepare int +} + +func (c *fakeConn) incrStat(v *int) { + c.mu.Lock() + *v++ + c.mu.Unlock() +} + +type fakeTx struct { + c *fakeConn +} + +type fakeStmt struct { + c *fakeConn + q string // just for debugging + + cmd string + table string + + closed bool + + colName []string // used by CREATE, INSERT, SELECT (selected columns) + colType []string // used by CREATE + colValue []interface{} // used by INSERT (mix of strings and "?" for bound params) + placeholders int // used by INSERT/SELECT: number of ? params + + whereCol []string // used by SELECT (all placeholders) + + placeholderConverter []driver.ValueConverter // used by INSERT +} + +var fdriver driver.Driver = &fakeDriver{} + +func init() { + Register("test", fdriver) +} + +// Supports dsn forms: +// +// ; (no currently supported options) +func (d *fakeDriver) Open(dsn string) (driver.Conn, error) { + parts := strings.Split(dsn, ";") + if len(parts) < 1 { + return nil, errors.New("fakedb: no database name") + } + name := parts[0] + + db := d.getDB(name) + + d.mu.Lock() + d.openCount++ + d.mu.Unlock() + return &fakeConn{db: db}, nil +} + +func (d *fakeDriver) getDB(name string) *fakeDB { + d.mu.Lock() + defer d.mu.Unlock() + if d.dbs == nil { + d.dbs = make(map[string]*fakeDB) + } + db, ok := d.dbs[name] + if !ok { + db = &fakeDB{name: name} + d.dbs[name] = db + } + return db +} + +func (db *fakeDB) wipe() { + db.mu.Lock() + defer db.mu.Unlock() + db.tables = nil +} + +func (db *fakeDB) createTable(name string, columnNames, columnTypes []string) error { + db.mu.Lock() + defer db.mu.Unlock() + if db.tables == nil { + db.tables = make(map[string]*table) + } + if _, exist := db.tables[name]; exist { + return fmt.Errorf("table %q already exists", name) + } + if len(columnNames) != len(columnTypes) { + return fmt.Errorf("create table of %q len(names) != len(types): %d vs %d", + name, len(columnNames), len(columnTypes)) + } + db.tables[name] = &table{colname: columnNames, coltype: columnTypes} + return nil +} + +// must be called with db.mu lock held +func (db *fakeDB) table(table string) (*table, bool) { + if db.tables == nil { + return nil, false + } + t, ok := db.tables[table] + return t, ok +} + +func (db *fakeDB) columnType(table, column string) (typ string, ok bool) { + db.mu.Lock() + defer db.mu.Unlock() + t, ok := db.table(table) + if !ok { + return + } + for n, cname := range t.colname { + if cname == column { + return t.coltype[n], true + } + } + return "", false +} + +func (c *fakeConn) Begin() (driver.Tx, error) { + if c.currTx != nil { + return nil, errors.New("already in a transaction") + } + c.currTx = &fakeTx{c: c} + return c.currTx, nil +} + +func (c *fakeConn) Close() error { + if c.currTx != nil { + return errors.New("can't close fakeConn; in a Transaction") + } + if c.db == nil { + return errors.New("can't close fakeConn; already closed") + } + if c.stmtsMade > c.stmtsClosed { + return errors.New("can't close; dangling statement(s)") + } + c.db = nil + return nil +} + +func checkSubsetTypes(args []driver.Value) error { + for n, arg := range args { + switch arg.(type) { + case int64, float64, bool, nil, []byte, string, time.Time: + default: + return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg) + } + } + return nil +} + +func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) { + // This is an optional interface, but it's implemented here + // just to check that all the args of of the proper types. + // ErrSkip is returned so the caller acts as if we didn't + // implement this at all. + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + return nil, driver.ErrSkip +} + +func errf(msg string, args ...interface{}) error { + return errors.New("fakedb: " + fmt.Sprintf(msg, args...)) +} + +// parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=? +// (note that where where columns must always contain ? marks, +// just a limitation for fakedb) +func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (driver.Stmt, error) { + if len(parts) != 3 { + stmt.Close() + return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts)) + } + stmt.table = parts[0] + stmt.colName = strings.Split(parts[1], ",") + for n, colspec := range strings.Split(parts[2], ",") { + if colspec == "" { + continue + } + nameVal := strings.Split(colspec, "=") + if len(nameVal) != 2 { + stmt.Close() + return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) + } + column, value := nameVal[0], nameVal[1] + _, ok := c.db.columnType(stmt.table, column) + if !ok { + stmt.Close() + return nil, errf("SELECT on table %q references non-existent column %q", stmt.table, column) + } + if value != "?" { + stmt.Close() + return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark", + stmt.table, column) + } + stmt.whereCol = append(stmt.whereCol, column) + stmt.placeholders++ + } + return stmt, nil +} + +// parts are table|col=type,col2=type2 +func (c *fakeConn) prepareCreate(stmt *fakeStmt, parts []string) (driver.Stmt, error) { + if len(parts) != 2 { + stmt.Close() + return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts)) + } + stmt.table = parts[0] + for n, colspec := range strings.Split(parts[1], ",") { + nameType := strings.Split(colspec, "=") + if len(nameType) != 2 { + stmt.Close() + return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) + } + stmt.colName = append(stmt.colName, nameType[0]) + stmt.colType = append(stmt.colType, nameType[1]) + } + return stmt, nil +} + +// parts are table|col=?,col2=val +func (c *fakeConn) prepareInsert(stmt *fakeStmt, parts []string) (driver.Stmt, error) { + if len(parts) != 2 { + stmt.Close() + return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts)) + } + stmt.table = parts[0] + for n, colspec := range strings.Split(parts[1], ",") { + nameVal := strings.Split(colspec, "=") + if len(nameVal) != 2 { + stmt.Close() + return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt.table, colspec, n) + } + column, value := nameVal[0], nameVal[1] + ctype, ok := c.db.columnType(stmt.table, column) + if !ok { + stmt.Close() + return nil, errf("INSERT table %q references non-existent column %q", stmt.table, column) + } + stmt.colName = append(stmt.colName, column) + + if value != "?" { + var subsetVal interface{} + // Convert to driver subset type + switch ctype { + case "string": + subsetVal = []byte(value) + case "blob": + subsetVal = []byte(value) + case "int32": + i, err := strconv.Atoi(value) + if err != nil { + stmt.Close() + return nil, errf("invalid conversion to int32 from %q", value) + } + subsetVal = int64(i) // int64 is a subset type, but not int32 + default: + stmt.Close() + return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value, ctype) + } + stmt.colValue = append(stmt.colValue, subsetVal) + } else { + stmt.placeholders++ + stmt.placeholderConverter = append(stmt.placeholderConverter, converterForType(ctype)) + stmt.colValue = append(stmt.colValue, "?") + } + } + return stmt, nil +} + +func (c *fakeConn) Prepare(query string) (driver.Stmt, error) { + c.numPrepare++ + if c.db == nil { + panic("nil c.db; conn = " + fmt.Sprintf("%#v", c)) + } + parts := strings.Split(query, "|") + if len(parts) < 1 { + return nil, errf("empty query") + } + cmd := parts[0] + parts = parts[1:] + stmt := &fakeStmt{q: query, c: c, cmd: cmd} + c.incrStat(&c.stmtsMade) + switch cmd { + case "WIPE": + // Nothing + case "SELECT": + return c.prepareSelect(stmt, parts) + case "CREATE": + return c.prepareCreate(stmt, parts) + case "INSERT": + return c.prepareInsert(stmt, parts) + default: + stmt.Close() + return nil, errf("unsupported command type %q", cmd) + } + return stmt, nil +} + +func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter { + return s.placeholderConverter[idx] +} + +func (s *fakeStmt) Close() error { + if !s.closed { + s.c.incrStat(&s.c.stmtsClosed) + s.closed = true + } + return nil +} + +var errClosed = errors.New("fakedb: statement has been closed") + +func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) { + if s.closed { + return nil, errClosed + } + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + + db := s.c.db + switch s.cmd { + case "WIPE": + db.wipe() + return driver.ResultNoRows, nil + case "CREATE": + if err := db.createTable(s.table, s.colName, s.colType); err != nil { + return nil, err + } + return driver.ResultNoRows, nil + case "INSERT": + return s.execInsert(args) + } + fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s) + return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd) +} + +func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) { + db := s.c.db + if len(args) != s.placeholders { + panic("error in pkg db; should only get here if size is correct") + } + db.mu.Lock() + t, ok := db.table(s.table) + db.mu.Unlock() + if !ok { + return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table) + } + + t.mu.Lock() + defer t.mu.Unlock() + + cols := make([]interface{}, len(t.colname)) + argPos := 0 + for n, colname := range s.colName { + colidx := t.columnIndex(colname) + if colidx == -1 { + return nil, fmt.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname) + } + var val interface{} + if strvalue, ok := s.colValue[n].(string); ok && strvalue == "?" { + val = args[argPos] + argPos++ + } else { + val = s.colValue[n] + } + cols[colidx] = val + } + + t.rows = append(t.rows, &row{cols: cols}) + return driver.RowsAffected(1), nil +} + +func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) { + if s.closed { + return nil, errClosed + } + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + + db := s.c.db + if len(args) != s.placeholders { + panic("error in pkg db; should only get here if size is correct") + } + + db.mu.Lock() + t, ok := db.table(s.table) + db.mu.Unlock() + if !ok { + return nil, fmt.Errorf("fakedb: table %q doesn't exist", s.table) + } + t.mu.Lock() + defer t.mu.Unlock() + + colIdx := make(map[string]int) // select column name -> column index in table + for _, name := range s.colName { + idx := t.columnIndex(name) + if idx == -1 { + return nil, fmt.Errorf("fakedb: unknown column name %q", name) + } + colIdx[name] = idx + } + + mrows := []*row{} +rows: + for _, trow := range t.rows { + // Process the where clause, skipping non-match rows. This is lazy + // and just uses fmt.Sprintf("%v") to test equality. Good enough + // for test code. + for widx, wcol := range s.whereCol { + idx := t.columnIndex(wcol) + if idx == -1 { + return nil, fmt.Errorf("db: invalid where clause column %q", wcol) + } + tcol := trow.cols[idx] + if bs, ok := tcol.([]byte); ok { + // lazy hack to avoid sprintf %v on a []byte + tcol = string(bs) + } + if fmt.Sprintf("%v", tcol) != fmt.Sprintf("%v", args[widx]) { + continue rows + } + } + mrow := &row{cols: make([]interface{}, len(s.colName))} + for seli, name := range s.colName { + mrow.cols[seli] = trow.cols[colIdx[name]] + } + mrows = append(mrows, mrow) + } + + cursor := &rowsCursor{ + pos: -1, + rows: mrows, + cols: s.colName, + } + return cursor, nil +} + +func (s *fakeStmt) NumInput() int { + return s.placeholders +} + +func (tx *fakeTx) Commit() error { + tx.c.currTx = nil + return nil +} + +func (tx *fakeTx) Rollback() error { + tx.c.currTx = nil + return nil +} + +type rowsCursor struct { + cols []string + pos int + rows []*row + closed bool + + // a clone of slices to give out to clients, indexed by the + // the original slice's first byte address. we clone them + // just so we're able to corrupt them on close. + bytesClone map[*byte][]byte +} + +func (rc *rowsCursor) Close() error { + if !rc.closed { + for _, bs := range rc.bytesClone { + bs[0] = 255 // first byte corrupted + } + } + rc.closed = true + return nil +} + +func (rc *rowsCursor) Columns() []string { + return rc.cols +} + +func (rc *rowsCursor) Next(dest []driver.Value) error { + if rc.closed { + return errors.New("fakedb: cursor is closed") + } + rc.pos++ + if rc.pos >= len(rc.rows) { + return io.EOF // per interface spec + } + for i, v := range rc.rows[rc.pos].cols { + // TODO(bradfitz): convert to subset types? naah, I + // think the subset types should only be input to + // driver, but the sql package should be able to handle + // a wider range of types coming out of drivers. all + // for ease of drivers, and to prevent drivers from + // messing up conversions or doing them differently. + dest[i] = v + + if bs, ok := v.([]byte); ok { + if rc.bytesClone == nil { + rc.bytesClone = make(map[*byte][]byte) + } + clone, ok := rc.bytesClone[&bs[0]] + if !ok { + clone = make([]byte, len(bs)) + copy(clone, bs) + rc.bytesClone[&bs[0]] = clone + } + dest[i] = clone + } + } + return nil +} + +func converterForType(typ string) driver.ValueConverter { + switch typ { + case "bool": + return driver.Bool + case "nullbool": + return driver.Null{Converter: driver.Bool} + case "int32": + return driver.Int32 + case "string": + return driver.NotNull{Converter: driver.String} + case "nullstring": + return driver.Null{Converter: driver.String} + case "int64": + // TODO(coopernurse): add type-specific converter + return driver.NotNull{Converter: driver.DefaultParameterConverter} + case "nullint64": + // TODO(coopernurse): add type-specific converter + return driver.Null{Converter: driver.DefaultParameterConverter} + case "float64": + // TODO(coopernurse): add type-specific converter + return driver.NotNull{Converter: driver.DefaultParameterConverter} + case "nullfloat64": + // TODO(coopernurse): add type-specific converter + return driver.Null{Converter: driver.DefaultParameterConverter} + case "datetime": + return driver.DefaultParameterConverter + } + panic("invalid fakedb column type of " + typ) +} diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go new file mode 100644 index 000000000..51a357b37 --- /dev/null +++ b/src/pkg/database/sql/sql.go @@ -0,0 +1,1063 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package sql provides a generic interface around SQL (or SQL-like) +// databases. +package sql + +import ( + "database/sql/driver" + "errors" + "fmt" + "io" + "sync" +) + +var drivers = make(map[string]driver.Driver) + +// Register makes a database driver available by the provided name. +// If Register is called twice with the same name or if driver is nil, +// it panics. +func Register(name string, driver driver.Driver) { + if driver == nil { + panic("sql: Register driver is nil") + } + if _, dup := drivers[name]; dup { + panic("sql: Register called twice for driver " + name) + } + drivers[name] = driver +} + +// RawBytes is a byte slice that holds a reference to memory owned by +// the database itself. After a Scan into a RawBytes, the slice is only +// valid until the next call to Next, Scan, or Close. +type RawBytes []byte + +// NullString represents a string that may be null. +// NullString implements the Scanner interface so +// it can be used as a scan destination: +// +// var s NullString +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s) +// ... +// if s.Valid { +// // use s.String +// } else { +// // NULL value +// } +// +type NullString struct { + String string + Valid bool // Valid is true if String is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullString) Scan(value interface{}) error { + if value == nil { + ns.String, ns.Valid = "", false + return nil + } + ns.Valid = true + return convertAssign(&ns.String, value) +} + +// Value implements the driver Valuer interface. +func (ns NullString) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return ns.String, nil +} + +// NullInt64 represents an int64 that may be null. +// NullInt64 implements the Scanner interface so +// it can be used as a scan destination, similar to NullString. +type NullInt64 struct { + Int64 int64 + Valid bool // Valid is true if Int64 is not NULL +} + +// Scan implements the Scanner interface. +func (n *NullInt64) Scan(value interface{}) error { + if value == nil { + n.Int64, n.Valid = 0, false + return nil + } + n.Valid = true + return convertAssign(&n.Int64, value) +} + +// Value implements the driver Valuer interface. +func (n NullInt64) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + return n.Int64, nil +} + +// NullFloat64 represents a float64 that may be null. +// NullFloat64 implements the Scanner interface so +// it can be used as a scan destination, similar to NullString. +type NullFloat64 struct { + Float64 float64 + Valid bool // Valid is true if Float64 is not NULL +} + +// Scan implements the Scanner interface. +func (n *NullFloat64) Scan(value interface{}) error { + if value == nil { + n.Float64, n.Valid = 0, false + return nil + } + n.Valid = true + return convertAssign(&n.Float64, value) +} + +// Value implements the driver Valuer interface. +func (n NullFloat64) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + return n.Float64, nil +} + +// NullBool represents a bool that may be null. +// NullBool implements the Scanner interface so +// it can be used as a scan destination, similar to NullString. +type NullBool struct { + Bool bool + Valid bool // Valid is true if Bool is not NULL +} + +// Scan implements the Scanner interface. +func (n *NullBool) Scan(value interface{}) error { + if value == nil { + n.Bool, n.Valid = false, false + return nil + } + n.Valid = true + return convertAssign(&n.Bool, value) +} + +// Value implements the driver Valuer interface. +func (n NullBool) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + return n.Bool, nil +} + +// Scanner is an interface used by Scan. +type Scanner interface { + // Scan assigns a value from a database driver. + // + // The src value will be of one of the following restricted + // set of types: + // + // int64 + // float64 + // bool + // []byte + // string + // time.Time + // nil - for NULL values + // + // An error should be returned if the value can not be stored + // without loss of information. + Scan(src interface{}) error +} + +// ErrNoRows is returned by Scan when QueryRow doesn't return a +// row. In such a case, QueryRow returns a placeholder *Row value that +// defers this error until a Scan. +var ErrNoRows = errors.New("sql: no rows in result set") + +// DB is a database handle. It's safe for concurrent use by multiple +// goroutines. +// +// If the underlying database driver has the concept of a connection +// and per-connection session state, the sql package manages creating +// and freeing connections automatically, including maintaining a free +// pool of idle connections. If observing session state is required, +// either do not share a *DB between multiple concurrent goroutines or +// create and observe all state only within a transaction. Once +// DB.Open is called, the returned Tx is bound to a single isolated +// connection. Once Tx.Commit or Tx.Rollback is called, that +// connection is returned to DB's idle connection pool. +type DB struct { + driver driver.Driver + dsn string + + mu sync.Mutex // protects freeConn and closed + freeConn []driver.Conn + closed bool +} + +// Open opens a database specified by its database driver name and a +// driver-specific data source name, usually consisting of at least a +// database name and connection information. +// +// Most users will open a database via a driver-specific connection +// helper function that returns a *DB. +func Open(driverName, dataSourceName string) (*DB, error) { + driver, ok := drivers[driverName] + if !ok { + return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName) + } + return &DB{driver: driver, dsn: dataSourceName}, nil +} + +// Close closes the database, releasing any open resources. +func (db *DB) Close() error { + db.mu.Lock() + defer db.mu.Unlock() + var err error + for _, c := range db.freeConn { + err1 := c.Close() + if err1 != nil { + err = err1 + } + } + db.freeConn = nil + db.closed = true + return err +} + +func (db *DB) maxIdleConns() int { + const defaultMaxIdleConns = 2 + // TODO(bradfitz): ask driver, if supported, for its default preference + // TODO(bradfitz): let users override? + return defaultMaxIdleConns +} + +// conn returns a newly-opened or cached driver.Conn +func (db *DB) conn() (driver.Conn, error) { + db.mu.Lock() + if db.closed { + db.mu.Unlock() + return nil, errors.New("sql: database is closed") + } + if n := len(db.freeConn); n > 0 { + conn := db.freeConn[n-1] + db.freeConn = db.freeConn[:n-1] + db.mu.Unlock() + return conn, nil + } + db.mu.Unlock() + return db.driver.Open(db.dsn) +} + +func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) { + db.mu.Lock() + defer db.mu.Unlock() + for i, conn := range db.freeConn { + if conn != wanted { + continue + } + db.freeConn[i] = db.freeConn[len(db.freeConn)-1] + db.freeConn = db.freeConn[:len(db.freeConn)-1] + return wanted, true + } + return nil, false +} + +// putConnHook is a hook for testing. +var putConnHook func(*DB, driver.Conn) + +// putConn adds a connection to the db's free pool. +// err is optionally the last error that occured on this connection. +func (db *DB) putConn(c driver.Conn, err error) { + if err == driver.ErrBadConn { + // Don't reuse bad connections. + return + } + db.mu.Lock() + if putConnHook != nil { + putConnHook(db, c) + } + if n := len(db.freeConn); !db.closed && n < db.maxIdleConns() { + db.freeConn = append(db.freeConn, c) + db.mu.Unlock() + return + } + // TODO: check to see if we need this Conn for any prepared + // statements which are still active? + db.mu.Unlock() + c.Close() +} + +// Prepare creates a prepared statement for later execution. +func (db *DB) Prepare(query string) (*Stmt, error) { + var stmt *Stmt + var err error + for i := 0; i < 10; i++ { + stmt, err = db.prepare(query) + if err != driver.ErrBadConn { + break + } + } + return stmt, err +} + +func (db *DB) prepare(query string) (stmt *Stmt, err error) { + // TODO: check if db.driver supports an optional + // driver.Preparer interface and call that instead, if so, + // otherwise we make a prepared statement that's bound + // to a connection, and to execute this prepared statement + // we either need to use this connection (if it's free), else + // get a new connection + re-prepare + execute on that one. + ci, err := db.conn() + if err != nil { + return nil, err + } + defer db.putConn(ci, err) + si, err := ci.Prepare(query) + if err != nil { + return nil, err + } + stmt = &Stmt{ + db: db, + query: query, + css: []connStmt{{ci, si}}, + } + return stmt, nil +} + +// Exec executes a query without returning any rows. +func (db *DB) Exec(query string, args ...interface{}) (Result, error) { + sargs, err := subsetTypeArgs(args) + var res Result + for i := 0; i < 10; i++ { + res, err = db.exec(query, sargs) + if err != driver.ErrBadConn { + break + } + } + return res, err +} + +func (db *DB) exec(query string, sargs []driver.Value) (res Result, err error) { + ci, err := db.conn() + if err != nil { + return nil, err + } + defer db.putConn(ci, err) + + if execer, ok := ci.(driver.Execer); ok { + resi, err := execer.Exec(query, sargs) + if err != driver.ErrSkip { + if err != nil { + return nil, err + } + return result{resi}, nil + } + } + + sti, err := ci.Prepare(query) + if err != nil { + return nil, err + } + defer sti.Close() + + resi, err := sti.Exec(sargs) + if err != nil { + return nil, err + } + return result{resi}, nil +} + +// Query executes a query that returns rows, typically a SELECT. +func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { + stmt, err := db.Prepare(query) + if err != nil { + return nil, err + } + rows, err := stmt.Query(args...) + if err != nil { + stmt.Close() + return nil, err + } + rows.closeStmt = stmt + return rows, nil +} + +// QueryRow executes a query that is expected to return at most one row. +// QueryRow always return a non-nil value. Errors are deferred until +// Row's Scan method is called. +func (db *DB) QueryRow(query string, args ...interface{}) *Row { + rows, err := db.Query(query, args...) + return &Row{rows: rows, err: err} +} + +// Begin starts a transaction. The isolation level is dependent on +// the driver. +func (db *DB) Begin() (*Tx, error) { + var tx *Tx + var err error + for i := 0; i < 10; i++ { + tx, err = db.begin() + if err != driver.ErrBadConn { + break + } + } + return tx, err +} + +func (db *DB) begin() (tx *Tx, err error) { + ci, err := db.conn() + if err != nil { + return nil, err + } + txi, err := ci.Begin() + if err != nil { + db.putConn(ci, err) + return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err) + } + return &Tx{ + db: db, + ci: ci, + txi: txi, + }, nil +} + +// Driver returns the database's underlying driver. +func (db *DB) Driver() driver.Driver { + return db.driver +} + +// Tx is an in-progress database transaction. +// +// A transaction must end with a call to Commit or Rollback. +// +// After a call to Commit or Rollback, all operations on the +// transaction fail with ErrTxDone. +type Tx struct { + db *DB + + // ci is owned exclusively until Commit or Rollback, at which point + // it's returned with putConn. + ci driver.Conn + txi driver.Tx + + // cimu is held while somebody is using ci (between grabConn + // and releaseConn) + cimu sync.Mutex + + // done transitions from false to true exactly once, on Commit + // or Rollback. once done, all operations fail with + // ErrTxDone. + done bool +} + +var ErrTxDone = errors.New("sql: Transaction has already been committed or rolled back") + +func (tx *Tx) close() { + if tx.done { + panic("double close") // internal error + } + tx.done = true + tx.db.putConn(tx.ci, nil) + tx.ci = nil + tx.txi = nil +} + +func (tx *Tx) grabConn() (driver.Conn, error) { + if tx.done { + return nil, ErrTxDone + } + tx.cimu.Lock() + return tx.ci, nil +} + +func (tx *Tx) releaseConn() { + tx.cimu.Unlock() +} + +// Commit commits the transaction. +func (tx *Tx) Commit() error { + if tx.done { + return ErrTxDone + } + defer tx.close() + return tx.txi.Commit() +} + +// Rollback aborts the transaction. +func (tx *Tx) Rollback() error { + if tx.done { + return ErrTxDone + } + defer tx.close() + return tx.txi.Rollback() +} + +// Prepare creates a prepared statement for use within a transaction. +// +// The returned statement operates within the transaction and can no longer +// be used once the transaction has been committed or rolled back. +// +// To use an existing prepared statement on this transaction, see Tx.Stmt. +func (tx *Tx) Prepare(query string) (*Stmt, error) { + // TODO(bradfitz): We could be more efficient here and either + // provide a method to take an existing Stmt (created on + // perhaps a different Conn), and re-create it on this Conn if + // necessary. Or, better: keep a map in DB of query string to + // Stmts, and have Stmt.Execute do the right thing and + // re-prepare if the Conn in use doesn't have that prepared + // statement. But we'll want to avoid caching the statement + // in the case where we only call conn.Prepare implicitly + // (such as in db.Exec or tx.Exec), but the caller package + // can't be holding a reference to the returned statement. + // Perhaps just looking at the reference count (by noting + // Stmt.Close) would be enough. We might also want a finalizer + // on Stmt to drop the reference count. + ci, err := tx.grabConn() + if err != nil { + return nil, err + } + defer tx.releaseConn() + + si, err := ci.Prepare(query) + if err != nil { + return nil, err + } + + stmt := &Stmt{ + db: tx.db, + tx: tx, + txsi: si, + query: query, + } + return stmt, nil +} + +// Stmt returns a transaction-specific prepared statement from +// an existing statement. +// +// Example: +// updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?") +// ... +// tx, err := db.Begin() +// ... +// res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203) +func (tx *Tx) Stmt(stmt *Stmt) *Stmt { + // TODO(bradfitz): optimize this. Currently this re-prepares + // each time. This is fine for now to illustrate the API but + // we should really cache already-prepared statements + // per-Conn. See also the big comment in Tx.Prepare. + + if tx.db != stmt.db { + return &Stmt{stickyErr: errors.New("sql: Tx.Stmt: statement from different database used")} + } + ci, err := tx.grabConn() + if err != nil { + return &Stmt{stickyErr: err} + } + defer tx.releaseConn() + si, err := ci.Prepare(stmt.query) + return &Stmt{ + db: tx.db, + tx: tx, + txsi: si, + query: stmt.query, + stickyErr: err, + } +} + +// Exec executes a query that doesn't return rows. +// For example: an INSERT and UPDATE. +func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { + ci, err := tx.grabConn() + if err != nil { + return nil, err + } + defer tx.releaseConn() + + sargs, err := subsetTypeArgs(args) + if err != nil { + return nil, err + } + + if execer, ok := ci.(driver.Execer); ok { + resi, err := execer.Exec(query, sargs) + if err == nil { + return result{resi}, nil + } + if err != driver.ErrSkip { + return nil, err + } + } + + sti, err := ci.Prepare(query) + if err != nil { + return nil, err + } + defer sti.Close() + + resi, err := sti.Exec(sargs) + if err != nil { + return nil, err + } + return result{resi}, nil +} + +// Query executes a query that returns rows, typically a SELECT. +func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { + if tx.done { + return nil, ErrTxDone + } + stmt, err := tx.Prepare(query) + if err != nil { + return nil, err + } + rows, err := stmt.Query(args...) + if err != nil { + stmt.Close() + return nil, err + } + rows.closeStmt = stmt + return rows, err +} + +// QueryRow executes a query that is expected to return at most one row. +// QueryRow always return a non-nil value. Errors are deferred until +// Row's Scan method is called. +func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { + rows, err := tx.Query(query, args...) + return &Row{rows: rows, err: err} +} + +// connStmt is a prepared statement on a particular connection. +type connStmt struct { + ci driver.Conn + si driver.Stmt +} + +// Stmt is a prepared statement. Stmt is safe for concurrent use by multiple goroutines. +type Stmt struct { + // Immutable: + db *DB // where we came from + query string // that created the Stmt + stickyErr error // if non-nil, this error is returned for all operations + + // If in a transaction, else both nil: + tx *Tx + txsi driver.Stmt + + mu sync.Mutex // protects the rest of the fields + closed bool + + // css is a list of underlying driver statement interfaces + // that are valid on particular connections. This is only + // used if tx == nil and one is found that has idle + // connections. If tx != nil, txsi is always used. + css []connStmt +} + +// Exec executes a prepared statement with the given arguments and +// returns a Result summarizing the effect of the statement. +func (s *Stmt) Exec(args ...interface{}) (Result, error) { + _, releaseConn, si, err := s.connStmt() + if err != nil { + return nil, err + } + defer releaseConn(nil) + + // -1 means the driver doesn't know how to count the number of + // placeholders, so we won't sanity check input here and instead let the + // driver deal with errors. + if want := si.NumInput(); want != -1 && len(args) != want { + return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args)) + } + + sargs := make([]driver.Value, len(args)) + + // Convert args to subset types. + if cc, ok := si.(driver.ColumnConverter); ok { + for n, arg := range args { + // First, see if the value itself knows how to convert + // itself to a driver type. For example, a NullString + // struct changing into a string or nil. + if svi, ok := arg.(driver.Valuer); ok { + sv, err := svi.Value() + if err != nil { + return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err) + } + if !driver.IsValue(sv) { + return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv) + } + arg = sv + } + + // Second, ask the column to sanity check itself. For + // example, drivers might use this to make sure that + // an int64 values being inserted into a 16-bit + // integer field is in range (before getting + // truncated), or that a nil can't go into a NOT NULL + // column before going across the network to get the + // same error. + sargs[n], err = cc.ColumnConverter(n).ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) + } + if !driver.IsValue(sargs[n]) { + return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T", + arg, sargs[n]) + } + } + } else { + for n, arg := range args { + sargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) + } + } + } + + resi, err := si.Exec(sargs) + if err != nil { + return nil, err + } + return result{resi}, nil +} + +// connStmt returns a free driver connection on which to execute the +// statement, a function to call to release the connection, and a +// statement bound to that connection. +func (s *Stmt) connStmt() (ci driver.Conn, releaseConn func(error), si driver.Stmt, err error) { + if err = s.stickyErr; err != nil { + return + } + s.mu.Lock() + if s.closed { + s.mu.Unlock() + err = errors.New("sql: statement is closed") + return + } + + // In a transaction, we always use the connection that the + // transaction was created on. + if s.tx != nil { + s.mu.Unlock() + ci, err = s.tx.grabConn() // blocks, waiting for the connection. + if err != nil { + return + } + releaseConn = func(error) { s.tx.releaseConn() } + return ci, releaseConn, s.txsi, nil + } + + var cs connStmt + match := false + for _, v := range s.css { + // TODO(bradfitz): lazily clean up entries in this + // list with dead conns while enumerating + if _, match = s.db.connIfFree(v.ci); match { + cs = v + break + } + } + s.mu.Unlock() + + // Make a new conn if all are busy. + // TODO(bradfitz): or wait for one? make configurable later? + if !match { + for i := 0; ; i++ { + ci, err := s.db.conn() + if err != nil { + return nil, nil, nil, err + } + si, err := ci.Prepare(s.query) + if err == driver.ErrBadConn && i < 10 { + continue + } + if err != nil { + return nil, nil, nil, err + } + s.mu.Lock() + cs = connStmt{ci, si} + s.css = append(s.css, cs) + s.mu.Unlock() + break + } + } + + conn := cs.ci + releaseConn = func(err error) { s.db.putConn(conn, err) } + return conn, releaseConn, cs.si, nil +} + +// Query executes a prepared query statement with the given arguments +// and returns the query results as a *Rows. +func (s *Stmt) Query(args ...interface{}) (*Rows, error) { + ci, releaseConn, si, err := s.connStmt() + if err != nil { + return nil, err + } + + // -1 means the driver doesn't know how to count the number of + // placeholders, so we won't sanity check input here and instead let the + // driver deal with errors. + if want := si.NumInput(); want != -1 && len(args) != want { + return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", si.NumInput(), len(args)) + } + sargs, err := subsetTypeArgs(args) + if err != nil { + return nil, err + } + rowsi, err := si.Query(sargs) + if err != nil { + releaseConn(err) + return nil, err + } + // Note: ownership of ci passes to the *Rows, to be freed + // with releaseConn. + rows := &Rows{ + db: s.db, + ci: ci, + releaseConn: releaseConn, + rowsi: rowsi, + } + return rows, nil +} + +// QueryRow executes a prepared query statement with the given arguments. +// If an error occurs during the execution of the statement, that error will +// be returned by a call to Scan on the returned *Row, which is always non-nil. +// If the query selects no rows, the *Row's Scan will return ErrNoRows. +// Otherwise, the *Row's Scan scans the first selected row and discards +// the rest. +// +// Example usage: +// +// var name string +// err := nameByUseridStmt.QueryRow(id).Scan(&name) +func (s *Stmt) QueryRow(args ...interface{}) *Row { + rows, err := s.Query(args...) + if err != nil { + return &Row{err: err} + } + return &Row{rows: rows} +} + +// Close closes the statement. +func (s *Stmt) Close() error { + if s.stickyErr != nil { + return s.stickyErr + } + s.mu.Lock() + defer s.mu.Unlock() + if s.closed { + return nil + } + s.closed = true + + if s.tx != nil { + s.txsi.Close() + } else { + for _, v := range s.css { + if ci, match := s.db.connIfFree(v.ci); match { + v.si.Close() + s.db.putConn(ci, nil) + } else { + // TODO(bradfitz): care that we can't close + // this statement because the statement's + // connection is in use? + } + } + } + return nil +} + +// Rows is the result of a query. Its cursor starts before the first row +// of the result set. Use Next to advance through the rows: +// +// rows, err := db.Query("SELECT ...") +// ... +// for rows.Next() { +// var id int +// var name string +// err = rows.Scan(&id, &name) +// ... +// } +// err = rows.Err() // get any error encountered during iteration +// ... +type Rows struct { + db *DB + ci driver.Conn // owned; must call putconn when closed to release + releaseConn func(error) + rowsi driver.Rows + + closed bool + lastcols []driver.Value + lasterr error + closeStmt *Stmt // if non-nil, statement to Close on close +} + +// Next prepares the next result row for reading with the Scan method. +// It returns true on success, false if there is no next result row. +// Every call to Scan, even the first one, must be preceded by a call +// to Next. +func (rs *Rows) Next() bool { + if rs.closed { + return false + } + if rs.lasterr != nil { + return false + } + if rs.lastcols == nil { + rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns())) + } + rs.lasterr = rs.rowsi.Next(rs.lastcols) + if rs.lasterr == io.EOF { + rs.Close() + } + return rs.lasterr == nil +} + +// Err returns the error, if any, that was encountered during iteration. +func (rs *Rows) Err() error { + if rs.lasterr == io.EOF { + return nil + } + return rs.lasterr +} + +// Columns returns the column names. +// Columns returns an error if the rows are closed, or if the rows +// are from QueryRow and there was a deferred error. +func (rs *Rows) Columns() ([]string, error) { + if rs.closed { + return nil, errors.New("sql: Rows are closed") + } + if rs.rowsi == nil { + return nil, errors.New("sql: no Rows available") + } + return rs.rowsi.Columns(), nil +} + +// Scan copies the columns in the current row into the values pointed +// at by dest. +// +// If an argument has type *[]byte, Scan saves in that argument a copy +// of the corresponding data. The copy is owned by the caller and can +// be modified and held indefinitely. The copy can be avoided by using +// an argument of type *RawBytes instead; see the documentation for +// RawBytes for restrictions on its use. +// +// If an argument has type *interface{}, Scan copies the value +// provided by the underlying driver without conversion. If the value +// is of type []byte, a copy is made and the caller owns the result. +func (rs *Rows) Scan(dest ...interface{}) error { + if rs.closed { + return errors.New("sql: Rows closed") + } + if rs.lasterr != nil { + return rs.lasterr + } + if rs.lastcols == nil { + return errors.New("sql: Scan called without calling Next") + } + if len(dest) != len(rs.lastcols) { + return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest)) + } + for i, sv := range rs.lastcols { + err := convertAssign(dest[i], sv) + if err != nil { + return fmt.Errorf("sql: Scan error on column index %d: %v", i, err) + } + } + for _, dp := range dest { + b, ok := dp.(*[]byte) + if !ok { + continue + } + if *b == nil { + // If the []byte is now nil (for a NULL value), + // don't fall through to below which would + // turn it into a non-nil 0-length byte slice + continue + } + if _, ok = dp.(*RawBytes); ok { + continue + } + clone := make([]byte, len(*b)) + copy(clone, *b) + *b = clone + } + return nil +} + +// Close closes the Rows, preventing further enumeration. If the +// end is encountered, the Rows are closed automatically. Close +// is idempotent. +func (rs *Rows) Close() error { + if rs.closed { + return nil + } + rs.closed = true + err := rs.rowsi.Close() + rs.releaseConn(err) + if rs.closeStmt != nil { + rs.closeStmt.Close() + } + return err +} + +// Row is the result of calling QueryRow to select a single row. +type Row struct { + // One of these two will be non-nil: + err error // deferred error for easy chaining + rows *Rows +} + +// Scan copies the columns from the matched row into the values +// pointed at by dest. If more than one row matches the query, +// Scan uses the first row and discards the rest. If no row matches +// the query, Scan returns ErrNoRows. +func (r *Row) Scan(dest ...interface{}) error { + if r.err != nil { + return r.err + } + + // TODO(bradfitz): for now we need to defensively clone all + // []byte that the driver returned (not permitting + // *RawBytes in Rows.Scan), since we're about to close + // the Rows in our defer, when we return from this function. + // the contract with the driver.Next(...) interface is that it + // can return slices into read-only temporary memory that's + // only valid until the next Scan/Close. But the TODO is that + // for a lot of drivers, this copy will be unnecessary. We + // should provide an optional interface for drivers to + // implement to say, "don't worry, the []bytes that I return + // from Next will not be modified again." (for instance, if + // they were obtained from the network anyway) But for now we + // don't care. + for _, dp := range dest { + if _, ok := dp.(*RawBytes); ok { + return errors.New("sql: RawBytes isn't allowed on Row.Scan") + } + } + + defer r.rows.Close() + if !r.rows.Next() { + return ErrNoRows + } + err := r.rows.Scan(dest...) + if err != nil { + return err + } + + return nil +} + +// A Result summarizes an executed SQL command. +type Result interface { + LastInsertId() (int64, error) + RowsAffected() (int64, error) +} + +type result struct { + driver.Result +} diff --git a/src/pkg/database/sql/sql_test.go b/src/pkg/database/sql/sql_test.go new file mode 100644 index 000000000..b29670586 --- /dev/null +++ b/src/pkg/database/sql/sql_test.go @@ -0,0 +1,614 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sql + +import ( + "database/sql/driver" + "fmt" + "reflect" + "runtime" + "strings" + "testing" + "time" +) + +func init() { + type dbConn struct { + db *DB + c driver.Conn + } + freedFrom := make(map[dbConn]string) + putConnHook = func(db *DB, c driver.Conn) { + for _, oc := range db.freeConn { + if oc == c { + // print before panic, as panic may get lost due to conflicting panic + // (all goroutines asleep) elsewhere, since we might not unlock + // the mutex in freeConn here. + println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack()) + panic("double free of conn.") + } + } + freedFrom[dbConn{db, c}] = stack() + } +} + +const fakeDBName = "foo" + +var chrisBirthday = time.Unix(123456789, 0) + +func newTestDB(t *testing.T, name string) *DB { + db, err := Open("test", fakeDBName) + if err != nil { + t.Fatalf("Open: %v", err) + } + if _, err := db.Exec("WIPE"); err != nil { + t.Fatalf("exec wipe: %v", err) + } + if name == "people" { + exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool,bdate=datetime") + exec(t, db, "INSERT|people|name=Alice,age=?,photo=APHOTO", 1) + exec(t, db, "INSERT|people|name=Bob,age=?,photo=BPHOTO", 2) + exec(t, db, "INSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday) + } + return db +} + +func exec(t *testing.T, db *DB, query string, args ...interface{}) { + _, err := db.Exec(query, args...) + if err != nil { + t.Fatalf("Exec of %q: %v", query, err) + } +} + +func closeDB(t *testing.T, db *DB) { + err := db.Close() + if err != nil { + t.Fatalf("error closing DB: %v", err) + } +} + +// numPrepares assumes that db has exactly 1 idle conn and returns +// its count of calls to Prepare +func numPrepares(t *testing.T, db *DB) int { + if n := len(db.freeConn); n != 1 { + t.Fatalf("free conns = %d; want 1", n) + } + return db.freeConn[0].(*fakeConn).numPrepare +} + +func TestQuery(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + prepares0 := numPrepares(t, db) + rows, err := db.Query("SELECT|people|age,name|") + if err != nil { + t.Fatalf("Query: %v", err) + } + type row struct { + age int + name string + } + got := []row{} + for rows.Next() { + var r row + err = rows.Scan(&r.age, &r.name) + if err != nil { + t.Fatalf("Scan: %v", err) + } + got = append(got, r) + } + err = rows.Err() + if err != nil { + t.Fatalf("Err: %v", err) + } + want := []row{ + {age: 1, name: "Alice"}, + {age: 2, name: "Bob"}, + {age: 3, name: "Chris"}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want) + } + + // And verify that the final rows.Next() call, which hit EOF, + // also closed the rows connection. + if n := len(db.freeConn); n != 1 { + t.Fatalf("free conns after query hitting EOF = %d; want 1", n) + } + if prepares := numPrepares(t, db) - prepares0; prepares != 1 { + t.Errorf("executed %d Prepare statements; want 1", prepares) + } +} + +func TestByteOwnership(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + rows, err := db.Query("SELECT|people|name,photo|") + if err != nil { + t.Fatalf("Query: %v", err) + } + type row struct { + name []byte + photo RawBytes + } + got := []row{} + for rows.Next() { + var r row + err = rows.Scan(&r.name, &r.photo) + if err != nil { + t.Fatalf("Scan: %v", err) + } + got = append(got, r) + } + corruptMemory := []byte("\xffPHOTO") + want := []row{ + {name: []byte("Alice"), photo: corruptMemory}, + {name: []byte("Bob"), photo: corruptMemory}, + {name: []byte("Chris"), photo: corruptMemory}, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want) + } + + var photo RawBytes + err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo) + if err == nil { + t.Error("want error scanning into RawBytes from QueryRow") + } +} + +func TestRowsColumns(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + rows, err := db.Query("SELECT|people|age,name|") + if err != nil { + t.Fatalf("Query: %v", err) + } + cols, err := rows.Columns() + if err != nil { + t.Fatalf("Columns: %v", err) + } + want := []string{"age", "name"} + if !reflect.DeepEqual(cols, want) { + t.Errorf("got %#v; want %#v", cols, want) + } +} + +func TestQueryRow(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + var name string + var age int + var birthday time.Time + + err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age) + if err == nil || !strings.Contains(err.Error(), "expected 2 destination arguments") { + t.Errorf("expected error from wrong number of arguments; actually got: %v", err) + } + + err = db.QueryRow("SELECT|people|bdate|age=?", 3).Scan(&birthday) + if err != nil || !birthday.Equal(chrisBirthday) { + t.Errorf("chris birthday = %v, err = %v; want %v", birthday, err, chrisBirthday) + } + + err = db.QueryRow("SELECT|people|age,name|age=?", 2).Scan(&age, &name) + if err != nil { + t.Fatalf("age QueryRow+Scan: %v", err) + } + if name != "Bob" { + t.Errorf("expected name Bob, got %q", name) + } + if age != 2 { + t.Errorf("expected age 2, got %d", age) + } + + err = db.QueryRow("SELECT|people|age,name|name=?", "Alice").Scan(&age, &name) + if err != nil { + t.Fatalf("name QueryRow+Scan: %v", err) + } + if name != "Alice" { + t.Errorf("expected name Alice, got %q", name) + } + if age != 1 { + t.Errorf("expected age 1, got %d", age) + } + + var photo []byte + err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo) + if err != nil { + t.Fatalf("photo QueryRow+Scan: %v", err) + } + want := []byte("APHOTO") + if !reflect.DeepEqual(photo, want) { + t.Errorf("photo = %q; want %q", photo, want) + } +} + +func TestStatementErrorAfterClose(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + stmt, err := db.Prepare("SELECT|people|age|name=?") + if err != nil { + t.Fatalf("Prepare: %v", err) + } + err = stmt.Close() + if err != nil { + t.Fatalf("Close: %v", err) + } + var name string + err = stmt.QueryRow("foo").Scan(&name) + if err == nil { + t.Errorf("expected error from QueryRow.Scan after Stmt.Close") + } +} + +func TestStatementQueryRow(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + stmt, err := db.Prepare("SELECT|people|age|name=?") + if err != nil { + t.Fatalf("Prepare: %v", err) + } + defer stmt.Close() + var age int + for n, tt := range []struct { + name string + want int + }{ + {"Alice", 1}, + {"Bob", 2}, + {"Chris", 3}, + } { + if err := stmt.QueryRow(tt.name).Scan(&age); err != nil { + t.Errorf("%d: on %q, QueryRow/Scan: %v", n, tt.name, err) + } else if age != tt.want { + t.Errorf("%d: age=%d, want %d", n, age, tt.want) + } + } + +} + +// just a test of fakedb itself +func TestBogusPreboundParameters(t *testing.T) { + db := newTestDB(t, "foo") + defer closeDB(t, db) + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + _, err := db.Prepare("INSERT|t1|name=?,age=bogusconversion") + if err == nil { + t.Fatalf("expected error") + } + if err.Error() != `fakedb: invalid conversion to int32 from "bogusconversion"` { + t.Errorf("unexpected error: %v", err) + } +} + +func TestExec(t *testing.T) { + db := newTestDB(t, "foo") + defer closeDB(t, db) + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + stmt, err := db.Prepare("INSERT|t1|name=?,age=?") + if err != nil { + t.Errorf("Stmt, err = %v, %v", stmt, err) + } + defer stmt.Close() + + type execTest struct { + args []interface{} + wantErr string + } + execTests := []execTest{ + // Okay: + {[]interface{}{"Brad", 31}, ""}, + {[]interface{}{"Brad", int64(31)}, ""}, + {[]interface{}{"Bob", "32"}, ""}, + {[]interface{}{7, 9}, ""}, + + // Invalid conversions: + {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"}, + {[]interface{}{"Brad", "strconv fail"}, "sql: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"}, + + // Wrong number of args: + {[]interface{}{}, "sql: expected 2 arguments, got 0"}, + {[]interface{}{1, 2, 3}, "sql: expected 2 arguments, got 3"}, + } + for n, et := range execTests { + _, err := stmt.Exec(et.args...) + errStr := "" + if err != nil { + errStr = err.Error() + } + if errStr != et.wantErr { + t.Errorf("stmt.Execute #%d: for %v, got error %q, want error %q", + n, et.args, errStr, et.wantErr) + } + } +} + +func TestTxStmt(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + stmt, err := db.Prepare("INSERT|t1|name=?,age=?") + if err != nil { + t.Fatalf("Stmt, err = %v, %v", stmt, err) + } + defer stmt.Close() + tx, err := db.Begin() + if err != nil { + t.Fatalf("Begin = %v", err) + } + txs := tx.Stmt(stmt) + defer txs.Close() + _, err = txs.Exec("Bobby", 7) + if err != nil { + t.Fatalf("Exec = %v", err) + } + err = tx.Commit() + if err != nil { + t.Fatalf("Commit = %v", err) + } +} + +// Issue: http://golang.org/issue/2784 +// This test didn't fail before because we got luckly with the fakedb driver. +// It was failing, and now not, in github.com/bradfitz/go-sql-test +func TestTxQuery(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") + exec(t, db, "INSERT|t1|name=Alice") + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer tx.Rollback() + + r, err := tx.Query("SELECT|t1|name|") + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if !r.Next() { + if r.Err() != nil { + t.Fatal(r.Err()) + } + t.Fatal("expected one row") + } + + var x string + err = r.Scan(&x) + if err != nil { + t.Fatal(err) + } +} + +func TestTxQueryInvalid(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer tx.Rollback() + + _, err = tx.Query("SELECT|t1|name|") + if err == nil { + t.Fatal("Error expected") + } +} + +// Tests fix for issue 2542, that we release a lock when querying on +// a closed connection. +func TestIssue2542Deadlock(t *testing.T) { + db := newTestDB(t, "people") + closeDB(t, db) + for i := 0; i < 2; i++ { + _, err := db.Query("SELECT|people|age,name|") + if err == nil { + t.Fatalf("expected error") + } + } +} + +// Tests fix for issue 2788, that we bind nil to a []byte if the +// value in the column is sql null +func TestNullByteSlice(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, "CREATE|t|id=int32,name=nullstring") + exec(t, db, "INSERT|t|id=10,name=?", nil) + + var name []byte + + err := db.QueryRow("SELECT|t|name|id=?", 10).Scan(&name) + if err != nil { + t.Fatal(err) + } + if name != nil { + t.Fatalf("name []byte should be nil for null column value, got: %#v", name) + } + + exec(t, db, "INSERT|t|id=11,name=?", "bob") + err = db.QueryRow("SELECT|t|name|id=?", 11).Scan(&name) + if err != nil { + t.Fatal(err) + } + if string(name) != "bob" { + t.Fatalf("name []byte should be bob, got: %q", string(name)) + } +} + +func TestPointerParamsAndScans(t *testing.T) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, "CREATE|t|id=int32,name=nullstring") + + bob := "bob" + var name *string + + name = &bob + exec(t, db, "INSERT|t|id=10,name=?", name) + name = nil + exec(t, db, "INSERT|t|id=20,name=?", name) + + err := db.QueryRow("SELECT|t|name|id=?", 10).Scan(&name) + if err != nil { + t.Fatalf("querying id 10: %v", err) + } + if name == nil { + t.Errorf("id 10's name = nil; want bob") + } else if *name != "bob" { + t.Errorf("id 10's name = %q; want bob", *name) + } + + err = db.QueryRow("SELECT|t|name|id=?", 20).Scan(&name) + if err != nil { + t.Fatalf("querying id 20: %v", err) + } + if name != nil { + t.Errorf("id 20 = %q; want nil", *name) + } +} + +func TestQueryRowClosingStmt(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + var name string + var age int + err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age, &name) + if err != nil { + t.Fatal(err) + } + if len(db.freeConn) != 1 { + t.Fatalf("expected 1 free conn") + } + fakeConn := db.freeConn[0].(*fakeConn) + if made, closed := fakeConn.stmtsMade, fakeConn.stmtsClosed; made != closed { + t.Errorf("statement close mismatch: made %d, closed %d", made, closed) + } +} + +type nullTestRow struct { + nullParam interface{} + notNullParam interface{} + scanNullVal interface{} +} + +type nullTestSpec struct { + nullType string + notNullType string + rows [6]nullTestRow +} + +func TestNullStringParam(t *testing.T) { + spec := nullTestSpec{"nullstring", "string", [6]nullTestRow{ + {NullString{"aqua", true}, "", NullString{"aqua", true}}, + {NullString{"brown", false}, "", NullString{"", false}}, + {"chartreuse", "", NullString{"chartreuse", true}}, + {NullString{"darkred", true}, "", NullString{"darkred", true}}, + {NullString{"eel", false}, "", NullString{"", false}}, + {"foo", NullString{"black", false}, nil}, + }} + nullTestRun(t, spec) +} + +func TestNullInt64Param(t *testing.T) { + spec := nullTestSpec{"nullint64", "int64", [6]nullTestRow{ + {NullInt64{31, true}, 1, NullInt64{31, true}}, + {NullInt64{-22, false}, 1, NullInt64{0, false}}, + {22, 1, NullInt64{22, true}}, + {NullInt64{33, true}, 1, NullInt64{33, true}}, + {NullInt64{222, false}, 1, NullInt64{0, false}}, + {0, NullInt64{31, false}, nil}, + }} + nullTestRun(t, spec) +} + +func TestNullFloat64Param(t *testing.T) { + spec := nullTestSpec{"nullfloat64", "float64", [6]nullTestRow{ + {NullFloat64{31.2, true}, 1, NullFloat64{31.2, true}}, + {NullFloat64{13.1, false}, 1, NullFloat64{0, false}}, + {-22.9, 1, NullFloat64{-22.9, true}}, + {NullFloat64{33.81, true}, 1, NullFloat64{33.81, true}}, + {NullFloat64{222, false}, 1, NullFloat64{0, false}}, + {10, NullFloat64{31.2, false}, nil}, + }} + nullTestRun(t, spec) +} + +func TestNullBoolParam(t *testing.T) { + spec := nullTestSpec{"nullbool", "bool", [6]nullTestRow{ + {NullBool{false, true}, true, NullBool{false, true}}, + {NullBool{true, false}, false, NullBool{false, false}}, + {true, true, NullBool{true, true}}, + {NullBool{true, true}, false, NullBool{true, true}}, + {NullBool{true, false}, true, NullBool{false, false}}, + {true, NullBool{true, false}, nil}, + }} + nullTestRun(t, spec) +} + +func nullTestRun(t *testing.T, spec nullTestSpec) { + db := newTestDB(t, "") + defer closeDB(t, db) + exec(t, db, fmt.Sprintf("CREATE|t|id=int32,name=string,nullf=%s,notnullf=%s", spec.nullType, spec.notNullType)) + + // Inserts with db.Exec: + exec(t, db, "INSERT|t|id=?,name=?,nullf=?,notnullf=?", 1, "alice", spec.rows[0].nullParam, spec.rows[0].notNullParam) + exec(t, db, "INSERT|t|id=?,name=?,nullf=?,notnullf=?", 2, "bob", spec.rows[1].nullParam, spec.rows[1].notNullParam) + + // Inserts with a prepared statement: + stmt, err := db.Prepare("INSERT|t|id=?,name=?,nullf=?,notnullf=?") + if err != nil { + t.Fatalf("prepare: %v", err) + } + defer stmt.Close() + if _, err := stmt.Exec(3, "chris", spec.rows[2].nullParam, spec.rows[2].notNullParam); err != nil { + t.Errorf("exec insert chris: %v", err) + } + if _, err := stmt.Exec(4, "dave", spec.rows[3].nullParam, spec.rows[3].notNullParam); err != nil { + t.Errorf("exec insert dave: %v", err) + } + if _, err := stmt.Exec(5, "eleanor", spec.rows[4].nullParam, spec.rows[4].notNullParam); err != nil { + t.Errorf("exec insert eleanor: %v", err) + } + + // Can't put null val into non-null col + if _, err := stmt.Exec(6, "bob", spec.rows[5].nullParam, spec.rows[5].notNullParam); err == nil { + t.Errorf("expected error inserting nil val with prepared statement Exec") + } + + _, err = db.Exec("INSERT|t|id=?,name=?,nullf=?", 999, nil, nil) + if err == nil { + // TODO: this test fails, but it's just because + // fakeConn implements the optional Execer interface, + // so arguably this is the correct behavior. But + // maybe I should flesh out the fakeConn.Exec + // implementation so this properly fails. + // t.Errorf("expected error inserting nil name with Exec") + } + + paramtype := reflect.TypeOf(spec.rows[0].nullParam) + bindVal := reflect.New(paramtype).Interface() + + for i := 0; i < 5; i++ { + id := i + 1 + if err := db.QueryRow("SELECT|t|nullf|id=?", id).Scan(bindVal); err != nil { + t.Errorf("id=%d Scan: %v", id, err) + } + bindValDeref := reflect.ValueOf(bindVal).Elem().Interface() + if !reflect.DeepEqual(bindValDeref, spec.rows[i].scanNullVal) { + t.Errorf("id=%d got %#v, want %#v", id, bindValDeref, spec.rows[i].scanNullVal) + } + } +} + +func stack() string { + buf := make([]byte, 1024) + return string(buf[:runtime.Stack(buf, false)]) +} diff --git a/src/pkg/debug/dwarf/Makefile b/src/pkg/debug/dwarf/Makefile deleted file mode 100644 index c4203188e..000000000 --- a/src/pkg/debug/dwarf/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=debug/dwarf -GOFILES=\ - buf.go\ - const.go\ - entry.go\ - open.go\ - type.go\ - unit.go\ - -include ../../../Make.pkg diff --git a/src/pkg/debug/dwarf/buf.go b/src/pkg/debug/dwarf/buf.go index 2d29cebdd..6dc28d256 100644 --- a/src/pkg/debug/dwarf/buf.go +++ b/src/pkg/debug/dwarf/buf.go @@ -8,7 +8,6 @@ package dwarf import ( "encoding/binary" - "os" "strconv" ) @@ -20,7 +19,7 @@ type buf struct { off Offset data []byte addrsize int - err os.Error + err error } func makeBuf(d *Data, name string, off Offset, data []byte, addrsize int) buf { @@ -146,9 +145,9 @@ func (b *buf) error(s string) { type DecodeError struct { Name string Offset Offset - Error string + Err string } -func (e DecodeError) String() string { - return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.Itob64(int64(e.Offset), 16) + ": " + e.Error +func (e DecodeError) Error() string { + return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.FormatInt(int64(e.Offset), 16) + ": " + e.Err } diff --git a/src/pkg/debug/dwarf/const.go b/src/pkg/debug/dwarf/const.go index 1a3fec155..918b153d0 100644 --- a/src/pkg/debug/dwarf/const.go +++ b/src/pkg/debug/dwarf/const.go @@ -178,7 +178,7 @@ func (a Attr) GoString() string { return "dwarf.Attr" + s } } - return "dwarf.Attr(" + strconv.Itoa64(int64(a)) + ")" + return "dwarf.Attr(" + strconv.FormatInt(int64(a), 10) + ")" } // A format is a DWARF data encoding format. @@ -347,7 +347,7 @@ func (t Tag) GoString() string { return "dwarf.Tag" + s } } - return "dwarf.Tag(" + strconv.Itoa64(int64(t)) + ")" + return "dwarf.Tag(" + strconv.FormatInt(int64(t), 10) + ")" } // Location expression operators. diff --git a/src/pkg/debug/dwarf/entry.go b/src/pkg/debug/dwarf/entry.go index 549e5c2cc..2885d8fa2 100644 --- a/src/pkg/debug/dwarf/entry.go +++ b/src/pkg/debug/dwarf/entry.go @@ -10,7 +10,7 @@ package dwarf -import "os" +import "errors" // a single entry's description: a sequence of attributes type abbrev struct { @@ -29,7 +29,7 @@ type abbrevTable map[uint32]abbrev // ParseAbbrev returns the abbreviation table that starts at byte off // in the .debug_abbrev section. -func (d *Data) parseAbbrev(off uint32) (abbrevTable, os.Error) { +func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) { if m, ok := d.abbrevCache[off]; ok { return m, nil } @@ -232,7 +232,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { type Reader struct { b buf d *Data - err os.Error + err error unit int lastChildren bool // .Children of last entry returned by Next lastSibling Offset // .Val(AttrSibling) of last entry returned by Next @@ -273,7 +273,7 @@ func (r *Reader) Seek(off Offset) { return } } - r.err = os.NewError("offset out of range") + r.err = errors.New("offset out of range") } // maybeNextUnit advances to the next unit if this one is finished. @@ -289,7 +289,7 @@ func (r *Reader) maybeNextUnit() { // It returns nil, nil when it reaches the end of the section. // It returns an error if the current offset is invalid or the data at the // offset cannot be decoded as a valid Entry. -func (r *Reader) Next() (*Entry, os.Error) { +func (r *Reader) Next() (*Entry, error) { if r.err != nil { return nil, r.err } diff --git a/src/pkg/debug/dwarf/open.go b/src/pkg/debug/dwarf/open.go index d9525f788..37a518b6d 100644 --- a/src/pkg/debug/dwarf/open.go +++ b/src/pkg/debug/dwarf/open.go @@ -7,10 +7,7 @@ // http://dwarfstd.org/doc/dwarf-2.0.0.pdf package dwarf -import ( - "encoding/binary" - "os" -) +import "encoding/binary" // Data represents the DWARF debugging information // loaded from an executable file (for example, an ELF or Mach-O executable). @@ -34,13 +31,14 @@ type Data struct { } // New returns a new Data object initialized from the given parameters. -// Clients should typically use [TODO(rsc): method to be named later] instead of calling -// New directly. +// Rather than calling this function directly, clients should typically use +// the DWARF method of the File type of the appropriate package debug/elf, +// debug/macho, or debug/pe. // // The []byte arguments are the data from the corresponding debug section // in the object file; for example, for an ELF object, abbrev is the contents of // the ".debug_abbrev" section. -func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, os.Error) { +func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, error) { d := &Data{ abbrev: abbrev, aranges: aranges, diff --git a/src/pkg/debug/dwarf/testdata/typedef.c b/src/pkg/debug/dwarf/testdata/typedef.c index 664d021ce..f05f01564 100644 --- a/src/pkg/debug/dwarf/testdata/typedef.c +++ b/src/pkg/debug/dwarf/testdata/typedef.c @@ -28,8 +28,13 @@ typedef struct my_struct { volatile int vi; char x : 1; int y : 4; + int z[0]; long long array[40]; + int zz[0]; } t_my_struct; +typedef struct my_struct1 { + int zz [1]; +} t_my_struct1; typedef union my_union { volatile int vi; char x : 1; @@ -65,7 +70,8 @@ t_func_void_of_char *a9; t_func_void_of_void *a10; t_func_void_of_ptr_char_dots *a11; t_my_struct *a12; -t_my_union *a12a; +t_my_struct1 *a12a; +t_my_union *a12b; t_my_enum *a13; t_my_list *a14; t_my_tree *a15; diff --git a/src/pkg/debug/dwarf/testdata/typedef.elf b/src/pkg/debug/dwarf/testdata/typedef.elf index 44df8da9b..b2062d2c4 100755 Binary files a/src/pkg/debug/dwarf/testdata/typedef.elf and b/src/pkg/debug/dwarf/testdata/typedef.elf differ diff --git a/src/pkg/debug/dwarf/testdata/typedef.macho b/src/pkg/debug/dwarf/testdata/typedef.macho index 41019c1e1..f75afcccb 100644 Binary files a/src/pkg/debug/dwarf/testdata/typedef.macho and b/src/pkg/debug/dwarf/testdata/typedef.macho differ diff --git a/src/pkg/debug/dwarf/type.go b/src/pkg/debug/dwarf/type.go index f35365ebe..450235502 100644 --- a/src/pkg/debug/dwarf/type.go +++ b/src/pkg/debug/dwarf/type.go @@ -8,10 +8,7 @@ package dwarf -import ( - "os" - "strconv" -) +import "strconv" // A Type conventionally represents a pointer to any of the // specific Type structures (CharType, StructType, etc.). @@ -113,7 +110,7 @@ type ArrayType struct { } func (t *ArrayType) String() string { - return "[" + strconv.Itoa64(t.Count) + "]" + t.Type.String() + return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String() } func (t *ArrayType) Size() int64 { return t.Count * t.Type.Size() } @@ -174,10 +171,10 @@ func (t *StructType) Defn() string { s += "; " } s += f.Name + " " + f.Type.String() - s += "@" + strconv.Itoa64(f.ByteOffset) + s += "@" + strconv.FormatInt(f.ByteOffset, 10) if f.BitSize > 0 { - s += " : " + strconv.Itoa64(f.BitSize) - s += "@" + strconv.Itoa64(f.BitOffset) + s += " : " + strconv.FormatInt(f.BitSize, 10) + s += "@" + strconv.FormatInt(f.BitOffset, 10) } } s += "}" @@ -209,7 +206,7 @@ func (t *EnumType) String() string { if i > 0 { s += "; " } - s += v.Name + "=" + strconv.Itoa64(v.Val) + s += v.Name + "=" + strconv.FormatInt(v.Val, 10) } s += "}" return s @@ -254,7 +251,7 @@ func (t *TypedefType) String() string { return t.Name } func (t *TypedefType) Size() int64 { return t.Type.Size() } -func (d *Data) Type(off Offset) (Type, os.Error) { +func (d *Data) Type(off Offset) (Type, error) { if t, ok := d.typeCache[off]; ok { return t, nil } @@ -429,6 +426,8 @@ func (d *Data) Type(off Offset) (Type, os.Error) { t.StructName, _ = e.Val(AttrName).(string) t.Incomplete = e.Val(AttrDeclaration) != nil t.Field = make([]*StructField, 0, 8) + var lastFieldType Type + var lastFieldBitOffset int64 for kid := next(); kid != nil; kid = next() { if kid.Tag == TagMember { f := new(StructField) @@ -447,11 +446,32 @@ func (d *Data) Type(off Offset) (Type, os.Error) { goto Error } } + + haveBitOffset := false f.Name, _ = kid.Val(AttrName).(string) f.ByteSize, _ = kid.Val(AttrByteSize).(int64) - f.BitOffset, _ = kid.Val(AttrBitOffset).(int64) + f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64) f.BitSize, _ = kid.Val(AttrBitSize).(int64) t.Field = append(t.Field, f) + + bito := f.BitOffset + if !haveBitOffset { + bito = f.ByteOffset * 8 + } + if bito == lastFieldBitOffset && t.Kind != "union" { + // Last field was zero width. Fix array length. + // (DWARF writes out 0-length arrays as if they were 1-length arrays.) + zeroArray(lastFieldType) + } + lastFieldType = f.Type + lastFieldBitOffset = bito + } + } + if t.Kind != "union" { + b, ok := e.Val(AttrByteSize).(int64) + if ok && b*8 == lastFieldBitOffset { + // Final field must be zero width. Fix array length. + zeroArray(lastFieldType) } } @@ -579,6 +599,17 @@ Error: // If the parse fails, take the type out of the cache // so that the next call with this offset doesn't hit // the cache and return success. - d.typeCache[off] = nil, false + delete(d.typeCache, off) return nil, err } + +func zeroArray(t Type) { + for { + at, ok := t.(*ArrayType) + if !ok { + break + } + at.Count = 0 + t = at.Type + } +} diff --git a/src/pkg/debug/dwarf/type_test.go b/src/pkg/debug/dwarf/type_test.go index b9470a4fc..b5b255f6f 100644 --- a/src/pkg/debug/dwarf/type_test.go +++ b/src/pkg/debug/dwarf/type_test.go @@ -25,13 +25,22 @@ var typedefTests = map[string]string{ "t_func_void_of_char": "func(char) void", "t_func_void_of_void": "func() void", "t_func_void_of_ptr_char_dots": "func(*char, ...) void", - "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; array [40]long long int@8}", + "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; z [0]int@8; array [40]long long int@8; zz [0]int@328}", + "t_my_struct1": "struct my_struct1 {zz [1]int@0}", "t_my_union": "union my_union {vi volatile int@0; x char@0 : 1@7; y int@0 : 4@28; array [40]long long int@0}", "t_my_enum": "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}", "t_my_list": "struct list {val short int@0; next *t_my_list@8}", "t_my_tree": "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}", } +// As Apple converts gcc to a clang-based front end +// they keep breaking the DWARF output. This map lists the +// conversion from real answer to Apple answer. +var machoBug = map[string]string{ + "func(*char, ...) void": "func(*char) void", + "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}": "enum my_enum {e1=1; e2=2; e3=-5; e4=-1530494976}", +} + func elfData(t *testing.T, name string) *Data { f, err := elf.Open(name) if err != nil { @@ -58,13 +67,13 @@ func machoData(t *testing.T, name string) *Data { return d } -func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf")) } +func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf") } func TestTypedefsMachO(t *testing.T) { - testTypedefs(t, machoData(t, "testdata/typedef.macho")) + testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho") } -func testTypedefs(t *testing.T, d *Data) { +func testTypedefs(t *testing.T, d *Data, kind string) { r := d.Reader() seen := make(map[string]bool) for { @@ -93,7 +102,7 @@ func testTypedefs(t *testing.T, d *Data) { t.Errorf("multiple definitions for %s", t1.Name) } seen[t1.Name] = true - if typstr != want { + if typstr != want && (kind != "macho" || typstr != machoBug[want]) { t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want) } } diff --git a/src/pkg/debug/dwarf/unit.go b/src/pkg/debug/dwarf/unit.go index 02cb363b4..c10d75dbd 100644 --- a/src/pkg/debug/dwarf/unit.go +++ b/src/pkg/debug/dwarf/unit.go @@ -4,10 +4,7 @@ package dwarf -import ( - "os" - "strconv" -) +import "strconv" // DWARF debug info is split into a sequence of compilation units. // Each unit has its own abbreviation table and address size. @@ -20,7 +17,7 @@ type unit struct { addrsize int } -func (d *Data) parseUnits() ([]unit, os.Error) { +func (d *Data) parseUnits() ([]unit, error) { // Count units. nunit := 0 b := makeBuf(d, "info", 0, d.info, 0) diff --git a/src/pkg/debug/elf/Makefile b/src/pkg/debug/elf/Makefile deleted file mode 100644 index dd431f653..000000000 --- a/src/pkg/debug/elf/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=debug/elf -GOFILES=\ - elf.go\ - file.go\ - -include ../../../Make.pkg diff --git a/src/pkg/debug/elf/elf.go b/src/pkg/debug/elf/elf.go index c71b230bd..03e42b034 100644 --- a/src/pkg/debug/elf/elf.go +++ b/src/pkg/debug/elf/elf.go @@ -1490,11 +1490,11 @@ func stringName(i uint32, names []intName, goSyntax bool) string { if goSyntax { s = "elf." + s } - return s + "+" + strconv.Uitoa64(uint64(i-n.i)) + return s + "+" + strconv.FormatUint(uint64(i-n.i), 10) } } - return strconv.Uitoa64(uint64(i)) + return strconv.FormatUint(uint64(i), 10) } func flagName(i uint32, names []intName, goSyntax bool) string { @@ -1512,10 +1512,10 @@ func flagName(i uint32, names []intName, goSyntax bool) string { } } if len(s) == 0 { - return "0x" + strconv.Uitob64(uint64(i), 16) + return "0x" + strconv.FormatUint(uint64(i), 16) } if i != 0 { - s += "+0x" + strconv.Uitob64(uint64(i), 16) + s += "+0x" + strconv.FormatUint(uint64(i), 16) } return s } diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index a0ddb1fc7..184ca8375 100644 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@ -9,6 +9,7 @@ import ( "bytes" "debug/dwarf" "encoding/binary" + "errors" "fmt" "io" "os" @@ -71,7 +72,7 @@ type Section struct { } // Data reads and returns the contents of the ELF section. -func (s *Section) Data() ([]byte, os.Error) { +func (s *Section) Data() ([]byte, error) { dat := make([]byte, s.sr.Size()) n, err := s.sr.ReadAt(dat, 0) return dat[0:n], err @@ -79,9 +80,9 @@ func (s *Section) Data() ([]byte, os.Error) { // stringTable reads and returns the string table given by the // specified link value. -func (f *File) stringTable(link uint32) ([]byte, os.Error) { +func (f *File) stringTable(link uint32) ([]byte, error) { if link <= 0 || link >= uint32(len(f.Sections)) { - return nil, os.NewError("section has invalid string table link") + return nil, errors.New("section has invalid string table link") } return f.Sections[link].Data() } @@ -136,7 +137,7 @@ type FormatError struct { val interface{} } -func (e *FormatError) String() string { +func (e *FormatError) Error() string { msg := e.msg if e.val != nil { msg += fmt.Sprintf(" '%v' ", e.val) @@ -146,7 +147,7 @@ func (e *FormatError) String() string { } // Open opens the named file using os.Open and prepares it for use as an ELF binary. -func Open(name string) (*File, os.Error) { +func Open(name string) (*File, error) { f, err := os.Open(name) if err != nil { return nil, err @@ -163,8 +164,8 @@ func Open(name string) (*File, os.Error) { // Close closes the File. // If the File was created using NewFile directly instead of Open, // Close has no effect. -func (f *File) Close() os.Error { - var err os.Error +func (f *File) Close() error { + var err error if f.closer != nil { err = f.closer.Close() f.closer = nil @@ -185,7 +186,7 @@ func (f *File) SectionByType(typ SectionType) *Section { // NewFile creates a new File for accessing an ELF binary in an underlying reader. // The ELF binary is expected to start at position 0 in the ReaderAt. -func NewFile(r io.ReaderAt) (*File, os.Error) { +func NewFile(r io.ReaderAt) (*File, error) { sr := io.NewSectionReader(r, 0, 1<<63-1) // Read and decode ELF identifier var ident [16]uint8 @@ -381,7 +382,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { // getSymbols returns a slice of Symbols from parsing the symbol table // with the given type, along with the associated string table. -func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, os.Error) { +func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) { switch f.Class { case ELFCLASS64: return f.getSymbols64(typ) @@ -390,27 +391,27 @@ func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, os.Error) { return f.getSymbols32(typ) } - return nil, nil, os.NewError("not implemented") + return nil, nil, errors.New("not implemented") } -func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, os.Error) { +func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { symtabSection := f.SectionByType(typ) if symtabSection == nil { - return nil, nil, os.NewError("no symbol section") + return nil, nil, errors.New("no symbol section") } data, err := symtabSection.Data() if err != nil { - return nil, nil, os.NewError("cannot load symbol section") + return nil, nil, errors.New("cannot load symbol section") } symtab := bytes.NewBuffer(data) if symtab.Len()%Sym32Size != 0 { - return nil, nil, os.NewError("length of symbol section is not a multiple of SymSize") + return nil, nil, errors.New("length of symbol section is not a multiple of SymSize") } strdata, err := f.stringTable(symtabSection.Link) if err != nil { - return nil, nil, os.NewError("cannot load string table section") + return nil, nil, errors.New("cannot load string table section") } // The first entry is all zeros. @@ -436,24 +437,24 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, os.Error) { return symbols, strdata, nil } -func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, os.Error) { +func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { symtabSection := f.SectionByType(typ) if symtabSection == nil { - return nil, nil, os.NewError("no symbol section") + return nil, nil, errors.New("no symbol section") } data, err := symtabSection.Data() if err != nil { - return nil, nil, os.NewError("cannot load symbol section") + return nil, nil, errors.New("cannot load symbol section") } symtab := bytes.NewBuffer(data) if symtab.Len()%Sym64Size != 0 { - return nil, nil, os.NewError("length of symbol section is not a multiple of Sym64Size") + return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size") } strdata, err := f.stringTable(symtabSection.Link) if err != nil { - return nil, nil, os.NewError("cannot load string table section") + return nil, nil, errors.New("cannot load string table section") } // The first entry is all zeros. @@ -506,17 +507,17 @@ func (f *File) Section(name string) *Section { // applyRelocations applies relocations to dst. rels is a relocations section // in RELA format. -func (f *File) applyRelocations(dst []byte, rels []byte) os.Error { +func (f *File) applyRelocations(dst []byte, rels []byte) error { if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 { return f.applyRelocationsAMD64(dst, rels) } - return os.NewError("not implemented") + return errors.New("not implemented") } -func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) os.Error { +func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error { if len(rels)%Sym64Size != 0 { - return os.NewError("length of relocation section is not a multiple of Sym64Size") + return errors.New("length of relocation section is not a multiple of Sym64Size") } symbols, _, err := f.getSymbols(SHT_SYMTAB) @@ -558,7 +559,7 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) os.Error { return nil } -func (f *File) DWARF() (*dwarf.Data, os.Error) { +func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the required ones, and the debug/dwarf package // does not use the others, so don't bother loading them. @@ -596,7 +597,7 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { } // Symbols returns the symbol table for f. -func (f *File) Symbols() ([]Symbol, os.Error) { +func (f *File) Symbols() ([]Symbol, error) { sym, _, err := f.getSymbols(SHT_SYMTAB) return sym, err } @@ -611,7 +612,7 @@ type ImportedSymbol struct { // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. // It does not return weak symbols. -func (f *File) ImportedSymbols() ([]ImportedSymbol, os.Error) { +func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { sym, str, err := f.getSymbols(SHT_DYNSYM) if err != nil { return nil, err @@ -721,7 +722,7 @@ func (f *File) gnuVersion(i int, sym *ImportedSymbol) { // ImportedLibraries returns the names of all libraries // referred to by the binary f that are expected to be // linked with the binary at dynamic link time. -func (f *File) ImportedLibraries() ([]string, os.Error) { +func (f *File) ImportedLibraries() ([]string, error) { ds := f.SectionByType(SHT_DYNAMIC) if ds == nil { // not dynamic, so no libraries diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 451d3d514..98f2723c8 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -227,6 +227,9 @@ func TestNoSectionOverlaps(t *testing.T) { } for i, si := range f.Sections { sih := si.SectionHeader + if sih.Type == SHT_NOBITS { + continue + } for j, sj := range f.Sections { sjh := sj.SectionHeader if i == j || sjh.Type == SHT_NOBITS || sih.Offset == sjh.Offset && sih.Size == 0 { diff --git a/src/pkg/debug/gosym/Makefile b/src/pkg/debug/gosym/Makefile deleted file mode 100644 index 4f420e729..000000000 --- a/src/pkg/debug/gosym/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=debug/gosym -GOFILES=\ - pclntab.go\ - symtab.go\ - -include ../../../Make.pkg - -test: make-pclinetest - -testshort: make-pclinetest - -make-pclinetest: - @if [ "`uname`-`uname -m`" = Linux-x86_64 -a $(GOARCH) = amd64 ]; then mkdir -p _test && $(AS) pclinetest.s && $(LD) -E main -o _test/pclinetest pclinetest.$O; fi diff --git a/src/pkg/debug/gosym/pclinetest.asm b/src/pkg/debug/gosym/pclinetest.asm new file mode 100644 index 000000000..6305435b0 --- /dev/null +++ b/src/pkg/debug/gosym/pclinetest.asm @@ -0,0 +1,58 @@ +TEXT linefrompc(SB),7,$0 // Each byte stores its line delta +BYTE $2; +BYTE $1; +BYTE $1; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; +BYTE $1; +BYTE $1; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; +#include "pclinetest.h" +BYTE $2; +#include "pclinetest.h" +BYTE $2; + +TEXT pcfromline(SB),7,$0 // Each record stores its line delta, then n, then n more bytes +BYTE $31; BYTE $0; +BYTE $1; BYTE $1; BYTE $0; +BYTE $1; BYTE $0; + +BYTE $2; BYTE $4; BYTE $0; BYTE $0; BYTE $0; BYTE $0; + + +#include "pclinetest.h" +BYTE $4; BYTE $0; + + +BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0; +#include "pclinetest.h" + + +BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0; + +TEXT main(SB),7,$0 + // Prevent GC of our test symbols + CALL linefrompc(SB) + CALL pcfromline(SB) + +// Keep the linker happy +TEXT main·main(SB),7,$0 + RET + +TEXT main·init(SB),7,$0 + RET diff --git a/src/pkg/debug/gosym/pclinetest.h b/src/pkg/debug/gosym/pclinetest.h index a6c40e76c..156c0b87b 100644 --- a/src/pkg/debug/gosym/pclinetest.h +++ b/src/pkg/debug/gosym/pclinetest.h @@ -1,3 +1,5 @@ +// +build ignore + // Empty include file to generate z symbols diff --git a/src/pkg/debug/gosym/pclinetest.s b/src/pkg/debug/gosym/pclinetest.s deleted file mode 100644 index 6305435b0..000000000 --- a/src/pkg/debug/gosym/pclinetest.s +++ /dev/null @@ -1,58 +0,0 @@ -TEXT linefrompc(SB),7,$0 // Each byte stores its line delta -BYTE $2; -BYTE $1; -BYTE $1; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; -BYTE $1; -BYTE $1; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -BYTE $1; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; BYTE $0; -#include "pclinetest.h" -BYTE $2; -#include "pclinetest.h" -BYTE $2; - -TEXT pcfromline(SB),7,$0 // Each record stores its line delta, then n, then n more bytes -BYTE $31; BYTE $0; -BYTE $1; BYTE $1; BYTE $0; -BYTE $1; BYTE $0; - -BYTE $2; BYTE $4; BYTE $0; BYTE $0; BYTE $0; BYTE $0; - - -#include "pclinetest.h" -BYTE $4; BYTE $0; - - -BYTE $3; BYTE $3; BYTE $0; BYTE $0; BYTE $0; -#include "pclinetest.h" - - -BYTE $4; BYTE $3; BYTE $0; BYTE $0; BYTE $0; - -TEXT main(SB),7,$0 - // Prevent GC of our test symbols - CALL linefrompc(SB) - CALL pcfromline(SB) - -// Keep the linker happy -TEXT main·main(SB),7,$0 - RET - -TEXT main·init(SB),7,$0 - RET diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go index c83e64eab..b2400bb3b 100644 --- a/src/pkg/debug/gosym/pclntab_test.go +++ b/src/pkg/debug/gosym/pclntab_test.go @@ -6,14 +6,37 @@ package gosym import ( "debug/elf" + "fmt" "os" + "os/exec" + "runtime" + "strings" "testing" - "syscall" ) +var pclinetestBinary string + func dotest() bool { // For now, only works on ELF platforms. - return syscall.OS == "linux" && os.Getenv("GOARCH") == "amd64" + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + return false + } + if pclinetestBinary != "" { + return true + } + // This command builds pclinetest from pclinetest.asm; + // the resulting binary looks like it was built from pclinetest.s, + // but we have renamed it to keep it away from the go tool. + pclinetestBinary = os.TempDir() + "/pclinetest" + command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6", + pclinetestBinary, pclinetestBinary, pclinetestBinary) + cmd := exec.Command("sh", "-c", command) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + return true } func getTable(t *testing.T) *Table { @@ -148,7 +171,7 @@ func TestPCLine(t *testing.T) { return } - f, tab := crack("_test/pclinetest", t) + f, tab := crack(pclinetestBinary, t) text := f.Section(".text") textdat, err := text.Data() if err != nil { @@ -162,10 +185,13 @@ func TestPCLine(t *testing.T) { file, line, fn := tab.PCToLine(pc) off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g wantLine += int(textdat[off]) + t.Logf("off is %d", off) if fn == nil { t.Errorf("failed to get line of PC %#x", pc) - } else if len(file) < 12 || file[len(file)-12:] != "pclinetest.s" || line != wantLine || fn != sym { - t.Errorf("expected %s:%d (%s) at PC %#x, got %s:%d (%s)", "pclinetest.s", wantLine, sym.Name, pc, file, line, fn.Name) + } else if !strings.HasSuffix(file, "pclinetest.asm") { + t.Errorf("expected %s (%s) at PC %#x, got %s (%s)", "pclinetest.asm", sym.Name, pc, file, fn.Name) + } else if line != wantLine || fn != sym { + t.Errorf("expected :%d (%s) at PC %#x, got :%d (%s)", wantLine, sym.Name, pc, line, fn.Name) } } diff --git a/src/pkg/debug/gosym/symtab.go b/src/pkg/debug/gosym/symtab.go index dea460d71..52d7d55a3 100644 --- a/src/pkg/debug/gosym/symtab.go +++ b/src/pkg/debug/gosym/symtab.go @@ -15,7 +15,6 @@ package gosym import ( "encoding/binary" "fmt" - "os" "strconv" "strings" ) @@ -105,7 +104,7 @@ type sym struct { name []byte } -func walksymtab(data []byte, fn func(sym) os.Error) os.Error { +func walksymtab(data []byte, fn func(sym) error) error { var s sym p := data for len(p) >= 6 { @@ -149,9 +148,9 @@ func walksymtab(data []byte, fn func(sym) os.Error) os.Error { // NewTable decodes the Go symbol table in data, // returning an in-memory representation. -func NewTable(symtab []byte, pcln *LineTable) (*Table, os.Error) { +func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { var n int - err := walksymtab(symtab, func(s sym) os.Error { + err := walksymtab(symtab, func(s sym) error { n++ return nil }) @@ -165,7 +164,7 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, os.Error) { nf := 0 nz := 0 lasttyp := uint8(0) - err = walksymtab(symtab, func(s sym) os.Error { + err = walksymtab(symtab, func(s sym) error { n := len(t.Syms) t.Syms = t.Syms[0 : n+1] ts := &t.Syms[n] @@ -355,7 +354,7 @@ func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) { // LineToPC looks up the first program counter on the given line in // the named file. Returns UnknownPathError or UnknownLineError if // there is an error looking up this line. -func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err os.Error) { +func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) { obj, ok := t.Files[file] if !ok { return 0, nil, UnknownFileError(file) @@ -466,7 +465,7 @@ pathloop: return tos.path, aline - tos.start - tos.offset + 1 } -func (o *Obj) alineFromLine(path string, line int) (int, os.Error) { +func (o *Obj) alineFromLine(path string, line int) (int, error) { if line < 1 { return 0, &UnknownLineError{path, line} } @@ -516,7 +515,7 @@ func (o *Obj) alineFromLine(path string, line int) (int, os.Error) { // the symbol table. type UnknownFileError string -func (e UnknownFileError) String() string { return "unknown file: " + string(e) } +func (e UnknownFileError) Error() string { return "unknown file: " + string(e) } // UnknownLineError represents a failure to map a line to a program // counter, either because the line is beyond the bounds of the file @@ -526,7 +525,7 @@ type UnknownLineError struct { Line int } -func (e *UnknownLineError) String() string { +func (e *UnknownLineError) Error() string { return "no code at " + e.File + ":" + strconv.Itoa(e.Line) } @@ -538,7 +537,7 @@ type DecodingError struct { val interface{} } -func (e *DecodingError) String() string { +func (e *DecodingError) Error() string { msg := e.msg if e.val != nil { msg += fmt.Sprintf(" '%v'", e.val) diff --git a/src/pkg/debug/macho/Makefile b/src/pkg/debug/macho/Makefile deleted file mode 100644 index 5fbbf1efe..000000000 --- a/src/pkg/debug/macho/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=debug/macho -GOFILES=\ - macho.go\ - file.go\ - -include ../../../Make.pkg diff --git a/src/pkg/debug/macho/file.go b/src/pkg/debug/macho/file.go index 721a4c416..fa73a315c 100644 --- a/src/pkg/debug/macho/file.go +++ b/src/pkg/debug/macho/file.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package macho implements access to Mach-O object files, as defined by -// http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html. +// Package macho implements access to Mach-O object files. package macho // High level access to low level data structures. @@ -12,6 +11,7 @@ import ( "bytes" "debug/dwarf" "encoding/binary" + "errors" "fmt" "io" "os" @@ -71,7 +71,7 @@ type Segment struct { } // Data reads and returns the contents of the segment. -func (s *Segment) Data() ([]byte, os.Error) { +func (s *Segment) Data() ([]byte, error) { dat := make([]byte, s.sr.Size()) n, err := s.sr.ReadAt(dat, 0) return dat[0:n], err @@ -106,7 +106,7 @@ type Section struct { } // Data reads and returns the contents of the Mach-O section. -func (s *Section) Data() ([]byte, os.Error) { +func (s *Section) Data() ([]byte, error) { dat := make([]byte, s.sr.Size()) n, err := s.sr.ReadAt(dat, 0) return dat[0:n], err @@ -148,7 +148,7 @@ type FormatError struct { val interface{} } -func (e *FormatError) String() string { +func (e *FormatError) Error() string { msg := e.msg if e.val != nil { msg += fmt.Sprintf(" '%v'", e.val) @@ -158,7 +158,7 @@ func (e *FormatError) String() string { } // Open opens the named file using os.Open and prepares it for use as a Mach-O binary. -func Open(name string) (*File, os.Error) { +func Open(name string) (*File, error) { f, err := os.Open(name) if err != nil { return nil, err @@ -175,8 +175,8 @@ func Open(name string) (*File, os.Error) { // Close closes the File. // If the File was created using NewFile directly instead of Open, // Close has no effect. -func (f *File) Close() os.Error { - var err os.Error +func (f *File) Close() error { + var err error if f.closer != nil { err = f.closer.Close() f.closer = nil @@ -186,7 +186,7 @@ func (f *File) Close() os.Error { // NewFile creates a new File for accessing a Mach-O binary in an underlying reader. // The Mach-O binary is expected to start at position 0 in the ReaderAt. -func NewFile(r io.ReaderAt) (*File, os.Error) { +func NewFile(r io.ReaderAt) (*File, error) { f := new(File) sr := io.NewSectionReader(r, 0, 1<<63-1) @@ -391,7 +391,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { return f, nil } -func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, os.Error) { +func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) { bo := f.ByteOrder symtab := make([]Symbol, hdr.Nsyms) b := bytes.NewBuffer(symdat) @@ -463,7 +463,7 @@ func (f *File) Section(name string) *Section { } // DWARF returns the DWARF debug information for the Mach-O file. -func (f *File) DWARF() (*dwarf.Data, os.Error) { +func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the required ones, and the debug/dwarf package // does not use the others, so don't bother loading them. @@ -473,7 +473,7 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { name = "__debug_" + name s := f.Section(name) if s == nil { - return nil, os.NewError("missing Mach-O section " + name) + return nil, errors.New("missing Mach-O section " + name) } b, err := s.Data() if err != nil && uint64(len(b)) < s.Size { @@ -489,7 +489,7 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { // ImportedSymbols returns the names of all symbols // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. -func (f *File) ImportedSymbols() ([]string, os.Error) { +func (f *File) ImportedSymbols() ([]string, error) { if f.Dysymtab == nil || f.Symtab == nil { return nil, &FormatError{0, "missing symbol table", nil} } @@ -506,7 +506,7 @@ func (f *File) ImportedSymbols() ([]string, os.Error) { // ImportedLibraries returns the paths of all libraries // referred to by the binary f that are expected to be // linked with the binary at dynamic link time. -func (f *File) ImportedLibraries() ([]string, os.Error) { +func (f *File) ImportedLibraries() ([]string, error) { var all []string for _, l := range f.Loads { if lib, ok := l.(*Dylib); ok { diff --git a/src/pkg/debug/macho/file_test.go b/src/pkg/debug/macho/file_test.go index 56d8a20be..640225b32 100644 --- a/src/pkg/debug/macho/file_test.go +++ b/src/pkg/debug/macho/file_test.go @@ -21,11 +21,11 @@ var fileTests = []fileTest{ "testdata/gcc-386-darwin-exec", FileHeader{0xfeedface, Cpu386, 0x3, 0x2, 0xc, 0x3c0, 0x85}, []*SegmentHeader{ - &SegmentHeader{LoadCmdSegment, 0x38, "__PAGEZERO", 0x0, 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - &SegmentHeader{LoadCmdSegment, 0xc0, "__TEXT", 0x1000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x2, 0x0}, - &SegmentHeader{LoadCmdSegment, 0xc0, "__DATA", 0x2000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x2, 0x0}, - &SegmentHeader{LoadCmdSegment, 0x7c, "__IMPORT", 0x3000, 0x1000, 0x2000, 0x1000, 0x7, 0x7, 0x1, 0x0}, - &SegmentHeader{LoadCmdSegment, 0x38, "__LINKEDIT", 0x4000, 0x1000, 0x3000, 0x12c, 0x7, 0x1, 0x0, 0x0}, + {LoadCmdSegment, 0x38, "__PAGEZERO", 0x0, 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {LoadCmdSegment, 0xc0, "__TEXT", 0x1000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x2, 0x0}, + {LoadCmdSegment, 0xc0, "__DATA", 0x2000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x2, 0x0}, + {LoadCmdSegment, 0x7c, "__IMPORT", 0x3000, 0x1000, 0x2000, 0x1000, 0x7, 0x7, 0x1, 0x0}, + {LoadCmdSegment, 0x38, "__LINKEDIT", 0x4000, 0x1000, 0x3000, 0x12c, 0x7, 0x1, 0x0, 0x0}, nil, nil, nil, @@ -35,21 +35,21 @@ var fileTests = []fileTest{ nil, }, []*SectionHeader{ - &SectionHeader{"__text", "__TEXT", 0x1f68, 0x88, 0xf68, 0x2, 0x0, 0x0, 0x80000400}, - &SectionHeader{"__cstring", "__TEXT", 0x1ff0, 0xd, 0xff0, 0x0, 0x0, 0x0, 0x2}, - &SectionHeader{"__data", "__DATA", 0x2000, 0x14, 0x1000, 0x2, 0x0, 0x0, 0x0}, - &SectionHeader{"__dyld", "__DATA", 0x2014, 0x1c, 0x1014, 0x2, 0x0, 0x0, 0x0}, - &SectionHeader{"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008}, + {"__text", "__TEXT", 0x1f68, 0x88, 0xf68, 0x2, 0x0, 0x0, 0x80000400}, + {"__cstring", "__TEXT", 0x1ff0, 0xd, 0xff0, 0x0, 0x0, 0x0, 0x2}, + {"__data", "__DATA", 0x2000, 0x14, 0x1000, 0x2, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x2014, 0x1c, 0x1014, 0x2, 0x0, 0x0, 0x0}, + {"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008}, }, }, { "testdata/gcc-amd64-darwin-exec", FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0xb, 0x568, 0x85}, []*SegmentHeader{ - &SegmentHeader{LoadCmdSegment64, 0x48, "__PAGEZERO", 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x5, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x3, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x48, "__LINKEDIT", 0x100002000, 0x1000, 0x2000, 0x140, 0x7, 0x1, 0x0, 0x0}, + {LoadCmdSegment64, 0x48, "__PAGEZERO", 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x5, 0x0}, + {LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x3, 0x0}, + {LoadCmdSegment64, 0x48, "__LINKEDIT", 0x100002000, 0x1000, 0x2000, 0x140, 0x7, 0x1, 0x0, 0x0}, nil, nil, nil, @@ -59,14 +59,14 @@ var fileTests = []fileTest{ nil, }, []*SectionHeader{ - &SectionHeader{"__text", "__TEXT", 0x100000f14, 0x6d, 0xf14, 0x2, 0x0, 0x0, 0x80000400}, - &SectionHeader{"__symbol_stub1", "__TEXT", 0x100000f81, 0xc, 0xf81, 0x0, 0x0, 0x0, 0x80000408}, - &SectionHeader{"__stub_helper", "__TEXT", 0x100000f90, 0x18, 0xf90, 0x2, 0x0, 0x0, 0x0}, - &SectionHeader{"__cstring", "__TEXT", 0x100000fa8, 0xd, 0xfa8, 0x0, 0x0, 0x0, 0x2}, - &SectionHeader{"__eh_frame", "__TEXT", 0x100000fb8, 0x48, 0xfb8, 0x3, 0x0, 0x0, 0x6000000b}, - &SectionHeader{"__data", "__DATA", 0x100001000, 0x1c, 0x1000, 0x3, 0x0, 0x0, 0x0}, - &SectionHeader{"__dyld", "__DATA", 0x100001020, 0x38, 0x1020, 0x3, 0x0, 0x0, 0x0}, - &SectionHeader{"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7}, + {"__text", "__TEXT", 0x100000f14, 0x6d, 0xf14, 0x2, 0x0, 0x0, 0x80000400}, + {"__symbol_stub1", "__TEXT", 0x100000f81, 0xc, 0xf81, 0x0, 0x0, 0x0, 0x80000408}, + {"__stub_helper", "__TEXT", 0x100000f90, 0x18, 0xf90, 0x2, 0x0, 0x0, 0x0}, + {"__cstring", "__TEXT", 0x100000fa8, 0xd, 0xfa8, 0x0, 0x0, 0x0, 0x2}, + {"__eh_frame", "__TEXT", 0x100000fb8, 0x48, 0xfb8, 0x3, 0x0, 0x0, 0x6000000b}, + {"__data", "__DATA", 0x100001000, 0x1c, 0x1000, 0x3, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x100001020, 0x38, 0x1020, 0x3, 0x0, 0x0, 0x0}, + {"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7}, }, }, { @@ -74,26 +74,26 @@ var fileTests = []fileTest{ FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0xa, 0x4, 0x5a0, 0}, []*SegmentHeader{ nil, - &SegmentHeader{LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x0, 0x7, 0x5, 0x5, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x0, 0x0, 0x7, 0x3, 0x3, 0x0}, - &SegmentHeader{LoadCmdSegment64, 0x278, "__DWARF", 0x100002000, 0x1000, 0x1000, 0x1bc, 0x7, 0x3, 0x7, 0x0}, + {LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x0, 0x7, 0x5, 0x5, 0x0}, + {LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x0, 0x0, 0x7, 0x3, 0x3, 0x0}, + {LoadCmdSegment64, 0x278, "__DWARF", 0x100002000, 0x1000, 0x1000, 0x1bc, 0x7, 0x3, 0x7, 0x0}, }, []*SectionHeader{ - &SectionHeader{"__text", "__TEXT", 0x100000f14, 0x0, 0x0, 0x2, 0x0, 0x0, 0x80000400}, - &SectionHeader{"__symbol_stub1", "__TEXT", 0x100000f81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000408}, - &SectionHeader{"__stub_helper", "__TEXT", 0x100000f90, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0}, - &SectionHeader{"__cstring", "__TEXT", 0x100000fa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, - &SectionHeader{"__eh_frame", "__TEXT", 0x100000fb8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x6000000b}, - &SectionHeader{"__data", "__DATA", 0x100001000, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, - &SectionHeader{"__dyld", "__DATA", 0x100001020, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, - &SectionHeader{"__la_symbol_ptr", "__DATA", 0x100001058, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7}, - &SectionHeader{"__debug_abbrev", "__DWARF", 0x100002000, 0x36, 0x1000, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_aranges", "__DWARF", 0x100002036, 0x30, 0x1036, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_frame", "__DWARF", 0x100002066, 0x40, 0x1066, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_info", "__DWARF", 0x1000020a6, 0x54, 0x10a6, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_line", "__DWARF", 0x1000020fa, 0x47, 0x10fa, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_pubnames", "__DWARF", 0x100002141, 0x1b, 0x1141, 0x0, 0x0, 0x0, 0x0}, - &SectionHeader{"__debug_str", "__DWARF", 0x10000215c, 0x60, 0x115c, 0x0, 0x0, 0x0, 0x0}, + {"__text", "__TEXT", 0x100000f14, 0x0, 0x0, 0x2, 0x0, 0x0, 0x80000400}, + {"__symbol_stub1", "__TEXT", 0x100000f81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000408}, + {"__stub_helper", "__TEXT", 0x100000f90, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0}, + {"__cstring", "__TEXT", 0x100000fa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, + {"__eh_frame", "__TEXT", 0x100000fb8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x6000000b}, + {"__data", "__DATA", 0x100001000, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, + {"__dyld", "__DATA", 0x100001020, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0}, + {"__la_symbol_ptr", "__DATA", 0x100001058, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7}, + {"__debug_abbrev", "__DWARF", 0x100002000, 0x36, 0x1000, 0x0, 0x0, 0x0, 0x0}, + {"__debug_aranges", "__DWARF", 0x100002036, 0x30, 0x1036, 0x0, 0x0, 0x0, 0x0}, + {"__debug_frame", "__DWARF", 0x100002066, 0x40, 0x1066, 0x0, 0x0, 0x0, 0x0}, + {"__debug_info", "__DWARF", 0x1000020a6, 0x54, 0x10a6, 0x0, 0x0, 0x0, 0x0}, + {"__debug_line", "__DWARF", 0x1000020fa, 0x47, 0x10fa, 0x0, 0x0, 0x0, 0x0}, + {"__debug_pubnames", "__DWARF", 0x100002141, 0x1b, 0x1141, 0x0, 0x0, 0x0, 0x0}, + {"__debug_str", "__DWARF", 0x10000215c, 0x60, 0x115c, 0x0, 0x0, 0x0, 0x0}, }, }, } diff --git a/src/pkg/debug/macho/macho.go b/src/pkg/debug/macho/macho.go index 1386f5acf..bc14226c5 100644 --- a/src/pkg/debug/macho/macho.go +++ b/src/pkg/debug/macho/macho.go @@ -278,7 +278,7 @@ func stringName(i uint32, names []intName, goSyntax bool) string { return n.s } } - return strconv.Uitoa64(uint64(i)) + return strconv.FormatUint(uint64(i), 10) } func flagName(i uint32, names []intName, goSyntax bool) string { @@ -296,10 +296,10 @@ func flagName(i uint32, names []intName, goSyntax bool) string { } } if len(s) == 0 { - return "0x" + strconv.Uitob64(uint64(i), 16) + return "0x" + strconv.FormatUint(uint64(i), 16) } if i != 0 { - s += "+0x" + strconv.Uitob64(uint64(i), 16) + s += "+0x" + strconv.FormatUint(uint64(i), 16) } return s } diff --git a/src/pkg/debug/pe/Makefile b/src/pkg/debug/pe/Makefile deleted file mode 100644 index 998e6a418..000000000 --- a/src/pkg/debug/pe/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=debug/pe -GOFILES=\ - pe.go\ - file.go\ - -include ../../../Make.pkg diff --git a/src/pkg/debug/pe/file.go b/src/pkg/debug/pe/file.go index d86d916f5..6b98a5f45 100644 --- a/src/pkg/debug/pe/file.go +++ b/src/pkg/debug/pe/file.go @@ -8,6 +8,7 @@ package pe import ( "debug/dwarf" "encoding/binary" + "errors" "fmt" "io" "os" @@ -59,7 +60,7 @@ type ImportDirectory struct { } // Data reads and returns the contents of the PE section. -func (s *Section) Data() ([]byte, os.Error) { +func (s *Section) Data() ([]byte, error) { dat := make([]byte, s.sr.Size()) n, err := s.sr.ReadAt(dat, 0) return dat[0:n], err @@ -74,7 +75,7 @@ type FormatError struct { val interface{} } -func (e *FormatError) String() string { +func (e *FormatError) Error() string { msg := e.msg if e.val != nil { msg += fmt.Sprintf(" '%v'", e.val) @@ -84,7 +85,7 @@ func (e *FormatError) String() string { } // Open opens the named file using os.Open and prepares it for use as a PE binary. -func Open(name string) (*File, os.Error) { +func Open(name string) (*File, error) { f, err := os.Open(name) if err != nil { return nil, err @@ -101,8 +102,8 @@ func Open(name string) (*File, os.Error) { // Close closes the File. // If the File was created using NewFile directly instead of Open, // Close has no effect. -func (f *File) Close() os.Error { - var err os.Error +func (f *File) Close() error { + var err error if f.closer != nil { err = f.closer.Close() f.closer = nil @@ -111,7 +112,7 @@ func (f *File) Close() os.Error { } // NewFile creates a new File for accessing a PE binary in an underlying reader. -func NewFile(r io.ReaderAt) (*File, os.Error) { +func NewFile(r io.ReaderAt) (*File, error) { f := new(File) sr := io.NewSectionReader(r, 0, 1<<63-1) @@ -124,7 +125,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { var sign [4]byte r.ReadAt(sign[0:], int64(dosheader[0x3c])) if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { - return nil, os.NewError("Invalid PE File Format.") + return nil, errors.New("Invalid PE File Format.") } base = int64(dosheader[0x3c]) + 4 } else { @@ -135,7 +136,7 @@ func NewFile(r io.ReaderAt) (*File, os.Error) { return nil, err } if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { - return nil, os.NewError("Invalid PE File Format.") + return nil, errors.New("Invalid PE File Format.") } // get symbol string table sr.Seek(int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols), os.SEEK_SET) @@ -215,7 +216,7 @@ func (f *File) Section(name string) *Section { return nil } -func (f *File) DWARF() (*dwarf.Data, os.Error) { +func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the required ones, and the debug/dwarf package // does not use the others, so don't bother loading them. @@ -242,7 +243,7 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) { // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. // It does not return weak symbols. -func (f *File) ImportedSymbols() ([]string, os.Error) { +func (f *File) ImportedSymbols() ([]string, error) { pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 ds := f.Section(".idata") if ds == nil { @@ -308,7 +309,7 @@ func (f *File) ImportedSymbols() ([]string, os.Error) { // ImportedLibraries returns the names of all libraries // referred to by the binary f that are expected to be // linked with the binary at dynamic link time. -func (f *File) ImportedLibraries() ([]string, os.Error) { +func (f *File) ImportedLibraries() ([]string, error) { // TODO // cgo -dynimport don't use this for windows PE, so just return. return nil, nil diff --git a/src/pkg/debug/pe/file_test.go b/src/pkg/debug/pe/file_test.go index 2c5c25b8c..2815d720b 100644 --- a/src/pkg/debug/pe/file_test.go +++ b/src/pkg/debug/pe/file_test.go @@ -20,39 +20,39 @@ var fileTests = []fileTest{ "testdata/gcc-386-mingw-obj", FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104}, []*SectionHeader{ - &SectionHeader{".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020}, - &SectionHeader{".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264}, - &SectionHeader{".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328}, - &SectionHeader{".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000}, - &SectionHeader{".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832}, - &SectionHeader{".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832}, - &SectionHeader{".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616}, - &SectionHeader{".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984}, - &SectionHeader{".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832}, - &SectionHeader{".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832}, - &SectionHeader{".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, - &SectionHeader{".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, + {".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020}, + {".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264}, + {".bss", 0, 0, 0, 0, 0, 0, 0, 0, 3224371328}, + {".debug_abbrev", 0, 0, 137, 536, 0, 0, 0, 0, 0x42100000}, + {".debug_info", 0, 0, 418, 673, 1470, 0, 7, 0, 1108344832}, + {".debug_line", 0, 0, 128, 1091, 1540, 0, 1, 0, 1108344832}, + {".rdata", 0, 0, 16, 1219, 0, 0, 0, 0, 1076887616}, + {".debug_frame", 0, 0, 52, 1235, 1550, 0, 2, 0, 1110441984}, + {".debug_loc", 0, 0, 56, 1287, 0, 0, 0, 0, 1108344832}, + {".debug_pubnames", 0, 0, 27, 1343, 1570, 0, 1, 0, 1108344832}, + {".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, + {".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, }, }, { "testdata/gcc-386-mingw-exec", FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107}, []*SectionHeader{ - &SectionHeader{Name: ".text", VirtualSize: 0xcd8, VirtualAddress: 0x1000, Size: 0xe00, Offset: 0x400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x60500060}, - &SectionHeader{Name: ".data", VirtualSize: 0x10, VirtualAddress: 0x2000, Size: 0x200, Offset: 0x1200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, - &SectionHeader{Name: ".rdata", VirtualSize: 0x120, VirtualAddress: 0x3000, Size: 0x200, Offset: 0x1400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x40300040}, - &SectionHeader{Name: ".bss", VirtualSize: 0xdc, VirtualAddress: 0x4000, Size: 0x0, Offset: 0x0, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0400080}, - &SectionHeader{Name: ".idata", VirtualSize: 0x3c8, VirtualAddress: 0x5000, Size: 0x400, Offset: 0x1600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, - &SectionHeader{Name: ".CRT", VirtualSize: 0x18, VirtualAddress: 0x6000, Size: 0x200, Offset: 0x1a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, - &SectionHeader{Name: ".tls", VirtualSize: 0x20, VirtualAddress: 0x7000, Size: 0x200, Offset: 0x1c00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, - &SectionHeader{Name: ".debug_aranges", VirtualSize: 0x20, VirtualAddress: 0x8000, Size: 0x200, Offset: 0x1e00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_pubnames", VirtualSize: 0x51, VirtualAddress: 0x9000, Size: 0x200, Offset: 0x2000, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_pubtypes", VirtualSize: 0x91, VirtualAddress: 0xa000, Size: 0x200, Offset: 0x2200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_info", VirtualSize: 0xe22, VirtualAddress: 0xb000, Size: 0x1000, Offset: 0x2400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_abbrev", VirtualSize: 0x157, VirtualAddress: 0xc000, Size: 0x200, Offset: 0x3400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_line", VirtualSize: 0x144, VirtualAddress: 0xd000, Size: 0x200, Offset: 0x3600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, - &SectionHeader{Name: ".debug_frame", VirtualSize: 0x34, VirtualAddress: 0xe000, Size: 0x200, Offset: 0x3800, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42300000}, - &SectionHeader{Name: ".debug_loc", VirtualSize: 0x38, VirtualAddress: 0xf000, Size: 0x200, Offset: 0x3a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".text", VirtualSize: 0xcd8, VirtualAddress: 0x1000, Size: 0xe00, Offset: 0x400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x60500060}, + {Name: ".data", VirtualSize: 0x10, VirtualAddress: 0x2000, Size: 0x200, Offset: 0x1200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + {Name: ".rdata", VirtualSize: 0x120, VirtualAddress: 0x3000, Size: 0x200, Offset: 0x1400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x40300040}, + {Name: ".bss", VirtualSize: 0xdc, VirtualAddress: 0x4000, Size: 0x0, Offset: 0x0, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0400080}, + {Name: ".idata", VirtualSize: 0x3c8, VirtualAddress: 0x5000, Size: 0x400, Offset: 0x1600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + {Name: ".CRT", VirtualSize: 0x18, VirtualAddress: 0x6000, Size: 0x200, Offset: 0x1a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + {Name: ".tls", VirtualSize: 0x20, VirtualAddress: 0x7000, Size: 0x200, Offset: 0x1c00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040}, + {Name: ".debug_aranges", VirtualSize: 0x20, VirtualAddress: 0x8000, Size: 0x200, Offset: 0x1e00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_pubnames", VirtualSize: 0x51, VirtualAddress: 0x9000, Size: 0x200, Offset: 0x2000, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_pubtypes", VirtualSize: 0x91, VirtualAddress: 0xa000, Size: 0x200, Offset: 0x2200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_info", VirtualSize: 0xe22, VirtualAddress: 0xb000, Size: 0x1000, Offset: 0x2400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_abbrev", VirtualSize: 0x157, VirtualAddress: 0xc000, Size: 0x200, Offset: 0x3400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_line", VirtualSize: 0x144, VirtualAddress: 0xd000, Size: 0x200, Offset: 0x3600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, + {Name: ".debug_frame", VirtualSize: 0x34, VirtualAddress: 0xe000, Size: 0x200, Offset: 0x3800, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42300000}, + {Name: ".debug_loc", VirtualSize: 0x38, VirtualAddress: 0xf000, Size: 0x200, Offset: 0x3a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, }, }, } diff --git a/src/pkg/deps.bash b/src/pkg/deps.bash deleted file mode 100755 index 2095ec1d8..000000000 --- a/src/pkg/deps.bash +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -eval $(gomake --no-print-directory -f ../Make.inc go-env) - -OUT="Make.deps" -TMP="Make.deps.tmp" - -if [ -f $OUT ] && ! [ -w $OUT ]; then - echo "$0: $OUT is read-only; aborting." 1>&2 - exit 1 -fi - -# Get list of directories from Makefile -dirs=$(gomake --no-print-directory echo-dirs) -dirpat=$(echo $dirs C | awk '{ - for(i=1;i<=NF;i++){ - x=$i - gsub("/", "\\/", x) - printf("/^(%s)$/\n", x) - } -}') - -for dir in $dirs; do ( - cd $dir || exit 1 - - sources=$(sed -n 's/^[ ]*\([^ ]*\.go\)[ ]*\\*[ ]*$/\1/p' Makefile) - sources=$(echo $sources | sed 's/\$(GOOS)/'$GOOS'/g') - sources=$(echo $sources | sed 's/\$(GOARCH)/'$GOARCH'/g') - # /dev/null here means we get an empty dependency list if $sources is empty - # instead of listing every file in the directory. - sources=$(ls $sources /dev/null 2> /dev/null) # remove .s, .c, etc. - - deps=$( - sed -n '/^import.*"/p; /^import[ \t]*(/,/^)/p' $sources /dev/null | - cut -d '"' -f2 | - awk "$dirpat" | - grep -v "^$dir\$" | - sed 's/$/.install/' | - sed 's;^C\.install;runtime/cgo.install;' | - sort -u - ) - - echo $dir.install: $deps -) done > $TMP - -mv $TMP $OUT diff --git a/src/pkg/ebnf/Makefile b/src/pkg/ebnf/Makefile deleted file mode 100644 index f5555d272..000000000 --- a/src/pkg/ebnf/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../Make.inc - -TARG=ebnf -GOFILES=\ - ebnf.go\ - parser.go\ - -include ../../Make.pkg diff --git a/src/pkg/ebnf/ebnf.go b/src/pkg/ebnf/ebnf.go deleted file mode 100644 index 69da11767..000000000 --- a/src/pkg/ebnf/ebnf.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package ebnf is a library for EBNF grammars. The input is text ([]byte) -// satisfying the following grammar (represented itself in EBNF): -// -// Production = name "=" [ Expression ] "." . -// Expression = Alternative { "|" Alternative } . -// Alternative = Term { Term } . -// Term = name | token [ "…" token ] | Group | Option | Repetition . -// Group = "(" Expression ")" . -// Option = "[" Expression "]" . -// Repetition = "{" Expression "}" . -// -// A name is a Go identifier, a token is a Go string, and comments -// and white space follow the same rules as for the Go language. -// Production names starting with an uppercase Unicode letter denote -// non-terminal productions (i.e., productions which allow white-space -// and comments between tokens); all other production names denote -// lexical productions. -// -package ebnf - -import ( - "go/scanner" - "go/token" - "os" - "unicode" - "utf8" -) - -// ---------------------------------------------------------------------------- -// Internal representation - -type ( - // An Expression node represents a production expression. - Expression interface { - // Pos is the position of the first character of the syntactic construct - Pos() token.Pos - } - - // An Alternative node represents a non-empty list of alternative expressions. - Alternative []Expression // x | y | z - - // A Sequence node represents a non-empty list of sequential expressions. - Sequence []Expression // x y z - - // A Name node represents a production name. - Name struct { - StringPos token.Pos - String string - } - - // A Token node represents a literal. - Token struct { - StringPos token.Pos - String string - } - - // A List node represents a range of characters. - Range struct { - Begin, End *Token // begin ... end - } - - // A Group node represents a grouped expression. - Group struct { - Lparen token.Pos - Body Expression // (body) - } - - // An Option node represents an optional expression. - Option struct { - Lbrack token.Pos - Body Expression // [body] - } - - // A Repetition node represents a repeated expression. - Repetition struct { - Lbrace token.Pos - Body Expression // {body} - } - - // A Bad node stands for pieces of source code that lead to a parse error. - Bad struct { - TokPos token.Pos - Error string // parser error message - } - - // A Production node represents an EBNF production. - Production struct { - Name *Name - Expr Expression - } - - // A Grammar is a set of EBNF productions. The map - // is indexed by production name. - // - Grammar map[string]*Production -) - -func (x Alternative) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Alternative -func (x Sequence) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Sequences -func (x *Name) Pos() token.Pos { return x.StringPos } -func (x *Token) Pos() token.Pos { return x.StringPos } -func (x *Range) Pos() token.Pos { return x.Begin.Pos() } -func (x *Group) Pos() token.Pos { return x.Lparen } -func (x *Option) Pos() token.Pos { return x.Lbrack } -func (x *Repetition) Pos() token.Pos { return x.Lbrace } -func (x *Bad) Pos() token.Pos { return x.TokPos } -func (x *Production) Pos() token.Pos { return x.Name.Pos() } - -// ---------------------------------------------------------------------------- -// Grammar verification - -func isLexical(name string) bool { - ch, _ := utf8.DecodeRuneInString(name) - return !unicode.IsUpper(ch) -} - -type verifier struct { - fset *token.FileSet - scanner.ErrorVector - worklist []*Production - reached Grammar // set of productions reached from (and including) the root production - grammar Grammar -} - -func (v *verifier) error(pos token.Pos, msg string) { - v.Error(v.fset.Position(pos), msg) -} - -func (v *verifier) push(prod *Production) { - name := prod.Name.String - if _, found := v.reached[name]; !found { - v.worklist = append(v.worklist, prod) - v.reached[name] = prod - } -} - -func (v *verifier) verifyChar(x *Token) int { - s := x.String - if utf8.RuneCountInString(s) != 1 { - v.error(x.Pos(), "single char expected, found "+s) - return 0 - } - ch, _ := utf8.DecodeRuneInString(s) - return ch -} - -func (v *verifier) verifyExpr(expr Expression, lexical bool) { - switch x := expr.(type) { - case nil: - // empty expression - case Alternative: - for _, e := range x { - v.verifyExpr(e, lexical) - } - case Sequence: - for _, e := range x { - v.verifyExpr(e, lexical) - } - case *Name: - // a production with this name must exist; - // add it to the worklist if not yet processed - if prod, found := v.grammar[x.String]; found { - v.push(prod) - } else { - v.error(x.Pos(), "missing production "+x.String) - } - // within a lexical production references - // to non-lexical productions are invalid - if lexical && !isLexical(x.String) { - v.error(x.Pos(), "reference to non-lexical production "+x.String) - } - case *Token: - // nothing to do for now - case *Range: - i := v.verifyChar(x.Begin) - j := v.verifyChar(x.End) - if i >= j { - v.error(x.Pos(), "decreasing character range") - } - case *Group: - v.verifyExpr(x.Body, lexical) - case *Option: - v.verifyExpr(x.Body, lexical) - case *Repetition: - v.verifyExpr(x.Body, lexical) - default: - panic("unreachable") - } -} - -func (v *verifier) verify(fset *token.FileSet, grammar Grammar, start string) { - // find root production - root, found := grammar[start] - if !found { - // token.NoPos doesn't require a file set; - // ok to set v.fset only afterwards - v.error(token.NoPos, "no start production "+start) - return - } - - // initialize verifier - v.fset = fset - v.ErrorVector.Reset() - v.worklist = v.worklist[0:0] - v.reached = make(Grammar) - v.grammar = grammar - - // work through the worklist - v.push(root) - for { - n := len(v.worklist) - 1 - if n < 0 { - break - } - prod := v.worklist[n] - v.worklist = v.worklist[0:n] - v.verifyExpr(prod.Expr, isLexical(prod.Name.String)) - } - - // check if all productions were reached - if len(v.reached) < len(v.grammar) { - for name, prod := range v.grammar { - if _, found := v.reached[name]; !found { - v.error(prod.Pos(), name+" is unreachable") - } - } - } -} - -// Verify checks that: -// - all productions used are defined -// - all productions defined are used when beginning at start -// - lexical productions refer only to other lexical productions -// -// Position information is interpreted relative to the file set fset. -// -func Verify(fset *token.FileSet, grammar Grammar, start string) os.Error { - var v verifier - v.verify(fset, grammar, start) - return v.GetError(scanner.Sorted) -} diff --git a/src/pkg/ebnf/ebnf_test.go b/src/pkg/ebnf/ebnf_test.go deleted file mode 100644 index b086facc3..000000000 --- a/src/pkg/ebnf/ebnf_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ebnf - -import ( - "go/token" - "io/ioutil" - "testing" -) - -var fset = token.NewFileSet() - -var goodGrammars = []string{ - `Program = .`, - - `Program = foo . - foo = "foo" .`, - - `Program = "a" | "b" "c" .`, - - `Program = "a" … "z" .`, - - `Program = Song . - Song = { Note } . - Note = Do | (Re | Mi | Fa | So | La) | Ti . - Do = "c" . - Re = "d" . - Mi = "e" . - Fa = "f" . - So = "g" . - La = "a" . - Ti = ti . - ti = "b" .`, -} - -var badGrammars = []string{ - `Program = | .`, - `Program = | b .`, - `Program = a … b .`, - `Program = "a" … .`, - `Program = … "b" .`, - `Program = () .`, - `Program = [] .`, - `Program = {} .`, -} - -func checkGood(t *testing.T, filename string, src []byte) { - grammar, err := Parse(fset, filename, src) - if err != nil { - t.Errorf("Parse(%s) failed: %v", src, err) - } - if err = Verify(fset, grammar, "Program"); err != nil { - t.Errorf("Verify(%s) failed: %v", src, err) - } -} - -func checkBad(t *testing.T, filename string, src []byte) { - _, err := Parse(fset, filename, src) - if err == nil { - t.Errorf("Parse(%s) should have failed", src) - } -} - -func TestGrammars(t *testing.T) { - for _, src := range goodGrammars { - checkGood(t, "", []byte(src)) - } - for _, src := range badGrammars { - checkBad(t, "", []byte(src)) - } -} - -var files = []string{ -// TODO(gri) add some test files -} - -func TestFiles(t *testing.T) { - for _, filename := range files { - src, err := ioutil.ReadFile(filename) - if err != nil { - t.Fatal(err) - } - checkGood(t, filename, src) - } -} diff --git a/src/pkg/ebnf/parser.go b/src/pkg/ebnf/parser.go deleted file mode 100644 index ef2fac000..000000000 --- a/src/pkg/ebnf/parser.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ebnf - -import ( - "go/scanner" - "go/token" - "os" - "strconv" -) - -type parser struct { - fset *token.FileSet - scanner.ErrorVector - scanner scanner.Scanner - pos token.Pos // token position - tok token.Token // one token look-ahead - lit string // token literal -} - -func (p *parser) next() { - p.pos, p.tok, p.lit = p.scanner.Scan() - if p.tok.IsKeyword() { - // TODO Should keyword mapping always happen outside scanner? - // Or should there be a flag to scanner to enable keyword mapping? - p.tok = token.IDENT - } -} - -func (p *parser) error(pos token.Pos, msg string) { - p.Error(p.fset.Position(pos), msg) -} - -func (p *parser) errorExpected(pos token.Pos, msg string) { - msg = "expected " + msg - if pos == p.pos { - // the error happened at the current position; - // make the error message more specific - msg += ", found '" + p.tok.String() + "'" - if p.tok.IsLiteral() { - msg += " " + p.lit - } - } - p.error(pos, msg) -} - -func (p *parser) expect(tok token.Token) token.Pos { - pos := p.pos - if p.tok != tok { - p.errorExpected(pos, "'"+tok.String()+"'") - } - p.next() // make progress in any case - return pos -} - -func (p *parser) parseIdentifier() *Name { - pos := p.pos - name := p.lit - p.expect(token.IDENT) - return &Name{pos, name} -} - -func (p *parser) parseToken() *Token { - pos := p.pos - value := "" - if p.tok == token.STRING { - value, _ = strconv.Unquote(p.lit) - // Unquote may fail with an error, but only if the scanner found - // an illegal string in the first place. In this case the error - // has already been reported. - p.next() - } else { - p.expect(token.STRING) - } - return &Token{pos, value} -} - -// ParseTerm returns nil if no term was found. -func (p *parser) parseTerm() (x Expression) { - pos := p.pos - - switch p.tok { - case token.IDENT: - x = p.parseIdentifier() - - case token.STRING: - tok := p.parseToken() - x = tok - const ellipsis = "…" // U+2026, the horizontal ellipsis character - if p.tok == token.ILLEGAL && p.lit == ellipsis { - p.next() - x = &Range{tok, p.parseToken()} - } - - case token.LPAREN: - p.next() - x = &Group{pos, p.parseExpression()} - p.expect(token.RPAREN) - - case token.LBRACK: - p.next() - x = &Option{pos, p.parseExpression()} - p.expect(token.RBRACK) - - case token.LBRACE: - p.next() - x = &Repetition{pos, p.parseExpression()} - p.expect(token.RBRACE) - } - - return x -} - -func (p *parser) parseSequence() Expression { - var list Sequence - - for x := p.parseTerm(); x != nil; x = p.parseTerm() { - list = append(list, x) - } - - // no need for a sequence if list.Len() < 2 - switch len(list) { - case 0: - p.errorExpected(p.pos, "term") - return &Bad{p.pos, "term expected"} - case 1: - return list[0] - } - - return list -} - -func (p *parser) parseExpression() Expression { - var list Alternative - - for { - list = append(list, p.parseSequence()) - if p.tok != token.OR { - break - } - p.next() - } - // len(list) > 0 - - // no need for an Alternative node if list.Len() < 2 - if len(list) == 1 { - return list[0] - } - - return list -} - -func (p *parser) parseProduction() *Production { - name := p.parseIdentifier() - p.expect(token.ASSIGN) - var expr Expression - if p.tok != token.PERIOD { - expr = p.parseExpression() - } - p.expect(token.PERIOD) - return &Production{name, expr} -} - -func (p *parser) parse(fset *token.FileSet, filename string, src []byte) Grammar { - // initialize parser - p.fset = fset - p.ErrorVector.Reset() - p.scanner.Init(fset.AddFile(filename, fset.Base(), len(src)), src, p, scanner.AllowIllegalChars) - p.next() // initializes pos, tok, lit - - grammar := make(Grammar) - for p.tok != token.EOF { - prod := p.parseProduction() - name := prod.Name.String - if _, found := grammar[name]; !found { - grammar[name] = prod - } else { - p.error(prod.Pos(), name+" declared already") - } - } - - return grammar -} - -// Parse parses a set of EBNF productions from source src. -// It returns a set of productions. Errors are reported -// for incorrect syntax and if a production is declared -// more than once. Position information is recorded relative -// to the file set fset. -// -func Parse(fset *token.FileSet, filename string, src []byte) (Grammar, os.Error) { - var p parser - grammar := p.parse(fset, filename, src) - return grammar, p.GetError(scanner.Sorted) -} diff --git a/src/pkg/encoding/ascii85/Makefile b/src/pkg/encoding/ascii85/Makefile deleted file mode 100644 index 412383efd..000000000 --- a/src/pkg/encoding/ascii85/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=encoding/ascii85 -GOFILES=\ - ascii85.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/ascii85/ascii85.go b/src/pkg/encoding/ascii85/ascii85.go index ead0c2475..7d004b5e5 100644 --- a/src/pkg/encoding/ascii85/ascii85.go +++ b/src/pkg/encoding/ascii85/ascii85.go @@ -8,7 +8,6 @@ package ascii85 import ( "io" - "os" "strconv" ) @@ -93,14 +92,14 @@ func MaxEncodedLen(n int) int { return (n + 3) / 4 * 5 } func NewEncoder(w io.Writer) io.WriteCloser { return &encoder{w: w} } type encoder struct { - err os.Error + err error w io.Writer buf [4]byte // buffered data waiting to be encoded nbuf int // number of bytes in buf out [1024]byte // output buffer } -func (e *encoder) Write(p []byte) (n int, err os.Error) { +func (e *encoder) Write(p []byte) (n int, err error) { if e.err != nil { return 0, e.err } @@ -152,7 +151,7 @@ func (e *encoder) Write(p []byte) (n int, err os.Error) { // Close flushes any pending output from the encoder. // It is an error to call Write after calling Close. -func (e *encoder) Close() os.Error { +func (e *encoder) Close() error { // If there's anything left in the buffer, flush it out if e.err == nil && e.nbuf > 0 { nout := Encode(e.out[0:], e.buf[0:e.nbuf]) @@ -168,8 +167,8 @@ func (e *encoder) Close() os.Error { type CorruptInputError int64 -func (e CorruptInputError) String() string { - return "illegal ascii85 data at input byte " + strconv.Itoa64(int64(e)) +func (e CorruptInputError) Error() string { + return "illegal ascii85 data at input byte " + strconv.FormatInt(int64(e), 10) } // Decode decodes src into dst, returning both the number @@ -186,7 +185,7 @@ func (e CorruptInputError) String() string { // // NewDecoder wraps an io.Reader interface around Decode. // -func Decode(dst, src []byte, flush bool) (ndst, nsrc int, err os.Error) { +func Decode(dst, src []byte, flush bool) (ndst, nsrc int, err error) { var v uint32 var nb int for i, b := range src { @@ -246,8 +245,8 @@ func Decode(dst, src []byte, flush bool) (ndst, nsrc int, err os.Error) { func NewDecoder(r io.Reader) io.Reader { return &decoder{r: r} } type decoder struct { - err os.Error - readErr os.Error + err error + readErr error r io.Reader end bool // saw end of message buf [1024]byte // leftover input @@ -256,7 +255,7 @@ type decoder struct { outbuf [1024]byte } -func (d *decoder) Read(p []byte) (n int, err os.Error) { +func (d *decoder) Read(p []byte) (n int, err error) { if len(p) == 0 { return 0, nil } diff --git a/src/pkg/encoding/ascii85/ascii85_test.go b/src/pkg/encoding/ascii85/ascii85_test.go index fdfeb889f..70e67d8b0 100644 --- a/src/pkg/encoding/ascii85/ascii85_test.go +++ b/src/pkg/encoding/ascii85/ascii85_test.go @@ -6,8 +6,8 @@ package ascii85 import ( "bytes" + "io" "io/ioutil" - "os" "testing" ) @@ -83,11 +83,11 @@ func TestEncoderBuffering(t *testing.T) { end = len(input) } n, err := encoder.Write(input[pos:end]) - testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, os.Error(nil)) + testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, error(nil)) testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) } err := encoder.Close() - testEqual(t, "Close gave error %v, want %v", err, os.Error(nil)) + testEqual(t, "Close gave error %v, want %v", err, error(nil)) testEqual(t, "Encoding/%d of %q = %q, want %q", bs, bigtest.decoded, strip85(bb.String()), strip85(bigtest.encoded)) } } @@ -96,7 +96,7 @@ func TestDecode(t *testing.T) { for _, p := range pairs { dbuf := make([]byte, 4*len(p.encoded)) ndst, nsrc, err := Decode(dbuf, []byte(p.encoded), true) - testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) + testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, error(nil)) testEqual(t, "Decode(%q) = nsrc %v, want %v", p.encoded, nsrc, len(p.encoded)) testEqual(t, "Decode(%q) = ndst %v, want %v", p.encoded, ndst, len(p.decoded)) testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:ndst]), p.decoded) @@ -113,7 +113,7 @@ func TestDecoder(t *testing.T) { testEqual(t, "Read from %q = length %v, want %v", p.encoded, len(dbuf), len(p.decoded)) testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf), p.decoded) if err != nil { - testEqual(t, "Read from %q = %v, want %v", p.encoded, err, os.EOF) + testEqual(t, "Read from %q = %v, want %v", p.encoded, err, io.EOF) } } } @@ -125,7 +125,7 @@ func TestDecoderBuffering(t *testing.T) { var total int for total = 0; total < len(bigtest.decoded); { n, err := decoder.Read(buf[total : total+bs]) - testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, os.Error(nil)) + testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, error(nil)) total += n } testEqual(t, "Decoding/%d of %q = %q, want %q", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded) diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go new file mode 100644 index 000000000..3bf81a68c --- /dev/null +++ b/src/pkg/encoding/asn1/asn1.go @@ -0,0 +1,846 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package asn1 implements parsing of DER-encoded ASN.1 data structures, +// as defined in ITU-T Rec X.690. +// +// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// http://luca.ntop.org/Teaching/Appunti/asn1.html. +package asn1 + +// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc +// are different encoding formats for those objects. Here, we'll be dealing +// with DER, the Distinguished Encoding Rules. DER is used in X.509 because +// it's fast to parse and, unlike BER, has a unique encoding for every object. +// When calculating hashes over objects, it's important that the resulting +// bytes be the same at both ends and DER removes this margin of error. +// +// ASN.1 is very complex and this package doesn't attempt to implement +// everything by any means. + +import ( + "fmt" + "math/big" + "reflect" + "time" +) + +// A StructuralError suggests that the ASN.1 data is valid, but the Go type +// which is receiving it doesn't match. +type StructuralError struct { + Msg string +} + +func (e StructuralError) Error() string { return "ASN.1 structure error: " + e.Msg } + +// A SyntaxError suggests that the ASN.1 data is invalid. +type SyntaxError struct { + Msg string +} + +func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg } + +// We start by dealing with each of the primitive types in turn. + +// BOOLEAN + +func parseBool(bytes []byte) (ret bool, err error) { + if len(bytes) != 1 { + err = SyntaxError{"invalid boolean"} + return + } + + return bytes[0] != 0, nil +} + +// INTEGER + +// parseInt64 treats the given bytes as a big-endian, signed integer and +// returns the result. +func parseInt64(bytes []byte) (ret int64, err error) { + if len(bytes) > 8 { + // We'll overflow an int64 in this case. + err = StructuralError{"integer too large"} + return + } + for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { + ret <<= 8 + ret |= int64(bytes[bytesRead]) + } + + // Shift up and down in order to sign extend the result. + ret <<= 64 - uint8(len(bytes))*8 + ret >>= 64 - uint8(len(bytes))*8 + return +} + +// parseInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseInt(bytes []byte) (int, error) { + ret64, err := parseInt64(bytes) + if err != nil { + return 0, err + } + if ret64 != int64(int(ret64)) { + return 0, StructuralError{"integer too large"} + } + return int(ret64), nil +} + +var bigOne = big.NewInt(1) + +// parseBigInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseBigInt(bytes []byte) *big.Int { + ret := new(big.Int) + if len(bytes) > 0 && bytes[0]&0x80 == 0x80 { + // This is a negative number. + notBytes := make([]byte, len(bytes)) + for i := range notBytes { + notBytes[i] = ^bytes[i] + } + ret.SetBytes(notBytes) + ret.Add(ret, bigOne) + ret.Neg(ret) + return ret + } + ret.SetBytes(bytes) + return ret +} + +// BIT STRING + +// BitString is the structure to use when you want an ASN.1 BIT STRING type. A +// bit string is padded up to the nearest byte in memory and the number of +// valid bits is recorded. Padding bits will be zero. +type BitString struct { + Bytes []byte // bits packed into bytes. + BitLength int // length in bits. +} + +// At returns the bit at the given index. If the index is out of range it +// returns false. +func (b BitString) At(i int) int { + if i < 0 || i >= b.BitLength { + return 0 + } + x := i / 8 + y := 7 - uint(i%8) + return int(b.Bytes[x]>>y) & 1 +} + +// RightAlign returns a slice where the padding bits are at the beginning. The +// slice may share memory with the BitString. +func (b BitString) RightAlign() []byte { + shift := uint(8 - (b.BitLength % 8)) + if shift == 8 || len(b.Bytes) == 0 { + return b.Bytes + } + + a := make([]byte, len(b.Bytes)) + a[0] = b.Bytes[0] >> shift + for i := 1; i < len(b.Bytes); i++ { + a[i] = b.Bytes[i-1] << (8 - shift) + a[i] |= b.Bytes[i] >> shift + } + + return a +} + +// parseBitString parses an ASN.1 bit string from the given byte slice and returns it. +func parseBitString(bytes []byte) (ret BitString, err error) { + if len(bytes) == 0 { + err = SyntaxError{"zero length BIT STRING"} + return + } + paddingBits := int(bytes[0]) + if paddingBits > 7 || + len(bytes) == 1 && paddingBits > 0 || + bytes[len(bytes)-1]&((1< 4 { + err = StructuralError{"base 128 integer too large"} + return + } + ret <<= 7 + b := bytes[offset] + ret |= int(b & 0x7f) + offset++ + if b&0x80 == 0 { + return + } + } + err = SyntaxError{"truncated base 128 integer"} + return +} + +// UTCTime + +func parseUTCTime(bytes []byte) (ret time.Time, err error) { + s := string(bytes) + ret, err = time.Parse("0601021504Z0700", s) + if err != nil { + ret, err = time.Parse("060102150405Z0700", s) + } + if err == nil && ret.Year() >= 2050 { + // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 + ret = ret.AddDate(-100, 0, 0) + } + + return +} + +// parseGeneralizedTime parses the GeneralizedTime from the given byte slice +// and returns the resulting time. +func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) { + return time.Parse("20060102150405Z0700", string(bytes)) +} + +// PrintableString + +// parsePrintableString parses a ASN.1 PrintableString from the given byte +// array and returns it. +func parsePrintableString(bytes []byte) (ret string, err error) { + for _, b := range bytes { + if !isPrintable(b) { + err = SyntaxError{"PrintableString contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// isPrintable returns true iff the given b is in the ASN.1 PrintableString set. +func isPrintable(b byte) bool { + return 'a' <= b && b <= 'z' || + 'A' <= b && b <= 'Z' || + '0' <= b && b <= '9' || + '\'' <= b && b <= ')' || + '+' <= b && b <= '/' || + b == ' ' || + b == ':' || + b == '=' || + b == '?' || + // This is technically not allowed in a PrintableString. + // However, x509 certificates with wildcard strings don't + // always use the correct string type so we permit it. + b == '*' +} + +// IA5String + +// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given +// byte slice and returns it. +func parseIA5String(bytes []byte) (ret string, err error) { + for _, b := range bytes { + if b >= 0x80 { + err = SyntaxError{"IA5String contains invalid character"} + return + } + } + ret = string(bytes) + return +} + +// T61String + +// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// byte slice and returns it. +func parseT61String(bytes []byte) (ret string, err error) { + return string(bytes), nil +} + +// UTF8String + +// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte +// array and returns it. +func parseUTF8String(bytes []byte) (ret string, err error) { + return string(bytes), nil +} + +// A RawValue represents an undecoded ASN.1 object. +type RawValue struct { + Class, Tag int + IsCompound bool + Bytes []byte + FullBytes []byte // includes the tag and length +} + +// RawContent is used to signal that the undecoded, DER data needs to be +// preserved for a struct. To use it, the first field of the struct must have +// this type. It's an error for any of the other fields to have this type. +type RawContent []byte + +// Tagging + +// parseTagAndLength parses an ASN.1 tag and length pair from the given offset +// into a byte slice. It returns the parsed data and the new offset. SET and +// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we +// don't distinguish between ordered and unordered objects in this code. +func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err error) { + offset = initOffset + b := bytes[offset] + offset++ + ret.class = int(b >> 6) + ret.isCompound = b&0x20 == 0x20 + ret.tag = int(b & 0x1f) + + // If the bottom five bits are set, then the tag number is actually base 128 + // encoded afterwards + if ret.tag == 0x1f { + ret.tag, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + } + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if b&0x80 == 0 { + // The length is encoded in the bottom 7 bits. + ret.length = int(b & 0x7f) + } else { + // Bottom 7 bits give the number of length bytes to follow. + numBytes := int(b & 0x7f) + // We risk overflowing a signed 32-bit number if we accept more than 3 bytes. + if numBytes > 3 { + err = StructuralError{"length too large"} + return + } + if numBytes == 0 { + err = SyntaxError{"indefinite length found (not DER)"} + return + } + ret.length = 0 + for i := 0; i < numBytes; i++ { + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + ret.length <<= 8 + ret.length |= int(b) + } + } + + return +} + +// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse +// a number of ASN.1 values from the given byte slice and returns them as a +// slice of Go values of the given type. +func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err error) { + expectedTag, compoundType, ok := getUniversalType(elemType) + if !ok { + err = StructuralError{"unknown Go type for slice"} + return + } + + // First we iterate over the input and count the number of elements, + // checking that the types are correct in each case. + numElements := 0 + for offset := 0; offset < len(bytes); { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so + // that a sequence of them can be parsed into a []string. + if t.tag == tagGeneralString { + t.tag = tagPrintableString + } + if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag { + err = StructuralError{"sequence tag mismatch"} + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"truncated sequence"} + return + } + offset += t.length + numElements++ + } + ret = reflect.MakeSlice(sliceType, numElements, numElements) + params := fieldParameters{} + offset := 0 + for i := 0; i < numElements; i++ { + offset, err = parseField(ret.Index(i), bytes, offset, params) + if err != nil { + return + } + } + return +} + +var ( + bitStringType = reflect.TypeOf(BitString{}) + objectIdentifierType = reflect.TypeOf(ObjectIdentifier{}) + enumeratedType = reflect.TypeOf(Enumerated(0)) + flagType = reflect.TypeOf(Flag(false)) + timeType = reflect.TypeOf(time.Time{}) + rawValueType = reflect.TypeOf(RawValue{}) + rawContentsType = reflect.TypeOf(RawContent(nil)) + bigIntType = reflect.TypeOf(new(big.Int)) +) + +// invalidLength returns true iff offset + length > sliceLength, or if the +// addition would overflow. +func invalidLength(offset, length, sliceLength int) bool { + return offset+length < offset || offset+length > sliceLength +} + +// parseField is the main parsing function. Given a byte slice and an offset +// into the array, it will try to parse a suitable ASN.1 value out and store it +// in the given Value. +func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err error) { + offset = initOffset + fieldType := v.Type() + + // If we have run out of data, it may be that there are optional elements at the end. + if offset == len(bytes) { + if !setDefaultValue(v, params) { + err = SyntaxError{"sequence truncated"} + } + return + } + + // Deal with raw values. + if fieldType == rawValueType { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} + offset += t.length + v.Set(reflect.ValueOf(result)) + return + } + + // Deal with the ANY type. + if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 { + var t tagAndLength + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + var result interface{} + if !t.isCompound && t.class == classUniversal { + innerBytes := bytes[offset : offset+t.length] + switch t.tag { + case tagPrintableString: + result, err = parsePrintableString(innerBytes) + case tagIA5String: + result, err = parseIA5String(innerBytes) + case tagT61String: + result, err = parseT61String(innerBytes) + case tagUTF8String: + result, err = parseUTF8String(innerBytes) + case tagInteger: + result, err = parseInt64(innerBytes) + case tagBitString: + result, err = parseBitString(innerBytes) + case tagOID: + result, err = parseObjectIdentifier(innerBytes) + case tagUTCTime: + result, err = parseUTCTime(innerBytes) + case tagOctetString: + result = innerBytes + default: + // If we don't know how to handle the type, we just leave Value as nil. + } + } + offset += t.length + if err != nil { + return + } + if result != nil { + v.Set(reflect.ValueOf(result)) + } + return + } + universalTag, compoundType, ok1 := getUniversalType(fieldType) + if !ok1 { + err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)} + return + } + + t, offset, err := parseTagAndLength(bytes, offset) + if err != nil { + return + } + if params.explicit { + expectedClass := classContextSpecific + if params.application { + expectedClass = classApplication + } + if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) { + if t.length > 0 { + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + } else { + if fieldType != flagType { + err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + return + } + v.SetBool(true) + return + } + } else { + // The tags didn't match, it might be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{"explicitly tagged member didn't match"} + } + return + } + } + + // Special case for strings: all the ASN.1 string types map to the Go + // type string. getUniversalType returns the tag for PrintableString + // when it sees a string, so if we see a different string type on the + // wire, we change the universal type to match. + if universalTag == tagPrintableString { + switch t.tag { + case tagIA5String, tagGeneralString, tagT61String, tagUTF8String: + universalTag = t.tag + } + } + + // Special case for time: UTCTime and GeneralizedTime both map to the + // Go type time.Time. + if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { + universalTag = tagGeneralizedTime + } + + expectedClass := classUniversal + expectedTag := universalTag + + if !params.explicit && params.tag != nil { + expectedClass = classContextSpecific + expectedTag = *params.tag + } + + if !params.explicit && params.application && params.tag != nil { + expectedClass = classApplication + expectedTag = *params.tag + } + + // We have unwrapped any explicit tagging at this point. + if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType { + // Tags don't match. Again, it could be an optional element. + ok := setDefaultValue(v, params) + if ok { + offset = initOffset + } else { + err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)} + } + return + } + if invalidLength(offset, t.length, len(bytes)) { + err = SyntaxError{"data truncated"} + return + } + innerBytes := bytes[offset : offset+t.length] + offset += t.length + + // We deal with the structures defined in this package first. + switch fieldType { + case objectIdentifierType: + newSlice, err1 := parseObjectIdentifier(innerBytes) + v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice))) + if err1 == nil { + reflect.Copy(v, reflect.ValueOf(newSlice)) + } + err = err1 + return + case bitStringType: + bs, err1 := parseBitString(innerBytes) + if err1 == nil { + v.Set(reflect.ValueOf(bs)) + } + err = err1 + return + case timeType: + var time time.Time + var err1 error + if universalTag == tagUTCTime { + time, err1 = parseUTCTime(innerBytes) + } else { + time, err1 = parseGeneralizedTime(innerBytes) + } + if err1 == nil { + v.Set(reflect.ValueOf(time)) + } + err = err1 + return + case enumeratedType: + parsedInt, err1 := parseInt(innerBytes) + if err1 == nil { + v.SetInt(int64(parsedInt)) + } + err = err1 + return + case flagType: + v.SetBool(true) + return + case bigIntType: + parsedInt := parseBigInt(innerBytes) + v.Set(reflect.ValueOf(parsedInt)) + return + } + switch val := v; val.Kind() { + case reflect.Bool: + parsedBool, err1 := parseBool(innerBytes) + if err1 == nil { + val.SetBool(parsedBool) + } + err = err1 + return + case reflect.Int, reflect.Int32: + parsedInt, err1 := parseInt(innerBytes) + if err1 == nil { + val.SetInt(int64(parsedInt)) + } + err = err1 + return + case reflect.Int64: + parsedInt, err1 := parseInt64(innerBytes) + if err1 == nil { + val.SetInt(parsedInt) + } + err = err1 + return + // TODO(dfc) Add support for the remaining integer types + case reflect.Struct: + structType := fieldType + + if structType.NumField() > 0 && + structType.Field(0).Type == rawContentsType { + bytes := bytes[initOffset:offset] + val.Field(0).Set(reflect.ValueOf(RawContent(bytes))) + } + + innerOffset := 0 + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if i == 0 && field.Type == rawContentsType { + continue + } + innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1"))) + if err != nil { + return + } + } + // We allow extra bytes at the end of the SEQUENCE because + // adding elements to the end has been used in X.509 as the + // version numbers have increased. + return + case reflect.Slice: + sliceType := fieldType + if sliceType.Elem().Kind() == reflect.Uint8 { + val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) + reflect.Copy(val, reflect.ValueOf(innerBytes)) + return + } + newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) + if err1 == nil { + val.Set(newSlice) + } + err = err1 + return + case reflect.String: + var v string + switch universalTag { + case tagPrintableString: + v, err = parsePrintableString(innerBytes) + case tagIA5String: + v, err = parseIA5String(innerBytes) + case tagT61String: + v, err = parseT61String(innerBytes) + case tagUTF8String: + v, err = parseUTF8String(innerBytes) + case tagGeneralString: + // GeneralString is specified in ISO-2022/ECMA-35, + // A brief review suggests that it includes structures + // that allow the encoding to change midstring and + // such. We give up and pass it as an 8-bit string. + v, err = parseT61String(innerBytes) + default: + err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} + } + if err == nil { + val.SetString(v) + } + return + } + err = StructuralError{"unsupported: " + v.Type().String()} + return +} + +// setDefaultValue is used to install a default value, from a tag string, into +// a Value. It is successful is the field was optional, even if a default value +// wasn't provided or it failed to install it into the Value. +func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { + if !params.optional { + return + } + ok = true + if params.defaultValue == nil { + return + } + switch val := v; val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + val.SetInt(*params.defaultValue) + } + return +} + +// Unmarshal parses the DER-encoded ASN.1 data structure b +// and uses the reflect package to fill in an arbitrary value pointed at by val. +// Because Unmarshal uses the reflect package, the structs +// being written to must use upper case field names. +// +// An ASN.1 INTEGER can be written to an int, int32, int64, +// or *big.Int (from the math/big package). +// If the encoded value does not fit in the Go type, +// Unmarshal returns a parse error. +// +// An ASN.1 BIT STRING can be written to a BitString. +// +// An ASN.1 OCTET STRING can be written to a []byte. +// +// An ASN.1 OBJECT IDENTIFIER can be written to an +// ObjectIdentifier. +// +// An ASN.1 ENUMERATED can be written to an Enumerated. +// +// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time. +// +// An ASN.1 PrintableString or IA5String can be written to a string. +// +// Any of the above ASN.1 values can be written to an interface{}. +// The value stored in the interface has the corresponding Go type. +// For integers, that type is int64. +// +// An ASN.1 SEQUENCE OF x or SET OF x can be written +// to a slice if an x can be written to the slice's element type. +// +// An ASN.1 SEQUENCE or SET can be written to a struct +// if each of the elements in the sequence can be +// written to the corresponding element in the struct. +// +// The following tags on struct fields have special meaning to Unmarshal: +// +// optional marks the field as ASN.1 OPTIONAL +// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC +// default:x sets the default value for optional integer fields +// +// If the type of the first field of a structure is RawContent then the raw +// ASN1 contents of the struct will be stored in it. +// +// Other ASN.1 types are not supported; if it encounters them, +// Unmarshal returns a parse error. +func Unmarshal(b []byte, val interface{}) (rest []byte, err error) { + return UnmarshalWithParams(b, val, "") +} + +// UnmarshalWithParams allows field parameters to be specified for the +// top-level element. The form of the params is the same as the field tags. +func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err error) { + v := reflect.ValueOf(val).Elem() + offset, err := parseField(v, b, 0, parseFieldParameters(params)) + if err != nil { + return nil, err + } + return b[offset:], nil +} diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go new file mode 100644 index 000000000..93803f435 --- /dev/null +++ b/src/pkg/encoding/asn1/asn1_test.go @@ -0,0 +1,706 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package asn1 + +import ( + "bytes" + "math/big" + "reflect" + "testing" + "time" +) + +type int64Test struct { + in []byte + ok bool + out int64 +} + +var int64TestData = []int64Test{ + {[]byte{0x00}, true, 0}, + {[]byte{0x7f}, true, 127}, + {[]byte{0x00, 0x80}, true, 128}, + {[]byte{0x01, 0x00}, true, 256}, + {[]byte{0x80}, true, -128}, + {[]byte{0xff, 0x7f}, true, -129}, + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, true, -1}, + {[]byte{0xff}, true, -1}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0}, +} + +func TestParseInt64(t *testing.T) { + for i, test := range int64TestData { + ret, err := parseInt64(test.in) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if test.ok && ret != test.out { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } +} + +type int32Test struct { + in []byte + ok bool + out int32 +} + +var int32TestData = []int32Test{ + {[]byte{0x00}, true, 0}, + {[]byte{0x7f}, true, 127}, + {[]byte{0x00, 0x80}, true, 128}, + {[]byte{0x01, 0x00}, true, 256}, + {[]byte{0x80}, true, -128}, + {[]byte{0xff, 0x7f}, true, -129}, + {[]byte{0xff, 0xff, 0xff, 0xff}, true, -1}, + {[]byte{0xff}, true, -1}, + {[]byte{0x80, 0x00, 0x00, 0x00}, true, -2147483648}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00}, false, 0}, +} + +func TestParseInt32(t *testing.T) { + for i, test := range int32TestData { + ret, err := parseInt(test.in) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if test.ok && int32(ret) != test.out { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } +} + +var bigIntTests = []struct { + in []byte + base10 string +}{ + {[]byte{0xff}, "-1"}, + {[]byte{0x00}, "0"}, + {[]byte{0x01}, "1"}, + {[]byte{0x00, 0xff}, "255"}, + {[]byte{0xff, 0x00}, "-256"}, + {[]byte{0x01, 0x00}, "256"}, +} + +func TestParseBigInt(t *testing.T) { + for i, test := range bigIntTests { + ret := parseBigInt(test.in) + if ret.String() != test.base10 { + t.Errorf("#%d: bad result from %x, got %s want %s", i, test.in, ret.String(), test.base10) + } + fw := newForkableWriter() + marshalBigInt(fw, ret) + result := fw.Bytes() + if !bytes.Equal(result, test.in) { + t.Errorf("#%d: got %x from marshaling %s, want %x", i, result, ret, test.in) + } + } +} + +type bitStringTest struct { + in []byte + ok bool + out []byte + bitLength int +} + +var bitStringTestData = []bitStringTest{ + {[]byte{}, false, []byte{}, 0}, + {[]byte{0x00}, true, []byte{}, 0}, + {[]byte{0x07, 0x00}, true, []byte{0x00}, 1}, + {[]byte{0x07, 0x01}, false, []byte{}, 0}, + {[]byte{0x07, 0x40}, false, []byte{}, 0}, + {[]byte{0x08, 0x00}, false, []byte{}, 0}, +} + +func TestBitString(t *testing.T) { + for i, test := range bitStringTestData { + ret, err := parseBitString(test.in) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if err == nil { + if test.bitLength != ret.BitLength || bytes.Compare(ret.Bytes, test.out) != 0 { + t.Errorf("#%d: Bad result: %v (expected %v %v)", i, ret, test.out, test.bitLength) + } + } + } +} + +func TestBitStringAt(t *testing.T) { + bs := BitString{[]byte{0x82, 0x40}, 16} + if bs.At(0) != 1 { + t.Error("#1: Failed") + } + if bs.At(1) != 0 { + t.Error("#2: Failed") + } + if bs.At(6) != 1 { + t.Error("#3: Failed") + } + if bs.At(9) != 1 { + t.Error("#4: Failed") + } +} + +type bitStringRightAlignTest struct { + in []byte + inlen int + out []byte +} + +var bitStringRightAlignTests = []bitStringRightAlignTest{ + {[]byte{0x80}, 1, []byte{0x01}}, + {[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}}, + {[]byte{}, 0, []byte{}}, + {[]byte{0xce}, 8, []byte{0xce}}, + {[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}}, + {[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}}, +} + +func TestBitStringRightAlign(t *testing.T) { + for i, test := range bitStringRightAlignTests { + bs := BitString{test.in, test.inlen} + out := bs.RightAlign() + if bytes.Compare(out, test.out) != 0 { + t.Errorf("#%d got: %x want: %x", i, out, test.out) + } + } +} + +type objectIdentifierTest struct { + in []byte + ok bool + out []int +} + +var objectIdentifierTestData = []objectIdentifierTest{ + {[]byte{}, false, []int{}}, + {[]byte{85}, true, []int{2, 5}}, + {[]byte{85, 0x02}, true, []int{2, 5, 2}}, + {[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}}, + {[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}}, +} + +func TestObjectIdentifier(t *testing.T) { + for i, test := range objectIdentifierTestData { + ret, err := parseObjectIdentifier(test.in) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if err == nil { + if !reflect.DeepEqual(test.out, ret) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } + } +} + +type timeTest struct { + in string + ok bool + out time.Time +} + +var utcTestData = []timeTest{ + {"910506164540-0700", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", -7*60*60))}, + {"910506164540+0730", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", 7*60*60+30*60))}, + {"910506234540Z", true, time.Date(1991, 05, 06, 23, 45, 40, 0, time.UTC)}, + {"9105062345Z", true, time.Date(1991, 05, 06, 23, 45, 0, 0, time.UTC)}, + {"a10506234540Z", false, time.Time{}}, + {"91a506234540Z", false, time.Time{}}, + {"9105a6234540Z", false, time.Time{}}, + {"910506a34540Z", false, time.Time{}}, + {"910506334a40Z", false, time.Time{}}, + {"91050633444aZ", false, time.Time{}}, + {"910506334461Z", false, time.Time{}}, + {"910506334400Za", false, time.Time{}}, +} + +func TestUTCTime(t *testing.T) { + for i, test := range utcTestData { + ret, err := parseUTCTime([]byte(test.in)) + if err != nil { + if test.ok { + t.Errorf("#%d: parseUTCTime(%q) = error %v", i, test.in, err) + } + continue + } + if !test.ok { + t.Errorf("#%d: parseUTCTime(%q) succeeded, should have failed", i, test.in) + continue + } + const format = "Jan _2 15:04:05 -0700 2006" // ignore zone name, just offset + have := ret.Format(format) + want := test.out.Format(format) + if have != want { + t.Errorf("#%d: parseUTCTime(%q) = %s, want %s", i, test.in, have, want) + } + } +} + +var generalizedTimeTestData = []timeTest{ + {"20100102030405Z", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.UTC)}, + {"20100102030405", false, time.Time{}}, + {"20100102030405+0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))}, + {"20100102030405-0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", -6*60*60-7*60))}, +} + +func TestGeneralizedTime(t *testing.T) { + for i, test := range generalizedTimeTestData { + ret, err := parseGeneralizedTime([]byte(test.in)) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if err == nil { + if !reflect.DeepEqual(test.out, ret) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } + } +} + +type tagAndLengthTest struct { + in []byte + ok bool + out tagAndLength +} + +var tagAndLengthData = []tagAndLengthTest{ + {[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}}, + {[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}}, + {[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}}, + {[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}}, + {[]byte{0x1f, 0x01, 0x00}, true, tagAndLength{0, 1, 0, false}}, + {[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}}, + {[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}}, + {[]byte{0x00, 0x81, 0x01}, true, tagAndLength{0, 0, 1, false}}, + {[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}}, + {[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}}, + {[]byte{0x1f, 0x85}, false, tagAndLength{}}, + {[]byte{0x30, 0x80}, false, tagAndLength{}}, +} + +func TestParseTagAndLength(t *testing.T) { + for i, test := range tagAndLengthData { + tagAndLength, _, err := parseTagAndLength(test.in, 0) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did pass? %v, expected: %v)", i, err == nil, test.ok) + } + if err == nil && !reflect.DeepEqual(test.out, tagAndLength) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, tagAndLength, test.out) + } + } +} + +type parseFieldParametersTest struct { + in string + out fieldParameters +} + +func newInt(n int) *int { return &n } + +func newInt64(n int64) *int64 { return &n } + +func newString(s string) *string { return &s } + +func newBool(b bool) *bool { return &b } + +var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParametersTest{ + {"", fieldParameters{}}, + {"ia5", fieldParameters{stringType: tagIA5String}}, + {"printable", fieldParameters{stringType: tagPrintableString}}, + {"optional", fieldParameters{optional: true}}, + {"explicit", fieldParameters{explicit: true, tag: new(int)}}, + {"application", fieldParameters{application: true, tag: new(int)}}, + {"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}}, + {"default:42", fieldParameters{defaultValue: newInt64(42)}}, + {"tag:17", fieldParameters{tag: newInt(17)}}, + {"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}}, + {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false, false}}, + {"set", fieldParameters{set: true}}, +} + +func TestParseFieldParameters(t *testing.T) { + for i, test := range parseFieldParametersTestData { + f := parseFieldParameters(test.in) + if !reflect.DeepEqual(f, test.out) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, f, test.out) + } + } +} + +type TestObjectIdentifierStruct struct { + OID ObjectIdentifier +} + +type TestContextSpecificTags struct { + A int `asn1:"tag:1"` +} + +type TestContextSpecificTags2 struct { + A int `asn1:"explicit,tag:1"` + B int +} + +type TestElementsAfterString struct { + S string + A, B int +} + +type TestBigInt struct { + X *big.Int +} + +var unmarshalTestData = []struct { + in []byte + out interface{} +}{ + {[]byte{0x02, 0x01, 0x42}, newInt(0x42)}, + {[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}}, + {[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}}, + {[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}}, + {[]byte{0x02, 0x01, 0x10}, newInt(16)}, + {[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")}, + {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")}, + {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test"), []byte("\x16\x04test")}}, + {[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}}, + {[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, + {[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, + {[]byte{0x01, 0x01, 0x00}, newBool(false)}, + {[]byte{0x01, 0x01, 0x01}, newBool(true)}, + {[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}}, + {[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}}, +} + +func TestUnmarshal(t *testing.T) { + for i, test := range unmarshalTestData { + pv := reflect.New(reflect.TypeOf(test.out).Elem()) + val := pv.Interface() + _, err := Unmarshal(test.in, val) + if err != nil { + t.Errorf("Unmarshal failed at index %d %v", i, err) + } + if !reflect.DeepEqual(val, test.out) { + t.Errorf("#%d:\nhave %#v\nwant %#v", i, val, test.out) + } + } +} + +type Certificate struct { + TBSCertificate TBSCertificate + SignatureAlgorithm AlgorithmIdentifier + SignatureValue BitString +} + +type TBSCertificate struct { + Version int `asn1:"optional,explicit,default:0,tag:0"` + SerialNumber RawValue + SignatureAlgorithm AlgorithmIdentifier + Issuer RDNSequence + Validity Validity + Subject RDNSequence + PublicKey PublicKeyInfo +} + +type AlgorithmIdentifier struct { + Algorithm ObjectIdentifier +} + +type RDNSequence []RelativeDistinguishedNameSET + +type RelativeDistinguishedNameSET []AttributeTypeAndValue + +type AttributeTypeAndValue struct { + Type ObjectIdentifier + Value interface{} +} + +type Validity struct { + NotBefore, NotAfter time.Time +} + +type PublicKeyInfo struct { + Algorithm AlgorithmIdentifier + PublicKey BitString +} + +func TestCertificate(t *testing.T) { + // This is a minimal, self-signed certificate that should parse correctly. + var cert Certificate + if _, err := Unmarshal(derEncodedSelfSignedCertBytes, &cert); err != nil { + t.Errorf("Unmarshal failed: %v", err) + } + if !reflect.DeepEqual(cert, derEncodedSelfSignedCert) { + t.Errorf("Bad result:\ngot: %+v\nwant: %+v", cert, derEncodedSelfSignedCert) + } +} + +func TestCertificateWithNUL(t *testing.T) { + // This is the paypal NUL-hack certificate. It should fail to parse because + // NUL isn't a permitted character in a PrintableString. + + var cert Certificate + if _, err := Unmarshal(derEncodedPaypalNULCertBytes, &cert); err == nil { + t.Error("Unmarshal succeeded, should not have") + } +} + +type rawStructTest struct { + Raw RawContent + A int +} + +func TestRawStructs(t *testing.T) { + var s rawStructTest + input := []byte{0x30, 0x03, 0x02, 0x01, 0x50} + + rest, err := Unmarshal(input, &s) + if len(rest) != 0 { + t.Errorf("incomplete parse: %x", rest) + return + } + if err != nil { + t.Error(err) + return + } + if s.A != 0x50 { + t.Errorf("bad value for A: got %d want %d", s.A, 0x50) + } + if bytes.Compare([]byte(s.Raw), input) != 0 { + t.Errorf("bad value for Raw: got %x want %x", s.Raw, input) + } +} + +var derEncodedSelfSignedCert = Certificate{ + TBSCertificate: TBSCertificate{ + Version: 0, + SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, + SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, + Issuer: RDNSequence{ + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, + }, + Validity: Validity{ + NotBefore: time.Date(2009, 10, 8, 00, 25, 53, 0, time.UTC), + NotAfter: time.Date(2010, 10, 8, 00, 25, 53, 0, time.UTC), + }, + Subject: RDNSequence{ + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, + RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, + }, + PublicKey: PublicKeyInfo{ + Algorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}}, + PublicKey: BitString{ + Bytes: []uint8{ + 0x30, 0x48, 0x2, 0x41, 0x0, 0xcd, 0xb7, + 0x63, 0x9c, 0x32, 0x78, 0xf0, 0x6, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, + 0x90, 0x2b, 0x59, 0x2d, 0x8c, 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, + 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, + 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, 0x96, 0x57, 0x72, 0x2a, 0x4f, + 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, 0xdc, 0x8f, 0xde, 0xec, + 0x35, 0x7d, 0x2, 0x3, 0x1, 0x0, 0x1, + }, + BitLength: 592, + }, + }, + }, + SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, + SignatureValue: BitString{ + Bytes: []uint8{ + 0xa6, 0x7b, 0x6, 0xec, 0x5e, 0xce, + 0x92, 0x77, 0x2c, 0xa4, 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, + 0x7b, 0x45, 0x11, 0xcd, 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x4, 0x2, 0xdf, 0x2b, + 0x99, 0x8b, 0xb9, 0xa4, 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, + 0xd9, 0x1e, 0xde, 0x14, 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, + 0xfa, 0x88, 0x21, 0x49, 0x4, 0x35, + }, + BitLength: 512, + }, +} + +var derEncodedSelfSignedCertBytes = []byte{ + 0x30, 0x82, 0x02, 0x18, 0x30, + 0x82, 0x01, 0xc2, 0x02, 0x09, 0x00, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, + 0x98, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, + 0x69, 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, + 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, + 0x30, 0x39, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, 0x33, 0x5a, + 0x17, 0x0d, 0x31, 0x30, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, + 0x33, 0x5a, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, 0x69, + 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x1a, + 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, 0x73, + 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x5c, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xcd, 0xb7, 0x63, 0x9c, 0x32, 0x78, + 0xf0, 0x06, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, 0x90, 0x2b, 0x59, 0x2d, 0x8c, + 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, + 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, + 0x96, 0x57, 0x72, 0x2a, 0x4f, 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, + 0xdc, 0x8f, 0xde, 0xec, 0x35, 0x7d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x41, 0x00, 0xa6, 0x7b, 0x06, 0xec, 0x5e, 0xce, 0x92, 0x77, 0x2c, 0xa4, + 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, 0x7b, 0x45, 0x11, 0xcd, + 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x04, 0x02, 0xdf, 0x2b, 0x99, 0x8b, 0xb9, 0xa4, + 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, 0xd9, 0x1e, 0xde, 0x14, + 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, 0xfa, 0x88, 0x21, 0x49, + 0x04, 0x35, +} + +var derEncodedPaypalNULCertBytes = []byte{ + 0x30, 0x82, 0x06, 0x44, 0x30, + 0x82, 0x05, 0xad, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x00, 0xf0, 0x9b, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x30, 0x82, 0x01, 0x12, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x09, 0x42, 0x61, 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x42, 0x61, + 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x20, 0x49, 0x50, 0x53, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x73, 0x2e, 0x6c, 0x2e, 0x31, 0x2e, + 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x14, 0x25, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, + 0x20, 0x43, 0x2e, 0x49, 0x2e, 0x46, 0x2e, 0x20, 0x20, 0x42, 0x2d, 0x42, 0x36, + 0x32, 0x32, 0x31, 0x30, 0x36, 0x39, 0x35, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x13, 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, + 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, + 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, + 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, + 0x01, 0x16, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, + 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, + 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, 0x17, 0x0d, + 0x31, 0x31, 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, + 0x30, 0x81, 0x94, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, + 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x53, 0x61, 0x6e, 0x20, + 0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x31, 0x11, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0b, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x55, 0x6e, 0x69, 0x74, 0x31, 0x2f, + 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x77, 0x77, 0x77, 0x2e, + 0x70, 0x61, 0x79, 0x70, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x73, 0x73, + 0x6c, 0x2e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x63, 0x63, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xd2, 0x69, + 0xfa, 0x6f, 0x3a, 0x00, 0xb4, 0x21, 0x1b, 0xc8, 0xb1, 0x02, 0xd7, 0x3f, 0x19, + 0xb2, 0xc4, 0x6d, 0xb4, 0x54, 0xf8, 0x8b, 0x8a, 0xcc, 0xdb, 0x72, 0xc2, 0x9e, + 0x3c, 0x60, 0xb9, 0xc6, 0x91, 0x3d, 0x82, 0xb7, 0x7d, 0x99, 0xff, 0xd1, 0x29, + 0x84, 0xc1, 0x73, 0x53, 0x9c, 0x82, 0xdd, 0xfc, 0x24, 0x8c, 0x77, 0xd5, 0x41, + 0xf3, 0xe8, 0x1e, 0x42, 0xa1, 0xad, 0x2d, 0x9e, 0xff, 0x5b, 0x10, 0x26, 0xce, + 0x9d, 0x57, 0x17, 0x73, 0x16, 0x23, 0x38, 0xc8, 0xd6, 0xf1, 0xba, 0xa3, 0x96, + 0x5b, 0x16, 0x67, 0x4a, 0x4f, 0x73, 0x97, 0x3a, 0x4d, 0x14, 0xa4, 0xf4, 0xe2, + 0x3f, 0x8b, 0x05, 0x83, 0x42, 0xd1, 0xd0, 0xdc, 0x2f, 0x7a, 0xe5, 0xb6, 0x10, + 0xb2, 0x11, 0xc0, 0xdc, 0x21, 0x2a, 0x90, 0xff, 0xae, 0x97, 0x71, 0x5a, 0x49, + 0x81, 0xac, 0x40, 0xf3, 0x3b, 0xb8, 0x59, 0xb2, 0x4f, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x82, 0x03, 0x21, 0x30, 0x82, 0x03, 0x1d, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x11, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x06, 0x40, + 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xf8, + 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x61, 0x8f, 0x61, 0x34, 0x43, 0x55, 0x14, + 0x7f, 0x27, 0x09, 0xce, 0x4c, 0x8b, 0xea, 0x9b, 0x7b, 0x19, 0x25, 0xbc, 0x6e, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x0e, 0x07, 0x60, 0xd4, 0x39, 0xc9, 0x1b, 0x5b, 0x5d, 0x90, 0x7b, 0x23, 0xc8, + 0xd2, 0x34, 0x9d, 0x4a, 0x9a, 0x46, 0x39, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, + 0x11, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, + 0x15, 0x30, 0x13, 0x81, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, + 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x72, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x65, 0x16, 0x63, + 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, + 0x4f, 0x54, 0x20, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x44, 0x2e, + 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, + 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x2f, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x02, 0x04, 0x22, 0x16, 0x20, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, + 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, + 0x32, 0x30, 0x30, 0x32, 0x2f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x86, 0xf8, 0x42, 0x01, 0x04, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, + 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, + 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, + 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x03, 0x04, 0x39, 0x16, 0x37, + 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, + 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, + 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, + 0x6d, 0x6c, 0x3f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, + 0x42, 0x01, 0x07, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, + 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, + 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x3f, 0x30, 0x41, 0x06, 0x09, 0x60, 0x86, + 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x08, 0x04, 0x34, 0x16, 0x32, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, + 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, + 0x30, 0x30, 0x32, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x4c, 0x41, + 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x30, 0x81, 0x83, 0x06, + 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x7c, 0x30, 0x7a, 0x30, 0x39, 0xa0, 0x37, 0xa0, + 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, + 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, + 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, + 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x3d, 0xa0, 0x3b, 0xa0, 0x39, 0x86, 0x37, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x69, + 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, + 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, + 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, + 0x73, 0x70, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x68, 0xee, 0x79, 0x97, 0x97, 0xdd, 0x3b, + 0xef, 0x16, 0x6a, 0x06, 0xf2, 0x14, 0x9a, 0x6e, 0xcd, 0x9e, 0x12, 0xf7, 0xaa, + 0x83, 0x10, 0xbd, 0xd1, 0x7c, 0x98, 0xfa, 0xc7, 0xae, 0xd4, 0x0e, 0x2c, 0x9e, + 0x38, 0x05, 0x9d, 0x52, 0x60, 0xa9, 0x99, 0x0a, 0x81, 0xb4, 0x98, 0x90, 0x1d, + 0xae, 0xbb, 0x4a, 0xd7, 0xb9, 0xdc, 0x88, 0x9e, 0x37, 0x78, 0x41, 0x5b, 0xf7, + 0x82, 0xa5, 0xf2, 0xba, 0x41, 0x25, 0x5a, 0x90, 0x1a, 0x1e, 0x45, 0x38, 0xa1, + 0x52, 0x58, 0x75, 0x94, 0x26, 0x44, 0xfb, 0x20, 0x07, 0xba, 0x44, 0xcc, 0xe5, + 0x4a, 0x2d, 0x72, 0x3f, 0x98, 0x47, 0xf6, 0x26, 0xdc, 0x05, 0x46, 0x05, 0x07, + 0x63, 0x21, 0xab, 0x46, 0x9b, 0x9c, 0x78, 0xd5, 0x54, 0x5b, 0x3d, 0x0c, 0x1e, + 0xc8, 0x64, 0x8c, 0xb5, 0x50, 0x23, 0x82, 0x6f, 0xdb, 0xb8, 0x22, 0x1c, 0x43, + 0x96, 0x07, 0xa8, 0xbb, +} diff --git a/src/pkg/encoding/asn1/common.go b/src/pkg/encoding/asn1/common.go new file mode 100644 index 000000000..03856bc55 --- /dev/null +++ b/src/pkg/encoding/asn1/common.go @@ -0,0 +1,161 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package asn1 + +import ( + "reflect" + "strconv" + "strings" +) + +// ASN.1 objects have metadata preceding them: +// the tag: the type of the object +// a flag denoting if this object is compound or not +// the class type: the namespace of the tag +// the length of the object, in bytes + +// Here are some standard tags and classes + +const ( + tagBoolean = 1 + tagInteger = 2 + tagBitString = 3 + tagOctetString = 4 + tagOID = 6 + tagEnum = 10 + tagUTF8String = 12 + tagSequence = 16 + tagSet = 17 + tagPrintableString = 19 + tagT61String = 20 + tagIA5String = 22 + tagUTCTime = 23 + tagGeneralizedTime = 24 + tagGeneralString = 27 +) + +const ( + classUniversal = 0 + classApplication = 1 + classContextSpecific = 2 + classPrivate = 3 +) + +type tagAndLength struct { + class, tag, length int + isCompound bool +} + +// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead +// of" and "in addition to". When not specified, every primitive type has a +// default tag in the UNIVERSAL class. +// +// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1 +// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT +// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another. +// +// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an +// /additional/ tag would wrap the default tag. This explicit tag will have the +// compound flag set. +// +// (This is used in order to remove ambiguity with optional elements.) +// +// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we +// don't support that here. We support a single layer of EXPLICIT or IMPLICIT +// tagging with tag strings on the fields of a structure. + +// fieldParameters is the parsed representation of tag string from a structure field. +type fieldParameters struct { + optional bool // true iff the field is OPTIONAL + explicit bool // true iff an EXPLICIT tag is in use. + application bool // true iff an APPLICATION tag is in use. + defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). + tag *int // the EXPLICIT or IMPLICIT tag (maybe nil). + stringType int // the string tag to use when marshaling. + set bool // true iff this should be encoded as a SET + omitEmpty bool // true iff this should be omitted if empty when marshaling. + + // Invariants: + // if explicit is set, tag is non-nil. +} + +// Given a tag string with the format specified in the package comment, +// parseFieldParameters will parse it into a fieldParameters structure, +// ignoring unknown parts of the string. +func parseFieldParameters(str string) (ret fieldParameters) { + for _, part := range strings.Split(str, ",") { + switch { + case part == "optional": + ret.optional = true + case part == "explicit": + ret.explicit = true + if ret.tag == nil { + ret.tag = new(int) + } + case part == "ia5": + ret.stringType = tagIA5String + case part == "printable": + ret.stringType = tagPrintableString + case strings.HasPrefix(part, "default:"): + i, err := strconv.ParseInt(part[8:], 10, 64) + if err == nil { + ret.defaultValue = new(int64) + *ret.defaultValue = i + } + case strings.HasPrefix(part, "tag:"): + i, err := strconv.Atoi(part[4:]) + if err == nil { + ret.tag = new(int) + *ret.tag = i + } + case part == "set": + ret.set = true + case part == "application": + ret.application = true + if ret.tag == nil { + ret.tag = new(int) + } + case part == "omitempty": + ret.omitEmpty = true + } + } + return +} + +// Given a reflected Go type, getUniversalType returns the default tag number +// and expected compound flag. +func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) { + switch t { + case objectIdentifierType: + return tagOID, false, true + case bitStringType: + return tagBitString, false, true + case timeType: + return tagUTCTime, false, true + case enumeratedType: + return tagEnum, false, true + case bigIntType: + return tagInteger, false, true + } + switch t.Kind() { + case reflect.Bool: + return tagBoolean, false, true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return tagInteger, false, true + case reflect.Struct: + return tagSequence, true, true + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + return tagOctetString, false, true + } + if strings.HasSuffix(t.Name(), "SET") { + return tagSet, true, true + } + return tagSequence, true, true + case reflect.String: + return tagPrintableString, false, true + } + return 0, false, false +} diff --git a/src/pkg/encoding/asn1/marshal.go b/src/pkg/encoding/asn1/marshal.go new file mode 100644 index 000000000..163bca575 --- /dev/null +++ b/src/pkg/encoding/asn1/marshal.go @@ -0,0 +1,557 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package asn1 + +import ( + "bytes" + "fmt" + "io" + "math/big" + "reflect" + "time" +) + +// A forkableWriter is an in-memory buffer that can be +// 'forked' to create new forkableWriters that bracket the +// original. After +// pre, post := w.fork(); +// the overall sequence of bytes represented is logically w+pre+post. +type forkableWriter struct { + *bytes.Buffer + pre, post *forkableWriter +} + +func newForkableWriter() *forkableWriter { + return &forkableWriter{new(bytes.Buffer), nil, nil} +} + +func (f *forkableWriter) fork() (pre, post *forkableWriter) { + if f.pre != nil || f.post != nil { + panic("have already forked") + } + f.pre = newForkableWriter() + f.post = newForkableWriter() + return f.pre, f.post +} + +func (f *forkableWriter) Len() (l int) { + l += f.Buffer.Len() + if f.pre != nil { + l += f.pre.Len() + } + if f.post != nil { + l += f.post.Len() + } + return +} + +func (f *forkableWriter) writeTo(out io.Writer) (n int, err error) { + n, err = out.Write(f.Bytes()) + if err != nil { + return + } + + var nn int + + if f.pre != nil { + nn, err = f.pre.writeTo(out) + n += nn + if err != nil { + return + } + } + + if f.post != nil { + nn, err = f.post.writeTo(out) + n += nn + } + return +} + +func marshalBase128Int(out *forkableWriter, n int64) (err error) { + if n == 0 { + err = out.WriteByte(0) + return + } + + l := 0 + for i := n; i > 0; i >>= 7 { + l++ + } + + for i := l - 1; i >= 0; i-- { + o := byte(n >> uint(i*7)) + o &= 0x7f + if i != 0 { + o |= 0x80 + } + err = out.WriteByte(o) + if err != nil { + return + } + } + + return nil +} + +func marshalInt64(out *forkableWriter, i int64) (err error) { + n := int64Length(i) + + for ; n > 0; n-- { + err = out.WriteByte(byte(i >> uint((n-1)*8))) + if err != nil { + return + } + } + + return nil +} + +func int64Length(i int64) (numBytes int) { + numBytes = 1 + + for i > 127 { + numBytes++ + i >>= 8 + } + + for i < -128 { + numBytes++ + i >>= 8 + } + + return +} + +func marshalBigInt(out *forkableWriter, n *big.Int) (err error) { + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement + // form. So we'll subtract 1 and invert. If the + // most-significant-bit isn't set then we'll need to pad the + // beginning with 0xff in order to keep the number negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + err = out.WriteByte(0xff) + if err != nil { + return + } + } + _, err = out.Write(bytes) + } else if n.Sign() == 0 { + // Zero is written as a single 0 zero rather than no bytes. + err = out.WriteByte(0x00) + } else { + bytes := n.Bytes() + if len(bytes) > 0 && bytes[0]&0x80 != 0 { + // We'll have to pad this with 0x00 in order to stop it + // looking like a negative number. + err = out.WriteByte(0) + if err != nil { + return + } + } + _, err = out.Write(bytes) + } + return +} + +func marshalLength(out *forkableWriter, i int) (err error) { + n := lengthLength(i) + + for ; n > 0; n-- { + err = out.WriteByte(byte(i >> uint((n-1)*8))) + if err != nil { + return + } + } + + return nil +} + +func lengthLength(i int) (numBytes int) { + numBytes = 1 + for i > 255 { + numBytes++ + i >>= 8 + } + return +} + +func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err error) { + b := uint8(t.class) << 6 + if t.isCompound { + b |= 0x20 + } + if t.tag >= 31 { + b |= 0x1f + err = out.WriteByte(b) + if err != nil { + return + } + err = marshalBase128Int(out, int64(t.tag)) + if err != nil { + return + } + } else { + b |= uint8(t.tag) + err = out.WriteByte(b) + if err != nil { + return + } + } + + if t.length >= 128 { + l := lengthLength(t.length) + err = out.WriteByte(0x80 | byte(l)) + if err != nil { + return + } + err = marshalLength(out, t.length) + if err != nil { + return + } + } else { + err = out.WriteByte(byte(t.length)) + if err != nil { + return + } + } + + return nil +} + +func marshalBitString(out *forkableWriter, b BitString) (err error) { + paddingBits := byte((8 - b.BitLength%8) % 8) + err = out.WriteByte(paddingBits) + if err != nil { + return + } + _, err = out.Write(b.Bytes) + return +} + +func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) { + if len(oid) < 2 || oid[0] > 6 || oid[1] >= 40 { + return StructuralError{"invalid object identifier"} + } + + err = out.WriteByte(byte(oid[0]*40 + oid[1])) + if err != nil { + return + } + for i := 2; i < len(oid); i++ { + err = marshalBase128Int(out, int64(oid[i])) + if err != nil { + return + } + } + + return +} + +func marshalPrintableString(out *forkableWriter, s string) (err error) { + b := []byte(s) + for _, c := range b { + if !isPrintable(c) { + return StructuralError{"PrintableString contains invalid character"} + } + } + + _, err = out.Write(b) + return +} + +func marshalIA5String(out *forkableWriter, s string) (err error) { + b := []byte(s) + for _, c := range b { + if c > 127 { + return StructuralError{"IA5String contains invalid character"} + } + } + + _, err = out.Write(b) + return +} + +func marshalTwoDigits(out *forkableWriter, v int) (err error) { + err = out.WriteByte(byte('0' + (v/10)%10)) + if err != nil { + return + } + return out.WriteByte(byte('0' + v%10)) +} + +func marshalUTCTime(out *forkableWriter, t time.Time) (err error) { + utc := t.UTC() + year, month, day := utc.Date() + + switch { + case 1950 <= year && year < 2000: + err = marshalTwoDigits(out, int(year-1900)) + case 2000 <= year && year < 2050: + err = marshalTwoDigits(out, int(year-2000)) + default: + return StructuralError{"Cannot represent time as UTCTime"} + } + if err != nil { + return + } + + err = marshalTwoDigits(out, int(month)) + if err != nil { + return + } + + err = marshalTwoDigits(out, day) + if err != nil { + return + } + + hour, min, sec := utc.Clock() + + err = marshalTwoDigits(out, hour) + if err != nil { + return + } + + err = marshalTwoDigits(out, min) + if err != nil { + return + } + + err = marshalTwoDigits(out, sec) + if err != nil { + return + } + + _, offset := t.Zone() + + switch { + case offset/60 == 0: + err = out.WriteByte('Z') + return + case offset > 0: + err = out.WriteByte('+') + case offset < 0: + err = out.WriteByte('-') + } + + if err != nil { + return + } + + offsetMinutes := offset / 60 + if offsetMinutes < 0 { + offsetMinutes = -offsetMinutes + } + + err = marshalTwoDigits(out, offsetMinutes/60) + if err != nil { + return + } + + err = marshalTwoDigits(out, offsetMinutes%60) + return +} + +func stripTagAndLength(in []byte) []byte { + _, offset, err := parseTagAndLength(in, 0) + if err != nil { + return in + } + return in[offset:] +} + +func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) { + switch value.Type() { + case timeType: + return marshalUTCTime(out, value.Interface().(time.Time)) + case bitStringType: + return marshalBitString(out, value.Interface().(BitString)) + case objectIdentifierType: + return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier)) + case bigIntType: + return marshalBigInt(out, value.Interface().(*big.Int)) + } + + switch v := value; v.Kind() { + case reflect.Bool: + if v.Bool() { + return out.WriteByte(255) + } else { + return out.WriteByte(0) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return marshalInt64(out, int64(v.Int())) + case reflect.Struct: + t := v.Type() + + startingField := 0 + + // If the first element of the structure is a non-empty + // RawContents, then we don't bother serializing the rest. + if t.NumField() > 0 && t.Field(0).Type == rawContentsType { + s := v.Field(0) + if s.Len() > 0 { + bytes := make([]byte, s.Len()) + for i := 0; i < s.Len(); i++ { + bytes[i] = uint8(s.Index(i).Uint()) + } + /* The RawContents will contain the tag and + * length fields but we'll also be writing + * those ourselves, so we strip them out of + * bytes */ + _, err = out.Write(stripTagAndLength(bytes)) + return + } else { + startingField = 1 + } + } + + for i := startingField; i < t.NumField(); i++ { + var pre *forkableWriter + pre, out = out.fork() + err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1"))) + if err != nil { + return + } + } + return + case reflect.Slice: + sliceType := v.Type() + if sliceType.Elem().Kind() == reflect.Uint8 { + bytes := make([]byte, v.Len()) + for i := 0; i < v.Len(); i++ { + bytes[i] = uint8(v.Index(i).Uint()) + } + _, err = out.Write(bytes) + return + } + + var params fieldParameters + for i := 0; i < v.Len(); i++ { + var pre *forkableWriter + pre, out = out.fork() + err = marshalField(pre, v.Index(i), params) + if err != nil { + return + } + } + return + case reflect.String: + if params.stringType == tagIA5String { + return marshalIA5String(out, v.String()) + } else { + return marshalPrintableString(out, v.String()) + } + return + } + + return StructuralError{"unknown Go type"} +} + +func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err error) { + // If the field is an interface{} then recurse into it. + if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 { + return marshalField(out, v.Elem(), params) + } + + if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty { + return + } + + if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) { + return + } + + if v.Type() == rawValueType { + rv := v.Interface().(RawValue) + if len(rv.FullBytes) != 0 { + _, err = out.Write(rv.FullBytes) + } else { + err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound}) + if err != nil { + return + } + _, err = out.Write(rv.Bytes) + } + return + } + + tag, isCompound, ok := getUniversalType(v.Type()) + if !ok { + err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())} + return + } + class := classUniversal + + if params.stringType != 0 { + if tag != tagPrintableString { + return StructuralError{"Explicit string type given to non-string member"} + } + tag = params.stringType + } + + if params.set { + if tag != tagSequence { + return StructuralError{"Non sequence tagged as set"} + } + tag = tagSet + } + + tags, body := out.fork() + + err = marshalBody(body, v, params) + if err != nil { + return + } + + bodyLen := body.Len() + + var explicitTag *forkableWriter + if params.explicit { + explicitTag, tags = tags.fork() + } + + if !params.explicit && params.tag != nil { + // implicit tag. + tag = *params.tag + class = classContextSpecific + } + + err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound}) + if err != nil { + return + } + + if params.explicit { + err = marshalTagAndLength(explicitTag, tagAndLength{ + class: classContextSpecific, + tag: *params.tag, + length: bodyLen + tags.Len(), + isCompound: true, + }) + } + + return nil +} + +// Marshal returns the ASN.1 encoding of val. +func Marshal(val interface{}) ([]byte, error) { + var out bytes.Buffer + v := reflect.ValueOf(val) + f := newForkableWriter() + err := marshalField(f, v, fieldParameters{}) + if err != nil { + return nil, err + } + _, err = f.writeTo(&out) + return out.Bytes(), nil +} diff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go new file mode 100644 index 000000000..f43bcae68 --- /dev/null +++ b/src/pkg/encoding/asn1/marshal_test.go @@ -0,0 +1,139 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package asn1 + +import ( + "bytes" + "encoding/hex" + "math/big" + "testing" + "time" +) + +type intStruct struct { + A int +} + +type twoIntStruct struct { + A int + B int +} + +type bigIntStruct struct { + A *big.Int +} + +type nestedStruct struct { + A intStruct +} + +type rawContentsStruct struct { + Raw RawContent + A int +} + +type implicitTagTest struct { + A int `asn1:"implicit,tag:5"` +} + +type explicitTagTest struct { + A int `asn1:"explicit,tag:5"` +} + +type ia5StringTest struct { + A string `asn1:"ia5"` +} + +type printableStringTest struct { + A string `asn1:"printable"` +} + +type optionalRawValueTest struct { + A RawValue `asn1:"optional"` +} + +type omitEmptyTest struct { + A []string `asn1:"omitempty"` +} + +type testSET []int + +var PST = time.FixedZone("PST", -8*60*60) + +type marshalTest struct { + in interface{} + out string // hex encoded +} + +var marshalTests = []marshalTest{ + {10, "02010a"}, + {127, "02017f"}, + {128, "02020080"}, + {-128, "020180"}, + {-129, "0202ff7f"}, + {intStruct{64}, "3003020140"}, + {bigIntStruct{big.NewInt(0x123456)}, "30050203123456"}, + {twoIntStruct{64, 65}, "3006020140020141"}, + {nestedStruct{intStruct{127}}, "3005300302017f"}, + {[]byte{1, 2, 3}, "0403010203"}, + {implicitTagTest{64}, "3003850140"}, + {explicitTagTest{64}, "3005a503020140"}, + {time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"}, + {time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"}, + {time.Unix(1258325776, 0).In(PST), "17113039313131353232353631362d30383030"}, + {BitString{[]byte{0x80}, 1}, "03020780"}, + {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, + {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, + {ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"}, + {"test", "130474657374"}, + { + "" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 127 times 'x' + "137f" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "78787878787878787878787878787878787878787878787878787878787878", + }, + { + "" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 128 times 'x' + "138180" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878" + + "7878787878787878787878787878787878787878787878787878787878787878", + }, + {ia5StringTest{"test"}, "3006160474657374"}, + {optionalRawValueTest{}, "3000"}, + {printableStringTest{"test"}, "3006130474657374"}, + {printableStringTest{"test*"}, "30071305746573742a"}, + {rawContentsStruct{nil, 64}, "3003020140"}, + {rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"}, + {RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"}, + {testSET([]int{10}), "310302010a"}, + {omitEmptyTest{[]string{}}, "3000"}, + {omitEmptyTest{[]string{"1"}}, "30053003130131"}, +} + +func TestMarshal(t *testing.T) { + for i, test := range marshalTests { + data, err := Marshal(test.in) + if err != nil { + t.Errorf("#%d failed: %s", i, err) + } + out, _ := hex.DecodeString(test.out) + if bytes.Compare(out, data) != 0 { + t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out) + + } + } +} diff --git a/src/pkg/encoding/base32/Makefile b/src/pkg/encoding/base32/Makefile deleted file mode 100644 index c0e85b644..000000000 --- a/src/pkg/encoding/base32/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include $(GOROOT)/src/Make.inc - -TARG=encoding/base32 -GOFILES=\ - base32.go\ - -include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/encoding/base32/base32.go b/src/pkg/encoding/base32/base32.go index acace30d6..71da6e22b 100644 --- a/src/pkg/encoding/base32/base32.go +++ b/src/pkg/encoding/base32/base32.go @@ -7,7 +7,6 @@ package base32 import ( "io" - "os" "strconv" ) @@ -126,8 +125,15 @@ func (enc *Encoding) Encode(dst, src []byte) { } } +// EncodeToString returns the base32 encoding of src. +func (enc *Encoding) EncodeToString(src []byte) string { + buf := make([]byte, enc.EncodedLen(len(src))) + enc.Encode(buf, src) + return string(buf) +} + type encoder struct { - err os.Error + err error enc *Encoding w io.Writer buf [5]byte // buffered data waiting to be encoded @@ -135,7 +141,7 @@ type encoder struct { out [1024]byte // output buffer } -func (e *encoder) Write(p []byte) (n int, err os.Error) { +func (e *encoder) Write(p []byte) (n int, err error) { if e.err != nil { return 0, e.err } @@ -187,7 +193,7 @@ func (e *encoder) Write(p []byte) (n int, err os.Error) { // Close flushes any pending output from the encoder. // It is an error to call Write after calling Close. -func (e *encoder) Close() os.Error { +func (e *encoder) Close() error { // If there's anything left in the buffer, flush it out if e.err == nil && e.nbuf > 0 { e.enc.Encode(e.out[0:], e.buf[0:e.nbuf]) @@ -216,30 +222,38 @@ func (enc *Encoding) EncodedLen(n int) int { return (n + 4) / 5 * 8 } type CorruptInputError int64 -func (e CorruptInputError) String() string { - return "illegal base32 data at input byte " + strconv.Itoa64(int64(e)) +func (e CorruptInputError) Error() string { + return "illegal base32 data at input byte " + strconv.FormatInt(int64(e), 10) } // decode is like Decode but returns an additional 'end' value, which // indicates if end-of-message padding was encountered and thus any -// additional data is an error. decode also assumes len(src)%8==0, -// since it is meant for internal use. -func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { - for i := 0; i < len(src)/8 && !end; i++ { +// additional data is an error. +func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { + osrc := src + for len(src) > 0 && !end { // Decode quantum using the base32 alphabet var dbuf [8]byte dlen := 8 // do the top bytes contain any data? dbufloop: - for j := 0; j < 8; j++ { - in := src[i*8+j] - if in == '=' && j >= 2 && i == len(src)/8-1 { + for j := 0; j < 8; { + if len(src) == 0 { + return n, false, CorruptInputError(len(osrc) - len(src) - j) + } + in := src[0] + src = src[1:] + if in == '\r' || in == '\n' { + // Ignore this character. + continue + } + if in == '=' && j >= 2 && len(src) < 8 { // We've reached the end and there's // padding, the rest should be padded - for k := j; k < 8; k++ { - if src[i*8+k] != '=' { - return n, false, CorruptInputError(i*8 + j) + for k := 0; k < 8-j-1; k++ { + if len(src) > k && src[k] != '=' { + return n, false, CorruptInputError(len(osrc) - len(src) + k - 1) } } dlen = j @@ -248,28 +262,30 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { } dbuf[j] = enc.decodeMap[in] if dbuf[j] == 0xFF { - return n, false, CorruptInputError(i*8 + j) + return n, false, CorruptInputError(len(osrc) - len(src) - 1) } + j++ } // Pack 8x 5-bit source blocks into 5 byte destination // quantum switch dlen { case 7, 8: - dst[i*5+4] = dbuf[6]<<5 | dbuf[7] + dst[4] = dbuf[6]<<5 | dbuf[7] fallthrough case 6, 5: - dst[i*5+3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3 + dst[3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3 fallthrough case 4: - dst[i*5+2] = dbuf[3]<<4 | dbuf[4]>>1 + dst[2] = dbuf[3]<<4 | dbuf[4]>>1 fallthrough case 3: - dst[i*5+1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4 + dst[1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4 fallthrough case 2: - dst[i*5+0] = dbuf[0]<<3 | dbuf[1]>>2 + dst[0] = dbuf[0]<<3 | dbuf[1]>>2 } + dst = dst[5:] switch dlen { case 2: n += 1 @@ -290,17 +306,21 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { // DecodedLen(len(src)) bytes to dst and returns the number of bytes // written. If src contains invalid base32 data, it will return the // number of bytes successfully written and CorruptInputError. -func (enc *Encoding) Decode(dst, src []byte) (n int, err os.Error) { - if len(src)%8 != 0 { - return 0, CorruptInputError(len(src) / 8 * 8) - } - +// New line characters (\r and \n) are ignored. +func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { n, _, err = enc.decode(dst, src) return } +// DecodeString returns the bytes represented by the base32 string s. +func (enc *Encoding) DecodeString(s string) ([]byte, error) { + dbuf := make([]byte, enc.DecodedLen(len(s))) + n, err := enc.Decode(dbuf, []byte(s)) + return dbuf[:n], err +} + type decoder struct { - err os.Error + err error enc *Encoding r io.Reader end bool // saw end of message @@ -310,7 +330,7 @@ type decoder struct { outbuf [1024 / 8 * 5]byte } -func (d *decoder) Read(p []byte) (n int, err os.Error) { +func (d *decoder) Read(p []byte) (n int, err error) { if d.err != nil { return 0, d.err } diff --git a/src/pkg/encoding/base32/base32_test.go b/src/pkg/encoding/base32/base32_test.go index 3fa1c2b26..98365e18c 100644 --- a/src/pkg/encoding/base32/base32_test.go +++ b/src/pkg/encoding/base32/base32_test.go @@ -6,8 +6,8 @@ package base32 import ( "bytes" + "io" "io/ioutil" - "os" "testing" ) @@ -51,9 +51,8 @@ func testEqual(t *testing.T, msg string, args ...interface{}) bool { func TestEncode(t *testing.T) { for _, p := range pairs { - buf := make([]byte, StdEncoding.EncodedLen(len(p.decoded))) - StdEncoding.Encode(buf, []byte(p.decoded)) - testEqual(t, "Encode(%q) = %q, want %q", p.decoded, string(buf), p.encoded) + got := StdEncoding.EncodeToString([]byte(p.decoded)) + testEqual(t, "Encode(%q) = %q, want %q", p.decoded, got, p.encoded) } } @@ -78,11 +77,11 @@ func TestEncoderBuffering(t *testing.T) { end = len(input) } n, err := encoder.Write(input[pos:end]) - testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, os.Error(nil)) + testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, error(nil)) testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) } err := encoder.Close() - testEqual(t, "Close gave error %v, want %v", err, os.Error(nil)) + testEqual(t, "Close gave error %v, want %v", err, error(nil)) testEqual(t, "Encoding/%d of %q = %q, want %q", bs, bigtest.decoded, bb.String(), bigtest.encoded) } } @@ -91,7 +90,7 @@ func TestDecode(t *testing.T) { for _, p := range pairs { dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) count, end, err := StdEncoding.decode(dbuf, []byte(p.encoded)) - testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) + testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, error(nil)) testEqual(t, "Decode(%q) = length %v, want %v", p.encoded, count, len(p.decoded)) if len(p.encoded) > 0 { testEqual(t, "Decode(%q) = end %v, want %v", p.encoded, end, (p.encoded[len(p.encoded)-1] == '=')) @@ -99,6 +98,10 @@ func TestDecode(t *testing.T) { testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) + + dbuf, err = StdEncoding.DecodeString(p.encoded) + testEqual(t, "DecodeString(%q) = error %v, want %v", p.encoded, err, error(nil)) + testEqual(t, "DecodeString(%q) = %q, want %q", p.encoded, string(dbuf), p.decoded) } } @@ -107,15 +110,15 @@ func TestDecoder(t *testing.T) { decoder := NewDecoder(StdEncoding, bytes.NewBufferString(p.encoded)) dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) count, err := decoder.Read(dbuf) - if err != nil && err != os.EOF { + if err != nil && err != io.EOF { t.Fatal("Read failed", err) } testEqual(t, "Read from %q = length %v, want %v", p.encoded, count, len(p.decoded)) testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) - if err != os.EOF { + if err != io.EOF { count, err = decoder.Read(dbuf) } - testEqual(t, "Read from %q = %v, want %v", p.encoded, err, os.EOF) + testEqual(t, "Read from %q = %v, want %v", p.encoded, err, io.EOF) } } @@ -126,7 +129,7 @@ func TestDecoderBuffering(t *testing.T) { var total int for total = 0; total < len(bigtest.decoded); { n, err := decoder.Read(buf[total : total+bs]) - testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, os.Error(nil)) + testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, error(nil)) total += n } testEqual(t, "Decoding/%d of %q = %q, want %q", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded) @@ -191,3 +194,29 @@ func TestBig(t *testing.T) { t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i) } } + +func TestNewLineCharacters(t *testing.T) { + // Each of these should decode to the string "sure", without errors. + const expected = "sure" + examples := []string{ + "ON2XEZI=", + "ON2XEZI=\r", + "ON2XEZI=\n", + "ON2XEZI=\r\n", + "ON2XEZ\r\nI=", + "ON2X\rEZ\nI=", + "ON2X\nEZ\rI=", + "ON2XEZ\nI=", + "ON2XEZI\n=", + } + for _, e := range examples { + buf, err := StdEncoding.DecodeString(e) + if err != nil { + t.Errorf("Decode(%q) failed: %v", e, err) + continue + } + if s := string(buf); s != expected { + t.Errorf("Decode(%q) = %q, want %q", e, s, expected) + } + } +} diff --git a/src/pkg/encoding/base64/Makefile b/src/pkg/encoding/base64/Makefile deleted file mode 100644 index 2f54ed839..000000000 --- a/src/pkg/encoding/base64/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=encoding/base64 -GOFILES=\ - base64.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/base64/base64.go b/src/pkg/encoding/base64/base64.go index c6b2a13e4..55f9f67a4 100644 --- a/src/pkg/encoding/base64/base64.go +++ b/src/pkg/encoding/base64/base64.go @@ -7,7 +7,6 @@ package base64 import ( "io" - "os" "strconv" ) @@ -114,7 +113,7 @@ func (enc *Encoding) EncodeToString(src []byte) string { } type encoder struct { - err os.Error + err error enc *Encoding w io.Writer buf [3]byte // buffered data waiting to be encoded @@ -122,7 +121,7 @@ type encoder struct { out [1024]byte // output buffer } -func (e *encoder) Write(p []byte) (n int, err os.Error) { +func (e *encoder) Write(p []byte) (n int, err error) { if e.err != nil { return 0, e.err } @@ -174,7 +173,7 @@ func (e *encoder) Write(p []byte) (n int, err os.Error) { // Close flushes any pending output from the encoder. // It is an error to call Write after calling Close. -func (e *encoder) Close() os.Error { +func (e *encoder) Close() error { // If there's anything left in the buffer, flush it out if e.err == nil && e.nbuf > 0 { e.enc.Encode(e.out[0:], e.buf[0:e.nbuf]) @@ -203,28 +202,36 @@ func (enc *Encoding) EncodedLen(n int) int { return (n + 2) / 3 * 4 } type CorruptInputError int64 -func (e CorruptInputError) String() string { - return "illegal base64 data at input byte " + strconv.Itoa64(int64(e)) +func (e CorruptInputError) Error() string { + return "illegal base64 data at input byte " + strconv.FormatInt(int64(e), 10) } // decode is like Decode but returns an additional 'end' value, which // indicates if end-of-message padding was encountered and thus any -// additional data is an error. decode also assumes len(src)%4==0, -// since it is meant for internal use. -func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { - for i := 0; i < len(src)/4 && !end; i++ { +// additional data is an error. +func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) { + osrc := src + for len(src) > 0 && !end { // Decode quantum using the base64 alphabet var dbuf [4]byte dlen := 4 dbufloop: - for j := 0; j < 4; j++ { - in := src[i*4+j] - if in == '=' && j >= 2 && i == len(src)/4-1 { + for j := 0; j < 4; { + if len(src) == 0 { + return n, false, CorruptInputError(len(osrc) - len(src) - j) + } + in := src[0] + src = src[1:] + if in == '\r' || in == '\n' { + // Ignore this character. + continue + } + if in == '=' && j >= 2 && len(src) < 4 { // We've reached the end and there's // padding - if src[i*4+3] != '=' { - return n, false, CorruptInputError(i*4 + 2) + if len(src) > 0 && src[0] != '=' { + return n, false, CorruptInputError(len(osrc) - len(src) - 1) } dlen = j end = true @@ -232,22 +239,24 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { } dbuf[j] = enc.decodeMap[in] if dbuf[j] == 0xFF { - return n, false, CorruptInputError(i*4 + j) + return n, false, CorruptInputError(len(osrc) - len(src) - 1) } + j++ } // Pack 4x 6-bit source blocks into 3 byte destination // quantum switch dlen { case 4: - dst[i*3+2] = dbuf[2]<<6 | dbuf[3] + dst[2] = dbuf[2]<<6 | dbuf[3] fallthrough case 3: - dst[i*3+1] = dbuf[1]<<4 | dbuf[2]>>2 + dst[1] = dbuf[1]<<4 | dbuf[2]>>2 fallthrough case 2: - dst[i*3+0] = dbuf[0]<<2 | dbuf[1]>>4 + dst[0] = dbuf[0]<<2 | dbuf[1]>>4 } + dst = dst[3:] n += dlen - 1 } @@ -258,24 +267,21 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err os.Error) { // DecodedLen(len(src)) bytes to dst and returns the number of bytes // written. If src contains invalid base64 data, it will return the // number of bytes successfully written and CorruptInputError. -func (enc *Encoding) Decode(dst, src []byte) (n int, err os.Error) { - if len(src)%4 != 0 { - return 0, CorruptInputError(len(src) / 4 * 4) - } - +// New line characters (\r and \n) are ignored. +func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { n, _, err = enc.decode(dst, src) return } // DecodeString returns the bytes represented by the base64 string s. -func (enc *Encoding) DecodeString(s string) ([]byte, os.Error) { +func (enc *Encoding) DecodeString(s string) ([]byte, error) { dbuf := make([]byte, enc.DecodedLen(len(s))) n, err := enc.Decode(dbuf, []byte(s)) return dbuf[:n], err } type decoder struct { - err os.Error + err error enc *Encoding r io.Reader end bool // saw end of message @@ -285,7 +291,7 @@ type decoder struct { outbuf [1024 / 4 * 3]byte } -func (d *decoder) Read(p []byte) (n int, err os.Error) { +func (d *decoder) Read(p []byte) (n int, err error) { if d.err != nil { return 0, d.err } diff --git a/src/pkg/encoding/base64/base64_test.go b/src/pkg/encoding/base64/base64_test.go index c163dae84..3e9a84393 100644 --- a/src/pkg/encoding/base64/base64_test.go +++ b/src/pkg/encoding/base64/base64_test.go @@ -6,8 +6,8 @@ package base64 import ( "bytes" + "io" "io/ioutil" - "os" "testing" ) @@ -82,11 +82,11 @@ func TestEncoderBuffering(t *testing.T) { end = len(input) } n, err := encoder.Write(input[pos:end]) - testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, os.Error(nil)) + testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, error(nil)) testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) } err := encoder.Close() - testEqual(t, "Close gave error %v, want %v", err, os.Error(nil)) + testEqual(t, "Close gave error %v, want %v", err, error(nil)) testEqual(t, "Encoding/%d of %q = %q, want %q", bs, bigtest.decoded, bb.String(), bigtest.encoded) } } @@ -95,7 +95,7 @@ func TestDecode(t *testing.T) { for _, p := range pairs { dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) count, end, err := StdEncoding.decode(dbuf, []byte(p.encoded)) - testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) + testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, error(nil)) testEqual(t, "Decode(%q) = length %v, want %v", p.encoded, count, len(p.decoded)) if len(p.encoded) > 0 { testEqual(t, "Decode(%q) = end %v, want %v", p.encoded, end, (p.encoded[len(p.encoded)-1] == '=')) @@ -103,7 +103,7 @@ func TestDecode(t *testing.T) { testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) dbuf, err = StdEncoding.DecodeString(p.encoded) - testEqual(t, "DecodeString(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) + testEqual(t, "DecodeString(%q) = error %v, want %v", p.encoded, err, error(nil)) testEqual(t, "DecodeString(%q) = %q, want %q", string(dbuf), p.decoded) } } @@ -113,15 +113,15 @@ func TestDecoder(t *testing.T) { decoder := NewDecoder(StdEncoding, bytes.NewBufferString(p.encoded)) dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) count, err := decoder.Read(dbuf) - if err != nil && err != os.EOF { + if err != nil && err != io.EOF { t.Fatal("Read failed", err) } testEqual(t, "Read from %q = length %v, want %v", p.encoded, count, len(p.decoded)) testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) - if err != os.EOF { + if err != io.EOF { count, err = decoder.Read(dbuf) } - testEqual(t, "Read from %q = %v, want %v", p.encoded, err, os.EOF) + testEqual(t, "Read from %q = %v, want %v", p.encoded, err, io.EOF) } } @@ -132,7 +132,7 @@ func TestDecoderBuffering(t *testing.T) { var total int for total = 0; total < len(bigtest.decoded); { n, err := decoder.Read(buf[total : total+bs]) - testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, os.Error(nil)) + testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, error(nil)) total += n } testEqual(t, "Decoding/%d of %q = %q, want %q", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded) @@ -197,3 +197,29 @@ func TestBig(t *testing.T) { t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i) } } + +func TestNewLineCharacters(t *testing.T) { + // Each of these should decode to the string "sure", without errors. + const expected = "sure" + examples := []string{ + "c3VyZQ==", + "c3VyZQ==\r", + "c3VyZQ==\n", + "c3VyZQ==\r\n", + "c3VyZ\r\nQ==", + "c3V\ryZ\nQ==", + "c3V\nyZ\rQ==", + "c3VyZ\nQ==", + "c3VyZQ\n==", + } + for _, e := range examples { + buf, err := StdEncoding.DecodeString(e) + if err != nil { + t.Errorf("Decode(%q) failed: %v", e, err) + continue + } + if s := string(buf); s != expected { + t.Errorf("Decode(%q) = %q, want %q", e, s, expected) + } + } +} diff --git a/src/pkg/encoding/binary/Makefile b/src/pkg/encoding/binary/Makefile deleted file mode 100644 index dc46abe90..000000000 --- a/src/pkg/encoding/binary/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=encoding/binary -GOFILES=\ - binary.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/binary/binary.go b/src/pkg/encoding/binary/binary.go index 8e55cb23b..712e490e6 100644 --- a/src/pkg/encoding/binary/binary.go +++ b/src/pkg/encoding/binary/binary.go @@ -2,41 +2,45 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package binary implements translation between -// unsigned integer values and byte sequences -// and the reading and writing of fixed-size values. +// Package binary implements translation between numbers and byte sequences +// and encoding and decoding of varints. +// +// Numbers are translated by reading and writing fixed-size values. +// A fixed-size value is either a fixed-size arithmetic +// type (int8, uint8, int16, float32, complex64, ...) +// or an array or struct containing only fixed-size values. +// +// Varints are a method of encoding integers using one or more bytes; +// numbers with smaller absolute value take a smaller number of bytes. +// For a specification, see http://code.google.com/apis/protocolbuffers/docs/encoding.html. package binary import ( - "math" + "errors" "io" - "os" + "math" "reflect" ) // A ByteOrder specifies how to convert byte sequences into // 16-, 32-, or 64-bit unsigned integers. type ByteOrder interface { - Uint16(b []byte) uint16 - Uint32(b []byte) uint32 - Uint64(b []byte) uint64 + Uint16([]byte) uint16 + Uint32([]byte) uint32 + Uint64([]byte) uint64 PutUint16([]byte, uint16) PutUint32([]byte, uint32) PutUint64([]byte, uint64) String() string } -// This is byte instead of struct{} so that it can be compared, -// allowing, e.g., order == binary.LittleEndian. -type unused byte - // LittleEndian is the little-endian implementation of ByteOrder. var LittleEndian littleEndian // BigEndian is the big-endian implementation of ByteOrder. var BigEndian bigEndian -type littleEndian unused +type littleEndian struct{} func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 } @@ -76,7 +80,7 @@ func (littleEndian) String() string { return "LittleEndian" } func (littleEndian) GoString() string { return "binary.LittleEndian" } -type bigEndian unused +type bigEndian struct{} func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 } @@ -119,12 +123,9 @@ func (bigEndian) GoString() string { return "binary.BigEndian" } // Read reads structured binary data from r into data. // Data must be a pointer to a fixed-size value or a slice // of fixed-size values. -// A fixed-size value is either a fixed-size arithmetic -// type (int8, uint8, int16, float32, complex64, ...) -// or an array or struct containing only fixed-size values. // Bytes read from r are decoded using the specified byte order // and written to successive fields of the data. -func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { +func Read(r io.Reader, order ByteOrder, data interface{}) error { // Fast path for basic types. if n := intDestSize(data); n != 0 { var b [8]byte @@ -161,11 +162,11 @@ func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { case reflect.Slice: v = d default: - return os.NewError("binary.Read: invalid type " + d.Type().String()) + return errors.New("binary.Read: invalid type " + d.Type().String()) } - size := TotalSize(v) + size := dataSize(v) if size < 0 { - return os.NewError("binary.Read: invalid type " + v.Type().String()) + return errors.New("binary.Read: invalid type " + v.Type().String()) } d := &decoder{order: order, buf: make([]byte, size)} if _, err := io.ReadFull(r, d.buf); err != nil { @@ -176,14 +177,11 @@ func Read(r io.Reader, order ByteOrder, data interface{}) os.Error { } // Write writes the binary representation of data into w. -// Data must be a fixed-size value or a pointer to -// a fixed-size value. -// A fixed-size value is either a fixed-size arithmetic -// type (int8, uint8, int16, float32, complex64, ...) -// or an array or struct containing only fixed-size values. +// Data must be a fixed-size value or a slice of fixed-size +// values, or a pointer to such data. // Bytes written to w are encoded using the specified byte order // and read from successive fields of the data. -func Write(w io.Writer, order ByteOrder, data interface{}) os.Error { +func Write(w io.Writer, order ByteOrder, data interface{}) error { // Fast path for basic types. var b [8]byte var bs []byte @@ -242,9 +240,9 @@ func Write(w io.Writer, order ByteOrder, data interface{}) os.Error { return err } v := reflect.Indirect(reflect.ValueOf(data)) - size := TotalSize(v) + size := dataSize(v) if size < 0 { - return os.NewError("binary.Write: invalid type " + v.Type().String()) + return errors.New("binary.Write: invalid type " + v.Type().String()) } buf := make([]byte, size) e := &encoder{order: order, buf: buf} @@ -253,7 +251,17 @@ func Write(w io.Writer, order ByteOrder, data interface{}) os.Error { return err } -func TotalSize(v reflect.Value) int { +// Size returns how many bytes Write would generate to encode the value v, which +// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data. +func Size(v interface{}) int { + return dataSize(reflect.Indirect(reflect.ValueOf(v))) +} + +// dataSize returns the number of bytes the actual data represented by v occupies in memory. +// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice +// it returns the length of the slice times the element size and does not count the memory +// occupied by the header. +func dataSize(v reflect.Value) int { if v.Kind() == reflect.Slice { elem := sizeof(v.Type().Elem()) if elem < 0 { @@ -369,6 +377,7 @@ func (d *decoder) value(v reflect.Value) { for i := 0; i < l; i++ { d.value(v.Index(i)) } + case reflect.Struct: l := v.NumField() for i := 0; i < l; i++ { @@ -424,11 +433,13 @@ func (e *encoder) value(v reflect.Value) { for i := 0; i < l; i++ { e.value(v.Index(i)) } + case reflect.Struct: l := v.NumField() for i := 0; i < l; i++ { e.value(v.Field(i)) } + case reflect.Slice: l := v.Len() for i := 0; i < l; i++ { diff --git a/src/pkg/encoding/binary/binary_test.go b/src/pkg/encoding/binary/binary_test.go index b266996f6..ff361b7e3 100644 --- a/src/pkg/encoding/binary/binary_test.go +++ b/src/pkg/encoding/binary/binary_test.go @@ -5,9 +5,8 @@ package binary import ( - "io" - "os" "bytes" + "io" "math" "reflect" "testing" @@ -99,7 +98,7 @@ var little = []byte{ var src = []byte{1, 2, 3, 4, 5, 6, 7, 8} var res = []int32{0x01020304, 0x05060708} -func checkResult(t *testing.T, dir string, order, err os.Error, have, want interface{}) { +func checkResult(t *testing.T, dir string, order ByteOrder, err error, have, want interface{}) { if err != nil { t.Errorf("%v %v: %v", dir, order, err) return @@ -166,17 +165,48 @@ type byteSliceReader struct { remain []byte } -func (br *byteSliceReader) Read(p []byte) (int, os.Error) { +func (br *byteSliceReader) Read(p []byte) (int, error) { n := copy(p, br.remain) br.remain = br.remain[n:] return n, nil } -func BenchmarkRead(b *testing.B) { +func BenchmarkReadSlice1000Int32s(b *testing.B) { + bsr := &byteSliceReader{} + slice := make([]int32, 1000) + buf := make([]byte, len(slice)*4) + b.SetBytes(int64(len(buf))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + bsr.remain = buf + Read(bsr, BigEndian, slice) + } +} + +func BenchmarkReadStruct(b *testing.B) { + bsr := &byteSliceReader{} + var buf bytes.Buffer + Write(&buf, BigEndian, &s) + n := dataSize(reflect.ValueOf(s)) + b.SetBytes(int64(n)) + t := s + b.ResetTimer() + for i := 0; i < b.N; i++ { + bsr.remain = buf.Bytes() + Read(bsr, BigEndian, &t) + } + b.StopTimer() + if !reflect.DeepEqual(s, t) { + b.Fatal("no match") + } +} + +func BenchmarkReadInts(b *testing.B) { var ls Struct bsr := &byteSliceReader{} var r io.Reader = bsr - + b.SetBytes(2 * (1 + 2 + 4 + 8)) + b.ResetTimer() for i := 0; i < b.N; i++ { bsr.remain = big Read(r, BigEndian, &ls.Int8) @@ -197,25 +227,19 @@ func BenchmarkRead(b *testing.B) { for i := range want.Array { want.Array[i] = 0 } + b.StopTimer() if !reflect.DeepEqual(ls, want) { panic("no match") } } -func BenchmarkWrite(b *testing.B) { +func BenchmarkWriteInts(b *testing.B) { buf := new(bytes.Buffer) var w io.Writer = buf - + b.SetBytes(2 * (1 + 2 + 4 + 8)) + b.ResetTimer() for i := 0; i < b.N; i++ { buf.Reset() - Write(w, BigEndian, &s.Int8) - Write(w, BigEndian, &s.Int16) - Write(w, BigEndian, &s.Int32) - Write(w, BigEndian, &s.Int64) - Write(w, BigEndian, &s.Uint8) - Write(w, BigEndian, &s.Uint16) - Write(w, BigEndian, &s.Uint32) - Write(w, BigEndian, &s.Uint64) Write(w, BigEndian, s.Int8) Write(w, BigEndian, s.Int16) Write(w, BigEndian, s.Int32) @@ -225,11 +249,8 @@ func BenchmarkWrite(b *testing.B) { Write(w, BigEndian, s.Uint32) Write(w, BigEndian, s.Uint64) } - - if !bytes.Equal(buf.Bytes()[:30], big[:30]) { - panic("first half doesn't match") - } - if !bytes.Equal(buf.Bytes()[30:], big[:30]) { - panic("second half doesn't match") + b.StopTimer() + if !bytes.Equal(buf.Bytes(), big[:30]) { + b.Fatalf("first half doesn't match: %x %x", buf.Bytes(), big[:30]) } } diff --git a/src/pkg/encoding/binary/example_test.go b/src/pkg/encoding/binary/example_test.go new file mode 100644 index 000000000..dec12ebf5 --- /dev/null +++ b/src/pkg/encoding/binary/example_test.go @@ -0,0 +1,52 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package binary_test + +import ( + "bytes" + "encoding/binary" + "fmt" + "math" +) + +func ExampleWrite() { + buf := new(bytes.Buffer) + var pi float64 = math.Pi + err := binary.Write(buf, binary.LittleEndian, pi) + if err != nil { + fmt.Println("binary.Write failed:", err) + } + fmt.Printf("% x", buf.Bytes()) + // Output: 18 2d 44 54 fb 21 09 40 +} + +func ExampleWrite_multi() { + buf := new(bytes.Buffer) + var data = []interface{}{ + uint16(61374), + int8(-54), + uint8(254), + } + for _, v := range data { + err := binary.Write(buf, binary.LittleEndian, v) + if err != nil { + fmt.Println("binary.Write failed:", err) + } + } + fmt.Printf("%x", buf.Bytes()) + // Output: beefcafe +} + +func ExampleRead() { + var pi float64 + b := []byte{0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40} + buf := bytes.NewBuffer(b) + err := binary.Read(buf, binary.LittleEndian, &pi) + if err != nil { + fmt.Println("binary.Read failed:", err) + } + fmt.Print(pi) + // Output: 3.141592653589793 +} diff --git a/src/pkg/encoding/binary/varint.go b/src/pkg/encoding/binary/varint.go new file mode 100644 index 000000000..b756afdd0 --- /dev/null +++ b/src/pkg/encoding/binary/varint.go @@ -0,0 +1,134 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package binary + +// This file implements "varint" encoding of 64-bit integers. +// The encoding is: +// - unsigned integers are serialized 7 bits at a time, starting with the +// least significant bits +// - the most significant bit (msb) in each output byte indicates if there +// is a continuation byte (msb = 1) +// - signed integers are mapped to unsigned integers using "zig-zag" +// encoding: Positive values x are written as 2*x + 0, negative values +// are written as 2*(^x) + 1; that is, negative numbers are complemented +// and whether to complement is encoded in bit 0. +// +// Design note: +// At most 10 bytes are needed for 64-bit values. The encoding could +// be more dense: a full 64-bit value needs an extra byte just to hold bit 63. +// Instead, the msb of the previous byte could be used to hold bit 63 since we +// know there can't be more than 64 bits. This is a trivial improvement and +// would reduce the maximum encoding length to 9 bytes. However, it breaks the +// invariant that the msb is always the "continuation bit" and thus makes the +// format incompatible with a varint encoding for larger numbers (say 128-bit). + +import ( + "errors" + "io" +) + +// MaxVarintLenN is the maximum length of a varint-encoded N-bit integer. +const ( + MaxVarintLen16 = 3 + MaxVarintLen32 = 5 + MaxVarintLen64 = 10 +) + +// PutUvarint encodes a uint64 into buf and returns the number of bytes written. +// If the buffer is too small, PutUvarint will panic. +func PutUvarint(buf []byte, x uint64) int { + i := 0 + for x >= 0x80 { + buf[i] = byte(x) | 0x80 + x >>= 7 + i++ + } + buf[i] = byte(x) + return i + 1 +} + +// Uvarint decodes a uint64 from buf and returns that value and the +// number of bytes read (> 0). If an error occurred, the value is 0 +// and the number of bytes n is <= 0 meaning: +// +// n == 0: buf too small +// n < 0: value larger than 64 bits (overflow) +// and -n is the number of bytes read +// +func Uvarint(buf []byte) (uint64, int) { + var x uint64 + var s uint + for i, b := range buf { + if b < 0x80 { + if i > 9 || i == 9 && b > 1 { + return 0, -(i + 1) // overflow + } + return x | uint64(b)< 0). If an error occurred, the value is 0 +// and the number of bytes n is <= 0 with the following meaning: +// +// n == 0: buf too small +// n < 0: value larger than 64 bits (overflow) +// and -n is the number of bytes read +// +func Varint(buf []byte) (int64, int) { + ux, n := Uvarint(buf) // ok to continue in presence of error + x := int64(ux >> 1) + if ux&1 != 0 { + x = ^x + } + return x, n +} + +var overflow = errors.New("binary: varint overflows a 64-bit integer") + +// ReadUvarint reads an encoded unsigned integer from r and returns it as a uint64. +func ReadUvarint(r io.ByteReader) (uint64, error) { + var x uint64 + var s uint + for i := 0; ; i++ { + b, err := r.ReadByte() + if err != nil { + return x, err + } + if b < 0x80 { + if i > 9 || i == 9 && b > 1 { + return x, overflow + } + return x | uint64(b)<> 1) + if ux&1 != 0 { + x = ^x + } + return x, err +} diff --git a/src/pkg/encoding/binary/varint_test.go b/src/pkg/encoding/binary/varint_test.go new file mode 100644 index 000000000..9476bd5fb --- /dev/null +++ b/src/pkg/encoding/binary/varint_test.go @@ -0,0 +1,168 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package binary + +import ( + "bytes" + "io" + "testing" +) + +func testConstant(t *testing.T, w uint, max int) { + buf := make([]byte, MaxVarintLen64) + n := PutUvarint(buf, 1< 0 { + if len(record) != r.FieldsPerRecord { + r.column = 0 // report at start of record + return record, r.error(ErrFieldCount) + } + } else if r.FieldsPerRecord == 0 { + r.FieldsPerRecord = len(record) + } + return record, nil +} + +// ReadAll reads all the remaining records from r. +// Each record is a slice of fields. +// A successful call returns err == nil, not err == EOF. Because ReadAll is +// defined to read until EOF, it does not treat end of file as an error to be +// reported. +func (r *Reader) ReadAll() (records [][]string, err error) { + for { + record, err := r.Read() + if err == io.EOF { + return records, nil + } + if err != nil { + return nil, err + } + records = append(records, record) + } + panic("unreachable") +} + +// readRune reads one rune from r, folding \r\n to \n and keeping track +// of how far into the line we have read. r.column will point to the start +// of this rune, not the end of this rune. +func (r *Reader) readRune() (rune, error) { + r1, _, err := r.r.ReadRune() + + // Handle \r\n here. We make the simplifying assumption that + // anytime \r is followed by \n that it can be folded to \n. + // We will not detect files which contain both \r\n and bare \n. + if r1 == '\r' { + r1, _, err = r.r.ReadRune() + if err == nil { + if r1 != '\n' { + r.r.UnreadRune() + r1 = '\r' + } + } + } + r.column++ + return r1, err +} + +// unreadRune puts the last rune read from r back. +func (r *Reader) unreadRune() { + r.r.UnreadRune() + r.column-- +} + +// skip reads runes up to and including the rune delim or until error. +func (r *Reader) skip(delim rune) error { + for { + r1, err := r.readRune() + if err != nil { + return err + } + if r1 == delim { + return nil + } + } + panic("unreachable") +} + +// parseRecord reads and parses a single csv record from r. +func (r *Reader) parseRecord() (fields []string, err error) { + // Each record starts on a new line. We increment our line + // number (lines start at 1, not 0) and set column to -1 + // so as we increment in readRune it points to the character we read. + r.line++ + r.column = -1 + + // Peek at the first rune. If it is an error we are done. + // If we are support comments and it is the comment character + // then skip to the end of line. + + r1, _, err := r.r.ReadRune() + if err != nil { + return nil, err + } + + if r.Comment != 0 && r1 == r.Comment { + return nil, r.skip('\n') + } + r.r.UnreadRune() + + // At this point we have at least one field. + for { + haveField, delim, err := r.parseField() + if haveField { + fields = append(fields, r.field.String()) + } + if delim == '\n' || err == io.EOF { + return fields, err + } else if err != nil { + return nil, err + } + } + panic("unreachable") +} + +// parseField parses the next field in the record. The read field is +// located in r.field. Delim is the first character not part of the field +// (r.Comma or '\n'). +func (r *Reader) parseField() (haveField bool, delim rune, err error) { + r.field.Reset() + + r1, err := r.readRune() + if err != nil { + // If we have EOF and are not at the start of a line + // then we return the empty field. We have already + // checked for trailing commas if needed. + if err == io.EOF && r.column != 0 { + return true, 0, err + } + return false, 0, err + } + + if r.TrimLeadingSpace { + for r1 != '\n' && unicode.IsSpace(r1) { + r1, err = r.readRune() + if err != nil { + return false, 0, err + } + } + } + + switch r1 { + case r.Comma: + // will check below + + case '\n': + // We are a trailing empty field or a blank line + if r.column == 0 { + return false, r1, nil + } + return true, r1, nil + + case '"': + // quoted field + Quoted: + for { + r1, err = r.readRune() + if err != nil { + if err == io.EOF { + if r.LazyQuotes { + return true, 0, err + } + return false, 0, r.error(ErrQuote) + } + return false, 0, err + } + switch r1 { + case '"': + r1, err = r.readRune() + if err != nil || r1 == r.Comma { + break Quoted + } + if r1 == '\n' { + return true, r1, nil + } + if r1 != '"' { + if !r.LazyQuotes { + r.column-- + return false, 0, r.error(ErrQuote) + } + // accept the bare quote + r.field.WriteRune('"') + } + case '\n': + r.line++ + r.column = -1 + } + r.field.WriteRune(r1) + } + + default: + // unquoted field + for { + r.field.WriteRune(r1) + r1, err = r.readRune() + if err != nil || r1 == r.Comma { + break + } + if r1 == '\n' { + return true, r1, nil + } + if !r.LazyQuotes && r1 == '"' { + return false, 0, r.error(ErrBareQuote) + } + } + } + + if err != nil { + if err == io.EOF { + return true, 0, err + } + return false, 0, err + } + + if !r.TrailingComma { + // We don't allow trailing commas. See if we + // are at the end of the line (being mindful + // of trimming spaces). + c := r.column + r1, err = r.readRune() + if r.TrimLeadingSpace { + for r1 != '\n' && unicode.IsSpace(r1) { + r1, err = r.readRune() + if err != nil { + break + } + } + } + if err == io.EOF || r1 == '\n' { + r.column = c // report the comma + return false, 0, r.error(ErrTrailingComma) + } + r.unreadRune() + } + return true, r1, nil +} diff --git a/src/pkg/encoding/csv/reader_test.go b/src/pkg/encoding/csv/reader_test.go new file mode 100644 index 000000000..5fd84a76b --- /dev/null +++ b/src/pkg/encoding/csv/reader_test.go @@ -0,0 +1,281 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package csv + +import ( + "reflect" + "strings" + "testing" +) + +var readTests = []struct { + Name string + Input string + Output [][]string + UseFieldsPerRecord bool // false (default) means FieldsPerRecord is -1 + + // These fields are copied into the Reader + Comma rune + Comment rune + FieldsPerRecord int + LazyQuotes bool + TrailingComma bool + TrimLeadingSpace bool + + Error string + Line int // Expected error line if != 0 + Column int // Expected error column if line != 0 +}{ + { + Name: "Simple", + Input: "a,b,c\n", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "CRLF", + Input: "a,b\r\nc,d\r\n", + Output: [][]string{{"a", "b"}, {"c", "d"}}, + }, + { + Name: "BareCR", + Input: "a,b\rc,d\r\n", + Output: [][]string{{"a", "b\rc", "d"}}, + }, + { + Name: "RFC4180test", + UseFieldsPerRecord: true, + Input: `#field1,field2,field3 +"aaa","bb +b","ccc" +"a,a","b""bb","ccc" +zzz,yyy,xxx +`, + Output: [][]string{ + {"#field1", "field2", "field3"}, + {"aaa", "bb\nb", "ccc"}, + {"a,a", `b"bb`, "ccc"}, + {"zzz", "yyy", "xxx"}, + }, + }, + { + Name: "NoEOLTest", + Input: "a,b,c", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "Semicolon", + Comma: ';', + Input: "a;b;c\n", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "MultiLine", + Input: `"two +line","one line","three +line +field"`, + Output: [][]string{{"two\nline", "one line", "three\nline\nfield"}}, + }, + { + Name: "BlankLine", + Input: "a,b,c\n\nd,e,f\n\n", + Output: [][]string{ + {"a", "b", "c"}, + {"d", "e", "f"}, + }, + }, + { + Name: "TrimSpace", + Input: " a, b, c\n", + TrimLeadingSpace: true, + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "LeadingSpace", + Input: " a, b, c\n", + Output: [][]string{{" a", " b", " c"}}, + }, + { + Name: "Comment", + Comment: '#', + Input: "#1,2,3\na,b,c\n#comment", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "NoComment", + Input: "#1,2,3\na,b,c", + Output: [][]string{{"#1", "2", "3"}, {"a", "b", "c"}}, + }, + { + Name: "LazyQuotes", + LazyQuotes: true, + Input: `a "word","1"2",a","b`, + Output: [][]string{{`a "word"`, `1"2`, `a"`, `b`}}, + }, + { + Name: "BareQuotes", + LazyQuotes: true, + Input: `a "word","1"2",a"`, + Output: [][]string{{`a "word"`, `1"2`, `a"`}}, + }, + { + Name: "BareDoubleQuotes", + LazyQuotes: true, + Input: `a""b,c`, + Output: [][]string{{`a""b`, `c`}}, + }, + { + Name: "BadDoubleQuotes", + Input: `a""b,c`, + Error: `bare " in non-quoted-field`, Line: 1, Column: 1, + }, + { + Name: "TrimQuote", + Input: ` "a"," b",c`, + TrimLeadingSpace: true, + Output: [][]string{{"a", " b", "c"}}, + }, + { + Name: "BadBareQuote", + Input: `a "word","b"`, + Error: `bare " in non-quoted-field`, Line: 1, Column: 2, + }, + { + Name: "BadTrailingQuote", + Input: `"a word",b"`, + Error: `bare " in non-quoted-field`, Line: 1, Column: 10, + }, + { + Name: "ExtraneousQuote", + Input: `"a "word","b"`, + Error: `extraneous " in field`, Line: 1, Column: 3, + }, + { + Name: "BadFieldCount", + UseFieldsPerRecord: true, + Input: "a,b,c\nd,e", + Error: "wrong number of fields", Line: 2, + }, + { + Name: "BadFieldCount1", + UseFieldsPerRecord: true, + FieldsPerRecord: 2, + Input: `a,b,c`, + Error: "wrong number of fields", Line: 1, + }, + { + Name: "FieldCount", + Input: "a,b,c\nd,e", + Output: [][]string{{"a", "b", "c"}, {"d", "e"}}, + }, + { + Name: "BadTrailingCommaEOF", + Input: "a,b,c,", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaEOL", + Input: "a,b,c,\n", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaSpaceEOF", + TrimLeadingSpace: true, + Input: "a,b,c, ", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaSpaceEOL", + TrimLeadingSpace: true, + Input: "a,b,c, \n", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaLine3", + TrimLeadingSpace: true, + Input: "a,b,c\nd,e,f\ng,hi,", + Error: "extra delimiter at end of line", Line: 3, Column: 4, + }, + { + Name: "NotTrailingComma3", + Input: "a,b,c, \n", + Output: [][]string{{"a", "b", "c", " "}}, + }, + { + Name: "CommaFieldTest", + TrailingComma: true, + Input: `x,y,z,w +x,y,z, +x,y,, +x,,, +,,, +"x","y","z","w" +"x","y","z","" +"x","y","","" +"x","","","" +"","","","" +`, + Output: [][]string{ + {"x", "y", "z", "w"}, + {"x", "y", "z", ""}, + {"x", "y", "", ""}, + {"x", "", "", ""}, + {"", "", "", ""}, + {"x", "y", "z", "w"}, + {"x", "y", "z", ""}, + {"x", "y", "", ""}, + {"x", "", "", ""}, + {"", "", "", ""}, + }, + }, + { + Name: "Issue 2366", + TrailingComma: true, + TrimLeadingSpace: true, + Input: "a,b,\nc,d,e", + Output: [][]string{ + {"a", "b", ""}, + {"c", "d", "e"}, + }, + }, + { + Name: "Issue 2366a", + TrailingComma: false, + TrimLeadingSpace: true, + Input: "a,b,\nc,d,e", + Error: "extra delimiter at end of line", + }, +} + +func TestRead(t *testing.T) { + for _, tt := range readTests { + r := NewReader(strings.NewReader(tt.Input)) + r.Comment = tt.Comment + if tt.UseFieldsPerRecord { + r.FieldsPerRecord = tt.FieldsPerRecord + } else { + r.FieldsPerRecord = -1 + } + r.LazyQuotes = tt.LazyQuotes + r.TrailingComma = tt.TrailingComma + r.TrimLeadingSpace = tt.TrimLeadingSpace + if tt.Comma != 0 { + r.Comma = tt.Comma + } + out, err := r.ReadAll() + perr, _ := err.(*ParseError) + if tt.Error != "" { + if err == nil || !strings.Contains(err.Error(), tt.Error) { + t.Errorf("%s: error %v, want error %q", tt.Name, err, tt.Error) + } else if tt.Line != 0 && (tt.Line != perr.Line || tt.Column != perr.Column) { + t.Errorf("%s: error at %d:%d expected %d:%d", tt.Name, perr.Line, perr.Column, tt.Line, tt.Column) + } + } else if err != nil { + t.Errorf("%s: unexpected error %v", tt.Name, err) + } else if !reflect.DeepEqual(out, tt.Output) { + t.Errorf("%s: out=%q want %q", tt.Name, out, tt.Output) + } + } +} diff --git a/src/pkg/encoding/csv/writer.go b/src/pkg/encoding/csv/writer.go new file mode 100644 index 000000000..c4dcba566 --- /dev/null +++ b/src/pkg/encoding/csv/writer.go @@ -0,0 +1,121 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package csv + +import ( + "bufio" + "io" + "strings" + "unicode" + "unicode/utf8" +) + +// A Writer writes records to a CSV encoded file. +// +// As returned by NewWriter, a Writer writes records terminated by a +// newline and uses ',' as the field delimiter. The exported fields can be +// changed to customize the details before the first call to Write or WriteAll. +// +// Comma is the field delimiter. +// +// If UseCRLF is true, the Writer ends each record with \r\n instead of \n. +type Writer struct { + Comma rune // Field delimiter (set to to ',' by NewWriter) + UseCRLF bool // True to use \r\n as the line terminator + w *bufio.Writer +} + +// NewWriter returns a new Writer that writes to w. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + Comma: ',', + w: bufio.NewWriter(w), + } +} + +// Writer writes a single CSV record to w along with any necessary quoting. +// A record is a slice of strings with each string being one field. +func (w *Writer) Write(record []string) (err error) { + for n, field := range record { + if n > 0 { + if _, err = w.w.WriteRune(w.Comma); err != nil { + return + } + } + + // If we don't have to have a quoted field then just + // write out the field and continue to the next field. + if !w.fieldNeedsQuotes(field) { + if _, err = w.w.WriteString(field); err != nil { + return + } + continue + } + if err = w.w.WriteByte('"'); err != nil { + return + } + + for _, r1 := range field { + switch r1 { + case '"': + _, err = w.w.WriteString(`""`) + case '\r': + if !w.UseCRLF { + err = w.w.WriteByte('\r') + } + case '\n': + if w.UseCRLF { + _, err = w.w.WriteString("\r\n") + } else { + err = w.w.WriteByte('\n') + } + default: + _, err = w.w.WriteRune(r1) + } + if err != nil { + return + } + } + + if err = w.w.WriteByte('"'); err != nil { + return + } + } + if w.UseCRLF { + _, err = w.w.WriteString("\r\n") + } else { + err = w.w.WriteByte('\n') + } + return +} + +// Flush writes any buffered data to the underlying io.Writer. +func (w *Writer) Flush() { + w.w.Flush() +} + +// WriteAll writes multiple CSV records to w using Write and then calls Flush. +func (w *Writer) WriteAll(records [][]string) (err error) { + for _, record := range records { + err = w.Write(record) + if err != nil { + break + } + } + w.Flush() + return nil +} + +// fieldNeedsQuotes returns true if our field must be enclosed in quotes. +// Empty fields, files with a Comma, fields with a quote or newline, and +// fields which start with a space must be enclosed in quotes. +func (w *Writer) fieldNeedsQuotes(field string) bool { + if len(field) == 0 || strings.IndexRune(field, w.Comma) >= 0 || strings.IndexAny(field, "\"\r\n") >= 0 { + return true + } + + r1, _ := utf8.DecodeRuneInString(field) + return unicode.IsSpace(r1) +} diff --git a/src/pkg/encoding/csv/writer_test.go b/src/pkg/encoding/csv/writer_test.go new file mode 100644 index 000000000..578959007 --- /dev/null +++ b/src/pkg/encoding/csv/writer_test.go @@ -0,0 +1,44 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package csv + +import ( + "bytes" + "testing" +) + +var writeTests = []struct { + Input [][]string + Output string + UseCRLF bool +}{ + {Input: [][]string{{"abc"}}, Output: "abc\n"}, + {Input: [][]string{{"abc"}}, Output: "abc\r\n", UseCRLF: true}, + {Input: [][]string{{`"abc"`}}, Output: `"""abc"""` + "\n"}, + {Input: [][]string{{`a"b`}}, Output: `"a""b"` + "\n"}, + {Input: [][]string{{`"a"b"`}}, Output: `"""a""b"""` + "\n"}, + {Input: [][]string{{" abc"}}, Output: `" abc"` + "\n"}, + {Input: [][]string{{"abc,def"}}, Output: `"abc,def"` + "\n"}, + {Input: [][]string{{"abc", "def"}}, Output: "abc,def\n"}, + {Input: [][]string{{"abc"}, {"def"}}, Output: "abc\ndef\n"}, + {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\ndef\"\n"}, + {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\r\ndef\"\r\n", UseCRLF: true}, +} + +func TestWrite(t *testing.T) { + for n, tt := range writeTests { + b := &bytes.Buffer{} + f := NewWriter(b) + f.UseCRLF = tt.UseCRLF + err := f.WriteAll(tt.Input) + if err != nil { + t.Errorf("Unexpected error: %s\n", err) + } + out := b.String() + if out != tt.Output { + t.Errorf("#%d: out=%q want %q", n, out, tt.Output) + } + } +} diff --git a/src/pkg/encoding/git85/Makefile b/src/pkg/encoding/git85/Makefile deleted file mode 100644 index fbd003454..000000000 --- a/src/pkg/encoding/git85/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=encoding/git85 -GOFILES=\ - git.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/git85/git.go b/src/pkg/encoding/git85/git.go deleted file mode 100644 index 6bb74f46c..000000000 --- a/src/pkg/encoding/git85/git.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package git85 implements the radix 85 data encoding -// used in the Git version control system. -package git85 - -import ( - "bytes" - "io" - "os" - "strconv" -) - -type CorruptInputError int64 - -func (e CorruptInputError) String() string { - return "illegal git85 data at input byte " + strconv.Itoa64(int64(e)) -} - -const encode = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~" - -// The decodings are 1+ the actual value, so that the -// default zero value can be used to mean "not valid". -var decode = [256]uint8{ - '0': 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 'A': 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 'a': 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, - '!': 63, - '#': 64, 65, 66, 67, - '(': 68, 69, 70, 71, - '-': 72, - ';': 73, - '<': 74, 75, 76, 77, - '@': 78, - '^': 79, 80, 81, - '{': 82, 83, 84, 85, -} - -// Encode encodes src into EncodedLen(len(src)) -// bytes of dst. As a convenience, it returns the number -// of bytes written to dst, but this value is always EncodedLen(len(src)). -// Encode implements the radix 85 encoding used in the -// Git version control tool. -// -// The encoding splits src into chunks of at most 52 bytes -// and encodes each chunk on its own line. -func Encode(dst, src []byte) int { - ndst := 0 - for len(src) > 0 { - n := len(src) - if n > 52 { - n = 52 - } - if n <= 27 { - dst[ndst] = byte('A' + n - 1) - } else { - dst[ndst] = byte('a' + n - 26 - 1) - } - ndst++ - for i := 0; i < n; i += 4 { - var v uint32 - for j := 0; j < 4 && i+j < n; j++ { - v |= uint32(src[i+j]) << uint(24-j*8) - } - for j := 4; j >= 0; j-- { - dst[ndst+j] = encode[v%85] - v /= 85 - } - ndst += 5 - } - dst[ndst] = '\n' - ndst++ - src = src[n:] - } - return ndst -} - -// EncodedLen returns the length of an encoding of n source bytes. -func EncodedLen(n int) int { - if n == 0 { - return 0 - } - // 5 bytes per 4 bytes of input, rounded up. - // 2 extra bytes for each line of 52 src bytes, rounded up. - return (n+3)/4*5 + (n+51)/52*2 -} - -var newline = []byte{'\n'} - -// Decode decodes src into at most MaxDecodedLen(len(src)) -// bytes, returning the actual number of bytes written to dst. -// -// If Decode encounters invalid input, it returns a CorruptInputError. -// -func Decode(dst, src []byte) (n int, err os.Error) { - ndst := 0 - nsrc := 0 - for nsrc < len(src) { - var l int - switch ch := int(src[nsrc]); { - case 'A' <= ch && ch <= 'Z': - l = ch - 'A' + 1 - case 'a' <= ch && ch <= 'z': - l = ch - 'a' + 26 + 1 - default: - return ndst, CorruptInputError(nsrc) - } - if nsrc+1+l > len(src) { - return ndst, CorruptInputError(nsrc) - } - el := (l + 3) / 4 * 5 // encoded len - if nsrc+1+el+1 > len(src) || src[nsrc+1+el] != '\n' { - return ndst, CorruptInputError(nsrc) - } - line := src[nsrc+1 : nsrc+1+el] - for i := 0; i < el; i += 5 { - var v uint32 - for j := 0; j < 5; j++ { - ch := decode[line[i+j]] - if ch == 0 { - return ndst, CorruptInputError(nsrc + 1 + i + j) - } - v = v*85 + uint32(ch-1) - } - for j := 0; j < 4; j++ { - dst[ndst] = byte(v >> 24) - v <<= 8 - ndst++ - } - } - // Last fragment may have run too far (but there was room in dst). - // Back up. - if l%4 != 0 { - ndst -= 4 - l%4 - } - nsrc += 1 + el + 1 - } - return ndst, nil -} - -func MaxDecodedLen(n int) int { return n / 5 * 4 } - -// NewEncoder returns a new Git base85 stream encoder. Data written to -// the returned writer will be encoded and then written to w. -// The Git encoding operates on 52-byte blocks; when finished -// writing, the caller must Close the returned encoder to flush any -// partially written blocks. -func NewEncoder(w io.Writer) io.WriteCloser { return &encoder{w: w} } - -type encoder struct { - w io.Writer - err os.Error - buf [52]byte - nbuf int - out [1024]byte - nout int -} - -func (e *encoder) Write(p []byte) (n int, err os.Error) { - if e.err != nil { - return 0, e.err - } - - // Leading fringe. - if e.nbuf > 0 { - var i int - for i = 0; i < len(p) && e.nbuf < 52; i++ { - e.buf[e.nbuf] = p[i] - e.nbuf++ - } - n += i - p = p[i:] - if e.nbuf < 52 { - return - } - nout := Encode(e.out[0:], e.buf[0:]) - if _, e.err = e.w.Write(e.out[0:nout]); e.err != nil { - return n, e.err - } - e.nbuf = 0 - } - - // Large interior chunks. - for len(p) >= 52 { - nn := len(e.out) / (1 + 52/4*5 + 1) * 52 - if nn > len(p) { - nn = len(p) / 52 * 52 - } - if nn > 0 { - nout := Encode(e.out[0:], p[0:nn]) - if _, e.err = e.w.Write(e.out[0:nout]); e.err != nil { - return n, e.err - } - } - n += nn - p = p[nn:] - } - - // Trailing fringe. - for i := 0; i < len(p); i++ { - e.buf[i] = p[i] - } - e.nbuf = len(p) - n += len(p) - return -} - -func (e *encoder) Close() os.Error { - // If there's anything left in the buffer, flush it out - if e.err == nil && e.nbuf > 0 { - nout := Encode(e.out[0:], e.buf[0:e.nbuf]) - e.nbuf = 0 - _, e.err = e.w.Write(e.out[0:nout]) - } - return e.err -} - -// NewDecoder returns a new Git base85 stream decoder. -func NewDecoder(r io.Reader) io.Reader { return &decoder{r: r} } - -type decoder struct { - r io.Reader - err os.Error - readErr os.Error - buf [1024]byte - nbuf int - out []byte - outbuf [1024]byte - off int64 -} - -func (d *decoder) Read(p []byte) (n int, err os.Error) { - if len(p) == 0 { - return 0, nil - } - - for { - // Copy leftover output from last decode. - if len(d.out) > 0 { - n = copy(p, d.out) - d.out = d.out[n:] - return - } - - // Out of decoded output. Check errors. - if d.err != nil { - return 0, d.err - } - if d.readErr != nil { - d.err = d.readErr - return 0, d.err - } - - // Read and decode more input. - var nn int - nn, d.readErr = d.r.Read(d.buf[d.nbuf:]) - d.nbuf += nn - - // Send complete lines to Decode. - nl := bytes.LastIndex(d.buf[0:d.nbuf], newline) - if nl < 0 { - continue - } - nn, d.err = Decode(d.outbuf[0:], d.buf[0:nl+1]) - if e, ok := d.err.(CorruptInputError); ok { - d.err = CorruptInputError(int64(e) + d.off) - } - d.out = d.outbuf[0:nn] - d.nbuf = copy(d.buf[0:], d.buf[nl+1:d.nbuf]) - d.off += int64(nl + 1) - } - panic("unreachable") -} diff --git a/src/pkg/encoding/git85/git_test.go b/src/pkg/encoding/git85/git_test.go deleted file mode 100644 index c76385c35..000000000 --- a/src/pkg/encoding/git85/git_test.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package git85 - -import ( - "bytes" - "io/ioutil" - "os" - "testing" -) - -type testpair struct { - decoded, encoded string -} - -func testEqual(t *testing.T, msg string, args ...interface{}) bool { - if args[len(args)-2] != args[len(args)-1] { - t.Errorf(msg, args...) - return false - } - return true -} - -func TestGitTable(t *testing.T) { - var saw [256]bool - for i, c := range encode { - if decode[c] != uint8(i+1) { - t.Errorf("decode['%c'] = %d, want %d", c, decode[c], i+1) - } - saw[c] = true - } - for i, b := range saw { - if !b && decode[i] != 0 { - t.Errorf("decode[%d] = %d, want 0", i, decode[i]) - } - } -} - -var gitPairs = []testpair{ - // Wikipedia example, adapted. - { - "Man is distinguished, not only by his reason, but by this singular passion from " + - "other animals, which is a lust of the mind, that by a perseverance of delight in " + - "the continued and indefatigable generation of knowledge, exceeds the short " + - "vehemence of any carnal pleasure.", - - "zO<`^zX>%ZCX>)XGZfA9Ab7*B`EFf-gbRchTYDO_b1WctXlY|;AZc?T\n" + - "zVIXXEb95kYW*~HEWgu;7Ze%PVbZB98AYyqSVIXj2a&u*NWpZI|V`U(3W*}r`Y-wj`\n" + - "zbRcPNAarPDAY*TCbZKsNWn>^>Ze$>7Ze(RV>IZ)PBC\n" + - "zZf|#NWn^b%EFfigV`XJzb0BnRWgv5CZ*p`Xc4cT~ZDnp_Wgu^6AYpEKAY);2ZeeU7\n" + - "IaBO8^b9HiME&u=k\n", - }, -} - -var gitBigtest = gitPairs[len(gitPairs)-1] - -func TestEncode(t *testing.T) { - for _, p := range gitPairs { - buf := make([]byte, EncodedLen(len(p.decoded))) - n := Encode(buf, []byte(p.decoded)) - if n != len(buf) { - t.Errorf("EncodedLen does not agree with Encode") - } - buf = buf[0:n] - testEqual(t, "Encode(%q) = %q, want %q", p.decoded, string(buf), p.encoded) - } -} - -func TestEncoder(t *testing.T) { - for _, p := range gitPairs { - bb := &bytes.Buffer{} - encoder := NewEncoder(bb) - encoder.Write([]byte(p.decoded)) - encoder.Close() - testEqual(t, "Encode(%q) = %q, want %q", p.decoded, bb.String(), p.encoded) - } -} - -func TestEncoderBuffering(t *testing.T) { - input := []byte(gitBigtest.decoded) - for bs := 1; bs <= 12; bs++ { - bb := &bytes.Buffer{} - encoder := NewEncoder(bb) - for pos := 0; pos < len(input); pos += bs { - end := pos + bs - if end > len(input) { - end = len(input) - } - n, err := encoder.Write(input[pos:end]) - testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, os.Error(nil)) - testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) - } - err := encoder.Close() - testEqual(t, "Close gave error %v, want %v", err, os.Error(nil)) - testEqual(t, "Encoding/%d of %q = %q, want %q", bs, gitBigtest.decoded, bb.String(), gitBigtest.encoded) - } -} - -func TestDecode(t *testing.T) { - for _, p := range gitPairs { - dbuf := make([]byte, 4*len(p.encoded)) - ndst, err := Decode(dbuf, []byte(p.encoded)) - testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)) - testEqual(t, "Decode(%q) = ndst %v, want %v", p.encoded, ndst, len(p.decoded)) - testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:ndst]), p.decoded) - } -} - -func TestDecoder(t *testing.T) { - for _, p := range gitPairs { - decoder := NewDecoder(bytes.NewBufferString(p.encoded)) - dbuf, err := ioutil.ReadAll(decoder) - if err != nil { - t.Fatal("Read failed", err) - } - testEqual(t, "Read from %q = length %v, want %v", p.encoded, len(dbuf), len(p.decoded)) - testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf), p.decoded) - if err != nil { - testEqual(t, "Read from %q = %v, want %v", p.encoded, err, os.EOF) - } - } -} - -func TestDecoderBuffering(t *testing.T) { - for bs := 1; bs <= 12; bs++ { - decoder := NewDecoder(bytes.NewBufferString(gitBigtest.encoded)) - buf := make([]byte, len(gitBigtest.decoded)+12) - var total int - for total = 0; total < len(gitBigtest.decoded); { - n, err := decoder.Read(buf[total : total+bs]) - testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", gitBigtest.encoded, total, n, err, os.Error(nil)) - total += n - } - testEqual(t, "Decoding/%d of %q = %q, want %q", bs, gitBigtest.encoded, string(buf[0:total]), gitBigtest.decoded) - } -} - -func TestDecodeCorrupt(t *testing.T) { - type corrupt struct { - e string - p int - } - examples := []corrupt{ - {"v", 0}, - {"!z!!!!!!!!!", 0}, - } - - for _, e := range examples { - dbuf := make([]byte, 2*len(e.e)) - _, err := Decode(dbuf, []byte(e.e)) - switch err := err.(type) { - case CorruptInputError: - testEqual(t, "Corruption in %q at offset %v, want %v", e.e, int(err), e.p) - default: - t.Error("Decoder failed to detect corruption in", e) - } - } -} - -func TestGitBig(t *testing.T) { - n := 3*1000 + 1 - raw := make([]byte, n) - const alpha = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - for i := 0; i < n; i++ { - raw[i] = alpha[i%len(alpha)] - } - encoded := new(bytes.Buffer) - w := NewEncoder(encoded) - nn, err := w.Write(raw) - if nn != n || err != nil { - t.Fatalf("Encoder.Write(raw) = %d, %v want %d, nil", nn, err, n) - } - err = w.Close() - if err != nil { - t.Fatalf("Encoder.Close() = %v want nil", err) - } - decoded, err := ioutil.ReadAll(NewDecoder(encoded)) - if err != nil { - t.Fatalf("ioutil.ReadAll(NewDecoder(...)): %v", err) - } - - if !bytes.Equal(raw, decoded) { - var i int - for i = 0; i < len(decoded) && i < len(raw); i++ { - if decoded[i] != raw[i] { - break - } - } - t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i) - } -} diff --git a/src/pkg/encoding/gob/codec_test.go b/src/pkg/encoding/gob/codec_test.go new file mode 100644 index 000000000..ebcbb78eb --- /dev/null +++ b/src/pkg/encoding/gob/codec_test.go @@ -0,0 +1,1471 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "bytes" + "errors" + "math" + "math/rand" + "reflect" + "strings" + "testing" + "time" + "unsafe" +) + +// Guarantee encoding format by comparing some encodings to hand-written values +type EncodeT struct { + x uint64 + b []byte +} + +var encodeT = []EncodeT{ + {0x00, []byte{0x00}}, + {0x0F, []byte{0x0F}}, + {0xFF, []byte{0xFF, 0xFF}}, + {0xFFFF, []byte{0xFE, 0xFF, 0xFF}}, + {0xFFFFFF, []byte{0xFD, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFF, []byte{0xFC, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFF, []byte{0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFFFF, []byte{0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFFFFFF, []byte{0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0xFFFFFFFFFFFFFFFF, []byte{0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {0x1111, []byte{0xFE, 0x11, 0x11}}, + {0x1111111111111111, []byte{0xF8, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}}, + {0x8888888888888888, []byte{0xF8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}}, + {1 << 63, []byte{0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, +} + +// testError is meant to be used as a deferred function to turn a panic(gobError) into a +// plain test.Error call. +func testError(t *testing.T) { + if e := recover(); e != nil { + t.Error(e.(gobError).err) // Will re-panic if not one of our errors, such as a runtime error. + } + return +} + +// Test basic encode/decode routines for unsigned integers +func TestUintCodec(t *testing.T) { + defer testError(t) + b := new(bytes.Buffer) + encState := newEncoderState(b) + for _, tt := range encodeT { + b.Reset() + encState.encodeUint(tt.x) + if !bytes.Equal(tt.b, b.Bytes()) { + t.Errorf("encodeUint: %#x encode: expected % x got % x", tt.x, tt.b, b.Bytes()) + } + } + decState := newDecodeState(b) + for u := uint64(0); ; u = (u + 1) * 7 { + b.Reset() + encState.encodeUint(u) + v := decState.decodeUint() + if u != v { + t.Errorf("Encode/Decode: sent %#x received %#x", u, v) + } + if u&(1<<63) != 0 { + break + } + } +} + +func verifyInt(i int64, t *testing.T) { + defer testError(t) + var b = new(bytes.Buffer) + encState := newEncoderState(b) + encState.encodeInt(i) + decState := newDecodeState(b) + decState.buf = make([]byte, 8) + j := decState.decodeInt() + if i != j { + t.Errorf("Encode/Decode: sent %#x received %#x", uint64(i), uint64(j)) + } +} + +// Test basic encode/decode routines for signed integers +func TestIntCodec(t *testing.T) { + for u := uint64(0); ; u = (u + 1) * 7 { + // Do positive and negative values + i := int64(u) + verifyInt(i, t) + verifyInt(-i, t) + verifyInt(^i, t) + if u&(1<<63) != 0 { + break + } + } + verifyInt(-1<<63, t) // a tricky case +} + +// The result of encoding a true boolean with field number 7 +var boolResult = []byte{0x07, 0x01} + +// The result of encoding a number 17 with field number 7 +var signedResult = []byte{0x07, 2 * 17} +var unsignedResult = []byte{0x07, 17} +var floatResult = []byte{0x07, 0xFE, 0x31, 0x40} + +// The result of encoding a number 17+19i with field number 7 +var complexResult = []byte{0x07, 0xFE, 0x31, 0x40, 0xFE, 0x33, 0x40} + +// The result of encoding "hello" with field number 7 +var bytesResult = []byte{0x07, 0x05, 'h', 'e', 'l', 'l', 'o'} + +func newDecodeState(buf *bytes.Buffer) *decoderState { + d := new(decoderState) + d.b = buf + d.buf = make([]byte, uint64Size) + return d +} + +func newEncoderState(b *bytes.Buffer) *encoderState { + b.Reset() + state := &encoderState{enc: nil, b: b} + state.fieldnum = -1 + return state +} + +// Test instruction execution for encoding. +// Do not run the machine yet; instead do individual instructions crafted by hand. +func TestScalarEncInstructions(t *testing.T) { + var b = new(bytes.Buffer) + + // bool + { + data := struct{ a bool }{true} + instr := &encInstr{encBool, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(boolResult, b.Bytes()) { + t.Errorf("bool enc instructions: expected % x got % x", boolResult, b.Bytes()) + } + } + + // int + { + b.Reset() + data := struct{ a int }{17} + instr := &encInstr{encInt, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint + { + b.Reset() + data := struct{ a uint }{17} + instr := &encInstr{encUint, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // int8 + { + b.Reset() + data := struct{ a int8 }{17} + instr := &encInstr{encInt8, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int8 enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint8 + { + b.Reset() + data := struct{ a uint8 }{17} + instr := &encInstr{encUint8, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint8 enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // int16 + { + b.Reset() + data := struct{ a int16 }{17} + instr := &encInstr{encInt16, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int16 enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint16 + { + b.Reset() + data := struct{ a uint16 }{17} + instr := &encInstr{encUint16, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint16 enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // int32 + { + b.Reset() + data := struct{ a int32 }{17} + instr := &encInstr{encInt32, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int32 enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint32 + { + b.Reset() + data := struct{ a uint32 }{17} + instr := &encInstr{encUint32, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint32 enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // int64 + { + b.Reset() + data := struct{ a int64 }{17} + instr := &encInstr{encInt64, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(signedResult, b.Bytes()) { + t.Errorf("int64 enc instructions: expected % x got % x", signedResult, b.Bytes()) + } + } + + // uint64 + { + b.Reset() + data := struct{ a uint64 }{17} + instr := &encInstr{encUint64, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(unsignedResult, b.Bytes()) { + t.Errorf("uint64 enc instructions: expected % x got % x", unsignedResult, b.Bytes()) + } + } + + // float32 + { + b.Reset() + data := struct{ a float32 }{17} + instr := &encInstr{encFloat32, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(floatResult, b.Bytes()) { + t.Errorf("float32 enc instructions: expected % x got % x", floatResult, b.Bytes()) + } + } + + // float64 + { + b.Reset() + data := struct{ a float64 }{17} + instr := &encInstr{encFloat64, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(floatResult, b.Bytes()) { + t.Errorf("float64 enc instructions: expected % x got % x", floatResult, b.Bytes()) + } + } + + // bytes == []uint8 + { + b.Reset() + data := struct{ a []byte }{[]byte("hello")} + instr := &encInstr{encUint8Array, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(bytesResult, b.Bytes()) { + t.Errorf("bytes enc instructions: expected % x got % x", bytesResult, b.Bytes()) + } + } + + // string + { + b.Reset() + data := struct{ a string }{"hello"} + instr := &encInstr{encString, 6, 0, 0} + state := newEncoderState(b) + instr.op(instr, state, unsafe.Pointer(&data)) + if !bytes.Equal(bytesResult, b.Bytes()) { + t.Errorf("string enc instructions: expected % x got % x", bytesResult, b.Bytes()) + } + } +} + +func execDec(typ string, instr *decInstr, state *decoderState, t *testing.T, p unsafe.Pointer) { + defer testError(t) + v := int(state.decodeUint()) + if v+state.fieldnum != 6 { + t.Fatalf("decoding field number %d, got %d", 6, v+state.fieldnum) + } + instr.op(instr, state, decIndirect(p, instr.indir)) + state.fieldnum = 6 +} + +func newDecodeStateFromData(data []byte) *decoderState { + b := bytes.NewBuffer(data) + state := newDecodeState(b) + state.fieldnum = -1 + return state +} + +// Test instruction execution for decoding. +// Do not run the machine yet; instead do individual instructions crafted by hand. +func TestScalarDecInstructions(t *testing.T) { + ovfl := errors.New("overflow") + + // bool + { + var data struct { + a bool + } + instr := &decInstr{decBool, 6, 0, 0, ovfl} + state := newDecodeStateFromData(boolResult) + execDec("bool", instr, state, t, unsafe.Pointer(&data)) + if data.a != true { + t.Errorf("bool a = %v not true", data.a) + } + } + // int + { + var data struct { + a int + } + instr := &decInstr{decOpTable[reflect.Int], 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int a = %v not 17", data.a) + } + } + + // uint + { + var data struct { + a uint + } + instr := &decInstr{decOpTable[reflect.Uint], 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint a = %v not 17", data.a) + } + } + + // int8 + { + var data struct { + a int8 + } + instr := &decInstr{decInt8, 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int8", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int8 a = %v not 17", data.a) + } + } + + // uint8 + { + var data struct { + a uint8 + } + instr := &decInstr{decUint8, 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint8", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint8 a = %v not 17", data.a) + } + } + + // int16 + { + var data struct { + a int16 + } + instr := &decInstr{decInt16, 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int16", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int16 a = %v not 17", data.a) + } + } + + // uint16 + { + var data struct { + a uint16 + } + instr := &decInstr{decUint16, 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint16", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint16 a = %v not 17", data.a) + } + } + + // int32 + { + var data struct { + a int32 + } + instr := &decInstr{decInt32, 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int32", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int32 a = %v not 17", data.a) + } + } + + // uint32 + { + var data struct { + a uint32 + } + instr := &decInstr{decUint32, 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint32", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint32 a = %v not 17", data.a) + } + } + + // uintptr + { + var data struct { + a uintptr + } + instr := &decInstr{decOpTable[reflect.Uintptr], 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uintptr", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uintptr a = %v not 17", data.a) + } + } + + // int64 + { + var data struct { + a int64 + } + instr := &decInstr{decInt64, 6, 0, 0, ovfl} + state := newDecodeStateFromData(signedResult) + execDec("int64", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("int64 a = %v not 17", data.a) + } + } + + // uint64 + { + var data struct { + a uint64 + } + instr := &decInstr{decUint64, 6, 0, 0, ovfl} + state := newDecodeStateFromData(unsignedResult) + execDec("uint64", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("uint64 a = %v not 17", data.a) + } + } + + // float32 + { + var data struct { + a float32 + } + instr := &decInstr{decFloat32, 6, 0, 0, ovfl} + state := newDecodeStateFromData(floatResult) + execDec("float32", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("float32 a = %v not 17", data.a) + } + } + + // float64 + { + var data struct { + a float64 + } + instr := &decInstr{decFloat64, 6, 0, 0, ovfl} + state := newDecodeStateFromData(floatResult) + execDec("float64", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17 { + t.Errorf("float64 a = %v not 17", data.a) + } + } + + // complex64 + { + var data struct { + a complex64 + } + instr := &decInstr{decOpTable[reflect.Complex64], 6, 0, 0, ovfl} + state := newDecodeStateFromData(complexResult) + execDec("complex", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17+19i { + t.Errorf("complex a = %v not 17+19i", data.a) + } + } + + // complex128 + { + var data struct { + a complex128 + } + instr := &decInstr{decOpTable[reflect.Complex128], 6, 0, 0, ovfl} + state := newDecodeStateFromData(complexResult) + execDec("complex", instr, state, t, unsafe.Pointer(&data)) + if data.a != 17+19i { + t.Errorf("complex a = %v not 17+19i", data.a) + } + } + + // bytes == []uint8 + { + var data struct { + a []byte + } + instr := &decInstr{decUint8Slice, 6, 0, 0, ovfl} + state := newDecodeStateFromData(bytesResult) + execDec("bytes", instr, state, t, unsafe.Pointer(&data)) + if string(data.a) != "hello" { + t.Errorf(`bytes a = %q not "hello"`, string(data.a)) + } + } + + // string + { + var data struct { + a string + } + instr := &decInstr{decString, 6, 0, 0, ovfl} + state := newDecodeStateFromData(bytesResult) + execDec("bytes", instr, state, t, unsafe.Pointer(&data)) + if data.a != "hello" { + t.Errorf(`bytes a = %q not "hello"`, data.a) + } + } +} + +func TestEndToEnd(t *testing.T) { + type T2 struct { + T string + } + s1 := "string1" + s2 := "string2" + type T1 struct { + A, B, C int + M map[string]*float64 + EmptyMap map[string]int // to check that we receive a non-nil map. + N *[3]float64 + Strs *[2]string + Int64s *[]int64 + RI complex64 + S string + Y []byte + T *T2 + } + pi := 3.14159 + e := 2.71828 + t1 := &T1{ + A: 17, + B: 18, + C: -5, + M: map[string]*float64{"pi": &pi, "e": &e}, + EmptyMap: make(map[string]int), + N: &[3]float64{1.5, 2.5, 3.5}, + Strs: &[2]string{s1, s2}, + Int64s: &[]int64{77, 89, 123412342134}, + RI: 17 - 23i, + S: "Now is the time", + Y: []byte("hello, sailor"), + T: &T2{"this is T2"}, + } + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(t1) + if err != nil { + t.Error("encode:", err) + } + var _t1 T1 + err = NewDecoder(b).Decode(&_t1) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(t1, &_t1) { + t.Errorf("encode expected %v got %v", *t1, _t1) + } + // Be absolutely sure the received map is non-nil. + if t1.EmptyMap == nil { + t.Errorf("nil map sent") + } + if _t1.EmptyMap == nil { + t.Errorf("nil map received") + } +} + +func TestOverflow(t *testing.T) { + type inputT struct { + Maxi int64 + Mini int64 + Maxu uint64 + Maxf float64 + Minf float64 + Maxc complex128 + Minc complex128 + } + var it inputT + var err error + b := new(bytes.Buffer) + enc := NewEncoder(b) + dec := NewDecoder(b) + + // int8 + b.Reset() + it = inputT{ + Maxi: math.MaxInt8 + 1, + } + type outi8 struct { + Maxi int8 + Mini int8 + } + var o1 outi8 + enc.Encode(it) + err = dec.Decode(&o1) + if err == nil || err.Error() != `value for "Maxi" out of range` { + t.Error("wrong overflow error for int8:", err) + } + it = inputT{ + Mini: math.MinInt8 - 1, + } + b.Reset() + enc.Encode(it) + err = dec.Decode(&o1) + if err == nil || err.Error() != `value for "Mini" out of range` { + t.Error("wrong underflow error for int8:", err) + } + + // int16 + b.Reset() + it = inputT{ + Maxi: math.MaxInt16 + 1, + } + type outi16 struct { + Maxi int16 + Mini int16 + } + var o2 outi16 + enc.Encode(it) + err = dec.Decode(&o2) + if err == nil || err.Error() != `value for "Maxi" out of range` { + t.Error("wrong overflow error for int16:", err) + } + it = inputT{ + Mini: math.MinInt16 - 1, + } + b.Reset() + enc.Encode(it) + err = dec.Decode(&o2) + if err == nil || err.Error() != `value for "Mini" out of range` { + t.Error("wrong underflow error for int16:", err) + } + + // int32 + b.Reset() + it = inputT{ + Maxi: math.MaxInt32 + 1, + } + type outi32 struct { + Maxi int32 + Mini int32 + } + var o3 outi32 + enc.Encode(it) + err = dec.Decode(&o3) + if err == nil || err.Error() != `value for "Maxi" out of range` { + t.Error("wrong overflow error for int32:", err) + } + it = inputT{ + Mini: math.MinInt32 - 1, + } + b.Reset() + enc.Encode(it) + err = dec.Decode(&o3) + if err == nil || err.Error() != `value for "Mini" out of range` { + t.Error("wrong underflow error for int32:", err) + } + + // uint8 + b.Reset() + it = inputT{ + Maxu: math.MaxUint8 + 1, + } + type outu8 struct { + Maxu uint8 + } + var o4 outu8 + enc.Encode(it) + err = dec.Decode(&o4) + if err == nil || err.Error() != `value for "Maxu" out of range` { + t.Error("wrong overflow error for uint8:", err) + } + + // uint16 + b.Reset() + it = inputT{ + Maxu: math.MaxUint16 + 1, + } + type outu16 struct { + Maxu uint16 + } + var o5 outu16 + enc.Encode(it) + err = dec.Decode(&o5) + if err == nil || err.Error() != `value for "Maxu" out of range` { + t.Error("wrong overflow error for uint16:", err) + } + + // uint32 + b.Reset() + it = inputT{ + Maxu: math.MaxUint32 + 1, + } + type outu32 struct { + Maxu uint32 + } + var o6 outu32 + enc.Encode(it) + err = dec.Decode(&o6) + if err == nil || err.Error() != `value for "Maxu" out of range` { + t.Error("wrong overflow error for uint32:", err) + } + + // float32 + b.Reset() + it = inputT{ + Maxf: math.MaxFloat32 * 2, + } + type outf32 struct { + Maxf float32 + Minf float32 + } + var o7 outf32 + enc.Encode(it) + err = dec.Decode(&o7) + if err == nil || err.Error() != `value for "Maxf" out of range` { + t.Error("wrong overflow error for float32:", err) + } + + // complex64 + b.Reset() + it = inputT{ + Maxc: complex(math.MaxFloat32*2, math.MaxFloat32*2), + } + type outc64 struct { + Maxc complex64 + Minc complex64 + } + var o8 outc64 + enc.Encode(it) + err = dec.Decode(&o8) + if err == nil || err.Error() != `value for "Maxc" out of range` { + t.Error("wrong overflow error for complex64:", err) + } +} + +func TestNesting(t *testing.T) { + type RT struct { + A string + Next *RT + } + rt := new(RT) + rt.A = "level1" + rt.Next = new(RT) + rt.Next.A = "level2" + b := new(bytes.Buffer) + NewEncoder(b).Encode(rt) + var drt RT + dec := NewDecoder(b) + err := dec.Decode(&drt) + if err != nil { + t.Fatal("decoder error:", err) + } + if drt.A != rt.A { + t.Errorf("nesting: encode expected %v got %v", *rt, drt) + } + if drt.Next == nil { + t.Errorf("nesting: recursion failed") + } + if drt.Next.A != rt.Next.A { + t.Errorf("nesting: encode expected %v got %v", *rt.Next, *drt.Next) + } +} + +// These three structures have the same data with different indirections +type T0 struct { + A int + B int + C int + D int +} +type T1 struct { + A int + B *int + C **int + D ***int +} +type T2 struct { + A ***int + B **int + C *int + D int +} + +func TestAutoIndirection(t *testing.T) { + // First transfer t1 into t0 + var t1 T1 + t1.A = 17 + t1.B = new(int) + *t1.B = 177 + t1.C = new(*int) + *t1.C = new(int) + **t1.C = 1777 + t1.D = new(**int) + *t1.D = new(*int) + **t1.D = new(int) + ***t1.D = 17777 + b := new(bytes.Buffer) + enc := NewEncoder(b) + enc.Encode(t1) + dec := NewDecoder(b) + var t0 T0 + dec.Decode(&t0) + if t0.A != 17 || t0.B != 177 || t0.C != 1777 || t0.D != 17777 { + t.Errorf("t1->t0: expected {17 177 1777 17777}; got %v", t0) + } + + // Now transfer t2 into t0 + var t2 T2 + t2.D = 17777 + t2.C = new(int) + *t2.C = 1777 + t2.B = new(*int) + *t2.B = new(int) + **t2.B = 177 + t2.A = new(**int) + *t2.A = new(*int) + **t2.A = new(int) + ***t2.A = 17 + b.Reset() + enc.Encode(t2) + t0 = T0{} + dec.Decode(&t0) + if t0.A != 17 || t0.B != 177 || t0.C != 1777 || t0.D != 17777 { + t.Errorf("t2->t0 expected {17 177 1777 17777}; got %v", t0) + } + + // Now transfer t0 into t1 + t0 = T0{17, 177, 1777, 17777} + b.Reset() + enc.Encode(t0) + t1 = T1{} + dec.Decode(&t1) + if t1.A != 17 || *t1.B != 177 || **t1.C != 1777 || ***t1.D != 17777 { + t.Errorf("t0->t1 expected {17 177 1777 17777}; got {%d %d %d %d}", t1.A, *t1.B, **t1.C, ***t1.D) + } + + // Now transfer t0 into t2 + b.Reset() + enc.Encode(t0) + t2 = T2{} + dec.Decode(&t2) + if ***t2.A != 17 || **t2.B != 177 || *t2.C != 1777 || t2.D != 17777 { + t.Errorf("t0->t2 expected {17 177 1777 17777}; got {%d %d %d %d}", ***t2.A, **t2.B, *t2.C, t2.D) + } + + // Now do t2 again but without pre-allocated pointers. + b.Reset() + enc.Encode(t0) + ***t2.A = 0 + **t2.B = 0 + *t2.C = 0 + t2.D = 0 + dec.Decode(&t2) + if ***t2.A != 17 || **t2.B != 177 || *t2.C != 1777 || t2.D != 17777 { + t.Errorf("t0->t2 expected {17 177 1777 17777}; got {%d %d %d %d}", ***t2.A, **t2.B, *t2.C, t2.D) + } +} + +type RT0 struct { + A int + B string + C float64 +} +type RT1 struct { + C float64 + B string + A int + NotSet string +} + +func TestReorderedFields(t *testing.T) { + var rt0 RT0 + rt0.A = 17 + rt0.B = "hello" + rt0.C = 3.14159 + b := new(bytes.Buffer) + NewEncoder(b).Encode(rt0) + dec := NewDecoder(b) + var rt1 RT1 + // Wire type is RT0, local type is RT1. + err := dec.Decode(&rt1) + if err != nil { + t.Fatal("decode error:", err) + } + if rt0.A != rt1.A || rt0.B != rt1.B || rt0.C != rt1.C { + t.Errorf("rt1->rt0: expected %v; got %v", rt0, rt1) + } +} + +// Like an RT0 but with fields we'll ignore on the decode side. +type IT0 struct { + A int64 + B string + Ignore_d []int + Ignore_e [3]float64 + Ignore_f bool + Ignore_g string + Ignore_h []byte + Ignore_i *RT1 + Ignore_m map[string]int + C float64 +} + +func TestIgnoredFields(t *testing.T) { + var it0 IT0 + it0.A = 17 + it0.B = "hello" + it0.C = 3.14159 + it0.Ignore_d = []int{1, 2, 3} + it0.Ignore_e[0] = 1.0 + it0.Ignore_e[1] = 2.0 + it0.Ignore_e[2] = 3.0 + it0.Ignore_f = true + it0.Ignore_g = "pay no attention" + it0.Ignore_h = []byte("to the curtain") + it0.Ignore_i = &RT1{3.1, "hi", 7, "hello"} + it0.Ignore_m = map[string]int{"one": 1, "two": 2} + + b := new(bytes.Buffer) + NewEncoder(b).Encode(it0) + dec := NewDecoder(b) + var rt1 RT1 + // Wire type is IT0, local type is RT1. + err := dec.Decode(&rt1) + if err != nil { + t.Error("error: ", err) + } + if int(it0.A) != rt1.A || it0.B != rt1.B || it0.C != rt1.C { + t.Errorf("rt0->rt1: expected %v; got %v", it0, rt1) + } +} + +func TestBadRecursiveType(t *testing.T) { + type Rec ***Rec + var rec Rec + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(&rec) + if err == nil { + t.Error("expected error; got none") + } else if strings.Index(err.Error(), "recursive") < 0 { + t.Error("expected recursive type error; got", err) + } + // Can't test decode easily because we can't encode one, so we can't pass one to a Decoder. +} + +type Bad0 struct { + CH chan int + C float64 +} + +func TestInvalidField(t *testing.T) { + var bad0 Bad0 + bad0.CH = make(chan int) + b := new(bytes.Buffer) + dummyEncoder := new(Encoder) // sufficient for this purpose. + dummyEncoder.encode(b, reflect.ValueOf(&bad0), userType(reflect.TypeOf(&bad0))) + if err := dummyEncoder.err; err == nil { + t.Error("expected error; got none") + } else if strings.Index(err.Error(), "type") < 0 { + t.Error("expected type error; got", err) + } +} + +type Indirect struct { + A ***[3]int + S ***[]int + M ****map[string]int +} + +type Direct struct { + A [3]int + S []int + M map[string]int +} + +func TestIndirectSliceMapArray(t *testing.T) { + // Marshal indirect, unmarshal to direct. + i := new(Indirect) + i.A = new(**[3]int) + *i.A = new(*[3]int) + **i.A = new([3]int) + ***i.A = [3]int{1, 2, 3} + i.S = new(**[]int) + *i.S = new(*[]int) + **i.S = new([]int) + ***i.S = []int{4, 5, 6} + i.M = new(***map[string]int) + *i.M = new(**map[string]int) + **i.M = new(*map[string]int) + ***i.M = new(map[string]int) + ****i.M = map[string]int{"one": 1, "two": 2, "three": 3} + b := new(bytes.Buffer) + NewEncoder(b).Encode(i) + dec := NewDecoder(b) + var d Direct + err := dec.Decode(&d) + if err != nil { + t.Error("error: ", err) + } + if len(d.A) != 3 || d.A[0] != 1 || d.A[1] != 2 || d.A[2] != 3 { + t.Errorf("indirect to direct: d.A is %v not %v", d.A, ***i.A) + } + if len(d.S) != 3 || d.S[0] != 4 || d.S[1] != 5 || d.S[2] != 6 { + t.Errorf("indirect to direct: d.S is %v not %v", d.S, ***i.S) + } + if len(d.M) != 3 || d.M["one"] != 1 || d.M["two"] != 2 || d.M["three"] != 3 { + t.Errorf("indirect to direct: d.M is %v not %v", d.M, ***i.M) + } + // Marshal direct, unmarshal to indirect. + d.A = [3]int{11, 22, 33} + d.S = []int{44, 55, 66} + d.M = map[string]int{"four": 4, "five": 5, "six": 6} + i = new(Indirect) + b.Reset() + NewEncoder(b).Encode(d) + dec = NewDecoder(b) + err = dec.Decode(&i) + if err != nil { + t.Fatal("error: ", err) + } + if len(***i.A) != 3 || (***i.A)[0] != 11 || (***i.A)[1] != 22 || (***i.A)[2] != 33 { + t.Errorf("direct to indirect: ***i.A is %v not %v", ***i.A, d.A) + } + if len(***i.S) != 3 || (***i.S)[0] != 44 || (***i.S)[1] != 55 || (***i.S)[2] != 66 { + t.Errorf("direct to indirect: ***i.S is %v not %v", ***i.S, ***i.S) + } + if len(****i.M) != 3 || (****i.M)["four"] != 4 || (****i.M)["five"] != 5 || (****i.M)["six"] != 6 { + t.Errorf("direct to indirect: ****i.M is %v not %v", ****i.M, d.M) + } +} + +// An interface with several implementations +type Squarer interface { + Square() int +} + +type Int int + +func (i Int) Square() int { + return int(i * i) +} + +type Float float64 + +func (f Float) Square() int { + return int(f * f) +} + +type Vector []int + +func (v Vector) Square() int { + sum := 0 + for _, x := range v { + sum += x * x + } + return sum +} + +type Point struct { + X, Y int +} + +func (p Point) Square() int { + return p.X*p.X + p.Y*p.Y +} + +// A struct with interfaces in it. +type InterfaceItem struct { + I int + Sq1, Sq2, Sq3 Squarer + F float64 + Sq []Squarer +} + +// The same struct without interfaces +type NoInterfaceItem struct { + I int + F float64 +} + +func TestInterface(t *testing.T) { + iVal := Int(3) + fVal := Float(5) + // Sending a Vector will require that the receiver define a type in the middle of + // receiving the value for item2. + vVal := Vector{1, 2, 3} + b := new(bytes.Buffer) + item1 := &InterfaceItem{1, iVal, fVal, vVal, 11.5, []Squarer{iVal, fVal, nil, vVal}} + // Register the types. + Register(Int(0)) + Register(Float(0)) + Register(Vector{}) + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := InterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + if item2.I != item1.I { + t.Error("normal int did not decode correctly") + } + if item2.Sq1 == nil || item2.Sq1.Square() != iVal.Square() { + t.Error("Int did not decode correctly") + } + if item2.Sq2 == nil || item2.Sq2.Square() != fVal.Square() { + t.Error("Float did not decode correctly") + } + if item2.Sq3 == nil || item2.Sq3.Square() != vVal.Square() { + t.Error("Vector did not decode correctly") + } + if item2.F != item1.F { + t.Error("normal float did not decode correctly") + } + // Now check that we received a slice of Squarers correctly, including a nil element + if len(item1.Sq) != len(item2.Sq) { + t.Fatalf("[]Squarer length wrong: got %d; expected %d", len(item2.Sq), len(item1.Sq)) + } + for i, v1 := range item1.Sq { + v2 := item2.Sq[i] + if v1 == nil || v2 == nil { + if v1 != nil || v2 != nil { + t.Errorf("item %d inconsistent nils", i) + } + continue + if v1.Square() != v2.Square() { + t.Errorf("item %d inconsistent values: %v %v", i, v1, v2) + } + } + } +} + +// A struct with all basic types, stored in interfaces. +type BasicInterfaceItem struct { + Int, Int8, Int16, Int32, Int64 interface{} + Uint, Uint8, Uint16, Uint32, Uint64 interface{} + Float32, Float64 interface{} + Complex64, Complex128 interface{} + Bool interface{} + String interface{} + Bytes interface{} +} + +func TestInterfaceBasic(t *testing.T) { + b := new(bytes.Buffer) + item1 := &BasicInterfaceItem{ + int(1), int8(1), int16(1), int32(1), int64(1), + uint(1), uint8(1), uint16(1), uint32(1), uint64(1), + float32(1), 1.0, + complex64(1i), complex128(1i), + true, + "hello", + []byte("sailor"), + } + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := &BasicInterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(item1, item2) { + t.Errorf("encode expected %v got %v", item1, item2) + } + // Hand check a couple for correct types. + if v, ok := item2.Bool.(bool); !ok || !v { + t.Error("boolean should be true") + } + if v, ok := item2.String.(string); !ok || v != item1.String.(string) { + t.Errorf("string should be %v is %v", item1.String, v) + } +} + +type String string + +type PtrInterfaceItem struct { + Str1 interface{} // basic + Str2 interface{} // derived +} + +// We'll send pointers; should receive values. +// Also check that we can register T but send *T. +func TestInterfacePointer(t *testing.T) { + b := new(bytes.Buffer) + str1 := "howdy" + str2 := String("kiddo") + item1 := &PtrInterfaceItem{ + &str1, + &str2, + } + // Register the type. + Register(str2) + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := &PtrInterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + // Hand test for correct types and values. + if v, ok := item2.Str1.(string); !ok || v != str1 { + t.Errorf("basic string failed: %q should be %q", v, str1) + } + if v, ok := item2.Str2.(String); !ok || v != str2 { + t.Errorf("derived type String failed: %q should be %q", v, str2) + } +} + +func TestIgnoreInterface(t *testing.T) { + iVal := Int(3) + fVal := Float(5) + // Sending a Point will require that the receiver define a type in the middle of + // receiving the value for item2. + pVal := Point{2, 3} + b := new(bytes.Buffer) + item1 := &InterfaceItem{1, iVal, fVal, pVal, 11.5, nil} + // Register the types. + Register(Int(0)) + Register(Float(0)) + Register(Point{}) + err := NewEncoder(b).Encode(item1) + if err != nil { + t.Error("expected no encode error; got", err) + } + + item2 := NoInterfaceItem{} + err = NewDecoder(b).Decode(&item2) + if err != nil { + t.Fatal("decode:", err) + } + if item2.I != item1.I { + t.Error("normal int did not decode correctly") + } + if item2.F != item2.F { + t.Error("normal float did not decode correctly") + } +} + +type U struct { + A int + B string + c float64 + D uint +} + +func TestUnexportedFields(t *testing.T) { + var u0 U + u0.A = 17 + u0.B = "hello" + u0.c = 3.14159 + u0.D = 23 + b := new(bytes.Buffer) + NewEncoder(b).Encode(u0) + dec := NewDecoder(b) + var u1 U + u1.c = 1234. + err := dec.Decode(&u1) + if err != nil { + t.Fatal("decode error:", err) + } + if u0.A != u0.A || u0.B != u1.B || u0.D != u1.D { + t.Errorf("u1->u0: expected %v; got %v", u0, u1) + } + if u1.c != 1234. { + t.Error("u1.c modified") + } +} + +var singletons = []interface{}{ + true, + 7, + 3.2, + "hello", + [3]int{11, 22, 33}, + []float32{0.5, 0.25, 0.125}, + map[string]int{"one": 1, "two": 2}, +} + +func TestDebugSingleton(t *testing.T) { + if debugFunc == nil { + return + } + b := new(bytes.Buffer) + // Accumulate a number of values and print them out all at once. + for _, x := range singletons { + err := NewEncoder(b).Encode(x) + if err != nil { + t.Fatal("encode:", err) + } + } + debugFunc(b) +} + +// A type that won't be defined in the gob until we send it in an interface value. +type OnTheFly struct { + A int +} + +type DT struct { + // X OnTheFly + A int + B string + C float64 + I interface{} + J interface{} + I_nil interface{} + M map[string]int + T [3]int + S []string +} + +func TestDebugStruct(t *testing.T) { + if debugFunc == nil { + return + } + Register(OnTheFly{}) + var dt DT + dt.A = 17 + dt.B = "hello" + dt.C = 3.14159 + dt.I = 271828 + dt.J = OnTheFly{3} + dt.I_nil = nil + dt.M = map[string]int{"one": 1, "two": 2} + dt.T = [3]int{11, 22, 33} + dt.S = []string{"hi", "joe"} + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(dt) + if err != nil { + t.Fatal("encode:", err) + } + debugBuffer := bytes.NewBuffer(b.Bytes()) + dt2 := &DT{} + err = NewDecoder(b).Decode(&dt2) + if err != nil { + t.Error("decode:", err) + } + debugFunc(debugBuffer) +} + +func encFuzzDec(rng *rand.Rand, in interface{}) error { + buf := new(bytes.Buffer) + enc := NewEncoder(buf) + if err := enc.Encode(&in); err != nil { + return err + } + + b := buf.Bytes() + for i, bi := range b { + if rng.Intn(10) < 3 { + b[i] = bi + uint8(rng.Intn(256)) + } + } + + dec := NewDecoder(buf) + var e interface{} + if err := dec.Decode(&e); err != nil { + return err + } + return nil +} + +// This does some "fuzz testing" by attempting to decode a sequence of random bytes. +func TestFuzz(t *testing.T) { + if testing.Short() { + return + } + + // all possible inputs + input := []interface{}{ + new(int), + new(float32), + new(float64), + new(complex128), + &ByteStruct{255}, + &ArrayStruct{}, + &StringStruct{"hello"}, + &GobTest1{0, &StringStruct{"hello"}}, + } + testFuzz(t, time.Now().UnixNano(), 100, input...) +} + +func TestFuzzRegressions(t *testing.T) { + // An instance triggering a type name of length ~102 GB. + testFuzz(t, 1328492090837718000, 100, new(float32)) + // An instance triggering a type name of 1.6 GB. + // Commented out because it takes 5m to run. + //testFuzz(t, 1330522872628565000, 100, new(int)) +} + +func testFuzz(t *testing.T, seed int64, n int, input ...interface{}) { + for _, e := range input { + t.Logf("seed=%d n=%d e=%T", seed, n, e) + rng := rand.New(rand.NewSource(seed)) + for i := 0; i < n; i++ { + encFuzzDec(rng, e) + } + } +} diff --git a/src/pkg/encoding/gob/debug.go b/src/pkg/encoding/gob/debug.go new file mode 100644 index 000000000..31d1351fc --- /dev/null +++ b/src/pkg/encoding/gob/debug.go @@ -0,0 +1,695 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Delete the next line to include in the gob package. +// +build ignore + +package gob + +// This file is not normally included in the gob package. Used only for debugging the package itself. +// Except for reading uints, it is an implementation of a reader that is independent of +// the one implemented by Decoder. +// To enable the Debug function, delete the +build ignore line above and do +// go install + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + "sync" +) + +var dumpBytes = false // If true, print the remaining bytes in the input buffer at each item. + +// Init installs the debugging facility. If this file is not compiled in the +// package, the tests in codec_test.go are no-ops. +func init() { + debugFunc = Debug +} + +var ( + blanks = bytes.Repeat([]byte{' '}, 3*10) + empty = []byte(": \n") + tabs = strings.Repeat("\t", 100) +) + +// tab indents itself when printed. +type tab int + +func (t tab) String() string { + n := int(t) + if n > len(tabs) { + n = len(tabs) + } + return tabs[0:n] +} + +func (t tab) print() { + fmt.Fprint(os.Stderr, t) +} + +// A peekReader wraps an io.Reader, allowing one to peek ahead to see +// what's coming without stealing the data from the client of the Reader. +type peekReader struct { + r io.Reader + data []byte // read-ahead data +} + +// newPeekReader returns a peekReader that wraps r. +func newPeekReader(r io.Reader) *peekReader { + return &peekReader{r: r} +} + +// Read is the usual method. It will first take data that has been read ahead. +func (p *peekReader) Read(b []byte) (n int, err error) { + if len(p.data) == 0 { + return p.r.Read(b) + } + // Satisfy what's possible from the read-ahead data. + n = copy(b, p.data) + // Move data down to beginning of slice, to avoid endless growth + copy(p.data, p.data[n:]) + p.data = p.data[:len(p.data)-n] + return +} + +// peek returns as many bytes as possible from the unread +// portion of the stream, up to the length of b. +func (p *peekReader) peek(b []byte) (n int, err error) { + if len(p.data) > 0 { + n = copy(b, p.data) + if n == len(b) { + return + } + b = b[n:] + } + if len(b) == 0 { + return + } + m, e := io.ReadFull(p.r, b) + if m > 0 { + p.data = append(p.data, b[:m]...) + } + n += m + if e == io.ErrUnexpectedEOF { + // That means m > 0 but we reached EOF. If we got data + // we won't complain about not being able to peek enough. + if n > 0 { + e = nil + } else { + e = io.EOF + } + } + return n, e +} + +type debugger struct { + mutex sync.Mutex + remain int // the number of bytes known to remain in the input + remainingKnown bool // the value of 'remain' is valid + r *peekReader + wireType map[typeId]*wireType + tmp []byte // scratch space for decoding uints. +} + +// dump prints the next nBytes of the input. +// It arranges to print the output aligned from call to +// call, to make it easy to see what has been consumed. +func (deb *debugger) dump(format string, args ...interface{}) { + if !dumpBytes { + return + } + fmt.Fprintf(os.Stderr, format+" ", args...) + if !deb.remainingKnown { + return + } + if deb.remain < 0 { + fmt.Fprintf(os.Stderr, "remaining byte count is negative! %d\n", deb.remain) + return + } + data := make([]byte, deb.remain) + n, _ := deb.r.peek(data) + if n == 0 { + os.Stderr.Write(empty) + return + } + b := new(bytes.Buffer) + fmt.Fprintf(b, "[%d]{\n", deb.remain) + // Blanks until first byte + lineLength := 0 + if n := len(data); n%10 != 0 { + lineLength = 10 - n%10 + fmt.Fprintf(b, "\t%s", blanks[:lineLength*3]) + } + // 10 bytes per line + for len(data) > 0 { + if lineLength == 0 { + fmt.Fprint(b, "\t") + } + m := 10 - lineLength + lineLength = 0 + if m > len(data) { + m = len(data) + } + fmt.Fprintf(b, "% x\n", data[:m]) + data = data[m:] + } + fmt.Fprint(b, "}\n") + os.Stderr.Write(b.Bytes()) +} + +// Debug prints a human-readable representation of the gob data read from r. +// It is a no-op unless debugging was enabled when the package was built. +func Debug(r io.Reader) { + err := debug(r) + if err != nil { + fmt.Fprintf(os.Stderr, "gob debug: %s\n", err) + } +} + +// debug implements Debug, but catches panics and returns +// them as errors to be printed by Debug. +func debug(r io.Reader) (err error) { + defer catchError(&err) + fmt.Fprintln(os.Stderr, "Start of debugging") + deb := &debugger{ + r: newPeekReader(r), + wireType: make(map[typeId]*wireType), + tmp: make([]byte, 16), + } + if b, ok := r.(*bytes.Buffer); ok { + deb.remain = b.Len() + deb.remainingKnown = true + } + deb.gobStream() + return +} + +// note that we've consumed some bytes +func (deb *debugger) consumed(n int) { + if deb.remainingKnown { + deb.remain -= n + } +} + +// int64 decodes and returns the next integer, which must be present. +// Don't call this if you could be at EOF. +func (deb *debugger) int64() int64 { + return toInt(deb.uint64()) +} + +// uint64 returns and decodes the next unsigned integer, which must be present. +// Don't call this if you could be at EOF. +// TODO: handle errors better. +func (deb *debugger) uint64() uint64 { + n, w, err := decodeUintReader(deb.r, deb.tmp) + if err != nil { + errorf("debug: read error: %s", err) + } + deb.consumed(w) + return n +} + +// GobStream: +// DelimitedMessage* (until EOF) +func (deb *debugger) gobStream() { + // Make sure we're single-threaded through here. + deb.mutex.Lock() + defer deb.mutex.Unlock() + + for deb.delimitedMessage(0) { + } +} + +// DelimitedMessage: +// uint(lengthOfMessage) Message +func (deb *debugger) delimitedMessage(indent tab) bool { + for { + n := deb.loadBlock(true) + if n < 0 { + return false + } + deb.dump("Delimited message of length %d", n) + deb.message(indent) + } + return true +} + +// loadBlock preps us to read a message +// of the length specified next in the input. It returns +// the length of the block. The argument tells whether +// an EOF is acceptable now. If it is and one is found, +// the return value is negative. +func (deb *debugger) loadBlock(eofOK bool) int { + n64, w, err := decodeUintReader(deb.r, deb.tmp) // deb.uint64 will error at EOF + if err != nil { + if eofOK && err == io.EOF { + return -1 + } + errorf("debug: unexpected error: %s", err) + } + deb.consumed(w) + n := int(n64) + if n < 0 { + errorf("huge value for message length: %d", n64) + } + return int(n) +} + +// Message: +// TypeSequence TypedValue +// TypeSequence +// (TypeDefinition DelimitedTypeDefinition*)? +// DelimitedTypeDefinition: +// uint(lengthOfTypeDefinition) TypeDefinition +// TypedValue: +// int(typeId) Value +func (deb *debugger) message(indent tab) bool { + for { + // Convert the uint64 to a signed integer typeId + uid := deb.int64() + id := typeId(uid) + deb.dump("type id=%d", id) + if id < 0 { + deb.typeDefinition(indent, -id) + n := deb.loadBlock(false) + deb.dump("Message of length %d", n) + continue + } else { + deb.value(indent, id) + break + } + } + return true +} + +// Helper methods to make it easy to scan a type descriptor. + +// common returns the CommonType at the input point. +func (deb *debugger) common() CommonType { + fieldNum := -1 + name := "" + id := typeId(0) + for { + delta := deb.delta(-1) + if delta == 0 { + break + } + fieldNum += delta + switch fieldNum { + case 0: + name = deb.string() + case 1: + // Id typeId + id = deb.typeId() + default: + errorf("corrupted CommonType") + } + } + return CommonType{name, id} +} + +// uint returns the unsigned int at the input point, as a uint (not uint64). +func (deb *debugger) uint() uint { + return uint(deb.uint64()) +} + +// int returns the signed int at the input point, as an int (not int64). +func (deb *debugger) int() int { + return int(deb.int64()) +} + +// typeId returns the type id at the input point. +func (deb *debugger) typeId() typeId { + return typeId(deb.int64()) +} + +// string returns the string at the input point. +func (deb *debugger) string() string { + x := int(deb.uint64()) + b := make([]byte, x) + nb, _ := deb.r.Read(b) + if nb != x { + errorf("corrupted type") + } + deb.consumed(nb) + return string(b) +} + +// delta returns the field delta at the input point. The expect argument, +// if non-negative, identifies what the value should be. +func (deb *debugger) delta(expect int) int { + delta := int(deb.uint64()) + if delta < 0 || (expect >= 0 && delta != expect) { + errorf("decode: corrupted type: delta %d expected %d", delta, expect) + } + return delta +} + +// TypeDefinition: +// [int(-typeId) (already read)] encodingOfWireType +func (deb *debugger) typeDefinition(indent tab, id typeId) { + deb.dump("type definition for id %d", id) + // Encoding is of a wireType. Decode the structure as usual + fieldNum := -1 + wire := new(wireType) + // A wireType defines a single field. + delta := deb.delta(-1) + fieldNum += delta + switch fieldNum { + case 0: // array type, one field of {{Common}, elem, length} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is type Id of elem + deb.delta(1) + id := deb.typeId() + // Field number 3 is length + deb.delta(1) + length := deb.int() + wire.ArrayT = &arrayType{com, id, length} + + case 1: // slice type, one field of {{Common}, elem} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is type Id of elem + deb.delta(1) + id := deb.typeId() + wire.SliceT = &sliceType{com, id} + + case 2: // struct type, one field of {{Common}, []fieldType} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is slice of FieldType + deb.delta(1) + numField := int(deb.uint()) + field := make([]*fieldType, numField) + for i := 0; i < numField; i++ { + field[i] = new(fieldType) + deb.delta(1) // field 0 of fieldType: name + field[i].Name = deb.string() + deb.delta(1) // field 1 of fieldType: id + field[i].Id = deb.typeId() + deb.delta(0) // end of fieldType + } + wire.StructT = &structType{com, field} + + case 3: // map type, one field of {{Common}, key, elem} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is type Id of key + deb.delta(1) + keyId := deb.typeId() + // Field number 2 is type Id of elem + deb.delta(1) + elemId := deb.typeId() + wire.MapT = &mapType{com, keyId, elemId} + case 4: // GobEncoder type, one field of {{Common}} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + wire.GobEncoderT = &gobEncoderType{com} + default: + errorf("bad field in type %d", fieldNum) + } + deb.printWireType(indent, wire) + deb.delta(0) // end inner type (arrayType, etc.) + deb.delta(0) // end wireType + // Remember we've seen this type. + deb.wireType[id] = wire +} + +// Value: +// SingletonValue | StructValue +func (deb *debugger) value(indent tab, id typeId) { + wire, ok := deb.wireType[id] + if ok && wire.StructT != nil { + deb.structValue(indent, id) + } else { + deb.singletonValue(indent, id) + } +} + +// SingletonValue: +// uint(0) FieldValue +func (deb *debugger) singletonValue(indent tab, id typeId) { + deb.dump("Singleton value") + // is it a builtin type? + wire := deb.wireType[id] + _, ok := builtinIdToType[id] + if !ok && wire == nil { + errorf("type id %d not defined", id) + } + m := deb.uint64() + if m != 0 { + errorf("expected zero; got %d", m) + } + deb.fieldValue(indent, id) +} + +// InterfaceValue: +// NilInterfaceValue | NonNilInterfaceValue +func (deb *debugger) interfaceValue(indent tab) { + deb.dump("Start of interface value") + if nameLen := deb.uint64(); nameLen == 0 { + deb.nilInterfaceValue(indent) + } else { + deb.nonNilInterfaceValue(indent, int(nameLen)) + } +} + +// NilInterfaceValue: +// uint(0) [already read] +func (deb *debugger) nilInterfaceValue(indent tab) int { + fmt.Fprintf(os.Stderr, "%snil interface\n", indent) + return 0 +} + +// NonNilInterfaceValue: +// ConcreteTypeName TypeSequence InterfaceContents +// ConcreteTypeName: +// uint(lengthOfName) [already read=n] name +// InterfaceContents: +// int(concreteTypeId) DelimitedValue +// DelimitedValue: +// uint(length) Value +func (deb *debugger) nonNilInterfaceValue(indent tab, nameLen int) { + // ConcreteTypeName + b := make([]byte, nameLen) + deb.r.Read(b) // TODO: CHECK THESE READS!! + deb.consumed(nameLen) + name := string(b) + + for { + id := deb.typeId() + if id < 0 { + deb.typeDefinition(indent, -id) + n := deb.loadBlock(false) + deb.dump("Nested message of length %d", n) + } else { + // DelimitedValue + x := deb.uint64() // in case we want to ignore the value; we don't. + fmt.Fprintf(os.Stderr, "%sinterface value, type %q id=%d; valueLength %d\n", indent, name, id, x) + deb.value(indent, id) + break + } + } +} + +// printCommonType prints a common type; used by printWireType. +func (deb *debugger) printCommonType(indent tab, kind string, common *CommonType) { + indent.print() + fmt.Fprintf(os.Stderr, "%s %q id=%d\n", kind, common.Name, common.Id) +} + +// printWireType prints the contents of a wireType. +func (deb *debugger) printWireType(indent tab, wire *wireType) { + fmt.Fprintf(os.Stderr, "%stype definition {\n", indent) + indent++ + switch { + case wire.ArrayT != nil: + deb.printCommonType(indent, "array", &wire.ArrayT.CommonType) + fmt.Fprintf(os.Stderr, "%slen %d\n", indent+1, wire.ArrayT.Len) + fmt.Fprintf(os.Stderr, "%selemid %d\n", indent+1, wire.ArrayT.Elem) + case wire.MapT != nil: + deb.printCommonType(indent, "map", &wire.MapT.CommonType) + fmt.Fprintf(os.Stderr, "%skey id=%d\n", indent+1, wire.MapT.Key) + fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.MapT.Elem) + case wire.SliceT != nil: + deb.printCommonType(indent, "slice", &wire.SliceT.CommonType) + fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.SliceT.Elem) + case wire.StructT != nil: + deb.printCommonType(indent, "struct", &wire.StructT.CommonType) + for i, field := range wire.StructT.Field { + fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\tid=%d\n", indent+1, i, field.Name, field.Id) + } + case wire.GobEncoderT != nil: + deb.printCommonType(indent, "GobEncoder", &wire.GobEncoderT.CommonType) + } + indent-- + fmt.Fprintf(os.Stderr, "%s}\n", indent) +} + +// fieldValue prints a value of any type, such as a struct field. +// FieldValue: +// builtinValue | ArrayValue | MapValue | SliceValue | StructValue | InterfaceValue +func (deb *debugger) fieldValue(indent tab, id typeId) { + _, ok := builtinIdToType[id] + if ok { + if id == tInterface { + deb.interfaceValue(indent) + } else { + deb.printBuiltin(indent, id) + } + return + } + wire, ok := deb.wireType[id] + if !ok { + errorf("type id %d not defined", id) + } + switch { + case wire.ArrayT != nil: + deb.arrayValue(indent, wire) + case wire.MapT != nil: + deb.mapValue(indent, wire) + case wire.SliceT != nil: + deb.sliceValue(indent, wire) + case wire.StructT != nil: + deb.structValue(indent, id) + case wire.GobEncoderT != nil: + deb.gobEncoderValue(indent, id) + default: + panic("bad wire type for field") + } +} + +// printBuiltin prints a value not of a fundamental type, that is, +// one whose type is known to gobs at bootstrap time. +func (deb *debugger) printBuiltin(indent tab, id typeId) { + switch id { + case tBool: + x := deb.int64() + if x == 0 { + fmt.Fprintf(os.Stderr, "%sfalse\n", indent) + } else { + fmt.Fprintf(os.Stderr, "%strue\n", indent) + } + case tInt: + x := deb.int64() + fmt.Fprintf(os.Stderr, "%s%d\n", indent, x) + case tUint: + x := deb.int64() + fmt.Fprintf(os.Stderr, "%s%d\n", indent, x) + case tFloat: + x := deb.uint64() + fmt.Fprintf(os.Stderr, "%s%g\n", indent, floatFromBits(x)) + case tComplex: + r := deb.uint64() + i := deb.uint64() + fmt.Fprintf(os.Stderr, "%s%g+%gi\n", indent, floatFromBits(r), floatFromBits(i)) + case tBytes: + x := int(deb.uint64()) + b := make([]byte, x) + deb.r.Read(b) + deb.consumed(x) + fmt.Fprintf(os.Stderr, "%s{% x}=%q\n", indent, b, b) + case tString: + x := int(deb.uint64()) + b := make([]byte, x) + deb.r.Read(b) + deb.consumed(x) + fmt.Fprintf(os.Stderr, "%s%q\n", indent, b) + default: + panic("unknown builtin") + } +} + +// ArrayValue: +// uint(n) FieldValue*n +func (deb *debugger) arrayValue(indent tab, wire *wireType) { + elemId := wire.ArrayT.Elem + u := deb.uint64() + length := int(u) + for i := 0; i < length; i++ { + deb.fieldValue(indent, elemId) + } + if length != wire.ArrayT.Len { + fmt.Fprintf(os.Stderr, "%s(wrong length for array: %d should be %d)\n", indent, length, wire.ArrayT.Len) + } +} + +// MapValue: +// uint(n) (FieldValue FieldValue)*n [n (key, value) pairs] +func (deb *debugger) mapValue(indent tab, wire *wireType) { + keyId := wire.MapT.Key + elemId := wire.MapT.Elem + u := deb.uint64() + length := int(u) + for i := 0; i < length; i++ { + deb.fieldValue(indent+1, keyId) + deb.fieldValue(indent+1, elemId) + } +} + +// SliceValue: +// uint(n) (n FieldValue) +func (deb *debugger) sliceValue(indent tab, wire *wireType) { + elemId := wire.SliceT.Elem + u := deb.uint64() + length := int(u) + deb.dump("Start of slice of length %d", length) + + for i := 0; i < length; i++ { + deb.fieldValue(indent, elemId) + } +} + +// StructValue: +// (uint(fieldDelta) FieldValue)* +func (deb *debugger) structValue(indent tab, id typeId) { + deb.dump("Start of struct value of %q id=%d\n<<\n", id.name(), id) + fmt.Fprintf(os.Stderr, "%s%s struct {\n", indent, id.name()) + wire, ok := deb.wireType[id] + if !ok { + errorf("type id %d not defined", id) + } + strct := wire.StructT + fieldNum := -1 + indent++ + for { + delta := deb.uint64() + if delta == 0 { // struct terminator is zero delta fieldnum + break + } + fieldNum += int(delta) + if fieldNum < 0 || fieldNum >= len(strct.Field) { + deb.dump("field number out of range: prevField=%d delta=%d", fieldNum-int(delta), delta) + break + } + fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\n", indent, fieldNum, wire.StructT.Field[fieldNum].Name) + deb.fieldValue(indent+1, strct.Field[fieldNum].Id) + } + indent-- + fmt.Fprintf(os.Stderr, "%s} // end %s struct\n", indent, id.name()) + deb.dump(">> End of struct value of type %d %q", id, id.name()) +} + +// GobEncoderValue: +// uint(n) byte*n +func (deb *debugger) gobEncoderValue(indent tab, id typeId) { + len := deb.uint64() + deb.dump("GobEncoder value of %q id=%d, length %d\n", id.name(), id, len) + fmt.Fprintf(os.Stderr, "%s%s (implements GobEncoder)\n", indent, id.name()) + data := make([]byte, len) + _, err := deb.r.Read(data) + if err != nil { + errorf("gobEncoder data read: %s", err) + } + fmt.Fprintf(os.Stderr, "%s[% .2x]\n", indent+1, data) +} diff --git a/src/pkg/encoding/gob/decode.go b/src/pkg/encoding/gob/decode.go new file mode 100644 index 000000000..e32a178ab --- /dev/null +++ b/src/pkg/encoding/gob/decode.go @@ -0,0 +1,1300 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +// TODO(rsc): When garbage collector changes, revisit +// the allocations in this file that use unsafe.Pointer. + +import ( + "bytes" + "errors" + "io" + "math" + "reflect" + "unsafe" +) + +var ( + errBadUint = errors.New("gob: encoded unsigned integer out of range") + errBadType = errors.New("gob: unknown type id or corrupted data") + errRange = errors.New("gob: bad data: field numbers out of bounds") +) + +// decoderState is the execution state of an instance of the decoder. A new state +// is created for nested objects. +type decoderState struct { + dec *Decoder + // The buffer is stored with an extra indirection because it may be replaced + // if we load a type during decode (when reading an interface value). + b *bytes.Buffer + fieldnum int // the last field number read. + buf []byte + next *decoderState // for free list +} + +// We pass the bytes.Buffer separately for easier testing of the infrastructure +// without requiring a full Decoder. +func (dec *Decoder) newDecoderState(buf *bytes.Buffer) *decoderState { + d := dec.freeList + if d == nil { + d = new(decoderState) + d.dec = dec + d.buf = make([]byte, uint64Size) + } else { + dec.freeList = d.next + } + d.b = buf + return d +} + +func (dec *Decoder) freeDecoderState(d *decoderState) { + d.next = dec.freeList + dec.freeList = d +} + +func overflow(name string) error { + return errors.New(`value for "` + name + `" out of range`) +} + +// decodeUintReader reads an encoded unsigned integer from an io.Reader. +// Used only by the Decoder to read the message length. +func decodeUintReader(r io.Reader, buf []byte) (x uint64, width int, err error) { + width = 1 + _, err = r.Read(buf[0:width]) + if err != nil { + return + } + b := buf[0] + if b <= 0x7f { + return uint64(b), width, nil + } + n := -int(int8(b)) + if n > uint64Size { + err = errBadUint + return + } + width, err = io.ReadFull(r, buf[0:n]) + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return + } + // Could check that the high byte is zero but it's not worth it. + for _, b := range buf[0:width] { + x = x<<8 | uint64(b) + } + width++ // +1 for length byte + return +} + +// decodeUint reads an encoded unsigned integer from state.r. +// Does not check for overflow. +func (state *decoderState) decodeUint() (x uint64) { + b, err := state.b.ReadByte() + if err != nil { + error_(err) + } + if b <= 0x7f { + return uint64(b) + } + n := -int(int8(b)) + if n > uint64Size { + error_(errBadUint) + } + width, err := state.b.Read(state.buf[0:n]) + if err != nil { + error_(err) + } + // Don't need to check error; it's safe to loop regardless. + // Could check that the high byte is zero but it's not worth it. + for _, b := range state.buf[0:width] { + x = x<<8 | uint64(b) + } + return x +} + +// decodeInt reads an encoded signed integer from state.r. +// Does not check for overflow. +func (state *decoderState) decodeInt() int64 { + x := state.decodeUint() + if x&1 != 0 { + return ^int64(x >> 1) + } + return int64(x >> 1) +} + +// decOp is the signature of a decoding operator for a given type. +type decOp func(i *decInstr, state *decoderState, p unsafe.Pointer) + +// The 'instructions' of the decoding machine +type decInstr struct { + op decOp + field int // field number of the wire type + indir int // how many pointer indirections to reach the value in the struct + offset uintptr // offset in the structure of the field to encode + ovfl error // error message for overflow/underflow (for arrays, of the elements) +} + +// Since the encoder writes no zeros, if we arrive at a decoder we have +// a value to extract and store. The field number has already been read +// (it's how we knew to call this decoder). +// Each decoder is responsible for handling any indirections associated +// with the data structure. If any pointer so reached is nil, allocation must +// be done. + +// Walk the pointer hierarchy, allocating if we find a nil. Stop one before the end. +func decIndirect(p unsafe.Pointer, indir int) unsafe.Pointer { + for ; indir > 1; indir-- { + if *(*unsafe.Pointer)(p) == nil { + // Allocation required + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(unsafe.Pointer)) + } + p = *(*unsafe.Pointer)(p) + } + return p +} + +// ignoreUint discards a uint value with no destination. +func ignoreUint(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.decodeUint() +} + +// ignoreTwoUints discards a uint value with no destination. It's used to skip +// complex values. +func ignoreTwoUints(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.decodeUint() + state.decodeUint() +} + +// decBool decodes a uint and stores it as a boolean through p. +func decBool(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(bool)) + } + p = *(*unsafe.Pointer)(p) + } + *(*bool)(p) = state.decodeUint() != 0 +} + +// decInt8 decodes an integer and stores it as an int8 through p. +func decInt8(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int8)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeInt() + if v < math.MinInt8 || math.MaxInt8 < v { + error_(i.ovfl) + } else { + *(*int8)(p) = int8(v) + } +} + +// decUint8 decodes an unsigned integer and stores it as a uint8 through p. +func decUint8(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint8)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeUint() + if math.MaxUint8 < v { + error_(i.ovfl) + } else { + *(*uint8)(p) = uint8(v) + } +} + +// decInt16 decodes an integer and stores it as an int16 through p. +func decInt16(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int16)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeInt() + if v < math.MinInt16 || math.MaxInt16 < v { + error_(i.ovfl) + } else { + *(*int16)(p) = int16(v) + } +} + +// decUint16 decodes an unsigned integer and stores it as a uint16 through p. +func decUint16(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint16)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeUint() + if math.MaxUint16 < v { + error_(i.ovfl) + } else { + *(*uint16)(p) = uint16(v) + } +} + +// decInt32 decodes an integer and stores it as an int32 through p. +func decInt32(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int32)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeInt() + if v < math.MinInt32 || math.MaxInt32 < v { + error_(i.ovfl) + } else { + *(*int32)(p) = int32(v) + } +} + +// decUint32 decodes an unsigned integer and stores it as a uint32 through p. +func decUint32(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint32)) + } + p = *(*unsafe.Pointer)(p) + } + v := state.decodeUint() + if math.MaxUint32 < v { + error_(i.ovfl) + } else { + *(*uint32)(p) = uint32(v) + } +} + +// decInt64 decodes an integer and stores it as an int64 through p. +func decInt64(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(int64)) + } + p = *(*unsafe.Pointer)(p) + } + *(*int64)(p) = int64(state.decodeInt()) +} + +// decUint64 decodes an unsigned integer and stores it as a uint64 through p. +func decUint64(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(uint64)) + } + p = *(*unsafe.Pointer)(p) + } + *(*uint64)(p) = uint64(state.decodeUint()) +} + +// Floating-point numbers are transmitted as uint64s holding the bits +// of the underlying representation. They are sent byte-reversed, with +// the exponent end coming out first, so integer floating point numbers +// (for example) transmit more compactly. This routine does the +// unswizzling. +func floatFromBits(u uint64) float64 { + var v uint64 + for i := 0; i < 8; i++ { + v <<= 8 + v |= u & 0xFF + u >>= 8 + } + return math.Float64frombits(v) +} + +// storeFloat32 decodes an unsigned integer, treats it as a 32-bit floating-point +// number, and stores it through p. It's a helper function for float32 and complex64. +func storeFloat32(i *decInstr, state *decoderState, p unsafe.Pointer) { + v := floatFromBits(state.decodeUint()) + av := v + if av < 0 { + av = -av + } + // +Inf is OK in both 32- and 64-bit floats. Underflow is always OK. + if math.MaxFloat32 < av && av <= math.MaxFloat64 { + error_(i.ovfl) + } else { + *(*float32)(p) = float32(v) + } +} + +// decFloat32 decodes an unsigned integer, treats it as a 32-bit floating-point +// number, and stores it through p. +func decFloat32(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(float32)) + } + p = *(*unsafe.Pointer)(p) + } + storeFloat32(i, state, p) +} + +// decFloat64 decodes an unsigned integer, treats it as a 64-bit floating-point +// number, and stores it through p. +func decFloat64(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(float64)) + } + p = *(*unsafe.Pointer)(p) + } + *(*float64)(p) = floatFromBits(uint64(state.decodeUint())) +} + +// decComplex64 decodes a pair of unsigned integers, treats them as a +// pair of floating point numbers, and stores them as a complex64 through p. +// The real part comes first. +func decComplex64(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(complex64)) + } + p = *(*unsafe.Pointer)(p) + } + storeFloat32(i, state, p) + storeFloat32(i, state, unsafe.Pointer(uintptr(p)+unsafe.Sizeof(float32(0)))) +} + +// decComplex128 decodes a pair of unsigned integers, treats them as a +// pair of floating point numbers, and stores them as a complex128 through p. +// The real part comes first. +func decComplex128(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(complex128)) + } + p = *(*unsafe.Pointer)(p) + } + real := floatFromBits(uint64(state.decodeUint())) + imag := floatFromBits(uint64(state.decodeUint())) + *(*complex128)(p) = complex(real, imag) +} + +// decUint8Slice decodes a byte slice and stores through p a slice header +// describing the data. +// uint8 slices are encoded as an unsigned count followed by the raw bytes. +func decUint8Slice(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new([]uint8)) + } + p = *(*unsafe.Pointer)(p) + } + n := state.decodeUint() + if n > uint64(state.b.Len()) { + errorf("length of []byte exceeds input size (%d bytes)", n) + } + slice := (*[]uint8)(p) + if uint64(cap(*slice)) < n { + *slice = make([]uint8, n) + } else { + *slice = (*slice)[0:n] + } + if _, err := state.b.Read(*slice); err != nil { + errorf("error decoding []byte: %s", err) + } +} + +// decString decodes byte array and stores through p a string header +// describing the data. +// Strings are encoded as an unsigned count followed by the raw bytes. +func decString(i *decInstr, state *decoderState, p unsafe.Pointer) { + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(new(string)) + } + p = *(*unsafe.Pointer)(p) + } + n := state.decodeUint() + if n > uint64(state.b.Len()) { + errorf("string length exceeds input size (%d bytes)", n) + } + b := make([]byte, n) + state.b.Read(b) + // It would be a shame to do the obvious thing here, + // *(*string)(p) = string(b) + // because we've already allocated the storage and this would + // allocate again and copy. So we do this ugly hack, which is even + // even more unsafe than it looks as it depends the memory + // representation of a string matching the beginning of the memory + // representation of a byte slice (a byte slice is longer). + *(*string)(p) = *(*string)(unsafe.Pointer(&b)) +} + +// ignoreUint8Array skips over the data for a byte slice value with no destination. +func ignoreUint8Array(i *decInstr, state *decoderState, p unsafe.Pointer) { + b := make([]byte, state.decodeUint()) + state.b.Read(b) +} + +// Execution engine + +// The encoder engine is an array of instructions indexed by field number of the incoming +// decoder. It is executed with random access according to field number. +type decEngine struct { + instr []decInstr + numInstr int // the number of active instructions +} + +// allocate makes sure storage is available for an object of underlying type rtyp +// that is indir levels of indirection through p. +func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr { + if indir == 0 { + return p + } + up := unsafe.Pointer(p) + if indir > 1 { + up = decIndirect(up, indir) + } + if *(*unsafe.Pointer)(up) == nil { + // Allocate object. + *(*unsafe.Pointer)(up) = unsafe.Pointer(reflect.New(rtyp).Pointer()) + } + return *(*uintptr)(up) +} + +// decodeSingle decodes a top-level value that is not a struct and stores it through p. +// Such values are preceded by a zero, making them have the memory layout of a +// struct field (although with an illegal field number). +func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) { + state := dec.newDecoderState(&dec.buf) + state.fieldnum = singletonField + delta := int(state.decodeUint()) + if delta != 0 { + errorf("decode: corrupted data: non-zero delta for singleton") + } + instr := &engine.instr[singletonField] + if instr.indir != ut.indir { + errorf("internal error: inconsistent indirection instr %d ut %d", instr.indir, ut.indir) + } + ptr := unsafe.Pointer(basep) // offset will be zero + if instr.indir > 1 { + ptr = decIndirect(ptr, instr.indir) + } + instr.op(instr, state, ptr) + dec.freeDecoderState(state) +} + +// decodeStruct decodes a top-level struct and stores it through p. +// Indir is for the value, not the type. At the time of the call it may +// differ from ut.indir, which was computed when the engine was built. +// This state cannot arise for decodeSingle, which is called directly +// from the user's value, not from the innards of an engine. +func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr, indir int) { + p = allocate(ut.base, p, indir) + state := dec.newDecoderState(&dec.buf) + state.fieldnum = -1 + basep := p + for state.b.Len() > 0 { + delta := int(state.decodeUint()) + if delta < 0 { + errorf("decode: corrupted data: negative delta") + } + if delta == 0 { // struct terminator is zero delta fieldnum + break + } + fieldnum := state.fieldnum + delta + if fieldnum >= len(engine.instr) { + error_(errRange) + break + } + instr := &engine.instr[fieldnum] + p := unsafe.Pointer(basep + instr.offset) + if instr.indir > 1 { + p = decIndirect(p, instr.indir) + } + instr.op(instr, state, p) + state.fieldnum = fieldnum + } + dec.freeDecoderState(state) +} + +// ignoreStruct discards the data for a struct with no destination. +func (dec *Decoder) ignoreStruct(engine *decEngine) { + state := dec.newDecoderState(&dec.buf) + state.fieldnum = -1 + for state.b.Len() > 0 { + delta := int(state.decodeUint()) + if delta < 0 { + errorf("ignore decode: corrupted data: negative delta") + } + if delta == 0 { // struct terminator is zero delta fieldnum + break + } + fieldnum := state.fieldnum + delta + if fieldnum >= len(engine.instr) { + error_(errRange) + } + instr := &engine.instr[fieldnum] + instr.op(instr, state, unsafe.Pointer(nil)) + state.fieldnum = fieldnum + } + dec.freeDecoderState(state) +} + +// ignoreSingle discards the data for a top-level non-struct value with no +// destination. It's used when calling Decode with a nil value. +func (dec *Decoder) ignoreSingle(engine *decEngine) { + state := dec.newDecoderState(&dec.buf) + state.fieldnum = singletonField + delta := int(state.decodeUint()) + if delta != 0 { + errorf("decode: corrupted data: non-zero delta for singleton") + } + instr := &engine.instr[singletonField] + instr.op(instr, state, unsafe.Pointer(nil)) + dec.freeDecoderState(state) +} + +// decodeArrayHelper does the work for decoding arrays and slices. +func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl error) { + instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl} + for i := 0; i < length; i++ { + up := unsafe.Pointer(p) + if elemIndir > 1 { + up = decIndirect(up, elemIndir) + } + elemOp(instr, state, up) + p += uintptr(elemWid) + } +} + +// decodeArray decodes an array and stores it through p, that is, p points to the zeroth element. +// The length is an unsigned integer preceding the elements. Even though the length is redundant +// (it's part of the type), it's a useful check and is included in the encoding. +func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl error) { + if indir > 0 { + p = allocate(atyp, p, 1) // All but the last level has been allocated by dec.Indirect + } + if n := state.decodeUint(); n != uint64(length) { + errorf("length mismatch in decodeArray") + } + dec.decodeArrayHelper(state, p, elemOp, elemWid, length, elemIndir, ovfl) +} + +// decodeIntoValue is a helper for map decoding. Since maps are decoded using reflection, +// unlike the other items we can't use a pointer directly. +func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, ovfl error) reflect.Value { + instr := &decInstr{op, 0, indir, 0, ovfl} + up := unsafe.Pointer(unsafeAddr(v)) + if indir > 1 { + up = decIndirect(up, indir) + } + op(instr, state, up) + return v +} + +// decodeMap decodes a map and stores its header through p. +// Maps are encoded as a length followed by key:value pairs. +// Because the internals of maps are not visible to us, we must +// use reflection rather than pointer magic. +func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl error) { + if indir > 0 { + p = allocate(mtyp, p, 1) // All but the last level has been allocated by dec.Indirect + } + up := unsafe.Pointer(p) + if *(*unsafe.Pointer)(up) == nil { // maps are represented as a pointer in the runtime + // Allocate map. + *(*unsafe.Pointer)(up) = unsafe.Pointer(reflect.MakeMap(mtyp).Pointer()) + } + // Maps cannot be accessed by moving addresses around the way + // that slices etc. can. We must recover a full reflection value for + // the iteration. + v := reflect.NewAt(mtyp, unsafe.Pointer(p)).Elem() + n := int(state.decodeUint()) + for i := 0; i < n; i++ { + key := decodeIntoValue(state, keyOp, keyIndir, allocValue(mtyp.Key()), ovfl) + elem := decodeIntoValue(state, elemOp, elemIndir, allocValue(mtyp.Elem()), ovfl) + v.SetMapIndex(key, elem) + } +} + +// ignoreArrayHelper does the work for discarding arrays and slices. +func (dec *Decoder) ignoreArrayHelper(state *decoderState, elemOp decOp, length int) { + instr := &decInstr{elemOp, 0, 0, 0, errors.New("no error")} + for i := 0; i < length; i++ { + elemOp(instr, state, nil) + } +} + +// ignoreArray discards the data for an array value with no destination. +func (dec *Decoder) ignoreArray(state *decoderState, elemOp decOp, length int) { + if n := state.decodeUint(); n != uint64(length) { + errorf("length mismatch in ignoreArray") + } + dec.ignoreArrayHelper(state, elemOp, length) +} + +// ignoreMap discards the data for a map value with no destination. +func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) { + n := int(state.decodeUint()) + keyInstr := &decInstr{keyOp, 0, 0, 0, errors.New("no error")} + elemInstr := &decInstr{elemOp, 0, 0, 0, errors.New("no error")} + for i := 0; i < n; i++ { + keyOp(keyInstr, state, nil) + elemOp(elemInstr, state, nil) + } +} + +// decodeSlice decodes a slice and stores the slice header through p. +// Slices are encoded as an unsigned length followed by the elements. +func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl error) { + nr := state.decodeUint() + if nr > uint64(state.b.Len()) { + errorf("length of slice exceeds input size (%d elements)", nr) + } + n := int(nr) + if indir > 0 { + up := unsafe.Pointer(p) + if *(*unsafe.Pointer)(up) == nil { + // Allocate the slice header. + *(*unsafe.Pointer)(up) = unsafe.Pointer(new([]unsafe.Pointer)) + } + p = *(*uintptr)(up) + } + // Allocate storage for the slice elements, that is, the underlying array, + // if the existing slice does not have the capacity. + // Always write a header at p. + hdrp := (*reflect.SliceHeader)(unsafe.Pointer(p)) + if hdrp.Cap < n { + hdrp.Data = reflect.MakeSlice(atyp, n, n).Pointer() + hdrp.Cap = n + } + hdrp.Len = n + dec.decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl) +} + +// ignoreSlice skips over the data for a slice value with no destination. +func (dec *Decoder) ignoreSlice(state *decoderState, elemOp decOp) { + dec.ignoreArrayHelper(state, elemOp, int(state.decodeUint())) +} + +// setInterfaceValue sets an interface value to a concrete value, +// but first it checks that the assignment will succeed. +func setInterfaceValue(ivalue reflect.Value, value reflect.Value) { + if !value.Type().AssignableTo(ivalue.Type()) { + errorf("cannot assign value of type %s to %s", value.Type(), ivalue.Type()) + } + ivalue.Set(value) +} + +// decodeInterface decodes an interface value and stores it through p. +// Interfaces are encoded as the name of a concrete type followed by a value. +// If the name is empty, the value is nil and no value is sent. +func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p uintptr, indir int) { + // Create a writable interface reflect.Value. We need one even for the nil case. + ivalue := allocValue(ityp) + // Read the name of the concrete type. + nr := state.decodeUint() + if nr < 0 || nr > 1<<31 { // zero is permissible for anonymous types + errorf("invalid type name length %d", nr) + } + b := make([]byte, nr) + state.b.Read(b) + name := string(b) + if name == "" { + // Copy the representation of the nil interface value to the target. + // This is horribly unsafe and special. + if indir > 0 { + p = allocate(ityp, p, 1) // All but the last level has been allocated by dec.Indirect + } + *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.InterfaceData() + return + } + if len(name) > 1024 { + errorf("name too long (%d bytes): %.20q...", len(name), name) + } + // The concrete type must be registered. + typ, ok := nameToConcreteType[name] + if !ok { + errorf("name not registered for interface: %q", name) + } + // Read the type id of the concrete value. + concreteId := dec.decodeTypeSequence(true) + if concreteId < 0 { + error_(dec.err) + } + // Byte count of value is next; we don't care what it is (it's there + // in case we want to ignore the value by skipping it completely). + state.decodeUint() + // Read the concrete value. + value := allocValue(typ) + dec.decodeValue(concreteId, value) + if dec.err != nil { + error_(dec.err) + } + // Allocate the destination interface value. + if indir > 0 { + p = allocate(ityp, p, 1) // All but the last level has been allocated by dec.Indirect + } + // Assign the concrete value to the interface. + // Tread carefully; it might not satisfy the interface. + setInterfaceValue(ivalue, value) + // Copy the representation of the interface value to the target. + // This is horribly unsafe and special. + *(*[2]uintptr)(unsafe.Pointer(p)) = ivalue.InterfaceData() +} + +// ignoreInterface discards the data for an interface value with no destination. +func (dec *Decoder) ignoreInterface(state *decoderState) { + // Read the name of the concrete type. + b := make([]byte, state.decodeUint()) + _, err := state.b.Read(b) + if err != nil { + error_(err) + } + id := dec.decodeTypeSequence(true) + if id < 0 { + error_(dec.err) + } + // At this point, the decoder buffer contains a delimited value. Just toss it. + state.b.Next(int(state.decodeUint())) +} + +// decodeGobDecoder decodes something implementing the GobDecoder interface. +// The data is encoded as a byte slice. +func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value) { + // Read the bytes for the value. + b := make([]byte, state.decodeUint()) + _, err := state.b.Read(b) + if err != nil { + error_(err) + } + // We know it's a GobDecoder, so just call the method directly. + err = v.Interface().(GobDecoder).GobDecode(b) + if err != nil { + error_(err) + } +} + +// ignoreGobDecoder discards the data for a GobDecoder value with no destination. +func (dec *Decoder) ignoreGobDecoder(state *decoderState) { + // Read the bytes for the value. + b := make([]byte, state.decodeUint()) + _, err := state.b.Read(b) + if err != nil { + error_(err) + } +} + +// Index by Go types. +var decOpTable = [...]decOp{ + reflect.Bool: decBool, + reflect.Int8: decInt8, + reflect.Int16: decInt16, + reflect.Int32: decInt32, + reflect.Int64: decInt64, + reflect.Uint8: decUint8, + reflect.Uint16: decUint16, + reflect.Uint32: decUint32, + reflect.Uint64: decUint64, + reflect.Float32: decFloat32, + reflect.Float64: decFloat64, + reflect.Complex64: decComplex64, + reflect.Complex128: decComplex128, + reflect.String: decString, +} + +// Indexed by gob types. tComplex will be added during type.init(). +var decIgnoreOpMap = map[typeId]decOp{ + tBool: ignoreUint, + tInt: ignoreUint, + tUint: ignoreUint, + tFloat: ignoreUint, + tBytes: ignoreUint8Array, + tString: ignoreUint8Array, + tComplex: ignoreTwoUints, +} + +// decOpFor returns the decoding op for the base type under rt and +// the indirection count to reach it. +func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProgress map[reflect.Type]*decOp) (*decOp, int) { + ut := userType(rt) + // If the type implements GobEncoder, we handle it without further processing. + if ut.isGobDecoder { + return dec.gobDecodeOpFor(ut) + } + // If this type is already in progress, it's a recursive type (e.g. map[string]*T). + // Return the pointer to the op we're already building. + if opPtr := inProgress[rt]; opPtr != nil { + return opPtr, ut.indir + } + typ := ut.base + indir := ut.indir + var op decOp + k := typ.Kind() + if int(k) < len(decOpTable) { + op = decOpTable[k] + } + if op == nil { + inProgress[rt] = &op + // Special cases + switch t := typ; t.Kind() { + case reflect.Array: + name = "element of " + name + elemId := dec.wireType[wireId].ArrayT.Elem + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress) + ovfl := overflow(name) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.decodeArray(t, state, uintptr(p), *elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl) + } + + case reflect.Map: + keyId := dec.wireType[wireId].MapT.Key + elemId := dec.wireType[wireId].MapT.Elem + keyOp, keyIndir := dec.decOpFor(keyId, t.Key(), "key of "+name, inProgress) + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), "element of "+name, inProgress) + ovfl := overflow(name) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + up := unsafe.Pointer(p) + state.dec.decodeMap(t, state, uintptr(up), *keyOp, *elemOp, i.indir, keyIndir, elemIndir, ovfl) + } + + case reflect.Slice: + name = "element of " + name + if t.Elem().Kind() == reflect.Uint8 { + op = decUint8Slice + break + } + var elemId typeId + if tt, ok := builtinIdToType[wireId]; ok { + elemId = tt.(*sliceType).Elem + } else { + elemId = dec.wireType[wireId].SliceT.Elem + } + elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress) + ovfl := overflow(name) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.decodeSlice(t, state, uintptr(p), *elemOp, t.Elem().Size(), i.indir, elemIndir, ovfl) + } + + case reflect.Struct: + // Generate a closure that calls out to the engine for the nested type. + enginePtr, err := dec.getDecEnginePtr(wireId, userType(typ)) + if err != nil { + error_(err) + } + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + // indirect through enginePtr to delay evaluation for recursive structs. + dec.decodeStruct(*enginePtr, userType(typ), uintptr(p), i.indir) + } + case reflect.Interface: + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.decodeInterface(t, state, uintptr(p), i.indir) + } + } + } + if op == nil { + errorf("decode can't handle type %s", rt) + } + return &op, indir +} + +// decIgnoreOpFor returns the decoding op for a field that has no destination. +func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp { + op, ok := decIgnoreOpMap[wireId] + if !ok { + if wireId == tInterface { + // Special case because it's a method: the ignored item might + // define types and we need to record their state in the decoder. + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreInterface(state) + } + return op + } + // Special cases + wire := dec.wireType[wireId] + switch { + case wire == nil: + errorf("bad data: undefined type %s", wireId.string()) + case wire.ArrayT != nil: + elemId := wire.ArrayT.Elem + elemOp := dec.decIgnoreOpFor(elemId) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreArray(state, elemOp, wire.ArrayT.Len) + } + + case wire.MapT != nil: + keyId := dec.wireType[wireId].MapT.Key + elemId := dec.wireType[wireId].MapT.Elem + keyOp := dec.decIgnoreOpFor(keyId) + elemOp := dec.decIgnoreOpFor(elemId) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreMap(state, keyOp, elemOp) + } + + case wire.SliceT != nil: + elemId := wire.SliceT.Elem + elemOp := dec.decIgnoreOpFor(elemId) + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreSlice(state, elemOp) + } + + case wire.StructT != nil: + // Generate a closure that calls out to the engine for the nested type. + enginePtr, err := dec.getIgnoreEnginePtr(wireId) + if err != nil { + error_(err) + } + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + // indirect through enginePtr to delay evaluation for recursive structs + state.dec.ignoreStruct(*enginePtr) + } + + case wire.GobEncoderT != nil: + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + state.dec.ignoreGobDecoder(state) + } + } + } + if op == nil { + errorf("bad data: ignore can't handle type %s", wireId.string()) + } + return op +} + +// gobDecodeOpFor returns the op for a type that is known to implement +// GobDecoder. +func (dec *Decoder) gobDecodeOpFor(ut *userTypeInfo) (*decOp, int) { + rcvrType := ut.user + if ut.decIndir == -1 { + rcvrType = reflect.PtrTo(rcvrType) + } else if ut.decIndir > 0 { + for i := int8(0); i < ut.decIndir; i++ { + rcvrType = rcvrType.Elem() + } + } + var op decOp + op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { + // Caller has gotten us to within one indirection of our value. + if i.indir > 0 { + if *(*unsafe.Pointer)(p) == nil { + *(*unsafe.Pointer)(p) = unsafe.Pointer(reflect.New(ut.base).Pointer()) + } + } + // Now p is a pointer to the base type. Do we need to climb out to + // get to the receiver type? + var v reflect.Value + if ut.decIndir == -1 { + v = reflect.NewAt(rcvrType, unsafe.Pointer(&p)).Elem() + } else { + v = reflect.NewAt(rcvrType, p).Elem() + } + state.dec.decodeGobDecoder(state, v) + } + return &op, int(ut.indir) + +} + +// compatibleType asks: Are these two gob Types compatible? +// Answers the question for basic types, arrays, maps and slices, plus +// GobEncoder/Decoder pairs. +// Structs are considered ok; fields will be checked later. +func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[reflect.Type]typeId) bool { + if rhs, ok := inProgress[fr]; ok { + return rhs == fw + } + inProgress[fr] = fw + ut := userType(fr) + wire, ok := dec.wireType[fw] + // If fr is a GobDecoder, the wire type must be GobEncoder. + // And if fr is not a GobDecoder, the wire type must not be either. + if ut.isGobDecoder != (ok && wire.GobEncoderT != nil) { // the parentheses look odd but are correct. + return false + } + if ut.isGobDecoder { // This test trumps all others. + return true + } + switch t := ut.base; t.Kind() { + default: + // chan, etc: cannot handle. + return false + case reflect.Bool: + return fw == tBool + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return fw == tInt + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return fw == tUint + case reflect.Float32, reflect.Float64: + return fw == tFloat + case reflect.Complex64, reflect.Complex128: + return fw == tComplex + case reflect.String: + return fw == tString + case reflect.Interface: + return fw == tInterface + case reflect.Array: + if !ok || wire.ArrayT == nil { + return false + } + array := wire.ArrayT + return t.Len() == array.Len && dec.compatibleType(t.Elem(), array.Elem, inProgress) + case reflect.Map: + if !ok || wire.MapT == nil { + return false + } + MapType := wire.MapT + return dec.compatibleType(t.Key(), MapType.Key, inProgress) && dec.compatibleType(t.Elem(), MapType.Elem, inProgress) + case reflect.Slice: + // Is it an array of bytes? + if t.Elem().Kind() == reflect.Uint8 { + return fw == tBytes + } + // Extract and compare element types. + var sw *sliceType + if tt, ok := builtinIdToType[fw]; ok { + sw, _ = tt.(*sliceType) + } else if wire != nil { + sw = wire.SliceT + } + elem := userType(t.Elem()).base + return sw != nil && dec.compatibleType(elem, sw.Elem, inProgress) + case reflect.Struct: + return true + } + return true +} + +// typeString returns a human-readable description of the type identified by remoteId. +func (dec *Decoder) typeString(remoteId typeId) string { + if t := idToType[remoteId]; t != nil { + // globally known type. + return t.string() + } + return dec.wireType[remoteId].string() +} + +// compileSingle compiles the decoder engine for a non-struct top-level value, including +// GobDecoders. +func (dec *Decoder) compileSingle(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err error) { + rt := ut.user + engine = new(decEngine) + engine.instr = make([]decInstr, 1) // one item + name := rt.String() // best we can do + if !dec.compatibleType(rt, remoteId, make(map[reflect.Type]typeId)) { + remoteType := dec.typeString(remoteId) + // Common confusing case: local interface type, remote concrete type. + if ut.base.Kind() == reflect.Interface && remoteId != tInterface { + return nil, errors.New("gob: local interface type " + name + " can only be decoded from remote interface type; received concrete type " + remoteType) + } + return nil, errors.New("gob: decoding into local type " + name + ", received remote type " + remoteType) + } + op, indir := dec.decOpFor(remoteId, rt, name, make(map[reflect.Type]*decOp)) + ovfl := errors.New(`value for "` + name + `" out of range`) + engine.instr[singletonField] = decInstr{*op, singletonField, indir, 0, ovfl} + engine.numInstr = 1 + return +} + +// compileIgnoreSingle compiles the decoder engine for a non-struct top-level value that will be discarded. +func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err error) { + engine = new(decEngine) + engine.instr = make([]decInstr, 1) // one item + op := dec.decIgnoreOpFor(remoteId) + ovfl := overflow(dec.typeString(remoteId)) + engine.instr[0] = decInstr{op, 0, 0, 0, ovfl} + engine.numInstr = 1 + return +} + +// compileDec compiles the decoder engine for a value. If the value is not a struct, +// it calls out to compileSingle. +func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err error) { + rt := ut.base + srt := rt + if srt.Kind() != reflect.Struct || + ut.isGobDecoder { + return dec.compileSingle(remoteId, ut) + } + var wireStruct *structType + // Builtin types can come from global pool; the rest must be defined by the decoder. + // Also we know we're decoding a struct now, so the client must have sent one. + if t, ok := builtinIdToType[remoteId]; ok { + wireStruct, _ = t.(*structType) + } else { + wire := dec.wireType[remoteId] + if wire == nil { + error_(errBadType) + } + wireStruct = wire.StructT + } + if wireStruct == nil { + errorf("type mismatch in decoder: want struct type %s; got non-struct", rt) + } + engine = new(decEngine) + engine.instr = make([]decInstr, len(wireStruct.Field)) + seen := make(map[reflect.Type]*decOp) + // Loop over the fields of the wire type. + for fieldnum := 0; fieldnum < len(wireStruct.Field); fieldnum++ { + wireField := wireStruct.Field[fieldnum] + if wireField.Name == "" { + errorf("empty name for remote field of type %s", wireStruct.Name) + } + ovfl := overflow(wireField.Name) + // Find the field of the local type with the same name. + localField, present := srt.FieldByName(wireField.Name) + // TODO(r): anonymous names + if !present || !isExported(wireField.Name) { + op := dec.decIgnoreOpFor(wireField.Id) + engine.instr[fieldnum] = decInstr{op, fieldnum, 0, 0, ovfl} + continue + } + if !dec.compatibleType(localField.Type, wireField.Id, make(map[reflect.Type]typeId)) { + errorf("wrong type (%s) for received field %s.%s", localField.Type, wireStruct.Name, wireField.Name) + } + op, indir := dec.decOpFor(wireField.Id, localField.Type, localField.Name, seen) + engine.instr[fieldnum] = decInstr{*op, fieldnum, indir, uintptr(localField.Offset), ovfl} + engine.numInstr++ + } + return +} + +// getDecEnginePtr returns the engine for the specified type. +func (dec *Decoder) getDecEnginePtr(remoteId typeId, ut *userTypeInfo) (enginePtr **decEngine, err error) { + rt := ut.user + decoderMap, ok := dec.decoderCache[rt] + if !ok { + decoderMap = make(map[typeId]**decEngine) + dec.decoderCache[rt] = decoderMap + } + if enginePtr, ok = decoderMap[remoteId]; !ok { + // To handle recursive types, mark this engine as underway before compiling. + enginePtr = new(*decEngine) + decoderMap[remoteId] = enginePtr + *enginePtr, err = dec.compileDec(remoteId, ut) + if err != nil { + delete(decoderMap, remoteId) + } + } + return +} + +// emptyStruct is the type we compile into when ignoring a struct value. +type emptyStruct struct{} + +var emptyStructType = reflect.TypeOf(emptyStruct{}) + +// getDecEnginePtr returns the engine for the specified type when the value is to be discarded. +func (dec *Decoder) getIgnoreEnginePtr(wireId typeId) (enginePtr **decEngine, err error) { + var ok bool + if enginePtr, ok = dec.ignorerCache[wireId]; !ok { + // To handle recursive types, mark this engine as underway before compiling. + enginePtr = new(*decEngine) + dec.ignorerCache[wireId] = enginePtr + wire := dec.wireType[wireId] + if wire != nil && wire.StructT != nil { + *enginePtr, err = dec.compileDec(wireId, userType(emptyStructType)) + } else { + *enginePtr, err = dec.compileIgnoreSingle(wireId) + } + if err != nil { + delete(dec.ignorerCache, wireId) + } + } + return +} + +// decodeValue decodes the data stream representing a value and stores it in val. +func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) { + defer catchError(&dec.err) + // If the value is nil, it means we should just ignore this item. + if !val.IsValid() { + dec.decodeIgnoredValue(wireId) + return + } + // Dereference down to the underlying type. + ut := userType(val.Type()) + base := ut.base + var enginePtr **decEngine + enginePtr, dec.err = dec.getDecEnginePtr(wireId, ut) + if dec.err != nil { + return + } + engine := *enginePtr + if st := base; st.Kind() == reflect.Struct && !ut.isGobDecoder { + if engine.numInstr == 0 && st.NumField() > 0 && len(dec.wireType[wireId].StructT.Field) > 0 { + name := base.Name() + errorf("type mismatch: no fields matched compiling decoder for %s", name) + } + dec.decodeStruct(engine, ut, uintptr(unsafeAddr(val)), ut.indir) + } else { + dec.decodeSingle(engine, ut, uintptr(unsafeAddr(val))) + } +} + +// decodeIgnoredValue decodes the data stream representing a value of the specified type and discards it. +func (dec *Decoder) decodeIgnoredValue(wireId typeId) { + var enginePtr **decEngine + enginePtr, dec.err = dec.getIgnoreEnginePtr(wireId) + if dec.err != nil { + return + } + wire := dec.wireType[wireId] + if wire != nil && wire.StructT != nil { + dec.ignoreStruct(*enginePtr) + } else { + dec.ignoreSingle(*enginePtr) + } +} + +func init() { + var iop, uop decOp + switch reflect.TypeOf(int(0)).Bits() { + case 32: + iop = decInt32 + uop = decUint32 + case 64: + iop = decInt64 + uop = decUint64 + default: + panic("gob: unknown size of int/uint") + } + decOpTable[reflect.Int] = iop + decOpTable[reflect.Uint] = uop + + // Finally uintptr + switch reflect.TypeOf(uintptr(0)).Bits() { + case 32: + uop = decUint32 + case 64: + uop = decUint64 + default: + panic("gob: unknown size of uintptr") + } + decOpTable[reflect.Uintptr] = uop +} + +// Gob assumes it can call UnsafeAddr on any Value +// in order to get a pointer it can copy data from. +// Values that have just been created and do not point +// into existing structs or slices cannot be addressed, +// so simulate it by returning a pointer to a copy. +// Each call allocates once. +func unsafeAddr(v reflect.Value) uintptr { + if v.CanAddr() { + return v.UnsafeAddr() + } + x := reflect.New(v.Type()).Elem() + x.Set(v) + return x.UnsafeAddr() +} + +// Gob depends on being able to take the address +// of zeroed Values it creates, so use this wrapper instead +// of the standard reflect.Zero. +// Each call allocates once. +func allocValue(t reflect.Type) reflect.Value { + return reflect.New(t).Elem() +} diff --git a/src/pkg/encoding/gob/decoder.go b/src/pkg/encoding/gob/decoder.go new file mode 100644 index 000000000..c5c7d3fdb --- /dev/null +++ b/src/pkg/encoding/gob/decoder.go @@ -0,0 +1,216 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "bufio" + "bytes" + "errors" + "io" + "reflect" + "sync" +) + +// A Decoder manages the receipt of type and data information read from the +// remote side of a connection. +type Decoder struct { + mutex sync.Mutex // each item must be received atomically + r io.Reader // source of the data + buf bytes.Buffer // buffer for more efficient i/o from r + wireType map[typeId]*wireType // map from remote ID to local description + decoderCache map[reflect.Type]map[typeId]**decEngine // cache of compiled engines + ignorerCache map[typeId]**decEngine // ditto for ignored objects + freeList *decoderState // list of free decoderStates; avoids reallocation + countBuf []byte // used for decoding integers while parsing messages + tmp []byte // temporary storage for i/o; saves reallocating + err error +} + +// NewDecoder returns a new decoder that reads from the io.Reader. +// If r does not also implement io.ByteReader, it will be wrapped in a +// bufio.Reader. +func NewDecoder(r io.Reader) *Decoder { + dec := new(Decoder) + // We use the ability to read bytes as a plausible surrogate for buffering. + if _, ok := r.(io.ByteReader); !ok { + r = bufio.NewReader(r) + } + dec.r = r + dec.wireType = make(map[typeId]*wireType) + dec.decoderCache = make(map[reflect.Type]map[typeId]**decEngine) + dec.ignorerCache = make(map[typeId]**decEngine) + dec.countBuf = make([]byte, 9) // counts may be uint64s (unlikely!), require 9 bytes + + return dec +} + +// recvType loads the definition of a type. +func (dec *Decoder) recvType(id typeId) { + // Have we already seen this type? That's an error + if id < firstUserId || dec.wireType[id] != nil { + dec.err = errors.New("gob: duplicate type received") + return + } + + // Type: + wire := new(wireType) + dec.decodeValue(tWireType, reflect.ValueOf(wire)) + if dec.err != nil { + return + } + // Remember we've seen this type. + dec.wireType[id] = wire +} + +var errBadCount = errors.New("invalid message length") + +// recvMessage reads the next count-delimited item from the input. It is the converse +// of Encoder.writeMessage. It returns false on EOF or other error reading the message. +func (dec *Decoder) recvMessage() bool { + // Read a count. + nbytes, _, err := decodeUintReader(dec.r, dec.countBuf) + if err != nil { + dec.err = err + return false + } + // Upper limit of 1GB, allowing room to grow a little without overflow. + // TODO: We might want more control over this limit. + if nbytes >= 1<<30 { + dec.err = errBadCount + return false + } + dec.readMessage(int(nbytes)) + return dec.err == nil +} + +// readMessage reads the next nbytes bytes from the input. +func (dec *Decoder) readMessage(nbytes int) { + // Allocate the buffer. + if cap(dec.tmp) < nbytes { + dec.tmp = make([]byte, nbytes+100) // room to grow + } + dec.tmp = dec.tmp[:nbytes] + + // Read the data + _, dec.err = io.ReadFull(dec.r, dec.tmp) + if dec.err != nil { + if dec.err == io.EOF { + dec.err = io.ErrUnexpectedEOF + } + return + } + dec.buf.Write(dec.tmp) +} + +// toInt turns an encoded uint64 into an int, according to the marshaling rules. +func toInt(x uint64) int64 { + i := int64(x >> 1) + if x&1 != 0 { + i = ^i + } + return i +} + +func (dec *Decoder) nextInt() int64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return toInt(n) +} + +func (dec *Decoder) nextUint() uint64 { + n, _, err := decodeUintReader(&dec.buf, dec.countBuf) + if err != nil { + dec.err = err + } + return n +} + +// decodeTypeSequence parses: +// TypeSequence +// (TypeDefinition DelimitedTypeDefinition*)? +// and returns the type id of the next value. It returns -1 at +// EOF. Upon return, the remainder of dec.buf is the value to be +// decoded. If this is an interface value, it can be ignored by +// resetting that buffer. +func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { + for dec.err == nil { + if dec.buf.Len() == 0 { + if !dec.recvMessage() { + break + } + } + // Receive a type id. + id := typeId(dec.nextInt()) + if id >= 0 { + // Value follows. + return id + } + // Type definition for (-id) follows. + dec.recvType(-id) + // When decoding an interface, after a type there may be a + // DelimitedValue still in the buffer. Skip its count. + // (Alternatively, the buffer is empty and the byte count + // will be absorbed by recvMessage.) + if dec.buf.Len() > 0 { + if !isInterface { + dec.err = errors.New("extra data in buffer") + break + } + dec.nextUint() + } + } + return -1 +} + +// Decode reads the next value from the connection and stores +// it in the data represented by the empty interface value. +// If e is nil, the value will be discarded. Otherwise, +// the value underlying e must be a pointer to the +// correct type for the next data item received. +func (dec *Decoder) Decode(e interface{}) error { + if e == nil { + return dec.DecodeValue(reflect.Value{}) + } + value := reflect.ValueOf(e) + // If e represents a value as opposed to a pointer, the answer won't + // get back to the caller. Make sure it's a pointer. + if value.Type().Kind() != reflect.Ptr { + dec.err = errors.New("gob: attempt to decode into a non-pointer") + return dec.err + } + return dec.DecodeValue(value) +} + +// DecodeValue reads the next value from the connection. +// If v is the zero reflect.Value (v.Kind() == Invalid), DecodeValue discards the value. +// Otherwise, it stores the value into v. In that case, v must represent +// a non-nil pointer to data or be an assignable reflect.Value (v.CanSet()) +func (dec *Decoder) DecodeValue(v reflect.Value) error { + if v.IsValid() { + if v.Kind() == reflect.Ptr && !v.IsNil() { + // That's okay, we'll store through the pointer. + } else if !v.CanSet() { + return errors.New("gob: DecodeValue of unassignable value") + } + } + // Make sure we're single-threaded through here. + dec.mutex.Lock() + defer dec.mutex.Unlock() + + dec.buf.Reset() // In case data lingers from previous invocation. + dec.err = nil + id := dec.decodeTypeSequence(false) + if dec.err == nil { + dec.decodeValue(id, v) + } + return dec.err +} + +// If debug.go is compiled into the program , debugFunc prints a human-readable +// representation of the gob data read from r by calling that file's Debug function. +// Otherwise it is nil. +var debugFunc func(io.Reader) diff --git a/src/pkg/encoding/gob/doc.go b/src/pkg/encoding/gob/doc.go new file mode 100644 index 000000000..96885f8de --- /dev/null +++ b/src/pkg/encoding/gob/doc.go @@ -0,0 +1,366 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package gob manages streams of gobs - binary values exchanged between an +Encoder (transmitter) and a Decoder (receiver). A typical use is transporting +arguments and results of remote procedure calls (RPCs) such as those provided by +package "rpc". + +A stream of gobs is self-describing. Each data item in the stream is preceded by +a specification of its type, expressed in terms of a small set of predefined +types. Pointers are not transmitted, but the things they point to are +transmitted; that is, the values are flattened. Recursive types work fine, but +recursive values (data with cycles) are problematic. This may change. + +To use gobs, create an Encoder and present it with a series of data items as +values or addresses that can be dereferenced to values. The Encoder makes sure +all type information is sent before it is needed. At the receive side, a +Decoder retrieves values from the encoded stream and unpacks them into local +variables. + +The source and destination values/types need not correspond exactly. For structs, +fields (identified by name) that are in the source but absent from the receiving +variable will be ignored. Fields that are in the receiving variable but missing +from the transmitted type or value will be ignored in the destination. If a field +with the same name is present in both, their types must be compatible. Both the +receiver and transmitter will do all necessary indirection and dereferencing to +convert between gobs and actual Go values. For instance, a gob type that is +schematically, + + struct { A, B int } + +can be sent from or received into any of these Go types: + + struct { A, B int } // the same + *struct { A, B int } // extra indirection of the struct + struct { *A, **B int } // extra indirection of the fields + struct { A, B int64 } // different concrete value type; see below + +It may also be received into any of these: + + struct { A, B int } // the same + struct { B, A int } // ordering doesn't matter; matching is by name + struct { A, B, C int } // extra field (C) ignored + struct { B int } // missing field (A) ignored; data will be dropped + struct { B, C int } // missing field (A) ignored; extra field (C) ignored. + +Attempting to receive into these types will draw a decode error: + + struct { A int; B uint } // change of signedness for B + struct { A int; B float } // change of type for B + struct { } // no field names in common + struct { C, D int } // no field names in common + +Integers are transmitted two ways: arbitrary precision signed integers or +arbitrary precision unsigned integers. There is no int8, int16 etc. +discrimination in the gob format; there are only signed and unsigned integers. As +described below, the transmitter sends the value in a variable-length encoding; +the receiver accepts the value and stores it in the destination variable. +Floating-point numbers are always sent using IEEE-754 64-bit precision (see +below). + +Signed integers may be received into any signed integer variable: int, int16, etc.; +unsigned integers may be received into any unsigned integer variable; and floating +point values may be received into any floating point variable. However, +the destination variable must be able to represent the value or the decode +operation will fail. + +Structs, arrays and slices are also supported. Strings and arrays of bytes are +supported with a special, efficient representation (see below). When a slice is +decoded, if the existing slice has capacity the slice will be extended in place; +if not, a new array is allocated. Regardless, the length of the resulting slice +reports the number of elements decoded. + +Functions and channels cannot be sent in a gob. Attempting +to encode a value that contains one will fail. + +The rest of this comment documents the encoding, details that are not important +for most users. Details are presented bottom-up. + +An unsigned integer is sent one of two ways. If it is less than 128, it is sent +as a byte with that value. Otherwise it is sent as a minimal-length big-endian +(high byte first) byte stream holding the value, preceded by one byte holding the +byte count, negated. Thus 0 is transmitted as (00), 7 is transmitted as (07) and +256 is transmitted as (FE 01 00). + +A boolean is encoded within an unsigned integer: 0 for false, 1 for true. + +A signed integer, i, is encoded within an unsigned integer, u. Within u, bits 1 +upward contain the value; bit 0 says whether they should be complemented upon +receipt. The encode algorithm looks like this: + + uint u; + if i < 0 { + u = (^i << 1) | 1 // complement i, bit 0 is 1 + } else { + u = (i << 1) // do not complement i, bit 0 is 0 + } + encodeUnsigned(u) + +The low bit is therefore analogous to a sign bit, but making it the complement bit +instead guarantees that the largest negative integer is not a special case. For +example, -129=^128=(^256>>1) encodes as (FE 01 01). + +Floating-point numbers are always sent as a representation of a float64 value. +That value is converted to a uint64 using math.Float64bits. The uint64 is then +byte-reversed and sent as a regular unsigned integer. The byte-reversal means the +exponent and high-precision part of the mantissa go first. Since the low bits are +often zero, this can save encoding bytes. For instance, 17.0 is encoded in only +three bytes (FE 31 40). + +Strings and slices of bytes are sent as an unsigned count followed by that many +uninterpreted bytes of the value. + +All other slices and arrays are sent as an unsigned count followed by that many +elements using the standard gob encoding for their type, recursively. + +Maps are sent as an unsigned count followed by that man key, element +pairs. Empty but non-nil maps are sent, so if the sender has allocated +a map, the receiver will allocate a map even no elements are +transmitted. + +Structs are sent as a sequence of (field number, field value) pairs. The field +value is sent using the standard gob encoding for its type, recursively. If a +field has the zero value for its type, it is omitted from the transmission. The +field number is defined by the type of the encoded struct: the first field of the +encoded type is field 0, the second is field 1, etc. When encoding a value, the +field numbers are delta encoded for efficiency and the fields are always sent in +order of increasing field number; the deltas are therefore unsigned. The +initialization for the delta encoding sets the field number to -1, so an unsigned +integer field 0 with value 7 is transmitted as unsigned delta = 1, unsigned value += 7 or (01 07). Finally, after all the fields have been sent a terminating mark +denotes the end of the struct. That mark is a delta=0 value, which has +representation (00). + +Interface types are not checked for compatibility; all interface types are +treated, for transmission, as members of a single "interface" type, analogous to +int or []byte - in effect they're all treated as interface{}. Interface values +are transmitted as a string identifying the concrete type being sent (a name +that must be pre-defined by calling Register), followed by a byte count of the +length of the following data (so the value can be skipped if it cannot be +stored), followed by the usual encoding of concrete (dynamic) value stored in +the interface value. (A nil interface value is identified by the empty string +and transmits no value.) Upon receipt, the decoder verifies that the unpacked +concrete item satisfies the interface of the receiving variable. + +The representation of types is described below. When a type is defined on a given +connection between an Encoder and Decoder, it is assigned a signed integer type +id. When Encoder.Encode(v) is called, it makes sure there is an id assigned for +the type of v and all its elements and then it sends the pair (typeid, encoded-v) +where typeid is the type id of the encoded type of v and encoded-v is the gob +encoding of the value v. + +To define a type, the encoder chooses an unused, positive type id and sends the +pair (-type id, encoded-type) where encoded-type is the gob encoding of a wireType +description, constructed from these types: + + type wireType struct { + ArrayT *ArrayType + SliceT *SliceType + StructT *StructType + MapT *MapType + } + type arrayType struct { + CommonType + Elem typeId + Len int + } + type CommonType struct { + Name string // the name of the struct type + Id int // the id of the type, repeated so it's inside the type + } + type sliceType struct { + CommonType + Elem typeId + } + type structType struct { + CommonType + Field []*fieldType // the fields of the struct. + } + type fieldType struct { + Name string // the name of the field. + Id int // the type id of the field, which must be already defined + } + type mapType struct { + CommonType + Key typeId + Elem typeId + } + +If there are nested type ids, the types for all inner type ids must be defined +before the top-level type id is used to describe an encoded-v. + +For simplicity in setup, the connection is defined to understand these types a +priori, as well as the basic gob types int, uint, etc. Their ids are: + + bool 1 + int 2 + uint 3 + float 4 + []byte 5 + string 6 + complex 7 + interface 8 + // gap for reserved ids. + WireType 16 + ArrayType 17 + CommonType 18 + SliceType 19 + StructType 20 + FieldType 21 + // 22 is slice of fieldType. + MapType 23 + +Finally, each message created by a call to Encode is preceded by an encoded +unsigned integer count of the number of bytes remaining in the message. After +the initial type name, interface values are wrapped the same way; in effect, the +interface value acts like a recursive invocation of Encode. + +In summary, a gob stream looks like + + (byteCount (-type id, encoding of a wireType)* (type id, encoding of a value))* + +where * signifies zero or more repetitions and the type id of a value must +be predefined or be defined before the value in the stream. + +See "Gobs of data" for a design discussion of the gob wire format: +http://golang.org/doc/articles/gobs_of_data.html +*/ +package gob + +/* +Grammar: + +Tokens starting with a lower case letter are terminals; int(n) +and uint(n) represent the signed/unsigned encodings of the value n. + +GobStream: + DelimitedMessage* +DelimitedMessage: + uint(lengthOfMessage) Message +Message: + TypeSequence TypedValue +TypeSequence + (TypeDefinition DelimitedTypeDefinition*)? +DelimitedTypeDefinition: + uint(lengthOfTypeDefinition) TypeDefinition +TypedValue: + int(typeId) Value +TypeDefinition: + int(-typeId) encodingOfWireType +Value: + SingletonValue | StructValue +SingletonValue: + uint(0) FieldValue +FieldValue: + builtinValue | ArrayValue | MapValue | SliceValue | StructValue | InterfaceValue +InterfaceValue: + NilInterfaceValue | NonNilInterfaceValue +NilInterfaceValue: + uint(0) +NonNilInterfaceValue: + ConcreteTypeName TypeSequence InterfaceContents +ConcreteTypeName: + uint(lengthOfName) [already read=n] name +InterfaceContents: + int(concreteTypeId) DelimitedValue +DelimitedValue: + uint(length) Value +ArrayValue: + uint(n) FieldValue*n [n elements] +MapValue: + uint(n) (FieldValue FieldValue)*n [n (key, value) pairs] +SliceValue: + uint(n) FieldValue*n [n elements] +StructValue: + (uint(fieldDelta) FieldValue)* +*/ + +/* +For implementers and the curious, here is an encoded example. Given + type Point struct {X, Y int} +and the value + p := Point{22, 33} +the bytes transmitted that encode p will be: + 1f ff 81 03 01 01 05 50 6f 69 6e 74 01 ff 82 00 + 01 02 01 01 58 01 04 00 01 01 59 01 04 00 00 00 + 07 ff 82 01 2c 01 42 00 +They are determined as follows. + +Since this is the first transmission of type Point, the type descriptor +for Point itself must be sent before the value. This is the first type +we've sent on this Encoder, so it has type id 65 (0 through 64 are +reserved). + + 1f // This item (a type descriptor) is 31 bytes long. + ff 81 // The negative of the id for the type we're defining, -65. + // This is one byte (indicated by FF = -1) followed by + // ^-65<<1 | 1. The low 1 bit signals to complement the + // rest upon receipt. + + // Now we send a type descriptor, which is itself a struct (wireType). + // The type of wireType itself is known (it's built in, as is the type of + // all its components), so we just need to send a *value* of type wireType + // that represents type "Point". + // Here starts the encoding of that value. + // Set the field number implicitly to -1; this is done at the beginning + // of every struct, including nested structs. + 03 // Add 3 to field number; now 2 (wireType.structType; this is a struct). + // structType starts with an embedded CommonType, which appears + // as a regular structure here too. + 01 // add 1 to field number (now 0); start of embedded CommonType. + 01 // add 1 to field number (now 0, the name of the type) + 05 // string is (unsigned) 5 bytes long + 50 6f 69 6e 74 // wireType.structType.CommonType.name = "Point" + 01 // add 1 to field number (now 1, the id of the type) + ff 82 // wireType.structType.CommonType._id = 65 + 00 // end of embedded wiretype.structType.CommonType struct + 01 // add 1 to field number (now 1, the field array in wireType.structType) + 02 // There are two fields in the type (len(structType.field)) + 01 // Start of first field structure; add 1 to get field number 0: field[0].name + 01 // 1 byte + 58 // structType.field[0].name = "X" + 01 // Add 1 to get field number 1: field[0].id + 04 // structType.field[0].typeId is 2 (signed int). + 00 // End of structType.field[0]; start structType.field[1]; set field number to -1. + 01 // Add 1 to get field number 0: field[1].name + 01 // 1 byte + 59 // structType.field[1].name = "Y" + 01 // Add 1 to get field number 1: field[0].id + 04 // struct.Type.field[1].typeId is 2 (signed int). + 00 // End of structType.field[1]; end of structType.field. + 00 // end of wireType.structType structure + 00 // end of wireType structure + +Now we can send the Point value. Again the field number resets to -1: + + 07 // this value is 7 bytes long + ff 82 // the type number, 65 (1 byte (-FF) followed by 65<<1) + 01 // add one to field number, yielding field 0 + 2c // encoding of signed "22" (0x22 = 44 = 22<<1); Point.x = 22 + 01 // add one to field number, yielding field 1 + 42 // encoding of signed "33" (0x42 = 66 = 33<<1); Point.y = 33 + 00 // end of structure + +The type encoding is long and fairly intricate but we send it only once. +If p is transmitted a second time, the type is already known so the +output will be just: + + 07 ff 82 01 2c 01 42 00 + +A single non-struct value at top level is transmitted like a field with +delta tag 0. For instance, a signed integer with value 3 presented as +the argument to Encode will emit: + + 03 04 00 06 + +Which represents: + + 03 // this value is 3 bytes long + 04 // the type number, 2, represents an integer + 00 // tag delta 0 + 06 // value 3 + +*/ diff --git a/src/pkg/encoding/gob/dump.go b/src/pkg/encoding/gob/dump.go new file mode 100644 index 000000000..17238c98d --- /dev/null +++ b/src/pkg/encoding/gob/dump.go @@ -0,0 +1,29 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +// Need to compile package gob with debug.go to build this program. +// See comments in debug.go for how to do this. + +import ( + "encoding/gob" + "fmt" + "os" +) + +func main() { + var err error + file := os.Stdin + if len(os.Args) > 1 { + file, err = os.Open(os.Args[1]) + if err != nil { + fmt.Fprintf(os.Stderr, "dump: %s\n", err) + os.Exit(1) + } + } + gob.Debug(file) +} diff --git a/src/pkg/encoding/gob/encode.go b/src/pkg/encoding/gob/encode.go new file mode 100644 index 000000000..168e08b13 --- /dev/null +++ b/src/pkg/encoding/gob/encode.go @@ -0,0 +1,731 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "bytes" + "math" + "reflect" + "unsafe" +) + +const uint64Size = int(unsafe.Sizeof(uint64(0))) + +// encoderState is the global execution state of an instance of the encoder. +// Field numbers are delta encoded and always increase. The field +// number is initialized to -1 so 0 comes out as delta(1). A delta of +// 0 terminates the structure. +type encoderState struct { + enc *Encoder + b *bytes.Buffer + sendZero bool // encoding an array element or map key/value pair; send zero values + fieldnum int // the last field number written. + buf [1 + uint64Size]byte // buffer used by the encoder; here to avoid allocation. + next *encoderState // for free list +} + +func (enc *Encoder) newEncoderState(b *bytes.Buffer) *encoderState { + e := enc.freeList + if e == nil { + e = new(encoderState) + e.enc = enc + } else { + enc.freeList = e.next + } + e.sendZero = false + e.fieldnum = 0 + e.b = b + return e +} + +func (enc *Encoder) freeEncoderState(e *encoderState) { + e.next = enc.freeList + enc.freeList = e +} + +// Unsigned integers have a two-state encoding. If the number is less +// than 128 (0 through 0x7F), its value is written directly. +// Otherwise the value is written in big-endian byte order preceded +// by the byte length, negated. + +// encodeUint writes an encoded unsigned integer to state.b. +func (state *encoderState) encodeUint(x uint64) { + if x <= 0x7F { + err := state.b.WriteByte(uint8(x)) + if err != nil { + error_(err) + } + return + } + i := uint64Size + for x > 0 { + state.buf[i] = uint8(x) + x >>= 8 + i-- + } + state.buf[i] = uint8(i - uint64Size) // = loop count, negated + _, err := state.b.Write(state.buf[i : uint64Size+1]) + if err != nil { + error_(err) + } +} + +// encodeInt writes an encoded signed integer to state.w. +// The low bit of the encoding says whether to bit complement the (other bits of the) +// uint to recover the int. +func (state *encoderState) encodeInt(i int64) { + var x uint64 + if i < 0 { + x = uint64(^i<<1) | 1 + } else { + x = uint64(i << 1) + } + state.encodeUint(uint64(x)) +} + +// encOp is the signature of an encoding operator for a given type. +type encOp func(i *encInstr, state *encoderState, p unsafe.Pointer) + +// The 'instructions' of the encoding machine +type encInstr struct { + op encOp + field int // field number + indir int // how many pointer indirections to reach the value in the struct + offset uintptr // offset in the structure of the field to encode +} + +// update emits a field number and updates the state to record its value for delta encoding. +// If the instruction pointer is nil, it does nothing +func (state *encoderState) update(instr *encInstr) { + if instr != nil { + state.encodeUint(uint64(instr.field - state.fieldnum)) + state.fieldnum = instr.field + } +} + +// Each encoder for a composite is responsible for handling any +// indirections associated with the elements of the data structure. +// If any pointer so reached is nil, no bytes are written. If the +// data item is zero, no bytes are written. Single values - ints, +// strings etc. - are indirected before calling their encoders. +// Otherwise, the output (for a scalar) is the field number, as an +// encoded integer, followed by the field data in its appropriate +// format. + +// encIndirect dereferences p indir times and returns the result. +func encIndirect(p unsafe.Pointer, indir int) unsafe.Pointer { + for ; indir > 0; indir-- { + p = *(*unsafe.Pointer)(p) + if p == nil { + return unsafe.Pointer(nil) + } + } + return p +} + +// encBool encodes the bool with address p as an unsigned 0 or 1. +func encBool(i *encInstr, state *encoderState, p unsafe.Pointer) { + b := *(*bool)(p) + if b || state.sendZero { + state.update(i) + if b { + state.encodeUint(1) + } else { + state.encodeUint(0) + } + } +} + +// encInt encodes the int with address p. +func encInt(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := int64(*(*int)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encUint encodes the uint with address p. +func encUint(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uint)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encInt8 encodes the int8 with address p. +func encInt8(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := int64(*(*int8)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encUint8 encodes the uint8 with address p. +func encUint8(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uint8)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encInt16 encodes the int16 with address p. +func encInt16(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := int64(*(*int16)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encUint16 encodes the uint16 with address p. +func encUint16(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uint16)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encInt32 encodes the int32 with address p. +func encInt32(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := int64(*(*int32)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encUint encodes the uint32 with address p. +func encUint32(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uint32)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encInt64 encodes the int64 with address p. +func encInt64(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := *(*int64)(p) + if v != 0 || state.sendZero { + state.update(i) + state.encodeInt(v) + } +} + +// encInt64 encodes the uint64 with address p. +func encUint64(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := *(*uint64)(p) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// encUintptr encodes the uintptr with address p. +func encUintptr(i *encInstr, state *encoderState, p unsafe.Pointer) { + v := uint64(*(*uintptr)(p)) + if v != 0 || state.sendZero { + state.update(i) + state.encodeUint(v) + } +} + +// floatBits returns a uint64 holding the bits of a floating-point number. +// Floating-point numbers are transmitted as uint64s holding the bits +// of the underlying representation. They are sent byte-reversed, with +// the exponent end coming out first, so integer floating point numbers +// (for example) transmit more compactly. This routine does the +// swizzling. +func floatBits(f float64) uint64 { + u := math.Float64bits(f) + var v uint64 + for i := 0; i < 8; i++ { + v <<= 8 + v |= u & 0xFF + u >>= 8 + } + return v +} + +// encFloat32 encodes the float32 with address p. +func encFloat32(i *encInstr, state *encoderState, p unsafe.Pointer) { + f := *(*float32)(p) + if f != 0 || state.sendZero { + v := floatBits(float64(f)) + state.update(i) + state.encodeUint(v) + } +} + +// encFloat64 encodes the float64 with address p. +func encFloat64(i *encInstr, state *encoderState, p unsafe.Pointer) { + f := *(*float64)(p) + if f != 0 || state.sendZero { + state.update(i) + v := floatBits(f) + state.encodeUint(v) + } +} + +// encComplex64 encodes the complex64 with address p. +// Complex numbers are just a pair of floating-point numbers, real part first. +func encComplex64(i *encInstr, state *encoderState, p unsafe.Pointer) { + c := *(*complex64)(p) + if c != 0+0i || state.sendZero { + rpart := floatBits(float64(real(c))) + ipart := floatBits(float64(imag(c))) + state.update(i) + state.encodeUint(rpart) + state.encodeUint(ipart) + } +} + +// encComplex128 encodes the complex128 with address p. +func encComplex128(i *encInstr, state *encoderState, p unsafe.Pointer) { + c := *(*complex128)(p) + if c != 0+0i || state.sendZero { + rpart := floatBits(real(c)) + ipart := floatBits(imag(c)) + state.update(i) + state.encodeUint(rpart) + state.encodeUint(ipart) + } +} + +// encUint8Array encodes the byte slice whose header has address p. +// Byte arrays are encoded as an unsigned count followed by the raw bytes. +func encUint8Array(i *encInstr, state *encoderState, p unsafe.Pointer) { + b := *(*[]byte)(p) + if len(b) > 0 || state.sendZero { + state.update(i) + state.encodeUint(uint64(len(b))) + state.b.Write(b) + } +} + +// encString encodes the string whose header has address p. +// Strings are encoded as an unsigned count followed by the raw bytes. +func encString(i *encInstr, state *encoderState, p unsafe.Pointer) { + s := *(*string)(p) + if len(s) > 0 || state.sendZero { + state.update(i) + state.encodeUint(uint64(len(s))) + state.b.WriteString(s) + } +} + +// encStructTerminator encodes the end of an encoded struct +// as delta field number of 0. +func encStructTerminator(i *encInstr, state *encoderState, p unsafe.Pointer) { + state.encodeUint(0) +} + +// Execution engine + +// encEngine an array of instructions indexed by field number of the encoding +// data, typically a struct. It is executed top to bottom, walking the struct. +type encEngine struct { + instr []encInstr +} + +const singletonField = 0 + +// encodeSingle encodes a single top-level non-struct value. +func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintptr) { + state := enc.newEncoderState(b) + state.fieldnum = singletonField + // There is no surrounding struct to frame the transmission, so we must + // generate data even if the item is zero. To do this, set sendZero. + state.sendZero = true + instr := &engine.instr[singletonField] + p := unsafe.Pointer(basep) // offset will be zero + if instr.indir > 0 { + if p = encIndirect(p, instr.indir); p == nil { + return + } + } + instr.op(instr, state, p) + enc.freeEncoderState(state) +} + +// encodeStruct encodes a single struct value. +func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintptr) { + state := enc.newEncoderState(b) + state.fieldnum = -1 + for i := 0; i < len(engine.instr); i++ { + instr := &engine.instr[i] + p := unsafe.Pointer(basep + instr.offset) + if instr.indir > 0 { + if p = encIndirect(p, instr.indir); p == nil { + continue + } + } + instr.op(instr, state, p) + } + enc.freeEncoderState(state) +} + +// encodeArray encodes the array whose 0th element is at p. +func (enc *Encoder) encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid uintptr, elemIndir int, length int) { + state := enc.newEncoderState(b) + state.fieldnum = -1 + state.sendZero = true + state.encodeUint(uint64(length)) + for i := 0; i < length; i++ { + elemp := p + up := unsafe.Pointer(elemp) + if elemIndir > 0 { + if up = encIndirect(up, elemIndir); up == nil { + errorf("encodeArray: nil element") + } + elemp = uintptr(up) + } + op(nil, state, unsafe.Pointer(elemp)) + p += uintptr(elemWid) + } + enc.freeEncoderState(state) +} + +// encodeReflectValue is a helper for maps. It encodes the value v. +func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir int) { + for i := 0; i < indir && v.IsValid(); i++ { + v = reflect.Indirect(v) + } + if !v.IsValid() { + errorf("encodeReflectValue: nil element") + } + op(nil, state, unsafe.Pointer(unsafeAddr(v))) +} + +// encodeMap encodes a map as unsigned count followed by key:value pairs. +// Because map internals are not exposed, we must use reflection rather than +// addresses. +func (enc *Encoder) encodeMap(b *bytes.Buffer, mv reflect.Value, keyOp, elemOp encOp, keyIndir, elemIndir int) { + state := enc.newEncoderState(b) + state.fieldnum = -1 + state.sendZero = true + keys := mv.MapKeys() + state.encodeUint(uint64(len(keys))) + for _, key := range keys { + encodeReflectValue(state, key, keyOp, keyIndir) + encodeReflectValue(state, mv.MapIndex(key), elemOp, elemIndir) + } + enc.freeEncoderState(state) +} + +// encodeInterface encodes the interface value iv. +// To send an interface, we send a string identifying the concrete type, followed +// by the type identifier (which might require defining that type right now), followed +// by the concrete value. A nil value gets sent as the empty string for the name, +// followed by no value. +func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) { + state := enc.newEncoderState(b) + state.fieldnum = -1 + state.sendZero = true + if iv.IsNil() { + state.encodeUint(0) + return + } + + ut := userType(iv.Elem().Type()) + name, ok := concreteTypeToName[ut.base] + if !ok { + errorf("type not registered for interface: %s", ut.base) + } + // Send the name. + state.encodeUint(uint64(len(name))) + _, err := state.b.WriteString(name) + if err != nil { + error_(err) + } + // Define the type id if necessary. + enc.sendTypeDescriptor(enc.writer(), state, ut) + // Send the type id. + enc.sendTypeId(state, ut) + // Encode the value into a new buffer. Any nested type definitions + // should be written to b, before the encoded value. + enc.pushWriter(b) + data := new(bytes.Buffer) + data.Write(spaceForLength) + enc.encode(data, iv.Elem(), ut) + if enc.err != nil { + error_(enc.err) + } + enc.popWriter() + enc.writeMessage(b, data) + if enc.err != nil { + error_(err) + } + enc.freeEncoderState(state) +} + +// isZero returns whether the value is the zero of its type. +func isZero(val reflect.Value) bool { + switch val.Kind() { + case reflect.Array: + for i := 0; i < val.Len(); i++ { + if !isZero(val.Index(i)) { + return false + } + } + return true + case reflect.Map, reflect.Slice, reflect.String: + return val.Len() == 0 + case reflect.Bool: + return !val.Bool() + case reflect.Complex64, reflect.Complex128: + return val.Complex() == 0 + case reflect.Chan, reflect.Func, reflect.Ptr: + return val.IsNil() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return val.Int() == 0 + case reflect.Float32, reflect.Float64: + return val.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return val.Uint() == 0 + case reflect.Struct: + for i := 0; i < val.NumField(); i++ { + if !isZero(val.Field(i)) { + return false + } + } + return true + } + panic("unknown type in isZero " + val.Type().String()) +} + +// encGobEncoder encodes a value that implements the GobEncoder interface. +// The data is sent as a byte array. +func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value) { + // TODO: should we catch panics from the called method? + // We know it's a GobEncoder, so just call the method directly. + data, err := v.Interface().(GobEncoder).GobEncode() + if err != nil { + error_(err) + } + state := enc.newEncoderState(b) + state.fieldnum = -1 + state.encodeUint(uint64(len(data))) + state.b.Write(data) + enc.freeEncoderState(state) +} + +var encOpTable = [...]encOp{ + reflect.Bool: encBool, + reflect.Int: encInt, + reflect.Int8: encInt8, + reflect.Int16: encInt16, + reflect.Int32: encInt32, + reflect.Int64: encInt64, + reflect.Uint: encUint, + reflect.Uint8: encUint8, + reflect.Uint16: encUint16, + reflect.Uint32: encUint32, + reflect.Uint64: encUint64, + reflect.Uintptr: encUintptr, + reflect.Float32: encFloat32, + reflect.Float64: encFloat64, + reflect.Complex64: encComplex64, + reflect.Complex128: encComplex128, + reflect.String: encString, +} + +// encOpFor returns (a pointer to) the encoding op for the base type under rt and +// the indirection count to reach it. +func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) { + ut := userType(rt) + // If the type implements GobEncoder, we handle it without further processing. + if ut.isGobEncoder { + return enc.gobEncodeOpFor(ut) + } + // If this type is already in progress, it's a recursive type (e.g. map[string]*T). + // Return the pointer to the op we're already building. + if opPtr := inProgress[rt]; opPtr != nil { + return opPtr, ut.indir + } + typ := ut.base + indir := ut.indir + k := typ.Kind() + var op encOp + if int(k) < len(encOpTable) { + op = encOpTable[k] + } + if op == nil { + inProgress[rt] = &op + // Special cases + switch t := typ; t.Kind() { + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { + op = encUint8Array + break + } + // Slices have a header; we decode it to find the underlying array. + elemOp, indir := enc.encOpFor(t.Elem(), inProgress) + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + slice := (*reflect.SliceHeader)(p) + if !state.sendZero && slice.Len == 0 { + return + } + state.update(i) + state.enc.encodeArray(state.b, slice.Data, *elemOp, t.Elem().Size(), indir, int(slice.Len)) + } + case reflect.Array: + // True arrays have size in the type. + elemOp, indir := enc.encOpFor(t.Elem(), inProgress) + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + state.update(i) + state.enc.encodeArray(state.b, uintptr(p), *elemOp, t.Elem().Size(), indir, t.Len()) + } + case reflect.Map: + keyOp, keyIndir := enc.encOpFor(t.Key(), inProgress) + elemOp, elemIndir := enc.encOpFor(t.Elem(), inProgress) + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + // Maps cannot be accessed by moving addresses around the way + // that slices etc. can. We must recover a full reflection value for + // the iteration. + v := reflect.NewAt(t, unsafe.Pointer(p)).Elem() + mv := reflect.Indirect(v) + // We send zero-length (but non-nil) maps because the + // receiver might want to use the map. (Maps don't use append.) + if !state.sendZero && mv.IsNil() { + return + } + state.update(i) + state.enc.encodeMap(state.b, mv, *keyOp, *elemOp, keyIndir, elemIndir) + } + case reflect.Struct: + // Generate a closure that calls out to the engine for the nested type. + enc.getEncEngine(userType(typ)) + info := mustGetTypeInfo(typ) + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + state.update(i) + // indirect through info to delay evaluation for recursive structs + state.enc.encodeStruct(state.b, info.encoder, uintptr(p)) + } + case reflect.Interface: + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + // Interfaces transmit the name and contents of the concrete + // value they contain. + v := reflect.NewAt(t, unsafe.Pointer(p)).Elem() + iv := reflect.Indirect(v) + if !state.sendZero && (!iv.IsValid() || iv.IsNil()) { + return + } + state.update(i) + state.enc.encodeInterface(state.b, iv) + } + } + } + if op == nil { + errorf("can't happen: encode type %s", rt) + } + return &op, indir +} + +// gobEncodeOpFor returns the op for a type that is known to implement +// GobEncoder. +func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) { + rt := ut.user + if ut.encIndir == -1 { + rt = reflect.PtrTo(rt) + } else if ut.encIndir > 0 { + for i := int8(0); i < ut.encIndir; i++ { + rt = rt.Elem() + } + } + var op encOp + op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { + var v reflect.Value + if ut.encIndir == -1 { + // Need to climb up one level to turn value into pointer. + v = reflect.NewAt(rt, unsafe.Pointer(&p)).Elem() + } else { + v = reflect.NewAt(rt, p).Elem() + } + if !state.sendZero && isZero(v) { + return + } + state.update(i) + state.enc.encodeGobEncoder(state.b, v) + } + return &op, int(ut.encIndir) // encIndir: op will get called with p == address of receiver. +} + +// compileEnc returns the engine to compile the type. +func (enc *Encoder) compileEnc(ut *userTypeInfo) *encEngine { + srt := ut.base + engine := new(encEngine) + seen := make(map[reflect.Type]*encOp) + rt := ut.base + if ut.isGobEncoder { + rt = ut.user + } + if !ut.isGobEncoder && + srt.Kind() == reflect.Struct { + for fieldNum, wireFieldNum := 0, 0; fieldNum < srt.NumField(); fieldNum++ { + f := srt.Field(fieldNum) + if !isExported(f.Name) { + continue + } + op, indir := enc.encOpFor(f.Type, seen) + engine.instr = append(engine.instr, encInstr{*op, wireFieldNum, indir, uintptr(f.Offset)}) + wireFieldNum++ + } + if srt.NumField() > 0 && len(engine.instr) == 0 { + errorf("type %s has no exported fields", rt) + } + engine.instr = append(engine.instr, encInstr{encStructTerminator, 0, 0, 0}) + } else { + engine.instr = make([]encInstr, 1) + op, indir := enc.encOpFor(rt, seen) + engine.instr[0] = encInstr{*op, singletonField, indir, 0} // offset is zero + } + return engine +} + +// getEncEngine returns the engine to compile the type. +// typeLock must be held (or we're in initialization and guaranteed single-threaded). +func (enc *Encoder) getEncEngine(ut *userTypeInfo) *encEngine { + info, err1 := getTypeInfo(ut) + if err1 != nil { + error_(err1) + } + if info.encoder == nil { + // mark this engine as underway before compiling to handle recursive types. + info.encoder = new(encEngine) + info.encoder = enc.compileEnc(ut) + } + return info.encoder +} + +// lockAndGetEncEngine is a function that locks and compiles. +// This lets us hold the lock only while compiling, not when encoding. +func (enc *Encoder) lockAndGetEncEngine(ut *userTypeInfo) *encEngine { + typeLock.Lock() + defer typeLock.Unlock() + return enc.getEncEngine(ut) +} + +func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInfo) { + defer catchError(&enc.err) + engine := enc.lockAndGetEncEngine(ut) + indir := ut.indir + if ut.isGobEncoder { + indir = int(ut.encIndir) + } + for i := 0; i < indir; i++ { + value = reflect.Indirect(value) + } + if !ut.isGobEncoder && value.Type().Kind() == reflect.Struct { + enc.encodeStruct(b, engine, unsafeAddr(value)) + } else { + enc.encodeSingle(b, engine, unsafeAddr(value)) + } +} diff --git a/src/pkg/encoding/gob/encoder.go b/src/pkg/encoding/gob/encoder.go new file mode 100644 index 000000000..a15b5a1f9 --- /dev/null +++ b/src/pkg/encoding/gob/encoder.go @@ -0,0 +1,253 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "bytes" + "errors" + "io" + "reflect" + "sync" +) + +// An Encoder manages the transmission of type and data information to the +// other side of a connection. +type Encoder struct { + mutex sync.Mutex // each item must be sent atomically + w []io.Writer // where to send the data + sent map[reflect.Type]typeId // which types we've already sent + countState *encoderState // stage for writing counts + freeList *encoderState // list of free encoderStates; avoids reallocation + byteBuf bytes.Buffer // buffer for top-level encoderState + err error +} + +// Before we encode a message, we reserve space at the head of the +// buffer in which to encode its length. This means we can use the +// buffer to assemble the message without another allocation. +const maxLength = 9 // Maximum size of an encoded length. +var spaceForLength = make([]byte, maxLength) + +// NewEncoder returns a new encoder that will transmit on the io.Writer. +func NewEncoder(w io.Writer) *Encoder { + enc := new(Encoder) + enc.w = []io.Writer{w} + enc.sent = make(map[reflect.Type]typeId) + enc.countState = enc.newEncoderState(new(bytes.Buffer)) + return enc +} + +// writer() returns the innermost writer the encoder is using +func (enc *Encoder) writer() io.Writer { + return enc.w[len(enc.w)-1] +} + +// pushWriter adds a writer to the encoder. +func (enc *Encoder) pushWriter(w io.Writer) { + enc.w = append(enc.w, w) +} + +// popWriter pops the innermost writer. +func (enc *Encoder) popWriter() { + enc.w = enc.w[0 : len(enc.w)-1] +} + +func (enc *Encoder) badType(rt reflect.Type) { + enc.setError(errors.New("gob: can't encode type " + rt.String())) +} + +func (enc *Encoder) setError(err error) { + if enc.err == nil { // remember the first. + enc.err = err + } +} + +// writeMessage sends the data item preceded by a unsigned count of its length. +func (enc *Encoder) writeMessage(w io.Writer, b *bytes.Buffer) { + // Space has been reserved for the length at the head of the message. + // This is a little dirty: we grab the slice from the bytes.Buffer and massage + // it by hand. + message := b.Bytes() + messageLen := len(message) - maxLength + // Encode the length. + enc.countState.b.Reset() + enc.countState.encodeUint(uint64(messageLen)) + // Copy the length to be a prefix of the message. + offset := maxLength - enc.countState.b.Len() + copy(message[offset:], enc.countState.b.Bytes()) + // Write the data. + _, err := w.Write(message[offset:]) + // Drain the buffer and restore the space at the front for the count of the next message. + b.Reset() + b.Write(spaceForLength) + if err != nil { + enc.setError(err) + } +} + +// sendActualType sends the requested type, without further investigation, unless +// it's been sent before. +func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTypeInfo, actual reflect.Type) (sent bool) { + if _, alreadySent := enc.sent[actual]; alreadySent { + return false + } + typeLock.Lock() + info, err := getTypeInfo(ut) + typeLock.Unlock() + if err != nil { + enc.setError(err) + return + } + // Send the pair (-id, type) + // Id: + state.encodeInt(-int64(info.id)) + // Type: + enc.encode(state.b, reflect.ValueOf(info.wire), wireTypeUserInfo) + enc.writeMessage(w, state.b) + if enc.err != nil { + return + } + + // Remember we've sent this type, both what the user gave us and the base type. + enc.sent[ut.base] = info.id + if ut.user != ut.base { + enc.sent[ut.user] = info.id + } + // Now send the inner types + switch st := actual; st.Kind() { + case reflect.Struct: + for i := 0; i < st.NumField(); i++ { + if isExported(st.Field(i).Name) { + enc.sendType(w, state, st.Field(i).Type) + } + } + case reflect.Array, reflect.Slice: + enc.sendType(w, state, st.Elem()) + case reflect.Map: + enc.sendType(w, state, st.Key()) + enc.sendType(w, state, st.Elem()) + } + return true +} + +// sendType sends the type info to the other side, if necessary. +func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { + ut := userType(origt) + if ut.isGobEncoder { + // The rules are different: regardless of the underlying type's representation, + // we need to tell the other side that this exact type is a GobEncoder. + return enc.sendActualType(w, state, ut, ut.user) + } + + // It's a concrete value, so drill down to the base type. + switch rt := ut.base; rt.Kind() { + default: + // Basic types and interfaces do not need to be described. + return + case reflect.Slice: + // If it's []uint8, don't send; it's considered basic. + if rt.Elem().Kind() == reflect.Uint8 { + return + } + // Otherwise we do send. + break + case reflect.Array: + // arrays must be sent so we know their lengths and element types. + break + case reflect.Map: + // maps must be sent so we know their lengths and key/value types. + break + case reflect.Struct: + // structs must be sent so we know their fields. + break + case reflect.Chan, reflect.Func: + // Probably a bad field in a struct. + enc.badType(rt) + return + } + + return enc.sendActualType(w, state, ut, ut.base) +} + +// Encode transmits the data item represented by the empty interface value, +// guaranteeing that all necessary type information has been transmitted first. +func (enc *Encoder) Encode(e interface{}) error { + return enc.EncodeValue(reflect.ValueOf(e)) +} + +// sendTypeDescriptor makes sure the remote side knows about this type. +// It will send a descriptor if this is the first time the type has been +// sent. +func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *userTypeInfo) { + // Make sure the type is known to the other side. + // First, have we already sent this type? + rt := ut.base + if ut.isGobEncoder { + rt = ut.user + } + if _, alreadySent := enc.sent[rt]; !alreadySent { + // No, so send it. + sent := enc.sendType(w, state, rt) + if enc.err != nil { + return + } + // If the type info has still not been transmitted, it means we have + // a singleton basic type (int, []byte etc.) at top level. We don't + // need to send the type info but we do need to update enc.sent. + if !sent { + typeLock.Lock() + info, err := getTypeInfo(ut) + typeLock.Unlock() + if err != nil { + enc.setError(err) + return + } + enc.sent[rt] = info.id + } + } +} + +// sendTypeId sends the id, which must have already been defined. +func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) { + // Identify the type of this top-level value. + state.encodeInt(int64(enc.sent[ut.base])) +} + +// EncodeValue transmits the data item represented by the reflection value, +// guaranteeing that all necessary type information has been transmitted first. +func (enc *Encoder) EncodeValue(value reflect.Value) error { + // Make sure we're single-threaded through here, so multiple + // goroutines can share an encoder. + enc.mutex.Lock() + defer enc.mutex.Unlock() + + // Remove any nested writers remaining due to previous errors. + enc.w = enc.w[0:1] + + ut, err := validUserType(value.Type()) + if err != nil { + return err + } + + enc.err = nil + enc.byteBuf.Reset() + enc.byteBuf.Write(spaceForLength) + state := enc.newEncoderState(&enc.byteBuf) + + enc.sendTypeDescriptor(enc.writer(), state, ut) + enc.sendTypeId(state, ut) + if enc.err != nil { + return enc.err + } + + // Encode the object. + enc.encode(state.b, value, ut) + if enc.err == nil { + enc.writeMessage(enc.writer(), state.b) + } + + enc.freeEncoderState(state) + return enc.err +} diff --git a/src/pkg/encoding/gob/encoder_test.go b/src/pkg/encoding/gob/encoder_test.go new file mode 100644 index 000000000..c4947cbb8 --- /dev/null +++ b/src/pkg/encoding/gob/encoder_test.go @@ -0,0 +1,738 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strings" + "testing" +) + +type ET2 struct { + X string +} + +type ET1 struct { + A int + Et2 *ET2 + Next *ET1 +} + +// Like ET1 but with a different name for a field +type ET3 struct { + A int + Et2 *ET2 + DifferentNext *ET1 +} + +// Like ET1 but with a different type for a field +type ET4 struct { + A int + Et2 float64 + Next int +} + +func TestEncoderDecoder(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + et1 := new(ET1) + et1.A = 7 + et1.Et2 = new(ET2) + err := enc.Encode(et1) + if err != nil { + t.Error("encoder fail:", err) + } + dec := NewDecoder(b) + newEt1 := new(ET1) + err = dec.Decode(newEt1) + if err != nil { + t.Fatal("error decoding ET1:", err) + } + + if !reflect.DeepEqual(et1, newEt1) { + t.Fatalf("invalid data for et1: expected %+v; got %+v", *et1, *newEt1) + } + if b.Len() != 0 { + t.Error("not at eof;", b.Len(), "bytes left") + } + + enc.Encode(et1) + newEt1 = new(ET1) + err = dec.Decode(newEt1) + if err != nil { + t.Fatal("round 2: error decoding ET1:", err) + } + if !reflect.DeepEqual(et1, newEt1) { + t.Fatalf("round 2: invalid data for et1: expected %+v; got %+v", *et1, *newEt1) + } + if b.Len() != 0 { + t.Error("round 2: not at eof;", b.Len(), "bytes left") + } + + // Now test with a running encoder/decoder pair that we recognize a type mismatch. + err = enc.Encode(et1) + if err != nil { + t.Error("round 3: encoder fail:", err) + } + newEt2 := new(ET2) + err = dec.Decode(newEt2) + if err == nil { + t.Fatal("round 3: expected `bad type' error decoding ET2") + } +} + +// Run one value through the encoder/decoder, but use the wrong type. +// Input is always an ET1; we compare it to whatever is under 'e'. +func badTypeCheck(e interface{}, shouldFail bool, msg string, t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + et1 := new(ET1) + et1.A = 7 + et1.Et2 = new(ET2) + err := enc.Encode(et1) + if err != nil { + t.Error("encoder fail:", err) + } + dec := NewDecoder(b) + err = dec.Decode(e) + if shouldFail && err == nil { + t.Error("expected error for", msg) + } + if !shouldFail && err != nil { + t.Error("unexpected error for", msg, err) + } +} + +// Test that we recognize a bad type the first time. +func TestWrongTypeDecoder(t *testing.T) { + badTypeCheck(new(ET2), true, "no fields in common", t) + badTypeCheck(new(ET3), false, "different name of field", t) + badTypeCheck(new(ET4), true, "different type of field", t) +} + +func corruptDataCheck(s string, err error, t *testing.T) { + b := bytes.NewBufferString(s) + dec := NewDecoder(b) + err1 := dec.Decode(new(ET2)) + if err1 != err { + t.Errorf("from %q expected error %s; got %s", s, err, err1) + } +} + +// Check that we survive bad data. +func TestBadData(t *testing.T) { + corruptDataCheck("", io.EOF, t) + corruptDataCheck("\x7Fhi", io.ErrUnexpectedEOF, t) + corruptDataCheck("\x03now is the time for all good men", errBadType, t) +} + +// Types not supported by the Encoder. +var unsupportedValues = []interface{}{ + make(chan int), + func(a int) bool { return true }, +} + +func TestUnsupported(t *testing.T) { + var b bytes.Buffer + enc := NewEncoder(&b) + for _, v := range unsupportedValues { + err := enc.Encode(v) + if err == nil { + t.Errorf("expected error for %T; got none", v) + } + } +} + +func encAndDec(in, out interface{}) error { + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(in) + if err != nil { + return err + } + dec := NewDecoder(b) + err = dec.Decode(out) + if err != nil { + return err + } + return nil +} + +func TestTypeToPtrType(t *testing.T) { + // Encode a T, decode a *T + type Type0 struct { + A int + } + t0 := Type0{7} + t0p := new(Type0) + if err := encAndDec(t0, t0p); err != nil { + t.Error(err) + } +} + +func TestPtrTypeToType(t *testing.T) { + // Encode a *T, decode a T + type Type1 struct { + A uint + } + t1p := &Type1{17} + var t1 Type1 + if err := encAndDec(t1, t1p); err != nil { + t.Error(err) + } +} + +func TestTypeToPtrPtrPtrPtrType(t *testing.T) { + type Type2 struct { + A ****float64 + } + t2 := Type2{} + t2.A = new(***float64) + *t2.A = new(**float64) + **t2.A = new(*float64) + ***t2.A = new(float64) + ****t2.A = 27.4 + t2pppp := new(***Type2) + if err := encAndDec(t2, t2pppp); err != nil { + t.Fatal(err) + } + if ****(****t2pppp).A != ****t2.A { + t.Errorf("wrong value after decode: %g not %g", ****(****t2pppp).A, ****t2.A) + } +} + +func TestSlice(t *testing.T) { + type Type3 struct { + A []string + } + t3p := &Type3{[]string{"hello", "world"}} + var t3 Type3 + if err := encAndDec(t3, t3p); err != nil { + t.Error(err) + } +} + +func TestValueError(t *testing.T) { + // Encode a *T, decode a T + type Type4 struct { + A int + } + t4p := &Type4{3} + var t4 Type4 // note: not a pointer. + if err := encAndDec(t4p, t4); err == nil || strings.Index(err.Error(), "pointer") < 0 { + t.Error("expected error about pointer; got", err) + } +} + +func TestArray(t *testing.T) { + type Type5 struct { + A [3]string + B [3]byte + } + type Type6 struct { + A [2]string // can't hold t5.a + } + t5 := Type5{[3]string{"hello", ",", "world"}, [3]byte{1, 2, 3}} + var t5p Type5 + if err := encAndDec(t5, &t5p); err != nil { + t.Error(err) + } + var t6 Type6 + if err := encAndDec(t5, &t6); err == nil { + t.Error("should fail with mismatched array sizes") + } +} + +func TestRecursiveMapType(t *testing.T) { + type recursiveMap map[string]recursiveMap + r1 := recursiveMap{"A": recursiveMap{"B": nil, "C": nil}, "D": nil} + r2 := make(recursiveMap) + if err := encAndDec(r1, &r2); err != nil { + t.Error(err) + } +} + +func TestRecursiveSliceType(t *testing.T) { + type recursiveSlice []recursiveSlice + r1 := recursiveSlice{0: recursiveSlice{0: nil}, 1: nil} + r2 := make(recursiveSlice, 0) + if err := encAndDec(r1, &r2); err != nil { + t.Error(err) + } +} + +// Regression test for bug: must send zero values inside arrays +func TestDefaultsInArray(t *testing.T) { + type Type7 struct { + B []bool + I []int + S []string + F []float64 + } + t7 := Type7{ + []bool{false, false, true}, + []int{0, 0, 1}, + []string{"hi", "", "there"}, + []float64{0, 0, 1}, + } + var t7p Type7 + if err := encAndDec(t7, &t7p); err != nil { + t.Error(err) + } +} + +var testInt int +var testFloat32 float32 +var testString string +var testSlice []string +var testMap map[string]int +var testArray [7]int + +type SingleTest struct { + in interface{} + out interface{} + err string +} + +var singleTests = []SingleTest{ + {17, &testInt, ""}, + {float32(17.5), &testFloat32, ""}, + {"bike shed", &testString, ""}, + {[]string{"bike", "shed", "paint", "color"}, &testSlice, ""}, + {map[string]int{"seven": 7, "twelve": 12}, &testMap, ""}, + {[7]int{4, 55, 0, 0, 0, 0, 0}, &testArray, ""}, // case that once triggered a bug + {[7]int{4, 55, 1, 44, 22, 66, 1234}, &testArray, ""}, + + // Decode errors + {172, &testFloat32, "type"}, +} + +func TestSingletons(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + dec := NewDecoder(b) + for _, test := range singleTests { + b.Reset() + err := enc.Encode(test.in) + if err != nil { + t.Errorf("error encoding %v: %s", test.in, err) + continue + } + err = dec.Decode(test.out) + switch { + case err != nil && test.err == "": + t.Errorf("error decoding %v: %s", test.in, err) + continue + case err == nil && test.err != "": + t.Errorf("expected error decoding %v: %s", test.in, test.err) + continue + case err != nil && test.err != "": + if strings.Index(err.Error(), test.err) < 0 { + t.Errorf("wrong error decoding %v: wanted %s, got %v", test.in, test.err, err) + } + continue + } + // Get rid of the pointer in the rhs + val := reflect.ValueOf(test.out).Elem().Interface() + if !reflect.DeepEqual(test.in, val) { + t.Errorf("decoding singleton: expected %v got %v", test.in, val) + } + } +} + +func TestStructNonStruct(t *testing.T) { + type Struct struct { + A string + } + type NonStruct string + s := Struct{"hello"} + var sp Struct + if err := encAndDec(s, &sp); err != nil { + t.Error(err) + } + var ns NonStruct + if err := encAndDec(s, &ns); err == nil { + t.Error("should get error for struct/non-struct") + } else if strings.Index(err.Error(), "type") < 0 { + t.Error("for struct/non-struct expected type error; got", err) + } + // Now try the other way + var nsp NonStruct + if err := encAndDec(ns, &nsp); err != nil { + t.Error(err) + } + if err := encAndDec(ns, &s); err == nil { + t.Error("should get error for non-struct/struct") + } else if strings.Index(err.Error(), "type") < 0 { + t.Error("for non-struct/struct expected type error; got", err) + } +} + +type interfaceIndirectTestI interface { + F() bool +} + +type interfaceIndirectTestT struct{} + +func (this *interfaceIndirectTestT) F() bool { + return true +} + +// A version of a bug reported on golang-nuts. Also tests top-level +// slice of interfaces. The issue was registering *T caused T to be +// stored as the concrete type. +func TestInterfaceIndirect(t *testing.T) { + Register(&interfaceIndirectTestT{}) + b := new(bytes.Buffer) + w := []interfaceIndirectTestI{&interfaceIndirectTestT{}} + err := NewEncoder(b).Encode(w) + if err != nil { + t.Fatal("encode error:", err) + } + + var r []interfaceIndirectTestI + err = NewDecoder(b).Decode(&r) + if err != nil { + t.Fatal("decode error:", err) + } +} + +// Now follow various tests that decode into things that can't represent the +// encoded value, all of which should be legal. + +// Also, when the ignored object contains an interface value, it may define +// types. Make sure that skipping the value still defines the types by using +// the encoder/decoder pair to send a value afterwards. If an interface +// is sent, its type in the test is always NewType0, so this checks that the +// encoder and decoder don't skew with respect to type definitions. + +type Struct0 struct { + I interface{} +} + +type NewType0 struct { + S string +} + +type ignoreTest struct { + in, out interface{} +} + +var ignoreTests = []ignoreTest{ + // Decode normal struct into an empty struct + {&struct{ A int }{23}, &struct{}{}}, + // Decode normal struct into a nil. + {&struct{ A int }{23}, nil}, + // Decode singleton string into a nil. + {"hello, world", nil}, + // Decode singleton slice into a nil. + {[]int{1, 2, 3, 4}, nil}, + // Decode struct containing an interface into a nil. + {&Struct0{&NewType0{"value0"}}, nil}, + // Decode singleton slice of interfaces into a nil. + {[]interface{}{"hi", &NewType0{"value1"}, 23}, nil}, +} + +func TestDecodeIntoNothing(t *testing.T) { + Register(new(NewType0)) + for i, test := range ignoreTests { + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(test.in) + if err != nil { + t.Errorf("%d: encode error %s:", i, err) + continue + } + dec := NewDecoder(b) + err = dec.Decode(test.out) + if err != nil { + t.Errorf("%d: decode error: %s", i, err) + continue + } + // Now see if the encoder and decoder are in a consistent state. + str := fmt.Sprintf("Value %d", i) + err = enc.Encode(&NewType0{str}) + if err != nil { + t.Fatalf("%d: NewType0 encode error: %s", i, err) + } + ns := new(NewType0) + err = dec.Decode(ns) + if err != nil { + t.Fatalf("%d: NewType0 decode error: %s", i, err) + } + if ns.S != str { + t.Fatalf("%d: expected %q got %q", i, str, ns.S) + } + } +} + +// Another bug from golang-nuts, involving nested interfaces. +type Bug0Outer struct { + Bug0Field interface{} +} + +type Bug0Inner struct { + A int +} + +func TestNestedInterfaces(t *testing.T) { + var buf bytes.Buffer + e := NewEncoder(&buf) + d := NewDecoder(&buf) + Register(new(Bug0Outer)) + Register(new(Bug0Inner)) + f := &Bug0Outer{&Bug0Outer{&Bug0Inner{7}}} + var v interface{} = f + err := e.Encode(&v) + if err != nil { + t.Fatal("Encode:", err) + } + err = d.Decode(&v) + if err != nil { + t.Fatal("Decode:", err) + } + // Make sure it decoded correctly. + outer1, ok := v.(*Bug0Outer) + if !ok { + t.Fatalf("v not Bug0Outer: %T", v) + } + outer2, ok := outer1.Bug0Field.(*Bug0Outer) + if !ok { + t.Fatalf("v.Bug0Field not Bug0Outer: %T", outer1.Bug0Field) + } + inner, ok := outer2.Bug0Field.(*Bug0Inner) + if !ok { + t.Fatalf("v.Bug0Field.Bug0Field not Bug0Inner: %T", outer2.Bug0Field) + } + if inner.A != 7 { + t.Fatalf("final value %d; expected %d", inner.A, 7) + } +} + +// The bugs keep coming. We forgot to send map subtypes before the map. + +type Bug1Elem struct { + Name string + Id int +} + +type Bug1StructMap map[string]Bug1Elem + +func bug1EncDec(in Bug1StructMap, out *Bug1StructMap) error { + return nil +} + +func TestMapBug1(t *testing.T) { + in := make(Bug1StructMap) + in["val1"] = Bug1Elem{"elem1", 1} + in["val2"] = Bug1Elem{"elem2", 2} + + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(in) + if err != nil { + t.Fatal("encode:", err) + } + dec := NewDecoder(b) + out := make(Bug1StructMap) + err = dec.Decode(&out) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(in, out) { + t.Errorf("mismatch: %v %v", in, out) + } +} + +func TestGobMapInterfaceEncode(t *testing.T) { + m := map[string]interface{}{ + "up": uintptr(0), + "i0": []int{-1}, + "i1": []int8{-1}, + "i2": []int16{-1}, + "i3": []int32{-1}, + "i4": []int64{-1}, + "u0": []uint{1}, + "u1": []uint8{1}, + "u2": []uint16{1}, + "u3": []uint32{1}, + "u4": []uint64{1}, + "f0": []float32{1}, + "f1": []float64{1}, + "c0": []complex64{complex(2, -2)}, + "c1": []complex128{complex(2, float64(-2))}, + "us": []uintptr{0}, + "bo": []bool{false}, + "st": []string{"s"}, + } + enc := NewEncoder(new(bytes.Buffer)) + err := enc.Encode(m) + if err != nil { + t.Errorf("encode map: %s", err) + } +} + +func TestSliceReusesMemory(t *testing.T) { + buf := new(bytes.Buffer) + // Bytes + { + x := []byte("abcd") + enc := NewEncoder(buf) + err := enc.Encode(x) + if err != nil { + t.Errorf("bytes: encode: %s", err) + } + // Decode into y, which is big enough. + y := []byte("ABCDE") + addr := &y[0] + dec := NewDecoder(buf) + err = dec.Decode(&y) + if err != nil { + t.Fatal("bytes: decode:", err) + } + if !bytes.Equal(x, y) { + t.Errorf("bytes: expected %q got %q\n", x, y) + } + if addr != &y[0] { + t.Errorf("bytes: unnecessary reallocation") + } + } + // general slice + { + x := []rune("abcd") + enc := NewEncoder(buf) + err := enc.Encode(x) + if err != nil { + t.Errorf("ints: encode: %s", err) + } + // Decode into y, which is big enough. + y := []rune("ABCDE") + addr := &y[0] + dec := NewDecoder(buf) + err = dec.Decode(&y) + if err != nil { + t.Fatal("ints: decode:", err) + } + if !reflect.DeepEqual(x, y) { + t.Errorf("ints: expected %q got %q\n", x, y) + } + if addr != &y[0] { + t.Errorf("ints: unnecessary reallocation") + } + } +} + +// Used to crash: negative count in recvMessage. +func TestBadCount(t *testing.T) { + b := []byte{0xfb, 0xa5, 0x82, 0x2f, 0xca, 0x1} + if err := NewDecoder(bytes.NewBuffer(b)).Decode(nil); err == nil { + t.Error("expected error from bad count") + } else if err.Error() != errBadCount.Error() { + t.Error("expected bad count error; got", err) + } +} + +// Verify that sequential Decoders built on a single input will +// succeed if the input implements ReadByte and there is no +// type information in the stream. +func TestSequentialDecoder(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + const count = 10 + for i := 0; i < count; i++ { + s := fmt.Sprintf("%d", i) + if err := enc.Encode(s); err != nil { + t.Error("encoder fail:", err) + } + } + for i := 0; i < count; i++ { + dec := NewDecoder(b) + var s string + if err := dec.Decode(&s); err != nil { + t.Fatal("decoder fail:", err) + } + if s != fmt.Sprintf("%d", i) { + t.Fatalf("decode expected %d got %s", i, s) + } + } +} + +// Should be able to have unrepresentable fields (chan, func) as long as they +// are unexported. +type Bug2 struct { + A int + b chan int +} + +func TestUnexportedChan(t *testing.T) { + b := Bug2{23, make(chan int)} + var stream bytes.Buffer + enc := NewEncoder(&stream) + if err := enc.Encode(b); err != nil { + t.Fatalf("error encoding unexported channel: %s", err) + } +} + +func TestSliceIncompatibility(t *testing.T) { + var in = []byte{1, 2, 3} + var out []int + if err := encAndDec(in, &out); err == nil { + t.Error("expected compatibility error") + } +} + +// Mutually recursive slices of structs caused problems. +type Bug3 struct { + Num int + Children []*Bug3 +} + +func TestGobPtrSlices(t *testing.T) { + in := []*Bug3{ + {1, nil}, + {2, nil}, + } + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(&in) + if err != nil { + t.Fatal("encode:", err) + } + + var out []*Bug3 + err = NewDecoder(b).Decode(&out) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(in, out) { + t.Fatalf("got %v; wanted %v", out, in) + } +} + +// getDecEnginePtr cached engine for ut.base instead of ut.user so we passed +// a *map and then tried to reuse its engine to decode the inner map. +func TestPtrToMapOfMap(t *testing.T) { + Register(make(map[string]interface{})) + subdata := make(map[string]interface{}) + subdata["bar"] = "baz" + data := make(map[string]interface{}) + data["foo"] = subdata + + b := new(bytes.Buffer) + err := NewEncoder(b).Encode(data) + if err != nil { + t.Fatal("encode:", err) + } + var newData map[string]interface{} + err = NewDecoder(b).Decode(&newData) + if err != nil { + t.Fatal("decode:", err) + } + if !reflect.DeepEqual(data, newData) { + t.Fatalf("expected %v got %v", data, newData) + } +} diff --git a/src/pkg/encoding/gob/error.go b/src/pkg/encoding/gob/error.go new file mode 100644 index 000000000..92cc0c615 --- /dev/null +++ b/src/pkg/encoding/gob/error.go @@ -0,0 +1,43 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import "fmt" + +// Errors in decoding and encoding are handled using panic and recover. +// Panics caused by user error (that is, everything except run-time panics +// such as "index out of bounds" errors) do not leave the file that caused +// them, but are instead turned into plain error returns. Encoding and +// decoding functions and methods that do not return an error either use +// panic to report an error or are guaranteed error-free. + +// A gobError is used to distinguish errors (panics) generated in this package. +type gobError struct { + err error +} + +// errorf is like error_ but takes Printf-style arguments to construct an error. +// It always prefixes the message with "gob: ". +func errorf(format string, args ...interface{}) { + error_(fmt.Errorf("gob: "+format, args...)) +} + +// error wraps the argument error and uses it as the argument to panic. +func error_(err error) { + panic(gobError{err}) +} + +// catchError is meant to be used as a deferred function to turn a panic(gobError) into a +// plain error. It overwrites the error return of the function that deferred its call. +func catchError(err *error) { + if e := recover(); e != nil { + ge, ok := e.(gobError) + if !ok { + panic(e) + } + *err = ge.err + } + return +} diff --git a/src/pkg/encoding/gob/gobencdec_test.go b/src/pkg/encoding/gob/gobencdec_test.go new file mode 100644 index 000000000..45240d764 --- /dev/null +++ b/src/pkg/encoding/gob/gobencdec_test.go @@ -0,0 +1,594 @@ +// Copyright 20011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains tests of the GobEncoder/GobDecoder support. + +package gob + +import ( + "bytes" + "errors" + "fmt" + "io" + "strings" + "testing" + "time" +) + +// Types that implement the GobEncoder/Decoder interfaces. + +type ByteStruct struct { + a byte // not an exported field +} + +type StringStruct struct { + s string // not an exported field +} + +type ArrayStruct struct { + a [8192]byte // not an exported field +} + +type Gobber int + +type ValueGobber string // encodes with a value, decodes with a pointer. + +// The relevant methods + +func (g *ByteStruct) GobEncode() ([]byte, error) { + b := make([]byte, 3) + b[0] = g.a + b[1] = g.a + 1 + b[2] = g.a + 2 + return b, nil +} + +func (g *ByteStruct) GobDecode(data []byte) error { + if g == nil { + return errors.New("NIL RECEIVER") + } + // Expect N sequential-valued bytes. + if len(data) == 0 { + return io.EOF + } + g.a = data[0] + for i, c := range data { + if c != g.a+byte(i) { + return errors.New("invalid data sequence") + } + } + return nil +} + +func (g *StringStruct) GobEncode() ([]byte, error) { + return []byte(g.s), nil +} + +func (g *StringStruct) GobDecode(data []byte) error { + // Expect N sequential-valued bytes. + if len(data) == 0 { + return io.EOF + } + a := data[0] + for i, c := range data { + if c != a+byte(i) { + return errors.New("invalid data sequence") + } + } + g.s = string(data) + return nil +} + +func (a *ArrayStruct) GobEncode() ([]byte, error) { + return a.a[:], nil +} + +func (a *ArrayStruct) GobDecode(data []byte) error { + if len(data) != len(a.a) { + return errors.New("wrong length in array decode") + } + copy(a.a[:], data) + return nil +} + +func (g *Gobber) GobEncode() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%d", *g)), nil +} + +func (g *Gobber) GobDecode(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) + return err +} + +func (v ValueGobber) GobEncode() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%s", v)), nil +} + +func (v *ValueGobber) GobDecode(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) + return err +} + +// Structs that include GobEncodable fields. + +type GobTest0 struct { + X int // guarantee we have something in common with GobTest* + G *ByteStruct +} + +type GobTest1 struct { + X int // guarantee we have something in common with GobTest* + G *StringStruct +} + +type GobTest2 struct { + X int // guarantee we have something in common with GobTest* + G string // not a GobEncoder - should give us errors +} + +type GobTest3 struct { + X int // guarantee we have something in common with GobTest* + G *Gobber +} + +type GobTest4 struct { + X int // guarantee we have something in common with GobTest* + V ValueGobber +} + +type GobTest5 struct { + X int // guarantee we have something in common with GobTest* + V *ValueGobber +} + +type GobTestIgnoreEncoder struct { + X int // guarantee we have something in common with GobTest* +} + +type GobTestValueEncDec struct { + X int // guarantee we have something in common with GobTest* + G StringStruct // not a pointer. +} + +type GobTestIndirectEncDec struct { + X int // guarantee we have something in common with GobTest* + G ***StringStruct // indirections to the receiver. +} + +type GobTestArrayEncDec struct { + X int // guarantee we have something in common with GobTest* + A ArrayStruct // not a pointer. +} + +type GobTestIndirectArrayEncDec struct { + X int // guarantee we have something in common with GobTest* + A ***ArrayStruct // indirections to a large receiver. +} + +func TestGobEncoderField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + err := enc.Encode(GobTest0{17, &ByteStruct{'A'}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest0) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.G.a != 'A' { + t.Errorf("expected 'A' got %c", x.G.a) + } + // Now a field that's not a structure. + b.Reset() + gobber := Gobber(23) + err = enc.Encode(GobTest3{17, &gobber}) + if err != nil { + t.Fatal("encode error:", err) + } + y := new(GobTest3) + err = dec.Decode(y) + if err != nil { + t.Fatal("decode error:", err) + } + if *y.G != 23 { + t.Errorf("expected '23 got %d", *y.G) + } +} + +// Even though the field is a value, we can still take its address +// and should be able to call the methods. +func TestGobEncoderValueField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + err := enc.Encode(GobTestValueEncDec{17, StringStruct{"HIJKL"}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestValueEncDec) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.G.s != "HIJKL" { + t.Errorf("expected `HIJKL` got %s", x.G.s) + } +} + +// GobEncode/Decode should work even if the value is +// more indirect than the receiver. +func TestGobEncoderIndirectField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + s := &StringStruct{"HIJKL"} + sp := &s + err := enc.Encode(GobTestIndirectEncDec{17, &sp}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestIndirectEncDec) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if (***x.G).s != "HIJKL" { + t.Errorf("expected `HIJKL` got %s", (***x.G).s) + } +} + +// Test with a large field with methods. +func TestGobEncoderArrayField(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + var a GobTestArrayEncDec + a.X = 17 + for i := range a.A.a { + a.A.a[i] = byte(i) + } + err := enc.Encode(a) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestArrayEncDec) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + for i, v := range x.A.a { + if v != byte(i) { + t.Errorf("expected %x got %x", byte(i), v) + break + } + } +} + +// Test an indirection to a large field with methods. +func TestGobEncoderIndirectArrayField(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + var a GobTestIndirectArrayEncDec + a.X = 17 + var array ArrayStruct + ap := &array + app := &ap + a.A = &app + for i := range array.a { + array.a[i] = byte(i) + } + err := enc.Encode(a) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestIndirectArrayEncDec) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + for i, v := range (***x.A).a { + if v != byte(i) { + t.Errorf("expected %x got %x", byte(i), v) + break + } + } +} + +// As long as the fields have the same name and implement the +// interface, we can cross-connect them. Not sure it's useful +// and may even be bad but it works and it's hard to prevent +// without exposing the contents of the object, which would +// defeat the purpose. +func TestGobEncoderFieldsOfDifferentType(t *testing.T) { + // first, string in field to byte in field + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(GobTest1{17, &StringStruct{"ABC"}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest0) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.G.a != 'A' { + t.Errorf("expected 'A' got %c", x.G.a) + } + // now the other direction, byte in field to string in field + b.Reset() + err = enc.Encode(GobTest0{17, &ByteStruct{'X'}}) + if err != nil { + t.Fatal("encode error:", err) + } + y := new(GobTest1) + err = dec.Decode(y) + if err != nil { + t.Fatal("decode error:", err) + } + if y.G.s != "XYZ" { + t.Fatalf("expected `XYZ` got %c", y.G.s) + } +} + +// Test that we can encode a value and decode into a pointer. +func TestGobEncoderValueEncoder(t *testing.T) { + // first, string in field to byte in field + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(GobTest4{17, ValueGobber("hello")}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest5) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if *x.V != "hello" { + t.Errorf("expected `hello` got %s", x.V) + } +} + +func TestGobEncoderFieldTypeError(t *testing.T) { + // GobEncoder to non-decoder: error + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(GobTest1{17, &StringStruct{"ABC"}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := &GobTest2{} + err = dec.Decode(x) + if err == nil { + t.Fatal("expected decode error for mismatched fields (encoder to non-decoder)") + } + if strings.Index(err.Error(), "type") < 0 { + t.Fatal("expected type error; got", err) + } + // Non-encoder to GobDecoder: error + b.Reset() + err = enc.Encode(GobTest2{17, "ABC"}) + if err != nil { + t.Fatal("encode error:", err) + } + y := &GobTest1{} + err = dec.Decode(y) + if err == nil { + t.Fatal("expected decode error for mismatched fields (non-encoder to decoder)") + } + if strings.Index(err.Error(), "type") < 0 { + t.Fatal("expected type error; got", err) + } +} + +// Even though ByteStruct is a struct, it's treated as a singleton at the top level. +func TestGobEncoderStructSingleton(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(&ByteStruct{'A'}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(ByteStruct) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.a != 'A' { + t.Errorf("expected 'A' got %c", x.a) + } +} + +func TestGobEncoderNonStructSingleton(t *testing.T) { + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(Gobber(1234)) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + var x Gobber + err = dec.Decode(&x) + if err != nil { + t.Fatal("decode error:", err) + } + if x != 1234 { + t.Errorf("expected 1234 got %d", x) + } +} + +func TestGobEncoderIgnoreStructField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + err := enc.Encode(GobTest0{17, &ByteStruct{'A'}}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestIgnoreEncoder) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.X != 17 { + t.Errorf("expected 17 got %c", x.X) + } +} + +func TestGobEncoderIgnoreNonStructField(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + gobber := Gobber(23) + err := enc.Encode(GobTest3{17, &gobber}) + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTestIgnoreEncoder) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.X != 17 { + t.Errorf("expected 17 got %c", x.X) + } +} + +func TestGobEncoderIgnoreNilEncoder(t *testing.T) { + b := new(bytes.Buffer) + // First a field that's a structure. + enc := NewEncoder(b) + err := enc.Encode(GobTest0{X: 18}) // G is nil + if err != nil { + t.Fatal("encode error:", err) + } + dec := NewDecoder(b) + x := new(GobTest0) + err = dec.Decode(x) + if err != nil { + t.Fatal("decode error:", err) + } + if x.X != 18 { + t.Errorf("expected x.X = 18, got %v", x.X) + } + if x.G != nil { + t.Errorf("expected x.G = nil, got %v", x.G) + } +} + +type gobDecoderBug0 struct { + foo, bar string +} + +func (br *gobDecoderBug0) String() string { + return br.foo + "-" + br.bar +} + +func (br *gobDecoderBug0) GobEncode() ([]byte, error) { + return []byte(br.String()), nil +} + +func (br *gobDecoderBug0) GobDecode(b []byte) error { + br.foo = "foo" + br.bar = "bar" + return nil +} + +// This was a bug: the receiver has a different indirection level +// than the variable. +func TestGobEncoderExtraIndirect(t *testing.T) { + gdb := &gobDecoderBug0{"foo", "bar"} + buf := new(bytes.Buffer) + e := NewEncoder(buf) + if err := e.Encode(gdb); err != nil { + t.Fatalf("encode: %v", err) + } + d := NewDecoder(buf) + var got *gobDecoderBug0 + if err := d.Decode(&got); err != nil { + t.Fatalf("decode: %v", err) + } + if got.foo != gdb.foo || got.bar != gdb.bar { + t.Errorf("got = %q, want %q", got, gdb) + } +} + +// Another bug: this caused a crash with the new Go1 Time type. +// We throw in a gob-encoding array, to test another case of isZero + +type isZeroBug struct { + T time.Time + S string + I int + A isZeroBugArray +} + +type isZeroBugArray [2]uint8 + +// Receiver is value, not pointer, to test isZero of array. +func (a isZeroBugArray) GobEncode() (b []byte, e error) { + b = append(b, a[:]...) + return b, nil +} + +func (a *isZeroBugArray) GobDecode(data []byte) error { + if len(data) != len(a) { + return io.EOF + } + a[0] = data[0] + a[1] = data[1] + return nil +} + +func TestGobEncodeIsZero(t *testing.T) { + x := isZeroBug{time.Now(), "hello", -55, isZeroBugArray{1, 2}} + b := new(bytes.Buffer) + enc := NewEncoder(b) + err := enc.Encode(x) + if err != nil { + t.Fatal("encode:", err) + } + var y isZeroBug + dec := NewDecoder(b) + err = dec.Decode(&y) + if err != nil { + t.Fatal("decode:", err) + } + if x != y { + t.Fatalf("%v != %v", x, y) + } +} + +func TestGobEncodePtrError(t *testing.T) { + var err error + b := new(bytes.Buffer) + enc := NewEncoder(b) + err = enc.Encode(&err) + if err != nil { + t.Fatal("encode:", err) + } + dec := NewDecoder(b) + err2 := fmt.Errorf("foo") + err = dec.Decode(&err2) + if err != nil { + t.Fatal("decode:", err) + } + if err2 != nil { + t.Fatalf("expected nil, got %v", err2) + } +} diff --git a/src/pkg/encoding/gob/timing_test.go b/src/pkg/encoding/gob/timing_test.go new file mode 100644 index 000000000..b9371c423 --- /dev/null +++ b/src/pkg/encoding/gob/timing_test.go @@ -0,0 +1,96 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "bytes" + "fmt" + "io" + "os" + "runtime" + "testing" +) + +type Bench struct { + A int + B float64 + C string + D []byte +} + +func benchmarkEndToEnd(r io.Reader, w io.Writer, b *testing.B) { + b.StopTimer() + enc := NewEncoder(w) + dec := NewDecoder(r) + bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")} + b.StartTimer() + for i := 0; i < b.N; i++ { + if enc.Encode(bench) != nil { + panic("encode error") + } + if dec.Decode(bench) != nil { + panic("decode error") + } + } +} + +func BenchmarkEndToEndPipe(b *testing.B) { + r, w, err := os.Pipe() + if err != nil { + b.Fatal("can't get pipe:", err) + } + benchmarkEndToEnd(r, w, b) +} + +func BenchmarkEndToEndByteBuffer(b *testing.B) { + var buf bytes.Buffer + benchmarkEndToEnd(&buf, &buf, b) +} + +func TestCountEncodeMallocs(t *testing.T) { + var buf bytes.Buffer + enc := NewEncoder(&buf) + bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")} + memstats := new(runtime.MemStats) + runtime.ReadMemStats(memstats) + mallocs := 0 - memstats.Mallocs + const count = 1000 + for i := 0; i < count; i++ { + err := enc.Encode(bench) + if err != nil { + t.Fatal("encode:", err) + } + } + runtime.ReadMemStats(memstats) + mallocs += memstats.Mallocs + fmt.Printf("mallocs per encode of type Bench: %d\n", mallocs/count) +} + +func TestCountDecodeMallocs(t *testing.T) { + var buf bytes.Buffer + enc := NewEncoder(&buf) + bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")} + const count = 1000 + for i := 0; i < count; i++ { + err := enc.Encode(bench) + if err != nil { + t.Fatal("encode:", err) + } + } + dec := NewDecoder(&buf) + memstats := new(runtime.MemStats) + runtime.ReadMemStats(memstats) + mallocs := 0 - memstats.Mallocs + for i := 0; i < count; i++ { + *bench = Bench{} + err := dec.Decode(&bench) + if err != nil { + t.Fatal("decode:", err) + } + } + runtime.ReadMemStats(memstats) + mallocs += memstats.Mallocs + fmt.Printf("mallocs per decode of type Bench: %d\n", mallocs/count) +} diff --git a/src/pkg/encoding/gob/type.go b/src/pkg/encoding/gob/type.go new file mode 100644 index 000000000..0dd7a0a77 --- /dev/null +++ b/src/pkg/encoding/gob/type.go @@ -0,0 +1,808 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "errors" + "fmt" + "os" + "reflect" + "sync" + "unicode" + "unicode/utf8" +) + +// userTypeInfo stores the information associated with a type the user has handed +// to the package. It's computed once and stored in a map keyed by reflection +// type. +type userTypeInfo struct { + user reflect.Type // the type the user handed us + base reflect.Type // the base type after all indirections + indir int // number of indirections to reach the base type + isGobEncoder bool // does the type implement GobEncoder? + isGobDecoder bool // does the type implement GobDecoder? + encIndir int8 // number of indirections to reach the receiver type; may be negative + decIndir int8 // number of indirections to reach the receiver type; may be negative +} + +var ( + // Protected by an RWMutex because we read it a lot and write + // it only when we see a new type, typically when compiling. + userTypeLock sync.RWMutex + userTypeCache = make(map[reflect.Type]*userTypeInfo) +) + +// validType returns, and saves, the information associated with user-provided type rt. +// If the user type is not valid, err will be non-nil. To be used when the error handler +// is not set up. +func validUserType(rt reflect.Type) (ut *userTypeInfo, err error) { + userTypeLock.RLock() + ut = userTypeCache[rt] + userTypeLock.RUnlock() + if ut != nil { + return + } + // Now set the value under the write lock. + userTypeLock.Lock() + defer userTypeLock.Unlock() + if ut = userTypeCache[rt]; ut != nil { + // Lost the race; not a problem. + return + } + ut = new(userTypeInfo) + ut.base = rt + ut.user = rt + // A type that is just a cycle of pointers (such as type T *T) cannot + // be represented in gobs, which need some concrete data. We use a + // cycle detection algorithm from Knuth, Vol 2, Section 3.1, Ex 6, + // pp 539-540. As we step through indirections, run another type at + // half speed. If they meet up, there's a cycle. + slowpoke := ut.base // walks half as fast as ut.base + for { + pt := ut.base + if pt.Kind() != reflect.Ptr { + break + } + ut.base = pt.Elem() + if ut.base == slowpoke { // ut.base lapped slowpoke + // recursive pointer type. + return nil, errors.New("can't represent recursive pointer type " + ut.base.String()) + } + if ut.indir%2 == 0 { + slowpoke = slowpoke.Elem() + } + ut.indir++ + } + ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderInterfaceType) + ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderInterfaceType) + userTypeCache[rt] = ut + return +} + +var ( + gobEncoderInterfaceType = reflect.TypeOf((*GobEncoder)(nil)).Elem() + gobDecoderInterfaceType = reflect.TypeOf((*GobDecoder)(nil)).Elem() +) + +// implementsInterface reports whether the type implements the +// gobEncoder/gobDecoder interface. +// It also returns the number of indirections required to get to the +// implementation. +func implementsInterface(typ, gobEncDecType reflect.Type) (success bool, indir int8) { + if typ == nil { + return + } + rt := typ + // The type might be a pointer and we need to keep + // dereferencing to the base type until we find an implementation. + for { + if rt.Implements(gobEncDecType) { + return true, indir + } + if p := rt; p.Kind() == reflect.Ptr { + indir++ + if indir > 100 { // insane number of indirections + return false, 0 + } + rt = p.Elem() + continue + } + break + } + // No luck yet, but if this is a base type (non-pointer), the pointer might satisfy. + if typ.Kind() != reflect.Ptr { + // Not a pointer, but does the pointer work? + if reflect.PtrTo(typ).Implements(gobEncDecType) { + return true, -1 + } + } + return false, 0 +} + +// userType returns, and saves, the information associated with user-provided type rt. +// If the user type is not valid, it calls error. +func userType(rt reflect.Type) *userTypeInfo { + ut, err := validUserType(rt) + if err != nil { + error_(err) + } + return ut +} + +// A typeId represents a gob Type as an integer that can be passed on the wire. +// Internally, typeIds are used as keys to a map to recover the underlying type info. +type typeId int32 + +var nextId typeId // incremented for each new type we build +var typeLock sync.Mutex // set while building a type +const firstUserId = 64 // lowest id number granted to user + +type gobType interface { + id() typeId + setId(id typeId) + name() string + string() string // not public; only for debugging + safeString(seen map[typeId]bool) string +} + +var types = make(map[reflect.Type]gobType) +var idToType = make(map[typeId]gobType) +var builtinIdToType map[typeId]gobType // set in init() after builtins are established + +func setTypeId(typ gobType) { + // When building recursive types, someone may get there before us. + if typ.id() != 0 { + return + } + nextId++ + typ.setId(nextId) + idToType[nextId] = typ +} + +func (t typeId) gobType() gobType { + if t == 0 { + return nil + } + return idToType[t] +} + +// string returns the string representation of the type associated with the typeId. +func (t typeId) string() string { + if t.gobType() == nil { + return "" + } + return t.gobType().string() +} + +// Name returns the name of the type associated with the typeId. +func (t typeId) name() string { + if t.gobType() == nil { + return "" + } + return t.gobType().name() +} + +// CommonType holds elements of all types. +// It is a historical artifact, kept for binary compatibility and exported +// only for the benefit of the package's encoding of type descriptors. It is +// not intended for direct use by clients. +type CommonType struct { + Name string + Id typeId +} + +func (t *CommonType) id() typeId { return t.Id } + +func (t *CommonType) setId(id typeId) { t.Id = id } + +func (t *CommonType) string() string { return t.Name } + +func (t *CommonType) safeString(seen map[typeId]bool) string { + return t.Name +} + +func (t *CommonType) name() string { return t.Name } + +// Create and check predefined types +// The string for tBytes is "bytes" not "[]byte" to signify its specialness. + +var ( + // Primordial types, needed during initialization. + // Always passed as pointers so the interface{} type + // goes through without losing its interfaceness. + tBool = bootstrapType("bool", (*bool)(nil), 1) + tInt = bootstrapType("int", (*int)(nil), 2) + tUint = bootstrapType("uint", (*uint)(nil), 3) + tFloat = bootstrapType("float", (*float64)(nil), 4) + tBytes = bootstrapType("bytes", (*[]byte)(nil), 5) + tString = bootstrapType("string", (*string)(nil), 6) + tComplex = bootstrapType("complex", (*complex128)(nil), 7) + tInterface = bootstrapType("interface", (*interface{})(nil), 8) + // Reserve some Ids for compatible expansion + tReserved7 = bootstrapType("_reserved1", (*struct{ r7 int })(nil), 9) + tReserved6 = bootstrapType("_reserved1", (*struct{ r6 int })(nil), 10) + tReserved5 = bootstrapType("_reserved1", (*struct{ r5 int })(nil), 11) + tReserved4 = bootstrapType("_reserved1", (*struct{ r4 int })(nil), 12) + tReserved3 = bootstrapType("_reserved1", (*struct{ r3 int })(nil), 13) + tReserved2 = bootstrapType("_reserved1", (*struct{ r2 int })(nil), 14) + tReserved1 = bootstrapType("_reserved1", (*struct{ r1 int })(nil), 15) +) + +// Predefined because it's needed by the Decoder +var tWireType = mustGetTypeInfo(reflect.TypeOf(wireType{})).id +var wireTypeUserInfo *userTypeInfo // userTypeInfo of (*wireType) + +func init() { + // Some magic numbers to make sure there are no surprises. + checkId(16, tWireType) + checkId(17, mustGetTypeInfo(reflect.TypeOf(arrayType{})).id) + checkId(18, mustGetTypeInfo(reflect.TypeOf(CommonType{})).id) + checkId(19, mustGetTypeInfo(reflect.TypeOf(sliceType{})).id) + checkId(20, mustGetTypeInfo(reflect.TypeOf(structType{})).id) + checkId(21, mustGetTypeInfo(reflect.TypeOf(fieldType{})).id) + checkId(23, mustGetTypeInfo(reflect.TypeOf(mapType{})).id) + + builtinIdToType = make(map[typeId]gobType) + for k, v := range idToType { + builtinIdToType[k] = v + } + + // Move the id space upwards to allow for growth in the predefined world + // without breaking existing files. + if nextId > firstUserId { + panic(fmt.Sprintln("nextId too large:", nextId)) + } + nextId = firstUserId + registerBasics() + wireTypeUserInfo = userType(reflect.TypeOf((*wireType)(nil))) +} + +// Array type +type arrayType struct { + CommonType + Elem typeId + Len int +} + +func newArrayType(name string) *arrayType { + a := &arrayType{CommonType{Name: name}, 0, 0} + return a +} + +func (a *arrayType) init(elem gobType, len int) { + // Set our type id before evaluating the element's, in case it's our own. + setTypeId(a) + a.Elem = elem.id() + a.Len = len +} + +func (a *arrayType) safeString(seen map[typeId]bool) string { + if seen[a.Id] { + return a.Name + } + seen[a.Id] = true + return fmt.Sprintf("[%d]%s", a.Len, a.Elem.gobType().safeString(seen)) +} + +func (a *arrayType) string() string { return a.safeString(make(map[typeId]bool)) } + +// GobEncoder type (something that implements the GobEncoder interface) +type gobEncoderType struct { + CommonType +} + +func newGobEncoderType(name string) *gobEncoderType { + g := &gobEncoderType{CommonType{Name: name}} + setTypeId(g) + return g +} + +func (g *gobEncoderType) safeString(seen map[typeId]bool) string { + return g.Name +} + +func (g *gobEncoderType) string() string { return g.Name } + +// Map type +type mapType struct { + CommonType + Key typeId + Elem typeId +} + +func newMapType(name string) *mapType { + m := &mapType{CommonType{Name: name}, 0, 0} + return m +} + +func (m *mapType) init(key, elem gobType) { + // Set our type id before evaluating the element's, in case it's our own. + setTypeId(m) + m.Key = key.id() + m.Elem = elem.id() +} + +func (m *mapType) safeString(seen map[typeId]bool) string { + if seen[m.Id] { + return m.Name + } + seen[m.Id] = true + key := m.Key.gobType().safeString(seen) + elem := m.Elem.gobType().safeString(seen) + return fmt.Sprintf("map[%s]%s", key, elem) +} + +func (m *mapType) string() string { return m.safeString(make(map[typeId]bool)) } + +// Slice type +type sliceType struct { + CommonType + Elem typeId +} + +func newSliceType(name string) *sliceType { + s := &sliceType{CommonType{Name: name}, 0} + return s +} + +func (s *sliceType) init(elem gobType) { + // Set our type id before evaluating the element's, in case it's our own. + setTypeId(s) + // See the comments about ids in newTypeObject. Only slices and + // structs have mutual recursion. + if elem.id() == 0 { + setTypeId(elem) + } + s.Elem = elem.id() +} + +func (s *sliceType) safeString(seen map[typeId]bool) string { + if seen[s.Id] { + return s.Name + } + seen[s.Id] = true + return fmt.Sprintf("[]%s", s.Elem.gobType().safeString(seen)) +} + +func (s *sliceType) string() string { return s.safeString(make(map[typeId]bool)) } + +// Struct type +type fieldType struct { + Name string + Id typeId +} + +type structType struct { + CommonType + Field []*fieldType +} + +func (s *structType) safeString(seen map[typeId]bool) string { + if s == nil { + return "" + } + if _, ok := seen[s.Id]; ok { + return s.Name + } + seen[s.Id] = true + str := s.Name + " = struct { " + for _, f := range s.Field { + str += fmt.Sprintf("%s %s; ", f.Name, f.Id.gobType().safeString(seen)) + } + str += "}" + return str +} + +func (s *structType) string() string { return s.safeString(make(map[typeId]bool)) } + +func newStructType(name string) *structType { + s := &structType{CommonType{Name: name}, nil} + // For historical reasons we set the id here rather than init. + // See the comment in newTypeObject for details. + setTypeId(s) + return s +} + +// newTypeObject allocates a gobType for the reflection type rt. +// Unless ut represents a GobEncoder, rt should be the base type +// of ut. +// This is only called from the encoding side. The decoding side +// works through typeIds and userTypeInfos alone. +func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, error) { + // Does this type implement GobEncoder? + if ut.isGobEncoder { + return newGobEncoderType(name), nil + } + var err error + var type0, type1 gobType + defer func() { + if err != nil { + delete(types, rt) + } + }() + // Install the top-level type before the subtypes (e.g. struct before + // fields) so recursive types can be constructed safely. + switch t := rt; t.Kind() { + // All basic types are easy: they are predefined. + case reflect.Bool: + return tBool.gobType(), nil + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return tInt.gobType(), nil + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return tUint.gobType(), nil + + case reflect.Float32, reflect.Float64: + return tFloat.gobType(), nil + + case reflect.Complex64, reflect.Complex128: + return tComplex.gobType(), nil + + case reflect.String: + return tString.gobType(), nil + + case reflect.Interface: + return tInterface.gobType(), nil + + case reflect.Array: + at := newArrayType(name) + types[rt] = at + type0, err = getBaseType("", t.Elem()) + if err != nil { + return nil, err + } + // Historical aside: + // For arrays, maps, and slices, we set the type id after the elements + // are constructed. This is to retain the order of type id allocation after + // a fix made to handle recursive types, which changed the order in + // which types are built. Delaying the setting in this way preserves + // type ids while allowing recursive types to be described. Structs, + // done below, were already handling recursion correctly so they + // assign the top-level id before those of the field. + at.init(type0, t.Len()) + return at, nil + + case reflect.Map: + mt := newMapType(name) + types[rt] = mt + type0, err = getBaseType("", t.Key()) + if err != nil { + return nil, err + } + type1, err = getBaseType("", t.Elem()) + if err != nil { + return nil, err + } + mt.init(type0, type1) + return mt, nil + + case reflect.Slice: + // []byte == []uint8 is a special case + if t.Elem().Kind() == reflect.Uint8 { + return tBytes.gobType(), nil + } + st := newSliceType(name) + types[rt] = st + type0, err = getBaseType(t.Elem().Name(), t.Elem()) + if err != nil { + return nil, err + } + st.init(type0) + return st, nil + + case reflect.Struct: + st := newStructType(name) + types[rt] = st + idToType[st.id()] = st + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if !isExported(f.Name) { + continue + } + typ := userType(f.Type).base + tname := typ.Name() + if tname == "" { + t := userType(f.Type).base + tname = t.String() + } + gt, err := getBaseType(tname, f.Type) + if err != nil { + return nil, err + } + // Some mutually recursive types can cause us to be here while + // still defining the element. Fix the element type id here. + // We could do this more neatly by setting the id at the start of + // building every type, but that would break binary compatibility. + if gt.id() == 0 { + setTypeId(gt) + } + st.Field = append(st.Field, &fieldType{f.Name, gt.id()}) + } + return st, nil + + default: + return nil, errors.New("gob NewTypeObject can't handle type: " + rt.String()) + } + return nil, nil +} + +// isExported reports whether this is an exported - upper case - name. +func isExported(name string) bool { + rune, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(rune) +} + +// getBaseType returns the Gob type describing the given reflect.Type's base type. +// typeLock must be held. +func getBaseType(name string, rt reflect.Type) (gobType, error) { + ut := userType(rt) + return getType(name, ut, ut.base) +} + +// getType returns the Gob type describing the given reflect.Type. +// Should be called only when handling GobEncoders/Decoders, +// which may be pointers. All other types are handled through the +// base type, never a pointer. +// typeLock must be held. +func getType(name string, ut *userTypeInfo, rt reflect.Type) (gobType, error) { + typ, present := types[rt] + if present { + return typ, nil + } + typ, err := newTypeObject(name, ut, rt) + if err == nil { + types[rt] = typ + } + return typ, err +} + +func checkId(want, got typeId) { + if want != got { + fmt.Fprintf(os.Stderr, "checkId: %d should be %d\n", int(got), int(want)) + panic("bootstrap type wrong id: " + got.name() + " " + got.string() + " not " + want.string()) + } +} + +// used for building the basic types; called only from init(). the incoming +// interface always refers to a pointer. +func bootstrapType(name string, e interface{}, expect typeId) typeId { + rt := reflect.TypeOf(e).Elem() + _, present := types[rt] + if present { + panic("bootstrap type already present: " + name + ", " + rt.String()) + } + typ := &CommonType{Name: name} + types[rt] = typ + setTypeId(typ) + checkId(expect, nextId) + userType(rt) // might as well cache it now + return nextId +} + +// Representation of the information we send and receive about this type. +// Each value we send is preceded by its type definition: an encoded int. +// However, the very first time we send the value, we first send the pair +// (-id, wireType). +// For bootstrapping purposes, we assume that the recipient knows how +// to decode a wireType; it is exactly the wireType struct here, interpreted +// using the gob rules for sending a structure, except that we assume the +// ids for wireType and structType etc. are known. The relevant pieces +// are built in encode.go's init() function. +// To maintain binary compatibility, if you extend this type, always put +// the new fields last. +type wireType struct { + ArrayT *arrayType + SliceT *sliceType + StructT *structType + MapT *mapType + GobEncoderT *gobEncoderType +} + +func (w *wireType) string() string { + const unknown = "unknown type" + if w == nil { + return unknown + } + switch { + case w.ArrayT != nil: + return w.ArrayT.Name + case w.SliceT != nil: + return w.SliceT.Name + case w.StructT != nil: + return w.StructT.Name + case w.MapT != nil: + return w.MapT.Name + case w.GobEncoderT != nil: + return w.GobEncoderT.Name + } + return unknown +} + +type typeInfo struct { + id typeId + encoder *encEngine + wire *wireType +} + +var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock + +// typeLock must be held. +func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { + rt := ut.base + if ut.isGobEncoder { + // We want the user type, not the base type. + rt = ut.user + } + info, ok := typeInfoMap[rt] + if ok { + return info, nil + } + info = new(typeInfo) + gt, err := getBaseType(rt.Name(), rt) + if err != nil { + return nil, err + } + info.id = gt.id() + + if ut.isGobEncoder { + userType, err := getType(rt.Name(), ut, rt) + if err != nil { + return nil, err + } + info.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)} + typeInfoMap[ut.user] = info + return info, nil + } + + t := info.id.gobType() + switch typ := rt; typ.Kind() { + case reflect.Array: + info.wire = &wireType{ArrayT: t.(*arrayType)} + case reflect.Map: + info.wire = &wireType{MapT: t.(*mapType)} + case reflect.Slice: + // []byte == []uint8 is a special case handled separately + if typ.Elem().Kind() != reflect.Uint8 { + info.wire = &wireType{SliceT: t.(*sliceType)} + } + case reflect.Struct: + info.wire = &wireType{StructT: t.(*structType)} + } + typeInfoMap[rt] = info + return info, nil +} + +// Called only when a panic is acceptable and unexpected. +func mustGetTypeInfo(rt reflect.Type) *typeInfo { + t, err := getTypeInfo(userType(rt)) + if err != nil { + panic("getTypeInfo: " + err.Error()) + } + return t +} + +// GobEncoder is the interface describing data that provides its own +// representation for encoding values for transmission to a GobDecoder. +// A type that implements GobEncoder and GobDecoder has complete +// control over the representation of its data and may therefore +// contain things such as private fields, channels, and functions, +// which are not usually transmissible in gob streams. +// +// Note: Since gobs can be stored permanently, It is good design +// to guarantee the encoding used by a GobEncoder is stable as the +// software evolves. For instance, it might make sense for GobEncode +// to include a version number in the encoding. +type GobEncoder interface { + // GobEncode returns a byte slice representing the encoding of the + // receiver for transmission to a GobDecoder, usually of the same + // concrete type. + GobEncode() ([]byte, error) +} + +// GobDecoder is the interface describing data that provides its own +// routine for decoding transmitted values sent by a GobEncoder. +type GobDecoder interface { + // GobDecode overwrites the receiver, which must be a pointer, + // with the value represented by the byte slice, which was written + // by GobEncode, usually for the same concrete type. + GobDecode([]byte) error +} + +var ( + nameToConcreteType = make(map[string]reflect.Type) + concreteTypeToName = make(map[reflect.Type]string) +) + +// RegisterName is like Register but uses the provided name rather than the +// type's default. +func RegisterName(name string, value interface{}) { + if name == "" { + // reserved for nil + panic("attempt to register empty name") + } + ut := userType(reflect.TypeOf(value)) + // Check for incompatible duplicates. The name must refer to the + // same user type, and vice versa. + if t, ok := nameToConcreteType[name]; ok && t != ut.user { + panic(fmt.Sprintf("gob: registering duplicate types for %q: %s != %s", name, t, ut.user)) + } + if n, ok := concreteTypeToName[ut.base]; ok && n != name { + panic(fmt.Sprintf("gob: registering duplicate names for %s: %q != %q", ut.user, n, name)) + } + // Store the name and type provided by the user.... + nameToConcreteType[name] = reflect.TypeOf(value) + // but the flattened type in the type table, since that's what decode needs. + concreteTypeToName[ut.base] = name +} + +// Register records a type, identified by a value for that type, under its +// internal type name. That name will identify the concrete type of a value +// sent or received as an interface variable. Only types that will be +// transferred as implementations of interface values need to be registered. +// Expecting to be used only during initialization, it panics if the mapping +// between types and names is not a bijection. +func Register(value interface{}) { + // Default to printed representation for unnamed types + rt := reflect.TypeOf(value) + name := rt.String() + + // But for named types (or pointers to them), qualify with import path. + // Dereference one pointer looking for a named type. + star := "" + if rt.Name() == "" { + if pt := rt; pt.Kind() == reflect.Ptr { + star = "*" + rt = pt + } + } + if rt.Name() != "" { + if rt.PkgPath() == "" { + name = star + rt.Name() + } else { + name = star + rt.PkgPath() + "." + rt.Name() + } + } + + RegisterName(name, value) +} + +func registerBasics() { + Register(int(0)) + Register(int8(0)) + Register(int16(0)) + Register(int32(0)) + Register(int64(0)) + Register(uint(0)) + Register(uint8(0)) + Register(uint16(0)) + Register(uint32(0)) + Register(uint64(0)) + Register(float32(0)) + Register(float64(0)) + Register(complex64(0i)) + Register(complex128(0i)) + Register(uintptr(0)) + Register(false) + Register("") + Register([]byte(nil)) + Register([]int(nil)) + Register([]int8(nil)) + Register([]int16(nil)) + Register([]int32(nil)) + Register([]int64(nil)) + Register([]uint(nil)) + Register([]uint8(nil)) + Register([]uint16(nil)) + Register([]uint32(nil)) + Register([]uint64(nil)) + Register([]float32(nil)) + Register([]float64(nil)) + Register([]complex64(nil)) + Register([]complex128(nil)) + Register([]uintptr(nil)) + Register([]bool(nil)) + Register([]string(nil)) +} diff --git a/src/pkg/encoding/gob/type_test.go b/src/pkg/encoding/gob/type_test.go new file mode 100644 index 000000000..42bdb4cf7 --- /dev/null +++ b/src/pkg/encoding/gob/type_test.go @@ -0,0 +1,161 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gob + +import ( + "reflect" + "testing" +) + +type typeT struct { + id typeId + str string +} + +var basicTypes = []typeT{ + {tBool, "bool"}, + {tInt, "int"}, + {tUint, "uint"}, + {tFloat, "float"}, + {tBytes, "bytes"}, + {tString, "string"}, +} + +func getTypeUnlocked(name string, rt reflect.Type) gobType { + typeLock.Lock() + defer typeLock.Unlock() + t, err := getBaseType(name, rt) + if err != nil { + panic("getTypeUnlocked: " + err.Error()) + } + return t +} + +// Sanity checks +func TestBasic(t *testing.T) { + for _, tt := range basicTypes { + if tt.id.string() != tt.str { + t.Errorf("checkType: expected %q got %s", tt.str, tt.id.string()) + } + if tt.id == 0 { + t.Errorf("id for %q is zero", tt.str) + } + } +} + +// Reregister some basic types to check registration is idempotent. +func TestReregistration(t *testing.T) { + newtyp := getTypeUnlocked("int", reflect.TypeOf(int(0))) + if newtyp != tInt.gobType() { + t.Errorf("reregistration of %s got new type", newtyp.string()) + } + newtyp = getTypeUnlocked("uint", reflect.TypeOf(uint(0))) + if newtyp != tUint.gobType() { + t.Errorf("reregistration of %s got new type", newtyp.string()) + } + newtyp = getTypeUnlocked("string", reflect.TypeOf("hello")) + if newtyp != tString.gobType() { + t.Errorf("reregistration of %s got new type", newtyp.string()) + } +} + +func TestArrayType(t *testing.T) { + var a3 [3]int + a3int := getTypeUnlocked("foo", reflect.TypeOf(a3)) + newa3int := getTypeUnlocked("bar", reflect.TypeOf(a3)) + if a3int != newa3int { + t.Errorf("second registration of [3]int creates new type") + } + var a4 [4]int + a4int := getTypeUnlocked("goo", reflect.TypeOf(a4)) + if a3int == a4int { + t.Errorf("registration of [3]int creates same type as [4]int") + } + var b3 [3]bool + a3bool := getTypeUnlocked("", reflect.TypeOf(b3)) + if a3int == a3bool { + t.Errorf("registration of [3]bool creates same type as [3]int") + } + str := a3bool.string() + expected := "[3]bool" + if str != expected { + t.Errorf("array printed as %q; expected %q", str, expected) + } +} + +func TestSliceType(t *testing.T) { + var s []int + sint := getTypeUnlocked("slice", reflect.TypeOf(s)) + var news []int + newsint := getTypeUnlocked("slice1", reflect.TypeOf(news)) + if sint != newsint { + t.Errorf("second registration of []int creates new type") + } + var b []bool + sbool := getTypeUnlocked("", reflect.TypeOf(b)) + if sbool == sint { + t.Errorf("registration of []bool creates same type as []int") + } + str := sbool.string() + expected := "[]bool" + if str != expected { + t.Errorf("slice printed as %q; expected %q", str, expected) + } +} + +func TestMapType(t *testing.T) { + var m map[string]int + mapStringInt := getTypeUnlocked("map", reflect.TypeOf(m)) + var newm map[string]int + newMapStringInt := getTypeUnlocked("map1", reflect.TypeOf(newm)) + if mapStringInt != newMapStringInt { + t.Errorf("second registration of map[string]int creates new type") + } + var b map[string]bool + mapStringBool := getTypeUnlocked("", reflect.TypeOf(b)) + if mapStringBool == mapStringInt { + t.Errorf("registration of map[string]bool creates same type as map[string]int") + } + str := mapStringBool.string() + expected := "map[string]bool" + if str != expected { + t.Errorf("map printed as %q; expected %q", str, expected) + } +} + +type Bar struct { + X string +} + +// This structure has pointers and refers to itself, making it a good test case. +type Foo struct { + A int + B int32 // will become int + C string + D []byte + E *float64 // will become float64 + F ****float64 // will become float64 + G *Bar + H *Bar // should not interpolate the definition of Bar again + I *Foo // will not explode +} + +func TestStructType(t *testing.T) { + sstruct := getTypeUnlocked("Foo", reflect.TypeOf(Foo{})) + str := sstruct.string() + // If we can print it correctly, we built it correctly. + expected := "Foo = struct { A int; B int; C string; D bytes; E float; F float; G Bar = struct { X string; }; H Bar; I Foo; }" + if str != expected { + t.Errorf("struct printed as %q; expected %q", str, expected) + } +} + +// Should be OK to register the same type multiple times, as long as they're +// at the same level of indirection. +func TestRegistration(t *testing.T) { + type T struct{ a int } + Register(new(T)) + Register(new(T)) +} diff --git a/src/pkg/encoding/hex/Makefile b/src/pkg/encoding/hex/Makefile deleted file mode 100644 index 22049f43b..000000000 --- a/src/pkg/encoding/hex/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../Make.inc - -TARG=encoding/hex -GOFILES=\ - hex.go\ - -include ../../../Make.pkg diff --git a/src/pkg/encoding/hex/hex.go b/src/pkg/encoding/hex/hex.go index e7ea8b0f2..167d00e03 100644 --- a/src/pkg/encoding/hex/hex.go +++ b/src/pkg/encoding/hex/hex.go @@ -7,9 +7,9 @@ package hex import ( "bytes" + "errors" + "fmt" "io" - "os" - "strconv" ) const hextable = "0123456789abcdef" @@ -30,16 +30,14 @@ func Encode(dst, src []byte) int { return len(src) * 2 } -// OddLengthInputError results from decoding an odd length slice. -type OddLengthInputError struct{} +// ErrLength results from decoding an odd length slice. +var ErrLength = errors.New("encoding/hex: odd length hex string") -func (OddLengthInputError) String() string { return "odd length hex string" } +// InvalidByteError values describe errors resulting from an invalid byte in a hex string. +type InvalidByteError byte -// InvalidHexCharError results from finding an invalid character in a hex string. -type InvalidHexCharError byte - -func (e InvalidHexCharError) String() string { - return "invalid hex char: " + strconv.Itoa(int(e)) +func (e InvalidByteError) Error() string { + return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e)) } func DecodedLen(x int) int { return x / 2 } @@ -47,21 +45,20 @@ func DecodedLen(x int) int { return x / 2 } // Decode decodes src into DecodedLen(len(src)) bytes, returning the actual // number of bytes written to dst. // -// If Decode encounters invalid input, it returns an OddLengthInputError or an -// InvalidHexCharError. -func Decode(dst, src []byte) (int, os.Error) { +// If Decode encounters invalid input, it returns an error describing the failure. +func Decode(dst, src []byte) (int, error) { if len(src)%2 == 1 { - return 0, OddLengthInputError{} + return 0, ErrLength } for i := 0; i < len(src)/2; i++ { a, ok := fromHexChar(src[i*2]) if !ok { - return 0, InvalidHexCharError(src[i*2]) + return 0, InvalidByteError(src[i*2]) } b, ok := fromHexChar(src[i*2+1]) if !ok { - return 0, InvalidHexCharError(src[i*2+1]) + return 0, InvalidByteError(src[i*2+1]) } dst[i] = (a << 4) | b } @@ -91,7 +88,7 @@ func EncodeToString(src []byte) string { } // DecodeString returns the bytes represented by the hexadecimal string s. -func DecodeString(s string) ([]byte, os.Error) { +func DecodeString(s string) ([]byte, error) { src := []byte(s) dst := make([]byte, DecodedLen(len(src))) _, err := Decode(dst, src) @@ -104,8 +101,8 @@ func DecodeString(s string) ([]byte, os.Error) { // Dump returns a string that contains a hex dump of the given data. The format // of the hex dump matches the output of `hexdump -C` on the command line. func Dump(data []byte) string { - buf := bytes.NewBuffer(nil) - dumper := Dumper(buf) + var buf bytes.Buffer + dumper := Dumper(&buf) dumper.Write(data) dumper.Close() return string(buf.Bytes()) @@ -133,7 +130,7 @@ func toChar(b byte) byte { return b } -func (h *dumper) Write(data []byte) (n int, err os.Error) { +func (h *dumper) Write(data []byte) (n int, err error) { // Output lines look like: // 00000010 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d |./0123456789:;<=| // ^ offset ^ extra space ^ ASCII of line. @@ -185,7 +182,7 @@ func (h *dumper) Write(data []byte) (n int, err os.Error) { return } -func (h *dumper) Close() (err os.Error) { +func (h *dumper) Close() (err error) { // See the comments in Write() for the details of this format. if h.used == 0 { return diff --git a/src/pkg/encoding/hex/hex_test.go b/src/pkg/encoding/hex/hex_test.go index 8e1838e51..456f9eac7 100644 --- a/src/pkg/encoding/hex/hex_test.go +++ b/src/pkg/encoding/hex/hex_test.go @@ -9,141 +9,98 @@ import ( "testing" ) -type encodeTest struct { - in, out []byte +type encDecTest struct { + enc string + dec []byte } -var encodeTests = []encodeTest{ - {[]byte{}, []byte{}}, - {[]byte{0x01}, []byte{'0', '1'}}, - {[]byte{0xff}, []byte{'f', 'f'}}, - {[]byte{0xff, 00}, []byte{'f', 'f', '0', '0'}}, - {[]byte{0}, []byte{'0', '0'}}, - {[]byte{1}, []byte{'0', '1'}}, - {[]byte{2}, []byte{'0', '2'}}, - {[]byte{3}, []byte{'0', '3'}}, - {[]byte{4}, []byte{'0', '4'}}, - {[]byte{5}, []byte{'0', '5'}}, - {[]byte{6}, []byte{'0', '6'}}, - {[]byte{7}, []byte{'0', '7'}}, - {[]byte{8}, []byte{'0', '8'}}, - {[]byte{9}, []byte{'0', '9'}}, - {[]byte{10}, []byte{'0', 'a'}}, - {[]byte{11}, []byte{'0', 'b'}}, - {[]byte{12}, []byte{'0', 'c'}}, - {[]byte{13}, []byte{'0', 'd'}}, - {[]byte{14}, []byte{'0', 'e'}}, - {[]byte{15}, []byte{'0', 'f'}}, +var encDecTests = []encDecTest{ + {"", []byte{}}, + {"0001020304050607", []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + {"08090a0b0c0d0e0f", []byte{8, 9, 10, 11, 12, 13, 14, 15}}, + {"f0f1f2f3f4f5f6f7", []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7}}, + {"f8f9fafbfcfdfeff", []byte{0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}}, + {"67", []byte{'g'}}, + {"e3a1", []byte{0xe3, 0xa1}}, } func TestEncode(t *testing.T) { - for i, test := range encodeTests { - dst := make([]byte, EncodedLen(len(test.in))) - n := Encode(dst, test.in) + for i, test := range encDecTests { + dst := make([]byte, EncodedLen(len(test.dec))) + n := Encode(dst, test.dec) if n != len(dst) { t.Errorf("#%d: bad return value: got: %d want: %d", i, n, len(dst)) } - if bytes.Compare(dst, test.out) != 0 { - t.Errorf("#%d: got: %#v want: %#v", i, dst, test.out) + if string(dst) != test.enc { + t.Errorf("#%d: got: %#v want: %#v", i, dst, test.enc) } } } -type decodeTest struct { - in, out []byte - ok bool -} - -var decodeTests = []decodeTest{ - {[]byte{}, []byte{}, true}, - {[]byte{'0'}, []byte{}, false}, - {[]byte{'0', 'g'}, []byte{}, false}, - {[]byte{'0', '\x01'}, []byte{}, false}, - {[]byte{'0', '0'}, []byte{0}, true}, - {[]byte{'0', '1'}, []byte{1}, true}, - {[]byte{'0', '2'}, []byte{2}, true}, - {[]byte{'0', '3'}, []byte{3}, true}, - {[]byte{'0', '4'}, []byte{4}, true}, - {[]byte{'0', '5'}, []byte{5}, true}, - {[]byte{'0', '6'}, []byte{6}, true}, - {[]byte{'0', '7'}, []byte{7}, true}, - {[]byte{'0', '8'}, []byte{8}, true}, - {[]byte{'0', '9'}, []byte{9}, true}, - {[]byte{'0', 'a'}, []byte{10}, true}, - {[]byte{'0', 'b'}, []byte{11}, true}, - {[]byte{'0', 'c'}, []byte{12}, true}, - {[]byte{'0', 'd'}, []byte{13}, true}, - {[]byte{'0', 'e'}, []byte{14}, true}, - {[]byte{'0', 'f'}, []byte{15}, true}, - {[]byte{'0', 'A'}, []byte{10}, true}, - {[]byte{'0', 'B'}, []byte{11}, true}, - {[]byte{'0', 'C'}, []byte{12}, true}, - {[]byte{'0', 'D'}, []byte{13}, true}, - {[]byte{'0', 'E'}, []byte{14}, true}, - {[]byte{'0', 'F'}, []byte{15}, true}, -} - func TestDecode(t *testing.T) { - for i, test := range decodeTests { - dst := make([]byte, DecodedLen(len(test.in))) - n, err := Decode(dst, test.in) - if err == nil && n != len(dst) { + for i, test := range encDecTests { + dst := make([]byte, DecodedLen(len(test.enc))) + n, err := Decode(dst, []byte(test.enc)) + if err != nil { t.Errorf("#%d: bad return value: got:%d want:%d", i, n, len(dst)) - } - if test.ok != (err == nil) { - t.Errorf("#%d: unexpected err value: %s", i, err) - } - if err == nil && bytes.Compare(dst, test.out) != 0 { - t.Errorf("#%d: got: %#v want: %#v", i, dst, test.out) + } else if !bytes.Equal(dst, test.dec) { + t.Errorf("#%d: got: %#v want: %#v", i, dst, test.dec) } } } -type encodeStringTest struct { - in []byte - out string -} - -var encodeStringTests = []encodeStringTest{ - {[]byte{}, ""}, - {[]byte{0}, "00"}, - {[]byte{0, 1}, "0001"}, - {[]byte{0, 1, 255}, "0001ff"}, +func TestEncodeToString(t *testing.T) { + for i, test := range encDecTests { + s := EncodeToString(test.dec) + if s != test.enc { + t.Errorf("#%d got:%s want:%s", i, s, test.enc) + } + } } -func TestEncodeToString(t *testing.T) { - for i, test := range encodeStringTests { - s := EncodeToString(test.in) - if s != test.out { - t.Errorf("#%d got:%s want:%s", i, s, test.out) +func TestDecodeString(t *testing.T) { + for i, test := range encDecTests { + dst, err := DecodeString(test.enc) + if err != nil { + t.Errorf("#%d: unexpected err value: %s", i, err) + continue + } + if bytes.Compare(dst, test.dec) != 0 { + t.Errorf("#%d: got: %#v want: #%v", i, dst, test.dec) } } } -type decodeStringTest struct { +type errTest struct { in string - out []byte - ok bool + err string } -var decodeStringTests = []decodeStringTest{ - {"", []byte{}, true}, - {"0", []byte{}, false}, - {"00", []byte{0}, true}, - {"0\x01", []byte{}, false}, - {"0g", []byte{}, false}, - {"00ff00", []byte{0, 255, 0}, true}, - {"0000ff", []byte{0, 0, 255}, true}, +var errTests = []errTest{ + {"0", "encoding/hex: odd length hex string"}, + {"0g", "encoding/hex: invalid byte: U+0067 'g'"}, + {"0\x01", "encoding/hex: invalid byte: U+0001"}, } -func TestDecodeString(t *testing.T) { - for i, test := range decodeStringTests { - dst, err := DecodeString(test.in) - if test.ok != (err == nil) { - t.Errorf("#%d: unexpected err value: %s", i, err) +func TestInvalidErr(t *testing.T) { + for i, test := range errTests { + dst := make([]byte, DecodedLen(len(test.in))) + _, err := Decode(dst, []byte(test.in)) + if err == nil { + t.Errorf("#%d: expected error; got none", i) + } else if err.Error() != test.err { + t.Errorf("#%d: got: %v want: %v", i, err, test.err) } - if err == nil && bytes.Compare(dst, test.out) != 0 { - t.Errorf("#%d: got: %#v want: #%v", i, dst, test.out) + } +} + +func TestInvalidStringErr(t *testing.T) { + for i, test := range errTests { + _, err := DecodeString(test.in) + if err == nil { + t.Errorf("#%d: expected error; got none", i) + } else if err.Error() != test.err { + t.Errorf("#%d: got: %v want: %v", i, err, test.err) } } } @@ -155,8 +112,8 @@ func TestDumper(t *testing.T) { } for stride := 1; stride < len(in); stride++ { - out := bytes.NewBuffer(nil) - dumper := Dumper(out) + var out bytes.Buffer + dumper := Dumper(&out) done := 0 for done < len(in) { todo := done + stride diff --git a/src/pkg/encoding/json/bench_test.go b/src/pkg/encoding/json/bench_test.go new file mode 100644 index 000000000..333c1c0ce --- /dev/null +++ b/src/pkg/encoding/json/bench_test.go @@ -0,0 +1,157 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Large data benchmark. +// The JSON data is a summary of agl's changes in the +// go, webkit, and chromium open source projects. +// We benchmark converting between the JSON form +// and in-memory data structures. + +package json + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + "os" + "testing" +) + +type codeResponse struct { + Tree *codeNode `json:"tree"` + Username string `json:"username"` +} + +type codeNode struct { + Name string `json:"name"` + Kids []*codeNode `json:"kids"` + CLWeight float64 `json:"cl_weight"` + Touches int `json:"touches"` + MinT int64 `json:"min_t"` + MaxT int64 `json:"max_t"` + MeanT int64 `json:"mean_t"` +} + +var codeJSON []byte +var codeStruct codeResponse + +func codeInit() { + f, err := os.Open("testdata/code.json.gz") + if err != nil { + panic(err) + } + defer f.Close() + gz, err := gzip.NewReader(f) + if err != nil { + panic(err) + } + data, err := ioutil.ReadAll(gz) + if err != nil { + panic(err) + } + + codeJSON = data + + if err := Unmarshal(codeJSON, &codeStruct); err != nil { + panic("unmarshal code.json: " + err.Error()) + } + + if data, err = Marshal(&codeStruct); err != nil { + panic("marshal code.json: " + err.Error()) + } + + if !bytes.Equal(data, codeJSON) { + println("different lengths", len(data), len(codeJSON)) + for i := 0; i < len(data) && i < len(codeJSON); i++ { + if data[i] != codeJSON[i] { + println("re-marshal: changed at byte", i) + println("orig: ", string(codeJSON[i-10:i+10])) + println("new: ", string(data[i-10:i+10])) + break + } + } + panic("re-marshal code.json: different result") + } +} + +func BenchmarkCodeEncoder(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + enc := NewEncoder(ioutil.Discard) + for i := 0; i < b.N; i++ { + if err := enc.Encode(&codeStruct); err != nil { + b.Fatal("Encode:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeMarshal(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + for i := 0; i < b.N; i++ { + if _, err := Marshal(&codeStruct); err != nil { + b.Fatal("Marshal:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeDecoder(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + var buf bytes.Buffer + dec := NewDecoder(&buf) + var r codeResponse + for i := 0; i < b.N; i++ { + buf.Write(codeJSON) + // hide EOF + buf.WriteByte('\n') + buf.WriteByte('\n') + buf.WriteByte('\n') + if err := dec.Decode(&r); err != nil { + b.Fatal("Decode:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeUnmarshal(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + for i := 0; i < b.N; i++ { + var r codeResponse + if err := Unmarshal(codeJSON, &r); err != nil { + b.Fatal("Unmmarshal:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeUnmarshalReuse(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + var r codeResponse + for i := 0; i < b.N; i++ { + if err := Unmarshal(codeJSON, &r); err != nil { + b.Fatal("Unmmarshal:", err) + } + } + b.SetBytes(int64(len(codeJSON))) +} diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go new file mode 100644 index 000000000..110c6fd62 --- /dev/null +++ b/src/pkg/encoding/json/decode.go @@ -0,0 +1,979 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Represents JSON data structure using native Go types: booleans, floats, +// strings, arrays, and maps. + +package json + +import ( + "encoding/base64" + "errors" + "fmt" + "reflect" + "runtime" + "strconv" + "strings" + "unicode" + "unicode/utf16" + "unicode/utf8" +) + +// Unmarshal parses the JSON-encoded data and stores the result +// in the value pointed to by v. +// +// Unmarshal uses the inverse of the encodings that +// Marshal uses, allocating maps, slices, and pointers as necessary, +// with the following additional rules: +// +// To unmarshal JSON into a pointer, Unmarshal first handles the case of +// the JSON being the JSON literal null. In that case, Unmarshal sets +// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into +// the value pointed at by the pointer. If the pointer is nil, Unmarshal +// allocates a new value for it to point to. +// +// To unmarshal JSON into an interface value, Unmarshal unmarshals +// the JSON into the concrete value contained in the interface value. +// If the interface value is nil, that is, has no concrete value stored in it, +// Unmarshal stores one of these in the interface value: +// +// bool, for JSON booleans +// float64, for JSON numbers +// string, for JSON strings +// []interface{}, for JSON arrays +// map[string]interface{}, for JSON objects +// nil for JSON null +// +// If a JSON value is not appropriate for a given target type, +// or if a JSON number overflows the target type, Unmarshal +// skips that field and completes the unmarshalling as best it can. +// If no more serious errors are encountered, Unmarshal returns +// an UnmarshalTypeError describing the earliest such error. +// +func Unmarshal(data []byte, v interface{}) error { + d := new(decodeState).init(data) + + // Quick check for well-formedness. + // Avoids filling out half a data structure + // before discovering a JSON syntax error. + err := checkValid(data, &d.scan) + if err != nil { + return err + } + + return d.unmarshal(v) +} + +// Unmarshaler is the interface implemented by objects +// that can unmarshal a JSON description of themselves. +// The input can be assumed to be a valid JSON object +// encoding. UnmarshalJSON must copy the JSON data +// if it wishes to retain the data after returning. +type Unmarshaler interface { + UnmarshalJSON([]byte) error +} + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to +} + +func (e *UnmarshalTypeError) Error() string { + return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +type UnmarshalFieldError struct { + Key string + Type reflect.Type + Field reflect.StructField +} + +func (e *UnmarshalFieldError) Error() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) Error() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return "json: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "json: Unmarshal(nil " + e.Type.String() + ")" +} + +func (d *decodeState) unmarshal(v interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(error) + } + }() + + rv := reflect.ValueOf(v) + pv := rv + if pv.Kind() != reflect.Ptr || pv.IsNil() { + return &InvalidUnmarshalError{reflect.TypeOf(v)} + } + + d.scan.reset() + // We decode rv not pv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) + return d.savedError +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + savedError error + tempstr string // scratch space to avoid some allocations +} + +// errPhase is used for errors that should not happen unless +// there is a bug in the JSON decoder or something is editing +// the data slice while the decoder executes. +var errPhase = errors.New("JSON decoder out of sync - data changing underfoot?") + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + d.savedError = nil + return d +} + +// error aborts the decoding by panicking with err. +func (d *decodeState) error(err error) { + panic(err) +} + +// saveError saves the first err it is called with, +// for reporting at the end of the unmarshal. +func (d *decodeState) saveError(err error) { + if d.savedError == nil { + d.savedError = err + } +} + +// next cuts off and returns the next full JSON value in d.data[d.off:]. +// The next value is known to be an object or array, not a literal. +func (d *decodeState) next() []byte { + c := d.data[d.off] + item, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // Our scanner has seen the opening brace/bracket + // and thinks we're still in the middle of the object. + // invent a closing brace/bracket to get it out. + if c == '{' { + d.scan.step(&d.scan, '}') + } else { + d.scan.step(&d.scan, ']') + } + + return item +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +// It updates d.off and returns the new scan code. +func (d *decodeState) scanWhile(op int) int { + var newOp int + for { + if d.off >= len(d.data) { + newOp = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } else { + c := int(d.data[d.off]) + d.off++ + newOp = d.scan.step(&d.scan, c) + } + if newOp != op { + break + } + } + return newOp +} + +// value decodes a JSON value from d.data[d.off:] into the value. +// it updates d.off to point past the decoded value. +func (d *decodeState) value(v reflect.Value) { + if !v.IsValid() { + _, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // d.scan thinks we're still at the beginning of the item. + // Feed in an empty string - the shortest, simplest value - + // so that it knows we got to the end of the value. + if d.scan.redo { + // rewind. + d.scan.redo = false + d.scan.step = stateBeginValue + } + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + return + } + + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(v) + + case scanBeginObject: + d.object(v) + + case scanBeginLiteral: + d.literal(v) + } +} + +// indirect walks down v allocating pointers as needed, +// until it gets to a non-pointer. +// if it encounters an Unmarshaler, indirect stops and returns that. +// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. +func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) { + // If v is a named type and is addressable, + // start with its address, so that if the type has pointer methods, + // we find them. + if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { + v = v.Addr() + } + for { + var isUnmarshaler bool + if v.Type().NumMethod() > 0 { + // Remember that this is an unmarshaler, + // but wait to return it until after allocating + // the pointer (if necessary). + _, isUnmarshaler = v.Interface().(Unmarshaler) + } + + if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() { + v = iv.Elem() + continue + } + + pv := v + if pv.Kind() != reflect.Ptr { + break + } + + if pv.Elem().Kind() != reflect.Ptr && decodingNull && pv.CanSet() { + return nil, pv + } + if pv.IsNil() { + pv.Set(reflect.New(pv.Type().Elem())) + } + if isUnmarshaler { + // Using v.Interface().(Unmarshaler) + // here means that we have to use a pointer + // as the struct field. We cannot use a value inside + // a pointer to a struct, because in that case + // v.Interface() is the value (x.f) not the pointer (&x.f). + // This is an unfortunate consequence of reflect. + // An alternative would be to look up the + // UnmarshalJSON method and return a FuncValue. + return v.Interface().(Unmarshaler), reflect.Value{} + } + v = pv.Elem() + } + return nil, v +} + +// array consumes an array from d.data[d.off-1:], decoding into the value v. +// the first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Check type of target. + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"array", v.Type()}) + d.off-- + d.next() + return + case reflect.Interface: + // Decoding into nil interface? Switch to non-reflect code. + v.Set(reflect.ValueOf(d.arrayInterface())) + return + case reflect.Array: + case reflect.Slice: + break + } + + i := 0 + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + // Get element of array, growing if necessary. + if v.Kind() == reflect.Slice { + // Grow slice if necessary + if i >= v.Cap() { + newcap := v.Cap() + v.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) + reflect.Copy(newv, v) + v.Set(newv) + } + if i >= v.Len() { + v.SetLen(i + 1) + } + } + + if i < v.Len() { + // Decode into element. + d.value(v.Index(i)) + } else { + // Ran out of fixed array: skip. + d.value(reflect.Value{}) + } + i++ + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + + if i < v.Len() { + if v.Kind() == reflect.Array { + // Array. Zero the rest. + z := reflect.Zero(v.Type().Elem()) + for ; i < v.Len(); i++ { + v.Index(i).Set(z) + } + } else { + v.SetLen(i) + } + } + if i == 0 && v.Kind() == reflect.Slice { + v.Set(reflect.MakeSlice(v.Type(), 0, 0)) + } +} + +// object consumes an object from d.data[d.off-1:], decoding into the value v. +// the first byte of the object ('{') has been read already. +func (d *decodeState) object(v reflect.Value) { + // Check for unmarshaler. + unmarshaler, pv := d.indirect(v, false) + if unmarshaler != nil { + d.off-- + err := unmarshaler.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + iv := v + if iv.Kind() == reflect.Interface { + iv.Set(reflect.ValueOf(d.objectInterface())) + return + } + + // Check type of target: struct or map[string]T + var ( + mv reflect.Value + sv reflect.Value + ) + switch v.Kind() { + case reflect.Map: + // map must have string type + t := v.Type() + if t.Key() != reflect.TypeOf("") { + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + break + } + mv = v + if mv.IsNil() { + mv.Set(reflect.MakeMap(t)) + } + case reflect.Struct: + sv = v + default: + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + } + + if !mv.IsValid() && !sv.IsValid() { + d.off-- + d.next() // skip over { } in input + return + } + + var mapElem reflect.Value + + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Figure out field corresponding to key. + var subv reflect.Value + destring := false // whether the value is wrapped in a string to be decoded first + + if mv.IsValid() { + elemType := mv.Type().Elem() + if !mapElem.IsValid() { + mapElem = reflect.New(elemType).Elem() + } else { + mapElem.Set(reflect.Zero(elemType)) + } + subv = mapElem + } else { + var f reflect.StructField + var ok bool + st := sv.Type() + for i := 0; i < sv.NumField(); i++ { + sf := st.Field(i) + tag := sf.Tag.Get("json") + if tag == "-" { + // Pretend this field doesn't exist. + continue + } + if sf.Anonymous { + // Pretend this field doesn't exist, + // so that we can do a good job with + // these in a later version. + continue + } + // First, tag match + tagName, _ := parseTag(tag) + if tagName == key { + f = sf + ok = true + break // no better match possible + } + // Second, exact field name match + if sf.Name == key { + f = sf + ok = true + } + // Third, case-insensitive field name match, + // but only if a better match hasn't already been seen + if !ok && strings.EqualFold(sf.Name, key) { + f = sf + ok = true + } + } + + // Extract value; name must be exported. + if ok { + if f.PkgPath != "" { + d.saveError(&UnmarshalFieldError{key, st, f}) + } else { + subv = sv.FieldByIndex(f.Index) + } + _, opts := parseTag(f.Tag.Get("json")) + destring = opts.Contains("string") + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + if destring { + d.value(reflect.ValueOf(&d.tempstr)) + d.literalStore([]byte(d.tempstr), subv, true) + } else { + d.value(subv) + } + // Write value back to map; + // if using struct, subv points into struct already. + if mv.IsValid() { + mv.SetMapIndex(reflect.ValueOf(key), subv) + } + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } +} + +// literal consumes a literal from d.data[d.off-1:], decoding into the value v. +// The first byte of the literal has been read already +// (that's how the caller knows it's a literal). +func (d *decodeState) literal(v reflect.Value) { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + + d.literalStore(d.data[start:d.off], v, false) +} + +// literalStore decodes a literal stored in item into v. +// +// fromQuoted indicates whether this literal came from unwrapping a +// string from the ",string" struct tag option. this is used only to +// produce more helpful error messages. +func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { + // Check for unmarshaler. + wantptr := item[0] == 'n' // null + unmarshaler, pv := d.indirect(v, wantptr) + if unmarshaler != nil { + err := unmarshaler.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + v = pv + + switch c := item[0]; c { + case 'n': // null + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"null", v.Type()}) + case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + v.Set(reflect.Zero(v.Type())) + } + + case 't', 'f': // true, false + value := c == 't' + switch v.Kind() { + default: + if fromQuoted { + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type()}) + } + case reflect.Bool: + v.SetBool(value) + case reflect.Interface: + v.Set(reflect.ValueOf(value)) + } + + case '"': // string + s, ok := unquoteBytes(item) + if !ok { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + case reflect.Slice: + if v.Type() != byteSliceType { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + break + } + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + d.saveError(err) + break + } + v.Set(reflect.ValueOf(b[0:n])) + case reflect.String: + v.SetString(string(s)) + case reflect.Interface: + v.Set(reflect.ValueOf(string(s))) + } + + default: // number + if c != '-' && (c < '0' || c > '9') { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + s := string(item) + switch v.Kind() { + default: + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(&UnmarshalTypeError{"number", v.Type()}) + } + case reflect.Interface: + n, err := strconv.ParseFloat(s, 64) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.Set(reflect.ValueOf(n)) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.ParseInt(s, 10, 64) + if err != nil || v.OverflowInt(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetInt(n) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.ParseUint(s, 10, 64) + if err != nil || v.OverflowUint(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetUint(n) + + case reflect.Float32, reflect.Float64: + n, err := strconv.ParseFloat(s, v.Type().Bits()) + if err != nil || v.OverflowFloat(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + break + } + v.SetFloat(n) + } + } +} + +// The xxxInterface routines build up a value to be stored +// in an empty interface. They are not strictly necessary, +// but they avoid the weight of reflection in this common case. + +// valueInterface is like value but returns interface{} +func (d *decodeState) valueInterface() interface{} { + switch d.scanWhile(scanSkipSpace) { + default: + d.error(errPhase) + case scanBeginArray: + return d.arrayInterface() + case scanBeginObject: + return d.objectInterface() + case scanBeginLiteral: + return d.literalInterface() + } + panic("unreachable") +} + +// arrayInterface is like array but returns []interface{}. +func (d *decodeState) arrayInterface() []interface{} { + var v []interface{} + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + v = append(v, d.valueInterface()) + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + return v +} + +// objectInterface is like object but returns map[string]interface{}. +func (d *decodeState) objectInterface() map[string]interface{} { + m := make(map[string]interface{}) + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + m[key] = d.valueInterface() + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } + return m +} + +// literalInterface is like literal but returns an interface value. +func (d *decodeState) literalInterface() interface{} { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + switch c := item[0]; c { + case 'n': // null + return nil + + case 't', 'f': // true, false + return c == 't' + + case '"': // string + s, ok := unquote(item) + if !ok { + d.error(errPhase) + } + return s + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + n, err := strconv.ParseFloat(string(item), 64) + if err != nil { + d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.TypeOf(0.0)}) + } + return n + } + panic("unreachable") +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) rune { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + r, err := strconv.ParseUint(string(s[2:6]), 16, 64) + if err != nil { + return -1 + } + return rune(r) +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rr := getu4(s[r:]) + if rr < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rr) { + rr1 := getu4(s[r:]) + if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rr = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rr) + } + + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + // Coerce to well-formed UTF-8. + default: + rr, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rr) + } + } + return b[0:w], true +} + +// The following is issue 3069. + +// BUG(rsc): This package ignores anonymous (embedded) struct fields +// during encoding and decoding. A future version may assign meaning +// to them. To force an anonymous field to be ignored in all future +// versions of this package, use an explicit `json:"-"` tag in the struct +// definition. diff --git a/src/pkg/encoding/json/decode_test.go b/src/pkg/encoding/json/decode_test.go new file mode 100644 index 000000000..d758758d9 --- /dev/null +++ b/src/pkg/encoding/json/decode_test.go @@ -0,0 +1,640 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package json + +import ( + "bytes" + "fmt" + "reflect" + "strings" + "testing" +) + +type T struct { + X string + Y int + Z int `json:"-"` +} + +type tx struct { + x int +} + +var txType = reflect.TypeOf((*tx)(nil)).Elem() + +// A type that can unmarshal itself. + +type unmarshaler struct { + T bool +} + +func (u *unmarshaler) UnmarshalJSON(b []byte) error { + *u = unmarshaler{true} // All we need to see that UnmarshalJson is called. + return nil +} + +type ustruct struct { + M unmarshaler +} + +var ( + um0, um1 unmarshaler // target2 of unmarshaling + ump = &um1 + umtrue = unmarshaler{true} + umslice = []unmarshaler{{true}} + umslicep = new([]unmarshaler) + umstruct = ustruct{unmarshaler{true}} +) + +type unmarshalTest struct { + in string + ptr interface{} + out interface{} + err error +} + +var unmarshalTests = []unmarshalTest{ + // basic types + {`true`, new(bool), true, nil}, + {`1`, new(int), 1, nil}, + {`1.2`, new(float64), 1.2, nil}, + {`-5`, new(int16), int16(-5), nil}, + {`"a\u1234"`, new(string), "a\u1234", nil}, + {`"http:\/\/"`, new(string), "http://", nil}, + {`"g-clef: \uD834\uDD1E"`, new(string), "g-clef: \U0001D11E", nil}, + {`"invalid: \uD834x\uDD1E"`, new(string), "invalid: \uFFFDx\uFFFD", nil}, + {"null", new(interface{}), nil, nil}, + {`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.TypeOf("")}}, + {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}}, + + // Z has a "-" tag. + {`{"Y": 1, "Z": 2}`, new(T), T{Y: 1}, nil}, + + // syntax errors + {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}}, + {`[1, 2, 3+]`, nil, nil, &SyntaxError{"invalid character '+' after array element", 9}}, + + // array tests + {`[1, 2, 3]`, new([3]int), [3]int{1, 2, 3}, nil}, + {`[1, 2, 3]`, new([1]int), [1]int{1}, nil}, + {`[1, 2, 3]`, new([5]int), [5]int{1, 2, 3, 0, 0}, nil}, + + // composite tests + {allValueIndent, new(All), allValue, nil}, + {allValueCompact, new(All), allValue, nil}, + {allValueIndent, new(*All), &allValue, nil}, + {allValueCompact, new(*All), &allValue, nil}, + {pallValueIndent, new(All), pallValue, nil}, + {pallValueCompact, new(All), pallValue, nil}, + {pallValueIndent, new(*All), &pallValue, nil}, + {pallValueCompact, new(*All), &pallValue, nil}, + + // unmarshal interface test + {`{"T":false}`, &um0, umtrue, nil}, // use "false" so test will fail if custom unmarshaler is not called + {`{"T":false}`, &ump, &umtrue, nil}, + {`[{"T":false}]`, &umslice, umslice, nil}, + {`[{"T":false}]`, &umslicep, &umslice, nil}, + {`{"M":{"T":false}}`, &umstruct, umstruct, nil}, +} + +func TestMarshal(t *testing.T) { + b, err := Marshal(allValue) + if err != nil { + t.Fatalf("Marshal allValue: %v", err) + } + if string(b) != allValueCompact { + t.Errorf("Marshal allValueCompact") + diff(t, b, []byte(allValueCompact)) + return + } + + b, err = Marshal(pallValue) + if err != nil { + t.Fatalf("Marshal pallValue: %v", err) + } + if string(b) != pallValueCompact { + t.Errorf("Marshal pallValueCompact") + diff(t, b, []byte(pallValueCompact)) + return + } +} + +func TestMarshalBadUTF8(t *testing.T) { + s := "hello\xffworld" + b, err := Marshal(s) + if err == nil { + t.Fatal("Marshal bad UTF8: no error") + } + if len(b) != 0 { + t.Fatal("Marshal returned data") + } + if _, ok := err.(*InvalidUTF8Error); !ok { + t.Fatalf("Marshal did not return InvalidUTF8Error: %T %v", err, err) + } +} + +func TestUnmarshal(t *testing.T) { + for i, tt := range unmarshalTests { + var scan scanner + in := []byte(tt.in) + if err := checkValid(in, &scan); err != nil { + if !reflect.DeepEqual(err, tt.err) { + t.Errorf("#%d: checkValid: %#v", i, err) + continue + } + } + if tt.ptr == nil { + continue + } + // v = new(right-type) + v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) + if err := Unmarshal([]byte(in), v.Interface()); !reflect.DeepEqual(err, tt.err) { + t.Errorf("#%d: %v want %v", i, err, tt.err) + continue + } + if !reflect.DeepEqual(v.Elem().Interface(), tt.out) { + t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out) + data, _ := Marshal(v.Elem().Interface()) + println(string(data)) + data, _ = Marshal(tt.out) + println(string(data)) + continue + } + } +} + +func TestUnmarshalMarshal(t *testing.T) { + initBig() + var v interface{} + if err := Unmarshal(jsonBig, &v); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + b, err := Marshal(v) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if bytes.Compare(jsonBig, b) != 0 { + t.Errorf("Marshal jsonBig") + diff(t, b, jsonBig) + return + } +} + +func TestLargeByteSlice(t *testing.T) { + s0 := make([]byte, 2000) + for i := range s0 { + s0[i] = byte(i) + } + b, err := Marshal(s0) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + var s1 []byte + if err := Unmarshal(b, &s1); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if bytes.Compare(s0, s1) != 0 { + t.Errorf("Marshal large byte slice") + diff(t, s0, s1) + } +} + +type Xint struct { + X int +} + +func TestUnmarshalInterface(t *testing.T) { + var xint Xint + var i interface{} = &xint + if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if xint.X != 1 { + t.Fatalf("Did not write to xint") + } +} + +func TestUnmarshalPtrPtr(t *testing.T) { + var xint Xint + pxint := &xint + if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if xint.X != 1 { + t.Fatalf("Did not write to xint") + } +} + +func TestEscape(t *testing.T) { + const input = `"foobar"` + const expected = `"\"foobar\"\u003chtml\u003e"` + b, err := Marshal(input) + if err != nil { + t.Fatalf("Marshal error: %v", err) + } + if s := string(b); s != expected { + t.Errorf("Encoding of [%s] was [%s], want [%s]", input, s, expected) + } +} + +// WrongString is a struct that's misusing the ,string modifier. +type WrongString struct { + Message string `json:"result,string"` +} + +type wrongStringTest struct { + in, err string +} + +var wrongStringTests = []wrongStringTest{ + {`{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`}, + {`{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`}, + {`{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`}, +} + +// If people misuse the ,string modifier, the error message should be +// helpful, telling the user that they're doing it wrong. +func TestErrorMessageFromMisusedString(t *testing.T) { + for n, tt := range wrongStringTests { + r := strings.NewReader(tt.in) + var s WrongString + err := NewDecoder(r).Decode(&s) + got := fmt.Sprintf("%v", err) + if got != tt.err { + t.Errorf("%d. got err = %q, want %q", n, got, tt.err) + } + } +} + +func noSpace(c rune) rune { + if isSpace(c) { + return -1 + } + return c +} + +type All struct { + Bool bool + Int int + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + Uint uint + Uint8 uint8 + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + Uintptr uintptr + Float32 float32 + Float64 float64 + + Foo string `json:"bar"` + Foo2 string `json:"bar2,dummyopt"` + + IntStr int64 `json:",string"` + + PBool *bool + PInt *int + PInt8 *int8 + PInt16 *int16 + PInt32 *int32 + PInt64 *int64 + PUint *uint + PUint8 *uint8 + PUint16 *uint16 + PUint32 *uint32 + PUint64 *uint64 + PUintptr *uintptr + PFloat32 *float32 + PFloat64 *float64 + + String string + PString *string + + Map map[string]Small + MapP map[string]*Small + PMap *map[string]Small + PMapP *map[string]*Small + + EmptyMap map[string]Small + NilMap map[string]Small + + Slice []Small + SliceP []*Small + PSlice *[]Small + PSliceP *[]*Small + + EmptySlice []Small + NilSlice []Small + + StringSlice []string + ByteSlice []byte + + Small Small + PSmall *Small + PPSmall **Small + + Interface interface{} + PInterface *interface{} + + unexported int +} + +type Small struct { + Tag string +} + +var allValue = All{ + Bool: true, + Int: 2, + Int8: 3, + Int16: 4, + Int32: 5, + Int64: 6, + Uint: 7, + Uint8: 8, + Uint16: 9, + Uint32: 10, + Uint64: 11, + Uintptr: 12, + Float32: 14.1, + Float64: 15.1, + Foo: "foo", + Foo2: "foo2", + IntStr: 42, + String: "16", + Map: map[string]Small{ + "17": {Tag: "tag17"}, + "18": {Tag: "tag18"}, + }, + MapP: map[string]*Small{ + "19": {Tag: "tag19"}, + "20": nil, + }, + EmptyMap: map[string]Small{}, + Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, + SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}}, + EmptySlice: []Small{}, + StringSlice: []string{"str24", "str25", "str26"}, + ByteSlice: []byte{27, 28, 29}, + Small: Small{Tag: "tag30"}, + PSmall: &Small{Tag: "tag31"}, + Interface: 5.2, +} + +var pallValue = All{ + PBool: &allValue.Bool, + PInt: &allValue.Int, + PInt8: &allValue.Int8, + PInt16: &allValue.Int16, + PInt32: &allValue.Int32, + PInt64: &allValue.Int64, + PUint: &allValue.Uint, + PUint8: &allValue.Uint8, + PUint16: &allValue.Uint16, + PUint32: &allValue.Uint32, + PUint64: &allValue.Uint64, + PUintptr: &allValue.Uintptr, + PFloat32: &allValue.Float32, + PFloat64: &allValue.Float64, + PString: &allValue.String, + PMap: &allValue.Map, + PMapP: &allValue.MapP, + PSlice: &allValue.Slice, + PSliceP: &allValue.SliceP, + PPSmall: &allValue.PSmall, + PInterface: &allValue.Interface, +} + +var allValueIndent = `{ + "Bool": true, + "Int": 2, + "Int8": 3, + "Int16": 4, + "Int32": 5, + "Int64": 6, + "Uint": 7, + "Uint8": 8, + "Uint16": 9, + "Uint32": 10, + "Uint64": 11, + "Uintptr": 12, + "Float32": 14.1, + "Float64": 15.1, + "bar": "foo", + "bar2": "foo2", + "IntStr": "42", + "PBool": null, + "PInt": null, + "PInt8": null, + "PInt16": null, + "PInt32": null, + "PInt64": null, + "PUint": null, + "PUint8": null, + "PUint16": null, + "PUint32": null, + "PUint64": null, + "PUintptr": null, + "PFloat32": null, + "PFloat64": null, + "String": "16", + "PString": null, + "Map": { + "17": { + "Tag": "tag17" + }, + "18": { + "Tag": "tag18" + } + }, + "MapP": { + "19": { + "Tag": "tag19" + }, + "20": null + }, + "PMap": null, + "PMapP": null, + "EmptyMap": {}, + "NilMap": null, + "Slice": [ + { + "Tag": "tag20" + }, + { + "Tag": "tag21" + } + ], + "SliceP": [ + { + "Tag": "tag22" + }, + null, + { + "Tag": "tag23" + } + ], + "PSlice": null, + "PSliceP": null, + "EmptySlice": [], + "NilSlice": null, + "StringSlice": [ + "str24", + "str25", + "str26" + ], + "ByteSlice": "Gxwd", + "Small": { + "Tag": "tag30" + }, + "PSmall": { + "Tag": "tag31" + }, + "PPSmall": null, + "Interface": 5.2, + "PInterface": null +}` + +var allValueCompact = strings.Map(noSpace, allValueIndent) + +var pallValueIndent = `{ + "Bool": false, + "Int": 0, + "Int8": 0, + "Int16": 0, + "Int32": 0, + "Int64": 0, + "Uint": 0, + "Uint8": 0, + "Uint16": 0, + "Uint32": 0, + "Uint64": 0, + "Uintptr": 0, + "Float32": 0, + "Float64": 0, + "bar": "", + "bar2": "", + "IntStr": "0", + "PBool": true, + "PInt": 2, + "PInt8": 3, + "PInt16": 4, + "PInt32": 5, + "PInt64": 6, + "PUint": 7, + "PUint8": 8, + "PUint16": 9, + "PUint32": 10, + "PUint64": 11, + "PUintptr": 12, + "PFloat32": 14.1, + "PFloat64": 15.1, + "String": "", + "PString": "16", + "Map": null, + "MapP": null, + "PMap": { + "17": { + "Tag": "tag17" + }, + "18": { + "Tag": "tag18" + } + }, + "PMapP": { + "19": { + "Tag": "tag19" + }, + "20": null + }, + "EmptyMap": null, + "NilMap": null, + "Slice": null, + "SliceP": null, + "PSlice": [ + { + "Tag": "tag20" + }, + { + "Tag": "tag21" + } + ], + "PSliceP": [ + { + "Tag": "tag22" + }, + null, + { + "Tag": "tag23" + } + ], + "EmptySlice": null, + "NilSlice": null, + "StringSlice": null, + "ByteSlice": null, + "Small": { + "Tag": "" + }, + "PSmall": null, + "PPSmall": { + "Tag": "tag31" + }, + "Interface": null, + "PInterface": 5.2 +}` + +var pallValueCompact = strings.Map(noSpace, pallValueIndent) + +func TestRefUnmarshal(t *testing.T) { + type S struct { + // Ref is defined in encode_test.go. + R0 Ref + R1 *Ref + } + want := S{ + R0: 12, + R1: new(Ref), + } + *want.R1 = 12 + + var got S + if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref"}`), &got); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got %+v, want %+v", got, want) + } +} + +// Test that anonymous fields are ignored. +// We may assign meaning to them later. +func TestAnonymous(t *testing.T) { + type S struct { + T + N int + } + + data, err := Marshal(new(S)) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + want := `{"N":0}` + if string(data) != want { + t.Fatalf("Marshal = %#q, want %#q", string(data), want) + } + + var s S + if err := Unmarshal([]byte(`{"T": 1, "T": {"Y": 1}, "N": 2}`), &s); err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if s.N != 2 { + t.Fatal("Unmarshal: did not set N") + } + if s.T.Y != 0 { + t.Fatal("Unmarshal: did set T.Y") + } +} diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go new file mode 100644 index 000000000..14957b848 --- /dev/null +++ b/src/pkg/encoding/json/encode.go @@ -0,0 +1,551 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package json implements encoding and decoding of JSON objects as defined in +// RFC 4627. +// +// See "JSON and Go" for an introduction to this package: +// http://golang.org/doc/articles/json_and_go.html +package json + +import ( + "bytes" + "encoding/base64" + "math" + "reflect" + "runtime" + "sort" + "strconv" + "sync" + "unicode" + "unicode/utf8" +) + +// Marshal returns the JSON encoding of v. +// +// Marshal traverses the value v recursively. +// If an encountered value implements the Marshaler interface +// and is not a nil pointer, Marshal calls its MarshalJSON method +// to produce JSON. The nil pointer exception is not strictly necessary +// but mimics a similar, necessary exception in the behavior of +// UnmarshalJSON. +// +// Otherwise, Marshal uses the following type-dependent default encodings: +// +// Boolean values encode as JSON booleans. +// +// Floating point and integer values encode as JSON numbers. +// +// String values encode as JSON strings, with each invalid UTF-8 sequence +// replaced by the encoding of the Unicode replacement character U+FFFD. +// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" +// to keep some browsers from misinterpreting JSON output as HTML. +// +// Array and slice values encode as JSON arrays, except that +// []byte encodes as a base64-encoded string, and a nil slice +// encodes as the null JSON object. +// +// Struct values encode as JSON objects. Each exported struct field +// becomes a member of the object unless +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option. +// The empty values are false, 0, any +// nil pointer or interface value, and any array, slice, map, or string of +// length zero. The object's default key string is the struct field name +// but can be specified in the struct field's tag value. The "json" key in +// struct field's tag value is the key name, followed by an optional comma +// and options. Examples: +// +// // Field is ignored by this package. +// Field int `json:"-"` +// +// // Field appears in JSON as key "myName". +// Field int `json:"myName"` +// +// // Field appears in JSON as key "myName" and +// // the field is omitted from the object if its value is empty, +// // as defined above. +// Field int `json:"myName,omitempty"` +// +// // Field appears in JSON as key "Field" (the default), but +// // the field is skipped if empty. +// // Note the leading comma. +// Field int `json:",omitempty"` +// +// The "string" option signals that a field is stored as JSON inside a +// JSON-encoded string. This extra level of encoding is sometimes +// used when communicating with JavaScript programs: +// +// Int64String int64 `json:",string"` +// +// The key name will be used if it's a non-empty string consisting of +// only Unicode letters, digits, dollar signs, percent signs, hyphens, +// underscores and slashes. +// +// Map values encode as JSON objects. +// The map's key type must be string; the object keys are used directly +// as map keys. +// +// Pointer values encode as the value pointed to. +// A nil pointer encodes as the null JSON object. +// +// Interface values encode as the value contained in the interface. +// A nil interface value encodes as the null JSON object. +// +// Channel, complex, and function values cannot be encoded in JSON. +// Attempting to encode such a value causes Marshal to return +// an InvalidTypeError. +// +// JSON cannot represent cyclic data structures and Marshal does not +// handle them. Passing cyclic structures to Marshal will result in +// an infinite recursion. +// +func Marshal(v interface{}) ([]byte, error) { + e := &encodeState{} + err := e.marshal(v) + if err != nil { + return nil, err + } + return e.Bytes(), nil +} + +// MarshalIndent is like Marshal but applies Indent to format the output. +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + err = Indent(&buf, b, prefix, indent) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// HTMLEscape appends to dst the JSON-encoded src with <, >, and & +// characters inside string literals changed to \u003c, \u003e, \u0026 +// so that the JSON will be safe to embed inside HTML ") + +produces + + Hello, ! + +but the contextual autoescaping in html/template + + import "html/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", "") + +produces safe, escaped HTML output + + Hello, <script>alert('you have been pwned')</script>! + + +Contexts + +This package understands HTML, CSS, JavaScript, and URIs. It adds sanitizing +functions to each simple action pipeline, so given the excerpt + + {{.}} + +At parse time each {{.}} is overwritten to add escaping functions as necessary. +In this case it becomes + + {{. | html}} + + +Errors + +See the documentation of ErrorCode for details. + + +A fuller picture + +The rest of this package comment may be skipped on first reading; it includes +details necessary to understand escaping contexts and error messages. Most users +will not need to understand these details. + + +Contexts + +Assuming {{.}} is `O'Reilly: How are you?`, the table below shows +how {{.}} appears when used in the context to the left. + + Context {{.}} After + {{.}} O'Reilly: How are <i>you</i>? + O'Reilly: How are you? + O'Reilly: How are %3ci%3eyou%3c/i%3e? + O'Reilly%3a%20How%20are%3ci%3e...%3f + O\x27Reilly: How are \x3ci\x3eyou...? + "O\x27Reilly: How are \x3ci\x3eyou...?" + O\x27Reilly: How are \x3ci\x3eyou...\x3f + +If used in an unsafe context, then the value might be filtered out: + + Context {{.}} After + #ZgotmplZ + +since "O'Reilly:" is not an allowed protocol like "http:". + + +If {{.}} is the innocuous word, `left`, then it can appear more widely, + + Context {{.}} After + {{.}} left + left + left + left + left + left + left + left + left + +Non-string values can be used in JavaScript contexts. +If {{.}} is + + []struct{A,B string}{ "foo", "bar" } + +in the escaped template + + + +then the template output is + + + +See package json to understand how non-string content is marshalled for +embedding in JavaScript contexts. + + +Typed Strings + +By default, this package assumes that all pipelines produce a plain text string. +It adds escaping pipeline stages necessary to correctly and safely embed that +plain text string in the appropriate context. + +When a data value is not plain text, you can make sure it is not over-escaped +by marking it with its type. + +Types HTML, JS, URL, and others from content.go can carry safe content that is +exempted from escaping. + +The template + + Hello, {{.}}! + +can be invoked with + + tmpl.Execute(out, HTML(`World`)) + +to produce + + Hello, World! + +instead of the + + Hello, <b>World<b>! + +that would have been produced if {{.}} was a regular string. + + +Security Model + +http://js-quasis-libraries-and-repl.googlecode.com/svn/trunk/safetemplate.html#problem_definition defines "safe" as used by this package. + +This package assumes that template authors are trusted, that Execute's data +parameter is not, and seeks to preserve the properties below in the face +of untrusted data: + +Structure Preservation Property: +"... when a template author writes an HTML tag in a safe templating language, +the browser will interpret the corresponding portion of the output as a tag +regardless of the values of untrusted data, and similarly for other structures +such as attribute boundaries and JS and CSS string boundaries." + +Code Effect Property: +"... only code specified by the template author should run as a result of +injecting the template output into a page and all code specified by the +template author should run as a result of the same." + +Least Surprise Property: +"A developer (or code reviewer) familiar with HTML, CSS, and JavaScript, who +knows that contextual autoescaping happens should be able to look at a {{.}} +and correctly infer what sanitization happens." +*/ +package template diff --git a/src/pkg/html/template/error.go b/src/pkg/html/template/error.go new file mode 100644 index 000000000..dcac74896 --- /dev/null +++ b/src/pkg/html/template/error.go @@ -0,0 +1,197 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "fmt" +) + +// Error describes a problem encountered during template Escaping. +type Error struct { + // ErrorCode describes the kind of error. + ErrorCode ErrorCode + // Name is the name of the template in which the error was encountered. + Name string + // Line is the line number of the error in the template source or 0. + Line int + // Description is a human-readable description of the problem. + Description string +} + +// ErrorCode is a code for a kind of error. +type ErrorCode int + +// We define codes for each error that manifests while escaping templates, but +// escaped templates may also fail at runtime. +// +// Output: "ZgotmplZ" +// Example: +// +// where {{.X}} evaluates to `javascript:...` +// Discussion: +// "ZgotmplZ" is a special value that indicates that unsafe content reached a +// CSS or URL context at runtime. The output of the example will be +// +// If the data comes from a trusted source, use content types to exempt it +// from filtering: URL(`javascript:...`). +const ( + // OK indicates the lack of an error. + OK ErrorCode = iota + + // ErrAmbigContext: "... appears in an ambiguous URL context" + // Example: + // + // Discussion: + // {{.X}} is in an ambiguous URL context since, depending on {{.C}}, + // it may be either a URL suffix or a query parameter. + // Moving {{.X}} into the condition removes the ambiguity: + // + ErrAmbigContext + + // ErrBadHTML: "expected space, attr name, or end of tag, but got ...", + // "... in unquoted attr", "... in attribute name" + // Example: + // + // + //
+ //
{{end}} + // {{define "attrs"}}href="{{.URL}}"{{end}} + // Discussion: + // Package html/template looks through template calls to compute the + // context. + // Here the {{.URL}} in "attrs" must be treated as a URL when called + // from "main", but you will get this error if "attrs" is not defined + // when "main" is parsed. + ErrNoSuchTemplate + + // ErrOutputContext: "cannot compute output context for template ..." + // Examples: + // {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}} + // Discussion: + // A recursive template does not end in the same context in which it + // starts, and a reliable output context cannot be computed. + // Look for typos in the named template. + // If the template should not be called in the named start context, + // look for calls to that template in unexpected contexts. + // Maybe refactor recursive templates to not be recursive. + ErrOutputContext + + // ErrPartialCharset: "unfinished JS regexp charset in ..." + // Example: + // + // Discussion: + // Package html/template does not support interpolation into regular + // expression literal character sets. + ErrPartialCharset + + // ErrPartialEscape: "unfinished escape sequence in ..." + // Example: + // + // Discussion: + // Package html/template does not support actions following a + // backslash. + // This is usually an error and there are better solutions; for + // example + // + // should work, and if {{.X}} is a partial escape sequence such as + // "xA0", mark the whole sequence as safe content: JSStr(`\xA0`) + ErrPartialEscape + + // ErrRangeLoopReentry: "on range loop re-entry: ..." + // Example: + // + // Discussion: + // If an iteration through a range would cause it to end in a + // different context than an earlier pass, there is no single context. + // In the example, there is missing a quote, so it is not clear + // whether {{.}} is meant to be inside a JS string or in a JS value + // context. The second iteration would produce something like + // + // + ErrRangeLoopReentry + + // ErrSlashAmbig: '/' could start a division or regexp. + // Example: + // + // Discussion: + // The example above could produce `var x = 1/-2/i.test(s)...` + // in which the first '/' is a mathematical division operator or it + // could produce `/-2/i.test(s)` in which the first '/' starts a + // regexp literal. + // Look for missing semicolons inside branches, and maybe add + // parentheses to make it clear which interpretation you intend. + ErrSlashAmbig +) + +func (e *Error) Error() string { + if e.Line != 0 { + return fmt.Sprintf("html/template:%s:%d: %s", e.Name, e.Line, e.Description) + } else if e.Name != "" { + return fmt.Sprintf("html/template:%s: %s", e.Name, e.Description) + } + return "html/template: " + e.Description +} + +// errorf creates an error given a format string f and args. +// The template Name still needs to be supplied. +func errorf(k ErrorCode, line int, f string, args ...interface{}) *Error { + return &Error{k, "", line, fmt.Sprintf(f, args...)} +} diff --git a/src/pkg/html/template/escape.go b/src/pkg/html/template/escape.go new file mode 100644 index 000000000..5f0e28e8c --- /dev/null +++ b/src/pkg/html/template/escape.go @@ -0,0 +1,795 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "bytes" + "fmt" + "html" + "io" + "text/template" + "text/template/parse" +) + +// escapeTemplates rewrites the named templates, which must be +// associated with t, to guarantee that the output of any of the named +// templates is properly escaped. Names should include the names of +// all templates that might be Executed but need not include helper +// templates. If no error is returned, then the named templates have +// been modified. Otherwise the named templates have been rendered +// unusable. +func escapeTemplates(tmpl *Template, names ...string) error { + e := newEscaper(tmpl) + for _, name := range names { + c, _ := e.escapeTree(context{}, name, 0) + var err error + if c.err != nil { + err, c.err.Name = c.err, name + } else if c.state != stateText { + err = &Error{ErrEndContext, name, 0, fmt.Sprintf("ends in a non-text context: %v", c)} + } + if err != nil { + // Prevent execution of unsafe templates. + for _, name := range names { + if t := tmpl.set[name]; t != nil { + t.text.Tree = nil + } + } + return err + } + tmpl.escaped = true + } + e.commit() + return nil +} + +// funcMap maps command names to functions that render their inputs safe. +var funcMap = template.FuncMap{ + "html_template_attrescaper": attrEscaper, + "html_template_commentescaper": commentEscaper, + "html_template_cssescaper": cssEscaper, + "html_template_cssvaluefilter": cssValueFilter, + "html_template_htmlnamefilter": htmlNameFilter, + "html_template_htmlescaper": htmlEscaper, + "html_template_jsregexpescaper": jsRegexpEscaper, + "html_template_jsstrescaper": jsStrEscaper, + "html_template_jsvalescaper": jsValEscaper, + "html_template_nospaceescaper": htmlNospaceEscaper, + "html_template_rcdataescaper": rcdataEscaper, + "html_template_urlescaper": urlEscaper, + "html_template_urlfilter": urlFilter, + "html_template_urlnormalizer": urlNormalizer, +} + +// equivEscapers matches contextual escapers to equivalent template builtins. +var equivEscapers = map[string]string{ + "html_template_attrescaper": "html", + "html_template_htmlescaper": "html", + "html_template_nospaceescaper": "html", + "html_template_rcdataescaper": "html", + "html_template_urlescaper": "urlquery", + "html_template_urlnormalizer": "urlquery", +} + +// escaper collects type inferences about templates and changes needed to make +// templates injection safe. +type escaper struct { + tmpl *Template + // output[templateName] is the output context for a templateName that + // has been mangled to include its input context. + output map[string]context + // derived[c.mangle(name)] maps to a template derived from the template + // named name templateName for the start context c. + derived map[string]*template.Template + // called[templateName] is a set of called mangled template names. + called map[string]bool + // xxxNodeEdits are the accumulated edits to apply during commit. + // Such edits are not applied immediately in case a template set + // executes a given template in different escaping contexts. + actionNodeEdits map[*parse.ActionNode][]string + templateNodeEdits map[*parse.TemplateNode]string + textNodeEdits map[*parse.TextNode][]byte +} + +// newEscaper creates a blank escaper for the given set. +func newEscaper(t *Template) *escaper { + return &escaper{ + t, + map[string]context{}, + map[string]*template.Template{}, + map[string]bool{}, + map[*parse.ActionNode][]string{}, + map[*parse.TemplateNode]string{}, + map[*parse.TextNode][]byte{}, + } +} + +// filterFailsafe is an innocuous word that is emitted in place of unsafe values +// by sanitizer functions. It is not a keyword in any programming language, +// contains no special characters, is not empty, and when it appears in output +// it is distinct enough that a developer can find the source of the problem +// via a search engine. +const filterFailsafe = "ZgotmplZ" + +// escape escapes a template node. +func (e *escaper) escape(c context, n parse.Node) context { + switch n := n.(type) { + case *parse.ActionNode: + return e.escapeAction(c, n) + case *parse.IfNode: + return e.escapeBranch(c, &n.BranchNode, "if") + case *parse.ListNode: + return e.escapeList(c, n) + case *parse.RangeNode: + return e.escapeBranch(c, &n.BranchNode, "range") + case *parse.TemplateNode: + return e.escapeTemplate(c, n) + case *parse.TextNode: + return e.escapeText(c, n) + case *parse.WithNode: + return e.escapeBranch(c, &n.BranchNode, "with") + } + panic("escaping " + n.String() + " is unimplemented") +} + +// escapeAction escapes an action template node. +func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { + if len(n.Pipe.Decl) != 0 { + // A local variable assignment, not an interpolation. + return c + } + c = nudge(c) + s := make([]string, 0, 3) + switch c.state { + case stateError: + return c + case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL: + switch c.urlPart { + case urlPartNone: + s = append(s, "html_template_urlfilter") + fallthrough + case urlPartPreQuery: + switch c.state { + case stateCSSDqStr, stateCSSSqStr: + s = append(s, "html_template_cssescaper") + default: + s = append(s, "html_template_urlnormalizer") + } + case urlPartQueryOrFrag: + s = append(s, "html_template_urlescaper") + case urlPartUnknown: + return context{ + state: stateError, + err: errorf(ErrAmbigContext, n.Line, "%s appears in an ambiguous URL context", n), + } + default: + panic(c.urlPart.String()) + } + case stateJS: + s = append(s, "html_template_jsvalescaper") + // A slash after a value starts a div operator. + c.jsCtx = jsCtxDivOp + case stateJSDqStr, stateJSSqStr: + s = append(s, "html_template_jsstrescaper") + case stateJSRegexp: + s = append(s, "html_template_jsregexpescaper") + case stateCSS: + s = append(s, "html_template_cssvaluefilter") + case stateText: + s = append(s, "html_template_htmlescaper") + case stateRCDATA: + s = append(s, "html_template_rcdataescaper") + case stateAttr: + // Handled below in delim check. + case stateAttrName, stateTag: + c.state = stateAttrName + s = append(s, "html_template_htmlnamefilter") + default: + if isComment(c.state) { + s = append(s, "html_template_commentescaper") + } else { + panic("unexpected state " + c.state.String()) + } + } + switch c.delim { + case delimNone: + // No extra-escaping needed for raw text content. + case delimSpaceOrTagEnd: + s = append(s, "html_template_nospaceescaper") + default: + s = append(s, "html_template_attrescaper") + } + e.editActionNode(n, s) + return c +} + +// ensurePipelineContains ensures that the pipeline has commands with +// the identifiers in s in order. +// If the pipeline already has some of the sanitizers, do not interfere. +// For example, if p is (.X | html) and s is ["escapeJSVal", "html"] then it +// has one matching, "html", and one to insert, "escapeJSVal", to produce +// (.X | escapeJSVal | html). +func ensurePipelineContains(p *parse.PipeNode, s []string) { + if len(s) == 0 { + return + } + n := len(p.Cmds) + // Find the identifiers at the end of the command chain. + idents := p.Cmds + for i := n - 1; i >= 0; i-- { + if cmd := p.Cmds[i]; len(cmd.Args) != 0 { + if id, ok := cmd.Args[0].(*parse.IdentifierNode); ok { + if id.Ident == "noescape" { + return + } + continue + } + } + idents = p.Cmds[i+1:] + } + dups := 0 + for _, id := range idents { + if escFnsEq(s[dups], (id.Args[0].(*parse.IdentifierNode)).Ident) { + dups++ + if dups == len(s) { + return + } + } + } + newCmds := make([]*parse.CommandNode, n-len(idents), n+len(s)-dups) + copy(newCmds, p.Cmds) + // Merge existing identifier commands with the sanitizers needed. + for _, id := range idents { + i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s, escFnsEq) + if i != -1 { + for _, name := range s[:i] { + newCmds = appendCmd(newCmds, newIdentCmd(name)) + } + s = s[i+1:] + } + newCmds = appendCmd(newCmds, id) + } + // Create any remaining sanitizers. + for _, name := range s { + newCmds = appendCmd(newCmds, newIdentCmd(name)) + } + p.Cmds = newCmds +} + +// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x) +// for all x. +var redundantFuncs = map[string]map[string]bool{ + "html_template_commentescaper": { + "html_template_attrescaper": true, + "html_template_nospaceescaper": true, + "html_template_htmlescaper": true, + }, + "html_template_cssescaper": { + "html_template_attrescaper": true, + }, + "html_template_jsregexpescaper": { + "html_template_attrescaper": true, + }, + "html_template_jsstrescaper": { + "html_template_attrescaper": true, + }, + "html_template_urlescaper": { + "html_template_urlnormalizer": true, + }, +} + +// appendCmd appends the given command to the end of the command pipeline +// unless it is redundant with the last command. +func appendCmd(cmds []*parse.CommandNode, cmd *parse.CommandNode) []*parse.CommandNode { + if n := len(cmds); n != 0 { + last, ok := cmds[n-1].Args[0].(*parse.IdentifierNode) + next, _ := cmd.Args[0].(*parse.IdentifierNode) + if ok && redundantFuncs[last.Ident][next.Ident] { + return cmds + } + } + return append(cmds, cmd) +} + +// indexOfStr is the first i such that eq(s, strs[i]) or -1 if s was not found. +func indexOfStr(s string, strs []string, eq func(a, b string) bool) int { + for i, t := range strs { + if eq(s, t) { + return i + } + } + return -1 +} + +// escFnsEq returns whether the two escaping functions are equivalent. +func escFnsEq(a, b string) bool { + if e := equivEscapers[a]; e != "" { + a = e + } + if e := equivEscapers[b]; e != "" { + b = e + } + return a == b +} + +// newIdentCmd produces a command containing a single identifier node. +func newIdentCmd(identifier string) *parse.CommandNode { + return &parse.CommandNode{ + NodeType: parse.NodeCommand, + Args: []parse.Node{parse.NewIdentifier(identifier)}, + } +} + +// nudge returns the context that would result from following empty string +// transitions from the input context. +// For example, parsing: +// `90% of the time. + e.output[t.Name()] = c + return e.escapeListConditionally(c, t.Tree.Root, filter) +} + +// delimEnds maps each delim to a string of characters that terminate it. +var delimEnds = [...]string{ + delimDoubleQuote: `"`, + delimSingleQuote: "'", + // Determined empirically by running the below in various browsers. + // var div = document.createElement("DIV"); + // for (var i = 0; i < 0x10000; ++i) { + // div.innerHTML = ""; + // if (div.getElementsByTagName("SPAN")[0].title.indexOf("bar") < 0) + // document.write("

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

", + "

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

- -#data -

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

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

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

baz

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

-| "baz" - -#data -
foobar

baz

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

-| "baz" - -#data -
foobar

baz

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

-| "baz" -|

-| "quux" - -#data -
foobarbaz

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

-| "quux" - -#data -foobar

baz

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

-| "baz" -| -| -|

-| "quux" - -#data -

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

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

-| "quux" - -#data -foobar

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

-| "baz" - -#data -foobar

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

-| "baz" - -#data -

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

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

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

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

-| -| -| -|

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

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

          -| -| -|

          - -#data -

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

          -| -| -|

          - -#data -

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

          -| -| -| -|

          -|

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

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

foobazeggs

spam

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

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

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

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

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

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

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

-| "X" - -#data -

-

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

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

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

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