From 38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Thu, 12 May 2016 23:56:07 -0400 Subject: Moving to glide --- .gitignore | 2 + Godeps/Godeps.json | 221 - Godeps/Readme | 5 - Makefile | 31 +- api/apitestlib.go | 8 +- glide.lock | 104 + glide.yaml | 41 + vendor/github.com/NYTimes/gziphandler/gzip_test.go | 134 + .../NYTimes/gziphandler/testdata/benchmark.json | 5456 ++++ .../log4go/examples/ConsoleLogWriter_Manual.go | 14 + .../log4go/examples/FileLogWriter_Manual.go | 57 + .../log4go/examples/SimpleNetLogServer.go | 42 + .../log4go/examples/SocketLogWriter_Manual.go | 18 + .../log4go/examples/XMLConfigurationExample.go | 13 + .../alecthomas/log4go/examples/example.xml | 47 + vendor/github.com/alecthomas/log4go/log4go_test.go | 534 + .../github.com/braintree/manners/helpers_test.go | 119 + vendor/github.com/braintree/manners/server_test.go | 289 + .../braintree/manners/test_helpers/certs.go | 29 + .../braintree/manners/test_helpers/conn.go | 13 + .../braintree/manners/test_helpers/listener.go | 34 + .../braintree/manners/test_helpers/temp_file.go | 27 + .../braintree/manners/test_helpers/wait_group.go | 33 + .../braintree/manners/transition_test.go | 54 + .../jibber_jabber/ci/scripts/windows-64-test.bat | 5 + .../jibber_jabber/jibber_jabber_suite_test.go | 13 + .../jibber_jabber/jibber_jabber_unix_test.go | 104 + .../jibber_jabber/jibber_jabber_windows_test.go | 51 + .../github.com/dgryski/dgoogauth/googauth_test.go | 251 + .../disintegration/imaging/adjust_test.go | 504 + .../disintegration/imaging/effects_test.go | 190 + .../disintegration/imaging/helpers_test.go | 399 + .../disintegration/imaging/resize_test.go | 570 + .../disintegration/imaging/tools_test.go | 652 + .../disintegration/imaging/transform_test.go | 261 + .../disintegration/imaging/utils_test.go | 81 + vendor/github.com/garyburd/redigo/.travis.yml | 16 + vendor/github.com/garyburd/redigo/README.markdown | 50 + .../garyburd/redigo/internal/commandinfo.go | 2 +- .../garyburd/redigo/internal/commandinfo_test.go | 27 + .../garyburd/redigo/internal/redistest/testdb.go | 68 + .../github.com/garyburd/redigo/redis/conn_test.go | 670 + vendor/github.com/garyburd/redigo/redis/doc.go | 2 +- .../github.com/garyburd/redigo/redis/pool_test.go | 684 + .../garyburd/redigo/redis/pubsub_test.go | 148 + .../github.com/garyburd/redigo/redis/reply_test.go | 179 + .../github.com/garyburd/redigo/redis/scan_test.go | 440 + .../garyburd/redigo/redis/script_test.go | 100 + .../github.com/garyburd/redigo/redis/test_test.go | 177 + .../garyburd/redigo/redis/zpop_example_test.go | 113 + .../github.com/garyburd/redigo/redisx/connmux.go | 152 + .../garyburd/redigo/redisx/connmux_test.go | 259 + vendor/github.com/garyburd/redigo/redisx/doc.go | 17 + .../github.com/go-gorp/gorp/dialect_mysql_test.go | 204 + vendor/github.com/go-gorp/gorp/gorp_suite_test.go | 13 + vendor/github.com/go-gorp/gorp/gorp_test.go | 2412 ++ vendor/github.com/go-gorp/gorp/test_all.sh | 0 vendor/github.com/go-ldap/ldap/conn_test.go | 53 + vendor/github.com/go-ldap/ldap/dn_test.go | 70 + vendor/github.com/go-ldap/ldap/error_test.go | 29 + vendor/github.com/go-ldap/ldap/example_test.go | 305 + vendor/github.com/go-ldap/ldap/filter_test.go | 248 + vendor/github.com/go-ldap/ldap/ldap_test.go | 275 + vendor/github.com/go-ldap/ldap/search_test.go | 31 + .../go-sql-driver/mysql/benchmark_test.go | 246 + .../github.com/go-sql-driver/mysql/driver_test.go | 1857 ++ vendor/github.com/go-sql-driver/mysql/dsn_test.go | 207 + .../github.com/go-sql-driver/mysql/errors_test.go | 42 + .../github.com/go-sql-driver/mysql/utils_test.go | 197 + vendor/github.com/goamz/goamz/.gitignore | 1 + vendor/github.com/goamz/goamz/.lbox | 1 + vendor/github.com/goamz/goamz/.travis.yml | 32 + vendor/github.com/goamz/goamz/CHANGES.md | 5 + vendor/github.com/goamz/goamz/README.md | 64 + .../goamz/goamz/autoscaling/autoscaling.go | 1768 ++ .../goamz/goamz/autoscaling/autoscaling_test.go | 1180 + .../goamz/goamz/autoscaling/responses_test.go | 627 + vendor/github.com/goamz/goamz/aws/attempt_test.go | 58 + vendor/github.com/goamz/goamz/aws/aws_test.go | 211 + vendor/github.com/goamz/goamz/aws/client_test.go | 121 + vendor/github.com/goamz/goamz/aws/export_test.go | 29 + vendor/github.com/goamz/goamz/aws/sign_test.go | 540 + .../goamz/goamz/cloudformation/cloudformation.go | 837 + .../goamz/cloudformation/cloudformation_test.go | 653 + .../goamz/goamz/cloudformation/responses_test.go | 371 + .../goamz/goamz/cloudfront/cloudfront.go | 135 + .../goamz/goamz/cloudfront/cloudfront_test.go | 52 + .../goamz/goamz/cloudfront/testdata/key.pem | 15 + .../goamz/goamz/cloudfront/testdata/key.pub | 6 + vendor/github.com/goamz/goamz/cloudwatch/ChangeLog | 7 + vendor/github.com/goamz/goamz/cloudwatch/README.md | 109 + .../goamz/goamz/cloudwatch/cloudwatch.go | 404 + .../goamz/goamz/cloudwatch/cloudwatch_test.go | 132 + vendor/github.com/goamz/goamz/dynamodb/.gitignore | 1 + vendor/github.com/goamz/goamz/dynamodb/Makefile | 13 + vendor/github.com/goamz/goamz/dynamodb/README.md | 27 + .../github.com/goamz/goamz/dynamodb/attribute.go | 185 + vendor/github.com/goamz/goamz/dynamodb/const.go | 11 + vendor/github.com/goamz/goamz/dynamodb/dynamodb.go | 142 + .../goamz/goamz/dynamodb/dynamodb_test.go | 166 + vendor/github.com/goamz/goamz/dynamodb/item.go | 351 + .../github.com/goamz/goamz/dynamodb/item_test.go | 446 + .../github.com/goamz/goamz/dynamodb/marshaller.go | 626 + .../goamz/goamz/dynamodb/marshaller_test.go | 283 + vendor/github.com/goamz/goamz/dynamodb/query.go | 111 + .../goamz/goamz/dynamodb/query_builder.go | 362 + .../goamz/goamz/dynamodb/query_builder_test.go | 380 + vendor/github.com/goamz/goamz/dynamodb/scan.go | 51 + vendor/github.com/goamz/goamz/dynamodb/stream.go | 307 + .../github.com/goamz/goamz/dynamodb/stream_test.go | 198 + vendor/github.com/goamz/goamz/dynamodb/table.go | 259 + .../github.com/goamz/goamz/dynamodb/table_test.go | 79 + .../github.com/goamz/goamz/dynamodb/update_item.go | 94 + vendor/github.com/goamz/goamz/ec2/ec2.go | 2267 ++ vendor/github.com/goamz/goamz/ec2/ec2_test.go | 1280 + vendor/github.com/goamz/goamz/ec2/ec2i_test.go | 204 + vendor/github.com/goamz/goamz/ec2/ec2t_test.go | 581 + .../github.com/goamz/goamz/ec2/ec2test/filter.go | 84 + .../github.com/goamz/goamz/ec2/ec2test/server.go | 993 + vendor/github.com/goamz/goamz/ec2/export_test.go | 22 + .../github.com/goamz/goamz/ec2/responses_test.go | 1084 + vendor/github.com/goamz/goamz/ec2/sign.go | 45 + vendor/github.com/goamz/goamz/ec2/sign_test.go | 68 + vendor/github.com/goamz/goamz/ec2/vpc.go | 399 + vendor/github.com/goamz/goamz/ec2/vpc_test.go | 224 + vendor/github.com/goamz/goamz/ecs/ecs.go | 1075 + vendor/github.com/goamz/goamz/ecs/ecs_test.go | 806 + .../github.com/goamz/goamz/ecs/responses_test.go | 637 + vendor/github.com/goamz/goamz/elb/elb.go | 435 + vendor/github.com/goamz/goamz/elb/elb_test.go | 369 + vendor/github.com/goamz/goamz/elb/elbi_test.go | 308 + vendor/github.com/goamz/goamz/elb/elbt_test.go | 243 + .../github.com/goamz/goamz/elb/elbtest/server.go | 551 + vendor/github.com/goamz/goamz/elb/export_test.go | 9 + vendor/github.com/goamz/goamz/elb/response_test.go | 234 + vendor/github.com/goamz/goamz/elb/sign.go | 35 + vendor/github.com/goamz/goamz/elb/sign_test.go | 66 + vendor/github.com/goamz/goamz/elb/suite_test.go | 119 + .../goamz/goamz/exp/mturk/example_test.go | 66 + .../goamz/goamz/exp/mturk/export_test.go | 9 + vendor/github.com/goamz/goamz/exp/mturk/mturk.go | 480 + .../github.com/goamz/goamz/exp/mturk/mturk_test.go | 170 + .../goamz/goamz/exp/mturk/responses_test.go | 36 + vendor/github.com/goamz/goamz/exp/mturk/sign.go | 22 + .../github.com/goamz/goamz/exp/mturk/sign_test.go | 19 + .../github.com/goamz/goamz/exp/sdb/export_test.go | 9 + .../goamz/goamz/exp/sdb/responses_test.go | 120 + vendor/github.com/goamz/goamz/exp/sdb/sdb.go | 413 + vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go | 219 + vendor/github.com/goamz/goamz/exp/sdb/sign.go | 54 + vendor/github.com/goamz/goamz/exp/sdb/sign_test.go | 29 + vendor/github.com/goamz/goamz/exp/ses/ses.go | 145 + vendor/github.com/goamz/goamz/exp/ses/ses_test.go | 69 + vendor/github.com/goamz/goamz/exp/ses/ses_types.go | 156 + vendor/github.com/goamz/goamz/exp/ses/sign.go | 26 + vendor/github.com/goamz/goamz/exp/sns/Makefile | 21 + vendor/github.com/goamz/goamz/exp/sns/README | 1 + vendor/github.com/goamz/goamz/exp/sns/endpoint.go | 132 + .../github.com/goamz/goamz/exp/sns/permissions.go | 51 + vendor/github.com/goamz/goamz/exp/sns/platform.go | 135 + .../goamz/goamz/exp/sns/responses_test.go | 317 + vendor/github.com/goamz/goamz/exp/sns/sign.go | 72 + vendor/github.com/goamz/goamz/exp/sns/sns.go | 113 + vendor/github.com/goamz/goamz/exp/sns/sns_test.go | 455 + .../github.com/goamz/goamz/exp/sns/subscription.go | 165 + vendor/github.com/goamz/goamz/exp/sns/topic.go | 104 + vendor/github.com/goamz/goamz/iam/iam.go | 643 + vendor/github.com/goamz/goamz/iam/iam_test.go | 450 + vendor/github.com/goamz/goamz/iam/iami_test.go | 209 + vendor/github.com/goamz/goamz/iam/iamt_test.go | 39 + .../github.com/goamz/goamz/iam/iamtest/server.go | 432 + .../github.com/goamz/goamz/iam/responses_test.go | 261 + vendor/github.com/goamz/goamz/iam/sign.go | 38 + vendor/github.com/goamz/goamz/rds/rds.go | 96 + vendor/github.com/goamz/goamz/rds/rds_test.go | 77 + .../github.com/goamz/goamz/rds/responses_test.go | 57 + vendor/github.com/goamz/goamz/rds/types.go | 388 + vendor/github.com/goamz/goamz/route53/route53.go | 254 + vendor/github.com/goamz/goamz/s3/export_test.go | 17 + vendor/github.com/goamz/goamz/s3/multi_test.go | 373 + vendor/github.com/goamz/goamz/s3/responses_test.go | 202 + vendor/github.com/goamz/goamz/s3/s3_test.go | 427 + vendor/github.com/goamz/goamz/s3/s3i_test.go | 590 + vendor/github.com/goamz/goamz/s3/s3t_test.go | 83 + vendor/github.com/goamz/goamz/s3/s3test/server.go | 640 + vendor/github.com/goamz/goamz/s3/sign_test.go | 132 + vendor/github.com/goamz/goamz/sqs/Makefile | 20 + vendor/github.com/goamz/goamz/sqs/README.md | 38 + .../github.com/goamz/goamz/sqs/responses_test.go | 196 + vendor/github.com/goamz/goamz/sqs/sqs.go | 570 + vendor/github.com/goamz/goamz/sqs/sqs_test.go | 414 + vendor/github.com/goamz/goamz/sqs/suite_test.go | 145 + .../github.com/goamz/goamz/sts/responses_test.go | 84 + vendor/github.com/goamz/goamz/sts/sts.go | 273 + vendor/github.com/goamz/goamz/sts/sts_test.go | 151 + vendor/github.com/goamz/goamz/testutil/http.go | 180 + vendor/github.com/goamz/goamz/testutil/suite.go | 31 + .../golang/freetype/cmd/print-glyph-points/main.c | 87 + .../golang/freetype/example/capjoin/main.go | 85 + .../golang/freetype/example/drawer/main.go | 158 + .../golang/freetype/example/freetype/main.go | 150 + .../golang/freetype/example/gamma/main.go | 86 + .../golang/freetype/example/raster/main.go | 185 + .../golang/freetype/example/round/main.go | 110 + .../golang/freetype/example/truetype/main.go | 89 + vendor/github.com/golang/freetype/freetype.go | 2 +- vendor/github.com/golang/freetype/freetype_test.go | 59 + vendor/github.com/golang/freetype/licenses/ftl.txt | 169 + vendor/github.com/golang/freetype/licenses/gpl.txt | 340 + vendor/github.com/golang/freetype/raster/raster.go | 2 +- vendor/github.com/golang/freetype/testdata/COPYING | 42 + vendor/github.com/golang/freetype/testdata/README | 13 + .../github.com/golang/freetype/testdata/luximr.ttf | Bin 0 -> 71784 bytes .../github.com/golang/freetype/testdata/luximr.ttx | 24616 +++++++++++++++ .../github.com/golang/freetype/testdata/luxirr.ttf | Bin 0 -> 88732 bytes .../github.com/golang/freetype/testdata/luxirr.ttx | 30264 +++++++++++++++++++ .../freetype/testdata/luxisr-12pt-sans-hinting.txt | 392 + .../freetype/testdata/luxisr-12pt-with-hinting.txt | 392 + .../github.com/golang/freetype/testdata/luxisr.ttf | Bin 0 -> 67548 bytes .../github.com/golang/freetype/testdata/luxisr.ttx | 22503 ++++++++++++++ .../freetype/testdata/make-other-hinting-txts.sh | 34 + .../golang/freetype/truetype/face_test.go | 48 + .../golang/freetype/truetype/hint_test.go | 675 + .../golang/freetype/truetype/truetype.go | 2 +- .../golang/freetype/truetype/truetype_test.go | 400 + vendor/github.com/golang/groupcache/.gitignore | 1 + vendor/github.com/golang/groupcache/README.md | 73 + vendor/github.com/golang/groupcache/byteview.go | 160 + .../github.com/golang/groupcache/byteview_test.go | 142 + .../groupcache/consistenthash/consistenthash.go | 81 + .../consistenthash/consistenthash_test.go | 110 + vendor/github.com/golang/groupcache/groupcache.go | 489 + .../golang/groupcache/groupcache_test.go | 447 + .../groupcache/groupcachepb/groupcache.pb.go | 65 + .../groupcache/groupcachepb/groupcache.proto | 34 + vendor/github.com/golang/groupcache/http.go | 227 + vendor/github.com/golang/groupcache/http_test.go | 166 + .../github.com/golang/groupcache/lru/lru_test.go | 73 + vendor/github.com/golang/groupcache/peers.go | 71 + .../golang/groupcache/singleflight/singleflight.go | 64 + .../groupcache/singleflight/singleflight_test.go | 85 + vendor/github.com/golang/groupcache/sinks.go | 322 + .../github.com/golang/groupcache/testpb/test.pb.go | 235 + .../github.com/golang/groupcache/testpb/test.proto | 63 + vendor/github.com/gorilla/context/context_test.go | 161 + .../github.com/gorilla/handlers/canonical_test.go | 127 + .../github.com/gorilla/handlers/compress_test.go | 154 + vendor/github.com/gorilla/handlers/cors_test.go | 336 + .../github.com/gorilla/handlers/handlers_test.go | 354 + .../gorilla/handlers/proxy_headers_test.go | 100 + .../github.com/gorilla/handlers/recovery_test.go | 44 + vendor/github.com/gorilla/mux/bench_test.go | 49 + vendor/github.com/gorilla/mux/mux_test.go | 1471 + vendor/github.com/gorilla/mux/old_test.go | 710 + vendor/github.com/gorilla/websocket/bench_test.go | 19 + .../gorilla/websocket/client_server_test.go | 451 + vendor/github.com/gorilla/websocket/client_test.go | 72 + vendor/github.com/gorilla/websocket/conn_test.go | 402 + .../github.com/gorilla/websocket/example_test.go | 46 + .../gorilla/websocket/examples/autobahn/README.md | 13 + .../websocket/examples/autobahn/fuzzingclient.json | 14 + .../gorilla/websocket/examples/autobahn/server.go | 246 + .../gorilla/websocket/examples/chat/README.md | 20 + .../gorilla/websocket/examples/chat/conn.go | 105 + .../gorilla/websocket/examples/chat/home.html | 92 + .../gorilla/websocket/examples/chat/hub.go | 51 + .../gorilla/websocket/examples/chat/main.go | 39 + .../gorilla/websocket/examples/command/README.md | 19 + .../gorilla/websocket/examples/command/home.html | 96 + .../gorilla/websocket/examples/command/main.go | 188 + .../gorilla/websocket/examples/echo/README.md | 17 + .../gorilla/websocket/examples/echo/client.go | 81 + .../gorilla/websocket/examples/echo/server.go | 132 + .../gorilla/websocket/examples/filewatch/README.md | 9 + .../gorilla/websocket/examples/filewatch/main.go | 193 + vendor/github.com/gorilla/websocket/json_test.go | 119 + vendor/github.com/gorilla/websocket/server_test.go | 51 + vendor/github.com/gorilla/websocket/util_test.go | 34 + vendor/github.com/lib/pq/.travis.yml | 3 +- vendor/github.com/lib/pq/README.md | 4 +- vendor/github.com/lib/pq/bench_test.go | 435 + vendor/github.com/lib/pq/certs/README | 3 + vendor/github.com/lib/pq/certs/postgresql.crt | 69 + vendor/github.com/lib/pq/certs/postgresql.key | 15 + vendor/github.com/lib/pq/certs/root.crt | 24 + vendor/github.com/lib/pq/certs/server.crt | 81 + vendor/github.com/lib/pq/certs/server.key | 27 + vendor/github.com/lib/pq/conn_test.go | 1434 + vendor/github.com/lib/pq/copy_test.go | 465 + vendor/github.com/lib/pq/encode.go | 3 +- vendor/github.com/lib/pq/encode_test.go | 727 + vendor/github.com/lib/pq/hstore/hstore.go | 118 + vendor/github.com/lib/pq/hstore/hstore_test.go | 148 + vendor/github.com/lib/pq/listen_example/doc.go | 102 + vendor/github.com/lib/pq/notify.go | 22 +- vendor/github.com/lib/pq/notify_test.go | 574 + vendor/github.com/lib/pq/ssl_test.go | 226 + vendor/github.com/lib/pq/url_test.go | 66 + vendor/github.com/mattermost/rsc/.gitignore | 22 + vendor/github.com/mattermost/rsc/README.md | 4 + vendor/github.com/mattermost/rsc/app/app.go | 39 + vendor/github.com/mattermost/rsc/app/app.yaml | 23 + vendor/github.com/mattermost/rsc/app/index.yaml | 16 + .../mattermost/rsc/appfs/appfile/main.go | 156 + .../mattermost/rsc/appfs/appmount/main.go | 287 + .../mattermost/rsc/appfs/client/client.go | 150 + vendor/github.com/mattermost/rsc/appfs/fs/fs.go | 273 + vendor/github.com/mattermost/rsc/appfs/fs/local.go | 82 + .../github.com/mattermost/rsc/appfs/proto/data.go | 55 + .../github.com/mattermost/rsc/appfs/server/app.go | 982 + vendor/github.com/mattermost/rsc/arq/arq.go | 663 + vendor/github.com/mattermost/rsc/arq/arqfs/main.go | 247 + vendor/github.com/mattermost/rsc/arq/crypto.go | 93 + vendor/github.com/mattermost/rsc/arq/data.go | 240 + vendor/github.com/mattermost/rsc/arq/hist/hist.go | 160 + vendor/github.com/mattermost/rsc/arq/unpack.go | 227 + vendor/github.com/mattermost/rsc/blog/atom/atom.go | 58 + vendor/github.com/mattermost/rsc/blog/main.go | 15 + vendor/github.com/mattermost/rsc/blog/post/post.go | 534 + vendor/github.com/mattermost/rsc/blog/post/qr.go | 34 + .../github.com/mattermost/rsc/cmd/crypt/crypt.go | 79 + .../github.com/mattermost/rsc/cmd/issue/issue.go | 185 + vendor/github.com/mattermost/rsc/cmd/jfmt/main.go | 37 + vendor/github.com/mattermost/rsc/crypt/crypt.go | 150 + vendor/github.com/mattermost/rsc/devweb/main.go | 317 + .../mattermost/rsc/devweb/slave/slave.go | 26 + vendor/github.com/mattermost/rsc/fuse/debug.go | 20 + vendor/github.com/mattermost/rsc/fuse/fuse.go | 1650 + .../github.com/mattermost/rsc/fuse/fuse_kernel.go | 539 + .../mattermost/rsc/fuse/fuse_kernel_darwin.go | 58 + .../mattermost/rsc/fuse/fuse_kernel_linux.go | 50 + .../mattermost/rsc/fuse/fuse_kernel_std.go | 1 + vendor/github.com/mattermost/rsc/fuse/fuse_test.go | 594 + .../mattermost/rsc/fuse/hellofs/hello.go | 62 + .../github.com/mattermost/rsc/fuse/mount_darwin.go | 122 + .../github.com/mattermost/rsc/fuse/mount_linux.go | 67 + vendor/github.com/mattermost/rsc/fuse/serve.go | 1022 + vendor/github.com/mattermost/rsc/fuse/tree.go | 93 + .../github.com/mattermost/rsc/gf256/blog_test.go | 85 + .../github.com/mattermost/rsc/gf256/gf256_test.go | 194 + .../mattermost/rsc/google/acme/Chat/main.go | 575 + vendor/github.com/mattermost/rsc/google/chat.go | 39 + .../mattermost/rsc/google/gmail/gmail.go | 1241 + .../mattermost/rsc/google/gmailsend/send.go | 370 + .../mattermost/rsc/google/googleserver/chat.go | 80 + .../mattermost/rsc/google/googleserver/main.go | 139 + vendor/github.com/mattermost/rsc/google/main.go | 181 + vendor/github.com/mattermost/rsc/gtfs/gtfs.pb.go | 440 + vendor/github.com/mattermost/rsc/imap/Makefile | 15 + vendor/github.com/mattermost/rsc/imap/decode.go | 227 + .../github.com/mattermost/rsc/imap/decode_test.go | 26 + vendor/github.com/mattermost/rsc/imap/imap.go | 1110 + vendor/github.com/mattermost/rsc/imap/imap_test.go | 433 + vendor/github.com/mattermost/rsc/imap/mail.go | 468 + vendor/github.com/mattermost/rsc/imap/mail_test.go | 335 + vendor/github.com/mattermost/rsc/imap/rfc2045.txt | 1739 ++ vendor/github.com/mattermost/rsc/imap/rfc2971.txt | 451 + vendor/github.com/mattermost/rsc/imap/rfc3501.txt | 6051 ++++ vendor/github.com/mattermost/rsc/imap/sx.go | 350 + vendor/github.com/mattermost/rsc/imap/sx_test.go | 60 + vendor/github.com/mattermost/rsc/imap/tcs.go | 602 + vendor/github.com/mattermost/rsc/keychain/doc.go | 28 + vendor/github.com/mattermost/rsc/keychain/mac.go | 107 + vendor/github.com/mattermost/rsc/mbta/Passages.pb | Bin 0 -> 69799 bytes vendor/github.com/mattermost/rsc/mbta/Vehicles.pb | Bin 0 -> 35798 bytes vendor/github.com/mattermost/rsc/mbta/mbta.go | 28 + vendor/github.com/mattermost/rsc/mkapp | 51 + vendor/github.com/mattermost/rsc/plist/plist.go | 179 + .../github.com/mattermost/rsc/plist/plist_test.go | 110 + .../github.com/mattermost/rsc/qr/coding/qr_test.go | 133 + .../mattermost/rsc/qr/libqrencode/Makefile | 4 + .../mattermost/rsc/qr/libqrencode/qrencode.go | 149 + vendor/github.com/mattermost/rsc/qr/png_test.go | 73 + vendor/github.com/mattermost/rsc/qr/web/pic.go | 506 + vendor/github.com/mattermost/rsc/qr/web/play.go | 1118 + .../mattermost/rsc/qr/web/resize/resize.go | 152 + .../mattermost/rsc/regexp/regmerge/copy.go | 225 + .../mattermost/rsc/regexp/regmerge/main.go | 96 + .../mattermost/rsc/regexp/regmerge/match.go | 406 + .../mattermost/rsc/regexp/regmerge/merge.go | 103 + .../mattermost/rsc/regexp/regmerge/sort.go | 199 + .../mattermost/rsc/regexp/regmerge/sparse.go | 80 + .../mattermost/rsc/regexp/regmerge/utf.go | 270 + .../mattermost/rsc/rosetta/graph/Makefile | 7 + .../mattermost/rsc/rosetta/graph/graph.go | 133 + .../mattermost/rsc/rosetta/maze/Makefile | 8 + .../github.com/mattermost/rsc/rosetta/maze/maze.go | 191 + vendor/github.com/mattermost/rsc/s3get/main.go | 85 + vendor/github.com/mattermost/rsc/smugmug/smug.go | 542 + .../mattermost/rsc/smugmug/smugup/main.go | 158 + vendor/github.com/mattermost/rsc/tmp/app | 1 + vendor/github.com/mattermost/rsc/tmp/appfs | 1 + vendor/github.com/mattermost/rsc/tmp/arq | 1 + vendor/github.com/mattermost/rsc/tmp/blog | 1 + vendor/github.com/mattermost/rsc/tmp/cmd | 1 + vendor/github.com/mattermost/rsc/tmp/crypt | 1 + vendor/github.com/mattermost/rsc/tmp/devweb | 1 + vendor/github.com/mattermost/rsc/tmp/fuse | 1 + vendor/github.com/mattermost/rsc/tmp/gf256 | 1 + vendor/github.com/mattermost/rsc/tmp/google | 1 + vendor/github.com/mattermost/rsc/tmp/gtfs | 1 + vendor/github.com/mattermost/rsc/tmp/imap | 1 + vendor/github.com/mattermost/rsc/tmp/keychain | 1 + vendor/github.com/mattermost/rsc/tmp/mbta | 1 + vendor/github.com/mattermost/rsc/tmp/mkapp | 1 + vendor/github.com/mattermost/rsc/tmp/plist | 1 + vendor/github.com/mattermost/rsc/tmp/qr | 1 + vendor/github.com/mattermost/rsc/tmp/regexp | 1 + vendor/github.com/mattermost/rsc/tmp/rosetta | 1 + vendor/github.com/mattermost/rsc/tmp/s3get | 1 + vendor/github.com/mattermost/rsc/tmp/smugmug | 1 + vendor/github.com/mattermost/rsc/tmp/xmpp | 1 + vendor/github.com/mattermost/rsc/xmpp/xmpp.go | 572 + vendor/github.com/mssola/user_agent/all_test.go | 529 + vendor/github.com/nicksnyder/go-i18n/.gitignore | 6 + vendor/github.com/nicksnyder/go-i18n/.travis.yml | 8 + vendor/github.com/nicksnyder/go-i18n/CHANGELOG | 5 + vendor/github.com/nicksnyder/go-i18n/README.md | 130 + vendor/github.com/nicksnyder/go-i18n/goi18n/doc.go | 59 + .../github.com/nicksnyder/go-i18n/goi18n/gendoc.sh | 10 + .../github.com/nicksnyder/go-i18n/goi18n/goi18n.go | 82 + .../github.com/nicksnyder/go-i18n/goi18n/merge.go | 127 + .../nicksnyder/go-i18n/goi18n/merge_test.go | 74 + .../nicksnyder/go-i18n/goi18n/testdata/en-us.yaml | 30 + .../goi18n/testdata/expected/ar-ar.all.json | 65 + .../testdata/expected/ar-ar.untranslated.json | 50 + .../goi18n/testdata/expected/en-us.all.json | 45 + .../testdata/expected/en-us.untranslated.json | 1 + .../goi18n/testdata/expected/fr-fr.all.json | 45 + .../testdata/expected/fr-fr.untranslated.json | 45 + .../go-i18n/goi18n/testdata/input/ar-ar.one.json | 54 + .../go-i18n/goi18n/testdata/input/ar-ar.two.json | 54 + .../go-i18n/goi18n/testdata/input/en-us.one.json | 30 + .../go-i18n/goi18n/testdata/input/en-us.two.json | 26 + .../go-i18n/goi18n/testdata/input/fr-fr.json | 0 .../goi18n/testdata/input/yaml/ar-ar.one.json | 54 + .../goi18n/testdata/input/yaml/ar-ar.two.json | 54 + .../goi18n/testdata/input/yaml/en-us.one.yaml | 19 + .../goi18n/testdata/input/yaml/en-us.two.json | 26 + .../go-i18n/goi18n/testdata/input/yaml/fr-fr.json | 0 .../nicksnyder/go-i18n/i18n/bundle/bundle_test.go | 289 + .../nicksnyder/go-i18n/i18n/example_test.go | 63 + .../go-i18n/i18n/exampletemplate_test.go | 63 + .../nicksnyder/go-i18n/i18n/exampleyaml_test.go | 62 + .../go-i18n/i18n/language/codegen/generate.sh | 5 + .../go-i18n/i18n/language/codegen/main.go | 132 + .../go-i18n/i18n/language/codegen/plurals.xml | 230 + .../go-i18n/i18n/language/codegen/xml.go | 143 + .../go-i18n/i18n/language/language_test.go | 85 + .../go-i18n/i18n/language/operands_test.go | 45 + .../go-i18n/i18n/language/plural_test.go | 28 + .../go-i18n/i18n/language/pluralspec_gen_test.go | 645 + .../go-i18n/i18n/language/pluralspec_test.go | 733 + .../i18n/translation/plural_translation_test.go | 308 + .../go-i18n/i18n/translation/template_test.go | 146 + .../go-i18n/i18n/translation/translation_test.go | 17 + vendor/github.com/pborman/uuid/dce.go | 0 vendor/github.com/pborman/uuid/doc.go | 0 vendor/github.com/pborman/uuid/json_test.go | 61 + vendor/github.com/pborman/uuid/node.go | 0 vendor/github.com/pborman/uuid/seq_test.go | 66 + vendor/github.com/pborman/uuid/sql_test.go | 96 + vendor/github.com/pborman/uuid/time.go | 0 vendor/github.com/pborman/uuid/uuid_test.go | 543 + vendor/github.com/rwcarlsen/goexif/.gitignore | 23 + vendor/github.com/rwcarlsen/goexif/README.md | 71 + .../goexif/exif/corrupt/huge_tag_exif.jpg | Bin 0 -> 65536 bytes .../goexif/exif/corrupt/infinite_loop_exif.jpg | Bin 0 -> 3738 bytes .../goexif/exif/corrupt/max_uint32_exif.jpg | Bin 0 -> 65536 bytes .../rwcarlsen/goexif/exif/example_test.go | 42 + .../github.com/rwcarlsen/goexif/exif/exif_test.go | 202 + .../rwcarlsen/goexif/exif/regress_expected_test.go | 2293 ++ ...004-01-11-22-45-15-sep-2004-01-11-22-45-15a.jpg | Bin 0 -> 4586 bytes ...006-08-03-16-29-38-sep-2006-08-03-16-29-38a.jpg | Bin 0 -> 9735 bytes ...006-11-11-19-17-56-sep-2006-11-11-19-17-56a.jpg | Bin 0 -> 35406 bytes ...006-12-10-23-58-20-sep-2006-12-10-23-58-20a.jpg | Bin 0 -> 8711 bytes ...006-12-17-07-09-14-sep-2006-12-17-07-09-14a.jpg | Bin 0 -> 38252 bytes ...006-12-21-15-55-26-sep-2006-12-21-15-55-26a.jpg | Bin 0 -> 16072 bytes ...007-01-01-12-00-00-sep-2007-01-01-12-00-00a.jpg | Bin 0 -> 17301 bytes ...007-01-17-21-49-44-sep-2007-01-17-21-49-44a.jpg | Bin 0 -> 7999 bytes ...007-02-02-18-13-29-sep-2007-02-02-18-13-29a.jpg | Bin 0 -> 39915 bytes ...007-05-02-17-02-21-sep-2007-05-02-17-02-21a.jpg | Bin 0 -> 11783 bytes ...007-05-12-08-19-07-sep-2007-05-12-08-19-07a.jpg | Bin 0 -> 35771 bytes ...007-05-26-04-49-45-sep-2007-05-26-04-49-45a.jpg | Bin 0 -> 35406 bytes ...007-05-30-14-28-01-sep-2007-05-30-14-28-01a.jpg | Bin 0 -> 35406 bytes ...007-06-06-16-15-25-sep-2007-06-06-16-15-25a.jpg | Bin 0 -> 35406 bytes ...007-06-26-10-13-04-sep-2007-06-26-10-13-04a.jpg | Bin 0 -> 7615 bytes ...007-07-13-17-02-30-sep-2007-07-13-17-02-30a.jpg | Bin 0 -> 21719 bytes ...007-08-15-14-42-46-sep-2007-08-15-14-42-46a.jpg | Bin 0 -> 11549 bytes ...007-08-24-02-40-42-sep-2007-08-24-02-40-42a.jpg | Bin 0 -> 7687 bytes ...007-11-07-11-40-44-sep-2007-11-07-11-40-44a.jpg | Bin 0 -> 11223 bytes ...008-06-02-10-03-57-sep-2008-06-02-10-03-57a.jpg | Bin 0 -> 9745 bytes ...008-06-06-13-29-29-sep-2008-06-06-13-29-29a.jpg | Bin 0 -> 11783 bytes ...008-06-17-01-21-30-sep-2008-06-17-01-21-30a.jpg | Bin 0 -> 14564 bytes ...008-09-02-17-43-48-sep-2008-09-02-17-43-48a.jpg | Bin 0 -> 5406 bytes ...009-03-26-09-23-20-sep-2009-03-26-09-23-20a.jpg | Bin 0 -> 10759 bytes ...009-04-11-03-01-38-sep-2009-04-11-03-01-38a.jpg | Bin 0 -> 43374 bytes ...009-04-23-07-21-35-sep-2009-04-23-07-21-35a.jpg | Bin 0 -> 37208 bytes ...009-06-11-19-23-18-sep-2009-06-11-19-23-18a.jpg | Bin 0 -> 7791 bytes ...009-06-20-07-59-05-sep-2009-06-20-07-59-05a.jpg | Bin 0 -> 13618 bytes ...009-08-05-08-11-31-sep-2009-08-05-08-11-31a.jpg | Bin 0 -> 9919 bytes ...010-06-08-04-44-24-sep-2010-06-08-04-44-24a.jpg | Bin 0 -> 10939 bytes ...010-06-20-20-07-39-sep-2010-06-20-20-07-39a.jpg | Bin 0 -> 8551 bytes ...010-09-02-08-43-02-sep-2010-09-02-08-43-02a.jpg | Bin 0 -> 19534 bytes ...011-01-24-22-06-02-sep-2011-01-24-22-06-02a.jpg | Bin 0 -> 29003 bytes ...011-03-07-09-28-03-sep-2011-03-07-09-28-03a.jpg | Bin 0 -> 10529 bytes ...011-05-07-13-02-49-sep-2011-05-07-13-02-49a.jpg | Bin 0 -> 23743 bytes ...011-08-07-19-22-57-sep-2011-08-07-19-22-57a.jpg | Bin 0 -> 9936 bytes ...011-10-28-17-50-18-sep-2011-10-28-17-50-18a.jpg | Bin 0 -> 7487 bytes ...2011-10-28-18-25-43-sep-2011-10-28-18-25-43.jpg | Bin 0 -> 7433 bytes .../2011-11-18-15-38-34-sep-Photo11181538.jpg | Bin 0 -> 12885 bytes ...2012-06-02-10-12-28-sep-2012-06-02-10-12-28.jpg | Bin 0 -> 32165 bytes ...2012-09-21-22-07-34-sep-2012-09-21-22-07-34.jpg | Bin 0 -> 10247 bytes .../2012-12-19-21-38-40-sep-temple_square1.jpg | Bin 0 -> 39182 bytes .../samples/2012-12-21-11-15-19-sep-IMG_0001.jpg | Bin 0 -> 25269 bytes .../samples/2013-02-05-23-12-09-sep-DSCI0001.jpg | Bin 0 -> 10854 bytes ...099-08-12-19-59-29-sep-2099-08-12-19-59-29a.jpg | Bin 0 -> 37491 bytes ...216-11-15-11-46-51-sep-2216-11-15-11-46-51a.jpg | Bin 0 -> 23011 bytes .../samples/FailedHash-NoDate-sep-remembory.jpg | Bin 0 -> 935 bytes .../rwcarlsen/goexif/exif/samples/f1-exif.jpg | Bin 0 -> 992 bytes .../rwcarlsen/goexif/exif/samples/f2-exif.jpg | Bin 0 -> 994 bytes .../rwcarlsen/goexif/exif/samples/f3-exif.jpg | Bin 0 -> 992 bytes .../rwcarlsen/goexif/exif/samples/f4-exif.jpg | Bin 0 -> 994 bytes .../rwcarlsen/goexif/exif/samples/f5-exif.jpg | Bin 0 -> 980 bytes .../rwcarlsen/goexif/exif/samples/f6-exif.jpg | Bin 0 -> 982 bytes .../rwcarlsen/goexif/exif/samples/f7-exif.jpg | Bin 0 -> 980 bytes .../rwcarlsen/goexif/exif/samples/f8-exif.jpg | Bin 0 -> 982 bytes .../goexif/exif/samples/geodegrees_as_string.jpg | Bin 0 -> 22420 bytes .../goexif/exif/samples/has-lens-info.jpg | Bin 0 -> 22493 bytes .../github.com/rwcarlsen/goexif/exifstat/main.go | 60 + .../github.com/rwcarlsen/goexif/mknote/fields.go | 268 + .../github.com/rwcarlsen/goexif/mknote/mknote.go | 70 + .../github.com/rwcarlsen/goexif/tiff/tiff_test.go | 235 + .../github.com/vaughan0/go-ini/ini_linux_test.go | 43 + vendor/github.com/vaughan0/go-ini/ini_test.go | 89 + vendor/golang.org/x/crypto/.gitattributes | 10 + vendor/golang.org/x/crypto/.gitignore | 2 + vendor/golang.org/x/crypto/AUTHORS | 3 + vendor/golang.org/x/crypto/CONTRIBUTING.md | 31 + vendor/golang.org/x/crypto/CONTRIBUTORS | 3 + vendor/golang.org/x/crypto/README | 3 + .../golang.org/x/crypto/acme/internal/acme/acme.go | 473 + .../x/crypto/acme/internal/acme/acme_test.go | 759 + .../golang.org/x/crypto/acme/internal/acme/jws.go | 67 + .../x/crypto/acme/internal/acme/jws_test.go | 139 + .../x/crypto/acme/internal/acme/types.go | 181 + vendor/golang.org/x/crypto/bcrypt/bcrypt.go | 2 +- vendor/golang.org/x/crypto/bcrypt/bcrypt_test.go | 226 + .../golang.org/x/crypto/blowfish/blowfish_test.go | 274 + vendor/golang.org/x/crypto/blowfish/cipher.go | 2 +- vendor/golang.org/x/crypto/bn256/bn256.go | 404 + vendor/golang.org/x/crypto/bn256/bn256_test.go | 304 + vendor/golang.org/x/crypto/bn256/constants.go | 44 + vendor/golang.org/x/crypto/bn256/curve.go | 278 + vendor/golang.org/x/crypto/bn256/example_test.go | 43 + vendor/golang.org/x/crypto/bn256/gfp12.go | 200 + vendor/golang.org/x/crypto/bn256/gfp2.go | 219 + vendor/golang.org/x/crypto/bn256/gfp6.go | 296 + vendor/golang.org/x/crypto/bn256/optate.go | 395 + vendor/golang.org/x/crypto/bn256/twist.go | 249 + vendor/golang.org/x/crypto/cast5/cast5.go | 526 + vendor/golang.org/x/crypto/cast5/cast5_test.go | 106 + vendor/golang.org/x/crypto/codereview.cfg | 1 + .../golang.org/x/crypto/curve25519/const_amd64.s | 20 + .../golang.org/x/crypto/curve25519/cswap_amd64.s | 88 + .../golang.org/x/crypto/curve25519/curve25519.go | 841 + .../x/crypto/curve25519/curve25519_test.go | 29 + vendor/golang.org/x/crypto/curve25519/doc.go | 23 + .../golang.org/x/crypto/curve25519/freeze_amd64.s | 94 + .../x/crypto/curve25519/ladderstep_amd64.s | 1398 + .../x/crypto/curve25519/mont25519_amd64.go | 240 + vendor/golang.org/x/crypto/curve25519/mul_amd64.s | 191 + .../golang.org/x/crypto/curve25519/square_amd64.s | 153 + vendor/golang.org/x/crypto/ed25519/ed25519.go | 181 + vendor/golang.org/x/crypto/ed25519/ed25519_test.go | 183 + .../crypto/ed25519/internal/edwards25519/const.go | 1422 + .../ed25519/internal/edwards25519/edwards25519.go | 1771 ++ .../x/crypto/ed25519/testdata/sign.input.gz | Bin 0 -> 50330 bytes vendor/golang.org/x/crypto/hkdf/example_test.go | 61 + vendor/golang.org/x/crypto/hkdf/hkdf.go | 75 + vendor/golang.org/x/crypto/hkdf/hkdf_test.go | 370 + vendor/golang.org/x/crypto/md4/md4.go | 118 + vendor/golang.org/x/crypto/md4/md4_test.go | 71 + vendor/golang.org/x/crypto/md4/md4block.go | 89 + vendor/golang.org/x/crypto/nacl/box/box.go | 85 + vendor/golang.org/x/crypto/nacl/box/box_test.go | 78 + .../x/crypto/nacl/secretbox/secretbox.go | 149 + .../x/crypto/nacl/secretbox/secretbox_test.go | 91 + vendor/golang.org/x/crypto/ocsp/ocsp.go | 673 + vendor/golang.org/x/crypto/ocsp/ocsp_test.go | 584 + vendor/golang.org/x/crypto/openpgp/armor/armor.go | 219 + .../x/crypto/openpgp/armor/armor_test.go | 95 + vendor/golang.org/x/crypto/openpgp/armor/encode.go | 160 + .../golang.org/x/crypto/openpgp/canonical_text.go | 59 + .../x/crypto/openpgp/canonical_text_test.go | 52 + .../x/crypto/openpgp/clearsign/clearsign.go | 376 + .../x/crypto/openpgp/clearsign/clearsign_test.go | 210 + .../golang.org/x/crypto/openpgp/elgamal/elgamal.go | 122 + .../x/crypto/openpgp/elgamal/elgamal_test.go | 49 + .../golang.org/x/crypto/openpgp/errors/errors.go | 72 + vendor/golang.org/x/crypto/openpgp/keys.go | 633 + vendor/golang.org/x/crypto/openpgp/keys_test.go | 370 + .../x/crypto/openpgp/packet/compressed.go | 123 + .../x/crypto/openpgp/packet/compressed_test.go | 41 + .../golang.org/x/crypto/openpgp/packet/config.go | 91 + .../x/crypto/openpgp/packet/encrypted_key.go | 199 + .../x/crypto/openpgp/packet/encrypted_key_test.go | 146 + .../golang.org/x/crypto/openpgp/packet/literal.go | 89 + vendor/golang.org/x/crypto/openpgp/packet/ocfb.go | 143 + .../x/crypto/openpgp/packet/ocfb_test.go | 46 + .../x/crypto/openpgp/packet/one_pass_signature.go | 73 + .../golang.org/x/crypto/openpgp/packet/opaque.go | 162 + .../x/crypto/openpgp/packet/opaque_test.go | 67 + .../golang.org/x/crypto/openpgp/packet/packet.go | 539 + .../x/crypto/openpgp/packet/packet_test.go | 255 + .../x/crypto/openpgp/packet/private_key.go | 362 + .../x/crypto/openpgp/packet/private_key_test.go | 126 + .../x/crypto/openpgp/packet/public_key.go | 750 + .../x/crypto/openpgp/packet/public_key_test.go | 202 + .../x/crypto/openpgp/packet/public_key_v3.go | 280 + .../x/crypto/openpgp/packet/public_key_v3_test.go | 82 + .../golang.org/x/crypto/openpgp/packet/reader.go | 76 + .../x/crypto/openpgp/packet/signature.go | 706 + .../x/crypto/openpgp/packet/signature_test.go | 42 + .../x/crypto/openpgp/packet/signature_v3.go | 146 + .../x/crypto/openpgp/packet/signature_v3_test.go | 92 + .../openpgp/packet/symmetric_key_encrypted.go | 155 + .../openpgp/packet/symmetric_key_encrypted_test.go | 103 + .../openpgp/packet/symmetrically_encrypted.go | 290 + .../openpgp/packet/symmetrically_encrypted_test.go | 123 + .../x/crypto/openpgp/packet/userattribute.go | 91 + .../x/crypto/openpgp/packet/userattribute_test.go | 109 + .../golang.org/x/crypto/openpgp/packet/userid.go | 160 + .../x/crypto/openpgp/packet/userid_test.go | 87 + vendor/golang.org/x/crypto/openpgp/read.go | 442 + vendor/golang.org/x/crypto/openpgp/read_test.go | 613 + vendor/golang.org/x/crypto/openpgp/s2k/s2k.go | 273 + vendor/golang.org/x/crypto/openpgp/s2k/s2k_test.go | 137 + vendor/golang.org/x/crypto/openpgp/write.go | 378 + vendor/golang.org/x/crypto/openpgp/write_test.go | 273 + .../golang.org/x/crypto/otr/libotr_test_helper.c | 197 + vendor/golang.org/x/crypto/otr/otr.go | 1408 + vendor/golang.org/x/crypto/otr/otr_test.go | 470 + vendor/golang.org/x/crypto/otr/smp.go | 572 + vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go | 77 + vendor/golang.org/x/crypto/pbkdf2/pbkdf2_test.go | 157 + vendor/golang.org/x/crypto/pkcs12/bmp-string.go | 50 + .../golang.org/x/crypto/pkcs12/bmp-string_test.go | 63 + vendor/golang.org/x/crypto/pkcs12/crypto.go | 131 + vendor/golang.org/x/crypto/pkcs12/crypto_test.go | 125 + vendor/golang.org/x/crypto/pkcs12/errors.go | 23 + .../x/crypto/pkcs12/internal/rc2/bench_test.go | 27 + .../golang.org/x/crypto/pkcs12/internal/rc2/rc2.go | 274 + .../x/crypto/pkcs12/internal/rc2/rc2_test.go | 93 + vendor/golang.org/x/crypto/pkcs12/mac.go | 45 + vendor/golang.org/x/crypto/pkcs12/mac_test.go | 42 + vendor/golang.org/x/crypto/pkcs12/pbkdf.go | 170 + vendor/golang.org/x/crypto/pkcs12/pbkdf_test.go | 34 + vendor/golang.org/x/crypto/pkcs12/pkcs12.go | 342 + vendor/golang.org/x/crypto/pkcs12/pkcs12_test.go | 138 + vendor/golang.org/x/crypto/pkcs12/safebags.go | 57 + vendor/golang.org/x/crypto/poly1305/const_amd64.s | 45 + vendor/golang.org/x/crypto/poly1305/poly1305.go | 32 + .../golang.org/x/crypto/poly1305/poly1305_amd64.s | 497 + vendor/golang.org/x/crypto/poly1305/poly1305_arm.s | 379 + .../golang.org/x/crypto/poly1305/poly1305_test.go | 86 + vendor/golang.org/x/crypto/poly1305/sum_amd64.go | 24 + vendor/golang.org/x/crypto/poly1305/sum_arm.go | 24 + vendor/golang.org/x/crypto/poly1305/sum_ref.go | 1531 + vendor/golang.org/x/crypto/ripemd160/ripemd160.go | 120 + .../x/crypto/ripemd160/ripemd160_test.go | 64 + .../x/crypto/ripemd160/ripemd160block.go | 161 + .../golang.org/x/crypto/salsa20/salsa/hsalsa20.go | 144 + .../x/crypto/salsa20/salsa/salsa2020_amd64.s | 902 + .../golang.org/x/crypto/salsa20/salsa/salsa208.go | 199 + .../x/crypto/salsa20/salsa/salsa20_amd64.go | 23 + .../x/crypto/salsa20/salsa/salsa20_ref.go | 234 + .../x/crypto/salsa20/salsa/salsa_test.go | 35 + vendor/golang.org/x/crypto/salsa20/salsa20.go | 54 + vendor/golang.org/x/crypto/salsa20/salsa20_test.go | 139 + vendor/golang.org/x/crypto/scrypt/scrypt.go | 243 + vendor/golang.org/x/crypto/scrypt/scrypt_test.go | 160 + vendor/golang.org/x/crypto/sha3/doc.go | 66 + vendor/golang.org/x/crypto/sha3/hashes.go | 65 + vendor/golang.org/x/crypto/sha3/keccakf.go | 410 + vendor/golang.org/x/crypto/sha3/register.go | 18 + vendor/golang.org/x/crypto/sha3/sha3.go | 193 + vendor/golang.org/x/crypto/sha3/sha3_test.go | 306 + vendor/golang.org/x/crypto/sha3/shake.go | 60 + .../x/crypto/sha3/testdata/keccakKats.json.deflate | Bin 0 -> 521342 bytes vendor/golang.org/x/crypto/sha3/xor.go | 16 + vendor/golang.org/x/crypto/sha3/xor_generic.go | 28 + vendor/golang.org/x/crypto/sha3/xor_unaligned.go | 58 + vendor/golang.org/x/crypto/ssh/agent/client.go | 622 + .../golang.org/x/crypto/ssh/agent/client_test.go | 287 + .../golang.org/x/crypto/ssh/agent/example_test.go | 40 + vendor/golang.org/x/crypto/ssh/agent/forward.go | 103 + vendor/golang.org/x/crypto/ssh/agent/keyring.go | 184 + .../golang.org/x/crypto/ssh/agent/keyring_test.go | 78 + vendor/golang.org/x/crypto/ssh/agent/server.go | 420 + .../golang.org/x/crypto/ssh/agent/server_test.go | 186 + .../golang.org/x/crypto/ssh/agent/testdata_test.go | 64 + vendor/golang.org/x/crypto/ssh/benchmark_test.go | 122 + vendor/golang.org/x/crypto/ssh/buffer.go | 98 + vendor/golang.org/x/crypto/ssh/buffer_test.go | 87 + vendor/golang.org/x/crypto/ssh/certs.go | 503 + vendor/golang.org/x/crypto/ssh/certs_test.go | 216 + vendor/golang.org/x/crypto/ssh/channel.go | 631 + vendor/golang.org/x/crypto/ssh/cipher.go | 552 + vendor/golang.org/x/crypto/ssh/cipher_test.go | 127 + vendor/golang.org/x/crypto/ssh/client.go | 213 + vendor/golang.org/x/crypto/ssh/client_auth.go | 439 + vendor/golang.org/x/crypto/ssh/client_auth_test.go | 393 + vendor/golang.org/x/crypto/ssh/client_test.go | 39 + vendor/golang.org/x/crypto/ssh/common.go | 356 + vendor/golang.org/x/crypto/ssh/connection.go | 144 + vendor/golang.org/x/crypto/ssh/doc.go | 18 + vendor/golang.org/x/crypto/ssh/example_test.go | 243 + vendor/golang.org/x/crypto/ssh/handshake.go | 449 + vendor/golang.org/x/crypto/ssh/handshake_test.go | 486 + vendor/golang.org/x/crypto/ssh/kex.go | 526 + vendor/golang.org/x/crypto/ssh/kex_test.go | 50 + vendor/golang.org/x/crypto/ssh/keys.go | 846 + vendor/golang.org/x/crypto/ssh/keys_test.go | 440 + vendor/golang.org/x/crypto/ssh/mac.go | 57 + vendor/golang.org/x/crypto/ssh/mempipe_test.go | 110 + vendor/golang.org/x/crypto/ssh/messages.go | 758 + vendor/golang.org/x/crypto/ssh/messages_test.go | 288 + vendor/golang.org/x/crypto/ssh/mux.go | 330 + vendor/golang.org/x/crypto/ssh/mux_test.go | 502 + vendor/golang.org/x/crypto/ssh/server.go | 489 + vendor/golang.org/x/crypto/ssh/session.go | 612 + vendor/golang.org/x/crypto/ssh/session_test.go | 774 + vendor/golang.org/x/crypto/ssh/tcpip.go | 407 + vendor/golang.org/x/crypto/ssh/tcpip_test.go | 20 + .../golang.org/x/crypto/ssh/terminal/terminal.go | 892 + .../x/crypto/ssh/terminal/terminal_test.go | 291 + vendor/golang.org/x/crypto/ssh/terminal/util.go | 128 + .../golang.org/x/crypto/ssh/terminal/util_bsd.go | 12 + .../golang.org/x/crypto/ssh/terminal/util_linux.go | 11 + .../golang.org/x/crypto/ssh/terminal/util_plan9.go | 58 + .../x/crypto/ssh/terminal/util_windows.go | 174 + .../x/crypto/ssh/test/agent_unix_test.go | 59 + vendor/golang.org/x/crypto/ssh/test/cert_test.go | 47 + vendor/golang.org/x/crypto/ssh/test/doc.go | 7 + .../x/crypto/ssh/test/forward_unix_test.go | 160 + .../golang.org/x/crypto/ssh/test/session_test.go | 365 + vendor/golang.org/x/crypto/ssh/test/tcpip_test.go | 46 + .../golang.org/x/crypto/ssh/test/test_unix_test.go | 268 + .../golang.org/x/crypto/ssh/test/testdata_test.go | 64 + vendor/golang.org/x/crypto/ssh/testdata/doc.go | 8 + vendor/golang.org/x/crypto/ssh/testdata/keys.go | 57 + vendor/golang.org/x/crypto/ssh/testdata_test.go | 63 + vendor/golang.org/x/crypto/ssh/transport.go | 328 + vendor/golang.org/x/crypto/ssh/transport_test.go | 109 + vendor/golang.org/x/crypto/tea/cipher.go | 109 + vendor/golang.org/x/crypto/tea/tea_test.go | 93 + vendor/golang.org/x/crypto/twofish/twofish.go | 342 + vendor/golang.org/x/crypto/twofish/twofish_test.go | 129 + vendor/golang.org/x/crypto/xtea/block.go | 66 + vendor/golang.org/x/crypto/xtea/cipher.go | 82 + vendor/golang.org/x/crypto/xtea/xtea_test.go | 229 + vendor/golang.org/x/crypto/xts/xts.go | 138 + vendor/golang.org/x/crypto/xts/xts_test.go | 85 + vendor/golang.org/x/image/.gitattributes | 10 + vendor/golang.org/x/image/.gitignore | 2 + vendor/golang.org/x/image/AUTHORS | 3 + vendor/golang.org/x/image/CONTRIBUTING.md | 31 + vendor/golang.org/x/image/CONTRIBUTORS | 3 + vendor/golang.org/x/image/README | 3 + vendor/golang.org/x/image/bmp/reader.go | 2 +- vendor/golang.org/x/image/bmp/reader_test.go | 75 + vendor/golang.org/x/image/bmp/writer_test.go | 91 + .../x/image/cmd/webp-manual-test/main.go | 215 + vendor/golang.org/x/image/codereview.cfg | 1 + vendor/golang.org/x/image/colornames/colornames.go | 10 + .../x/image/colornames/colornames_test.go | 42 + vendor/golang.org/x/image/colornames/gen.go | 187 + vendor/golang.org/x/image/colornames/table.go | 307 + vendor/golang.org/x/image/draw/draw.go | 79 + vendor/golang.org/x/image/draw/example_test.go | 118 + vendor/golang.org/x/image/draw/gen.go | 1403 + vendor/golang.org/x/image/draw/impl.go | 6668 ++++ vendor/golang.org/x/image/draw/scale.go | 527 + vendor/golang.org/x/image/draw/scale_test.go | 731 + vendor/golang.org/x/image/draw/stdlib_test.go | 96 + vendor/golang.org/x/image/example/font/main.go | 106 + .../golang.org/x/image/font/basicfont/basicfont.go | 122 + vendor/golang.org/x/image/font/basicfont/data.go | 1456 + vendor/golang.org/x/image/font/basicfont/gen.go | 115 + vendor/golang.org/x/image/font/font.go | 2 +- .../x/image/font/plan9font/example_test.go | 92 + .../golang.org/x/image/font/plan9font/plan9font.go | 585 + .../x/image/font/plan9font/plan9font_test.go | 24 + .../x/image/font/testdata/fixed/7x13.0000 | Bin 0 -> 3136 bytes .../x/image/font/testdata/fixed/7x13.0100 | Bin 0 -> 3908 bytes .../x/image/font/testdata/fixed/7x13.0200 | Bin 0 -> 3095 bytes .../x/image/font/testdata/fixed/7x13.0300 | Bin 0 -> 2631 bytes .../x/image/font/testdata/fixed/7x13.0400 | Bin 0 -> 3623 bytes .../x/image/font/testdata/fixed/7x13.0500 | Bin 0 -> 2492 bytes .../x/image/font/testdata/fixed/7x13.0E00 | Bin 0 -> 1235 bytes .../x/image/font/testdata/fixed/7x13.1000 | Bin 0 -> 2354 bytes .../x/image/font/testdata/fixed/7x13.1600 | Bin 0 -> 1825 bytes .../x/image/font/testdata/fixed/7x13.1E00 | Bin 0 -> 3713 bytes .../x/image/font/testdata/fixed/7x13.1F00 | Bin 0 -> 3012 bytes .../x/image/font/testdata/fixed/7x13.2000 | Bin 0 -> 2310 bytes .../x/image/font/testdata/fixed/7x13.2100 | Bin 0 -> 3206 bytes .../x/image/font/testdata/fixed/7x13.2200 | Bin 0 -> 3532 bytes .../x/image/font/testdata/fixed/7x13.2300 | Bin 0 -> 1613 bytes .../x/image/font/testdata/fixed/7x13.2400 | Bin 0 -> 1013 bytes .../x/image/font/testdata/fixed/7x13.2500 | Bin 0 -> 2747 bytes .../x/image/font/testdata/fixed/7x13.2600 | Bin 0 -> 1765 bytes .../x/image/font/testdata/fixed/7x13.2700 | Bin 0 -> 1762 bytes .../x/image/font/testdata/fixed/7x13.2800 | Bin 0 -> 1918 bytes .../x/image/font/testdata/fixed/7x13.2A00 | Bin 0 -> 620 bytes .../x/image/font/testdata/fixed/7x13.3000 | Bin 0 -> 569 bytes .../x/image/font/testdata/fixed/7x13.FB00 | Bin 0 -> 912 bytes .../x/image/font/testdata/fixed/7x13.FE00 | Bin 0 -> 387 bytes .../x/image/font/testdata/fixed/7x13.FF00 | Bin 0 -> 1687 bytes .../golang.org/x/image/font/testdata/fixed/README | 9 + .../x/image/font/testdata/fixed/unicode.7x13.font | 68 + vendor/golang.org/x/image/math/f32/f32.go | 37 + vendor/golang.org/x/image/math/f64/f64.go | 37 + vendor/golang.org/x/image/math/fixed/fixed.go | 2 +- vendor/golang.org/x/image/math/fixed/fixed_test.go | 110 + vendor/golang.org/x/image/riff/example_test.go | 113 + vendor/golang.org/x/image/riff/riff.go | 179 + .../testdata/blue-purple-pink-large.lossless.webp | Bin 0 -> 175232 bytes .../blue-purple-pink-large.no-filter.lossy.webp | Bin 0 -> 22678 bytes ...urple-pink-large.no-filter.lossy.webp.ycbcr.png | Bin 0 -> 118713 bytes ...blue-purple-pink-large.normal-filter.lossy.webp | Bin 0 -> 22680 bytes ...e-pink-large.normal-filter.lossy.webp.ycbcr.png | Bin 0 -> 142114 bytes .../x/image/testdata/blue-purple-pink-large.png | Bin 0 -> 255171 bytes ...blue-purple-pink-large.simple-filter.lossy.webp | Bin 0 -> 22680 bytes ...e-pink-large.simple-filter.lossy.webp.ycbcr.png | Bin 0 -> 132078 bytes .../image/testdata/blue-purple-pink.lossless.webp | Bin 0 -> 19574 bytes .../x/image/testdata/blue-purple-pink.lossy.webp | Bin 0 -> 2450 bytes .../testdata/blue-purple-pink.lossy.webp.ycbcr.png | Bin 0 -> 11482 bytes .../testdata/blue-purple-pink.lzwcompressed.tiff | Bin 0 -> 38994 bytes .../x/image/testdata/blue-purple-pink.png | Bin 0 -> 25003 bytes vendor/golang.org/x/image/testdata/bw-deflate.tiff | Bin 0 -> 594 bytes .../golang.org/x/image/testdata/bw-packbits.tiff | Bin 0 -> 890 bytes .../x/image/testdata/bw-uncompressed.tiff | Bin 0 -> 1396 bytes .../x/image/testdata/go-turns-two-14x18.png | Bin 0 -> 798 bytes .../x/image/testdata/go-turns-two-280x360.jpeg | Bin 0 -> 36888 bytes .../x/image/testdata/go-turns-two-down-ab.png | Bin 0 -> 21338 bytes .../x/image/testdata/go-turns-two-down-bl.png | Bin 0 -> 18581 bytes .../x/image/testdata/go-turns-two-down-cr.png | Bin 0 -> 19519 bytes .../x/image/testdata/go-turns-two-down-nn.png | Bin 0 -> 21504 bytes .../x/image/testdata/go-turns-two-rotate-ab.png | Bin 0 -> 7654 bytes .../x/image/testdata/go-turns-two-rotate-bl.png | Bin 0 -> 7653 bytes .../x/image/testdata/go-turns-two-rotate-cr.png | Bin 0 -> 7808 bytes .../x/image/testdata/go-turns-two-rotate-nn.png | Bin 0 -> 4915 bytes .../x/image/testdata/go-turns-two-up-ab.png | Bin 0 -> 9633 bytes .../x/image/testdata/go-turns-two-up-bl.png | Bin 0 -> 9639 bytes .../x/image/testdata/go-turns-two-up-cr.png | Bin 0 -> 10987 bytes .../x/image/testdata/go-turns-two-up-nn.png | Bin 0 -> 1368 bytes .../x/image/testdata/gopher-doc.1bpp.lossless.webp | Bin 0 -> 442 bytes .../x/image/testdata/gopher-doc.1bpp.png | Bin 0 -> 1026 bytes .../x/image/testdata/gopher-doc.2bpp.lossless.webp | Bin 0 -> 772 bytes .../x/image/testdata/gopher-doc.2bpp.png | Bin 0 -> 1544 bytes .../x/image/testdata/gopher-doc.4bpp.lossless.webp | Bin 0 -> 1456 bytes .../x/image/testdata/gopher-doc.4bpp.png | Bin 0 -> 2667 bytes .../x/image/testdata/gopher-doc.8bpp.lossless.webp | Bin 0 -> 3504 bytes .../x/image/testdata/gopher-doc.8bpp.png | Bin 0 -> 6839 bytes .../golang.org/x/image/testdata/no_compress.tiff | Bin 0 -> 1142 bytes vendor/golang.org/x/image/testdata/no_rps.tiff | Bin 0 -> 1294 bytes vendor/golang.org/x/image/testdata/testpattern.png | Bin 0 -> 3195 bytes .../golang.org/x/image/testdata/tux-rotate-ab.png | Bin 0 -> 3237 bytes .../golang.org/x/image/testdata/tux-rotate-bl.png | Bin 0 -> 3751 bytes .../golang.org/x/image/testdata/tux-rotate-cr.png | Bin 0 -> 3753 bytes .../golang.org/x/image/testdata/tux-rotate-nn.png | Bin 0 -> 3055 bytes .../golang.org/x/image/testdata/tux.lossless.webp | Bin 0 -> 29920 bytes vendor/golang.org/x/image/testdata/tux.png | Bin 0 -> 41427 bytes .../x/image/testdata/video-001-16bit.tiff | Bin 0 -> 42146 bytes .../x/image/testdata/video-001-gray-16bit.tiff | Bin 0 -> 31254 bytes .../x/image/testdata/video-001-gray.tiff | Bin 0 -> 15742 bytes .../x/image/testdata/video-001-paletted.tiff | Bin 0 -> 11214 bytes .../x/image/testdata/video-001-strip-64.tiff | Bin 0 -> 30916 bytes .../x/image/testdata/video-001-tile-64x64.tiff | Bin 0 -> 56404 bytes .../x/image/testdata/video-001-uncompressed.tiff | Bin 0 -> 46674 bytes vendor/golang.org/x/image/testdata/video-001.bmp | Bin 0 -> 46610 bytes .../x/image/testdata/video-001.lossy.webp | Bin 0 -> 3266 bytes .../image/testdata/video-001.lossy.webp.ycbcr.png | Bin 0 -> 12501 bytes vendor/golang.org/x/image/testdata/video-001.png | Bin 0 -> 29228 bytes vendor/golang.org/x/image/testdata/video-001.tiff | Bin 0 -> 30810 bytes .../x/image/testdata/yellow_rose-small.bmp | Bin 0 -> 822 bytes .../x/image/testdata/yellow_rose-small.png | Bin 0 -> 692 bytes .../x/image/testdata/yellow_rose.lossless.webp | Bin 0 -> 90752 bytes .../testdata/yellow_rose.lossy-with-alpha.webp | Bin 0 -> 11572 bytes .../yellow_rose.lossy-with-alpha.webp.nycbcra.png | Bin 0 -> 67874 bytes .../x/image/testdata/yellow_rose.lossy.webp | Bin 0 -> 14708 bytes .../testdata/yellow_rose.lossy.webp.ycbcr.png | Bin 0 -> 60923 bytes vendor/golang.org/x/image/testdata/yellow_rose.png | Bin 0 -> 125392 bytes vendor/golang.org/x/image/tiff/buffer_test.go | 36 + vendor/golang.org/x/image/tiff/lzw/reader.go | 2 +- vendor/golang.org/x/image/tiff/reader.go | 2 +- vendor/golang.org/x/image/tiff/reader_test.go | 377 + vendor/golang.org/x/image/tiff/writer_test.go | 95 + vendor/golang.org/x/image/vp8/decode.go | 403 + vendor/golang.org/x/image/vp8/filter.go | 273 + vendor/golang.org/x/image/vp8/idct.go | 98 + vendor/golang.org/x/image/vp8/partition.go | 129 + vendor/golang.org/x/image/vp8/pred.go | 201 + vendor/golang.org/x/image/vp8/predfunc.go | 553 + vendor/golang.org/x/image/vp8/quant.go | 98 + vendor/golang.org/x/image/vp8/reconstruct.go | 442 + vendor/golang.org/x/image/vp8/token.go | 381 + vendor/golang.org/x/image/vp8l/decode.go | 603 + vendor/golang.org/x/image/vp8l/huffman.go | 245 + vendor/golang.org/x/image/vp8l/transform.go | 299 + vendor/golang.org/x/image/webp/decode.go | 274 + vendor/golang.org/x/image/webp/decode_test.go | 294 + vendor/golang.org/x/image/webp/nycbcra/nycbcra.go | 194 + vendor/golang.org/x/sys/.gitattributes | 10 + vendor/golang.org/x/sys/.gitignore | 2 + vendor/golang.org/x/sys/AUTHORS | 3 + vendor/golang.org/x/sys/CONTRIBUTING.md | 31 + vendor/golang.org/x/sys/CONTRIBUTORS | 3 + vendor/golang.org/x/sys/README | 3 + vendor/golang.org/x/sys/codereview.cfg | 1 + vendor/golang.org/x/sys/plan9/asm.s | 8 + vendor/golang.org/x/sys/plan9/asm_plan9_386.s | 30 + vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s | 30 + vendor/golang.org/x/sys/plan9/const_plan9.go | 70 + vendor/golang.org/x/sys/plan9/dir_plan9.go | 212 + vendor/golang.org/x/sys/plan9/env_plan9.go | 27 + vendor/golang.org/x/sys/plan9/env_unset.go | 14 + vendor/golang.org/x/sys/plan9/errors_plan9.go | 50 + vendor/golang.org/x/sys/plan9/mkall.sh | 138 + vendor/golang.org/x/sys/plan9/mkerrors.sh | 246 + vendor/golang.org/x/sys/plan9/mksyscall.pl | 319 + vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh | 23 + vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go | 21 + vendor/golang.org/x/sys/plan9/pwd_plan9.go | 23 + vendor/golang.org/x/sys/plan9/race.go | 30 + vendor/golang.org/x/sys/plan9/race0.go | 25 + vendor/golang.org/x/sys/plan9/str.go | 22 + vendor/golang.org/x/sys/plan9/syscall.go | 74 + vendor/golang.org/x/sys/plan9/syscall_plan9.go | 349 + vendor/golang.org/x/sys/plan9/syscall_test.go | 33 + .../golang.org/x/sys/plan9/zsyscall_plan9_386.go | 292 + .../golang.org/x/sys/plan9/zsyscall_plan9_amd64.go | 292 + vendor/golang.org/x/sys/plan9/zsysnum_plan9.go | 49 + vendor/golang.org/x/sys/unix/asm_dragonfly_386.s | 29 - vendor/golang.org/x/sys/unix/creds_test.go | 121 + vendor/golang.org/x/sys/unix/export_test.go | 9 + vendor/golang.org/x/sys/unix/mkall.sh | 0 vendor/golang.org/x/sys/unix/mkerrors.sh | 0 vendor/golang.org/x/sys/unix/mksyscall.pl | 0 vendor/golang.org/x/sys/unix/mksyscall_solaris.pl | 0 vendor/golang.org/x/sys/unix/mksysctl_openbsd.pl | 0 vendor/golang.org/x/sys/unix/mksysnum_darwin.pl | 0 vendor/golang.org/x/sys/unix/mksysnum_dragonfly.pl | 0 vendor/golang.org/x/sys/unix/mksysnum_freebsd.pl | 0 vendor/golang.org/x/sys/unix/mksysnum_linux.pl | 0 vendor/golang.org/x/sys/unix/mksysnum_netbsd.pl | 0 vendor/golang.org/x/sys/unix/mksysnum_openbsd.pl | 0 vendor/golang.org/x/sys/unix/mmap_unix_test.go | 23 + vendor/golang.org/x/sys/unix/syscall.go | 2 +- vendor/golang.org/x/sys/unix/syscall_bsd_test.go | 47 + .../golang.org/x/sys/unix/syscall_dragonfly_386.go | 61 - .../golang.org/x/sys/unix/syscall_freebsd_test.go | 24 + vendor/golang.org/x/sys/unix/syscall_linux_test.go | 110 + vendor/golang.org/x/sys/unix/syscall_test.go | 50 + vendor/golang.org/x/sys/unix/syscall_unix_test.go | 353 + .../golang.org/x/sys/unix/zerrors_dragonfly_386.go | 1530 - .../x/sys/unix/zsyscall_dragonfly_386.go | 1412 - .../golang.org/x/sys/unix/zsysnum_dragonfly_386.go | 304 - .../golang.org/x/sys/unix/ztypes_dragonfly_386.go | 437 - vendor/golang.org/x/sys/windows/asm_windows_386.s | 13 + .../golang.org/x/sys/windows/asm_windows_amd64.s | 13 + vendor/golang.org/x/sys/windows/dll_windows.go | 374 + vendor/golang.org/x/sys/windows/env_unset.go | 15 + vendor/golang.org/x/sys/windows/env_windows.go | 25 + vendor/golang.org/x/sys/windows/eventlog.go | 20 + vendor/golang.org/x/sys/windows/exec_windows.go | 97 + vendor/golang.org/x/sys/windows/race.go | 30 + vendor/golang.org/x/sys/windows/race0.go | 25 + .../x/sys/windows/registry/export_test.go | 11 + vendor/golang.org/x/sys/windows/registry/key.go | 178 + .../x/sys/windows/registry/registry_test.go | 756 + .../golang.org/x/sys/windows/registry/syscall.go | 33 + vendor/golang.org/x/sys/windows/registry/value.go | 384 + .../x/sys/windows/registry/zsyscall_windows.go | 85 + .../golang.org/x/sys/windows/security_windows.go | 435 + vendor/golang.org/x/sys/windows/service.go | 143 + vendor/golang.org/x/sys/windows/str.go | 22 + vendor/golang.org/x/sys/windows/svc/debug/log.go | 56 + .../golang.org/x/sys/windows/svc/debug/service.go | 45 + vendor/golang.org/x/sys/windows/svc/event.go | 48 + .../x/sys/windows/svc/eventlog/install.go | 80 + .../golang.org/x/sys/windows/svc/eventlog/log.go | 70 + .../x/sys/windows/svc/eventlog/log_test.go | 51 + .../golang.org/x/sys/windows/svc/example/beep.go | 22 + .../x/sys/windows/svc/example/install.go | 92 + .../golang.org/x/sys/windows/svc/example/main.go | 76 + .../golang.org/x/sys/windows/svc/example/manage.go | 62 + .../x/sys/windows/svc/example/service.go | 82 + vendor/golang.org/x/sys/windows/svc/go12.c | 24 + vendor/golang.org/x/sys/windows/svc/go12.go | 11 + vendor/golang.org/x/sys/windows/svc/go13.go | 31 + vendor/golang.org/x/sys/windows/svc/mgr/config.go | 139 + vendor/golang.org/x/sys/windows/svc/mgr/mgr.go | 119 + .../golang.org/x/sys/windows/svc/mgr/mgr_test.go | 154 + vendor/golang.org/x/sys/windows/svc/mgr/service.go | 74 + vendor/golang.org/x/sys/windows/svc/security.go | 62 + vendor/golang.org/x/sys/windows/svc/service.go | 316 + vendor/golang.org/x/sys/windows/svc/svc_test.go | 118 + vendor/golang.org/x/sys/windows/svc/sys_386.s | 67 + vendor/golang.org/x/sys/windows/svc/sys_amd64.s | 41 + vendor/golang.org/x/sys/windows/syscall.go | 71 + vendor/golang.org/x/sys/windows/syscall_test.go | 33 + vendor/golang.org/x/sys/windows/syscall_windows.go | 991 + .../x/sys/windows/syscall_windows_test.go | 107 + .../golang.org/x/sys/windows/zsyscall_windows.go | 2245 ++ vendor/golang.org/x/sys/windows/ztypes_windows.go | 1242 + .../golang.org/x/sys/windows/ztypes_windows_386.go | 22 + .../x/sys/windows/ztypes_windows_amd64.go | 22 + vendor/gopkg.in/asn1-ber.v1/ber_test.go | 168 + vendor/gopkg.in/asn1-ber.v1/header_test.go | 135 + vendor/gopkg.in/asn1-ber.v1/identifier_test.go | 344 + vendor/gopkg.in/asn1-ber.v1/length_test.go | 158 + vendor/gopkg.in/asn1-ber.v1/suite_test.go | 182 + vendor/gopkg.in/asn1-ber.v1/tests/tc1.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc10.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc11.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc12.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc13.ber | Bin 0 -> 11 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc14.ber | Bin 0 -> 7 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc15.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc16.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc17.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc18.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc19.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc2.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc20.ber | Bin 0 -> 11 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc21.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc22.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc23.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc24.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc25.ber | Bin 0 -> 5 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc26.ber | Bin 0 -> 5 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc27.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc28.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc29.ber | Bin 0 -> 3 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc3.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc30.ber | Bin 0 -> 5 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc31.ber | Bin 0 -> 4 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc32.ber | Bin 0 -> 2 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc33.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc34.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc35.ber | Bin 0 -> 16 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc36.ber | Bin 0 -> 20 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc37.ber | Bin 0 -> 14 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc38.ber | Bin 0 -> 16 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc39.ber | Bin 0 -> 2 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc4.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc40.ber | Bin 0 -> 2 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc41.ber | Bin 0 -> 16 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc42.ber | Bin 0 -> 14 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc43.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc44.ber | Bin 0 -> 2 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc45.ber | Bin 0 -> 2 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc46.ber | Bin 0 -> 11 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc47.ber | Bin 0 -> 16 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc48.ber | Bin 0 -> 16 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc5.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc6.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc7.ber | 1 + vendor/gopkg.in/asn1-ber.v1/tests/tc8.ber | Bin 0 -> 5 bytes vendor/gopkg.in/asn1-ber.v1/tests/tc9.ber | 1 + vendor/gopkg.in/fsnotify.v1/example_test.go | 42 + vendor/gopkg.in/fsnotify.v1/inotify_poller_test.go | 229 + vendor/gopkg.in/fsnotify.v1/inotify_test.go | 344 + .../fsnotify.v1/integration_darwin_test.go | 147 + vendor/gopkg.in/fsnotify.v1/integration_test.go | 1237 + .../gopkg.in/throttled/throttled.v1/common_test.go | 65 + .../throttled/throttled.v1/delayer_test.go | 65 + vendor/gopkg.in/throttled/throttled.v1/doc.go | 2 +- .../throttled/throttled.v1/examples/README.md | 12 + .../throttled/throttled.v1/examples/custom/main.go | 90 + .../throttled.v1/examples/interval-many/main.go | 79 + .../throttled.v1/examples/interval-vary/main.go | 74 + .../throttled.v1/examples/interval-vary/siege-urls | 4 + .../throttled.v1/examples/interval/main.go | 69 + .../throttled.v1/examples/memstats/main.go | 97 + .../throttled.v1/examples/memstats/test-file | Bin 0 -> 65536 bytes .../throttled.v1/examples/rate-limit/main.go | 101 + .../throttled/throttled.v1/interval_test.go | 114 + .../throttled/throttled.v1/memstats_test.go | 64 + .../throttled/throttled.v1/misc/pre-commit | 38 + .../gopkg.in/throttled/throttled.v1/rate_test.go | 101 + .../gopkg.in/throttled/throttled.v1/store/doc.go | 2 +- .../throttled/throttled.v1/store/mem_test.go | 43 + .../throttled/throttled.v1/store/redis_test.go | 66 + .../gopkg.in/throttled/throttled.v1/varyby_test.go | 56 + vendor/gopkg.in/yaml.v2/decode_test.go | 988 + vendor/gopkg.in/yaml.v2/encode_test.go | 501 + vendor/gopkg.in/yaml.v2/suite_test.go | 12 + 1100 files changed, 277734 insertions(+), 4040 deletions(-) delete mode 100644 Godeps/Godeps.json delete mode 100644 Godeps/Readme create mode 100644 glide.lock create mode 100644 glide.yaml create mode 100644 vendor/github.com/NYTimes/gziphandler/gzip_test.go create mode 100644 vendor/github.com/NYTimes/gziphandler/testdata/benchmark.json create mode 100644 vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go create mode 100644 vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go create mode 100644 vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go create mode 100644 vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go create mode 100644 vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go create mode 100644 vendor/github.com/alecthomas/log4go/examples/example.xml create mode 100644 vendor/github.com/alecthomas/log4go/log4go_test.go create mode 100644 vendor/github.com/braintree/manners/helpers_test.go create mode 100644 vendor/github.com/braintree/manners/server_test.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/certs.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/conn.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/listener.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/temp_file.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/wait_group.go create mode 100644 vendor/github.com/braintree/manners/transition_test.go create mode 100755 vendor/github.com/cloudfoundry/jibber_jabber/ci/scripts/windows-64-test.bat create mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_suite_test.go create mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix_test.go create mode 100644 vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows_test.go create mode 100644 vendor/github.com/dgryski/dgoogauth/googauth_test.go create mode 100644 vendor/github.com/disintegration/imaging/adjust_test.go create mode 100644 vendor/github.com/disintegration/imaging/effects_test.go create mode 100644 vendor/github.com/disintegration/imaging/helpers_test.go create mode 100644 vendor/github.com/disintegration/imaging/resize_test.go create mode 100644 vendor/github.com/disintegration/imaging/tools_test.go create mode 100644 vendor/github.com/disintegration/imaging/transform_test.go create mode 100644 vendor/github.com/disintegration/imaging/utils_test.go create mode 100644 vendor/github.com/garyburd/redigo/.travis.yml create mode 100644 vendor/github.com/garyburd/redigo/README.markdown create mode 100644 vendor/github.com/garyburd/redigo/internal/commandinfo_test.go create mode 100644 vendor/github.com/garyburd/redigo/internal/redistest/testdb.go create mode 100644 vendor/github.com/garyburd/redigo/redis/conn_test.go create mode 100644 vendor/github.com/garyburd/redigo/redis/pool_test.go create mode 100644 vendor/github.com/garyburd/redigo/redis/pubsub_test.go create mode 100644 vendor/github.com/garyburd/redigo/redis/reply_test.go create mode 100644 vendor/github.com/garyburd/redigo/redis/scan_test.go create mode 100644 vendor/github.com/garyburd/redigo/redis/script_test.go create mode 100644 vendor/github.com/garyburd/redigo/redis/test_test.go create mode 100644 vendor/github.com/garyburd/redigo/redis/zpop_example_test.go create mode 100644 vendor/github.com/garyburd/redigo/redisx/connmux.go create mode 100644 vendor/github.com/garyburd/redigo/redisx/connmux_test.go create mode 100644 vendor/github.com/garyburd/redigo/redisx/doc.go create mode 100644 vendor/github.com/go-gorp/gorp/dialect_mysql_test.go create mode 100644 vendor/github.com/go-gorp/gorp/gorp_suite_test.go create mode 100644 vendor/github.com/go-gorp/gorp/gorp_test.go mode change 100644 => 100755 vendor/github.com/go-gorp/gorp/test_all.sh create mode 100644 vendor/github.com/go-ldap/ldap/conn_test.go create mode 100644 vendor/github.com/go-ldap/ldap/dn_test.go create mode 100644 vendor/github.com/go-ldap/ldap/error_test.go create mode 100644 vendor/github.com/go-ldap/ldap/example_test.go create mode 100644 vendor/github.com/go-ldap/ldap/filter_test.go create mode 100644 vendor/github.com/go-ldap/ldap/ldap_test.go create mode 100644 vendor/github.com/go-ldap/ldap/search_test.go create mode 100644 vendor/github.com/go-sql-driver/mysql/benchmark_test.go create mode 100644 vendor/github.com/go-sql-driver/mysql/driver_test.go create mode 100644 vendor/github.com/go-sql-driver/mysql/dsn_test.go create mode 100644 vendor/github.com/go-sql-driver/mysql/errors_test.go create mode 100644 vendor/github.com/go-sql-driver/mysql/utils_test.go create mode 100644 vendor/github.com/goamz/goamz/.gitignore create mode 100644 vendor/github.com/goamz/goamz/.lbox create mode 100644 vendor/github.com/goamz/goamz/.travis.yml create mode 100644 vendor/github.com/goamz/goamz/CHANGES.md create mode 100644 vendor/github.com/goamz/goamz/README.md create mode 100644 vendor/github.com/goamz/goamz/autoscaling/autoscaling.go create mode 100644 vendor/github.com/goamz/goamz/autoscaling/autoscaling_test.go create mode 100644 vendor/github.com/goamz/goamz/autoscaling/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/aws/attempt_test.go create mode 100644 vendor/github.com/goamz/goamz/aws/aws_test.go create mode 100644 vendor/github.com/goamz/goamz/aws/client_test.go create mode 100644 vendor/github.com/goamz/goamz/aws/export_test.go create mode 100644 vendor/github.com/goamz/goamz/aws/sign_test.go create mode 100644 vendor/github.com/goamz/goamz/cloudformation/cloudformation.go create mode 100644 vendor/github.com/goamz/goamz/cloudformation/cloudformation_test.go create mode 100644 vendor/github.com/goamz/goamz/cloudformation/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/cloudfront/cloudfront.go create mode 100644 vendor/github.com/goamz/goamz/cloudfront/cloudfront_test.go create mode 100644 vendor/github.com/goamz/goamz/cloudfront/testdata/key.pem create mode 100644 vendor/github.com/goamz/goamz/cloudfront/testdata/key.pub create mode 100644 vendor/github.com/goamz/goamz/cloudwatch/ChangeLog create mode 100644 vendor/github.com/goamz/goamz/cloudwatch/README.md create mode 100644 vendor/github.com/goamz/goamz/cloudwatch/cloudwatch.go create mode 100644 vendor/github.com/goamz/goamz/cloudwatch/cloudwatch_test.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/.gitignore create mode 100644 vendor/github.com/goamz/goamz/dynamodb/Makefile create mode 100644 vendor/github.com/goamz/goamz/dynamodb/README.md create mode 100755 vendor/github.com/goamz/goamz/dynamodb/attribute.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/const.go create mode 100755 vendor/github.com/goamz/goamz/dynamodb/dynamodb.go create mode 100755 vendor/github.com/goamz/goamz/dynamodb/dynamodb_test.go create mode 100755 vendor/github.com/goamz/goamz/dynamodb/item.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/item_test.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/marshaller.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/marshaller_test.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/query.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/query_builder.go create mode 100755 vendor/github.com/goamz/goamz/dynamodb/query_builder_test.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/scan.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/stream.go create mode 100755 vendor/github.com/goamz/goamz/dynamodb/stream_test.go create mode 100755 vendor/github.com/goamz/goamz/dynamodb/table.go create mode 100755 vendor/github.com/goamz/goamz/dynamodb/table_test.go create mode 100644 vendor/github.com/goamz/goamz/dynamodb/update_item.go create mode 100644 vendor/github.com/goamz/goamz/ec2/ec2.go create mode 100644 vendor/github.com/goamz/goamz/ec2/ec2_test.go create mode 100644 vendor/github.com/goamz/goamz/ec2/ec2i_test.go create mode 100644 vendor/github.com/goamz/goamz/ec2/ec2t_test.go create mode 100644 vendor/github.com/goamz/goamz/ec2/ec2test/filter.go create mode 100644 vendor/github.com/goamz/goamz/ec2/ec2test/server.go create mode 100644 vendor/github.com/goamz/goamz/ec2/export_test.go create mode 100644 vendor/github.com/goamz/goamz/ec2/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/ec2/sign.go create mode 100644 vendor/github.com/goamz/goamz/ec2/sign_test.go create mode 100644 vendor/github.com/goamz/goamz/ec2/vpc.go create mode 100644 vendor/github.com/goamz/goamz/ec2/vpc_test.go create mode 100644 vendor/github.com/goamz/goamz/ecs/ecs.go create mode 100644 vendor/github.com/goamz/goamz/ecs/ecs_test.go create mode 100644 vendor/github.com/goamz/goamz/ecs/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/elb/elb.go create mode 100644 vendor/github.com/goamz/goamz/elb/elb_test.go create mode 100644 vendor/github.com/goamz/goamz/elb/elbi_test.go create mode 100644 vendor/github.com/goamz/goamz/elb/elbt_test.go create mode 100644 vendor/github.com/goamz/goamz/elb/elbtest/server.go create mode 100644 vendor/github.com/goamz/goamz/elb/export_test.go create mode 100644 vendor/github.com/goamz/goamz/elb/response_test.go create mode 100644 vendor/github.com/goamz/goamz/elb/sign.go create mode 100644 vendor/github.com/goamz/goamz/elb/sign_test.go create mode 100644 vendor/github.com/goamz/goamz/elb/suite_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/mturk/example_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/mturk/export_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/mturk/mturk.go create mode 100644 vendor/github.com/goamz/goamz/exp/mturk/mturk_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/mturk/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/mturk/sign.go create mode 100644 vendor/github.com/goamz/goamz/exp/mturk/sign_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/sdb/export_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/sdb/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/sdb/sdb.go create mode 100644 vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/sdb/sign.go create mode 100644 vendor/github.com/goamz/goamz/exp/sdb/sign_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/ses/ses.go create mode 100644 vendor/github.com/goamz/goamz/exp/ses/ses_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/ses/ses_types.go create mode 100644 vendor/github.com/goamz/goamz/exp/ses/sign.go create mode 100644 vendor/github.com/goamz/goamz/exp/sns/Makefile create mode 100644 vendor/github.com/goamz/goamz/exp/sns/README create mode 100644 vendor/github.com/goamz/goamz/exp/sns/endpoint.go create mode 100644 vendor/github.com/goamz/goamz/exp/sns/permissions.go create mode 100644 vendor/github.com/goamz/goamz/exp/sns/platform.go create mode 100644 vendor/github.com/goamz/goamz/exp/sns/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/sns/sign.go create mode 100644 vendor/github.com/goamz/goamz/exp/sns/sns.go create mode 100644 vendor/github.com/goamz/goamz/exp/sns/sns_test.go create mode 100644 vendor/github.com/goamz/goamz/exp/sns/subscription.go create mode 100644 vendor/github.com/goamz/goamz/exp/sns/topic.go create mode 100644 vendor/github.com/goamz/goamz/iam/iam.go create mode 100644 vendor/github.com/goamz/goamz/iam/iam_test.go create mode 100644 vendor/github.com/goamz/goamz/iam/iami_test.go create mode 100644 vendor/github.com/goamz/goamz/iam/iamt_test.go create mode 100644 vendor/github.com/goamz/goamz/iam/iamtest/server.go create mode 100644 vendor/github.com/goamz/goamz/iam/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/iam/sign.go create mode 100644 vendor/github.com/goamz/goamz/rds/rds.go create mode 100644 vendor/github.com/goamz/goamz/rds/rds_test.go create mode 100644 vendor/github.com/goamz/goamz/rds/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/rds/types.go create mode 100644 vendor/github.com/goamz/goamz/route53/route53.go create mode 100644 vendor/github.com/goamz/goamz/s3/export_test.go create mode 100644 vendor/github.com/goamz/goamz/s3/multi_test.go create mode 100644 vendor/github.com/goamz/goamz/s3/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/s3/s3_test.go create mode 100644 vendor/github.com/goamz/goamz/s3/s3i_test.go create mode 100644 vendor/github.com/goamz/goamz/s3/s3t_test.go create mode 100644 vendor/github.com/goamz/goamz/s3/s3test/server.go create mode 100644 vendor/github.com/goamz/goamz/s3/sign_test.go create mode 100644 vendor/github.com/goamz/goamz/sqs/Makefile create mode 100644 vendor/github.com/goamz/goamz/sqs/README.md create mode 100644 vendor/github.com/goamz/goamz/sqs/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/sqs/sqs.go create mode 100644 vendor/github.com/goamz/goamz/sqs/sqs_test.go create mode 100644 vendor/github.com/goamz/goamz/sqs/suite_test.go create mode 100644 vendor/github.com/goamz/goamz/sts/responses_test.go create mode 100644 vendor/github.com/goamz/goamz/sts/sts.go create mode 100644 vendor/github.com/goamz/goamz/sts/sts_test.go create mode 100644 vendor/github.com/goamz/goamz/testutil/http.go create mode 100644 vendor/github.com/goamz/goamz/testutil/suite.go create mode 100644 vendor/github.com/golang/freetype/cmd/print-glyph-points/main.c create mode 100644 vendor/github.com/golang/freetype/example/capjoin/main.go create mode 100644 vendor/github.com/golang/freetype/example/drawer/main.go create mode 100644 vendor/github.com/golang/freetype/example/freetype/main.go create mode 100644 vendor/github.com/golang/freetype/example/gamma/main.go create mode 100644 vendor/github.com/golang/freetype/example/raster/main.go create mode 100644 vendor/github.com/golang/freetype/example/round/main.go create mode 100644 vendor/github.com/golang/freetype/example/truetype/main.go create mode 100644 vendor/github.com/golang/freetype/freetype_test.go create mode 100644 vendor/github.com/golang/freetype/licenses/ftl.txt create mode 100644 vendor/github.com/golang/freetype/licenses/gpl.txt create mode 100644 vendor/github.com/golang/freetype/testdata/COPYING create mode 100644 vendor/github.com/golang/freetype/testdata/README create mode 100644 vendor/github.com/golang/freetype/testdata/luximr.ttf create mode 100644 vendor/github.com/golang/freetype/testdata/luximr.ttx create mode 100644 vendor/github.com/golang/freetype/testdata/luxirr.ttf create mode 100644 vendor/github.com/golang/freetype/testdata/luxirr.ttx create mode 100644 vendor/github.com/golang/freetype/testdata/luxisr-12pt-sans-hinting.txt create mode 100644 vendor/github.com/golang/freetype/testdata/luxisr-12pt-with-hinting.txt create mode 100644 vendor/github.com/golang/freetype/testdata/luxisr.ttf create mode 100644 vendor/github.com/golang/freetype/testdata/luxisr.ttx create mode 100755 vendor/github.com/golang/freetype/testdata/make-other-hinting-txts.sh create mode 100644 vendor/github.com/golang/freetype/truetype/face_test.go create mode 100644 vendor/github.com/golang/freetype/truetype/hint_test.go create mode 100644 vendor/github.com/golang/freetype/truetype/truetype_test.go create mode 100644 vendor/github.com/golang/groupcache/.gitignore create mode 100644 vendor/github.com/golang/groupcache/README.md create mode 100644 vendor/github.com/golang/groupcache/byteview.go create mode 100644 vendor/github.com/golang/groupcache/byteview_test.go create mode 100644 vendor/github.com/golang/groupcache/consistenthash/consistenthash.go create mode 100644 vendor/github.com/golang/groupcache/consistenthash/consistenthash_test.go create mode 100644 vendor/github.com/golang/groupcache/groupcache.go create mode 100644 vendor/github.com/golang/groupcache/groupcache_test.go create mode 100644 vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go create mode 100644 vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto create mode 100644 vendor/github.com/golang/groupcache/http.go create mode 100644 vendor/github.com/golang/groupcache/http_test.go create mode 100644 vendor/github.com/golang/groupcache/lru/lru_test.go create mode 100644 vendor/github.com/golang/groupcache/peers.go create mode 100644 vendor/github.com/golang/groupcache/singleflight/singleflight.go create mode 100644 vendor/github.com/golang/groupcache/singleflight/singleflight_test.go create mode 100644 vendor/github.com/golang/groupcache/sinks.go create mode 100644 vendor/github.com/golang/groupcache/testpb/test.pb.go create mode 100644 vendor/github.com/golang/groupcache/testpb/test.proto create mode 100644 vendor/github.com/gorilla/context/context_test.go create mode 100644 vendor/github.com/gorilla/handlers/canonical_test.go create mode 100644 vendor/github.com/gorilla/handlers/compress_test.go create mode 100644 vendor/github.com/gorilla/handlers/cors_test.go create mode 100644 vendor/github.com/gorilla/handlers/handlers_test.go create mode 100644 vendor/github.com/gorilla/handlers/proxy_headers_test.go create mode 100644 vendor/github.com/gorilla/handlers/recovery_test.go create mode 100644 vendor/github.com/gorilla/mux/bench_test.go create mode 100644 vendor/github.com/gorilla/mux/mux_test.go create mode 100644 vendor/github.com/gorilla/mux/old_test.go create mode 100644 vendor/github.com/gorilla/websocket/bench_test.go create mode 100644 vendor/github.com/gorilla/websocket/client_server_test.go create mode 100644 vendor/github.com/gorilla/websocket/client_test.go create mode 100644 vendor/github.com/gorilla/websocket/conn_test.go create mode 100644 vendor/github.com/gorilla/websocket/example_test.go create mode 100644 vendor/github.com/gorilla/websocket/examples/autobahn/README.md create mode 100644 vendor/github.com/gorilla/websocket/examples/autobahn/fuzzingclient.json create mode 100644 vendor/github.com/gorilla/websocket/examples/autobahn/server.go create mode 100644 vendor/github.com/gorilla/websocket/examples/chat/README.md create mode 100644 vendor/github.com/gorilla/websocket/examples/chat/conn.go create mode 100644 vendor/github.com/gorilla/websocket/examples/chat/home.html create mode 100644 vendor/github.com/gorilla/websocket/examples/chat/hub.go create mode 100644 vendor/github.com/gorilla/websocket/examples/chat/main.go create mode 100644 vendor/github.com/gorilla/websocket/examples/command/README.md create mode 100644 vendor/github.com/gorilla/websocket/examples/command/home.html create mode 100644 vendor/github.com/gorilla/websocket/examples/command/main.go create mode 100644 vendor/github.com/gorilla/websocket/examples/echo/README.md create mode 100644 vendor/github.com/gorilla/websocket/examples/echo/client.go create mode 100644 vendor/github.com/gorilla/websocket/examples/echo/server.go create mode 100644 vendor/github.com/gorilla/websocket/examples/filewatch/README.md create mode 100644 vendor/github.com/gorilla/websocket/examples/filewatch/main.go create mode 100644 vendor/github.com/gorilla/websocket/json_test.go create mode 100644 vendor/github.com/gorilla/websocket/server_test.go create mode 100644 vendor/github.com/gorilla/websocket/util_test.go create mode 100644 vendor/github.com/lib/pq/bench_test.go create mode 100644 vendor/github.com/lib/pq/certs/README create mode 100644 vendor/github.com/lib/pq/certs/postgresql.crt create mode 100644 vendor/github.com/lib/pq/certs/postgresql.key create mode 100644 vendor/github.com/lib/pq/certs/root.crt create mode 100644 vendor/github.com/lib/pq/certs/server.crt create mode 100644 vendor/github.com/lib/pq/certs/server.key create mode 100644 vendor/github.com/lib/pq/conn_test.go create mode 100644 vendor/github.com/lib/pq/copy_test.go create mode 100644 vendor/github.com/lib/pq/encode_test.go create mode 100644 vendor/github.com/lib/pq/hstore/hstore.go create mode 100644 vendor/github.com/lib/pq/hstore/hstore_test.go create mode 100644 vendor/github.com/lib/pq/listen_example/doc.go create mode 100644 vendor/github.com/lib/pq/notify_test.go create mode 100644 vendor/github.com/lib/pq/ssl_test.go create mode 100644 vendor/github.com/lib/pq/url_test.go create mode 100644 vendor/github.com/mattermost/rsc/.gitignore create mode 100644 vendor/github.com/mattermost/rsc/README.md create mode 100644 vendor/github.com/mattermost/rsc/app/app.go create mode 100644 vendor/github.com/mattermost/rsc/app/app.yaml create mode 100644 vendor/github.com/mattermost/rsc/app/index.yaml create mode 100644 vendor/github.com/mattermost/rsc/appfs/appfile/main.go create mode 100644 vendor/github.com/mattermost/rsc/appfs/appmount/main.go create mode 100644 vendor/github.com/mattermost/rsc/appfs/client/client.go create mode 100644 vendor/github.com/mattermost/rsc/appfs/fs/fs.go create mode 100644 vendor/github.com/mattermost/rsc/appfs/fs/local.go create mode 100644 vendor/github.com/mattermost/rsc/appfs/proto/data.go create mode 100644 vendor/github.com/mattermost/rsc/appfs/server/app.go create mode 100644 vendor/github.com/mattermost/rsc/arq/arq.go create mode 100644 vendor/github.com/mattermost/rsc/arq/arqfs/main.go create mode 100644 vendor/github.com/mattermost/rsc/arq/crypto.go create mode 100644 vendor/github.com/mattermost/rsc/arq/data.go create mode 100644 vendor/github.com/mattermost/rsc/arq/hist/hist.go create mode 100644 vendor/github.com/mattermost/rsc/arq/unpack.go create mode 100644 vendor/github.com/mattermost/rsc/blog/atom/atom.go create mode 100644 vendor/github.com/mattermost/rsc/blog/main.go create mode 100644 vendor/github.com/mattermost/rsc/blog/post/post.go create mode 100644 vendor/github.com/mattermost/rsc/blog/post/qr.go create mode 100644 vendor/github.com/mattermost/rsc/cmd/crypt/crypt.go create mode 100644 vendor/github.com/mattermost/rsc/cmd/issue/issue.go create mode 100644 vendor/github.com/mattermost/rsc/cmd/jfmt/main.go create mode 100644 vendor/github.com/mattermost/rsc/crypt/crypt.go create mode 100644 vendor/github.com/mattermost/rsc/devweb/main.go create mode 100644 vendor/github.com/mattermost/rsc/devweb/slave/slave.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/debug.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/fuse.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/fuse_kernel.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/fuse_kernel_darwin.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/fuse_kernel_linux.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/fuse_kernel_std.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/fuse_test.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/hellofs/hello.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/mount_darwin.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/mount_linux.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/serve.go create mode 100644 vendor/github.com/mattermost/rsc/fuse/tree.go create mode 100644 vendor/github.com/mattermost/rsc/gf256/blog_test.go create mode 100644 vendor/github.com/mattermost/rsc/gf256/gf256_test.go create mode 100644 vendor/github.com/mattermost/rsc/google/acme/Chat/main.go create mode 100644 vendor/github.com/mattermost/rsc/google/chat.go create mode 100644 vendor/github.com/mattermost/rsc/google/gmail/gmail.go create mode 100644 vendor/github.com/mattermost/rsc/google/gmailsend/send.go create mode 100644 vendor/github.com/mattermost/rsc/google/googleserver/chat.go create mode 100644 vendor/github.com/mattermost/rsc/google/googleserver/main.go create mode 100644 vendor/github.com/mattermost/rsc/google/main.go create mode 100644 vendor/github.com/mattermost/rsc/gtfs/gtfs.pb.go create mode 100644 vendor/github.com/mattermost/rsc/imap/Makefile create mode 100644 vendor/github.com/mattermost/rsc/imap/decode.go create mode 100644 vendor/github.com/mattermost/rsc/imap/decode_test.go create mode 100644 vendor/github.com/mattermost/rsc/imap/imap.go create mode 100644 vendor/github.com/mattermost/rsc/imap/imap_test.go create mode 100644 vendor/github.com/mattermost/rsc/imap/mail.go create mode 100644 vendor/github.com/mattermost/rsc/imap/mail_test.go create mode 100644 vendor/github.com/mattermost/rsc/imap/rfc2045.txt create mode 100644 vendor/github.com/mattermost/rsc/imap/rfc2971.txt create mode 100644 vendor/github.com/mattermost/rsc/imap/rfc3501.txt create mode 100644 vendor/github.com/mattermost/rsc/imap/sx.go create mode 100644 vendor/github.com/mattermost/rsc/imap/sx_test.go create mode 100644 vendor/github.com/mattermost/rsc/imap/tcs.go create mode 100644 vendor/github.com/mattermost/rsc/keychain/doc.go create mode 100644 vendor/github.com/mattermost/rsc/keychain/mac.go create mode 100644 vendor/github.com/mattermost/rsc/mbta/Passages.pb create mode 100644 vendor/github.com/mattermost/rsc/mbta/Vehicles.pb create mode 100644 vendor/github.com/mattermost/rsc/mbta/mbta.go create mode 100755 vendor/github.com/mattermost/rsc/mkapp create mode 100644 vendor/github.com/mattermost/rsc/plist/plist.go create mode 100644 vendor/github.com/mattermost/rsc/plist/plist_test.go create mode 100644 vendor/github.com/mattermost/rsc/qr/coding/qr_test.go create mode 100644 vendor/github.com/mattermost/rsc/qr/libqrencode/Makefile create mode 100644 vendor/github.com/mattermost/rsc/qr/libqrencode/qrencode.go create mode 100644 vendor/github.com/mattermost/rsc/qr/png_test.go create mode 100644 vendor/github.com/mattermost/rsc/qr/web/pic.go create mode 100644 vendor/github.com/mattermost/rsc/qr/web/play.go create mode 100644 vendor/github.com/mattermost/rsc/qr/web/resize/resize.go create mode 100644 vendor/github.com/mattermost/rsc/regexp/regmerge/copy.go create mode 100644 vendor/github.com/mattermost/rsc/regexp/regmerge/main.go create mode 100644 vendor/github.com/mattermost/rsc/regexp/regmerge/match.go create mode 100644 vendor/github.com/mattermost/rsc/regexp/regmerge/merge.go create mode 100644 vendor/github.com/mattermost/rsc/regexp/regmerge/sort.go create mode 100644 vendor/github.com/mattermost/rsc/regexp/regmerge/sparse.go create mode 100644 vendor/github.com/mattermost/rsc/regexp/regmerge/utf.go create mode 100644 vendor/github.com/mattermost/rsc/rosetta/graph/Makefile create mode 100644 vendor/github.com/mattermost/rsc/rosetta/graph/graph.go create mode 100644 vendor/github.com/mattermost/rsc/rosetta/maze/Makefile create mode 100644 vendor/github.com/mattermost/rsc/rosetta/maze/maze.go create mode 100644 vendor/github.com/mattermost/rsc/s3get/main.go create mode 100644 vendor/github.com/mattermost/rsc/smugmug/smug.go create mode 100644 vendor/github.com/mattermost/rsc/smugmug/smugup/main.go create mode 120000 vendor/github.com/mattermost/rsc/tmp/app create mode 120000 vendor/github.com/mattermost/rsc/tmp/appfs create mode 120000 vendor/github.com/mattermost/rsc/tmp/arq create mode 120000 vendor/github.com/mattermost/rsc/tmp/blog create mode 120000 vendor/github.com/mattermost/rsc/tmp/cmd create mode 120000 vendor/github.com/mattermost/rsc/tmp/crypt create mode 120000 vendor/github.com/mattermost/rsc/tmp/devweb create mode 120000 vendor/github.com/mattermost/rsc/tmp/fuse create mode 120000 vendor/github.com/mattermost/rsc/tmp/gf256 create mode 120000 vendor/github.com/mattermost/rsc/tmp/google create mode 120000 vendor/github.com/mattermost/rsc/tmp/gtfs create mode 120000 vendor/github.com/mattermost/rsc/tmp/imap create mode 120000 vendor/github.com/mattermost/rsc/tmp/keychain create mode 120000 vendor/github.com/mattermost/rsc/tmp/mbta create mode 120000 vendor/github.com/mattermost/rsc/tmp/mkapp create mode 120000 vendor/github.com/mattermost/rsc/tmp/plist create mode 120000 vendor/github.com/mattermost/rsc/tmp/qr create mode 120000 vendor/github.com/mattermost/rsc/tmp/regexp create mode 120000 vendor/github.com/mattermost/rsc/tmp/rosetta create mode 120000 vendor/github.com/mattermost/rsc/tmp/s3get create mode 120000 vendor/github.com/mattermost/rsc/tmp/smugmug create mode 120000 vendor/github.com/mattermost/rsc/tmp/xmpp create mode 100644 vendor/github.com/mattermost/rsc/xmpp/xmpp.go create mode 100644 vendor/github.com/mssola/user_agent/all_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/.gitignore create mode 100644 vendor/github.com/nicksnyder/go-i18n/.travis.yml create mode 100644 vendor/github.com/nicksnyder/go-i18n/CHANGELOG create mode 100644 vendor/github.com/nicksnyder/go-i18n/README.md create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/doc.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/gendoc.sh create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/goi18n.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/merge.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/merge_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/en-us.yaml create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.all.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/ar-ar.untranslated.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.all.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/en-us.untranslated.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.all.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/expected/fr-fr.untranslated.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.one.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/ar-ar.two.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.one.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/en-us.two.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/fr-fr.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.one.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/ar-ar.two.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.one.yaml create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/en-us.two.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/goi18n/testdata/input/yaml/fr-fr.json create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/example_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/exampletemplate_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/exampleyaml_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/generate.sh create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/main.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/plurals.xml create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/language/codegen/xml.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/language/language_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/language/operands_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/language/plural_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/translation/template_test.go create mode 100644 vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation_test.go mode change 100644 => 100755 vendor/github.com/pborman/uuid/dce.go mode change 100644 => 100755 vendor/github.com/pborman/uuid/doc.go create mode 100644 vendor/github.com/pborman/uuid/json_test.go mode change 100644 => 100755 vendor/github.com/pborman/uuid/node.go create mode 100644 vendor/github.com/pborman/uuid/seq_test.go create mode 100644 vendor/github.com/pborman/uuid/sql_test.go mode change 100644 => 100755 vendor/github.com/pborman/uuid/time.go create mode 100644 vendor/github.com/pborman/uuid/uuid_test.go create mode 100644 vendor/github.com/rwcarlsen/goexif/.gitignore create mode 100644 vendor/github.com/rwcarlsen/goexif/README.md create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/corrupt/huge_tag_exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/corrupt/infinite_loop_exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/corrupt/max_uint32_exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/example_test.go create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/exif_test.go create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/regress_expected_test.go create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2004-01-11-22-45-15-sep-2004-01-11-22-45-15a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2006-08-03-16-29-38-sep-2006-08-03-16-29-38a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2006-11-11-19-17-56-sep-2006-11-11-19-17-56a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2006-12-10-23-58-20-sep-2006-12-10-23-58-20a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2006-12-17-07-09-14-sep-2006-12-17-07-09-14a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2006-12-21-15-55-26-sep-2006-12-21-15-55-26a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-01-01-12-00-00-sep-2007-01-01-12-00-00a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-01-17-21-49-44-sep-2007-01-17-21-49-44a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-02-02-18-13-29-sep-2007-02-02-18-13-29a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-05-02-17-02-21-sep-2007-05-02-17-02-21a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-05-12-08-19-07-sep-2007-05-12-08-19-07a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-05-26-04-49-45-sep-2007-05-26-04-49-45a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-05-30-14-28-01-sep-2007-05-30-14-28-01a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-06-06-16-15-25-sep-2007-06-06-16-15-25a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-06-26-10-13-04-sep-2007-06-26-10-13-04a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-07-13-17-02-30-sep-2007-07-13-17-02-30a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-08-15-14-42-46-sep-2007-08-15-14-42-46a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-08-24-02-40-42-sep-2007-08-24-02-40-42a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2007-11-07-11-40-44-sep-2007-11-07-11-40-44a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2008-06-02-10-03-57-sep-2008-06-02-10-03-57a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2008-06-06-13-29-29-sep-2008-06-06-13-29-29a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2008-06-17-01-21-30-sep-2008-06-17-01-21-30a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2008-09-02-17-43-48-sep-2008-09-02-17-43-48a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2009-03-26-09-23-20-sep-2009-03-26-09-23-20a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2009-04-11-03-01-38-sep-2009-04-11-03-01-38a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2009-04-23-07-21-35-sep-2009-04-23-07-21-35a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2009-06-11-19-23-18-sep-2009-06-11-19-23-18a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2009-06-20-07-59-05-sep-2009-06-20-07-59-05a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2009-08-05-08-11-31-sep-2009-08-05-08-11-31a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2010-06-08-04-44-24-sep-2010-06-08-04-44-24a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2010-06-20-20-07-39-sep-2010-06-20-20-07-39a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2010-09-02-08-43-02-sep-2010-09-02-08-43-02a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2011-01-24-22-06-02-sep-2011-01-24-22-06-02a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2011-03-07-09-28-03-sep-2011-03-07-09-28-03a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2011-05-07-13-02-49-sep-2011-05-07-13-02-49a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2011-08-07-19-22-57-sep-2011-08-07-19-22-57a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2011-10-28-17-50-18-sep-2011-10-28-17-50-18a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2011-10-28-18-25-43-sep-2011-10-28-18-25-43.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2011-11-18-15-38-34-sep-Photo11181538.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2012-06-02-10-12-28-sep-2012-06-02-10-12-28.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2012-09-21-22-07-34-sep-2012-09-21-22-07-34.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2012-12-19-21-38-40-sep-temple_square1.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2012-12-21-11-15-19-sep-IMG_0001.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2013-02-05-23-12-09-sep-DSCI0001.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2099-08-12-19-59-29-sep-2099-08-12-19-59-29a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/2216-11-15-11-46-51-sep-2216-11-15-11-46-51a.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/FailedHash-NoDate-sep-remembory.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/f1-exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/f2-exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/f3-exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/f4-exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/f5-exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/f6-exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/f7-exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/f8-exif.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/geodegrees_as_string.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exif/samples/has-lens-info.jpg create mode 100644 vendor/github.com/rwcarlsen/goexif/exifstat/main.go create mode 100644 vendor/github.com/rwcarlsen/goexif/mknote/fields.go create mode 100644 vendor/github.com/rwcarlsen/goexif/mknote/mknote.go create mode 100644 vendor/github.com/rwcarlsen/goexif/tiff/tiff_test.go create mode 100644 vendor/github.com/vaughan0/go-ini/ini_linux_test.go create mode 100644 vendor/github.com/vaughan0/go-ini/ini_test.go create mode 100644 vendor/golang.org/x/crypto/.gitattributes create mode 100644 vendor/golang.org/x/crypto/.gitignore create mode 100644 vendor/golang.org/x/crypto/AUTHORS create mode 100644 vendor/golang.org/x/crypto/CONTRIBUTING.md create mode 100644 vendor/golang.org/x/crypto/CONTRIBUTORS create mode 100644 vendor/golang.org/x/crypto/README create mode 100644 vendor/golang.org/x/crypto/acme/internal/acme/acme.go create mode 100644 vendor/golang.org/x/crypto/acme/internal/acme/acme_test.go create mode 100644 vendor/golang.org/x/crypto/acme/internal/acme/jws.go create mode 100644 vendor/golang.org/x/crypto/acme/internal/acme/jws_test.go create mode 100644 vendor/golang.org/x/crypto/acme/internal/acme/types.go create mode 100644 vendor/golang.org/x/crypto/bcrypt/bcrypt_test.go create mode 100644 vendor/golang.org/x/crypto/blowfish/blowfish_test.go create mode 100644 vendor/golang.org/x/crypto/bn256/bn256.go create mode 100644 vendor/golang.org/x/crypto/bn256/bn256_test.go create mode 100644 vendor/golang.org/x/crypto/bn256/constants.go create mode 100644 vendor/golang.org/x/crypto/bn256/curve.go create mode 100644 vendor/golang.org/x/crypto/bn256/example_test.go create mode 100644 vendor/golang.org/x/crypto/bn256/gfp12.go create mode 100644 vendor/golang.org/x/crypto/bn256/gfp2.go create mode 100644 vendor/golang.org/x/crypto/bn256/gfp6.go create mode 100644 vendor/golang.org/x/crypto/bn256/optate.go create mode 100644 vendor/golang.org/x/crypto/bn256/twist.go create mode 100644 vendor/golang.org/x/crypto/cast5/cast5.go create mode 100644 vendor/golang.org/x/crypto/cast5/cast5_test.go create mode 100644 vendor/golang.org/x/crypto/codereview.cfg create mode 100644 vendor/golang.org/x/crypto/curve25519/const_amd64.s create mode 100644 vendor/golang.org/x/crypto/curve25519/cswap_amd64.s create mode 100644 vendor/golang.org/x/crypto/curve25519/curve25519.go create mode 100644 vendor/golang.org/x/crypto/curve25519/curve25519_test.go create mode 100644 vendor/golang.org/x/crypto/curve25519/doc.go create mode 100644 vendor/golang.org/x/crypto/curve25519/freeze_amd64.s create mode 100644 vendor/golang.org/x/crypto/curve25519/ladderstep_amd64.s create mode 100644 vendor/golang.org/x/crypto/curve25519/mont25519_amd64.go create mode 100644 vendor/golang.org/x/crypto/curve25519/mul_amd64.s create mode 100644 vendor/golang.org/x/crypto/curve25519/square_amd64.s create mode 100644 vendor/golang.org/x/crypto/ed25519/ed25519.go create mode 100644 vendor/golang.org/x/crypto/ed25519/ed25519_test.go create mode 100644 vendor/golang.org/x/crypto/ed25519/internal/edwards25519/const.go create mode 100644 vendor/golang.org/x/crypto/ed25519/internal/edwards25519/edwards25519.go create mode 100644 vendor/golang.org/x/crypto/ed25519/testdata/sign.input.gz create mode 100644 vendor/golang.org/x/crypto/hkdf/example_test.go create mode 100644 vendor/golang.org/x/crypto/hkdf/hkdf.go create mode 100644 vendor/golang.org/x/crypto/hkdf/hkdf_test.go create mode 100644 vendor/golang.org/x/crypto/md4/md4.go create mode 100644 vendor/golang.org/x/crypto/md4/md4_test.go create mode 100644 vendor/golang.org/x/crypto/md4/md4block.go create mode 100644 vendor/golang.org/x/crypto/nacl/box/box.go create mode 100644 vendor/golang.org/x/crypto/nacl/box/box_test.go create mode 100644 vendor/golang.org/x/crypto/nacl/secretbox/secretbox.go create mode 100644 vendor/golang.org/x/crypto/nacl/secretbox/secretbox_test.go create mode 100644 vendor/golang.org/x/crypto/ocsp/ocsp.go create mode 100644 vendor/golang.org/x/crypto/ocsp/ocsp_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/armor/armor.go create mode 100644 vendor/golang.org/x/crypto/openpgp/armor/armor_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/armor/encode.go create mode 100644 vendor/golang.org/x/crypto/openpgp/canonical_text.go create mode 100644 vendor/golang.org/x/crypto/openpgp/canonical_text_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/clearsign/clearsign.go create mode 100644 vendor/golang.org/x/crypto/openpgp/clearsign/clearsign_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go create mode 100644 vendor/golang.org/x/crypto/openpgp/elgamal/elgamal_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/errors/errors.go create mode 100644 vendor/golang.org/x/crypto/openpgp/keys.go create mode 100644 vendor/golang.org/x/crypto/openpgp/keys_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/compressed.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/compressed_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/config.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/encrypted_key_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/literal.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/ocfb.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/ocfb_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/opaque.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/opaque_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/packet.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/packet_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/private_key.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/private_key_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/public_key.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/public_key_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/public_key_v3_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/reader.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/signature.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/signature_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/signature_v3_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/userattribute.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/userattribute_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/userid.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/userid_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/read.go create mode 100644 vendor/golang.org/x/crypto/openpgp/read_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/s2k/s2k.go create mode 100644 vendor/golang.org/x/crypto/openpgp/s2k/s2k_test.go create mode 100644 vendor/golang.org/x/crypto/openpgp/write.go create mode 100644 vendor/golang.org/x/crypto/openpgp/write_test.go create mode 100644 vendor/golang.org/x/crypto/otr/libotr_test_helper.c create mode 100644 vendor/golang.org/x/crypto/otr/otr.go create mode 100644 vendor/golang.org/x/crypto/otr/otr_test.go create mode 100644 vendor/golang.org/x/crypto/otr/smp.go create mode 100644 vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go create mode 100644 vendor/golang.org/x/crypto/pbkdf2/pbkdf2_test.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/bmp-string.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/bmp-string_test.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/crypto.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/crypto_test.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/errors.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/internal/rc2/bench_test.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2_test.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/mac.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/mac_test.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/pbkdf.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/pbkdf_test.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/pkcs12.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/pkcs12_test.go create mode 100644 vendor/golang.org/x/crypto/pkcs12/safebags.go create mode 100644 vendor/golang.org/x/crypto/poly1305/const_amd64.s create mode 100644 vendor/golang.org/x/crypto/poly1305/poly1305.go create mode 100644 vendor/golang.org/x/crypto/poly1305/poly1305_amd64.s create mode 100644 vendor/golang.org/x/crypto/poly1305/poly1305_arm.s create mode 100644 vendor/golang.org/x/crypto/poly1305/poly1305_test.go create mode 100644 vendor/golang.org/x/crypto/poly1305/sum_amd64.go create mode 100644 vendor/golang.org/x/crypto/poly1305/sum_arm.go create mode 100644 vendor/golang.org/x/crypto/poly1305/sum_ref.go create mode 100644 vendor/golang.org/x/crypto/ripemd160/ripemd160.go create mode 100644 vendor/golang.org/x/crypto/ripemd160/ripemd160_test.go create mode 100644 vendor/golang.org/x/crypto/ripemd160/ripemd160block.go create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/hsalsa20.go create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/salsa2020_amd64.s create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/salsa208.go create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/salsa20_ref.go create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa/salsa_test.go create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa20.go create mode 100644 vendor/golang.org/x/crypto/salsa20/salsa20_test.go create mode 100644 vendor/golang.org/x/crypto/scrypt/scrypt.go create mode 100644 vendor/golang.org/x/crypto/scrypt/scrypt_test.go create mode 100644 vendor/golang.org/x/crypto/sha3/doc.go create mode 100644 vendor/golang.org/x/crypto/sha3/hashes.go create mode 100644 vendor/golang.org/x/crypto/sha3/keccakf.go create mode 100644 vendor/golang.org/x/crypto/sha3/register.go create mode 100644 vendor/golang.org/x/crypto/sha3/sha3.go create mode 100644 vendor/golang.org/x/crypto/sha3/sha3_test.go create mode 100644 vendor/golang.org/x/crypto/sha3/shake.go create mode 100644 vendor/golang.org/x/crypto/sha3/testdata/keccakKats.json.deflate create mode 100644 vendor/golang.org/x/crypto/sha3/xor.go create mode 100644 vendor/golang.org/x/crypto/sha3/xor_generic.go create mode 100644 vendor/golang.org/x/crypto/sha3/xor_unaligned.go create mode 100644 vendor/golang.org/x/crypto/ssh/agent/client.go create mode 100644 vendor/golang.org/x/crypto/ssh/agent/client_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/agent/example_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/agent/forward.go create mode 100644 vendor/golang.org/x/crypto/ssh/agent/keyring.go create mode 100644 vendor/golang.org/x/crypto/ssh/agent/keyring_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/agent/server.go create mode 100644 vendor/golang.org/x/crypto/ssh/agent/server_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/agent/testdata_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/benchmark_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/buffer.go create mode 100644 vendor/golang.org/x/crypto/ssh/buffer_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/certs.go create mode 100644 vendor/golang.org/x/crypto/ssh/certs_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/channel.go create mode 100644 vendor/golang.org/x/crypto/ssh/cipher.go create mode 100644 vendor/golang.org/x/crypto/ssh/cipher_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/client.go create mode 100644 vendor/golang.org/x/crypto/ssh/client_auth.go create mode 100644 vendor/golang.org/x/crypto/ssh/client_auth_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/client_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/common.go create mode 100644 vendor/golang.org/x/crypto/ssh/connection.go create mode 100644 vendor/golang.org/x/crypto/ssh/doc.go create mode 100644 vendor/golang.org/x/crypto/ssh/example_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/handshake.go create mode 100644 vendor/golang.org/x/crypto/ssh/handshake_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/kex.go create mode 100644 vendor/golang.org/x/crypto/ssh/kex_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/keys.go create mode 100644 vendor/golang.org/x/crypto/ssh/keys_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/mac.go create mode 100644 vendor/golang.org/x/crypto/ssh/mempipe_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/messages.go create mode 100644 vendor/golang.org/x/crypto/ssh/messages_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/mux.go create mode 100644 vendor/golang.org/x/crypto/ssh/mux_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/server.go create mode 100644 vendor/golang.org/x/crypto/ssh/session.go create mode 100644 vendor/golang.org/x/crypto/ssh/session_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/tcpip.go create mode 100644 vendor/golang.org/x/crypto/ssh/tcpip_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/terminal.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_linux.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go create mode 100644 vendor/golang.org/x/crypto/ssh/terminal/util_windows.go create mode 100644 vendor/golang.org/x/crypto/ssh/test/agent_unix_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/test/cert_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/test/doc.go create mode 100644 vendor/golang.org/x/crypto/ssh/test/forward_unix_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/test/session_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/test/tcpip_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/test/test_unix_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/test/testdata_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/testdata/doc.go create mode 100644 vendor/golang.org/x/crypto/ssh/testdata/keys.go create mode 100644 vendor/golang.org/x/crypto/ssh/testdata_test.go create mode 100644 vendor/golang.org/x/crypto/ssh/transport.go create mode 100644 vendor/golang.org/x/crypto/ssh/transport_test.go create mode 100644 vendor/golang.org/x/crypto/tea/cipher.go create mode 100644 vendor/golang.org/x/crypto/tea/tea_test.go create mode 100644 vendor/golang.org/x/crypto/twofish/twofish.go create mode 100644 vendor/golang.org/x/crypto/twofish/twofish_test.go create mode 100644 vendor/golang.org/x/crypto/xtea/block.go create mode 100644 vendor/golang.org/x/crypto/xtea/cipher.go create mode 100644 vendor/golang.org/x/crypto/xtea/xtea_test.go create mode 100644 vendor/golang.org/x/crypto/xts/xts.go create mode 100644 vendor/golang.org/x/crypto/xts/xts_test.go create mode 100644 vendor/golang.org/x/image/.gitattributes create mode 100644 vendor/golang.org/x/image/.gitignore create mode 100644 vendor/golang.org/x/image/AUTHORS create mode 100644 vendor/golang.org/x/image/CONTRIBUTING.md create mode 100644 vendor/golang.org/x/image/CONTRIBUTORS create mode 100644 vendor/golang.org/x/image/README create mode 100644 vendor/golang.org/x/image/bmp/reader_test.go create mode 100644 vendor/golang.org/x/image/bmp/writer_test.go create mode 100644 vendor/golang.org/x/image/cmd/webp-manual-test/main.go create mode 100644 vendor/golang.org/x/image/codereview.cfg create mode 100644 vendor/golang.org/x/image/colornames/colornames.go create mode 100644 vendor/golang.org/x/image/colornames/colornames_test.go create mode 100644 vendor/golang.org/x/image/colornames/gen.go create mode 100644 vendor/golang.org/x/image/colornames/table.go create mode 100644 vendor/golang.org/x/image/draw/draw.go create mode 100644 vendor/golang.org/x/image/draw/example_test.go create mode 100644 vendor/golang.org/x/image/draw/gen.go create mode 100644 vendor/golang.org/x/image/draw/impl.go create mode 100644 vendor/golang.org/x/image/draw/scale.go create mode 100644 vendor/golang.org/x/image/draw/scale_test.go create mode 100644 vendor/golang.org/x/image/draw/stdlib_test.go create mode 100644 vendor/golang.org/x/image/example/font/main.go create mode 100644 vendor/golang.org/x/image/font/basicfont/basicfont.go create mode 100644 vendor/golang.org/x/image/font/basicfont/data.go create mode 100644 vendor/golang.org/x/image/font/basicfont/gen.go create mode 100644 vendor/golang.org/x/image/font/plan9font/example_test.go create mode 100644 vendor/golang.org/x/image/font/plan9font/plan9font.go create mode 100644 vendor/golang.org/x/image/font/plan9font/plan9font_test.go create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.0000 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.0100 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.0200 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.0300 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.0400 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.0500 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.0E00 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.1000 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.1600 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.1E00 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.1F00 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2000 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2100 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2200 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2300 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2400 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2500 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2600 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2700 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2800 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.2A00 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.3000 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.FB00 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.FE00 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/7x13.FF00 create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/README create mode 100644 vendor/golang.org/x/image/font/testdata/fixed/unicode.7x13.font create mode 100644 vendor/golang.org/x/image/math/f32/f32.go create mode 100644 vendor/golang.org/x/image/math/f64/f64.go create mode 100644 vendor/golang.org/x/image/math/fixed/fixed_test.go create mode 100644 vendor/golang.org/x/image/riff/example_test.go create mode 100644 vendor/golang.org/x/image/riff/riff.go create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink-large.lossless.webp create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink-large.no-filter.lossy.webp create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink-large.no-filter.lossy.webp.ycbcr.png create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink-large.normal-filter.lossy.webp create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink-large.normal-filter.lossy.webp.ycbcr.png create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink-large.png create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink-large.simple-filter.lossy.webp create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink-large.simple-filter.lossy.webp.ycbcr.png create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink.lossless.webp create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink.lossy.webp create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink.lossy.webp.ycbcr.png create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink.lzwcompressed.tiff create mode 100644 vendor/golang.org/x/image/testdata/blue-purple-pink.png create mode 100644 vendor/golang.org/x/image/testdata/bw-deflate.tiff create mode 100644 vendor/golang.org/x/image/testdata/bw-packbits.tiff create mode 100644 vendor/golang.org/x/image/testdata/bw-uncompressed.tiff create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-14x18.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-280x360.jpeg create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-down-ab.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-down-bl.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-down-cr.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-down-nn.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-rotate-ab.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-rotate-bl.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-rotate-cr.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-rotate-nn.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-up-ab.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-up-bl.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-up-cr.png create mode 100644 vendor/golang.org/x/image/testdata/go-turns-two-up-nn.png create mode 100644 vendor/golang.org/x/image/testdata/gopher-doc.1bpp.lossless.webp create mode 100644 vendor/golang.org/x/image/testdata/gopher-doc.1bpp.png create mode 100644 vendor/golang.org/x/image/testdata/gopher-doc.2bpp.lossless.webp create mode 100644 vendor/golang.org/x/image/testdata/gopher-doc.2bpp.png create mode 100644 vendor/golang.org/x/image/testdata/gopher-doc.4bpp.lossless.webp create mode 100644 vendor/golang.org/x/image/testdata/gopher-doc.4bpp.png create mode 100644 vendor/golang.org/x/image/testdata/gopher-doc.8bpp.lossless.webp create mode 100644 vendor/golang.org/x/image/testdata/gopher-doc.8bpp.png create mode 100644 vendor/golang.org/x/image/testdata/no_compress.tiff create mode 100644 vendor/golang.org/x/image/testdata/no_rps.tiff create mode 100644 vendor/golang.org/x/image/testdata/testpattern.png create mode 100644 vendor/golang.org/x/image/testdata/tux-rotate-ab.png create mode 100644 vendor/golang.org/x/image/testdata/tux-rotate-bl.png create mode 100644 vendor/golang.org/x/image/testdata/tux-rotate-cr.png create mode 100644 vendor/golang.org/x/image/testdata/tux-rotate-nn.png create mode 100644 vendor/golang.org/x/image/testdata/tux.lossless.webp create mode 100644 vendor/golang.org/x/image/testdata/tux.png create mode 100644 vendor/golang.org/x/image/testdata/video-001-16bit.tiff create mode 100644 vendor/golang.org/x/image/testdata/video-001-gray-16bit.tiff create mode 100644 vendor/golang.org/x/image/testdata/video-001-gray.tiff create mode 100644 vendor/golang.org/x/image/testdata/video-001-paletted.tiff create mode 100644 vendor/golang.org/x/image/testdata/video-001-strip-64.tiff create mode 100644 vendor/golang.org/x/image/testdata/video-001-tile-64x64.tiff create mode 100644 vendor/golang.org/x/image/testdata/video-001-uncompressed.tiff create mode 100644 vendor/golang.org/x/image/testdata/video-001.bmp create mode 100644 vendor/golang.org/x/image/testdata/video-001.lossy.webp create mode 100644 vendor/golang.org/x/image/testdata/video-001.lossy.webp.ycbcr.png create mode 100644 vendor/golang.org/x/image/testdata/video-001.png create mode 100644 vendor/golang.org/x/image/testdata/video-001.tiff create mode 100644 vendor/golang.org/x/image/testdata/yellow_rose-small.bmp create mode 100644 vendor/golang.org/x/image/testdata/yellow_rose-small.png create mode 100644 vendor/golang.org/x/image/testdata/yellow_rose.lossless.webp create mode 100644 vendor/golang.org/x/image/testdata/yellow_rose.lossy-with-alpha.webp create mode 100644 vendor/golang.org/x/image/testdata/yellow_rose.lossy-with-alpha.webp.nycbcra.png create mode 100644 vendor/golang.org/x/image/testdata/yellow_rose.lossy.webp create mode 100644 vendor/golang.org/x/image/testdata/yellow_rose.lossy.webp.ycbcr.png create mode 100644 vendor/golang.org/x/image/testdata/yellow_rose.png create mode 100644 vendor/golang.org/x/image/tiff/buffer_test.go create mode 100644 vendor/golang.org/x/image/tiff/reader_test.go create mode 100644 vendor/golang.org/x/image/tiff/writer_test.go create mode 100644 vendor/golang.org/x/image/vp8/decode.go create mode 100644 vendor/golang.org/x/image/vp8/filter.go create mode 100644 vendor/golang.org/x/image/vp8/idct.go create mode 100644 vendor/golang.org/x/image/vp8/partition.go create mode 100644 vendor/golang.org/x/image/vp8/pred.go create mode 100644 vendor/golang.org/x/image/vp8/predfunc.go create mode 100644 vendor/golang.org/x/image/vp8/quant.go create mode 100644 vendor/golang.org/x/image/vp8/reconstruct.go create mode 100644 vendor/golang.org/x/image/vp8/token.go create mode 100644 vendor/golang.org/x/image/vp8l/decode.go create mode 100644 vendor/golang.org/x/image/vp8l/huffman.go create mode 100644 vendor/golang.org/x/image/vp8l/transform.go create mode 100644 vendor/golang.org/x/image/webp/decode.go create mode 100644 vendor/golang.org/x/image/webp/decode_test.go create mode 100644 vendor/golang.org/x/image/webp/nycbcra/nycbcra.go create mode 100644 vendor/golang.org/x/sys/.gitattributes create mode 100644 vendor/golang.org/x/sys/.gitignore create mode 100644 vendor/golang.org/x/sys/AUTHORS create mode 100644 vendor/golang.org/x/sys/CONTRIBUTING.md create mode 100644 vendor/golang.org/x/sys/CONTRIBUTORS create mode 100644 vendor/golang.org/x/sys/README create mode 100644 vendor/golang.org/x/sys/codereview.cfg create mode 100644 vendor/golang.org/x/sys/plan9/asm.s create mode 100644 vendor/golang.org/x/sys/plan9/asm_plan9_386.s create mode 100644 vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s create mode 100644 vendor/golang.org/x/sys/plan9/const_plan9.go create mode 100644 vendor/golang.org/x/sys/plan9/dir_plan9.go create mode 100644 vendor/golang.org/x/sys/plan9/env_plan9.go create mode 100644 vendor/golang.org/x/sys/plan9/env_unset.go create mode 100644 vendor/golang.org/x/sys/plan9/errors_plan9.go create mode 100755 vendor/golang.org/x/sys/plan9/mkall.sh create mode 100755 vendor/golang.org/x/sys/plan9/mkerrors.sh create mode 100755 vendor/golang.org/x/sys/plan9/mksyscall.pl create mode 100755 vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh create mode 100644 vendor/golang.org/x/sys/plan9/pwd_go15_plan9.go create mode 100644 vendor/golang.org/x/sys/plan9/pwd_plan9.go create mode 100644 vendor/golang.org/x/sys/plan9/race.go create mode 100644 vendor/golang.org/x/sys/plan9/race0.go create mode 100644 vendor/golang.org/x/sys/plan9/str.go create mode 100644 vendor/golang.org/x/sys/plan9/syscall.go create mode 100644 vendor/golang.org/x/sys/plan9/syscall_plan9.go create mode 100644 vendor/golang.org/x/sys/plan9/syscall_test.go create mode 100644 vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go create mode 100644 vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go create mode 100644 vendor/golang.org/x/sys/plan9/zsysnum_plan9.go delete mode 100644 vendor/golang.org/x/sys/unix/asm_dragonfly_386.s create mode 100644 vendor/golang.org/x/sys/unix/creds_test.go create mode 100644 vendor/golang.org/x/sys/unix/export_test.go mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mkall.sh mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mkerrors.sh mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mksyscall.pl mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mksyscall_solaris.pl mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mksysctl_openbsd.pl mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mksysnum_darwin.pl mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mksysnum_dragonfly.pl mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mksysnum_freebsd.pl mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mksysnum_linux.pl mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mksysnum_netbsd.pl mode change 100644 => 100755 vendor/golang.org/x/sys/unix/mksysnum_openbsd.pl create mode 100644 vendor/golang.org/x/sys/unix/mmap_unix_test.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_bsd_test.go delete mode 100644 vendor/golang.org/x/sys/unix/syscall_dragonfly_386.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_freebsd_test.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_linux_test.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_test.go create mode 100644 vendor/golang.org/x/sys/unix/syscall_unix_test.go delete mode 100644 vendor/golang.org/x/sys/unix/zerrors_dragonfly_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsyscall_dragonfly_386.go delete mode 100644 vendor/golang.org/x/sys/unix/zsysnum_dragonfly_386.go delete mode 100644 vendor/golang.org/x/sys/unix/ztypes_dragonfly_386.go create mode 100644 vendor/golang.org/x/sys/windows/asm_windows_386.s create mode 100644 vendor/golang.org/x/sys/windows/asm_windows_amd64.s create mode 100644 vendor/golang.org/x/sys/windows/dll_windows.go create mode 100644 vendor/golang.org/x/sys/windows/env_unset.go create mode 100644 vendor/golang.org/x/sys/windows/env_windows.go create mode 100644 vendor/golang.org/x/sys/windows/eventlog.go create mode 100644 vendor/golang.org/x/sys/windows/exec_windows.go create mode 100644 vendor/golang.org/x/sys/windows/race.go create mode 100644 vendor/golang.org/x/sys/windows/race0.go create mode 100644 vendor/golang.org/x/sys/windows/registry/export_test.go create mode 100644 vendor/golang.org/x/sys/windows/registry/key.go create mode 100644 vendor/golang.org/x/sys/windows/registry/registry_test.go create mode 100644 vendor/golang.org/x/sys/windows/registry/syscall.go create mode 100644 vendor/golang.org/x/sys/windows/registry/value.go create mode 100644 vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go create mode 100644 vendor/golang.org/x/sys/windows/security_windows.go create mode 100644 vendor/golang.org/x/sys/windows/service.go create mode 100644 vendor/golang.org/x/sys/windows/str.go create mode 100644 vendor/golang.org/x/sys/windows/svc/debug/log.go create mode 100644 vendor/golang.org/x/sys/windows/svc/debug/service.go create mode 100644 vendor/golang.org/x/sys/windows/svc/event.go create mode 100644 vendor/golang.org/x/sys/windows/svc/eventlog/install.go create mode 100644 vendor/golang.org/x/sys/windows/svc/eventlog/log.go create mode 100644 vendor/golang.org/x/sys/windows/svc/eventlog/log_test.go create mode 100644 vendor/golang.org/x/sys/windows/svc/example/beep.go create mode 100644 vendor/golang.org/x/sys/windows/svc/example/install.go create mode 100644 vendor/golang.org/x/sys/windows/svc/example/main.go create mode 100644 vendor/golang.org/x/sys/windows/svc/example/manage.go create mode 100644 vendor/golang.org/x/sys/windows/svc/example/service.go create mode 100644 vendor/golang.org/x/sys/windows/svc/go12.c create mode 100644 vendor/golang.org/x/sys/windows/svc/go12.go create mode 100644 vendor/golang.org/x/sys/windows/svc/go13.go create mode 100644 vendor/golang.org/x/sys/windows/svc/mgr/config.go create mode 100644 vendor/golang.org/x/sys/windows/svc/mgr/mgr.go create mode 100644 vendor/golang.org/x/sys/windows/svc/mgr/mgr_test.go create mode 100644 vendor/golang.org/x/sys/windows/svc/mgr/service.go create mode 100644 vendor/golang.org/x/sys/windows/svc/security.go create mode 100644 vendor/golang.org/x/sys/windows/svc/service.go create mode 100644 vendor/golang.org/x/sys/windows/svc/svc_test.go create mode 100644 vendor/golang.org/x/sys/windows/svc/sys_386.s create mode 100644 vendor/golang.org/x/sys/windows/svc/sys_amd64.s create mode 100644 vendor/golang.org/x/sys/windows/syscall.go create mode 100644 vendor/golang.org/x/sys/windows/syscall_test.go create mode 100644 vendor/golang.org/x/sys/windows/syscall_windows.go create mode 100644 vendor/golang.org/x/sys/windows/syscall_windows_test.go create mode 100644 vendor/golang.org/x/sys/windows/zsyscall_windows.go create mode 100644 vendor/golang.org/x/sys/windows/ztypes_windows.go create mode 100644 vendor/golang.org/x/sys/windows/ztypes_windows_386.go create mode 100644 vendor/golang.org/x/sys/windows/ztypes_windows_amd64.go create mode 100644 vendor/gopkg.in/asn1-ber.v1/ber_test.go create mode 100644 vendor/gopkg.in/asn1-ber.v1/header_test.go create mode 100644 vendor/gopkg.in/asn1-ber.v1/identifier_test.go create mode 100644 vendor/gopkg.in/asn1-ber.v1/length_test.go create mode 100644 vendor/gopkg.in/asn1-ber.v1/suite_test.go create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc1.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc10.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc11.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc12.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc13.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc14.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc15.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc16.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc17.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc18.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc19.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc2.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc20.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc21.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc22.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc23.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc24.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc25.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc26.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc27.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc28.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc29.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc3.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc30.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc31.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc32.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc33.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc34.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc35.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc36.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc37.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc38.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc39.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc4.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc40.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc41.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc42.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc43.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc44.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc45.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc46.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc47.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc48.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc5.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc6.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc7.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc8.ber create mode 100644 vendor/gopkg.in/asn1-ber.v1/tests/tc9.ber create mode 100644 vendor/gopkg.in/fsnotify.v1/example_test.go create mode 100644 vendor/gopkg.in/fsnotify.v1/inotify_poller_test.go create mode 100644 vendor/gopkg.in/fsnotify.v1/inotify_test.go create mode 100644 vendor/gopkg.in/fsnotify.v1/integration_darwin_test.go create mode 100644 vendor/gopkg.in/fsnotify.v1/integration_test.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/common_test.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/delayer_test.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/examples/README.md create mode 100644 vendor/gopkg.in/throttled/throttled.v1/examples/custom/main.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/examples/interval-many/main.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/main.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/examples/interval-vary/siege-urls create mode 100644 vendor/gopkg.in/throttled/throttled.v1/examples/interval/main.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/examples/memstats/main.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/examples/memstats/test-file create mode 100644 vendor/gopkg.in/throttled/throttled.v1/examples/rate-limit/main.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/interval_test.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/memstats_test.go create mode 100755 vendor/gopkg.in/throttled/throttled.v1/misc/pre-commit create mode 100644 vendor/gopkg.in/throttled/throttled.v1/rate_test.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/store/mem_test.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/store/redis_test.go create mode 100644 vendor/gopkg.in/throttled/throttled.v1/varyby_test.go create mode 100644 vendor/gopkg.in/yaml.v2/decode_test.go create mode 100644 vendor/gopkg.in/yaml.v2/encode_test.go create mode 100644 vendor/gopkg.in/yaml.v2/suite_test.go diff --git a/.gitignore b/.gitignore index 882c5a783..1fba4cd03 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,8 @@ web/sass-files/sass/.sass-cache/ data/* api/data/* +enterprise + .agignore .ctags tags diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json deleted file mode 100644 index cf298917d..000000000 --- a/Godeps/Godeps.json +++ /dev/null @@ -1,221 +0,0 @@ -{ - "ImportPath": "github.com/mattermost/platform", - "GoVersion": "go1.6", - "GodepVersion": "v65", - "Deps": [ - { - "ImportPath": "github.com/NYTimes/gziphandler", - "Rev": "63027b26b87e2ae2ce3810393d4b81021cfd3a35" - }, - { - "ImportPath": "github.com/alecthomas/log4go", - "Rev": "e5dc62318d9bd58682f1dceb53a4b24e8253682f" - }, - { - "ImportPath": "github.com/braintree/manners", - "Comment": "0.4.0-15-g82a8879", - "Rev": "82a8879fc5fd0381fa8b2d8033b19bf255252088" - }, - { - "ImportPath": "github.com/cloudfoundry/jibber_jabber", - "Rev": "bcc4c8345a21301bf47c032ff42dd1aae2fe3027" - }, - { - "ImportPath": "github.com/dgryski/dgoogauth", - "Rev": "67642ac6f9144f6610279e37e7be9af13f1cd668" - }, - { - "ImportPath": "github.com/disintegration/imaging", - "Rev": "d8bbae1de109b518dabc98c6c1633eb358c148a4" - }, - { - "ImportPath": "github.com/garyburd/redigo/internal", - "Rev": "8873b2f1995f59d4bcdd2b0dc9858e2cb9bf0c13" - }, - { - "ImportPath": "github.com/garyburd/redigo/redis", - "Rev": "8873b2f1995f59d4bcdd2b0dc9858e2cb9bf0c13" - }, - { - "ImportPath": "github.com/go-gorp/gorp", - "Comment": "v1.7-184-g6a3c8a8", - "Rev": "6a3c8a87d0457cf700e57046c41e19b7cf3c44fa" - }, - { - "ImportPath": "github.com/go-ldap/ldap", - "Comment": "v2.3.0", - "Rev": "0e7db8eb77695b5a952f0e5d78df9ab160050c73" - }, - { - "ImportPath": "github.com/go-sql-driver/mysql", - "Comment": "v1.2-194-g7ebe0a5", - "Rev": "7ebe0a500653eeb1859664bed5e48dec1e164e73" - }, - { - "ImportPath": "github.com/goamz/goamz/aws", - "Rev": "02d5144a587b982e33b95f484a34164ce6923c99" - }, - { - "ImportPath": "github.com/goamz/goamz/s3", - "Rev": "02d5144a587b982e33b95f484a34164ce6923c99" - }, - { - "ImportPath": "github.com/golang/freetype", - "Comment": "release-129-gc67e4d9", - "Rev": "c67e4d98d212356ec0d9436a1edcbb6eb799f847" - }, - { - "ImportPath": "github.com/golang/freetype/raster", - "Comment": "release-129-gc67e4d9", - "Rev": "c67e4d98d212356ec0d9436a1edcbb6eb799f847" - }, - { - "ImportPath": "github.com/golang/freetype/truetype", - "Comment": "release-129-gc67e4d9", - "Rev": "c67e4d98d212356ec0d9436a1edcbb6eb799f847" - }, - { - "ImportPath": "github.com/golang/groupcache/lru", - "Rev": "4eab30f13db9d8b25c752e99d1583628ac2fa422" - }, - { - "ImportPath": "github.com/gorilla/context", - "Comment": "v1.1-2-ga8d44e7", - "Rev": "a8d44e7d8e4d532b6a27a02dd82abb31cc1b01bd" - }, - { - "ImportPath": "github.com/gorilla/handlers", - "Comment": "v1.1-6-g66e6c6f", - "Rev": "66e6c6f01d8da976ee113437745ca029c2b585a6" - }, - { - "ImportPath": "github.com/gorilla/mux", - "Comment": "v1.1-7-g9c19ed5", - "Rev": "9c19ed558d5df4da88e2ade9c8940d742aef0e7e" - }, - { - "ImportPath": "github.com/gorilla/websocket", - "Rev": "1f512fc3f05332ba7117626cdfb4e07474e58e60" - }, - { - "ImportPath": "github.com/lib/pq", - "Comment": "go1.0-cutoff-86-gdd3290b", - "Rev": "dd3290b2f71a8b30bee8e4e75a337a825263d26f" - }, - { - "ImportPath": "github.com/lib/pq/oid", - "Comment": "go1.0-cutoff-86-gdd3290b", - "Rev": "dd3290b2f71a8b30bee8e4e75a337a825263d26f" - }, - { - "ImportPath": "github.com/mattermost/rsc/gf256", - "Rev": "bbaefb05eaa0389ea712340066837c8ce4d287f9" - }, - { - "ImportPath": "github.com/mattermost/rsc/qr", - "Rev": "bbaefb05eaa0389ea712340066837c8ce4d287f9" - }, - { - "ImportPath": "github.com/mattermost/rsc/qr/coding", - "Rev": "bbaefb05eaa0389ea712340066837c8ce4d287f9" - }, - { - "ImportPath": "github.com/mssola/user_agent", - "Comment": "v0.4.1-14-g8e786bc", - "Rev": "8e786bcb38b846e5eb8eb5f036d9144fc7b0a1f8" - }, - { - "ImportPath": "github.com/nicksnyder/go-i18n/i18n", - "Comment": "v1.4.0", - "Rev": "37e5c2de3e03e4b82693e3fcb4a6aa2cc4eb07e3" - }, - { - "ImportPath": "github.com/nicksnyder/go-i18n/i18n/bundle", - "Comment": "v1.4.0", - "Rev": "37e5c2de3e03e4b82693e3fcb4a6aa2cc4eb07e3" - }, - { - "ImportPath": "github.com/nicksnyder/go-i18n/i18n/language", - "Comment": "v1.4.0", - "Rev": "37e5c2de3e03e4b82693e3fcb4a6aa2cc4eb07e3" - }, - { - "ImportPath": "github.com/nicksnyder/go-i18n/i18n/translation", - "Comment": "v1.4.0", - "Rev": "37e5c2de3e03e4b82693e3fcb4a6aa2cc4eb07e3" - }, - { - "ImportPath": "github.com/pborman/uuid", - "Comment": "v1.0-11-gc55201b", - "Rev": "c55201b036063326c5b1b89ccfe45a184973d073" - }, - { - "ImportPath": "github.com/rwcarlsen/goexif/exif", - "Rev": "709fab3d192d7c62f86043caff1e7e3fb0f42bd8" - }, - { - "ImportPath": "github.com/rwcarlsen/goexif/tiff", - "Rev": "709fab3d192d7c62f86043caff1e7e3fb0f42bd8" - }, - { - "ImportPath": "github.com/vaughan0/go-ini", - "Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1" - }, - { - "ImportPath": "golang.org/x/crypto/bcrypt", - "Rev": "91ab96ae987aef3e74ab78b3aaf026109d206148" - }, - { - "ImportPath": "golang.org/x/crypto/blowfish", - "Rev": "91ab96ae987aef3e74ab78b3aaf026109d206148" - }, - { - "ImportPath": "golang.org/x/image/bmp", - "Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec" - }, - { - "ImportPath": "golang.org/x/image/font", - "Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec" - }, - { - "ImportPath": "golang.org/x/image/math/fixed", - "Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec" - }, - { - "ImportPath": "golang.org/x/image/tiff", - "Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec" - }, - { - "ImportPath": "golang.org/x/image/tiff/lzw", - "Rev": "f551d3a6b7fc11df315ad9e18b404280680f8bec" - }, - { - "ImportPath": "golang.org/x/sys/unix", - "Rev": "b776ec39b3e54652e09028aaaaac9757f4f8211a" - }, - { - "ImportPath": "gopkg.in/asn1-ber.v1", - "Comment": "v1.1", - "Rev": "4e86f4367175e39f69d9358a5f17b4dda270378d" - }, - { - "ImportPath": "gopkg.in/fsnotify.v1", - "Comment": "v1.3.0", - "Rev": "30411dbcefb7a1da7e84f75530ad3abe4011b4f8" - }, - { - "ImportPath": "gopkg.in/throttled/throttled.v1", - "Comment": "v1.0.0", - "Rev": "74e328a1af88a9b54f9eca1397d74ad98572a6df" - }, - { - "ImportPath": "gopkg.in/throttled/throttled.v1/store", - "Comment": "v1.0.0", - "Rev": "74e328a1af88a9b54f9eca1397d74ad98572a6df" - }, - { - "ImportPath": "gopkg.in/yaml.v2", - "Rev": "a83829b6f1293c91addabc89d0571c246397bbf4" - } - ] -} diff --git a/Godeps/Readme b/Godeps/Readme deleted file mode 100644 index 4cdaa53d5..000000000 --- a/Godeps/Readme +++ /dev/null @@ -1,5 +0,0 @@ -This directory tree is generated automatically by godep. - -Please do not edit. - -See https://github.com/tools/godep for more information. diff --git a/Makefile b/Makefile index 4231fe805..2ead86fb5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: build package run stop run-client run-server stop-client stop-server restart-server restart-client start-docker clean-dist clean nuke check-style check-unit-tests test dist setup-mac prepare-enteprise run-client-tests setup-run-client-tests cleanup-run-client-tests test-client build-linux build-osx build-windows # For golang 1.5.x compatibility (remove when we don't want to support it anymore) -GO15VENDOREXPERIMENT=1 +export GO15VENDOREXPERIMENT=1 # Build Flags BUILD_NUMBER ?= $(BUILD_NUMBER:) @@ -30,9 +30,9 @@ endif BUILD_WEBAPP_DIR = ./webapp # Golang Flags -GOPATH ?= $(GOPATH:) +GOPATH ?= $(GOPATH:):./vendor GOFLAGS ?= $(GOFLAGS:) -GO=$(GOPATH)/bin/godep go +GO=go GO_LINKER_FLAGS ?= -ldflags \ "-X github.com/mattermost/platform/model.BuildNumber=$(BUILD_NUMBER)\ -X 'github.com/mattermost/platform/model.BuildDate=$(BUILD_DATE)'\ @@ -143,18 +143,20 @@ check-style: exit 1; \ fi -test: start-docker +test: prepare-enteprise start-docker @echo Running tests - $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=340s ./api || exit 1 - $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1 - $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./store || exit 1 - $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1 - $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1 + #$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=340s ./api || exit 1 + #$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1 + #$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./store || exit 1 + #$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1 + #$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1 ifeq ($(BUILD_ENTERPRISE_READY),true) @echo Running Enterprise tests - $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s $(BUILD_ENTERPRISE_DIR)/ldap || exit 1 - $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s $(BUILD_ENTERPRISE_DIR)/compliance || exit 1 + $(GO) test $(GOFLAGS) -run=$(TESTS) -c ./enterprise/ldap && ./ldap.test -test.v -test.timeout=120s || exit 1 + $(GO) test $(GOFLAGS) -run=$(TESTS) -c ./enterprise/compliance && ./compliance.test -test.v -test.timeout=120s || exit 1 + rm -r ldap.test + rm -r compliance.test endif setup-run-client-tests: @@ -173,7 +175,7 @@ test-client: setup-run-client-tests run-server run-client-tests stop-server clea .prebuild: @echo Preparation for running go code - go get $(GOFLAGS) github.com/tools/godep + go get $(GOFLAGS) github.com/Masterminds/glide touch $@ @@ -181,6 +183,8 @@ prepare-enterprise: ifeq ($(BUILD_ENTERPRISE_READY),true) @echo Enterprise build selected, preparing cp $(BUILD_ENTERPRISE_DIR)/imports.go . + rm -f enterprise + ln -s $(BUILD_ENTERPRISE_DIR) enterprise endif build-linux: .prebuild prepare-enterprise @@ -318,10 +322,9 @@ clean: stop-docker rm -rf api/data rm -rf logs - rm -rf Godeps/_workspace/pkg/ - rm -f mattermost.log rm -f .prepare-go + rm -f enterprise nuke: clean clean-docker @echo BOOM diff --git a/api/apitestlib.go b/api/apitestlib.go index 6372ea6b1..ab342c6b7 100644 --- a/api/apitestlib.go +++ b/api/apitestlib.go @@ -27,16 +27,16 @@ type TestHelper struct { SystemAdminChannel *model.Channel } -func SetupEnterprise(platformDir string) *TestHelper { +func SetupEnterprise() *TestHelper { if Srv == nil { - utils.LoadConfig(platformDir + "/config/config.json") - utils.InitTranslationsWithDir(platformDir + "/i18n") + utils.LoadConfig("config.json") + utils.InitTranslations() utils.Cfg.TeamSettings.MaxUsersPerTeam = 50 utils.DisableDebugLogForTest() utils.License.Features.SetDefaults() NewServer() StartServer() - utils.InitHTMLWithDir(platformDir + "/templates") + utils.InitHTML() InitApi() utils.EnableDebugLogForTest() Srv.Store.MarkSystemRanUnitTests() diff --git a/glide.lock b/glide.lock new file mode 100644 index 000000000..1d93d5484 --- /dev/null +++ b/glide.lock @@ -0,0 +1,104 @@ +hash: aa2fadc7f997a93e78d46d009d5695fb079697453d83e6a6dd7481d46ce73b7e +updated: 2016-05-12T19:14:13.836695608-04:00 +imports: +- name: github.com/alecthomas/log4go + version: e5dc62318d9bd58682f1dceb53a4b24e8253682f +- name: github.com/braintree/manners + version: 82a8879fc5fd0381fa8b2d8033b19bf255252088 +- name: github.com/cloudfoundry/jibber_jabber + version: bcc4c8345a21301bf47c032ff42dd1aae2fe3027 +- name: github.com/dgryski/dgoogauth + version: 67642ac6f9144f6610279e37e7be9af13f1cd668 +- name: github.com/disintegration/imaging + version: d8bbae1de109b518dabc98c6c1633eb358c148a4 +- name: github.com/garyburd/redigo + version: 8873b2f1995f59d4bcdd2b0dc9858e2cb9bf0c13 + subpackages: + - redis + - internal +- name: github.com/go-gorp/gorp + version: 6a3c8a87d0457cf700e57046c41e19b7cf3c44fa +- name: github.com/go-ldap/ldap + version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73 +- name: github.com/go-sql-driver/mysql + version: 7ebe0a500653eeb1859664bed5e48dec1e164e73 +- name: github.com/goamz/goamz + version: 02d5144a587b982e33b95f484a34164ce6923c99 + subpackages: + - aws + - s3 +- name: github.com/golang/freetype + version: c67e4d98d212356ec0d9436a1edcbb6eb799f847 + subpackages: + - raster + - truetype +- name: github.com/golang/groupcache + version: 4eab30f13db9d8b25c752e99d1583628ac2fa422 + subpackages: + - lru +- name: github.com/gorilla/context + version: a8d44e7d8e4d532b6a27a02dd82abb31cc1b01bd +- name: github.com/gorilla/handlers + version: 66e6c6f01d8da976ee113437745ca029c2b585a6 +- name: github.com/gorilla/mux + version: 9c19ed558d5df4da88e2ade9c8940d742aef0e7e +- name: github.com/gorilla/websocket + version: 1f512fc3f05332ba7117626cdfb4e07474e58e60 +- name: github.com/lib/pq + version: ee1442bda7bd1b6a84e913bdb421cb1874ec629d + subpackages: + - oid +- name: github.com/mattermost/rsc + version: bbaefb05eaa0389ea712340066837c8ce4d287f9 + subpackages: + - qr + - qr/coding + - gf256 +- name: github.com/mssola/user_agent + version: 8e786bcb38b846e5eb8eb5f036d9144fc7b0a1f8 +- name: github.com/nicksnyder/go-i18n + version: 37e5c2de3e03e4b82693e3fcb4a6aa2cc4eb07e3 + subpackages: + - i18n + - i18n/bundle + - i18n/language + - i18n/translation +- name: github.com/NYTimes/gziphandler + version: 63027b26b87e2ae2ce3810393d4b81021cfd3a35 +- name: github.com/pborman/uuid + version: c55201b036063326c5b1b89ccfe45a184973d073 +- name: github.com/rwcarlsen/goexif + version: 709fab3d192d7c62f86043caff1e7e3fb0f42bd8 + subpackages: + - exif + - tiff +- name: github.com/vaughan0/go-ini + version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1 +- name: golang.org/x/crypto + version: 1e61df8d9ea476e2e1504cd9a32b40280c7c6c7e + subpackages: + - bcrypt + - blowfish +- name: golang.org/x/image + version: f551d3a6b7fc11df315ad9e18b404280680f8bec + subpackages: + - bmp + - tiff + - font + - math/fixed + - tiff/lzw +- name: golang.org/x/sys + version: e82cb4d7dffc35bcec7bc8bf9e402377e0ecf3f4 + subpackages: + - unix +- name: gopkg.in/asn1-ber.v1 + version: 4e86f4367175e39f69d9358a5f17b4dda270378d +- name: gopkg.in/fsnotify.v1 + version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8 +- name: gopkg.in/throttled/throttled.v1 + version: 74e328a1af88a9b54f9eca1397d74ad98572a6df + subpackages: + - store +- name: gopkg.in/yaml.v2 + version: a83829b6f1293c91addabc89d0571c246397bbf4 +devImports: [] diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 000000000..396b9f81a --- /dev/null +++ b/glide.yaml @@ -0,0 +1,41 @@ +package: github.com/mattermost/platform +import: +- package: github.com/NYTimes/gziphandler +- package: github.com/alecthomas/log4go +- package: github.com/braintree/manners +- package: github.com/cloudfoundry/jibber_jabber +- package: github.com/dgryski/dgoogauth +- package: github.com/disintegration/imaging +- package: github.com/go-gorp/gorp +- package: github.com/go-ldap/ldap +- package: github.com/go-sql-driver/mysql +- package: github.com/goamz/goamz + subpackages: + - aws + - s3 +- package: github.com/golang/freetype +- package: github.com/gorilla/handlers +- package: github.com/gorilla/mux +- package: github.com/gorilla/websocket +- package: github.com/lib/pq +- package: github.com/mattermost/rsc + subpackages: + - qr +- package: github.com/mssola/user_agent +- package: github.com/nicksnyder/go-i18n + subpackages: + - i18n +- package: github.com/pborman/uuid +- package: github.com/rwcarlsen/goexif + subpackages: + - exif +- package: golang.org/x/crypto + subpackages: + - bcrypt +- package: golang.org/x/image + subpackages: + - bmp +- package: gopkg.in/fsnotify.v1 +- package: gopkg.in/throttled/throttled.v1 + subpackages: + - store diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_test.go b/vendor/github.com/NYTimes/gziphandler/gzip_test.go new file mode 100644 index 000000000..9a62bcbaa --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/gzip_test.go @@ -0,0 +1,134 @@ +package gziphandler + +import ( + "bytes" + "compress/gzip" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseEncodings(t *testing.T) { + + examples := map[string]codings{ + + // Examples from RFC 2616 + "compress, gzip": codings{"compress": 1.0, "gzip": 1.0}, + "": codings{}, + "*": codings{"*": 1.0}, + "compress;q=0.5, gzip;q=1.0": codings{"compress": 0.5, "gzip": 1.0}, + "gzip;q=1.0, identity; q=0.5, *;q=0": codings{"gzip": 1.0, "identity": 0.5, "*": 0.0}, + + // More random stuff + "AAA;q=1": codings{"aaa": 1.0}, + "BBB ; q = 2": codings{"bbb": 1.0}, + } + + for eg, exp := range examples { + act, _ := parseEncodings(eg) + assert.Equal(t, exp, act) + } +} + +func TestGzipHandler(t *testing.T) { + testBody := "aaabbbccc" + + // This just exists to provide something for GzipHandler to wrap. + handler := newTestHandler(testBody) + + // requests without accept-encoding are passed along as-is + + req1, _ := http.NewRequest("GET", "/whatever", nil) + res1 := httptest.NewRecorder() + handler.ServeHTTP(res1, req1) + + assert.Equal(t, 200, res1.Code) + assert.Equal(t, "", res1.Header().Get("Content-Encoding")) + assert.Equal(t, "Accept-Encoding", res1.Header().Get("Vary")) + assert.Equal(t, testBody, res1.Body.String()) + + // but requests with accept-encoding:gzip are compressed if possible + + req2, _ := http.NewRequest("GET", "/whatever", nil) + req2.Header.Set("Accept-Encoding", "gzip") + res2 := httptest.NewRecorder() + handler.ServeHTTP(res2, req2) + + assert.Equal(t, 200, res2.Code) + assert.Equal(t, "gzip", res2.Header().Get("Content-Encoding")) + assert.Equal(t, "Accept-Encoding", res2.Header().Get("Vary")) + assert.Equal(t, gzipStr(testBody), res2.Body.Bytes()) + + // content-type header is correctly set based on uncompressed body + + req3, _ := http.NewRequest("GET", "/whatever", nil) + req3.Header.Set("Accept-Encoding", "gzip") + res3 := httptest.NewRecorder() + handler.ServeHTTP(res3, req3) + + assert.Equal(t, http.DetectContentType([]byte(testBody)), res3.Header().Get("Content-Type")) +} + +// -------------------------------------------------------------------- + +func BenchmarkGzipHandler_S2k(b *testing.B) { benchmark(b, false, 2048) } +func BenchmarkGzipHandler_S20k(b *testing.B) { benchmark(b, false, 20480) } +func BenchmarkGzipHandler_S100k(b *testing.B) { benchmark(b, false, 102400) } +func BenchmarkGzipHandler_P2k(b *testing.B) { benchmark(b, true, 2048) } +func BenchmarkGzipHandler_P20k(b *testing.B) { benchmark(b, true, 20480) } +func BenchmarkGzipHandler_P100k(b *testing.B) { benchmark(b, true, 102400) } + +// -------------------------------------------------------------------- + +func gzipStr(s string) []byte { + var b bytes.Buffer + w := gzip.NewWriter(&b) + io.WriteString(w, s) + w.Close() + return b.Bytes() +} + +func benchmark(b *testing.B, parallel bool, size int) { + bin, err := ioutil.ReadFile("testdata/benchmark.json") + if err != nil { + b.Fatal(err) + } + + req, _ := http.NewRequest("GET", "/whatever", nil) + req.Header.Set("Accept-Encoding", "gzip") + handler := newTestHandler(string(bin[:size])) + + if parallel { + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + runBenchmark(b, req, handler) + } + }) + } else { + b.ResetTimer() + for i := 0; i < b.N; i++ { + runBenchmark(b, req, handler) + } + } +} + +func runBenchmark(b *testing.B, req *http.Request, handler http.Handler) { + res := httptest.NewRecorder() + handler.ServeHTTP(res, req) + if code := res.Code; code != 200 { + b.Fatalf("Expected 200 but got %d", code) + } else if blen := res.Body.Len(); blen < 500 { + b.Fatalf("Expected complete response body, but got %d bytes", blen) + } +} + +func newTestHandler(body string) http.Handler { + return GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, body) + })) +} diff --git a/vendor/github.com/NYTimes/gziphandler/testdata/benchmark.json b/vendor/github.com/NYTimes/gziphandler/testdata/benchmark.json new file mode 100644 index 000000000..d9180d6ac --- /dev/null +++ b/vendor/github.com/NYTimes/gziphandler/testdata/benchmark.json @@ -0,0 +1,5456 @@ +[ + { + "_id": "55d2fc86da7c3f96f90aa005", + "age": 20, + "name": "Luann Grant", + "gender": "female", + "company": "ZOGAK", + "email": "luanngrant@zogak.com", + "phone": "+1 (915) 479-2908" + }, + { + "_id": "55d2fc8653953bc9a0958a92", + "age": 34, + "name": "Sanders Gonzalez", + "gender": "male", + "company": "PIVITOL", + "email": "sandersgonzalez@pivitol.com", + "phone": "+1 (914) 563-2007" + }, + { + "_id": "55d2fc86a38634b0954fe3c0", + "age": 26, + "name": "Compton Terry", + "gender": "male", + "company": "KOZGENE", + "email": "comptonterry@kozgene.com", + "phone": "+1 (812) 558-2536" + }, + { + "_id": "55d2fc86edf1be88253e4e2e", + "age": 29, + "name": "Erma Armstrong", + "gender": "female", + "company": "DYNO", + "email": "ermaarmstrong@dyno.com", + "phone": "+1 (811) 556-3980" + }, + { + "_id": "55d2fc86bee1bf1f233f8170", + "age": 20, + "name": "Carson Garcia", + "gender": "male", + "company": "JUNIPOOR", + "email": "carsongarcia@junipoor.com", + "phone": "+1 (820) 410-3221" + }, + { + "_id": "55d2fc864810db4a3738dea8", + "age": 38, + "name": "Henson Townsend", + "gender": "male", + "company": "OVIUM", + "email": "hensontownsend@ovium.com", + "phone": "+1 (982) 412-3108" + }, + { + "_id": "55d2fc86d714d77af8ed3fe4", + "age": 34, + "name": "Yesenia Garner", + "gender": "female", + "company": "TERRAGO", + "email": "yeseniagarner@terrago.com", + "phone": "+1 (878) 561-2314" + }, + { + "_id": "55d2fc867651311b2e11925a", + "age": 31, + "name": "Rachelle Stanton", + "gender": "female", + "company": "UNISURE", + "email": "rachellestanton@unisure.com", + "phone": "+1 (961) 504-3072" + }, + { + "_id": "55d2fc866415f6c03d5228eb", + "age": 30, + "name": "Franklin Rasmussen", + "gender": "male", + "company": "CAPSCREEN", + "email": "franklinrasmussen@capscreen.com", + "phone": "+1 (886) 525-2217" + }, + { + "_id": "55d2fc86e2e0fa5f81fe5279", + "age": 32, + "name": "Fischer Humphrey", + "gender": "male", + "company": "ATGEN", + "email": "fischerhumphrey@atgen.com", + "phone": "+1 (855) 424-3693" + }, + { + "_id": "55d2fc862c2a416777837c76", + "age": 29, + "name": "Olsen Moran", + "gender": "male", + "company": "SULTRAX", + "email": "olsenmoran@sultrax.com", + "phone": "+1 (860) 583-3380" + }, + { + "_id": "55d2fc868c3c9e44d59ec2a2", + "age": 29, + "name": "Mattie Myers", + "gender": "female", + "company": "VALPREAL", + "email": "mattiemyers@valpreal.com", + "phone": "+1 (834) 587-2707" + }, + { + "_id": "55d2fc860a6afc8beebe477d", + "age": 39, + "name": "Cleveland Jordan", + "gender": "male", + "company": "XLEEN", + "email": "clevelandjordan@xleen.com", + "phone": "+1 (848) 449-2037" + }, + { + "_id": "55d2fc8605766af1b531b5ea", + "age": 35, + "name": "Gordon Rios", + "gender": "male", + "company": "BULLZONE", + "email": "gordonrios@bullzone.com", + "phone": "+1 (882) 436-2216" + }, + { + "_id": "55d2fc86e052bfb7cf9b5a38", + "age": 33, + "name": "Todd Burch", + "gender": "male", + "company": "MOTOVATE", + "email": "toddburch@motovate.com", + "phone": "+1 (911) 470-2129" + }, + { + "_id": "55d2fc86212924928b4112d6", + "age": 31, + "name": "Autumn Strong", + "gender": "female", + "company": "NURPLEX", + "email": "autumnstrong@nurplex.com", + "phone": "+1 (827) 483-2571" + }, + { + "_id": "55d2fc86286c9bc87359e326", + "age": 27, + "name": "Rochelle Fitzgerald", + "gender": "female", + "company": "RAMEON", + "email": "rochellefitzgerald@rameon.com", + "phone": "+1 (820) 402-3411" + }, + { + "_id": "55d2fc86511439244d21c569", + "age": 24, + "name": "Perry Hopkins", + "gender": "male", + "company": "KRAGGLE", + "email": "perryhopkins@kraggle.com", + "phone": "+1 (826) 469-3928" + }, + { + "_id": "55d2fc868b8652b54c66051c", + "age": 37, + "name": "Guzman Williamson", + "gender": "male", + "company": "OCTOCORE", + "email": "guzmanwilliamson@octocore.com", + "phone": "+1 (826) 529-3380" + }, + { + "_id": "55d2fc86f16a5a4c310483df", + "age": 28, + "name": "Sheri Thompson", + "gender": "female", + "company": "AUTOGRATE", + "email": "sherithompson@autograte.com", + "phone": "+1 (942) 463-2727" + }, + { + "_id": "55d2fc86675d3040fc35daa8", + "age": 24, + "name": "Walton Macdonald", + "gender": "male", + "company": "ZIZZLE", + "email": "waltonmacdonald@zizzle.com", + "phone": "+1 (990) 510-2656" + }, + { + "_id": "55d2fc864e90e236f9e5a174", + "age": 39, + "name": "Gwendolyn Ross", + "gender": "female", + "company": "XIIX", + "email": "gwendolynross@xiix.com", + "phone": "+1 (869) 565-2774" + }, + { + "_id": "55d2fc866f306c575a36cb97", + "age": 23, + "name": "Sexton Herring", + "gender": "male", + "company": "MEDIOT", + "email": "sextonherring@mediot.com", + "phone": "+1 (935) 510-2049" + }, + { + "_id": "55d2fc867526db78550711a3", + "age": 26, + "name": "Twila Vang", + "gender": "female", + "company": "ARCTIQ", + "email": "twilavang@arctiq.com", + "phone": "+1 (979) 429-2135" + }, + { + "_id": "55d2fc86fe5e24c1b4b0dc96", + "age": 26, + "name": "Marjorie Snider", + "gender": "female", + "company": "QUARX", + "email": "marjoriesnider@quarx.com", + "phone": "+1 (913) 469-2916" + }, + { + "_id": "55d2fc861e85e0104fa7d33e", + "age": 36, + "name": "Malone Diaz", + "gender": "male", + "company": "COMVEYOR", + "email": "malonediaz@comveyor.com", + "phone": "+1 (882) 541-3306" + }, + { + "_id": "55d2fc86bc04a4fa0a338403", + "age": 39, + "name": "Amelia Sanford", + "gender": "female", + "company": "IPLAX", + "email": "ameliasanford@iplax.com", + "phone": "+1 (872) 589-3509" + }, + { + "_id": "55d2fc86f7854f672e80c1dd", + "age": 27, + "name": "Kristie Fernandez", + "gender": "female", + "company": "RONBERT", + "email": "kristiefernandez@ronbert.com", + "phone": "+1 (983) 419-3564" + }, + { + "_id": "55d2fc867faa140f152b7229", + "age": 23, + "name": "Bray Wyatt", + "gender": "male", + "company": "DEMINIMUM", + "email": "braywyatt@deminimum.com", + "phone": "+1 (943) 485-3961" + }, + { + "_id": "55d2fc863a42779d68c0dd53", + "age": 36, + "name": "Meyer Pickett", + "gender": "male", + "company": "GENEKOM", + "email": "meyerpickett@genekom.com", + "phone": "+1 (999) 534-3038" + }, + { + "_id": "55d2fc86095f5ceb3c57efeb", + "age": 22, + "name": "Carlson Ramirez", + "gender": "male", + "company": "KANGLE", + "email": "carlsonramirez@kangle.com", + "phone": "+1 (972) 590-3152" + }, + { + "_id": "55d2fc868036a76e42f30954", + "age": 24, + "name": "Roth Murray", + "gender": "male", + "company": "DEEPENDS", + "email": "rothmurray@deepends.com", + "phone": "+1 (944) 408-2208" + }, + { + "_id": "55d2fc8688ac278604ea592b", + "age": 24, + "name": "Cecelia Lambert", + "gender": "female", + "company": "SLOFAST", + "email": "cecelialambert@slofast.com", + "phone": "+1 (907) 485-2284" + }, + { + "_id": "55d2fc86b05fc3906ce838a2", + "age": 31, + "name": "Leah Ferguson", + "gender": "female", + "company": "NEBULEAN", + "email": "leahferguson@nebulean.com", + "phone": "+1 (883) 574-2101" + }, + { + "_id": "55d2fc86d21f9d73f11076b9", + "age": 28, + "name": "Therese Mueller", + "gender": "female", + "company": "STEELFAB", + "email": "theresemueller@steelfab.com", + "phone": "+1 (920) 485-2265" + }, + { + "_id": "55d2fc866254f87d0389dc98", + "age": 32, + "name": "Wanda Byrd", + "gender": "female", + "company": "HOTCAKES", + "email": "wandabyrd@hotcakes.com", + "phone": "+1 (893) 579-3658" + }, + { + "_id": "55d2fc868cf04450063aba0e", + "age": 25, + "name": "Felecia Mccall", + "gender": "female", + "company": "GINK", + "email": "feleciamccall@gink.com", + "phone": "+1 (848) 486-3047" + }, + { + "_id": "55d2fc86e0ee0341850d35ab", + "age": 37, + "name": "Goldie Stafford", + "gender": "female", + "company": "FIREWAX", + "email": "goldiestafford@firewax.com", + "phone": "+1 (831) 507-3578" + }, + { + "_id": "55d2fc868acfa20489a61720", + "age": 20, + "name": "Amy Patterson", + "gender": "female", + "company": "KNEEDLES", + "email": "amypatterson@kneedles.com", + "phone": "+1 (950) 478-3558" + }, + { + "_id": "55d2fc8680c104681b074336", + "age": 29, + "name": "Robles Alford", + "gender": "male", + "company": "TALENDULA", + "email": "roblesalford@talendula.com", + "phone": "+1 (813) 424-3650" + }, + { + "_id": "55d2fc86faeb57024907ea70", + "age": 28, + "name": "Adkins Hampton", + "gender": "male", + "company": "SYNKGEN", + "email": "adkinshampton@synkgen.com", + "phone": "+1 (974) 522-2517" + }, + { + "_id": "55d2fc866ba878e18ee65c0f", + "age": 32, + "name": "Bernadine Trevino", + "gender": "female", + "company": "EVENTEX", + "email": "bernadinetrevino@eventex.com", + "phone": "+1 (914) 577-2655" + }, + { + "_id": "55d2fc86524364ec8cb2d0c7", + "age": 38, + "name": "Robin Le", + "gender": "female", + "company": "GALLAXIA", + "email": "robinle@gallaxia.com", + "phone": "+1 (821) 472-2416" + }, + { + "_id": "55d2fc86c53dc98dacabc399", + "age": 23, + "name": "Leanna Hicks", + "gender": "female", + "company": "ZEROLOGY", + "email": "leannahicks@zerology.com", + "phone": "+1 (946) 531-3368" + }, + { + "_id": "55d2fc86e293b8cfb2d1a5bd", + "age": 31, + "name": "Herman Bridges", + "gender": "male", + "company": "DADABASE", + "email": "hermanbridges@dadabase.com", + "phone": "+1 (836) 408-3169" + }, + { + "_id": "55d2fc865dfc0cc61ec50b41", + "age": 30, + "name": "Olive Terrell", + "gender": "female", + "company": "ACCRUEX", + "email": "oliveterrell@accruex.com", + "phone": "+1 (837) 573-2059" + }, + { + "_id": "55d2fc8669dc78cdeb374ff5", + "age": 26, + "name": "Miranda Banks", + "gender": "female", + "company": "GEOFARM", + "email": "mirandabanks@geofarm.com", + "phone": "+1 (984) 574-2877" + }, + { + "_id": "55d2fc86f471f565df37a756", + "age": 31, + "name": "Noelle Wolf", + "gender": "female", + "company": "ENDIPINE", + "email": "noellewolf@endipine.com", + "phone": "+1 (880) 548-2427" + }, + { + "_id": "55d2fc8603e133136e5ca325", + "age": 38, + "name": "Amber Klein", + "gender": "female", + "company": "ISOPLEX", + "email": "amberklein@isoplex.com", + "phone": "+1 (853) 548-3856" + }, + { + "_id": "55d2fc86962b00cfd86e9d8d", + "age": 32, + "name": "Jodie Mcclain", + "gender": "female", + "company": "BESTO", + "email": "jodiemcclain@besto.com", + "phone": "+1 (857) 417-2691" + }, + { + "_id": "55d2fc8622eaf8052986a5bb", + "age": 35, + "name": "Margo Melendez", + "gender": "female", + "company": "KLUGGER", + "email": "margomelendez@klugger.com", + "phone": "+1 (823) 445-3570" + }, + { + "_id": "55d2fc861e37cd298741e801", + "age": 37, + "name": "Castaneda Dudley", + "gender": "male", + "company": "URBANSHEE", + "email": "castanedadudley@urbanshee.com", + "phone": "+1 (865) 449-2445" + }, + { + "_id": "55d2fc86e4c07ff933f7b531", + "age": 40, + "name": "Sherman Combs", + "gender": "male", + "company": "GRACKER", + "email": "shermancombs@gracker.com", + "phone": "+1 (817) 472-2316" + }, + { + "_id": "55d2fc86c4613e0a622727e6", + "age": 31, + "name": "Louise Nichols", + "gender": "female", + "company": "SPORTAN", + "email": "louisenichols@sportan.com", + "phone": "+1 (823) 543-2230" + }, + { + "_id": "55d2fc86d40dd218587cc632", + "age": 29, + "name": "Daniels Jacobs", + "gender": "male", + "company": "CALCULA", + "email": "danielsjacobs@calcula.com", + "phone": "+1 (979) 448-2244" + }, + { + "_id": "55d2fc86b44f3ec927f3b2e1", + "age": 22, + "name": "Cristina Crosby", + "gender": "female", + "company": "PHUEL", + "email": "cristinacrosby@phuel.com", + "phone": "+1 (816) 504-3557" + }, + { + "_id": "55d2fc86ba635f5436325487", + "age": 23, + "name": "Tisha Sawyer", + "gender": "female", + "company": "IMAGEFLOW", + "email": "tishasawyer@imageflow.com", + "phone": "+1 (894) 551-2933" + }, + { + "_id": "55d2fc8640e8af981a47216a", + "age": 32, + "name": "Janice Graham", + "gender": "female", + "company": "PYRAMIS", + "email": "janicegraham@pyramis.com", + "phone": "+1 (986) 409-2529" + }, + { + "_id": "55d2fc86cdbe80c5cb184731", + "age": 22, + "name": "Holman Joyce", + "gender": "male", + "company": "VERTON", + "email": "holmanjoyce@verton.com", + "phone": "+1 (955) 445-2054" + }, + { + "_id": "55d2fc860f70e0d0890e8e47", + "age": 21, + "name": "Webb Sears", + "gender": "male", + "company": "ENQUILITY", + "email": "webbsears@enquility.com", + "phone": "+1 (961) 406-3600" + }, + { + "_id": "55d2fc86150c8385661fde6f", + "age": 29, + "name": "Bush Farrell", + "gender": "male", + "company": "ENDICIL", + "email": "bushfarrell@endicil.com", + "phone": "+1 (894) 550-2963" + }, + { + "_id": "55d2fc862e2f1ca9662b03e3", + "age": 36, + "name": "Gay Walters", + "gender": "male", + "company": "ZENSURE", + "email": "gaywalters@zensure.com", + "phone": "+1 (859) 467-3816" + }, + { + "_id": "55d2fc86da1249ca6d3a068f", + "age": 33, + "name": "Marquez Palmer", + "gender": "male", + "company": "TURNLING", + "email": "marquezpalmer@turnling.com", + "phone": "+1 (878) 522-3859" + }, + { + "_id": "55d2fc86a61a66a69db5c8a9", + "age": 29, + "name": "Huber Parrish", + "gender": "male", + "company": "DELPHIDE", + "email": "huberparrish@delphide.com", + "phone": "+1 (947) 406-3267" + }, + { + "_id": "55d2fc86fff10fda8f106c2a", + "age": 22, + "name": "Foreman Cohen", + "gender": "male", + "company": "TROPOLIS", + "email": "foremancohen@tropolis.com", + "phone": "+1 (802) 409-2459" + }, + { + "_id": "55d2fc8632b185024494802d", + "age": 31, + "name": "Kathryn Peterson", + "gender": "female", + "company": "PROSURE", + "email": "kathrynpeterson@prosure.com", + "phone": "+1 (811) 531-3085" + }, + { + "_id": "55d2fc86f8e11410bb5b8e88", + "age": 34, + "name": "Aisha Duke", + "gender": "female", + "company": "APPLICA", + "email": "aishaduke@applica.com", + "phone": "+1 (977) 438-2754" + }, + { + "_id": "55d2fc86190a0097728af887", + "age": 37, + "name": "Maynard Henderson", + "gender": "male", + "company": "ILLUMITY", + "email": "maynardhenderson@illumity.com", + "phone": "+1 (832) 472-2261" + }, + { + "_id": "55d2fc86d298e6e1cf9332df", + "age": 33, + "name": "Villarreal Santiago", + "gender": "male", + "company": "QUAILCOM", + "email": "villarrealsantiago@quailcom.com", + "phone": "+1 (981) 422-2572" + }, + { + "_id": "55d2fc86b7eb3b9ed01fc186", + "age": 30, + "name": "Diaz Allison", + "gender": "male", + "company": "ISOSTREAM", + "email": "diazallison@isostream.com", + "phone": "+1 (883) 530-2186" + }, + { + "_id": "55d2fc86da92ac006d69c236", + "age": 20, + "name": "Kirkland Mccullough", + "gender": "male", + "company": "ADORNICA", + "email": "kirklandmccullough@adornica.com", + "phone": "+1 (950) 556-3562" + }, + { + "_id": "55d2fc86195f59929397f2fc", + "age": 32, + "name": "Dolores Howard", + "gender": "female", + "company": "ZINCA", + "email": "doloreshoward@zinca.com", + "phone": "+1 (904) 450-2101" + }, + { + "_id": "55d2fc860c9f707c22eaca7c", + "age": 26, + "name": "Mills Gamble", + "gender": "male", + "company": "BLEEKO", + "email": "millsgamble@bleeko.com", + "phone": "+1 (942) 402-2630" + }, + { + "_id": "55d2fc86b539183079fcab65", + "age": 35, + "name": "Walls Dotson", + "gender": "male", + "company": "GRUPOLI", + "email": "wallsdotson@grupoli.com", + "phone": "+1 (979) 477-3065" + }, + { + "_id": "55d2fc866377b14a737261c3", + "age": 29, + "name": "Hannah Coleman", + "gender": "female", + "company": "COMCUR", + "email": "hannahcoleman@comcur.com", + "phone": "+1 (944) 595-2415" + }, + { + "_id": "55d2fc867b77f0e8d5f08276", + "age": 25, + "name": "Kinney Oneill", + "gender": "male", + "company": "ELPRO", + "email": "kinneyoneill@elpro.com", + "phone": "+1 (851) 570-2363" + }, + { + "_id": "55d2fc868745f5f71578624c", + "age": 32, + "name": "Bennett Henson", + "gender": "male", + "company": "ZISIS", + "email": "bennetthenson@zisis.com", + "phone": "+1 (958) 584-2257" + }, + { + "_id": "55d2fc8604d1e7576b1a83fb", + "age": 32, + "name": "Ross Chavez", + "gender": "male", + "company": "DUOFLEX", + "email": "rosschavez@duoflex.com", + "phone": "+1 (890) 556-2052" + }, + { + "_id": "55d2fc86bd556093136802dd", + "age": 26, + "name": "Raymond Rutledge", + "gender": "male", + "company": "BUNGA", + "email": "raymondrutledge@bunga.com", + "phone": "+1 (994) 504-2118" + }, + { + "_id": "55d2fc86a122c7d89560c130", + "age": 31, + "name": "Rosemary Larsen", + "gender": "female", + "company": "EXOPLODE", + "email": "rosemarylarsen@exoplode.com", + "phone": "+1 (864) 558-3569" + }, + { + "_id": "55d2fc86dd3fe9ea62fdbea1", + "age": 27, + "name": "Tara Roth", + "gender": "female", + "company": "GAPTEC", + "email": "tararoth@gaptec.com", + "phone": "+1 (893) 531-2962" + }, + { + "_id": "55d2fc866f372ca411cd425d", + "age": 30, + "name": "Ronda Sheppard", + "gender": "female", + "company": "GREEKER", + "email": "rondasheppard@greeker.com", + "phone": "+1 (963) 569-2851" + }, + { + "_id": "55d2fc860a0dc369ac8bed9f", + "age": 37, + "name": "Nita Washington", + "gender": "female", + "company": "AQUASURE", + "email": "nitawashington@aquasure.com", + "phone": "+1 (858) 579-3734" + }, + { + "_id": "55d2fc863d59ecccd8b482b7", + "age": 34, + "name": "Shannon Sanchez", + "gender": "male", + "company": "QUORDATE", + "email": "shannonsanchez@quordate.com", + "phone": "+1 (880) 542-2259" + }, + { + "_id": "55d2fc861c73e366d8983f98", + "age": 23, + "name": "Amalia George", + "gender": "female", + "company": "QUINEX", + "email": "amaliageorge@quinex.com", + "phone": "+1 (918) 412-3805" + }, + { + "_id": "55d2fc866910c4329a167746", + "age": 30, + "name": "West Parsons", + "gender": "male", + "company": "RODEMCO", + "email": "westparsons@rodemco.com", + "phone": "+1 (869) 480-3404" + }, + { + "_id": "55d2fc861d97ff31a8eb7f06", + "age": 30, + "name": "Day Mercado", + "gender": "male", + "company": "ENJOLA", + "email": "daymercado@enjola.com", + "phone": "+1 (861) 546-2601" + }, + { + "_id": "55d2fc861e100baa15994161", + "age": 20, + "name": "Hubbard Randolph", + "gender": "male", + "company": "INSOURCE", + "email": "hubbardrandolph@insource.com", + "phone": "+1 (939) 422-2753" + }, + { + "_id": "55d2fc86dc33a0101292ea57", + "age": 32, + "name": "Ward Patrick", + "gender": "male", + "company": "ZUVY", + "email": "wardpatrick@zuvy.com", + "phone": "+1 (852) 461-3079" + }, + { + "_id": "55d2fc863074ff55ec22d7ac", + "age": 26, + "name": "Barker Small", + "gender": "male", + "company": "ZIDANT", + "email": "barkersmall@zidant.com", + "phone": "+1 (901) 514-3653" + }, + { + "_id": "55d2fc86008067de21a65447", + "age": 26, + "name": "Margret Porter", + "gender": "female", + "company": "DYMI", + "email": "margretporter@dymi.com", + "phone": "+1 (820) 404-2199" + }, + { + "_id": "55d2fc8694b7f8670d10781e", + "age": 40, + "name": "Roslyn Richmond", + "gender": "female", + "company": "SONGBIRD", + "email": "roslynrichmond@songbird.com", + "phone": "+1 (913) 406-3720" + }, + { + "_id": "55d2fc86ffff28aef88e1d69", + "age": 29, + "name": "Jimenez Yates", + "gender": "male", + "company": "IDETICA", + "email": "jimenezyates@idetica.com", + "phone": "+1 (803) 421-2358" + }, + { + "_id": "55d2fc8604bfeab3a975b2d8", + "age": 21, + "name": "Mcneil Jensen", + "gender": "male", + "company": "EXOBLUE", + "email": "mcneiljensen@exoblue.com", + "phone": "+1 (856) 581-3756" + }, + { + "_id": "55d2fc86bee368958f567c45", + "age": 40, + "name": "Millicent Trujillo", + "gender": "female", + "company": "ARTWORLDS", + "email": "millicenttrujillo@artworlds.com", + "phone": "+1 (926) 456-2237" + }, + { + "_id": "55d2fc86de2a5a6babe51d48", + "age": 26, + "name": "Dorothea Duffy", + "gender": "female", + "company": "EBIDCO", + "email": "dorotheaduffy@ebidco.com", + "phone": "+1 (873) 458-2694" + }, + { + "_id": "55d2fc86f8793320727d2020", + "age": 34, + "name": "Gloria Ward", + "gender": "female", + "company": "EXOVENT", + "email": "gloriaward@exovent.com", + "phone": "+1 (948) 552-3275" + }, + { + "_id": "55d2fc86967e5599adde68e8", + "age": 33, + "name": "Denise Hogan", + "gender": "female", + "company": "GLEAMINK", + "email": "denisehogan@gleamink.com", + "phone": "+1 (902) 544-2943" + }, + { + "_id": "55d2fc862833baf2918f7860", + "age": 23, + "name": "Clemons Berger", + "gender": "male", + "company": "ARTIQ", + "email": "clemonsberger@artiq.com", + "phone": "+1 (873) 516-2440" + }, + { + "_id": "55d2fc8610af4c20d426854a", + "age": 40, + "name": "Colette Scott", + "gender": "female", + "company": "INVENTURE", + "email": "colettescott@inventure.com", + "phone": "+1 (814) 556-3466" + }, + { + "_id": "55d2fc8697af4d0d06a2c4a6", + "age": 34, + "name": "Chavez Aguilar", + "gender": "male", + "company": "ISBOL", + "email": "chavezaguilar@isbol.com", + "phone": "+1 (952) 543-3992" + }, + { + "_id": "55d2fc861da12d86036c3e48", + "age": 29, + "name": "Arnold Collier", + "gender": "male", + "company": "MELBACOR", + "email": "arnoldcollier@melbacor.com", + "phone": "+1 (907) 404-2090" + }, + { + "_id": "55d2fc86deb921c78b56fec1", + "age": 24, + "name": "Nixon Ingram", + "gender": "male", + "company": "ELENTRIX", + "email": "nixoningram@elentrix.com", + "phone": "+1 (884) 596-3023" + }, + { + "_id": "55d2fc86a8b288ba6b8aa48f", + "age": 28, + "name": "Hurst Hull", + "gender": "male", + "company": "INCUBUS", + "email": "hursthull@incubus.com", + "phone": "+1 (970) 445-2279" + }, + { + "_id": "55d2fc864dc593d113336b1b", + "age": 32, + "name": "Giles Jacobson", + "gender": "male", + "company": "MIXERS", + "email": "gilesjacobson@mixers.com", + "phone": "+1 (868) 402-3161" + }, + { + "_id": "55d2fc86fd0246fcd0c8f864", + "age": 25, + "name": "Millie Giles", + "gender": "female", + "company": "ZAJ", + "email": "milliegiles@zaj.com", + "phone": "+1 (831) 535-3535" + }, + { + "_id": "55d2fc86dceeca0deb9f6d67", + "age": 21, + "name": "Sargent Morse", + "gender": "male", + "company": "ANARCO", + "email": "sargentmorse@anarco.com", + "phone": "+1 (994) 536-2563" + }, + { + "_id": "55d2fc86abbd1add64df6aa2", + "age": 26, + "name": "Reed Camacho", + "gender": "male", + "company": "ICOLOGY", + "email": "reedcamacho@icology.com", + "phone": "+1 (999) 408-3042" + }, + { + "_id": "55d2fc86a2593db6d584f022", + "age": 21, + "name": "Bryant Colon", + "gender": "male", + "company": "ZANILLA", + "email": "bryantcolon@zanilla.com", + "phone": "+1 (964) 570-3418" + }, + { + "_id": "55d2fc86f980f74ba093fde6", + "age": 38, + "name": "Rosella Pierce", + "gender": "female", + "company": "PRIMORDIA", + "email": "rosellapierce@primordia.com", + "phone": "+1 (936) 579-3014" + }, + { + "_id": "55d2fc86797a0001329071fb", + "age": 20, + "name": "Patrice Mckee", + "gender": "female", + "company": "GEEKY", + "email": "patricemckee@geeky.com", + "phone": "+1 (866) 565-3764" + }, + { + "_id": "55d2fc86bcc6181d0b8cae17", + "age": 29, + "name": "Wilkinson Levy", + "gender": "male", + "company": "FISHLAND", + "email": "wilkinsonlevy@fishland.com", + "phone": "+1 (998) 509-3800" + }, + { + "_id": "55d2fc86784b1e22004d637d", + "age": 28, + "name": "Isabella Snyder", + "gender": "female", + "company": "TETRATREX", + "email": "isabellasnyder@tetratrex.com", + "phone": "+1 (998) 451-2871" + }, + { + "_id": "55d2fc8603abab523ff1f02d", + "age": 35, + "name": "Claudine Berg", + "gender": "female", + "company": "ZYTREK", + "email": "claudineberg@zytrek.com", + "phone": "+1 (836) 563-3376" + }, + { + "_id": "55d2fc862796e94846a82a8b", + "age": 27, + "name": "Hyde Simon", + "gender": "male", + "company": "FUELWORKS", + "email": "hydesimon@fuelworks.com", + "phone": "+1 (872) 594-2291" + }, + { + "_id": "55d2fc86f2fa84a4f7ca4548", + "age": 29, + "name": "Wendy Roberts", + "gender": "female", + "company": "OPTICALL", + "email": "wendyroberts@opticall.com", + "phone": "+1 (895) 534-3852" + }, + { + "_id": "55d2fc8651dbb76674b6d2df", + "age": 40, + "name": "Tasha Erickson", + "gender": "female", + "company": "PURIA", + "email": "tashaerickson@puria.com", + "phone": "+1 (905) 526-2175" + }, + { + "_id": "55d2fc86b7f867e76e709d68", + "age": 32, + "name": "Marisa Dunlap", + "gender": "female", + "company": "GLUKGLUK", + "email": "marisadunlap@glukgluk.com", + "phone": "+1 (855) 400-2200" + }, + { + "_id": "55d2fc8695b6ccc0707570af", + "age": 38, + "name": "Kaitlin Baldwin", + "gender": "female", + "company": "EGYPTO", + "email": "kaitlinbaldwin@egypto.com", + "phone": "+1 (980) 482-3256" + }, + { + "_id": "55d2fc86f36b539d4494395d", + "age": 38, + "name": "Abigail Kirkland", + "gender": "female", + "company": "INRT", + "email": "abigailkirkland@inrt.com", + "phone": "+1 (916) 440-3469" + }, + { + "_id": "55d2fc86e401343dbf1de380", + "age": 34, + "name": "Marci Maynard", + "gender": "female", + "company": "INSURITY", + "email": "marcimaynard@insurity.com", + "phone": "+1 (917) 482-3601" + }, + { + "_id": "55d2fc86ba3f7fdb738899f4", + "age": 31, + "name": "Tanya Michael", + "gender": "female", + "company": "ENVIRE", + "email": "tanyamichael@envire.com", + "phone": "+1 (826) 436-3177" + }, + { + "_id": "55d2fc861cb8a1d3151ae2c1", + "age": 34, + "name": "Farrell Irwin", + "gender": "male", + "company": "EXTRAGEN", + "email": "farrellirwin@extragen.com", + "phone": "+1 (948) 442-3796" + }, + { + "_id": "55d2fc86b90f948949af3d8e", + "age": 27, + "name": "Dickson Shepherd", + "gender": "male", + "company": "COMTOURS", + "email": "dicksonshepherd@comtours.com", + "phone": "+1 (871) 458-2972" + }, + { + "_id": "55d2fc862065aeaf17581fa6", + "age": 40, + "name": "Fowler Adams", + "gender": "male", + "company": "NEPTIDE", + "email": "fowleradams@neptide.com", + "phone": "+1 (836) 431-3585" + }, + { + "_id": "55d2fc866aab8b242a30acfd", + "age": 22, + "name": "Durham Frost", + "gender": "male", + "company": "SNOWPOKE", + "email": "durhamfrost@snowpoke.com", + "phone": "+1 (801) 510-2084" + }, + { + "_id": "55d2fc86d3a4aafedc274ffa", + "age": 27, + "name": "Delia Barnes", + "gender": "female", + "company": "CONFERIA", + "email": "deliabarnes@conferia.com", + "phone": "+1 (999) 471-2182" + }, + { + "_id": "55d2fc8601ed1a317e289785", + "age": 27, + "name": "Gill Tillman", + "gender": "male", + "company": "MATRIXITY", + "email": "gilltillman@matrixity.com", + "phone": "+1 (880) 419-3960" + }, + { + "_id": "55d2fc86098dda4fa3812793", + "age": 27, + "name": "Loraine Saunders", + "gender": "female", + "company": "DOGTOWN", + "email": "lorainesaunders@dogtown.com", + "phone": "+1 (868) 500-3061" + }, + { + "_id": "55d2fc865f9615b558837892", + "age": 34, + "name": "Lou Mcdonald", + "gender": "female", + "company": "COMCUBINE", + "email": "loumcdonald@comcubine.com", + "phone": "+1 (857) 568-3427" + }, + { + "_id": "55d2fc865a5a752bb88dbcd8", + "age": 28, + "name": "Beth Lester", + "gender": "female", + "company": "EARTHWAX", + "email": "bethlester@earthwax.com", + "phone": "+1 (805) 565-2363" + }, + { + "_id": "55d2fc862e7d03a2f76c9e58", + "age": 37, + "name": "Karin Mason", + "gender": "female", + "company": "GEEKNET", + "email": "karinmason@geeknet.com", + "phone": "+1 (923) 438-3684" + }, + { + "_id": "55d2fc8617970ba4987ed4b3", + "age": 39, + "name": "Jeanine Watson", + "gender": "female", + "company": "LINGOAGE", + "email": "jeaninewatson@lingoage.com", + "phone": "+1 (919) 444-3722" + }, + { + "_id": "55d2fc86445181571721c6c9", + "age": 20, + "name": "Soto Wilkins", + "gender": "male", + "company": "JUMPSTACK", + "email": "sotowilkins@jumpstack.com", + "phone": "+1 (818) 518-3028" + }, + { + "_id": "55d2fc86b8b146361e17fa83", + "age": 20, + "name": "Mabel Fields", + "gender": "female", + "company": "MAGNINA", + "email": "mabelfields@magnina.com", + "phone": "+1 (861) 583-2161" + }, + { + "_id": "55d2fc8667abfe5721a74176", + "age": 26, + "name": "Ruthie Bailey", + "gender": "female", + "company": "ROCKLOGIC", + "email": "ruthiebailey@rocklogic.com", + "phone": "+1 (872) 402-3619" + }, + { + "_id": "55d2fc865cfa092adb20dc0f", + "age": 22, + "name": "Vaughan Vincent", + "gender": "male", + "company": "ACIUM", + "email": "vaughanvincent@acium.com", + "phone": "+1 (919) 548-3948" + }, + { + "_id": "55d2fc86fe25d347c15e1749", + "age": 38, + "name": "Myrtle Burris", + "gender": "female", + "company": "IRACK", + "email": "myrtleburris@irack.com", + "phone": "+1 (840) 526-2646" + }, + { + "_id": "55d2fc8689cd98b47e44118d", + "age": 37, + "name": "Peggy Mercer", + "gender": "female", + "company": "ESCENTA", + "email": "peggymercer@escenta.com", + "phone": "+1 (817) 574-3310" + }, + { + "_id": "55d2fc86ae662f567ae09404", + "age": 24, + "name": "Mollie Simmons", + "gender": "female", + "company": "STOCKPOST", + "email": "molliesimmons@stockpost.com", + "phone": "+1 (854) 461-2851" + }, + { + "_id": "55d2fc86f738f2bd6ebebd1c", + "age": 37, + "name": "Case Cox", + "gender": "male", + "company": "ACCEL", + "email": "casecox@accel.com", + "phone": "+1 (853) 473-2780" + }, + { + "_id": "55d2fc86c80907be4ff386d5", + "age": 26, + "name": "Snyder Mcclure", + "gender": "male", + "company": "PLASMOX", + "email": "snydermcclure@plasmox.com", + "phone": "+1 (980) 583-3213" + }, + { + "_id": "55d2fc86ed956580fa2cff25", + "age": 31, + "name": "Kelly Malone", + "gender": "male", + "company": "XANIDE", + "email": "kellymalone@xanide.com", + "phone": "+1 (871) 436-2431" + }, + { + "_id": "55d2fc86fcd2b8d749bd1feb", + "age": 20, + "name": "Jackie Carr", + "gender": "female", + "company": "VOIPA", + "email": "jackiecarr@voipa.com", + "phone": "+1 (807) 431-3436" + }, + { + "_id": "55d2fc86d9aac03a007489ef", + "age": 34, + "name": "Cochran Walter", + "gender": "male", + "company": "NIKUDA", + "email": "cochranwalter@nikuda.com", + "phone": "+1 (977) 410-3770" + }, + { + "_id": "55d2fc86140fefcd667c533f", + "age": 22, + "name": "Ellen Ortiz", + "gender": "female", + "company": "NEWCUBE", + "email": "ellenortiz@newcube.com", + "phone": "+1 (980) 541-3099" + }, + { + "_id": "55d2fc86d374c6a649e5877e", + "age": 35, + "name": "Concetta Beard", + "gender": "female", + "company": "PLASMOS", + "email": "concettabeard@plasmos.com", + "phone": "+1 (839) 539-2423" + }, + { + "_id": "55d2fc861ee809861b7c2b38", + "age": 33, + "name": "Josephine Alexander", + "gender": "female", + "company": "LOVEPAD", + "email": "josephinealexander@lovepad.com", + "phone": "+1 (880) 452-2208" + }, + { + "_id": "55d2fc8677db4f8446d43041", + "age": 39, + "name": "Melisa Dean", + "gender": "female", + "company": "GEOFORM", + "email": "melisadean@geoform.com", + "phone": "+1 (934) 452-2532" + }, + { + "_id": "55d2fc861dc0856e2ec744ab", + "age": 26, + "name": "Morgan Galloway", + "gender": "male", + "company": "ELEMANTRA", + "email": "morgangalloway@elemantra.com", + "phone": "+1 (888) 461-2261" + }, + { + "_id": "55d2fc869bc97691cec7d569", + "age": 26, + "name": "Curtis Griffith", + "gender": "male", + "company": "TINGLES", + "email": "curtisgriffith@tingles.com", + "phone": "+1 (881) 517-2174" + }, + { + "_id": "55d2fc8624b80b5986e1de83", + "age": 40, + "name": "Gentry Mccarthy", + "gender": "male", + "company": "GEEKOLA", + "email": "gentrymccarthy@geekola.com", + "phone": "+1 (908) 559-3049" + }, + { + "_id": "55d2fc869c2a158da79f9a7f", + "age": 37, + "name": "Lancaster Justice", + "gender": "male", + "company": "NAXDIS", + "email": "lancasterjustice@naxdis.com", + "phone": "+1 (980) 456-3515" + }, + { + "_id": "55d2fc867c8e30a12fddcb79", + "age": 28, + "name": "Jenifer Barr", + "gender": "female", + "company": "ZORROMOP", + "email": "jeniferbarr@zorromop.com", + "phone": "+1 (901) 440-3979" + }, + { + "_id": "55d2fc8696d7c8b59507f10c", + "age": 35, + "name": "Benjamin Nolan", + "gender": "male", + "company": "DIGITALUS", + "email": "benjaminnolan@digitalus.com", + "phone": "+1 (828) 582-3041" + }, + { + "_id": "55d2fc86b7e719a5c07b9f9e", + "age": 27, + "name": "Beach Valentine", + "gender": "male", + "company": "KAGGLE", + "email": "beachvalentine@kaggle.com", + "phone": "+1 (961) 563-3631" + }, + { + "_id": "55d2fc860bb827b4eab70038", + "age": 27, + "name": "Brady Moore", + "gender": "male", + "company": "CORPULSE", + "email": "bradymoore@corpulse.com", + "phone": "+1 (859) 434-2962" + }, + { + "_id": "55d2fc862fc00a2ce6ab7f32", + "age": 27, + "name": "Page Ray", + "gender": "male", + "company": "BEZAL", + "email": "pageray@bezal.com", + "phone": "+1 (845) 492-2182" + }, + { + "_id": "55d2fc861b57330b56d5b3f8", + "age": 40, + "name": "Lupe Gould", + "gender": "female", + "company": "DOGNOSIS", + "email": "lupegould@dognosis.com", + "phone": "+1 (955) 579-2141" + }, + { + "_id": "55d2fc864a8fabbf89d106cd", + "age": 23, + "name": "Melva Abbott", + "gender": "female", + "company": "CODACT", + "email": "melvaabbott@codact.com", + "phone": "+1 (801) 478-2678" + }, + { + "_id": "55d2fc86107ad0ad66c9d3ea", + "age": 22, + "name": "Mendez Middleton", + "gender": "male", + "company": "CUJO", + "email": "mendezmiddleton@cujo.com", + "phone": "+1 (908) 430-2032" + }, + { + "_id": "55d2fc86d2f5edd1222e87cc", + "age": 31, + "name": "Meredith Ayers", + "gender": "female", + "company": "OPTYK", + "email": "meredithayers@optyk.com", + "phone": "+1 (875) 472-2514" + }, + { + "_id": "55d2fc86551f2a796de0f3ad", + "age": 21, + "name": "Burns Serrano", + "gender": "male", + "company": "ZOUNDS", + "email": "burnsserrano@zounds.com", + "phone": "+1 (939) 558-2221" + }, + { + "_id": "55d2fc86540849ad56aeac98", + "age": 30, + "name": "Barr Sykes", + "gender": "male", + "company": "CORECOM", + "email": "barrsykes@corecom.com", + "phone": "+1 (982) 523-3577" + }, + { + "_id": "55d2fc86e7d43d06e4d135f2", + "age": 28, + "name": "Julie Johnson", + "gender": "female", + "company": "BIOTICA", + "email": "juliejohnson@biotica.com", + "phone": "+1 (918) 487-3230" + }, + { + "_id": "55d2fc86450521acf4a465d9", + "age": 23, + "name": "Dawn Vinson", + "gender": "female", + "company": "MENBRAIN", + "email": "dawnvinson@menbrain.com", + "phone": "+1 (936) 525-3273" + }, + { + "_id": "55d2fc86df3e093a104f498b", + "age": 30, + "name": "Ginger Ryan", + "gender": "female", + "company": "ENTHAZE", + "email": "gingerryan@enthaze.com", + "phone": "+1 (865) 530-2726" + }, + { + "_id": "55d2fc86b6a6fed233908500", + "age": 40, + "name": "Mcconnell Prince", + "gender": "male", + "company": "TRIPSCH", + "email": "mcconnellprince@tripsch.com", + "phone": "+1 (923) 586-2117" + }, + { + "_id": "55d2fc8619de2514561b7ac1", + "age": 23, + "name": "Peck Blackwell", + "gender": "male", + "company": "ASSISTIA", + "email": "peckblackwell@assistia.com", + "phone": "+1 (988) 549-3418" + }, + { + "_id": "55d2fc8619b2c263aec32e51", + "age": 32, + "name": "Simmons Benton", + "gender": "male", + "company": "TROLLERY", + "email": "simmonsbenton@trollery.com", + "phone": "+1 (924) 439-2962" + }, + { + "_id": "55d2fc8612c77e18fa3c743d", + "age": 36, + "name": "Mari Silva", + "gender": "female", + "company": "MAGMINA", + "email": "marisilva@magmina.com", + "phone": "+1 (863) 509-3186" + }, + { + "_id": "55d2fc86226a40ce862e518e", + "age": 40, + "name": "Erin Jefferson", + "gender": "female", + "company": "VIAGREAT", + "email": "erinjefferson@viagreat.com", + "phone": "+1 (921) 408-2295" + }, + { + "_id": "55d2fc8601013536fc55a097", + "age": 37, + "name": "Nellie Ballard", + "gender": "female", + "company": "GEOFORMA", + "email": "nellieballard@geoforma.com", + "phone": "+1 (918) 406-2600" + }, + { + "_id": "55d2fc864402965704b3453a", + "age": 27, + "name": "Austin Brewer", + "gender": "male", + "company": "METROZ", + "email": "austinbrewer@metroz.com", + "phone": "+1 (968) 584-2959" + }, + { + "_id": "55d2fc8690a7d6957a09f6c8", + "age": 34, + "name": "Pickett Buckley", + "gender": "male", + "company": "SUREPLEX", + "email": "pickettbuckley@sureplex.com", + "phone": "+1 (975) 520-3259" + }, + { + "_id": "55d2fc8695f3020c96b14f95", + "age": 39, + "name": "Coleen Herman", + "gender": "female", + "company": "NORALI", + "email": "coleenherman@norali.com", + "phone": "+1 (916) 506-2704" + }, + { + "_id": "55d2fc869b58b96d1d2cdfcc", + "age": 37, + "name": "Roy Guerrero", + "gender": "male", + "company": "PLUTORQUE", + "email": "royguerrero@plutorque.com", + "phone": "+1 (922) 541-3741" + }, + { + "_id": "55d2fc86ba1ed1189e9020ee", + "age": 29, + "name": "Oneal Curtis", + "gender": "male", + "company": "DATAGEN", + "email": "onealcurtis@datagen.com", + "phone": "+1 (847) 421-3483" + }, + { + "_id": "55d2fc86b5c5e89eb9fe1e79", + "age": 29, + "name": "Chaney Christian", + "gender": "male", + "company": "XPLOR", + "email": "chaneychristian@xplor.com", + "phone": "+1 (847) 517-3918" + }, + { + "_id": "55d2fc86f6b5a3d91952d941", + "age": 29, + "name": "Cantu Richard", + "gender": "male", + "company": "ZANYMAX", + "email": "canturichard@zanymax.com", + "phone": "+1 (972) 487-2616" + }, + { + "_id": "55d2fc8609b0f98cbc2e101d", + "age": 37, + "name": "Newton Barrera", + "gender": "male", + "company": "PORTALINE", + "email": "newtonbarrera@portaline.com", + "phone": "+1 (964) 527-3130" + }, + { + "_id": "55d2fc86d0e063ca7629c11b", + "age": 28, + "name": "Jodi Pollard", + "gender": "female", + "company": "GOKO", + "email": "jodipollard@goko.com", + "phone": "+1 (957) 468-2658" + }, + { + "_id": "55d2fc8688d95c5c579bd328", + "age": 35, + "name": "Effie Nunez", + "gender": "female", + "company": "SNACKTION", + "email": "effienunez@snacktion.com", + "phone": "+1 (805) 576-3749" + }, + { + "_id": "55d2fc86f36c0eab69222b17", + "age": 38, + "name": "Haley Battle", + "gender": "male", + "company": "TERRAGEN", + "email": "haleybattle@terragen.com", + "phone": "+1 (955) 581-3931" + }, + { + "_id": "55d2fc86ee6900b197860a35", + "age": 29, + "name": "Cathy Vaughn", + "gender": "female", + "company": "CANOPOLY", + "email": "cathyvaughn@canopoly.com", + "phone": "+1 (875) 539-3578" + }, + { + "_id": "55d2fc86613f698b355e87bc", + "age": 26, + "name": "Mendoza Maxwell", + "gender": "male", + "company": "TERAPRENE", + "email": "mendozamaxwell@teraprene.com", + "phone": "+1 (820) 471-3500" + }, + { + "_id": "55d2fc86f86a80f78c45936a", + "age": 28, + "name": "Rosetta Hughes", + "gender": "female", + "company": "DEVILTOE", + "email": "rosettahughes@deviltoe.com", + "phone": "+1 (911) 418-2439" + }, + { + "_id": "55d2fc8683d3c4a377a984a5", + "age": 28, + "name": "Frazier Larson", + "gender": "male", + "company": "DAIDO", + "email": "frazierlarson@daido.com", + "phone": "+1 (995) 459-3756" + }, + { + "_id": "55d2fc8669baef03c4d6a4a5", + "age": 27, + "name": "Brittney Ratliff", + "gender": "female", + "company": "TALKALOT", + "email": "brittneyratliff@talkalot.com", + "phone": "+1 (865) 568-2986" + }, + { + "_id": "55d2fc86404583ccc6b6a4f4", + "age": 21, + "name": "Hancock Gilliam", + "gender": "male", + "company": "EXPOSA", + "email": "hancockgilliam@exposa.com", + "phone": "+1 (894) 519-3139" + }, + { + "_id": "55d2fc8675478e0b84bb5ab3", + "age": 20, + "name": "Lilia Mccormick", + "gender": "female", + "company": "DENTREX", + "email": "liliamccormick@dentrex.com", + "phone": "+1 (997) 552-3944" + }, + { + "_id": "55d2fc8658bf3954cdf52ebb", + "age": 20, + "name": "Chrystal Mcneil", + "gender": "female", + "company": "LIMOZEN", + "email": "chrystalmcneil@limozen.com", + "phone": "+1 (943) 519-2952" + }, + { + "_id": "55d2fc865ebec9e8c7adc383", + "age": 31, + "name": "Hebert Alston", + "gender": "male", + "company": "IZZBY", + "email": "hebertalston@izzby.com", + "phone": "+1 (953) 482-2029" + }, + { + "_id": "55d2fc86bf1085e6fd2e9ea1", + "age": 23, + "name": "Mcfarland Carrillo", + "gender": "male", + "company": "EQUITOX", + "email": "mcfarlandcarrillo@equitox.com", + "phone": "+1 (999) 478-3822" + }, + { + "_id": "55d2fc8641d4f2d09f539bd6", + "age": 40, + "name": "Porter Weaver", + "gender": "male", + "company": "QUILM", + "email": "porterweaver@quilm.com", + "phone": "+1 (831) 501-2739" + }, + { + "_id": "55d2fc869ee5331a50039e30", + "age": 23, + "name": "Dale Sims", + "gender": "male", + "company": "SCENTRIC", + "email": "dalesims@scentric.com", + "phone": "+1 (845) 597-2120" + }, + { + "_id": "55d2fc86cb96d31fe71e4a18", + "age": 22, + "name": "William Dixon", + "gender": "male", + "company": "NAMEBOX", + "email": "williamdixon@namebox.com", + "phone": "+1 (970) 448-2651" + }, + { + "_id": "55d2fc86e88daeb1e198671d", + "age": 33, + "name": "Patrica Reed", + "gender": "female", + "company": "CORPORANA", + "email": "patricareed@corporana.com", + "phone": "+1 (817) 457-2413" + }, + { + "_id": "55d2fc86c33d9f422bf8bfcc", + "age": 20, + "name": "Marylou Mcmillan", + "gender": "female", + "company": "RETRACK", + "email": "maryloumcmillan@retrack.com", + "phone": "+1 (908) 568-2328" + }, + { + "_id": "55d2fc86c4b1b139887e76f1", + "age": 20, + "name": "Maritza David", + "gender": "female", + "company": "ORBEAN", + "email": "maritzadavid@orbean.com", + "phone": "+1 (851) 503-3737" + }, + { + "_id": "55d2fc86ac0f360d91c740e6", + "age": 23, + "name": "Lorie Moses", + "gender": "female", + "company": "ROCKABYE", + "email": "loriemoses@rockabye.com", + "phone": "+1 (823) 431-2387" + }, + { + "_id": "55d2fc86d7d406569b386b17", + "age": 35, + "name": "Shields Weiss", + "gender": "male", + "company": "RODEOCEAN", + "email": "shieldsweiss@rodeocean.com", + "phone": "+1 (957) 490-3725" + }, + { + "_id": "55d2fc86e4f66ce770ae2c93", + "age": 25, + "name": "Eugenia Berry", + "gender": "female", + "company": "BISBA", + "email": "eugeniaberry@bisba.com", + "phone": "+1 (948) 403-3403" + }, + { + "_id": "55d2fc8618f2f4f428223522", + "age": 33, + "name": "Howard Compton", + "gender": "male", + "company": "NAMEGEN", + "email": "howardcompton@namegen.com", + "phone": "+1 (827) 439-3667" + }, + { + "_id": "55d2fc86f5adc0f6dd535ea6", + "age": 28, + "name": "Key Davis", + "gender": "male", + "company": "PORTICO", + "email": "keydavis@portico.com", + "phone": "+1 (873) 533-2980" + }, + { + "_id": "55d2fc86876669f4e9431417", + "age": 33, + "name": "Phillips Solis", + "gender": "male", + "company": "DANCITY", + "email": "phillipssolis@dancity.com", + "phone": "+1 (883) 481-3114" + }, + { + "_id": "55d2fc86f2bb610d7ad9ea36", + "age": 40, + "name": "Cash Pugh", + "gender": "male", + "company": "STUCCO", + "email": "cashpugh@stucco.com", + "phone": "+1 (873) 512-2106" + }, + { + "_id": "55d2fc863be1649d5bd3be39", + "age": 21, + "name": "Elinor Warner", + "gender": "female", + "company": "FOSSIEL", + "email": "elinorwarner@fossiel.com", + "phone": "+1 (950) 431-3679" + }, + { + "_id": "55d2fc86fdad2af5536237e2", + "age": 35, + "name": "Jacquelyn Doyle", + "gender": "female", + "company": "CYTREX", + "email": "jacquelyndoyle@cytrex.com", + "phone": "+1 (924) 569-2919" + }, + { + "_id": "55d2fc86f3affa20ab27edff", + "age": 33, + "name": "Jeannine Mosley", + "gender": "female", + "company": "ACUSAGE", + "email": "jeanninemosley@acusage.com", + "phone": "+1 (954) 517-2805" + }, + { + "_id": "55d2fc8670dd0dbdd6e4d195", + "age": 37, + "name": "Logan Brady", + "gender": "male", + "company": "TELLIFLY", + "email": "loganbrady@tellifly.com", + "phone": "+1 (861) 576-2313" + }, + { + "_id": "55d2fc86b9a15e4721982a39", + "age": 26, + "name": "Houston Joseph", + "gender": "male", + "company": "BOILICON", + "email": "houstonjoseph@boilicon.com", + "phone": "+1 (822) 519-3430" + }, + { + "_id": "55d2fc86f225999b0b8742d2", + "age": 38, + "name": "Rita Lindsey", + "gender": "female", + "company": "FIBEROX", + "email": "ritalindsey@fiberox.com", + "phone": "+1 (805) 551-3755" + }, + { + "_id": "55d2fc86e9dad38b6873b807", + "age": 22, + "name": "Strong Poole", + "gender": "male", + "company": "KINDALOO", + "email": "strongpoole@kindaloo.com", + "phone": "+1 (918) 426-2076" + }, + { + "_id": "55d2fc861608b965b2283827", + "age": 38, + "name": "Hines Mathews", + "gender": "male", + "company": "INTRADISK", + "email": "hinesmathews@intradisk.com", + "phone": "+1 (932) 420-2236" + }, + { + "_id": "55d2fc863079075f91241a16", + "age": 28, + "name": "Trina Wiley", + "gender": "female", + "company": "HATOLOGY", + "email": "trinawiley@hatology.com", + "phone": "+1 (855) 466-3287" + }, + { + "_id": "55d2fc86f2fae1a79253fb61", + "age": 23, + "name": "Kirby Tucker", + "gender": "male", + "company": "AQUAMATE", + "email": "kirbytucker@aquamate.com", + "phone": "+1 (935) 456-3272" + }, + { + "_id": "55d2fc86c42bf49f8202b2fa", + "age": 28, + "name": "Ballard Stein", + "gender": "male", + "company": "KOOGLE", + "email": "ballardstein@koogle.com", + "phone": "+1 (943) 586-2225" + }, + { + "_id": "55d2fc865db815da198c0776", + "age": 36, + "name": "Wagner Mcfarland", + "gender": "male", + "company": "ACCIDENCY", + "email": "wagnermcfarland@accidency.com", + "phone": "+1 (920) 533-2157" + }, + { + "_id": "55d2fc866aeb268fe48fd6be", + "age": 22, + "name": "Wiley Wilder", + "gender": "male", + "company": "KIDSTOCK", + "email": "wileywilder@kidstock.com", + "phone": "+1 (957) 459-3416" + }, + { + "_id": "55d2fc8606f67a423d303437", + "age": 37, + "name": "Rosario Slater", + "gender": "female", + "company": "SPRINGBEE", + "email": "rosarioslater@springbee.com", + "phone": "+1 (950) 506-3454" + }, + { + "_id": "55d2fc86510fd16a269a0201", + "age": 37, + "name": "Walker Mcdowell", + "gender": "male", + "company": "ONTAGENE", + "email": "walkermcdowell@ontagene.com", + "phone": "+1 (953) 579-3429" + }, + { + "_id": "55d2fc867d419d30f9394f56", + "age": 32, + "name": "Booth Pratt", + "gender": "male", + "company": "ZIGGLES", + "email": "boothpratt@ziggles.com", + "phone": "+1 (835) 453-3707" + }, + { + "_id": "55d2fc86e631b8f71bbe7b35", + "age": 33, + "name": "Georgia Carpenter", + "gender": "female", + "company": "STRALUM", + "email": "georgiacarpenter@stralum.com", + "phone": "+1 (923) 536-3557" + }, + { + "_id": "55d2fc866df6437afa4cfaf6", + "age": 26, + "name": "Harding Powers", + "gender": "male", + "company": "BEADZZA", + "email": "hardingpowers@beadzza.com", + "phone": "+1 (855) 467-2993" + }, + { + "_id": "55d2fc867ade492afcfc24a6", + "age": 37, + "name": "Kaye Brown", + "gender": "female", + "company": "AMTAS", + "email": "kayebrown@amtas.com", + "phone": "+1 (926) 444-3936" + }, + { + "_id": "55d2fc862bf33dd3169710ff", + "age": 24, + "name": "Mccray Padilla", + "gender": "male", + "company": "FUTURIS", + "email": "mccraypadilla@futuris.com", + "phone": "+1 (969) 561-3819" + }, + { + "_id": "55d2fc8666f74690303abf65", + "age": 35, + "name": "Moon Moss", + "gender": "male", + "company": "EURON", + "email": "moonmoss@euron.com", + "phone": "+1 (885) 514-2872" + }, + { + "_id": "55d2fc860a8b5abfdf57bd37", + "age": 26, + "name": "Lane Gregory", + "gender": "male", + "company": "SKINSERVE", + "email": "lanegregory@skinserve.com", + "phone": "+1 (818) 455-3048" + }, + { + "_id": "55d2fc86f17541fe3b770b26", + "age": 30, + "name": "Cummings Good", + "gender": "male", + "company": "GEEKOLOGY", + "email": "cummingsgood@geekology.com", + "phone": "+1 (821) 426-3476" + }, + { + "_id": "55d2fc865b6232d788278e1f", + "age": 26, + "name": "Lottie Soto", + "gender": "female", + "company": "INTERGEEK", + "email": "lottiesoto@intergeek.com", + "phone": "+1 (905) 516-2928" + }, + { + "_id": "55d2fc868388a50b97dda5c2", + "age": 38, + "name": "Bridges Bell", + "gender": "male", + "company": "MIRACULA", + "email": "bridgesbell@miracula.com", + "phone": "+1 (917) 438-3079" + }, + { + "_id": "55d2fc86cc2120d10b75c41b", + "age": 23, + "name": "Marcella Lancaster", + "gender": "female", + "company": "NAVIR", + "email": "marcellalancaster@navir.com", + "phone": "+1 (851) 478-2535" + }, + { + "_id": "55d2fc86f52bd008c87c6993", + "age": 32, + "name": "Foley Yang", + "gender": "male", + "company": "APEXTRI", + "email": "foleyyang@apextri.com", + "phone": "+1 (978) 504-2003" + }, + { + "_id": "55d2fc86088b65117b293eef", + "age": 21, + "name": "Debora Levine", + "gender": "female", + "company": "VANTAGE", + "email": "deboralevine@vantage.com", + "phone": "+1 (820) 472-2507" + }, + { + "_id": "55d2fc86765d079d8584c281", + "age": 30, + "name": "Jill Durham", + "gender": "female", + "company": "FUTURITY", + "email": "jilldurham@futurity.com", + "phone": "+1 (996) 499-2910" + }, + { + "_id": "55d2fc860ed183243d043f79", + "age": 28, + "name": "Della Sherman", + "gender": "female", + "company": "EXTRO", + "email": "dellasherman@extro.com", + "phone": "+1 (893) 541-2867" + }, + { + "_id": "55d2fc8646733a05fa448c6e", + "age": 30, + "name": "Tamara Albert", + "gender": "female", + "company": "ECOLIGHT", + "email": "tamaraalbert@ecolight.com", + "phone": "+1 (870) 514-2615" + }, + { + "_id": "55d2fc86b8bf0a0f7ffb702e", + "age": 39, + "name": "Lynn Green", + "gender": "male", + "company": "SNIPS", + "email": "lynngreen@snips.com", + "phone": "+1 (938) 464-2073" + }, + { + "_id": "55d2fc863e577905fc3ea8e7", + "age": 29, + "name": "Barbra Tate", + "gender": "female", + "company": "ACRUEX", + "email": "barbratate@acruex.com", + "phone": "+1 (809) 418-2604" + }, + { + "_id": "55d2fc86335b53151fc242b5", + "age": 33, + "name": "Potts Dickerson", + "gender": "male", + "company": "SHADEASE", + "email": "pottsdickerson@shadease.com", + "phone": "+1 (967) 539-3330" + }, + { + "_id": "55d2fc86716df5cb28925d59", + "age": 36, + "name": "Nancy Woodard", + "gender": "female", + "company": "ZOSIS", + "email": "nancywoodard@zosis.com", + "phone": "+1 (811) 434-3223" + }, + { + "_id": "55d2fc86058113d9a4909796", + "age": 29, + "name": "Park Evans", + "gender": "male", + "company": "XUMONK", + "email": "parkevans@xumonk.com", + "phone": "+1 (836) 443-2361" + }, + { + "_id": "55d2fc8659b6d92fb2880f83", + "age": 25, + "name": "Nicole Sullivan", + "gender": "female", + "company": "QUALITEX", + "email": "nicolesullivan@qualitex.com", + "phone": "+1 (823) 584-2994" + }, + { + "_id": "55d2fc86510772cfe78617a9", + "age": 33, + "name": "Bowers Barnett", + "gender": "male", + "company": "HOUSEDOWN", + "email": "bowersbarnett@housedown.com", + "phone": "+1 (872) 466-3548" + }, + { + "_id": "55d2fc868dec3ac619cfc262", + "age": 21, + "name": "Jeri Nielsen", + "gender": "female", + "company": "MOBILDATA", + "email": "jerinielsen@mobildata.com", + "phone": "+1 (886) 581-2045" + }, + { + "_id": "55d2fc863e89d05123e90aaf", + "age": 26, + "name": "Delores Farmer", + "gender": "female", + "company": "XERONK", + "email": "deloresfarmer@xeronk.com", + "phone": "+1 (872) 556-2716" + }, + { + "_id": "55d2fc8618c10cef39c48f97", + "age": 38, + "name": "Mathis Walsh", + "gender": "male", + "company": "VURBO", + "email": "mathiswalsh@vurbo.com", + "phone": "+1 (837) 459-2909" + }, + { + "_id": "55d2fc868b3487cb71c8bac4", + "age": 20, + "name": "Ingrid Shelton", + "gender": "female", + "company": "ORBIN", + "email": "ingridshelton@orbin.com", + "phone": "+1 (914) 592-2364" + }, + { + "_id": "55d2fc86bcf5c885edacef50", + "age": 36, + "name": "Socorro Burns", + "gender": "female", + "company": "NETAGY", + "email": "socorroburns@netagy.com", + "phone": "+1 (931) 523-3116" + }, + { + "_id": "55d2fc8693f704f5c95c48a7", + "age": 39, + "name": "Jo Ware", + "gender": "female", + "company": "FLYBOYZ", + "email": "joware@flyboyz.com", + "phone": "+1 (844) 467-2192" + }, + { + "_id": "55d2fc86e87485075df33029", + "age": 38, + "name": "Emilia Flores", + "gender": "female", + "company": "ZAGGLES", + "email": "emiliaflores@zaggles.com", + "phone": "+1 (992) 408-2629" + }, + { + "_id": "55d2fc861ab925ca0b9b15c6", + "age": 31, + "name": "Burks Haney", + "gender": "male", + "company": "ZYPLE", + "email": "burkshaney@zyple.com", + "phone": "+1 (882) 401-2811" + }, + { + "_id": "55d2fc86e9c252588455b811", + "age": 31, + "name": "Holly Snow", + "gender": "female", + "company": "FURNAFIX", + "email": "hollysnow@furnafix.com", + "phone": "+1 (802) 592-2798" + }, + { + "_id": "55d2fc86e07c753096021423", + "age": 25, + "name": "Frances Hayden", + "gender": "female", + "company": "SNORUS", + "email": "franceshayden@snorus.com", + "phone": "+1 (818) 481-2431" + }, + { + "_id": "55d2fc86496884876065c346", + "age": 28, + "name": "Lila Lewis", + "gender": "female", + "company": "ZOMBOID", + "email": "lilalewis@zomboid.com", + "phone": "+1 (893) 593-2423" + }, + { + "_id": "55d2fc865c6ea080a4d0a5b5", + "age": 34, + "name": "Torres Finley", + "gender": "male", + "company": "MAXIMIND", + "email": "torresfinley@maximind.com", + "phone": "+1 (888) 504-3674" + }, + { + "_id": "55d2fc86391c44c545fe989f", + "age": 23, + "name": "Horne James", + "gender": "male", + "company": "PETICULAR", + "email": "hornejames@peticular.com", + "phone": "+1 (865) 558-2517" + }, + { + "_id": "55d2fc86c8c2529939eada03", + "age": 28, + "name": "Jennings Wallace", + "gender": "male", + "company": "CAXT", + "email": "jenningswallace@caxt.com", + "phone": "+1 (833) 599-3895" + }, + { + "_id": "55d2fc86ee656fe865fbde29", + "age": 33, + "name": "Lily Gilmore", + "gender": "female", + "company": "RADIANTIX", + "email": "lilygilmore@radiantix.com", + "phone": "+1 (854) 561-3148" + }, + { + "_id": "55d2fc868455126d596ab537", + "age": 35, + "name": "Kristy Delacruz", + "gender": "female", + "company": "EQUICOM", + "email": "kristydelacruz@equicom.com", + "phone": "+1 (871) 502-3732" + }, + { + "_id": "55d2fc863096983ef370ca49", + "age": 31, + "name": "England Vance", + "gender": "male", + "company": "VOLAX", + "email": "englandvance@volax.com", + "phone": "+1 (840) 593-3417" + }, + { + "_id": "55d2fc8671f3c1e30bee852f", + "age": 23, + "name": "Rodriguez Foreman", + "gender": "male", + "company": "EXTRAWEAR", + "email": "rodriguezforeman@extrawear.com", + "phone": "+1 (804) 497-2101" + }, + { + "_id": "55d2fc86bea38e2b0cd970cb", + "age": 35, + "name": "Richard Garrett", + "gender": "male", + "company": "SUPREMIA", + "email": "richardgarrett@supremia.com", + "phone": "+1 (925) 461-3414" + }, + { + "_id": "55d2fc862c5193ab7b4668b7", + "age": 40, + "name": "Connie Ortega", + "gender": "female", + "company": "ZILODYNE", + "email": "connieortega@zilodyne.com", + "phone": "+1 (838) 582-3241" + }, + { + "_id": "55d2fc865a6b25d6bc09180e", + "age": 24, + "name": "Solomon Bates", + "gender": "male", + "company": "EXOSPACE", + "email": "solomonbates@exospace.com", + "phone": "+1 (897) 496-2243" + }, + { + "_id": "55d2fc86a16a4e077136e4b5", + "age": 32, + "name": "Valencia Andrews", + "gender": "male", + "company": "NORSUP", + "email": "valenciaandrews@norsup.com", + "phone": "+1 (891) 503-3593" + }, + { + "_id": "55d2fc86bc5f1bf697a90465", + "age": 24, + "name": "Briggs Vasquez", + "gender": "male", + "company": "ZENTIA", + "email": "briggsvasquez@zentia.com", + "phone": "+1 (802) 415-3377" + }, + { + "_id": "55d2fc86c088d6466d83f8fc", + "age": 26, + "name": "Hester Rice", + "gender": "female", + "company": "CYTREK", + "email": "hesterrice@cytrek.com", + "phone": "+1 (855) 544-3905" + }, + { + "_id": "55d2fc8622fc3e78977288af", + "age": 25, + "name": "Shelly Hendrix", + "gender": "female", + "company": "POWERNET", + "email": "shellyhendrix@powernet.com", + "phone": "+1 (912) 431-2318" + }, + { + "_id": "55d2fc862cdec6cd321df6e0", + "age": 22, + "name": "Alison Newman", + "gender": "female", + "company": "COMTOUR", + "email": "alisonnewman@comtour.com", + "phone": "+1 (836) 582-3513" + }, + { + "_id": "55d2fc86e0070d54d4712ca4", + "age": 30, + "name": "French Rivera", + "gender": "male", + "company": "ACUMENTOR", + "email": "frenchrivera@acumentor.com", + "phone": "+1 (902) 579-2193" + }, + { + "_id": "55d2fc860f64b4423a9d6ffc", + "age": 38, + "name": "Terrell Mendez", + "gender": "male", + "company": "IMAGINART", + "email": "terrellmendez@imaginart.com", + "phone": "+1 (967) 494-2713" + }, + { + "_id": "55d2fc86548793116b225b3b", + "age": 30, + "name": "Parsons Robertson", + "gender": "male", + "company": "OBONES", + "email": "parsonsrobertson@obones.com", + "phone": "+1 (984) 434-2810" + }, + { + "_id": "55d2fc865b8b0d6a59db876c", + "age": 33, + "name": "Livingston Barry", + "gender": "male", + "company": "TYPHONICA", + "email": "livingstonbarry@typhonica.com", + "phone": "+1 (976) 560-3878" + }, + { + "_id": "55d2fc865ce23fb8b34cae53", + "age": 40, + "name": "Valeria Stout", + "gender": "female", + "company": "AVENETRO", + "email": "valeriastout@avenetro.com", + "phone": "+1 (885) 557-3624" + }, + { + "_id": "55d2fc86c03ea1d6e81563ac", + "age": 39, + "name": "Grimes Dyer", + "gender": "male", + "company": "GEOLOGIX", + "email": "grimesdyer@geologix.com", + "phone": "+1 (896) 533-2919" + }, + { + "_id": "55d2fc8655c0acb356a06c8f", + "age": 29, + "name": "Higgins Short", + "gender": "male", + "company": "BICOL", + "email": "higginsshort@bicol.com", + "phone": "+1 (976) 444-3073" + }, + { + "_id": "55d2fc865b6db005487c52bb", + "age": 34, + "name": "Gilmore Campos", + "gender": "male", + "company": "PASTURIA", + "email": "gilmorecampos@pasturia.com", + "phone": "+1 (862) 442-2147" + }, + { + "_id": "55d2fc863df4791bcb269217", + "age": 29, + "name": "Sloan Kane", + "gender": "male", + "company": "XELEGYL", + "email": "sloankane@xelegyl.com", + "phone": "+1 (946) 526-2275" + }, + { + "_id": "55d2fc86b2eb7dedbd5a9e8d", + "age": 26, + "name": "Mcpherson Thornton", + "gender": "male", + "company": "KAGE", + "email": "mcphersonthornton@kage.com", + "phone": "+1 (803) 478-2690" + }, + { + "_id": "55d2fc8603f6a8c17148c8dd", + "age": 31, + "name": "Christi Welch", + "gender": "female", + "company": "WARETEL", + "email": "christiwelch@waretel.com", + "phone": "+1 (999) 552-3114" + }, + { + "_id": "55d2fc86118d83cb9d06aa2e", + "age": 29, + "name": "Padilla Travis", + "gender": "male", + "company": "ENERVATE", + "email": "padillatravis@enervate.com", + "phone": "+1 (897) 577-3387" + }, + { + "_id": "55d2fc86aba06801708bed65", + "age": 22, + "name": "Stanton Casey", + "gender": "male", + "company": "BUZZMAKER", + "email": "stantoncasey@buzzmaker.com", + "phone": "+1 (858) 571-2667" + }, + { + "_id": "55d2fc86184810b00043a4b7", + "age": 29, + "name": "Krista Hernandez", + "gender": "female", + "company": "BIOHAB", + "email": "kristahernandez@biohab.com", + "phone": "+1 (832) 510-3654" + }, + { + "_id": "55d2fc86e9c951b5bcea3938", + "age": 36, + "name": "Deleon Oliver", + "gender": "male", + "company": "NETBOOK", + "email": "deleonoliver@netbook.com", + "phone": "+1 (934) 504-2964" + }, + { + "_id": "55d2fc86923000f3ea91ae38", + "age": 36, + "name": "Vasquez Fowler", + "gender": "male", + "company": "ORGANICA", + "email": "vasquezfowler@organica.com", + "phone": "+1 (949) 546-2722" + }, + { + "_id": "55d2fc861e12cd0fa6207a9e", + "age": 33, + "name": "Rutledge Keith", + "gender": "male", + "company": "COLAIRE", + "email": "rutledgekeith@colaire.com", + "phone": "+1 (936) 472-3739" + }, + { + "_id": "55d2fc86927eca39ef0c7ae9", + "age": 26, + "name": "Kirsten Valenzuela", + "gender": "female", + "company": "SEQUITUR", + "email": "kirstenvalenzuela@sequitur.com", + "phone": "+1 (958) 564-3259" + }, + { + "_id": "55d2fc869e922239ce293d2c", + "age": 40, + "name": "Garza Gutierrez", + "gender": "male", + "company": "SPLINX", + "email": "garzagutierrez@splinx.com", + "phone": "+1 (850) 525-3114" + }, + { + "_id": "55d2fc869dd88389d4785283", + "age": 27, + "name": "Shawna Peck", + "gender": "female", + "company": "UNQ", + "email": "shawnapeck@unq.com", + "phone": "+1 (961) 579-3704" + }, + { + "_id": "55d2fc86722d8e2a714bf7f2", + "age": 23, + "name": "Aurelia Mcpherson", + "gender": "female", + "company": "BOINK", + "email": "aureliamcpherson@boink.com", + "phone": "+1 (946) 479-2080" + }, + { + "_id": "55d2fc862324d173c26dfc68", + "age": 39, + "name": "Maryellen Daugherty", + "gender": "female", + "company": "ZILCH", + "email": "maryellendaugherty@zilch.com", + "phone": "+1 (817) 577-3290" + }, + { + "_id": "55d2fc86d311107f869da748", + "age": 35, + "name": "Zelma Hancock", + "gender": "female", + "company": "EVENTAGE", + "email": "zelmahancock@eventage.com", + "phone": "+1 (845) 578-3887" + }, + { + "_id": "55d2fc86afb4ede1f20a6d15", + "age": 34, + "name": "Tessa Adkins", + "gender": "female", + "company": "CONJURICA", + "email": "tessaadkins@conjurica.com", + "phone": "+1 (807) 497-2845" + }, + { + "_id": "55d2fc8648a2e36d6f97fad1", + "age": 22, + "name": "Wong Shaffer", + "gender": "male", + "company": "ACCUFARM", + "email": "wongshaffer@accufarm.com", + "phone": "+1 (865) 589-3833" + }, + { + "_id": "55d2fc86c4c10d58f1fe357f", + "age": 32, + "name": "Ivy Suarez", + "gender": "female", + "company": "UNCORP", + "email": "ivysuarez@uncorp.com", + "phone": "+1 (851) 582-2829" + }, + { + "_id": "55d2fc867c11d1885ca4d8f9", + "age": 25, + "name": "Sosa Barber", + "gender": "male", + "company": "FUELTON", + "email": "sosabarber@fuelton.com", + "phone": "+1 (831) 404-2343" + }, + { + "_id": "55d2fc866405b373cff4d477", + "age": 23, + "name": "Rosalind Craft", + "gender": "female", + "company": "OTHERSIDE", + "email": "rosalindcraft@otherside.com", + "phone": "+1 (847) 455-2079" + }, + { + "_id": "55d2fc863a038b06c712b4b9", + "age": 30, + "name": "Bean Mathis", + "gender": "male", + "company": "QUILK", + "email": "beanmathis@quilk.com", + "phone": "+1 (974) 596-2868" + }, + { + "_id": "55d2fc86922e4a50dfef56ac", + "age": 21, + "name": "Kelley Ruiz", + "gender": "female", + "company": "APEX", + "email": "kelleyruiz@apex.com", + "phone": "+1 (889) 522-2938" + }, + { + "_id": "55d2fc868c7bf8c05a228366", + "age": 22, + "name": "Georgette Chaney", + "gender": "female", + "company": "EXOTECHNO", + "email": "georgettechaney@exotechno.com", + "phone": "+1 (920) 591-3934" + }, + { + "_id": "55d2fc86988c5be0c8ba5412", + "age": 38, + "name": "Tami Bullock", + "gender": "female", + "company": "ISOTRONIC", + "email": "tamibullock@isotronic.com", + "phone": "+1 (828) 567-2857" + }, + { + "_id": "55d2fc862264a1d2de2e8a6b", + "age": 39, + "name": "Castillo Rosario", + "gender": "male", + "company": "KONNECT", + "email": "castillorosario@konnect.com", + "phone": "+1 (938) 402-3484" + }, + { + "_id": "55d2fc8622ee680ff3c522e1", + "age": 31, + "name": "George Weber", + "gender": "male", + "company": "FARMAGE", + "email": "georgeweber@farmage.com", + "phone": "+1 (895) 502-2654" + }, + { + "_id": "55d2fc86a0d45d2916aacb5a", + "age": 28, + "name": "Wheeler Villarreal", + "gender": "male", + "company": "IMPERIUM", + "email": "wheelervillarreal@imperium.com", + "phone": "+1 (889) 507-3796" + }, + { + "_id": "55d2fc866c6e8e85c17c61ca", + "age": 36, + "name": "Arlene Bean", + "gender": "female", + "company": "UNIA", + "email": "arlenebean@unia.com", + "phone": "+1 (970) 463-2147" + }, + { + "_id": "55d2fc86c3c95fd98562a429", + "age": 30, + "name": "Oneil Madden", + "gender": "male", + "company": "COMBOGENE", + "email": "oneilmadden@combogene.com", + "phone": "+1 (849) 507-3555" + }, + { + "_id": "55d2fc86b0f7cc31af45078a", + "age": 35, + "name": "Vaughn Merritt", + "gender": "male", + "company": "ACCUPHARM", + "email": "vaughnmerritt@accupharm.com", + "phone": "+1 (886) 428-2966" + }, + { + "_id": "55d2fc86ad825cb66f2a2feb", + "age": 21, + "name": "Duran Bradford", + "gender": "male", + "company": "SQUISH", + "email": "duranbradford@squish.com", + "phone": "+1 (930) 434-2976" + }, + { + "_id": "55d2fc86efa80c332066194d", + "age": 30, + "name": "Tanisha Knox", + "gender": "female", + "company": "FARMEX", + "email": "tanishaknox@farmex.com", + "phone": "+1 (924) 540-2066" + }, + { + "_id": "55d2fc861dbd55c1fd4bdf23", + "age": 38, + "name": "Esther Foster", + "gender": "female", + "company": "SENSATE", + "email": "estherfoster@sensate.com", + "phone": "+1 (812) 417-2687" + }, + { + "_id": "55d2fc86115b9a01067db6ad", + "age": 35, + "name": "Marion Gray", + "gender": "female", + "company": "HINWAY", + "email": "mariongray@hinway.com", + "phone": "+1 (850) 526-2167" + }, + { + "_id": "55d2fc864b40086c2e4963e2", + "age": 38, + "name": "Ava Flowers", + "gender": "female", + "company": "REVERSUS", + "email": "avaflowers@reversus.com", + "phone": "+1 (989) 415-2504" + }, + { + "_id": "55d2fc8686c59f7395222b11", + "age": 35, + "name": "Katina Burnett", + "gender": "female", + "company": "DUFLEX", + "email": "katinaburnett@duflex.com", + "phone": "+1 (843) 464-3718" + }, + { + "_id": "55d2fc86e012c977bc5b57d6", + "age": 37, + "name": "Ester Cooley", + "gender": "female", + "company": "RUBADUB", + "email": "estercooley@rubadub.com", + "phone": "+1 (856) 407-3009" + }, + { + "_id": "55d2fc865688875c75a158b5", + "age": 36, + "name": "Dennis Mccray", + "gender": "male", + "company": "PETIGEMS", + "email": "dennismccray@petigems.com", + "phone": "+1 (989) 525-3768" + }, + { + "_id": "55d2fc86ca333a1c715a35c7", + "age": 25, + "name": "Mitzi Carson", + "gender": "female", + "company": "KENEGY", + "email": "mitzicarson@kenegy.com", + "phone": "+1 (819) 450-2923" + }, + { + "_id": "55d2fc86a8eb68257312e735", + "age": 33, + "name": "Guthrie Tyson", + "gender": "male", + "company": "GLUID", + "email": "guthrietyson@gluid.com", + "phone": "+1 (878) 496-3831" + }, + { + "_id": "55d2fc865d4f3b3777fc1573", + "age": 38, + "name": "Sellers Hodges", + "gender": "male", + "company": "BALOOBA", + "email": "sellershodges@balooba.com", + "phone": "+1 (895) 557-2331" + }, + { + "_id": "55d2fc860a91cf55298e2a24", + "age": 32, + "name": "Hawkins Hardin", + "gender": "male", + "company": "ZILLANET", + "email": "hawkinshardin@zillanet.com", + "phone": "+1 (852) 511-2796" + }, + { + "_id": "55d2fc867b1c618fcb9cb2c3", + "age": 26, + "name": "Bowman Buck", + "gender": "male", + "company": "APPLIDEC", + "email": "bowmanbuck@applidec.com", + "phone": "+1 (995) 500-2863" + }, + { + "_id": "55d2fc8666610d156551484b", + "age": 23, + "name": "Mcgee Delgado", + "gender": "male", + "company": "MANTRO", + "email": "mcgeedelgado@mantro.com", + "phone": "+1 (917) 490-2295" + }, + { + "_id": "55d2fc8685385c63f5a509b3", + "age": 24, + "name": "Petty Pena", + "gender": "male", + "company": "EXOSPEED", + "email": "pettypena@exospeed.com", + "phone": "+1 (929) 470-2022" + }, + { + "_id": "55d2fc864296df53bb778e52", + "age": 38, + "name": "Ray Mclaughlin", + "gender": "male", + "company": "PYRAMAX", + "email": "raymclaughlin@pyramax.com", + "phone": "+1 (935) 453-3720" + }, + { + "_id": "55d2fc86b157acc34692412b", + "age": 22, + "name": "Hopkins Wells", + "gender": "male", + "company": "NORALEX", + "email": "hopkinswells@noralex.com", + "phone": "+1 (986) 421-2293" + }, + { + "_id": "55d2fc861febd65bb3c91219", + "age": 38, + "name": "Patsy Strickland", + "gender": "female", + "company": "POLARIA", + "email": "patsystrickland@polaria.com", + "phone": "+1 (885) 408-2213" + }, + { + "_id": "55d2fc8693fbc24aa2bcc5a8", + "age": 31, + "name": "Wolf Delaney", + "gender": "male", + "company": "EXERTA", + "email": "wolfdelaney@exerta.com", + "phone": "+1 (969) 537-3201" + }, + { + "_id": "55d2fc86b923e0543d39fed4", + "age": 30, + "name": "Fulton Hewitt", + "gender": "male", + "company": "TWIGGERY", + "email": "fultonhewitt@twiggery.com", + "phone": "+1 (894) 483-2549" + }, + { + "_id": "55d2fc8672aff3d2369b2749", + "age": 40, + "name": "Nona Meadows", + "gender": "female", + "company": "ULTRIMAX", + "email": "nonameadows@ultrimax.com", + "phone": "+1 (997) 459-2012" + }, + { + "_id": "55d2fc86a3b6922e61cdcd72", + "age": 24, + "name": "Irwin Russo", + "gender": "male", + "company": "QUINTITY", + "email": "irwinrusso@quintity.com", + "phone": "+1 (985) 597-3841" + }, + { + "_id": "55d2fc86c28f4a90a41581b7", + "age": 34, + "name": "Mara Bowman", + "gender": "female", + "company": "ATOMICA", + "email": "marabowman@atomica.com", + "phone": "+1 (927) 578-2958" + }, + { + "_id": "55d2fc86ef827e1bbb5b3ceb", + "age": 40, + "name": "Leigh Schroeder", + "gender": "female", + "company": "ZIORE", + "email": "leighschroeder@ziore.com", + "phone": "+1 (963) 484-2519" + }, + { + "_id": "55d2fc86921329e8e044472a", + "age": 27, + "name": "Sweeney Riddle", + "gender": "male", + "company": "ELITA", + "email": "sweeneyriddle@elita.com", + "phone": "+1 (974) 536-2132" + }, + { + "_id": "55d2fc864b6067f7d828f1ba", + "age": 23, + "name": "Bell Kline", + "gender": "male", + "company": "ORBOID", + "email": "bellkline@orboid.com", + "phone": "+1 (827) 461-3466" + }, + { + "_id": "55d2fc86a541995fa67027ae", + "age": 20, + "name": "Morgan Aguirre", + "gender": "female", + "company": "AEORA", + "email": "morganaguirre@aeora.com", + "phone": "+1 (987) 494-2357" + }, + { + "_id": "55d2fc86af8d98e486bda0f5", + "age": 24, + "name": "Morrison Mcbride", + "gender": "male", + "company": "TECHMANIA", + "email": "morrisonmcbride@techmania.com", + "phone": "+1 (994) 470-2394" + }, + { + "_id": "55d2fc86371a2691da434cdd", + "age": 22, + "name": "Miles Salinas", + "gender": "male", + "company": "RODEOLOGY", + "email": "milessalinas@rodeology.com", + "phone": "+1 (898) 461-3008" + }, + { + "_id": "55d2fc865196aa926884d957", + "age": 36, + "name": "Lang Riggs", + "gender": "male", + "company": "PHOTOBIN", + "email": "langriggs@photobin.com", + "phone": "+1 (849) 503-2335" + }, + { + "_id": "55d2fc86b4e30cd686840e28", + "age": 30, + "name": "Kathy Phelps", + "gender": "female", + "company": "INTRAWEAR", + "email": "kathyphelps@intrawear.com", + "phone": "+1 (992) 499-2474" + }, + { + "_id": "55d2fc86f09accb089f91415", + "age": 24, + "name": "Moss Jimenez", + "gender": "male", + "company": "PROFLEX", + "email": "mossjimenez@proflex.com", + "phone": "+1 (842) 546-3491" + }, + { + "_id": "55d2fc86e3ba881a25584928", + "age": 20, + "name": "Moody Sexton", + "gender": "male", + "company": "CENTREGY", + "email": "moodysexton@centregy.com", + "phone": "+1 (856) 581-3293" + }, + { + "_id": "55d2fc861c85cf26c6d21a64", + "age": 31, + "name": "Nannie Price", + "gender": "female", + "company": "GEOSTELE", + "email": "nannieprice@geostele.com", + "phone": "+1 (936) 447-3486" + }, + { + "_id": "55d2fc86f1e23452254fda91", + "age": 32, + "name": "Summer Johnston", + "gender": "female", + "company": "EXTRAGENE", + "email": "summerjohnston@extragene.com", + "phone": "+1 (808) 508-2748" + }, + { + "_id": "55d2fc86940fcc0f17d5f213", + "age": 24, + "name": "Genevieve Lynch", + "gender": "female", + "company": "COREPAN", + "email": "genevievelynch@corepan.com", + "phone": "+1 (921) 532-2893" + }, + { + "_id": "55d2fc86cdf6056cd058466c", + "age": 23, + "name": "Stuart Oconnor", + "gender": "male", + "company": "PUSHCART", + "email": "stuartoconnor@pushcart.com", + "phone": "+1 (925) 515-3434" + }, + { + "_id": "55d2fc863bca896e3e2ac1a7", + "age": 21, + "name": "Adela Nieves", + "gender": "female", + "company": "MEDICROIX", + "email": "adelanieves@medicroix.com", + "phone": "+1 (910) 568-3916" + }, + { + "_id": "55d2fc86826d7c7fcfc2562e", + "age": 28, + "name": "Eaton Mcintosh", + "gender": "male", + "company": "MEGALL", + "email": "eatonmcintosh@megall.com", + "phone": "+1 (806) 440-2196" + }, + { + "_id": "55d2fc863d76c0458de8afb2", + "age": 35, + "name": "Sharron Hood", + "gender": "female", + "company": "UTARIAN", + "email": "sharronhood@utarian.com", + "phone": "+1 (824) 477-3364" + }, + { + "_id": "55d2fc86c0f317fe0cdd6f66", + "age": 24, + "name": "Allison Osborn", + "gender": "female", + "company": "TUBALUM", + "email": "allisonosborn@tubalum.com", + "phone": "+1 (913) 546-3966" + }, + { + "_id": "55d2fc86e1521a5a9896e0fa", + "age": 38, + "name": "Chelsea Jarvis", + "gender": "female", + "company": "SOPRANO", + "email": "chelseajarvis@soprano.com", + "phone": "+1 (834) 417-2904" + }, + { + "_id": "55d2fc86a5f4df72a31c9201", + "age": 22, + "name": "Finley Freeman", + "gender": "male", + "company": "COMBOT", + "email": "finleyfreeman@combot.com", + "phone": "+1 (886) 448-2820" + }, + { + "_id": "55d2fc86c4c2c783570c50eb", + "age": 31, + "name": "Lynn Miles", + "gender": "female", + "company": "ZENTIX", + "email": "lynnmiles@zentix.com", + "phone": "+1 (891) 450-2505" + }, + { + "_id": "55d2fc863e649b4a99367f40", + "age": 39, + "name": "Montoya Greene", + "gender": "male", + "company": "MEMORA", + "email": "montoyagreene@memora.com", + "phone": "+1 (809) 402-3541" + }, + { + "_id": "55d2fc86b82e96b2275df9f3", + "age": 28, + "name": "Castro Huff", + "gender": "male", + "company": "BLUPLANET", + "email": "castrohuff@bluplanet.com", + "phone": "+1 (966) 554-2469" + }, + { + "_id": "55d2fc86f4a8a7700a99e31c", + "age": 23, + "name": "Guadalupe Harmon", + "gender": "female", + "company": "ISOLOGIA", + "email": "guadalupeharmon@isologia.com", + "phone": "+1 (937) 497-2022" + }, + { + "_id": "55d2fc86551e7044f31f2520", + "age": 25, + "name": "Heidi Navarro", + "gender": "female", + "company": "POLARIUM", + "email": "heidinavarro@polarium.com", + "phone": "+1 (830) 493-3328" + }, + { + "_id": "55d2fc861ee43e4303351a4b", + "age": 25, + "name": "Fry Webster", + "gender": "male", + "company": "RENOVIZE", + "email": "frywebster@renovize.com", + "phone": "+1 (960) 600-3488" + }, + { + "_id": "55d2fc86e0d6778a1c6d7195", + "age": 35, + "name": "Candice Sharpe", + "gender": "female", + "company": "UNDERTAP", + "email": "candicesharpe@undertap.com", + "phone": "+1 (989) 436-2856" + }, + { + "_id": "55d2fc86267005541d225276", + "age": 25, + "name": "Vonda Hansen", + "gender": "female", + "company": "PROVIDCO", + "email": "vondahansen@providco.com", + "phone": "+1 (883) 484-2047" + }, + { + "_id": "55d2fc8605000bc9329d28e0", + "age": 21, + "name": "Lara Dominguez", + "gender": "male", + "company": "VIXO", + "email": "laradominguez@vixo.com", + "phone": "+1 (998) 440-3632" + }, + { + "_id": "55d2fc868b364833d935b192", + "age": 36, + "name": "Jillian Gibbs", + "gender": "female", + "company": "CUIZINE", + "email": "jilliangibbs@cuizine.com", + "phone": "+1 (996) 447-3083" + }, + { + "_id": "55d2fc863690a0784d2e8bc1", + "age": 35, + "name": "Frankie Cervantes", + "gender": "female", + "company": "ISODRIVE", + "email": "frankiecervantes@isodrive.com", + "phone": "+1 (895) 533-2371" + }, + { + "_id": "55d2fc86efb91d645101592f", + "age": 36, + "name": "Nieves Walton", + "gender": "male", + "company": "ZILPHUR", + "email": "nieveswalton@zilphur.com", + "phone": "+1 (866) 412-2377" + }, + { + "_id": "55d2fc86cba2fb0dfd9eb8c3", + "age": 38, + "name": "Celina Orr", + "gender": "female", + "company": "COMVEX", + "email": "celinaorr@comvex.com", + "phone": "+1 (960) 433-2380" + }, + { + "_id": "55d2fc8650eaf118ae048bce", + "age": 33, + "name": "Moreno Conway", + "gender": "male", + "company": "VIOCULAR", + "email": "morenoconway@viocular.com", + "phone": "+1 (808) 535-2624" + }, + { + "_id": "55d2fc860e41c77df0ea1151", + "age": 33, + "name": "Wilkerson Dodson", + "gender": "male", + "company": "COMTRAK", + "email": "wilkersondodson@comtrak.com", + "phone": "+1 (932) 427-2400" + }, + { + "_id": "55d2fc86175167613833c577", + "age": 21, + "name": "Crane Lloyd", + "gender": "male", + "company": "ARCHITAX", + "email": "cranelloyd@architax.com", + "phone": "+1 (984) 467-3498" + }, + { + "_id": "55d2fc863aa593013e09d04b", + "age": 25, + "name": "Marguerite Dorsey", + "gender": "female", + "company": "UNI", + "email": "margueritedorsey@uni.com", + "phone": "+1 (989) 558-2105" + }, + { + "_id": "55d2fc86248902726f0d1c53", + "age": 35, + "name": "Dillon William", + "gender": "male", + "company": "ROOFORIA", + "email": "dillonwilliam@rooforia.com", + "phone": "+1 (879) 600-3589" + }, + { + "_id": "55d2fc86caa1b7374cd83a72", + "age": 32, + "name": "Small Floyd", + "gender": "male", + "company": "ANACHO", + "email": "smallfloyd@anacho.com", + "phone": "+1 (906) 463-2357" + }, + { + "_id": "55d2fc86c086267c74616688", + "age": 32, + "name": "Dominique Horn", + "gender": "female", + "company": "ENTOGROK", + "email": "dominiquehorn@entogrok.com", + "phone": "+1 (871) 556-2943" + }, + { + "_id": "55d2fc86d3f7731c49d48fa7", + "age": 35, + "name": "Haley Shannon", + "gender": "female", + "company": "UNEEQ", + "email": "haleyshannon@uneeq.com", + "phone": "+1 (984) 471-3688" + }, + { + "_id": "55d2fc8678e81807871b2b13", + "age": 24, + "name": "Flowers Richards", + "gender": "male", + "company": "QUILITY", + "email": "flowersrichards@quility.com", + "phone": "+1 (956) 548-3667" + }, + { + "_id": "55d2fc86b4b462a2743c10d3", + "age": 33, + "name": "Hopper Rush", + "gender": "male", + "company": "VERBUS", + "email": "hopperrush@verbus.com", + "phone": "+1 (866) 578-2948" + }, + { + "_id": "55d2fc86d660e6f5bf9c1ce1", + "age": 31, + "name": "Randall Kelley", + "gender": "male", + "company": "INSURON", + "email": "randallkelley@insuron.com", + "phone": "+1 (922) 520-3464" + }, + { + "_id": "55d2fc86e70ad03e58c3e01f", + "age": 36, + "name": "Owens Drake", + "gender": "male", + "company": "FROLIX", + "email": "owensdrake@frolix.com", + "phone": "+1 (982) 516-3575" + }, + { + "_id": "55d2fc86ef78c8ad566afe4f", + "age": 28, + "name": "Irma Garrison", + "gender": "female", + "company": "EXOZENT", + "email": "irmagarrison@exozent.com", + "phone": "+1 (882) 536-2614" + }, + { + "_id": "55d2fc8675d6b4b33c5e482f", + "age": 34, + "name": "Baldwin Carver", + "gender": "male", + "company": "PHEAST", + "email": "baldwincarver@pheast.com", + "phone": "+1 (824) 521-2892" + }, + { + "_id": "55d2fc86d382b214a715305f", + "age": 23, + "name": "Short Harding", + "gender": "male", + "company": "LIQUICOM", + "email": "shortharding@liquicom.com", + "phone": "+1 (836) 578-2063" + }, + { + "_id": "55d2fc860420ee3ca95e2166", + "age": 35, + "name": "Christy Roberson", + "gender": "female", + "company": "BITREX", + "email": "christyroberson@bitrex.com", + "phone": "+1 (840) 426-2954" + }, + { + "_id": "55d2fc86dd67ce4e4f7b6d0c", + "age": 21, + "name": "Fern Knight", + "gender": "female", + "company": "ENTROPIX", + "email": "fernknight@entropix.com", + "phone": "+1 (944) 546-2456" + }, + { + "_id": "55d2fc8651510fb366709f12", + "age": 40, + "name": "Elva Taylor", + "gender": "female", + "company": "COMFIRM", + "email": "elvataylor@comfirm.com", + "phone": "+1 (834) 468-2999" + }, + { + "_id": "55d2fc869603515857919a7c", + "age": 26, + "name": "Carmen Bentley", + "gender": "female", + "company": "LIMAGE", + "email": "carmenbentley@limage.com", + "phone": "+1 (882) 425-3588" + }, + { + "_id": "55d2fc860991d6904808f8b3", + "age": 28, + "name": "Blair Dunn", + "gender": "male", + "company": "ESSENSIA", + "email": "blairdunn@essensia.com", + "phone": "+1 (896) 477-2617" + }, + { + "_id": "55d2fc862c99e4fefd17ab8d", + "age": 23, + "name": "Minerva Swanson", + "gender": "female", + "company": "EMTRAC", + "email": "minervaswanson@emtrac.com", + "phone": "+1 (905) 427-3942" + }, + { + "_id": "55d2fc86a2b4277d171e6ed6", + "age": 40, + "name": "Alicia Lawrence", + "gender": "female", + "company": "CONCILITY", + "email": "alicialawrence@concility.com", + "phone": "+1 (903) 452-2010" + }, + { + "_id": "55d2fc86f98eb74fe02a49f5", + "age": 33, + "name": "Bernice Fitzpatrick", + "gender": "female", + "company": "JASPER", + "email": "bernicefitzpatrick@jasper.com", + "phone": "+1 (987) 518-2248" + }, + { + "_id": "55d2fc86070af0b0109b6a4c", + "age": 20, + "name": "Wilder Blackburn", + "gender": "male", + "company": "ZENTILITY", + "email": "wilderblackburn@zentility.com", + "phone": "+1 (921) 548-2995" + }, + { + "_id": "55d2fc86249796ecd0b25829", + "age": 25, + "name": "Lambert Wilson", + "gender": "male", + "company": "ANIVET", + "email": "lambertwilson@anivet.com", + "phone": "+1 (920) 592-2261" + }, + { + "_id": "55d2fc863c6bdb691f62bdf4", + "age": 24, + "name": "Cook Lee", + "gender": "male", + "company": "ZILLADYNE", + "email": "cooklee@zilladyne.com", + "phone": "+1 (812) 566-2988" + }, + { + "_id": "55d2fc86cdeae66cf4fe6582", + "age": 29, + "name": "Karyn Horne", + "gender": "female", + "company": "OBLIQ", + "email": "karynhorne@obliq.com", + "phone": "+1 (909) 452-3599" + }, + { + "_id": "55d2fc86bcbabc9eb159e1e4", + "age": 23, + "name": "Rowe Stokes", + "gender": "male", + "company": "ANDERSHUN", + "email": "rowestokes@andershun.com", + "phone": "+1 (948) 531-2335" + }, + { + "_id": "55d2fc86a1b344b621d41baa", + "age": 27, + "name": "Gonzalez Merrill", + "gender": "male", + "company": "PLAYCE", + "email": "gonzalezmerrill@playce.com", + "phone": "+1 (989) 418-3057" + }, + { + "_id": "55d2fc86300074c184103b47", + "age": 33, + "name": "Kathie Preston", + "gender": "female", + "company": "LUMBREX", + "email": "kathiepreston@lumbrex.com", + "phone": "+1 (911) 586-3177" + }, + { + "_id": "55d2fc8646059e576c41ed1f", + "age": 26, + "name": "Antoinette Stevens", + "gender": "female", + "company": "APEXIA", + "email": "antoinettestevens@apexia.com", + "phone": "+1 (828) 597-3083" + }, + { + "_id": "55d2fc86b5ec5743baacc091", + "age": 22, + "name": "Schmidt Morton", + "gender": "male", + "company": "NORSUL", + "email": "schmidtmorton@norsul.com", + "phone": "+1 (821) 530-2454" + }, + { + "_id": "55d2fc86a521743243b01d2b", + "age": 37, + "name": "Joyner Wise", + "gender": "male", + "company": "MANGELICA", + "email": "joynerwise@mangelica.com", + "phone": "+1 (884) 448-3942" + }, + { + "_id": "55d2fc86d7c827b5ded4027f", + "age": 22, + "name": "Carpenter Carey", + "gender": "male", + "company": "CENTICE", + "email": "carpentercarey@centice.com", + "phone": "+1 (823) 585-2581" + }, + { + "_id": "55d2fc866866bf441f74abdf", + "age": 34, + "name": "Luz Hays", + "gender": "female", + "company": "KONGENE", + "email": "luzhays@kongene.com", + "phone": "+1 (999) 558-2282" + }, + { + "_id": "55d2fc86af95d619737eccb7", + "age": 27, + "name": "Lesley Frye", + "gender": "female", + "company": "SUREMAX", + "email": "lesleyfrye@suremax.com", + "phone": "+1 (982) 485-2811" + }, + { + "_id": "55d2fc86b41a0594552e4839", + "age": 31, + "name": "Jacqueline Ramsey", + "gender": "female", + "company": "LYRIA", + "email": "jacquelineramsey@lyria.com", + "phone": "+1 (961) 581-2500" + }, + { + "_id": "55d2fc8682aa54548863d4b1", + "age": 31, + "name": "Ina Ford", + "gender": "female", + "company": "ENERSOL", + "email": "inaford@enersol.com", + "phone": "+1 (895) 514-3441" + }, + { + "_id": "55d2fc866bc796d86ebd697b", + "age": 22, + "name": "Francisca Ashley", + "gender": "female", + "company": "SIGNIDYNE", + "email": "franciscaashley@signidyne.com", + "phone": "+1 (911) 566-2135" + }, + { + "_id": "55d2fc868ccc122c6a517033", + "age": 34, + "name": "Morton Owen", + "gender": "male", + "company": "SHOPABOUT", + "email": "mortonowen@shopabout.com", + "phone": "+1 (932) 576-3821" + }, + { + "_id": "55d2fc86751e661b56e6e3cd", + "age": 36, + "name": "Weber Manning", + "gender": "male", + "company": "CENTREE", + "email": "webermanning@centree.com", + "phone": "+1 (865) 582-3809" + }, + { + "_id": "55d2fc8620b9766f76797a13", + "age": 37, + "name": "Edwards Steele", + "gender": "male", + "company": "KATAKANA", + "email": "edwardssteele@katakana.com", + "phone": "+1 (959) 402-3657" + }, + { + "_id": "55d2fc860a13152e82b74839", + "age": 38, + "name": "Gabriela Boone", + "gender": "female", + "company": "OVOLO", + "email": "gabrielaboone@ovolo.com", + "phone": "+1 (910) 587-2744" + }, + { + "_id": "55d2fc864e9ca8a988600768", + "age": 32, + "name": "Tricia Guy", + "gender": "female", + "company": "TALKOLA", + "email": "triciaguy@talkola.com", + "phone": "+1 (960) 474-3508" + }, + { + "_id": "55d2fc86ce61b77671fd1f33", + "age": 32, + "name": "James Romero", + "gender": "female", + "company": "CANDECOR", + "email": "jamesromero@candecor.com", + "phone": "+1 (928) 478-3272" + }, + { + "_id": "55d2fc86f3cc105e20a44aa9", + "age": 25, + "name": "Casey Hammond", + "gender": "male", + "company": "PYRAMIA", + "email": "caseyhammond@pyramia.com", + "phone": "+1 (965) 539-2923" + }, + { + "_id": "55d2fc86429d859d9e54585f", + "age": 33, + "name": "Moran Wade", + "gender": "male", + "company": "BUGSALL", + "email": "moranwade@bugsall.com", + "phone": "+1 (996) 559-2965" + }, + { + "_id": "55d2fc86820b562eff651ebc", + "age": 34, + "name": "Earline Goff", + "gender": "female", + "company": "DATAGENE", + "email": "earlinegoff@datagene.com", + "phone": "+1 (976) 565-3513" + }, + { + "_id": "55d2fc865f6176e8301edcf3", + "age": 38, + "name": "Vickie Cherry", + "gender": "female", + "company": "SILODYNE", + "email": "vickiecherry@silodyne.com", + "phone": "+1 (943) 522-2438" + }, + { + "_id": "55d2fc8633cb19840995984c", + "age": 30, + "name": "Yates Avery", + "gender": "male", + "company": "DIGINETIC", + "email": "yatesavery@diginetic.com", + "phone": "+1 (813) 587-3611" + }, + { + "_id": "55d2fc8677d1a1c749d498eb", + "age": 24, + "name": "Hodges Langley", + "gender": "male", + "company": "QUOTEZART", + "email": "hodgeslangley@quotezart.com", + "phone": "+1 (857) 496-3905" + }, + { + "_id": "55d2fc86f49e5ceca6ff241d", + "age": 35, + "name": "Serrano Bartlett", + "gender": "male", + "company": "EXOSWITCH", + "email": "serranobartlett@exoswitch.com", + "phone": "+1 (809) 421-3677" + }, + { + "_id": "55d2fc86cd4921dcf0316691", + "age": 28, + "name": "Faye Wood", + "gender": "female", + "company": "EXOTERIC", + "email": "fayewood@exoteric.com", + "phone": "+1 (840) 417-2329" + }, + { + "_id": "55d2fc8697911f7ca9adfe37", + "age": 38, + "name": "Jamie Jennings", + "gender": "female", + "company": "SURETECH", + "email": "jamiejennings@suretech.com", + "phone": "+1 (945) 599-3768" + }, + { + "_id": "55d2fc8614e0225c06b40348", + "age": 34, + "name": "Levy Sellers", + "gender": "male", + "company": "STELAECOR", + "email": "levysellers@stelaecor.com", + "phone": "+1 (805) 519-2578" + }, + { + "_id": "55d2fc8635986d3994af8adc", + "age": 36, + "name": "Kelsey Montgomery", + "gender": "female", + "company": "IMANT", + "email": "kelseymontgomery@imant.com", + "phone": "+1 (894) 555-3420" + }, + { + "_id": "55d2fc8624dfa620d7b8a991", + "age": 33, + "name": "Rush Gates", + "gender": "male", + "company": "DIGIAL", + "email": "rushgates@digial.com", + "phone": "+1 (891) 477-2651" + }, + { + "_id": "55d2fc862d52fe806312e6dd", + "age": 40, + "name": "Holloway Gay", + "gender": "male", + "company": "EARTHMARK", + "email": "hollowaygay@earthmark.com", + "phone": "+1 (811) 540-3123" + }, + { + "_id": "55d2fc86a168313cea778ac0", + "age": 20, + "name": "Cotton Jackson", + "gender": "male", + "company": "ZOLARITY", + "email": "cottonjackson@zolarity.com", + "phone": "+1 (980) 445-3468" + }, + { + "_id": "55d2fc86577d2794043213ea", + "age": 38, + "name": "Lakeisha Blanchard", + "gender": "female", + "company": "SPEEDBOLT", + "email": "lakeishablanchard@speedbolt.com", + "phone": "+1 (839) 406-2400" + }, + { + "_id": "55d2fc8609e2760d5cc298f1", + "age": 39, + "name": "Alford Church", + "gender": "male", + "company": "GADTRON", + "email": "alfordchurch@gadtron.com", + "phone": "+1 (869) 400-2097" + }, + { + "_id": "55d2fc867e4a904c96668e08", + "age": 30, + "name": "Collins Carlson", + "gender": "male", + "company": "EVEREST", + "email": "collinscarlson@everest.com", + "phone": "+1 (944) 580-3905" + }, + { + "_id": "55d2fc86ddb3ea916627267a", + "age": 33, + "name": "Cain Hester", + "gender": "male", + "company": "IMMUNICS", + "email": "cainhester@immunics.com", + "phone": "+1 (899) 586-2934" + }, + { + "_id": "55d2fc86ffaa0a8952d1a400", + "age": 23, + "name": "Mia Baird", + "gender": "female", + "company": "NIPAZ", + "email": "miabaird@nipaz.com", + "phone": "+1 (940) 569-2850" + }, + { + "_id": "55d2fc865f259364ad5b4fff", + "age": 25, + "name": "Ramona Ewing", + "gender": "female", + "company": "COMTEST", + "email": "ramonaewing@comtest.com", + "phone": "+1 (879) 546-2754" + }, + { + "_id": "55d2fc862d6af75d6794bef5", + "age": 34, + "name": "Delacruz Goodwin", + "gender": "male", + "company": "SLOGANAUT", + "email": "delacruzgoodwin@sloganaut.com", + "phone": "+1 (928) 477-2688" + }, + { + "_id": "55d2fc86e34b19d6fd2ae261", + "age": 38, + "name": "Mcleod Moody", + "gender": "male", + "company": "ECRAZE", + "email": "mcleodmoody@ecraze.com", + "phone": "+1 (989) 598-3350" + }, + { + "_id": "55d2fc8608e305ebd55e0bac", + "age": 34, + "name": "Maddox Calhoun", + "gender": "male", + "company": "TELEPARK", + "email": "maddoxcalhoun@telepark.com", + "phone": "+1 (815) 593-3540" + }, + { + "_id": "55d2fc869e175ca83d7d6597", + "age": 36, + "name": "Cora Dale", + "gender": "female", + "company": "ZILLACTIC", + "email": "coradale@zillactic.com", + "phone": "+1 (866) 545-3632" + }, + { + "_id": "55d2fc861968b039322cb743", + "age": 27, + "name": "Knapp Miranda", + "gender": "male", + "company": "TOYLETRY", + "email": "knappmiranda@toyletry.com", + "phone": "+1 (835) 591-3111" + }, + { + "_id": "55d2fc86423f7b5a304d2175", + "age": 32, + "name": "Ida Petersen", + "gender": "female", + "company": "BILLMED", + "email": "idapetersen@billmed.com", + "phone": "+1 (898) 492-2148" + }, + { + "_id": "55d2fc862cfd92eb67375bba", + "age": 37, + "name": "Concepcion Wilcox", + "gender": "female", + "company": "MAXEMIA", + "email": "concepcionwilcox@maxemia.com", + "phone": "+1 (812) 516-2631" + }, + { + "_id": "55d2fc8695ffe246079f8f0c", + "age": 40, + "name": "Corine Daniel", + "gender": "female", + "company": "MEDCOM", + "email": "corinedaniel@medcom.com", + "phone": "+1 (991) 483-2257" + }, + { + "_id": "55d2fc861f1ff641b3aa7ee5", + "age": 31, + "name": "Latasha Byers", + "gender": "female", + "company": "RUGSTARS", + "email": "latashabyers@rugstars.com", + "phone": "+1 (817) 542-3231" + }, + { + "_id": "55d2fc86724fbfd025371582", + "age": 31, + "name": "Gayle Barrett", + "gender": "female", + "company": "PARAGONIA", + "email": "gaylebarrett@paragonia.com", + "phone": "+1 (870) 547-2454" + }, + { + "_id": "55d2fc869add6e70699650fa", + "age": 40, + "name": "Angelina Tyler", + "gender": "female", + "company": "VIRVA", + "email": "angelinatyler@virva.com", + "phone": "+1 (960) 425-3784" + }, + { + "_id": "55d2fc864c12d18424ac35be", + "age": 27, + "name": "Ratliff Franks", + "gender": "male", + "company": "CEMENTION", + "email": "ratlifffranks@cemention.com", + "phone": "+1 (958) 424-2396" + }, + { + "_id": "55d2fc86bad0be82f6e2f83b", + "age": 40, + "name": "Landry Zimmerman", + "gender": "male", + "company": "JETSILK", + "email": "landryzimmerman@jetsilk.com", + "phone": "+1 (947) 573-2755" + }, + { + "_id": "55d2fc86b56ff40ff0ed70e3", + "age": 23, + "name": "Greta West", + "gender": "female", + "company": "UBERLUX", + "email": "gretawest@uberlux.com", + "phone": "+1 (995) 542-3886" + }, + { + "_id": "55d2fc8667dfd03049bf08eb", + "age": 30, + "name": "Camacho Nelson", + "gender": "male", + "company": "KYAGORO", + "email": "camachonelson@kyagoro.com", + "phone": "+1 (881) 500-3970" + }, + { + "_id": "55d2fc865458ecf3d2995a63", + "age": 36, + "name": "June Turner", + "gender": "female", + "company": "RECOGNIA", + "email": "juneturner@recognia.com", + "phone": "+1 (976) 466-2777" + }, + { + "_id": "55d2fc8682f4304f0889c829", + "age": 31, + "name": "Mckinney Stark", + "gender": "male", + "company": "OPTICOM", + "email": "mckinneystark@opticom.com", + "phone": "+1 (951) 500-3946" + }, + { + "_id": "55d2fc86ff8211995849d831", + "age": 20, + "name": "Hammond Fletcher", + "gender": "male", + "company": "ERSUM", + "email": "hammondfletcher@ersum.com", + "phone": "+1 (974) 541-3273" + }, + { + "_id": "55d2fc86a9792a08912bdb8e", + "age": 23, + "name": "White Fischer", + "gender": "male", + "company": "REALMO", + "email": "whitefischer@realmo.com", + "phone": "+1 (963) 533-2428" + }, + { + "_id": "55d2fc86c5ba8287455030be", + "age": 36, + "name": "Kimberly Mcguire", + "gender": "female", + "company": "TETAK", + "email": "kimberlymcguire@tetak.com", + "phone": "+1 (846) 410-3414" + }, + { + "_id": "55d2fc862a665bca942115f3", + "age": 35, + "name": "Sara Hurst", + "gender": "female", + "company": "FORTEAN", + "email": "sarahurst@fortean.com", + "phone": "+1 (857) 530-3627" + }, + { + "_id": "55d2fc86b72bee240f10055e", + "age": 29, + "name": "Chandler English", + "gender": "male", + "company": "PAPRICUT", + "email": "chandlerenglish@papricut.com", + "phone": "+1 (978) 582-2348" + }, + { + "_id": "55d2fc861c2bccedeac29892", + "age": 29, + "name": "Waters Riley", + "gender": "male", + "company": "ECSTASIA", + "email": "watersriley@ecstasia.com", + "phone": "+1 (842) 579-3426" + }, + { + "_id": "55d2fc86526f36994de2038a", + "age": 34, + "name": "Wood Gomez", + "gender": "male", + "company": "ASSITIA", + "email": "woodgomez@assitia.com", + "phone": "+1 (954) 565-2413" + }, + { + "_id": "55d2fc86bec8fe1e60977c9c", + "age": 31, + "name": "Fields Decker", + "gender": "male", + "company": "EMERGENT", + "email": "fieldsdecker@emergent.com", + "phone": "+1 (992) 489-3712" + }, + { + "_id": "55d2fc868b903951ddd71703", + "age": 28, + "name": "Barry Woods", + "gender": "male", + "company": "OZEAN", + "email": "barrywoods@ozean.com", + "phone": "+1 (885) 433-3285" + }, + { + "_id": "55d2fc86ca3984177237f17e", + "age": 29, + "name": "Whitfield Higgins", + "gender": "male", + "company": "PEARLESEX", + "email": "whitfieldhiggins@pearlesex.com", + "phone": "+1 (869) 468-3186" + }, + { + "_id": "55d2fc8678dd83bf6cc16648", + "age": 27, + "name": "Haynes Mills", + "gender": "male", + "company": "ZOARERE", + "email": "haynesmills@zoarere.com", + "phone": "+1 (886) 576-3206" + }, + { + "_id": "55d2fc86b6ba36d7927b8765", + "age": 27, + "name": "Kellie Hurley", + "gender": "female", + "company": "GYNKO", + "email": "kelliehurley@gynko.com", + "phone": "+1 (844) 548-2894" + }, + { + "_id": "55d2fc869adb64b23212bdfc", + "age": 30, + "name": "Brandi Shields", + "gender": "female", + "company": "KENGEN", + "email": "brandishields@kengen.com", + "phone": "+1 (947) 447-3081" + }, + { + "_id": "55d2fc8617e038558bbd0e5f", + "age": 22, + "name": "Malinda Gordon", + "gender": "female", + "company": "QUILCH", + "email": "malindagordon@quilch.com", + "phone": "+1 (945) 466-2414" + }, + { + "_id": "55d2fc8614756992c50aabfd", + "age": 30, + "name": "Wooten Mcknight", + "gender": "male", + "company": "APPLIDECK", + "email": "wootenmcknight@applideck.com", + "phone": "+1 (994) 416-2156" + }, + { + "_id": "55d2fc86b44fbdb8c8ea3a67", + "age": 36, + "name": "Mona Thomas", + "gender": "female", + "company": "KROG", + "email": "monathomas@krog.com", + "phone": "+1 (924) 423-3381" + }, + { + "_id": "55d2fc86ded545b6b4f5e536", + "age": 22, + "name": "Bates Cole", + "gender": "male", + "company": "DIGIRANG", + "email": "batescole@digirang.com", + "phone": "+1 (956) 409-2471" + }, + { + "_id": "55d2fc864abd3a5951c8e07c", + "age": 33, + "name": "Shirley Potts", + "gender": "female", + "company": "OMATOM", + "email": "shirleypotts@omatom.com", + "phone": "+1 (804) 496-2921" + }, + { + "_id": "55d2fc864e3992c902987b9c", + "age": 22, + "name": "Adrian Branch", + "gender": "female", + "company": "MULTIFLEX", + "email": "adrianbranch@multiflex.com", + "phone": "+1 (817) 499-3955" + }, + { + "_id": "55d2fc8679037ccc9d0d84d0", + "age": 25, + "name": "Deanne Rosa", + "gender": "female", + "company": "QUONATA", + "email": "deannerosa@quonata.com", + "phone": "+1 (896) 463-2190" + }, + { + "_id": "55d2fc869ec47f3f9745bcbc", + "age": 28, + "name": "Sherrie Bowers", + "gender": "female", + "company": "GOLISTIC", + "email": "sherriebowers@golistic.com", + "phone": "+1 (854) 539-3836" + }, + { + "_id": "55d2fc86c953130389da55f9", + "age": 21, + "name": "Sharp Douglas", + "gender": "male", + "company": "CHILLIUM", + "email": "sharpdouglas@chillium.com", + "phone": "+1 (999) 513-3550" + }, + { + "_id": "55d2fc86d42d710fef2a7781", + "age": 22, + "name": "Sandy Dillard", + "gender": "female", + "company": "PHARMACON", + "email": "sandydillard@pharmacon.com", + "phone": "+1 (844) 433-2832" + }, + { + "_id": "55d2fc86aa4130e6998a333b", + "age": 20, + "name": "Naomi Willis", + "gender": "female", + "company": "SAVVY", + "email": "naomiwillis@savvy.com", + "phone": "+1 (826) 499-3221" + }, + { + "_id": "55d2fc869c2c97145040281b", + "age": 38, + "name": "Rivera Stone", + "gender": "male", + "company": "ORBIXTAR", + "email": "riverastone@orbixtar.com", + "phone": "+1 (994) 439-3810" + }, + { + "_id": "55d2fc86e7165c13cd5905c1", + "age": 22, + "name": "Oliver Day", + "gender": "male", + "company": "PORTALIS", + "email": "oliverday@portalis.com", + "phone": "+1 (844) 464-2363" + }, + { + "_id": "55d2fc86b6b619b4d04c7640", + "age": 39, + "name": "Rachael Owens", + "gender": "female", + "company": "NURALI", + "email": "rachaelowens@nurali.com", + "phone": "+1 (856) 418-3617" + }, + { + "_id": "55d2fc865f2612144e6f27e6", + "age": 30, + "name": "Winifred Molina", + "gender": "female", + "company": "NITRACYR", + "email": "winifredmolina@nitracyr.com", + "phone": "+1 (881) 417-3559" + }, + { + "_id": "55d2fc86c38ab2f341eb9717", + "age": 33, + "name": "Helen Callahan", + "gender": "female", + "company": "BOLAX", + "email": "helencallahan@bolax.com", + "phone": "+1 (929) 407-3095" + }, + { + "_id": "55d2fc86e6aa094f47df5373", + "age": 32, + "name": "Leblanc Christensen", + "gender": "male", + "company": "LIQUIDOC", + "email": "leblancchristensen@liquidoc.com", + "phone": "+1 (878) 568-2054" + }, + { + "_id": "55d2fc86b297912d153c4e8f", + "age": 31, + "name": "Hill Robbins", + "gender": "male", + "company": "QUANTALIA", + "email": "hillrobbins@quantalia.com", + "phone": "+1 (826) 430-2750" + }, + { + "_id": "55d2fc86e776e4075d7df74e", + "age": 36, + "name": "Tabitha Whitley", + "gender": "female", + "company": "ZILLIDIUM", + "email": "tabithawhitley@zillidium.com", + "phone": "+1 (838) 516-3637" + }, + { + "_id": "55d2fc86197a382bbf34e81f", + "age": 36, + "name": "May Pearson", + "gender": "male", + "company": "RODEOMAD", + "email": "maypearson@rodeomad.com", + "phone": "+1 (854) 429-3462" + }, + { + "_id": "55d2fc863ade7d3517aed2c6", + "age": 28, + "name": "Alvarez Austin", + "gender": "male", + "company": "CUBIX", + "email": "alvarezaustin@cubix.com", + "phone": "+1 (847) 594-3735" + }, + { + "_id": "55d2fc86b158d5d260362ac4", + "age": 31, + "name": "Misty Shepard", + "gender": "female", + "company": "COMVEYER", + "email": "mistyshepard@comveyer.com", + "phone": "+1 (901) 567-3881" + }, + { + "_id": "55d2fc8646bce5646a0b5258", + "age": 22, + "name": "Yvette Hensley", + "gender": "female", + "company": "MYOPIUM", + "email": "yvettehensley@myopium.com", + "phone": "+1 (890) 456-2157" + }, + { + "_id": "55d2fc86c362f848f08340c2", + "age": 36, + "name": "Hernandez Rowe", + "gender": "male", + "company": "EARTHPLEX", + "email": "hernandezrowe@earthplex.com", + "phone": "+1 (807) 502-2308" + }, + { + "_id": "55d2fc8640a621a6f035ce8d", + "age": 39, + "name": "Maura Harper", + "gender": "female", + "company": "ROBOID", + "email": "mauraharper@roboid.com", + "phone": "+1 (927) 506-2290" + }, + { + "_id": "55d2fc86965968be6314d56f", + "age": 20, + "name": "Luisa Gardner", + "gender": "female", + "company": "WAAB", + "email": "luisagardner@waab.com", + "phone": "+1 (964) 514-2189" + }, + { + "_id": "55d2fc86e12b5b70fffd430a", + "age": 36, + "name": "Christa Bradley", + "gender": "female", + "company": "FURNIGEER", + "email": "christabradley@furnigeer.com", + "phone": "+1 (871) 587-3404" + }, + { + "_id": "55d2fc86153d11b40a9bf8bc", + "age": 26, + "name": "Murphy Fleming", + "gender": "male", + "company": "COLLAIRE", + "email": "murphyfleming@collaire.com", + "phone": "+1 (909) 598-3130" + }, + { + "_id": "55d2fc863ec1e4fc29e5ce50", + "age": 28, + "name": "Bobbi Harrington", + "gender": "female", + "company": "MEDIFAX", + "email": "bobbiharrington@medifax.com", + "phone": "+1 (825) 598-2607" + }, + { + "_id": "55d2fc86f4b17262ea0129c6", + "age": 40, + "name": "Paige Flynn", + "gender": "female", + "company": "PULZE", + "email": "paigeflynn@pulze.com", + "phone": "+1 (956) 529-3295" + }, + { + "_id": "55d2fc861c8dc17cb598e70d", + "age": 21, + "name": "Nina Moon", + "gender": "female", + "company": "ZOLAVO", + "email": "ninamoon@zolavo.com", + "phone": "+1 (863) 540-3993" + }, + { + "_id": "55d2fc869d8ee9d95ab9fee4", + "age": 36, + "name": "Shauna Mckay", + "gender": "female", + "company": "LUNCHPOD", + "email": "shaunamckay@lunchpod.com", + "phone": "+1 (879) 435-3179" + }, + { + "_id": "55d2fc86a03f430c5e56194b", + "age": 36, + "name": "Amie Nicholson", + "gender": "female", + "company": "LETPRO", + "email": "amienicholson@letpro.com", + "phone": "+1 (839) 600-3014" + }, + { + "_id": "55d2fc86062b4615153832f6", + "age": 31, + "name": "Pennington Whitney", + "gender": "male", + "company": "TRI@TRIBALOG", + "email": "penningtonwhitney@tri@tribalog.com", + "phone": "+1 (950) 487-3727" + }, + { + "_id": "55d2fc868849f1c7f4f80541", + "age": 23, + "name": "Gena Barton", + "gender": "female", + "company": "VORTEXACO", + "email": "genabarton@vortexaco.com", + "phone": "+1 (889) 515-2172" + }, + { + "_id": "55d2fc860c13e786f86024fd", + "age": 27, + "name": "Ashley Stephens", + "gender": "female", + "company": "ZENSUS", + "email": "ashleystephens@zensus.com", + "phone": "+1 (949) 525-3726" + }, + { + "_id": "55d2fc86cca3638bdc9c942c", + "age": 38, + "name": "Cherie Morgan", + "gender": "female", + "company": "HELIXO", + "email": "cheriemorgan@helixo.com", + "phone": "+1 (815) 514-2167" + }, + { + "_id": "55d2fc8630d769398c9a0788", + "age": 31, + "name": "Ann Wiggins", + "gender": "female", + "company": "NIXELT", + "email": "annwiggins@nixelt.com", + "phone": "+1 (878) 567-2808" + }, + { + "_id": "55d2fc86f3d0744abd99ee4a", + "age": 36, + "name": "Hinton Keller", + "gender": "male", + "company": "VENOFLEX", + "email": "hintonkeller@venoflex.com", + "phone": "+1 (978) 499-2652" + }, + { + "_id": "55d2fc86714b2f2f7f59ff69", + "age": 32, + "name": "Marsh Mullins", + "gender": "male", + "company": "ZIALACTIC", + "email": "marshmullins@zialactic.com", + "phone": "+1 (908) 537-2112" + }, + { + "_id": "55d2fc8644c73b5870be171c", + "age": 28, + "name": "Holland Underwood", + "gender": "male", + "company": "ZILLACON", + "email": "hollandunderwood@zillacon.com", + "phone": "+1 (968) 454-2162" + }, + { + "_id": "55d2fc8648b8691a6a646f9e", + "age": 31, + "name": "Beverly Oneal", + "gender": "female", + "company": "BYTREX", + "email": "beverlyoneal@bytrex.com", + "phone": "+1 (969) 522-2598" + }, + { + "_id": "55d2fc86b3e9627aa4f5f88a", + "age": 24, + "name": "Leanne Frazier", + "gender": "female", + "company": "HOPELI", + "email": "leannefrazier@hopeli.com", + "phone": "+1 (923) 532-3379" + }, + { + "_id": "55d2fc867bdc2935055e4595", + "age": 31, + "name": "Rhodes Cash", + "gender": "male", + "company": "PAPRIKUT", + "email": "rhodescash@paprikut.com", + "phone": "+1 (830) 507-2776" + }, + { + "_id": "55d2fc86543e21b2bc15201d", + "age": 30, + "name": "Cherry Bush", + "gender": "male", + "company": "PROGENEX", + "email": "cherrybush@progenex.com", + "phone": "+1 (935) 577-2984" + }, + { + "_id": "55d2fc8660c1a32dfdf4fe67", + "age": 32, + "name": "Jacobs Clark", + "gender": "male", + "company": "COMDOM", + "email": "jacobsclark@comdom.com", + "phone": "+1 (947) 434-2665" + }, + { + "_id": "55d2fc861641831257904d9c", + "age": 37, + "name": "Nell Mcmahon", + "gender": "female", + "company": "SLAMBDA", + "email": "nellmcmahon@slambda.com", + "phone": "+1 (831) 462-2693" + }, + { + "_id": "55d2fc86835340478ec889e2", + "age": 37, + "name": "Palmer Livingston", + "gender": "male", + "company": "DIGIGEN", + "email": "palmerlivingston@digigen.com", + "phone": "+1 (817) 443-2049" + }, + { + "_id": "55d2fc8697f9fd666529fa34", + "age": 40, + "name": "Ayala Schmidt", + "gender": "male", + "company": "EWAVES", + "email": "ayalaschmidt@ewaves.com", + "phone": "+1 (899) 576-2845" + }, + { + "_id": "55d2fc8682936b03c7c044de", + "age": 26, + "name": "Lynch Beck", + "gender": "male", + "company": "INDEXIA", + "email": "lynchbeck@indexia.com", + "phone": "+1 (942) 411-3724" + }, + { + "_id": "55d2fc86bb74729fe35b2bcc", + "age": 20, + "name": "Yang Hickman", + "gender": "male", + "company": "UXMOX", + "email": "yanghickman@uxmox.com", + "phone": "+1 (944) 554-2948" + }, + { + "_id": "55d2fc86bb04a1e5e39143b1", + "age": 30, + "name": "Andrews Lucas", + "gender": "male", + "company": "CIPROMOX", + "email": "andrewslucas@cipromox.com", + "phone": "+1 (942) 401-2756" + }, + { + "_id": "55d2fc86b862c1492a4c5bc1", + "age": 34, + "name": "Rosa Valdez", + "gender": "female", + "company": "ONTALITY", + "email": "rosavaldez@ontality.com", + "phone": "+1 (963) 414-3056" + }, + { + "_id": "55d2fc868ab9f2f25a46a850", + "age": 30, + "name": "Maria Caldwell", + "gender": "female", + "company": "ACRODANCE", + "email": "mariacaldwell@acrodance.com", + "phone": "+1 (963) 433-2398" + }, + { + "_id": "55d2fc868d206b4d99f1f0b2", + "age": 36, + "name": "Gilda Chase", + "gender": "female", + "company": "KOFFEE", + "email": "gildachase@koffee.com", + "phone": "+1 (980) 591-3955" + }, + { + "_id": "55d2fc86e0c5a2f031b4a0d9", + "age": 24, + "name": "Dejesus Pittman", + "gender": "male", + "company": "SLAX", + "email": "dejesuspittman@slax.com", + "phone": "+1 (819) 574-2826" + }, + { + "_id": "55d2fc868190178a9af16ec5", + "age": 23, + "name": "Valdez Gibson", + "gender": "male", + "company": "ELECTONIC", + "email": "valdezgibson@electonic.com", + "phone": "+1 (809) 520-3985" + }, + { + "_id": "55d2fc86d3bc3cf86e16bc5b", + "age": 21, + "name": "Aguilar Bird", + "gender": "male", + "company": "ULTRASURE", + "email": "aguilarbird@ultrasure.com", + "phone": "+1 (813) 455-3814" + }, + { + "_id": "55d2fc86701c49cc235f0b49", + "age": 24, + "name": "Bentley Mooney", + "gender": "male", + "company": "BEDLAM", + "email": "bentleymooney@bedlam.com", + "phone": "+1 (870) 530-2188" + }, + { + "_id": "55d2fc863fe6d9fc492a1ca5", + "age": 28, + "name": "Ruby Wooten", + "gender": "female", + "company": "MARKETOID", + "email": "rubywooten@marketoid.com", + "phone": "+1 (813) 470-3521" + }, + { + "_id": "55d2fc8622f489f721743001", + "age": 28, + "name": "Garrison Blevins", + "gender": "male", + "company": "KEENGEN", + "email": "garrisonblevins@keengen.com", + "phone": "+1 (974) 538-2989" + }, + { + "_id": "55d2fc863dc60d226c55ece7", + "age": 33, + "name": "Harper Tanner", + "gender": "male", + "company": "QABOOS", + "email": "harpertanner@qaboos.com", + "phone": "+1 (953) 406-3082" + }, + { + "_id": "55d2fc86fecd601439c2702e", + "age": 32, + "name": "Best Robles", + "gender": "male", + "company": "OCEANICA", + "email": "bestrobles@oceanica.com", + "phone": "+1 (815) 539-3097" + }, + { + "_id": "55d2fc86ba25530a2149beab", + "age": 36, + "name": "Marian Bradshaw", + "gender": "female", + "company": "XYQAG", + "email": "marianbradshaw@xyqag.com", + "phone": "+1 (928) 410-3218" + }, + { + "_id": "55d2fc867352b6b799d365e4", + "age": 23, + "name": "Whitley Oneil", + "gender": "male", + "company": "XURBAN", + "email": "whitleyoneil@xurban.com", + "phone": "+1 (802) 578-3671" + }, + { + "_id": "55d2fc865ee137dbdee5cde2", + "age": 34, + "name": "Ella Fox", + "gender": "female", + "company": "TUBESYS", + "email": "ellafox@tubesys.com", + "phone": "+1 (920) 524-3066" + }, + { + "_id": "55d2fc860cc159486a822879", + "age": 30, + "name": "Farmer Castro", + "gender": "male", + "company": "QNEKT", + "email": "farmercastro@qnekt.com", + "phone": "+1 (866) 578-2968" + }, + { + "_id": "55d2fc863b57eefc3015d373", + "age": 28, + "name": "Guy Cochran", + "gender": "male", + "company": "VICON", + "email": "guycochran@vicon.com", + "phone": "+1 (840) 567-2191" + }, + { + "_id": "55d2fc863df7dfda22e99029", + "age": 32, + "name": "Leach Rocha", + "gender": "male", + "company": "DANJA", + "email": "leachrocha@danja.com", + "phone": "+1 (971) 589-3164" + }, + { + "_id": "55d2fc86aba3b9d7ce3f877c", + "age": 36, + "name": "Tanner Hayes", + "gender": "male", + "company": "TELEQUIET", + "email": "tannerhayes@telequiet.com", + "phone": "+1 (813) 526-2989" + }, + { + "_id": "55d2fc862cd6fb84f734fa0e", + "age": 30, + "name": "Keith Maldonado", + "gender": "male", + "company": "MAGNEATO", + "email": "keithmaldonado@magneato.com", + "phone": "+1 (997) 419-3200" + }, + { + "_id": "55d2fc8663d4dc1e43943f62", + "age": 29, + "name": "Winnie Harrell", + "gender": "female", + "company": "FRENEX", + "email": "winnieharrell@frenex.com", + "phone": "+1 (966) 565-2447" + }, + { + "_id": "55d2fc86b57f9312b0d28a1d", + "age": 28, + "name": "Sandoval Garza", + "gender": "male", + "company": "INTERLOO", + "email": "sandovalgarza@interloo.com", + "phone": "+1 (972) 597-3431" + }, + { + "_id": "55d2fc86a356d194d285d160", + "age": 23, + "name": "Lina Dejesus", + "gender": "female", + "company": "ORONOKO", + "email": "linadejesus@oronoko.com", + "phone": "+1 (910) 560-2515" + }, + { + "_id": "55d2fc862f4cd754495f93c7", + "age": 30, + "name": "Jana Spence", + "gender": "female", + "company": "ZILLACOM", + "email": "janaspence@zillacom.com", + "phone": "+1 (994) 436-2023" + }, + { + "_id": "55d2fc869b25329fae4936d0", + "age": 32, + "name": "Mcdowell Fisher", + "gender": "male", + "company": "GYNK", + "email": "mcdowellfisher@gynk.com", + "phone": "+1 (941) 587-3569" + }, + { + "_id": "55d2fc866b52b90a3758bdd3", + "age": 28, + "name": "Farley Bernard", + "gender": "male", + "company": "NETROPIC", + "email": "farleybernard@netropic.com", + "phone": "+1 (856) 540-2658" + }, + { + "_id": "55d2fc864086901eaeb80443", + "age": 25, + "name": "Lorna Howe", + "gender": "female", + "company": "ISOSWITCH", + "email": "lornahowe@isoswitch.com", + "phone": "+1 (851) 432-3160" + }, + { + "_id": "55d2fc86b4ac38891f11340b", + "age": 25, + "name": "English Watts", + "gender": "male", + "company": "INFOTRIPS", + "email": "englishwatts@infotrips.com", + "phone": "+1 (942) 481-2578" + }, + { + "_id": "55d2fc86e0d047d4eb3c224f", + "age": 35, + "name": "Burch Howell", + "gender": "male", + "company": "FANFARE", + "email": "burchhowell@fanfare.com", + "phone": "+1 (986) 507-2725" + }, + { + "_id": "55d2fc86330a8dab2ddbc0c4", + "age": 39, + "name": "Hudson Bender", + "gender": "male", + "company": "ENORMO", + "email": "hudsonbender@enormo.com", + "phone": "+1 (982) 553-3993" + }, + { + "_id": "55d2fc8600bb27f4ba215be8", + "age": 30, + "name": "Mcdonald Whitehead", + "gender": "male", + "company": "SENMAO", + "email": "mcdonaldwhitehead@senmao.com", + "phone": "+1 (837) 449-3264" + }, + { + "_id": "55d2fc8683787d8b7b400408", + "age": 31, + "name": "Hope Holden", + "gender": "female", + "company": "EVENTIX", + "email": "hopeholden@eventix.com", + "phone": "+1 (888) 436-2921" + }, + { + "_id": "55d2fc86cd7d2a5c962d2c05", + "age": 36, + "name": "Suarez Mejia", + "gender": "male", + "company": "BUZZWORKS", + "email": "suarezmejia@buzzworks.com", + "phone": "+1 (919) 526-3966" + }, + { + "_id": "55d2fc8612bf58c9b2d953cf", + "age": 27, + "name": "Michele Little", + "gender": "female", + "company": "VINCH", + "email": "michelelittle@vinch.com", + "phone": "+1 (817) 414-2165" + }, + { + "_id": "55d2fc864a8103126f905972", + "age": 25, + "name": "Patrick Cooke", + "gender": "male", + "company": "BEDDER", + "email": "patrickcooke@bedder.com", + "phone": "+1 (993) 587-2086" + }, + { + "_id": "55d2fc865ddf784cac1f023c", + "age": 31, + "name": "Holcomb Beasley", + "gender": "male", + "company": "TECHTRIX", + "email": "holcombbeasley@techtrix.com", + "phone": "+1 (879) 458-3507" + }, + { + "_id": "55d2fc86e27a28e9e9b0d232", + "age": 34, + "name": "Catalina Donovan", + "gender": "female", + "company": "FILODYNE", + "email": "catalinadonovan@filodyne.com", + "phone": "+1 (818) 542-2296" + }, + { + "_id": "55d2fc861801cbac57fa3186", + "age": 21, + "name": "Leslie Bryan", + "gender": "female", + "company": "LUXURIA", + "email": "lesliebryan@luxuria.com", + "phone": "+1 (917) 590-3272" + }, + { + "_id": "55d2fc86d3aa760444ec40cc", + "age": 37, + "name": "Hobbs Noel", + "gender": "male", + "company": "ZILLA", + "email": "hobbsnoel@zilla.com", + "phone": "+1 (917) 430-3792" + }, + { + "_id": "55d2fc86f53d0267e33ddb67", + "age": 38, + "name": "Nunez Meyers", + "gender": "male", + "company": "ENTROFLEX", + "email": "nunezmeyers@entroflex.com", + "phone": "+1 (940) 419-3943" + }, + { + "_id": "55d2fc867805cf4262e648a9", + "age": 37, + "name": "Sonya Sloan", + "gender": "female", + "company": "SURELOGIC", + "email": "sonyasloan@surelogic.com", + "phone": "+1 (924) 561-3268" + }, + { + "_id": "55d2fc86a3b756eaaef9f9fa", + "age": 31, + "name": "Angeline Sargent", + "gender": "female", + "company": "QUIZMO", + "email": "angelinesargent@quizmo.com", + "phone": "+1 (952) 539-3859" + }, + { + "_id": "55d2fc860f0e4be242c866a3", + "age": 40, + "name": "Norris Webb", + "gender": "male", + "company": "ZENCO", + "email": "norriswebb@zenco.com", + "phone": "+1 (834) 527-2399" + }, + { + "_id": "55d2fc86b2da030fb755d74c", + "age": 38, + "name": "Wise Bonner", + "gender": "male", + "company": "KINETICA", + "email": "wisebonner@kinetica.com", + "phone": "+1 (938) 416-3537" + }, + { + "_id": "55d2fc8699036f35ee214843", + "age": 25, + "name": "Imogene Blankenship", + "gender": "female", + "company": "POLARAX", + "email": "imogeneblankenship@polarax.com", + "phone": "+1 (877) 476-3735" + }, + { + "_id": "55d2fc86009b5a1658986a92", + "age": 27, + "name": "Silva Schneider", + "gender": "male", + "company": "MINGA", + "email": "silvaschneider@minga.com", + "phone": "+1 (884) 420-2111" + }, + { + "_id": "55d2fc86e3dcb6d4996e9813", + "age": 40, + "name": "Lawanda Cortez", + "gender": "female", + "company": "HOMETOWN", + "email": "lawandacortez@hometown.com", + "phone": "+1 (946) 525-3826" + }, + { + "_id": "55d2fc8641977ef422e73176", + "age": 40, + "name": "Clements Waters", + "gender": "male", + "company": "FLEETMIX", + "email": "clementswaters@fleetmix.com", + "phone": "+1 (973) 523-2395" + }, + { + "_id": "55d2fc869d0f5363ad055935", + "age": 20, + "name": "Ofelia Gilbert", + "gender": "female", + "company": "ECRATER", + "email": "ofeliagilbert@ecrater.com", + "phone": "+1 (828) 404-2646" + }, + { + "_id": "55d2fc86c39b876162269895", + "age": 23, + "name": "Valenzuela Carney", + "gender": "male", + "company": "HYDROCOM", + "email": "valenzuelacarney@hydrocom.com", + "phone": "+1 (842) 566-3650" + }, + { + "_id": "55d2fc86546c31933b02dd85", + "age": 28, + "name": "Wells Santana", + "gender": "male", + "company": "ZIDOX", + "email": "wellssantana@zidox.com", + "phone": "+1 (886) 527-2963" + }, + { + "_id": "55d2fc86a981c3741ad50f8c", + "age": 29, + "name": "Karla Carroll", + "gender": "female", + "company": "MAGNEMO", + "email": "karlacarroll@magnemo.com", + "phone": "+1 (922) 418-3361" + }, + { + "_id": "55d2fc861054327ef76378e3", + "age": 29, + "name": "Juliet Butler", + "gender": "female", + "company": "ORBAXTER", + "email": "julietbutler@orbaxter.com", + "phone": "+1 (838) 554-2269" + }, + { + "_id": "55d2fc868120c8a8e4eb9149", + "age": 26, + "name": "Lisa Copeland", + "gender": "female", + "company": "SOFTMICRO", + "email": "lisacopeland@softmicro.com", + "phone": "+1 (915) 577-2302" + }, + { + "_id": "55d2fc86b0eb908d67787f43", + "age": 37, + "name": "Mcmahon Spencer", + "gender": "male", + "company": "BOILCAT", + "email": "mcmahonspencer@boilcat.com", + "phone": "+1 (871) 501-2558" + }, + { + "_id": "55d2fc860c0b6f5fb520ad2a", + "age": 38, + "name": "Campbell Baxter", + "gender": "male", + "company": "DREAMIA", + "email": "campbellbaxter@dreamia.com", + "phone": "+1 (858) 524-3012" + }, + { + "_id": "55d2fc86460508fbed08e924", + "age": 23, + "name": "Lindsay Sharp", + "gender": "male", + "company": "SYBIXTEX", + "email": "lindsaysharp@sybixtex.com", + "phone": "+1 (974) 573-3073" + }, + { + "_id": "55d2fc860bf5295ce5679523", + "age": 35, + "name": "Kathrine Browning", + "gender": "female", + "company": "LUNCHPAD", + "email": "kathrinebrowning@lunchpad.com", + "phone": "+1 (922) 458-2466" + }, + { + "_id": "55d2fc86d2311e0208cd17a2", + "age": 35, + "name": "Alice Faulkner", + "gender": "female", + "company": "PLEXIA", + "email": "alicefaulkner@plexia.com", + "phone": "+1 (939) 419-3621" + }, + { + "_id": "55d2fc86e85116cc8d6c0d70", + "age": 32, + "name": "Ford Mclean", + "gender": "male", + "company": "QUADEEBO", + "email": "fordmclean@quadeebo.com", + "phone": "+1 (974) 521-2540" + }, + { + "_id": "55d2fc86f60393147a3a8a07", + "age": 29, + "name": "Ollie Cannon", + "gender": "female", + "company": "RAMJOB", + "email": "olliecannon@ramjob.com", + "phone": "+1 (961) 497-3201" + }, + { + "_id": "55d2fc8661d02152a5ba425a", + "age": 36, + "name": "Calderon Vaughan", + "gender": "male", + "company": "DIGIQUE", + "email": "calderonvaughan@digique.com", + "phone": "+1 (912) 553-3902" + }, + { + "_id": "55d2fc86a55890c6ce345bf0", + "age": 28, + "name": "Warren Henry", + "gender": "male", + "company": "ZERBINA", + "email": "warrenhenry@zerbina.com", + "phone": "+1 (850) 464-3656" + }, + { + "_id": "55d2fc86ccead2741ea21e0e", + "age": 21, + "name": "Liliana York", + "gender": "female", + "company": "ANIXANG", + "email": "lilianayork@anixang.com", + "phone": "+1 (956) 403-2096" + }, + { + "_id": "55d2fc86d72dd184f6884371", + "age": 33, + "name": "Lora Alvarez", + "gender": "female", + "company": "BLURRYBUS", + "email": "loraalvarez@blurrybus.com", + "phone": "+1 (917) 457-2866" + }, + { + "_id": "55d2fc86fda0c180ccc9598a", + "age": 22, + "name": "Luna Ellis", + "gender": "male", + "company": "SLUMBERIA", + "email": "lunaellis@slumberia.com", + "phone": "+1 (878) 589-3511" + }, + { + "_id": "55d2fc860dd81b364fc1c2a9", + "age": 30, + "name": "Phoebe Chang", + "gender": "female", + "company": "OPTICON", + "email": "phoebechang@opticon.com", + "phone": "+1 (962) 559-3475" + }, + { + "_id": "55d2fc8657954cc73c166579", + "age": 40, + "name": "Anna Crane", + "gender": "female", + "company": "AQUAFIRE", + "email": "annacrane@aquafire.com", + "phone": "+1 (989) 567-3649" + }, + { + "_id": "55d2fc86d996f0f466a006c8", + "age": 39, + "name": "Matthews French", + "gender": "male", + "company": "CINESANCT", + "email": "matthewsfrench@cinesanct.com", + "phone": "+1 (896) 518-2965" + }, + { + "_id": "55d2fc8601aad1428aa65531", + "age": 34, + "name": "Hutchinson Ellison", + "gender": "male", + "company": "MOREGANIC", + "email": "hutchinsonellison@moreganic.com", + "phone": "+1 (860) 563-2707" + }, + { + "_id": "55d2fc86ec14a6c798e22d72", + "age": 21, + "name": "Gwen Russell", + "gender": "female", + "company": "COMVOY", + "email": "gwenrussell@comvoy.com", + "phone": "+1 (873) 468-2314" + }, + { + "_id": "55d2fc865123af00125fd9bd", + "age": 22, + "name": "Natalie Stuart", + "gender": "female", + "company": "MOMENTIA", + "email": "nataliestuart@momentia.com", + "phone": "+1 (908) 573-2177" + }, + { + "_id": "55d2fc8688af31f1e9ff0d20", + "age": 28, + "name": "Brianna Meyer", + "gender": "female", + "company": "VIASIA", + "email": "briannameyer@viasia.com", + "phone": "+1 (860) 475-3139" + }, + { + "_id": "55d2fc8687f0ff5daa16cbbd", + "age": 28, + "name": "Trisha Castillo", + "gender": "female", + "company": "CYCLONICA", + "email": "trishacastillo@cyclonica.com", + "phone": "+1 (869) 564-2957" + }, + { + "_id": "55d2fc863b1c51f11ce4e921", + "age": 36, + "name": "Powers Weeks", + "gender": "male", + "company": "BIZMATIC", + "email": "powersweeks@bizmatic.com", + "phone": "+1 (981) 464-3668" + }, + { + "_id": "55d2fc86d8f5d2bef6f84bba", + "age": 23, + "name": "Young Cabrera", + "gender": "female", + "company": "CINASTER", + "email": "youngcabrera@cinaster.com", + "phone": "+1 (897) 528-3924" + }, + { + "_id": "55d2fc86861238b57e2932fd", + "age": 27, + "name": "Maxine Rodgers", + "gender": "female", + "company": "CHORIZON", + "email": "maxinerodgers@chorizon.com", + "phone": "+1 (996) 449-2805" + }, + { + "_id": "55d2fc86fe5b2f6823cc295f", + "age": 26, + "name": "Davis Norris", + "gender": "male", + "company": "CORIANDER", + "email": "davisnorris@coriander.com", + "phone": "+1 (947) 512-2093" + }, + { + "_id": "55d2fc86862e7d0bba1ab524", + "age": 25, + "name": "Ericka Conner", + "gender": "female", + "company": "STRALOY", + "email": "erickaconner@straloy.com", + "phone": "+1 (922) 565-2956" + }, + { + "_id": "55d2fc8625bf91e39382ab4c", + "age": 21, + "name": "Payne Joyner", + "gender": "male", + "company": "OMNIGOG", + "email": "paynejoyner@omnigog.com", + "phone": "+1 (998) 521-3917" + }, + { + "_id": "55d2fc866835ee51ccea79bb", + "age": 24, + "name": "Fletcher Payne", + "gender": "male", + "company": "AMTAP", + "email": "fletcherpayne@amtap.com", + "phone": "+1 (991) 517-3798" + }, + { + "_id": "55d2fc863df3424f70db684c", + "age": 38, + "name": "Mosley Cobb", + "gender": "male", + "company": "HONOTRON", + "email": "mosleycobb@honotron.com", + "phone": "+1 (873) 593-2248" + }, + { + "_id": "55d2fc867c4ad9b233d15983", + "age": 24, + "name": "Webster Sandoval", + "gender": "male", + "company": "HOMELUX", + "email": "webstersandoval@homelux.com", + "phone": "+1 (967) 431-2940" + }, + { + "_id": "55d2fc8613bf2fe49b1a1f8c", + "age": 36, + "name": "Colon Mcgee", + "gender": "male", + "company": "ZAPPIX", + "email": "colonmcgee@zappix.com", + "phone": "+1 (806) 444-2451" + }, + { + "_id": "55d2fc8681a8ccebe8aacd93", + "age": 32, + "name": "Monique Logan", + "gender": "female", + "company": "CALLFLEX", + "email": "moniquelogan@callflex.com", + "phone": "+1 (957) 577-3780" + }, + { + "_id": "55d2fc868756d302f29fddb2", + "age": 38, + "name": "Stewart Ball", + "gender": "male", + "company": "NETPLODE", + "email": "stewartball@netplode.com", + "phone": "+1 (966) 435-2206" + }, + { + "_id": "55d2fc86457f4d474388d2c8", + "age": 23, + "name": "Montgomery Carter", + "gender": "male", + "company": "OLUCORE", + "email": "montgomerycarter@olucore.com", + "phone": "+1 (894) 556-2662" + }, + { + "_id": "55d2fc866da04e0d15b12b36", + "age": 28, + "name": "Brenda Mccoy", + "gender": "female", + "company": "AQUAZURE", + "email": "brendamccoy@aquazure.com", + "phone": "+1 (837) 483-3741" + }, + { + "_id": "55d2fc86e2a61a730a8d5c8f", + "age": 34, + "name": "Eddie Buchanan", + "gender": "female", + "company": "COGNICODE", + "email": "eddiebuchanan@cognicode.com", + "phone": "+1 (924) 479-3753" + }, + { + "_id": "55d2fc8616e7989042f61488", + "age": 23, + "name": "Eva Mendoza", + "gender": "female", + "company": "SOLGAN", + "email": "evamendoza@solgan.com", + "phone": "+1 (899) 522-3051" + }, + { + "_id": "55d2fc86152fd9e4c5471fd7", + "age": 21, + "name": "Dawson Medina", + "gender": "male", + "company": "AQUASSEUR", + "email": "dawsonmedina@aquasseur.com", + "phone": "+1 (877) 580-2295" + }, + { + "_id": "55d2fc86a3633b3a799a7811", + "age": 34, + "name": "Terrie Hobbs", + "gender": "female", + "company": "VORATAK", + "email": "terriehobbs@voratak.com", + "phone": "+1 (938) 511-2077" + }, + { + "_id": "55d2fc8665a05f05f07bb790", + "age": 37, + "name": "Iris Bishop", + "gender": "female", + "company": "INSURESYS", + "email": "irisbishop@insuresys.com", + "phone": "+1 (819) 415-3840" + }, + { + "_id": "55d2fc8799e7556a033b93f6", + "age": 29, + "name": "Estelle Grant", + "gender": "female", + "company": "ZOGAK", + "email": "estellegrant@zogak.com", + "phone": "+1 (854) 437-2898" + }, + { + "_id": "55d2fc87745c675e697af04c", + "age": 30, + "name": "Dianna Gonzalez", + "gender": "female", + "company": "PIVITOL", + "email": "diannagonzalez@pivitol.com", + "phone": "+1 (816) 545-3520" + } +] diff --git a/vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go b/vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go new file mode 100644 index 000000000..698dd332d --- /dev/null +++ b/vendor/github.com/alecthomas/log4go/examples/ConsoleLogWriter_Manual.go @@ -0,0 +1,14 @@ +package main + +import ( + "time" +) + +import l4g "code.google.com/p/log4go" + +func main() { + log := l4g.NewLogger() + defer log.Close() + log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter()) + log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) +} diff --git a/vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go b/vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go new file mode 100644 index 000000000..efd596aa6 --- /dev/null +++ b/vendor/github.com/alecthomas/log4go/examples/FileLogWriter_Manual.go @@ -0,0 +1,57 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "time" +) + +import l4g "code.google.com/p/log4go" + +const ( + filename = "flw.log" +) + +func main() { + // Get a new logger instance + log := l4g.NewLogger() + + // Create a default logger that is logging messages of FINE or higher + log.AddFilter("file", l4g.FINE, l4g.NewFileLogWriter(filename, false)) + log.Close() + + /* Can also specify manually via the following: (these are the defaults) */ + flw := l4g.NewFileLogWriter(filename, false) + flw.SetFormat("[%D %T] [%L] (%S) %M") + flw.SetRotate(false) + flw.SetRotateSize(0) + flw.SetRotateLines(0) + flw.SetRotateDaily(false) + log.AddFilter("file", l4g.FINE, flw) + + // Log some experimental messages + log.Finest("Everything is created now (notice that I will not be printing to the file)") + log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) + log.Critical("Time to close out!") + + // Close the log + log.Close() + + // Print what was logged to the file (yes, I know I'm skipping error checking) + fd, _ := os.Open(filename) + in := bufio.NewReader(fd) + fmt.Print("Messages logged to file were: (line numbers not included)\n") + for lineno := 1; ; lineno++ { + line, err := in.ReadString('\n') + if err == io.EOF { + break + } + fmt.Printf("%3d:\t%s", lineno, line) + } + fd.Close() + + // Remove the file so it's not lying around + os.Remove(filename) +} diff --git a/vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go b/vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go new file mode 100644 index 000000000..83c80ad12 --- /dev/null +++ b/vendor/github.com/alecthomas/log4go/examples/SimpleNetLogServer.go @@ -0,0 +1,42 @@ +package main + +import ( + "flag" + "fmt" + "net" + "os" +) + +var ( + port = flag.String("p", "12124", "Port number to listen on") +) + +func e(err error) { + if err != nil { + fmt.Printf("Erroring out: %s\n", err) + os.Exit(1) + } +} + +func main() { + flag.Parse() + + // Bind to the port + bind, err := net.ResolveUDPAddr("0.0.0.0:" + *port) + e(err) + + // Create listener + listener, err := net.ListenUDP("udp", bind) + e(err) + + fmt.Printf("Listening to port %s...\n", *port) + for { + // read into a new buffer + buffer := make([]byte, 1024) + _, _, err := listener.ReadFrom(buffer) + e(err) + + // log to standard output + fmt.Println(string(buffer)) + } +} diff --git a/vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go b/vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go new file mode 100644 index 000000000..400b698ca --- /dev/null +++ b/vendor/github.com/alecthomas/log4go/examples/SocketLogWriter_Manual.go @@ -0,0 +1,18 @@ +package main + +import ( + "time" +) + +import l4g "code.google.com/p/log4go" + +func main() { + log := l4g.NewLogger() + log.AddFilter("network", l4g.FINEST, l4g.NewSocketLogWriter("udp", "192.168.1.255:12124")) + + // Run `nc -u -l -p 12124` or similar before you run this to see the following message + log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) + + // This makes sure the output stream buffer is written + log.Close() +} diff --git a/vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go b/vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go new file mode 100644 index 000000000..164c2add4 --- /dev/null +++ b/vendor/github.com/alecthomas/log4go/examples/XMLConfigurationExample.go @@ -0,0 +1,13 @@ +package main + +import l4g "code.google.com/p/log4go" + +func main() { + // Load the configuration (isn't this easy?) + l4g.LoadConfiguration("example.xml") + + // And now we're ready! + l4g.Finest("This will only go to those of you really cool UDP kids! If you change enabled=true.") + l4g.Debug("Oh no! %d + %d = %d!", 2, 2, 2+2) + l4g.Info("About that time, eh chaps?") +} diff --git a/vendor/github.com/alecthomas/log4go/examples/example.xml b/vendor/github.com/alecthomas/log4go/examples/example.xml new file mode 100644 index 000000000..e791278ce --- /dev/null +++ b/vendor/github.com/alecthomas/log4go/examples/example.xml @@ -0,0 +1,47 @@ + + + stdout + console + + DEBUG + + + file + file + FINEST + test.log + + [%D %T] [%L] (%S) %M + false + 0M + 0K + true + + + xmllog + xml + TRACE + trace.xml + true + 100M + 6K + false + + + donotopen + socket + FINEST + 192.168.1.255:12124 + udp + + diff --git a/vendor/github.com/alecthomas/log4go/log4go_test.go b/vendor/github.com/alecthomas/log4go/log4go_test.go new file mode 100644 index 000000000..c4b92f6a7 --- /dev/null +++ b/vendor/github.com/alecthomas/log4go/log4go_test.go @@ -0,0 +1,534 @@ +// Copyright (C) 2010, Kyle Lemons . All rights reserved. + +package log4go + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "runtime" + "testing" + "time" +) + +const testLogFile = "_logtest.log" + +var now time.Time = time.Unix(0, 1234567890123456789).In(time.UTC) + +func newLogRecord(lvl Level, src string, msg string) *LogRecord { + return &LogRecord{ + Level: lvl, + Source: src, + Created: now, + Message: msg, + } +} + +func TestELog(t *testing.T) { + fmt.Printf("Testing %s\n", L4G_VERSION) + lr := newLogRecord(CRITICAL, "source", "message") + if lr.Level != CRITICAL { + t.Errorf("Incorrect level: %d should be %d", lr.Level, CRITICAL) + } + if lr.Source != "source" { + t.Errorf("Incorrect source: %s should be %s", lr.Source, "source") + } + if lr.Message != "message" { + t.Errorf("Incorrect message: %s should be %s", lr.Source, "message") + } +} + +var formatTests = []struct { + Test string + Record *LogRecord + Formats map[string]string +}{ + { + Test: "Standard formats", + Record: &LogRecord{ + Level: ERROR, + Source: "source", + Message: "message", + Created: now, + }, + Formats: map[string]string{ + // TODO(kevlar): How can I do this so it'll work outside of PST? + FORMAT_DEFAULT: "[2009/02/13 23:31:30 UTC] [EROR] (source) message\n", + FORMAT_SHORT: "[23:31 13/02/09] [EROR] message\n", + FORMAT_ABBREV: "[EROR] message\n", + }, + }, +} + +func TestFormatLogRecord(t *testing.T) { + for _, test := range formatTests { + name := test.Test + for fmt, want := range test.Formats { + if got := FormatLogRecord(fmt, test.Record); got != want { + t.Errorf("%s - %s:", name, fmt) + t.Errorf(" got %q", got) + t.Errorf(" want %q", want) + } + } + } +} + +var logRecordWriteTests = []struct { + Test string + Record *LogRecord + Console string +}{ + { + Test: "Normal message", + Record: &LogRecord{ + Level: CRITICAL, + Source: "source", + Message: "message", + Created: now, + }, + Console: "[23:31:30 UTC 2009/02/13] [CRIT] message\n", + }, +} + +func TestConsoleLogWriter(t *testing.T) { + console := make(ConsoleLogWriter) + + r, w := io.Pipe() + go console.run(w) + defer console.Close() + + buf := make([]byte, 1024) + + for _, test := range logRecordWriteTests { + name := test.Test + + console.LogWrite(test.Record) + n, _ := r.Read(buf) + + if got, want := string(buf[:n]), test.Console; got != want { + t.Errorf("%s: got %q", name, got) + t.Errorf("%s: want %q", name, want) + } + } +} + +func TestFileLogWriter(t *testing.T) { + defer func(buflen int) { + LogBufferLength = buflen + }(LogBufferLength) + LogBufferLength = 0 + + w := NewFileLogWriter(testLogFile, false) + if w == nil { + t.Fatalf("Invalid return: w should not be nil") + } + defer os.Remove(testLogFile) + + w.LogWrite(newLogRecord(CRITICAL, "source", "message")) + w.Close() + runtime.Gosched() + + if contents, err := ioutil.ReadFile(testLogFile); err != nil { + t.Errorf("read(%q): %s", testLogFile, err) + } else if len(contents) != 50 { + t.Errorf("malformed filelog: %q (%d bytes)", string(contents), len(contents)) + } +} + +func TestXMLLogWriter(t *testing.T) { + defer func(buflen int) { + LogBufferLength = buflen + }(LogBufferLength) + LogBufferLength = 0 + + w := NewXMLLogWriter(testLogFile, false) + if w == nil { + t.Fatalf("Invalid return: w should not be nil") + } + defer os.Remove(testLogFile) + + w.LogWrite(newLogRecord(CRITICAL, "source", "message")) + w.Close() + runtime.Gosched() + + if contents, err := ioutil.ReadFile(testLogFile); err != nil { + t.Errorf("read(%q): %s", testLogFile, err) + } else if len(contents) != 185 { + t.Errorf("malformed xmllog: %q (%d bytes)", string(contents), len(contents)) + } +} + +func TestLogger(t *testing.T) { + sl := NewDefaultLogger(WARNING) + if sl == nil { + t.Fatalf("NewDefaultLogger should never return nil") + } + if lw, exist := sl["stdout"]; lw == nil || exist != true { + t.Fatalf("NewDefaultLogger produced invalid logger (DNE or nil)") + } + if sl["stdout"].Level != WARNING { + t.Fatalf("NewDefaultLogger produced invalid logger (incorrect level)") + } + if len(sl) != 1 { + t.Fatalf("NewDefaultLogger produced invalid logger (incorrect map count)") + } + + //func (l *Logger) AddFilter(name string, level int, writer LogWriter) {} + l := make(Logger) + l.AddFilter("stdout", DEBUG, NewConsoleLogWriter()) + if lw, exist := l["stdout"]; lw == nil || exist != true { + t.Fatalf("AddFilter produced invalid logger (DNE or nil)") + } + if l["stdout"].Level != DEBUG { + t.Fatalf("AddFilter produced invalid logger (incorrect level)") + } + if len(l) != 1 { + t.Fatalf("AddFilter produced invalid logger (incorrect map count)") + } + + //func (l *Logger) Warn(format string, args ...interface{}) error {} + if err := l.Warn("%s %d %#v", "Warning:", 1, []int{}); err.Error() != "Warning: 1 []int{}" { + t.Errorf("Warn returned invalid error: %s", err) + } + + //func (l *Logger) Error(format string, args ...interface{}) error {} + if err := l.Error("%s %d %#v", "Error:", 10, []string{}); err.Error() != "Error: 10 []string{}" { + t.Errorf("Error returned invalid error: %s", err) + } + + //func (l *Logger) Critical(format string, args ...interface{}) error {} + if err := l.Critical("%s %d %#v", "Critical:", 100, []int64{}); err.Error() != "Critical: 100 []int64{}" { + t.Errorf("Critical returned invalid error: %s", err) + } + + // Already tested or basically untestable + //func (l *Logger) Log(level int, source, message string) {} + //func (l *Logger) Logf(level int, format string, args ...interface{}) {} + //func (l *Logger) intLogf(level int, format string, args ...interface{}) string {} + //func (l *Logger) Finest(format string, args ...interface{}) {} + //func (l *Logger) Fine(format string, args ...interface{}) {} + //func (l *Logger) Debug(format string, args ...interface{}) {} + //func (l *Logger) Trace(format string, args ...interface{}) {} + //func (l *Logger) Info(format string, args ...interface{}) {} +} + +func TestLogOutput(t *testing.T) { + const ( + expected = "fdf3e51e444da56b4cb400f30bc47424" + ) + + // Unbuffered output + defer func(buflen int) { + LogBufferLength = buflen + }(LogBufferLength) + LogBufferLength = 0 + + l := make(Logger) + + // Delete and open the output log without a timestamp (for a constant md5sum) + l.AddFilter("file", FINEST, NewFileLogWriter(testLogFile, false).SetFormat("[%L] %M")) + defer os.Remove(testLogFile) + + // Send some log messages + l.Log(CRITICAL, "testsrc1", fmt.Sprintf("This message is level %d", int(CRITICAL))) + l.Logf(ERROR, "This message is level %v", ERROR) + l.Logf(WARNING, "This message is level %s", WARNING) + l.Logc(INFO, func() string { return "This message is level INFO" }) + l.Trace("This message is level %d", int(TRACE)) + l.Debug("This message is level %s", DEBUG) + l.Fine(func() string { return fmt.Sprintf("This message is level %v", FINE) }) + l.Finest("This message is level %v", FINEST) + l.Finest(FINEST, "is also this message's level") + + l.Close() + + contents, err := ioutil.ReadFile(testLogFile) + if err != nil { + t.Fatalf("Could not read output log: %s", err) + } + + sum := md5.New() + sum.Write(contents) + if sumstr := hex.EncodeToString(sum.Sum(nil)); sumstr != expected { + t.Errorf("--- Log Contents:\n%s---", string(contents)) + t.Fatalf("Checksum does not match: %s (expecting %s)", sumstr, expected) + } +} + +func TestCountMallocs(t *testing.T) { + const N = 1 + var m runtime.MemStats + getMallocs := func() uint64 { + runtime.ReadMemStats(&m) + return m.Mallocs + } + + // Console logger + sl := NewDefaultLogger(INFO) + mallocs := 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Log(WARNING, "here", "This is a WARNING message") + } + mallocs += getMallocs() + fmt.Printf("mallocs per sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N) + + // Console logger formatted + mallocs = 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Logf(WARNING, "%s is a log message with level %d", "This", WARNING) + } + mallocs += getMallocs() + fmt.Printf("mallocs per sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N) + + // Console logger (not logged) + sl = NewDefaultLogger(INFO) + mallocs = 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Log(DEBUG, "here", "This is a DEBUG log message") + } + mallocs += getMallocs() + fmt.Printf("mallocs per unlogged sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N) + + // Console logger formatted (not logged) + mallocs = 0 - getMallocs() + for i := 0; i < N; i++ { + sl.Logf(DEBUG, "%s is a log message with level %d", "This", DEBUG) + } + mallocs += getMallocs() + fmt.Printf("mallocs per unlogged sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N) +} + +func TestXMLConfig(t *testing.T) { + const ( + configfile = "example.xml" + ) + + fd, err := os.Create(configfile) + if err != nil { + t.Fatalf("Could not open %s for writing: %s", configfile, err) + } + + fmt.Fprintln(fd, "") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " stdout") + fmt.Fprintln(fd, " console") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " DEBUG") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " file") + fmt.Fprintln(fd, " file") + fmt.Fprintln(fd, " FINEST") + fmt.Fprintln(fd, " test.log") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " [%D %T] [%L] (%S) %M") + fmt.Fprintln(fd, " false ") + fmt.Fprintln(fd, " 0M ") + fmt.Fprintln(fd, " 0K ") + fmt.Fprintln(fd, " true ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " xmllog") + fmt.Fprintln(fd, " xml") + fmt.Fprintln(fd, " TRACE") + fmt.Fprintln(fd, " trace.xml") + fmt.Fprintln(fd, " true ") + fmt.Fprintln(fd, " 100M ") + fmt.Fprintln(fd, " 6K ") + fmt.Fprintln(fd, " false ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, " donotopen") + fmt.Fprintln(fd, " socket") + fmt.Fprintln(fd, " FINEST") + fmt.Fprintln(fd, " 192.168.1.255:12124 ") + fmt.Fprintln(fd, " udp ") + fmt.Fprintln(fd, " ") + fmt.Fprintln(fd, "") + fd.Close() + + log := make(Logger) + log.LoadConfiguration(configfile) + defer os.Remove("trace.xml") + defer os.Remove("test.log") + defer log.Close() + + // Make sure we got all loggers + if len(log) != 3 { + t.Fatalf("XMLConfig: Expected 3 filters, found %d", len(log)) + } + + // Make sure they're the right keys + if _, ok := log["stdout"]; !ok { + t.Errorf("XMLConfig: Expected stdout logger") + } + if _, ok := log["file"]; !ok { + t.Fatalf("XMLConfig: Expected file logger") + } + if _, ok := log["xmllog"]; !ok { + t.Fatalf("XMLConfig: Expected xmllog logger") + } + + // Make sure they're the right type + if _, ok := log["stdout"].LogWriter.(ConsoleLogWriter); !ok { + t.Fatalf("XMLConfig: Expected stdout to be ConsoleLogWriter, found %T", log["stdout"].LogWriter) + } + if _, ok := log["file"].LogWriter.(*FileLogWriter); !ok { + t.Fatalf("XMLConfig: Expected file to be *FileLogWriter, found %T", log["file"].LogWriter) + } + if _, ok := log["xmllog"].LogWriter.(*FileLogWriter); !ok { + t.Fatalf("XMLConfig: Expected xmllog to be *FileLogWriter, found %T", log["xmllog"].LogWriter) + } + + // Make sure levels are set + if lvl := log["stdout"].Level; lvl != DEBUG { + t.Errorf("XMLConfig: Expected stdout to be set to level %d, found %d", DEBUG, lvl) + } + if lvl := log["file"].Level; lvl != FINEST { + t.Errorf("XMLConfig: Expected file to be set to level %d, found %d", FINEST, lvl) + } + if lvl := log["xmllog"].Level; lvl != TRACE { + t.Errorf("XMLConfig: Expected xmllog to be set to level %d, found %d", TRACE, lvl) + } + + // Make sure the w is open and points to the right file + if fname := log["file"].LogWriter.(*FileLogWriter).file.Name(); fname != "test.log" { + t.Errorf("XMLConfig: Expected file to have opened %s, found %s", "test.log", fname) + } + + // Make sure the XLW is open and points to the right file + if fname := log["xmllog"].LogWriter.(*FileLogWriter).file.Name(); fname != "trace.xml" { + t.Errorf("XMLConfig: Expected xmllog to have opened %s, found %s", "trace.xml", fname) + } + + // Move XML log file + os.Rename(configfile, "examples/"+configfile) // Keep this so that an example with the documentation is available +} + +func BenchmarkFormatLogRecord(b *testing.B) { + const updateEvery = 1 + rec := &LogRecord{ + Level: CRITICAL, + Created: now, + Source: "source", + Message: "message", + } + for i := 0; i < b.N; i++ { + rec.Created = rec.Created.Add(1 * time.Second / updateEvery) + if i%2 == 0 { + FormatLogRecord(FORMAT_DEFAULT, rec) + } else { + FormatLogRecord(FORMAT_SHORT, rec) + } + } +} + +func BenchmarkConsoleLog(b *testing.B) { + /* This doesn't seem to work on OS X + sink, err := os.Open(os.DevNull) + if err != nil { + panic(err) + } + if err := syscall.Dup2(int(sink.Fd()), syscall.Stdout); err != nil { + panic(err) + } + */ + + stdout = ioutil.Discard + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Log(WARNING, "here", "This is a log message") + } +} + +func BenchmarkConsoleNotLogged(b *testing.B) { + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Log(DEBUG, "here", "This is a log message") + } +} + +func BenchmarkConsoleUtilLog(b *testing.B) { + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Info("%s is a log message", "This") + } +} + +func BenchmarkConsoleUtilNotLog(b *testing.B) { + sl := NewDefaultLogger(INFO) + for i := 0; i < b.N; i++ { + sl.Debug("%s is a log message", "This") + } +} + +func BenchmarkFileLog(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Log(WARNING, "here", "This is a log message") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +func BenchmarkFileNotLogged(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Log(DEBUG, "here", "This is a log message") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +func BenchmarkFileUtilLog(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Info("%s is a log message", "This") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +func BenchmarkFileUtilNotLog(b *testing.B) { + sl := make(Logger) + b.StopTimer() + sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false)) + b.StartTimer() + for i := 0; i < b.N; i++ { + sl.Debug("%s is a log message", "This") + } + b.StopTimer() + os.Remove("benchlog.log") +} + +// Benchmark results (darwin amd64 6g) +//elog.BenchmarkConsoleLog 100000 22819 ns/op +//elog.BenchmarkConsoleNotLogged 2000000 879 ns/op +//elog.BenchmarkConsoleUtilLog 50000 34380 ns/op +//elog.BenchmarkConsoleUtilNotLog 1000000 1339 ns/op +//elog.BenchmarkFileLog 100000 26497 ns/op +//elog.BenchmarkFileNotLogged 2000000 821 ns/op +//elog.BenchmarkFileUtilLog 50000 33945 ns/op +//elog.BenchmarkFileUtilNotLog 1000000 1258 ns/op diff --git a/vendor/github.com/braintree/manners/helpers_test.go b/vendor/github.com/braintree/manners/helpers_test.go new file mode 100644 index 000000000..3c11a081d --- /dev/null +++ b/vendor/github.com/braintree/manners/helpers_test.go @@ -0,0 +1,119 @@ +package manners + +import ( + "bufio" + "crypto/tls" + "io/ioutil" + "net" + "net/http" + "testing" +) + +// a simple step-controllable http client +type client struct { + tls bool + addr net.Addr + connected chan error + sendrequest chan bool + response chan *rawResponse + closed chan bool +} + +type rawResponse struct { + body []string + err error +} + +func (c *client) Run() { + go func() { + var err error + conn, err := net.Dial(c.addr.Network(), c.addr.String()) + if err != nil { + c.connected <- err + return + } + if c.tls { + conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) + } + c.connected <- nil + for <-c.sendrequest { + _, err = conn.Write([]byte("GET / HTTP/1.1\nHost: localhost:8000\n\n")) + if err != nil { + c.response <- &rawResponse{err: err} + } + // Read response; no content + scanner := bufio.NewScanner(conn) + var lines []string + for scanner.Scan() { + // our null handler doesn't send a body, so we know the request is + // done when we reach the blank line after the headers + line := scanner.Text() + if line == "" { + break + } + lines = append(lines, line) + } + c.response <- &rawResponse{lines, scanner.Err()} + } + conn.Close() + ioutil.ReadAll(conn) + c.closed <- true + }() +} + +func newClient(addr net.Addr, tls bool) *client { + return &client{ + addr: addr, + tls: tls, + connected: make(chan error), + sendrequest: make(chan bool), + response: make(chan *rawResponse), + closed: make(chan bool), + } +} + +// a handler that returns 200 ok with no body +var nullHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + +func startGenericServer(t *testing.T, server *GracefulServer, statechanged chan http.ConnState, runner func() error) (l net.Listener, errc chan error) { + server.Addr = "localhost:0" + server.Handler = nullHandler + if statechanged != nil { + // Wrap the ConnState handler with something that will notify + // the statechanged channel when a state change happens + server.ConnState = func(conn net.Conn, newState http.ConnState) { + statechanged <- newState + } + } + + server.up = make(chan net.Listener) + exitchan := make(chan error) + + go func() { + exitchan <- runner() + }() + + // wait for server socket to be bound + select { + case l = <-server.up: + // all good + + case err := <-exitchan: + // all bad + t.Fatal("Server failed to start", err) + } + return l, exitchan +} + +func startServer(t *testing.T, server *GracefulServer, statechanged chan http.ConnState) ( + l net.Listener, errc chan error) { + return startGenericServer(t, server, statechanged, server.ListenAndServe) +} + +func startTLSServer(t *testing.T, server *GracefulServer, certFile, keyFile string, statechanged chan http.ConnState) (l net.Listener, errc chan error) { + runner := func() error { + return server.ListenAndServeTLS(certFile, keyFile) + } + + return startGenericServer(t, server, statechanged, runner) +} diff --git a/vendor/github.com/braintree/manners/server_test.go b/vendor/github.com/braintree/manners/server_test.go new file mode 100644 index 000000000..994284216 --- /dev/null +++ b/vendor/github.com/braintree/manners/server_test.go @@ -0,0 +1,289 @@ +package manners + +import ( + "net" + "net/http" + "testing" + "time" + + helpers "github.com/braintree/manners/test_helpers" +) + +type httpInterface interface { + ListenAndServe() error + ListenAndServeTLS(certFile, keyFile string) error + Serve(listener net.Listener) error +} + +// Test that the method signatures of the methods we override from net/http/Server match those of the original. +func TestInterface(t *testing.T) { + var original, ours interface{} + original = &http.Server{} + ours = &GracefulServer{} + if _, ok := original.(httpInterface); !ok { + t.Errorf("httpInterface definition does not match the canonical server!") + } + if _, ok := ours.(httpInterface); !ok { + t.Errorf("GracefulServer does not implement httpInterface") + } +} + +// Tests that the server allows in-flight requests to complete +// before shutting down. +func TestGracefulness(t *testing.T) { + server := NewServer() + wg := helpers.NewWaitGroup() + server.wg = wg + statechanged := make(chan http.ConnState) + listener, exitchan := startServer(t, server, statechanged) + + client := newClient(listener.Addr(), false) + client.Run() + + // wait for client to connect, but don't let it send the request yet + if err := <-client.connected; err != nil { + t.Fatal("Client failed to connect to server", err) + } + // Even though the client is connected, the server ConnState handler may + // not know about that yet. So wait until it is called. + waitForState(t, statechanged, http.StateNew, "Request not received") + + server.Close() + + waiting := <-wg.WaitCalled + if waiting < 1 { + t.Errorf("Expected the waitgroup to equal 1 at shutdown; actually %d", waiting) + } + + // allow the client to finish sending the request and make sure the server exits after + // (client will be in connected but idle state at that point) + client.sendrequest <- true + close(client.sendrequest) + if err := <-exitchan; err != nil { + t.Error("Unexpected error during shutdown", err) + } +} + +// Tests that starting the server and closing in 2 new, separate goroutines doesnot +// get flagged by the race detector (need to run 'go test' w/the -race flag) +func TestRacyClose(t *testing.T) { + go func() { + ListenAndServe(":9000", nil) + }() + + go func() { + Close() + }() +} + +// Tests that the server begins to shut down when told to and does not accept +// new requests once shutdown has begun +func TestShutdown(t *testing.T) { + server := NewServer() + wg := helpers.NewWaitGroup() + server.wg = wg + statechanged := make(chan http.ConnState) + listener, exitchan := startServer(t, server, statechanged) + + client1 := newClient(listener.Addr(), false) + client1.Run() + + // wait for client1 to connect + if err := <-client1.connected; err != nil { + t.Fatal("Client failed to connect to server", err) + } + // Even though the client is connected, the server ConnState handler may + // not know about that yet. So wait until it is called. + waitForState(t, statechanged, http.StateNew, "Request not received") + + // start the shutdown; once it hits waitgroup.Wait() + // the listener should of been closed, though client1 is still connected + if server.Close() != true { + t.Fatal("first call to Close returned false") + } + if server.Close() != false { + t.Fatal("second call to Close returned true") + } + + waiting := <-wg.WaitCalled + if waiting != 1 { + t.Errorf("Waitcount should be one, got %d", waiting) + } + + // should get connection refused at this point + client2 := newClient(listener.Addr(), false) + client2.Run() + + if err := <-client2.connected; err == nil { + t.Fatal("client2 connected when it should of received connection refused") + } + + // let client1 finish so the server can exit + close(client1.sendrequest) // don't bother sending an actual request + + <-exitchan +} + +// If a request is sent to a closed server via a kept alive connection then +// the server closes the connection upon receiving the request. +func TestRequestAfterClose(t *testing.T) { + // Given + server := NewServer() + srvStateChangedCh := make(chan http.ConnState, 100) + listener, srvClosedCh := startServer(t, server, srvStateChangedCh) + + client := newClient(listener.Addr(), false) + client.Run() + <-client.connected + client.sendrequest <- true + <-client.response + + server.Close() + if err := <-srvClosedCh; err != nil { + t.Error("Unexpected error during shutdown", err) + } + + // When + client.sendrequest <- true + rr := <-client.response + + // Then + if rr.body != nil || rr.err != nil { + t.Errorf("Request should be rejected, body=%v, err=%v", rr.body, rr.err) + } +} + +func waitForState(t *testing.T, waiter chan http.ConnState, state http.ConnState, errmsg string) { + for { + select { + case ns := <-waiter: + if ns == state { + return + } + case <-time.After(time.Second): + t.Fatal(errmsg) + } + } +} + +// Test that a request moving from active->idle->active using an actual +// network connection still results in a corect shutdown +func TestStateTransitionActiveIdleActive(t *testing.T) { + server := NewServer() + wg := helpers.NewWaitGroup() + statechanged := make(chan http.ConnState) + server.wg = wg + listener, exitchan := startServer(t, server, statechanged) + + client := newClient(listener.Addr(), false) + client.Run() + + // wait for client to connect, but don't let it send the request + if err := <-client.connected; err != nil { + t.Fatal("Client failed to connect to server", err) + } + + for i := 0; i < 2; i++ { + client.sendrequest <- true + waitForState(t, statechanged, http.StateActive, "Client failed to reach active state") + <-client.response + waitForState(t, statechanged, http.StateIdle, "Client failed to reach idle state") + } + + // client is now in an idle state + + server.Close() + waiting := <-wg.WaitCalled + if waiting != 0 { + t.Errorf("Waitcount should be zero, got %d", waiting) + } + + if err := <-exitchan; err != nil { + t.Error("Unexpected error during shutdown", err) + } +} + +// Test state transitions from new->active->-idle->closed using an actual +// network connection and make sure the waitgroup count is correct at the end. +func TestStateTransitionActiveIdleClosed(t *testing.T) { + var ( + listener net.Listener + exitchan chan error + ) + + keyFile, err1 := helpers.NewTempFile(helpers.Key) + certFile, err2 := helpers.NewTempFile(helpers.Cert) + defer keyFile.Unlink() + defer certFile.Unlink() + + if err1 != nil || err2 != nil { + t.Fatal("Failed to create temporary files", err1, err2) + } + + for _, withTLS := range []bool{false, true} { + server := NewServer() + wg := helpers.NewWaitGroup() + statechanged := make(chan http.ConnState) + server.wg = wg + if withTLS { + listener, exitchan = startTLSServer(t, server, certFile.Name(), keyFile.Name(), statechanged) + } else { + listener, exitchan = startServer(t, server, statechanged) + } + + client := newClient(listener.Addr(), withTLS) + client.Run() + + // wait for client to connect, but don't let it send the request + if err := <-client.connected; err != nil { + t.Fatal("Client failed to connect to server", err) + } + + client.sendrequest <- true + waitForState(t, statechanged, http.StateActive, "Client failed to reach active state") + + rr := <-client.response + if rr.err != nil { + t.Fatalf("tls=%t unexpected error from client %s", withTLS, rr.err) + } + + waitForState(t, statechanged, http.StateIdle, "Client failed to reach idle state") + + // client is now in an idle state + close(client.sendrequest) + <-client.closed + waitForState(t, statechanged, http.StateClosed, "Client failed to reach closed state") + + server.Close() + waiting := <-wg.WaitCalled + if waiting != 0 { + t.Errorf("Waitcount should be zero, got %d", waiting) + } + + if err := <-exitchan; err != nil { + t.Error("Unexpected error during shutdown", err) + } + } +} + +func TestRoutinesCount(t *testing.T) { + var count int + server := NewServer() + + count = server.RoutinesCount() + if count != 0 { + t.Errorf("Expected the routines count to equal 0; actually %d", count) + } + + server.StartRoutine() + count = server.RoutinesCount() + if count != 1 { + t.Errorf("Expected the routines count to equal 1; actually %d", count) + } + + server.FinishRoutine() + count = server.RoutinesCount() + if count != 0 { + t.Errorf("Expected the routines count to equal 0; actually %d", count) + } +} diff --git a/vendor/github.com/braintree/manners/test_helpers/certs.go b/vendor/github.com/braintree/manners/test_helpers/certs.go new file mode 100644 index 000000000..ede248b3d --- /dev/null +++ b/vendor/github.com/braintree/manners/test_helpers/certs.go @@ -0,0 +1,29 @@ +package test_helpers + +// A PEM-encoded TLS cert with SAN IPs "127.0.0.1" and "[::1]", expiring at the +// last second of 2049 (the end of ASN.1 time). + +// generated from src/pkg/crypto/tls: +// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h +var ( + Cert = []byte(`-----BEGIN CERTIFICATE----- +MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD +bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj +bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa +IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA +AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud +EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA +AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk +Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA== +-----END CERTIFICATE-----`) + + Key = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0 +0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV +NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d +AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW +MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD +EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA +1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE= +-----END RSA PRIVATE KEY-----`) +) diff --git a/vendor/github.com/braintree/manners/test_helpers/conn.go b/vendor/github.com/braintree/manners/test_helpers/conn.go new file mode 100644 index 000000000..8c610f58e --- /dev/null +++ b/vendor/github.com/braintree/manners/test_helpers/conn.go @@ -0,0 +1,13 @@ +package test_helpers + +import "net" + +type Conn struct { + net.Conn + CloseCalled bool +} + +func (c *Conn) Close() error { + c.CloseCalled = true + return nil +} diff --git a/vendor/github.com/braintree/manners/test_helpers/listener.go b/vendor/github.com/braintree/manners/test_helpers/listener.go new file mode 100644 index 000000000..e3af35a6e --- /dev/null +++ b/vendor/github.com/braintree/manners/test_helpers/listener.go @@ -0,0 +1,34 @@ +package test_helpers + +import ( + "errors" + "net" +) + +type Listener struct { + AcceptRelease chan bool + CloseCalled chan bool +} + +func NewListener() *Listener { + return &Listener{ + make(chan bool, 1), + make(chan bool, 1), + } +} + +func (l *Listener) Addr() net.Addr { + addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8080") + return addr +} + +func (l *Listener) Close() error { + l.CloseCalled <- true + l.AcceptRelease <- true + return nil +} + +func (l *Listener) Accept() (net.Conn, error) { + <-l.AcceptRelease + return nil, errors.New("connection closed") +} diff --git a/vendor/github.com/braintree/manners/test_helpers/temp_file.go b/vendor/github.com/braintree/manners/test_helpers/temp_file.go new file mode 100644 index 000000000..c4aa263a0 --- /dev/null +++ b/vendor/github.com/braintree/manners/test_helpers/temp_file.go @@ -0,0 +1,27 @@ +package test_helpers + +import ( + "io/ioutil" + "os" +) + +type TempFile struct { + *os.File +} + +func NewTempFile(content []byte) (*TempFile, error) { + f, err := ioutil.TempFile("", "graceful-test") + if err != nil { + return nil, err + } + + f.Write(content) + return &TempFile{f}, nil +} + +func (tf *TempFile) Unlink() { + if tf.File != nil { + os.Remove(tf.Name()) + tf.File = nil + } +} diff --git a/vendor/github.com/braintree/manners/test_helpers/wait_group.go b/vendor/github.com/braintree/manners/test_helpers/wait_group.go new file mode 100644 index 000000000..1df590db7 --- /dev/null +++ b/vendor/github.com/braintree/manners/test_helpers/wait_group.go @@ -0,0 +1,33 @@ +package test_helpers + +import "sync" + +type WaitGroup struct { + sync.Mutex + Count int + WaitCalled chan int +} + +func NewWaitGroup() *WaitGroup { + return &WaitGroup{ + WaitCalled: make(chan int, 1), + } +} + +func (wg *WaitGroup) Add(delta int) { + wg.Lock() + wg.Count++ + wg.Unlock() +} + +func (wg *WaitGroup) Done() { + wg.Lock() + wg.Count-- + wg.Unlock() +} + +func (wg *WaitGroup) Wait() { + wg.Lock() + wg.WaitCalled <- wg.Count + wg.Unlock() +} diff --git a/vendor/github.com/braintree/manners/transition_test.go b/vendor/github.com/braintree/manners/transition_test.go new file mode 100644 index 000000000..5d398514e --- /dev/null +++ b/vendor/github.com/braintree/manners/transition_test.go @@ -0,0 +1,54 @@ +package manners + +import ( + helpers "github.com/braintree/manners/test_helpers" + "net/http" + "strings" + "testing" +) + +func TestStateTransitions(t *testing.T) { + tests := []transitionTest{ + transitionTest{[]http.ConnState{http.StateNew, http.StateActive}, 1}, + transitionTest{[]http.ConnState{http.StateNew, http.StateClosed}, 0}, + transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateClosed}, 0}, + transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateHijacked}, 0}, + transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle}, 0}, + transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle, http.StateActive}, 1}, + transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle, http.StateActive, http.StateIdle}, 0}, + transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle, http.StateActive, http.StateClosed}, 0}, + transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle, http.StateActive, http.StateIdle, http.StateClosed}, 0}, + } + + for _, test := range tests { + testStateTransition(t, test) + } +} + +type transitionTest struct { + states []http.ConnState + expectedWgCount int +} + +func testStateTransition(t *testing.T, test transitionTest) { + server := NewServer() + wg := helpers.NewWaitGroup() + server.wg = wg + startServer(t, server, nil) + + conn := &helpers.Conn{} + for _, newState := range test.states { + server.ConnState(conn, newState) + } + + server.Close() + waiting := <-wg.WaitCalled + if waiting != test.expectedWgCount { + names := make([]string, len(test.states)) + for i, s := range test.states { + names[i] = s.String() + } + transitions := strings.Join(names, " -> ") + t.Errorf("%s - Waitcount should be %d, got %d", transitions, test.expectedWgCount, waiting) + } +} diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/ci/scripts/windows-64-test.bat b/vendor/github.com/cloudfoundry/jibber_jabber/ci/scripts/windows-64-test.bat new file mode 100755 index 000000000..b9a87bf7a --- /dev/null +++ b/vendor/github.com/cloudfoundry/jibber_jabber/ci/scripts/windows-64-test.bat @@ -0,0 +1,5 @@ +git fetch +git checkout %GIT_COMMIT% + +SET GOPATH=%CD%\Godeps\_workspace;c:\Users\Administrator\go +c:\Go\bin\go test -v . diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_suite_test.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_suite_test.go new file mode 100644 index 000000000..3da19c84b --- /dev/null +++ b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_suite_test.go @@ -0,0 +1,13 @@ +package jibber_jabber_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestJibberJabber(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Jibber Jabber Suite") +} diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix_test.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix_test.go new file mode 100644 index 000000000..a5e3074a2 --- /dev/null +++ b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix_test.go @@ -0,0 +1,104 @@ +// +build darwin freebsd linux netbsd openbsd + +package jibber_jabber_test + +import ( + "os" + + . "github.com/cloudfoundry/jibber_jabber" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Unix", func() { + AfterEach(func() { + os.Setenv("LC_ALL", "") + os.Setenv("LANG", "en_US.UTF-8") + }) + + Describe("#DetectIETF", func() { + Context("Returns IETF encoded locale", func() { + It("should return the locale set to LC_ALL", func() { + os.Setenv("LC_ALL", "fr_FR.UTF-8") + result, _ := DetectIETF() + Ω(result).Should(Equal("fr-FR")) + }) + + It("should return the locale set to LANG if LC_ALL isn't set", func() { + os.Setenv("LANG", "fr_FR.UTF-8") + + result, _ := DetectIETF() + Ω(result).Should(Equal("fr-FR")) + }) + + It("should return an error if it cannot detect a locale", func() { + os.Setenv("LANG", "") + + _, err := DetectIETF() + Ω(err.Error()).Should(Equal(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE)) + }) + }) + + Context("when the locale is simply 'fr'", func() { + BeforeEach(func() { + os.Setenv("LANG", "fr") + }) + + It("should return the locale without a territory", func() { + language, err := DetectIETF() + Ω(err).ShouldNot(HaveOccurred()) + Ω(language).Should(Equal("fr")) + }) + }) + }) + + Describe("#DetectLanguage", func() { + Context("Returns encoded language", func() { + It("should return the language set to LC_ALL", func() { + os.Setenv("LC_ALL", "fr_FR.UTF-8") + result, _ := DetectLanguage() + Ω(result).Should(Equal("fr")) + }) + + It("should return the language set to LANG if LC_ALL isn't set", func() { + os.Setenv("LANG", "fr_FR.UTF-8") + + result, _ := DetectLanguage() + Ω(result).Should(Equal("fr")) + }) + + It("should return an error if it cannot detect a language", func() { + os.Setenv("LANG", "") + + _, err := DetectLanguage() + Ω(err.Error()).Should(Equal(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE)) + }) + }) + }) + + Describe("#DetectTerritory", func() { + Context("Returns encoded territory", func() { + It("should return the territory set to LC_ALL", func() { + os.Setenv("LC_ALL", "fr_FR.UTF-8") + result, _ := DetectTerritory() + Ω(result).Should(Equal("FR")) + }) + + It("should return the territory set to LANG if LC_ALL isn't set", func() { + os.Setenv("LANG", "fr_FR.UTF-8") + + result, _ := DetectTerritory() + Ω(result).Should(Equal("FR")) + }) + + It("should return an error if it cannot detect a territory", func() { + os.Setenv("LANG", "") + + _, err := DetectTerritory() + Ω(err.Error()).Should(Equal(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE)) + }) + }) + }) + +}) diff --git a/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows_test.go b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows_test.go new file mode 100644 index 000000000..f325d981e --- /dev/null +++ b/vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows_test.go @@ -0,0 +1,51 @@ +// +build windows + +package jibber_jabber_test + +import ( + "regexp" + + . "github.com/cloudfoundry/jibber_jabber" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +const ( + LOCALE_REGEXP = "^[a-z]{2}-[A-Z]{2}$" + LANGUAGE_REGEXP = "^[a-z]{2}$" + TERRITORY_REGEXP = "^[A-Z]{2}$" +) + +var _ = Describe("Windows", func() { + BeforeEach(func() { + locale, err := DetectIETF() + Ω(err).Should(BeNil()) + Ω(locale).ShouldNot(BeNil()) + Ω(locale).ShouldNot(Equal("")) + }) + + Describe("#DetectIETF", func() { + It("detects correct IETF locale", func() { + locale, _ := DetectIETF() + matched, _ := regexp.MatchString(LOCALE_REGEXP, locale) + Ω(matched).Should(BeTrue()) + }) + }) + + Describe("#DetectLanguage", func() { + It("detects correct Language", func() { + language, _ := DetectLanguage() + matched, _ := regexp.MatchString(LANGUAGE_REGEXP, language) + Ω(matched).Should(BeTrue()) + }) + }) + + Describe("#DetectTerritory", func() { + It("detects correct Territory", func() { + territory, _ := DetectTerritory() + matched, _ := regexp.MatchString(TERRITORY_REGEXP, territory) + Ω(matched).Should(BeTrue()) + }) + }) +}) diff --git a/vendor/github.com/dgryski/dgoogauth/googauth_test.go b/vendor/github.com/dgryski/dgoogauth/googauth_test.go new file mode 100644 index 000000000..031922c47 --- /dev/null +++ b/vendor/github.com/dgryski/dgoogauth/googauth_test.go @@ -0,0 +1,251 @@ +package dgoogauth + +import ( + "strconv" + "testing" + "time" +) + +// Test vectors via: +// http://code.google.com/p/google-authenticator/source/browse/libpam/pam_google_authenticator_unittest.c +// https://google-authenticator.googlecode.com/hg/libpam/totp.html + +var codeTests = []struct { + secret string + value int64 + code int +}{ + {"2SH3V3GDW7ZNMGYE", 1, 293240}, + {"2SH3V3GDW7ZNMGYE", 5, 932068}, + {"2SH3V3GDW7ZNMGYE", 10000, 50548}, +} + +func TestCode(t *testing.T) { + + for _, v := range codeTests { + c := ComputeCode(v.secret, v.value) + + if c != v.code { + t.Errorf("computeCode(%s, %d): got %d expected %d\n", v.secret, v.value, c, v.code) + } + + } +} + +func TestScratchCode(t *testing.T) { + + var cotp OTPConfig + + cotp.ScratchCodes = []int{11112222, 22223333} + + var scratchTests = []struct { + code int + result bool + }{ + {33334444, false}, + {11112222, true}, + {11112222, false}, + {22223333, true}, + {22223333, false}, + {33334444, false}, + } + + for _, s := range scratchTests { + r := cotp.checkScratchCodes(s.code) + if r != s.result { + t.Errorf("scratchcode(%d) failed: got %t expected %t", s.code, r, s.result) + } + } +} + +func TestHotpCode(t *testing.T) { + + var cotp OTPConfig + + // reuse our test values from above + // perhaps create more? + cotp.Secret = "2SH3V3GDW7ZNMGYE" + cotp.HotpCounter = 1 + cotp.WindowSize = 3 + + var counterCodes = []struct { + code int + result bool + counter int + }{ + { /* 1 */ 293240, true, 2}, // increments on success + { /* 1 */ 293240, false, 3}, // and failure + { /* 5 */ 932068, true, 6}, // inside of window + { /* 10 */ 481725, false, 7}, // outside of window + { /* 10 */ 481725, false, 8}, // outside of window + { /* 10 */ 481725, true, 11}, // now inside of window + } + + for i, s := range counterCodes { + r := cotp.checkHotpCode(s.code) + if r != s.result { + t.Errorf("counterCode(%d) (step %d) failed: got %t expected %t", s.code, i, r, s.result) + } + if cotp.HotpCounter != s.counter { + t.Errorf("hotpCounter incremented poorly: got %d expected %d", cotp.HotpCounter, s.counter) + } + } +} + +func TestTotpCode(t *testing.T) { + + var cotp OTPConfig + + // reuse our test values from above + cotp.Secret = "2SH3V3GDW7ZNMGYE" + cotp.WindowSize = 5 + + var windowTest = []struct { + code int + t0 int + result bool + }{ + {50548, 9997, false}, + {50548, 9998, true}, + {50548, 9999, true}, + {50548, 10000, true}, + {50548, 10001, true}, + {50548, 10002, true}, + {50548, 10003, false}, + } + + for i, s := range windowTest { + r := cotp.checkTotpCode(s.t0, s.code) + if r != s.result { + t.Errorf("counterCode(%d) (step %d) failed: got %t expected %t", s.code, i, r, s.result) + } + } + + cotp.DisallowReuse = make([]int, 0) + var noreuseTest = []struct { + code int + t0 int + result bool + disallowed []int + }{ + {50548 /* 10000 */, 9997, false, []int{}}, + {50548 /* 10000 */, 9998, true, []int{10000}}, + {50548 /* 10000 */, 9999, false, []int{10000}}, + {478726 /* 10001 */, 10001, true, []int{10000, 10001}}, + {646986 /* 10002 */, 10002, true, []int{10000, 10001, 10002}}, + {842639 /* 10003 */, 10003, true, []int{10001, 10002, 10003}}, + } + + for i, s := range noreuseTest { + r := cotp.checkTotpCode(s.t0, s.code) + if r != s.result { + t.Errorf("timeCode(%d) (step %d) failed: got %t expected %t", s.code, i, r, s.result) + } + if len(cotp.DisallowReuse) != len(s.disallowed) { + t.Errorf("timeCode(%d) (step %d) failed: disallowReuse len mismatch: got %d expected %d", s.code, i, len(cotp.DisallowReuse), len(s.disallowed)) + } else { + same := true + for j := range s.disallowed { + if s.disallowed[j] != cotp.DisallowReuse[j] { + same = false + } + } + if !same { + t.Errorf("timeCode(%d) (step %d) failed: disallowReused: got %v expected %v", s.code, i, cotp.DisallowReuse, s.disallowed) + } + } + } +} + +func TestAuthenticate(t *testing.T) { + + otpconf := &OTPConfig{ + Secret: "2SH3V3GDW7ZNMGYE", + WindowSize: 3, + HotpCounter: 1, + ScratchCodes: []int{11112222, 22223333}, + } + + type attempt struct { + code string + result bool + } + + var attempts = []attempt{ + {"foobar", false}, // not digits + {"1fooba", false}, // not valid number + {"1111111", false}, // bad length + { /* 1 */ "293240", true}, // hopt increments on success + { /* 1 */ "293240", false}, // hopt failure + {"33334444", false}, // scratch + {"11112222", true}, + {"11112222", false}, + } + + for _, a := range attempts { + r, _ := otpconf.Authenticate(a.code) + if r != a.result { + t.Errorf("bad result from code=%s: got %t expected %t\n", a.code, r, a.result) + } + } + + // let's check some time-based codes + otpconf.HotpCounter = 0 + // I haven't mocked the clock, so we'll just compute one + var t0 int64 + if otpconf.UTC { + t0 = int64(time.Now().UTC().Unix() / 30) + } else { + t0 = int64(time.Now().Unix() / 30) + } + c := ComputeCode(otpconf.Secret, t0) + + invalid := c + 1 + attempts = []attempt{ + {strconv.Itoa(invalid), false}, + {strconv.Itoa(c), true}, + } + + for _, a := range attempts { + r, _ := otpconf.Authenticate(a.code) + if r != a.result { + t.Errorf("bad result from code=%s: got %t expected %t\n", a.code, r, a.result) + } + + otpconf.UTC = true + r, _ = otpconf.Authenticate(a.code) + if r != a.result { + t.Errorf("bad result from code=%s: got %t expected %t\n", a.code, r, a.result) + } + otpconf.UTC = false + } + +} + +func TestProvisionURI(t *testing.T) { + otpconf := OTPConfig{ + Secret: "x", + } + + cases := []struct { + user, iss string + hotp bool + out string + }{ + {"test", "", false, "otpauth://totp/test?secret=x"}, + {"test", "", true, "otpauth://hotp/test?counter=1&secret=x"}, + {"test", "Company", true, "otpauth://hotp/Company:test?counter=1&issuer=Company&secret=x"}, + {"test", "Company", false, "otpauth://totp/Company:test?issuer=Company&secret=x"}, + } + + for i, c := range cases { + otpconf.HotpCounter = 0 + if c.hotp { + otpconf.HotpCounter = 1 + } + got := otpconf.ProvisionURIWithIssuer(c.user, c.iss) + if got != c.out { + t.Errorf("%d: want %q, got %q", i, c.out, got) + } + } +} diff --git a/vendor/github.com/disintegration/imaging/adjust_test.go b/vendor/github.com/disintegration/imaging/adjust_test.go new file mode 100644 index 000000000..99898b0dc --- /dev/null +++ b/vendor/github.com/disintegration/imaging/adjust_test.go @@ -0,0 +1,504 @@ +package imaging + +import ( + "image" + "testing" +) + +func TestGrayscale(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "Grayscale 3x3", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x3d, 0x3d, 0x3d, 0x01, 0x78, 0x78, 0x78, 0x02, 0x17, 0x17, 0x17, 0x03, + 0x1f, 0x1f, 0x1f, 0xff, 0x25, 0x25, 0x25, 0xff, 0x66, 0x66, 0x66, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + } + for _, d := range td { + got := Grayscale(d.src) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestInvert(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "Invert 3x3", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x33, 0xff, 0xff, 0x01, 0xff, 0x33, 0xff, 0x02, 0xff, 0xff, 0x33, 0x03, + 0xee, 0xdd, 0xcc, 0xff, 0xcc, 0xdd, 0xee, 0xff, 0x55, 0xcc, 0x44, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0x00, 0x00, 0x00, 0xff, + }, + }, + }, + } + for _, d := range td { + got := Invert(d.src) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestAdjustContrast(t *testing.T) { + td := []struct { + desc string + src image.Image + p float64 + want *image.NRGBA + }{ + { + "AdjustContrast 3x3 10", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 10, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xd5, 0x00, 0x00, 0x01, 0x00, 0xd5, 0x00, 0x02, 0x00, 0x00, 0xd5, 0x03, + 0x05, 0x18, 0x2b, 0xff, 0x2b, 0x18, 0x05, 0xff, 0xaf, 0x2b, 0xc2, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x2b, 0x2b, 0x2b, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + { + "AdjustContrast 3x3 100", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 100, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0x00, 0x02, 0x00, 0x00, 0xff, 0x03, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + { + "AdjustContrast 3x3 -10", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + -10, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xc4, 0x0d, 0x0d, 0x01, 0x0d, 0xc4, 0x0d, 0x02, 0x0d, 0x0d, 0xc4, 0x03, + 0x1c, 0x2b, 0x3b, 0xff, 0x3b, 0x2b, 0x1c, 0xff, 0xa6, 0x3b, 0xb5, 0xff, + 0x0d, 0x0d, 0x0d, 0xff, 0x3b, 0x3b, 0x3b, 0xff, 0xf2, 0xf2, 0xf2, 0xff, + }, + }, + }, + { + "AdjustContrast 3x3 -100", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + -100, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x80, 0x80, 0x80, 0x01, 0x80, 0x80, 0x80, 0x02, 0x80, 0x80, 0x80, 0x03, + 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0xff, + 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0xff, 0x80, 0x80, 0x80, 0xff, + }, + }, + }, + { + "AdjustContrast 3x3 0", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 0, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + } + for _, d := range td { + got := AdjustContrast(d.src, d.p) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestAdjustBrightness(t *testing.T) { + td := []struct { + desc string + src image.Image + p float64 + want *image.NRGBA + }{ + { + "AdjustBrightness 3x3 10", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 10, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xe6, 0x1a, 0x1a, 0x01, 0x1a, 0xe6, 0x1a, 0x02, 0x1a, 0x1a, 0xe6, 0x03, + 0x2b, 0x3c, 0x4d, 0xff, 0x4d, 0x3c, 0x2b, 0xff, 0xc4, 0x4d, 0xd5, 0xff, + 0x1a, 0x1a, 0x1a, 0xff, 0x4d, 0x4d, 0x4d, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + { + "AdjustBrightness 3x3 100", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 100, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, 0x03, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + { + "AdjustBrightness 3x3 -10", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + -10, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xb3, 0x00, 0x00, 0x01, 0x00, 0xb3, 0x00, 0x02, 0x00, 0x00, 0xb3, 0x03, + 0x00, 0x09, 0x1a, 0xff, 0x1a, 0x09, 0x00, 0xff, 0x91, 0x1a, 0xa2, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x1a, 0x1a, 0x1a, 0xff, 0xe6, 0xe6, 0xe6, 0xff, + }, + }, + }, + { + "AdjustBrightness 3x3 -100", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + -100, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + }, + }, + }, + { + "AdjustBrightness 3x3 0", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 0, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + } + for _, d := range td { + got := AdjustBrightness(d.src, d.p) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestAdjustGamma(t *testing.T) { + td := []struct { + desc string + src image.Image + p float64 + want *image.NRGBA + }{ + { + "AdjustGamma 3x3 0.75", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 0.75, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xbd, 0x00, 0x00, 0x01, 0x00, 0xbd, 0x00, 0x02, 0x00, 0x00, 0xbd, 0x03, + 0x07, 0x11, 0x1e, 0xff, 0x1e, 0x11, 0x07, 0xff, 0x95, 0x1e, 0xa9, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x1e, 0x1e, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + { + "AdjustGamma 3x3 1.5", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 1.5, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xdc, 0x00, 0x00, 0x01, 0x00, 0xdc, 0x00, 0x02, 0x00, 0x00, 0xdc, 0x03, + 0x2a, 0x43, 0x57, 0xff, 0x57, 0x43, 0x2a, 0xff, 0xc3, 0x57, 0xcf, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x57, 0x57, 0x57, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + { + "AdjustGamma 3x3 1.0", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 1.0, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + } + for _, d := range td { + got := AdjustGamma(d.src, d.p) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestAdjustSigmoid(t *testing.T) { + td := []struct { + desc string + src image.Image + m float64 + p float64 + want *image.NRGBA + }{ + { + "AdjustSigmoid 3x3 0.5 3.0", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 0.5, + 3.0, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xd4, 0x00, 0x00, 0x01, 0x00, 0xd4, 0x00, 0x02, 0x00, 0x00, 0xd4, 0x03, + 0x0d, 0x1b, 0x2b, 0xff, 0x2b, 0x1b, 0x0d, 0xff, 0xb1, 0x2b, 0xc3, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x2b, 0x2b, 0x2b, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + { + "AdjustSigmoid 3x3 0.5 -3.0", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 0.5, + -3.0, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xc4, 0x00, 0x00, 0x01, 0x00, 0xc4, 0x00, 0x02, 0x00, 0x00, 0xc4, 0x03, + 0x16, 0x2a, 0x3b, 0xff, 0x3b, 0x2a, 0x16, 0xff, 0xa4, 0x3b, 0xb3, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x3b, 0x3b, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + { + "AdjustSigmoid 3x3 0.5 0.0", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 0.5, + 0.0, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0x00, 0x00, 0x01, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x00, 0xcc, 0x03, + 0x11, 0x22, 0x33, 0xff, 0x33, 0x22, 0x11, 0xff, 0xaa, 0x33, 0xbb, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + } + for _, d := range td { + got := AdjustSigmoid(d.src, d.m, d.p) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} diff --git a/vendor/github.com/disintegration/imaging/effects_test.go b/vendor/github.com/disintegration/imaging/effects_test.go new file mode 100644 index 000000000..a7e8cfffe --- /dev/null +++ b/vendor/github.com/disintegration/imaging/effects_test.go @@ -0,0 +1,190 @@ +package imaging + +import ( + "image" + "testing" +) + +func TestBlur(t *testing.T) { + td := []struct { + desc string + src image.Image + sigma float64 + want *image.NRGBA + }{ + { + "Blur 3x3 0", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, + 0.0, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, + }, + { + "Blur 3x3 0.5", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, + 0.5, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x01, 0x02, 0x04, 0x04, 0x0a, 0x10, 0x18, 0x18, 0x01, 0x02, 0x04, 0x04, + 0x09, 0x10, 0x18, 0x18, 0x3f, 0x69, 0x9e, 0x9e, 0x09, 0x10, 0x18, 0x18, + 0x01, 0x02, 0x04, 0x04, 0x0a, 0x10, 0x18, 0x18, 0x01, 0x02, 0x04, 0x04, + }, + }, + }, + { + "Blur 3x3 10", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, + 10, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, + 0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, + 0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, + }, + }, + }, + } + for _, d := range td { + got := Blur(d.src, d.sigma) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestSharpen(t *testing.T) { + td := []struct { + desc string + src image.Image + sigma float64 + want *image.NRGBA + }{ + { + "Sharpen 3x3 0", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + }, + }, + 0, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + }, + }, + }, + { + "Sharpen 3x3 0.5", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + }, + }, + 0.5, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x66, 0x66, 0x66, 0x66, 0x64, 0x64, 0x64, 0x64, 0x66, 0x66, 0x66, 0x66, + 0x64, 0x64, 0x64, 0x64, 0x7e, 0x7e, 0x7e, 0x7e, 0x64, 0x64, 0x64, 0x64, + 0x66, 0x66, 0x66, 0x66, 0x64, 0x64, 0x64, 0x64, 0x66, 0x66, 0x66, 0x66, + }, + }, + }, + { + "Sharpen 3x3 100", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, 0x77, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + }, + }, + 100, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x86, 0x86, 0x86, 0x86, 0x64, 0x64, 0x64, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, + }, + }, + }, + { + "Sharpen 3x1 10", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 0), + Stride: 3 * 4, + Pix: []uint8{ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + }, + }, + 10, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 1), + Stride: 3 * 4, + Pix: []uint8{ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + }, + }, + }, + } + for _, d := range td { + got := Sharpen(d.src, d.sigma) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} diff --git a/vendor/github.com/disintegration/imaging/helpers_test.go b/vendor/github.com/disintegration/imaging/helpers_test.go new file mode 100644 index 000000000..1287ec588 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/helpers_test.go @@ -0,0 +1,399 @@ +package imaging + +import ( + "bytes" + "image" + "image/color" + "testing" +) + +func compareNRGBA(img1, img2 *image.NRGBA, delta int) bool { + if !img1.Rect.Eq(img2.Rect) { + return false + } + + if len(img1.Pix) != len(img2.Pix) { + return false + } + + for i := 0; i < len(img1.Pix); i++ { + if absint(int(img1.Pix[i])-int(img2.Pix[i])) > delta { + return false + } + } + + return true +} + +func TestEncodeDecode(t *testing.T) { + imgWithAlpha := image.NewNRGBA(image.Rect(0, 0, 3, 3)) + imgWithAlpha.Pix = []uint8{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 244, 245, 246, 247, 248, 249, 250, 252, 252, 253, 254, 255, + } + + imgWithoutAlpha := image.NewNRGBA(image.Rect(0, 0, 3, 3)) + imgWithoutAlpha.Pix = []uint8{ + 0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 10, 255, + 127, 128, 129, 255, 131, 132, 133, 255, 135, 136, 137, 255, + 244, 245, 246, 255, 248, 249, 250, 255, 252, 253, 254, 255, + } + + for _, format := range []Format{JPEG, PNG, GIF, BMP, TIFF} { + img := imgWithoutAlpha + if format == PNG { + img = imgWithAlpha + } + + buf := &bytes.Buffer{} + err := Encode(buf, img, format) + if err != nil { + t.Errorf("fail encoding format %s", format) + continue + } + + img2, err := Decode(buf) + if err != nil { + t.Errorf("fail decoding format %s", format) + continue + } + img2cloned := Clone(img2) + + delta := 0 + if format == JPEG { + delta = 3 + } else if format == GIF { + delta = 16 + } + + if !compareNRGBA(img, img2cloned, delta) { + t.Errorf("test [DecodeEncode %s] failed: %#v %#v", format, img, img2cloned) + continue + } + } + + buf := &bytes.Buffer{} + err := Encode(buf, imgWithAlpha, JPEG) + if err != nil { + t.Errorf("failed encoding alpha to JPEG format %s", err) + } + + buf = &bytes.Buffer{} + err = Encode(buf, imgWithAlpha, Format(100)) + if err != ErrUnsupportedFormat { + t.Errorf("expected ErrUnsupportedFormat") + } + + buf = bytes.NewBuffer([]byte("bad data")) + _, err = Decode(buf) + if err == nil { + t.Errorf("decoding bad data, expected error") + } +} + +func TestNew(t *testing.T) { + td := []struct { + desc string + w, h int + c color.Color + dstBounds image.Rectangle + dstPix []uint8 + }{ + { + "New 1x1 black", + 1, 1, + color.NRGBA{0, 0, 0, 0}, + image.Rect(0, 0, 1, 1), + []uint8{0x00, 0x00, 0x00, 0x00}, + }, + { + "New 1x2 red", + 1, 2, + color.NRGBA{255, 0, 0, 255}, + image.Rect(0, 0, 1, 2), + []uint8{0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff}, + }, + { + "New 2x1 white", + 2, 1, + color.NRGBA{255, 255, 255, 255}, + image.Rect(0, 0, 2, 1), + []uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + }, + { + "New 0x0 white", + 0, 0, + color.NRGBA{255, 255, 255, 255}, + image.Rect(0, 0, 0, 0), + nil, + }, + } + + for _, d := range td { + got := New(d.w, d.h, d.c) + want := image.NewNRGBA(d.dstBounds) + want.Pix = d.dstPix + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestClone(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "Clone NRGBA", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + }, + { + "Clone NRGBA64", + &image.NRGBA64{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 8, + Pix: []uint8{ + 0x00, 0x00, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, + 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xff, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + }, + { + "Clone RGBA", + &image.RGBA{ + Rect: image.Rect(-1, -1, 0, 2), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 3), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + }, + { + "Clone RGBA64", + &image.RGBA64{ + Rect: image.Rect(-1, -1, 0, 2), + Stride: 1 * 8, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, + 0xcc, 0xcc, 0xdd, 0xdd, 0xee, 0xee, 0xff, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 3), + Stride: 1 * 4, + Pix: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x33, 0xcc, 0xdd, 0xee, 0xff}, + }, + }, + { + "Clone Gray", + &image.Gray{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 1, + Pix: []uint8{0x11, 0xee}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0x11, 0x11, 0x11, 0xff, 0xee, 0xee, 0xee, 0xff}, + }, + }, + { + "Clone Gray16", + &image.Gray16{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 2, + Pix: []uint8{0x11, 0x11, 0xee, 0xee}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0x11, 0x11, 0x11, 0xff, 0xee, 0xee, 0xee, 0xff}, + }, + }, + { + "Clone Alpha", + &image.Alpha{ + Rect: image.Rect(-1, -1, 0, 1), + Stride: 1 * 1, + Pix: []uint8{0x11, 0xee}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 2), + Stride: 1 * 4, + Pix: []uint8{0xff, 0xff, 0xff, 0x11, 0xff, 0xff, 0xff, 0xee}, + }, + }, + { + "Clone YCbCr", + &image.YCbCr{ + Rect: image.Rect(-1, -1, 5, 0), + SubsampleRatio: image.YCbCrSubsampleRatio444, + YStride: 6, + CStride: 6, + Y: []uint8{0x00, 0xff, 0x7f, 0x26, 0x4b, 0x0e}, + Cb: []uint8{0x80, 0x80, 0x80, 0x6b, 0x56, 0xc0}, + Cr: []uint8{0x80, 0x80, 0x80, 0xc0, 0x4b, 0x76}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 6, 1), + Stride: 6 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x7f, 0x7f, 0x7f, 0xff, + 0x7f, 0x00, 0x00, 0xff, + 0x00, 0x7f, 0x00, 0xff, + 0x00, 0x00, 0x7f, 0xff, + }, + }, + }, + { + "Clone YCbCr 444", + &image.YCbCr{ + Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, + Cb: []uint8{0x55, 0xd4, 0xff, 0x8e, 0x2c, 0x01, 0x6b, 0xaa, 0xc0, 0x95, 0x56, 0x40, 0x80, 0x80, 0x80, 0x80}, + Cr: []uint8{0xff, 0xeb, 0x6b, 0x36, 0x15, 0x95, 0xc0, 0xb5, 0x76, 0x41, 0x4b, 0x8c, 0x80, 0x80, 0x80, 0x80}, + YStride: 4, + CStride: 4, + SubsampleRatio: image.YCbCrSubsampleRatio444, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + &image.NRGBA{ + Pix: []uint8{0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x49, 0xe1, 0xca, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0x0, 0xff, 0x7f, 0x0, 0x0, 0xff, 0x7f, 0x0, 0x7f, 0xff, 0x0, 0x0, 0x7f, 0xff, 0x0, 0x7f, 0x7f, 0xff, 0x0, 0x7f, 0x0, 0xff, 0x82, 0x7f, 0x0, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, + Stride: 16, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + }, + { + "Clone YCbCr 440", + &image.YCbCr{ + Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, + Cb: []uint8{0x2c, 0x01, 0x6b, 0xaa, 0x80, 0x80, 0x80, 0x80}, + Cr: []uint8{0x15, 0x95, 0xc0, 0xb5, 0x80, 0x80, 0x80, 0x80}, + YStride: 4, + CStride: 4, + SubsampleRatio: image.YCbCrSubsampleRatio440, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + &image.NRGBA{ + Pix: []uint8{0x0, 0xb5, 0x0, 0xff, 0x86, 0x86, 0x0, 0xff, 0x77, 0x0, 0x0, 0xff, 0xfb, 0x7d, 0xfb, 0xff, 0x0, 0xff, 0x1, 0xff, 0xff, 0xff, 0x1, 0xff, 0x80, 0x0, 0x1, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0xe, 0xe, 0xe, 0xff, 0x59, 0x59, 0x59, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0x71, 0x71, 0x71, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, + Stride: 16, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + }, + { + "Clone YCbCr 422", + &image.YCbCr{ + Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, + Cb: []uint8{0xd4, 0x8e, 0x01, 0xaa, 0x95, 0x40, 0x80, 0x80}, + Cr: []uint8{0xeb, 0x36, 0x95, 0xb5, 0x41, 0x8c, 0x80, 0x80}, + YStride: 4, + CStride: 2, + SubsampleRatio: image.YCbCrSubsampleRatio422, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + &image.NRGBA{ + Pix: []uint8{0xe2, 0x0, 0xe1, 0xff, 0xff, 0x0, 0xfe, 0xff, 0x0, 0x4d, 0x36, 0xff, 0x49, 0xe1, 0xca, 0xff, 0xb3, 0xb3, 0x0, 0xff, 0xff, 0xff, 0x1, 0xff, 0x70, 0x0, 0x70, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0x0, 0x34, 0x33, 0xff, 0x1, 0x7f, 0x7e, 0xff, 0x5c, 0x58, 0x0, 0xff, 0x82, 0x7e, 0x0, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, + Stride: 16, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + }, + { + "Clone YCbCr 420", + &image.YCbCr{ + Y: []uint8{0x4c, 0x69, 0x1d, 0xb1, 0x96, 0xe2, 0x26, 0x34, 0xe, 0x59, 0x4b, 0x71, 0x0, 0x4c, 0x99, 0xff}, + Cb: []uint8{0x01, 0xaa, 0x80, 0x80}, + Cr: []uint8{0x95, 0xb5, 0x80, 0x80}, + YStride: 4, CStride: 2, + SubsampleRatio: image.YCbCrSubsampleRatio420, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + &image.NRGBA{ + Pix: []uint8{0x69, 0x69, 0x0, 0xff, 0x86, 0x86, 0x0, 0xff, 0x67, 0x0, 0x67, 0xff, 0xfb, 0x7d, 0xfb, 0xff, 0xb3, 0xb3, 0x0, 0xff, 0xff, 0xff, 0x1, 0xff, 0x70, 0x0, 0x70, 0xff, 0x7e, 0x0, 0x7e, 0xff, 0xe, 0xe, 0xe, 0xff, 0x59, 0x59, 0x59, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0x71, 0x71, 0x71, 0xff, 0x0, 0x0, 0x0, 0xff, 0x4c, 0x4c, 0x4c, 0xff, 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff}, + Stride: 16, + Rect: image.Rectangle{Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: 4, Y: 4}}, + }, + }, + { + "Clone Paletted", + &image.Paletted{ + Rect: image.Rect(-1, -1, 5, 0), + Stride: 6 * 1, + Palette: color.Palette{ + color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xff}, + color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}, + color.NRGBA{R: 0x7f, G: 0x7f, B: 0x7f, A: 0xff}, + color.NRGBA{R: 0x7f, G: 0x00, B: 0x00, A: 0xff}, + color.NRGBA{R: 0x00, G: 0x7f, B: 0x00, A: 0xff}, + color.NRGBA{R: 0x00, G: 0x00, B: 0x7f, A: 0xff}, + }, + Pix: []uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5}, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 6, 1), + Stride: 6 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x7f, 0x7f, 0x7f, 0xff, + 0x7f, 0x00, 0x00, 0xff, + 0x00, 0x7f, 0x00, 0xff, + 0x00, 0x00, 0x7f, 0xff, + }, + }, + }, + } + + for _, d := range td { + got := Clone(d.src) + want := d.want + + delta := 0 + if _, ok := d.src.(*image.YCbCr); ok { + delta = 1 + } + + if !compareNRGBA(got, want, delta) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestFormats(t *testing.T) { + formatNames := map[Format]string{ + JPEG: "JPEG", + PNG: "PNG", + GIF: "GIF", + BMP: "BMP", + TIFF: "TIFF", + Format(-1): "Unsupported", + } + for format, name := range formatNames { + got := format.String() + if got != name { + t.Errorf("test [Format names] failed: got %#v want %#v", got, name) + continue + } + } +} diff --git a/vendor/github.com/disintegration/imaging/resize_test.go b/vendor/github.com/disintegration/imaging/resize_test.go new file mode 100644 index 000000000..08d7f2d85 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/resize_test.go @@ -0,0 +1,570 @@ +package imaging + +import ( + "image" + "testing" +) + +func TestResize(t *testing.T) { + td := []struct { + desc string + src image.Image + w, h int + f ResampleFilter + want *image.NRGBA + }{ + { + "Resize 2x2 1x1 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 1, 1, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 1), + Stride: 1 * 4, + Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, + }, + }, + { + "Resize 2x2 2x2 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 2, 2, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + }, + { + "Resize 3x1 1x1 nearest", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 0), + Stride: 3 * 4, + Pix: []uint8{ + 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 1, 1, + NearestNeighbor, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 1), + Stride: 1 * 4, + Pix: []uint8{0x00, 0xff, 0x00, 0xff}, + }, + }, + { + "Resize 2x2 0x4 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 0, 4, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 4, 4), + Stride: 4 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + }, + { + "Resize 2x2 4x0 linear", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 4, 0, + Linear, + &image.NRGBA{ + Rect: image.Rect(0, 0, 4, 4), + Stride: 4 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0xbf, 0x00, 0x00, 0xbf, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x40, 0x00, 0x40, 0x30, 0x30, 0x10, 0x70, 0x8f, 0x10, 0x30, 0xcf, 0xbf, 0x00, 0x40, 0xff, + 0x00, 0xbf, 0x00, 0xbf, 0x10, 0x8f, 0x30, 0xcf, 0x30, 0x30, 0x8f, 0xef, 0x40, 0x00, 0xbf, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xbf, 0x40, 0xff, 0x00, 0x40, 0xbf, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + }, + { + "Resize 0x0 1x1 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, -1, -1), + Stride: 0, + Pix: []uint8{}, + }, + 1, 1, + Box, + &image.NRGBA{}, + }, + { + "Resize 2x2 0x0 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 0, 0, + Box, + &image.NRGBA{}, + }, + { + "Resize 2x2 -1x0 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + -1, 0, + Box, + &image.NRGBA{}, + }, + } + for _, d := range td { + got := Resize(d.src, d.w, d.h, d.f) + want := d.want + if !compareNRGBA(got, want, 1) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } + + for i, filter := range []ResampleFilter{ + NearestNeighbor, + Box, + Linear, + Hermite, + MitchellNetravali, + CatmullRom, + BSpline, + Gaussian, + Lanczos, + Hann, + Hamming, + Blackman, + Bartlett, + Welch, + Cosine, + } { + src := image.NewNRGBA(image.Rect(-1, -1, 2, 3)) + got := Resize(src, 5, 6, filter) + want := image.NewNRGBA(image.Rect(0, 0, 5, 6)) + if !compareNRGBA(got, want, 0) { + t.Errorf("test [Resize all filters #%d] failed: %#v", i, got) + } + + if filter.Kernel != nil { + x := filter.Kernel(filter.Support + 0.0001) + if x != 0 { + t.Errorf("test [ResampleFilter edge cases #%d] failed: %f", i, x) + } + } + } + + bcs2 := bcspline(2, 1, 0) + if bcs2 != 0 { + t.Errorf("test [bcspline 2] failed: %f", bcs2) + } +} + +func TestFit(t *testing.T) { + td := []struct { + desc string + src image.Image + w, h int + f ResampleFilter + want *image.NRGBA + }{ + { + "Fit 2x2 1x10 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 1, 10, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 1), + Stride: 1 * 4, + Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, + }, + }, + { + "Fit 2x2 10x1 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 10, 1, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 1), + Stride: 1 * 4, + Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, + }, + }, + { + "Fit 2x2 10x10 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 10, 10, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + }, + { + "Fit 0x0 1x1 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, -1, -1), + Stride: 0, + Pix: []uint8{}, + }, + 1, 1, + Box, + &image.NRGBA{}, + }, + { + "Fit 2x2 0x0 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + 0, 0, + Box, + &image.NRGBA{}, + }, + { + "Fit 2x2 -1x0 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + }, + }, + -1, 0, + Box, + &image.NRGBA{}, + }, + } + for _, d := range td { + got := Fit(d.src, d.w, d.h, d.f) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestFill(t *testing.T) { + td := []struct { + desc string + src image.Image + w, h int + a Anchor + f ResampleFilter + want *image.NRGBA + }{ + { + "Fill 4x4 2x2 Center Nearest", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + Center, + NearestNeighbor, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x14, 0x15, 0x16, 0x17, 0x1c, 0x1d, 0x1e, 0x1f, + 0x34, 0x35, 0x36, 0x37, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + }, + { + "Fill 4x4 1x4 TopLeft Nearest", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 1, 4, + TopLeft, + NearestNeighbor, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 4), + Stride: 1 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x02, 0x03, + 0x10, 0x11, 0x12, 0x13, + 0x20, 0x21, 0x22, 0x23, + 0x30, 0x31, 0x32, 0x33, + }, + }, + }, + { + "Fill 4x4 8x2 Bottom Nearest", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 8, 2, + Bottom, + NearestNeighbor, + &image.NRGBA{ + Rect: image.Rect(0, 0, 8, 2), + Stride: 8 * 4, + Pix: []uint8{ + 0x30, 0x31, 0x32, 0x33, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x3c, 0x3d, 0x3e, 0x3f, + 0x30, 0x31, 0x32, 0x33, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + }, + { + "Fill 4x4 2x8 Top Nearest", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 8, + Top, + NearestNeighbor, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 8), + Stride: 2 * 4, + Pix: []uint8{ + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + }, + }, + }, + { + "Fill 4x4 4x4 TopRight Box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 4, 4, + TopRight, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 4, 4), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + }, + { + "Fill 4x4 0x4 Left Box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 0, 4, + Left, + Box, + &image.NRGBA{}, + }, + { + "Fill 0x0 4x4 Right Box", + &image.NRGBA{}, + 4, 4, + Right, + Box, + &image.NRGBA{}, + }, + } + for _, d := range td { + got := Fill(d.src, d.w, d.h, d.a, d.f) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestThumbnail(t *testing.T) { + td := []struct { + desc string + src image.Image + w, h int + f ResampleFilter + want *image.NRGBA + }{ + { + "Thumbnail 6x2 1x1 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 5, 1), + Stride: 6 * 4, + Pix: []uint8{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 1, 1, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 1), + Stride: 1 * 4, + Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, + }, + }, + { + "Thumbnail 2x6 1x1 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 5), + Stride: 2 * 4, + Pix: []uint8{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + 1, 1, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 1), + Stride: 1 * 4, + Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, + }, + }, + { + "Thumbnail 1x3 2x2 box", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 0, 2), + Stride: 1 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, + }, + }, + 2, 2, + Box, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + }, + }, + }, + } + for _, d := range td { + got := Thumbnail(d.src, d.w, d.h, d.f) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} diff --git a/vendor/github.com/disintegration/imaging/tools_test.go b/vendor/github.com/disintegration/imaging/tools_test.go new file mode 100644 index 000000000..2ac7b31d3 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/tools_test.go @@ -0,0 +1,652 @@ +package imaging + +import ( + "image" + "testing" +) + +func TestCrop(t *testing.T) { + td := []struct { + desc string + src image.Image + r image.Rectangle + want *image.NRGBA + }{ + { + "Crop 2x3 2x1", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + image.Rect(-1, 0, 1, 1), + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + }, + }, + }, + } + for _, d := range td { + got := Crop(d.src, d.r) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestCropCenter(t *testing.T) { + td := []struct { + desc string + src image.Image + w, h int + want *image.NRGBA + }{ + { + "CropCenter 2x3 2x1", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + 2, 1, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + }, + }, + }, + { + "CropCenter 2x3 0x1", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + 0, 1, + &image.NRGBA{ + Rect: image.Rect(0, 0, 0, 0), + Stride: 0, + Pix: []uint8{}, + }, + }, + { + "CropCenter 2x3 5x5", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + 5, 5, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 3), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + }, + } + for _, d := range td { + got := CropCenter(d.src, d.w, d.h) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestCropAnchor(t *testing.T) { + td := []struct { + desc string + src image.Image + w, h int + anchor Anchor + want *image.NRGBA + }{ + { + "CropAnchor 4x4 2x2 TopLeft", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + TopLeft, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + }, + }, + }, + { + "CropAnchor 4x4 2x2 Top", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + Top, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + }, + }, + }, + { + "CropAnchor 4x4 2x2 TopRight", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + TopRight, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }, + }, + }, + { + "CropAnchor 4x4 2x2 Left", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + Left, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + }, + }, + }, + { + "CropAnchor 4x4 2x2 Center", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + Center, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + }, + }, + }, + { + "CropAnchor 4x4 2x2 Right", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + Right, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + }, + }, + }, + { + "CropAnchor 4x4 2x2 BottomLeft", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + BottomLeft, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + }, + }, + }, + { + "CropAnchor 4x4 2x2 Bottom", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + Bottom, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + }, + }, + }, + { + "CropAnchor 4x4 2x2 BottomRight", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 2, 2, + BottomRight, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + }, + { + "CropAnchor 4x4 0x0 BottomRight", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 0, 0, + BottomRight, + &image.NRGBA{ + Rect: image.Rect(0, 0, 0, 0), + Stride: 0, + Pix: []uint8{}, + }, + }, + { + "CropAnchor 4x4 100x100 BottomRight", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 100, 100, + BottomRight, + &image.NRGBA{ + Rect: image.Rect(0, 0, 4, 4), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + }, + { + "CropAnchor 4x4 1x100 BottomRight", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 1, 100, + BottomRight, + &image.NRGBA{ + Rect: image.Rect(0, 0, 1, 4), + Stride: 1 * 4, + Pix: []uint8{ + 0x0c, 0x0d, 0x0e, 0x0f, + 0x1c, 0x1d, 0x1e, 0x1f, + 0x2c, 0x2d, 0x2e, 0x2f, + 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + }, + { + "CropAnchor 4x4 0x100 BottomRight", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 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, + }, + }, + 0, 100, + BottomRight, + &image.NRGBA{ + Rect: image.Rect(0, 0, 0, 0), + Stride: 0, + Pix: []uint8{}, + }, + }, + } + for _, d := range td { + got := CropAnchor(d.src, d.w, d.h, d.anchor) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestPaste(t *testing.T) { + td := []struct { + desc string + src1 image.Image + src2 image.Image + p image.Point + want *image.NRGBA + }{ + { + "Paste 2x3 2x1", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(1, 1, 3, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + }, + }, + image.Pt(-1, 0), + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 3), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + }, + } + for _, d := range td { + got := Paste(d.src1, d.src2, d.p) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestPasteCenter(t *testing.T) { + td := []struct { + desc string + src1 image.Image + src2 image.Image + want *image.NRGBA + }{ + { + "PasteCenter 2x3 2x1", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(1, 1, 3, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 3), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + }, + } + for _, d := range td { + got := PasteCenter(d.src1, d.src2) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestOverlay(t *testing.T) { + td := []struct { + desc string + src1 image.Image + src2 image.Image + p image.Point + a float64 + want *image.NRGBA + }{ + { + "Overlay 2x3 2x1 1.0", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0x60, 0x00, 0x90, 0xff, 0xff, 0x00, 0x99, 0x7f, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(1, 1, 3, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x20, 0x40, 0x80, 0x7f, 0xaa, 0xbb, 0xcc, 0xff, + }, + }, + image.Pt(-1, 0), + 1.0, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 3), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0x40, 0x1f, 0x88, 0xff, 0xaa, 0xbb, 0xcc, 0xff, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + }, + { + "Overlay 2x2 2x2 0.5", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x20, 0x20, 0x20, 0x00, + }, + }, + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 1), + Stride: 2 * 4, + Pix: []uint8{ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0xff, 0x20, 0x20, 0x20, 0xff, + }, + }, + image.Pt(-1, -1), + 0.5, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0xff, 0x7f, 0x7f, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x7f, 0x7f, 0x7f, 0xff, 0x20, 0x20, 0x20, 0x7f, + }, + }, + }, + } + for _, d := range td { + got := Overlay(d.src1, d.src2, d.p, d.a) + want := d.want + if !compareNRGBA(got, want, 1) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestOverlayCenter(t *testing.T) { + td := []struct { + desc string + src1 image.Image + src2 image.Image + a float64 + want *image.NRGBA + }{ + { + "OverlayCenter 2x3 2x1", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0xff, + 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0xff, + 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(1, 1, 3, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + }, + }, + 0.5, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 3), + Stride: 2 * 4, + Pix: []uint8{ + 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0xff, + 0x2c, 0x2c, 0x2c, 0xff, 0x2c, 0x2c, 0x2c, 0xff, + 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0xff, + }, + }, + }, + } + for _, d := range td { + got := OverlayCenter(d.src1, d.src2, 0.5) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} diff --git a/vendor/github.com/disintegration/imaging/transform_test.go b/vendor/github.com/disintegration/imaging/transform_test.go new file mode 100644 index 000000000..6e64082f4 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/transform_test.go @@ -0,0 +1,261 @@ +package imaging + +import ( + "image" + "testing" +) + +func TestRotate90(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "Rotate90 2x3", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0xcc, 0xdd, 0xee, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x11, 0x22, 0x33, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + }, + }, + }, + } + for _, d := range td { + got := Rotate90(d.src) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestRotate180(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "Rotate180 2x3", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 3), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, + }, + }, + }, + } + for _, d := range td { + got := Rotate180(d.src) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestRotate270(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "Rotate270 2x3", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xcc, 0xdd, 0xee, 0xff, + }, + }, + }, + } + for _, d := range td { + got := Rotate270(d.src) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestFlipV(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "FlipV 2x3", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 3), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + }, + }, + }, + } + for _, d := range td { + got := FlipV(d.src) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestFlipH(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "FlipH 2x3", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 2, 3), + Stride: 2 * 4, + Pix: []uint8{ + 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, + 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, + }, + }, + }, + } + for _, d := range td { + got := FlipH(d.src) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestTranspose(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "Transpose 2x3", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0xcc, 0xdd, 0xee, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + }, + } + for _, d := range td { + got := Transpose(d.src) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} + +func TestTransverse(t *testing.T) { + td := []struct { + desc string + src image.Image + want *image.NRGBA + }{ + { + "Transverse 2x3", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 1, 2), + Stride: 2 * 4, + Pix: []uint8{ + 0x00, 0x11, 0x22, 0x33, 0xcc, 0xdd, 0xee, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, + }, + }, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xcc, 0xdd, 0xee, 0xff, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, + }, + }, + }, + } + for _, d := range td { + got := Transverse(d.src) + want := d.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: %#v", d.desc, got) + } + } +} diff --git a/vendor/github.com/disintegration/imaging/utils_test.go b/vendor/github.com/disintegration/imaging/utils_test.go new file mode 100644 index 000000000..99aa8c82f --- /dev/null +++ b/vendor/github.com/disintegration/imaging/utils_test.go @@ -0,0 +1,81 @@ +package imaging + +import ( + "runtime" + "testing" +) + +func testParallelN(enabled bool, n, procs int) bool { + data := make([]bool, n) + before := runtime.GOMAXPROCS(0) + runtime.GOMAXPROCS(procs) + parallel(n, func(start, end int) { + for i := start; i < end; i++ { + data[i] = true + } + }) + for i := 0; i < n; i++ { + if data[i] != true { + return false + } + } + runtime.GOMAXPROCS(before) + return true +} + +func TestParallel(t *testing.T) { + for _, e := range []bool{true, false} { + for _, n := range []int{1, 10, 100, 1000} { + for _, p := range []int{1, 2, 4, 8, 16, 100} { + if testParallelN(e, n, p) != true { + t.Errorf("test [parallel %v %d %d] failed", e, n, p) + } + } + } + } +} + +func TestClamp(t *testing.T) { + td := []struct { + f float64 + u uint8 + }{ + {0, 0}, + {255, 255}, + {128, 128}, + {0.49, 0}, + {0.50, 1}, + {254.9, 255}, + {254.0, 254}, + {256, 255}, + {2500, 255}, + {-10, 0}, + {127.6, 128}, + } + + for _, d := range td { + if clamp(d.f) != d.u { + t.Errorf("test [clamp %v %v] failed: %v", d.f, d.u, clamp(d.f)) + } + } +} + +func TestClampint32(t *testing.T) { + td := []struct { + i int32 + u uint8 + }{ + {0, 0}, + {255, 255}, + {128, 128}, + {256, 255}, + {2500, 255}, + {-10, 0}, + } + + for _, d := range td { + if clampint32(d.i) != d.u { + t.Errorf("test [clampint32 %v %v] failed: %v", d.i, d.u, clampint32(d.i)) + } + } +} diff --git a/vendor/github.com/garyburd/redigo/.travis.yml b/vendor/github.com/garyburd/redigo/.travis.yml new file mode 100644 index 000000000..80c179fe5 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/.travis.yml @@ -0,0 +1,16 @@ +language: go +sudo: false +services: + - redis-server + +go: + - 1.4 + - 1.5 + - 1.6 + - tip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go vet $(go list ./... | grep -v /vendor/) + - go test -v -race ./... diff --git a/vendor/github.com/garyburd/redigo/README.markdown b/vendor/github.com/garyburd/redigo/README.markdown new file mode 100644 index 000000000..662690b3d --- /dev/null +++ b/vendor/github.com/garyburd/redigo/README.markdown @@ -0,0 +1,50 @@ +Redigo +====== + +[![Build Status](https://travis-ci.org/garyburd/redigo.svg?branch=master)](https://travis-ci.org/garyburd/redigo) +[![GoDoc](https://godoc.org/github.com/garyburd/redigo/redis?status.svg)](https://godoc.org/github.com/garyburd/redigo/redis) + +Redigo is a [Go](http://golang.org/) client for the [Redis](http://redis.io/) database. + +Features +------- + +* A [Print-like](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Executing_Commands) API with support for all Redis commands. +* [Pipelining](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining), including pipelined transactions. +* [Publish/Subscribe](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Publish_and_Subscribe). +* [Connection pooling](http://godoc.org/github.com/garyburd/redigo/redis#Pool). +* [Script helper type](http://godoc.org/github.com/garyburd/redigo/redis#Script) with optimistic use of EVALSHA. +* [Helper functions](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Reply_Helpers) for working with command replies. + +Documentation +------------- + +- [API Reference](http://godoc.org/github.com/garyburd/redigo/redis) +- [FAQ](https://github.com/garyburd/redigo/wiki/FAQ) + +Installation +------------ + +Install Redigo using the "go get" command: + + go get github.com/garyburd/redigo/redis + +The Go distribution is Redigo's only dependency. + +Related Projects +---------------- + +- [rafaeljusto/redigomock](https://godoc.org/github.com/rafaeljusto/redigomock) - A mock library for Redigo. +- [chasex/redis-go-cluster](https://github.com/chasex/redis-go-cluster) - A Redis cluster client implementation. +- [FZambia/go-sentinel](https://github.com/FZambia/go-sentinel) - Redis Sentinel support for Redigo +- [PuerkitoBio/redisc](https://github.com/PuerkitoBio/redisc) - Redis Cluster client built on top of Redigo + +Contributing +------------ + +Send email to Gary Burd (address in GitHub profile) before doing any work on Redigo. + +License +------- + +Redigo is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). diff --git a/vendor/github.com/garyburd/redigo/internal/commandinfo.go b/vendor/github.com/garyburd/redigo/internal/commandinfo.go index dbc60fc8e..11e584257 100644 --- a/vendor/github.com/garyburd/redigo/internal/commandinfo.go +++ b/vendor/github.com/garyburd/redigo/internal/commandinfo.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package internal +package internal // import "github.com/garyburd/redigo/internal" import ( "strings" diff --git a/vendor/github.com/garyburd/redigo/internal/commandinfo_test.go b/vendor/github.com/garyburd/redigo/internal/commandinfo_test.go new file mode 100644 index 000000000..118e94b67 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/internal/commandinfo_test.go @@ -0,0 +1,27 @@ +package internal + +import "testing" + +func TestLookupCommandInfo(t *testing.T) { + for _, n := range []string{"watch", "WATCH", "wAtch"} { + if LookupCommandInfo(n) == (CommandInfo{}) { + t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n) + } + } +} + +func benchmarkLookupCommandInfo(b *testing.B, names ...string) { + for i := 0; i < b.N; i++ { + for _, c := range names { + LookupCommandInfo(c) + } + } +} + +func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) { + benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR") +} + +func BenchmarkLookupCommandInfoMixedCase(b *testing.B) { + benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR") +} diff --git a/vendor/github.com/garyburd/redigo/internal/redistest/testdb.go b/vendor/github.com/garyburd/redigo/internal/redistest/testdb.go new file mode 100644 index 000000000..b6f205b7f --- /dev/null +++ b/vendor/github.com/garyburd/redigo/internal/redistest/testdb.go @@ -0,0 +1,68 @@ +// Copyright 2014 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package redistest contains utilities for writing Redigo tests. +package redistest + +import ( + "errors" + "time" + + "github.com/garyburd/redigo/redis" +) + +type testConn struct { + redis.Conn +} + +func (t testConn) Close() error { + _, err := t.Conn.Do("SELECT", "9") + if err != nil { + return nil + } + _, err = t.Conn.Do("FLUSHDB") + if err != nil { + return err + } + return t.Conn.Close() +} + +// Dial dials the local Redis server and selects database 9. To prevent +// stomping on real data, DialTestDB fails if database 9 contains data. The +// returned connection flushes database 9 on close. +func Dial() (redis.Conn, error) { + c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second) + if err != nil { + return nil, err + } + + _, err = c.Do("SELECT", "9") + if err != nil { + c.Close() + return nil, err + } + + n, err := redis.Int(c.Do("DBSIZE")) + if err != nil { + c.Close() + return nil, err + } + + if n != 0 { + c.Close() + return nil, errors.New("database #9 is not empty, test can not continue") + } + + return testConn{c}, nil +} diff --git a/vendor/github.com/garyburd/redigo/redis/conn_test.go b/vendor/github.com/garyburd/redigo/redis/conn_test.go new file mode 100644 index 000000000..2ead63326 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/conn_test.go @@ -0,0 +1,670 @@ +// Copyright 2012 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redis_test + +import ( + "bytes" + "io" + "math" + "net" + "os" + "reflect" + "strings" + "testing" + "time" + + "github.com/garyburd/redigo/redis" +) + +type testConn struct { + io.Reader + io.Writer +} + +func (*testConn) Close() error { return nil } +func (*testConn) LocalAddr() net.Addr { return nil } +func (*testConn) RemoteAddr() net.Addr { return nil } +func (*testConn) SetDeadline(t time.Time) error { return nil } +func (*testConn) SetReadDeadline(t time.Time) error { return nil } +func (*testConn) SetWriteDeadline(t time.Time) error { return nil } + +func dialTestConn(r io.Reader, w io.Writer) redis.DialOption { + return redis.DialNetDial(func(net, addr string) (net.Conn, error) { + return &testConn{Reader: r, Writer: w}, nil + }) +} + +var writeTests = []struct { + args []interface{} + expected string +}{ + { + []interface{}{"SET", "key", "value"}, + "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n", + }, + { + []interface{}{"SET", "key", "value"}, + "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n", + }, + { + []interface{}{"SET", "key", byte(100)}, + "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n", + }, + { + []interface{}{"SET", "key", 100}, + "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n", + }, + { + []interface{}{"SET", "key", int64(math.MinInt64)}, + "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$20\r\n-9223372036854775808\r\n", + }, + { + []interface{}{"SET", "key", float64(1349673917.939762)}, + "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$21\r\n1.349673917939762e+09\r\n", + }, + { + []interface{}{"SET", "key", ""}, + "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n", + }, + { + []interface{}{"SET", "key", nil}, + "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n", + }, + { + []interface{}{"ECHO", true, false}, + "*3\r\n$4\r\nECHO\r\n$1\r\n1\r\n$1\r\n0\r\n", + }, +} + +func TestWrite(t *testing.T) { + for _, tt := range writeTests { + var buf bytes.Buffer + c, _ := redis.Dial("", "", dialTestConn(nil, &buf)) + err := c.Send(tt.args[0].(string), tt.args[1:]...) + if err != nil { + t.Errorf("Send(%v) returned error %v", tt.args, err) + continue + } + c.Flush() + actual := buf.String() + if actual != tt.expected { + t.Errorf("Send(%v) = %q, want %q", tt.args, actual, tt.expected) + } + } +} + +var errorSentinel = &struct{}{} + +var readTests = []struct { + reply string + expected interface{} +}{ + { + "+OK\r\n", + "OK", + }, + { + "+PONG\r\n", + "PONG", + }, + { + "@OK\r\n", + errorSentinel, + }, + { + "$6\r\nfoobar\r\n", + []byte("foobar"), + }, + { + "$-1\r\n", + nil, + }, + { + ":1\r\n", + int64(1), + }, + { + ":-2\r\n", + int64(-2), + }, + { + "*0\r\n", + []interface{}{}, + }, + { + "*-1\r\n", + nil, + }, + { + "*4\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$5\r\nHello\r\n$5\r\nWorld\r\n", + []interface{}{[]byte("foo"), []byte("bar"), []byte("Hello"), []byte("World")}, + }, + { + "*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n", + []interface{}{[]byte("foo"), nil, []byte("bar")}, + }, + + { + // "x" is not a valid length + "$x\r\nfoobar\r\n", + errorSentinel, + }, + { + // -2 is not a valid length + "$-2\r\n", + errorSentinel, + }, + { + // "x" is not a valid integer + ":x\r\n", + errorSentinel, + }, + { + // missing \r\n following value + "$6\r\nfoobar", + errorSentinel, + }, + { + // short value + "$6\r\nxx", + errorSentinel, + }, + { + // long value + "$6\r\nfoobarx\r\n", + errorSentinel, + }, +} + +func TestRead(t *testing.T) { + for _, tt := range readTests { + c, _ := redis.Dial("", "", dialTestConn(strings.NewReader(tt.reply), nil)) + actual, err := c.Receive() + if tt.expected == errorSentinel { + if err == nil { + t.Errorf("Receive(%q) did not return expected error", tt.reply) + } + } else { + if err != nil { + t.Errorf("Receive(%q) returned error %v", tt.reply, err) + continue + } + if !reflect.DeepEqual(actual, tt.expected) { + t.Errorf("Receive(%q) = %v, want %v", tt.reply, actual, tt.expected) + } + } + } +} + +var testCommands = []struct { + args []interface{} + expected interface{} +}{ + { + []interface{}{"PING"}, + "PONG", + }, + { + []interface{}{"SET", "foo", "bar"}, + "OK", + }, + { + []interface{}{"GET", "foo"}, + []byte("bar"), + }, + { + []interface{}{"GET", "nokey"}, + nil, + }, + { + []interface{}{"MGET", "nokey", "foo"}, + []interface{}{nil, []byte("bar")}, + }, + { + []interface{}{"INCR", "mycounter"}, + int64(1), + }, + { + []interface{}{"LPUSH", "mylist", "foo"}, + int64(1), + }, + { + []interface{}{"LPUSH", "mylist", "bar"}, + int64(2), + }, + { + []interface{}{"LRANGE", "mylist", 0, -1}, + []interface{}{[]byte("bar"), []byte("foo")}, + }, + { + []interface{}{"MULTI"}, + "OK", + }, + { + []interface{}{"LRANGE", "mylist", 0, -1}, + "QUEUED", + }, + { + []interface{}{"PING"}, + "QUEUED", + }, + { + []interface{}{"EXEC"}, + []interface{}{ + []interface{}{[]byte("bar"), []byte("foo")}, + "PONG", + }, + }, +} + +func TestDoCommands(t *testing.T) { + c, err := redis.DialDefaultServer() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + defer c.Close() + + for _, cmd := range testCommands { + actual, err := c.Do(cmd.args[0].(string), cmd.args[1:]...) + if err != nil { + t.Errorf("Do(%v) returned error %v", cmd.args, err) + continue + } + if !reflect.DeepEqual(actual, cmd.expected) { + t.Errorf("Do(%v) = %v, want %v", cmd.args, actual, cmd.expected) + } + } +} + +func TestPipelineCommands(t *testing.T) { + c, err := redis.DialDefaultServer() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + defer c.Close() + + for _, cmd := range testCommands { + if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil { + t.Fatalf("Send(%v) returned error %v", cmd.args, err) + } + } + if err := c.Flush(); err != nil { + t.Errorf("Flush() returned error %v", err) + } + for _, cmd := range testCommands { + actual, err := c.Receive() + if err != nil { + t.Fatalf("Receive(%v) returned error %v", cmd.args, err) + } + if !reflect.DeepEqual(actual, cmd.expected) { + t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected) + } + } +} + +func TestBlankCommmand(t *testing.T) { + c, err := redis.DialDefaultServer() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + defer c.Close() + + for _, cmd := range testCommands { + if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil { + t.Fatalf("Send(%v) returned error %v", cmd.args, err) + } + } + reply, err := redis.Values(c.Do("")) + if err != nil { + t.Fatalf("Do() returned error %v", err) + } + if len(reply) != len(testCommands) { + t.Fatalf("len(reply)=%d, want %d", len(reply), len(testCommands)) + } + for i, cmd := range testCommands { + actual := reply[i] + if !reflect.DeepEqual(actual, cmd.expected) { + t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected) + } + } +} + +func TestRecvBeforeSend(t *testing.T) { + c, err := redis.DialDefaultServer() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + defer c.Close() + done := make(chan struct{}) + go func() { + c.Receive() + close(done) + }() + time.Sleep(time.Millisecond) + c.Send("PING") + c.Flush() + <-done + _, err = c.Do("") + if err != nil { + t.Fatalf("error=%v", err) + } +} + +func TestError(t *testing.T) { + c, err := redis.DialDefaultServer() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + defer c.Close() + + c.Do("SET", "key", "val") + _, err = c.Do("HSET", "key", "fld", "val") + if err == nil { + t.Errorf("Expected err for HSET on string key.") + } + if c.Err() != nil { + t.Errorf("Conn has Err()=%v, expect nil", c.Err()) + } + _, err = c.Do("SET", "key", "val") + if err != nil { + t.Errorf("Do(SET, key, val) returned error %v, expected nil.", err) + } +} + +func TestReadTimeout(t *testing.T) { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("net.Listen returned %v", err) + } + defer l.Close() + + go func() { + for { + c, err := l.Accept() + if err != nil { + return + } + go func() { + time.Sleep(time.Second) + c.Write([]byte("+OK\r\n")) + c.Close() + }() + } + }() + + // Do + + c1, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond)) + if err != nil { + t.Fatalf("redis.Dial returned %v", err) + } + defer c1.Close() + + _, err = c1.Do("PING") + if err == nil { + t.Fatalf("c1.Do() returned nil, expect error") + } + if c1.Err() == nil { + t.Fatalf("c1.Err() = nil, expect error") + } + + // Send/Flush/Receive + + c2, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond)) + if err != nil { + t.Fatalf("redis.Dial returned %v", err) + } + defer c2.Close() + + c2.Send("PING") + c2.Flush() + _, err = c2.Receive() + if err == nil { + t.Fatalf("c2.Receive() returned nil, expect error") + } + if c2.Err() == nil { + t.Fatalf("c2.Err() = nil, expect error") + } +} + +var dialErrors = []struct { + rawurl string + expectedError string +}{ + { + "localhost", + "invalid redis URL scheme", + }, + // The error message for invalid hosts is diffferent in different + // versions of Go, so just check that there is an error message. + { + "redis://weird url", + "", + }, + { + "redis://foo:bar:baz", + "", + }, + { + "http://www.google.com", + "invalid redis URL scheme: http", + }, + { + "redis://localhost:6379/abc123", + "invalid database: abc123", + }, +} + +func TestDialURLErrors(t *testing.T) { + for _, d := range dialErrors { + _, err := redis.DialURL(d.rawurl) + if err == nil || !strings.Contains(err.Error(), d.expectedError) { + t.Errorf("DialURL did not return expected error (expected %v to contain %s)", err, d.expectedError) + } + } +} + +func TestDialURLPort(t *testing.T) { + checkPort := func(network, address string) (net.Conn, error) { + if address != "localhost:6379" { + t.Errorf("DialURL did not set port to 6379 by default (got %v)", address) + } + return nil, nil + } + _, err := redis.DialURL("redis://localhost", redis.DialNetDial(checkPort)) + if err != nil { + t.Error("dial error:", err) + } +} + +func TestDialURLHost(t *testing.T) { + checkHost := func(network, address string) (net.Conn, error) { + if address != "localhost:6379" { + t.Errorf("DialURL did not set host to localhost by default (got %v)", address) + } + return nil, nil + } + _, err := redis.DialURL("redis://:6379", redis.DialNetDial(checkHost)) + if err != nil { + t.Error("dial error:", err) + } +} + +func TestDialURLPassword(t *testing.T) { + var buf bytes.Buffer + _, err := redis.DialURL("redis://x:abc123@localhost", dialTestConn(strings.NewReader("+OK\r\n"), &buf)) + if err != nil { + t.Error("dial error:", err) + } + expected := "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n" + actual := buf.String() + if actual != expected { + t.Errorf("commands = %q, want %q", actual, expected) + } +} + +func TestDialURLDatabase(t *testing.T) { + var buf3 bytes.Buffer + _, err3 := redis.DialURL("redis://localhost/3", dialTestConn(strings.NewReader("+OK\r\n"), &buf3)) + if err3 != nil { + t.Error("dial error:", err3) + } + expected3 := "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n" + actual3 := buf3.String() + if actual3 != expected3 { + t.Errorf("commands = %q, want %q", actual3, expected3) + } + // empty DB means 0 + var buf0 bytes.Buffer + _, err0 := redis.DialURL("redis://localhost/", dialTestConn(strings.NewReader("+OK\r\n"), &buf0)) + if err0 != nil { + t.Error("dial error:", err0) + } + expected0 := "" + actual0 := buf0.String() + if actual0 != expected0 { + t.Errorf("commands = %q, want %q", actual0, expected0) + } +} + +// Connect to local instance of Redis running on the default port. +func ExampleDial() { + c, err := redis.Dial("tcp", ":6379") + if err != nil { + // handle error + } + defer c.Close() +} + +// Connect to remote instance of Redis using a URL. +func ExampleDialURL() { + c, err := redis.DialURL(os.Getenv("REDIS_URL")) + if err != nil { + // handle connection error + } + defer c.Close() +} + +// TextExecError tests handling of errors in a transaction. See +// http://redis.io/topics/transactions for information on how Redis handles +// errors in a transaction. +func TestExecError(t *testing.T) { + c, err := redis.DialDefaultServer() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + defer c.Close() + + // Execute commands that fail before EXEC is called. + + c.Do("DEL", "k0") + c.Do("ZADD", "k0", 0, 0) + c.Send("MULTI") + c.Send("NOTACOMMAND", "k0", 0, 0) + c.Send("ZINCRBY", "k0", 0, 0) + v, err := c.Do("EXEC") + if err == nil { + t.Fatalf("EXEC returned values %v, expected error", v) + } + + // Execute commands that fail after EXEC is called. The first command + // returns an error. + + c.Do("DEL", "k1") + c.Do("ZADD", "k1", 0, 0) + c.Send("MULTI") + c.Send("HSET", "k1", 0, 0) + c.Send("ZINCRBY", "k1", 0, 0) + v, err = c.Do("EXEC") + if err != nil { + t.Fatalf("EXEC returned error %v", err) + } + + vs, err := redis.Values(v, nil) + if err != nil { + t.Fatalf("Values(v) returned error %v", err) + } + + if len(vs) != 2 { + t.Fatalf("len(vs) == %d, want 2", len(vs)) + } + + if _, ok := vs[0].(error); !ok { + t.Fatalf("first result is type %T, expected error", vs[0]) + } + + if _, ok := vs[1].([]byte); !ok { + t.Fatalf("second result is type %T, expected []byte", vs[1]) + } + + // Execute commands that fail after EXEC is called. The second command + // returns an error. + + c.Do("ZADD", "k2", 0, 0) + c.Send("MULTI") + c.Send("ZINCRBY", "k2", 0, 0) + c.Send("HSET", "k2", 0, 0) + v, err = c.Do("EXEC") + if err != nil { + t.Fatalf("EXEC returned error %v", err) + } + + vs, err = redis.Values(v, nil) + if err != nil { + t.Fatalf("Values(v) returned error %v", err) + } + + if len(vs) != 2 { + t.Fatalf("len(vs) == %d, want 2", len(vs)) + } + + if _, ok := vs[0].([]byte); !ok { + t.Fatalf("first result is type %T, expected []byte", vs[0]) + } + + if _, ok := vs[1].(error); !ok { + t.Fatalf("second result is type %T, expected error", vs[2]) + } +} + +func BenchmarkDoEmpty(b *testing.B) { + b.StopTimer() + c, err := redis.DialDefaultServer() + if err != nil { + b.Fatal(err) + } + defer c.Close() + b.StartTimer() + for i := 0; i < b.N; i++ { + if _, err := c.Do(""); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkDoPing(b *testing.B) { + b.StopTimer() + c, err := redis.DialDefaultServer() + if err != nil { + b.Fatal(err) + } + defer c.Close() + b.StartTimer() + for i := 0; i < b.N; i++ { + if _, err := c.Do("PING"); err != nil { + b.Fatal(err) + } + } +} diff --git a/vendor/github.com/garyburd/redigo/redis/doc.go b/vendor/github.com/garyburd/redigo/redis/doc.go index 1ae6f0cc2..a5cd454af 100644 --- a/vendor/github.com/garyburd/redigo/redis/doc.go +++ b/vendor/github.com/garyburd/redigo/redis/doc.go @@ -166,4 +166,4 @@ // if _, err := redis.Scan(reply, &value1, &value2); err != nil { // // handle error // } -package redis +package redis // import "github.com/garyburd/redigo/redis" diff --git a/vendor/github.com/garyburd/redigo/redis/pool_test.go b/vendor/github.com/garyburd/redigo/redis/pool_test.go new file mode 100644 index 000000000..9419a128f --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/pool_test.go @@ -0,0 +1,684 @@ +// Copyright 2011 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redis_test + +import ( + "errors" + "io" + "reflect" + "sync" + "testing" + "time" + + "github.com/garyburd/redigo/redis" +) + +type poolTestConn struct { + d *poolDialer + err error + redis.Conn +} + +func (c *poolTestConn) Close() error { + c.d.mu.Lock() + c.d.open -= 1 + c.d.mu.Unlock() + return c.Conn.Close() +} + +func (c *poolTestConn) Err() error { return c.err } + +func (c *poolTestConn) Do(commandName string, args ...interface{}) (interface{}, error) { + if commandName == "ERR" { + c.err = args[0].(error) + commandName = "PING" + } + if commandName != "" { + c.d.commands = append(c.d.commands, commandName) + } + return c.Conn.Do(commandName, args...) +} + +func (c *poolTestConn) Send(commandName string, args ...interface{}) error { + c.d.commands = append(c.d.commands, commandName) + return c.Conn.Send(commandName, args...) +} + +type poolDialer struct { + mu sync.Mutex + t *testing.T + dialed int + open int + commands []string + dialErr error +} + +func (d *poolDialer) dial() (redis.Conn, error) { + d.mu.Lock() + d.dialed += 1 + dialErr := d.dialErr + d.mu.Unlock() + if dialErr != nil { + return nil, d.dialErr + } + c, err := redis.DialDefaultServer() + if err != nil { + return nil, err + } + d.mu.Lock() + d.open += 1 + d.mu.Unlock() + return &poolTestConn{d: d, Conn: c}, nil +} + +func (d *poolDialer) check(message string, p *redis.Pool, dialed, open int) { + d.mu.Lock() + if d.dialed != dialed { + d.t.Errorf("%s: dialed=%d, want %d", message, d.dialed, dialed) + } + if d.open != open { + d.t.Errorf("%s: open=%d, want %d", message, d.open, open) + } + if active := p.ActiveCount(); active != open { + d.t.Errorf("%s: active=%d, want %d", message, active, open) + } + d.mu.Unlock() +} + +func TestPoolReuse(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + Dial: d.dial, + } + + for i := 0; i < 10; i++ { + c1 := p.Get() + c1.Do("PING") + c2 := p.Get() + c2.Do("PING") + c1.Close() + c2.Close() + } + + d.check("before close", p, 2, 2) + p.Close() + d.check("after close", p, 2, 0) +} + +func TestPoolMaxIdle(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + Dial: d.dial, + } + defer p.Close() + + for i := 0; i < 10; i++ { + c1 := p.Get() + c1.Do("PING") + c2 := p.Get() + c2.Do("PING") + c3 := p.Get() + c3.Do("PING") + c1.Close() + c2.Close() + c3.Close() + } + d.check("before close", p, 12, 2) + p.Close() + d.check("after close", p, 12, 0) +} + +func TestPoolError(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + Dial: d.dial, + } + defer p.Close() + + c := p.Get() + c.Do("ERR", io.EOF) + if c.Err() == nil { + t.Errorf("expected c.Err() != nil") + } + c.Close() + + c = p.Get() + c.Do("ERR", io.EOF) + c.Close() + + d.check(".", p, 2, 0) +} + +func TestPoolClose(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + Dial: d.dial, + } + defer p.Close() + + c1 := p.Get() + c1.Do("PING") + c2 := p.Get() + c2.Do("PING") + c3 := p.Get() + c3.Do("PING") + + c1.Close() + if _, err := c1.Do("PING"); err == nil { + t.Errorf("expected error after connection closed") + } + + c2.Close() + c2.Close() + + p.Close() + + d.check("after pool close", p, 3, 1) + + if _, err := c1.Do("PING"); err == nil { + t.Errorf("expected error after connection and pool closed") + } + + c3.Close() + + d.check("after conn close", p, 3, 0) + + c1 = p.Get() + if _, err := c1.Do("PING"); err == nil { + t.Errorf("expected error after pool closed") + } +} + +func TestPoolTimeout(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + IdleTimeout: 300 * time.Second, + Dial: d.dial, + } + defer p.Close() + + now := time.Now() + redis.SetNowFunc(func() time.Time { return now }) + defer redis.SetNowFunc(time.Now) + + c := p.Get() + c.Do("PING") + c.Close() + + d.check("1", p, 1, 1) + + now = now.Add(p.IdleTimeout) + + c = p.Get() + c.Do("PING") + c.Close() + + d.check("2", p, 2, 1) +} + +func TestPoolConcurrenSendReceive(t *testing.T) { + p := &redis.Pool{ + Dial: redis.DialDefaultServer, + } + defer p.Close() + + c := p.Get() + done := make(chan error, 1) + go func() { + _, err := c.Receive() + done <- err + }() + c.Send("PING") + c.Flush() + err := <-done + if err != nil { + t.Fatalf("Receive() returned error %v", err) + } + _, err = c.Do("") + if err != nil { + t.Fatalf("Do() returned error %v", err) + } + c.Close() +} + +func TestPoolBorrowCheck(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + Dial: d.dial, + TestOnBorrow: func(redis.Conn, time.Time) error { return redis.Error("BLAH") }, + } + defer p.Close() + + for i := 0; i < 10; i++ { + c := p.Get() + c.Do("PING") + c.Close() + } + d.check("1", p, 10, 1) +} + +func TestPoolMaxActive(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + MaxActive: 2, + Dial: d.dial, + } + defer p.Close() + + c1 := p.Get() + c1.Do("PING") + c2 := p.Get() + c2.Do("PING") + + d.check("1", p, 2, 2) + + c3 := p.Get() + if _, err := c3.Do("PING"); err != redis.ErrPoolExhausted { + t.Errorf("expected pool exhausted") + } + + c3.Close() + d.check("2", p, 2, 2) + c2.Close() + d.check("3", p, 2, 2) + + c3 = p.Get() + if _, err := c3.Do("PING"); err != nil { + t.Errorf("expected good channel, err=%v", err) + } + c3.Close() + + d.check("4", p, 2, 2) +} + +func TestPoolMonitorCleanup(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + MaxActive: 2, + Dial: d.dial, + } + defer p.Close() + + c := p.Get() + c.Send("MONITOR") + c.Close() + + d.check("", p, 1, 0) +} + +func TestPoolPubSubCleanup(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + MaxActive: 2, + Dial: d.dial, + } + defer p.Close() + + c := p.Get() + c.Send("SUBSCRIBE", "x") + c.Close() + + want := []string{"SUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"} + if !reflect.DeepEqual(d.commands, want) { + t.Errorf("got commands %v, want %v", d.commands, want) + } + d.commands = nil + + c = p.Get() + c.Send("PSUBSCRIBE", "x*") + c.Close() + + want = []string{"PSUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"} + if !reflect.DeepEqual(d.commands, want) { + t.Errorf("got commands %v, want %v", d.commands, want) + } + d.commands = nil +} + +func TestPoolTransactionCleanup(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 2, + MaxActive: 2, + Dial: d.dial, + } + defer p.Close() + + c := p.Get() + c.Do("WATCH", "key") + c.Do("PING") + c.Close() + + want := []string{"WATCH", "PING", "UNWATCH"} + if !reflect.DeepEqual(d.commands, want) { + t.Errorf("got commands %v, want %v", d.commands, want) + } + d.commands = nil + + c = p.Get() + c.Do("WATCH", "key") + c.Do("UNWATCH") + c.Do("PING") + c.Close() + + want = []string{"WATCH", "UNWATCH", "PING"} + if !reflect.DeepEqual(d.commands, want) { + t.Errorf("got commands %v, want %v", d.commands, want) + } + d.commands = nil + + c = p.Get() + c.Do("WATCH", "key") + c.Do("MULTI") + c.Do("PING") + c.Close() + + want = []string{"WATCH", "MULTI", "PING", "DISCARD"} + if !reflect.DeepEqual(d.commands, want) { + t.Errorf("got commands %v, want %v", d.commands, want) + } + d.commands = nil + + c = p.Get() + c.Do("WATCH", "key") + c.Do("MULTI") + c.Do("DISCARD") + c.Do("PING") + c.Close() + + want = []string{"WATCH", "MULTI", "DISCARD", "PING"} + if !reflect.DeepEqual(d.commands, want) { + t.Errorf("got commands %v, want %v", d.commands, want) + } + d.commands = nil + + c = p.Get() + c.Do("WATCH", "key") + c.Do("MULTI") + c.Do("EXEC") + c.Do("PING") + c.Close() + + want = []string{"WATCH", "MULTI", "EXEC", "PING"} + if !reflect.DeepEqual(d.commands, want) { + t.Errorf("got commands %v, want %v", d.commands, want) + } + d.commands = nil +} + +func startGoroutines(p *redis.Pool, cmd string, args ...interface{}) chan error { + errs := make(chan error, 10) + for i := 0; i < cap(errs); i++ { + go func() { + c := p.Get() + _, err := c.Do(cmd, args...) + errs <- err + c.Close() + }() + } + + // Wait for goroutines to block. + time.Sleep(time.Second / 4) + + return errs +} + +func TestWaitPool(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 1, + MaxActive: 1, + Dial: d.dial, + Wait: true, + } + defer p.Close() + + c := p.Get() + errs := startGoroutines(p, "PING") + d.check("before close", p, 1, 1) + c.Close() + timeout := time.After(2 * time.Second) + for i := 0; i < cap(errs); i++ { + select { + case err := <-errs: + if err != nil { + t.Fatal(err) + } + case <-timeout: + t.Fatalf("timeout waiting for blocked goroutine %d", i) + } + } + d.check("done", p, 1, 1) +} + +func TestWaitPoolClose(t *testing.T) { + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 1, + MaxActive: 1, + Dial: d.dial, + Wait: true, + } + defer p.Close() + + c := p.Get() + if _, err := c.Do("PING"); err != nil { + t.Fatal(err) + } + errs := startGoroutines(p, "PING") + d.check("before close", p, 1, 1) + p.Close() + timeout := time.After(2 * time.Second) + for i := 0; i < cap(errs); i++ { + select { + case err := <-errs: + switch err { + case nil: + t.Fatal("blocked goroutine did not get error") + case redis.ErrPoolExhausted: + t.Fatal("blocked goroutine got pool exhausted error") + } + case <-timeout: + t.Fatal("timeout waiting for blocked goroutine") + } + } + c.Close() + d.check("done", p, 1, 0) +} + +func TestWaitPoolCommandError(t *testing.T) { + testErr := errors.New("test") + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 1, + MaxActive: 1, + Dial: d.dial, + Wait: true, + } + defer p.Close() + + c := p.Get() + errs := startGoroutines(p, "ERR", testErr) + d.check("before close", p, 1, 1) + c.Close() + timeout := time.After(2 * time.Second) + for i := 0; i < cap(errs); i++ { + select { + case err := <-errs: + if err != nil { + t.Fatal(err) + } + case <-timeout: + t.Fatalf("timeout waiting for blocked goroutine %d", i) + } + } + d.check("done", p, cap(errs), 0) +} + +func TestWaitPoolDialError(t *testing.T) { + testErr := errors.New("test") + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: 1, + MaxActive: 1, + Dial: d.dial, + Wait: true, + } + defer p.Close() + + c := p.Get() + errs := startGoroutines(p, "ERR", testErr) + d.check("before close", p, 1, 1) + + d.dialErr = errors.New("dial") + c.Close() + + nilCount := 0 + errCount := 0 + timeout := time.After(2 * time.Second) + for i := 0; i < cap(errs); i++ { + select { + case err := <-errs: + switch err { + case nil: + nilCount++ + case d.dialErr: + errCount++ + default: + t.Fatalf("expected dial error or nil, got %v", err) + } + case <-timeout: + t.Fatalf("timeout waiting for blocked goroutine %d", i) + } + } + if nilCount != 1 { + t.Errorf("expected one nil error, got %d", nilCount) + } + if errCount != cap(errs)-1 { + t.Errorf("expected %d dial erors, got %d", cap(errs)-1, errCount) + } + d.check("done", p, cap(errs), 0) +} + +// Borrowing requires us to iterate over the idle connections, unlock the pool, +// and perform a blocking operation to check the connection still works. If +// TestOnBorrow fails, we must reacquire the lock and continue iteration. This +// test ensures that iteration will work correctly if multiple threads are +// iterating simultaneously. +func TestLocking_TestOnBorrowFails_PoolDoesntCrash(t *testing.T) { + const count = 100 + + // First we'll Create a pool where the pilfering of idle connections fails. + d := poolDialer{t: t} + p := &redis.Pool{ + MaxIdle: count, + MaxActive: count, + Dial: d.dial, + TestOnBorrow: func(c redis.Conn, t time.Time) error { + return errors.New("No way back into the real world.") + }, + } + defer p.Close() + + // Fill the pool with idle connections. + conns := make([]redis.Conn, count) + for i := range conns { + conns[i] = p.Get() + } + for i := range conns { + conns[i].Close() + } + + // Spawn a bunch of goroutines to thrash the pool. + var wg sync.WaitGroup + wg.Add(count) + for i := 0; i < count; i++ { + go func() { + c := p.Get() + if c.Err() != nil { + t.Errorf("pool get failed: %v", c.Err()) + } + c.Close() + wg.Done() + }() + } + wg.Wait() + if d.dialed != count*2 { + t.Errorf("Expected %d dials, got %d", count*2, d.dialed) + } +} + +func BenchmarkPoolGet(b *testing.B) { + b.StopTimer() + p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2} + c := p.Get() + if err := c.Err(); err != nil { + b.Fatal(err) + } + c.Close() + defer p.Close() + b.StartTimer() + for i := 0; i < b.N; i++ { + c = p.Get() + c.Close() + } +} + +func BenchmarkPoolGetErr(b *testing.B) { + b.StopTimer() + p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2} + c := p.Get() + if err := c.Err(); err != nil { + b.Fatal(err) + } + c.Close() + defer p.Close() + b.StartTimer() + for i := 0; i < b.N; i++ { + c = p.Get() + if err := c.Err(); err != nil { + b.Fatal(err) + } + c.Close() + } +} + +func BenchmarkPoolGetPing(b *testing.B) { + b.StopTimer() + p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2} + c := p.Get() + if err := c.Err(); err != nil { + b.Fatal(err) + } + c.Close() + defer p.Close() + b.StartTimer() + for i := 0; i < b.N; i++ { + c = p.Get() + if _, err := c.Do("PING"); err != nil { + b.Fatal(err) + } + c.Close() + } +} diff --git a/vendor/github.com/garyburd/redigo/redis/pubsub_test.go b/vendor/github.com/garyburd/redigo/redis/pubsub_test.go new file mode 100644 index 000000000..b95513155 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/pubsub_test.go @@ -0,0 +1,148 @@ +// Copyright 2012 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redis_test + +import ( + "fmt" + "reflect" + "sync" + "testing" + + "github.com/garyburd/redigo/redis" +) + +func publish(channel, value interface{}) { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + c.Do("PUBLISH", channel, value) +} + +// Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine. +func ExamplePubSubConn() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + var wg sync.WaitGroup + wg.Add(2) + + psc := redis.PubSubConn{Conn: c} + + // This goroutine receives and prints pushed notifications from the server. + // The goroutine exits when the connection is unsubscribed from all + // channels or there is an error. + go func() { + defer wg.Done() + for { + switch n := psc.Receive().(type) { + case redis.Message: + fmt.Printf("Message: %s %s\n", n.Channel, n.Data) + case redis.PMessage: + fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data) + case redis.Subscription: + fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count) + if n.Count == 0 { + return + } + case error: + fmt.Printf("error: %v\n", n) + return + } + } + }() + + // This goroutine manages subscriptions for the connection. + go func() { + defer wg.Done() + + psc.Subscribe("example") + psc.PSubscribe("p*") + + // The following function calls publish a message using another + // connection to the Redis server. + publish("example", "hello") + publish("example", "world") + publish("pexample", "foo") + publish("pexample", "bar") + + // Unsubscribe from all connections. This will cause the receiving + // goroutine to exit. + psc.Unsubscribe() + psc.PUnsubscribe() + }() + + wg.Wait() + + // Output: + // Subscription: subscribe example 1 + // Subscription: psubscribe p* 2 + // Message: example hello + // Message: example world + // PMessage: p* pexample foo + // PMessage: p* pexample bar + // Subscription: unsubscribe example 1 + // Subscription: punsubscribe p* 0 +} + +func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) { + actual := c.Receive() + if !reflect.DeepEqual(actual, expected) { + t.Errorf("%s = %v, want %v", message, actual, expected) + } +} + +func TestPushed(t *testing.T) { + pc, err := redis.DialDefaultServer() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + defer pc.Close() + + sc, err := redis.DialDefaultServer() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + defer sc.Close() + + c := redis.PubSubConn{Conn: sc} + + c.Subscribe("c1") + expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1}) + c.Subscribe("c2") + expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2}) + c.PSubscribe("p1") + expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3}) + c.PSubscribe("p2") + expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4}) + c.PUnsubscribe() + expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3}) + expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2}) + + pc.Do("PUBLISH", "c1", "hello") + expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")}) + + c.Ping("hello") + expectPushed(t, c, `Ping("hello")`, redis.Pong{Data: "hello"}) + + c.Conn.Send("PING") + c.Conn.Flush() + expectPushed(t, c, `Send("PING")`, redis.Pong{}) +} diff --git a/vendor/github.com/garyburd/redigo/redis/reply_test.go b/vendor/github.com/garyburd/redigo/redis/reply_test.go new file mode 100644 index 000000000..2c774866d --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/reply_test.go @@ -0,0 +1,179 @@ +// Copyright 2012 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redis_test + +import ( + "fmt" + "reflect" + "testing" + + "github.com/garyburd/redigo/redis" +) + +type valueError struct { + v interface{} + err error +} + +func ve(v interface{}, err error) valueError { + return valueError{v, err} +} + +var replyTests = []struct { + name interface{} + actual valueError + expected valueError +}{ + { + "ints([v1, v2])", + ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)), + ve([]int{4, 5}, nil), + }, + { + "ints(nil)", + ve(redis.Ints(nil, nil)), + ve([]int(nil), redis.ErrNil), + }, + { + "strings([v1, v2])", + ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)), + ve([]string{"v1", "v2"}, nil), + }, + { + "strings(nil)", + ve(redis.Strings(nil, nil)), + ve([]string(nil), redis.ErrNil), + }, + { + "byteslices([v1, v2])", + ve(redis.ByteSlices([]interface{}{[]byte("v1"), []byte("v2")}, nil)), + ve([][]byte{[]byte("v1"), []byte("v2")}, nil), + }, + { + "byteslices(nil)", + ve(redis.ByteSlices(nil, nil)), + ve([][]byte(nil), redis.ErrNil), + }, + { + "values([v1, v2])", + ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)), + ve([]interface{}{[]byte("v1"), []byte("v2")}, nil), + }, + { + "values(nil)", + ve(redis.Values(nil, nil)), + ve([]interface{}(nil), redis.ErrNil), + }, + { + "float64(1.0)", + ve(redis.Float64([]byte("1.0"), nil)), + ve(float64(1.0), nil), + }, + { + "float64(nil)", + ve(redis.Float64(nil, nil)), + ve(float64(0.0), redis.ErrNil), + }, + { + "uint64(1)", + ve(redis.Uint64(int64(1), nil)), + ve(uint64(1), nil), + }, + { + "uint64(-1)", + ve(redis.Uint64(int64(-1), nil)), + ve(uint64(0), redis.ErrNegativeInt), + }, +} + +func TestReply(t *testing.T) { + for _, rt := range replyTests { + if rt.actual.err != rt.expected.err { + t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err) + continue + } + if !reflect.DeepEqual(rt.actual.v, rt.expected.v) { + t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v) + } + } +} + +// dial wraps DialDefaultServer() with a more suitable function name for examples. +func dial() (redis.Conn, error) { + return redis.DialDefaultServer() +} + +func ExampleBool() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + + c.Do("SET", "foo", 1) + exists, _ := redis.Bool(c.Do("EXISTS", "foo")) + fmt.Printf("%#v\n", exists) + // Output: + // true +} + +func ExampleInt() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + + c.Do("SET", "k1", 1) + n, _ := redis.Int(c.Do("GET", "k1")) + fmt.Printf("%#v\n", n) + n, _ = redis.Int(c.Do("INCR", "k1")) + fmt.Printf("%#v\n", n) + // Output: + // 1 + // 2 +} + +func ExampleInts() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + + c.Do("SADD", "set_with_integers", 4, 5, 6) + ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers")) + fmt.Printf("%#v\n", ints) + // Output: + // []int{4, 5, 6} +} + +func ExampleString() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + + c.Do("SET", "hello", "world") + s, err := redis.String(c.Do("GET", "hello")) + fmt.Printf("%#v\n", s) + // Output: + // "world" +} diff --git a/vendor/github.com/garyburd/redigo/redis/scan_test.go b/vendor/github.com/garyburd/redigo/redis/scan_test.go new file mode 100644 index 000000000..d364dff42 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/scan_test.go @@ -0,0 +1,440 @@ +// Copyright 2012 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redis_test + +import ( + "fmt" + "math" + "reflect" + "testing" + + "github.com/garyburd/redigo/redis" +) + +var scanConversionTests = []struct { + src interface{} + dest interface{} +}{ + {[]byte("-inf"), math.Inf(-1)}, + {[]byte("+inf"), math.Inf(1)}, + {[]byte("0"), float64(0)}, + {[]byte("3.14159"), float64(3.14159)}, + {[]byte("3.14"), float32(3.14)}, + {[]byte("-100"), int(-100)}, + {[]byte("101"), int(101)}, + {int64(102), int(102)}, + {[]byte("103"), uint(103)}, + {int64(104), uint(104)}, + {[]byte("105"), int8(105)}, + {int64(106), int8(106)}, + {[]byte("107"), uint8(107)}, + {int64(108), uint8(108)}, + {[]byte("0"), false}, + {int64(0), false}, + {[]byte("f"), false}, + {[]byte("1"), true}, + {int64(1), true}, + {[]byte("t"), true}, + {"hello", "hello"}, + {[]byte("hello"), "hello"}, + {[]byte("world"), []byte("world")}, + {[]interface{}{[]byte("foo")}, []interface{}{[]byte("foo")}}, + {[]interface{}{[]byte("foo")}, []string{"foo"}}, + {[]interface{}{[]byte("hello"), []byte("world")}, []string{"hello", "world"}}, + {[]interface{}{[]byte("bar")}, [][]byte{[]byte("bar")}}, + {[]interface{}{[]byte("1")}, []int{1}}, + {[]interface{}{[]byte("1"), []byte("2")}, []int{1, 2}}, + {[]interface{}{[]byte("1"), []byte("2")}, []float64{1, 2}}, + {[]interface{}{[]byte("1")}, []byte{1}}, + {[]interface{}{[]byte("1")}, []bool{true}}, +} + +func TestScanConversion(t *testing.T) { + for _, tt := range scanConversionTests { + values := []interface{}{tt.src} + dest := reflect.New(reflect.TypeOf(tt.dest)) + values, err := redis.Scan(values, dest.Interface()) + if err != nil { + t.Errorf("Scan(%v) returned error %v", tt, err) + continue + } + if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) { + t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest) + } + } +} + +var scanConversionErrorTests = []struct { + src interface{} + dest interface{} +}{ + {[]byte("1234"), byte(0)}, + {int64(1234), byte(0)}, + {[]byte("-1"), byte(0)}, + {int64(-1), byte(0)}, + {[]byte("junk"), false}, + {redis.Error("blah"), false}, +} + +func TestScanConversionError(t *testing.T) { + for _, tt := range scanConversionErrorTests { + values := []interface{}{tt.src} + dest := reflect.New(reflect.TypeOf(tt.dest)) + values, err := redis.Scan(values, dest.Interface()) + if err == nil { + t.Errorf("Scan(%v) did not return error", tt) + } + } +} + +func ExampleScan() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + + c.Send("HMSET", "album:1", "title", "Red", "rating", 5) + c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1) + c.Send("HMSET", "album:3", "title", "Beat") + c.Send("LPUSH", "albums", "1") + c.Send("LPUSH", "albums", "2") + c.Send("LPUSH", "albums", "3") + values, err := redis.Values(c.Do("SORT", "albums", + "BY", "album:*->rating", + "GET", "album:*->title", + "GET", "album:*->rating")) + if err != nil { + fmt.Println(err) + return + } + + for len(values) > 0 { + var title string + rating := -1 // initialize to illegal value to detect nil. + values, err = redis.Scan(values, &title, &rating) + if err != nil { + fmt.Println(err) + return + } + if rating == -1 { + fmt.Println(title, "not-rated") + } else { + fmt.Println(title, rating) + } + } + // Output: + // Beat not-rated + // Earthbound 1 + // Red 5 +} + +type s0 struct { + X int + Y int `redis:"y"` + Bt bool +} + +type s1 struct { + X int `redis:"-"` + I int `redis:"i"` + U uint `redis:"u"` + S string `redis:"s"` + P []byte `redis:"p"` + B bool `redis:"b"` + Bt bool + Bf bool + s0 +} + +var scanStructTests = []struct { + title string + reply []string + value interface{} +}{ + {"basic", + []string{"i", "-1234", "u", "5678", "s", "hello", "p", "world", "b", "t", "Bt", "1", "Bf", "0", "X", "123", "y", "456"}, + &s1{I: -1234, U: 5678, S: "hello", P: []byte("world"), B: true, Bt: true, Bf: false, s0: s0{X: 123, Y: 456}}, + }, +} + +func TestScanStruct(t *testing.T) { + for _, tt := range scanStructTests { + + var reply []interface{} + for _, v := range tt.reply { + reply = append(reply, []byte(v)) + } + + value := reflect.New(reflect.ValueOf(tt.value).Type().Elem()) + + if err := redis.ScanStruct(reply, value.Interface()); err != nil { + t.Fatalf("ScanStruct(%s) returned error %v", tt.title, err) + } + + if !reflect.DeepEqual(value.Interface(), tt.value) { + t.Fatalf("ScanStruct(%s) returned %v, want %v", tt.title, value.Interface(), tt.value) + } + } +} + +func TestBadScanStructArgs(t *testing.T) { + x := []interface{}{"A", "b"} + test := func(v interface{}) { + if err := redis.ScanStruct(x, v); err == nil { + t.Errorf("Expect error for ScanStruct(%T, %T)", x, v) + } + } + + test(nil) + + var v0 *struct{} + test(v0) + + var v1 int + test(&v1) + + x = x[:1] + v2 := struct{ A string }{} + test(&v2) +} + +var scanSliceTests = []struct { + src []interface{} + fieldNames []string + ok bool + dest interface{} +}{ + { + []interface{}{[]byte("1"), nil, []byte("-1")}, + nil, + true, + []int{1, 0, -1}, + }, + { + []interface{}{[]byte("1"), nil, []byte("2")}, + nil, + true, + []uint{1, 0, 2}, + }, + { + []interface{}{[]byte("-1")}, + nil, + false, + []uint{1}, + }, + { + []interface{}{[]byte("hello"), nil, []byte("world")}, + nil, + true, + [][]byte{[]byte("hello"), nil, []byte("world")}, + }, + { + []interface{}{[]byte("hello"), nil, []byte("world")}, + nil, + true, + []string{"hello", "", "world"}, + }, + { + []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, + nil, + true, + []struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}}, + }, + { + []interface{}{[]byte("a1"), []byte("b1")}, + nil, + false, + []struct{ A, B, C string }{{"a1", "b1", ""}}, + }, + { + []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, + nil, + true, + []*struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}}, + }, + { + []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, + []string{"A", "B"}, + true, + []struct{ A, C, B string }{{"a1", "", "b1"}, {"a2", "", "b2"}}, + }, + { + []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, + nil, + false, + []struct{}{}, + }, +} + +func TestScanSlice(t *testing.T) { + for _, tt := range scanSliceTests { + + typ := reflect.ValueOf(tt.dest).Type() + dest := reflect.New(typ) + + err := redis.ScanSlice(tt.src, dest.Interface(), tt.fieldNames...) + if tt.ok != (err == nil) { + t.Errorf("ScanSlice(%v, []%s, %v) returned error %v", tt.src, typ, tt.fieldNames, err) + continue + } + if tt.ok && !reflect.DeepEqual(dest.Elem().Interface(), tt.dest) { + t.Errorf("ScanSlice(src, []%s) returned %#v, want %#v", typ, dest.Elem().Interface(), tt.dest) + } + } +} + +func ExampleScanSlice() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + + c.Send("HMSET", "album:1", "title", "Red", "rating", 5) + c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1) + c.Send("HMSET", "album:3", "title", "Beat", "rating", 4) + c.Send("LPUSH", "albums", "1") + c.Send("LPUSH", "albums", "2") + c.Send("LPUSH", "albums", "3") + values, err := redis.Values(c.Do("SORT", "albums", + "BY", "album:*->rating", + "GET", "album:*->title", + "GET", "album:*->rating")) + if err != nil { + fmt.Println(err) + return + } + + var albums []struct { + Title string + Rating int + } + if err := redis.ScanSlice(values, &albums); err != nil { + fmt.Println(err) + return + } + fmt.Printf("%v\n", albums) + // Output: + // [{Earthbound 1} {Beat 4} {Red 5}] +} + +var argsTests = []struct { + title string + actual redis.Args + expected redis.Args +}{ + {"struct ptr", + redis.Args{}.AddFlat(&struct { + I int `redis:"i"` + U uint `redis:"u"` + S string `redis:"s"` + P []byte `redis:"p"` + M map[string]string `redis:"m"` + Bt bool + Bf bool + }{ + -1234, 5678, "hello", []byte("world"), map[string]string{"hello": "world"}, true, false, + }), + redis.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "m", map[string]string{"hello": "world"}, "Bt", true, "Bf", false}, + }, + {"struct", + redis.Args{}.AddFlat(struct{ I int }{123}), + redis.Args{"I", 123}, + }, + {"slice", + redis.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2), + redis.Args{1, "a", "b", "c", 2}, + }, + {"struct omitempty", + redis.Args{}.AddFlat(&struct { + I int `redis:"i,omitempty"` + U uint `redis:"u,omitempty"` + S string `redis:"s,omitempty"` + P []byte `redis:"p,omitempty"` + M map[string]string `redis:"m,omitempty"` + Bt bool `redis:"Bt,omitempty"` + Bf bool `redis:"Bf,omitempty"` + }{ + 0, 0, "", []byte{}, map[string]string{}, true, false, + }), + redis.Args{"Bt", true}, + }, +} + +func TestArgs(t *testing.T) { + for _, tt := range argsTests { + if !reflect.DeepEqual(tt.actual, tt.expected) { + t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected) + } + } +} + +func ExampleArgs() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + + var p1, p2 struct { + Title string `redis:"title"` + Author string `redis:"author"` + Body string `redis:"body"` + } + + p1.Title = "Example" + p1.Author = "Gary" + p1.Body = "Hello" + + if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil { + fmt.Println(err) + return + } + + m := map[string]string{ + "title": "Example2", + "author": "Steve", + "body": "Map", + } + + if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil { + fmt.Println(err) + return + } + + for _, id := range []string{"id1", "id2"} { + + v, err := redis.Values(c.Do("HGETALL", id)) + if err != nil { + fmt.Println(err) + return + } + + if err := redis.ScanStruct(v, &p2); err != nil { + fmt.Println(err) + return + } + + fmt.Printf("%+v\n", p2) + } + + // Output: + // {Title:Example Author:Gary Body:Hello} + // {Title:Example2 Author:Steve Body:Map} +} diff --git a/vendor/github.com/garyburd/redigo/redis/script_test.go b/vendor/github.com/garyburd/redigo/redis/script_test.go new file mode 100644 index 000000000..af282415c --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/script_test.go @@ -0,0 +1,100 @@ +// Copyright 2012 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redis_test + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/garyburd/redigo/redis" +) + +var ( + // These variables are declared at package level to remove distracting + // details from the examples. + c redis.Conn + reply interface{} + err error +) + +func ExampleScript() { + // Initialize a package-level variable with a script. + var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`) + + // In a function, use the script Do method to evaluate the script. The Do + // method optimistically uses the EVALSHA command. If the script is not + // loaded, then the Do method falls back to the EVAL command. + reply, err = getScript.Do(c, "foo") +} + +func TestScript(t *testing.T) { + c, err := redis.DialDefaultServer() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + defer c.Close() + + // To test fall back in Do, we make script unique by adding comment with current time. + script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano()) + s := redis.NewScript(2, script) + reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")} + + v, err := s.Do(c, "key1", "key2", "arg1", "arg2") + if err != nil { + t.Errorf("s.Do(c, ...) returned %v", err) + } + + if !reflect.DeepEqual(v, reply) { + t.Errorf("s.Do(c, ..); = %v, want %v", v, reply) + } + + err = s.Load(c) + if err != nil { + t.Errorf("s.Load(c) returned %v", err) + } + + err = s.SendHash(c, "key1", "key2", "arg1", "arg2") + if err != nil { + t.Errorf("s.SendHash(c, ...) returned %v", err) + } + + err = c.Flush() + if err != nil { + t.Errorf("c.Flush() returned %v", err) + } + + v, err = c.Receive() + if !reflect.DeepEqual(v, reply) { + t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply) + } + + err = s.Send(c, "key1", "key2", "arg1", "arg2") + if err != nil { + t.Errorf("s.Send(c, ...) returned %v", err) + } + + err = c.Flush() + if err != nil { + t.Errorf("c.Flush() returned %v", err) + } + + v, err = c.Receive() + if !reflect.DeepEqual(v, reply) { + t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply) + } + +} diff --git a/vendor/github.com/garyburd/redigo/redis/test_test.go b/vendor/github.com/garyburd/redigo/redis/test_test.go new file mode 100644 index 000000000..7240fa1f3 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/test_test.go @@ -0,0 +1,177 @@ +// Copyright 2012 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redis + +import ( + "bufio" + "errors" + "flag" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "strconv" + "strings" + "sync" + "testing" + "time" +) + +func SetNowFunc(f func() time.Time) { + nowFunc = f +} + +var ( + ErrNegativeInt = errNegativeInt + + serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary") + serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers") + serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`") + serverLog = ioutil.Discard + + defaultServerMu sync.Mutex + defaultServer *Server + defaultServerErr error +) + +type Server struct { + name string + cmd *exec.Cmd + done chan struct{} +} + +func NewServer(name string, args ...string) (*Server, error) { + s := &Server{ + name: name, + cmd: exec.Command(*serverPath, args...), + done: make(chan struct{}), + } + + r, err := s.cmd.StdoutPipe() + if err != nil { + return nil, err + } + + err = s.cmd.Start() + if err != nil { + return nil, err + } + + ready := make(chan error, 1) + go s.watch(r, ready) + + select { + case err = <-ready: + case <-time.After(time.Second * 10): + err = errors.New("timeout waiting for server to start") + } + + if err != nil { + s.Stop() + return nil, err + } + + return s, nil +} + +func (s *Server) watch(r io.Reader, ready chan error) { + fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name) + var listening bool + var text string + scn := bufio.NewScanner(r) + for scn.Scan() { + text = scn.Text() + fmt.Fprintf(serverLog, "%s\n", text) + if !listening { + if strings.Contains(text, "The server is now ready to accept connections on port") { + listening = true + ready <- nil + } + } + } + if !listening { + ready <- fmt.Errorf("server exited: %s", text) + } + s.cmd.Wait() + fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name) + close(s.done) +} + +func (s *Server) Stop() { + s.cmd.Process.Signal(os.Interrupt) + <-s.done +} + +// stopDefaultServer stops the server created by DialDefaultServer. +func stopDefaultServer() { + defaultServerMu.Lock() + defer defaultServerMu.Unlock() + if defaultServer != nil { + defaultServer.Stop() + defaultServer = nil + } +} + +// startDefaultServer starts the default server if not already running. +func startDefaultServer() error { + defaultServerMu.Lock() + defer defaultServerMu.Unlock() + if defaultServer != nil || defaultServerErr != nil { + return defaultServerErr + } + defaultServer, defaultServerErr = NewServer( + "default", + "--port", strconv.Itoa(*serverBasePort), + "--save", "", + "--appendonly", "no") + return defaultServerErr +} + +// DialDefaultServer starts the test server if not already started and dials a +// connection to the server. +func DialDefaultServer() (Conn, error) { + if err := startDefaultServer(); err != nil { + return nil, err + } + c, err := Dial("tcp", fmt.Sprintf(":%d", *serverBasePort), DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second)) + if err != nil { + return nil, err + } + c.Do("FLUSHDB") + return c, nil +} + +func TestMain(m *testing.M) { + os.Exit(func() int { + flag.Parse() + + var f *os.File + if *serverLogName != "" { + var err error + f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600) + if err != nil { + fmt.Fprintf(os.Stderr, "Error opening redis-log: %v\n", err) + return 1 + } + defer f.Close() + serverLog = f + } + + defer stopDefaultServer() + + return m.Run() + }()) +} diff --git a/vendor/github.com/garyburd/redigo/redis/zpop_example_test.go b/vendor/github.com/garyburd/redigo/redis/zpop_example_test.go new file mode 100644 index 000000000..1d86ee6ce --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redis/zpop_example_test.go @@ -0,0 +1,113 @@ +// Copyright 2013 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redis_test + +import ( + "fmt" + "github.com/garyburd/redigo/redis" +) + +// zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands. +func zpop(c redis.Conn, key string) (result string, err error) { + + defer func() { + // Return connection to normal state on error. + if err != nil { + c.Do("DISCARD") + } + }() + + // Loop until transaction is successful. + for { + if _, err := c.Do("WATCH", key); err != nil { + return "", err + } + + members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0)) + if err != nil { + return "", err + } + if len(members) != 1 { + return "", redis.ErrNil + } + + c.Send("MULTI") + c.Send("ZREM", key, members[0]) + queued, err := c.Do("EXEC") + if err != nil { + return "", err + } + + if queued != nil { + result = members[0] + break + } + } + + return result, nil +} + +// zpopScript pops a value from a ZSET. +var zpopScript = redis.NewScript(1, ` + local r = redis.call('ZRANGE', KEYS[1], 0, 0) + if r ~= nil then + r = r[1] + redis.call('ZREM', KEYS[1], r) + end + return r +`) + +// This example implements ZPOP as described at +// http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting. +func Example_zpop() { + c, err := dial() + if err != nil { + fmt.Println(err) + return + } + defer c.Close() + + // Add test data using a pipeline. + + for i, member := range []string{"red", "blue", "green"} { + c.Send("ZADD", "zset", i, member) + } + if _, err := c.Do(""); err != nil { + fmt.Println(err) + return + } + + // Pop using WATCH/MULTI/EXEC + + v, err := zpop(c, "zset") + if err != nil { + fmt.Println(err) + return + } + fmt.Println(v) + + // Pop using a script. + + v, err = redis.String(zpopScript.Do(c, "zset")) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(v) + + // Output: + // red + // blue +} diff --git a/vendor/github.com/garyburd/redigo/redisx/connmux.go b/vendor/github.com/garyburd/redigo/redisx/connmux.go new file mode 100644 index 000000000..af2cced3f --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redisx/connmux.go @@ -0,0 +1,152 @@ +// Copyright 2014 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redisx + +import ( + "errors" + "sync" + + "github.com/garyburd/redigo/internal" + "github.com/garyburd/redigo/redis" +) + +// ConnMux multiplexes one or more connections to a single underlying +// connection. The ConnMux connections do not support concurrency, commands +// that associate server side state with the connection or commands that put +// the connection in a special mode. +type ConnMux struct { + c redis.Conn + + sendMu sync.Mutex + sendID uint + + recvMu sync.Mutex + recvID uint + recvWait map[uint]chan struct{} +} + +func NewConnMux(c redis.Conn) *ConnMux { + return &ConnMux{c: c, recvWait: make(map[uint]chan struct{})} +} + +// Get gets a connection. The application must close the returned connection. +func (p *ConnMux) Get() redis.Conn { + c := &muxConn{p: p} + c.ids = c.buf[:0] + return c +} + +// Close closes the underlying connection. +func (p *ConnMux) Close() error { + return p.c.Close() +} + +type muxConn struct { + p *ConnMux + ids []uint + buf [8]uint +} + +func (c *muxConn) send(flush bool, cmd string, args ...interface{}) error { + if internal.LookupCommandInfo(cmd).Set != 0 { + return errors.New("command not supported by mux pool") + } + p := c.p + p.sendMu.Lock() + id := p.sendID + c.ids = append(c.ids, id) + p.sendID++ + err := p.c.Send(cmd, args...) + if flush { + err = p.c.Flush() + } + p.sendMu.Unlock() + return err +} + +func (c *muxConn) Send(cmd string, args ...interface{}) error { + return c.send(false, cmd, args...) +} + +func (c *muxConn) Flush() error { + p := c.p + p.sendMu.Lock() + err := p.c.Flush() + p.sendMu.Unlock() + return err +} + +func (c *muxConn) Receive() (interface{}, error) { + if len(c.ids) == 0 { + return nil, errors.New("mux pool underflow") + } + + id := c.ids[0] + c.ids = c.ids[1:] + if len(c.ids) == 0 { + c.ids = c.buf[:0] + } + + p := c.p + p.recvMu.Lock() + if p.recvID != id { + ch := make(chan struct{}) + p.recvWait[id] = ch + p.recvMu.Unlock() + <-ch + p.recvMu.Lock() + if p.recvID != id { + panic("out of sync") + } + } + + v, err := p.c.Receive() + + id++ + p.recvID = id + ch, ok := p.recvWait[id] + if ok { + delete(p.recvWait, id) + } + p.recvMu.Unlock() + if ok { + ch <- struct{}{} + } + + return v, err +} + +func (c *muxConn) Close() error { + var err error + if len(c.ids) == 0 { + return nil + } + c.Flush() + for _ = range c.ids { + _, err = c.Receive() + } + return err +} + +func (c *muxConn) Do(cmd string, args ...interface{}) (interface{}, error) { + if err := c.send(true, cmd, args...); err != nil { + return nil, err + } + return c.Receive() +} + +func (c *muxConn) Err() error { + return c.p.c.Err() +} diff --git a/vendor/github.com/garyburd/redigo/redisx/connmux_test.go b/vendor/github.com/garyburd/redigo/redisx/connmux_test.go new file mode 100644 index 000000000..9c3c8b162 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redisx/connmux_test.go @@ -0,0 +1,259 @@ +// Copyright 2014 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package redisx_test + +import ( + "net/textproto" + "sync" + "testing" + + "github.com/garyburd/redigo/internal/redistest" + "github.com/garyburd/redigo/redis" + "github.com/garyburd/redigo/redisx" +) + +func TestConnMux(t *testing.T) { + c, err := redistest.Dial() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + m := redisx.NewConnMux(c) + defer m.Close() + + c1 := m.Get() + c2 := m.Get() + c1.Send("ECHO", "hello") + c2.Send("ECHO", "world") + c1.Flush() + c2.Flush() + s, err := redis.String(c1.Receive()) + if err != nil { + t.Fatal(err) + } + if s != "hello" { + t.Fatalf("echo returned %q, want %q", s, "hello") + } + s, err = redis.String(c2.Receive()) + if err != nil { + t.Fatal(err) + } + if s != "world" { + t.Fatalf("echo returned %q, want %q", s, "world") + } + c1.Close() + c2.Close() +} + +func TestConnMuxClose(t *testing.T) { + c, err := redistest.Dial() + if err != nil { + t.Fatalf("error connection to database, %v", err) + } + m := redisx.NewConnMux(c) + defer m.Close() + + c1 := m.Get() + c2 := m.Get() + + if err := c1.Send("ECHO", "hello"); err != nil { + t.Fatal(err) + } + if err := c1.Close(); err != nil { + t.Fatal(err) + } + + if err := c2.Send("ECHO", "world"); err != nil { + t.Fatal(err) + } + if err := c2.Flush(); err != nil { + t.Fatal(err) + } + + s, err := redis.String(c2.Receive()) + if err != nil { + t.Fatal(err) + } + if s != "world" { + t.Fatalf("echo returned %q, want %q", s, "world") + } + c2.Close() +} + +func BenchmarkConn(b *testing.B) { + b.StopTimer() + c, err := redistest.Dial() + if err != nil { + b.Fatalf("error connection to database, %v", err) + } + defer c.Close() + b.StartTimer() + + for i := 0; i < b.N; i++ { + if _, err := c.Do("PING"); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkConnMux(b *testing.B) { + b.StopTimer() + c, err := redistest.Dial() + if err != nil { + b.Fatalf("error connection to database, %v", err) + } + m := redisx.NewConnMux(c) + defer m.Close() + + b.StartTimer() + + for i := 0; i < b.N; i++ { + c := m.Get() + if _, err := c.Do("PING"); err != nil { + b.Fatal(err) + } + c.Close() + } +} + +func BenchmarkPool(b *testing.B) { + b.StopTimer() + + p := redis.Pool{Dial: redistest.Dial, MaxIdle: 1} + defer p.Close() + + // Fill the pool. + c := p.Get() + if err := c.Err(); err != nil { + b.Fatal(err) + } + c.Close() + + b.StartTimer() + + for i := 0; i < b.N; i++ { + c := p.Get() + if _, err := c.Do("PING"); err != nil { + b.Fatal(err) + } + c.Close() + } +} + +const numConcurrent = 10 + +func BenchmarkConnMuxConcurrent(b *testing.B) { + b.StopTimer() + c, err := redistest.Dial() + if err != nil { + b.Fatalf("error connection to database, %v", err) + } + defer c.Close() + + m := redisx.NewConnMux(c) + + var wg sync.WaitGroup + wg.Add(numConcurrent) + + b.StartTimer() + + for i := 0; i < numConcurrent; i++ { + go func() { + defer wg.Done() + for i := 0; i < b.N; i++ { + c := m.Get() + if _, err := c.Do("PING"); err != nil { + b.Fatal(err) + } + c.Close() + } + }() + } + wg.Wait() +} + +func BenchmarkPoolConcurrent(b *testing.B) { + b.StopTimer() + + p := redis.Pool{Dial: redistest.Dial, MaxIdle: numConcurrent} + defer p.Close() + + // Fill the pool. + conns := make([]redis.Conn, numConcurrent) + for i := range conns { + c := p.Get() + if err := c.Err(); err != nil { + b.Fatal(err) + } + conns[i] = c + } + for _, c := range conns { + c.Close() + } + + var wg sync.WaitGroup + wg.Add(numConcurrent) + + b.StartTimer() + + for i := 0; i < numConcurrent; i++ { + go func() { + defer wg.Done() + for i := 0; i < b.N; i++ { + c := p.Get() + if _, err := c.Do("PING"); err != nil { + b.Fatal(err) + } + c.Close() + } + }() + } + wg.Wait() +} + +func BenchmarkPipelineConcurrency(b *testing.B) { + b.StopTimer() + c, err := redistest.Dial() + if err != nil { + b.Fatalf("error connection to database, %v", err) + } + defer c.Close() + + var wg sync.WaitGroup + wg.Add(numConcurrent) + + var pipeline textproto.Pipeline + + b.StartTimer() + + for i := 0; i < numConcurrent; i++ { + go func() { + defer wg.Done() + for i := 0; i < b.N; i++ { + id := pipeline.Next() + pipeline.StartRequest(id) + c.Send("PING") + c.Flush() + pipeline.EndRequest(id) + pipeline.StartResponse(id) + _, err := c.Receive() + if err != nil { + b.Fatal(err) + } + pipeline.EndResponse(id) + } + }() + } + wg.Wait() +} diff --git a/vendor/github.com/garyburd/redigo/redisx/doc.go b/vendor/github.com/garyburd/redigo/redisx/doc.go new file mode 100644 index 000000000..91653dbe2 --- /dev/null +++ b/vendor/github.com/garyburd/redigo/redisx/doc.go @@ -0,0 +1,17 @@ +// Copyright 2012 Gary Burd +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package redisx contains experimental features for Redigo. Features in this +// package may be modified or deleted at any time. +package redisx // import "github.com/garyburd/redigo/redisx" diff --git a/vendor/github.com/go-gorp/gorp/dialect_mysql_test.go b/vendor/github.com/go-gorp/gorp/dialect_mysql_test.go new file mode 100644 index 000000000..d1018cf69 --- /dev/null +++ b/vendor/github.com/go-gorp/gorp/dialect_mysql_test.go @@ -0,0 +1,204 @@ +// Copyright 2012 James Cooper. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +// Package gorp provides a simple way to marshal Go structs to and from +// SQL databases. It uses the database/sql package, and should work with any +// compliant database/sql driver. +// +// Source code and project home: +// https://github.com/go-gorp/gorp + +package gorp_test + +import ( + "database/sql" + "reflect" + "time" + + // ginkgo/gomega functions read better as dot-imports. + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" + + "github.com/go-gorp/gorp" +) + +var _ = Describe("MySQLDialect", func() { + var ( + engine, encoding string + dialect gorp.MySQLDialect + ) + + JustBeforeEach(func() { + dialect = gorp.MySQLDialect{ + Engine: engine, + Encoding: encoding, + } + }) + + DescribeTable("ToSqlType", + func(value interface{}, maxsize int, autoIncr bool, expected string) { + typ := reflect.TypeOf(value) + sqlType := dialect.ToSqlType(typ, maxsize, autoIncr) + Expect(sqlType).To(Equal(expected)) + }, + Entry("bool", true, 0, false, "boolean"), + Entry("int8", int8(1), 0, false, "tinyint"), + Entry("uint8", uint8(1), 0, false, "tinyint unsigned"), + Entry("int16", int16(1), 0, false, "smallint"), + Entry("uint16", uint16(1), 0, false, "smallint unsigned"), + Entry("int32", int32(1), 0, false, "int"), + Entry("int (treated as int32)", int(1), 0, false, "int"), + Entry("uint32", uint32(1), 0, false, "int unsigned"), + Entry("uint (treated as uint32)", uint(1), 0, false, "int unsigned"), + Entry("int64", int64(1), 0, false, "bigint"), + Entry("uint64", uint64(1), 0, false, "bigint unsigned"), + Entry("float32", float32(1), 0, false, "double"), + Entry("float64", float64(1), 0, false, "double"), + Entry("[]uint8", []uint8{1}, 0, false, "mediumblob"), + Entry("NullInt64", sql.NullInt64{}, 0, false, "bigint"), + Entry("NullFloat64", sql.NullFloat64{}, 0, false, "double"), + Entry("NullBool", sql.NullBool{}, 0, false, "tinyint"), + Entry("Time", time.Time{}, 0, false, "datetime"), + Entry("default-size string", "", 0, false, "varchar(255)"), + Entry("sized string", "", 50, false, "varchar(50)"), + Entry("large string", "", 1024, false, "text"), + ) + + Describe("AutoIncrStr", func() { + It("returns the auto increment string", func() { + Expect(dialect.AutoIncrStr()).To(Equal("auto_increment")) + }) + }) + + Describe("AutoIncrBindValue", func() { + It("returns the value used to bind the auto-increment value", func() { + Expect(dialect.AutoIncrBindValue()).To(Equal("null")) + }) + }) + + Describe("AutoIncrInsertSuffix", func() { + It("returns the suffix needed for auto-incrementing", func() { + Expect(dialect.AutoIncrInsertSuffix(nil)).To(BeEmpty()) + }) + }) + + Describe("CreateTableSuffix", func() { + Context("with an empty engine", func() { + BeforeEach(func() { + engine = "" + encoding = "foo" + }) + It("panics", func() { + Expect(func() { + dialect.CreateTableSuffix() + }).To(Panic()) + }) + }) + + Context("with an empty encoding", func() { + BeforeEach(func() { + engine = "foo" + encoding = "" + }) + It("panics", func() { + Expect(func() { + dialect.CreateTableSuffix() + }).To(Panic()) + }) + }) + + Context("with an engine and an encoding", func() { + BeforeEach(func() { + engine = "foo" + encoding = "bar" + }) + It("returns a valid suffix", func() { + Expect(dialect.CreateTableSuffix()).To(Equal(" engine=foo charset=bar")) + }) + }) + }) + + Describe("CreateIndexSuffix", func() { + It("returns the suffix for creating indexes", func() { + Expect(dialect.CreateIndexSuffix()).To(Equal("using")) + }) + }) + + Describe("DropIndexSuffix", func() { + It("returns the suffix for deleting indexes", func() { + Expect(dialect.DropIndexSuffix()).To(Equal("on")) + }) + }) + + Describe("TruncateClause", func() { + It("returns the clause for truncating a table", func() { + Expect(dialect.TruncateClause()).To(Equal("truncate")) + }) + }) + + Describe("BindVar", func() { + It("returns the variable binding sequence", func() { + Expect(dialect.BindVar(0)).To(Equal("?")) + }) + }) + + PDescribe("InsertAutoIncr", func() {}) + + Describe("QuoteField", func() { + It("returns the argument quoted as a field", func() { + Expect(dialect.QuoteField("foo")).To(Equal("`foo`")) + }) + }) + + Describe("QuotedTableForQuery", func() { + var ( + schema, table string + + quotedTable string + ) + + JustBeforeEach(func() { + quotedTable = dialect.QuotedTableForQuery(schema, table) + }) + + Context("using the default schema", func() { + BeforeEach(func() { + schema = "" + table = "foo" + }) + It("returns just the table", func() { + Expect(quotedTable).To(Equal("`foo`")) + }) + }) + + Context("with a supplied schema", func() { + BeforeEach(func() { + schema = "foo" + table = "bar" + }) + It("returns the schema and table", func() { + Expect(quotedTable).To(Equal("foo.`bar`")) + }) + }) + }) + + Describe("IfSchemaNotExists", func() { + It("appends 'if not exists' to the command", func() { + Expect(dialect.IfSchemaNotExists("foo", "bar")).To(Equal("foo if not exists")) + }) + }) + + Describe("IfTableExists", func() { + It("appends 'if exists' to the command", func() { + Expect(dialect.IfTableExists("foo", "bar", "baz")).To(Equal("foo if exists")) + }) + }) + + Describe("IfTableNotExists", func() { + It("appends 'if not exists' to the command", func() { + Expect(dialect.IfTableNotExists("foo", "bar", "baz")).To(Equal("foo if not exists")) + }) + }) +}) diff --git a/vendor/github.com/go-gorp/gorp/gorp_suite_test.go b/vendor/github.com/go-gorp/gorp/gorp_suite_test.go new file mode 100644 index 000000000..2abcaaf71 --- /dev/null +++ b/vendor/github.com/go-gorp/gorp/gorp_suite_test.go @@ -0,0 +1,13 @@ +package gorp_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestGorp(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Gorp Suite") +} diff --git a/vendor/github.com/go-gorp/gorp/gorp_test.go b/vendor/github.com/go-gorp/gorp/gorp_test.go new file mode 100644 index 000000000..7b2307158 --- /dev/null +++ b/vendor/github.com/go-gorp/gorp/gorp_test.go @@ -0,0 +1,2412 @@ +// Copyright 2012 James Cooper. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +// Package gorp provides a simple way to marshal Go structs to and from +// SQL databases. It uses the database/sql package, and should work with any +// compliant database/sql driver. +// +// Source code and project home: +// https://github.com/go-gorp/gorp + +package gorp_test + +import ( + "bytes" + "database/sql" + "database/sql/driver" + "encoding/json" + "errors" + "flag" + "fmt" + "log" + "math/rand" + "os" + "reflect" + "strconv" + "strings" + "testing" + "time" + + "github.com/go-gorp/gorp" + + _ "github.com/go-sql-driver/mysql" + _ "github.com/lib/pq" + _ "github.com/mattn/go-sqlite3" + _ "github.com/ziutek/mymysql/godrv" +) + +var ( + // verify interface compliance + _ = []gorp.Dialect{ + gorp.SqliteDialect{}, + gorp.PostgresDialect{}, + gorp.MySQLDialect{}, + gorp.SqlServerDialect{}, + gorp.OracleDialect{}, + } + + debug bool +) + +func init() { + flag.BoolVar(&debug, "trace", true, "Turn on or off database tracing (DbMap.TraceOn)") + flag.Parse() +} + +type testable interface { + GetId() int64 + Rand() +} + +type Invoice struct { + Id int64 + Created int64 + Updated int64 + Memo string + PersonId int64 + IsPaid bool +} + +type InvoiceWithValuer struct { + Id int64 + Created int64 + Updated int64 + Memo string + Person PersonValuerScanner `db:"personid"` + IsPaid bool +} + +func (me *Invoice) GetId() int64 { return me.Id } +func (me *Invoice) Rand() { + me.Memo = fmt.Sprintf("random %d", rand.Int63()) + me.Created = rand.Int63() + me.Updated = rand.Int63() +} + +type InvoiceTag struct { + Id int64 `db:"myid, primarykey, autoincrement"` + Created int64 `db:"myCreated"` + Updated int64 `db:"date_updated"` + Memo string + PersonId int64 `db:"person_id"` + IsPaid bool `db:"is_Paid"` +} + +func (me *InvoiceTag) GetId() int64 { return me.Id } +func (me *InvoiceTag) Rand() { + me.Memo = fmt.Sprintf("random %d", rand.Int63()) + me.Created = rand.Int63() + me.Updated = rand.Int63() +} + +// See: https://github.com/go-gorp/gorp/issues/175 +type AliasTransientField struct { + Id int64 `db:"id"` + Bar int64 `db:"-"` + BarStr string `db:"bar"` +} + +func (me *AliasTransientField) GetId() int64 { return me.Id } +func (me *AliasTransientField) Rand() { + me.BarStr = fmt.Sprintf("random %d", rand.Int63()) +} + +type OverriddenInvoice struct { + Invoice + Id string +} + +type Person struct { + Id int64 + Created int64 + Updated int64 + FName string + LName string + Version int64 +} + +// PersonValuerScanner is used as a field in test types to ensure that we +// make use of "database/sql/driver".Valuer for choosing column types when +// creating tables and that we don't get in the way of the underlying +// database libraries when they make use of either Valuer or +// "database/sql".Scanner. +type PersonValuerScanner struct { + Person +} + +// Value implements "database/sql/driver".Valuer. It will be automatically +// run by the "database/sql" package when inserting/updating data. +func (p PersonValuerScanner) Value() (driver.Value, error) { + return p.Id, nil +} + +// Scan implements "database/sql".Scanner. It will be automatically run +// by the "database/sql" package when reading column data into a field +// of type PersonValuerScanner. +func (p *PersonValuerScanner) Scan(value interface{}) (err error) { + switch src := value.(type) { + case []byte: + // TODO: this case is here for mysql only. For some reason, + // one (both?) of the mysql libraries opt to pass us a []byte + // instead of an int64 for the bigint column. We should add + // table tests around valuers/scanners and try to solve these + // types of odd discrepencies to make it easier for users of + // gorp to migrate to other database engines. + p.Id, err = strconv.ParseInt(string(src), 10, 64) + case int64: + // Most libraries pass in the type we'd expect. + p.Id = src + default: + typ := reflect.TypeOf(value) + return fmt.Errorf("Expected person value to be convertible to int64, got %v (type %s)", value, typ) + } + return +} + +type FNameOnly struct { + FName string +} + +type InvoicePersonView struct { + InvoiceId int64 + PersonId int64 + Memo string + FName string + LegacyVersion int64 +} + +type TableWithNull struct { + Id int64 + Str sql.NullString + Int64 sql.NullInt64 + Float64 sql.NullFloat64 + Bool sql.NullBool + Bytes []byte +} + +type WithIgnoredColumn struct { + internal int64 `db:"-"` + Id int64 + Created int64 +} + +type IdCreated struct { + Id int64 + Created int64 +} + +type IdCreatedExternal struct { + IdCreated + External int64 +} + +type WithStringPk struct { + Id string + Name string +} + +type CustomStringType string + +type TypeConversionExample struct { + Id int64 + PersonJSON Person + Name CustomStringType +} + +type PersonUInt32 struct { + Id uint32 + Name string +} + +type PersonUInt64 struct { + Id uint64 + Name string +} + +type PersonUInt16 struct { + Id uint16 + Name string +} + +type WithEmbeddedStruct struct { + Id int64 + Names +} + +type WithEmbeddedStructConflictingEmbeddedMemberNames struct { + Id int64 + Names + NamesConflict +} + +type WithEmbeddedStructSameMemberName struct { + Id int64 + SameName +} + +type WithEmbeddedStructBeforeAutoincrField struct { + Names + Id int64 +} + +type WithEmbeddedAutoincr struct { + WithEmbeddedStruct + MiddleName string +} + +type Names struct { + FirstName string + LastName string +} + +type NamesConflict struct { + FirstName string + Surname string +} + +type SameName struct { + SameName string +} + +type UniqueColumns struct { + FirstName string + LastName string + City string + ZipCode int64 +} + +type SingleColumnTable struct { + SomeId string +} + +type CustomDate struct { + time.Time +} + +type WithCustomDate struct { + Id int64 + Added CustomDate +} + +type WithNullTime struct { + Id int64 + Time gorp.NullTime +} + +type testTypeConverter struct{} + +func (me testTypeConverter) ToDb(val interface{}) (interface{}, error) { + + switch t := val.(type) { + case Person: + b, err := json.Marshal(t) + if err != nil { + return "", err + } + return string(b), nil + case CustomStringType: + return string(t), nil + case CustomDate: + return t.Time, nil + } + + return val, nil +} + +func (me testTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) { + switch target.(type) { + case *Person: + binder := func(holder, target interface{}) error { + s, ok := holder.(*string) + if !ok { + return errors.New("FromDb: Unable to convert Person to *string") + } + b := []byte(*s) + return json.Unmarshal(b, target) + } + return gorp.CustomScanner{new(string), target, binder}, true + case *CustomStringType: + binder := func(holder, target interface{}) error { + s, ok := holder.(*string) + if !ok { + return errors.New("FromDb: Unable to convert CustomStringType to *string") + } + st, ok := target.(*CustomStringType) + if !ok { + return errors.New(fmt.Sprint("FromDb: Unable to convert target to *CustomStringType: ", reflect.TypeOf(target))) + } + *st = CustomStringType(*s) + return nil + } + return gorp.CustomScanner{new(string), target, binder}, true + case *CustomDate: + binder := func(holder, target interface{}) error { + t, ok := holder.(*time.Time) + if !ok { + return errors.New("FromDb: Unable to convert CustomDate to *time.Time") + } + dateTarget, ok := target.(*CustomDate) + if !ok { + return errors.New(fmt.Sprint("FromDb: Unable to convert target to *CustomDate: ", reflect.TypeOf(target))) + } + dateTarget.Time = *t + return nil + } + return gorp.CustomScanner{new(time.Time), target, binder}, true + } + + return gorp.CustomScanner{}, false +} + +func (p *Person) PreInsert(s gorp.SqlExecutor) error { + p.Created = time.Now().UnixNano() + p.Updated = p.Created + if p.FName == "badname" { + return fmt.Errorf("Invalid name: %s", p.FName) + } + return nil +} + +func (p *Person) PostInsert(s gorp.SqlExecutor) error { + p.LName = "postinsert" + return nil +} + +func (p *Person) PreUpdate(s gorp.SqlExecutor) error { + p.FName = "preupdate" + return nil +} + +func (p *Person) PostUpdate(s gorp.SqlExecutor) error { + p.LName = "postupdate" + return nil +} + +func (p *Person) PreDelete(s gorp.SqlExecutor) error { + p.FName = "predelete" + return nil +} + +func (p *Person) PostDelete(s gorp.SqlExecutor) error { + p.LName = "postdelete" + return nil +} + +func (p *Person) PostGet(s gorp.SqlExecutor) error { + p.LName = "postget" + return nil +} + +type PersistentUser struct { + Key int32 + Id string + PassedTraining bool +} + +func TestCreateTablesIfNotExists(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + err := dbmap.CreateTablesIfNotExists() + if err != nil { + t.Error(err) + } +} + +func TestTruncateTables(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + err := dbmap.CreateTablesIfNotExists() + if err != nil { + t.Error(err) + } + + // Insert some data + p1 := &Person{0, 0, 0, "Bob", "Smith", 0} + dbmap.Insert(p1) + inv := &Invoice{0, 0, 1, "my invoice", 0, true} + dbmap.Insert(inv) + + err = dbmap.TruncateTables() + if err != nil { + t.Error(err) + } + + // Make sure all rows are deleted + rows, _ := dbmap.Select(Person{}, "SELECT * FROM person_test") + if len(rows) != 0 { + t.Errorf("Expected 0 person rows, got %d", len(rows)) + } + rows, _ = dbmap.Select(Invoice{}, "SELECT * FROM invoice_test") + if len(rows) != 0 { + t.Errorf("Expected 0 invoice rows, got %d", len(rows)) + } +} + +func TestCustomDateType(t *testing.T) { + dbmap := newDbMap() + dbmap.TypeConverter = testTypeConverter{} + dbmap.AddTable(WithCustomDate{}).SetKeys(true, "Id") + err := dbmap.CreateTables() + if err != nil { + panic(err) + } + defer dropAndClose(dbmap) + + test1 := &WithCustomDate{Added: CustomDate{Time: time.Now().Truncate(time.Second)}} + err = dbmap.Insert(test1) + if err != nil { + t.Errorf("Could not insert struct with custom date field: %s", err) + t.FailNow() + } + // Unfortunately, the mysql driver doesn't handle time.Time + // values properly during Get(). I can't find a way to work + // around that problem - every other type that I've tried is just + // silently converted. time.Time is the only type that causes + // the issue that this test checks for. As such, if the driver is + // mysql, we'll just skip the rest of this test. + if _, driver := dialectAndDriver(); driver == "mysql" { + t.Skip("TestCustomDateType can't run Get() with the mysql driver; skipping the rest of this test...") + } + result, err := dbmap.Get(new(WithCustomDate), test1.Id) + if err != nil { + t.Errorf("Could not get struct with custom date field: %s", err) + t.FailNow() + } + test2 := result.(*WithCustomDate) + if test2.Added.UTC() != test1.Added.UTC() { + t.Errorf("Custom dates do not match: %v != %v", test2.Added.UTC(), test1.Added.UTC()) + } +} + +func TestUIntPrimaryKey(t *testing.T) { + dbmap := newDbMap() + dbmap.AddTable(PersonUInt64{}).SetKeys(true, "Id") + dbmap.AddTable(PersonUInt32{}).SetKeys(true, "Id") + dbmap.AddTable(PersonUInt16{}).SetKeys(true, "Id") + err := dbmap.CreateTablesIfNotExists() + if err != nil { + panic(err) + } + defer dropAndClose(dbmap) + + p1 := &PersonUInt64{0, "name1"} + p2 := &PersonUInt32{0, "name2"} + p3 := &PersonUInt16{0, "name3"} + err = dbmap.Insert(p1, p2, p3) + if err != nil { + t.Error(err) + } + if p1.Id != 1 { + t.Errorf("%d != 1", p1.Id) + } + if p2.Id != 1 { + t.Errorf("%d != 1", p2.Id) + } + if p3.Id != 1 { + t.Errorf("%d != 1", p3.Id) + } +} + +func TestSetUniqueTogether(t *testing.T) { + dbmap := newDbMap() + dbmap.AddTable(UniqueColumns{}).SetUniqueTogether("FirstName", "LastName").SetUniqueTogether("City", "ZipCode") + err := dbmap.CreateTablesIfNotExists() + if err != nil { + panic(err) + } + defer dropAndClose(dbmap) + + n1 := &UniqueColumns{"Steve", "Jobs", "Cupertino", 95014} + err = dbmap.Insert(n1) + if err != nil { + t.Error(err) + } + + // Should fail because of the first constraint + n2 := &UniqueColumns{"Steve", "Jobs", "Sunnyvale", 94085} + err = dbmap.Insert(n2) + if err == nil { + t.Error(err) + } + // "unique" for Postgres/SQLite, "Duplicate entry" for MySQL + errLower := strings.ToLower(err.Error()) + if !strings.Contains(errLower, "unique") && !strings.Contains(errLower, "duplicate entry") { + t.Error(err) + } + + // Should also fail because of the second unique-together + n3 := &UniqueColumns{"Steve", "Wozniak", "Cupertino", 95014} + err = dbmap.Insert(n3) + if err == nil { + t.Error(err) + } + // "unique" for Postgres/SQLite, "Duplicate entry" for MySQL + errLower = strings.ToLower(err.Error()) + if !strings.Contains(errLower, "unique") && !strings.Contains(errLower, "duplicate entry") { + t.Error(err) + } + + // This one should finally succeed + n4 := &UniqueColumns{"Steve", "Wozniak", "Sunnyvale", 94085} + err = dbmap.Insert(n4) + if err != nil { + t.Error(err) + } +} + +func TestPersistentUser(t *testing.T) { + dbmap := newDbMap() + dbmap.Exec("drop table if exists PersistentUser") + table := dbmap.AddTable(PersistentUser{}).SetKeys(false, "Key") + table.ColMap("Key").Rename("mykey") + err := dbmap.CreateTablesIfNotExists() + if err != nil { + panic(err) + } + defer dropAndClose(dbmap) + pu := &PersistentUser{43, "33r", false} + err = dbmap.Insert(pu) + if err != nil { + panic(err) + } + + // prove we can pass a pointer into Get + pu2, err := dbmap.Get(pu, pu.Key) + if err != nil { + panic(err) + } + if !reflect.DeepEqual(pu, pu2) { + t.Errorf("%v!=%v", pu, pu2) + } + + arr, err := dbmap.Select(pu, "select * from "+tableName(dbmap, PersistentUser{})) + if err != nil { + panic(err) + } + if !reflect.DeepEqual(pu, arr[0]) { + t.Errorf("%v!=%v", pu, arr[0]) + } + + // prove we can get the results back in a slice + var puArr []*PersistentUser + _, err = dbmap.Select(&puArr, "select * from "+tableName(dbmap, PersistentUser{})) + if err != nil { + panic(err) + } + if len(puArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + if !reflect.DeepEqual(pu, puArr[0]) { + t.Errorf("%v!=%v", pu, puArr[0]) + } + + // prove we can get the results back in a non-pointer slice + var puValues []PersistentUser + _, err = dbmap.Select(&puValues, "select * from "+tableName(dbmap, PersistentUser{})) + if err != nil { + panic(err) + } + if len(puValues) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + if !reflect.DeepEqual(*pu, puValues[0]) { + t.Errorf("%v!=%v", *pu, puValues[0]) + } + + // prove we can get the results back in a string slice + var idArr []*string + _, err = dbmap.Select(&idArr, "select "+columnName(dbmap, PersistentUser{}, "Id")+" from "+tableName(dbmap, PersistentUser{})) + if err != nil { + panic(err) + } + if len(idArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + if !reflect.DeepEqual(pu.Id, *idArr[0]) { + t.Errorf("%v!=%v", pu.Id, *idArr[0]) + } + + // prove we can get the results back in an int slice + var keyArr []*int32 + _, err = dbmap.Select(&keyArr, "select mykey from "+tableName(dbmap, PersistentUser{})) + if err != nil { + panic(err) + } + if len(keyArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + if !reflect.DeepEqual(pu.Key, *keyArr[0]) { + t.Errorf("%v!=%v", pu.Key, *keyArr[0]) + } + + // prove we can get the results back in a bool slice + var passedArr []*bool + _, err = dbmap.Select(&passedArr, "select "+columnName(dbmap, PersistentUser{}, "PassedTraining")+" from "+tableName(dbmap, PersistentUser{})) + if err != nil { + panic(err) + } + if len(passedArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + if !reflect.DeepEqual(pu.PassedTraining, *passedArr[0]) { + t.Errorf("%v!=%v", pu.PassedTraining, *passedArr[0]) + } + + // prove we can get the results back in a non-pointer slice + var stringArr []string + _, err = dbmap.Select(&stringArr, "select "+columnName(dbmap, PersistentUser{}, "Id")+" from "+tableName(dbmap, PersistentUser{})) + if err != nil { + panic(err) + } + if len(stringArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + if !reflect.DeepEqual(pu.Id, stringArr[0]) { + t.Errorf("%v!=%v", pu.Id, stringArr[0]) + } +} + +func TestNamedQueryMap(t *testing.T) { + dbmap := newDbMap() + dbmap.Exec("drop table if exists PersistentUser") + table := dbmap.AddTable(PersistentUser{}).SetKeys(false, "Key") + table.ColMap("Key").Rename("mykey") + err := dbmap.CreateTablesIfNotExists() + if err != nil { + panic(err) + } + defer dropAndClose(dbmap) + pu := &PersistentUser{43, "33r", false} + pu2 := &PersistentUser{500, "abc", false} + err = dbmap.Insert(pu, pu2) + if err != nil { + panic(err) + } + + // Test simple case + var puArr []*PersistentUser + _, err = dbmap.Select(&puArr, "select * from "+tableName(dbmap, PersistentUser{})+" where mykey = :Key", map[string]interface{}{ + "Key": 43, + }) + if err != nil { + t.Errorf("Failed to select: %s", err) + t.FailNow() + } + if len(puArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + if !reflect.DeepEqual(pu, puArr[0]) { + t.Errorf("%v!=%v", pu, puArr[0]) + } + + // Test more specific map value type is ok + puArr = nil + _, err = dbmap.Select(&puArr, "select * from "+tableName(dbmap, PersistentUser{})+" where mykey = :Key", map[string]int{ + "Key": 43, + }) + if err != nil { + t.Errorf("Failed to select: %s", err) + t.FailNow() + } + if len(puArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + + // Test multiple parameters set. + puArr = nil + _, err = dbmap.Select(&puArr, ` +select * from `+tableName(dbmap, PersistentUser{})+` + where mykey = :Key + and `+columnName(dbmap, PersistentUser{}, "PassedTraining")+` = :PassedTraining + and `+columnName(dbmap, PersistentUser{}, "Id")+` = :Id`, map[string]interface{}{ + "Key": 43, + "PassedTraining": false, + "Id": "33r", + }) + if err != nil { + t.Errorf("Failed to select: %s", err) + t.FailNow() + } + if len(puArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + + // Test colon within a non-key string + // Test having extra, unused properties in the map. + puArr = nil + _, err = dbmap.Select(&puArr, ` +select * from `+tableName(dbmap, PersistentUser{})+` + where mykey = :Key + and `+columnName(dbmap, PersistentUser{}, "Id")+` != 'abc:def'`, map[string]interface{}{ + "Key": 43, + "PassedTraining": false, + }) + if err != nil { + t.Errorf("Failed to select: %s", err) + t.FailNow() + } + if len(puArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + + // Test to delete with Exec and named params. + result, err := dbmap.Exec("delete from "+tableName(dbmap, PersistentUser{})+" where mykey = :Key", map[string]interface{}{ + "Key": 43, + }) + count, err := result.RowsAffected() + if err != nil { + t.Errorf("Failed to exec: %s", err) + t.FailNow() + } + if count != 1 { + t.Errorf("Expected 1 persistentuser to be deleted, but %d deleted", count) + } +} + +func TestNamedQueryStruct(t *testing.T) { + dbmap := newDbMap() + dbmap.Exec("drop table if exists PersistentUser") + table := dbmap.AddTable(PersistentUser{}).SetKeys(false, "Key") + table.ColMap("Key").Rename("mykey") + err := dbmap.CreateTablesIfNotExists() + if err != nil { + panic(err) + } + defer dropAndClose(dbmap) + pu := &PersistentUser{43, "33r", false} + pu2 := &PersistentUser{500, "abc", false} + err = dbmap.Insert(pu, pu2) + if err != nil { + panic(err) + } + + // Test select self + var puArr []*PersistentUser + _, err = dbmap.Select(&puArr, ` +select * from `+tableName(dbmap, PersistentUser{})+` + where mykey = :Key + and `+columnName(dbmap, PersistentUser{}, "PassedTraining")+` = :PassedTraining + and `+columnName(dbmap, PersistentUser{}, "Id")+` = :Id`, pu) + if err != nil { + t.Errorf("Failed to select: %s", err) + t.FailNow() + } + if len(puArr) != 1 { + t.Errorf("Expected one persistentuser, found none") + } + if !reflect.DeepEqual(pu, puArr[0]) { + t.Errorf("%v!=%v", pu, puArr[0]) + } + + // Test delete self. + result, err := dbmap.Exec(` +delete from `+tableName(dbmap, PersistentUser{})+` + where mykey = :Key + and `+columnName(dbmap, PersistentUser{}, "PassedTraining")+` = :PassedTraining + and `+columnName(dbmap, PersistentUser{}, "Id")+` = :Id`, pu) + count, err := result.RowsAffected() + if err != nil { + t.Errorf("Failed to exec: %s", err) + t.FailNow() + } + if count != 1 { + t.Errorf("Expected 1 persistentuser to be deleted, but %d deleted", count) + } +} + +// Ensure that the slices containing SQL results are non-nil when the result set is empty. +func TestReturnsNonNilSlice(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + noResultsSQL := "select * from invoice_test where " + columnName(dbmap, Invoice{}, "Id") + "=99999" + var r1 []*Invoice + rawSelect(dbmap, &r1, noResultsSQL) + if r1 == nil { + t.Errorf("r1==nil") + } + + r2 := rawSelect(dbmap, Invoice{}, noResultsSQL) + if r2 == nil { + t.Errorf("r2==nil") + } +} + +func TestOverrideVersionCol(t *testing.T) { + dbmap := newDbMap() + t1 := dbmap.AddTable(InvoicePersonView{}).SetKeys(false, "InvoiceId", "PersonId") + err := dbmap.CreateTables() + if err != nil { + panic(err) + } + defer dropAndClose(dbmap) + c1 := t1.SetVersionCol("LegacyVersion") + if c1.ColumnName != "LegacyVersion" { + t.Errorf("Wrong col returned: %v", c1) + } + + ipv := &InvoicePersonView{1, 2, "memo", "fname", 0} + _update(dbmap, ipv) + if ipv.LegacyVersion != 1 { + t.Errorf("LegacyVersion not updated: %d", ipv.LegacyVersion) + } +} + +func TestOptimisticLocking(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + p1 := &Person{0, 0, 0, "Bob", "Smith", 0} + dbmap.Insert(p1) // Version is now 1 + if p1.Version != 1 { + t.Errorf("Insert didn't incr Version: %d != %d", 1, p1.Version) + return + } + if p1.Id == 0 { + t.Errorf("Insert didn't return a generated PK") + return + } + + obj, err := dbmap.Get(Person{}, p1.Id) + if err != nil { + panic(err) + } + p2 := obj.(*Person) + p2.LName = "Edwards" + dbmap.Update(p2) // Version is now 2 + if p2.Version != 2 { + t.Errorf("Update didn't incr Version: %d != %d", 2, p2.Version) + } + + p1.LName = "Howard" + count, err := dbmap.Update(p1) + if _, ok := err.(gorp.OptimisticLockError); !ok { + t.Errorf("update - Expected gorp.OptimisticLockError, got: %v", err) + } + if count != -1 { + t.Errorf("update - Expected -1 count, got: %d", count) + } + + count, err = dbmap.Delete(p1) + if _, ok := err.(gorp.OptimisticLockError); !ok { + t.Errorf("delete - Expected gorp.OptimisticLockError, got: %v", err) + } + if count != -1 { + t.Errorf("delete - Expected -1 count, got: %d", count) + } +} + +// what happens if a legacy table has a null value? +func TestDoubleAddTable(t *testing.T) { + dbmap := newDbMap() + t1 := dbmap.AddTable(TableWithNull{}).SetKeys(false, "Id") + t2 := dbmap.AddTable(TableWithNull{}) + if t1 != t2 { + t.Errorf("%v != %v", t1, t2) + } +} + +// what happens if a legacy table has a null value? +func TestNullValues(t *testing.T) { + dbmap := initDbMapNulls() + defer dropAndClose(dbmap) + + // insert a row directly + rawExec(dbmap, "insert into "+tableName(dbmap, TableWithNull{})+" values (10, null, "+ + "null, null, null, null)") + + // try to load it + expected := &TableWithNull{Id: 10} + obj := _get(dbmap, TableWithNull{}, 10) + t1 := obj.(*TableWithNull) + if !reflect.DeepEqual(expected, t1) { + t.Errorf("%v != %v", expected, t1) + } + + // update it + t1.Str = sql.NullString{"hi", true} + expected.Str = t1.Str + t1.Int64 = sql.NullInt64{999, true} + expected.Int64 = t1.Int64 + t1.Float64 = sql.NullFloat64{53.33, true} + expected.Float64 = t1.Float64 + t1.Bool = sql.NullBool{true, true} + expected.Bool = t1.Bool + t1.Bytes = []byte{1, 30, 31, 33} + expected.Bytes = t1.Bytes + _update(dbmap, t1) + + obj = _get(dbmap, TableWithNull{}, 10) + t1 = obj.(*TableWithNull) + if t1.Str.String != "hi" { + t.Errorf("%s != hi", t1.Str.String) + } + if !reflect.DeepEqual(expected, t1) { + t.Errorf("%v != %v", expected, t1) + } +} + +func TestScannerValuer(t *testing.T) { + dbmap := newDbMap() + dbmap.AddTableWithName(PersonValuerScanner{}, "person_test").SetKeys(true, "Id") + dbmap.AddTableWithName(InvoiceWithValuer{}, "invoice_test").SetKeys(true, "Id") + err := dbmap.CreateTables() + if err != nil { + panic(err) + } + defer dropAndClose(dbmap) + + pv := PersonValuerScanner{} + pv.FName = "foo" + pv.LName = "bar" + err = dbmap.Insert(&pv) + if err != nil { + t.Errorf("Could not insert PersonValuerScanner using Person table: %v", err) + t.FailNow() + } + + inv := InvoiceWithValuer{} + inv.Memo = "foo" + inv.Person = pv + err = dbmap.Insert(&inv) + if err != nil { + t.Errorf("Could not insert InvoiceWithValuer using Invoice table: %v", err) + t.FailNow() + } + + res, err := dbmap.Get(InvoiceWithValuer{}, inv.Id) + if err != nil { + t.Errorf("Could not get InvoiceWithValuer: %v", err) + t.FailNow() + } + dbInv := res.(*InvoiceWithValuer) + + if dbInv.Person.Id != pv.Id { + t.Errorf("InvoiceWithValuer got wrong person ID: %d (expected) != %d (actual)", pv.Id, dbInv.Person.Id) + } +} + +func TestColumnProps(t *testing.T) { + dbmap := newDbMap() + t1 := dbmap.AddTable(Invoice{}).SetKeys(true, "Id") + t1.ColMap("Created").Rename("date_created") + t1.ColMap("Updated").SetTransient(true) + t1.ColMap("Memo").SetMaxSize(10) + t1.ColMap("PersonId").SetUnique(true) + + err := dbmap.CreateTables() + if err != nil { + panic(err) + } + defer dropAndClose(dbmap) + + // test transient + inv := &Invoice{0, 0, 1, "my invoice", 0, true} + _insert(dbmap, inv) + obj := _get(dbmap, Invoice{}, inv.Id) + inv = obj.(*Invoice) + if inv.Updated != 0 { + t.Errorf("Saved transient column 'Updated'") + } + + // test max size + inv.Memo = "this memo is too long" + err = dbmap.Insert(inv) + if err == nil { + t.Errorf("max size exceeded, but Insert did not fail.") + } + + // test unique - same person id + inv = &Invoice{0, 0, 1, "my invoice2", 0, false} + err = dbmap.Insert(inv) + if err == nil { + t.Errorf("same PersonId inserted, but Insert did not fail.") + } +} + +func TestRawSelect(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + p1 := &Person{0, 0, 0, "bob", "smith", 0} + _insert(dbmap, p1) + + inv1 := &Invoice{0, 0, 0, "xmas order", p1.Id, true} + _insert(dbmap, inv1) + + expected := &InvoicePersonView{inv1.Id, p1.Id, inv1.Memo, p1.FName, 0} + + query := "select i." + columnName(dbmap, Invoice{}, "Id") + " InvoiceId, p." + columnName(dbmap, Person{}, "Id") + " PersonId, i." + columnName(dbmap, Invoice{}, "Memo") + ", p." + columnName(dbmap, Person{}, "FName") + " " + + "from invoice_test i, person_test p " + + "where i." + columnName(dbmap, Invoice{}, "PersonId") + " = p." + columnName(dbmap, Person{}, "Id") + list := rawSelect(dbmap, InvoicePersonView{}, query) + if len(list) != 1 { + t.Errorf("len(list) != 1: %d", len(list)) + } else if !reflect.DeepEqual(expected, list[0]) { + t.Errorf("%v != %v", expected, list[0]) + } +} + +func TestHooks(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + p1 := &Person{0, 0, 0, "bob", "smith", 0} + _insert(dbmap, p1) + if p1.Created == 0 || p1.Updated == 0 { + t.Errorf("p1.PreInsert() didn't run: %v", p1) + } else if p1.LName != "postinsert" { + t.Errorf("p1.PostInsert() didn't run: %v", p1) + } + + obj := _get(dbmap, Person{}, p1.Id) + p1 = obj.(*Person) + if p1.LName != "postget" { + t.Errorf("p1.PostGet() didn't run: %v", p1) + } + + _update(dbmap, p1) + if p1.FName != "preupdate" { + t.Errorf("p1.PreUpdate() didn't run: %v", p1) + } else if p1.LName != "postupdate" { + t.Errorf("p1.PostUpdate() didn't run: %v", p1) + } + + var persons []*Person + bindVar := dbmap.Dialect.BindVar(0) + rawSelect(dbmap, &persons, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+" = "+bindVar, p1.Id) + if persons[0].LName != "postget" { + t.Errorf("p1.PostGet() didn't run after select: %v", p1) + } + + _del(dbmap, p1) + if p1.FName != "predelete" { + t.Errorf("p1.PreDelete() didn't run: %v", p1) + } else if p1.LName != "postdelete" { + t.Errorf("p1.PostDelete() didn't run: %v", p1) + } + + // Test error case + p2 := &Person{0, 0, 0, "badname", "", 0} + err := dbmap.Insert(p2) + if err == nil { + t.Errorf("p2.PreInsert() didn't return an error") + } +} + +func TestTransaction(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + inv1 := &Invoice{0, 100, 200, "t1", 0, true} + inv2 := &Invoice{0, 100, 200, "t2", 0, false} + + trans, err := dbmap.Begin() + if err != nil { + panic(err) + } + trans.Insert(inv1, inv2) + err = trans.Commit() + if err != nil { + panic(err) + } + + obj, err := dbmap.Get(Invoice{}, inv1.Id) + if err != nil { + panic(err) + } + if !reflect.DeepEqual(inv1, obj) { + t.Errorf("%v != %v", inv1, obj) + } + obj, err = dbmap.Get(Invoice{}, inv2.Id) + if err != nil { + panic(err) + } + if !reflect.DeepEqual(inv2, obj) { + t.Errorf("%v != %v", inv2, obj) + } +} + +func TestSavepoint(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + inv1 := &Invoice{0, 100, 200, "unpaid", 0, false} + + trans, err := dbmap.Begin() + if err != nil { + panic(err) + } + trans.Insert(inv1) + + var checkMemo = func(want string) { + memo, err := trans.SelectStr("select " + columnName(dbmap, Invoice{}, "Memo") + " from invoice_test") + if err != nil { + panic(err) + } + if memo != want { + t.Errorf("%q != %q", want, memo) + } + } + checkMemo("unpaid") + + err = trans.Savepoint("foo") + if err != nil { + panic(err) + } + checkMemo("unpaid") + + inv1.Memo = "paid" + _, err = trans.Update(inv1) + if err != nil { + panic(err) + } + checkMemo("paid") + + err = trans.RollbackToSavepoint("foo") + if err != nil { + panic(err) + } + checkMemo("unpaid") + + err = trans.Rollback() + if err != nil { + panic(err) + } +} + +func TestMultiple(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + inv1 := &Invoice{0, 100, 200, "a", 0, false} + inv2 := &Invoice{0, 100, 200, "b", 0, true} + _insert(dbmap, inv1, inv2) + + inv1.Memo = "c" + inv2.Memo = "d" + _update(dbmap, inv1, inv2) + + count := _del(dbmap, inv1, inv2) + if count != 2 { + t.Errorf("%d != 2", count) + } +} + +func TestCrud(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + inv := &Invoice{0, 100, 200, "first order", 0, true} + testCrudInternal(t, dbmap, inv) + + invtag := &InvoiceTag{0, 300, 400, "some order", 33, false} + testCrudInternal(t, dbmap, invtag) + + foo := &AliasTransientField{BarStr: "some bar"} + testCrudInternal(t, dbmap, foo) +} + +func testCrudInternal(t *testing.T, dbmap *gorp.DbMap, val testable) { + table, err := dbmap.TableFor(reflect.TypeOf(val).Elem(), false) + if err != nil { + t.Errorf("couldn't call TableFor: val=%v err=%v", val, err) + } + + _, err = dbmap.Exec("delete from " + table.TableName) + if err != nil { + t.Errorf("couldn't delete rows from: val=%v err=%v", val, err) + } + + // INSERT row + _insert(dbmap, val) + if val.GetId() == 0 { + t.Errorf("val.GetId() was not set on INSERT") + return + } + + // SELECT row + val2 := _get(dbmap, val, val.GetId()) + if !reflect.DeepEqual(val, val2) { + t.Errorf("%v != %v", val, val2) + } + + // UPDATE row and SELECT + val.Rand() + count := _update(dbmap, val) + if count != 1 { + t.Errorf("update 1 != %d", count) + } + val2 = _get(dbmap, val, val.GetId()) + if !reflect.DeepEqual(val, val2) { + t.Errorf("%v != %v", val, val2) + } + + // Select * + rows, err := dbmap.Select(val, "select * from "+dbmap.Dialect.QuoteField(table.TableName)) + if err != nil { + t.Errorf("couldn't select * from %s err=%v", dbmap.Dialect.QuoteField(table.TableName), err) + } else if len(rows) != 1 { + t.Errorf("unexpected row count in %s: %d", dbmap.Dialect.QuoteField(table.TableName), len(rows)) + } else if !reflect.DeepEqual(val, rows[0]) { + t.Errorf("select * result: %v != %v", val, rows[0]) + } + + // DELETE row + deleted := _del(dbmap, val) + if deleted != 1 { + t.Errorf("Did not delete row with Id: %d", val.GetId()) + return + } + + // VERIFY deleted + val2 = _get(dbmap, val, val.GetId()) + if val2 != nil { + t.Errorf("Found invoice with id: %d after Delete()", val.GetId()) + } +} + +func TestWithIgnoredColumn(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + ic := &WithIgnoredColumn{-1, 0, 1} + _insert(dbmap, ic) + expected := &WithIgnoredColumn{0, 1, 1} + ic2 := _get(dbmap, WithIgnoredColumn{}, ic.Id).(*WithIgnoredColumn) + + if !reflect.DeepEqual(expected, ic2) { + t.Errorf("%v != %v", expected, ic2) + } + if _del(dbmap, ic) != 1 { + t.Errorf("Did not delete row with Id: %d", ic.Id) + return + } + if _get(dbmap, WithIgnoredColumn{}, ic.Id) != nil { + t.Errorf("Found id: %d after Delete()", ic.Id) + } +} + +func TestColumnFilter(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + inv1 := &Invoice{0, 100, 200, "a", 0, false} + _insert(dbmap, inv1) + + inv1.Memo = "c" + inv1.IsPaid = true + _updateColumns(dbmap, func(col *gorp.ColumnMap) bool { + return col.ColumnName == "Memo" + }, inv1) + + inv2 := &Invoice{} + inv2 = _get(dbmap, inv2, inv1.Id).(*Invoice) + if inv2.Memo != "c" { + t.Errorf("Expected column to be updated (%#v)", inv2) + } + if inv2.IsPaid { + t.Error("IsPaid shouldn't have been updated") + } +} + +func TestTypeConversionExample(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + p := Person{FName: "Bob", LName: "Smith"} + tc := &TypeConversionExample{-1, p, CustomStringType("hi")} + _insert(dbmap, tc) + + expected := &TypeConversionExample{1, p, CustomStringType("hi")} + tc2 := _get(dbmap, TypeConversionExample{}, tc.Id).(*TypeConversionExample) + if !reflect.DeepEqual(expected, tc2) { + t.Errorf("tc2 %v != %v", expected, tc2) + } + + tc2.Name = CustomStringType("hi2") + tc2.PersonJSON = Person{FName: "Jane", LName: "Doe"} + _update(dbmap, tc2) + + expected = &TypeConversionExample{1, tc2.PersonJSON, CustomStringType("hi2")} + tc3 := _get(dbmap, TypeConversionExample{}, tc.Id).(*TypeConversionExample) + if !reflect.DeepEqual(expected, tc3) { + t.Errorf("tc3 %v != %v", expected, tc3) + } + + if _del(dbmap, tc) != 1 { + t.Errorf("Did not delete row with Id: %d", tc.Id) + } + +} + +func TestWithEmbeddedStruct(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + es := &WithEmbeddedStruct{-1, Names{FirstName: "Alice", LastName: "Smith"}} + _insert(dbmap, es) + expected := &WithEmbeddedStruct{1, Names{FirstName: "Alice", LastName: "Smith"}} + es2 := _get(dbmap, WithEmbeddedStruct{}, es.Id).(*WithEmbeddedStruct) + if !reflect.DeepEqual(expected, es2) { + t.Errorf("%v != %v", expected, es2) + } + + es2.FirstName = "Bob" + expected.FirstName = "Bob" + _update(dbmap, es2) + es2 = _get(dbmap, WithEmbeddedStruct{}, es.Id).(*WithEmbeddedStruct) + if !reflect.DeepEqual(expected, es2) { + t.Errorf("%v != %v", expected, es2) + } + + ess := rawSelect(dbmap, WithEmbeddedStruct{}, "select * from embedded_struct_test") + if !reflect.DeepEqual(es2, ess[0]) { + t.Errorf("%v != %v", es2, ess[0]) + } +} + +/* +func TestWithEmbeddedStructConflictingEmbeddedMemberNames(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + es := &WithEmbeddedStructConflictingEmbeddedMemberNames{-1, Names{FirstName: "Alice", LastName: "Smith"}, NamesConflict{FirstName: "Andrew", Surname: "Wiggin"}} + _insert(dbmap, es) + expected := &WithEmbeddedStructConflictingEmbeddedMemberNames{-1, Names{FirstName: "Alice", LastName: "Smith"}, NamesConflict{FirstName: "Andrew", Surname: "Wiggin"}} + es2 := _get(dbmap, WithEmbeddedStructConflictingEmbeddedMemberNames{}, es.Id).(*WithEmbeddedStructConflictingEmbeddedMemberNames) + if !reflect.DeepEqual(expected, es2) { + t.Errorf("%v != %v", expected, es2) + } + + es2.Names.FirstName = "Bob" + expected.Names.FirstName = "Bob" + _update(dbmap, es2) + es2 = _get(dbmap, WithEmbeddedStructConflictingEmbeddedMemberNames{}, es.Id).(*WithEmbeddedStructConflictingEmbeddedMemberNames) + if !reflect.DeepEqual(expected, es2) { + t.Errorf("%v != %v", expected, es2) + } + + ess := rawSelect(dbmap, WithEmbeddedStructConflictingEmbeddedMemberNames{}, "select * from embedded_struct_conflict_name_test") + if !reflect.DeepEqual(es2, ess[0]) { + t.Errorf("%v != %v", es2, ess[0]) + } +} + +func TestWithEmbeddedStructSameMemberName(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + es := &WithEmbeddedStructSameMemberName{-1, SameName{SameName: "Alice"}} + _insert(dbmap, es) + expected := &WithEmbeddedStructSameMemberName{-1, SameName{SameName: "Alice"}} + es2 := _get(dbmap, WithEmbeddedStructSameMemberName{}, es.Id).(*WithEmbeddedStructSameMemberName) + if !reflect.DeepEqual(expected, es2) { + t.Errorf("%v != %v", expected, es2) + } + + es2.SameName = SameName{"Bob"} + expected.SameName = SameName{"Bob"} + _update(dbmap, es2) + es2 = _get(dbmap, WithEmbeddedStructSameMemberName{}, es.Id).(*WithEmbeddedStructSameMemberName) + if !reflect.DeepEqual(expected, es2) { + t.Errorf("%v != %v", expected, es2) + } + + ess := rawSelect(dbmap, WithEmbeddedStructSameMemberName{}, "select * from embedded_struct_same_member_name_test") + if !reflect.DeepEqual(es2, ess[0]) { + t.Errorf("%v != %v", es2, ess[0]) + } +} +//*/ + +func TestWithEmbeddedStructBeforeAutoincr(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + esba := &WithEmbeddedStructBeforeAutoincrField{Names: Names{FirstName: "Alice", LastName: "Smith"}} + _insert(dbmap, esba) + var expectedAutoincrId int64 = 1 + if esba.Id != expectedAutoincrId { + t.Errorf("%d != %d", expectedAutoincrId, esba.Id) + } +} + +func TestWithEmbeddedAutoincr(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + esa := &WithEmbeddedAutoincr{ + WithEmbeddedStruct: WithEmbeddedStruct{Names: Names{FirstName: "Alice", LastName: "Smith"}}, + MiddleName: "Rose", + } + _insert(dbmap, esa) + var expectedAutoincrId int64 = 1 + if esa.Id != expectedAutoincrId { + t.Errorf("%d != %d", expectedAutoincrId, esa.Id) + } +} + +func TestSelectVal(t *testing.T) { + dbmap := initDbMapNulls() + defer dropAndClose(dbmap) + + bindVar := dbmap.Dialect.BindVar(0) + + t1 := TableWithNull{Str: sql.NullString{"abc", true}, + Int64: sql.NullInt64{78, true}, + Float64: sql.NullFloat64{32.2, true}, + Bool: sql.NullBool{true, true}, + Bytes: []byte("hi")} + _insert(dbmap, &t1) + + // SelectInt + i64 := selectInt(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Int64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='abc'") + if i64 != 78 { + t.Errorf("int64 %d != 78", i64) + } + i64 = selectInt(dbmap, "select count(*) from "+tableName(dbmap, TableWithNull{})) + if i64 != 1 { + t.Errorf("int64 count %d != 1", i64) + } + i64 = selectInt(dbmap, "select count(*) from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"="+bindVar, "asdfasdf") + if i64 != 0 { + t.Errorf("int64 no rows %d != 0", i64) + } + + // SelectNullInt + n := selectNullInt(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Int64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='notfound'") + if !reflect.DeepEqual(n, sql.NullInt64{0, false}) { + t.Errorf("nullint %v != 0,false", n) + } + + n = selectNullInt(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Int64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='abc'") + if !reflect.DeepEqual(n, sql.NullInt64{78, true}) { + t.Errorf("nullint %v != 78, true", n) + } + + // SelectFloat + f64 := selectFloat(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Float64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='abc'") + if f64 != 32.2 { + t.Errorf("float64 %d != 32.2", f64) + } + f64 = selectFloat(dbmap, "select min("+columnName(dbmap, TableWithNull{}, "Float64")+") from "+tableName(dbmap, TableWithNull{})) + if f64 != 32.2 { + t.Errorf("float64 min %d != 32.2", f64) + } + f64 = selectFloat(dbmap, "select count(*) from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"="+bindVar, "asdfasdf") + if f64 != 0 { + t.Errorf("float64 no rows %d != 0", f64) + } + + // SelectNullFloat + nf := selectNullFloat(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Float64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='notfound'") + if !reflect.DeepEqual(nf, sql.NullFloat64{0, false}) { + t.Errorf("nullfloat %v != 0,false", nf) + } + + nf = selectNullFloat(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Float64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='abc'") + if !reflect.DeepEqual(nf, sql.NullFloat64{32.2, true}) { + t.Errorf("nullfloat %v != 32.2, true", nf) + } + + // SelectStr + s := selectStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Int64")+"="+bindVar, 78) + if s != "abc" { + t.Errorf("s %s != abc", s) + } + s = selectStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='asdfasdf'") + if s != "" { + t.Errorf("s no rows %s != ''", s) + } + + // SelectNullStr + ns := selectNullStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Int64")+"="+bindVar, 78) + if !reflect.DeepEqual(ns, sql.NullString{"abc", true}) { + t.Errorf("nullstr %v != abc,true", ns) + } + ns = selectNullStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='asdfasdf'") + if !reflect.DeepEqual(ns, sql.NullString{"", false}) { + t.Errorf("nullstr no rows %v != '',false", ns) + } + + // SelectInt/Str with named parameters + i64 = selectInt(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Int64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"=:abc", map[string]string{"abc": "abc"}) + if i64 != 78 { + t.Errorf("int64 %d != 78", i64) + } + ns = selectNullStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Int64")+"=:num", map[string]int{"num": 78}) + if !reflect.DeepEqual(ns, sql.NullString{"abc", true}) { + t.Errorf("nullstr %v != abc,true", ns) + } +} + +func TestVersionMultipleRows(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + persons := []*Person{ + &Person{0, 0, 0, "Bob", "Smith", 0}, + &Person{0, 0, 0, "Jane", "Smith", 0}, + &Person{0, 0, 0, "Mike", "Smith", 0}, + } + + _insert(dbmap, persons[0], persons[1], persons[2]) + + for x, p := range persons { + if p.Version != 1 { + t.Errorf("person[%d].Version != 1: %d", x, p.Version) + } + } +} + +func TestWithStringPk(t *testing.T) { + dbmap := newDbMap() + dbmap.AddTableWithName(WithStringPk{}, "string_pk_test").SetKeys(true, "Id") + _, err := dbmap.Exec("create table string_pk_test (Id varchar(255), Name varchar(255));") + if err != nil { + t.Errorf("couldn't create string_pk_test: %v", err) + } + defer dropAndClose(dbmap) + + row := &WithStringPk{"1", "foo"} + err = dbmap.Insert(row) + if err == nil { + t.Errorf("Expected error when inserting into table w/non Int PK and autoincr set true") + } +} + +// TestSqlExecutorInterfaceSelects ensures that all gorp.DbMap methods starting with Select... +// are also exposed in the gorp.SqlExecutor interface. Select... functions can always +// run on Pre/Post hooks. +func TestSqlExecutorInterfaceSelects(t *testing.T) { + dbMapType := reflect.TypeOf(&gorp.DbMap{}) + sqlExecutorType := reflect.TypeOf((*gorp.SqlExecutor)(nil)).Elem() + numDbMapMethods := dbMapType.NumMethod() + for i := 0; i < numDbMapMethods; i += 1 { + dbMapMethod := dbMapType.Method(i) + if !strings.HasPrefix(dbMapMethod.Name, "Select") { + continue + } + if _, found := sqlExecutorType.MethodByName(dbMapMethod.Name); !found { + t.Errorf("Method %s is defined on gorp.DbMap but not implemented in gorp.SqlExecutor", + dbMapMethod.Name) + } + } +} + +func TestNullTime(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + // if time is null + ent := &WithNullTime{ + Id: 0, + Time: gorp.NullTime{ + Valid: false, + }} + err := dbmap.Insert(ent) + if err != nil { + t.Error("failed insert on %s", err.Error()) + } + err = dbmap.SelectOne(ent, `select * from nulltime_test where `+columnName(dbmap, WithNullTime{}, "Id")+`=:Id`, map[string]interface{}{ + "Id": ent.Id, + }) + if err != nil { + t.Error("failed select on %s", err.Error()) + } + if ent.Time.Valid { + t.Error("gorp.NullTime returns valid but expected null.") + } + + // if time is not null + ts, err := time.Parse(time.Stamp, "Jan 2 15:04:05") + ent = &WithNullTime{ + Id: 1, + Time: gorp.NullTime{ + Valid: true, + Time: ts, + }} + err = dbmap.Insert(ent) + if err != nil { + t.Error("failed insert on %s", err.Error()) + } + err = dbmap.SelectOne(ent, `select * from nulltime_test where `+columnName(dbmap, WithNullTime{}, "Id")+`=:Id`, map[string]interface{}{ + "Id": ent.Id, + }) + if err != nil { + t.Error("failed select on %s", err.Error()) + } + if !ent.Time.Valid { + t.Error("gorp.NullTime returns invalid but expected valid.") + } + if ent.Time.Time.UTC() != ts.UTC() { + t.Errorf("expect %v but got %v.", ts, ent.Time.Time) + } + + return +} + +type WithTime struct { + Id int64 + Time time.Time +} + +type Times struct { + One time.Time + Two time.Time +} + +type EmbeddedTime struct { + Id string + Times +} + +func parseTimeOrPanic(format, date string) time.Time { + t1, err := time.Parse(format, date) + if err != nil { + panic(err) + } + return t1 +} + +// TODO: re-enable next two tests when this is merged: +// https://github.com/ziutek/mymysql/pull/77 +// +// This test currently fails w/MySQL b/c tz info is lost +func testWithTime(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + t1 := parseTimeOrPanic("2006-01-02 15:04:05 -0700 MST", + "2013-08-09 21:30:43 +0800 CST") + w1 := WithTime{1, t1} + _insert(dbmap, &w1) + + obj := _get(dbmap, WithTime{}, w1.Id) + w2 := obj.(*WithTime) + if w1.Time.UnixNano() != w2.Time.UnixNano() { + t.Errorf("%v != %v", w1, w2) + } +} + +// See: https://github.com/go-gorp/gorp/issues/86 +func testEmbeddedTime(t *testing.T) { + dbmap := newDbMap() + dbmap.AddTable(EmbeddedTime{}).SetKeys(false, "Id") + defer dropAndClose(dbmap) + err := dbmap.CreateTables() + if err != nil { + t.Fatal(err) + } + + time1 := parseTimeOrPanic("2006-01-02 15:04:05", "2013-08-09 21:30:43") + + t1 := &EmbeddedTime{Id: "abc", Times: Times{One: time1, Two: time1.Add(10 * time.Second)}} + _insert(dbmap, t1) + + x := _get(dbmap, EmbeddedTime{}, t1.Id) + t2, _ := x.(*EmbeddedTime) + if t1.One.UnixNano() != t2.One.UnixNano() || t1.Two.UnixNano() != t2.Two.UnixNano() { + t.Errorf("%v != %v", t1, t2) + } +} + +func TestWithTimeSelect(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + halfhourago := time.Now().UTC().Add(-30 * time.Minute) + + w1 := WithTime{1, halfhourago.Add(time.Minute * -1)} + w2 := WithTime{2, halfhourago.Add(time.Second)} + _insert(dbmap, &w1, &w2) + + var caseIds []int64 + _, err := dbmap.Select(&caseIds, "SELECT "+columnName(dbmap, WithTime{}, "Id")+" FROM time_test WHERE "+columnName(dbmap, WithTime{}, "Time")+" < "+dbmap.Dialect.BindVar(0), halfhourago) + + if err != nil { + t.Error(err) + } + if len(caseIds) != 1 { + t.Errorf("%d != 1", len(caseIds)) + } + if caseIds[0] != w1.Id { + t.Errorf("%d != %d", caseIds[0], w1.Id) + } +} + +func TestInvoicePersonView(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + // Create some rows + p1 := &Person{0, 0, 0, "bob", "smith", 0} + dbmap.Insert(p1) + + // notice how we can wire up p1.Id to the invoice easily + inv1 := &Invoice{0, 0, 0, "xmas order", p1.Id, false} + dbmap.Insert(inv1) + + // Run your query + query := "select i." + columnName(dbmap, Invoice{}, "Id") + " InvoiceId, p." + columnName(dbmap, Person{}, "Id") + " PersonId, i." + columnName(dbmap, Invoice{}, "Memo") + ", p." + columnName(dbmap, Person{}, "FName") + " " + + "from invoice_test i, person_test p " + + "where i." + columnName(dbmap, Invoice{}, "PersonId") + " = p." + columnName(dbmap, Person{}, "Id") + + // pass a slice of pointers to Select() + // this avoids the need to type assert after the query is run + var list []*InvoicePersonView + _, err := dbmap.Select(&list, query) + if err != nil { + panic(err) + } + + // this should test true + expected := &InvoicePersonView{inv1.Id, p1.Id, inv1.Memo, p1.FName, 0} + if !reflect.DeepEqual(list[0], expected) { + t.Errorf("%v != %v", list[0], expected) + } +} + +func TestQuoteTableNames(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + quotedTableName := dbmap.Dialect.QuoteField("person_test") + + // Use a buffer to hold the log to check generated queries + logBuffer := &bytes.Buffer{} + dbmap.TraceOn("", log.New(logBuffer, "gorptest:", log.Lmicroseconds)) + + // Create some rows + p1 := &Person{0, 0, 0, "bob", "smith", 0} + errorTemplate := "Expected quoted table name %v in query but didn't find it" + + // Check if Insert quotes the table name + id := dbmap.Insert(p1) + if !bytes.Contains(logBuffer.Bytes(), []byte(quotedTableName)) { + t.Errorf(errorTemplate, quotedTableName) + } + logBuffer.Reset() + + // Check if Get quotes the table name + dbmap.Get(Person{}, id) + if !bytes.Contains(logBuffer.Bytes(), []byte(quotedTableName)) { + t.Errorf(errorTemplate, quotedTableName) + } + logBuffer.Reset() +} + +func TestSelectTooManyCols(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + p1 := &Person{0, 0, 0, "bob", "smith", 0} + p2 := &Person{0, 0, 0, "jane", "doe", 0} + _insert(dbmap, p1) + _insert(dbmap, p2) + + obj := _get(dbmap, Person{}, p1.Id) + p1 = obj.(*Person) + obj = _get(dbmap, Person{}, p2.Id) + p2 = obj.(*Person) + + params := map[string]interface{}{ + "Id": p1.Id, + } + + var p3 FNameOnly + err := dbmap.SelectOne(&p3, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params) + if err != nil { + if !gorp.NonFatalError(err) { + t.Error(err) + } + } else { + t.Errorf("Non-fatal error expected") + } + + if p1.FName != p3.FName { + t.Errorf("%v != %v", p1.FName, p3.FName) + } + + var pSlice []FNameOnly + _, err = dbmap.Select(&pSlice, "select * from person_test order by "+columnName(dbmap, Person{}, "FName")+" asc") + if err != nil { + if !gorp.NonFatalError(err) { + t.Error(err) + } + } else { + t.Errorf("Non-fatal error expected") + } + + if p1.FName != pSlice[0].FName { + t.Errorf("%v != %v", p1.FName, pSlice[0].FName) + } + if p2.FName != pSlice[1].FName { + t.Errorf("%v != %v", p2.FName, pSlice[1].FName) + } +} + +func TestSelectSingleVal(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + p1 := &Person{0, 0, 0, "bob", "smith", 0} + _insert(dbmap, p1) + + obj := _get(dbmap, Person{}, p1.Id) + p1 = obj.(*Person) + + params := map[string]interface{}{ + "Id": p1.Id, + } + + var p2 Person + err := dbmap.SelectOne(&p2, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params) + if err != nil { + t.Error(err) + } + + if !reflect.DeepEqual(p1, &p2) { + t.Errorf("%v != %v", p1, &p2) + } + + // verify SelectOne allows non-struct holders + var s string + err = dbmap.SelectOne(&s, "select "+columnName(dbmap, Person{}, "FName")+" from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params) + if err != nil { + t.Error(err) + } + if s != "bob" { + t.Error("Expected bob but got: " + s) + } + + // verify SelectOne requires pointer receiver + err = dbmap.SelectOne(s, "select "+columnName(dbmap, Person{}, "FName")+" from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params) + if err == nil { + t.Error("SelectOne should have returned error for non-pointer holder") + } + + // verify SelectOne works with uninitialized pointers + var p3 *Person + err = dbmap.SelectOne(&p3, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params) + if err != nil { + t.Error(err) + } + + if !reflect.DeepEqual(p1, p3) { + t.Errorf("%v != %v", p1, p3) + } + + // verify that the receiver is still nil if nothing was found + var p4 *Person + dbmap.SelectOne(&p3, "select * from person_test where 2<1 AND "+columnName(dbmap, Person{}, "Id")+"=:Id", params) + if p4 != nil { + t.Error("SelectOne should not have changed a nil receiver when no rows were found") + } + + // verify that the error is set to sql.ErrNoRows if not found + err = dbmap.SelectOne(&p2, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", map[string]interface{}{ + "Id": -2222, + }) + if err == nil || err != sql.ErrNoRows { + t.Error("SelectOne should have returned an sql.ErrNoRows") + } + + _insert(dbmap, &Person{0, 0, 0, "bob", "smith", 0}) + err = dbmap.SelectOne(&p2, "select * from person_test where "+columnName(dbmap, Person{}, "FName")+"='bob'") + if err == nil { + t.Error("Expected error when two rows found") + } + + // tests for #150 + var tInt int64 + var tStr string + var tBool bool + var tFloat float64 + primVals := []interface{}{tInt, tStr, tBool, tFloat} + for _, prim := range primVals { + err = dbmap.SelectOne(&prim, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=-123") + if err == nil || err != sql.ErrNoRows { + t.Error("primVals: SelectOne should have returned sql.ErrNoRows") + } + } +} + +func TestSelectAlias(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + p1 := &IdCreatedExternal{IdCreated: IdCreated{Id: 1, Created: 3}, External: 2} + + // Insert using embedded IdCreated, which reflects the structure of the table + _insert(dbmap, &p1.IdCreated) + + // Select into IdCreatedExternal type, which includes some fields not present + // in id_created_test + var p2 IdCreatedExternal + err := dbmap.SelectOne(&p2, "select * from id_created_test where "+columnName(dbmap, IdCreatedExternal{}, "Id")+"=1") + if err != nil { + t.Error(err) + } + if p2.Id != 1 || p2.Created != 3 || p2.External != 0 { + t.Error("Expected ignored field defaults to not set") + } + + // Prove that we can supply an aliased value in the select, and that it will + // automatically map to IdCreatedExternal.External + err = dbmap.SelectOne(&p2, "SELECT *, 1 AS external FROM id_created_test") + if err != nil { + t.Error(err) + } + if p2.External != 1 { + t.Error("Expected select as can map to exported field.") + } + + var rows *sql.Rows + var cols []string + rows, err = dbmap.Db.Query("SELECT * FROM id_created_test") + cols, err = rows.Columns() + if err != nil || len(cols) != 2 { + t.Error("Expected ignored column not created") + } +} + +func TestMysqlPanicIfDialectNotInitialized(t *testing.T) { + _, driver := dialectAndDriver() + // this test only applies to MySQL + if os.Getenv("GORP_TEST_DIALECT") != "mysql" { + return + } + + // The expected behaviour is to catch a panic. + // Here is the deferred function which will check if a panic has indeed occurred : + defer func() { + r := recover() + if r == nil { + t.Error("db.CreateTables() should panic if db is initialized with an incorrect gorp.MySQLDialect") + } + }() + + // invalid MySQLDialect : does not contain Engine or Encoding specification + dialect := gorp.MySQLDialect{} + db := &gorp.DbMap{Db: connect(driver), Dialect: dialect} + db.AddTableWithName(Invoice{}, "invoice") + // the following call should panic : + db.CreateTables() +} + +func TestSingleColumnKeyDbReturnsZeroRowsUpdatedOnPKChange(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + dbmap.AddTableWithName(SingleColumnTable{}, "single_column_table").SetKeys(false, "SomeId") + err := dbmap.DropTablesIfExists() + if err != nil { + t.Error("Drop tables failed") + } + err = dbmap.CreateTablesIfNotExists() + if err != nil { + t.Error("Create tables failed") + } + err = dbmap.TruncateTables() + if err != nil { + t.Error("Truncate tables failed") + } + + sct := SingleColumnTable{ + SomeId: "A Unique Id String", + } + + count, err := dbmap.Update(&sct) + if err != nil { + t.Error(err) + } + if count != 0 { + t.Errorf("Expected 0 updated rows, got %d", count) + } + +} + +func TestPrepare(t *testing.T) { + dbmap := initDbMap() + defer dropAndClose(dbmap) + + inv1 := &Invoice{0, 100, 200, "prepare-foo", 0, false} + inv2 := &Invoice{0, 100, 200, "prepare-bar", 0, false} + _insert(dbmap, inv1, inv2) + + bindVar0 := dbmap.Dialect.BindVar(0) + bindVar1 := dbmap.Dialect.BindVar(1) + stmt, err := dbmap.Prepare(fmt.Sprintf("UPDATE invoice_test SET "+columnName(dbmap, Invoice{}, "Memo")+"=%s WHERE "+columnName(dbmap, Invoice{}, "Id")+"=%s", bindVar0, bindVar1)) + if err != nil { + t.Error(err) + } + defer stmt.Close() + _, err = stmt.Exec("prepare-baz", inv1.Id) + if err != nil { + t.Error(err) + } + err = dbmap.SelectOne(inv1, "SELECT * from invoice_test WHERE "+columnName(dbmap, Invoice{}, "Memo")+"='prepare-baz'") + if err != nil { + t.Error(err) + } + + trans, err := dbmap.Begin() + if err != nil { + t.Error(err) + } + transStmt, err := trans.Prepare(fmt.Sprintf("UPDATE invoice_test SET "+columnName(dbmap, Invoice{}, "IsPaid")+"=%s WHERE "+columnName(dbmap, Invoice{}, "Id")+"=%s", bindVar0, bindVar1)) + if err != nil { + t.Error(err) + } + defer transStmt.Close() + _, err = transStmt.Exec(true, inv2.Id) + if err != nil { + t.Error(err) + } + err = dbmap.SelectOne(inv2, fmt.Sprintf("SELECT * from invoice_test WHERE "+columnName(dbmap, Invoice{}, "IsPaid")+"=%s", bindVar0), true) + if err == nil || err != sql.ErrNoRows { + t.Error("SelectOne should have returned an sql.ErrNoRows") + } + err = trans.SelectOne(inv2, fmt.Sprintf("SELECT * from invoice_test WHERE "+columnName(dbmap, Invoice{}, "IsPaid")+"=%s", bindVar0), true) + if err != nil { + t.Error(err) + } + err = trans.Commit() + if err != nil { + t.Error(err) + } + err = dbmap.SelectOne(inv2, fmt.Sprintf("SELECT * from invoice_test WHERE "+columnName(dbmap, Invoice{}, "IsPaid")+"=%s", bindVar0), true) + if err != nil { + t.Error(err) + } +} + +func BenchmarkNativeCrud(b *testing.B) { + b.StopTimer() + dbmap := initDbMapBench() + defer dropAndClose(dbmap) + columnId := columnName(dbmap, Invoice{}, "Id") + columnCreated := columnName(dbmap, Invoice{}, "Created") + columnUpdated := columnName(dbmap, Invoice{}, "Updated") + columnMemo := columnName(dbmap, Invoice{}, "Memo") + columnPersonId := columnName(dbmap, Invoice{}, "PersonId") + b.StartTimer() + + var insert, sel, update, delete string + if os.Getenv("GORP_TEST_DIALECT") != "postgres" { + insert = "insert into invoice_test (" + columnCreated + ", " + columnUpdated + ", " + columnMemo + ", " + columnPersonId + ") values (?, ?, ?, ?)" + sel = "select " + columnId + ", " + columnCreated + ", " + columnUpdated + ", " + columnMemo + ", " + columnPersonId + " from invoice_test where " + columnId + "=?" + update = "update invoice_test set " + columnCreated + "=?, " + columnUpdated + "=?, " + columnMemo + "=?, " + columnPersonId + "=? where " + columnId + "=?" + delete = "delete from invoice_test where " + columnId + "=?" + } else { + insert = "insert into invoice_test (" + columnCreated + ", " + columnUpdated + ", " + columnMemo + ", " + columnPersonId + ") values ($1, $2, $3, $4)" + sel = "select " + columnId + ", " + columnCreated + ", " + columnUpdated + ", " + columnMemo + ", " + columnPersonId + " from invoice_test where " + columnId + "=$1" + update = "update invoice_test set " + columnCreated + "=$1, " + columnUpdated + "=$2, " + columnMemo + "=$3, " + columnPersonId + "=$4 where " + columnId + "=$5" + delete = "delete from invoice_test where " + columnId + "=$1" + } + + inv := &Invoice{0, 100, 200, "my memo", 0, false} + + for i := 0; i < b.N; i++ { + res, err := dbmap.Db.Exec(insert, inv.Created, inv.Updated, + inv.Memo, inv.PersonId) + if err != nil { + panic(err) + } + + newid, err := res.LastInsertId() + if err != nil { + panic(err) + } + inv.Id = newid + + row := dbmap.Db.QueryRow(sel, inv.Id) + err = row.Scan(&inv.Id, &inv.Created, &inv.Updated, &inv.Memo, + &inv.PersonId) + if err != nil { + panic(err) + } + + inv.Created = 1000 + inv.Updated = 2000 + inv.Memo = "my memo 2" + inv.PersonId = 3000 + + _, err = dbmap.Db.Exec(update, inv.Created, inv.Updated, inv.Memo, + inv.PersonId, inv.Id) + if err != nil { + panic(err) + } + + _, err = dbmap.Db.Exec(delete, inv.Id) + if err != nil { + panic(err) + } + } + +} + +func BenchmarkGorpCrud(b *testing.B) { + b.StopTimer() + dbmap := initDbMapBench() + defer dropAndClose(dbmap) + b.StartTimer() + + inv := &Invoice{0, 100, 200, "my memo", 0, true} + for i := 0; i < b.N; i++ { + err := dbmap.Insert(inv) + if err != nil { + panic(err) + } + + obj, err := dbmap.Get(Invoice{}, inv.Id) + if err != nil { + panic(err) + } + + inv2, ok := obj.(*Invoice) + if !ok { + panic(fmt.Sprintf("expected *Invoice, got: %v", obj)) + } + + inv2.Created = 1000 + inv2.Updated = 2000 + inv2.Memo = "my memo 2" + inv2.PersonId = 3000 + _, err = dbmap.Update(inv2) + if err != nil { + panic(err) + } + + _, err = dbmap.Delete(inv2) + if err != nil { + panic(err) + } + + } +} + +func initDbMapBench() *gorp.DbMap { + dbmap := newDbMap() + dbmap.Db.Exec("drop table if exists invoice_test") + dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id") + err := dbmap.CreateTables() + if err != nil { + panic(err) + } + return dbmap +} + +func initDbMap() *gorp.DbMap { + dbmap := newDbMap() + dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id") + dbmap.AddTableWithName(InvoiceTag{}, "invoice_tag_test") //key is set via primarykey attribute + dbmap.AddTableWithName(AliasTransientField{}, "alias_trans_field_test").SetKeys(true, "id") + dbmap.AddTableWithName(OverriddenInvoice{}, "invoice_override_test").SetKeys(false, "Id") + dbmap.AddTableWithName(Person{}, "person_test").SetKeys(true, "Id").SetVersionCol("Version") + dbmap.AddTableWithName(WithIgnoredColumn{}, "ignored_column_test").SetKeys(true, "Id") + dbmap.AddTableWithName(IdCreated{}, "id_created_test").SetKeys(true, "Id") + dbmap.AddTableWithName(TypeConversionExample{}, "type_conv_test").SetKeys(true, "Id") + dbmap.AddTableWithName(WithEmbeddedStruct{}, "embedded_struct_test").SetKeys(true, "Id") + //dbmap.AddTableWithName(WithEmbeddedStructConflictingEmbeddedMemberNames{}, "embedded_struct_conflict_name_test").SetKeys(true, "Id") + //dbmap.AddTableWithName(WithEmbeddedStructSameMemberName{}, "embedded_struct_same_member_name_test").SetKeys(true, "Id") + dbmap.AddTableWithName(WithEmbeddedStructBeforeAutoincrField{}, "embedded_struct_before_autoincr_test").SetKeys(true, "Id") + dbmap.AddTableWithName(WithEmbeddedAutoincr{}, "embedded_autoincr_test").SetKeys(true, "Id") + dbmap.AddTableWithName(WithTime{}, "time_test").SetKeys(true, "Id") + dbmap.AddTableWithName(WithNullTime{}, "nulltime_test").SetKeys(false, "Id") + dbmap.TypeConverter = testTypeConverter{} + err := dbmap.DropTablesIfExists() + if err != nil { + panic(err) + } + err = dbmap.CreateTables() + if err != nil { + panic(err) + } + + // See #146 and TestSelectAlias - this type is mapped to the same + // table as IdCreated, but includes an extra field that isn't in the table + dbmap.AddTableWithName(IdCreatedExternal{}, "id_created_test").SetKeys(true, "Id") + + return dbmap +} + +func initDbMapNulls() *gorp.DbMap { + dbmap := newDbMap() + dbmap.AddTable(TableWithNull{}).SetKeys(false, "Id") + err := dbmap.CreateTables() + if err != nil { + panic(err) + } + return dbmap +} + +func newDbMap() *gorp.DbMap { + dialect, driver := dialectAndDriver() + dbmap := &gorp.DbMap{Db: connect(driver), Dialect: dialect} + if debug { + dbmap.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds)) + } + return dbmap +} + +func dropAndClose(dbmap *gorp.DbMap) { + dbmap.DropTablesIfExists() + dbmap.Db.Close() +} + +func connect(driver string) *sql.DB { + dsn := os.Getenv("GORP_TEST_DSN") + if dsn == "" { + panic("GORP_TEST_DSN env variable is not set. Please see README.md") + } + + db, err := sql.Open(driver, dsn) + if err != nil { + panic("Error connecting to db: " + err.Error()) + } + return db +} + +func dialectAndDriver() (gorp.Dialect, string) { + switch os.Getenv("GORP_TEST_DIALECT") { + case "mysql": + return gorp.MySQLDialect{"InnoDB", "UTF8"}, "mymysql" + case "gomysql": + return gorp.MySQLDialect{"InnoDB", "UTF8"}, "mysql" + case "postgres": + return gorp.PostgresDialect{}, "postgres" + case "sqlite": + return gorp.SqliteDialect{}, "sqlite3" + } + panic("GORP_TEST_DIALECT env variable is not set or is invalid. Please see README.md") +} + +func _insert(dbmap *gorp.DbMap, list ...interface{}) { + err := dbmap.Insert(list...) + if err != nil { + panic(err) + } +} + +func _update(dbmap *gorp.DbMap, list ...interface{}) int64 { + count, err := dbmap.Update(list...) + if err != nil { + panic(err) + } + return count +} + +func _updateColumns(dbmap *gorp.DbMap, filter gorp.ColumnFilter, list ...interface{}) int64 { + count, err := dbmap.UpdateColumns(filter, list...) + if err != nil { + panic(err) + } + return count +} + +func _del(dbmap *gorp.DbMap, list ...interface{}) int64 { + count, err := dbmap.Delete(list...) + if err != nil { + panic(err) + } + + return count +} + +func _get(dbmap *gorp.DbMap, i interface{}, keys ...interface{}) interface{} { + obj, err := dbmap.Get(i, keys...) + if err != nil { + panic(err) + } + + return obj +} + +func selectInt(dbmap *gorp.DbMap, query string, args ...interface{}) int64 { + i64, err := gorp.SelectInt(dbmap, query, args...) + if err != nil { + panic(err) + } + + return i64 +} + +func selectNullInt(dbmap *gorp.DbMap, query string, args ...interface{}) sql.NullInt64 { + i64, err := gorp.SelectNullInt(dbmap, query, args...) + if err != nil { + panic(err) + } + + return i64 +} + +func selectFloat(dbmap *gorp.DbMap, query string, args ...interface{}) float64 { + f64, err := gorp.SelectFloat(dbmap, query, args...) + if err != nil { + panic(err) + } + + return f64 +} + +func selectNullFloat(dbmap *gorp.DbMap, query string, args ...interface{}) sql.NullFloat64 { + f64, err := gorp.SelectNullFloat(dbmap, query, args...) + if err != nil { + panic(err) + } + + return f64 +} + +func selectStr(dbmap *gorp.DbMap, query string, args ...interface{}) string { + s, err := gorp.SelectStr(dbmap, query, args...) + if err != nil { + panic(err) + } + + return s +} + +func selectNullStr(dbmap *gorp.DbMap, query string, args ...interface{}) sql.NullString { + s, err := gorp.SelectNullStr(dbmap, query, args...) + if err != nil { + panic(err) + } + + return s +} + +func rawExec(dbmap *gorp.DbMap, query string, args ...interface{}) sql.Result { + res, err := dbmap.Exec(query, args...) + if err != nil { + panic(err) + } + return res +} + +func rawSelect(dbmap *gorp.DbMap, i interface{}, query string, args ...interface{}) []interface{} { + list, err := dbmap.Select(i, query, args...) + if err != nil { + panic(err) + } + return list +} + +func tableName(dbmap *gorp.DbMap, i interface{}) string { + t := reflect.TypeOf(i) + if table, err := dbmap.TableFor(t, false); table != nil && err == nil { + return dbmap.Dialect.QuoteField(table.TableName) + } + return t.Name() +} + +func columnName(dbmap *gorp.DbMap, i interface{}, fieldName string) string { + t := reflect.TypeOf(i) + if table, err := dbmap.TableFor(t, false); table != nil && err == nil { + return dbmap.Dialect.QuoteField(table.ColMap(fieldName).ColumnName) + } + return fieldName +} diff --git a/vendor/github.com/go-gorp/gorp/test_all.sh b/vendor/github.com/go-gorp/gorp/test_all.sh old mode 100644 new mode 100755 diff --git a/vendor/github.com/go-ldap/ldap/conn_test.go b/vendor/github.com/go-ldap/ldap/conn_test.go new file mode 100644 index 000000000..8394e5339 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/conn_test.go @@ -0,0 +1,53 @@ +package ldap + +import ( + "net" + "net/http" + "net/http/httptest" + "testing" + "time" + + "gopkg.in/asn1-ber.v1" +) + +func TestUnresponsiveConnection(t *testing.T) { + // The do-nothing server that accepts requests and does nothing + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + })) + defer ts.Close() + c, err := net.Dial(ts.Listener.Addr().Network(), ts.Listener.Addr().String()) + if err != nil { + t.Fatalf("error connecting to localhost tcp: %v", err) + } + + // Create an Ldap connection + conn := NewConn(c, false) + conn.SetTimeout(time.Millisecond) + conn.Start() + defer conn.Close() + + // Mock a packet + messageID := conn.nextMessageID() + packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") + packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) + bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") + bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) + packet.AppendChild(bindRequest) + + // Send packet and test response + channel, err := conn.sendMessage(packet) + if err != nil { + t.Fatalf("error sending message: %v", err) + } + packetResponse, ok := <-channel + if !ok { + t.Fatalf("no PacketResponse in response channel") + } + packet, err = packetResponse.ReadPacket() + if err == nil { + t.Fatalf("expected timeout error") + } + if err.Error() != "ldap: connection timed out" { + t.Fatalf("unexpected error: %v", err) + } +} diff --git a/vendor/github.com/go-ldap/ldap/dn_test.go b/vendor/github.com/go-ldap/ldap/dn_test.go new file mode 100644 index 000000000..39817c427 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/dn_test.go @@ -0,0 +1,70 @@ +package ldap_test + +import ( + "reflect" + "testing" + + "gopkg.in/ldap.v2" +) + +func TestSuccessfulDNParsing(t *testing.T) { + testcases := map[string]ldap.DN{ + "": ldap.DN{[]*ldap.RelativeDN{}}, + "cn=Jim\\2C \\22Hasse Hö\\22 Hansson!,dc=dummy,dc=com": ldap.DN{[]*ldap.RelativeDN{ + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"cn", "Jim, \"Hasse Hö\" Hansson!"}}}, + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"dc", "dummy"}}}, + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"dc", "com"}}}}}, + "UID=jsmith,DC=example,DC=net": ldap.DN{[]*ldap.RelativeDN{ + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"UID", "jsmith"}}}, + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "example"}}}, + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}}, + "OU=Sales+CN=J. Smith,DC=example,DC=net": ldap.DN{[]*ldap.RelativeDN{ + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{ + &ldap.AttributeTypeAndValue{"OU", "Sales"}, + &ldap.AttributeTypeAndValue{"CN", "J. Smith"}}}, + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "example"}}}, + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}}, + "1.3.6.1.4.1.1466.0=#04024869": ldap.DN{[]*ldap.RelativeDN{ + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"}}}}}, + "1.3.6.1.4.1.1466.0=#04024869,DC=net": ldap.DN{[]*ldap.RelativeDN{ + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"}}}, + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}}, + "CN=Lu\\C4\\8Di\\C4\\87": ldap.DN{[]*ldap.RelativeDN{ + &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}}, + } + + for test, answer := range testcases { + dn, err := ldap.ParseDN(test) + if err != nil { + t.Errorf(err.Error()) + continue + } + if !reflect.DeepEqual(dn, &answer) { + t.Errorf("Parsed DN %s is not equal to the expected structure", test) + for _, rdn := range dn.RDNs { + for _, attribs := range rdn.Attributes { + t.Logf("#%v\n", attribs) + } + } + } + } +} + +func TestErrorDNParsing(t *testing.T) { + testcases := map[string]string{ + "*": "DN ended with incomplete type, value pair", + "cn=Jim\\0Test": "Failed to decode escaped character: encoding/hex: invalid byte: U+0054 'T'", + "cn=Jim\\0": "Got corrupted escaped character", + "DC=example,=net": "DN ended with incomplete type, value pair", + "1=#0402486": "Failed to decode BER encoding: encoding/hex: odd length hex string", + } + + for test, answer := range testcases { + _, err := ldap.ParseDN(test) + if err == nil { + t.Errorf("Expected %s to fail parsing but succeeded\n", test) + } else if err.Error() != answer { + t.Errorf("Unexpected error on %s:\n%s\nvs.\n%s\n", test, answer, err.Error()) + } + } +} diff --git a/vendor/github.com/go-ldap/ldap/error_test.go b/vendor/github.com/go-ldap/ldap/error_test.go new file mode 100644 index 000000000..4ec720d9f --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/error_test.go @@ -0,0 +1,29 @@ +package ldap + +import ( + "testing" + + "gopkg.in/asn1-ber.v1" +) + +// TestNilPacket tests that nil packets don't cause a panic. +func TestNilPacket(t *testing.T) { + // Test for nil packet + code, _ := getLDAPResultCode(nil) + if code != ErrorUnexpectedResponse { + t.Errorf("Should have an 'ErrorUnexpectedResponse' error in nil packets, got: %v", code) + } + + // Test for nil result + kids := []*ber.Packet{ + &ber.Packet{}, // Unused + nil, // Can't be nil + } + pack := &ber.Packet{Children: kids} + code, _ = getLDAPResultCode(pack) + + if code != ErrorUnexpectedResponse { + t.Errorf("Should have an 'ErrorUnexpectedResponse' error in nil packets, got: %v", code) + } + +} diff --git a/vendor/github.com/go-ldap/ldap/example_test.go b/vendor/github.com/go-ldap/ldap/example_test.go new file mode 100644 index 000000000..b018a9664 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/example_test.go @@ -0,0 +1,305 @@ +package ldap_test + +import ( + "crypto/tls" + "fmt" + "log" + + "gopkg.in/ldap.v2" +) + +// ExampleConn_Bind demonstrates how to bind a connection to an ldap user +// allowing access to restricted attrabutes that user has access to +func ExampleConn_Bind() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + err = l.Bind("cn=read-only-admin,dc=example,dc=com", "password") + if err != nil { + log.Fatal(err) + } +} + +// ExampleConn_Search demonstrates how to use the search interface +func ExampleConn_Search() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + searchRequest := ldap.NewSearchRequest( + "dc=example,dc=com", // The base dn to search + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(&(objectClass=organizationalPerson))", // The filter to apply + []string{"dn", "cn"}, // A list attributes to retrieve + nil, + ) + + sr, err := l.Search(searchRequest) + if err != nil { + log.Fatal(err) + } + + for _, entry := range sr.Entries { + fmt.Printf("%s: %v\n", entry.DN, entry.GetAttributeValue("cn")) + } +} + +// ExampleStartTLS demonstrates how to start a TLS connection +func ExampleConn_StartTLS() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + // Reconnect with TLS + err = l.StartTLS(&tls.Config{InsecureSkipVerify: true}) + if err != nil { + log.Fatal(err) + } + + // Opertations via l are now encrypted +} + +// ExampleConn_Compare demonstrates how to comapre an attribute with a value +func ExampleConn_Compare() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + matched, err := l.Compare("cn=user,dc=example,dc=com", "uid", "someuserid") + if err != nil { + log.Fatal(err) + } + + fmt.Println(matched) +} + +func ExampleConn_PasswordModify_admin() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + err = l.Bind("cn=admin,dc=example,dc=com", "password") + if err != nil { + log.Fatal(err) + } + + passwordModifyRequest := ldap.NewPasswordModifyRequest("cn=user,dc=example,dc=com", "", "NewPassword") + _, err = l.PasswordModify(passwordModifyRequest) + + if err != nil { + log.Fatalf("Password could not be changed: %s", err.Error()) + } +} + +func ExampleConn_PasswordModify_generatedPassword() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + err = l.Bind("cn=user,dc=example,dc=com", "password") + if err != nil { + log.Fatal(err) + } + + passwordModifyRequest := ldap.NewPasswordModifyRequest("", "OldPassword", "") + passwordModifyResponse, err := l.PasswordModify(passwordModifyRequest) + if err != nil { + log.Fatalf("Password could not be changed: %s", err.Error()) + } + + generatedPassword := passwordModifyResponse.GeneratedPassword + log.Printf("Generated password: %s\n", generatedPassword) +} + +func ExampleConn_PasswordModify_setNewPassword() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + err = l.Bind("cn=user,dc=example,dc=com", "password") + if err != nil { + log.Fatal(err) + } + + passwordModifyRequest := ldap.NewPasswordModifyRequest("", "OldPassword", "NewPassword") + _, err = l.PasswordModify(passwordModifyRequest) + + if err != nil { + log.Fatalf("Password could not be changed: %s", err.Error()) + } +} + +func ExampleConn_Modify() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + // Add a description, and replace the mail attributes + modify := ldap.NewModifyRequest("cn=user,dc=example,dc=com") + modify.Add("description", []string{"An example user"}) + modify.Replace("mail", []string{"user@example.org"}) + + err = l.Modify(modify) + if err != nil { + log.Fatal(err) + } +} + +// Example User Authentication shows how a typical application can verify a login attempt +func Example_userAuthentication() { + // The username and password we want to check + username := "someuser" + password := "userpassword" + + bindusername := "readonly" + bindpassword := "password" + + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + // Reconnect with TLS + err = l.StartTLS(&tls.Config{InsecureSkipVerify: true}) + if err != nil { + log.Fatal(err) + } + + // First bind with a read only user + err = l.Bind(bindusername, bindpassword) + if err != nil { + log.Fatal(err) + } + + // Search for the given username + searchRequest := ldap.NewSearchRequest( + "dc=example,dc=com", + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + fmt.Sprintf("(&(objectClass=organizationalPerson)&(uid=%s))", username), + []string{"dn"}, + nil, + ) + + sr, err := l.Search(searchRequest) + if err != nil { + log.Fatal(err) + } + + if len(sr.Entries) != 1 { + log.Fatal("User does not exist or too many entries returned") + } + + userdn := sr.Entries[0].DN + + // Bind as the user to verify their password + err = l.Bind(userdn, password) + if err != nil { + log.Fatal(err) + } + + // Rebind as the read only user for any futher queries + err = l.Bind(bindusername, bindpassword) + if err != nil { + log.Fatal(err) + } +} + +func Example_beherappolicy() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + controls := []ldap.Control{} + controls = append(controls, ldap.NewControlBeheraPasswordPolicy()) + bindRequest := ldap.NewSimpleBindRequest("cn=admin,dc=example,dc=com", "password", controls) + + r, err := l.SimpleBind(bindRequest) + ppolicyControl := ldap.FindControl(r.Controls, ldap.ControlTypeBeheraPasswordPolicy) + + var ppolicy *ldap.ControlBeheraPasswordPolicy + if ppolicyControl != nil { + ppolicy = ppolicyControl.(*ldap.ControlBeheraPasswordPolicy) + } else { + log.Printf("ppolicyControl response not avaliable.\n") + } + if err != nil { + errStr := "ERROR: Cannot bind: " + err.Error() + if ppolicy != nil && ppolicy.Error >= 0 { + errStr += ":" + ppolicy.ErrorString + } + log.Print(errStr) + } else { + logStr := "Login Ok" + if ppolicy != nil { + if ppolicy.Expire >= 0 { + logStr += fmt.Sprintf(". Password expires in %d seconds\n", ppolicy.Expire) + } else if ppolicy.Grace >= 0 { + logStr += fmt.Sprintf(". Password expired, %d grace logins remain\n", ppolicy.Grace) + } + } + log.Print(logStr) + } +} + +func Example_vchuppolicy() { + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389)) + if err != nil { + log.Fatal(err) + } + defer l.Close() + l.Debug = true + + bindRequest := ldap.NewSimpleBindRequest("cn=admin,dc=example,dc=com", "password", nil) + + r, err := l.SimpleBind(bindRequest) + + passwordMustChangeControl := ldap.FindControl(r.Controls, ldap.ControlTypeVChuPasswordMustChange) + var passwordMustChange *ldap.ControlVChuPasswordMustChange + if passwordMustChangeControl != nil { + passwordMustChange = passwordMustChangeControl.(*ldap.ControlVChuPasswordMustChange) + } + + if passwordMustChange != nil && passwordMustChange.MustChange { + log.Printf("Password Must be changed.\n") + } + + passwordWarningControl := ldap.FindControl(r.Controls, ldap.ControlTypeVChuPasswordWarning) + + var passwordWarning *ldap.ControlVChuPasswordWarning + if passwordWarningControl != nil { + passwordWarning = passwordWarningControl.(*ldap.ControlVChuPasswordWarning) + } else { + log.Printf("ppolicyControl response not available.\n") + } + if err != nil { + log.Print("ERROR: Cannot bind: " + err.Error()) + } else { + logStr := "Login Ok" + if passwordWarning != nil { + if passwordWarning.Expire >= 0 { + logStr += fmt.Sprintf(". Password expires in %d seconds\n", passwordWarning.Expire) + } + } + log.Print(logStr) + } +} diff --git a/vendor/github.com/go-ldap/ldap/filter_test.go b/vendor/github.com/go-ldap/ldap/filter_test.go new file mode 100644 index 000000000..ae1b79b0c --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/filter_test.go @@ -0,0 +1,248 @@ +package ldap_test + +import ( + "strings" + "testing" + + "gopkg.in/asn1-ber.v1" + "gopkg.in/ldap.v2" +) + +type compileTest struct { + filterStr string + + expectedFilter string + expectedType int + expectedErr string +} + +var testFilters = []compileTest{ + compileTest{ + filterStr: "(&(sn=Miller)(givenName=Bob))", + expectedFilter: "(&(sn=Miller)(givenName=Bob))", + expectedType: ldap.FilterAnd, + }, + compileTest{ + filterStr: "(|(sn=Miller)(givenName=Bob))", + expectedFilter: "(|(sn=Miller)(givenName=Bob))", + expectedType: ldap.FilterOr, + }, + compileTest{ + filterStr: "(!(sn=Miller))", + expectedFilter: "(!(sn=Miller))", + expectedType: ldap.FilterNot, + }, + compileTest{ + filterStr: "(sn=Miller)", + expectedFilter: "(sn=Miller)", + expectedType: ldap.FilterEqualityMatch, + }, + compileTest{ + filterStr: "(sn=Mill*)", + expectedFilter: "(sn=Mill*)", + expectedType: ldap.FilterSubstrings, + }, + compileTest{ + filterStr: "(sn=*Mill)", + expectedFilter: "(sn=*Mill)", + expectedType: ldap.FilterSubstrings, + }, + compileTest{ + filterStr: "(sn=*Mill*)", + expectedFilter: "(sn=*Mill*)", + expectedType: ldap.FilterSubstrings, + }, + compileTest{ + filterStr: "(sn=*i*le*)", + expectedFilter: "(sn=*i*le*)", + expectedType: ldap.FilterSubstrings, + }, + compileTest{ + filterStr: "(sn=Mi*l*r)", + expectedFilter: "(sn=Mi*l*r)", + expectedType: ldap.FilterSubstrings, + }, + // substring filters escape properly + compileTest{ + filterStr: `(sn=Mi*함*r)`, + expectedFilter: `(sn=Mi*\ed\95\a8*r)`, + expectedType: ldap.FilterSubstrings, + }, + // already escaped substring filters don't get double-escaped + compileTest{ + filterStr: `(sn=Mi*\ed\95\a8*r)`, + expectedFilter: `(sn=Mi*\ed\95\a8*r)`, + expectedType: ldap.FilterSubstrings, + }, + compileTest{ + filterStr: "(sn=Mi*le*)", + expectedFilter: "(sn=Mi*le*)", + expectedType: ldap.FilterSubstrings, + }, + compileTest{ + filterStr: "(sn=*i*ler)", + expectedFilter: "(sn=*i*ler)", + expectedType: ldap.FilterSubstrings, + }, + compileTest{ + filterStr: "(sn>=Miller)", + expectedFilter: "(sn>=Miller)", + expectedType: ldap.FilterGreaterOrEqual, + }, + compileTest{ + filterStr: "(sn<=Miller)", + expectedFilter: "(sn<=Miller)", + expectedType: ldap.FilterLessOrEqual, + }, + compileTest{ + filterStr: "(sn=*)", + expectedFilter: "(sn=*)", + expectedType: ldap.FilterPresent, + }, + compileTest{ + filterStr: "(sn~=Miller)", + expectedFilter: "(sn~=Miller)", + expectedType: ldap.FilterApproxMatch, + }, + compileTest{ + filterStr: `(objectGUID='\fc\fe\a3\ab\f9\90N\aaGm\d5I~\d12)`, + expectedFilter: `(objectGUID='\fc\fe\a3\ab\f9\90N\aaGm\d5I~\d12)`, + expectedType: ldap.FilterEqualityMatch, + }, + compileTest{ + filterStr: `(objectGUID=абвгдеёжзийклмнопрстуфхцчшщъыьэюя)`, + expectedFilter: `(objectGUID=\d0\b0\d0\b1\d0\b2\d0\b3\d0\b4\d0\b5\d1\91\d0\b6\d0\b7\d0\b8\d0\b9\d0\ba\d0\bb\d0\bc\d0\bd\d0\be\d0\bf\d1\80\d1\81\d1\82\d1\83\d1\84\d1\85\d1\86\d1\87\d1\88\d1\89\d1\8a\d1\8b\d1\8c\d1\8d\d1\8e\d1\8f)`, + expectedType: ldap.FilterEqualityMatch, + }, + compileTest{ + filterStr: `(objectGUID=함수목록)`, + expectedFilter: `(objectGUID=\ed\95\a8\ec\88\98\eb\aa\a9\eb\a1\9d)`, + expectedType: ldap.FilterEqualityMatch, + }, + compileTest{ + filterStr: `(objectGUID=`, + expectedFilter: ``, + expectedType: 0, + expectedErr: "unexpected end of filter", + }, + compileTest{ + filterStr: `(objectGUID=함수목록`, + expectedFilter: ``, + expectedType: 0, + expectedErr: "unexpected end of filter", + }, + compileTest{ + filterStr: `(&(objectclass=inetorgperson)(cn=中文))`, + expectedFilter: `(&(objectclass=inetorgperson)(cn=\e4\b8\ad\e6\96\87))`, + expectedType: 0, + }, + // attr extension + compileTest{ + filterStr: `(memberOf:=foo)`, + expectedFilter: `(memberOf:=foo)`, + expectedType: ldap.FilterExtensibleMatch, + }, + // attr+named matching rule extension + compileTest{ + filterStr: `(memberOf:test:=foo)`, + expectedFilter: `(memberOf:test:=foo)`, + expectedType: ldap.FilterExtensibleMatch, + }, + // attr+oid matching rule extension + compileTest{ + filterStr: `(cn:1.2.3.4.5:=Fred Flintstone)`, + expectedFilter: `(cn:1.2.3.4.5:=Fred Flintstone)`, + expectedType: ldap.FilterExtensibleMatch, + }, + // attr+dn+oid matching rule extension + compileTest{ + filterStr: `(sn:dn:2.4.6.8.10:=Barney Rubble)`, + expectedFilter: `(sn:dn:2.4.6.8.10:=Barney Rubble)`, + expectedType: ldap.FilterExtensibleMatch, + }, + // attr+dn extension + compileTest{ + filterStr: `(o:dn:=Ace Industry)`, + expectedFilter: `(o:dn:=Ace Industry)`, + expectedType: ldap.FilterExtensibleMatch, + }, + // dn extension + compileTest{ + filterStr: `(:dn:2.4.6.8.10:=Dino)`, + expectedFilter: `(:dn:2.4.6.8.10:=Dino)`, + expectedType: ldap.FilterExtensibleMatch, + }, + compileTest{ + filterStr: `(memberOf:1.2.840.113556.1.4.1941:=CN=User1,OU=blah,DC=mydomain,DC=net)`, + expectedFilter: `(memberOf:1.2.840.113556.1.4.1941:=CN=User1,OU=blah,DC=mydomain,DC=net)`, + expectedType: ldap.FilterExtensibleMatch, + }, + + // compileTest{ filterStr: "()", filterType: FilterExtensibleMatch }, +} + +var testInvalidFilters = []string{ + `(objectGUID=\zz)`, + `(objectGUID=\a)`, +} + +func TestFilter(t *testing.T) { + // Test Compiler and Decompiler + for _, i := range testFilters { + filter, err := ldap.CompileFilter(i.filterStr) + if err != nil { + if i.expectedErr == "" || !strings.Contains(err.Error(), i.expectedErr) { + t.Errorf("Problem compiling '%s' - '%v' (expected error to contain '%v')", i.filterStr, err, i.expectedErr) + } + } else if filter.Tag != ber.Tag(i.expectedType) { + t.Errorf("%q Expected %q got %q", i.filterStr, ldap.FilterMap[uint64(i.expectedType)], ldap.FilterMap[uint64(filter.Tag)]) + } else { + o, err := ldap.DecompileFilter(filter) + if err != nil { + t.Errorf("Problem compiling %s - %s", i.filterStr, err.Error()) + } else if i.expectedFilter != o { + t.Errorf("%q expected, got %q", i.expectedFilter, o) + } + } + } +} + +func TestInvalidFilter(t *testing.T) { + for _, filterStr := range testInvalidFilters { + if _, err := ldap.CompileFilter(filterStr); err == nil { + t.Errorf("Problem compiling %s - expected err", filterStr) + } + } +} + +func BenchmarkFilterCompile(b *testing.B) { + b.StopTimer() + filters := make([]string, len(testFilters)) + + // Test Compiler and Decompiler + for idx, i := range testFilters { + filters[idx] = i.filterStr + } + + maxIdx := len(filters) + b.StartTimer() + for i := 0; i < b.N; i++ { + ldap.CompileFilter(filters[i%maxIdx]) + } +} + +func BenchmarkFilterDecompile(b *testing.B) { + b.StopTimer() + filters := make([]*ber.Packet, len(testFilters)) + + // Test Compiler and Decompiler + for idx, i := range testFilters { + filters[idx], _ = ldap.CompileFilter(i.filterStr) + } + + maxIdx := len(filters) + b.StartTimer() + for i := 0; i < b.N; i++ { + ldap.DecompileFilter(filters[i%maxIdx]) + } +} diff --git a/vendor/github.com/go-ldap/ldap/ldap_test.go b/vendor/github.com/go-ldap/ldap/ldap_test.go new file mode 100644 index 000000000..9f4305180 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/ldap_test.go @@ -0,0 +1,275 @@ +package ldap_test + +import ( + "crypto/tls" + "fmt" + "testing" + + "gopkg.in/ldap.v2" +) + +var ldapServer = "ldap.itd.umich.edu" +var ldapPort = uint16(389) +var ldapTLSPort = uint16(636) +var baseDN = "dc=umich,dc=edu" +var filter = []string{ + "(cn=cis-fac)", + "(&(owner=*)(cn=cis-fac))", + "(&(objectclass=rfc822mailgroup)(cn=*Computer*))", + "(&(objectclass=rfc822mailgroup)(cn=*Mathematics*))"} +var attributes = []string{ + "cn", + "description"} + +func TestDial(t *testing.T) { + fmt.Printf("TestDial: starting...\n") + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) + if err != nil { + t.Errorf(err.Error()) + return + } + defer l.Close() + fmt.Printf("TestDial: finished...\n") +} + +func TestDialTLS(t *testing.T) { + fmt.Printf("TestDialTLS: starting...\n") + l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapTLSPort), &tls.Config{InsecureSkipVerify: true}) + if err != nil { + t.Errorf(err.Error()) + return + } + defer l.Close() + fmt.Printf("TestDialTLS: finished...\n") +} + +func TestStartTLS(t *testing.T) { + fmt.Printf("TestStartTLS: starting...\n") + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) + if err != nil { + t.Errorf(err.Error()) + return + } + err = l.StartTLS(&tls.Config{InsecureSkipVerify: true}) + if err != nil { + t.Errorf(err.Error()) + return + } + fmt.Printf("TestStartTLS: finished...\n") +} + +func TestSearch(t *testing.T) { + fmt.Printf("TestSearch: starting...\n") + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) + if err != nil { + t.Errorf(err.Error()) + return + } + defer l.Close() + + searchRequest := ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false, + filter[0], + attributes, + nil) + + sr, err := l.Search(searchRequest) + if err != nil { + t.Errorf(err.Error()) + return + } + + fmt.Printf("TestSearch: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries)) +} + +func TestSearchStartTLS(t *testing.T) { + fmt.Printf("TestSearchStartTLS: starting...\n") + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) + if err != nil { + t.Errorf(err.Error()) + return + } + defer l.Close() + + searchRequest := ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false, + filter[0], + attributes, + nil) + + sr, err := l.Search(searchRequest) + if err != nil { + t.Errorf(err.Error()) + return + } + + fmt.Printf("TestSearchStartTLS: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries)) + + fmt.Printf("TestSearchStartTLS: upgrading with startTLS\n") + err = l.StartTLS(&tls.Config{InsecureSkipVerify: true}) + if err != nil { + t.Errorf(err.Error()) + return + } + + sr, err = l.Search(searchRequest) + if err != nil { + t.Errorf(err.Error()) + return + } + + fmt.Printf("TestSearchStartTLS: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries)) +} + +func TestSearchWithPaging(t *testing.T) { + fmt.Printf("TestSearchWithPaging: starting...\n") + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) + if err != nil { + t.Errorf(err.Error()) + return + } + defer l.Close() + + err = l.Bind("", "") + if err != nil { + t.Errorf(err.Error()) + return + } + + searchRequest := ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false, + filter[2], + attributes, + nil) + sr, err := l.SearchWithPaging(searchRequest, 5) + if err != nil { + t.Errorf(err.Error()) + return + } + + fmt.Printf("TestSearchWithPaging: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries)) + + searchRequest = ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false, + filter[2], + attributes, + []ldap.Control{ldap.NewControlPaging(5)}) + sr, err = l.SearchWithPaging(searchRequest, 5) + if err != nil { + t.Errorf(err.Error()) + return + } + + fmt.Printf("TestSearchWithPaging: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries)) + + searchRequest = ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false, + filter[2], + attributes, + []ldap.Control{ldap.NewControlPaging(500)}) + sr, err = l.SearchWithPaging(searchRequest, 5) + if err == nil { + t.Errorf("expected an error when paging size in control in search request doesn't match size given in call, got none") + return + } +} + +func searchGoroutine(t *testing.T, l *ldap.Conn, results chan *ldap.SearchResult, i int) { + searchRequest := ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false, + filter[i], + attributes, + nil) + sr, err := l.Search(searchRequest) + if err != nil { + t.Errorf(err.Error()) + results <- nil + return + } + results <- sr +} + +func testMultiGoroutineSearch(t *testing.T, TLS bool, startTLS bool) { + fmt.Printf("TestMultiGoroutineSearch: starting...\n") + var l *ldap.Conn + var err error + if TLS { + l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapTLSPort), &tls.Config{InsecureSkipVerify: true}) + if err != nil { + t.Errorf(err.Error()) + return + } + defer l.Close() + } else { + l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) + if err != nil { + t.Errorf(err.Error()) + return + } + if startTLS { + fmt.Printf("TestMultiGoroutineSearch: using StartTLS...\n") + err := l.StartTLS(&tls.Config{InsecureSkipVerify: true}) + if err != nil { + t.Errorf(err.Error()) + return + } + + } + } + + results := make([]chan *ldap.SearchResult, len(filter)) + for i := range filter { + results[i] = make(chan *ldap.SearchResult) + go searchGoroutine(t, l, results[i], i) + } + for i := range filter { + sr := <-results[i] + if sr == nil { + t.Errorf("Did not receive results from goroutine for %q", filter[i]) + } else { + fmt.Printf("TestMultiGoroutineSearch(%d): %s -> num of entries = %d\n", i, filter[i], len(sr.Entries)) + } + } +} + +func TestMultiGoroutineSearch(t *testing.T) { + testMultiGoroutineSearch(t, false, false) + testMultiGoroutineSearch(t, true, true) + testMultiGoroutineSearch(t, false, true) +} + +func TestEscapeFilter(t *testing.T) { + if got, want := ldap.EscapeFilter("a\x00b(c)d*e\\f"), `a\00b\28c\29d\2ae\5cf`; got != want { + t.Errorf("Got %s, expected %s", want, got) + } + if got, want := ldap.EscapeFilter("Lučić"), `Lu\c4\8di\c4\87`; got != want { + t.Errorf("Got %s, expected %s", want, got) + } +} + +func TestCompare(t *testing.T) { + fmt.Printf("TestCompare: starting...\n") + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) + if err != nil { + t.Fatal(err.Error()) + } + defer l.Close() + + dn := "cn=math mich,ou=User Groups,ou=Groups,dc=umich,dc=edu" + attribute := "cn" + value := "math mich" + + sr, err := l.Compare(dn, attribute, value) + if err != nil { + t.Errorf(err.Error()) + return + } + + fmt.Printf("TestCompare: -> %v\n", sr) +} diff --git a/vendor/github.com/go-ldap/ldap/search_test.go b/vendor/github.com/go-ldap/ldap/search_test.go new file mode 100644 index 000000000..efb8147d1 --- /dev/null +++ b/vendor/github.com/go-ldap/ldap/search_test.go @@ -0,0 +1,31 @@ +package ldap + +import ( + "reflect" + "testing" +) + +// TestNewEntry tests that repeated calls to NewEntry return the same value with the same input +func TestNewEntry(t *testing.T) { + dn := "testDN" + attributes := map[string][]string{ + "alpha": {"value"}, + "beta": {"value"}, + "gamma": {"value"}, + "delta": {"value"}, + "epsilon": {"value"}, + } + exectedEntry := NewEntry(dn, attributes) + + iteration := 0 + for { + if iteration == 100 { + break + } + testEntry := NewEntry(dn, attributes) + if !reflect.DeepEqual(exectedEntry, testEntry) { + t.Fatalf("consequent calls to NewEntry did not yield the same result:\n\texpected:\n\t%s\n\tgot:\n\t%s\n", exectedEntry, testEntry) + } + iteration = iteration + 1 + } +} diff --git a/vendor/github.com/go-sql-driver/mysql/benchmark_test.go b/vendor/github.com/go-sql-driver/mysql/benchmark_test.go new file mode 100644 index 000000000..8f721139b --- /dev/null +++ b/vendor/github.com/go-sql-driver/mysql/benchmark_test.go @@ -0,0 +1,246 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +package mysql + +import ( + "bytes" + "database/sql" + "database/sql/driver" + "math" + "strings" + "sync" + "sync/atomic" + "testing" + "time" +) + +type TB testing.B + +func (tb *TB) check(err error) { + if err != nil { + tb.Fatal(err) + } +} + +func (tb *TB) checkDB(db *sql.DB, err error) *sql.DB { + tb.check(err) + return db +} + +func (tb *TB) checkRows(rows *sql.Rows, err error) *sql.Rows { + tb.check(err) + return rows +} + +func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt { + tb.check(err) + return stmt +} + +func initDB(b *testing.B, queries ...string) *sql.DB { + tb := (*TB)(b) + db := tb.checkDB(sql.Open("mysql", dsn)) + for _, query := range queries { + if _, err := db.Exec(query); err != nil { + if w, ok := err.(MySQLWarnings); ok { + b.Logf("warning on %q: %v", query, w) + } else { + b.Fatalf("error on %q: %v", query, err) + } + } + } + return db +} + +const concurrencyLevel = 10 + +func BenchmarkQuery(b *testing.B) { + tb := (*TB)(b) + b.StopTimer() + b.ReportAllocs() + db := initDB(b, + "DROP TABLE IF EXISTS foo", + "CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))", + `INSERT INTO foo VALUES (1, "one")`, + `INSERT INTO foo VALUES (2, "two")`, + ) + db.SetMaxIdleConns(concurrencyLevel) + defer db.Close() + + stmt := tb.checkStmt(db.Prepare("SELECT val FROM foo WHERE id=?")) + defer stmt.Close() + + remain := int64(b.N) + var wg sync.WaitGroup + wg.Add(concurrencyLevel) + defer wg.Wait() + b.StartTimer() + + for i := 0; i < concurrencyLevel; i++ { + go func() { + for { + if atomic.AddInt64(&remain, -1) < 0 { + wg.Done() + return + } + + var got string + tb.check(stmt.QueryRow(1).Scan(&got)) + if got != "one" { + b.Errorf("query = %q; want one", got) + wg.Done() + return + } + } + }() + } +} + +func BenchmarkExec(b *testing.B) { + tb := (*TB)(b) + b.StopTimer() + b.ReportAllocs() + db := tb.checkDB(sql.Open("mysql", dsn)) + db.SetMaxIdleConns(concurrencyLevel) + defer db.Close() + + stmt := tb.checkStmt(db.Prepare("DO 1")) + defer stmt.Close() + + remain := int64(b.N) + var wg sync.WaitGroup + wg.Add(concurrencyLevel) + defer wg.Wait() + b.StartTimer() + + for i := 0; i < concurrencyLevel; i++ { + go func() { + for { + if atomic.AddInt64(&remain, -1) < 0 { + wg.Done() + return + } + + if _, err := stmt.Exec(); err != nil { + b.Fatal(err.Error()) + } + } + }() + } +} + +// data, but no db writes +var roundtripSample []byte + +func initRoundtripBenchmarks() ([]byte, int, int) { + if roundtripSample == nil { + roundtripSample = []byte(strings.Repeat("0123456789abcdef", 1024*1024)) + } + return roundtripSample, 16, len(roundtripSample) +} + +func BenchmarkRoundtripTxt(b *testing.B) { + b.StopTimer() + sample, min, max := initRoundtripBenchmarks() + sampleString := string(sample) + b.ReportAllocs() + tb := (*TB)(b) + db := tb.checkDB(sql.Open("mysql", dsn)) + defer db.Close() + b.StartTimer() + var result string + for i := 0; i < b.N; i++ { + length := min + i + if length > max { + length = max + } + test := sampleString[0:length] + rows := tb.checkRows(db.Query(`SELECT "` + test + `"`)) + if !rows.Next() { + rows.Close() + b.Fatalf("crashed") + } + err := rows.Scan(&result) + if err != nil { + rows.Close() + b.Fatalf("crashed") + } + if result != test { + rows.Close() + b.Errorf("mismatch") + } + rows.Close() + } +} + +func BenchmarkRoundtripBin(b *testing.B) { + b.StopTimer() + sample, min, max := initRoundtripBenchmarks() + b.ReportAllocs() + tb := (*TB)(b) + db := tb.checkDB(sql.Open("mysql", dsn)) + defer db.Close() + stmt := tb.checkStmt(db.Prepare("SELECT ?")) + defer stmt.Close() + b.StartTimer() + var result sql.RawBytes + for i := 0; i < b.N; i++ { + length := min + i + if length > max { + length = max + } + test := sample[0:length] + rows := tb.checkRows(stmt.Query(test)) + if !rows.Next() { + rows.Close() + b.Fatalf("crashed") + } + err := rows.Scan(&result) + if err != nil { + rows.Close() + b.Fatalf("crashed") + } + if !bytes.Equal(result, test) { + rows.Close() + b.Errorf("mismatch") + } + rows.Close() + } +} + +func BenchmarkInterpolation(b *testing.B) { + mc := &mysqlConn{ + cfg: &Config{ + InterpolateParams: true, + Loc: time.UTC, + }, + maxPacketAllowed: maxPacketSize, + maxWriteSize: maxPacketSize - 1, + buf: newBuffer(nil), + } + + args := []driver.Value{ + int64(42424242), + float64(math.Pi), + false, + time.Unix(1423411542, 807015000), + []byte("bytes containing special chars ' \" \a \x00"), + "string containing special chars ' \" \a \x00", + } + q := "SELECT ?, ?, ?, ?, ?, ?" + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := mc.interpolateParams(q, args) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/vendor/github.com/go-sql-driver/mysql/driver_test.go b/vendor/github.com/go-sql-driver/mysql/driver_test.go new file mode 100644 index 000000000..efbc4925c --- /dev/null +++ b/vendor/github.com/go-sql-driver/mysql/driver_test.go @@ -0,0 +1,1857 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +package mysql + +import ( + "bytes" + "crypto/tls" + "database/sql" + "database/sql/driver" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/url" + "os" + "strings" + "sync" + "sync/atomic" + "testing" + "time" +) + +var ( + user string + pass string + prot string + addr string + dbname string + dsn string + netAddr string + available bool +) + +var ( + tDate = time.Date(2012, 6, 14, 0, 0, 0, 0, time.UTC) + sDate = "2012-06-14" + tDateTime = time.Date(2011, 11, 20, 21, 27, 37, 0, time.UTC) + sDateTime = "2011-11-20 21:27:37" + tDate0 = time.Time{} + sDate0 = "0000-00-00" + sDateTime0 = "0000-00-00 00:00:00" +) + +// See https://github.com/go-sql-driver/mysql/wiki/Testing +func init() { + // get environment variables + env := func(key, defaultValue string) string { + if value := os.Getenv(key); value != "" { + return value + } + return defaultValue + } + user = env("MYSQL_TEST_USER", "root") + pass = env("MYSQL_TEST_PASS", "") + prot = env("MYSQL_TEST_PROT", "tcp") + addr = env("MYSQL_TEST_ADDR", "localhost:3306") + dbname = env("MYSQL_TEST_DBNAME", "gotest") + netAddr = fmt.Sprintf("%s(%s)", prot, addr) + dsn = fmt.Sprintf("%s:%s@%s/%s?timeout=30s&strict=true", user, pass, netAddr, dbname) + c, err := net.Dial(prot, addr) + if err == nil { + available = true + c.Close() + } +} + +type DBTest struct { + *testing.T + db *sql.DB +} + +func runTestsWithMultiStatement(t *testing.T, dsn string, tests ...func(dbt *DBTest)) { + if !available { + t.Skipf("MySQL server not running on %s", netAddr) + } + + dsn += "&multiStatements=true" + var db *sql.DB + if _, err := ParseDSN(dsn); err != errInvalidDSNUnsafeCollation { + db, err = sql.Open("mysql", dsn) + if err != nil { + t.Fatalf("error connecting: %s", err.Error()) + } + defer db.Close() + } + + dbt := &DBTest{t, db} + for _, test := range tests { + test(dbt) + dbt.db.Exec("DROP TABLE IF EXISTS test") + } +} + +func runTests(t *testing.T, dsn string, tests ...func(dbt *DBTest)) { + if !available { + t.Skipf("MySQL server not running on %s", netAddr) + } + + db, err := sql.Open("mysql", dsn) + if err != nil { + t.Fatalf("error connecting: %s", err.Error()) + } + defer db.Close() + + db.Exec("DROP TABLE IF EXISTS test") + + dsn2 := dsn + "&interpolateParams=true" + var db2 *sql.DB + if _, err := ParseDSN(dsn2); err != errInvalidDSNUnsafeCollation { + db2, err = sql.Open("mysql", dsn2) + if err != nil { + t.Fatalf("error connecting: %s", err.Error()) + } + defer db2.Close() + } + + dsn3 := dsn + "&multiStatements=true" + var db3 *sql.DB + if _, err := ParseDSN(dsn3); err != errInvalidDSNUnsafeCollation { + db3, err = sql.Open("mysql", dsn3) + if err != nil { + t.Fatalf("error connecting: %s", err.Error()) + } + defer db3.Close() + } + + dbt := &DBTest{t, db} + dbt2 := &DBTest{t, db2} + dbt3 := &DBTest{t, db3} + for _, test := range tests { + test(dbt) + dbt.db.Exec("DROP TABLE IF EXISTS test") + if db2 != nil { + test(dbt2) + dbt2.db.Exec("DROP TABLE IF EXISTS test") + } + if db3 != nil { + test(dbt3) + dbt3.db.Exec("DROP TABLE IF EXISTS test") + } + } +} + +func (dbt *DBTest) fail(method, query string, err error) { + if len(query) > 300 { + query = "[query too large to print]" + } + dbt.Fatalf("error on %s %s: %s", method, query, err.Error()) +} + +func (dbt *DBTest) mustExec(query string, args ...interface{}) (res sql.Result) { + res, err := dbt.db.Exec(query, args...) + if err != nil { + dbt.fail("exec", query, err) + } + return res +} + +func (dbt *DBTest) mustQuery(query string, args ...interface{}) (rows *sql.Rows) { + rows, err := dbt.db.Query(query, args...) + if err != nil { + dbt.fail("query", query, err) + } + return rows +} + +func TestEmptyQuery(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + // just a comment, no query + rows := dbt.mustQuery("--") + // will hang before #255 + if rows.Next() { + dbt.Errorf("next on rows must be false") + } + }) +} + +func TestCRUD(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + // Create Table + dbt.mustExec("CREATE TABLE test (value BOOL)") + + // Test for unexpected data + var out bool + rows := dbt.mustQuery("SELECT * FROM test") + if rows.Next() { + dbt.Error("unexpected data in empty table") + } + + // Create Data + res := dbt.mustExec("INSERT INTO test VALUES (1)") + count, err := res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 1 { + dbt.Fatalf("expected 1 affected row, got %d", count) + } + + id, err := res.LastInsertId() + if err != nil { + dbt.Fatalf("res.LastInsertId() returned error: %s", err.Error()) + } + if id != 0 { + dbt.Fatalf("expected InsertId 0, got %d", id) + } + + // Read + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if true != out { + dbt.Errorf("true != %t", out) + } + + if rows.Next() { + dbt.Error("unexpected data") + } + } else { + dbt.Error("no data") + } + + // Update + res = dbt.mustExec("UPDATE test SET value = ? WHERE value = ?", false, true) + count, err = res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 1 { + dbt.Fatalf("expected 1 affected row, got %d", count) + } + + // Check Update + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if false != out { + dbt.Errorf("false != %t", out) + } + + if rows.Next() { + dbt.Error("unexpected data") + } + } else { + dbt.Error("no data") + } + + // Delete + res = dbt.mustExec("DELETE FROM test WHERE value = ?", false) + count, err = res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 1 { + dbt.Fatalf("expected 1 affected row, got %d", count) + } + + // Check for unexpected rows + res = dbt.mustExec("DELETE FROM test") + count, err = res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 0 { + dbt.Fatalf("expected 0 affected row, got %d", count) + } + }) +} + +func TestMultiQuery(t *testing.T) { + runTestsWithMultiStatement(t, dsn, func(dbt *DBTest) { + // Create Table + dbt.mustExec("CREATE TABLE `test` (`id` int(11) NOT NULL, `value` int(11) NOT NULL) ") + + // Create Data + res := dbt.mustExec("INSERT INTO test VALUES (1, 1)") + count, err := res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 1 { + dbt.Fatalf("expected 1 affected row, got %d", count) + } + + // Update + res = dbt.mustExec("UPDATE test SET value = 3 WHERE id = 1; UPDATE test SET value = 4 WHERE id = 1; UPDATE test SET value = 5 WHERE id = 1;") + count, err = res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 1 { + dbt.Fatalf("expected 1 affected row, got %d", count) + } + + // Read + var out int + rows := dbt.mustQuery("SELECT value FROM test WHERE id=1;") + if rows.Next() { + rows.Scan(&out) + if 5 != out { + dbt.Errorf("5 != %d", out) + } + + if rows.Next() { + dbt.Error("unexpected data") + } + } else { + dbt.Error("no data") + } + + }) +} + +func TestInt(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + types := [5]string{"TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT"} + in := int64(42) + var out int64 + var rows *sql.Rows + + // SIGNED + for _, v := range types { + dbt.mustExec("CREATE TABLE test (value " + v + ")") + + dbt.mustExec("INSERT INTO test VALUES (?)", in) + + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Errorf("%s: %d != %d", v, in, out) + } + } else { + dbt.Errorf("%s: no data", v) + } + + dbt.mustExec("DROP TABLE IF EXISTS test") + } + + // UNSIGNED ZEROFILL + for _, v := range types { + dbt.mustExec("CREATE TABLE test (value " + v + " ZEROFILL)") + + dbt.mustExec("INSERT INTO test VALUES (?)", in) + + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Errorf("%s ZEROFILL: %d != %d", v, in, out) + } + } else { + dbt.Errorf("%s ZEROFILL: no data", v) + } + + dbt.mustExec("DROP TABLE IF EXISTS test") + } + }) +} + +func TestFloat32(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + types := [2]string{"FLOAT", "DOUBLE"} + in := float32(42.23) + var out float32 + var rows *sql.Rows + for _, v := range types { + dbt.mustExec("CREATE TABLE test (value " + v + ")") + dbt.mustExec("INSERT INTO test VALUES (?)", in) + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Errorf("%s: %g != %g", v, in, out) + } + } else { + dbt.Errorf("%s: no data", v) + } + dbt.mustExec("DROP TABLE IF EXISTS test") + } + }) +} + +func TestFloat64(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + types := [2]string{"FLOAT", "DOUBLE"} + var expected float64 = 42.23 + var out float64 + var rows *sql.Rows + for _, v := range types { + dbt.mustExec("CREATE TABLE test (value " + v + ")") + dbt.mustExec("INSERT INTO test VALUES (42.23)") + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if expected != out { + dbt.Errorf("%s: %g != %g", v, expected, out) + } + } else { + dbt.Errorf("%s: no data", v) + } + dbt.mustExec("DROP TABLE IF EXISTS test") + } + }) +} + +func TestFloat64Placeholder(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + types := [2]string{"FLOAT", "DOUBLE"} + var expected float64 = 42.23 + var out float64 + var rows *sql.Rows + for _, v := range types { + dbt.mustExec("CREATE TABLE test (id int, value " + v + ")") + dbt.mustExec("INSERT INTO test VALUES (1, 42.23)") + rows = dbt.mustQuery("SELECT value FROM test WHERE id = ?", 1) + if rows.Next() { + rows.Scan(&out) + if expected != out { + dbt.Errorf("%s: %g != %g", v, expected, out) + } + } else { + dbt.Errorf("%s: no data", v) + } + dbt.mustExec("DROP TABLE IF EXISTS test") + } + }) +} + +func TestString(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + types := [6]string{"CHAR(255)", "VARCHAR(255)", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"} + in := "κόσμε üöäßñóùéàâÿœ'îë Árvíztűrő いろはにほへとちりぬるを イロハニホヘト דג סקרן чащах น่าฟังเอย" + var out string + var rows *sql.Rows + + for _, v := range types { + dbt.mustExec("CREATE TABLE test (value " + v + ") CHARACTER SET utf8") + + dbt.mustExec("INSERT INTO test VALUES (?)", in) + + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Errorf("%s: %s != %s", v, in, out) + } + } else { + dbt.Errorf("%s: no data", v) + } + + dbt.mustExec("DROP TABLE IF EXISTS test") + } + + // BLOB + dbt.mustExec("CREATE TABLE test (id int, value BLOB) CHARACTER SET utf8") + + id := 2 + in = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " + + "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. " + + "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. " + + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " + + "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " + + "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. " + + "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." + dbt.mustExec("INSERT INTO test VALUES (?, ?)", id, in) + + err := dbt.db.QueryRow("SELECT value FROM test WHERE id = ?", id).Scan(&out) + if err != nil { + dbt.Fatalf("Error on BLOB-Query: %s", err.Error()) + } else if out != in { + dbt.Errorf("BLOB: %s != %s", in, out) + } + }) +} + +type timeTests struct { + dbtype string + tlayout string + tests []timeTest +} + +type timeTest struct { + s string // leading "!": do not use t as value in queries + t time.Time +} + +type timeMode byte + +func (t timeMode) String() string { + switch t { + case binaryString: + return "binary:string" + case binaryTime: + return "binary:time.Time" + case textString: + return "text:string" + } + panic("unsupported timeMode") +} + +func (t timeMode) Binary() bool { + switch t { + case binaryString, binaryTime: + return true + } + return false +} + +const ( + binaryString timeMode = iota + binaryTime + textString +) + +func (t timeTest) genQuery(dbtype string, mode timeMode) string { + var inner string + if mode.Binary() { + inner = "?" + } else { + inner = `"%s"` + } + return `SELECT cast(` + inner + ` as ` + dbtype + `)` +} + +func (t timeTest) run(dbt *DBTest, dbtype, tlayout string, mode timeMode) { + var rows *sql.Rows + query := t.genQuery(dbtype, mode) + switch mode { + case binaryString: + rows = dbt.mustQuery(query, t.s) + case binaryTime: + rows = dbt.mustQuery(query, t.t) + case textString: + query = fmt.Sprintf(query, t.s) + rows = dbt.mustQuery(query) + default: + panic("unsupported mode") + } + defer rows.Close() + var err error + if !rows.Next() { + err = rows.Err() + if err == nil { + err = fmt.Errorf("no data") + } + dbt.Errorf("%s [%s]: %s", dbtype, mode, err) + return + } + var dst interface{} + err = rows.Scan(&dst) + if err != nil { + dbt.Errorf("%s [%s]: %s", dbtype, mode, err) + return + } + switch val := dst.(type) { + case []uint8: + str := string(val) + if str == t.s { + return + } + if mode.Binary() && dbtype == "DATETIME" && len(str) == 26 && str[:19] == t.s { + // a fix mainly for TravisCI: + // accept full microsecond resolution in result for DATETIME columns + // where the binary protocol was used + return + } + dbt.Errorf("%s [%s] to string: expected %q, got %q", + dbtype, mode, + t.s, str, + ) + case time.Time: + if val == t.t { + return + } + dbt.Errorf("%s [%s] to string: expected %q, got %q", + dbtype, mode, + t.s, val.Format(tlayout), + ) + default: + fmt.Printf("%#v\n", []interface{}{dbtype, tlayout, mode, t.s, t.t}) + dbt.Errorf("%s [%s]: unhandled type %T (is '%v')", + dbtype, mode, + val, val, + ) + } +} + +func TestDateTime(t *testing.T) { + afterTime := func(t time.Time, d string) time.Time { + dur, err := time.ParseDuration(d) + if err != nil { + panic(err) + } + return t.Add(dur) + } + // NOTE: MySQL rounds DATETIME(x) up - but that's not included in the tests + format := "2006-01-02 15:04:05.999999" + t0 := time.Time{} + tstr0 := "0000-00-00 00:00:00.000000" + testcases := []timeTests{ + {"DATE", format[:10], []timeTest{ + {t: time.Date(2011, 11, 20, 0, 0, 0, 0, time.UTC)}, + {t: t0, s: tstr0[:10]}, + }}, + {"DATETIME", format[:19], []timeTest{ + {t: time.Date(2011, 11, 20, 21, 27, 37, 0, time.UTC)}, + {t: t0, s: tstr0[:19]}, + }}, + {"DATETIME(0)", format[:21], []timeTest{ + {t: time.Date(2011, 11, 20, 21, 27, 37, 0, time.UTC)}, + {t: t0, s: tstr0[:19]}, + }}, + {"DATETIME(1)", format[:21], []timeTest{ + {t: time.Date(2011, 11, 20, 21, 27, 37, 100000000, time.UTC)}, + {t: t0, s: tstr0[:21]}, + }}, + {"DATETIME(6)", format, []timeTest{ + {t: time.Date(2011, 11, 20, 21, 27, 37, 123456000, time.UTC)}, + {t: t0, s: tstr0}, + }}, + {"TIME", format[11:19], []timeTest{ + {t: afterTime(t0, "12345s")}, + {s: "!-12:34:56"}, + {s: "!-838:59:59"}, + {s: "!838:59:59"}, + {t: t0, s: tstr0[11:19]}, + }}, + {"TIME(0)", format[11:19], []timeTest{ + {t: afterTime(t0, "12345s")}, + {s: "!-12:34:56"}, + {s: "!-838:59:59"}, + {s: "!838:59:59"}, + {t: t0, s: tstr0[11:19]}, + }}, + {"TIME(1)", format[11:21], []timeTest{ + {t: afterTime(t0, "12345600ms")}, + {s: "!-12:34:56.7"}, + {s: "!-838:59:58.9"}, + {s: "!838:59:58.9"}, + {t: t0, s: tstr0[11:21]}, + }}, + {"TIME(6)", format[11:], []timeTest{ + {t: afterTime(t0, "1234567890123000ns")}, + {s: "!-12:34:56.789012"}, + {s: "!-838:59:58.999999"}, + {s: "!838:59:58.999999"}, + {t: t0, s: tstr0[11:]}, + }}, + } + dsns := []string{ + dsn + "&parseTime=true", + dsn + "&parseTime=false", + } + for _, testdsn := range dsns { + runTests(t, testdsn, func(dbt *DBTest) { + microsecsSupported := false + zeroDateSupported := false + var rows *sql.Rows + var err error + rows, err = dbt.db.Query(`SELECT cast("00:00:00.1" as TIME(1)) = "00:00:00.1"`) + if err == nil { + rows.Scan(µsecsSupported) + rows.Close() + } + rows, err = dbt.db.Query(`SELECT cast("0000-00-00" as DATE) = "0000-00-00"`) + if err == nil { + rows.Scan(&zeroDateSupported) + rows.Close() + } + for _, setups := range testcases { + if t := setups.dbtype; !microsecsSupported && t[len(t)-1:] == ")" { + // skip fractional second tests if unsupported by server + continue + } + for _, setup := range setups.tests { + allowBinTime := true + if setup.s == "" { + // fill time string whereever Go can reliable produce it + setup.s = setup.t.Format(setups.tlayout) + } else if setup.s[0] == '!' { + // skip tests using setup.t as source in queries + allowBinTime = false + // fix setup.s - remove the "!" + setup.s = setup.s[1:] + } + if !zeroDateSupported && setup.s == tstr0[:len(setup.s)] { + // skip disallowed 0000-00-00 date + continue + } + setup.run(dbt, setups.dbtype, setups.tlayout, textString) + setup.run(dbt, setups.dbtype, setups.tlayout, binaryString) + if allowBinTime { + setup.run(dbt, setups.dbtype, setups.tlayout, binaryTime) + } + } + } + }) + } +} + +func TestTimestampMicros(t *testing.T) { + format := "2006-01-02 15:04:05.999999" + f0 := format[:19] + f1 := format[:21] + f6 := format[:26] + runTests(t, dsn, func(dbt *DBTest) { + // check if microseconds are supported. + // Do not use timestamp(x) for that check - before 5.5.6, x would mean display width + // and not precision. + // Se last paragraph at http://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html + microsecsSupported := false + if rows, err := dbt.db.Query(`SELECT cast("00:00:00.1" as TIME(1)) = "00:00:00.1"`); err == nil { + rows.Scan(µsecsSupported) + rows.Close() + } + if !microsecsSupported { + // skip test + return + } + _, err := dbt.db.Exec(` + CREATE TABLE test ( + value0 TIMESTAMP NOT NULL DEFAULT '` + f0 + `', + value1 TIMESTAMP(1) NOT NULL DEFAULT '` + f1 + `', + value6 TIMESTAMP(6) NOT NULL DEFAULT '` + f6 + `' + )`, + ) + if err != nil { + dbt.Error(err) + } + defer dbt.mustExec("DROP TABLE IF EXISTS test") + dbt.mustExec("INSERT INTO test SET value0=?, value1=?, value6=?", f0, f1, f6) + var res0, res1, res6 string + rows := dbt.mustQuery("SELECT * FROM test") + if !rows.Next() { + dbt.Errorf("test contained no selectable values") + } + err = rows.Scan(&res0, &res1, &res6) + if err != nil { + dbt.Error(err) + } + if res0 != f0 { + dbt.Errorf("expected %q, got %q", f0, res0) + } + if res1 != f1 { + dbt.Errorf("expected %q, got %q", f1, res1) + } + if res6 != f6 { + dbt.Errorf("expected %q, got %q", f6, res6) + } + }) +} + +func TestNULL(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + nullStmt, err := dbt.db.Prepare("SELECT NULL") + if err != nil { + dbt.Fatal(err) + } + defer nullStmt.Close() + + nonNullStmt, err := dbt.db.Prepare("SELECT 1") + if err != nil { + dbt.Fatal(err) + } + defer nonNullStmt.Close() + + // NullBool + var nb sql.NullBool + // Invalid + if err = nullStmt.QueryRow().Scan(&nb); err != nil { + dbt.Fatal(err) + } + if nb.Valid { + dbt.Error("valid NullBool which should be invalid") + } + // Valid + if err = nonNullStmt.QueryRow().Scan(&nb); err != nil { + dbt.Fatal(err) + } + if !nb.Valid { + dbt.Error("invalid NullBool which should be valid") + } else if nb.Bool != true { + dbt.Errorf("Unexpected NullBool value: %t (should be true)", nb.Bool) + } + + // NullFloat64 + var nf sql.NullFloat64 + // Invalid + if err = nullStmt.QueryRow().Scan(&nf); err != nil { + dbt.Fatal(err) + } + if nf.Valid { + dbt.Error("valid NullFloat64 which should be invalid") + } + // Valid + if err = nonNullStmt.QueryRow().Scan(&nf); err != nil { + dbt.Fatal(err) + } + if !nf.Valid { + dbt.Error("invalid NullFloat64 which should be valid") + } else if nf.Float64 != float64(1) { + dbt.Errorf("unexpected NullFloat64 value: %f (should be 1.0)", nf.Float64) + } + + // NullInt64 + var ni sql.NullInt64 + // Invalid + if err = nullStmt.QueryRow().Scan(&ni); err != nil { + dbt.Fatal(err) + } + if ni.Valid { + dbt.Error("valid NullInt64 which should be invalid") + } + // Valid + if err = nonNullStmt.QueryRow().Scan(&ni); err != nil { + dbt.Fatal(err) + } + if !ni.Valid { + dbt.Error("invalid NullInt64 which should be valid") + } else if ni.Int64 != int64(1) { + dbt.Errorf("unexpected NullInt64 value: %d (should be 1)", ni.Int64) + } + + // NullString + var ns sql.NullString + // Invalid + if err = nullStmt.QueryRow().Scan(&ns); err != nil { + dbt.Fatal(err) + } + if ns.Valid { + dbt.Error("valid NullString which should be invalid") + } + // Valid + if err = nonNullStmt.QueryRow().Scan(&ns); err != nil { + dbt.Fatal(err) + } + if !ns.Valid { + dbt.Error("invalid NullString which should be valid") + } else if ns.String != `1` { + dbt.Error("unexpected NullString value:" + ns.String + " (should be `1`)") + } + + // nil-bytes + var b []byte + // Read nil + if err = nullStmt.QueryRow().Scan(&b); err != nil { + dbt.Fatal(err) + } + if b != nil { + dbt.Error("non-nil []byte wich should be nil") + } + // Read non-nil + if err = nonNullStmt.QueryRow().Scan(&b); err != nil { + dbt.Fatal(err) + } + if b == nil { + dbt.Error("nil []byte wich should be non-nil") + } + // Insert nil + b = nil + success := false + if err = dbt.db.QueryRow("SELECT ? IS NULL", b).Scan(&success); err != nil { + dbt.Fatal(err) + } + if !success { + dbt.Error("inserting []byte(nil) as NULL failed") + } + // Check input==output with input==nil + b = nil + if err = dbt.db.QueryRow("SELECT ?", b).Scan(&b); err != nil { + dbt.Fatal(err) + } + if b != nil { + dbt.Error("non-nil echo from nil input") + } + // Check input==output with input!=nil + b = []byte("") + if err = dbt.db.QueryRow("SELECT ?", b).Scan(&b); err != nil { + dbt.Fatal(err) + } + if b == nil { + dbt.Error("nil echo from non-nil input") + } + + // Insert NULL + dbt.mustExec("CREATE TABLE test (dummmy1 int, value int, dummy2 int)") + + dbt.mustExec("INSERT INTO test VALUES (?, ?, ?)", 1, nil, 2) + + var out interface{} + rows := dbt.mustQuery("SELECT * FROM test") + if rows.Next() { + rows.Scan(&out) + if out != nil { + dbt.Errorf("%v != nil", out) + } + } else { + dbt.Error("no data") + } + }) +} + +func TestUint64(t *testing.T) { + const ( + u0 = uint64(0) + uall = ^u0 + uhigh = uall >> 1 + utop = ^uhigh + s0 = int64(0) + sall = ^s0 + shigh = int64(uhigh) + stop = ^shigh + ) + runTests(t, dsn, func(dbt *DBTest) { + stmt, err := dbt.db.Prepare(`SELECT ?, ?, ? ,?, ?, ?, ?, ?`) + if err != nil { + dbt.Fatal(err) + } + defer stmt.Close() + row := stmt.QueryRow( + u0, uhigh, utop, uall, + s0, shigh, stop, sall, + ) + + var ua, ub, uc, ud uint64 + var sa, sb, sc, sd int64 + + err = row.Scan(&ua, &ub, &uc, &ud, &sa, &sb, &sc, &sd) + if err != nil { + dbt.Fatal(err) + } + switch { + case ua != u0, + ub != uhigh, + uc != utop, + ud != uall, + sa != s0, + sb != shigh, + sc != stop, + sd != sall: + dbt.Fatal("unexpected result value") + } + }) +} + +func TestLongData(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + var maxAllowedPacketSize int + err := dbt.db.QueryRow("select @@max_allowed_packet").Scan(&maxAllowedPacketSize) + if err != nil { + dbt.Fatal(err) + } + maxAllowedPacketSize-- + + // don't get too ambitious + if maxAllowedPacketSize > 1<<25 { + maxAllowedPacketSize = 1 << 25 + } + + dbt.mustExec("CREATE TABLE test (value LONGBLOB)") + + in := strings.Repeat(`a`, maxAllowedPacketSize+1) + var out string + var rows *sql.Rows + + // Long text data + const nonDataQueryLen = 28 // length query w/o value + inS := in[:maxAllowedPacketSize-nonDataQueryLen] + dbt.mustExec("INSERT INTO test VALUES('" + inS + "')") + rows = dbt.mustQuery("SELECT value FROM test") + if rows.Next() { + rows.Scan(&out) + if inS != out { + dbt.Fatalf("LONGBLOB: length in: %d, length out: %d", len(inS), len(out)) + } + if rows.Next() { + dbt.Error("LONGBLOB: unexpexted row") + } + } else { + dbt.Fatalf("LONGBLOB: no data") + } + + // Empty table + dbt.mustExec("TRUNCATE TABLE test") + + // Long binary data + dbt.mustExec("INSERT INTO test VALUES(?)", in) + rows = dbt.mustQuery("SELECT value FROM test WHERE 1=?", 1) + if rows.Next() { + rows.Scan(&out) + if in != out { + dbt.Fatalf("LONGBLOB: length in: %d, length out: %d", len(in), len(out)) + } + if rows.Next() { + dbt.Error("LONGBLOB: unexpexted row") + } + } else { + if err = rows.Err(); err != nil { + dbt.Fatalf("LONGBLOB: no data (err: %s)", err.Error()) + } else { + dbt.Fatal("LONGBLOB: no data (err: )") + } + } + }) +} + +func TestLoadData(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + verifyLoadDataResult := func() { + rows, err := dbt.db.Query("SELECT * FROM test") + if err != nil { + dbt.Fatal(err.Error()) + } + + i := 0 + values := [4]string{ + "a string", + "a string containing a \t", + "a string containing a \n", + "a string containing both \t\n", + } + + var id int + var value string + + for rows.Next() { + i++ + err = rows.Scan(&id, &value) + if err != nil { + dbt.Fatal(err.Error()) + } + if i != id { + dbt.Fatalf("%d != %d", i, id) + } + if values[i-1] != value { + dbt.Fatalf("%q != %q", values[i-1], value) + } + } + err = rows.Err() + if err != nil { + dbt.Fatal(err.Error()) + } + + if i != 4 { + dbt.Fatalf("rows count mismatch. Got %d, want 4", i) + } + } + file, err := ioutil.TempFile("", "gotest") + defer os.Remove(file.Name()) + if err != nil { + dbt.Fatal(err) + } + file.WriteString("1\ta string\n2\ta string containing a \\t\n3\ta string containing a \\n\n4\ta string containing both \\t\\n\n") + file.Close() + + dbt.db.Exec("DROP TABLE IF EXISTS test") + dbt.mustExec("CREATE TABLE test (id INT NOT NULL PRIMARY KEY, value TEXT NOT NULL) CHARACTER SET utf8") + + // Local File + RegisterLocalFile(file.Name()) + dbt.mustExec(fmt.Sprintf("LOAD DATA LOCAL INFILE %q INTO TABLE test", file.Name())) + verifyLoadDataResult() + // negative test + _, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'doesnotexist' INTO TABLE test") + if err == nil { + dbt.Fatal("load non-existent file didn't fail") + } else if err.Error() != "local file 'doesnotexist' is not registered" { + dbt.Fatal(err.Error()) + } + + // Empty table + dbt.mustExec("TRUNCATE TABLE test") + + // Reader + RegisterReaderHandler("test", func() io.Reader { + file, err = os.Open(file.Name()) + if err != nil { + dbt.Fatal(err) + } + return file + }) + dbt.mustExec("LOAD DATA LOCAL INFILE 'Reader::test' INTO TABLE test") + verifyLoadDataResult() + // negative test + _, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'Reader::doesnotexist' INTO TABLE test") + if err == nil { + dbt.Fatal("load non-existent Reader didn't fail") + } else if err.Error() != "Reader 'doesnotexist' is not registered" { + dbt.Fatal(err.Error()) + } + }) +} + +func TestFoundRows(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + dbt.mustExec("CREATE TABLE test (id INT NOT NULL ,data INT NOT NULL)") + dbt.mustExec("INSERT INTO test (id, data) VALUES (0, 0),(0, 0),(1, 0),(1, 0),(1, 1)") + + res := dbt.mustExec("UPDATE test SET data = 1 WHERE id = 0") + count, err := res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 2 { + dbt.Fatalf("Expected 2 affected rows, got %d", count) + } + res = dbt.mustExec("UPDATE test SET data = 1 WHERE id = 1") + count, err = res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 2 { + dbt.Fatalf("Expected 2 affected rows, got %d", count) + } + }) + runTests(t, dsn+"&clientFoundRows=true", func(dbt *DBTest) { + dbt.mustExec("CREATE TABLE test (id INT NOT NULL ,data INT NOT NULL)") + dbt.mustExec("INSERT INTO test (id, data) VALUES (0, 0),(0, 0),(1, 0),(1, 0),(1, 1)") + + res := dbt.mustExec("UPDATE test SET data = 1 WHERE id = 0") + count, err := res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 2 { + dbt.Fatalf("Expected 2 matched rows, got %d", count) + } + res = dbt.mustExec("UPDATE test SET data = 1 WHERE id = 1") + count, err = res.RowsAffected() + if err != nil { + dbt.Fatalf("res.RowsAffected() returned error: %s", err.Error()) + } + if count != 3 { + dbt.Fatalf("Expected 3 matched rows, got %d", count) + } + }) +} + +func TestStrict(t *testing.T) { + // ALLOW_INVALID_DATES to get rid of stricter modes - we want to test for warnings, not errors + relaxedDsn := dsn + "&sql_mode='ALLOW_INVALID_DATES,NO_AUTO_CREATE_USER'" + // make sure the MySQL version is recent enough with a separate connection + // before running the test + conn, err := MySQLDriver{}.Open(relaxedDsn) + if conn != nil { + conn.Close() + } + if me, ok := err.(*MySQLError); ok && me.Number == 1231 { + // Error 1231: Variable 'sql_mode' can't be set to the value of 'ALLOW_INVALID_DATES' + // => skip test, MySQL server version is too old + return + } + runTests(t, relaxedDsn, func(dbt *DBTest) { + dbt.mustExec("CREATE TABLE test (a TINYINT NOT NULL, b CHAR(4))") + + var queries = [...]struct { + in string + codes []string + }{ + {"DROP TABLE IF EXISTS no_such_table", []string{"1051"}}, + {"INSERT INTO test VALUES(10,'mysql'),(NULL,'test'),(300,'Open Source')", []string{"1265", "1048", "1264", "1265"}}, + } + var err error + + var checkWarnings = func(err error, mode string, idx int) { + if err == nil { + dbt.Errorf("expected STRICT error on query [%s] %s", mode, queries[idx].in) + } + + if warnings, ok := err.(MySQLWarnings); ok { + var codes = make([]string, len(warnings)) + for i := range warnings { + codes[i] = warnings[i].Code + } + if len(codes) != len(queries[idx].codes) { + dbt.Errorf("unexpected STRICT error count on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes) + } + + for i := range warnings { + if codes[i] != queries[idx].codes[i] { + dbt.Errorf("unexpected STRICT error codes on query [%s] %s: Wanted %v, Got %v", mode, queries[idx].in, queries[idx].codes, codes) + return + } + } + + } else { + dbt.Errorf("unexpected error on query [%s] %s: %s", mode, queries[idx].in, err.Error()) + } + } + + // text protocol + for i := range queries { + _, err = dbt.db.Exec(queries[i].in) + checkWarnings(err, "text", i) + } + + var stmt *sql.Stmt + + // binary protocol + for i := range queries { + stmt, err = dbt.db.Prepare(queries[i].in) + if err != nil { + dbt.Errorf("error on preparing query %s: %s", queries[i].in, err.Error()) + } + + _, err = stmt.Exec() + checkWarnings(err, "binary", i) + + err = stmt.Close() + if err != nil { + dbt.Errorf("error on closing stmt for query %s: %s", queries[i].in, err.Error()) + } + } + }) +} + +func TestTLS(t *testing.T) { + tlsTest := func(dbt *DBTest) { + if err := dbt.db.Ping(); err != nil { + if err == ErrNoTLS { + dbt.Skip("server does not support TLS") + } else { + dbt.Fatalf("error on Ping: %s", err.Error()) + } + } + + rows := dbt.mustQuery("SHOW STATUS LIKE 'Ssl_cipher'") + + var variable, value *sql.RawBytes + for rows.Next() { + if err := rows.Scan(&variable, &value); err != nil { + dbt.Fatal(err.Error()) + } + + if value == nil { + dbt.Fatal("no Cipher") + } + } + } + + runTests(t, dsn+"&tls=skip-verify", tlsTest) + + // Verify that registering / using a custom cfg works + RegisterTLSConfig("custom-skip-verify", &tls.Config{ + InsecureSkipVerify: true, + }) + runTests(t, dsn+"&tls=custom-skip-verify", tlsTest) +} + +func TestReuseClosedConnection(t *testing.T) { + // this test does not use sql.database, it uses the driver directly + if !available { + t.Skipf("MySQL server not running on %s", netAddr) + } + + md := &MySQLDriver{} + conn, err := md.Open(dsn) + if err != nil { + t.Fatalf("error connecting: %s", err.Error()) + } + stmt, err := conn.Prepare("DO 1") + if err != nil { + t.Fatalf("error preparing statement: %s", err.Error()) + } + _, err = stmt.Exec(nil) + if err != nil { + t.Fatalf("error executing statement: %s", err.Error()) + } + err = conn.Close() + if err != nil { + t.Fatalf("error closing connection: %s", err.Error()) + } + + defer func() { + if err := recover(); err != nil { + t.Errorf("panic after reusing a closed connection: %v", err) + } + }() + _, err = stmt.Exec(nil) + if err != nil && err != driver.ErrBadConn { + t.Errorf("unexpected error '%s', expected '%s'", + err.Error(), driver.ErrBadConn.Error()) + } +} + +func TestCharset(t *testing.T) { + if !available { + t.Skipf("MySQL server not running on %s", netAddr) + } + + mustSetCharset := func(charsetParam, expected string) { + runTests(t, dsn+"&"+charsetParam, func(dbt *DBTest) { + rows := dbt.mustQuery("SELECT @@character_set_connection") + defer rows.Close() + + if !rows.Next() { + dbt.Fatalf("error getting connection charset: %s", rows.Err()) + } + + var got string + rows.Scan(&got) + + if got != expected { + dbt.Fatalf("expected connection charset %s but got %s", expected, got) + } + }) + } + + // non utf8 test + mustSetCharset("charset=ascii", "ascii") + + // when the first charset is invalid, use the second + mustSetCharset("charset=none,utf8", "utf8") + + // when the first charset is valid, use it + mustSetCharset("charset=ascii,utf8", "ascii") + mustSetCharset("charset=utf8,ascii", "utf8") +} + +func TestFailingCharset(t *testing.T) { + runTests(t, dsn+"&charset=none", func(dbt *DBTest) { + // run query to really establish connection... + _, err := dbt.db.Exec("SELECT 1") + if err == nil { + dbt.db.Close() + t.Fatalf("connection must not succeed without a valid charset") + } + }) +} + +func TestCollation(t *testing.T) { + if !available { + t.Skipf("MySQL server not running on %s", netAddr) + } + + defaultCollation := "utf8_general_ci" + testCollations := []string{ + "", // do not set + defaultCollation, // driver default + "latin1_general_ci", + "binary", + "utf8_unicode_ci", + "cp1257_bin", + } + + for _, collation := range testCollations { + var expected, tdsn string + if collation != "" { + tdsn = dsn + "&collation=" + collation + expected = collation + } else { + tdsn = dsn + expected = defaultCollation + } + + runTests(t, tdsn, func(dbt *DBTest) { + var got string + if err := dbt.db.QueryRow("SELECT @@collation_connection").Scan(&got); err != nil { + dbt.Fatal(err) + } + + if got != expected { + dbt.Fatalf("expected connection collation %s but got %s", expected, got) + } + }) + } +} + +func TestColumnsWithAlias(t *testing.T) { + runTests(t, dsn+"&columnsWithAlias=true", func(dbt *DBTest) { + rows := dbt.mustQuery("SELECT 1 AS A") + defer rows.Close() + cols, _ := rows.Columns() + if len(cols) != 1 { + t.Fatalf("expected 1 column, got %d", len(cols)) + } + if cols[0] != "A" { + t.Fatalf("expected column name \"A\", got \"%s\"", cols[0]) + } + rows.Close() + + rows = dbt.mustQuery("SELECT * FROM (SELECT 1 AS one) AS A") + cols, _ = rows.Columns() + if len(cols) != 1 { + t.Fatalf("expected 1 column, got %d", len(cols)) + } + if cols[0] != "A.one" { + t.Fatalf("expected column name \"A.one\", got \"%s\"", cols[0]) + } + }) +} + +func TestRawBytesResultExceedsBuffer(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + // defaultBufSize from buffer.go + expected := strings.Repeat("abc", defaultBufSize) + + rows := dbt.mustQuery("SELECT '" + expected + "'") + defer rows.Close() + if !rows.Next() { + dbt.Error("expected result, got none") + } + var result sql.RawBytes + rows.Scan(&result) + if expected != string(result) { + dbt.Error("result did not match expected value") + } + }) +} + +func TestTimezoneConversion(t *testing.T) { + zones := []string{"UTC", "US/Central", "US/Pacific", "Local"} + + // Regression test for timezone handling + tzTest := func(dbt *DBTest) { + + // Create table + dbt.mustExec("CREATE TABLE test (ts TIMESTAMP)") + + // Insert local time into database (should be converted) + usCentral, _ := time.LoadLocation("US/Central") + reftime := time.Date(2014, 05, 30, 18, 03, 17, 0, time.UTC).In(usCentral) + dbt.mustExec("INSERT INTO test VALUE (?)", reftime) + + // Retrieve time from DB + rows := dbt.mustQuery("SELECT ts FROM test") + if !rows.Next() { + dbt.Fatal("did not get any rows out") + } + + var dbTime time.Time + err := rows.Scan(&dbTime) + if err != nil { + dbt.Fatal("Err", err) + } + + // Check that dates match + if reftime.Unix() != dbTime.Unix() { + dbt.Errorf("times do not match.\n") + dbt.Errorf(" Now(%v)=%v\n", usCentral, reftime) + dbt.Errorf(" Now(UTC)=%v\n", dbTime) + } + } + + for _, tz := range zones { + runTests(t, dsn+"&parseTime=true&loc="+url.QueryEscape(tz), tzTest) + } +} + +// Special cases + +func TestRowsClose(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + rows, err := dbt.db.Query("SELECT 1") + if err != nil { + dbt.Fatal(err) + } + + err = rows.Close() + if err != nil { + dbt.Fatal(err) + } + + if rows.Next() { + dbt.Fatal("unexpected row after rows.Close()") + } + + err = rows.Err() + if err != nil { + dbt.Fatal(err) + } + }) +} + +// dangling statements +// http://code.google.com/p/go/issues/detail?id=3865 +func TestCloseStmtBeforeRows(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + stmt, err := dbt.db.Prepare("SELECT 1") + if err != nil { + dbt.Fatal(err) + } + + rows, err := stmt.Query() + if err != nil { + stmt.Close() + dbt.Fatal(err) + } + defer rows.Close() + + err = stmt.Close() + if err != nil { + dbt.Fatal(err) + } + + if !rows.Next() { + dbt.Fatal("getting row failed") + } else { + err = rows.Err() + if err != nil { + dbt.Fatal(err) + } + + var out bool + err = rows.Scan(&out) + if err != nil { + dbt.Fatalf("error on rows.Scan(): %s", err.Error()) + } + if out != true { + dbt.Errorf("true != %t", out) + } + } + }) +} + +// It is valid to have multiple Rows for the same Stmt +// http://code.google.com/p/go/issues/detail?id=3734 +func TestStmtMultiRows(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + stmt, err := dbt.db.Prepare("SELECT 1 UNION SELECT 0") + if err != nil { + dbt.Fatal(err) + } + + rows1, err := stmt.Query() + if err != nil { + stmt.Close() + dbt.Fatal(err) + } + defer rows1.Close() + + rows2, err := stmt.Query() + if err != nil { + stmt.Close() + dbt.Fatal(err) + } + defer rows2.Close() + + var out bool + + // 1 + if !rows1.Next() { + dbt.Fatal("first rows1.Next failed") + } else { + err = rows1.Err() + if err != nil { + dbt.Fatal(err) + } + + err = rows1.Scan(&out) + if err != nil { + dbt.Fatalf("error on rows.Scan(): %s", err.Error()) + } + if out != true { + dbt.Errorf("true != %t", out) + } + } + + if !rows2.Next() { + dbt.Fatal("first rows2.Next failed") + } else { + err = rows2.Err() + if err != nil { + dbt.Fatal(err) + } + + err = rows2.Scan(&out) + if err != nil { + dbt.Fatalf("error on rows.Scan(): %s", err.Error()) + } + if out != true { + dbt.Errorf("true != %t", out) + } + } + + // 2 + if !rows1.Next() { + dbt.Fatal("second rows1.Next failed") + } else { + err = rows1.Err() + if err != nil { + dbt.Fatal(err) + } + + err = rows1.Scan(&out) + if err != nil { + dbt.Fatalf("error on rows.Scan(): %s", err.Error()) + } + if out != false { + dbt.Errorf("false != %t", out) + } + + if rows1.Next() { + dbt.Fatal("unexpected row on rows1") + } + err = rows1.Close() + if err != nil { + dbt.Fatal(err) + } + } + + if !rows2.Next() { + dbt.Fatal("second rows2.Next failed") + } else { + err = rows2.Err() + if err != nil { + dbt.Fatal(err) + } + + err = rows2.Scan(&out) + if err != nil { + dbt.Fatalf("error on rows.Scan(): %s", err.Error()) + } + if out != false { + dbt.Errorf("false != %t", out) + } + + if rows2.Next() { + dbt.Fatal("unexpected row on rows2") + } + err = rows2.Close() + if err != nil { + dbt.Fatal(err) + } + } + }) +} + +// Regression test for +// * more than 32 NULL parameters (issue 209) +// * more parameters than fit into the buffer (issue 201) +func TestPreparedManyCols(t *testing.T) { + const numParams = defaultBufSize + runTests(t, dsn, func(dbt *DBTest) { + query := "SELECT ?" + strings.Repeat(",?", numParams-1) + stmt, err := dbt.db.Prepare(query) + if err != nil { + dbt.Fatal(err) + } + defer stmt.Close() + // create more parameters than fit into the buffer + // which will take nil-values + params := make([]interface{}, numParams) + rows, err := stmt.Query(params...) + if err != nil { + stmt.Close() + dbt.Fatal(err) + } + defer rows.Close() + }) +} + +func TestConcurrent(t *testing.T) { + if enabled, _ := readBool(os.Getenv("MYSQL_TEST_CONCURRENT")); !enabled { + t.Skip("MYSQL_TEST_CONCURRENT env var not set") + } + + runTests(t, dsn, func(dbt *DBTest) { + var max int + err := dbt.db.QueryRow("SELECT @@max_connections").Scan(&max) + if err != nil { + dbt.Fatalf("%s", err.Error()) + } + dbt.Logf("testing up to %d concurrent connections \r\n", max) + + var remaining, succeeded int32 = int32(max), 0 + + var wg sync.WaitGroup + wg.Add(max) + + var fatalError string + var once sync.Once + fatalf := func(s string, vals ...interface{}) { + once.Do(func() { + fatalError = fmt.Sprintf(s, vals...) + }) + } + + for i := 0; i < max; i++ { + go func(id int) { + defer wg.Done() + + tx, err := dbt.db.Begin() + atomic.AddInt32(&remaining, -1) + + if err != nil { + if err.Error() != "Error 1040: Too many connections" { + fatalf("error on conn %d: %s", id, err.Error()) + } + return + } + + // keep the connection busy until all connections are open + for remaining > 0 { + if _, err = tx.Exec("DO 1"); err != nil { + fatalf("error on conn %d: %s", id, err.Error()) + return + } + } + + if err = tx.Commit(); err != nil { + fatalf("error on conn %d: %s", id, err.Error()) + return + } + + // everything went fine with this connection + atomic.AddInt32(&succeeded, 1) + }(i) + } + + // wait until all conections are open + wg.Wait() + + if fatalError != "" { + dbt.Fatal(fatalError) + } + + dbt.Logf("reached %d concurrent connections\r\n", succeeded) + }) +} + +// Tests custom dial functions +func TestCustomDial(t *testing.T) { + if !available { + t.Skipf("MySQL server not running on %s", netAddr) + } + + // our custom dial function which justs wraps net.Dial here + RegisterDial("mydial", func(addr string) (net.Conn, error) { + return net.Dial(prot, addr) + }) + + db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@mydial(%s)/%s?timeout=30s&strict=true", user, pass, addr, dbname)) + if err != nil { + t.Fatalf("error connecting: %s", err.Error()) + } + defer db.Close() + + if _, err = db.Exec("DO 1"); err != nil { + t.Fatalf("connection failed: %s", err.Error()) + } +} + +func TestSQLInjection(t *testing.T) { + createTest := func(arg string) func(dbt *DBTest) { + return func(dbt *DBTest) { + dbt.mustExec("CREATE TABLE test (v INTEGER)") + dbt.mustExec("INSERT INTO test VALUES (?)", 1) + + var v int + // NULL can't be equal to anything, the idea here is to inject query so it returns row + // This test verifies that escapeQuotes and escapeBackslash are working properly + err := dbt.db.QueryRow("SELECT v FROM test WHERE NULL = ?", arg).Scan(&v) + if err == sql.ErrNoRows { + return // success, sql injection failed + } else if err == nil { + dbt.Errorf("sql injection successful with arg: %s", arg) + } else { + dbt.Errorf("error running query with arg: %s; err: %s", arg, err.Error()) + } + } + } + + dsns := []string{ + dsn, + dsn + "&sql_mode='NO_BACKSLASH_ESCAPES,NO_AUTO_CREATE_USER'", + } + for _, testdsn := range dsns { + runTests(t, testdsn, createTest("1 OR 1=1")) + runTests(t, testdsn, createTest("' OR '1'='1")) + } +} + +// Test if inserted data is correctly retrieved after being escaped +func TestInsertRetrieveEscapedData(t *testing.T) { + testData := func(dbt *DBTest) { + dbt.mustExec("CREATE TABLE test (v VARCHAR(255))") + + // All sequences that are escaped by escapeQuotes and escapeBackslash + v := "foo \x00\n\r\x1a\"'\\" + dbt.mustExec("INSERT INTO test VALUES (?)", v) + + var out string + err := dbt.db.QueryRow("SELECT v FROM test").Scan(&out) + if err != nil { + dbt.Fatalf("%s", err.Error()) + } + + if out != v { + dbt.Errorf("%q != %q", out, v) + } + } + + dsns := []string{ + dsn, + dsn + "&sql_mode='NO_BACKSLASH_ESCAPES,NO_AUTO_CREATE_USER'", + } + for _, testdsn := range dsns { + runTests(t, testdsn, testData) + } +} + +func TestUnixSocketAuthFail(t *testing.T) { + runTests(t, dsn, func(dbt *DBTest) { + // Save the current logger so we can restore it. + oldLogger := errLog + + // Set a new logger so we can capture its output. + buffer := bytes.NewBuffer(make([]byte, 0, 64)) + newLogger := log.New(buffer, "prefix: ", 0) + SetLogger(newLogger) + + // Restore the logger. + defer SetLogger(oldLogger) + + // Make a new DSN that uses the MySQL socket file and a bad password, which + // we can make by simply appending any character to the real password. + badPass := pass + "x" + socket := "" + if prot == "unix" { + socket = addr + } else { + // Get socket file from MySQL. + err := dbt.db.QueryRow("SELECT @@socket").Scan(&socket) + if err != nil { + t.Fatalf("error on SELECT @@socket: %s", err.Error()) + } + } + t.Logf("socket: %s", socket) + badDSN := fmt.Sprintf("%s:%s@unix(%s)/%s?timeout=30s&strict=true", user, badPass, socket, dbname) + db, err := sql.Open("mysql", badDSN) + if err != nil { + t.Fatalf("error connecting: %s", err.Error()) + } + defer db.Close() + + // Connect to MySQL for real. This will cause an auth failure. + err = db.Ping() + if err == nil { + t.Error("expected Ping() to return an error") + } + + // The driver should not log anything. + if actual := buffer.String(); actual != "" { + t.Errorf("expected no output, got %q", actual) + } + }) +} diff --git a/vendor/github.com/go-sql-driver/mysql/dsn_test.go b/vendor/github.com/go-sql-driver/mysql/dsn_test.go new file mode 100644 index 000000000..80949e18a --- /dev/null +++ b/vendor/github.com/go-sql-driver/mysql/dsn_test.go @@ -0,0 +1,207 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +package mysql + +import ( + "crypto/tls" + "fmt" + "net/url" + "testing" +) + +var testDSNs = []struct { + in string + out string +}{ + {"username:password@protocol(address)/dbname?param=value", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true", "&{User:username Passwd:password Net:protocol Addr:address DBName:dbname Params:map[param:value] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:true InterpolateParams:false MultiStatements:true ParseTime:false Strict:false}"}, + {"user@unix(/path/to/socket)/dbname?charset=utf8", "&{User:user Passwd: Net:unix Addr:/path/to/socket DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:true tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{User:user Passwd:password Net:tcp Addr:localhost:5555 DBName:dbname Params:map[charset:utf8mb4,utf8] Collation:utf8_general_ci Loc:UTC TLSConfig:skip-verify tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{User:user Passwd:password Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8mb4_unicode_ci Loc:UTC TLSConfig: tls: Timeout:30s ReadTimeout:1s WriteTimeout:1s AllowAllFiles:true AllowCleartextPasswords:false AllowOldPasswords:true ClientFoundRows:true ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{User:user Passwd:p@ss(word) Net:tcp Addr:[de:ad:be:ef::ca:fe]:80 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:Local TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"/dbname", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName:dbname Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"@/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"/", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"", "&{User: Passwd: Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"user:p@/ssword@/", "&{User:user Passwd:p@/ssword Net:tcp Addr:127.0.0.1:3306 DBName: Params:map[] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, + {"unix/?arg=%2Fsome%2Fpath.ext", "&{User: Passwd: Net:unix Addr:/tmp/mysql.sock DBName: Params:map[arg:/some/path.ext] Collation:utf8_general_ci Loc:UTC TLSConfig: tls: Timeout:0 ReadTimeout:0 WriteTimeout:0 AllowAllFiles:false AllowCleartextPasswords:false AllowOldPasswords:false ClientFoundRows:false ColumnsWithAlias:false InterpolateParams:false MultiStatements:false ParseTime:false Strict:false}"}, +} + +func TestDSNParser(t *testing.T) { + var cfg *Config + var err error + var res string + + for i, tst := range testDSNs { + cfg, err = ParseDSN(tst.in) + if err != nil { + t.Error(err.Error()) + } + + // pointer not static + cfg.tls = nil + + res = fmt.Sprintf("%+v", cfg) + if res != tst.out { + t.Errorf("%d. ParseDSN(%q) => %q, want %q", i, tst.in, res, tst.out) + } + } +} + +func TestDSNParserInvalid(t *testing.T) { + var invalidDSNs = []string{ + "@net(addr/", // no closing brace + "@tcp(/", // no closing brace + "tcp(/", // no closing brace + "(/", // no closing brace + "net(addr)//", // unescaped + "User:pass@tcp(1.2.3.4:3306)", // no trailing slash + //"/dbname?arg=/some/unescaped/path", + } + + for i, tst := range invalidDSNs { + if _, err := ParseDSN(tst); err == nil { + t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst) + } + } +} + +func TestDSNReformat(t *testing.T) { + for i, tst := range testDSNs { + dsn1 := tst.in + cfg1, err := ParseDSN(dsn1) + if err != nil { + t.Error(err.Error()) + continue + } + cfg1.tls = nil // pointer not static + res1 := fmt.Sprintf("%+v", cfg1) + + dsn2 := cfg1.FormatDSN() + cfg2, err := ParseDSN(dsn2) + if err != nil { + t.Error(err.Error()) + continue + } + cfg2.tls = nil // pointer not static + res2 := fmt.Sprintf("%+v", cfg2) + + if res1 != res2 { + t.Errorf("%d. %q does not match %q", i, res2, res1) + } + } +} + +func TestDSNWithCustomTLS(t *testing.T) { + baseDSN := "User:password@tcp(localhost:5555)/dbname?tls=" + tlsCfg := tls.Config{} + + RegisterTLSConfig("utils_test", &tlsCfg) + + // Custom TLS is missing + tst := baseDSN + "invalid_tls" + cfg, err := ParseDSN(tst) + if err == nil { + t.Errorf("invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg) + } + + tst = baseDSN + "utils_test" + + // Custom TLS with a server name + name := "foohost" + tlsCfg.ServerName = name + cfg, err = ParseDSN(tst) + + if err != nil { + t.Error(err.Error()) + } else if cfg.tls.ServerName != name { + t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst) + } + + // Custom TLS without a server name + name = "localhost" + tlsCfg.ServerName = "" + cfg, err = ParseDSN(tst) + + if err != nil { + t.Error(err.Error()) + } else if cfg.tls.ServerName != name { + t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst) + } + + DeregisterTLSConfig("utils_test") +} + +func TestDSNWithCustomTLSQueryEscape(t *testing.T) { + const configKey = "&%!:" + dsn := "User:password@tcp(localhost:5555)/dbname?tls=" + url.QueryEscape(configKey) + name := "foohost" + tlsCfg := tls.Config{ServerName: name} + + RegisterTLSConfig(configKey, &tlsCfg) + + cfg, err := ParseDSN(dsn) + + if err != nil { + t.Error(err.Error()) + } else if cfg.tls.ServerName != name { + t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, dsn) + } +} + +func TestDSNUnsafeCollation(t *testing.T) { + _, err := ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true") + if err != errInvalidDSNUnsafeCollation { + t.Errorf("expected %v, got %v", errInvalidDSNUnsafeCollation, err) + } + + _, err = ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false") + if err != nil { + t.Errorf("expected %v, got %v", nil, err) + } + + _, err = ParseDSN("/dbname?collation=gbk_chinese_ci") + if err != nil { + t.Errorf("expected %v, got %v", nil, err) + } + + _, err = ParseDSN("/dbname?collation=ascii_bin&interpolateParams=true") + if err != nil { + t.Errorf("expected %v, got %v", nil, err) + } + + _, err = ParseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true") + if err != nil { + t.Errorf("expected %v, got %v", nil, err) + } + + _, err = ParseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true") + if err != nil { + t.Errorf("expected %v, got %v", nil, err) + } + + _, err = ParseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true") + if err != nil { + t.Errorf("expected %v, got %v", nil, err) + } +} + +func BenchmarkParseDSN(b *testing.B) { + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for _, tst := range testDSNs { + if _, err := ParseDSN(tst.in); err != nil { + b.Error(err.Error()) + } + } + } +} diff --git a/vendor/github.com/go-sql-driver/mysql/errors_test.go b/vendor/github.com/go-sql-driver/mysql/errors_test.go new file mode 100644 index 000000000..96f9126d6 --- /dev/null +++ b/vendor/github.com/go-sql-driver/mysql/errors_test.go @@ -0,0 +1,42 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +package mysql + +import ( + "bytes" + "log" + "testing" +) + +func TestErrorsSetLogger(t *testing.T) { + previous := errLog + defer func() { + errLog = previous + }() + + // set up logger + const expected = "prefix: test\n" + buffer := bytes.NewBuffer(make([]byte, 0, 64)) + logger := log.New(buffer, "prefix: ", 0) + + // print + SetLogger(logger) + errLog.Print("test") + + // check result + if actual := buffer.String(); actual != expected { + t.Errorf("expected %q, got %q", expected, actual) + } +} + +func TestErrorsStrictIgnoreNotes(t *testing.T) { + runTests(t, dsn+"&sql_notes=false", func(dbt *DBTest) { + dbt.mustExec("DROP TABLE IF EXISTS does_not_exist") + }) +} diff --git a/vendor/github.com/go-sql-driver/mysql/utils_test.go b/vendor/github.com/go-sql-driver/mysql/utils_test.go new file mode 100644 index 000000000..0d6c6684f --- /dev/null +++ b/vendor/github.com/go-sql-driver/mysql/utils_test.go @@ -0,0 +1,197 @@ +// Go MySQL Driver - A MySQL-Driver for Go's database/sql package +// +// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +package mysql + +import ( + "bytes" + "encoding/binary" + "fmt" + "testing" + "time" +) + +func TestScanNullTime(t *testing.T) { + var scanTests = []struct { + in interface{} + error bool + valid bool + time time.Time + }{ + {tDate, false, true, tDate}, + {sDate, false, true, tDate}, + {[]byte(sDate), false, true, tDate}, + {tDateTime, false, true, tDateTime}, + {sDateTime, false, true, tDateTime}, + {[]byte(sDateTime), false, true, tDateTime}, + {tDate0, false, true, tDate0}, + {sDate0, false, true, tDate0}, + {[]byte(sDate0), false, true, tDate0}, + {sDateTime0, false, true, tDate0}, + {[]byte(sDateTime0), false, true, tDate0}, + {"", true, false, tDate0}, + {"1234", true, false, tDate0}, + {0, true, false, tDate0}, + } + + var nt = NullTime{} + var err error + + for _, tst := range scanTests { + err = nt.Scan(tst.in) + if (err != nil) != tst.error { + t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil)) + } + if nt.Valid != tst.valid { + t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid) + } + if nt.Time != tst.time { + t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time) + } + } +} + +func TestLengthEncodedInteger(t *testing.T) { + var integerTests = []struct { + num uint64 + encoded []byte + }{ + {0x0000000000000000, []byte{0x00}}, + {0x0000000000000012, []byte{0x12}}, + {0x00000000000000fa, []byte{0xfa}}, + {0x0000000000000100, []byte{0xfc, 0x00, 0x01}}, + {0x0000000000001234, []byte{0xfc, 0x34, 0x12}}, + {0x000000000000ffff, []byte{0xfc, 0xff, 0xff}}, + {0x0000000000010000, []byte{0xfd, 0x00, 0x00, 0x01}}, + {0x0000000000123456, []byte{0xfd, 0x56, 0x34, 0x12}}, + {0x0000000000ffffff, []byte{0xfd, 0xff, 0xff, 0xff}}, + {0x0000000001000000, []byte{0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}}, + {0x123456789abcdef0, []byte{0xfe, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12}}, + {0xffffffffffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + } + + for _, tst := range integerTests { + num, isNull, numLen := readLengthEncodedInteger(tst.encoded) + if isNull { + t.Errorf("%x: expected %d, got NULL", tst.encoded, tst.num) + } + if num != tst.num { + t.Errorf("%x: expected %d, got %d", tst.encoded, tst.num, num) + } + if numLen != len(tst.encoded) { + t.Errorf("%x: expected size %d, got %d", tst.encoded, len(tst.encoded), numLen) + } + encoded := appendLengthEncodedInteger(nil, num) + if !bytes.Equal(encoded, tst.encoded) { + t.Errorf("%v: expected %x, got %x", num, tst.encoded, encoded) + } + } +} + +func TestOldPass(t *testing.T) { + scramble := []byte{9, 8, 7, 6, 5, 4, 3, 2} + vectors := []struct { + pass string + out string + }{ + {" pass", "47575c5a435b4251"}, + {"pass ", "47575c5a435b4251"}, + {"123\t456", "575c47505b5b5559"}, + {"C0mpl!ca ted#PASS123", "5d5d554849584a45"}, + } + for _, tuple := range vectors { + ours := scrambleOldPassword(scramble, []byte(tuple.pass)) + if tuple.out != fmt.Sprintf("%x", ours) { + t.Errorf("Failed old password %q", tuple.pass) + } + } +} + +func TestFormatBinaryDateTime(t *testing.T) { + rawDate := [11]byte{} + binary.LittleEndian.PutUint16(rawDate[:2], 1978) // years + rawDate[2] = 12 // months + rawDate[3] = 30 // days + rawDate[4] = 15 // hours + rawDate[5] = 46 // minutes + rawDate[6] = 23 // seconds + binary.LittleEndian.PutUint32(rawDate[7:], 987654) // microseconds + expect := func(expected string, inlen, outlen uint8) { + actual, _ := formatBinaryDateTime(rawDate[:inlen], outlen, false) + bytes, ok := actual.([]byte) + if !ok { + t.Errorf("formatBinaryDateTime must return []byte, was %T", actual) + } + if string(bytes) != expected { + t.Errorf( + "expected %q, got %q for length in %d, out %d", + bytes, actual, inlen, outlen, + ) + } + } + expect("0000-00-00", 0, 10) + expect("0000-00-00 00:00:00", 0, 19) + expect("1978-12-30", 4, 10) + expect("1978-12-30 15:46:23", 7, 19) + expect("1978-12-30 15:46:23.987654", 11, 26) +} + +func TestEscapeBackslash(t *testing.T) { + expect := func(expected, value string) { + actual := string(escapeBytesBackslash([]byte{}, []byte(value))) + if actual != expected { + t.Errorf( + "expected %s, got %s", + expected, actual, + ) + } + + actual = string(escapeStringBackslash([]byte{}, value)) + if actual != expected { + t.Errorf( + "expected %s, got %s", + expected, actual, + ) + } + } + + expect("foo\\0bar", "foo\x00bar") + expect("foo\\nbar", "foo\nbar") + expect("foo\\rbar", "foo\rbar") + expect("foo\\Zbar", "foo\x1abar") + expect("foo\\\"bar", "foo\"bar") + expect("foo\\\\bar", "foo\\bar") + expect("foo\\'bar", "foo'bar") +} + +func TestEscapeQuotes(t *testing.T) { + expect := func(expected, value string) { + actual := string(escapeBytesQuotes([]byte{}, []byte(value))) + if actual != expected { + t.Errorf( + "expected %s, got %s", + expected, actual, + ) + } + + actual = string(escapeStringQuotes([]byte{}, value)) + if actual != expected { + t.Errorf( + "expected %s, got %s", + expected, actual, + ) + } + } + + expect("foo\x00bar", "foo\x00bar") // not affected + expect("foo\nbar", "foo\nbar") // not affected + expect("foo\rbar", "foo\rbar") // not affected + expect("foo\x1abar", "foo\x1abar") // not affected + expect("foo''bar", "foo'bar") // affected + expect("foo\"bar", "foo\"bar") // not affected +} diff --git a/vendor/github.com/goamz/goamz/.gitignore b/vendor/github.com/goamz/goamz/.gitignore new file mode 100644 index 000000000..1377554eb --- /dev/null +++ b/vendor/github.com/goamz/goamz/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/vendor/github.com/goamz/goamz/.lbox b/vendor/github.com/goamz/goamz/.lbox new file mode 100644 index 000000000..75e124512 --- /dev/null +++ b/vendor/github.com/goamz/goamz/.lbox @@ -0,0 +1 @@ +propose -for=lp:goamz -cr diff --git a/vendor/github.com/goamz/goamz/.travis.yml b/vendor/github.com/goamz/goamz/.travis.yml new file mode 100644 index 000000000..3c26b1759 --- /dev/null +++ b/vendor/github.com/goamz/goamz/.travis.yml @@ -0,0 +1,32 @@ +language: go + +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - tip + +before_script: + - FIXED=$(go fmt ./... | wc -l); if [ $FIXED -gt 0 ]; then echo "gofmt - $FIXED file(s) not formatted correctly, please run gofmt to fix this." && exit 1; fi + +script: + - go test -v ./autoscaling/ + - go test -v ./aws/ + - go test -v ./cloudformation/ + - go test -v ./cloudfront/ + - go test -v ./cloudwatch/ + - go test -v ./dynamodb/ + - go test -v ./ec2/ + - go test -v ./ecs/ + - go test -v ./elb/ + - go test -v ./iam/ + - go test -v ./rds/ + - go test -v ./route53/ + - go test -v ./s3/ + - go test -v ./sqs/ + - go test -v ./sts/ + - go test -v ./exp/mturk/ + - go test -v ./exp/sdb/ + # - go test -v ./exp/ses/ + # - go test -v ./exp/sns/ diff --git a/vendor/github.com/goamz/goamz/CHANGES.md b/vendor/github.com/goamz/goamz/CHANGES.md new file mode 100644 index 000000000..3d047b896 --- /dev/null +++ b/vendor/github.com/goamz/goamz/CHANGES.md @@ -0,0 +1,5 @@ +# Changes in this fork + +* Added CNNorth Region +* Change SignV2 to SignV4 +* V4Signer.canonicalQueryString empty value must append "=" \ No newline at end of file diff --git a/vendor/github.com/goamz/goamz/README.md b/vendor/github.com/goamz/goamz/README.md new file mode 100644 index 000000000..e1b617484 --- /dev/null +++ b/vendor/github.com/goamz/goamz/README.md @@ -0,0 +1,64 @@ +# goamz - An Amazon Library for Go + +[![Build Status](http://travis-ci.org/goamz/goamz.png?branch=master)](https://travis-ci.org/goamz/goamz) + +The _goamz_ package enables Go programs to interact with Amazon Web Services. + +This is a fork of the version [developed within Canonical](https://wiki.ubuntu.com/goamz) with additional functionality and services from [a number of contributors](https://github.com/goamz/goamz/contributors)! + +The API of AWS is very comprehensive, though, and goamz doesn't even scratch the surface of it. That said, it's fairly well tested, and is the foundation in which further calls can easily be integrated. We'll continue extending the API as necessary - Pull Requests are _very_ welcome! + +The following packages are available at the moment: + +``` +github.com/goamz/goamz/autoscaling +github.com/goamz/goamz/aws +github.com/goamz/goamz/cloudformation +github.com/goamz/goamz/cloudfront +github.com/goamz/goamz/cloudwatch +github.com/goamz/goamz/dynamodb +github.com/goamz/goamz/ecs +github.com/goamz/goamz/ec2 +github.com/goamz/goamz/elb +github.com/goamz/goamz/iam +github.com/goamz/goamz/rds +github.com/goamz/goamz/route53 +github.com/goamz/goamz/s3 +github.com/goamz/goamz/sqs +github.com/goamz/goamz/sts + +github.com/goamz/goamz/exp/mturk +github.com/goamz/goamz/exp/sdb +github.com/goamz/goamz/exp/sns +``` + +Packages under `exp/` are still in an experimental or unfinished/unpolished state. + +## API documentation + +The API documentation is currently available at: + +[http://godoc.org/github.com/goamz/goamz](http://godoc.org/github.com/goamz/goamz) + +## How to build and install goamz + +Just use `go get` with any of the available packages. For example: + +* `$ go get github.com/goamz/goamz/ec2` +* `$ go get github.com/goamz/goamz/s3` + +## Running tests + +To run tests, first install gocheck with: + +`$ go get gopkg.in/check.v1` + +Then run go test as usual: + +`$ go test github.com/goamz/goamz/...` + +_Note:_ running all tests with the command `go test ./...` will currently fail as tests do not tear down their HTTP listeners. + +If you want to run integration tests (costs money), set up the EC2 environment variables as usual, and run: + +$ gotest -i diff --git a/vendor/github.com/goamz/goamz/autoscaling/autoscaling.go b/vendor/github.com/goamz/goamz/autoscaling/autoscaling.go new file mode 100644 index 000000000..8e9f8ab02 --- /dev/null +++ b/vendor/github.com/goamz/goamz/autoscaling/autoscaling.go @@ -0,0 +1,1768 @@ +// +// autoscaling: This package provides types and functions to interact with the AWS Auto Scale API +// +// Depends on https://wiki.ubuntu.com/goamz +// + +package autoscaling + +import ( + "encoding/base64" + "encoding/xml" + "fmt" + "log" + "net/http" + "net/http/httputil" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "github.com/goamz/goamz/aws" +) + +const debug = false + +var timeNow = time.Now + +// AutoScaling contains the details of the AWS region to perform operations against. +type AutoScaling struct { + aws.Auth + aws.Region +} + +// New creates a new AutoScaling Client. +func New(auth aws.Auth, region aws.Region) *AutoScaling { + return &AutoScaling{auth, region} +} + +// ---------------------------------------------------------------------------- +// Request dispatching logic. + +// Error encapsulates an error returned by the AWS Auto Scaling API. +// +// See http://goo.gl/VZGuC for more details. +type Error struct { + // HTTP status code (200, 403, ...) + StatusCode int + // AutoScaling error code ("UnsupportedOperation", ...) + Code string + // The error type + Type string + // The human-oriented error message + Message string + RequestId string `xml:"RequestID"` +} + +func (err *Error) Error() string { + if err.Code == "" { + return err.Message + } + + return fmt.Sprintf("%s (%s)", err.Message, err.Code) +} + +type xmlErrors struct { + RequestId string `xml:"RequestId"` + Errors []Error `xml:"Error"` +} + +func (as *AutoScaling) query(params map[string]string, resp interface{}) error { + params["Version"] = "2011-01-01" + data := strings.NewReader(multimap(params).Encode()) + + hreq, err := http.NewRequest("POST", as.Region.AutoScalingEndpoint+"/", data) + if err != nil { + return err + } + + hreq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + + token := as.Auth.Token() + if token != "" { + hreq.Header.Set("X-Amz-Security-Token", token) + } + + signer := aws.NewV4Signer(as.Auth, "autoscaling", as.Region) + signer.Sign(hreq) + + if debug { + log.Printf("%v -> {\n", hreq) + } + r, err := http.DefaultClient.Do(hreq) + + if err != nil { + log.Printf("Error calling Amazon %v", err) + return err + } + + defer r.Body.Close() + + if debug { + dump, _ := httputil.DumpResponse(r, true) + log.Printf("response:\n") + log.Printf("%v\n}\n", string(dump)) + } + if r.StatusCode != 200 { + return buildError(r) + } + err = xml.NewDecoder(r.Body).Decode(resp) + return err +} + +func buildError(r *http.Response) error { + var ( + err Error + errors xmlErrors + ) + xml.NewDecoder(r.Body).Decode(&errors) + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } + + err.RequestId = errors.RequestId + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} + +func makeParams(action string) map[string]string { + params := make(map[string]string) + params["Action"] = action + return params +} + +func addParamsList(params map[string]string, label string, ids []string) { + for i, id := range ids { + params[label+"."+strconv.Itoa(i+1)] = id + } +} + +// ---------------------------------------------------------------------------- +// Filtering helper. + +// Filter builds filtering parameters to be used in an autoscaling query which supports +// filtering. For example: +// +// filter := NewFilter() +// filter.Add("architecture", "i386") +// filter.Add("launch-index", "0") +// resp, err := as.DescribeTags(filter,nil,nil) +// +type Filter struct { + m map[string][]string +} + +// NewFilter creates a new Filter. +func NewFilter() *Filter { + return &Filter{make(map[string][]string)} +} + +// Add appends a filtering parameter with the given name and value(s). +func (f *Filter) Add(name string, value ...string) { + f.m[name] = append(f.m[name], value...) +} + +func (f *Filter) addParams(params map[string]string) { + if f != nil { + a := make([]string, len(f.m)) + i := 0 + for k := range f.m { + a[i] = k + i++ + } + sort.StringSlice(a).Sort() + for i, k := range a { + prefix := "Filters.member." + strconv.Itoa(i+1) + params[prefix+".Name"] = k + for j, v := range f.m[k] { + params[prefix+".Values.member."+strconv.Itoa(j+1)] = v + } + } + } +} + +// ---------------------------------------------------------------------------- +// Auto Scaling base types and related functions. + +// SimpleResp is the basic response from most actions. +type SimpleResp struct { + XMLName xml.Name + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// EnabledMetric encapsulates a metric associated with an Auto Scaling Group +// +// See http://goo.gl/hXiH17 for more details +type EnabledMetric struct { + Granularity string `xml:"Granularity"` // The granularity of the enabled metric. + Metric string `xml:"Metric"` // The name of the enabled metric. +} + +// Instance encapsulates an instance type as returned by the Auto Scaling API +// +// See http://goo.gl/NwBxGh and http://goo.gl/OuoqhS for more details. +type Instance struct { + // General instance information + AutoScalingGroupName string `xml:"AutoScalingGroupName"` + AvailabilityZone string `xml:"AvailabilityZone"` + HealthStatus string `xml:"HealthStatus"` + InstanceId string `xml:"InstanceId"` + LaunchConfigurationName string `xml:"LaunchConfigurationName"` + LifecycleState string `xml:"LifecycleState"` +} + +// SuspenedProcess encapsulates an Auto Scaling process that has been suspended +// +// See http://goo.gl/iObPgF for more details +type SuspendedProcess struct { + ProcessName string `xml:"ProcessName"` + SuspensionReason string `xml:"SuspensionReason"` +} + +// Tag encapsulates tag applied to an Auto Scaling group. +// +// See http://goo.gl/MG1hqs for more details +type Tag struct { + Key string `xml:"Key"` + PropagateAtLaunch bool `xml:"PropagateAtLaunch"` // Specifies whether the new tag will be applied to instances launched after the tag is created + ResourceId string `xml:"ResourceId"` // the name of the Auto Scaling group - not required if creating ASG + ResourceType string `xml:"ResourceType"` // currently only auto-scaling-group is supported - not required if creating ASG + Value string `xml:"Value"` +} + +// AutoScalingGroup encapsulates an Auto Scaling Group object +// +// See http://goo.gl/fJdYhg for more details. +type AutoScalingGroup struct { + AutoScalingGroupARN string `xml:"AutoScalingGroupARN"` + AutoScalingGroupName string `xml:"AutoScalingGroupName"` + AvailabilityZones []string `xml:"AvailabilityZones>member"` + CreatedTime time.Time `xml:"CreatedTime"` + DefaultCooldown int `xml:"DefaultCooldown"` + DesiredCapacity int `xml:"DesiredCapacity"` + EnabledMetrics []EnabledMetric `xml:"EnabledMetric>member"` + HealthCheckGracePeriod int `xml:"HealthCheckGracePeriod"` + HealthCheckType string `xml:"HealthCheckType"` + Instances []Instance `xml:"Instances>member"` + LaunchConfigurationName string `xml:"LaunchConfigurationName"` + LoadBalancerNames []string `xml:"LoadBalancerNames>member"` + MaxSize int `xml:"MaxSize"` + MinSize int `xml:"MinSize"` + PlacementGroup string `xml:"PlacementGroup"` + Status string `xml:"Status"` + SuspendedProcesses []SuspendedProcess `xml:"SuspendedProcesses>member"` + Tags []Tag `xml:"Tags>member"` + TerminationPolicies []string `xml:"TerminationPolicies>member"` + VPCZoneIdentifier string `xml:"VPCZoneIdentifier"` +} + +// CreateAutoScalingGroupParams type encapsulates options for the respective request. +// +// See http://goo.gl/3S13Bv for more details. +type CreateAutoScalingGroupParams struct { + AutoScalingGroupName string + AvailabilityZones []string + DefaultCooldown int + DesiredCapacity int + HealthCheckGracePeriod int + HealthCheckType string + InstanceId string + LaunchConfigurationName string + LoadBalancerNames []string + MaxSize int + MinSize int + PlacementGroup string + Tags []Tag + TerminationPolicies []string + VPCZoneIdentifier string +} + +// AttachInstances Attach running instances to an autoscaling group +// +// See http://goo.gl/zDZbuQ for more details. +func (as *AutoScaling) AttachInstances(name string, instanceIds []string) (resp *SimpleResp, err error) { + params := makeParams("AttachInstances") + params["AutoScalingGroupName"] = name + + for i, id := range instanceIds { + key := fmt.Sprintf("InstanceIds.member.%d", i+1) + params[key] = id + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// CreateAutoScalingGroup creates an Auto Scaling Group on AWS +// +// Required params: AutoScalingGroupName, MinSize, MaxSize +// +// See http://goo.gl/3S13Bv for more details. +func (as *AutoScaling) CreateAutoScalingGroup(options *CreateAutoScalingGroupParams) ( + resp *SimpleResp, err error) { + params := makeParams("CreateAutoScalingGroup") + + params["AutoScalingGroupName"] = options.AutoScalingGroupName + params["MaxSize"] = strconv.Itoa(options.MaxSize) + params["MinSize"] = strconv.Itoa(options.MinSize) + params["DesiredCapacity"] = strconv.Itoa(options.DesiredCapacity) + + if options.DefaultCooldown > 0 { + params["DefaultCooldown"] = strconv.Itoa(options.DefaultCooldown) + } + if options.HealthCheckGracePeriod > 0 { + params["HealthCheckGracePeriod"] = strconv.Itoa(options.HealthCheckGracePeriod) + } + if options.HealthCheckType != "" { + params["HealthCheckType"] = options.HealthCheckType + } + if options.InstanceId != "" { + params["InstanceId"] = options.InstanceId + } + if options.LaunchConfigurationName != "" { + params["LaunchConfigurationName"] = options.LaunchConfigurationName + } + if options.PlacementGroup != "" { + params["PlacementGroup"] = options.PlacementGroup + } + if options.VPCZoneIdentifier != "" { + params["VPCZoneIdentifier"] = options.VPCZoneIdentifier + } + if len(options.LoadBalancerNames) > 0 { + addParamsList(params, "LoadBalancerNames.member", options.LoadBalancerNames) + } + if len(options.AvailabilityZones) > 0 { + addParamsList(params, "AvailabilityZones.member", options.AvailabilityZones) + } + if len(options.TerminationPolicies) > 0 { + addParamsList(params, "TerminationPolicies.member", options.TerminationPolicies) + } + for i, t := range options.Tags { + key := "Tags.member.%d.%s" + index := i + 1 + params[fmt.Sprintf(key, index, "Key")] = t.Key + params[fmt.Sprintf(key, index, "Value")] = t.Value + params[fmt.Sprintf(key, index, "PropagateAtLaunch")] = strconv.FormatBool(t.PropagateAtLaunch) + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// EBS represents the AWS EBS volume data type +// +// See http://goo.gl/nDUL2h for more details +type EBS struct { + DeleteOnTermination bool `xml:"DeleteOnTermination"` + Iops int `xml:"Iops"` + SnapshotId string `xml:"SnapshotId"` + VolumeSize int `xml:"VolumeSize"` + VolumeType string `xml:"VolumeType"` +} + +// BlockDeviceMapping represents the association of a block device with ebs volume. +// +// See http://goo.gl/wEGwkU for more details. +type BlockDeviceMapping struct { + DeviceName string `xml:"DeviceName"` + Ebs EBS `xml:"Ebs"` + NoDevice bool `xml:"NoDevice"` + VirtualName string `xml:"VirtualName"` +} + +// InstanceMonitoring data type +// +// See http://goo.gl/TfaPwz for more details +type InstanceMonitoring struct { + Enabled bool `xml:"Enabled"` +} + +// LaunchConfiguration encapsulates the LaunchConfiguration Data Type +// +// See http://goo.gl/TOJunp +type LaunchConfiguration struct { + AssociatePublicIpAddress bool `xml:"AssociatePublicIpAddress"` + BlockDeviceMappings []BlockDeviceMapping `xml:"BlockDeviceMappings>member"` + CreatedTime time.Time `xml:"CreatedTime"` + EbsOptimized bool `xml:"EbsOptimized"` + IamInstanceProfile string `xml:"IamInstanceProfile"` + ImageId string `xml:"ImageId"` + InstanceId string `xml:"InstanceId"` + InstanceMonitoring InstanceMonitoring `xml:"InstanceMonitoring"` + InstanceType string `xml:"InstanceType"` + KernelId string `xml:"KernelId"` + KeyName string `xml:"KeyName"` + LaunchConfigurationARN string `xml:"LaunchConfigurationARN"` + LaunchConfigurationName string `xml:"LaunchConfigurationName"` + RamdiskId string `xml:"RamdiskId"` + SecurityGroups []string `xml:"SecurityGroups>member"` + SpotPrice string `xml:"SpotPrice"` + UserData string `xml:"UserData"` +} + +// CreateLaunchConfiguration creates a launch configuration +// +// Required params: AutoScalingGroupName, MinSize, MaxSize +// +// See http://goo.gl/8e0BSF for more details. +func (as *AutoScaling) CreateLaunchConfiguration(lc *LaunchConfiguration) ( + resp *SimpleResp, err error) { + + var b64 = base64.StdEncoding + + params := makeParams("CreateLaunchConfiguration") + params["LaunchConfigurationName"] = lc.LaunchConfigurationName + + if lc.AssociatePublicIpAddress { + params["AssociatePublicIpAddress"] = strconv.FormatBool(lc.AssociatePublicIpAddress) + } + if lc.EbsOptimized { + params["EbsOptimized"] = strconv.FormatBool(lc.EbsOptimized) + } + if lc.IamInstanceProfile != "" { + params["IamInstanceProfile"] = lc.IamInstanceProfile + } + if lc.ImageId != "" { + params["ImageId"] = lc.ImageId + } + if lc.InstanceId != "" { + params["InstanceId"] = lc.InstanceId + } + if lc.InstanceMonitoring != (InstanceMonitoring{}) { + params["InstanceMonitoring.Enabled"] = strconv.FormatBool(lc.InstanceMonitoring.Enabled) + } + if lc.InstanceType != "" { + params["InstanceType"] = lc.InstanceType + } + if lc.KernelId != "" { + params["KernelId"] = lc.KernelId + } + if lc.KeyName != "" { + params["KeyName"] = lc.KeyName + } + if lc.RamdiskId != "" { + params["RamdiskId"] = lc.RamdiskId + } + if lc.SpotPrice != "" { + params["SpotPrice"] = lc.SpotPrice + } + if lc.UserData != "" { + params["UserData"] = b64.EncodeToString([]byte(lc.UserData)) + } + + // Add our block device mappings + for i, bdm := range lc.BlockDeviceMappings { + key := "BlockDeviceMappings.member.%d.%s" + index := i + 1 + params[fmt.Sprintf(key, index, "DeviceName")] = bdm.DeviceName + params[fmt.Sprintf(key, index, "VirtualName")] = bdm.VirtualName + + if bdm.NoDevice { + params[fmt.Sprintf(key, index, "NoDevice")] = "true" + } + + if bdm.Ebs != (EBS{}) { + key := "BlockDeviceMappings.member.%d.Ebs.%s" + + // Defaults to true + params[fmt.Sprintf(key, index, "DeleteOnTermination")] = strconv.FormatBool(bdm.Ebs.DeleteOnTermination) + + if bdm.Ebs.Iops > 0 { + params[fmt.Sprintf(key, index, "Iops")] = strconv.Itoa(bdm.Ebs.Iops) + } + if bdm.Ebs.SnapshotId != "" { + params[fmt.Sprintf(key, index, "SnapshotId")] = bdm.Ebs.SnapshotId + } + if bdm.Ebs.VolumeSize > 0 { + params[fmt.Sprintf(key, index, "VolumeSize")] = strconv.Itoa(bdm.Ebs.VolumeSize) + } + if bdm.Ebs.VolumeType != "" { + params[fmt.Sprintf(key, index, "VolumeType")] = bdm.Ebs.VolumeType + } + } + } + + if len(lc.SecurityGroups) > 0 { + addParamsList(params, "SecurityGroups.member", lc.SecurityGroups) + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// CreateOrUpdateTags creates or updates Auto Scaling Group Tags +// +// See http://goo.gl/e1UIXb for more details. +func (as *AutoScaling) CreateOrUpdateTags(tags []Tag) (resp *SimpleResp, err error) { + params := makeParams("CreateOrUpdateTags") + + for i, t := range tags { + key := "Tags.member.%d.%s" + index := i + 1 + params[fmt.Sprintf(key, index, "Key")] = t.Key + params[fmt.Sprintf(key, index, "Value")] = t.Value + params[fmt.Sprintf(key, index, "PropagateAtLaunch")] = strconv.FormatBool(t.PropagateAtLaunch) + params[fmt.Sprintf(key, index, "ResourceId")] = t.ResourceId + if t.ResourceType != "" { + params[fmt.Sprintf(key, index, "ResourceType")] = t.ResourceType + } else { + params[fmt.Sprintf(key, index, "ResourceType")] = "auto-scaling-group" + } + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +type CompleteLifecycleActionParams struct { + AutoScalingGroupName string + LifecycleActionResult string + LifecycleActionToken string + LifecycleHookName string +} + +// CompleteLifecycleAction completes the lifecycle action for the associated token initiated under the given lifecycle hook with the specified result. +// +// Part of the basic sequence for adding a lifecycle hook to an Auto Scaling group: +// 1) Create a notification target (SQS queue || SNS Topic) +// 2) Create an IAM role to allow the ASG topublish lifecycle notifications to the designated SQS queue or SNS topic +// 3) Create the lifecycle hook. You can create a hook that acts when instances launch or when instances terminate +// 4) If necessary, record the lifecycle action heartbeat to keep the instance in a pending state +// 5) ***Complete the lifecycle action*** +// +// See http://goo.gl/k4fl0p for more details +func (as *AutoScaling) CompleteLifecycleAction(options *CompleteLifecycleActionParams) ( + resp *SimpleResp, err error) { + params := makeParams("CompleteLifecycleAction") + + params["AutoScalingGroupName"] = options.AutoScalingGroupName + params["LifecycleActionResult"] = options.LifecycleActionResult + params["LifecycleActionToken"] = options.LifecycleActionToken + params["LifecycleHookName"] = options.LifecycleHookName + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteAutoScalingGroup deletes an Auto Scaling Group +// +// See http://goo.gl/us7VSffor for more details. +func (as *AutoScaling) DeleteAutoScalingGroup(asgName string, forceDelete bool) ( + resp *SimpleResp, err error) { + params := makeParams("DeleteAutoScalingGroup") + params["AutoScalingGroupName"] = asgName + + if forceDelete { + params["ForceDelete"] = "true" + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteLaunchConfiguration deletes a Launch Configuration +// +// See http://goo.gl/xksfyR for more details. +func (as *AutoScaling) DeleteLaunchConfiguration(name string) (resp *SimpleResp, err error) { + params := makeParams("DeleteLaunchConfiguration") + params["LaunchConfigurationName"] = name + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteLifecycleHook eletes the specified lifecycle hook. +// If there are any outstanding lifecycle actions, they are completed first +// +// See http://goo.gl/MwX1vG for more details. +func (as *AutoScaling) DeleteLifecycleHook(asgName, lifecycleHookName string) (resp *SimpleResp, err error) { + params := makeParams("DeleteLifecycleHook") + params["AutoScalingGroupName"] = asgName + params["LifecycleHookName"] = lifecycleHookName + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteNotificationConfiguration deletes notifications created by PutNotificationConfiguration. +// +// See http://goo.gl/jTqoYz for more details +func (as *AutoScaling) DeleteNotificationConfiguration(asgName string, topicARN string) ( + resp *SimpleResp, err error) { + params := makeParams("DeleteNotificationConfiguration") + params["AutoScalingGroupName"] = asgName + params["TopicARN"] = topicARN + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeletePolicy deletes a policy created by PutScalingPolicy. +// +// policyName might be the policy name or ARN +// +// See http://goo.gl/aOQPH2 for more details +func (as *AutoScaling) DeletePolicy(asgName string, policyName string) (resp *SimpleResp, err error) { + params := makeParams("DeletePolicy") + params["AutoScalingGroupName"] = asgName + params["PolicyName"] = policyName + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteScheduledAction deletes a scheduled action previously created using the PutScheduledUpdateGroupAction. +// +// See http://goo.gl/Zss9CH for more details +func (as *AutoScaling) DeleteScheduledAction(asgName string, scheduledActionName string) (resp *SimpleResp, err error) { + params := makeParams("DeleteScheduledAction") + params["AutoScalingGroupName"] = asgName + params["ScheduledActionName"] = scheduledActionName + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteTags deletes autoscaling group tags +// +// See http://goo.gl/o8HzAk for more details. +func (as *AutoScaling) DeleteTags(tags []Tag) (resp *SimpleResp, err error) { + params := makeParams("DeleteTags") + + for i, t := range tags { + key := "Tags.member.%d.%s" + index := i + 1 + params[fmt.Sprintf(key, index, "Key")] = t.Key + params[fmt.Sprintf(key, index, "Value")] = t.Value + params[fmt.Sprintf(key, index, "PropagateAtLaunch")] = strconv.FormatBool(t.PropagateAtLaunch) + params[fmt.Sprintf(key, index, "ResourceId")] = t.ResourceId + params[fmt.Sprintf(key, index, "ResourceType")] = "auto-scaling-group" + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +//DescribeAccountLimits response wrapper +// +// See http://goo.gl/tKsMN0 for more details. +type DescribeAccountLimitsResp struct { + MaxNumberOfAutoScalingGroups int `xml:"DescribeAccountLimitsResult>MaxNumberOfAutoScalingGroups"` + MaxNumberOfLaunchConfigurations int `xml:"DescribeAccountLimitsResult>MaxNumberOfLaunchConfigurations"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeAccountLimits - Returns the limits for the Auto Scaling resources currently allowed for your AWS account. +// +// See http://goo.gl/tKsMN0 for more details. +func (as *AutoScaling) DescribeAccountLimits() (resp *DescribeAccountLimitsResp, err error) { + params := makeParams("DescribeAccountLimits") + + resp = new(DescribeAccountLimitsResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// AdjustmentType specifies whether the PutScalingPolicy ScalingAdjustment parameter is an absolute number or a percentage of the current capacity. +// +// See http://goo.gl/tCFqeL for more details +type AdjustmentType struct { + AdjustmentType string //Valid values are ChangeInCapacity, ExactCapacity, and PercentChangeInCapacity. +} + +//DescribeAdjustmentTypes response wrapper +// +// See http://goo.gl/hGx3Pc for more details. +type DescribeAdjustmentTypesResp struct { + AdjustmentTypes []AdjustmentType `xml:"DescribeAdjustmentTypesResult>AdjustmentTypes>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeAdjustmentTypes returns policy adjustment types for use in the PutScalingPolicy action. +// +// See http://goo.gl/hGx3Pc for more details. +func (as *AutoScaling) DescribeAdjustmentTypes() (resp *DescribeAdjustmentTypesResp, err error) { + params := makeParams("DescribeAdjustmentTypes") + + resp = new(DescribeAdjustmentTypesResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DescribeAutoScalingGroups response wrapper +// +// See http://goo.gl/nW74Ut for more details. +type DescribeAutoScalingGroupsResp struct { + AutoScalingGroups []AutoScalingGroup `xml:"DescribeAutoScalingGroupsResult>AutoScalingGroups>member"` + NextToken string `xml:"DescribeAutoScalingGroupsResult>NextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeAutoScalingGroups returns a full description of each Auto Scaling group in the given list +// If no autoscaling groups are provided, returns the details of all autoscaling groups +// Supports pagination by using the returned "NextToken" parameter for subsequent calls +// +// See http://goo.gl/nW74Ut for more details. +func (as *AutoScaling) DescribeAutoScalingGroups(names []string, maxRecords int, nextToken string) ( + resp *DescribeAutoScalingGroupsResp, err error) { + params := makeParams("DescribeAutoScalingGroups") + + if maxRecords != 0 { + params["MaxRecords"] = strconv.Itoa(maxRecords) + } + if nextToken != "" { + params["NextToken"] = nextToken + } + if len(names) > 0 { + addParamsList(params, "AutoScalingGroupNames.member", names) + } + + resp = new(DescribeAutoScalingGroupsResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DescribeAutoScalingInstances response wrapper +// +// See http://goo.gl/ckzORt for more details. +type DescribeAutoScalingInstancesResp struct { + AutoScalingInstances []Instance `xml:"DescribeAutoScalingInstancesResult>AutoScalingInstances>member"` + NextToken string `xml:"DescribeAutoScalingInstancesResult>NextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeAutoScalingInstances returns a description of each Auto Scaling instance in the InstanceIds list. +// If a list is not provided, the service returns the full details of all instances up to a maximum of 50 +// By default, the service returns a list of 20 items. +// Supports pagination by using the returned "NextToken" parameter for subsequent calls +// +// See http://goo.gl/ckzORt for more details. +func (as *AutoScaling) DescribeAutoScalingInstances(ids []string, maxRecords int, nextToken string) ( + resp *DescribeAutoScalingInstancesResp, err error) { + params := makeParams("DescribeAutoScalingInstances") + + if maxRecords != 0 { + params["MaxRecords"] = strconv.Itoa(maxRecords) + } + if nextToken != "" { + params["NextToken"] = nextToken + } + if len(ids) > 0 { + addParamsList(params, "InstanceIds.member", ids) + } + + resp = new(DescribeAutoScalingInstancesResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DescribeAutoScalingNotificationTypes response wrapper +// +// See http://goo.gl/pmLIoE for more details. +type DescribeAutoScalingNotificationTypesResp struct { + AutoScalingNotificationTypes []string `xml:"DescribeAutoScalingNotificationTypesResult>AutoScalingNotificationTypes>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeAutoScalingNotificationTypes returns a list of all notification types that are supported by Auto Scaling +// +// See http://goo.gl/pmLIoE for more details. +func (as *AutoScaling) DescribeAutoScalingNotificationTypes() (resp *DescribeAutoScalingNotificationTypesResp, err error) { + params := makeParams("DescribeAutoScalingNotificationTypes") + + resp = new(DescribeAutoScalingNotificationTypesResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DescribeLaunchConfigurationResp defines the basic response structure for launch configuration +// requests +// +// See http://goo.gl/y31YYE for more details. +type DescribeLaunchConfigurationsResp struct { + LaunchConfigurations []LaunchConfiguration `xml:"DescribeLaunchConfigurationsResult>LaunchConfigurations>member"` + NextToken string `xml:"DescribeLaunchConfigurationsResult>NextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeLaunchConfigurations returns details about the launch configurations supplied in +// the list. If the list is nil, information is returned about all launch configurations in the +// region. +// +// See http://goo.gl/y31YYE for more details. +func (as *AutoScaling) DescribeLaunchConfigurations(names []string, maxRecords int, nextToken string) ( + resp *DescribeLaunchConfigurationsResp, err error) { + params := makeParams("DescribeLaunchConfigurations") + + if maxRecords != 0 { + params["MaxRecords"] = strconv.Itoa(maxRecords) + } + if nextToken != "" { + params["NextToken"] = nextToken + } + if len(names) > 0 { + addParamsList(params, "LaunchConfigurationNames.member", names) + } + + resp = new(DescribeLaunchConfigurationsResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + + return resp, nil +} + +// DescribeLifecycleHookTypesResult wraps a DescribeLifecycleHookTypes response +// +// See http://goo.gl/qiAH31 for more details. +type DescribeLifecycleHookTypesResult struct { + LifecycleHookTypes []string `xml:"DescribeLifecycleHookTypesResult>LifecycleHookTypes>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeLifecycleHookTypes describes the available types of lifecycle hooks +// +// See http://goo.gl/E9IBtY for more information +func (as *AutoScaling) DescribeLifecycleHookTypes() ( + resp *DescribeLifecycleHookTypesResult, err error) { + params := makeParams("DescribeLifecycleHookTypes") + + resp = new(DescribeLifecycleHookTypesResult) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// LifecycleHook represents a lifecyclehook object +// +// See http://goo.gl/j62Iqu for more information +type LifecycleHook struct { + AutoScalingGroupName string `xml:"AutoScalingGroupName"` + DefaultResult string `xml:"DefaultResult"` + GlobalTimeout int `xml:"GlobalTimeout"` + HeartbeatTimeout int `xml:"HeartbeatTimeout"` + LifecycleHookName string `xml:"LifecycleHookName"` + LifecycleTransition string `xml:"LifecycleTransition"` + NotificationMetadata string `xml:"NotificationMetadata"` + NotificationTargetARN string `xml:"NotificationTargetARN"` + RoleARN string `xml:"RoleARN"` +} + +// DescribeLifecycleHooks wraps a DescribeLifecycleHooks response +// +// See http://goo.gl/wQkWiz for more details. +type DescribeLifecycleHooksResult struct { + LifecycleHooks []string `xml:"DescribeLifecycleHooksResult>LifecycleHooks>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeLifecycleHooks describes the lifecycle hooks that currently belong to the specified Auto Scaling group +// +// See http://goo.gl/wQkWiz for more information +func (as *AutoScaling) DescribeLifecycleHooks(asgName string, hookNames []string) ( + resp *DescribeLifecycleHooksResult, err error) { + params := makeParams("DescribeLifecycleHooks") + params["AutoScalingGroupName"] = asgName + + if len(hookNames) > 0 { + addParamsList(params, "LifecycleHookNames.member", hookNames) + } + + resp = new(DescribeLifecycleHooksResult) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// MetricGranularity encapsulates the MetricGranularityType +// +// See http://goo.gl/WJ82AA for more details +type MetricGranularity struct { + Granularity string `xml:"Granularity"` +} + +//MetricCollection encapsulates the MetricCollectionType +// +// See http://goo.gl/YrEG6h for more details +type MetricCollection struct { + Metric string `xml:"Metric"` +} + +// DescribeMetricCollectionTypesResp response wrapper +// +// See http://goo.gl/UyYc3i for more details. +type DescribeMetricCollectionTypesResp struct { + Granularities []MetricGranularity `xml:"DescribeMetricCollectionTypesResult>Granularities>member"` + Metrics []MetricCollection `xml:"DescribeMetricCollectionTypesResult>Metrics>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeMetricCollectionTypes returns a list of metrics and a corresponding list of granularities for each metric +// +// See http://goo.gl/UyYc3i for more details. +func (as *AutoScaling) DescribeMetricCollectionTypes() (resp *DescribeMetricCollectionTypesResp, err error) { + params := makeParams("DescribeMetricCollectionTypes") + + resp = new(DescribeMetricCollectionTypesResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// NotificationConfiguration encapsulates the NotificationConfigurationType +// +// See http://goo.gl/M8xYOQ for more details +type NotificationConfiguration struct { + AutoScalingGroupName string `xml:"AutoScalingGroupName"` + NotificationType string `xml:"NotificationType"` + TopicARN string `xml:"TopicARN"` +} + +// DescribeNotificationConfigurations response wrapper +// +// See http://goo.gl/qiAH31 for more details. +type DescribeNotificationConfigurationsResp struct { + NotificationConfigurations []NotificationConfiguration `xml:"DescribeNotificationConfigurationsResult>NotificationConfigurations>member"` + NextToken string `xml:"DescribeNotificationConfigurationsResult>NextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeNotificationConfigurations returns a list of notification actions associated with Auto Scaling groups for specified events. +// Supports pagination by using the returned "NextToken" parameter for subsequent calls +// +// http://goo.gl/qiAH31 for more details. +func (as *AutoScaling) DescribeNotificationConfigurations(asgNames []string, maxRecords int, nextToken string) ( + resp *DescribeNotificationConfigurationsResp, err error) { + params := makeParams("DescribeNotificationConfigurations") + + if maxRecords != 0 { + params["MaxRecords"] = strconv.Itoa(maxRecords) + } + if nextToken != "" { + params["NextToken"] = nextToken + } + if len(asgNames) > 0 { + addParamsList(params, "AutoScalingGroupNames.member", asgNames) + } + + resp = new(DescribeNotificationConfigurationsResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Alarm encapsulates the Alarm data type. +// +// See http://goo.gl/Q0uPAB for more details +type Alarm struct { + AlarmARN string `xml:"AlarmARN"` + AlarmName string `xml:"AlarmName"` +} + +// ScalingPolicy encapsulates the ScalingPolicyType +// +// See http://goo.gl/BYAT18 for more details +type ScalingPolicy struct { + AdjustmentType string `xml:"AdjustmentType"` // ChangeInCapacity, ExactCapacity, and PercentChangeInCapacity + Alarms []Alarm `xml:"Alarms>member"` // A list of CloudWatch Alarms related to the policy + AutoScalingGroupName string `xml:"AutoScalingGroupName"` + Cooldown int `xml:"Cooldown"` + MinAdjustmentStep int `xml:"MinAdjustmentStep"` // Changes the DesiredCapacity of ASG by at least the specified number of instances. + PolicyARN string `xml:"PolicyARN"` + PolicyName string `xml:"PolicyName"` + ScalingAdjustment int `xml:"ScalingAdjustment"` +} + +// DescribePolicies response wrapper +// +// http://goo.gl/bN7A9T for more details. +type DescribePoliciesResp struct { + ScalingPolicies []ScalingPolicy `xml:"DescribePoliciesResult>ScalingPolicies>member"` + NextToken string `xml:"DescribePoliciesResult>NextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribePolicies returns descriptions of what each policy does. +// Supports pagination by using the returned "NextToken" parameter for subsequent calls +// +// http://goo.gl/bN7A9Tfor more details. +func (as *AutoScaling) DescribePolicies(asgName string, policyNames []string, maxRecords int, nextToken string) ( + resp *DescribePoliciesResp, err error) { + params := makeParams("DescribePolicies") + + if asgName != "" { + params["AutoScalingGroupName"] = asgName + } + if maxRecords != 0 { + params["MaxRecords"] = strconv.Itoa(maxRecords) + } + if nextToken != "" { + params["NextToken"] = nextToken + } + if len(policyNames) > 0 { + addParamsList(params, "PolicyNames.member", policyNames) + } + + resp = new(DescribePoliciesResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Activity encapsulates the Activity data type +// +// See http://goo.gl/fRaVi1 for more details +type Activity struct { + ActivityId string `xml:"ActivityId"` + AutoScalingGroupName string `xml:"AutoScalingGroupName"` + Cause string `xml:"Cause"` + Description string `xml:"Description"` + Details string `xml:"Details"` + EndTime time.Time `xml:"EndTime"` + Progress int `xml:"Progress"` + StartTime time.Time `xml:"StartTime"` + StatusCode string `xml:"StatusCode"` + StatusMessage string `xml:"StatusMessage"` +} + +// DescribeScalingActivities response wrapper +// +// http://goo.gl/noOXIC for more details. +type DescribeScalingActivitiesResp struct { + Activities []Activity `xml:"DescribeScalingActivitiesResult>Activities>member"` + NextToken string `xml:"DescribeScalingActivitiesResult>NextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeScalingActivities returns the scaling activities for the specified Auto Scaling group. +// Supports pagination by using the returned "NextToken" parameter for subsequent calls +// +// http://goo.gl/noOXIC more details. +func (as *AutoScaling) DescribeScalingActivities(asgName string, activityIds []string, maxRecords int, nextToken string) ( + resp *DescribeScalingActivitiesResp, err error) { + params := makeParams("DescribeScalingActivities") + + if asgName != "" { + params["AutoScalingGroupName"] = asgName + } + if maxRecords != 0 { + params["MaxRecords"] = strconv.Itoa(maxRecords) + } + if nextToken != "" { + params["NextToken"] = nextToken + } + if len(activityIds) > 0 { + addParamsList(params, "ActivityIds.member", activityIds) + } + + resp = new(DescribeScalingActivitiesResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ProcessType encapsulates the Auto Scaling process data type +// +// See http://goo.gl/9BvNik for more details. +type ProcessType struct { + ProcessName string `xml:"ProcessName"` +} + +// DescribeScalingProcessTypes response wrapper +// +// See http://goo.gl/rkp2tw for more details. +type DescribeScalingProcessTypesResp struct { + Processes []ProcessType `xml:"DescribeScalingProcessTypesResult>Processes>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeScalingProcessTypes returns scaling process types for use in the ResumeProcesses and SuspendProcesses actions. +// +// See http://goo.gl/rkp2tw for more details. +func (as *AutoScaling) DescribeScalingProcessTypes() (resp *DescribeScalingProcessTypesResp, err error) { + params := makeParams("DescribeScalingProcessTypes") + + resp = new(DescribeScalingProcessTypesResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ScheduledUpdateGroupAction contains the information to be used in a scheduled update to an +// AutoScalingGroup +// +// See http://goo.gl/z2Kfxe for more details +type ScheduledUpdateGroupAction struct { + AutoScalingGroupName string `xml:"AutoScalingGroupName"` + DesiredCapacity int `xml:"DesiredCapacity"` + EndTime time.Time `xml:"EndTime"` + MaxSize int `xml:"MaxSize"` + MinSize int `xml:"MinSize"` + Recurrence string `xml:"Recurrence"` + ScheduledActionARN string `xml:"ScheduledActionARN"` + ScheduledActionName string `xml:"ScheduledActionName"` + StartTime time.Time `xml:"StartTime"` + Time time.Time `xml:"Time"` +} + +// DescribeScheduledActionsResult contains the response from a DescribeScheduledActions. +// +// See http://goo.gl/zqrJLx for more details. +type DescribeScheduledActionsResult struct { + ScheduledUpdateGroupActions []ScheduledUpdateGroupAction `xml:"DescribeScheduledActionsResult>ScheduledUpdateGroupActions>member"` + NextToken string `xml:"DescribeScheduledActionsResult>NextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ScheduledActionsRequestParams contains the items that can be specified when making +// a ScheduledActions request +type DescribeScheduledActionsParams struct { + AutoScalingGroupName string + EndTime time.Time + MaxRecords int + ScheduledActionNames []string + StartTime time.Time + NextToken string +} + +// DescribeScheduledActions returns a list of the current scheduled actions. If the +// AutoScalingGroup name is provided it will list all the scheduled actions for that group. +// +// See http://goo.gl/zqrJLx for more details. +func (as *AutoScaling) DescribeScheduledActions(options *DescribeScheduledActionsParams) ( + resp *DescribeScheduledActionsResult, err error) { + params := makeParams("DescribeScheduledActions") + + if options.AutoScalingGroupName != "" { + params["AutoScalingGroupName"] = options.AutoScalingGroupName + } + if !options.StartTime.IsZero() { + params["StartTime"] = options.StartTime.Format(time.RFC3339) + } + if !options.EndTime.IsZero() { + params["EndTime"] = options.EndTime.Format(time.RFC3339) + } + if options.MaxRecords > 0 { + params["MaxRecords"] = strconv.Itoa(options.MaxRecords) + } + if options.NextToken != "" { + params["NextToken"] = options.NextToken + } + if len(options.ScheduledActionNames) > 0 { + addParamsList(params, "ScheduledActionNames.member", options.ScheduledActionNames) + } + + resp = new(DescribeScheduledActionsResult) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DescribeTags response wrapper +// +// See http://goo.gl/ZTEU3G for more details. +type DescribeTagsResp struct { + Tags []Tag `xml:"DescribeTagsResult>Tags>member"` + NextToken string `xml:"DescribeTagsResult>NextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeTags lists the Auto Scaling group tags. +// Supports pagination by using the returned "NextToken" parameter for subsequent calls +// +// See http://goo.gl/ZTEU3G for more details. +func (as *AutoScaling) DescribeTags(filter *Filter, maxRecords int, nextToken string) ( + resp *DescribeTagsResp, err error) { + params := makeParams("DescribeTags") + + if maxRecords != 0 { + params["MaxRecords"] = strconv.Itoa(maxRecords) + } + if nextToken != "" { + params["NextToken"] = nextToken + } + + filter.addParams(params) + + resp = new(DescribeTagsResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DescribeTerminationPolicyTypes response wrapper +// +// See http://goo.gl/ZTEU3G for more details. +type DescribeTerminationPolicyTypesResp struct { + TerminationPolicyTypes []string `xml:"DescribeTerminationPolicyTypesResult>TerminationPolicyTypes>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeTerminationPolicyTypes returns a list of all termination policies supported by Auto Scaling +// +// See http://goo.gl/ZTEU3G for more details. +func (as *AutoScaling) DescribeTerminationPolicyTypes() (resp *DescribeTerminationPolicyTypesResp, err error) { + params := makeParams("DescribeTerminationPolicyTypes") + + resp = new(DescribeTerminationPolicyTypesResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DetachInstancesResult wraps a DetachInstances response +type DetachInstancesResult struct { + Activities []Activity `xml:"DetachInstancesResult>Activities>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DetachInstances removes an instance from an Auto Scaling group +// +// See http://goo.gl/cNwrqF for more details +func (as *AutoScaling) DetachInstances(asgName string, instanceIds []string, decrementCapacity bool) ( + resp *DetachInstancesResult, err error) { + params := makeParams("DetachInstances") + params["AutoScalingGroupName"] = asgName + params["ShouldDecrementDesiredCapacity"] = strconv.FormatBool(decrementCapacity) + + if len(instanceIds) > 0 { + addParamsList(params, "InstanceIds.member", instanceIds) + } + + resp = new(DetachInstancesResult) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DisableMetricsCollection disables monitoring of group metrics for the Auto Scaling group specified in asgName. +// You can specify the list of affected metrics with the metrics parameter. If no metrics are specified, all metrics are disabled +// +// See http://goo.gl/kAvzQw for more details. +func (as *AutoScaling) DisableMetricsCollection(asgName string, metrics []string) ( + resp *SimpleResp, err error) { + params := makeParams("DisableMetricsCollection") + params["AutoScalingGroupName"] = asgName + + if len(metrics) > 0 { + addParamsList(params, "Metrics.member", metrics) + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// EnableMetricsCollection enables monitoring of group metrics for the Auto Scaling group specified in asNmae. +// You can specify the list of affected metrics with the metrics parameter. +// Auto Scaling metrics collection can be turned on only if the InstanceMonitoring flag is set to true. +// Currently, the only legal granularity is "1Minute". +// +// See http://goo.gl/UcVDWn for more details. +func (as *AutoScaling) EnableMetricsCollection(asgName string, metrics []string, granularity string) ( + resp *SimpleResp, err error) { + params := makeParams("EnableMetricsCollection") + params["AutoScalingGroupName"] = asgName + params["Granularity"] = granularity + + if len(metrics) > 0 { + addParamsList(params, "Metrics.member", metrics) + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// EnterStandbyResult wraps an EnterStandby response +type EnterStandbyResult struct { + Activities []Activity `xml:"EnterStandbyResult>Activities>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// EnterStandby moves instances in an Auto Scaling group into a Standby mode. +// +// See http://goo.gl/BJ3lXs for more information +func (as *AutoScaling) EnterStandby(asgName string, instanceIds []string, decrementCapacity bool) ( + resp *EnterStandbyResult, err error) { + params := makeParams("EnterStandby") + params["AutoScalingGroupName"] = asgName + params["ShouldDecrementDesiredCapacity"] = strconv.FormatBool(decrementCapacity) + + if len(instanceIds) > 0 { + addParamsList(params, "InstanceIds.member", instanceIds) + } + + resp = new(EnterStandbyResult) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ExecutePolicy executes the specified policy. +// +// See http://goo.gl/BxHpFc for more details. +func (as *AutoScaling) ExecutePolicy(policyName string, asgName string, honorCooldown bool) ( + resp *SimpleResp, err error) { + params := makeParams("ExecutePolicy") + params["PolicyName"] = policyName + + if asgName != "" { + params["AutoScalingGroupName"] = asgName + } + if honorCooldown { + params["HonorCooldown"] = strconv.FormatBool(honorCooldown) + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ExitStandbyResult wraps an ExitStandby response +type ExitStandbyResult struct { + Activities []Activity `xml:"ExitStandbyResult>Activities>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ExitStandby moves an instance out of Standby mode. +// +// See http://goo.gl/9zQV4G for more information +func (as *AutoScaling) ExitStandby(asgName string, instanceIds []string) ( + resp *ExitStandbyResult, err error) { + params := makeParams("ExitStandby") + params["AutoScalingGroupName"] = asgName + + if len(instanceIds) > 0 { + addParamsList(params, "InstanceIds.member", instanceIds) + } + + resp = new(ExitStandbyResult) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// PutLifecycleHookParams wraps a PutLifecycleHook request +// +// See http://goo.gl/zsNqp5 for more details +type PutLifecycleHookParams struct { + AutoScalingGroupName string + DefaultResult string + HeartbeatTimeout int + LifecycleHookName string + LifecycleTransition string + NotificationMetadata string + NotificationTargetARN string + RoleARN string +} + +// PutLifecycleHook Creates or updates a lifecycle hook for an Auto Scaling Group. +// +// Part of the basic sequence for adding a lifecycle hook to an Auto Scaling group: +// 1) Create a notification target (SQS queue || SNS Topic) +// 2) Create an IAM role to allow the ASG topublish lifecycle notifications to the designated SQS queue or SNS topic +// 3) *** Create the lifecycle hook. You can create a hook that acts when instances launch or when instances terminate*** +// 4) If necessary, record the lifecycle action heartbeat to keep the instance in a pending state +// 5) Complete the lifecycle action +// +// See http://goo.gl/9XrROq for more details. +func (as *AutoScaling) PutLifecycleHook(options *PutLifecycleHookParams) ( + resp *SimpleResp, err error) { + params := makeParams("PutLifecycleHook") + params["AutoScalingGroupName"] = options.AutoScalingGroupName + params["LifecycleHookName"] = options.LifecycleHookName + + if options.DefaultResult != "" { + params["DefaultResult"] = options.DefaultResult + } + if options.HeartbeatTimeout != 0 { + params["HeartbeatTimeout"] = strconv.Itoa(options.HeartbeatTimeout) + } + if options.LifecycleTransition != "" { + params["LifecycleTransition"] = options.LifecycleTransition + } + if options.NotificationMetadata != "" { + params["NotificationMetadata"] = options.NotificationMetadata + } + if options.NotificationTargetARN != "" { + params["NotificationTargetARN"] = options.NotificationTargetARN + } + if options.RoleARN != "" { + params["RoleARN"] = options.RoleARN + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// PutNotificationConfiguration configures an Auto Scaling group to send notifications when specified events take place. +// +// See http://goo.gl/9XrROq for more details. +func (as *AutoScaling) PutNotificationConfiguration(asgName string, notificationTypes []string, topicARN string) ( + resp *SimpleResp, err error) { + params := makeParams("PutNotificationConfiguration") + params["AutoScalingGroupName"] = asgName + params["TopicARN"] = topicARN + + if len(notificationTypes) > 0 { + addParamsList(params, "NotificationTypes.member", notificationTypes) + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// PutScalingPolicyParams wraps a PutScalingPolicyParams request +// +// See http://goo.gl/o0E8hl for more details. +type PutScalingPolicyParams struct { + AutoScalingGroupName string + PolicyName string + ScalingAdjustment int + AdjustmentType string + Cooldown int + MinAdjustmentStep int +} + +// PutScalingPolicy response wrapper +// +// See http://goo.gl/o0E8hl for more details. +type PutScalingPolicyResp struct { + PolicyARN string `xml:"PutScalingPolicyResult>PolicyARN"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// PutScalingPolicy creates or updates a policy for an Auto Scaling group +// +// See http://goo.gl/o0E8hl for more details. +func (as *AutoScaling) PutScalingPolicy(options *PutScalingPolicyParams) ( + resp *PutScalingPolicyResp, err error) { + params := makeParams("PutScalingPolicy") + params["AutoScalingGroupName"] = options.AutoScalingGroupName + params["PolicyName"] = options.PolicyName + params["ScalingAdjustment"] = strconv.Itoa(options.ScalingAdjustment) + params["AdjustmentType"] = options.AdjustmentType + + if options.Cooldown != 0 { + params["Cooldown"] = strconv.Itoa(options.Cooldown) + } + if options.MinAdjustmentStep != 0 { + params["MinAdjustmentStep"] = strconv.Itoa(options.MinAdjustmentStep) + } + + resp = new(PutScalingPolicyResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// PutScheduledUpdateGroupActionParams contains the details of the ScheduledAction to be added. +// +// See http://goo.gl/sLPi0d for more details +type PutScheduledUpdateGroupActionParams struct { + AutoScalingGroupName string + DesiredCapacity int + EndTime time.Time + MaxSize int + MinSize int + Recurrence string + ScheduledActionName string + StartTime time.Time +} + +// PutScheduledUpdateGroupAction creates or updates a scheduled scaling action for an +// AutoScaling group. Scheduled actions can be made up to thirty days in advance. When updating +// a scheduled scaling action, if you leave a parameter unspecified, the corresponding value +// remains unchanged in the affected AutoScaling group. +// +// Auto Scaling supports the date and time expressed in "YYYY-MM-DDThh:mm:ssZ" format in UTC/GMT +// only. +// +// See http://goo.gl/sLPi0d for more details. +func (as *AutoScaling) PutScheduledUpdateGroupAction(options *PutScheduledUpdateGroupActionParams) ( + resp *SimpleResp, err error) { + params := makeParams("PutScheduledUpdateGroupAction") + params["AutoScalingGroupName"] = options.AutoScalingGroupName + params["ScheduledActionName"] = options.ScheduledActionName + params["MinSize"] = strconv.Itoa(options.MinSize) + params["MaxSize"] = strconv.Itoa(options.MaxSize) + params["DesiredCapacity"] = strconv.Itoa(options.DesiredCapacity) + + if !options.StartTime.IsZero() { + params["StartTime"] = options.StartTime.Format(time.RFC3339) + } + if !options.EndTime.IsZero() { + params["EndTime"] = options.EndTime.Format(time.RFC3339) + } + if options.Recurrence != "" { + params["Recurrence"] = options.Recurrence + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// RecordLifecycleActionHeartbeat ecords a heartbeat for the lifecycle action associated with a specific token. +// This extends the timeout by the length of time defined by the HeartbeatTimeout parameter of the +// PutLifecycleHook operation. +// +// Part of the basic sequence for adding a lifecycle hook to an Auto Scaling group: +// 1) Create a notification target (SQS queue || SNS Topic) +// 2) Create an IAM role to allow the ASG topublish lifecycle notifications to the designated SQS queue or SNS topic +// 3) Create the lifecycle hook. You can create a hook that acts when instances launch or when instances terminate +// 4) ***If necessary, record the lifecycle action heartbeat to keep the instance in a pending state*** +// 5) Complete the lifecycle action +// +// See http://goo.gl/jc70xp for more details. +func (as *AutoScaling) RecordLifecycleActionHeartbeat(asgName, lifecycleActionToken, hookName string) ( + resp *SimpleResp, err error) { + params := makeParams("RecordLifecycleActionHeartbeat") + params["AutoScalingGroupName"] = asgName + params["LifecycleActionToken"] = lifecycleActionToken + params["LifecycleHookName"] = hookName + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ResumeProcesses resumes the scaling processes for the scaling group. If no processes are +// provided, all processes are resumed. +// +// See http://goo.gl/XWIIg1 for more details. +func (as *AutoScaling) ResumeProcesses(asgName string, processes []string) ( + resp *SimpleResp, err error) { + params := makeParams("ResumeProcesses") + params["AutoScalingGroupName"] = asgName + + if len(processes) > 0 { + addParamsList(params, "ScalingProcesses.member", processes) + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// SetDesiredCapacity changes the DesiredCapacity of an AutoScaling group. +// +// See http://goo.gl/3WGZbI for more details. +func (as *AutoScaling) SetDesiredCapacity(asgName string, desiredCapacity int, honorCooldown bool) ( + resp *SimpleResp, err error) { + params := makeParams("SetDesiredCapacity") + params["AutoScalingGroupName"] = asgName + params["DesiredCapacity"] = strconv.Itoa(desiredCapacity) + params["HonorCooldown"] = strconv.FormatBool(honorCooldown) + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// SetInstanceHealth sets the health status of a specified instance that belongs to any of your Auto Scaling groups. +// +// See http://goo.gl/j4ZRxh for more details. +func (as *AutoScaling) SetInstanceHealth(id string, healthStatus string, respectGracePeriod bool) ( + resp *SimpleResp, err error) { + params := makeParams("SetInstanceHealth") + params["HealthStatus"] = healthStatus + params["InstanceId"] = id + // Default is true + params["ShouldRespectGracePeriod"] = strconv.FormatBool(respectGracePeriod) + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// SuspendProcesses suspends the processes for the autoscaling group. If no processes are +// provided, all processes are suspended. +// +// If you suspend either of the two primary processes (Launch or Terminate), this can prevent other +// process types from functioning properly. +// +// See http://goo.gl/DUJpQy for more details. +func (as *AutoScaling) SuspendProcesses(asgName string, processes []string) ( + resp *SimpleResp, err error) { + params := makeParams("SuspendProcesses") + params["AutoScalingGroupName"] = asgName + + if len(processes) > 0 { + addParamsList(params, "ScalingProcesses.member", processes) + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// TerminateInstanceInAutoScalingGroupResp response wrapper +// +// See http://goo.gl/ki5hMh for more details. +type TerminateInstanceInAutoScalingGroupResp struct { + Activity Activity `xml:"TerminateInstanceInAutoScalingGroupResult>Activity"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// TerminateInstanceInAutoScalingGroup terminates the specified instance. +// Optionally, the desired group size can be adjusted by setting decrCap to true +// +// See http://goo.gl/ki5hMh for more details. +func (as *AutoScaling) TerminateInstanceInAutoScalingGroup(id string, decrCap bool) ( + resp *TerminateInstanceInAutoScalingGroupResp, err error) { + params := makeParams("TerminateInstanceInAutoScalingGroup") + params["InstanceId"] = id + params["ShouldDecrementDesiredCapacity"] = strconv.FormatBool(decrCap) + + resp = new(TerminateInstanceInAutoScalingGroupResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// UpdateAutoScalingGroup updates the scaling group. +// +// To update an auto scaling group with a launch configuration that has the InstanceMonitoring +// flag set to False, you must first ensure that collection of group metrics is disabled. +// Otherwise calls to UpdateAutoScalingGroup will fail. +// +// See http://goo.gl/rqrmxy for more details. +func (as *AutoScaling) UpdateAutoScalingGroup(asg *AutoScalingGroup) (resp *SimpleResp, err error) { + params := makeParams("UpdateAutoScalingGroup") + + params["AutoScalingGroupName"] = asg.AutoScalingGroupName + params["MaxSize"] = strconv.Itoa(asg.MaxSize) + params["MinSize"] = strconv.Itoa(asg.MinSize) + params["DesiredCapacity"] = strconv.Itoa(asg.DesiredCapacity) + + if asg.DefaultCooldown > 0 { + params["DefaultCooldown"] = strconv.Itoa(asg.DefaultCooldown) + } + if asg.HealthCheckGracePeriod > 0 { + params["HealthCheckGracePeriod"] = strconv.Itoa(asg.HealthCheckGracePeriod) + } + if asg.HealthCheckType != "" { + params["HealthCheckType"] = asg.HealthCheckType + } + if asg.LaunchConfigurationName != "" { + params["LaunchConfigurationName"] = asg.LaunchConfigurationName + } + if asg.PlacementGroup != "" { + params["PlacementGroup"] = asg.PlacementGroup + } + if asg.VPCZoneIdentifier != "" { + params["VPCZoneIdentifier"] = asg.VPCZoneIdentifier + } + + if len(asg.AvailabilityZones) > 0 { + addParamsList(params, "AvailabilityZones.member", asg.AvailabilityZones) + } + if len(asg.TerminationPolicies) > 0 { + addParamsList(params, "TerminationPolicies.member", asg.TerminationPolicies) + } + + resp = new(SimpleResp) + if err := as.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/github.com/goamz/goamz/autoscaling/autoscaling_test.go b/vendor/github.com/goamz/goamz/autoscaling/autoscaling_test.go new file mode 100644 index 000000000..b728a1cf4 --- /dev/null +++ b/vendor/github.com/goamz/goamz/autoscaling/autoscaling_test.go @@ -0,0 +1,1180 @@ +package autoscaling + +import ( + "testing" + "time" + + . "gopkg.in/check.v1" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/testutil" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + as *AutoScaling +} + +var testServer = testutil.NewHTTPServer() + +var mockTest bool + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.as = New(auth, aws.Region{AutoScalingEndpoint: testServer.URL}) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func TestBasicGroupRequest(t *testing.T) { + var as *AutoScaling + awsAuth, err := aws.EnvAuth() + if err != nil { + mockTest = true + t.Log("Running mock tests as AWS environment variables are not set") + awsAuth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + as = New(awsAuth, aws.Region{AutoScalingEndpoint: testServer.URL}) + testServer.Start() + go testServer.WaitRequest() + testServer.Response(200, nil, BasicGroupResponse) + } else { + as = New(awsAuth, aws.USWest2) + } + + groupResp, err := as.DescribeAutoScalingGroups(nil, 10, "") + + if err != nil { + t.Fatal(err) + } + if len(groupResp.AutoScalingGroups) > 0 { + firstGroup := groupResp.AutoScalingGroups[0] + if len(firstGroup.AutoScalingGroupName) > 0 { + t.Logf("Found AutoScaling group %s\n", + firstGroup.AutoScalingGroupName) + } + } + testServer.Flush() +} + +func TestAutoScalingGroup(t *testing.T) { + var as *AutoScaling + // Launch configuration test config + lc := new(LaunchConfiguration) + lc.LaunchConfigurationName = "LConf1" + lc.ImageId = "ami-03e47533" // Octave debian ami + lc.KernelId = "aki-98e26fa8" + lc.KeyName = "testAWS" // Replace with valid key for your account + lc.InstanceType = "m1.small" + + // CreateAutoScalingGroup params test config + asgReq := new(CreateAutoScalingGroupParams) + asgReq.AutoScalingGroupName = "ASGTest1" + asgReq.LaunchConfigurationName = lc.LaunchConfigurationName + asgReq.DefaultCooldown = 300 + asgReq.HealthCheckGracePeriod = 300 + asgReq.DesiredCapacity = 1 + asgReq.MinSize = 1 + asgReq.MaxSize = 5 + asgReq.AvailabilityZones = []string{"us-west-2a"} + + asg := new(AutoScalingGroup) + asg.AutoScalingGroupName = "ASGTest1" + asg.LaunchConfigurationName = lc.LaunchConfigurationName + asg.DefaultCooldown = 300 + asg.HealthCheckGracePeriod = 300 + asg.DesiredCapacity = 1 + asg.MinSize = 1 + asg.MaxSize = 5 + asg.AvailabilityZones = []string{"us-west-2a"} + + awsAuth, err := aws.EnvAuth() + if err != nil { + mockTest = true + t.Log("Running mock tests as AWS environment variables are not set") + awsAuth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + as = New(awsAuth, aws.Region{AutoScalingEndpoint: testServer.URL}) + } else { + as = New(awsAuth, aws.USWest2) + } + + // Create the launch configuration + if mockTest { + testServer.Response(200, nil, CreateLaunchConfigurationResponse) + } + _, err = as.CreateLaunchConfiguration(lc) + if err != nil { + t.Fatal(err) + } + + // Check that we can get the launch configuration details + if mockTest { + testServer.Response(200, nil, DescribeLaunchConfigurationsResponse) + } + _, err = as.DescribeLaunchConfigurations([]string{lc.LaunchConfigurationName}, 10, "") + if err != nil { + t.Fatal(err) + } + + // Create the AutoScalingGroup + if mockTest { + testServer.Response(200, nil, CreateAutoScalingGroupResponse) + } + _, err = as.CreateAutoScalingGroup(asgReq) + if err != nil { + t.Fatal(err) + } + + // Check that we can get the autoscaling group details + if mockTest { + testServer.Response(200, nil, DescribeAutoScalingGroupsResponse) + } + _, err = as.DescribeAutoScalingGroups(nil, 10, "") + if err != nil { + t.Fatal(err) + } + + // Suspend the scaling processes for the test AutoScalingGroup + if mockTest { + testServer.Response(200, nil, SuspendProcessesResponse) + } + _, err = as.SuspendProcesses(asg.AutoScalingGroupName, nil) + if err != nil { + t.Fatal(err) + } + + // Resume scaling processes for the test AutoScalingGroup + if mockTest { + testServer.Response(200, nil, ResumeProcessesResponse) + } + _, err = as.ResumeProcesses(asg.AutoScalingGroupName, nil) + if err != nil { + t.Fatal(err) + } + + // Change the desired capacity from 1 to 2. This will launch a second instance + if mockTest { + testServer.Response(200, nil, SetDesiredCapacityResponse) + } + _, err = as.SetDesiredCapacity(asg.AutoScalingGroupName, 2, false) + if err != nil { + t.Fatal(err) + } + + // Change the desired capacity from 2 to 1. This will terminate one of the instances + if mockTest { + testServer.Response(200, nil, SetDesiredCapacityResponse) + } + + _, err = as.SetDesiredCapacity(asg.AutoScalingGroupName, 1, false) + if err != nil { + t.Fatal(err) + } + + // Update the max capacity for the scaling group + if mockTest { + testServer.Response(200, nil, UpdateAutoScalingGroupResponse) + } + asg.MinSize = 1 + asg.MaxSize = 6 + asg.DesiredCapacity = 1 + _, err = as.UpdateAutoScalingGroup(asg) + if err != nil { + t.Fatal(err) + } + + // Add a scheduled action to the group + psar := new(PutScheduledUpdateGroupActionParams) + psar.AutoScalingGroupName = asg.AutoScalingGroupName + psar.MaxSize = 4 + psar.ScheduledActionName = "SATest1" + psar.Recurrence = "30 0 1 1,6,12 *" + if mockTest { + testServer.Response(200, nil, PutScheduledUpdateGroupActionResponse) + } + _, err = as.PutScheduledUpdateGroupAction(psar) + if err != nil { + t.Fatal(err) + } + + // List the scheduled actions for the group + sar := new(DescribeScheduledActionsParams) + sar.AutoScalingGroupName = asg.AutoScalingGroupName + if mockTest { + testServer.Response(200, nil, DescribeScheduledActionsResponse) + } + _, err = as.DescribeScheduledActions(sar) + if err != nil { + t.Fatal(err) + } + + // Delete the test scheduled action from the group + if mockTest { + testServer.Response(200, nil, DeleteScheduledActionResponse) + } + _, err = as.DeleteScheduledAction(asg.AutoScalingGroupName, psar.ScheduledActionName) + if err != nil { + t.Fatal(err) + } + testServer.Flush() +} + +// -------------------------------------------------------------------------- +// Detailed Unit Tests + +func (s *S) TestAttachInstances(c *C) { + testServer.Response(200, nil, AttachInstancesResponse) + resp, err := s.as.AttachInstances("my-test-asg", []string{"i-21321afs", "i-baaffg23"}) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "AttachInstances") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("InstanceIds.member.1"), Equals, "i-21321afs") + c.Assert(values.Get("InstanceIds.member.2"), Equals, "i-baaffg23") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestCreateAutoScalingGroup(c *C) { + testServer.Response(200, nil, CreateAutoScalingGroupResponse) + testServer.Response(200, nil, DeleteAutoScalingGroupResponse) + + createAS := &CreateAutoScalingGroupParams{ + AutoScalingGroupName: "my-test-asg", + AvailabilityZones: []string{"us-east-1a", "us-east-1b"}, + MinSize: 3, + MaxSize: 3, + DefaultCooldown: 600, + DesiredCapacity: 0, + LaunchConfigurationName: "my-test-lc", + LoadBalancerNames: []string{"elb-1", "elb-2"}, + Tags: []Tag{ + { + Key: "foo", + Value: "bar", + }, + { + Key: "baz", + Value: "qux", + }, + }, + VPCZoneIdentifier: "subnet-610acd08,subnet-530fc83a", + } + resp, err := s.as.CreateAutoScalingGroup(createAS) + c.Assert(err, IsNil) + defer s.as.DeleteAutoScalingGroup(createAS.AutoScalingGroupName, true) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "CreateAutoScalingGroup") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("AvailabilityZones.member.1"), Equals, "us-east-1a") + c.Assert(values.Get("AvailabilityZones.member.2"), Equals, "us-east-1b") + c.Assert(values.Get("MinSize"), Equals, "3") + c.Assert(values.Get("MaxSize"), Equals, "3") + c.Assert(values.Get("DefaultCooldown"), Equals, "600") + c.Assert(values.Get("DesiredCapacity"), Equals, "0") + c.Assert(values.Get("LaunchConfigurationName"), Equals, "my-test-lc") + c.Assert(values.Get("LoadBalancerNames.member.1"), Equals, "elb-1") + c.Assert(values.Get("LoadBalancerNames.member.2"), Equals, "elb-2") + c.Assert(values.Get("Tags.member.1.Key"), Equals, "foo") + c.Assert(values.Get("Tags.member.1.Value"), Equals, "bar") + c.Assert(values.Get("Tags.member.2.Key"), Equals, "baz") + c.Assert(values.Get("Tags.member.2.Value"), Equals, "qux") + c.Assert(values.Get("VPCZoneIdentifier"), Equals, "subnet-610acd08,subnet-530fc83a") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestCreateLaunchConfiguration(c *C) { + testServer.Response(200, nil, CreateLaunchConfigurationResponse) + testServer.Response(200, nil, DeleteLaunchConfigurationResponse) + + launchConfig := &LaunchConfiguration{ + LaunchConfigurationName: "my-test-lc", + AssociatePublicIpAddress: true, + EbsOptimized: true, + SecurityGroups: []string{"sec-grp1", "sec-grp2"}, + UserData: "1234", + KeyName: "secretKeyPair", + ImageId: "ami-0078da69", + InstanceType: "m1.small", + SpotPrice: "0.03", + BlockDeviceMappings: []BlockDeviceMapping{ + { + DeviceName: "/dev/sda1", + VirtualName: "ephemeral0", + }, + { + DeviceName: "/dev/sdb", + VirtualName: "ephemeral1", + }, + { + DeviceName: "/dev/sdf", + Ebs: EBS{ + DeleteOnTermination: true, + SnapshotId: "snap-2a2b3c4d", + VolumeSize: 100, + }, + }, + }, + InstanceMonitoring: InstanceMonitoring{ + Enabled: true, + }, + } + resp, err := s.as.CreateLaunchConfiguration(launchConfig) + c.Assert(err, IsNil) + defer s.as.DeleteLaunchConfiguration(launchConfig.LaunchConfigurationName) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "CreateLaunchConfiguration") + c.Assert(values.Get("LaunchConfigurationName"), Equals, "my-test-lc") + c.Assert(values.Get("AssociatePublicIpAddress"), Equals, "true") + c.Assert(values.Get("EbsOptimized"), Equals, "true") + c.Assert(values.Get("SecurityGroups.member.1"), Equals, "sec-grp1") + c.Assert(values.Get("SecurityGroups.member.2"), Equals, "sec-grp2") + c.Assert(values.Get("UserData"), Equals, "MTIzNA==") + c.Assert(values.Get("KeyName"), Equals, "secretKeyPair") + c.Assert(values.Get("ImageId"), Equals, "ami-0078da69") + c.Assert(values.Get("InstanceType"), Equals, "m1.small") + c.Assert(values.Get("SpotPrice"), Equals, "0.03") + c.Assert(values.Get("BlockDeviceMappings.member.1.DeviceName"), Equals, "/dev/sda1") + c.Assert(values.Get("BlockDeviceMappings.member.1.VirtualName"), Equals, "ephemeral0") + c.Assert(values.Get("BlockDeviceMappings.member.2.DeviceName"), Equals, "/dev/sdb") + c.Assert(values.Get("BlockDeviceMappings.member.2.VirtualName"), Equals, "ephemeral1") + c.Assert(values.Get("BlockDeviceMappings.member.3.DeviceName"), Equals, "/dev/sdf") + c.Assert(values.Get("BlockDeviceMappings.member.3.Ebs.DeleteOnTermination"), Equals, "true") + c.Assert(values.Get("BlockDeviceMappings.member.3.Ebs.SnapshotId"), Equals, "snap-2a2b3c4d") + c.Assert(values.Get("BlockDeviceMappings.member.3.Ebs.VolumeSize"), Equals, "100") + c.Assert(values.Get("InstanceMonitoring.Enabled"), Equals, "true") + c.Assert(resp.RequestId, Equals, "7c6e177f-f082-11e1-ac58-3714bEXAMPLE") +} + +func (s *S) TestCreateOrUpdateTags(c *C) { + testServer.Response(200, nil, CreateOrUpdateTagsResponse) + tags := []Tag{ + { + Key: "foo", + Value: "bar", + ResourceId: "my-test-asg", + }, + { + Key: "baz", + Value: "qux", + ResourceId: "my-test-asg", + PropagateAtLaunch: true, + }, + } + resp, err := s.as.CreateOrUpdateTags(tags) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "CreateOrUpdateTags") + c.Assert(values.Get("Tags.member.1.Key"), Equals, "foo") + c.Assert(values.Get("Tags.member.1.Value"), Equals, "bar") + c.Assert(values.Get("Tags.member.1.ResourceId"), Equals, "my-test-asg") + c.Assert(values.Get("Tags.member.2.Key"), Equals, "baz") + c.Assert(values.Get("Tags.member.2.Value"), Equals, "qux") + c.Assert(values.Get("Tags.member.2.ResourceId"), Equals, "my-test-asg") + c.Assert(values.Get("Tags.member.2.PropagateAtLaunch"), Equals, "true") + c.Assert(resp.RequestId, Equals, "b0203919-bf1b-11e2-8a01-13263EXAMPLE") +} + +func (s *S) TestDeleteAutoScalingGroup(c *C) { + testServer.Response(200, nil, DeleteAutoScalingGroupResponse) + resp, err := s.as.DeleteAutoScalingGroup("my-test-asg", true) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DeleteAutoScalingGroup") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(resp.RequestId, Equals, "70a76d42-9665-11e2-9fdf-211deEXAMPLE") +} + +func (s *S) TestDeleteAutoScalingGroupWithExistingInstances(c *C) { + testServer.Response(400, nil, DeleteAutoScalingGroupErrorResponse) + resp, err := s.as.DeleteAutoScalingGroup("my-test-asg", false) + testServer.WaitRequest() + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + e, ok := err.(*Error) + if !ok { + c.Errorf("Unable to unmarshal error into AWS Autoscaling Error") + } + c.Assert(ok, Equals, true) + c.Assert(e.Message, Equals, "You cannot delete an AutoScalingGroup while there are instances or pending Spot instance request(s) still in the group.") + c.Assert(e.Code, Equals, "ResourceInUse") + c.Assert(e.StatusCode, Equals, 400) + c.Assert(e.RequestId, Equals, "70a76d42-9665-11e2-9fdf-211deEXAMPLE") +} + +func (s *S) TestDeleteLaunchConfiguration(c *C) { + testServer.Response(200, nil, DeleteLaunchConfigurationResponse) + resp, err := s.as.DeleteLaunchConfiguration("my-test-lc") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DeleteLaunchConfiguration") + c.Assert(values.Get("LaunchConfigurationName"), Equals, "my-test-lc") + c.Assert(resp.RequestId, Equals, "7347261f-97df-11e2-8756-35eEXAMPLE") +} + +func (s *S) TestDeleteLaunchConfigurationInUse(c *C) { + testServer.Response(400, nil, DeleteLaunchConfigurationInUseResponse) + resp, err := s.as.DeleteLaunchConfiguration("my-test-lc") + testServer.WaitRequest() + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + e, ok := err.(*Error) + if !ok { + c.Errorf("Unable to unmarshal error into AWS Autoscaling Error") + } + c.Logf("%v %v %v", e.Code, e.Message, e.RequestId) + c.Assert(ok, Equals, true) + c.Assert(e.Message, Equals, "Cannot delete launch configuration my-test-lc because it is attached to AutoScalingGroup test") + c.Assert(e.Code, Equals, "ResourceInUse") + c.Assert(e.StatusCode, Equals, 400) + c.Assert(e.RequestId, Equals, "7347261f-97df-11e2-8756-35eEXAMPLE") +} + +func (s *S) TestDeleteTags(c *C) { + testServer.Response(200, nil, DeleteTagsResponse) + tags := []Tag{ + { + Key: "foo", + Value: "bar", + ResourceId: "my-test-asg", + }, + { + Key: "baz", + Value: "qux", + ResourceId: "my-test-asg", + PropagateAtLaunch: true, + }, + } + resp, err := s.as.DeleteTags(tags) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DeleteTags") + c.Assert(values.Get("Tags.member.1.Key"), Equals, "foo") + c.Assert(values.Get("Tags.member.1.Value"), Equals, "bar") + c.Assert(values.Get("Tags.member.1.ResourceId"), Equals, "my-test-asg") + c.Assert(values.Get("Tags.member.2.Key"), Equals, "baz") + c.Assert(values.Get("Tags.member.2.Value"), Equals, "qux") + c.Assert(values.Get("Tags.member.2.ResourceId"), Equals, "my-test-asg") + c.Assert(values.Get("Tags.member.2.PropagateAtLaunch"), Equals, "true") + c.Assert(resp.RequestId, Equals, "b0203919-bf1b-11e2-8a01-13263EXAMPLE") +} + +func (s *S) TestDescribeAccountLimits(c *C) { + testServer.Response(200, nil, DescribeAccountLimitsResponse) + + resp, err := s.as.DescribeAccountLimits() + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeAccountLimits") + c.Assert(resp.RequestId, Equals, "a32bd184-519d-11e3-a8a4-c1c467cbcc3b") + c.Assert(resp.MaxNumberOfAutoScalingGroups, Equals, 20) + c.Assert(resp.MaxNumberOfLaunchConfigurations, Equals, 100) + +} + +func (s *S) TestDescribeAdjustmentTypes(c *C) { + testServer.Response(200, nil, DescribeAdjustmentTypesResponse) + resp, err := s.as.DescribeAdjustmentTypes() + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeAdjustmentTypes") + c.Assert(resp.RequestId, Equals, "cc5f0337-b694-11e2-afc0-6544dEXAMPLE") + c.Assert(resp.AdjustmentTypes, DeepEquals, []AdjustmentType{{"ChangeInCapacity"}, {"ExactCapacity"}, {"PercentChangeInCapacity"}}) +} + +func (s *S) TestDescribeAutoScalingGroups(c *C) { + testServer.Response(200, nil, DescribeAutoScalingGroupsResponse) + resp, err := s.as.DescribeAutoScalingGroups([]string{"my-test-asg-lbs"}, 0, "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + t, _ := time.Parse(time.RFC3339, "2013-05-06T17:47:15.107Z") + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeAutoScalingGroups") + c.Assert(values.Get("AutoScalingGroupNames.member.1"), Equals, "my-test-asg-lbs") + + expected := &DescribeAutoScalingGroupsResp{ + AutoScalingGroups: []AutoScalingGroup{ + { + AutoScalingGroupName: "my-test-asg-lbs", + Tags: []Tag{ + { + Key: "foo", + Value: "bar", + ResourceId: "my-test-asg-lbs", + PropagateAtLaunch: true, + ResourceType: "auto-scaling-group", + }, + { + Key: "baz", + Value: "qux", + ResourceId: "my-test-asg-lbs", + PropagateAtLaunch: true, + ResourceType: "auto-scaling-group", + }, + }, + Instances: []Instance{ + { + AvailabilityZone: "us-east-1b", + HealthStatus: "Healthy", + InstanceId: "i-zb1f313", + LaunchConfigurationName: "my-test-lc", + LifecycleState: "InService", + }, + { + AvailabilityZone: "us-east-1a", + HealthStatus: "Healthy", + InstanceId: "i-90123adv", + LaunchConfigurationName: "my-test-lc", + LifecycleState: "InService", + }, + }, + HealthCheckType: "ELB", + CreatedTime: t, + LaunchConfigurationName: "my-test-lc", + DesiredCapacity: 2, + AvailabilityZones: []string{"us-east-1b", "us-east-1a"}, + LoadBalancerNames: []string{"my-test-asg-loadbalancer"}, + MinSize: 2, + MaxSize: 10, + VPCZoneIdentifier: "subnet-32131da1,subnet-1312dad2", + HealthCheckGracePeriod: 120, + DefaultCooldown: 300, + AutoScalingGroupARN: "arn:aws:autoscaling:us-east-1:803981987763:autoScalingGroup:ca861182-c8f9-4ca7-b1eb-cd35505f5ebb:autoScalingGroupName/my-test-asg-lbs", + TerminationPolicies: []string{"Default"}, + }, + }, + RequestId: "0f02a07d-b677-11e2-9eb0-dd50EXAMPLE", + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestDescribeAutoScalingInstances(c *C) { + testServer.Response(200, nil, DescribeAutoScalingInstancesResponse) + resp, err := s.as.DescribeAutoScalingInstances([]string{"i-78e0d40b"}, 0, "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeAutoScalingInstances") + c.Assert(resp.RequestId, Equals, "df992dc3-b72f-11e2-81e1-750aa6EXAMPLE") + c.Assert(resp.AutoScalingInstances, DeepEquals, []Instance{ + { + AutoScalingGroupName: "my-test-asg", + AvailabilityZone: "us-east-1a", + HealthStatus: "Healthy", + InstanceId: "i-78e0d40b", + LaunchConfigurationName: "my-test-lc", + LifecycleState: "InService", + }, + }) +} + +func (s *S) TestDescribeLaunchConfigurations(c *C) { + testServer.Response(200, nil, DescribeLaunchConfigurationsResponse) + resp, err := s.as.DescribeLaunchConfigurations([]string{"my-test-lc"}, 0, "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + t, _ := time.Parse(time.RFC3339, "2013-01-21T23:04:42.200Z") + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeLaunchConfigurations") + c.Assert(values.Get("LaunchConfigurationNames.member.1"), Equals, "my-test-lc") + expected := &DescribeLaunchConfigurationsResp{ + LaunchConfigurations: []LaunchConfiguration{ + { + AssociatePublicIpAddress: true, + BlockDeviceMappings: []BlockDeviceMapping{ + { + DeviceName: "/dev/sdb", + VirtualName: "ephemeral0", + }, + { + DeviceName: "/dev/sdf", + Ebs: EBS{ + SnapshotId: "snap-XXXXYYY", + VolumeSize: 100, + Iops: 50, + VolumeType: "io1", + DeleteOnTermination: true, + }, + }, + }, + EbsOptimized: false, + CreatedTime: t, + LaunchConfigurationName: "my-test-lc", + InstanceType: "m1.small", + ImageId: "ami-514ac838", + InstanceMonitoring: InstanceMonitoring{Enabled: true}, + LaunchConfigurationARN: "arn:aws:autoscaling:us-east-1:803981987763:launchConfiguration:9dbbbf87-6141-428a-a409-0752edbe6cad:launchConfigurationName/my-test-lc", + }, + }, + RequestId: "d05a22f8-b690-11e2-bf8e-2113fEXAMPLE", + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestDescribeMetricCollectionTypes(c *C) { + testServer.Response(200, nil, DescribeMetricCollectionTypesResponse) + resp, err := s.as.DescribeMetricCollectionTypes() + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeMetricCollectionTypes") + c.Assert(resp.RequestId, Equals, "07f3fea2-bf3c-11e2-9b6f-f3cdbb80c073") + c.Assert(resp.Metrics, DeepEquals, []MetricCollection{ + { + Metric: "GroupMinSize", + }, + { + Metric: "GroupMaxSize", + }, + { + Metric: "GroupDesiredCapacity", + }, + { + Metric: "GroupInServiceInstances", + }, + { + Metric: "GroupPendingInstances", + }, + { + Metric: "GroupTerminatingInstances", + }, + { + Metric: "GroupTotalInstances", + }, + }) + c.Assert(resp.Granularities, DeepEquals, []MetricGranularity{ + { + Granularity: "1Minute", + }, + }) +} + +func (s *S) TestDescribeNotificationConfigurations(c *C) { + testServer.Response(200, nil, DescribeNotificationConfigurationsResponse) + resp, err := s.as.DescribeNotificationConfigurations([]string{"i-78e0d40b"}, 0, "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeNotificationConfigurations") + c.Assert(resp.RequestId, Equals, "07f3fea2-bf3c-11e2-9b6f-f3cdbb80c073") + c.Assert(resp.NotificationConfigurations, DeepEquals, []NotificationConfiguration{ + { + AutoScalingGroupName: "my-test-asg", + NotificationType: "autoscaling: EC2_INSTANCE_LAUNCH", + TopicARN: "vajdoafj231j41231/topic", + }, + }) +} + +func (s *S) TestDescribePolicies(c *C) { + testServer.Response(200, nil, DescribePoliciesResponse) + resp, err := s.as.DescribePolicies("my-test-asg", []string{}, 2, "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribePolicies") + c.Assert(values.Get("MaxRecords"), Equals, "2") + expected := &DescribePoliciesResp{ + RequestId: "ec3bffad-b739-11e2-b38d-15fbEXAMPLE", + NextToken: "3ef417fe-9202-12-8ddd-d13e1313413", + ScalingPolicies: []ScalingPolicy{ + { + PolicyARN: "arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:c322761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/my-test-asg:policyName/MyScaleDownPolicy", + AdjustmentType: "ChangeInCapacity", + ScalingAdjustment: -1, + PolicyName: "MyScaleDownPolicy", + AutoScalingGroupName: "my-test-asg", + Cooldown: 60, + Alarms: []Alarm{ + { + AlarmName: "TestQueue", + AlarmARN: "arn:aws:cloudwatch:us-east-1:803981987763:alarm:TestQueue", + }, + }, + }, + { + PolicyARN: "arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:c55a5cdd-9be0-435b-b60b-a8dd313159f5:autoScalingGroupName/my-test-asg:policyName/MyScaleUpPolicy", + AdjustmentType: "ChangeInCapacity", + ScalingAdjustment: 1, + PolicyName: "MyScaleUpPolicy", + AutoScalingGroupName: "my-test-asg", + Cooldown: 60, + Alarms: []Alarm{ + { + AlarmName: "TestQueue", + AlarmARN: "arn:aws:cloudwatch:us-east-1:803981987763:alarm:TestQueue", + }, + }, + }, + }, + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestDescribeScalingActivities(c *C) { + testServer.Response(200, nil, DescribeScalingActivitiesResponse) + resp, err := s.as.DescribeScalingActivities("my-test-asg", []string{}, 1, "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeScalingActivities") + c.Assert(values.Get("MaxRecords"), Equals, "1") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + st, _ := time.Parse(time.RFC3339, "2012-04-12T17:32:07.882Z") + et, _ := time.Parse(time.RFC3339, "2012-04-12T17:32:08Z") + expected := &DescribeScalingActivitiesResp{ + RequestId: "7a641adc-84c5-11e1-a8a5-217ebEXAMPLE", + NextToken: "3ef417fe-9202-12-8ddd-d13e1313413", + Activities: []Activity{ + { + StatusCode: "Failed", + Progress: 0, + ActivityId: "063308ae-aa22-4a9b-94f4-9faeEXAMPLE", + StartTime: st, + AutoScalingGroupName: "my-test-asg", + Details: "{}", + Cause: "At 2012-04-12T17:31:30Z a user request created an AutoScalingGroup changing the desired capacity from 0 to 1. At 2012-04-12T17:32:07Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 0 to 1.", + Description: "Launching a new EC2 instance. Status Reason: The image id 'ami-4edb0327' does not exist. Launching EC2 instance failed.", + EndTime: et, + StatusMessage: "The image id 'ami-4edb0327' does not exist. Launching EC2 instance failed.", + }, + }, + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestDescribeScalingProcessTypes(c *C) { + testServer.Response(200, nil, DescribeScalingProcessTypesResponse) + resp, err := s.as.DescribeScalingProcessTypes() + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeScalingProcessTypes") + c.Assert(resp.RequestId, Equals, "27f2eacc-b73f-11e2-ad99-c7aba3a9c963") + c.Assert(resp.Processes, DeepEquals, []ProcessType{ + {"AZRebalance"}, + {"AddToLoadBalancer"}, + {"AlarmNotification"}, + {"HealthCheck"}, + {"Launch"}, + {"ReplaceUnhealthy"}, + {"ScheduledActions"}, + {"Terminate"}, + }) +} + +func (s *S) TestDescribeScheduledActions(c *C) { + testServer.Response(200, nil, DescribeScheduledActionsResponse) + st, _ := time.Parse(time.RFC3339, "2014-06-01T00:30:00Z") + request := &DescribeScheduledActionsParams{ + AutoScalingGroupName: "ASGTest1", + MaxRecords: 1, + StartTime: st, + } + resp, err := s.as.DescribeScheduledActions(request) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeScheduledActions") + c.Assert(resp.RequestId, Equals, "0eb4217f-8421-11e3-9233-7100ef811766") + c.Assert(resp.ScheduledUpdateGroupActions, DeepEquals, []ScheduledUpdateGroupAction{ + { + AutoScalingGroupName: "ASGTest1", + ScheduledActionARN: "arn:aws:autoscaling:us-west-2:193024542802:scheduledUpdateGroupAction:61f68b2c-bde3-4316-9a81-eb95dc246509:autoScalingGroupName/ASGTest1:scheduledActionName/SATest1", + ScheduledActionName: "SATest1", + Recurrence: "30 0 1 1,6,12 *", + MaxSize: 4, + StartTime: st, + Time: st, + }, + }) +} + +func (s *S) TestDescribeTags(c *C) { + testServer.Response(200, nil, DescribeTagsResponse) + filter := NewFilter() + filter.Add("auto-scaling-group", "my-test-asg") + resp, err := s.as.DescribeTags(filter, 1, "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeTags") + c.Assert(values.Get("MaxRecords"), Equals, "1") + c.Assert(values.Get("Filters.member.1.Name"), Equals, "auto-scaling-group") + c.Assert(values.Get("Filters.member.1.Values.member.1"), Equals, "my-test-asg") + c.Assert(resp.RequestId, Equals, "086265fd-bf3e-11e2-85fc-fbb1EXAMPLE") + c.Assert(resp.Tags, DeepEquals, []Tag{ + { + Key: "version", + Value: "1.0", + ResourceId: "my-test-asg", + PropagateAtLaunch: true, + ResourceType: "auto-scaling-group", + }, + }) +} + +func (s *S) TestDescribeTerminationPolicyTypes(c *C) { + testServer.Response(200, nil, DescribeTerminationPolicyTypesResponse) + resp, err := s.as.DescribeTerminationPolicyTypes() + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DescribeTerminationPolicyTypes") + c.Assert(resp.RequestId, Equals, "d9a05827-b735-11e2-a40c-c79a5EXAMPLE") + c.Assert(resp.TerminationPolicyTypes, DeepEquals, []string{"ClosestToNextInstanceHour", "Default", "NewestInstance", "OldestInstance", "OldestLaunchConfiguration"}) +} + +func (s *S) TestDetachInstances(c *C) { + testServer.Response(200, nil, DetachInstancesResponse) + resp, err := s.as.DetachInstances("my-asg", []string{"i-5f2e8a0d"}, true) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DetachInstances") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-asg") + c.Assert(values.Get("ShouldDecrementDesiredCapacity"), Equals, "true") + c.Assert(values.Get("InstanceIds.member.1"), Equals, "i-5f2e8a0d") + st, _ := time.Parse(time.RFC3339, "2014-06-14T00:07:30.280Z") + expected := &DetachInstancesResult{ + RequestId: "e04f3b11-f357-11e3-a434-7f10009d5849", + Activities: []Activity{ + { + StatusCode: "InProgress", + Progress: 50, + ActivityId: "e54ff599-bf05-4076-8b95-a0f090ed90bb", + StartTime: st, + AutoScalingGroupName: "my-asg", + Details: "{\"Availability Zone\":\"us-east-1a\"}", + Cause: "At 2014-06-14T00:07:30Z instance i-5f2e8a0d was detached in response to a user request, shrinking the capacity from 4 to 3.", + Description: "Detaching EC2 instance: i-5f2e8a0d", + }, + }, + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestDisableMetricsCollection(c *C) { + testServer.Response(200, nil, DisableMetricsCollectionResponse) + resp, err := s.as.DisableMetricsCollection("my-test-asg", []string{"GroupMinSize"}) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "DisableMetricsCollection") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("Metrics.member.1"), Equals, "GroupMinSize") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestEnableMetricsCollection(c *C) { + testServer.Response(200, nil, DisableMetricsCollectionResponse) + resp, err := s.as.EnableMetricsCollection("my-test-asg", []string{"GroupMinSize", "GroupMaxSize"}, "1Minute") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "EnableMetricsCollection") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("Granularity"), Equals, "1Minute") + c.Assert(values.Get("Metrics.member.1"), Equals, "GroupMinSize") + c.Assert(values.Get("Metrics.member.2"), Equals, "GroupMaxSize") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestEnterStandby(c *C) { + testServer.Response(200, nil, EnterStandbyResponse) + resp, err := s.as.EnterStandby("my-asg", []string{"i-5b73d709"}, true) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "EnterStandby") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-asg") + c.Assert(values.Get("ShouldDecrementDesiredCapacity"), Equals, "true") + c.Assert(values.Get("InstanceIds.member.1"), Equals, "i-5b73d709") + st, _ := time.Parse(time.RFC3339, "2014-06-13T22:35:50.884Z") + expected := &EnterStandbyResult{ + RequestId: "126f2f31-f34b-11e3-bc51-b35178f0274f", + Activities: []Activity{ + { + StatusCode: "InProgress", + Progress: 50, + ActivityId: "462b4bc3-ad3b-4e67-a58d-96cd00f02f9e", + StartTime: st, + AutoScalingGroupName: "my-asg", + Details: "{\"Availability Zone\":\"us-east-1a\"}", + Cause: "At 2014-06-13T22:35:50Z instance i-5b73d709 was moved to standby in response to a user request, shrinking the capacity from 4 to 3.", + Description: "Moving EC2 instance to Standby: i-5b73d709", + }, + }, + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestExecutePolicy(c *C) { + testServer.Response(200, nil, ExecutePolicyResponse) + resp, err := s.as.ExecutePolicy("my-scaleout-policy", "my-test-asg", true) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "ExecutePolicy") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("PolicyName"), Equals, "my-scaleout-policy") + c.Assert(values.Get("HonorCooldown"), Equals, "true") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestExitStandby(c *C) { + testServer.Response(200, nil, ExitStandbyResponse) + resp, err := s.as.ExitStandby("my-asg", []string{"i-5b73d709"}) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "ExitStandby") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-asg") + c.Assert(values.Get("InstanceIds.member.1"), Equals, "i-5b73d709") + st, _ := time.Parse(time.RFC3339, "2014-06-13T22:43:53.523Z") + expected := &ExitStandbyResult{ + RequestId: "321a11c8-f34c-11e3-a434-7f10009d5849", + Activities: []Activity{ + { + StatusCode: "PreInService", + Progress: 30, + ActivityId: "dca4efcf-eea6-4844-8064-cab1fecd1aa2", + StartTime: st, + AutoScalingGroupName: "my-asg", + Details: "{\"Availability Zone\":\"us-east-1a\"}", + Cause: "At 2014-06-13T22:43:53Z instance i-5b73d709 was moved out of standby in response to a user request, increasing the capacity from 3 to 4.", + Description: "Moving EC2 instance out of Standby: i-5b73d709", + }, + }, + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestPutLifecycleHook(c *C) { + testServer.Response(200, nil, PutLifecycleHookResponse) + request := &PutLifecycleHookParams{ + AutoScalingGroupName: "my-asg", + LifecycleHookName: "ReadyForSoftwareInstall", + LifecycleTransition: "autoscaling:EC2_INSTANCE_LAUNCHING", + NotificationTargetARN: "arn:aws:sqs:us-east-1:896650972448:lifecyclehookqueue", + RoleARN: "arn:aws:iam::896650972448:role/AutoScaling", + } + resp, err := s.as.PutLifecycleHook(request) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "PutLifecycleHook") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-asg") + c.Assert(values.Get("LifecycleHookName"), Equals, "ReadyForSoftwareInstall") + c.Assert(values.Get("RoleARN"), Equals, "arn:aws:iam::896650972448:role/AutoScaling") + c.Assert(values.Get("LifecycleTransition"), Equals, "autoscaling:EC2_INSTANCE_LAUNCHING") + c.Assert(values.Get("NotificationTargetARN"), Equals, "arn:aws:sqs:us-east-1:896650972448:lifecyclehookqueue") + c.Assert(values.Get("DefaultResult"), Equals, "") + c.Assert(values.Get("HeartbeatTimeout"), Equals, "") + c.Assert(values.Get("NotificationMetadata"), Equals, "") + c.Assert(resp.RequestId, Equals, "1952f458-f645-11e3-bc51-b35178f0274f") +} + +func (s *S) TestPutNotificationConfiguration(c *C) { + testServer.Response(200, nil, PutNotificationConfigurationResponse) + resp, err := s.as.PutNotificationConfiguration("my-test-asg", []string{"autoscaling:EC2_INSTANCE_LAUNCH", "autoscaling:EC2_INSTANCE_LAUNCH_ERROR"}, "myTopicARN") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "PutNotificationConfiguration") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("TopicARN"), Equals, "myTopicARN") + c.Assert(values.Get("NotificationTypes.member.1"), Equals, "autoscaling:EC2_INSTANCE_LAUNCH") + c.Assert(values.Get("NotificationTypes.member.2"), Equals, "autoscaling:EC2_INSTANCE_LAUNCH_ERROR") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestPutScalingPolicy(c *C) { + testServer.Response(200, nil, PutScalingPolicyResponse) + request := &PutScalingPolicyParams{ + AutoScalingGroupName: "my-test-asg", + PolicyName: "my-scaleout-policy", + ScalingAdjustment: 30, + AdjustmentType: "PercentChangeInCapacity", + Cooldown: 0, + MinAdjustmentStep: 0, + } + resp, err := s.as.PutScalingPolicy(request) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "PutScalingPolicy") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("PolicyName"), Equals, "my-scaleout-policy") + c.Assert(values.Get("AdjustmentType"), Equals, "PercentChangeInCapacity") + c.Assert(values.Get("ScalingAdjustment"), Equals, "30") + c.Assert(resp.RequestId, Equals, "3cfc6fef-c08b-11e2-a697-2922EXAMPLE") + c.Assert(resp.PolicyARN, Equals, "arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:b0dcf5e8-02e6-4e31-9719-0675d0dc31ae:autoScalingGroupName/my-test-asg:policyName/my-scaleout-policy") +} + +func (s *S) TestPutScheduledUpdateGroupAction(c *C) { + testServer.Response(200, nil, PutScheduledUpdateGroupActionResponse) + st, _ := time.Parse(time.RFC3339, "2013-05-25T08:00:00Z") + request := &PutScheduledUpdateGroupActionParams{ + AutoScalingGroupName: "my-test-asg", + DesiredCapacity: 3, + ScheduledActionName: "ScaleUp", + StartTime: st, + } + resp, err := s.as.PutScheduledUpdateGroupAction(request) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "PutScheduledUpdateGroupAction") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("ScheduledActionName"), Equals, "ScaleUp") + c.Assert(values.Get("DesiredCapacity"), Equals, "3") + c.Assert(values.Get("StartTime"), Equals, "2013-05-25T08:00:00Z") + c.Assert(resp.RequestId, Equals, "3bc8c9bc-6a62-11e2-8a51-4b8a1EXAMPLE") +} + +func (s *S) TestPutScheduledUpdateGroupActionCron(c *C) { + testServer.Response(200, nil, PutScheduledUpdateGroupActionResponse) + st, _ := time.Parse(time.RFC3339, "2013-05-25T08:00:00Z") + request := &PutScheduledUpdateGroupActionParams{ + AutoScalingGroupName: "my-test-asg", + DesiredCapacity: 3, + ScheduledActionName: "scaleup-schedule-year", + StartTime: st, + Recurrence: "30 0 1 1,6,12 *", + } + resp, err := s.as.PutScheduledUpdateGroupAction(request) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "PutScheduledUpdateGroupAction") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("ScheduledActionName"), Equals, "scaleup-schedule-year") + c.Assert(values.Get("DesiredCapacity"), Equals, "3") + c.Assert(values.Get("Recurrence"), Equals, "30 0 1 1,6,12 *") + c.Assert(resp.RequestId, Equals, "3bc8c9bc-6a62-11e2-8a51-4b8a1EXAMPLE") + +} + +func (s *S) TestResumeProcesses(c *C) { + testServer.Response(200, nil, ResumeProcessesResponse) + resp, err := s.as.ResumeProcesses("my-test-asg", []string{"Launch", "Terminate"}) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "ResumeProcesses") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("ScalingProcesses.member.1"), Equals, "Launch") + c.Assert(values.Get("ScalingProcesses.member.2"), Equals, "Terminate") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") + +} + +func (s *S) TestSetDesiredCapacity(c *C) { + testServer.Response(200, nil, SetDesiredCapacityResponse) + resp, err := s.as.SetDesiredCapacity("my-test-asg", 3, true) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "SetDesiredCapacity") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("HonorCooldown"), Equals, "true") + c.Assert(values.Get("DesiredCapacity"), Equals, "3") + c.Assert(resp.RequestId, Equals, "9fb7e2db-6998-11e2-a985-57c82EXAMPLE") +} + +func (s *S) TestSetInstanceHealth(c *C) { + testServer.Response(200, nil, SetInstanceHealthResponse) + resp, err := s.as.SetInstanceHealth("i-baha3121", "Unhealthy", false) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "SetInstanceHealth") + c.Assert(values.Get("HealthStatus"), Equals, "Unhealthy") + c.Assert(values.Get("InstanceId"), Equals, "i-baha3121") + c.Assert(values.Get("ShouldRespectGracePeriod"), Equals, "false") + c.Assert(resp.RequestId, Equals, "9fb7e2db-6998-11e2-a985-57c82EXAMPLE") +} + +func (s *S) TestSuspendProcesses(c *C) { + testServer.Response(200, nil, SuspendProcessesResponse) + resp, err := s.as.SuspendProcesses("my-test-asg", []string{"Launch", "Terminate"}) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "SuspendProcesses") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("ScalingProcesses.member.1"), Equals, "Launch") + c.Assert(values.Get("ScalingProcesses.member.2"), Equals, "Terminate") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestTerminateInstanceInAutoScalingGroup(c *C) { + testServer.Response(200, nil, TerminateInstanceInAutoScalingGroupResponse) + st, _ := time.Parse(time.RFC3339, "2014-01-26T14:08:30.560Z") + resp, err := s.as.TerminateInstanceInAutoScalingGroup("i-br234123", false) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "TerminateInstanceInAutoScalingGroup") + c.Assert(values.Get("InstanceId"), Equals, "i-br234123") + c.Assert(values.Get("ShouldDecrementDesiredCapacity"), Equals, "false") + expected := &TerminateInstanceInAutoScalingGroupResp{ + Activity: Activity{ + ActivityId: "cczc44a87-7d04-dsa15-31-d27c219864c5", + Cause: "At 2014-01-26T14:08:30Z instance i-br234123 was taken out of service in response to a user request.", + Description: "Terminating EC2 instance: i-br234123", + Details: "{\"Availability Zone\":\"us-east-1b\"}", + Progress: 0, + StartTime: st, + StatusCode: "InProgress", + }, + RequestId: "8d798a29-f083-11e1-bdfb-cb223EXAMPLE", + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestUpdateAutoScalingGroup(c *C) { + testServer.Response(200, nil, UpdateAutoScalingGroupResponse) + + asg := &AutoScalingGroup{ + AutoScalingGroupName: "my-test-asg", + AvailabilityZones: []string{"us-east-1a", "us-east-1b"}, + MinSize: 3, + MaxSize: 3, + DefaultCooldown: 600, + DesiredCapacity: 3, + LaunchConfigurationName: "my-test-lc", + VPCZoneIdentifier: "subnet-610acd08,subnet-530fc83a", + } + resp, err := s.as.UpdateAutoScalingGroup(asg) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2011-01-01") + c.Assert(values.Get("Action"), Equals, "UpdateAutoScalingGroup") + c.Assert(values.Get("AutoScalingGroupName"), Equals, "my-test-asg") + c.Assert(values.Get("AvailabilityZones.member.1"), Equals, "us-east-1a") + c.Assert(values.Get("AvailabilityZones.member.2"), Equals, "us-east-1b") + c.Assert(values.Get("MinSize"), Equals, "3") + c.Assert(values.Get("MaxSize"), Equals, "3") + c.Assert(values.Get("DefaultCooldown"), Equals, "600") + c.Assert(values.Get("DesiredCapacity"), Equals, "3") + c.Assert(values.Get("LaunchConfigurationName"), Equals, "my-test-lc") + c.Assert(values.Get("VPCZoneIdentifier"), Equals, "subnet-610acd08,subnet-530fc83a") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} diff --git a/vendor/github.com/goamz/goamz/autoscaling/responses_test.go b/vendor/github.com/goamz/goamz/autoscaling/responses_test.go new file mode 100644 index 000000000..935231aa2 --- /dev/null +++ b/vendor/github.com/goamz/goamz/autoscaling/responses_test.go @@ -0,0 +1,627 @@ +package autoscaling + +var AttachInstancesResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` +var BasicGroupResponse = ` + + + + + + 08c3bedc-8421-11e3-9bb5-bfa219b29cce + + +` +var CreateAutoScalingGroupResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` +var CreateLaunchConfigurationResponse = ` + + + 7c6e177f-f082-11e1-ac58-3714bEXAMPLE + + +` +var CreateOrUpdateTagsResponse = ` + + + b0203919-bf1b-11e2-8a01-13263EXAMPLE + + +` +var DeleteAutoScalingGroupResponse = ` + + + 70a76d42-9665-11e2-9fdf-211deEXAMPLE + + +` +var DeleteAutoScalingGroupErrorResponse = ` + + + Sender + ResourceInUse + You cannot delete an AutoScalingGroup while there are instances or pending Spot instance request(s) still in the group. + + 70a76d42-9665-11e2-9fdf-211deEXAMPLE + +` +var DeleteLaunchConfigurationResponse = ` + + + 7347261f-97df-11e2-8756-35eEXAMPLE + + +` +var DeleteLaunchConfigurationInUseResponse = ` + + + Sender + ResourceInUse + Cannot delete launch configuration my-test-lc because it is attached to AutoScalingGroup test + + 7347261f-97df-11e2-8756-35eEXAMPLE + +` +var DeleteScheduledActionResponse = ` + + + 0f38bb02-8421-11e3-9bb5-bfa219b29cce + + +` +var DeleteTagsResponse = ` + + + b0203919-bf1b-11e2-8a01-13263EXAMPLE + + +` +var DescribeAccountLimitsResponse = ` + + + 100 + 20 + + + a32bd184-519d-11e3-a8a4-c1c467cbcc3b + + +` +var DescribeAdjustmentTypesResponse = ` + + + + + ChangeInCapacity + + + ExactCapacity + + + PercentChangeInCapacity + + + + + cc5f0337-b694-11e2-afc0-6544dEXAMPLE + + +` +var DescribeAutoScalingGroupsResponse = ` + + + + + + + my-test-asg-lbs + true + bar + foo + auto-scaling-group + + + my-test-asg-lbs + true + qux + baz + auto-scaling-group + + + + my-test-asg-lbs + ELB + 2013-05-06T17:47:15.107Z + + my-test-lc + + + Healthy + us-east-1b + i-zb1f313 + my-test-lc + InService + + + Healthy + us-east-1a + i-90123adv + my-test-lc + InService + + + 2 + + us-east-1b + us-east-1a + + + my-test-asg-loadbalancer + + 2 + subnet-32131da1,subnet-1312dad2 + 120 + 300 + arn:aws:autoscaling:us-east-1:803981987763:autoScalingGroup:ca861182-c8f9-4ca7-b1eb-cd35505f5ebb:autoScalingGroupName/my-test-asg-lbs + + Default + + 10 + + + + + 0f02a07d-b677-11e2-9eb0-dd50EXAMPLE + + +` +var DescribeAutoScalingInstancesResponse = ` + + + + + Healthy + my-test-asg + us-east-1a + i-78e0d40b + my-test-lc + InService + + + + + df992dc3-b72f-11e2-81e1-750aa6EXAMPLE + + +` +var DescribeLaunchConfigurationsResponse = ` + + + + + true + + 2013-01-21T23:04:42.200Z + + my-test-lc + + m1.small + arn:aws:autoscaling:us-east-1:803981987763:launchConfiguration:9dbbbf87-6141-428a-a409-0752edbe6cad:launchConfigurationName/my-test-lc + + + ephemeral0 + /dev/sdb + + + + snap-XXXXYYY + 100 + 50 + io1 + true + + /dev/sdf + + + ami-514ac838 + + + + true + + false + + + + + d05a22f8-b690-11e2-bf8e-2113fEXAMPLE + + +` +var DescribeMetricCollectionTypesResponse = ` + + + + + GroupMinSize + + + GroupMaxSize + + + GroupDesiredCapacity + + + GroupInServiceInstances + + + GroupPendingInstances + + + GroupTerminatingInstances + + + GroupTotalInstances + + + + + 1Minute + + + + + 07f3fea2-bf3c-11e2-9b6f-f3cdbb80c073 + + +` +var DescribeNotificationConfigurationsResponse = ` + + + + + my-test-asg + autoscaling: EC2_INSTANCE_LAUNCH + vajdoafj231j41231/topic + + + + + 07f3fea2-bf3c-11e2-9b6f-f3cdbb80c073 + + +` +var DescribePoliciesResponse = ` + + + 3ef417fe-9202-12-8ddd-d13e1313413 + + + arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:c322761b-3172-4d56-9a21-0ed9d6161d67:autoScalingGroupName/my-test-asg:policyName/MyScaleDownPolicy + ChangeInCapacity + -1 + MyScaleDownPolicy + my-test-asg + 60 + + + TestQueue + arn:aws:cloudwatch:us-east-1:803981987763:alarm:TestQueue + + + + + arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:c55a5cdd-9be0-435b-b60b-a8dd313159f5:autoScalingGroupName/my-test-asg:policyName/MyScaleUpPolicy + ChangeInCapacity + 1 + MyScaleUpPolicy + my-test-asg + 60 + + + TestQueue + arn:aws:cloudwatch:us-east-1:803981987763:alarm:TestQueue + + + + + + + ec3bffad-b739-11e2-b38d-15fbEXAMPLE + + +` +var DescribeScalingActivitiesResponse = ` + + + 3ef417fe-9202-12-8ddd-d13e1313413 + + + Failed + 0 + 063308ae-aa22-4a9b-94f4-9faeEXAMPLE + 2012-04-12T17:32:07.882Z + my-test-asg + At 2012-04-12T17:31:30Z a user request created an AutoScalingGroup changing the desired capacity from 0 to 1. At 2012-04-12T17:32:07Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 0 to 1. +
{}
+ Launching a new EC2 instance. Status Reason: The image id 'ami-4edb0327' does not exist. Launching EC2 instance failed. + 2012-04-12T17:32:08Z + The image id 'ami-4edb0327' does not exist. Launching EC2 instance failed. +
+
+
+ + 7a641adc-84c5-11e1-a8a5-217ebEXAMPLE + +
+` +var DescribeScalingProcessTypesResponse = ` + + + + + AZRebalance + + + AddToLoadBalancer + + + AlarmNotification + + + HealthCheck + + + Launch + + + ReplaceUnhealthy + + + ScheduledActions + + + Terminate + + + + + 27f2eacc-b73f-11e2-ad99-c7aba3a9c963 + + +` +var DescribeScheduledActionsResponse = ` + + + + + SATest1 + 2014-06-01T00:30:00Z + + arn:aws:autoscaling:us-west-2:193024542802:scheduledUpdateGroupAction:61f68b2c-bde3-4316-9a81-eb95dc246509:autoScalingGroupName/ASGTest1:scheduledActionName/SATest1 + ASGTest1 + 30 0 1 1,6,12 * + 4 + + + + + 0eb4217f-8421-11e3-9233-7100ef811766 + + +` + +var DescribeTerminationPolicyTypesResponse = ` + + + + ClosestToNextInstanceHour + Default + NewestInstance + OldestInstance + OldestLaunchConfiguration + + + + d9a05827-b735-11e2-a40c-c79a5EXAMPLE + + +` +var DescribeTagsResponse = ` + + + + + my-test-asg + true + 1.0 + version + auto-scaling-group + + + + + 086265fd-bf3e-11e2-85fc-fbb1EXAMPLE + + +` +var DetachInstancesResponse = ` + + + + + e54ff599-bf05-4076-8b95-a0f090ed90bb + 50 + InProgress + 2014-06-14T00:07:30.280Z + At 2014-06-14T00:07:30Z instance i-5f2e8a0d was detached in response to a user request, shrinking the capacity from 4 to 3. + my-asg +
{"Availability Zone":"us-east-1a"}
+ Detaching EC2 instance: i-5f2e8a0d +
+
+
+ + e04f3b11-f357-11e3-a434-7f10009d5849 + +
+` +var DisableMetricsCollectionResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` +var EnableMetricsCollectionResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` +var EnterStandbyResponse = ` + + + + + 462b4bc3-ad3b-4e67-a58d-96cd00f02f9e + 50 + InProgress + 2014-06-13T22:35:50.884Z + At 2014-06-13T22:35:50Z instance i-5b73d709 was moved to standby in response to a user request, shrinking the capacity from 4 to 3. + my-asg +
{"Availability Zone":"us-east-1a"}
+ Moving EC2 instance to Standby: i-5b73d709 +
+
+
+ + 126f2f31-f34b-11e3-bc51-b35178f0274f + +
+` +var ExecutePolicyResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` +var ExitStandbyResponse = ` + + + + + dca4efcf-eea6-4844-8064-cab1fecd1aa2 + 30 + PreInService + 2014-06-13T22:43:53.523Z + At 2014-06-13T22:43:53Z instance i-5b73d709 was moved out of standby in response to a user request, increasing the capacity from 3 to 4. + my-asg +
{"Availability Zone":"us-east-1a"}
+ Moving EC2 instance out of Standby: i-5b73d709 +
+
+
+ + 321a11c8-f34c-11e3-a434-7f10009d5849 + +
+` +var PutLifecycleHookResponse = ` + + + + 1952f458-f645-11e3-bc51-b35178f0274f + + +` +var PutNotificationConfigurationResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` +var PutScalingPolicyResponse = ` + + + arn:aws:autoscaling:us-east-1:803981987763:scalingPolicy:b0dcf5e8-02e6-4e31-9719-0675d0dc31ae:autoScalingGroupName/my-test-asg:policyName/my-scaleout-policy + + + 3cfc6fef-c08b-11e2-a697-2922EXAMPLE + + +` +var PutScheduledUpdateGroupActionResponse = ` + + + 3bc8c9bc-6a62-11e2-8a51-4b8a1EXAMPLE + + + ` +var ResumeProcessesResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` +var SetDesiredCapacityResponse = ` + + + 9fb7e2db-6998-11e2-a985-57c82EXAMPLE + + +` +var SetInstanceHealthResponse = ` + + + 9fb7e2db-6998-11e2-a985-57c82EXAMPLE + + +` +var SuspendProcessesResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` +var TerminateInstanceInAutoScalingGroupResponse = ` + + + + InProgress + cczc44a87-7d04-dsa15-31-d27c219864c5 + 0 + 2014-01-26T14:08:30.560Z + At 2014-01-26T14:08:30Z instance i-br234123 was taken out of service in response to a user request. +
{"Availability Zone":"us-east-1b"}
+ Terminating EC2 instance: i-br234123 +
+
+ + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + +
+` +var UpdateAutoScalingGroupResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` diff --git a/vendor/github.com/goamz/goamz/aws/attempt_test.go b/vendor/github.com/goamz/goamz/aws/attempt_test.go new file mode 100644 index 000000000..8ba497715 --- /dev/null +++ b/vendor/github.com/goamz/goamz/aws/attempt_test.go @@ -0,0 +1,58 @@ +package aws_test + +import ( + "time" + + "github.com/goamz/goamz/aws" + . "gopkg.in/check.v1" +) + +func (S) TestAttemptTiming(c *C) { + testAttempt := aws.AttemptStrategy{ + Total: 0.25e9, + Delay: 0.1e9, + } + want := []time.Duration{0, 0.1e9, 0.2e9, 0.2e9} + got := make([]time.Duration, 0, len(want)) // avoid allocation when testing timing + t0 := time.Now() + for a := testAttempt.Start(); a.Next(); { + got = append(got, time.Now().Sub(t0)) + } + got = append(got, time.Now().Sub(t0)) + c.Assert(got, HasLen, len(want)) + const margin = 0.01e9 + for i, got := range want { + lo := want[i] - margin + hi := want[i] + margin + if got < lo || got > hi { + c.Errorf("attempt %d want %g got %g", i, want[i].Seconds(), got.Seconds()) + } + } +} + +func (S) TestAttemptNextHasNext(c *C) { + a := aws.AttemptStrategy{}.Start() + c.Assert(a.Next(), Equals, true) + c.Assert(a.Next(), Equals, false) + + a = aws.AttemptStrategy{}.Start() + c.Assert(a.Next(), Equals, true) + c.Assert(a.HasNext(), Equals, false) + c.Assert(a.Next(), Equals, false) + + a = aws.AttemptStrategy{Total: 2e8}.Start() + c.Assert(a.Next(), Equals, true) + c.Assert(a.HasNext(), Equals, true) + time.Sleep(2e8) + c.Assert(a.HasNext(), Equals, true) + c.Assert(a.Next(), Equals, true) + c.Assert(a.Next(), Equals, false) + + a = aws.AttemptStrategy{Total: 1e8, Min: 2}.Start() + time.Sleep(1e8) + c.Assert(a.Next(), Equals, true) + c.Assert(a.HasNext(), Equals, true) + c.Assert(a.Next(), Equals, true) + c.Assert(a.HasNext(), Equals, false) + c.Assert(a.Next(), Equals, false) +} diff --git a/vendor/github.com/goamz/goamz/aws/aws_test.go b/vendor/github.com/goamz/goamz/aws/aws_test.go new file mode 100644 index 000000000..0c74a7905 --- /dev/null +++ b/vendor/github.com/goamz/goamz/aws/aws_test.go @@ -0,0 +1,211 @@ +package aws_test + +import ( + "io/ioutil" + "os" + "strings" + "testing" + "time" + + "github.com/goamz/goamz/aws" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + environ []string +} + +func (s *S) SetUpSuite(c *C) { + s.environ = os.Environ() +} + +func (s *S) TearDownTest(c *C) { + os.Clearenv() + for _, kv := range s.environ { + l := strings.SplitN(kv, "=", 2) + os.Setenv(l[0], l[1]) + } +} + +func (s *S) TestSharedAuthNoHome(c *C) { + os.Clearenv() + os.Setenv("AWS_PROFILE", "foo") + _, err := aws.SharedAuth() + c.Assert(err, ErrorMatches, "Could not get HOME") +} + +func (s *S) TestSharedAuthNoCredentialsFile(c *C) { + os.Clearenv() + os.Setenv("AWS_PROFILE", "foo") + os.Setenv("HOME", "/tmp") + _, err := aws.SharedAuth() + c.Assert(err, ErrorMatches, "Couldn't parse AWS credentials file") +} + +func (s *S) TestSharedAuthNoProfileInFile(c *C) { + os.Clearenv() + os.Setenv("AWS_PROFILE", "foo") + + d, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(d) + + err = os.Mkdir(d+"/.aws", 0755) + if err != nil { + panic(err) + } + + ioutil.WriteFile(d+"/.aws/credentials", []byte("[bar]\n"), 0644) + os.Setenv("HOME", d) + + _, err = aws.SharedAuth() + c.Assert(err, ErrorMatches, "Couldn't find profile in AWS credentials file") +} + +func (s *S) TestSharedAuthNoKeysInProfile(c *C) { + os.Clearenv() + os.Setenv("AWS_PROFILE", "bar") + + d, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(d) + + err = os.Mkdir(d+"/.aws", 0755) + if err != nil { + panic(err) + } + + ioutil.WriteFile(d+"/.aws/credentials", []byte("[bar]\nawsaccesskeyid = AK.."), 0644) + os.Setenv("HOME", d) + + _, err = aws.SharedAuth() + c.Assert(err, ErrorMatches, "AWS_SECRET_ACCESS_KEY not found in credentials file") +} + +func (s *S) TestSharedAuthDefaultCredentials(c *C) { + os.Clearenv() + + d, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(d) + + err = os.Mkdir(d+"/.aws", 0755) + if err != nil { + panic(err) + } + + ioutil.WriteFile(d+"/.aws/credentials", []byte("[default]\naws_access_key_id = access\naws_secret_access_key = secret\n"), 0644) + os.Setenv("HOME", d) + + auth, err := aws.SharedAuth() + c.Assert(err, IsNil) + c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"}) +} + +func (s *S) TestSharedAuth(c *C) { + os.Clearenv() + os.Setenv("AWS_PROFILE", "bar") + + d, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(d) + + err = os.Mkdir(d+"/.aws", 0755) + if err != nil { + panic(err) + } + + ioutil.WriteFile(d+"/.aws/credentials", []byte("[bar]\naws_access_key_id = access\naws_secret_access_key = secret\n"), 0644) + os.Setenv("HOME", d) + + auth, err := aws.SharedAuth() + c.Assert(err, IsNil) + c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"}) +} + +func (s *S) TestEnvAuthNoSecret(c *C) { + os.Clearenv() + _, err := aws.EnvAuth() + c.Assert(err, ErrorMatches, "AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment") +} + +func (s *S) TestEnvAuthNoAccess(c *C) { + os.Clearenv() + os.Setenv("AWS_SECRET_ACCESS_KEY", "foo") + _, err := aws.EnvAuth() + c.Assert(err, ErrorMatches, "AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment") +} + +func (s *S) TestEnvAuth(c *C) { + os.Clearenv() + os.Setenv("AWS_SECRET_ACCESS_KEY", "secret") + os.Setenv("AWS_ACCESS_KEY_ID", "access") + auth, err := aws.EnvAuth() + c.Assert(err, IsNil) + c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"}) +} + +func (s *S) TestEnvAuthAlt(c *C) { + os.Clearenv() + os.Setenv("AWS_SECRET_KEY", "secret") + os.Setenv("AWS_ACCESS_KEY", "access") + auth, err := aws.EnvAuth() + c.Assert(err, IsNil) + c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"}) +} + +func (s *S) TestEnvAuthToken(c *C) { + os.Clearenv() + os.Setenv("AWS_SECRET_KEY", "secret") + os.Setenv("AWS_ACCESS_KEY", "access") + os.Setenv("AWS_SESSION_TOKEN", "token") + auth, err := aws.EnvAuth() + c.Assert(err, IsNil) + c.Assert(auth.SecretKey, Equals, "secret") + c.Assert(auth.AccessKey, Equals, "access") + c.Assert(auth.Token(), Equals, "token") +} + +func (s *S) TestGetAuthStatic(c *C) { + exptdate := time.Now().Add(time.Hour) + auth, err := aws.GetAuth("access", "secret", "token", exptdate) + c.Assert(err, IsNil) + c.Assert(auth.AccessKey, Equals, "access") + c.Assert(auth.SecretKey, Equals, "secret") + c.Assert(auth.Token(), Equals, "token") + c.Assert(auth.Expiration(), Equals, exptdate) +} + +func (s *S) TestGetAuthEnv(c *C) { + os.Clearenv() + os.Setenv("AWS_SECRET_ACCESS_KEY", "secret") + os.Setenv("AWS_ACCESS_KEY_ID", "access") + auth, err := aws.GetAuth("", "", "", time.Time{}) + c.Assert(err, IsNil) + c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"}) +} + +func (s *S) TestEncode(c *C) { + c.Assert(aws.Encode("foo"), Equals, "foo") + c.Assert(aws.Encode("/"), Equals, "%2F") +} + +func (s *S) TestRegionsAreNamed(c *C) { + for n, r := range aws.Regions { + c.Assert(n, Equals, r.Name) + } +} diff --git a/vendor/github.com/goamz/goamz/aws/client_test.go b/vendor/github.com/goamz/goamz/aws/client_test.go new file mode 100644 index 000000000..c66a86333 --- /dev/null +++ b/vendor/github.com/goamz/goamz/aws/client_test.go @@ -0,0 +1,121 @@ +package aws_test + +import ( + "fmt" + "github.com/goamz/goamz/aws" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" +) + +// Retrieve the response from handler using aws.RetryingClient +func serveAndGet(handler http.HandlerFunc) (body string, err error) { + ts := httptest.NewServer(handler) + defer ts.Close() + resp, err := aws.RetryingClient.Get(ts.URL) + if err != nil { + return + } + if resp.StatusCode != 200 { + return "", fmt.Errorf("Bad status code: %d", resp.StatusCode) + } + greeting, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return + } + return strings.TrimSpace(string(greeting)), nil +} + +func TestClient_expected(t *testing.T) { + body := "foo bar" + + resp, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, body) + }) + if err != nil { + t.Fatal(err) + } + if resp != body { + t.Fatal("Body not as expected.") + } +} + +func TestClient_delay(t *testing.T) { + body := "baz" + wait := 4 + resp, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) { + if wait < 0 { + // If we dipped to zero delay and still failed. + t.Fatal("Never succeeded.") + } + wait -= 1 + time.Sleep(time.Second * time.Duration(wait)) + fmt.Fprintln(w, body) + }) + if err != nil { + t.Fatal(err) + } + if resp != body { + t.Fatal("Body not as expected.", resp) + } +} + +func TestClient_no4xxRetry(t *testing.T) { + tries := 0 + + // Fail once before succeeding. + _, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) { + tries += 1 + http.Error(w, "error", 404) + }) + + if err == nil { + t.Fatal("should have error") + } + + if tries != 1 { + t.Fatalf("should only try once: %d", tries) + } +} + +func TestClient_retries(t *testing.T) { + body := "biz" + failed := false + // Fail once before succeeding. + resp, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) { + if !failed { + http.Error(w, "error", 500) + failed = true + } else { + fmt.Fprintln(w, body) + } + }) + if failed != true { + t.Error("We didn't retry!") + } + if err != nil { + t.Fatal(err) + } + if resp != body { + t.Fatal("Body not as expected.") + } +} + +func TestClient_fails(t *testing.T) { + tries := 0 + // Fail 3 times and return the last error. + _, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) { + tries += 1 + http.Error(w, "error", 500) + }) + if err == nil { + t.Fatal(err) + } + if tries != 3 { + t.Fatal("Didn't retry enough") + } +} diff --git a/vendor/github.com/goamz/goamz/aws/export_test.go b/vendor/github.com/goamz/goamz/aws/export_test.go new file mode 100644 index 000000000..c4aca1d72 --- /dev/null +++ b/vendor/github.com/goamz/goamz/aws/export_test.go @@ -0,0 +1,29 @@ +package aws + +import ( + "net/http" + "time" +) + +// V4Signer: +// Exporting methods for testing + +func (s *V4Signer) RequestTime(req *http.Request) time.Time { + return s.requestTime(req) +} + +func (s *V4Signer) CanonicalRequest(req *http.Request) string { + return s.canonicalRequest(req) +} + +func (s *V4Signer) StringToSign(t time.Time, creq string) string { + return s.stringToSign(t, creq) +} + +func (s *V4Signer) Signature(t time.Time, sts string) string { + return s.signature(t, sts) +} + +func (s *V4Signer) Authorization(header http.Header, t time.Time, signature string) string { + return s.authorization(header, t, signature) +} diff --git a/vendor/github.com/goamz/goamz/aws/sign_test.go b/vendor/github.com/goamz/goamz/aws/sign_test.go new file mode 100644 index 000000000..4ce2ff9a0 --- /dev/null +++ b/vendor/github.com/goamz/goamz/aws/sign_test.go @@ -0,0 +1,540 @@ +package aws_test + +import ( + "fmt" + "net/http" + "strings" + "time" + + "github.com/goamz/goamz/aws" + . "gopkg.in/check.v1" +) + +var _ = Suite(&V4SignerSuite{}) + +type V4SignerSuite struct { + auth aws.Auth + region aws.Region + cases []V4SignerSuiteCase +} + +type V4SignerSuiteCase struct { + label string + request V4SignerSuiteCaseRequest + canonicalRequest string + stringToSign string + signature string + authorization string +} + +type V4SignerSuiteCaseRequest struct { + method string + host string + url string + headers []string + body string +} + +func (s *V4SignerSuite) SetUpSuite(c *C) { + s.auth = aws.Auth{AccessKey: "AKIDEXAMPLE", SecretKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"} + s.region = aws.USEast + + // Test cases from the Signature Version 4 Test Suite (http://goo.gl/nguvs0) + s.cases = append(s.cases, + + // get-header-key-duplicate + V4SignerSuiteCase{ + label: "get-header-key-duplicate", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/", + headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "ZOO:zoobar", "zoo:foobar", "zoo:zoobar"}, + }, + canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\nzoo:foobar,zoobar,zoobar\n\ndate;host;zoo\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n3c52f0eaae2b61329c0a332e3fa15842a37bc5812cf4d80eb64784308850e313", + signature: "54afcaaf45b331f81cd2edb974f7b824ff4dd594cbbaa945ed636b48477368ed", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=54afcaaf45b331f81cd2edb974f7b824ff4dd594cbbaa945ed636b48477368ed", + }, + + // get-header-value-order + V4SignerSuiteCase{ + label: "get-header-value-order", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/", + headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "p:z", "p:a", "p:p", "p:a"}, + }, + canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\np:a,a,p,z\n\ndate;host;p\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n94c0389fefe0988cbbedc8606f0ca0b485b48da010d09fc844b45b697c8924fe", + signature: "d2973954263943b11624a11d1c963ca81fb274169c7868b2858c04f083199e3d", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;p, Signature=d2973954263943b11624a11d1c963ca81fb274169c7868b2858c04f083199e3d", + }, + + // get-header-value-trim + V4SignerSuiteCase{ + label: "get-header-value-trim", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/", + headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "p: phfft "}, + }, + canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\np:phfft\n\ndate;host;p\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ndddd1902add08da1ac94782b05f9278c08dc7468db178a84f8950d93b30b1f35", + signature: "debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;p, Signature=debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592", + }, + + // get-relative-relative + V4SignerSuiteCase{ + label: "get-relative-relative", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/foo/bar/../..", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1", + signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + }, + + // get-relative + V4SignerSuiteCase{ + label: "get-relative", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/foo/..", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1", + signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + }, + + // get-slash-dot-slash + V4SignerSuiteCase{ + label: "get-slash-dot-slash", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/./", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1", + signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + }, + + // get-slash-pointless-dot + V4SignerSuiteCase{ + label: "get-slash-pointless-dot", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/./foo", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/foo\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n8021a97572ee460f87ca67f4e8c0db763216d84715f5424a843a5312a3321e2d", + signature: "910e4d6c9abafaf87898e1eb4c929135782ea25bb0279703146455745391e63a", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=910e4d6c9abafaf87898e1eb4c929135782ea25bb0279703146455745391e63a", + }, + + // get-slash + V4SignerSuiteCase{ + label: "get-slash", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "//", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1", + signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + }, + + // get-slashes + V4SignerSuiteCase{ + label: "get-slashes", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "//foo//", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/foo/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n6bb4476ee8745730c9cb79f33a0c70baa6d8af29c0077fa12e4e8f1dd17e7098", + signature: "b00392262853cfe3201e47ccf945601079e9b8a7f51ee4c3d9ee4f187aa9bf19", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b00392262853cfe3201e47ccf945601079e9b8a7f51ee4c3d9ee4f187aa9bf19", + }, + + // get-space + V4SignerSuiteCase{ + label: "get-space", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/%20/foo", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/%20/foo\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n69c45fb9fe3fd76442b5086e50b2e9fec8298358da957b293ef26e506fdfb54b", + signature: "f309cfbd10197a230c42dd17dbf5cca8a0722564cb40a872d25623cfa758e374", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=f309cfbd10197a230c42dd17dbf5cca8a0722564cb40a872d25623cfa758e374", + }, + + // get-unreserved + V4SignerSuiteCase{ + label: "get-unreserved", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ndf63ee3247c0356c696a3b21f8d8490b01fa9cd5bc6550ef5ef5f4636b7b8901", + signature: "830cc36d03f0f84e6ee4953fbe701c1c8b71a0372c63af9255aa364dd183281e", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=830cc36d03f0f84e6ee4953fbe701c1c8b71a0372c63af9255aa364dd183281e", + }, + + // get-utf8 + V4SignerSuiteCase{ + label: "get-utf8", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/%E1%88%B4", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/%E1%88%B4\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n27ba31df5dbc6e063d8f87d62eb07143f7f271c5330a917840586ac1c85b6f6b", + signature: "8d6634c189aa8c75c2e51e106b6b5121bed103fdb351f7d7d4381c738823af74", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=8d6634c189aa8c75c2e51e106b6b5121bed103fdb351f7d7d4381c738823af74", + }, + + // get-vanilla-empty-query-key + V4SignerSuiteCase{ + label: "get-vanilla-empty-query-key", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/?foo=bar", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\nfoo=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n0846c2945b0832deb7a463c66af5c4f8bd54ec28c438e67a214445b157c9ddf8", + signature: "56c054473fd260c13e4e7393eb203662195f5d4a1fada5314b8b52b23f985e9f", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=56c054473fd260c13e4e7393eb203662195f5d4a1fada5314b8b52b23f985e9f", + }, + + // get-vanilla-space-query-parameters + V4SignerSuiteCase{ + label: "get-vanilla-space-query-parameters", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/?foo foo=bar bar", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\nfoo%20foo=bar%20bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n6a81658ae0a0a4f73aa72ca7d3b01c9cb0c5d4099f7a5ee897f5a571bfe6f7ff", + signature: "c8556bc676129d0806ea8fe6ef182dc095666f4c1582c3d9751d47e4306037c9", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=c8556bc676129d0806ea8fe6ef182dc095666f4c1582c3d9751d47e4306037c9", + }, + + // get-vanilla-query-order-key-case + V4SignerSuiteCase{ + label: "get-vanilla-query-order-key-case", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/?foo=Zoo&foo=aha", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\nfoo=Zoo&foo=aha\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ne25f777ba161a0f1baf778a87faf057187cf5987f17953320e3ca399feb5f00d", + signature: "be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09", + }, + + // get-vanilla-query-order-key + V4SignerSuiteCase{ + label: "get-vanilla-query-order-key", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/?a=foo&b=foo", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\na=foo&b=foo\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n2f23d14fe13caebf6dfda346285c6d9c14f49eaca8f5ec55c627dd7404f7a727", + signature: "0dc122f3b28b831ab48ba65cb47300de53fbe91b577fe113edac383730254a3b", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=0dc122f3b28b831ab48ba65cb47300de53fbe91b577fe113edac383730254a3b", + }, + + // get-vanilla-query-order-value + V4SignerSuiteCase{ + label: "get-vanilla-query-order-value", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/?foo=b&foo=a", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\nfoo=a&foo=b\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n33dffc220e89131f8f6157a35c40903daa658608d9129ff9489e5cf5bbd9b11b", + signature: "feb926e49e382bec75c9d7dcb2a1b6dc8aa50ca43c25d2bc51143768c0875acc", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=feb926e49e382bec75c9d7dcb2a1b6dc8aa50ca43c25d2bc51143768c0875acc", + }, + + // get-vanilla-query-unreserved + V4SignerSuiteCase{ + label: "get-vanilla-query-unreserved", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\n-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\nd2578f3156d4c9d180713d1ff20601d8a3eed0dd35447d24603d7d67414bd6b5", + signature: "f1498ddb4d6dae767d97c466fb92f1b59a2c71ca29ac954692663f9db03426fb", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=f1498ddb4d6dae767d97c466fb92f1b59a2c71ca29ac954692663f9db03426fb", + }, + + // get-vanilla-query + V4SignerSuiteCase{ + label: "get-vanilla-query", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1", + signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + }, + + // get-vanilla-ut8-query + V4SignerSuiteCase{ + label: "get-vanilla-ut8-query", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/?ሴ=bar", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\n%E1%88%B4=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\nde5065ff39c131e6c2e2bd19cd9345a794bf3b561eab20b8d97b2093fc2a979e", + signature: "6fb359e9a05394cc7074e0feb42573a2601abc0c869a953e8c5c12e4e01f1a8c", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=6fb359e9a05394cc7074e0feb42573a2601abc0c869a953e8c5c12e4e01f1a8c", + }, + + // get-vanilla + V4SignerSuiteCase{ + label: "get-vanilla", + request: V4SignerSuiteCaseRequest{ + method: "GET", + host: "host.foo.com", + url: "/", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "GET\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n366b91fb121d72a00f46bbe8d395f53a102b06dfb7e79636515208ed3fa606b1", + signature: "b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470", + }, + + // post-header-key-case + V4SignerSuiteCase{ + label: "post-header-key-case", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/", + headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n05da62cee468d24ae84faff3c39f1b85540de60243c1bcaace39c0a2acc7b2c4", + signature: "22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726", + }, + + // post-header-key-sort + V4SignerSuiteCase{ + label: "post-header-key-sort", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/", + headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "ZOO:zoobar"}, + }, + canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\nzoo:zoobar\n\ndate;host;zoo\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n34e1bddeb99e76ee01d63b5e28656111e210529efeec6cdfd46a48e4c734545d", + signature: "b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a", + }, + + // post-header-value-case + V4SignerSuiteCase{ + label: "post-header-value-case", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/", + headers: []string{"DATE:Mon, 09 Sep 2011 23:36:00 GMT", "zoo:ZOOBAR"}, + }, + canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\nzoo:ZOOBAR\n\ndate;host;zoo\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n3aae6d8274b8c03e2cc96fc7d6bda4b9bd7a0a184309344470b2c96953e124aa", + signature: "273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7", + }, + + // post-vanilla-empty-query-value + V4SignerSuiteCase{ + label: "post-vanilla-empty-query-value", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/?foo=bar", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "POST\n/\nfoo=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ncd4f39132d8e60bb388831d734230460872b564871c47f5de62e62d1a68dbe1e", + signature: "b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92", + }, + + // post-vanilla-query + V4SignerSuiteCase{ + label: "post-vanilla-query", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/?foo=bar", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "POST\n/\nfoo=bar\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\ncd4f39132d8e60bb388831d734230460872b564871c47f5de62e62d1a68dbe1e", + signature: "b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92", + }, + + // post-vanilla + V4SignerSuiteCase{ + label: "post-vanilla", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/", + headers: []string{"Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + }, + canonicalRequest: "POST\n/\n\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ndate;host\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n05da62cee468d24ae84faff3c39f1b85540de60243c1bcaace39c0a2acc7b2c4", + signature: "22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726", + }, + + // post-x-www-form-urlencoded-parameters + V4SignerSuiteCase{ + label: "post-x-www-form-urlencoded-parameters", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/", + headers: []string{"Content-Type:application/x-www-form-urlencoded; charset=utf8", "Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + body: "foo=bar", + }, + canonicalRequest: "POST\n/\n\ncontent-type:application/x-www-form-urlencoded; charset=utf8\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ncontent-type;date;host\n3ba8907e7a252327488df390ed517c45b96dead033600219bdca7107d1d3f88a", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\nc4115f9e54b5cecf192b1eaa23b8e88ed8dc5391bd4fde7b3fff3d9c9fe0af1f", + signature: "b105eb10c6d318d2294de9d49dd8b031b55e3c3fe139f2e637da70511e9e7b71", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=b105eb10c6d318d2294de9d49dd8b031b55e3c3fe139f2e637da70511e9e7b71", + }, + + // post-x-www-form-urlencoded + V4SignerSuiteCase{ + label: "post-x-www-form-urlencoded", + request: V4SignerSuiteCaseRequest{ + method: "POST", + host: "host.foo.com", + url: "/", + headers: []string{"Content-Type:application/x-www-form-urlencoded", "Date:Mon, 09 Sep 2011 23:36:00 GMT"}, + body: "foo=bar", + }, + canonicalRequest: "POST\n/\n\ncontent-type:application/x-www-form-urlencoded\ndate:Mon, 09 Sep 2011 23:36:00 GMT\nhost:host.foo.com\n\ncontent-type;date;host\n3ba8907e7a252327488df390ed517c45b96dead033600219bdca7107d1d3f88a", + stringToSign: "AWS4-HMAC-SHA256\n20110909T233600Z\n20110909/us-east-1/host/aws4_request\n4c5c6e4b52fb5fb947a8733982a8a5a61b14f04345cbfe6e739236c76dd48f74", + signature: "5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc", + authorization: "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc", + }, + ) +} + +func (s *V4SignerSuite) TestCases(c *C) { + signer := aws.NewV4Signer(s.auth, "host", s.region) + + for _, testCase := range s.cases { + + req, err := http.NewRequest(testCase.request.method, "http://"+testCase.request.host+testCase.request.url, strings.NewReader(testCase.request.body)) + c.Assert(err, IsNil, Commentf("Testcase: %s", testCase.label)) + for _, v := range testCase.request.headers { + h := strings.SplitN(v, ":", 2) + req.Header.Add(h[0], h[1]) + } + req.Header.Set("host", req.Host) + + t := signer.RequestTime(req) + + canonicalRequest := signer.CanonicalRequest(req) + c.Check(canonicalRequest, Equals, testCase.canonicalRequest, Commentf("Testcase: %s", testCase.label)) + + stringToSign := signer.StringToSign(t, canonicalRequest) + c.Check(stringToSign, Equals, testCase.stringToSign, Commentf("Testcase: %s", testCase.label)) + + signature := signer.Signature(t, stringToSign) + c.Check(signature, Equals, testCase.signature, Commentf("Testcase: %s", testCase.label)) + + authorization := signer.Authorization(req.Header, t, signature) + c.Check(authorization, Equals, testCase.authorization, Commentf("Testcase: %s", testCase.label)) + + signer.Sign(req) + c.Check(req.Header.Get("Authorization"), Equals, testCase.authorization, Commentf("Testcase: %s", testCase.label)) + } +} + +func ExampleV4Signer() { + // Get auth from env vars + auth, err := aws.EnvAuth() + if err != nil { + fmt.Println(err) + } + + // Create a signer with the auth, name of the service, and aws region + signer := aws.NewV4Signer(auth, "dynamodb", aws.USEast) + + // Create a request + req, err := http.NewRequest("POST", aws.USEast.DynamoDBEndpoint, strings.NewReader("sample_request")) + if err != nil { + fmt.Println(err) + } + + // Date or x-amz-date header is required to sign a request + req.Header.Add("Date", time.Now().UTC().Format(http.TimeFormat)) + + // Sign the request + signer.Sign(req) + + // Issue signed request + http.DefaultClient.Do(req) +} diff --git a/vendor/github.com/goamz/goamz/cloudformation/cloudformation.go b/vendor/github.com/goamz/goamz/cloudformation/cloudformation.go new file mode 100644 index 000000000..00081d0c2 --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudformation/cloudformation.go @@ -0,0 +1,837 @@ +// +// cloudformation: This package provides types and functions to interact with the AWS CloudFormation API +// +// Depends on https://github.com/goamz/goamz +// + +package cloudformation + +import ( + "encoding/xml" + "fmt" + "log" + "net/http" + "net/http/httputil" + "net/url" + "strconv" + "strings" + "time" + + "github.com/goamz/goamz/aws" +) + +// The CloudFormation type encapsulates operations within a specific EC2 region. +type CloudFormation struct { + aws.Auth + aws.Region +} + +// New creates a new CloudFormation Client. +func New(auth aws.Auth, region aws.Region) *CloudFormation { + + return &CloudFormation{auth, region} + +} + +const debug = false + +// ---------------------------------------------------------------------------- +// Request dispatching logic. + +// Error encapsulates an error returned by the AWS CloudFormation API. +// +// See http://goo.gl/zDZbuQ for more details. +type Error struct { + // HTTP status code (200, 403, ...) + StatusCode int + // Error type + Type string `xml:"Type"` + // CloudFormation error code + Code string `xml:"Code"` + // The human-oriented error message + Message string `xml:"Message"` + RequestId string `xml:"RequestID"` +} + +func (err *Error) Error() string { + if err.Code == "" { + return err.Message + } + + return fmt.Sprintf("%s (%s)", err.Message, err.Code) +} + +type xmlErrors struct { + RequestId string `xml:"RequestId"` + Errors []Error `xml:"Error"` +} + +func (c *CloudFormation) query(params map[string]string, resp interface{}) error { + params["Version"] = "2010-05-15" + + data := strings.NewReader(multimap(params).Encode()) + + hreq, err := http.NewRequest("POST", c.Region.CloudFormationEndpoint+"/", data) + if err != nil { + return err + } + + hreq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + + token := c.Auth.Token() + if token != "" { + hreq.Header.Set("X-Amz-Security-Token", token) + } + + signer := aws.NewV4Signer(c.Auth, "cloudformation", c.Region) + signer.Sign(hreq) + + if debug { + log.Printf("%v -> {\n", hreq) + } + r, err := http.DefaultClient.Do(hreq) + + if err != nil { + log.Printf("Error calling Amazon") + return err + } + + defer r.Body.Close() + + if debug { + dump, _ := httputil.DumpResponse(r, true) + log.Printf("response:\n") + log.Printf("%v\n}\n", string(dump)) + } + if r.StatusCode != 200 { + return buildError(r) + } + err = xml.NewDecoder(r.Body).Decode(resp) + return err +} + +func buildError(r *http.Response) error { + var ( + err Error + errors xmlErrors + ) + xml.NewDecoder(r.Body).Decode(&errors) + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } + + err.RequestId = errors.RequestId + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +func makeParams(action string) map[string]string { + params := make(map[string]string) + params["Action"] = action + return params +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} + +// addParamsList adds params in the form of param.member.N to the params map +func addParamsList(params map[string]string, label string, ids []string) { + for i, id := range ids { + params[label+"."+strconv.Itoa(i+1)] = id + } +} + +// ----------------------------------------------------------------------- +// API Supported Types and Methods + +// SimpleResp is the basic response from most actions. +type SimpleResp struct { + XMLName xml.Name + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// CancelUpdateStack cancels an update on the specified stack. +// If the call completes successfully, the stack will roll back the update and revert +// to the previous stack configuration. +// +// See http://goo.gl/ZE6fOa for more details +func (c *CloudFormation) CancelUpdateStack(stackName string) (resp *SimpleResp, err error) { + params := makeParams("CancelUpdateStack") + + params["StackName"] = stackName + + resp = new(SimpleResp) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Parameter encapsulates the cloudstack paramter data type +// +// See http://goo.gl/2rg9eG for more details +type Parameter struct { + ParameterKey string `xml:"ParameterKey"` + ParameterValue string `xml:"ParameterValue"` + UsePreviousValue bool `xml:"UsePreviousValue"` +} + +type Tag struct { + Key string `xml:"Key"` + Value string `xml:"Value"` +} + +// CreateStackParams wraps CreateStack request options +// +// See http://goo.gl/yDZYuV for more information +type CreateStackParams struct { + Capabilities []string + DisableRollback bool + NotificationARNs []string + OnFailure string + Parameters []Parameter + StackName string + StackPolicyBody string + StackPolicyURL string + Tags []Tag + TemplateBody string + TemplateURL string + TimeoutInMinutes int +} + +// CreateStackResponse wraps a CreateStack call response +// +// See http://goo.gl/yDZYuV for more details +type CreateStackResponse struct { + StackId string `xml:"CreateStackResult>StackId"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// CreateStack creates a stack as specified in the template. After the call completes successfully, +// the stack creation starts. +// +// Required params: StackName +// +// See http://goo.gl/yDZYuV for more details +func (c *CloudFormation) CreateStack(options *CreateStackParams) ( + resp *CreateStackResponse, err error) { + params := makeParams("CreateStack") + + params["StackName"] = options.StackName + + if options.DisableRollback { + params["DisableRollback"] = strconv.FormatBool(options.DisableRollback) + } + if options.OnFailure != "" { + params["OnFailure"] = options.OnFailure + } + if options.StackPolicyBody != "" { + params["StackPolicyBody"] = options.StackPolicyBody + } + if options.StackPolicyURL != "" { + params["StackPolicyURL"] = options.StackPolicyURL + } + if options.TemplateBody != "" { + params["TemplateBody"] = options.TemplateBody + } + if options.TemplateURL != "" { + params["TemplateURL"] = options.TemplateURL + } + if options.TimeoutInMinutes != 0 { + params["TimeoutInMinutes"] = strconv.Itoa(options.TimeoutInMinutes) + } + if len(options.Capabilities) > 0 { + addParamsList(params, "Capabilities.member", options.Capabilities) + } + if len(options.NotificationARNs) > 0 { + addParamsList(params, "NotificationARNs.member", options.NotificationARNs) + } + // Add any parameters + for i, t := range options.Parameters { + key := "Parameters.member.%d.%s" + index := i + 1 + params[fmt.Sprintf(key, index, "ParameterKey")] = t.ParameterKey + params[fmt.Sprintf(key, index, "ParameterValue")] = t.ParameterValue + params[fmt.Sprintf(key, index, "UsePreviousValue")] = strconv.FormatBool(t.UsePreviousValue) + } + // Add any tags + for i, t := range options.Tags { + key := "Tags.member.%d.%s" + index := i + 1 + params[fmt.Sprintf(key, index, "Key")] = t.Key + params[fmt.Sprintf(key, index, "Value")] = t.Value + } + + resp = new(CreateStackResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteStack deletes a specified stack. +// Once the call completes successfully, stack deletion starts. +// +// See http://goo.gl/CVMpxC for more details +func (c *CloudFormation) DeleteStack(stackName string) (resp *SimpleResp, err error) { + params := makeParams("DeleteStack") + + params["StackName"] = stackName + + resp = new(SimpleResp) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// StackEvent encapsulates the StackEvent data type +// +// See http://goo.gl/EHwiMf for more details +type StackEvent struct { + EventId string `xml:"EventId"` + LogicalResourceId string `xml:"LogicalResourceId"` + PhysicalResourceId string `xml:"PhysicalResourceId"` + ResourceProperties string `xml:"ResourceProperties"` + ResourceStatus string `xml:"ResourceStatus"` + ResourceStatusReason string `xml:"ResourceStatusReason"` + ResourceType string `xml:"ResourceType"` + StackId string `xml:"StackId"` + StackName string `xml:"StackName"` + Timestamp time.Time `xml:"Timestamp"` +} + +// DescribeStackEventsResponse wraps a response returned by DescribeStackEvents request +// +// See http://goo.gl/zqj4Bz for more details +type DescribeStackEventsResponse struct { + NextToken string `xml:"DescribeStackEventsResult>NextToken"` + StackEvents []StackEvent `xml:"DescribeStackEventsResult>StackEvents>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeStackEvents returns all stack related events for a specified stack. +// +// See http://goo.gl/zqj4Bz for more details +func (c *CloudFormation) DescribeStackEvents(stackName string, nextToken string) ( + resp *DescribeStackEventsResponse, err error) { + params := makeParams("DescribeStackEvents") + + if stackName != "" { + params["StackName"] = stackName + } + if nextToken != "" { + params["NextToken"] = nextToken + } + + resp = new(DescribeStackEventsResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// StackResourceDetail encapsulates the StackResourceDetail data type +// +// See http://goo.gl/flce6I for more details +type StackResourceDetail struct { + Description string `xml:"Description"` + LastUpdatedTimestamp time.Time `xml:"LastUpdatedTimestamp"` + LogicalResourceId string `xml:"LogicalResourceId"` + Metadata string `xml:"Metadata"` + PhysicalResourceId string `xml:"PhysicalResourceId"` + ResourceStatus string `xml:"ResourceStatus"` + ResourceStatusReason string `xml:"ResourceStatusReason"` + ResourceType string `xml:"ResourceType"` + StackId string `xml:"StackId"` + StackName string `xml:"StackName"` +} + +// DescribeStackResourceResponse wraps a response returned by DescribeStackResource request +// +// See http://goo.gl/6pfPFs for more details +type DescribeStackResourceResponse struct { + StackResourceDetail StackResourceDetail `xml:"DescribeStackResourceResult>StackResourceDetail"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeStackResource returns a description of the specified resource in the specified stack. +// For deleted stacks, DescribeStackResource returns resource information +// for up to 90 days after the stack has been deleted. +// +// Required params: stackName, logicalResourceId +// +// See http://goo.gl/6pfPFs for more details +func (c *CloudFormation) DescribeStackResource(stackName string, logicalResourceId string) ( + resp *DescribeStackResourceResponse, err error) { + params := makeParams("DescribeStackResource") + + params["StackName"] = stackName + params["LogicalResourceId"] = logicalResourceId + + resp = new(DescribeStackResourceResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// StackResource encapsulates the StackResource data type +// +// See http://goo.gl/j4eli5 for more details +type StackResource struct { + Description string `xml:"Description"` + LogicalResourceId string `xml:"LogicalResourceId"` + PhysicalResourceId string `xml:"PhysicalResourceId"` + ResourceStatus string `xml:"ResourceStatus"` + ResourceStatusReason string `xml:"ResourceStatusReason"` + ResourceType string `xml:"ResourceType"` + StackId string `xml:"StackId"` + StackName string `xml:"StackName"` + Timestamp time.Time `xml:"Timestamp"` +} + +// DescribeStackResourcesResponse wraps a response returned by DescribeStackResources request +// +// See http://goo.gl/YnY5rs for more details +type DescribeStackResourcesResponse struct { + StackResources []StackResource `xml:"DescribeStackResourcesResult>StackResources>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeStackResources returns AWS resource descriptions for running and deleted stacks. +// If stackName is specified, all the associated resources that are part of the stack are returned. +// If physicalResourceId is specified, the associated resources of the stack that the resource +// belongs to are returned. +// +// Only the first 100 resources will be returned. If your stack has more resources than this, +// you should use ListStackResources instead. +// +// See http://goo.gl/YnY5rs for more details +func (c *CloudFormation) DescribeStackResources(stackName, physicalResourceId, logicalResourceId string) ( + resp *DescribeStackResourcesResponse, err error) { + params := makeParams("DescribeStackResources") + + if stackName != "" { + params["StackName"] = stackName + } + if physicalResourceId != "" { + params["PhysicalResourceId"] = physicalResourceId + } + if logicalResourceId != "" { + params["LogicalResourceId"] = logicalResourceId + } + + resp = new(DescribeStackResourcesResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Output encapsulates the Output AWS data type +// +// See http://goo.gl/UOn7q6 for more information +type Output struct { + Description string `xml:"Description"` + OutputKey string `xml:"OutputKey"` + OutputValue string `xml:"OutputValue"` +} + +// Stack encapsulates the Stack AWS data type +// +// See http://goo.gl/yDZYuV for more information +type Stack struct { + Capabilities []string `xml:"Capabilities>member"` + CreationTime time.Time `xml:"CreationTime"` + Description string `xml:"Description"` + DisableRollback bool `xml:"DisableRollback"` + LastUpdatedTime time.Time `xml:"LastUpdatedTime"` + NotificationARNs []string `xml:"NotificationARNs>member"` + Outputs []Output `xml:"Outputs>member"` + Parameters []Parameter `xml:"Parameters>member"` + StackId string `xml:"StackId"` + StackName string `xml:"StackName"` + StackStatus string `xml:"StackStatus"` + StackStatusReason string `xml:"StackStatusReason"` + Tags []Tag `xml:"Tags>member"` + TimeoutInMinutes int `xml:"TimeoutInMinutes"` +} + +// DescribeStacksResponse wraps a response returned by DescribeStacks request +// +// See http://goo.gl/UOLsXD for more information +type DescribeStacksResponse struct { + NextToken string `xml:"DescribeStacksResult>NextToken"` + Stacks []Stack `xml:"DescribeStacksResult>Stacks>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeStacks returns the description for the specified stack; +// If no stack name was specified, then it returns the description for all the stacks created. +// +// See http://goo.gl/UOLsXD for more information +func (c *CloudFormation) DescribeStacks(stackName string, nextToken string) ( + resp *DescribeStacksResponse, err error) { + params := makeParams("DescribeStacks") + + if stackName != "" { + params["StackName"] = stackName + } + if nextToken != "" { + params["NextToken"] = nextToken + } + + resp = new(DescribeStacksResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// EstimateTemplateCostResponse wraps a response returned by EstimateTemplateCost request +// +// See http://goo.gl/PD9hle for more information +type EstimateTemplateCostResponse struct { + Url string `xml:"EstimateTemplateCostResult>Url"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// EstimateTemplateCost returns the estimated monthly cost of a template. +// The return value is an AWS Simple Monthly Calculator URL with a query string that describes +// the resources required to run the template. +// +// See http://goo.gl/PD9hle for more information +func (c *CloudFormation) EstimateTemplateCost(parameters []Parameter, templateBody, templateUrl string) ( + resp *EstimateTemplateCostResponse, err error) { + params := makeParams("EstimateTemplateCost") + + if templateBody != "" { + params["TemplateBody"] = templateBody + } + if templateUrl != "" { + params["TemplateURL"] = templateUrl + } + // Add any parameters + for i, t := range parameters { + key := "Parameters.member.%d.%s" + index := i + 1 + params[fmt.Sprintf(key, index, "ParameterKey")] = t.ParameterKey + params[fmt.Sprintf(key, index, "ParameterValue")] = t.ParameterValue + params[fmt.Sprintf(key, index, "UsePreviousValue")] = strconv.FormatBool(t.UsePreviousValue) + } + + resp = new(EstimateTemplateCostResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// GetStackPolicyResponse wraps a response returned by GetStackPolicy request +// +// See http://goo.gl/iZFSgy for more information +type GetStackPolicyResponse struct { + StackPolicyBody string `xml:"GetStackPolicyResult>StackPolicyBody"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// GetStackPolicy returns the stack policy for a specified stack. If a stack doesn't have a policy, +// a null value is returned. +// +// See http://goo.gl/iZFSgy for more information +func (c *CloudFormation) GetStackPolicy(stackName string) ( + resp *GetStackPolicyResponse, err error) { + params := makeParams("GetStackPolicy") + + params["StackName"] = stackName + + resp = new(GetStackPolicyResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// GetTemplateResponse wraps a response returned by GetTemplate request +// +// See http://goo.gl/GU59CB for more information +type GetTemplateResponse struct { + TemplateBody string `xml:"GetTemplateResult>TemplateBody"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// GetTemplate returns the template body for a specified stack. +// You can get the template for running or deleted stacks +// +// Required Params: StackName - The name or the unique identifier associated with the stack, +// which are not always interchangeable: +// Running stacks: You can specify either the stack's name or its unique stack ID. +// Deleted stacks: You must specify the unique stack ID. +// +// See http://goo.gl/GU59CB for more information +func (c *CloudFormation) GetTemplate(stackName string) ( + resp *GetTemplateResponse, err error) { + params := makeParams("GetTemplate") + + params["StackName"] = stackName + + resp = new(GetTemplateResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// StackResourceSummary encapsulates the StackResourceSummary data type +// +// See http://goo.gl/Af0vcm for more details +type StackResourceSummary struct { + LastUpdatedTimestamp time.Time `xml:"LastUpdatedTimestamp"` + LogicalResourceId string `xml:"LogicalResourceId"` + PhysicalResourceId string `xml:"PhysicalResourceId"` + ResourceStatus string `xml:"ResourceStatus"` + ResourceStatusReason string `xml:"ResourceStatusReason"` + ResourceType string `xml:"ResourceType"` +} + +// ListStackResourcesResponse wraps a response returned by ListStackResources request +// +// See http://goo.gl/JUCgLf for more details +type ListStackResourcesResponse struct { + NextToken string `xml:"ListStackResourcesResult>NextToken"` + StackResourceSummaries []StackResourceSummary `xml:"ListStackResourcesResult>StackResourceSummaries>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ListStackResources returns descriptions of all resources of the specified stack. +// For deleted stacks, ListStackResources returns resource information for up to 90 days +// after the stack has been deleted. +// +// Required Params: stackName - the name or the unique identifier associated with the stack, +// which are not always interchangeable: +// Running stacks: You can specify either the stack's name or its unique stack ID. +// Deleted stacks: You must specify the unique stack ID. +// +// See http://goo.gl/JUCgLf for more details +func (c *CloudFormation) ListStackResources(stackName, nextToken string) ( + resp *ListStackResourcesResponse, err error) { + params := makeParams("ListStackResources") + + params["StackName"] = stackName + + if nextToken != "" { + params["NextToken"] = nextToken + } + + resp = new(ListStackResourcesResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// StackSummary encapsulates the StackSummary data type +// +// See http://goo.gl/35j3wf for more details +type StackSummary struct { + CreationTime time.Time `xml:"CreationTime"` + DeletionTime time.Time `xml:"DeletionTime"` + LastUpdatedTime time.Time `xml:"LastUpdatedTime"` + StackId string `xml:"StackId"` + StackName string `xml:"StackName"` + StackStatus string `xml:"StackStatus"` + StackStatusReason string `xml:"StackStatusReason"` + TemplateDescription string `xml:"TemplateDescription"` +} + +// ListStacksResponse wraps a response returned by ListStacks request +// +// See http://goo.gl/UWi6nm for more details +type ListStacksResponse struct { + NextToken string `xml:"ListStacksResult>NextToken"` + StackSummaries []StackSummary `xml:"ListStacksResult>StackSummaries>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ListStacks Returns the summary information for stacks whose status matches the specified StackStatusFilter. +// Summary information for stacks that have been deleted is kept for 90 days after the stack is deleted. +// If no StackStatusFilter is specified, summary information for all stacks is returned +// (including existing stacks and stacks that have been deleted). +// +// See http://goo.gl/UWi6nm for more details +func (c *CloudFormation) ListStacks(stackStatusFilters []string, nextToken string) ( + resp *ListStacksResponse, err error) { + params := makeParams("ListStacks") + + if nextToken != "" { + params["NextToken"] = nextToken + } + + if len(stackStatusFilters) > 0 { + addParamsList(params, "StackStatusFilter.member", stackStatusFilters) + } + + resp = new(ListStacksResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// SetStackPolicy sets a stack policy for a specified stack. +// +// Required Params: stackName +// +// See http://goo.gl/iY9ohu for more information +func (c *CloudFormation) SetStackPolicy(stackName, stackPolicyBody, stackPolicyUrl string) ( + resp *SimpleResp, err error) { + params := makeParams("SetStackPolicy") + + params["StackName"] = stackName + + if stackPolicyBody != "" { + params["StackPolicyBody"] = stackPolicyBody + } + if stackPolicyUrl != "" { + params["StackPolicyURL"] = stackPolicyUrl + } + + resp = new(SimpleResp) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// UpdateStackParams wraps UpdateStack request options +// +// See http://goo.gl/LvkhZq for more information +type UpdateStackParams struct { + Capabilities []string + NotificationARNs []string + Parameters []Parameter + StackName string + StackPolicyBody string + StackPolicyDuringUpdateBody string + StackPolicyDuringUpdateURL string + StackPolicyURL string + TemplateBody string + TemplateURL string + UsePreviousTemplate bool +} + +// UpdateStackResponse wraps the UpdateStack call response +// +// See http://goo.gl/LvkhZq for more information +type UpdateStackResponse struct { + StackId string `xml:"UpdateStackResult>StackId"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// UpdateStack updates a stack as specified in the template. +// After the call completes successfully, the stack update starts. +// You can check the status of the stack via the DescribeStacks action. +// +// Required Params: options.StackName +// +// See http://goo.gl/LvkhZq for more information +func (c *CloudFormation) UpdateStack(options *UpdateStackParams) ( + resp *UpdateStackResponse, err error) { + params := makeParams("UpdateStack") + + params["StackName"] = options.StackName + + if options.StackPolicyBody != "" { + params["StackPolicyBody"] = options.StackPolicyBody + } + if options.StackPolicyDuringUpdateBody != "" { + params["StackPolicyDuringUpdateBody"] = options.StackPolicyDuringUpdateBody + } + if options.StackPolicyDuringUpdateURL != "" { + params["StackPolicyDuringUpdateURL"] = options.StackPolicyDuringUpdateURL + } + if options.StackPolicyURL != "" { + params["StackPolicyURL"] = options.StackPolicyURL + } + if options.TemplateBody != "" { + params["TemplateBody"] = options.TemplateBody + } + if options.TemplateURL != "" { + params["TemplateURL"] = options.TemplateURL + } + if options.UsePreviousTemplate { + params["UsePreviousTemplate"] = strconv.FormatBool(options.UsePreviousTemplate) + } + + if len(options.Capabilities) > 0 { + addParamsList(params, "Capabilities.member", options.Capabilities) + } + if len(options.NotificationARNs) > 0 { + addParamsList(params, "NotificationARNs.member", options.NotificationARNs) + } + // Add any parameters + for i, t := range options.Parameters { + key := "Parameters.member.%d.%s" + index := i + 1 + params[fmt.Sprintf(key, index, "ParameterKey")] = t.ParameterKey + params[fmt.Sprintf(key, index, "ParameterValue")] = t.ParameterValue + params[fmt.Sprintf(key, index, "UsePreviousValue")] = strconv.FormatBool(t.UsePreviousValue) + } + + resp = new(UpdateStackResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// TemplateParameter encapsulates the AWS TemplateParameter data type +// +// See http://goo.gl/OBhNzk for more information +type TemplateParameter struct { + DefaultValue string `xml:"DefaultValue"` + Description string `xml:Description"` + NoEcho bool `xml:NoEcho"` + ParameterKey string `xml:ParameterKey"` +} + +// ValidateTemplateResponse wraps the ValidateTemplate call response +// +// See http://goo.gl/OBhNzk for more information +type ValidateTemplateResponse struct { + Capabilities []string `xml:"ValidateTemplateResult>Capabilities>member"` + CapabilitiesReason string `xml:"ValidateTemplateResult>CapabilitiesReason"` + Description string `xml:"ValidateTemplateResult>Description"` + Parameters []TemplateParameter `xml:"ValidateTemplateResult>Parameters>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ValidateTemplate validates a specified template. +// +// See http://goo.gl/OBhNzk for more information +func (c *CloudFormation) ValidateTemplate(templateBody, templateUrl string) ( + resp *ValidateTemplateResponse, err error) { + params := makeParams("ValidateTemplate") + + if templateBody != "" { + params["TemplateBody"] = templateBody + } + if templateUrl != "" { + params["TemplateURL"] = templateUrl + } + + resp = new(ValidateTemplateResponse) + if err := c.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/github.com/goamz/goamz/cloudformation/cloudformation_test.go b/vendor/github.com/goamz/goamz/cloudformation/cloudformation_test.go new file mode 100644 index 000000000..1937e8042 --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudformation/cloudformation_test.go @@ -0,0 +1,653 @@ +package cloudformation_test + +import ( + "testing" + "time" + + . "gopkg.in/check.v1" + + "github.com/goamz/goamz/aws" + cf "github.com/goamz/goamz/cloudformation" + "github.com/goamz/goamz/testutil" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + cf *cf.CloudFormation +} + +var testServer = testutil.NewHTTPServer() + +var mockTest bool + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.cf = cf.New(auth, aws.Region{CloudFormationEndpoint: testServer.URL}) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestCancelUpdateStack(c *C) { + testServer.Response(200, nil, CancelUpdateStackResponse) + + resp, err := s.cf.CancelUpdateStack("foo") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "CancelUpdateStack") + c.Assert(values.Get("StackName"), Equals, "foo") + // Response test + c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE") +} + +func (s *S) TestCreateStack(c *C) { + testServer.Response(200, nil, CreateStackResponse) + + stackParams := &cf.CreateStackParams{ + NotificationARNs: []string{"arn:aws:sns:us-east-1:1234567890:my-topic"}, + Parameters: []cf.Parameter{ + { + ParameterKey: "AvailabilityZone", + ParameterValue: "us-east-1a", + }, + }, + StackName: "MyStack", + TemplateBody: "[Template Document]", + } + resp, err := s.cf.CreateStack(stackParams) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "CreateStack") + c.Assert(values.Get("StackName"), Equals, "MyStack") + c.Assert(values.Get("NotificationARNs.member.1"), Equals, "arn:aws:sns:us-east-1:1234567890:my-topic") + c.Assert(values.Get("TemplateBody"), Equals, "[Template Document]") + c.Assert(values.Get("Parameters.member.1.ParameterKey"), Equals, "AvailabilityZone") + c.Assert(values.Get("Parameters.member.1.ParameterValue"), Equals, "us-east-1a") + // Response test + c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE") + c.Assert(resp.StackId, Equals, "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83") +} + +func (s *S) TestCreateStackWithInvalidParams(c *C) { + testServer.Response(400, nil, CreateStackWithInvalidParamsResponse) + //testServer.Response(200, nil, DeleteAutoScalingGroupResponse) + + cfTemplate := ` +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "Sample template", + "Parameters" : { + "KeyName" : { + "Description" : "key pair", + "Type" : "String" + } + }, + "Resources" : { + "Ec2Instance" : { + "Type" : "AWS::EC2::Instance", + "Properties" : { + "KeyName" : { "Ref" : "KeyName" }, + "ImageId" : "ami-7f418316", + "UserData" : { "Fn::Base64" : "80" } + } + } + }, + "Outputs" : { + "InstanceId" : { + "Description" : "InstanceId of the newly created EC2 instance", + "Value" : { "Ref" : "Ec2Instance" } + } +}` + + stackParams := &cf.CreateStackParams{ + Capabilities: []string{"CAPABILITY_IAM"}, + DisableRollback: true, + NotificationARNs: []string{ + "arn:aws:sns:us-east-1:1234567890:my-topic", + "arn:aws:sns:us-east-1:1234567890:my-topic2", + }, + OnFailure: "ROLLBACK", + Parameters: []cf.Parameter{ + { + ParameterKey: "AvailabilityZone", + ParameterValue: "us-east-1a", + }, + }, + StackName: "MyStack", + StackPolicyBody: "{PolicyBody}", + StackPolicyURL: "http://stack-policy-url", + Tags: []cf.Tag{ + { + Key: "TagKey", + Value: "TagValue", + }, + }, + TemplateBody: cfTemplate, + TemplateURL: "http://url", + TimeoutInMinutes: 20, + } + resp, err := s.cf.CreateStack(stackParams) + c.Assert(err, NotNil) + c.Assert(resp, IsNil) + values := testServer.WaitRequest().PostForm + + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "CreateStack") + c.Assert(values.Get("StackName"), Equals, "MyStack") + c.Assert(values.Get("NotificationARNs.member.1"), Equals, "arn:aws:sns:us-east-1:1234567890:my-topic") + c.Assert(values.Get("NotificationARNs.member.2"), Equals, "arn:aws:sns:us-east-1:1234567890:my-topic2") + c.Assert(values.Get("Capabilities.member.1"), Equals, "CAPABILITY_IAM") + c.Assert(values.Get("TemplateBody"), Equals, cfTemplate) + c.Assert(values.Get("TemplateURL"), Equals, "http://url") + c.Assert(values.Get("StackPolicyBody"), Equals, "{PolicyBody}") + c.Assert(values.Get("StackPolicyURL"), Equals, "http://stack-policy-url") + c.Assert(values.Get("OnFailure"), Equals, "ROLLBACK") + c.Assert(values.Get("DisableRollback"), Equals, "true") + c.Assert(values.Get("Tags.member.1.Key"), Equals, "TagKey") + c.Assert(values.Get("Tags.member.1.Value"), Equals, "TagValue") + c.Assert(values.Get("Parameters.member.1.ParameterKey"), Equals, "AvailabilityZone") + c.Assert(values.Get("Parameters.member.1.ParameterValue"), Equals, "us-east-1a") + c.Assert(values.Get("TimeoutInMinutes"), Equals, "20") + + // Response test + c.Assert(err.(*cf.Error).RequestId, Equals, "70a76d42-9665-11e2-9fdf-211deEXAMPLE") + c.Assert(err.(*cf.Error).Message, Equals, "Either Template URL or Template Body must be specified.") + c.Assert(err.(*cf.Error).Type, Equals, "Sender") + c.Assert(err.(*cf.Error).Code, Equals, "ValidationError") + c.Assert(err.(*cf.Error).StatusCode, Equals, 400) + +} + +func (s *S) TestDeleteStack(c *C) { + testServer.Response(200, nil, DeleteStackResponse) + + resp, err := s.cf.DeleteStack("foo") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "DeleteStack") + c.Assert(values.Get("StackName"), Equals, "foo") + // Response test + c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE") +} + +func (s *S) TestDescribeStackEvents(c *C) { + testServer.Response(200, nil, DescribeStackEventsResponse) + + resp, err := s.cf.DescribeStackEvents("MyStack", "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + + // Post request test + t1, _ := time.Parse(time.RFC3339, "2010-07-27T22:26:28Z") + t2, _ := time.Parse(time.RFC3339, "2010-07-27T22:27:28Z") + t3, _ := time.Parse(time.RFC3339, "2010-07-27T22:28:28Z") + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "DescribeStackEvents") + c.Assert(values.Get("StackName"), Equals, "MyStack") + c.Assert(values.Get("NextToken"), Equals, "") + + // Response test + expected := &cf.DescribeStackEventsResponse{ + StackEvents: []cf.StackEvent{ + { + EventId: "Event-1-Id", + StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83", + StackName: "MyStack", + LogicalResourceId: "MyStack", + PhysicalResourceId: "MyStack_One", + ResourceType: "AWS::CloudFormation::Stack", + Timestamp: t1, + ResourceStatus: "CREATE_IN_PROGRESS", + ResourceStatusReason: "User initiated", + }, + { + EventId: "Event-2-Id", + StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83", + StackName: "MyStack", + LogicalResourceId: "MyDBInstance", + PhysicalResourceId: "MyStack_DB1", + ResourceType: "AWS::SecurityGroup", + Timestamp: t2, + ResourceStatus: "CREATE_IN_PROGRESS", + ResourceProperties: "{\"GroupDescription\":...}", + }, + { + EventId: "Event-3-Id", + StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83", + StackName: "MyStack", + LogicalResourceId: "MySG1", + PhysicalResourceId: "MyStack_SG1", + ResourceType: "AWS::SecurityGroup", + Timestamp: t3, + ResourceStatus: "CREATE_COMPLETE", + }, + }, + NextToken: "", + RequestId: "4af14eec-350e-11e4-b260-EXAMPLE", + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestDescribeStackResource(c *C) { + testServer.Response(200, nil, DescribeStackResourceResponse) + + resp, err := s.cf.DescribeStackResource("MyStack", "MyDBInstance") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "DescribeStackResource") + c.Assert(values.Get("StackName"), Equals, "MyStack") + c.Assert(values.Get("LogicalResourceId"), Equals, "MyDBInstance") + t, _ := time.Parse(time.RFC3339, "2011-07-07T22:27:28Z") + // Response test + expected := &cf.DescribeStackResourceResponse{ + StackResourceDetail: cf.StackResourceDetail{ + StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83", + StackName: "MyStack", + LogicalResourceId: "MyDBInstance", + PhysicalResourceId: "MyStack_DB1", + ResourceType: "AWS::RDS::DBInstance", + LastUpdatedTimestamp: t, + ResourceStatus: "CREATE_COMPLETE", + }, + RequestId: "4af14eec-350e-11e4-b260-EXAMPLE", + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestDescribeStackResources(c *C) { + testServer.Response(200, nil, DescribeStackResourcesResponse) + + resp, err := s.cf.DescribeStackResources("MyStack", "", "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + + // Post request test + t1, _ := time.Parse(time.RFC3339, "2010-07-27T22:27:28Z") + t2, _ := time.Parse(time.RFC3339, "2010-07-27T22:28:28Z") + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "DescribeStackResources") + c.Assert(values.Get("StackName"), Equals, "MyStack") + c.Assert(values.Get("PhysicalResourceId"), Equals, "") + c.Assert(values.Get("LogicalResourceId"), Equals, "") + + // Response test + expected := &cf.DescribeStackResourcesResponse{ + StackResources: []cf.StackResource{ + { + StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83", + StackName: "MyStack", + LogicalResourceId: "MyDBInstance", + PhysicalResourceId: "MyStack_DB1", + ResourceType: "AWS::DBInstance", + Timestamp: t1, + ResourceStatus: "CREATE_COMPLETE", + }, + { + StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83", + StackName: "MyStack", + LogicalResourceId: "MyAutoScalingGroup", + PhysicalResourceId: "MyStack_ASG1", + ResourceType: "AWS::AutoScalingGroup", + Timestamp: t2, + ResourceStatus: "CREATE_IN_PROGRESS", + }, + }, + RequestId: "4af14eec-350e-11e4-b260-EXAMPLE", + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestDescribeStacks(c *C) { + testServer.Response(200, nil, DescribeStacksResponse) + + resp, err := s.cf.DescribeStacks("MyStack", "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + + // Post request test + t, _ := time.Parse(time.RFC3339, "2010-07-27T22:28:28Z") + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "DescribeStacks") + c.Assert(values.Get("StackName"), Equals, "MyStack") + c.Assert(values.Get("NextToken"), Equals, "") + + // Response test + expected := &cf.DescribeStacksResponse{ + Stacks: []cf.Stack{ + { + StackId: "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83", + StackName: "MyStack", + Description: "My Description", + Capabilities: []string{"CAPABILITY_IAM"}, + NotificationARNs: []string{"arn:aws:sns:region-name:account-name:topic-name"}, + Parameters: []cf.Parameter{ + { + ParameterKey: "MyKey", + ParameterValue: "MyValue", + }, + }, + Tags: []cf.Tag{ + { + Key: "MyTagKey", + Value: "MyTagValue", + }, + }, + CreationTime: t, + StackStatus: "CREATE_COMPLETE", + DisableRollback: false, + Outputs: []cf.Output{ + { + Description: "ServerUrl", + OutputKey: "StartPage", + OutputValue: "http://my-load-balancer.amazonaws.com:80/index.html", + }, + }, + }, + }, + NextToken: "", + RequestId: "4af14eec-350e-11e4-b260-EXAMPLE", + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestEstimateTemplateCost(c *C) { + testServer.Response(200, nil, EstimateTemplateCostResponse) + + resp, err := s.cf.EstimateTemplateCost(nil, "", "https://s3.amazonaws.com/cloudformation-samples-us-east-1/Drupal_Simple.template") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "EstimateTemplateCost") + c.Assert(values.Get("TemplateBody"), Equals, "") + c.Assert(values.Get("TemplateURL"), Equals, "https://s3.amazonaws.com/cloudformation-samples-us-east-1/Drupal_Simple.template") + // Response test + c.Assert(resp.Url, Equals, "http://calculator.s3.amazonaws.com/calc5.html?key=cf-2e351785-e821-450c-9d58-625e1e1ebfb6") + c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE") +} + +func (s *S) TestGetStackPolicy(c *C) { + testServer.Response(200, nil, GetStackPolicyResponse) + + resp, err := s.cf.GetStackPolicy("MyStack") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "GetStackPolicy") + + c.Assert(values.Get("StackName"), Equals, "MyStack") + // Response test + policy := `{ + "Statement" : [ + { + "Effect" : "Deny", + "Action" : "Update:*", + "Principal" : "*", + "Resource" : "LogicalResourceId/ProductionDatabase" + }, + { + "Effect" : "Allow", + "Action" : "Update:*", + "Principal" : "*", + "Resource" : "*" + } + ] + }` + c.Assert(resp.StackPolicyBody, Equals, policy) + c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE") +} + +func (s *S) TestGetTemplate(c *C) { + testServer.Response(200, nil, GetTemplateResponse) + + resp, err := s.cf.GetTemplate("MyStack") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "GetTemplate") + + c.Assert(values.Get("StackName"), Equals, "MyStack") + // Response test + templateBody := `{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "Simple example", + "Resources" : { + "MySQS" : { + "Type" : "AWS::SQS::Queue", + "Properties" : { + } + } + } + }` + c.Assert(resp.TemplateBody, Equals, templateBody) + c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE") +} + +func (s *S) TestListStackResources(c *C) { + testServer.Response(200, nil, ListStackResourcesResponse) + + resp, err := s.cf.ListStackResources("MyStack", "4dad1-32131da-d-31") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "ListStackResources") + c.Assert(values.Get("StackName"), Equals, "MyStack") + c.Assert(values.Get("NextToken"), Equals, "4dad1-32131da-d-31") + + // Response test + t1, _ := time.Parse(time.RFC3339, "2011-06-21T20:15:58Z") + t2, _ := time.Parse(time.RFC3339, "2011-06-21T20:25:57Z") + t3, _ := time.Parse(time.RFC3339, "2011-06-21T20:26:12Z") + t4, _ := time.Parse(time.RFC3339, "2011-06-21T20:28:48Z") + t5, _ := time.Parse(time.RFC3339, "2011-06-21T20:29:06Z") + t6, _ := time.Parse(time.RFC3339, "2011-06-21T20:29:23Z") + + expected := &cf.ListStackResourcesResponse{ + StackResourceSummaries: []cf.StackResourceSummary{ + { + LogicalResourceId: "DBSecurityGroup", + PhysicalResourceId: "gmarcteststack-dbsecuritygroup-1s5m0ez5lkk6w", + ResourceType: "AWS::RDS::DBSecurityGroup", + LastUpdatedTimestamp: t1, + ResourceStatus: "CREATE_COMPLETE", + }, + { + LogicalResourceId: "SampleDB", + PhysicalResourceId: "MyStack-sampledb-ycwhk1v830lx", + ResourceType: "AWS::RDS::DBInstance", + LastUpdatedTimestamp: t2, + ResourceStatus: "CREATE_COMPLETE", + }, + { + LogicalResourceId: "SampleApplication", + PhysicalResourceId: "MyStack-SampleApplication-1MKNASYR3RBQL", + ResourceType: "AWS::ElasticBeanstalk::Application", + LastUpdatedTimestamp: t3, + ResourceStatus: "CREATE_COMPLETE", + }, + { + LogicalResourceId: "SampleEnvironment", + PhysicalResourceId: "myst-Samp-1AGU6ERZX6M3Q", + ResourceType: "AWS::ElasticBeanstalk::Environment", + LastUpdatedTimestamp: t4, + ResourceStatus: "CREATE_COMPLETE", + }, + { + LogicalResourceId: "AlarmTopic", + PhysicalResourceId: "arn:aws:sns:us-east-1:803981987763:MyStack-AlarmTopic-SW4IQELG7RPJ", + ResourceType: "AWS::SNS::Topic", + LastUpdatedTimestamp: t5, + ResourceStatus: "CREATE_COMPLETE", + }, + { + LogicalResourceId: "CPUAlarmHigh", + PhysicalResourceId: "MyStack-CPUAlarmHigh-POBWQPDJA81F", + ResourceType: "AWS::CloudWatch::Alarm", + LastUpdatedTimestamp: t6, + ResourceStatus: "CREATE_COMPLETE", + }, + }, + NextToken: "", + RequestId: "2d06e36c-ac1d-11e0-a958-f9382b6eb86b", + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestListStacks(c *C) { + testServer.Response(200, nil, ListStacksResponse) + + resp, err := s.cf.ListStacks([]string{"CREATE_IN_PROGRESS", "DELETE_COMPLETE"}, "4dad1-32131da-d-31") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "ListStacks") + c.Assert(values.Get("StackStatusFilter.member.1"), Equals, "CREATE_IN_PROGRESS") + c.Assert(values.Get("StackStatusFilter.member.2"), Equals, "DELETE_COMPLETE") + c.Assert(values.Get("NextToken"), Equals, "4dad1-32131da-d-31") + + // Response test + c1, _ := time.Parse(time.RFC3339, "2011-05-23T15:47:44Z") + c2, _ := time.Parse(time.RFC3339, "2011-03-05T19:57:58Z") + d2, _ := time.Parse(time.RFC3339, "2011-03-10T16:20:51Z") + + expected := &cf.ListStacksResponse{ + StackSummaries: []cf.StackSummary{ + { + StackId: "arn:aws:cloudformation:us-east-1:1234567:stack/TestCreate1/aaaaa", + StackName: "vpc1", + StackStatus: "CREATE_IN_PROGRESS", + CreationTime: c1, + TemplateDescription: "Creates one EC2 instance and a load balancer.", + }, + { + StackId: "arn:aws:cloudformation:us-east-1:1234567:stack/TestDelete2/bbbbb", + StackName: "WP1", + StackStatus: "DELETE_COMPLETE", + CreationTime: c2, + DeletionTime: d2, + TemplateDescription: "A simple basic Cloudformation Template.", + }, + }, + NextToken: "", + RequestId: "2d06e36c-ac1d-11e0-a958-f9382b6eb86b", + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestSetStackPolicy(c *C) { + testServer.Response(200, nil, SetStackPolicyResponse) + + resp, err := s.cf.SetStackPolicy("MyStack", "[Stack Policy Document]", "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "SetStackPolicy") + c.Assert(values.Get("StackName"), Equals, "MyStack") + c.Assert(values.Get("StackPolicyBody"), Equals, "[Stack Policy Document]") + c.Assert(values.Get("StackPolicyUrl"), Equals, "") + // Response test + c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE") +} + +func (s *S) TestUpdateStack(c *C) { + testServer.Response(200, nil, UpdateStackResponse) + + stackParams := &cf.UpdateStackParams{ + Capabilities: []string{"CAPABILITY_IAM"}, + NotificationARNs: []string{"arn:aws:sns:us-east-1:1234567890:my-topic"}, + StackPolicyBody: "{PolicyBody}", + StackPolicyDuringUpdateBody: "{PolicyDuringUpdateBody}", + Parameters: []cf.Parameter{ + { + ParameterKey: "AvailabilityZone", + ParameterValue: "us-east-1a", + }, + }, + UsePreviousTemplate: true, + StackName: "MyStack", + TemplateBody: "[Template Document]", + } + resp, err := s.cf.UpdateStack(stackParams) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "UpdateStack") + c.Assert(values.Get("StackName"), Equals, "MyStack") + c.Assert(values.Get("NotificationARNs.member.1"), Equals, "arn:aws:sns:us-east-1:1234567890:my-topic") + c.Assert(values.Get("TemplateBody"), Equals, "[Template Document]") + c.Assert(values.Get("Parameters.member.1.ParameterKey"), Equals, "AvailabilityZone") + c.Assert(values.Get("Parameters.member.1.ParameterValue"), Equals, "us-east-1a") + c.Assert(values.Get("Capabilities.member.1"), Equals, "CAPABILITY_IAM") + c.Assert(values.Get("StackPolicyBody"), Equals, "{PolicyBody}") + c.Assert(values.Get("StackPolicyDuringUpdateBody"), Equals, "{PolicyDuringUpdateBody}") + c.Assert(values.Get("UsePreviousTemplate"), Equals, "true") + + // Response test + c.Assert(resp.RequestId, Equals, "4af14eec-350e-11e4-b260-EXAMPLE") + c.Assert(resp.StackId, Equals, "arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83") +} + +func (s *S) TestValidateTemplate(c *C) { + testServer.Response(200, nil, ValidateTemplateResponse) + + resp, err := s.cf.ValidateTemplate("", "http://myTemplateRepository/TemplateOne.template") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + + // Post request test + c.Assert(values.Get("Version"), Equals, "2010-05-15") + c.Assert(values.Get("Action"), Equals, "ValidateTemplate") + c.Assert(values.Get("TemplateURL"), Equals, "http://myTemplateRepository/TemplateOne.template") + c.Assert(values.Get("TemplateBody"), Equals, "") + + // Response test + expected := &cf.ValidateTemplateResponse{ + Description: "Test", + Capabilities: []string{"CAPABILITY_IAM"}, + Parameters: []cf.TemplateParameter{ + { + NoEcho: false, + ParameterKey: "InstanceType", + Description: "Type of instance to launch", + DefaultValue: "m1.small", + }, + { + NoEcho: false, + ParameterKey: "WebServerPort", + Description: "The TCP port for the Web Server", + DefaultValue: "8888", + }, + { + NoEcho: false, + ParameterKey: "KeyName", + Description: "Name of an existing EC2 KeyPair to enable SSH access into the server", + }, + }, + RequestId: "0be7b6e8-e4a0-11e0-a5bd-9f8d5a7dbc91", + } + c.Assert(resp, DeepEquals, expected) +} diff --git a/vendor/github.com/goamz/goamz/cloudformation/responses_test.go b/vendor/github.com/goamz/goamz/cloudformation/responses_test.go new file mode 100644 index 000000000..705b1d976 --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudformation/responses_test.go @@ -0,0 +1,371 @@ +package cloudformation_test + +var CancelUpdateStackResponse = ` + + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` + +var CreateStackResponse = ` + + + arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83 + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` + +var CreateStackWithInvalidParamsResponse = ` + + + Sender + ValidationError + Either Template URL or Template Body must be specified. + + 70a76d42-9665-11e2-9fdf-211deEXAMPLE + +` + +var DeleteStackResponse = ` + + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` +var DescribeStackEventsResponse = ` + + + + + Event-1-Id + arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83 + MyStack + MyStack + MyStack_One + AWS::CloudFormation::Stack + 2010-07-27T22:26:28Z + CREATE_IN_PROGRESS + User initiated + + + Event-2-Id + arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83 + MyStack + MyDBInstance + MyStack_DB1 + AWS::SecurityGroup + 2010-07-27T22:27:28Z + CREATE_IN_PROGRESS + {"GroupDescription":...} + + + Event-3-Id + arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83 + MyStack + MySG1 + MyStack_SG1 + AWS::SecurityGroup + 2010-07-27T22:28:28Z + CREATE_COMPLETE + + + + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` + +var DescribeStackResourceResponse = ` + + + + arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83 + MyStack + MyDBInstance + MyStack_DB1 + AWS::RDS::DBInstance + 2011-07-07T22:27:28Z + CREATE_COMPLETE + + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` +var DescribeStackResourcesResponse = ` + + + + + arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83 + MyStack + MyDBInstance + MyStack_DB1 + AWS::DBInstance + 2010-07-27T22:27:28Z + CREATE_COMPLETE + + + arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83 + MyStack + MyAutoScalingGroup + MyStack_ASG1 + AWS::AutoScalingGroup + 2010-07-27T22:28:28Z + CREATE_IN_PROGRESS + + + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` + +var DescribeStacksResponse = ` + + + + + MyStack + arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83 + + My Description + + CAPABILITY_IAM + + + arn:aws:sns:region-name:account-name:topic-name + + + + MyValue + MyKey + + + + + MyTagKey + MyTagValue + + + 2010-07-27T22:28:28Z + CREATE_COMPLETE + false + + + ServerUrl + StartPage + http://my-load-balancer.amazonaws.com:80/index.html + + + + + + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` + +var EstimateTemplateCostResponse = ` + + + http://calculator.s3.amazonaws.com/calc5.html?key=cf-2e351785-e821-450c-9d58-625e1e1ebfb6 + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` + +var GetStackPolicyResponse = ` + + + { + "Statement" : [ + { + "Effect" : "Deny", + "Action" : "Update:*", + "Principal" : "*", + "Resource" : "LogicalResourceId/ProductionDatabase" + }, + { + "Effect" : "Allow", + "Action" : "Update:*", + "Principal" : "*", + "Resource" : "*" + } + ] + } + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` + +var GetTemplateResponse = ` + + + { + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "Simple example", + "Resources" : { + "MySQS" : { + "Type" : "AWS::SQS::Queue", + "Properties" : { + } + } + } + } + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` + +var ListStackResourcesResponse = ` + + + + + CREATE_COMPLETE + DBSecurityGroup + 2011-06-21T20:15:58Z + gmarcteststack-dbsecuritygroup-1s5m0ez5lkk6w + AWS::RDS::DBSecurityGroup + + + CREATE_COMPLETE + SampleDB + 2011-06-21T20:25:57Z + MyStack-sampledb-ycwhk1v830lx + AWS::RDS::DBInstance + + + CREATE_COMPLETE + SampleApplication + 2011-06-21T20:26:12Z + MyStack-SampleApplication-1MKNASYR3RBQL + AWS::ElasticBeanstalk::Application + + + CREATE_COMPLETE + SampleEnvironment + 2011-06-21T20:28:48Z + myst-Samp-1AGU6ERZX6M3Q + AWS::ElasticBeanstalk::Environment + + + CREATE_COMPLETE + AlarmTopic + 2011-06-21T20:29:06Z + arn:aws:sns:us-east-1:803981987763:MyStack-AlarmTopic-SW4IQELG7RPJ + AWS::SNS::Topic + + + CREATE_COMPLETE + CPUAlarmHigh + 2011-06-21T20:29:23Z + MyStack-CPUAlarmHigh-POBWQPDJA81F + AWS::CloudWatch::Alarm + + + + + 2d06e36c-ac1d-11e0-a958-f9382b6eb86b + + +` + +var ListStacksResponse = ` + + + + + arn:aws:cloudformation:us-east-1:1234567:stack/TestCreate1/aaaaa + CREATE_IN_PROGRESS + vpc1 + 2011-05-23T15:47:44Z + Creates one EC2 instance and a load balancer. + + + arn:aws:cloudformation:us-east-1:1234567:stack/TestDelete2/bbbbb + DELETE_COMPLETE + 2011-03-10T16:20:51Z + WP1 + 2011-03-05T19:57:58Z + A simple basic Cloudformation Template. + + + + + 2d06e36c-ac1d-11e0-a958-f9382b6eb86b + + +` + +var SetStackPolicyResponse = ` + + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` + +var UpdateStackResponse = ` + + + arn:aws:cloudformation:us-east-1:123456789:stack/MyStack/aaf549a0-a413-11df-adb3-5081b3858e83 + + + 4af14eec-350e-11e4-b260-EXAMPLE + + +` +var ValidateTemplateResponse = ` + + + Test + + CAPABILITY_IAM + + + + false + InstanceType + Type of instance to launch + m1.small + + + false + WebServerPort + The TCP port for the Web Server + 8888 + + + false + KeyName + Name of an existing EC2 KeyPair to enable SSH access into the server + + + + + 0be7b6e8-e4a0-11e0-a5bd-9f8d5a7dbc91 + + +` diff --git a/vendor/github.com/goamz/goamz/cloudfront/cloudfront.go b/vendor/github.com/goamz/goamz/cloudfront/cloudfront.go new file mode 100644 index 000000000..745060f1c --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudfront/cloudfront.go @@ -0,0 +1,135 @@ +package cloudfront + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "encoding/base64" + "encoding/json" + "fmt" + "net/url" + "strconv" + "strings" + "time" +) + +type CloudFront struct { + BaseURL string + keyPairId string + key *rsa.PrivateKey +} + +var base64Replacer = strings.NewReplacer("=", "_", "+", "-", "/", "~") + +func New(baseurl string, key *rsa.PrivateKey, keyPairId string) *CloudFront { + return &CloudFront{ + BaseURL: baseurl, + keyPairId: keyPairId, + key: key, + } +} + +type epochTime struct { + EpochTime int64 `json:"AWS:EpochTime"` +} + +type condition struct { + DateLessThan epochTime +} + +type statement struct { + Resource string + Condition condition +} + +type policy struct { + Statement []statement +} + +func buildPolicy(resource string, expireTime time.Time) ([]byte, error) { + p := &policy{ + Statement: []statement{ + statement{ + Resource: resource, + Condition: condition{ + DateLessThan: epochTime{ + EpochTime: expireTime.Truncate(time.Millisecond).Unix(), + }, + }, + }, + }, + } + + return json.Marshal(p) +} + +func (cf *CloudFront) generateSignature(policy []byte) (string, error) { + hash := sha1.New() + if _, err := hash.Write(policy); err != nil { + return "", err + } + + hashed := hash.Sum(nil) + + signed, err := rsa.SignPKCS1v15(rand.Reader, cf.key, crypto.SHA1, hashed) + if err != nil { + return "", err + } + + encoded := base64Replacer.Replace(base64.StdEncoding.EncodeToString(signed)) + + return encoded, nil +} + +// Creates a signed url using RSAwithSHA1 as specified by +// http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html#private-content-canned-policy-creating-signature +func (cf *CloudFront) CannedSignedURL(path, queryString string, expires time.Time) (string, error) { + resource := cf.BaseURL + path + if queryString != "" { + resource = path + "?" + queryString + } + + policy, err := buildPolicy(resource, expires) + if err != nil { + return "", err + } + + signature, err := cf.generateSignature(policy) + if err != nil { + return "", err + } + + // TOOD: Do this once + uri, err := url.Parse(cf.BaseURL) + if err != nil { + return "", err + } + + uri.RawQuery = queryString + if queryString != "" { + uri.RawQuery += "&" + } + + expireTime := expires.Truncate(time.Millisecond).Unix() + + uri.Path = path + uri.RawQuery += fmt.Sprintf("Expires=%d&Signature=%s&Key-Pair-Id=%s", expireTime, signature, cf.keyPairId) + + return uri.String(), nil +} + +func (cloudfront *CloudFront) SignedURL(path, querystrings string, expires time.Time) string { + policy := `{"Statement":[{"Resource":"` + path + "?" + querystrings + `,"Condition":{"DateLessThan":{"AWS:EpochTime":` + strconv.FormatInt(expires.Truncate(time.Millisecond).Unix(), 10) + `}}}]}` + + hash := sha1.New() + hash.Write([]byte(policy)) + b := hash.Sum(nil) + he := base64.StdEncoding.EncodeToString(b) + + policySha1 := he + + url := cloudfront.BaseURL + path + "?" + querystrings + "&Expires=" + strconv.FormatInt(expires.Unix(), 10) + "&Signature=" + policySha1 + "&Key-Pair-Id=" + cloudfront.keyPairId + + return url +} diff --git a/vendor/github.com/goamz/goamz/cloudfront/cloudfront_test.go b/vendor/github.com/goamz/goamz/cloudfront/cloudfront_test.go new file mode 100644 index 000000000..63744d1cb --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudfront/cloudfront_test.go @@ -0,0 +1,52 @@ +package cloudfront + +import ( + "crypto/x509" + "encoding/pem" + "io/ioutil" + "net/url" + "testing" + "time" +) + +func TestSignedCannedURL(t *testing.T) { + rawKey, err := ioutil.ReadFile("testdata/key.pem") + if err != nil { + t.Fatal(err) + } + + pemKey, _ := pem.Decode(rawKey) + privateKey, err := x509.ParsePKCS1PrivateKey(pemKey.Bytes) + if err != nil { + t.Fatal(err) + } + + cf := &CloudFront{ + key: privateKey, + keyPairId: "test-key-pair-1231245", + BaseURL: "https://cloudfront.com", + } + + expireTime, err := time.Parse(time.RFC3339, "2014-03-28T14:00:21Z") + if err != nil { + t.Fatal(err) + } + + query := make(url.Values) + query.Add("test", "value") + + uri, err := cf.CannedSignedURL("test", "test=value", expireTime) + if err != nil { + t.Fatal(err) + } + + parsed, err := url.Parse(uri) + if err != nil { + t.Fatal(err) + } + + signature := parsed.Query().Get("Signature") + if signature == "" { + t.Fatal("Encoded signature is empty") + } +} diff --git a/vendor/github.com/goamz/goamz/cloudfront/testdata/key.pem b/vendor/github.com/goamz/goamz/cloudfront/testdata/key.pem new file mode 100644 index 000000000..96e820a2c --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudfront/testdata/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC0yMzp9DkPAE99DhsEaGkqougLvtmDKri4bZj0fFjmGmjyyjz9 +hlrsr87LHVWzH/7igK7040HG1UqypX3ijtJa9+6BKHwBBctboU3y4GfwFwVAOumY +9UytFpyPlgUFrffZLQAywKkT24OgcfEj0G5kiQn760wFnmSUtOuITo708QIDAQAB +AoGAJUA6+PoZx72Io3wElSPuh5qJteHdb+mdpmLu4XG936wRc/W4G4VTtvGC6tdg +kUhGfOWHJ26sXwwUGDuBdO146m0DkBTuIooy97afpL6hXgL5v4ELHbbuFJcf4Geg +/UAuexvRT1HenYFQ/iXM0LlqI33i8cFRc1A+j0Gseo07gAECQQDYFCn7OUokX+Q8 +M2Cwhu7JT1obmP2HwsBtXl0CDDxtOQkuYJP/UqvtdYPz/kRn3yQjoynaCTHYrFz/ +H8oN1nNhAkEA1i9TEpo7RbanIyT4vbc1/5xfjE7Pj0lnGku0QXFp/S+8YxbqhjrQ +4Qp7TTXIPPqvQhhEpAGGspM460K3F6h7kQJBANJCbMeFa9wRY2ohJIkiA+HoUWph +aPNeUxkZpa+EcJhn08NJPzpIG/ypSYl3duEMhYIYF3WPVO3ea2/mYxsr/oECQFj5 +td/fdEoEk7AU1sQxDNyPwF2QC8dxbcRNuKcLD0Wfg/oB9hEm88jYytoLQpCabx3c +6P7cp3EdmaKZx2erlRECQDYTSK2tS0+VoXSV9JbU08Pbu53j3Zhmp4l0csP+l7EU +U+rRQzKho4X9vpR/VpRGXbw8tTIhojNpHh5ofryVfgk= +-----END RSA PRIVATE KEY----- diff --git a/vendor/github.com/goamz/goamz/cloudfront/testdata/key.pub b/vendor/github.com/goamz/goamz/cloudfront/testdata/key.pub new file mode 100644 index 000000000..7d0b5b4d6 --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudfront/testdata/key.pub @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0yMzp9DkPAE99DhsEaGkqougL +vtmDKri4bZj0fFjmGmjyyjz9hlrsr87LHVWzH/7igK7040HG1UqypX3ijtJa9+6B +KHwBBctboU3y4GfwFwVAOumY9UytFpyPlgUFrffZLQAywKkT24OgcfEj0G5kiQn7 +60wFnmSUtOuITo708QIDAQAB +-----END PUBLIC KEY----- diff --git a/vendor/github.com/goamz/goamz/cloudwatch/ChangeLog b/vendor/github.com/goamz/goamz/cloudwatch/ChangeLog new file mode 100644 index 000000000..46fea639e --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudwatch/ChangeLog @@ -0,0 +1,7 @@ +2013-10-21 Carlos Salguero + +* Removed Namespace from the constructor as not all AWS API method needs it + and methods like ListMetrics you could need to call the method without a + Namespace to list all available metrics + +* Added ListMetrics method diff --git a/vendor/github.com/goamz/goamz/cloudwatch/README.md b/vendor/github.com/goamz/goamz/cloudwatch/README.md new file mode 100644 index 000000000..dc837b4c7 --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudwatch/README.md @@ -0,0 +1,109 @@ +#GoLang AWS Cloudwatch + +## Installation +Please refer to the project's main page at [https://github.com/goamz/goamz](https://github.com/goamz/goamz) for instructions about how to install. + +## Available methods + + + + + + + + + + + + + + + + + + +
GetMetricStatisticsGets statistics for the specified metric.
ListMetricsReturns a list of valid metrics stored for the AWS account.
PutMetricDataPublishes metric data points to Amazon CloudWatch.
PutMetricAlarmCreates or updates an alarm and associates it with the specified Amazon CloudWatch metric.
+ +[Please refer to AWS Cloudwatch's documentation for more info](http://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_Operations.html) + +##Examples +####Get Metric Statistics + +``` +import ( + "fmt" + "time" + "os" + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/cloudwatch" +) + +func test_get_metric_statistics() { + region := aws.Regions["a_region"] + namespace:= "AWS/ELB" + dimension := &cloudwatch.Dimension{ + Name: "LoadBalancerName", + Value: "your_value", + } + metricName := "RequestCount" + now := time.Now() + prev := now.Add(time.Duration(600)*time.Second*-1) // 600 secs = 10 minutes + + auth, err := aws.GetAuth("your_AccessKeyId", "your_SecretAccessKey", "", now) + if err != nil { + fmt.Printf("Error: %+v\n", err) + os.Exit(1) + } + + cw, err := cloudwatch.NewCloudWatch(auth, region.CloudWatchServicepoint) + request := &cloudwatch.GetMetricStatisticsRequest { + Dimensions: []cloudwatch.Dimension{*dimension}, + EndTime: now, + StartTime: prev, + MetricName: metricName, + Unit: "Count", // Not mandatory + Period: 60, + Statistics: []string{"Sum"}, + Namespace: namespace, + } + + response, err := cw.GetMetricStatistics(request) + if err == nil { + fmt.Printf("%+v\n", response) + } else { + fmt.Printf("Error: %+v\n", err) + } +} + +``` +####List Metrics + +``` +import ( + "fmt" + "time" + "os" + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/cloudwatch" +) + +func test_list_metrics() { + region := aws.Regions["us-east-1"] // Any region here + now := time.Now() + + auth, err := aws.GetAuth("an AccessKeyId", "a SecretAccessKey", "", now) + if err != nil { + fmt.Printf("Error: %+v\n", err) + os.Exit(1) + } + cw, err := cloudwatch.NewCloudWatch(auth, region.CloudWatchServicepoint) + request := &cloudwatch.ListMetricsRequest{Namespace: "AWS/EC2"} + + response, err := cw.ListMetrics(request) + if err == nil { + fmt.Printf("%+v\n", response) + } else { + fmt.Printf("Error: %+v\n", err) + } +} +``` diff --git a/vendor/github.com/goamz/goamz/cloudwatch/cloudwatch.go b/vendor/github.com/goamz/goamz/cloudwatch/cloudwatch.go new file mode 100644 index 000000000..461d6a102 --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudwatch/cloudwatch.go @@ -0,0 +1,404 @@ +/***** BEGIN LICENSE BLOCK ***** +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# The Initial Developer of the Original Code is the Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2012 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Ben Bangert (bbangert@mozilla.com) +# Logan Owen (lsowen@s1network.com) +# +# ***** END LICENSE BLOCK *****/ + +package cloudwatch + +import ( + "encoding/xml" + "errors" + "fmt" + "github.com/feyeleanor/sets" + "github.com/goamz/goamz/aws" + "strconv" + "time" +) + +// The CloudWatch type encapsulates all the CloudWatch operations in a region. +type CloudWatch struct { + Service aws.AWSService +} + +type Dimension struct { + Name string + Value string +} + +type StatisticSet struct { + Maximum float64 + Minimum float64 + SampleCount float64 + Sum float64 +} + +type MetricDatum struct { + Dimensions []Dimension + MetricName string + StatisticValues *StatisticSet + Timestamp time.Time + Unit string + Value float64 +} + +type Datapoint struct { + Average float64 + Maximum float64 + Minimum float64 + SampleCount float64 + Sum float64 + Timestamp time.Time + Unit string +} + +type GetMetricStatisticsRequest struct { + Dimensions []Dimension + EndTime time.Time + StartTime time.Time + MetricName string + Unit string + Period int + Statistics []string + Namespace string +} + +type GetMetricStatisticsResult struct { + Datapoints []Datapoint `xml:"Datapoints>member"` + NextToken string `xml:"NextToken"` +} + +type GetMetricStatisticsResponse struct { + GetMetricStatisticsResult GetMetricStatisticsResult + ResponseMetadata aws.ResponseMetadata +} + +type Metric struct { + Dimensions []Dimension `xml:"Dimensions>member"` + MetricName string + Namespace string +} + +type ListMetricsResult struct { + Metrics []Metric `xml:"Metrics>member"` + NextToken string +} + +type ListMetricsResponse struct { + ListMetricsResult ListMetricsResult + ResponseMetadata aws.ResponseMetadata +} + +type ListMetricsRequest struct { + Dimensions []Dimension + MetricName string + Namespace string + NextToken string +} + +type AlarmAction struct { + ARN string +} + +type MetricAlarm struct { + AlarmActions []AlarmAction + AlarmDescription string + AlarmName string + ComparisonOperator string + Dimensions []Dimension + EvaluationPeriods int + InsufficientDataActions []AlarmAction + MetricName string + Namespace string + OkActions []AlarmAction + Period int + Statistic string + Threshold float64 + Unit string +} + +var attempts = aws.AttemptStrategy{ + Min: 5, + Total: 5 * time.Second, + Delay: 200 * time.Millisecond, +} + +var validUnits = sets.SSet( + "Seconds", + "Microseconds", + "Milliseconds", + "Bytes", + "Kilobytes", + "Megabytes", + "Gigabytes", + "Terabytes", + "Bits", + "Kilobits", + "Megabits", + "Gigabits", + "Terabits", + "Percent", + "Count", + "Bytes/Second", + "Kilobytes/Second", + "Megabytes/Second", + "Gigabytes/Second", + "Terabytes/Second", + "Bits/Second", + "Kilobits/Second", + "Megabits/Second", + "Gigabits/Second", + "Terabits/Second", + "Count/Second", +) + +var validMetricStatistics = sets.SSet( + "Average", + "Sum", + "SampleCount", + "Maximum", + "Minimum", +) + +var validComparisonOperators = sets.SSet( + "LessThanThreshold", + "LessThanOrEqualToThreshold", + "GreaterThanThreshold", + "GreaterThanOrEqualToThreshold", +) + +// Create a new CloudWatch object for a given namespace +func NewCloudWatch(auth aws.Auth, region aws.ServiceInfo) (*CloudWatch, error) { + service, err := aws.NewService(auth, region) + if err != nil { + return nil, err + } + return &CloudWatch{ + Service: service, + }, nil +} + +func (c *CloudWatch) query(method, path string, params map[string]string, resp interface{}) error { + // Add basic Cloudwatch param + params["Version"] = "2010-08-01" + + r, err := c.Service.Query(method, path, params) + if err != nil { + return err + } + defer r.Body.Close() + + if r.StatusCode != 200 { + return c.Service.BuildError(r) + } + err = xml.NewDecoder(r.Body).Decode(resp) + return err +} + +// Get statistics for specified metric +// +// If the arguments are invalid or the server returns an error, the error will +// be set and the other values undefined. +func (c *CloudWatch) GetMetricStatistics(req *GetMetricStatisticsRequest) (result *GetMetricStatisticsResponse, err error) { + statisticsSet := sets.SSet(req.Statistics...) + // Kick out argument errors + switch { + case req.EndTime.IsZero(): + err = errors.New("No endTime specified") + case req.StartTime.IsZero(): + err = errors.New("No startTime specified") + case req.MetricName == "": + err = errors.New("No metricName specified") + case req.Namespace == "": + err = errors.New("No Namespace specified") + case req.Period < 60 || req.Period%60 != 0: + err = errors.New("Period not 60 seconds or a multiple of 60 seconds") + case len(req.Statistics) < 1: + err = errors.New("No statistics supplied") + case validMetricStatistics.Union(statisticsSet).Len() != validMetricStatistics.Len(): + err = errors.New("Invalid statistic values supplied") + case req.Unit != "" && !validUnits.Member(req.Unit): + err = errors.New("Unit is not a valid value") + } + if err != nil { + return + } + + // Serialize all the params + params := aws.MakeParams("GetMetricStatistics") + params["EndTime"] = req.EndTime.UTC().Format(time.RFC3339) + params["StartTime"] = req.StartTime.UTC().Format(time.RFC3339) + params["MetricName"] = req.MetricName + params["Namespace"] = req.Namespace + params["Period"] = strconv.Itoa(req.Period) + if req.Unit != "" { + params["Unit"] = req.Unit + } + + // Serialize the lists of data + for i, d := range req.Dimensions { + prefix := "Dimensions.member." + strconv.Itoa(i+1) + params[prefix+".Name"] = d.Name + params[prefix+".Value"] = d.Value + } + for i, d := range req.Statistics { + prefix := "Statistics.member." + strconv.Itoa(i+1) + params[prefix] = d + } + result = new(GetMetricStatisticsResponse) + err = c.query("GET", "/", params, result) + return +} + +// Returns a list of valid metrics stored for the AWS account owner. +// Returned metrics can be used with GetMetricStatistics to obtain statistical data for a given metric. + +func (c *CloudWatch) ListMetrics(req *ListMetricsRequest) (result *ListMetricsResponse, err error) { + + // Serialize all the params + params := aws.MakeParams("ListMetrics") + if req.Namespace != "" { + params["Namespace"] = req.Namespace + } + if len(req.Dimensions) > 0 { + for i, d := range req.Dimensions { + prefix := "Dimensions.member." + strconv.Itoa(i+1) + params[prefix+".Name"] = d.Name + params[prefix+".Value"] = d.Value + } + } + + result = new(ListMetricsResponse) + err = c.query("GET", "/", params, &result) + metrics := result.ListMetricsResult.Metrics + if result.ListMetricsResult.NextToken != "" { + params = aws.MakeParams("ListMetrics") + params["NextToken"] = result.ListMetricsResult.NextToken + for result.ListMetricsResult.NextToken != "" && err == nil { + result = new(ListMetricsResponse) + err = c.query("GET", "/", params, &result) + if err == nil { + newslice := make([]Metric, len(metrics)+len(result.ListMetricsResult.Metrics)) + copy(newslice, metrics) + copy(newslice[len(metrics):], result.ListMetricsResult.Metrics) + metrics = newslice + } + } + result.ListMetricsResult.Metrics = metrics + } + return +} + +func (c *CloudWatch) PutMetricData(metrics []MetricDatum) (result *aws.BaseResponse, err error) { + return c.PutMetricDataNamespace(metrics, "") +} + +func (c *CloudWatch) PutMetricDataNamespace(metrics []MetricDatum, namespace string) (result *aws.BaseResponse, err error) { + // Serialize the params + params := aws.MakeParams("PutMetricData") + if namespace != "" { + params["Namespace"] = namespace + } + for i, metric := range metrics { + prefix := "MetricData.member." + strconv.Itoa(i+1) + if metric.MetricName == "" { + err = fmt.Errorf("No metric name supplied for metric: %d", i) + return + } + params[prefix+".MetricName"] = metric.MetricName + if metric.Unit != "" { + params[prefix+".Unit"] = metric.Unit + } + params[prefix+".Value"] = strconv.FormatFloat(metric.Value, 'E', 10, 64) + if !metric.Timestamp.IsZero() { + params[prefix+".Timestamp"] = metric.Timestamp.UTC().Format(time.RFC3339) + } + for j, dim := range metric.Dimensions { + dimprefix := prefix + ".Dimensions.member." + strconv.Itoa(j+1) + params[dimprefix+".Name"] = dim.Name + params[dimprefix+".Value"] = dim.Value + } + if metric.StatisticValues != nil { + statprefix := prefix + ".StatisticValues" + params[statprefix+".Maximum"] = strconv.FormatFloat(metric.StatisticValues.Maximum, 'E', 10, 64) + params[statprefix+".Minimum"] = strconv.FormatFloat(metric.StatisticValues.Minimum, 'E', 10, 64) + params[statprefix+".SampleCount"] = strconv.FormatFloat(metric.StatisticValues.SampleCount, 'E', 10, 64) + params[statprefix+".Sum"] = strconv.FormatFloat(metric.StatisticValues.Sum, 'E', 10, 64) + } + } + result = new(aws.BaseResponse) + err = c.query("POST", "/", params, result) + return +} + +func (c *CloudWatch) PutMetricAlarm(alarm *MetricAlarm) (result *aws.BaseResponse, err error) { + // Serialize the params + params := aws.MakeParams("PutMetricAlarm") + + switch { + case alarm.AlarmName == "": + err = errors.New("No AlarmName supplied") + case !validComparisonOperators.Member(alarm.ComparisonOperator): + err = errors.New("ComparisonOperator is not valid") + case alarm.EvaluationPeriods == 0: + err = errors.New("No number of EvaluationPeriods specified") + case alarm.MetricName == "": + err = errors.New("No MetricName specified") + case alarm.Namespace == "": + err = errors.New("No Namespace specified") + case alarm.Period == 0: + err = errors.New("No Period over which statistic should apply was specified") + case !validMetricStatistics.Member(alarm.Statistic): + err = errors.New("Invalid statistic value supplied") + case alarm.Threshold == 0: + err = errors.New("No Threshold value specified") + case alarm.Unit != "" && !validUnits.Member(alarm.Unit): + err = errors.New("Unit is not a valid value") + } + if err != nil { + return + } + + for i, action := range alarm.AlarmActions { + params["AlarmActions.member."+strconv.Itoa(i+1)] = action.ARN + } + for i, action := range alarm.InsufficientDataActions { + params["InsufficientDataActions.member."+strconv.Itoa(i+1)] = action.ARN + } + for i, action := range alarm.OkActions { + params["OKActions.member."+strconv.Itoa(i+1)] = action.ARN + } + if alarm.AlarmDescription != "" { + params["AlarmDescription"] = alarm.AlarmDescription + } + params["AlarmDescription"] = alarm.AlarmDescription + params["AlarmName"] = alarm.AlarmName + params["ComparisonOperator"] = alarm.ComparisonOperator + for i, dim := range alarm.Dimensions { + dimprefix := "Dimensions.member." + strconv.Itoa(i+1) + params[dimprefix+".Name"] = dim.Name + params[dimprefix+".Value"] = dim.Value + } + params["EvaluationPeriods"] = strconv.Itoa(alarm.EvaluationPeriods) + params["MetricName"] = alarm.MetricName + params["Namespace"] = alarm.Namespace + params["Period"] = strconv.Itoa(alarm.Period) + params["Statistic"] = alarm.Statistic + params["Threshold"] = strconv.FormatFloat(alarm.Threshold, 'E', 10, 64) + if alarm.Unit != "" { + params["Unit"] = alarm.Unit + } + + result = new(aws.BaseResponse) + err = c.query("POST", "/", params, result) + return +} diff --git a/vendor/github.com/goamz/goamz/cloudwatch/cloudwatch_test.go b/vendor/github.com/goamz/goamz/cloudwatch/cloudwatch_test.go new file mode 100644 index 000000000..a4271f1ea --- /dev/null +++ b/vendor/github.com/goamz/goamz/cloudwatch/cloudwatch_test.go @@ -0,0 +1,132 @@ +package cloudwatch_test + +import ( + "testing" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/cloudwatch" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +type S struct { + cw *cloudwatch.CloudWatch +} + +var _ = Suite(&S{}) + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.cw, _ = cloudwatch.NewCloudWatch(auth, aws.ServiceInfo{testServer.URL, aws.V2Signature}) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func getTestAlarm() *cloudwatch.MetricAlarm { + alarm := new(cloudwatch.MetricAlarm) + + alarm.AlarmName = "TestAlarm" + alarm.MetricName = "TestMetric" + alarm.Namespace = "TestNamespace" + alarm.ComparisonOperator = "LessThanThreshold" + alarm.Threshold = 1 + alarm.EvaluationPeriods = 5 + alarm.Period = 60 + alarm.Statistic = "Sum" + + return alarm +} + +func (s *S) TestPutAlarm(c *C) { + testServer.Response(200, nil, "123") + + alarm := getTestAlarm() + + _, err := s.cw.PutMetricAlarm(alarm) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "POST") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Form["Action"], DeepEquals, []string{"PutMetricAlarm"}) + c.Assert(req.Form["AlarmName"], DeepEquals, []string{"TestAlarm"}) + c.Assert(req.Form["ComparisonOperator"], DeepEquals, []string{"LessThanThreshold"}) + c.Assert(req.Form["EvaluationPeriods"], DeepEquals, []string{"5"}) + c.Assert(req.Form["Threshold"], DeepEquals, []string{"1.0000000000E+00"}) + c.Assert(req.Form["Period"], DeepEquals, []string{"60"}) + c.Assert(req.Form["Statistic"], DeepEquals, []string{"Sum"}) +} + +func (s *S) TestPutAlarmWithAction(c *C) { + testServer.Response(200, nil, "123") + + alarm := getTestAlarm() + + alarm.AlarmActions = []cloudwatch.AlarmAction{ + cloudwatch.AlarmAction{ + ARN: "123", + }, + } + + alarm.OkActions = []cloudwatch.AlarmAction{ + cloudwatch.AlarmAction{ + ARN: "456", + }, + } + + alarm.InsufficientDataActions = []cloudwatch.AlarmAction{ + cloudwatch.AlarmAction{ + ARN: "789", + }, + } + + _, err := s.cw.PutMetricAlarm(alarm) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "POST") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Form["Action"], DeepEquals, []string{"PutMetricAlarm"}) + c.Assert(req.Form["AlarmActions.member.1"], DeepEquals, []string{"123"}) + c.Assert(req.Form["OKActions.member.1"], DeepEquals, []string{"456"}) + c.Assert(req.Form["InsufficientDataActions.member.1"], DeepEquals, []string{"789"}) + c.Assert(req.Form["AlarmName"], DeepEquals, []string{"TestAlarm"}) + c.Assert(req.Form["ComparisonOperator"], DeepEquals, []string{"LessThanThreshold"}) + c.Assert(req.Form["EvaluationPeriods"], DeepEquals, []string{"5"}) + c.Assert(req.Form["Threshold"], DeepEquals, []string{"1.0000000000E+00"}) + c.Assert(req.Form["Period"], DeepEquals, []string{"60"}) + c.Assert(req.Form["Statistic"], DeepEquals, []string{"Sum"}) +} + +func (s *S) TestPutAlarmInvalidComapirsonOperator(c *C) { + testServer.Response(200, nil, "123") + + alarm := getTestAlarm() + + alarm.ComparisonOperator = "LessThan" + + _, err := s.cw.PutMetricAlarm(alarm) + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "ComparisonOperator is not valid") +} + +func (s *S) TestPutAlarmInvalidStatistic(c *C) { + testServer.Response(200, nil, "123") + + alarm := getTestAlarm() + + alarm.Statistic = "Count" + + _, err := s.cw.PutMetricAlarm(alarm) + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "Invalid statistic value supplied") +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/.gitignore b/vendor/github.com/goamz/goamz/dynamodb/.gitignore new file mode 100644 index 000000000..2385ddf57 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/.gitignore @@ -0,0 +1 @@ +dynamodb_local* diff --git a/vendor/github.com/goamz/goamz/dynamodb/Makefile b/vendor/github.com/goamz/goamz/dynamodb/Makefile new file mode 100644 index 000000000..4c02cd4b7 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/Makefile @@ -0,0 +1,13 @@ +DYNAMODB_LOCAL_VERSION = 2013-12-12 + +launch: DynamoDBLocal.jar + cd dynamodb_local_$(DYNAMODB_LOCAL_VERSION) && java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar + +DynamoDBLocal.jar: dynamodb_local_$(DYNAMODB_LOCAL_VERSION).tar.gz + [ -d dynamodb_local_$(DYNAMODB_LOCAL_VERSION) ] || tar -zxf dynamodb_local_$(DYNAMODB_LOCAL_VERSION).tar.gz + +dynamodb_local_$(DYNAMODB_LOCAL_VERSION).tar.gz: + curl -O https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_$(DYNAMODB_LOCAL_VERSION).tar.gz + +clean: + rm -rf dynamodb_local_$(DYNAMODB_LOCAL_VERSION)* diff --git a/vendor/github.com/goamz/goamz/dynamodb/README.md b/vendor/github.com/goamz/goamz/dynamodb/README.md new file mode 100644 index 000000000..5896d67b6 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/README.md @@ -0,0 +1,27 @@ +# Running integration tests + +## against DynamoDB local + +To download and launch DynamoDB local: + +```sh +$ make +``` + +To test: + +```sh +$ go test -v -amazon +``` + +## against real DynamoDB server on us-east + +_WARNING_: Some dangerous operations such as `DeleteTable` will be performed during the tests. Please be careful. + +To test: + +```sh +$ go test -v -amazon -local=false +``` + +_Note_: Running tests against real DynamoDB will take several minutes. diff --git a/vendor/github.com/goamz/goamz/dynamodb/attribute.go b/vendor/github.com/goamz/goamz/dynamodb/attribute.go new file mode 100755 index 000000000..38389ada2 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/attribute.go @@ -0,0 +1,185 @@ +package dynamodb + +import ( + "strconv" +) + +const ( + TYPE_STRING = "S" + TYPE_NUMBER = "N" + TYPE_BINARY = "B" + + TYPE_STRING_SET = "SS" + TYPE_NUMBER_SET = "NS" + TYPE_BINARY_SET = "BS" + + COMPARISON_EQUAL = "EQ" + COMPARISON_NOT_EQUAL = "NE" + COMPARISON_LESS_THAN_OR_EQUAL = "LE" + COMPARISON_LESS_THAN = "LT" + COMPARISON_GREATER_THAN_OR_EQUAL = "GE" + COMPARISON_GREATER_THAN = "GT" + COMPARISON_ATTRIBUTE_EXISTS = "NOT_NULL" + COMPARISON_ATTRIBUTE_DOES_NOT_EXIST = "NULL" + COMPARISON_CONTAINS = "CONTAINS" + COMPARISON_DOES_NOT_CONTAIN = "NOT_CONTAINS" + COMPARISON_BEGINS_WITH = "BEGINS_WITH" + COMPARISON_IN = "IN" + COMPARISON_BETWEEN = "BETWEEN" +) + +type Key struct { + HashKey string + RangeKey string +} + +type PrimaryKey struct { + KeyAttribute *Attribute + RangeAttribute *Attribute +} + +type Attribute struct { + Type string + Name string + Value string + SetValues []string + Exists string // exists on dynamodb? Values: "true", "false", or "" +} + +type AttributeComparison struct { + AttributeName string + ComparisonOperator string + AttributeValueList []Attribute // contains attributes with only types and names (value ignored) +} + +func NewEqualInt64AttributeComparison(attributeName string, equalToValue int64) *AttributeComparison { + numeric := NewNumericAttribute(attributeName, strconv.FormatInt(equalToValue, 10)) + return &AttributeComparison{attributeName, + COMPARISON_EQUAL, + []Attribute{*numeric}, + } +} + +func NewEqualStringAttributeComparison(attributeName string, equalToValue string) *AttributeComparison { + str := NewStringAttribute(attributeName, equalToValue) + return &AttributeComparison{attributeName, + COMPARISON_EQUAL, + []Attribute{*str}, + } +} + +func NewStringAttributeComparison(attributeName string, comparisonOperator string, value string) *AttributeComparison { + valueToCompare := NewStringAttribute(attributeName, value) + return &AttributeComparison{attributeName, + comparisonOperator, + []Attribute{*valueToCompare}, + } +} + +func NewNumericAttributeComparison(attributeName string, comparisonOperator string, value int64) *AttributeComparison { + valueToCompare := NewNumericAttribute(attributeName, strconv.FormatInt(value, 10)) + return &AttributeComparison{attributeName, + comparisonOperator, + []Attribute{*valueToCompare}, + } +} + +func NewBinaryAttributeComparison(attributeName string, comparisonOperator string, value bool) *AttributeComparison { + valueToCompare := NewBinaryAttribute(attributeName, strconv.FormatBool(value)) + return &AttributeComparison{attributeName, + comparisonOperator, + []Attribute{*valueToCompare}, + } +} + +func NewStringAttribute(name string, value string) *Attribute { + return &Attribute{ + Type: TYPE_STRING, + Name: name, + Value: value, + } +} + +func NewNumericAttribute(name string, value string) *Attribute { + return &Attribute{ + Type: TYPE_NUMBER, + Name: name, + Value: value, + } +} + +func NewBinaryAttribute(name string, value string) *Attribute { + return &Attribute{ + Type: TYPE_BINARY, + Name: name, + Value: value, + } +} + +func NewStringSetAttribute(name string, values []string) *Attribute { + return &Attribute{ + Type: TYPE_STRING_SET, + Name: name, + SetValues: values, + } +} + +func NewNumericSetAttribute(name string, values []string) *Attribute { + return &Attribute{ + Type: TYPE_NUMBER_SET, + Name: name, + SetValues: values, + } +} + +func NewBinarySetAttribute(name string, values []string) *Attribute { + return &Attribute{ + Type: TYPE_BINARY_SET, + Name: name, + SetValues: values, + } +} + +func (a *Attribute) SetType() bool { + switch a.Type { + case TYPE_BINARY_SET, TYPE_NUMBER_SET, TYPE_STRING_SET: + return true + } + return false +} + +func (a *Attribute) SetExists(exists bool) *Attribute { + if exists { + a.Exists = "true" + } else { + a.Exists = "false" + } + return a +} + +func (k *PrimaryKey) HasRange() bool { + return k.RangeAttribute != nil +} + +// Useful when you may have many goroutines using a primary key, so they don't fuxor up your values. +func (k *PrimaryKey) Clone(h string, r string) []Attribute { + pk := &Attribute{ + Type: k.KeyAttribute.Type, + Name: k.KeyAttribute.Name, + Value: h, + } + + result := []Attribute{*pk} + + if k.HasRange() { + rk := &Attribute{ + Type: k.RangeAttribute.Type, + Name: k.RangeAttribute.Name, + Value: r, + } + + result = append(result, *rk) + } + + return result +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/const.go b/vendor/github.com/goamz/goamz/dynamodb/const.go new file mode 100644 index 000000000..b070d44cb --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/const.go @@ -0,0 +1,11 @@ +package dynamodb + +type ReturnValues string + +const ( + NONE ReturnValues = "NONE" + ALL_OLD ReturnValues = "ALL_HOLD" + UPDATED_OLD ReturnValues = "UPDATED_OLD" + ALL_NEW ReturnValues = "ALL_NEW" + UPDATED_NEW ReturnValues = "UPDATED_NEW" +) diff --git a/vendor/github.com/goamz/goamz/dynamodb/dynamodb.go b/vendor/github.com/goamz/goamz/dynamodb/dynamodb.go new file mode 100755 index 000000000..7881e8dc1 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/dynamodb.go @@ -0,0 +1,142 @@ +package dynamodb + +import simplejson "github.com/bitly/go-simplejson" +import ( + "errors" + "github.com/goamz/goamz/aws" + "io/ioutil" + "log" + "net/http" + "strings" + "time" +) + +type Server struct { + Auth aws.Auth + Region aws.Region +} + +/* +type Query struct { + Query string +} +*/ + +/* +func NewQuery(queryParts []string) *Query { + return &Query{ + "{" + strings.Join(queryParts, ",") + "}", + } +} +*/ + +const ( + // DynamoDBAPIPrefix is the versioned prefix for DynamoDB API commands. + DynamoDBAPIPrefix = "DynamoDB_20120810." + // DynamoDBStreamsAPIPrefix is the versioned prefix for DynamoDB Streams API commands. + DynamoDBStreamsAPIPrefix = "DynamoDBStreams_20120810." +) + +// Specific error constants +var ErrNotFound = errors.New("Item not found") + +// Error represents an error in an operation with Dynamodb (following goamz/s3) +type Error struct { + StatusCode int // HTTP status code (200, 403, ...) + Status string + Code string // Dynamodb error code ("MalformedQueryString", ...) + Message string // The human-oriented error message +} + +func (e *Error) Error() string { + return e.Code + ": " + e.Message +} + +func buildError(r *http.Response, jsonBody []byte) error { + + ddbError := Error{ + StatusCode: r.StatusCode, + Status: r.Status, + } + // TODO return error if Unmarshal fails? + + json, err := simplejson.NewJson(jsonBody) + if err != nil { + log.Printf("Failed to parse body as JSON") + return err + } + ddbError.Message = json.Get("message").MustString() + + // Of the form: com.amazon.coral.validate#ValidationException + // We only want the last part + codeStr := json.Get("__type").MustString() + hashIndex := strings.Index(codeStr, "#") + if hashIndex > 0 { + codeStr = codeStr[hashIndex+1:] + } + ddbError.Code = codeStr + + return &ddbError +} + +func (s *Server) queryServer(target string, query *Query) ([]byte, error) { + data := strings.NewReader(query.String()) + var endpoint string + if isStreamsTarget(target) { + endpoint = s.Region.DynamoDBStreamsEndpoint + } else { + endpoint = s.Region.DynamoDBEndpoint + } + hreq, err := http.NewRequest("POST", endpoint+"/", data) + if err != nil { + return nil, err + } + + hreq.Header.Set("Content-Type", "application/x-amz-json-1.0") + hreq.Header.Set("X-Amz-Date", time.Now().UTC().Format(aws.ISO8601BasicFormat)) + hreq.Header.Set("X-Amz-Target", target) + + token := s.Auth.Token() + if token != "" { + hreq.Header.Set("X-Amz-Security-Token", token) + } + + signer := aws.NewV4Signer(s.Auth, "dynamodb", s.Region) + signer.Sign(hreq) + + resp, err := http.DefaultClient.Do(hreq) + + if err != nil { + log.Printf("Error calling Amazon") + return nil, err + } + + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Printf("Could not read response body") + return nil, err + } + + // http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ErrorHandling.html + // "A response code of 200 indicates the operation was successful." + if resp.StatusCode != 200 { + ddbErr := buildError(resp, body) + return nil, ddbErr + } + + return body, nil +} + +func target(name string) string { + return DynamoDBAPIPrefix + name +} + +func streamsTarget(name string) string { + return DynamoDBStreamsAPIPrefix + name +} + +func isStreamsTarget(target string) bool { + return strings.HasPrefix(target, DynamoDBStreamsAPIPrefix) +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/dynamodb_test.go b/vendor/github.com/goamz/goamz/dynamodb/dynamodb_test.go new file mode 100755 index 000000000..63dd03da3 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/dynamodb_test.go @@ -0,0 +1,166 @@ +package dynamodb_test + +import ( + "flag" + "testing" + "time" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/dynamodb" + . "gopkg.in/check.v1" +) + +const TIMEOUT = 3 * time.Minute + +var amazon = flag.Bool("amazon", false, "Enable tests against dynamodb") +var local = flag.Bool("local", true, "Use DynamoDB local on 8080 instead of real server on us-east.") + +var dynamodb_region aws.Region +var dynamodb_auth aws.Auth + +type DynamoDBTest struct { + server *dynamodb.Server + aws.Region // Exports Region + TableDescriptionT dynamodb.TableDescriptionT + table *dynamodb.Table +} + +// Delete all items in the table +func (s *DynamoDBTest) TearDownTest(c *C) { + pk, err := s.TableDescriptionT.BuildPrimaryKey() + if err != nil { + c.Fatal(err) + } + + attrs, err := s.table.Scan(nil) + if err != nil { + c.Fatal(err) + } + for _, a := range attrs { + key := &dynamodb.Key{ + HashKey: a[pk.KeyAttribute.Name].Value, + } + if pk.HasRange() { + key.RangeKey = a[pk.RangeAttribute.Name].Value + } + if ok, err := s.table.DeleteItem(key); !ok { + c.Fatal(err) + } + } +} + +func (s *DynamoDBTest) TearDownSuite(c *C) { + // return immediately in the case of calling c.Skip() in SetUpSuite() + if s.server == nil { + return + } + + // check whether the table exists + if tables, err := s.server.ListTables(); err != nil { + c.Fatal(err) + } else { + if !findTableByName(tables, s.TableDescriptionT.TableName) { + return + } + } + + // Delete the table and wait + if _, err := s.server.DeleteTable(s.TableDescriptionT); err != nil { + c.Fatal(err) + } + + done := make(chan bool) + timeout := time.After(TIMEOUT) + go func() { + for { + select { + case <-done: + return + default: + tables, err := s.server.ListTables() + if err != nil { + c.Fatal(err) + } + if findTableByName(tables, s.TableDescriptionT.TableName) { + time.Sleep(5 * time.Second) + } else { + done <- true + return + } + } + } + }() + select { + case <-done: + break + case <-timeout: + c.Error("Expect the table to be deleted but timed out") + close(done) + } +} + +func (s *DynamoDBTest) WaitUntilStatus(c *C, status string) { + // We should wait until the table is in specified status because a real DynamoDB has some delay for ready + done := make(chan bool) + timeout := time.After(TIMEOUT) + go func() { + for { + select { + case <-done: + return + default: + desc, err := s.table.DescribeTable() + if err != nil { + c.Fatal(err) + } + if desc.TableStatus == status { + done <- true + return + } + time.Sleep(5 * time.Second) + } + } + }() + select { + case <-done: + break + case <-timeout: + c.Errorf("Expect a status to be %s, but timed out", status) + close(done) + } +} + +func setUpAuth(c *C) { + if !*amazon { + c.Skip("Test against amazon not enabled.") + } + if *local { + c.Log("Using local server") + dynamodb_region = aws.Region{ + DynamoDBEndpoint: "http://127.0.0.1:8000", + DynamoDBStreamsEndpoint: "http://127.0.0.1:8000", + } + dynamodb_auth = aws.Auth{AccessKey: "DUMMY_KEY", SecretKey: "DUMMY_SECRET"} + } else { + c.Log("Using REAL AMAZON SERVER") + dynamodb_region = aws.USEast + auth, err := aws.EnvAuth() + if err != nil { + c.Fatal(err) + } + dynamodb_auth = auth + } +} + +func findTableByName(tables []string, name string) bool { + for _, t := range tables { + if t == name { + return true + } + } + return false +} + +func Test(t *testing.T) { + TestingT(t) +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/item.go b/vendor/github.com/goamz/goamz/dynamodb/item.go new file mode 100755 index 000000000..a3814d9ad --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/item.go @@ -0,0 +1,351 @@ +package dynamodb + +import simplejson "github.com/bitly/go-simplejson" +import ( + "errors" + "fmt" + "log" +) + +type BatchGetItem struct { + Server *Server + Keys map[*Table][]Key +} + +type BatchWriteItem struct { + Server *Server + ItemActions map[*Table]map[string][][]Attribute +} + +func (t *Table) BatchGetItems(keys []Key) *BatchGetItem { + batchGetItem := &BatchGetItem{t.Server, make(map[*Table][]Key)} + + batchGetItem.Keys[t] = keys + return batchGetItem +} + +func (t *Table) BatchWriteItems(itemActions map[string][][]Attribute) *BatchWriteItem { + batchWriteItem := &BatchWriteItem{t.Server, make(map[*Table]map[string][][]Attribute)} + + batchWriteItem.ItemActions[t] = itemActions + return batchWriteItem +} + +func (batchGetItem *BatchGetItem) AddTable(t *Table, keys *[]Key) *BatchGetItem { + batchGetItem.Keys[t] = *keys + return batchGetItem +} + +func (batchWriteItem *BatchWriteItem) AddTable(t *Table, itemActions *map[string][][]Attribute) *BatchWriteItem { + batchWriteItem.ItemActions[t] = *itemActions + return batchWriteItem +} + +func (batchGetItem *BatchGetItem) Execute() (map[string][]map[string]*Attribute, error) { + q := NewEmptyQuery() + q.AddGetRequestItems(batchGetItem.Keys) + + jsonResponse, err := batchGetItem.Server.queryServer("DynamoDB_20120810.BatchGetItem", q) + if err != nil { + return nil, err + } + + json, err := simplejson.NewJson(jsonResponse) + + if err != nil { + return nil, err + } + + results := make(map[string][]map[string]*Attribute) + + tables, err := json.Get("Responses").Map() + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + + for table, entries := range tables { + var tableResult []map[string]*Attribute + + jsonEntriesArray, ok := entries.([]interface{}) + if !ok { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + + for _, entry := range jsonEntriesArray { + item, ok := entry.(map[string]interface{}) + if !ok { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + + unmarshalledItem := parseAttributes(item) + tableResult = append(tableResult, unmarshalledItem) + } + + results[table] = tableResult + } + + return results, nil +} + +func (batchWriteItem *BatchWriteItem) Execute() (map[string]interface{}, error) { + q := NewEmptyQuery() + q.AddWriteRequestItems(batchWriteItem.ItemActions) + + jsonResponse, err := batchWriteItem.Server.queryServer("DynamoDB_20120810.BatchWriteItem", q) + + if err != nil { + return nil, err + } + + json, err := simplejson.NewJson(jsonResponse) + + if err != nil { + return nil, err + } + + unprocessed, err := json.Get("UnprocessedItems").Map() + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + + if len(unprocessed) == 0 { + return nil, nil + } else { + return unprocessed, errors.New("One or more unprocessed items.") + } + +} + +func (t *Table) GetItem(key *Key) (map[string]*Attribute, error) { + return t.getItem(key, false) +} + +func (t *Table) GetItemConsistent(key *Key, consistentRead bool) (map[string]*Attribute, error) { + return t.getItem(key, consistentRead) +} + +func (t *Table) getItem(key *Key, consistentRead bool) (map[string]*Attribute, error) { + q := NewQuery(t) + q.AddKey(t, key) + + if consistentRead { + q.ConsistentRead(consistentRead) + } + + jsonResponse, err := t.Server.queryServer(target("GetItem"), q) + if err != nil { + return nil, err + } + + json, err := simplejson.NewJson(jsonResponse) + if err != nil { + return nil, err + } + + itemJson, ok := json.CheckGet("Item") + if !ok { + // We got an empty from amz. The item doesn't exist. + return nil, ErrNotFound + } + + item, err := itemJson.Map() + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + + return parseAttributes(item), nil + +} + +func (t *Table) PutItem(hashKey string, rangeKey string, attributes []Attribute) (bool, error) { + return t.putItem(hashKey, rangeKey, attributes, nil) +} + +func (t *Table) ConditionalPutItem(hashKey, rangeKey string, attributes, expected []Attribute) (bool, error) { + return t.putItem(hashKey, rangeKey, attributes, expected) +} + +func (t *Table) putItem(hashKey, rangeKey string, attributes, expected []Attribute) (bool, error) { + if len(attributes) == 0 { + return false, errors.New("At least one attribute is required.") + } + + q := NewQuery(t) + + keys := t.Key.Clone(hashKey, rangeKey) + attributes = append(attributes, keys...) + + q.AddItem(attributes) + if expected != nil { + q.AddExpected(expected) + } + + jsonResponse, err := t.Server.queryServer(target("PutItem"), q) + + if err != nil { + return false, err + } + + _, err = simplejson.NewJson(jsonResponse) + if err != nil { + return false, err + } + + return true, nil +} + +func (t *Table) deleteItem(key *Key, expected []Attribute) (bool, error) { + q := NewQuery(t) + q.AddKey(t, key) + + if expected != nil { + q.AddExpected(expected) + } + + jsonResponse, err := t.Server.queryServer(target("DeleteItem"), q) + + if err != nil { + return false, err + } + + _, err = simplejson.NewJson(jsonResponse) + if err != nil { + return false, err + } + + return true, nil +} + +func (t *Table) DeleteItem(key *Key) (bool, error) { + return t.deleteItem(key, nil) +} + +func (t *Table) ConditionalDeleteItem(key *Key, expected []Attribute) (bool, error) { + return t.deleteItem(key, expected) +} + +func (t *Table) AddAttributes(key *Key, attributes []Attribute) (bool, error) { + return t.modifyAttributes(key, attributes, nil, "ADD") +} + +func (t *Table) UpdateAttributes(key *Key, attributes []Attribute) (bool, error) { + return t.modifyAttributes(key, attributes, nil, "PUT") +} + +func (t *Table) DeleteAttributes(key *Key, attributes []Attribute) (bool, error) { + return t.modifyAttributes(key, attributes, nil, "DELETE") +} + +func (t *Table) ConditionalAddAttributes(key *Key, attributes, expected []Attribute) (bool, error) { + return t.modifyAttributes(key, attributes, expected, "ADD") +} + +func (t *Table) ConditionalUpdateAttributes(key *Key, attributes, expected []Attribute) (bool, error) { + return t.modifyAttributes(key, attributes, expected, "PUT") +} + +func (t *Table) ConditionalDeleteAttributes(key *Key, attributes, expected []Attribute) (bool, error) { + return t.modifyAttributes(key, attributes, expected, "DELETE") +} + +func (t *Table) modifyAttributes(key *Key, attributes, expected []Attribute, action string) (bool, error) { + + if len(attributes) == 0 { + return false, errors.New("At least one attribute is required.") + } + + q := NewQuery(t) + q.AddKey(t, key) + q.AddUpdates(attributes, action) + + if expected != nil { + q.AddExpected(expected) + } + + jsonResponse, err := t.Server.queryServer(target("UpdateItem"), q) + + if err != nil { + return false, err + } + + _, err = simplejson.NewJson(jsonResponse) + if err != nil { + return false, err + } + + return true, nil +} + +func parseAttributes(s map[string]interface{}) map[string]*Attribute { + results := map[string]*Attribute{} + + for key, value := range s { + if v, ok := value.(map[string]interface{}); ok { + if val, ok := v[TYPE_STRING].(string); ok { + results[key] = &Attribute{ + Type: TYPE_STRING, + Name: key, + Value: val, + } + } else if val, ok := v[TYPE_NUMBER].(string); ok { + results[key] = &Attribute{ + Type: TYPE_NUMBER, + Name: key, + Value: val, + } + } else if val, ok := v[TYPE_BINARY].(string); ok { + results[key] = &Attribute{ + Type: TYPE_BINARY, + Name: key, + Value: val, + } + } else if vals, ok := v[TYPE_STRING_SET].([]interface{}); ok { + arry := make([]string, len(vals)) + for i, ivalue := range vals { + if val, ok := ivalue.(string); ok { + arry[i] = val + } + } + results[key] = &Attribute{ + Type: TYPE_STRING_SET, + Name: key, + SetValues: arry, + } + } else if vals, ok := v[TYPE_NUMBER_SET].([]interface{}); ok { + arry := make([]string, len(vals)) + for i, ivalue := range vals { + if val, ok := ivalue.(string); ok { + arry[i] = val + } + } + results[key] = &Attribute{ + Type: TYPE_NUMBER_SET, + Name: key, + SetValues: arry, + } + } else if vals, ok := v[TYPE_BINARY_SET].([]interface{}); ok { + arry := make([]string, len(vals)) + for i, ivalue := range vals { + if val, ok := ivalue.(string); ok { + arry[i] = val + } + } + results[key] = &Attribute{ + Type: TYPE_BINARY_SET, + Name: key, + SetValues: arry, + } + } + } else { + log.Printf("type assertion to map[string] interface{} failed for : %s\n ", value) + } + + } + + return results +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/item_test.go b/vendor/github.com/goamz/goamz/dynamodb/item_test.go new file mode 100644 index 000000000..37b4b8838 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/item_test.go @@ -0,0 +1,446 @@ +package dynamodb_test + +import ( + "github.com/goamz/goamz/dynamodb" + . "gopkg.in/check.v1" +) + +type ItemSuite struct { + TableDescriptionT dynamodb.TableDescriptionT + DynamoDBTest + WithRange bool +} + +func (s *ItemSuite) SetUpSuite(c *C) { + setUpAuth(c) + s.DynamoDBTest.TableDescriptionT = s.TableDescriptionT + s.server = &dynamodb.Server{dynamodb_auth, dynamodb_region} + pk, err := s.TableDescriptionT.BuildPrimaryKey() + if err != nil { + c.Skip(err.Error()) + } + s.table = s.server.NewTable(s.TableDescriptionT.TableName, pk) + + // Cleanup + s.TearDownSuite(c) + _, err = s.server.CreateTable(s.TableDescriptionT) + if err != nil { + c.Fatal(err) + } + s.WaitUntilStatus(c, "ACTIVE") +} + +var item_suite = &ItemSuite{ + TableDescriptionT: dynamodb.TableDescriptionT{ + TableName: "DynamoDBTestMyTable", + AttributeDefinitions: []dynamodb.AttributeDefinitionT{ + dynamodb.AttributeDefinitionT{"TestHashKey", "S"}, + dynamodb.AttributeDefinitionT{"TestRangeKey", "N"}, + }, + KeySchema: []dynamodb.KeySchemaT{ + dynamodb.KeySchemaT{"TestHashKey", "HASH"}, + dynamodb.KeySchemaT{"TestRangeKey", "RANGE"}, + }, + ProvisionedThroughput: dynamodb.ProvisionedThroughputT{ + ReadCapacityUnits: 1, + WriteCapacityUnits: 1, + }, + }, + WithRange: true, +} + +var item_without_range_suite = &ItemSuite{ + TableDescriptionT: dynamodb.TableDescriptionT{ + TableName: "DynamoDBTestMyTable", + AttributeDefinitions: []dynamodb.AttributeDefinitionT{ + dynamodb.AttributeDefinitionT{"TestHashKey", "S"}, + }, + KeySchema: []dynamodb.KeySchemaT{ + dynamodb.KeySchemaT{"TestHashKey", "HASH"}, + }, + ProvisionedThroughput: dynamodb.ProvisionedThroughputT{ + ReadCapacityUnits: 1, + WriteCapacityUnits: 1, + }, + }, + WithRange: false, +} + +var _ = Suite(item_suite) +var _ = Suite(item_without_range_suite) + +func (s *ItemSuite) TestConditionalPutUpdateDeleteItem(c *C) { + if s.WithRange { + // No rangekey test required + return + } + + attrs := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("Attr1", "Attr1Val"), + } + pk := &dynamodb.Key{HashKey: "NewHashKeyVal"} + + // Put + if ok, err := s.table.PutItem("NewHashKeyVal", "", attrs); !ok { + c.Fatal(err) + } + + { + // Put with condition failed + expected := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("Attr1", "expectedAttr1Val").SetExists(true), + *dynamodb.NewStringAttribute("AttrNotExists", "").SetExists(false), + } + if ok, err := s.table.ConditionalPutItem("NewHashKeyVal", "", attrs, expected); ok { + c.Errorf("Expect condition does not meet.") + } else { + c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*") + } + + // Add attributes with condition failed + if ok, err := s.table.ConditionalAddAttributes(pk, attrs, expected); ok { + c.Errorf("Expect condition does not meet.") + } else { + c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*") + } + + // Update attributes with condition failed + if ok, err := s.table.ConditionalUpdateAttributes(pk, attrs, expected); ok { + c.Errorf("Expect condition does not meet.") + } else { + c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*") + } + + // Delete attributes with condition failed + if ok, err := s.table.ConditionalDeleteAttributes(pk, attrs, expected); ok { + c.Errorf("Expect condition does not meet.") + } else { + c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*") + } + } + + { + expected := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("Attr1", "Attr1Val").SetExists(true), + } + + // Add attributes with condition met + addNewAttrs := []dynamodb.Attribute{ + *dynamodb.NewNumericAttribute("AddNewAttr1", "10"), + *dynamodb.NewNumericAttribute("AddNewAttr2", "20"), + } + if ok, err := s.table.ConditionalAddAttributes(pk, addNewAttrs, nil); !ok { + c.Errorf("Expect condition met. %s", err) + } + + // Update attributes with condition met + updateAttrs := []dynamodb.Attribute{ + *dynamodb.NewNumericAttribute("AddNewAttr1", "100"), + } + if ok, err := s.table.ConditionalUpdateAttributes(pk, updateAttrs, expected); !ok { + c.Errorf("Expect condition met. %s", err) + } + + // Delete attributes with condition met + deleteAttrs := []dynamodb.Attribute{ + *dynamodb.NewNumericAttribute("AddNewAttr2", ""), + } + if ok, err := s.table.ConditionalDeleteAttributes(pk, deleteAttrs, expected); !ok { + c.Errorf("Expect condition met. %s", err) + } + + // Get to verify operations that condition are met + item, err := s.table.GetItem(pk) + if err != nil { + c.Fatal(err) + } + + if val, ok := item["AddNewAttr1"]; ok { + c.Check(val, DeepEquals, dynamodb.NewNumericAttribute("AddNewAttr1", "100")) + } else { + c.Error("Expect AddNewAttr1 attribute to be added and updated") + } + + if _, ok := item["AddNewAttr2"]; ok { + c.Error("Expect AddNewAttr2 attribute to be deleted") + } + } + + { + // Put with condition met + expected := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("Attr1", "Attr1Val").SetExists(true), + } + newattrs := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("Attr1", "Attr2Val"), + } + if ok, err := s.table.ConditionalPutItem("NewHashKeyVal", "", newattrs, expected); !ok { + c.Errorf("Expect condition met. %s", err) + } + + // Get to verify Put operation that condition are met + item, err := s.table.GetItem(pk) + if err != nil { + c.Fatal(err) + } + + if val, ok := item["Attr1"]; ok { + c.Check(val, DeepEquals, dynamodb.NewStringAttribute("Attr1", "Attr2Val")) + } else { + c.Error("Expect Attr1 attribute to be updated") + } + } + + { + // Delete with condition failed + expected := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("Attr1", "expectedAttr1Val").SetExists(true), + } + if ok, err := s.table.ConditionalDeleteItem(pk, expected); ok { + c.Errorf("Expect condition does not meet.") + } else { + c.Check(err.Error(), Matches, "ConditionalCheckFailedException.*") + } + } + + { + // Delete with condition met + expected := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("Attr1", "Attr2Val").SetExists(true), + } + if ok, _ := s.table.ConditionalDeleteItem(pk, expected); !ok { + c.Errorf("Expect condition met.") + } + + // Get to verify Delete operation + _, err := s.table.GetItem(pk) + c.Check(err.Error(), Matches, "Item not found") + } +} + +func (s *ItemSuite) TestPutGetDeleteItem(c *C) { + attrs := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("Attr1", "Attr1Val"), + } + + var rk string + if s.WithRange { + rk = "1" + } + + // Put + if ok, err := s.table.PutItem("NewHashKeyVal", rk, attrs); !ok { + c.Fatal(err) + } + + // Get to verify Put operation + pk := &dynamodb.Key{HashKey: "NewHashKeyVal", RangeKey: rk} + item, err := s.table.GetItem(pk) + if err != nil { + c.Fatal(err) + } + + if val, ok := item["TestHashKey"]; ok { + c.Check(val, DeepEquals, dynamodb.NewStringAttribute("TestHashKey", "NewHashKeyVal")) + } else { + c.Error("Expect TestHashKey to be found") + } + + if s.WithRange { + if val, ok := item["TestRangeKey"]; ok { + c.Check(val, DeepEquals, dynamodb.NewNumericAttribute("TestRangeKey", "1")) + } else { + c.Error("Expect TestRangeKey to be found") + } + } + + // Delete + if ok, _ := s.table.DeleteItem(pk); !ok { + c.Fatal(err) + } + + // Get to verify Delete operation + _, err = s.table.GetItem(pk) + c.Check(err.Error(), Matches, "Item not found") +} + +func (s *ItemSuite) TestUpdateItem(c *C) { + attrs := []dynamodb.Attribute{ + *dynamodb.NewNumericAttribute("count", "0"), + } + + var rk string + if s.WithRange { + rk = "1" + } + + if ok, err := s.table.PutItem("NewHashKeyVal", rk, attrs); !ok { + c.Fatal(err) + } + + // UpdateItem with Add + attrs = []dynamodb.Attribute{ + *dynamodb.NewNumericAttribute("count", "10"), + } + pk := &dynamodb.Key{HashKey: "NewHashKeyVal", RangeKey: rk} + if ok, err := s.table.AddAttributes(pk, attrs); !ok { + c.Error(err) + } + + // Get to verify Add operation + if item, err := s.table.GetItemConsistent(pk, true); err != nil { + c.Error(err) + } else { + if val, ok := item["count"]; ok { + c.Check(val, DeepEquals, dynamodb.NewNumericAttribute("count", "10")) + } else { + c.Error("Expect count to be found") + } + } + + // UpdateItem with Put + attrs = []dynamodb.Attribute{ + *dynamodb.NewNumericAttribute("count", "100"), + } + if ok, err := s.table.UpdateAttributes(pk, attrs); !ok { + c.Error(err) + } + + // Get to verify Put operation + if item, err := s.table.GetItem(pk); err != nil { + c.Fatal(err) + } else { + if val, ok := item["count"]; ok { + c.Check(val, DeepEquals, dynamodb.NewNumericAttribute("count", "100")) + } else { + c.Error("Expect count to be found") + } + } + + // UpdateItem with Delete + attrs = []dynamodb.Attribute{ + *dynamodb.NewNumericAttribute("count", ""), + } + if ok, err := s.table.DeleteAttributes(pk, attrs); !ok { + c.Error(err) + } + + // Get to verify Delete operation + if item, err := s.table.GetItem(pk); err != nil { + c.Error(err) + } else { + if _, ok := item["count"]; ok { + c.Error("Expect count not to be found") + } + } +} + +func (s *ItemSuite) TestUpdateItemWithSet(c *C) { + attrs := []dynamodb.Attribute{ + *dynamodb.NewStringSetAttribute("list", []string{"A", "B"}), + } + + var rk string + if s.WithRange { + rk = "1" + } + + if ok, err := s.table.PutItem("NewHashKeyVal", rk, attrs); !ok { + c.Error(err) + } + + // UpdateItem with Add + attrs = []dynamodb.Attribute{ + *dynamodb.NewStringSetAttribute("list", []string{"C"}), + } + pk := &dynamodb.Key{HashKey: "NewHashKeyVal", RangeKey: rk} + if ok, err := s.table.AddAttributes(pk, attrs); !ok { + c.Error(err) + } + + // Get to verify Add operation + if item, err := s.table.GetItem(pk); err != nil { + c.Error(err) + } else { + if val, ok := item["list"]; ok { + c.Check(val, DeepEquals, dynamodb.NewStringSetAttribute("list", []string{"A", "B", "C"})) + } else { + c.Error("Expect count to be found") + } + } + + // UpdateItem with Delete + attrs = []dynamodb.Attribute{ + *dynamodb.NewStringSetAttribute("list", []string{"A"}), + } + if ok, err := s.table.DeleteAttributes(pk, attrs); !ok { + c.Error(err) + } + + // Get to verify Delete operation + if item, err := s.table.GetItem(pk); err != nil { + c.Error(err) + } else { + if val, ok := item["list"]; ok { + c.Check(val, DeepEquals, dynamodb.NewStringSetAttribute("list", []string{"B", "C"})) + } else { + c.Error("Expect list to be remained") + } + } +} + +func (s *ItemSuite) TestUpdateItem_new(c *C) { + attrs := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("intval", "1"), + } + var rk string + if s.WithRange { + rk = "1" + } + pk := &dynamodb.Key{HashKey: "UpdateKeyVal", RangeKey: rk} + + num := func(a, b string) dynamodb.Attribute { + return *dynamodb.NewNumericAttribute(a, b) + } + + checkVal := func(i string) { + if item, err := s.table.GetItem(pk); err != nil { + c.Error(err) + } else { + c.Check(item["intval"], DeepEquals, dynamodb.NewNumericAttribute("intval", i)) + } + } + + if ok, err := s.table.PutItem("UpdateKeyVal", rk, attrs); !ok { + c.Error(err) + } + checkVal("1") + + // Simple Increment + s.table.UpdateItem(pk).UpdateExpression("SET intval = intval + :incr", num(":incr", "5")).Execute() + checkVal("6") + + conditionalUpdate := func(check string) { + s.table.UpdateItem(pk). + ConditionExpression("intval = :check"). + UpdateExpression("SET intval = intval + :incr"). + ExpressionAttributes(num(":check", check), num(":incr", "4")). + Execute() + } + // Conditional increment should be a no-op. + conditionalUpdate("42") + checkVal("6") + + // conditional increment should succeed this time + conditionalUpdate("6") + checkVal("10") + + // Update with new values getting values + result, err := s.table.UpdateItem(pk). + ReturnValues(dynamodb.UPDATED_NEW). + UpdateExpression("SET intval = intval + :incr", num(":incr", "2")). + Execute() + c.Check(err, IsNil) + c.Check(result.Attributes["intval"], DeepEquals, num("intval", "12")) + checkVal("12") +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/marshaller.go b/vendor/github.com/goamz/goamz/dynamodb/marshaller.go new file mode 100644 index 000000000..2898fbda9 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/marshaller.go @@ -0,0 +1,626 @@ +package dynamodb + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "math" + "reflect" + "sort" + "strconv" + "strings" + "sync" + "unicode" +) + +func MarshalAttributes(m interface{}) ([]Attribute, error) { + v := reflect.ValueOf(m).Elem() + + builder := &attributeBuilder{} + builder.buffer = []Attribute{} + for _, f := range cachedTypeFields(v.Type()) { // loop on each field + fv := fieldByIndex(v, f.index) + if !fv.IsValid() || isEmptyValueToOmit(fv) { + continue + } + + err := builder.reflectToDynamoDBAttribute(f.name, fv) + if err != nil { + return builder.buffer, err + } + } + + return builder.buffer, nil +} + +func UnmarshalAttributes(attributesRef *map[string]*Attribute, m interface{}) error { + rv := reflect.ValueOf(m) + if rv.Kind() != reflect.Ptr || rv.IsNil() { + return fmt.Errorf("InvalidUnmarshalError reflect.ValueOf(v): %#v, m interface{}: %#v", rv, reflect.TypeOf(m)) + } + + v := reflect.ValueOf(m).Elem() + + attributes := *attributesRef + for _, f := range cachedTypeFields(v.Type()) { // loop on each field + fv := fieldByIndex(v, f.index) + correlatedAttribute := attributes[f.name] + if correlatedAttribute == nil { + continue + } + err := unmarshallAttribute(correlatedAttribute, fv) + if err != nil { + return err + } + } + + return nil +} + +type attributeBuilder struct { + buffer []Attribute +} + +func (builder *attributeBuilder) Push(attribute *Attribute) { + builder.buffer = append(builder.buffer, *attribute) +} + +func unmarshallAttribute(a *Attribute, v reflect.Value) error { + switch v.Kind() { + case reflect.Bool: + n, err := strconv.ParseInt(a.Value, 10, 64) + if err != nil { + return fmt.Errorf("UnmarshalTypeError (bool) %#v: %#v", a.Value, err) + } + v.SetBool(n != 0) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.ParseInt(a.Value, 10, 64) + if err != nil || v.OverflowInt(n) { + return fmt.Errorf("UnmarshalTypeError (number) %#v: %#v", a.Value, err) + } + v.SetInt(n) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.ParseUint(a.Value, 10, 64) + if err != nil || v.OverflowUint(n) { + return fmt.Errorf("UnmarshalTypeError (number) %#v: %#v", a.Value, err) + } + v.SetUint(n) + + case reflect.Float32, reflect.Float64: + n, err := strconv.ParseFloat(a.Value, v.Type().Bits()) + if err != nil || v.OverflowFloat(n) { + return fmt.Errorf("UnmarshalTypeError (number) %#v: %#v", a.Value, err) + } + v.SetFloat(n) + + case reflect.String: + v.SetString(a.Value) + + case reflect.Slice: + if v.Type().Elem().Kind() == reflect.Uint8 { // byte arrays are a special case + b := make([]byte, base64.StdEncoding.DecodedLen(len(a.Value))) + n, err := base64.StdEncoding.Decode(b, []byte(a.Value)) + if err != nil { + return fmt.Errorf("UnmarshalTypeError (byte) %#v: %#v", a.Value, err) + } + v.Set(reflect.ValueOf(b[0:n])) + break + } + + if a.SetType() { // Special NS and SS types should be correctly handled + nativeSetCreated := false + switch v.Type().Elem().Kind() { + case reflect.Bool: + nativeSetCreated = true + arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues)) + for i, aval := range a.SetValues { + n, err := strconv.ParseInt(aval, 10, 64) + if err != nil { + return fmt.Errorf("UnmarshalSetTypeError (bool) %#v: %#v", aval, err) + } + arry.Index(i).SetBool(n != 0) + } + v.Set(arry) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + nativeSetCreated = true + arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues)) + for i, aval := range a.SetValues { + n, err := strconv.ParseInt(aval, 10, 64) + if err != nil || arry.Index(i).OverflowInt(n) { + return fmt.Errorf("UnmarshalSetTypeError (number) %#v: %#v", aval, err) + } + arry.Index(i).SetInt(n) + } + v.Set(arry) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + nativeSetCreated = true + arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues)) + for i, aval := range a.SetValues { + n, err := strconv.ParseUint(aval, 10, 64) + if err != nil || arry.Index(i).OverflowUint(n) { + return fmt.Errorf("UnmarshalSetTypeError (number) %#v: %#v", aval, err) + } + arry.Index(i).SetUint(n) + } + v.Set(arry) + + case reflect.Float32, reflect.Float64: + nativeSetCreated = true + arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues)) + for i, aval := range a.SetValues { + n, err := strconv.ParseFloat(aval, arry.Index(i).Type().Bits()) + if err != nil || arry.Index(i).OverflowFloat(n) { + return fmt.Errorf("UnmarshalSetTypeError (number) %#v: %#v", aval, err) + } + arry.Index(i).SetFloat(n) + } + v.Set(arry) + + case reflect.String: + nativeSetCreated = true + arry := reflect.MakeSlice(v.Type(), len(a.SetValues), len(a.SetValues)) + for i, aval := range a.SetValues { + arry.Index(i).SetString(aval) + } + v.Set(arry) + } + + if nativeSetCreated { + break + } + } + + // Slices can be marshalled as nil, but otherwise are handled + // as arrays. + fallthrough + case reflect.Array, reflect.Struct, reflect.Map, reflect.Interface, reflect.Ptr: + unmarshalled := reflect.New(v.Type()) + err := json.Unmarshal([]byte(a.Value), unmarshalled.Interface()) + if err != nil { + return err + } + v.Set(unmarshalled.Elem()) + + default: + return fmt.Errorf("UnsupportedTypeError %#v", v.Type()) + } + + return nil +} + +// reflectValueQuoted writes the value in v to the output. +// If quoted is true, the serialization is wrapped in a JSON string. +func (e *attributeBuilder) reflectToDynamoDBAttribute(name string, v reflect.Value) error { + if !v.IsValid() { + return nil + } // don't build + + switch v.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.Uintptr, reflect.Float32, reflect.Float64: + rv, err := numericReflectedValueString(v) + if err != nil { + return err + } + e.Push(NewNumericAttribute(name, rv)) + + case reflect.String: + e.Push(NewStringAttribute(name, v.String())) + + case reflect.Slice: + if v.IsNil() { + break + } + if v.Type().Elem().Kind() == reflect.Uint8 { + // Byte slices are treated as errors + s := v.Bytes() + dst := make([]byte, base64.StdEncoding.EncodedLen(len(s))) + base64.StdEncoding.Encode(dst, s) + e.Push(NewStringAttribute(name, string(dst))) + break + } + + // Special NS and SS types should be correctly handled + nativeSetCreated := false + switch v.Type().Elem().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.Uintptr, reflect.Float32, reflect.Float64: + nativeSetCreated = true + arrystrings := make([]string, v.Len()) + for i, _ := range arrystrings { + var err error + arrystrings[i], err = numericReflectedValueString(v.Index(i)) + if err != nil { + return err + } + } + e.Push(NewNumericSetAttribute(name, arrystrings)) + case reflect.String: // simple copy will suffice + nativeSetCreated = true + arrystrings := make([]string, v.Len()) + for i, _ := range arrystrings { + arrystrings[i] = v.Index(i).String() + } + e.Push(NewStringSetAttribute(name, arrystrings)) + } + + if nativeSetCreated { + break + } + + // Slices can be marshalled as nil, but otherwise are handled + // as arrays. + fallthrough + case reflect.Array, reflect.Struct, reflect.Map, reflect.Interface, reflect.Ptr: + jsonVersion, err := json.Marshal(v.Interface()) + if err != nil { + return err + } + escapedJson := `"` + string(jsonVersion) + `"` // strconv.Quote not required because the entire string is escaped from json Marshall + e.Push(NewStringAttribute(name, escapedJson[1:len(escapedJson)-1])) + + default: + return fmt.Errorf("UnsupportedTypeError %#v", v.Type()) + } + return nil +} + +func numericReflectedValueString(v reflect.Value) (string, error) { + switch v.Kind() { + case reflect.Bool: + x := v.Bool() + if x { + return "1", nil + } else { + return "0", nil + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(v.Int(), 10), nil + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return strconv.FormatUint(v.Uint(), 10), nil + + case reflect.Float32, reflect.Float64: + f := v.Float() + if math.IsInf(f, 0) || math.IsNaN(f) { + return "", fmt.Errorf("UnsupportedValueError %#v (formatted float: %s)", v, strconv.FormatFloat(f, 'g', -1, v.Type().Bits())) + } + return strconv.FormatFloat(f, 'g', -1, v.Type().Bits()), nil + } + return "", fmt.Errorf("UnsupportedNumericValueError %#v", v.Type()) +} + +// In DynamoDB we should omit empty value in some type +// See http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html +func isEmptyValueToOmit(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String, reflect.Interface, reflect.Ptr: + // should omit if empty value + return isEmptyValue(v) + } + // otherwise should not omit + return false +} + +// ---------------- Below are copied handy functions from http://golang.org/src/pkg/encoding/json/encode.go -------------------------------- +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} + +func fieldByIndex(v reflect.Value, index []int) reflect.Value { + for _, i := range index { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return reflect.Value{} + } + v = v.Elem() + } + v = v.Field(i) + } + return v +} + +// A field represents a single field found in a struct. +type field struct { + name string + tag bool + index []int + typ reflect.Type + omitEmpty bool + quoted bool +} + +// byName sorts field by name, breaking ties with depth, +// then breaking ties with "name came from json tag", then +// breaking ties with index sequence. +type byName []field + +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byName) Less(i, j int) bool { + if x[i].name != x[j].name { + return x[i].name < x[j].name + } + if len(x[i].index) != len(x[j].index) { + return len(x[i].index) < len(x[j].index) + } + if x[i].tag != x[j].tag { + return x[i].tag + } + return byIndex(x).Less(i, j) +} + +// byIndex sorts field by index sequence. +type byIndex []field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + for k, xik := range x[i].index { + if k >= len(x[j].index) { + return false + } + if xik != x[j].index[k] { + return xik < x[j].index[k] + } + } + return len(x[i].index) < len(x[j].index) +} + +func isValidTag(s string) bool { + if s == "" { + return false + } + for _, c := range s { + switch { + case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): + // Backslash and quote chars are reserved, but + // otherwise any punctuation chars are allowed + // in a tag name. + default: + if !unicode.IsLetter(c) && !unicode.IsDigit(c) { + return false + } + } + } + return true +} + +// tagOptions is the string following a comma in a struct field's "json" +// tag, or the empty string. It does not include the leading comma. +type tagOptions string + +// Contains returns whether checks that a comma-separated list of options +// contains a particular substr flag. substr must be surrounded by a +// string boundary or commas. +func (o tagOptions) Contains(optionName string) bool { + if len(o) == 0 { + return false + } + s := string(o) + for s != "" { + var next string + i := strings.Index(s, ",") + if i >= 0 { + s, next = s[:i], s[i+1:] + } + if s == optionName { + return true + } + s = next + } + return false +} + +// parseTag splits a struct field's json tag into its name and +// comma-separated options. +func parseTag(tag string) (string, tagOptions) { + if idx := strings.Index(tag, ","); idx != -1 { + return tag[:idx], tagOptions(tag[idx+1:]) + } + return tag, tagOptions("") +} + +// typeFields returns a list of fields that JSON should recognize for the given type. +// The algorithm is breadth-first search over the set of structs to include - the top struct +// and then any reachable anonymous structs. +func typeFields(t reflect.Type) []field { + // Anonymous fields to explore at the current level and the next. + current := []field{} + next := []field{{typ: t}} + + // Count of queued names for current level and the next. + count := map[reflect.Type]int{} + nextCount := map[reflect.Type]int{} + + // Types already visited at an earlier level. + visited := map[reflect.Type]bool{} + + // Fields found. + var fields []field + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, map[reflect.Type]int{} + + for _, f := range current { + if visited[f.typ] { + continue + } + visited[f.typ] = true + + // Scan f.typ for fields to include. + for i := 0; i < f.typ.NumField(); i++ { + sf := f.typ.Field(i) + if sf.PkgPath != "" { // unexported + continue + } + tag := sf.Tag.Get("json") + if tag == "-" { + continue + } + name, opts := parseTag(tag) + if !isValidTag(name) { + name = "" + } + index := make([]int, len(f.index)+1) + copy(index, f.index) + index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + + // Record found field and index sequence. + if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + tagged := name != "" + if name == "" { + name = sf.Name + } + fields = append(fields, field{name, tagged, index, ft, + opts.Contains("omitempty"), opts.Contains("string")}) + if count[f.typ] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + // It only cares about the distinction between 1 or 2, + // so don't bother generating any more copies. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Record new anonymous struct to explore in next round. + nextCount[ft]++ + if nextCount[ft] == 1 { + next = append(next, field{name: ft.Name(), index: index, typ: ft}) + } + } + } + } + + sort.Sort(byName(fields)) + + // Delete all fields that are hidden by the Go rules for embedded fields, + // except that fields with JSON tags are promoted. + + // The fields are sorted in primary order of name, secondary order + // of field index length. Loop over names; for each name, delete + // hidden fields by choosing the one dominant field that survives. + out := fields[:0] + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.name != name { + break + } + } + if advance == 1 { // Only one field with this name + out = append(out, fi) + continue + } + dominant, ok := dominantField(fields[i : i+advance]) + if ok { + out = append(out, dominant) + } + } + + fields = out + sort.Sort(byIndex(fields)) + + return fields +} + +// dominantField looks through the fields, all of which are known to +// have the same name, to find the single field that dominates the +// others using Go's embedding rules, modified by the presence of +// JSON tags. If there are multiple top-level fields, the boolean +// will be false: This condition is an error in Go and we skip all +// the fields. +func dominantField(fields []field) (field, bool) { + // The fields are sorted in increasing index-length order. The winner + // must therefore be one with the shortest index length. Drop all + // longer entries, which is easy: just truncate the slice. + length := len(fields[0].index) + tagged := -1 // Index of first tagged field. + for i, f := range fields { + if len(f.index) > length { + fields = fields[:i] + break + } + if f.tag { + if tagged >= 0 { + // Multiple tagged fields at the same level: conflict. + // Return no field. + return field{}, false + } + tagged = i + } + } + if tagged >= 0 { + return fields[tagged], true + } + // All remaining fields have the same length. If there's more than one, + // we have a conflict (two fields named "X" at the same level) and we + // return no field. + if len(fields) > 1 { + return field{}, false + } + return fields[0], true +} + +var fieldCache struct { + sync.RWMutex + m map[reflect.Type][]field +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +func cachedTypeFields(t reflect.Type) []field { + fieldCache.RLock() + f := fieldCache.m[t] + fieldCache.RUnlock() + if f != nil { + return f + } + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = typeFields(t) + if f == nil { + f = []field{} + } + + fieldCache.Lock() + if fieldCache.m == nil { + fieldCache.m = map[reflect.Type][]field{} + } + fieldCache.m[t] = f + fieldCache.Unlock() + return f +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/marshaller_test.go b/vendor/github.com/goamz/goamz/dynamodb/marshaller_test.go new file mode 100644 index 000000000..8b9d2fc08 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/marshaller_test.go @@ -0,0 +1,283 @@ +package dynamodb_test + +import ( + "time" + + "github.com/goamz/goamz/dynamodb" + . "gopkg.in/check.v1" +) + +type TestSubStruct struct { + SubBool bool + SubInt int + SubString string + SubStringArray []string +} + +type TestStruct struct { + TestBool bool + TestInt int + TestInt32 int32 + TestInt64 int64 + TestUint uint + TestFloat32 float32 + TestFloat64 float64 + TestString string + TestByteArray []byte + TestStringArray []string + TestIntArray []int + TestInt8Array []int8 + TestFloatArray []float64 + TestSub TestSubStruct +} + +type TestStructTime struct { + TestTime time.Time +} + +func testObject() *TestStruct { + return &TestStruct{ + TestBool: true, + TestInt: -99, + TestInt32: 999, + TestInt64: 9999, + TestUint: 99, + TestFloat32: 9.9999, + TestFloat64: 99.999999, + TestString: "test", + TestByteArray: []byte("bytes"), + TestStringArray: []string{"test1", "test2", "test3", "test4"}, + TestIntArray: []int{0, 1, 12, 123, 1234, 12345}, + TestInt8Array: []int8{0, 1, 12, 123}, + TestFloatArray: []float64{0.1, 1.1, 1.2, 1.23, 1.234, 1.2345}, + TestSub: TestSubStruct{ + SubBool: true, + SubInt: 2, + SubString: "subtest", + SubStringArray: []string{"sub1", "sub2", "sub3"}, + }, + } +} + +func testObjectTime() *TestStructTime { + t, _ := time.Parse("Jan 2, 2006 at 3:04pm", "Mar 3, 2003 at 5:03pm") + return &TestStructTime{ + TestTime: t, + } +} + +func testObjectWithZeroValues() *TestStruct { + return &TestStruct{} +} + +func testObjectWithNilSets() *TestStruct { + return &TestStruct{ + TestBool: true, + TestInt: -99, + TestInt32: 999, + TestInt64: 9999, + TestUint: 99, + TestFloat32: 9.9999, + TestFloat64: 99.999999, + TestString: "test", + TestByteArray: []byte("bytes"), + TestStringArray: []string(nil), + TestIntArray: []int(nil), + TestFloatArray: []float64(nil), + TestSub: TestSubStruct{ + SubBool: true, + SubInt: 2, + SubString: "subtest", + SubStringArray: []string{"sub1", "sub2", "sub3"}, + }, + } +} +func testObjectWithEmptySets() *TestStruct { + return &TestStruct{ + TestBool: true, + TestInt: -99, + TestInt32: 999, + TestInt64: 9999, + TestUint: 99, + TestFloat32: 9.9999, + TestFloat64: 99.999999, + TestString: "test", + TestByteArray: []byte("bytes"), + TestStringArray: []string{}, + TestIntArray: []int{}, + TestFloatArray: []float64{}, + TestSub: TestSubStruct{ + SubBool: true, + SubInt: 2, + SubString: "subtest", + SubStringArray: []string{"sub1", "sub2", "sub3"}, + }, + } +} + +func testAttrs() []dynamodb.Attribute { + return []dynamodb.Attribute{ + dynamodb.Attribute{Type: "N", Name: "TestBool", Value: "1", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt", Value: "-99", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt32", Value: "999", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt64", Value: "9999", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestUint", Value: "99", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestFloat32", Value: "9.9999", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestFloat64", Value: "99.999999", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "S", Name: "TestString", Value: "test", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "S", Name: "TestByteArray", Value: "Ynl0ZXM=", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "SS", Name: "TestStringArray", Value: "", SetValues: []string{"test1", "test2", "test3", "test4"}}, + dynamodb.Attribute{Type: "NS", Name: "TestIntArray", Value: "", SetValues: []string{"0", "1", "12", "123", "1234", "12345"}}, + dynamodb.Attribute{Type: "NS", Name: "TestInt8Array", Value: "", SetValues: []string{"0", "1", "12", "123"}}, + dynamodb.Attribute{Type: "NS", Name: "TestFloatArray", Value: "", SetValues: []string{"0.1", "1.1", "1.2", "1.23", "1.234", "1.2345"}}, + dynamodb.Attribute{Type: "S", Name: "TestSub", Value: `{"SubBool":true,"SubInt":2,"SubString":"subtest","SubStringArray":["sub1","sub2","sub3"]}`, SetValues: []string(nil)}, + } +} + +func testAttrsTime() []dynamodb.Attribute { + return []dynamodb.Attribute{ + dynamodb.Attribute{Type: "S", Name: "TestTime", Value: "\"2003-03-03T17:03:00Z\"", SetValues: []string(nil)}, + } +} + +func testAttrsWithZeroValues() []dynamodb.Attribute { + return []dynamodb.Attribute{ + dynamodb.Attribute{Type: "N", Name: "TestBool", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt32", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt64", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestUint", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestFloat32", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestFloat64", Value: "0", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "S", Name: "TestSub", Value: `{"SubBool":false,"SubInt":0,"SubString":"","SubStringArray":null}`, SetValues: []string(nil)}, + } +} + +func testAttrsWithNilSets() []dynamodb.Attribute { + return []dynamodb.Attribute{ + dynamodb.Attribute{Type: "N", Name: "TestBool", Value: "1", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt", Value: "-99", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt32", Value: "999", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestInt64", Value: "9999", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestUint", Value: "99", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestFloat32", Value: "9.9999", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "N", Name: "TestFloat64", Value: "99.999999", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "S", Name: "TestString", Value: "test", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "S", Name: "TestByteArray", Value: "Ynl0ZXM=", SetValues: []string(nil)}, + dynamodb.Attribute{Type: "S", Name: "TestSub", Value: `{"SubBool":true,"SubInt":2,"SubString":"subtest","SubStringArray":["sub1","sub2","sub3"]}`, SetValues: []string(nil)}, + } +} + +type MarshallerSuite struct { +} + +var _ = Suite(&MarshallerSuite{}) + +func (s *MarshallerSuite) TestMarshal(c *C) { + testObj := testObject() + attrs, err := dynamodb.MarshalAttributes(testObj) + if err != nil { + c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err) + } + + expected := testAttrs() + c.Check(attrs, DeepEquals, expected) +} + +func (s *MarshallerSuite) TestUnmarshal(c *C) { + testObj := &TestStruct{} + + attrMap := map[string]*dynamodb.Attribute{} + attrs := testAttrs() + for i, _ := range attrs { + attrMap[attrs[i].Name] = &attrs[i] + } + + err := dynamodb.UnmarshalAttributes(&attrMap, testObj) + if err != nil { + c.Fatalf("Error from dynamodb.UnmarshalAttributes: %#v (Built: %#v)", err, testObj) + } + + expected := testObject() + c.Check(testObj, DeepEquals, expected) +} + +func (s *MarshallerSuite) TestMarshalTime(c *C) { + testObj := testObjectTime() + attrs, err := dynamodb.MarshalAttributes(testObj) + if err != nil { + c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err) + } + + expected := testAttrsTime() + c.Check(attrs, DeepEquals, expected) +} + +func (s *MarshallerSuite) TestUnmarshalTime(c *C) { + testObj := &TestStructTime{} + + attrMap := map[string]*dynamodb.Attribute{} + attrs := testAttrsTime() + for i, _ := range attrs { + attrMap[attrs[i].Name] = &attrs[i] + } + + err := dynamodb.UnmarshalAttributes(&attrMap, testObj) + if err != nil { + c.Fatalf("Error from dynamodb.UnmarshalAttributes: %#v (Built: %#v)", err, testObj) + } + + expected := testObjectTime() + c.Check(testObj, DeepEquals, expected) +} + +func (s *MarshallerSuite) TestMarshalNilSets(c *C) { + testObj := testObjectWithNilSets() + attrs, err := dynamodb.MarshalAttributes(testObj) + if err != nil { + c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err) + } + + expected := testAttrsWithNilSets() + c.Check(attrs, DeepEquals, expected) +} + +func (s *MarshallerSuite) TestMarshalZeroValues(c *C) { + testObj := testObjectWithZeroValues() + attrs, err := dynamodb.MarshalAttributes(testObj) + if err != nil { + c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err) + } + + expected := testAttrsWithZeroValues() + c.Check(attrs, DeepEquals, expected) +} + +func (s *MarshallerSuite) TestMarshalEmptySets(c *C) { + testObj := testObjectWithEmptySets() + attrs, err := dynamodb.MarshalAttributes(testObj) + if err != nil { + c.Errorf("Error from dynamodb.MarshalAttributes: %#v", err) + } + + expected := testAttrsWithNilSets() + c.Check(attrs, DeepEquals, expected) +} + +func (s *MarshallerSuite) TestUnmarshalEmptySets(c *C) { + testObj := &TestStruct{} + + attrMap := map[string]*dynamodb.Attribute{} + attrs := testAttrsWithNilSets() + for i, _ := range attrs { + attrMap[attrs[i].Name] = &attrs[i] + } + + err := dynamodb.UnmarshalAttributes(&attrMap, testObj) + if err != nil { + c.Fatalf("Error from dynamodb.UnmarshalAttributes: %#v (Built: %#v)", err, testObj) + } + + expected := testObjectWithNilSets() + c.Check(testObj, DeepEquals, expected) +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/query.go b/vendor/github.com/goamz/goamz/dynamodb/query.go new file mode 100644 index 000000000..453e38733 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/query.go @@ -0,0 +1,111 @@ +package dynamodb + +import ( + "errors" + "fmt" + simplejson "github.com/bitly/go-simplejson" +) + +func (t *Table) Query(attributeComparisons []AttributeComparison) ([]map[string]*Attribute, error) { + q := NewQuery(t) + q.AddKeyConditions(attributeComparisons) + return runQuery(q, t) +} + +func (t *Table) QueryOnIndex(attributeComparisons []AttributeComparison, indexName string) ([]map[string]*Attribute, error) { + q := NewQuery(t) + q.AddKeyConditions(attributeComparisons) + q.AddIndex(indexName) + return runQuery(q, t) +} + +func (t *Table) QueryOnIndexDescending(attributeComparisons []AttributeComparison, indexName string) ([]map[string]*Attribute, error) { + q := NewQuery(t) + q.AddKeyConditions(attributeComparisons) + q.AddIndex(indexName) + q.ScanIndexDescending() + return runQuery(q, t) +} + +func (t *Table) LimitedQuery(attributeComparisons []AttributeComparison, limit int64) ([]map[string]*Attribute, error) { + q := NewQuery(t) + q.AddKeyConditions(attributeComparisons) + q.AddLimit(limit) + return runQuery(q, t) +} + +func (t *Table) LimitedQueryOnIndex(attributeComparisons []AttributeComparison, indexName string, limit int64) ([]map[string]*Attribute, error) { + q := NewQuery(t) + q.AddKeyConditions(attributeComparisons) + q.AddIndex(indexName) + q.AddLimit(limit) + return runQuery(q, t) +} + +func (t *Table) LimitedQueryDescending(attributeComparisons []AttributeComparison, limit int64) ([]map[string]*Attribute, error) { + q := NewQuery(t) + q.AddKeyConditions(attributeComparisons) + q.AddLimit(limit) + q.ScanIndexDescending() + return runQuery(q, t) +} + +func (t *Table) LimitedQueryOnIndexDescending(attributeComparisons []AttributeComparison, indexName string, limit int64) ([]map[string]*Attribute, error) { + q := NewQuery(t) + q.AddKeyConditions(attributeComparisons) + q.AddIndex(indexName) + q.AddLimit(limit) + q.ScanIndexDescending() + return runQuery(q, t) +} + +func (t *Table) CountQuery(attributeComparisons []AttributeComparison) (int64, error) { + q := NewQuery(t) + q.AddKeyConditions(attributeComparisons) + q.AddSelect("COUNT") + jsonResponse, err := t.Server.queryServer("DynamoDB_20120810.Query", q) + if err != nil { + return 0, err + } + json, err := simplejson.NewJson(jsonResponse) + if err != nil { + return 0, err + } + + itemCount, err := json.Get("Count").Int64() + if err != nil { + return 0, err + } + + return itemCount, nil +} + +func runQuery(q *Query, t *Table) ([]map[string]*Attribute, error) { + jsonResponse, err := t.Server.queryServer("DynamoDB_20120810.Query", q) + if err != nil { + return nil, err + } + + json, err := simplejson.NewJson(jsonResponse) + if err != nil { + return nil, err + } + + itemCount, err := json.Get("Count").Int() + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + + results := make([]map[string]*Attribute, itemCount) + + for i, _ := range results { + item, err := json.Get("Items").GetIndex(i).Map() + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + results[i] = parseAttributes(item) + } + return results, nil +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/query_builder.go b/vendor/github.com/goamz/goamz/dynamodb/query_builder.go new file mode 100644 index 000000000..47a90bb1c --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/query_builder.go @@ -0,0 +1,362 @@ +package dynamodb + +import ( + "encoding/json" + "sort" +) + +type msi map[string]interface{} +type Query struct { + buffer msi +} + +func NewEmptyQuery() *Query { + return &Query{msi{}} +} + +func NewQuery(t *Table) *Query { + q := &Query{msi{}} + q.addTable(t) + return q +} + +// This way of specifing the key is used when doing a Get. +// If rangeKey is "", it is assumed to not want to be used +func (q *Query) AddKey(t *Table, key *Key) { + k := t.Key + keymap := msi{ + k.KeyAttribute.Name: msi{ + k.KeyAttribute.Type: key.HashKey}, + } + if k.HasRange() { + keymap[k.RangeAttribute.Name] = msi{k.RangeAttribute.Type: key.RangeKey} + } + + q.buffer["Key"] = keymap +} + +func keyAttributes(t *Table, key *Key) msi { + k := t.Key + + out := msi{} + out[k.KeyAttribute.Name] = msi{k.KeyAttribute.Type: key.HashKey} + if k.HasRange() { + out[k.RangeAttribute.Name] = msi{k.RangeAttribute.Type: key.RangeKey} + } + return out +} + +func (q *Query) AddAttributesToGet(attributes []string) { + if len(attributes) == 0 { + return + } + + q.buffer["AttributesToGet"] = attributes +} + +func (q *Query) ConsistentRead(c bool) { + if c == true { + q.buffer["ConsistentRead"] = "true" //String "true", not bool true + } +} + +func (q *Query) AddGetRequestItems(tableKeys map[*Table][]Key) { + requestitems := msi{} + for table, keys := range tableKeys { + keyslist := []msi{} + for _, key := range keys { + keyslist = append(keyslist, keyAttributes(table, &key)) + } + requestitems[table.Name] = msi{"Keys": keyslist} + } + q.buffer["RequestItems"] = requestitems +} + +func (q *Query) AddWriteRequestItems(tableItems map[*Table]map[string][][]Attribute) { + b := q.buffer + + b["RequestItems"] = func() msi { + out := msi{} + for table, itemActions := range tableItems { + out[table.Name] = func() interface{} { + out2 := []interface{}{} + + // here breaks an order of array.... + // For now, we iterate over sorted key by action for stable testing + keys := []string{} + for k := range itemActions { + keys = append(keys, k) + } + sort.Strings(keys) + + for ki := range keys { + action := keys[ki] + items := itemActions[action] + for _, attributes := range items { + Item_or_Key := map[bool]string{true: "Item", false: "Key"}[action == "Put"] + out2 = append(out2, msi{action + "Request": msi{Item_or_Key: attributeList(attributes)}}) + } + } + return out2 + }() + } + return out + }() +} + +func (q *Query) AddCreateRequestTable(description TableDescriptionT) { + b := q.buffer + + attDefs := []interface{}{} + for _, attr := range description.AttributeDefinitions { + attDefs = append(attDefs, msi{ + "AttributeName": attr.Name, + "AttributeType": attr.Type, + }) + } + b["AttributeDefinitions"] = attDefs + b["KeySchema"] = description.KeySchema + b["TableName"] = description.TableName + b["ProvisionedThroughput"] = msi{ + "ReadCapacityUnits": int(description.ProvisionedThroughput.ReadCapacityUnits), + "WriteCapacityUnits": int(description.ProvisionedThroughput.WriteCapacityUnits), + } + + if description.StreamSpecification.StreamEnabled { + b["StreamSpecification"] = msi{ + "StreamEnabled": "true", + "StreamViewType": description.StreamSpecification.StreamViewType, + } + } + + localSecondaryIndexes := []interface{}{} + + for _, ind := range description.LocalSecondaryIndexes { + localSecondaryIndexes = append(localSecondaryIndexes, msi{ + "IndexName": ind.IndexName, + "KeySchema": ind.KeySchema, + "Projection": ind.Projection, + }) + } + + globalSecondaryIndexes := []interface{}{} + intmax := func(x, y int64) int64 { + if x > y { + return x + } + return y + } + for _, ind := range description.GlobalSecondaryIndexes { + rec := msi{ + "IndexName": ind.IndexName, + "KeySchema": ind.KeySchema, + "Projection": ind.Projection, + } + // need at least one unit, and since go's max() is float based. + rec["ProvisionedThroughput"] = msi{ + "ReadCapacityUnits": intmax(1, ind.ProvisionedThroughput.ReadCapacityUnits), + "WriteCapacityUnits": intmax(1, ind.ProvisionedThroughput.WriteCapacityUnits), + } + globalSecondaryIndexes = append(globalSecondaryIndexes, rec) + } + + if len(localSecondaryIndexes) > 0 { + b["LocalSecondaryIndexes"] = localSecondaryIndexes + } + + if len(globalSecondaryIndexes) > 0 { + b["GlobalSecondaryIndexes"] = globalSecondaryIndexes + } +} + +func (q *Query) AddDeleteRequestTable(description TableDescriptionT) { + b := q.buffer + b["TableName"] = description.TableName +} + +func (q *Query) AddUpdateRequestTable(description TableDescriptionT) { + b := q.buffer + + attDefs := []interface{}{} + for _, attr := range description.AttributeDefinitions { + attDefs = append(attDefs, msi{ + "AttributeName": attr.Name, + "AttributeType": attr.Type, + }) + } + if len(attDefs) > 0 { + b["AttributeDefinitions"] = attDefs + } + b["TableName"] = description.TableName + b["ProvisionedThroughput"] = msi{ + "ReadCapacityUnits": int(description.ProvisionedThroughput.ReadCapacityUnits), + "WriteCapacityUnits": int(description.ProvisionedThroughput.WriteCapacityUnits), + } + +} + +func (q *Query) AddKeyConditions(comparisons []AttributeComparison) { + q.buffer["KeyConditions"] = buildComparisons(comparisons) +} + +func (q *Query) AddLimit(limit int64) { + q.buffer["Limit"] = limit +} +func (q *Query) AddSelect(value string) { + q.buffer["Select"] = value +} + +func (q *Query) AddIndex(value string) { + q.buffer["IndexName"] = value +} + +func (q *Query) ScanIndexDescending() { + q.buffer["ScanIndexForward"] = "false" +} + +/* + "ScanFilter":{ + "AttributeName1":{"AttributeValueList":[{"S":"AttributeValue"}],"ComparisonOperator":"EQ"} + }, +*/ +func (q *Query) AddScanFilter(comparisons []AttributeComparison) { + q.buffer["ScanFilter"] = buildComparisons(comparisons) +} + +func (q *Query) AddParallelScanConfiguration(segment int, totalSegments int) { + q.buffer["Segment"] = segment + q.buffer["TotalSegments"] = totalSegments +} + +func buildComparisons(comparisons []AttributeComparison) msi { + out := msi{} + + for _, c := range comparisons { + avlist := []interface{}{} + for _, attributeValue := range c.AttributeValueList { + avlist = append(avlist, msi{attributeValue.Type: attributeValue.Value}) + } + out[c.AttributeName] = msi{ + "AttributeValueList": avlist, + "ComparisonOperator": c.ComparisonOperator, + } + } + + return out +} + +// The primary key must be included in attributes. +func (q *Query) AddItem(attributes []Attribute) { + q.buffer["Item"] = attributeList(attributes) +} + +func (q *Query) AddUpdates(attributes []Attribute, action string) { + updates := msi{} + for _, a := range attributes { + au := msi{ + "Value": msi{ + a.Type: map[bool]interface{}{true: a.SetValues, false: a.Value}[a.SetType()], + }, + "Action": action, + } + // Delete 'Value' from AttributeUpdates if Type is not Set + if action == "DELETE" && !a.SetType() { + delete(au, "Value") + } + updates[a.Name] = au + } + + q.buffer["AttributeUpdates"] = updates +} + +func (q *Query) AddExpected(attributes []Attribute) { + expected := msi{} + for _, a := range attributes { + value := msi{} + if a.Exists != "" { + value["Exists"] = a.Exists + } + // If set Exists to false, we must remove Value + if value["Exists"] != "false" { + value["Value"] = msi{a.Type: map[bool]interface{}{true: a.SetValues, false: a.Value}[a.SetType()]} + } + expected[a.Name] = value + } + q.buffer["Expected"] = expected +} + +// Add the ReturnValues parameter, used in UpdateItem queries. +func (q *Query) AddReturnValues(returnValues ReturnValues) { + q.buffer["ReturnValues"] = string(returnValues) +} + +// Add the UpdateExpression parameter, used in UpdateItem queries. +func (q *Query) AddUpdateExpression(expression string) { + q.buffer["UpdateExpression"] = expression +} + +// Add the ConditionExpression parameter, used in UpdateItem queries. +func (q *Query) AddConditionExpression(expression string) { + q.buffer["ConditionExpression"] = expression +} + +func (q *Query) AddExpressionAttributes(attributes []Attribute) { + existing, ok := q.buffer["ExpressionAttributes"].(msi) + if !ok { + existing = msi{} + q.buffer["ExpressionAttributes"] = existing + } + for key, val := range attributeList(attributes) { + existing[key] = val + } +} + +func (q *Query) AddExclusiveStartStreamArn(arn string) { + q.buffer["ExclusiveStartStreamArn"] = arn +} + +func (q *Query) AddStreamArn(arn string) { + q.buffer["StreamArn"] = arn +} + +func (q *Query) AddExclusiveStartShardId(shardId string) { + q.buffer["ExclusiveStartShardId"] = shardId +} + +func (q *Query) AddShardId(shardId string) { + q.buffer["ShardId"] = shardId +} + +func (q *Query) AddShardIteratorType(shardIteratorType string) { + q.buffer["ShardIteratorType"] = shardIteratorType +} + +func (q *Query) AddSequenceNumber(sequenceNumber string) { + q.buffer["SequenceNumber"] = sequenceNumber +} + +func (q *Query) AddShardIterator(shardIterator string) { + q.buffer["ShardIterator"] = shardIterator +} + +func attributeList(attributes []Attribute) msi { + b := msi{} + for _, a := range attributes { + //UGH!! (I miss the query operator) + b[a.Name] = msi{a.Type: map[bool]interface{}{true: a.SetValues, false: a.Value}[a.SetType()]} + } + return b +} + +func (q *Query) addTable(t *Table) { + q.addTableByName(t.Name) +} + +func (q *Query) addTableByName(tableName string) { + q.buffer["TableName"] = tableName +} + +func (q *Query) String() string { + bytes, _ := json.Marshal(q.buffer) + return string(bytes) +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/query_builder_test.go b/vendor/github.com/goamz/goamz/dynamodb/query_builder_test.go new file mode 100755 index 000000000..9a1f6f2c5 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/query_builder_test.go @@ -0,0 +1,380 @@ +package dynamodb_test + +import ( + simplejson "github.com/bitly/go-simplejson" + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/dynamodb" + . "gopkg.in/check.v1" +) + +type QueryBuilderSuite struct { + server *dynamodb.Server +} + +var _ = Suite(&QueryBuilderSuite{}) + +func (s *QueryBuilderSuite) SetUpSuite(c *C) { + auth := &aws.Auth{AccessKey: "", SecretKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"} + s.server = &dynamodb.Server{*auth, aws.USEast} +} + +func (s *QueryBuilderSuite) TestEmptyQuery(c *C) { + q := dynamodb.NewEmptyQuery() + queryString := q.String() + expectedString := "{}" + c.Check(queryString, Equals, expectedString) + + if expectedString != queryString { + c.Fatalf("Unexpected Query String : %s\n", queryString) + } +} + +func (s *QueryBuilderSuite) TestAddWriteRequestItems(c *C) { + primary := dynamodb.NewStringAttribute("WidgetFoo", "") + secondary := dynamodb.NewNumericAttribute("Created", "") + key := dynamodb.PrimaryKey{primary, secondary} + table := s.server.NewTable("FooData", key) + + primary2 := dynamodb.NewStringAttribute("TestHashKey", "") + secondary2 := dynamodb.NewNumericAttribute("TestRangeKey", "") + key2 := dynamodb.PrimaryKey{primary2, secondary2} + table2 := s.server.NewTable("TestTable", key2) + + q := dynamodb.NewEmptyQuery() + + attribute1 := dynamodb.NewNumericAttribute("testing", "4") + attribute2 := dynamodb.NewNumericAttribute("testingbatch", "2111") + attribute3 := dynamodb.NewStringAttribute("testingstrbatch", "mystr") + item1 := []dynamodb.Attribute{*attribute1, *attribute2, *attribute3} + + attribute4 := dynamodb.NewNumericAttribute("testing", "444") + attribute5 := dynamodb.NewNumericAttribute("testingbatch", "93748249272") + attribute6 := dynamodb.NewStringAttribute("testingstrbatch", "myotherstr") + item2 := []dynamodb.Attribute{*attribute4, *attribute5, *attribute6} + + attributeDel1 := dynamodb.NewStringAttribute("TestHashKeyDel", "DelKey") + attributeDel2 := dynamodb.NewNumericAttribute("TestRangeKeyDel", "7777777") + itemDel := []dynamodb.Attribute{*attributeDel1, *attributeDel2} + + attributeTest1 := dynamodb.NewStringAttribute("TestHashKey", "MyKey") + attributeTest2 := dynamodb.NewNumericAttribute("TestRangeKey", "0193820384293") + itemTest := []dynamodb.Attribute{*attributeTest1, *attributeTest2} + + tableItems := map[*dynamodb.Table]map[string][][]dynamodb.Attribute{} + actionItems := make(map[string][][]dynamodb.Attribute) + actionItems["Put"] = [][]dynamodb.Attribute{item1, item2} + actionItems["Delete"] = [][]dynamodb.Attribute{itemDel} + tableItems[table] = actionItems + + actionItems2 := make(map[string][][]dynamodb.Attribute) + actionItems2["Put"] = [][]dynamodb.Attribute{itemTest} + tableItems[table2] = actionItems2 + + q.AddWriteRequestItems(tableItems) + + queryJson, err := simplejson.NewJson([]byte(q.String())) + if err != nil { + c.Fatal(err) + } + + expectedJson, err := simplejson.NewJson([]byte(` +{ + "RequestItems": { + "TestTable": [ + { + "PutRequest": { + "Item": { + "TestRangeKey": { + "N": "0193820384293" + }, + "TestHashKey": { + "S": "MyKey" + } + } + } + } + ], + "FooData": [ + { + "DeleteRequest": { + "Key": { + "TestRangeKeyDel": { + "N": "7777777" + }, + "TestHashKeyDel": { + "S": "DelKey" + } + } + } + }, + { + "PutRequest": { + "Item": { + "testingstrbatch": { + "S": "mystr" + }, + "testingbatch": { + "N": "2111" + }, + "testing": { + "N": "4" + } + } + } + }, + { + "PutRequest": { + "Item": { + "testingstrbatch": { + "S": "myotherstr" + }, + "testingbatch": { + "N": "93748249272" + }, + "testing": { + "N": "444" + } + } + } + } + ] + } +} + `)) + if err != nil { + c.Fatal(err) + } + c.Check(queryJson, DeepEquals, expectedJson) +} + +func (s *QueryBuilderSuite) TestAddExpectedQuery(c *C) { + primary := dynamodb.NewStringAttribute("domain", "") + key := dynamodb.PrimaryKey{primary, nil} + table := s.server.NewTable("sites", key) + + q := dynamodb.NewQuery(table) + q.AddKey(table, &dynamodb.Key{HashKey: "test"}) + + expected := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("domain", "expectedTest").SetExists(true), + *dynamodb.NewStringAttribute("testKey", "").SetExists(false), + } + q.AddExpected(expected) + + queryJson, err := simplejson.NewJson([]byte(q.String())) + if err != nil { + c.Fatal(err) + } + + expectedJson, err := simplejson.NewJson([]byte(` + { + "Expected": { + "domain": { + "Exists": "true", + "Value": { + "S": "expectedTest" + } + }, + "testKey": { + "Exists": "false" + } + }, + "Key": { + "domain": { + "S": "test" + } + }, + "TableName": "sites" + } + `)) + if err != nil { + c.Fatal(err) + } + c.Check(queryJson, DeepEquals, expectedJson) +} + +func (s *QueryBuilderSuite) TestGetItemQuery(c *C) { + primary := dynamodb.NewStringAttribute("domain", "") + key := dynamodb.PrimaryKey{primary, nil} + table := s.server.NewTable("sites", key) + + q := dynamodb.NewQuery(table) + q.AddKey(table, &dynamodb.Key{HashKey: "test"}) + + { + queryJson, err := simplejson.NewJson([]byte(q.String())) + if err != nil { + c.Fatal(err) + } + + expectedJson, err := simplejson.NewJson([]byte(` + { + "Key": { + "domain": { + "S": "test" + } + }, + "TableName": "sites" + } + `)) + if err != nil { + c.Fatal(err) + } + c.Check(queryJson, DeepEquals, expectedJson) + } + + // Use ConsistentRead + { + q.ConsistentRead(true) + queryJson, err := simplejson.NewJson([]byte(q.String())) + if err != nil { + c.Fatal(err) + } + + expectedJson, err := simplejson.NewJson([]byte(` + { + "ConsistentRead": "true", + "Key": { + "domain": { + "S": "test" + } + }, + "TableName": "sites" + } + `)) + if err != nil { + c.Fatal(err) + } + c.Check(queryJson, DeepEquals, expectedJson) + } +} + +func (s *QueryBuilderSuite) TestUpdateQuery(c *C) { + primary := dynamodb.NewStringAttribute("domain", "") + rangek := dynamodb.NewNumericAttribute("time", "") + key := dynamodb.PrimaryKey{primary, rangek} + table := s.server.NewTable("sites", key) + + countAttribute := dynamodb.NewNumericAttribute("count", "4") + attributes := []dynamodb.Attribute{*countAttribute} + + q := dynamodb.NewQuery(table) + q.AddKey(table, &dynamodb.Key{HashKey: "test", RangeKey: "1234"}) + q.AddUpdates(attributes, "ADD") + + queryJson, err := simplejson.NewJson([]byte(q.String())) + if err != nil { + c.Fatal(err) + } + expectedJson, err := simplejson.NewJson([]byte(` +{ + "AttributeUpdates": { + "count": { + "Action": "ADD", + "Value": { + "N": "4" + } + } + }, + "Key": { + "domain": { + "S": "test" + }, + "time": { + "N": "1234" + } + }, + "TableName": "sites" +} + `)) + if err != nil { + c.Fatal(err) + } + c.Check(queryJson, DeepEquals, expectedJson) +} + +func (s *QueryBuilderSuite) TestAddUpdates(c *C) { + primary := dynamodb.NewStringAttribute("domain", "") + key := dynamodb.PrimaryKey{primary, nil} + table := s.server.NewTable("sites", key) + + q := dynamodb.NewQuery(table) + q.AddKey(table, &dynamodb.Key{HashKey: "test"}) + + attr := dynamodb.NewStringSetAttribute("StringSet", []string{"str", "str2"}) + + q.AddUpdates([]dynamodb.Attribute{*attr}, "ADD") + + queryJson, err := simplejson.NewJson([]byte(q.String())) + if err != nil { + c.Fatal(err) + } + expectedJson, err := simplejson.NewJson([]byte(` +{ + "AttributeUpdates": { + "StringSet": { + "Action": "ADD", + "Value": { + "SS": ["str", "str2"] + } + } + }, + "Key": { + "domain": { + "S": "test" + } + }, + "TableName": "sites" +} + `)) + if err != nil { + c.Fatal(err) + } + c.Check(queryJson, DeepEquals, expectedJson) +} + +func (s *QueryBuilderSuite) TestAddKeyConditions(c *C) { + primary := dynamodb.NewStringAttribute("domain", "") + key := dynamodb.PrimaryKey{primary, nil} + table := s.server.NewTable("sites", key) + + q := dynamodb.NewQuery(table) + acs := []dynamodb.AttributeComparison{ + *dynamodb.NewStringAttributeComparison("domain", "EQ", "example.com"), + *dynamodb.NewStringAttributeComparison("path", "EQ", "/"), + } + q.AddKeyConditions(acs) + queryJson, err := simplejson.NewJson([]byte(q.String())) + + if err != nil { + c.Fatal(err) + } + + expectedJson, err := simplejson.NewJson([]byte(` +{ + "KeyConditions": { + "domain": { + "AttributeValueList": [ + { + "S": "example.com" + } + ], + "ComparisonOperator": "EQ" + }, + "path": { + "AttributeValueList": [ + { + "S": "/" + } + ], + "ComparisonOperator": "EQ" + } + }, + "TableName": "sites" +} + `)) + if err != nil { + c.Fatal(err) + } + c.Check(queryJson, DeepEquals, expectedJson) +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/scan.go b/vendor/github.com/goamz/goamz/dynamodb/scan.go new file mode 100644 index 000000000..e8ed62363 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/scan.go @@ -0,0 +1,51 @@ +package dynamodb + +import ( + "errors" + "fmt" + simplejson "github.com/bitly/go-simplejson" +) + +func (t *Table) FetchResults(query *Query) ([]map[string]*Attribute, error) { + jsonResponse, err := t.Server.queryServer(target("Scan"), query) + if err != nil { + return nil, err + } + + json, err := simplejson.NewJson(jsonResponse) + if err != nil { + return nil, err + } + + itemCount, err := json.Get("Count").Int() + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + + results := make([]map[string]*Attribute, itemCount) + + for i, _ := range results { + item, err := json.Get("Items").GetIndex(i).Map() + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + results[i] = parseAttributes(item) + } + return results, nil + +} + +func (t *Table) Scan(attributeComparisons []AttributeComparison) ([]map[string]*Attribute, error) { + q := NewQuery(t) + q.AddScanFilter(attributeComparisons) + return t.FetchResults(q) +} + +func (t *Table) ParallelScan(attributeComparisons []AttributeComparison, segment int, totalSegments int) ([]map[string]*Attribute, error) { + q := NewQuery(t) + q.AddScanFilter(attributeComparisons) + q.AddParallelScanConfiguration(segment, totalSegments) + return t.FetchResults(q) +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/stream.go b/vendor/github.com/goamz/goamz/dynamodb/stream.go new file mode 100644 index 000000000..57f3a145f --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/stream.go @@ -0,0 +1,307 @@ +package dynamodb + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + + simplejson "github.com/bitly/go-simplejson" +) + +type Stream struct { + Server *Server + Arn string +} + +type StreamListItemT struct { + StreamArn string + StreamLabel string + TableName string +} + +type SequenceNumberRangeT struct { + EndingSequenceNumber string + StartingSequenceNumber string +} + +type ShardT struct { + ParentShardId string + SequenceNumberRange SequenceNumberRangeT + ShardId string +} + +type StreamDescriptionT struct { + CreationDateTime float64 + KeySchema []KeySchemaT + LastEvaluatedShardId string + Shards []ShardT + StreamArn string + StreamLabel string + StreamStatus string + StreamViewType string + TableName string +} + +type RecordT struct { + AwsRegion string + EventID string + EventName string + EventSource string + EventVersion string + StreamRecord *StreamRecordT +} + +type StreamRecordT struct { + Keys map[string]*Attribute + NewImage map[string]*Attribute + OldImage map[string]*Attribute + SequenceNumber string + StreamViewType string + SizeBytes int64 +} + +type listStreamsResponse struct { + Streams []StreamListItemT +} + +type describeStreamResponse struct { + StreamDescription StreamDescriptionT +} + +var ErrNoRecords = errors.New("No records") + +func (s *Server) ListStreams(startArn string) ([]StreamListItemT, error) { + return s.LimitedListTableStreams("", startArn, 0) +} + +func (s *Server) LimitedListStreams(startArn string, limit int64) ([]StreamListItemT, error) { + return s.LimitedListTableStreams("", startArn, limit) +} + +func (s *Server) ListTableStreams(table, startArn string) ([]StreamListItemT, error) { + return s.LimitedListTableStreams(table, startArn, 0) +} + +func (s *Server) LimitedListTableStreams(table, startArn string, limit int64) ([]StreamListItemT, error) { + query := NewEmptyQuery() + + if len(table) != 0 { + query.addTableByName(table) + } + + if len(startArn) != 0 { + query.AddExclusiveStartStreamArn(startArn) + } + + if limit > 0 { + query.AddLimit(limit) + } + + jsonResponse, err := s.queryServer(streamsTarget("ListStreams"), query) + if err != nil { + return nil, err + } + + var r listStreamsResponse + err = json.Unmarshal(jsonResponse, &r) + if err != nil { + return nil, err + } + + return r.Streams, nil +} + +func (s *Server) DescribeStream(arn, startShardId string) (*StreamDescriptionT, error) { + return s.LimitedDescribeStream(arn, startShardId, 0) +} + +func (s *Server) LimitedDescribeStream(arn, startShardId string, limit int64) (*StreamDescriptionT, error) { + query := NewEmptyQuery() + query.AddStreamArn(arn) + + if len(startShardId) != 0 { + query.AddExclusiveStartShardId(startShardId) + } + + if limit > 0 { + query.AddLimit(limit) + } + + jsonResponse, err := s.queryServer(streamsTarget("DescribeStream"), query) + if err != nil { + return nil, err + } + + var r describeStreamResponse + err = json.Unmarshal(jsonResponse, &r) + if err != nil { + return nil, err + } + + return &r.StreamDescription, nil +} + +func (s *Server) NewStream(streamArn string) *Stream { + return &Stream{s, streamArn} +} + +func (s *Stream) DescribeStream(startShardId string) (*StreamDescriptionT, error) { + return s.Server.DescribeStream(s.Arn, startShardId) +} + +func (s *Stream) LimitedDescribeStream(startShardId string, limit int64) (*StreamDescriptionT, error) { + return s.Server.LimitedDescribeStream(s.Arn, startShardId, limit) +} + +func (s *Server) GetShardIterator(streamArn, shardId, shardIteratorType, sequenceNumber string) (string, error) { + query := NewEmptyQuery() + query.AddStreamArn(streamArn) + query.AddShardId(shardId) + query.AddShardIteratorType(shardIteratorType) + + if len(sequenceNumber) != 0 { + query.AddSequenceNumber(sequenceNumber) + } + + jsonResponse, err := s.queryServer(streamsTarget("GetShardIterator"), query) + + if err != nil { + return "unknown", err + } + + json, err := simplejson.NewJson(jsonResponse) + + if err != nil { + return "unknown", err + } + + return json.Get("ShardIterator").MustString(), nil +} + +func (s *Stream) GetShardIterator(shardId, shardIteratorType, sequenceNumber string) (string, error) { + return s.Server.GetShardIterator(s.Arn, shardId, shardIteratorType, sequenceNumber) +} + +func (s *Server) GetRecords(shardIterator string) (string, []*RecordT, error) { + return s.LimitedGetRecords(shardIterator, 0) +} + +func (s *Server) LimitedGetRecords(shardIterator string, limit int64) (string, []*RecordT, error) { + query := NewEmptyQuery() + query.AddShardIterator(shardIterator) + + if limit > 0 { + query.AddLimit(limit) + } + + jsonResponse, err := s.queryServer(streamsTarget("GetRecords"), query) + if err != nil { + return "", nil, err + } + + jsonParsed, err := simplejson.NewJson(jsonResponse) + if err != nil { + return "", nil, err + } + + nextShardIt := "" + nextShardItJson, ok := jsonParsed.CheckGet("NextShardIterator") + if ok { + nextShardIt, err = nextShardItJson.String() + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return "", nil, errors.New(message) + } + } + + recordsJson, ok := jsonParsed.CheckGet("Records") + if !ok { + return nextShardIt, nil, ErrNoRecords + } + + recordsArray, err := recordsJson.Array() + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nextShardIt, nil, errors.New(message) + } + + var records []*RecordT + for _, record := range recordsArray { + if recordMap, ok := record.(map[string]interface{}); ok { + r := parseRecord(recordMap) + records = append(records, r) + } + } + + return nextShardIt, records, nil +} + +func (s *Stream) GetRecords(shardIterator string) (string, []*RecordT, error) { + return s.Server.GetRecords(shardIterator) +} + +func (s *Stream) LimitedGetRecords(shardIterator string, limit int64) (string, []*RecordT, error) { + return s.Server.LimitedGetRecords(shardIterator, limit) +} + +func parseRecord(r map[string]interface{}) *RecordT { + record := RecordT{} + rValue := reflect.ValueOf(&record) + + keys := []string{"awsRegion", "eventID", "eventName", "eventSource", "eventVersion"} + for i, key := range keys { + if value, ok := r[key]; ok { + if valueStr, ok := value.(string); ok { + rValue.Elem().Field(i).SetString(valueStr) + } + } + } + + if streamRecord, ok := r["dynamodb"]; ok { + if streamRecordMap, ok := streamRecord.(map[string]interface{}); ok { + record.StreamRecord = parseStreamRecord(streamRecordMap) + } + } + + return &record +} + +func parseStreamRecord(s map[string]interface{}) *StreamRecordT { + sr := StreamRecordT{} + rValue := reflect.ValueOf(&sr) + + attrKeys := []string{"Keys", "NewImage", "OldImage"} + numAttrKeys := len(attrKeys) + for i, key := range attrKeys { + if value, ok := s[key]; ok { + if valueMap, ok := value.(map[string]interface{}); ok { + attrs := parseAttributes(valueMap) + rValue.Elem().Field(i).Set(reflect.ValueOf(attrs)) + } + } + } + + strKeys := []string{"SequenceNumber", "StreamViewType"} + numStrKeys := len(strKeys) + for i, key := range strKeys { + if value, ok := s[key]; ok { + if valueStr, ok := value.(string); ok { + rValue.Elem().Field(i + numAttrKeys).SetString(valueStr) + } + } + } + + intKeys := []string{"SizeBytes"} + for i, key := range intKeys { + if value, ok := s[key]; ok { + if valueNumber, ok := value.(json.Number); ok { + if valueInt, err := valueNumber.Int64(); err == nil { + rValue.Elem().Field(i + numAttrKeys + numStrKeys).SetInt(valueInt) + } + } + } + } + + return &sr +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/stream_test.go b/vendor/github.com/goamz/goamz/dynamodb/stream_test.go new file mode 100755 index 000000000..a982ffa65 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/stream_test.go @@ -0,0 +1,198 @@ +package dynamodb_test + +import ( + "strconv" + + "github.com/goamz/goamz/dynamodb" + . "gopkg.in/check.v1" +) + +type StreamSuite struct { + TableDescriptionT dynamodb.TableDescriptionT + DynamoDBTest +} + +func (s *StreamSuite) SetUpSuite(c *C) { + setUpAuth(c) + s.DynamoDBTest.TableDescriptionT = s.TableDescriptionT + s.server = &dynamodb.Server{dynamodb_auth, dynamodb_region} + pk, err := s.TableDescriptionT.BuildPrimaryKey() + if err != nil { + c.Skip(err.Error()) + } + s.table = s.server.NewTable(s.TableDescriptionT.TableName, pk) + + // Cleanup + s.TearDownSuite(c) + _, err = s.server.CreateTable(s.TableDescriptionT) + if err != nil { + c.Fatal(err) + } + s.WaitUntilStatus(c, "ACTIVE") +} + +var stream_suite_keys_only = &StreamSuite{ + TableDescriptionT: dynamodb.TableDescriptionT{ + TableName: "StreamTable", + AttributeDefinitions: []dynamodb.AttributeDefinitionT{ + dynamodb.AttributeDefinitionT{"TestHashKey", "S"}, + dynamodb.AttributeDefinitionT{"TestRangeKey", "N"}, + }, + KeySchema: []dynamodb.KeySchemaT{ + dynamodb.KeySchemaT{"TestHashKey", "HASH"}, + dynamodb.KeySchemaT{"TestRangeKey", "RANGE"}, + }, + ProvisionedThroughput: dynamodb.ProvisionedThroughputT{ + ReadCapacityUnits: 1, + WriteCapacityUnits: 1, + }, + StreamSpecification: dynamodb.StreamSpecificationT{ + StreamEnabled: true, + StreamViewType: "KEYS_ONLY", + }, + }, +} + +var stream_suite_new_image = &StreamSuite{ + TableDescriptionT: dynamodb.TableDescriptionT{ + TableName: "StreamTable", + AttributeDefinitions: []dynamodb.AttributeDefinitionT{ + dynamodb.AttributeDefinitionT{"TestHashKey", "S"}, + dynamodb.AttributeDefinitionT{"TestRangeKey", "N"}, + }, + KeySchema: []dynamodb.KeySchemaT{ + dynamodb.KeySchemaT{"TestHashKey", "HASH"}, + dynamodb.KeySchemaT{"TestRangeKey", "RANGE"}, + }, + ProvisionedThroughput: dynamodb.ProvisionedThroughputT{ + ReadCapacityUnits: 1, + WriteCapacityUnits: 1, + }, + StreamSpecification: dynamodb.StreamSpecificationT{ + StreamEnabled: true, + StreamViewType: "NEW_IMAGE", + }, + }, +} + +var _ = Suite(stream_suite_keys_only) +var _ = Suite(stream_suite_new_image) + +func (s *StreamSuite) TestStream(c *C) { + checkStream(s.table, c) +} + +func checkStream(table *dynamodb.Table, c *C) { + // list the table's streams + streams, err := table.ListStreams("") + if err != nil { + c.Fatal(err) + } + c.Check(len(streams), Not(Equals), 0) + c.Check(streams[0].TableName, Equals, table.Name) + + // stick a couple of items in the table + attrs := []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("TestAttr", "0"), + } + if ok, err := table.PutItem("0", "0", attrs); !ok { + c.Fatal(err) + } + attrs = []dynamodb.Attribute{ + *dynamodb.NewStringAttribute("TestAttr", "1"), + } + if ok, err := table.PutItem("1", "1", attrs); !ok { + c.Fatal(err) + } + + // create a stream object + stream := table.Server.NewStream(streams[0].StreamArn) + + // describe the steam + desc, err := stream.DescribeStream("") + if err != nil { + c.Fatal(err) + } + + tableDesc, err := table.DescribeTable() + if err != nil { + c.Fatal(err) + } + + c.Check(desc.KeySchema[0], Equals, tableDesc.KeySchema[0]) + c.Check(desc.StreamArn, Equals, streams[0].StreamArn) + c.Check(desc.StreamStatus, Equals, "ENABLED") + c.Check(desc.StreamViewType, Equals, tableDesc.StreamSpecification.StreamViewType) + c.Check(desc.TableName, Equals, table.Name) + c.Check(len(desc.Shards), Equals, 1) + + // get a shard iterator + shardIt, err := stream.GetShardIterator(desc.Shards[0].ShardId, "TRIM_HORIZON", "") + if err != nil { + c.Fatal(err) + } + c.Check(len(shardIt), Not(Equals), 0) + + // poll for records + nextIt, records, err := stream.GetRecords(shardIt) + if err != nil { + c.Fatal(err) + } + c.Check(len(nextIt), Not(Equals), 0) + c.Check(len(records), Equals, 2) + + for index, record := range records { + c.Check(record.EventSource, Equals, "aws:dynamodb") + c.Check(record.EventName, Equals, "INSERT") + c.Check(len(record.EventID), Not(Equals), 0) + + // look at the actual record + streamRec := record.StreamRecord + c.Check(streamRec.StreamViewType, Equals, desc.StreamViewType) + c.Check(len(streamRec.SequenceNumber), Not(Equals), 0) + if streamRec.SizeBytes <= 0 { + c.Errorf("Expected greater-than-zero size, got: %d", streamRec.SizeBytes) + } + // check the keys + if streamRec.StreamViewType == "KEYS_ONLY" { + checkKeys(streamRec.Keys, index, c) + } + // check the image + if streamRec.StreamViewType == "NEW_IMAGE" { + checkNewImage(streamRec.NewImage, index, c) + } + } +} + +func checkKeys(keys map[string]*dynamodb.Attribute, expect int, c *C) { + c.Check(len(keys), Equals, 2) + value, err := strconv.Atoi(keys["TestHashKey"].Value) + if err != nil { + c.Fatal(err) + } + c.Check(value, Equals, expect) + value, err = strconv.Atoi(keys["TestRangeKey"].Value) + if err != nil { + c.Fatal(err) + } + c.Check(value, Equals, expect) +} + +func checkNewImage(image map[string]*dynamodb.Attribute, expect int, c *C) { + c.Check(len(image), Equals, 3) + value, err := strconv.Atoi(image["TestHashKey"].Value) + if err != nil { + c.Fatal(err) + } + c.Check(value, Equals, expect) + value, err = strconv.Atoi(image["TestRangeKey"].Value) + if err != nil { + c.Fatal(err) + } + c.Check(value, Equals, expect) + value, err = strconv.Atoi(image["TestAttr"].Value) + if err != nil { + c.Fatal(err) + } + c.Check(value, Equals, expect) +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/table.go b/vendor/github.com/goamz/goamz/dynamodb/table.go new file mode 100755 index 000000000..541433c13 --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/table.go @@ -0,0 +1,259 @@ +package dynamodb + +import ( + "encoding/json" + "errors" + "fmt" + simplejson "github.com/bitly/go-simplejson" +) + +type Table struct { + Server *Server + Name string + Key PrimaryKey +} + +type AttributeDefinitionT struct { + Name string `json:"AttributeName"` + Type string `json:"AttributeType"` +} + +type KeySchemaT struct { + AttributeName string + KeyType string +} + +type ProjectionT struct { + ProjectionType string +} + +type GlobalSecondaryIndexT struct { + IndexName string + IndexSizeBytes int64 + ItemCount int64 + KeySchema []KeySchemaT + Projection ProjectionT + ProvisionedThroughput ProvisionedThroughputT +} + +type LocalSecondaryIndexT struct { + IndexName string + IndexSizeBytes int64 + ItemCount int64 + KeySchema []KeySchemaT + Projection ProjectionT +} + +type ProvisionedThroughputT struct { + NumberOfDecreasesToday int64 + ReadCapacityUnits int64 + WriteCapacityUnits int64 +} + +type StreamSpecificationT struct { + StreamEnabled bool + StreamViewType string +} + +type TableDescriptionT struct { + AttributeDefinitions []AttributeDefinitionT + CreationDateTime float64 + ItemCount int64 + KeySchema []KeySchemaT + GlobalSecondaryIndexes []GlobalSecondaryIndexT + LocalSecondaryIndexes []LocalSecondaryIndexT + ProvisionedThroughput ProvisionedThroughputT + StreamSpecification StreamSpecificationT + TableName string + TableSizeBytes int64 + TableStatus string + LatestStreamArn string + LatestStreamLabel string +} + +type describeTableResponse struct { + Table TableDescriptionT +} + +func findAttributeDefinitionByName(ads []AttributeDefinitionT, name string) *AttributeDefinitionT { + for _, a := range ads { + if a.Name == name { + return &a + } + } + return nil +} + +func (a *AttributeDefinitionT) GetEmptyAttribute() *Attribute { + switch a.Type { + case "S": + return NewStringAttribute(a.Name, "") + case "N": + return NewNumericAttribute(a.Name, "") + case "B": + return NewBinaryAttribute(a.Name, "") + default: + return nil + } +} + +func (t *TableDescriptionT) BuildPrimaryKey() (pk PrimaryKey, err error) { + for _, k := range t.KeySchema { + var attr *Attribute + ad := findAttributeDefinitionByName(t.AttributeDefinitions, k.AttributeName) + if ad == nil { + return pk, errors.New("An inconsistency found in TableDescriptionT") + } + attr = ad.GetEmptyAttribute() + if attr == nil { + return pk, errors.New("An inconsistency found in TableDescriptionT") + } + + switch k.KeyType { + case "HASH": + pk.KeyAttribute = attr + case "RANGE": + pk.RangeAttribute = attr + } + } + return +} + +func (s *Server) NewTable(name string, key PrimaryKey) *Table { + return &Table{s, name, key} +} + +func (s *Server) ListTables() ([]string, error) { + var tables []string + + query := NewEmptyQuery() + + jsonResponse, err := s.queryServer(target("ListTables"), query) + + if err != nil { + return nil, err + } + + json, err := simplejson.NewJson(jsonResponse) + + if err != nil { + return nil, err + } + + response, err := json.Get("TableNames").Array() + + if err != nil { + message := fmt.Sprintf("Unexpected response %s", jsonResponse) + return nil, errors.New(message) + } + + for _, value := range response { + if t, ok := (value).(string); ok { + tables = append(tables, t) + } + } + + return tables, nil +} + +func (s *Server) CreateTable(tableDescription TableDescriptionT) (string, error) { + query := NewEmptyQuery() + query.AddCreateRequestTable(tableDescription) + + jsonResponse, err := s.queryServer(target("CreateTable"), query) + + if err != nil { + return "unknown", err + } + + json, err := simplejson.NewJson(jsonResponse) + + if err != nil { + return "unknown", err + } + + return json.Get("TableDescription").Get("TableStatus").MustString(), nil +} + +func (s *Server) DeleteTable(tableDescription TableDescriptionT) (string, error) { + query := NewEmptyQuery() + query.AddDeleteRequestTable(tableDescription) + + jsonResponse, err := s.queryServer(target("DeleteTable"), query) + + if err != nil { + return "unknown", err + } + + json, err := simplejson.NewJson(jsonResponse) + + if err != nil { + return "unknown", err + } + + return json.Get("TableDescription").Get("TableStatus").MustString(), nil +} + +func (t *Table) DescribeTable() (*TableDescriptionT, error) { + return t.Server.DescribeTable(t.Name) +} + +func (s *Server) DescribeTable(name string) (*TableDescriptionT, error) { + q := NewEmptyQuery() + q.addTableByName(name) + + jsonResponse, err := s.queryServer(target("DescribeTable"), q) + if err != nil { + return nil, err + } + + var r describeTableResponse + err = json.Unmarshal(jsonResponse, &r) + if err != nil { + return nil, err + } + + return &r.Table, nil +} + +func (s *Server) UpdateTable(tableDescription TableDescriptionT) (string, error) { + query := NewEmptyQuery() + query.AddUpdateRequestTable(tableDescription) + + jsonResponse, err := s.queryServer(target("UpdateTable"), query) + + if err != nil { + return "unknown", err + } + + json, err := simplejson.NewJson(jsonResponse) + + if err != nil { + return "unknown", err + } + + return json.Get("TableDescription").Get("TableStatus").MustString(), nil +} + +func (t *Table) ListStreams(startArn string) ([]StreamListItemT, error) { + return t.Server.ListTableStreams(t.Name, startArn) +} + +func (t *Table) LimitedListStreams(startArn string, limit int64) ([]StreamListItemT, error) { + return t.Server.LimitedListTableStreams(t.Name, startArn, limit) +} + +func keyParam(k *PrimaryKey, hashKey string, rangeKey string) string { + value := fmt.Sprintf("{\"HashKeyElement\":{%s}", keyValue(k.KeyAttribute.Type, hashKey)) + + if k.RangeAttribute != nil { + value = fmt.Sprintf("%s,\"RangeKeyElement\":{%s}", value, + keyValue(k.RangeAttribute.Type, rangeKey)) + } + + return fmt.Sprintf("\"Key\":%s}", value) +} + +func keyValue(key string, value string) string { + return fmt.Sprintf("\"%s\":\"%s\"", key, value) +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/table_test.go b/vendor/github.com/goamz/goamz/dynamodb/table_test.go new file mode 100755 index 000000000..8925bdc1b --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/table_test.go @@ -0,0 +1,79 @@ +package dynamodb_test + +import ( + "github.com/goamz/goamz/dynamodb" + . "gopkg.in/check.v1" +) + +type TableSuite struct { + TableDescriptionT dynamodb.TableDescriptionT + DynamoDBTest +} + +func (s *TableSuite) SetUpSuite(c *C) { + setUpAuth(c) + s.DynamoDBTest.TableDescriptionT = s.TableDescriptionT + s.server = &dynamodb.Server{dynamodb_auth, dynamodb_region} + pk, err := s.TableDescriptionT.BuildPrimaryKey() + if err != nil { + c.Skip(err.Error()) + } + s.table = s.server.NewTable(s.TableDescriptionT.TableName, pk) + + // Cleanup + s.TearDownSuite(c) +} + +var table_suite = &TableSuite{ + TableDescriptionT: dynamodb.TableDescriptionT{ + TableName: "DynamoDBTestMyTable", + AttributeDefinitions: []dynamodb.AttributeDefinitionT{ + dynamodb.AttributeDefinitionT{"TestHashKey", "S"}, + dynamodb.AttributeDefinitionT{"TestRangeKey", "N"}, + dynamodb.AttributeDefinitionT{"TestSecKey", "N"}, + }, + KeySchema: []dynamodb.KeySchemaT{ + dynamodb.KeySchemaT{"TestHashKey", "HASH"}, + dynamodb.KeySchemaT{"TestRangeKey", "RANGE"}, + }, + GlobalSecondaryIndexes: []dynamodb.GlobalSecondaryIndexT{ + dynamodb.GlobalSecondaryIndexT{ + IndexName: "gsiTest", + KeySchema: []dynamodb.KeySchemaT{ + dynamodb.KeySchemaT{"TestHashKey", "HASH"}, + dynamodb.KeySchemaT{"TestSecKey", "RANGE"}, + }, + Projection: dynamodb.ProjectionT{"ALL"}, + ProvisionedThroughput: dynamodb.ProvisionedThroughputT{ + ReadCapacityUnits: 1, + WriteCapacityUnits: 1, + }, + }, + }, + ProvisionedThroughput: dynamodb.ProvisionedThroughputT{ + ReadCapacityUnits: 1, + WriteCapacityUnits: 1, + }, + }, +} + +var _ = Suite(table_suite) + +func (s *TableSuite) TestCreateListTable(c *C) { + status, err := s.server.CreateTable(s.TableDescriptionT) + if err != nil { + c.Fatal(err) + } + if status != "ACTIVE" && status != "CREATING" { + c.Error("Expect status to be ACTIVE or CREATING") + } + + s.WaitUntilStatus(c, "ACTIVE") + + tables, err := s.server.ListTables() + if err != nil { + c.Fatal(err) + } + c.Check(len(tables), Not(Equals), 0) + c.Check(findTableByName(tables, s.TableDescriptionT.TableName), Equals, true) +} diff --git a/vendor/github.com/goamz/goamz/dynamodb/update_item.go b/vendor/github.com/goamz/goamz/dynamodb/update_item.go new file mode 100644 index 000000000..280eb4bed --- /dev/null +++ b/vendor/github.com/goamz/goamz/dynamodb/update_item.go @@ -0,0 +1,94 @@ +package dynamodb + +import simplejson "github.com/bitly/go-simplejson" + +/* +Construct an update item query. + +The query can be composed via chaining and then executed via Execute() + +Usage: + update := table.UpdateItem(key) + .ReturnValues(dynamodb.UPDATED_NEW) + .UpdateExpression("SET Counter = Counter + :incr") + .UpdateCondition("Counter < :checkVal") + .ExpressionAttributes(NewNumberAttribute(":incr", "1"), NewNumberAttribute(":checkVal", 42)) + result, err := update.Execute() + if err == nil { + log.Printf("Counter is now %v", result.Attributes["Counter"].Value) + } + +*/ +func (t *Table) UpdateItem(key *Key) *UpdateItem { + q := NewQuery(t) + q.AddKey(t, key) + return &UpdateItem{table: t, query: q} +} + +type UpdateItem struct { + table *Table + query *Query + hasReturnValues bool +} + +// Specify how return values are to be provided. +func (u *UpdateItem) ReturnValues(returnValues ReturnValues) *UpdateItem { + u.hasReturnValues = (returnValues != NONE) + u.query.AddReturnValues(returnValues) + return u +} + +/* +Specify an update expression and optional attribute settings at the same time. + + update.UpdateExpression("SET Foo = Foo + :incr", dynamodb.NewNumberAttribute(":incr", "7")) + +is equivalent to + + update.UpdateExpression("SET Foo = Foo + :incr") + .ExpressionAttributes(NewNumberAttribute(":incr", "7")) + +*/ +func (u *UpdateItem) UpdateExpression(expression string, attributes ...Attribute) *UpdateItem { + u.query.AddUpdateExpression(expression) + u.ExpressionAttributes(attributes...) + return u +} + +// Specify attribute substitutions to be used in expressions. +func (u *UpdateItem) ExpressionAttributes(attributes ...Attribute) *UpdateItem { + u.query.AddExpressionAttributes(attributes) + return u +} + +// Specify a check condition for conditional updates. +func (u *UpdateItem) ConditionExpression(expression string) *UpdateItem { + u.query.AddConditionExpression(expression) + return u +} + +// Execute this query. +func (u *UpdateItem) Execute() (*UpdateResult, error) { + jsonResponse, err := u.table.Server.queryServer(target("UpdateItem"), u.query) + + if err != nil { + return nil, err + } + + if u.hasReturnValues { + resp, err := simplejson.NewJson(jsonResponse) + if err != nil { + return nil, err + } + attrib, err := resp.Get("Attributes").Map() + if err != nil { + return nil, err + } + return &UpdateResult{parseAttributes(attrib)}, nil + } + return nil, nil +} + +type UpdateResult struct { + Attributes map[string]*Attribute +} diff --git a/vendor/github.com/goamz/goamz/ec2/ec2.go b/vendor/github.com/goamz/goamz/ec2/ec2.go new file mode 100644 index 000000000..e6ec612cf --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/ec2.go @@ -0,0 +1,2267 @@ +// +// goamz - Go packages to interact with the Amazon Web Services. +// +// https://wiki.ubuntu.com/goamz +// +// Copyright (c) 2011 Canonical Ltd. +// +// Written by Gustavo Niemeyer +// + +package ec2 + +import ( + "crypto/rand" + "encoding/hex" + "encoding/xml" + "fmt" + "log" + "net/http" + "net/http/httputil" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "github.com/goamz/goamz/aws" +) + +const debug = false + +// The EC2 type encapsulates operations with a specific EC2 region. +type EC2 struct { + aws.Auth + aws.Region + httpClient *http.Client + private byte // Reserve the right of using private data. +} + +// NewWithClient creates a new EC2 with a custom http client +func NewWithClient(auth aws.Auth, region aws.Region, client *http.Client) *EC2 { + return &EC2{auth, region, client, 0} +} + +// New creates a new EC2. +func New(auth aws.Auth, region aws.Region) *EC2 { + return NewWithClient(auth, region, aws.RetryingClient) +} + +// ---------------------------------------------------------------------------- +// Filtering helper. + +// Filter builds filtering parameters to be used in an EC2 query which supports +// filtering. For example: +// +// filter := NewFilter() +// filter.Add("architecture", "i386") +// filter.Add("launch-index", "0") +// resp, err := ec2.Instances(nil, filter) +// +type Filter struct { + m map[string][]string +} + +// NewFilter creates a new Filter. +func NewFilter() *Filter { + return &Filter{make(map[string][]string)} +} + +// Add appends a filtering parameter with the given name and value(s). +func (f *Filter) Add(name string, value ...string) { + f.m[name] = append(f.m[name], value...) +} + +func (f *Filter) addParams(params map[string]string) { + if f != nil { + a := make([]string, len(f.m)) + i := 0 + for k := range f.m { + a[i] = k + i++ + } + sort.StringSlice(a).Sort() + for i, k := range a { + prefix := "Filter." + strconv.Itoa(i+1) + params[prefix+".Name"] = k + for j, v := range f.m[k] { + params[prefix+".Value."+strconv.Itoa(j+1)] = v + } + } + } +} + +// ---------------------------------------------------------------------------- +// Request dispatching logic. + +// Error encapsulates an error returned by EC2. +// +// See http://goo.gl/VZGuC for more details. +type Error struct { + // HTTP status code (200, 403, ...) + StatusCode int + // EC2 error code ("UnsupportedOperation", ...) + Code string + // The human-oriented error message + Message string + RequestId string `xml:"RequestID"` +} + +func (err *Error) Error() string { + if err.Code == "" { + return err.Message + } + + return fmt.Sprintf("%s (%s)", err.Message, err.Code) +} + +// For now a single error inst is being exposed. In the future it may be useful +// to provide access to all of them, but rather than doing it as an array/slice, +// use a *next pointer, so that it's backward compatible and it continues to be +// easy to handle the first error, which is what most people will want. +type xmlErrors struct { + RequestId string `xml:"RequestID"` + Errors []Error `xml:"Errors>Error"` +} + +var timeNow = time.Now + +func (ec2 *EC2) query(params map[string]string, resp interface{}) error { + params["Version"] = "2014-02-01" + params["Timestamp"] = timeNow().In(time.UTC).Format(time.RFC3339) + endpoint, err := url.Parse(ec2.Region.EC2Endpoint) + if err != nil { + return err + } + if endpoint.Path == "" { + endpoint.Path = "/" + } + sign(ec2.Auth, "GET", endpoint.Path, params, endpoint.Host) + endpoint.RawQuery = multimap(params).Encode() + if debug { + log.Printf("get { %v } -> {\n", endpoint.String()) + } + r, err := ec2.httpClient.Get(endpoint.String()) + if err != nil { + return err + } + defer r.Body.Close() + + if debug { + dump, _ := httputil.DumpResponse(r, true) + log.Printf("response:\n") + log.Printf("%v\n}\n", string(dump)) + } + if r.StatusCode != 200 { + return buildError(r) + } + err = xml.NewDecoder(r.Body).Decode(resp) + return err +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} + +func buildError(r *http.Response) error { + errors := xmlErrors{} + xml.NewDecoder(r.Body).Decode(&errors) + var err Error + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } + err.RequestId = errors.RequestId + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +func makeParams(action string) map[string]string { + params := make(map[string]string) + params["Action"] = action + return params +} + +func addParamsList(params map[string]string, label string, ids []string) { + for i, id := range ids { + params[label+"."+strconv.Itoa(i+1)] = id + } +} + +func addBlockDeviceParams(prename string, params map[string]string, blockdevices []BlockDeviceMapping) { + for i, k := range blockdevices { + // Fixup index since Amazon counts these from 1 + prefix := prename + "BlockDeviceMapping." + strconv.Itoa(i+1) + "." + + if k.DeviceName != "" { + params[prefix+"DeviceName"] = k.DeviceName + } + if k.VirtualName != "" { + params[prefix+"VirtualName"] = k.VirtualName + } + if k.SnapshotId != "" { + params[prefix+"Ebs.SnapshotId"] = k.SnapshotId + } + if k.VolumeType != "" { + params[prefix+"Ebs.VolumeType"] = k.VolumeType + } + if k.IOPS != 0 { + params[prefix+"Ebs.Iops"] = strconv.FormatInt(k.IOPS, 10) + } + if k.VolumeSize != 0 { + params[prefix+"Ebs.VolumeSize"] = strconv.FormatInt(k.VolumeSize, 10) + } + if k.DeleteOnTermination { + params[prefix+"Ebs.DeleteOnTermination"] = "true" + } + if k.NoDevice { + params[prefix+"NoDevice"] = "true" + } + } +} + +// ---------------------------------------------------------------------------- +// Instance management functions and types. + +// RunInstancesOptions encapsulates options for the respective request in EC2. +// +// See http://goo.gl/Mcm3b for more details. +type RunInstancesOptions struct { + ImageId string + MinCount int + MaxCount int + KeyName string + InstanceType string + SecurityGroups []SecurityGroup + KernelId string + RamdiskId string + UserData []byte + AvailabilityZone string + PlacementGroupName string + Tenancy string + Monitoring bool + SubnetId string + DisableAPITermination bool + ShutdownBehavior string + PrivateIPAddress string + IamInstanceProfile IamInstanceProfile + BlockDevices []BlockDeviceMapping + EbsOptimized bool + AssociatePublicIpAddress bool +} + +// Response to a RunInstances request. +// +// See http://goo.gl/Mcm3b for more details. +type RunInstancesResp struct { + RequestId string `xml:"requestId"` + ReservationId string `xml:"reservationId"` + OwnerId string `xml:"ownerId"` + SecurityGroups []SecurityGroup `xml:"groupSet>item"` + Instances []Instance `xml:"instancesSet>item"` +} + +// Instance encapsulates a running instance in EC2. +// +// See http://goo.gl/OCH8a for more details. +type Instance struct { + + // General instance information + InstanceId string `xml:"instanceId"` // The ID of the instance launched + InstanceType string `xml:"instanceType"` // The instance type eg. m1.small | m1.medium | m1.large etc + AvailabilityZone string `xml:"placement>availabilityZone"` // The Availability Zone the instance is located in + Tags []Tag `xml:"tagSet>item"` // Any tags assigned to the resource + State InstanceState `xml:"instanceState"` // The current state of the instance + Reason string `xml:"reason"` // The reason for the most recent state transition. This might be an empty string + StateReason InstanceStateReason `xml:"stateReason"` // The reason for the most recent state transition + ImageId string `xml:"imageId"` // The ID of the AMI used to launch the instance + KeyName string `xml:"keyName"` // The key pair name, if this instance was launched with an associated key pair + Monitoring string `xml:"monitoring>state"` // Valid values: disabled | enabled | pending + IamInstanceProfile IamInstanceProfile `xml:"iamInstanceProfile"` // The IAM instance profile associated with the instance + LaunchTime string `xml:"launchTime"` // The time the instance was launched + OwnerId string // This isn't currently returned in the response, and is taken from the parent reservation + + // More specific information + Architecture string `xml:"architecture"` // Valid values: i386 | x86_64 + Hypervisor string `xml:"hypervisor"` // Valid values: ovm | xen + KernelId string `xml:"kernelId"` // The kernel associated with this instance + RamDiskId string `xml:"ramdiskId"` // The RAM disk associated with this instance + Platform string `xml:"platform"` // The value is Windows for Windows AMIs; otherwise blank + VirtualizationType string `xml:"virtualizationType"` // Valid values: paravirtual | hvm + AMILaunchIndex int `xml:"amiLaunchIndex"` // The AMI launch index, which can be used to find this instance in the launch group + PlacementGroupName string `xml:"placement>groupName"` // The name of the placement group the instance is in (for cluster compute instances) + Tenancy string `xml:"placement>tenancy"` // (VPC only) Valid values: default | dedicated + InstanceLifecycle string `xml:"instanceLifecycle"` // Spot instance? Valid values: "spot" or blank + SpotInstanceRequestId string `xml:"spotInstanceRequestId"` // The ID of the Spot Instance request + ClientToken string `xml:"clientToken"` // The idempotency token you provided when you launched the instance + ProductCodes []ProductCode `xml:"productCodes>item"` // The product codes attached to this instance + + // Storage + RootDeviceType string `xml:"rootDeviceType"` // Valid values: ebs | instance-store + RootDeviceName string `xml:"rootDeviceName"` // The root device name (for example, /dev/sda1) + BlockDevices []BlockDevice `xml:"blockDeviceMapping>item"` // Any block device mapping entries for the instance + EbsOptimized bool `xml:"ebsOptimized"` // Indicates whether the instance is optimized for Amazon EBS I/O + + // Network + DNSName string `xml:"dnsName"` // The public DNS name assigned to the instance. This element remains empty until the instance enters the running state + PrivateDNSName string `xml:"privateDnsName"` // The private DNS name assigned to the instance. This DNS name can only be used inside the Amazon EC2 network. This element remains empty until the instance enters the running state + IPAddress string `xml:"ipAddress"` // The public IP address assigned to the instance + PrivateIPAddress string `xml:"privateIpAddress"` // The private IP address assigned to the instance + SubnetId string `xml:"subnetId"` // The ID of the subnet in which the instance is running + VpcId string `xml:"vpcId"` // The ID of the VPC in which the instance is running + SecurityGroups []SecurityGroup `xml:"groupSet>item"` // A list of the security groups for the instance + + // Advanced Networking + NetworkInterfaces []InstanceNetworkInterface `xml:"networkInterfaceSet>item"` // (VPC) One or more network interfaces for the instance + SourceDestCheck bool `xml:"sourceDestCheck"` // Controls whether source/destination checking is enabled on the instance + SriovNetSupport string `xml:"sriovNetSupport"` // Specifies whether enhanced networking is enabled. Valid values: simple +} + +// isSpotInstance returns if the instance is a spot instance +func (i Instance) IsSpotInstance() bool { + if i.InstanceLifecycle == "spot" { + return true + } + return false +} + +type BlockDevice struct { + DeviceName string `xml:"deviceName"` + EBS EBS `xml:"ebs"` +} + +type EBS struct { + VolumeId string `xml:"volumeId"` + Status string `xml:"status"` + AttachTime string `xml:"attachTime"` + DeleteOnTermination bool `xml:"deleteOnTermination"` +} + +// ProductCode represents a product code +// See http://goo.gl/hswmQm for more details. +type ProductCode struct { + ProductCode string `xml:"productCode"` // The product code + Type string `xml:"type"` // Valid values: devpay | marketplace +} + +// InstanceNetworkInterface represents a network interface attached to an instance +// See http://goo.gl/9eW02N for more details. +type InstanceNetworkInterface struct { + Id string `xml:"networkInterfaceId"` + Description string `xml:"description"` + SubnetId string `xml:"subnetId"` + VpcId string `xml:"vpcId"` + OwnerId string `xml:"ownerId"` // The ID of the AWS account that created the network interface. + Status string `xml:"status"` // Valid values: available | attaching | in-use | detaching + MacAddress string `xml:"macAddress"` + PrivateIPAddress string `xml:"privateIpAddress"` + PrivateDNSName string `xml:"privateDnsName"` + SourceDestCheck bool `xml:"sourceDestCheck"` + SecurityGroups []SecurityGroup `xml:"groupSet>item"` + Attachment InstanceNetworkInterfaceAttachment `xml:"attachment"` + Association InstanceNetworkInterfaceAssociation `xml:"association"` + PrivateIPAddresses []InstancePrivateIpAddress `xml:"privateIpAddressesSet>item"` +} + +// InstanceNetworkInterfaceAttachment describes a network interface attachment to an instance +// See http://goo.gl/0ql0Cg for more details +type InstanceNetworkInterfaceAttachment struct { + AttachmentID string `xml:"attachmentID"` // The ID of the network interface attachment. + DeviceIndex int32 `xml:"deviceIndex"` // The index of the device on the instance for the network interface attachment. + Status string `xml:"status"` // Valid values: attaching | attached | detaching | detached + AttachTime string `xml:"attachTime"` // Time attached, as a Datetime + DeleteOnTermination bool `xml:"deleteOnTermination"` // Indicates whether the network interface is deleted when the instance is terminated. +} + +// Describes association information for an Elastic IP address. +// See http://goo.gl/YCDdMe for more details +type InstanceNetworkInterfaceAssociation struct { + PublicIP string `xml:"publicIp"` // The address of the Elastic IP address bound to the network interface + PublicDNSName string `xml:"publicDnsName"` // The public DNS name + IPOwnerId string `xml:"ipOwnerId"` // The ID of the owner of the Elastic IP address +} + +// InstancePrivateIpAddress describes a private IP address +// See http://goo.gl/irN646 for more details +type InstancePrivateIpAddress struct { + PrivateIPAddress string `xml:"privateIpAddress"` // The private IP address of the network interface + PrivateDNSName string `xml:"privateDnsName"` // The private DNS name + Primary bool `xml:"primary"` // Indicates whether this IP address is the primary private IP address of the network interface + Association InstanceNetworkInterfaceAssociation `xml:"association"` // The association information for an Elastic IP address for the network interface +} + +// IamInstanceProfile +// See http://goo.gl/PjyijL for more details +type IamInstanceProfile struct { + ARN string `xml:"arn"` + Id string `xml:"id"` + Name string `xml:"name"` +} + +// RunInstances starts new instances in EC2. +// If options.MinCount and options.MaxCount are both zero, a single instance +// will be started; otherwise if options.MaxCount is zero, options.MinCount +// will be used instead. +// +// See http://goo.gl/Mcm3b for more details. +func (ec2 *EC2) RunInstances(options *RunInstancesOptions) (resp *RunInstancesResp, err error) { + params := makeParams("RunInstances") + params["ImageId"] = options.ImageId + params["InstanceType"] = options.InstanceType + var min, max int + if options.MinCount == 0 && options.MaxCount == 0 { + min = 1 + max = 1 + } else if options.MaxCount == 0 { + min = options.MinCount + max = min + } else { + min = options.MinCount + max = options.MaxCount + } + params["MinCount"] = strconv.Itoa(min) + params["MaxCount"] = strconv.Itoa(max) + token, err := clientToken() + if err != nil { + return nil, err + } + params["ClientToken"] = token + + if options.KeyName != "" { + params["KeyName"] = options.KeyName + } + if options.KernelId != "" { + params["KernelId"] = options.KernelId + } + if options.RamdiskId != "" { + params["RamdiskId"] = options.RamdiskId + } + if options.UserData != nil { + userData := make([]byte, b64.EncodedLen(len(options.UserData))) + b64.Encode(userData, options.UserData) + params["UserData"] = string(userData) + } + if options.AvailabilityZone != "" { + params["Placement.AvailabilityZone"] = options.AvailabilityZone + } + if options.PlacementGroupName != "" { + params["Placement.GroupName"] = options.PlacementGroupName + } + if options.Tenancy != "" { + params["Placement.Tenancy"] = options.Tenancy + } + if options.Monitoring { + params["Monitoring.Enabled"] = "true" + } + if options.SubnetId != "" && options.AssociatePublicIpAddress { + // If we have a non-default VPC / Subnet specified, we can flag + // AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided. + // You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise + // you get: Network interfaces and an instance-level subnet ID may not be specified on the same request + // You also need to attach Security Groups to the NetworkInterface instead of the instance, + // to avoid: Network interfaces and an instance-level security groups may not be specified on + // the same request + params["NetworkInterface.0.DeviceIndex"] = "0" + params["NetworkInterface.0.AssociatePublicIpAddress"] = "true" + params["NetworkInterface.0.SubnetId"] = options.SubnetId + + i := 1 + for _, g := range options.SecurityGroups { + // We only have SecurityGroupId's on NetworkInterface's, no SecurityGroup params. + if g.Id != "" { + params["NetworkInterface.0.SecurityGroupId."+strconv.Itoa(i)] = g.Id + i++ + } + } + } else { + if options.SubnetId != "" { + params["SubnetId"] = options.SubnetId + } + + i, j := 1, 1 + for _, g := range options.SecurityGroups { + if g.Id != "" { + params["SecurityGroupId."+strconv.Itoa(i)] = g.Id + i++ + } else { + params["SecurityGroup."+strconv.Itoa(j)] = g.Name + j++ + } + } + } + if options.IamInstanceProfile.ARN != "" { + params["IamInstanceProfile.Arn"] = options.IamInstanceProfile.ARN + } + if options.IamInstanceProfile.Name != "" { + params["IamInstanceProfile.Name"] = options.IamInstanceProfile.Name + } + if options.DisableAPITermination { + params["DisableApiTermination"] = "true" + } + if options.ShutdownBehavior != "" { + params["InstanceInitiatedShutdownBehavior"] = options.ShutdownBehavior + } + if options.PrivateIPAddress != "" { + params["PrivateIpAddress"] = options.PrivateIPAddress + } + if options.EbsOptimized { + params["EbsOptimized"] = "true" + } + + addBlockDeviceParams("", params, options.BlockDevices) + + resp = &RunInstancesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +func clientToken() (string, error) { + // Maximum EC2 client token size is 64 bytes. + // Each byte expands to two when hex encoded. + buf := make([]byte, 32) + _, err := rand.Read(buf) + if err != nil { + return "", err + } + return hex.EncodeToString(buf), nil +} + +// ---------------------------------------------------------------------------- +// Spot Instance management functions and types. + +// The RequestSpotInstances type encapsulates options for the respective request in EC2. +// +// See http://goo.gl/GRZgCD for more details. +type RequestSpotInstances struct { + SpotPrice string + InstanceCount int + Type string + ImageId string + KeyName string + InstanceType string + SecurityGroups []SecurityGroup + IamInstanceProfile string + KernelId string + RamdiskId string + UserData []byte + AvailZone string + PlacementGroupName string + Monitoring bool + SubnetId string + AssociatePublicIpAddress bool + PrivateIPAddress string + BlockDevices []BlockDeviceMapping +} + +type SpotInstanceSpec struct { + ImageId string + KeyName string + InstanceType string + SecurityGroups []SecurityGroup + IamInstanceProfile string + KernelId string + RamdiskId string + UserData []byte + AvailZone string + PlacementGroupName string + Monitoring bool + SubnetId string + AssociatePublicIpAddress bool + PrivateIPAddress string + BlockDevices []BlockDeviceMapping +} + +type SpotLaunchSpec struct { + ImageId string `xml:"imageId"` + KeyName string `xml:"keyName"` + InstanceType string `xml:"instanceType"` + SecurityGroups []SecurityGroup `xml:"groupSet>item"` + IamInstanceProfile string `xml:"iamInstanceProfile"` + KernelId string `xml:"kernelId"` + RamdiskId string `xml:"ramdiskId"` + PlacementGroupName string `xml:"placement>groupName"` + Monitoring bool `xml:"monitoring>enabled"` + SubnetId string `xml:"subnetId"` + BlockDevices []BlockDeviceMapping `xml:"blockDeviceMapping>item"` +} + +type SpotRequestResult struct { + SpotRequestId string `xml:"spotInstanceRequestId"` + SpotPrice string `xml:"spotPrice"` + Type string `xml:"type"` + AvailZone string `xml:"launchedAvailabilityZone"` + InstanceId string `xml:"instanceId"` + State string `xml:"state"` + SpotLaunchSpec SpotLaunchSpec `xml:"launchSpecification"` + CreateTime string `xml:"createTime"` + Tags []Tag `xml:"tagSet>item"` +} + +// Response to a RequestSpotInstances request. +// +// See http://goo.gl/GRZgCD for more details. +type RequestSpotInstancesResp struct { + RequestId string `xml:"requestId"` + SpotRequestResults []SpotRequestResult `xml:"spotInstanceRequestSet>item"` +} + +// RequestSpotInstances requests a new spot instances in EC2. +func (ec2 *EC2) RequestSpotInstances(options *RequestSpotInstances) (resp *RequestSpotInstancesResp, err error) { + params := makeParams("RequestSpotInstances") + prefix := "LaunchSpecification" + "." + + params["SpotPrice"] = options.SpotPrice + params[prefix+"ImageId"] = options.ImageId + params[prefix+"InstanceType"] = options.InstanceType + + if options.InstanceCount != 0 { + params["InstanceCount"] = strconv.Itoa(options.InstanceCount) + } + if options.KeyName != "" { + params[prefix+"KeyName"] = options.KeyName + } + if options.KernelId != "" { + params[prefix+"KernelId"] = options.KernelId + } + if options.RamdiskId != "" { + params[prefix+"RamdiskId"] = options.RamdiskId + } + if options.UserData != nil { + userData := make([]byte, b64.EncodedLen(len(options.UserData))) + b64.Encode(userData, options.UserData) + params[prefix+"UserData"] = string(userData) + } + if options.AvailZone != "" { + params[prefix+"Placement.AvailabilityZone"] = options.AvailZone + } + if options.PlacementGroupName != "" { + params[prefix+"Placement.GroupName"] = options.PlacementGroupName + } + if options.Monitoring { + params[prefix+"Monitoring.Enabled"] = "true" + } + if options.SubnetId != "" && options.AssociatePublicIpAddress { + // If we have a non-default VPC / Subnet specified, we can flag + // AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided. + // You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise + // you get: Network interfaces and an instance-level subnet ID may not be specified on the same request + // You also need to attach Security Groups to the NetworkInterface instead of the instance, + // to avoid: Network interfaces and an instance-level security groups may not be specified on + // the same request + params[prefix+"NetworkInterface.0.DeviceIndex"] = "0" + params[prefix+"NetworkInterface.0.AssociatePublicIpAddress"] = "true" + params[prefix+"NetworkInterface.0.SubnetId"] = options.SubnetId + + i := 1 + for _, g := range options.SecurityGroups { + // We only have SecurityGroupId's on NetworkInterface's, no SecurityGroup params. + if g.Id != "" { + params[prefix+"NetworkInterface.0.SecurityGroupId."+strconv.Itoa(i)] = g.Id + i++ + } + } + } else { + if options.SubnetId != "" { + params[prefix+"SubnetId"] = options.SubnetId + } + + i, j := 1, 1 + for _, g := range options.SecurityGroups { + if g.Id != "" { + params[prefix+"SecurityGroupId."+strconv.Itoa(i)] = g.Id + i++ + } else { + params[prefix+"SecurityGroup."+strconv.Itoa(j)] = g.Name + j++ + } + } + } + if options.IamInstanceProfile != "" { + params[prefix+"IamInstanceProfile.Name"] = options.IamInstanceProfile + } + if options.PrivateIPAddress != "" { + params[prefix+"PrivateIpAddress"] = options.PrivateIPAddress + } + addBlockDeviceParams(prefix, params, options.BlockDevices) + + resp = &RequestSpotInstancesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// Response to a DescribeSpotInstanceRequests request. +// +// See http://goo.gl/KsKJJk for more details. +type SpotRequestsResp struct { + RequestId string `xml:"requestId"` + SpotRequestResults []SpotRequestResult `xml:"spotInstanceRequestSet>item"` +} + +// DescribeSpotInstanceRequests returns details about spot requests in EC2. Both parameters +// are optional, and if provided will limit the spot requests returned to those +// matching the given spot request ids or filtering rules. +// +// See http://goo.gl/KsKJJk for more details. +func (ec2 *EC2) DescribeSpotRequests(spotrequestIds []string, filter *Filter) (resp *SpotRequestsResp, err error) { + params := makeParams("DescribeSpotInstanceRequests") + addParamsList(params, "SpotInstanceRequestId", spotrequestIds) + filter.addParams(params) + resp = &SpotRequestsResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// Response to a CancelSpotInstanceRequests request. +// +// See http://goo.gl/3BKHj for more details. +type CancelSpotRequestResult struct { + SpotRequestId string `xml:"spotInstanceRequestId"` + State string `xml:"state"` +} +type CancelSpotRequestsResp struct { + RequestId string `xml:"requestId"` + CancelSpotRequestResults []CancelSpotRequestResult `xml:"spotInstanceRequestSet>item"` +} + +// CancelSpotRequests requests the cancellation of spot requests when the given ids. +// +// See http://goo.gl/3BKHj for more details. +func (ec2 *EC2) CancelSpotRequests(spotrequestIds []string) (resp *CancelSpotRequestsResp, err error) { + params := makeParams("CancelSpotInstanceRequests") + addParamsList(params, "SpotInstanceRequestId", spotrequestIds) + resp = &CancelSpotRequestsResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// Response to a TerminateInstances request. +// +// See http://goo.gl/3BKHj for more details. +type TerminateInstancesResp struct { + RequestId string `xml:"requestId"` + StateChanges []InstanceStateChange `xml:"instancesSet>item"` +} + +// InstanceState encapsulates the state of an instance in EC2. +// +// See http://goo.gl/y3ZBq for more details. +type InstanceState struct { + Code int `xml:"code"` // Watch out, bits 15-8 have unpublished meaning. + Name string `xml:"name"` +} + +// InstanceStateChange informs of the previous and current states +// for an instance when a state change is requested. +type InstanceStateChange struct { + InstanceId string `xml:"instanceId"` + CurrentState InstanceState `xml:"currentState"` + PreviousState InstanceState `xml:"previousState"` +} + +// InstanceStateReason describes a state change for an instance in EC2 +// +// See http://goo.gl/KZkbXi for more details +type InstanceStateReason struct { + Code string `xml:"code"` + Message string `xml:"message"` +} + +// TerminateInstances requests the termination of instances when the given ids. +// +// See http://goo.gl/3BKHj for more details. +func (ec2 *EC2) TerminateInstances(instIds []string) (resp *TerminateInstancesResp, err error) { + params := makeParams("TerminateInstances") + addParamsList(params, "InstanceId", instIds) + resp = &TerminateInstancesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// Response to a DescribeInstances request. +// +// See http://goo.gl/mLbmw for more details. +type DescribeInstancesResp struct { + RequestId string `xml:"requestId"` + Reservations []Reservation `xml:"reservationSet>item"` +} + +// Reservation represents details about a reservation in EC2. +// +// See http://goo.gl/0ItPT for more details. +type Reservation struct { + ReservationId string `xml:"reservationId"` + OwnerId string `xml:"ownerId"` + RequesterId string `xml:"requesterId"` + SecurityGroups []SecurityGroup `xml:"groupSet>item"` + Instances []Instance `xml:"instancesSet>item"` +} + +// Instances returns details about instances in EC2. Both parameters +// are optional, and if provided will limit the instances returned to those +// matching the given instance ids or filtering rules. +// +// See http://goo.gl/4No7c for more details. +func (ec2 *EC2) DescribeInstances(instIds []string, filter *Filter) (resp *DescribeInstancesResp, err error) { + params := makeParams("DescribeInstances") + addParamsList(params, "InstanceId", instIds) + filter.addParams(params) + resp = &DescribeInstancesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + // Add additional parameters to instances which aren't available in the response + for i, rsv := range resp.Reservations { + ownerId := rsv.OwnerId + for j, inst := range rsv.Instances { + inst.OwnerId = ownerId + resp.Reservations[i].Instances[j] = inst + } + } + + return +} + +// DescribeInstanceStatusOptions encapsulates the query parameters for the corresponding action. +// +// See http:////goo.gl/2FBTdS for more details. +type DescribeInstanceStatusOptions struct { + InstanceIds []string // If non-empty, limit the query to this subset of instances. Maximum length of 100. + IncludeAllInstances bool // If true, describe all instances, instead of just running instances (the default). + MaxResults int // Maximum number of results to return. Minimum of 5. Maximum of 1000. + NextToken string // The token for the next set of items to return. (You received this token from a prior call.) +} + +// Response to a DescribeInstanceStatus request. +// +// See http://goo.gl/2FBTdS for more details. +type DescribeInstanceStatusResp struct { + RequestId string `xml:"requestId"` + InstanceStatusSet []InstanceStatusItem `xml:"instanceStatusSet>item"` + NextToken string `xml:"nextToken"` +} + +// InstanceStatusItem describes the instance status, cause, details, and potential actions to take in response. +// +// See http://goo.gl/oImFZZ for more details. +type InstanceStatusItem struct { + InstanceId string `xml:"instanceId"` + AvailabilityZone string `xml:"availabilityZone"` + Events []InstanceStatusEvent `xml:"eventsSet>item"` // Extra information regarding events associated with the instance. + InstanceState InstanceState `xml:"instanceState"` // The intended state of the instance. Calls to DescribeInstanceStatus require that an instance be in the running state. + SystemStatus InstanceStatus `xml:"systemStatus"` + InstanceStatus InstanceStatus `xml:"instanceStatus"` +} + +// InstanceStatusEvent describes an instance event. +// +// See http://goo.gl/PXsDTn for more details. +type InstanceStatusEvent struct { + Code string `xml:"code"` // The associated code of the event. + Description string `xml:"description"` // A description of the event. + NotBefore string `xml:"notBefore"` // The earliest scheduled start time for the event. + NotAfter string `xml:"notAfter"` // The latest scheduled end time for the event. +} + +// InstanceStatus describes the status of an instance with details. +// +// See http://goo.gl/eFch4S for more details. +type InstanceStatus struct { + Status string `xml:"status"` // The instance status. + Details InstanceStatusDetails `xml:"details"` // The system instance health or application instance health. +} + +// InstanceStatusDetails describes the instance status with the cause and more detail. +// +// See http://goo.gl/3qoMC4 for more details. +type InstanceStatusDetails struct { + Name string `xml:"name"` // The type of instance status. + Status string `xml:"status"` // The status. + ImpairedSince string `xml:"impairedSince"` // The time when a status check failed. For an instance that was launched and impaired, this is the time when the instance was launched. +} + +// DescribeInstanceStatus returns instance status information about instances in EC2. +// instIds and filter are optional, and if provided will limit the instances returned to those +// matching the given instance ids or filtering rules. +// all determines whether to report all matching instances or only those in the running state +// +// See http://goo.gl/2FBTdS for more details. +func (ec2 *EC2) DescribeInstanceStatus(options *DescribeInstanceStatusOptions, filter *Filter) (resp *DescribeInstanceStatusResp, err error) { + params := makeParams("DescribeInstanceStatus") + if len(options.InstanceIds) > 0 { + addParamsList(params, "InstanceId", options.InstanceIds) + } + if options.IncludeAllInstances { + params["IncludeAllInstances"] = "true" + } + if options.MaxResults != 0 { + params["MaxResults"] = strconv.Itoa(options.MaxResults) + } + if options.NextToken != "" { + params["NextToken"] = options.NextToken + } + filter.addParams(params) + resp = &DescribeInstanceStatusResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// ---------------------------------------------------------------------------- +// KeyPair management functions and types. + +type CreateKeyPairResp struct { + RequestId string `xml:"requestId"` + KeyName string `xml:"keyName"` + KeyFingerprint string `xml:"keyFingerprint"` + KeyMaterial string `xml:"keyMaterial"` +} + +// CreateKeyPair creates a new key pair and returns the private key contents. +// +// See http://goo.gl/0S6hV +func (ec2 *EC2) CreateKeyPair(keyName string) (resp *CreateKeyPairResp, err error) { + params := makeParams("CreateKeyPair") + params["KeyName"] = keyName + + resp = &CreateKeyPairResp{} + err = ec2.query(params, resp) + if err == nil { + resp.KeyFingerprint = strings.TrimSpace(resp.KeyFingerprint) + } + return +} + +// DeleteKeyPair deletes a key pair. +// +// See http://goo.gl/0bqok +func (ec2 *EC2) DeleteKeyPair(name string) (resp *SimpleResp, err error) { + params := makeParams("DeleteKeyPair") + params["KeyName"] = name + + resp = &SimpleResp{} + err = ec2.query(params, resp) + return +} + +type ImportKeyPairOptions struct { + KeyName string + PublicKeyMaterial string +} + +type ImportKeyPairResp struct { + RequestId string `xml:"requestId"` + KeyName string `xml:"keyName"` + KeyFingerprint string `xml:"keyFingerprint"` +} + +// ImportKeyPair import a key pair. +// +// See http://goo.gl/xpTccS +func (ec2 *EC2) ImportKeyPair(options *ImportKeyPairOptions) (resp *ImportKeyPairResp, err error) { + params := makeParams("ImportKeyPair") + params["KeyName"] = options.KeyName + params["PublicKeyMaterial"] = options.PublicKeyMaterial + + resp = &ImportKeyPairResp{} + err = ec2.query(params, resp) + return +} + +// ResourceTag represents key-value metadata used to classify and organize +// EC2 instances. +// +// See http://goo.gl/bncl3 for more details +type Tag struct { + Key string `xml:"key"` + Value string `xml:"value"` +} + +// CreateTags adds or overwrites one or more tags for the specified taggable resources. +// For a list of tagable resources, see: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html +// +// See http://goo.gl/Vmkqc for more details +func (ec2 *EC2) CreateTags(resourceIds []string, tags []Tag) (resp *SimpleResp, err error) { + params := makeParams("CreateTags") + addParamsList(params, "ResourceId", resourceIds) + + for j, tag := range tags { + params["Tag."+strconv.Itoa(j+1)+".Key"] = tag.Key + params["Tag."+strconv.Itoa(j+1)+".Value"] = tag.Value + } + + resp = &SimpleResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// Response to a StartInstances request. +// +// See http://goo.gl/awKeF for more details. +type StartInstanceResp struct { + RequestId string `xml:"requestId"` + StateChanges []InstanceStateChange `xml:"instancesSet>item"` +} + +// Response to a StopInstances request. +// +// See http://goo.gl/436dJ for more details. +type StopInstanceResp struct { + RequestId string `xml:"requestId"` + StateChanges []InstanceStateChange `xml:"instancesSet>item"` +} + +// StartInstances starts an Amazon EBS-backed AMI that you've previously stopped. +// +// See http://goo.gl/awKeF for more details. +func (ec2 *EC2) StartInstances(ids ...string) (resp *StartInstanceResp, err error) { + params := makeParams("StartInstances") + addParamsList(params, "InstanceId", ids) + resp = &StartInstanceResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// StopInstances requests stopping one or more Amazon EBS-backed instances. +// +// See http://goo.gl/436dJ for more details. +func (ec2 *EC2) StopInstances(ids ...string) (resp *StopInstanceResp, err error) { + params := makeParams("StopInstances") + addParamsList(params, "InstanceId", ids) + resp = &StopInstanceResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// RebootInstance requests a reboot of one or more instances. This operation is asynchronous; +// it only queues a request to reboot the specified instance(s). The operation will succeed +// if the instances are valid and belong to you. +// +// Requests to reboot terminated instances are ignored. +// +// See http://goo.gl/baoUf for more details. +func (ec2 *EC2) RebootInstances(ids ...string) (resp *SimpleResp, err error) { + params := makeParams("RebootInstances") + addParamsList(params, "InstanceId", ids) + resp = &SimpleResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// The ModifyInstanceAttribute request parameters. +type ModifyInstance struct { + InstanceType string + BlockDevices []BlockDeviceMapping + DisableAPITermination bool + EbsOptimized bool + SecurityGroups []SecurityGroup + ShutdownBehavior string + KernelId string + RamdiskId string + SourceDestCheck bool + SriovNetSupport bool + UserData []byte +} + +// Response to a ModifyInstanceAttribute request. +// +// http://goo.gl/icuXh5 for more details. +type ModifyInstanceResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` +} + +// ModifyImageAttribute modifies the specified attribute of the specified instance. +// You can specify only one attribute at a time. To modify some attributes, the +// instance must be stopped. +// +// See http://goo.gl/icuXh5 for more details. +func (ec2 *EC2) ModifyInstance(instId string, options *ModifyInstance) (resp *ModifyInstanceResp, err error) { + params := makeParams("ModifyInstanceAttribute") + params["InstanceId"] = instId + addBlockDeviceParams("", params, options.BlockDevices) + + if options.InstanceType != "" { + params["InstanceType.Value"] = options.InstanceType + } + + if options.DisableAPITermination { + params["DisableApiTermination.Value"] = "true" + } + + if options.EbsOptimized { + params["EbsOptimized"] = "true" + } + + if options.ShutdownBehavior != "" { + params["InstanceInitiatedShutdownBehavior.Value"] = options.ShutdownBehavior + } + + if options.KernelId != "" { + params["Kernel.Value"] = options.KernelId + } + + if options.RamdiskId != "" { + params["Ramdisk.Value"] = options.RamdiskId + } + + if options.SourceDestCheck { + params["SourceDestCheck.Value"] = "true" + } + + if options.SriovNetSupport { + params["SriovNetSupport.Value"] = "simple" + } + + if options.UserData != nil { + userData := make([]byte, b64.EncodedLen(len(options.UserData))) + b64.Encode(userData, options.UserData) + params["UserData"] = string(userData) + } + + i := 1 + for _, g := range options.SecurityGroups { + if g.Id != "" { + params["GroupId."+strconv.Itoa(i)] = g.Id + i++ + } + } + + resp = &ModifyInstanceResp{} + err = ec2.query(params, resp) + if err != nil { + resp = nil + } + return +} + +// Reserved Instances + +// Structures + +// DescribeReservedInstancesResponse structure returned from a DescribeReservedInstances request. +// +// See +type DescribeReservedInstancesResponse struct { + RequestId string `xml:"requestId"` + ReservedInstances []ReservedInstancesResponseItem `xml:"reservedInstancesSet>item"` +} + +// +// +// See +type ReservedInstancesResponseItem struct { + ReservedInstanceId string `xml:"reservedInstancesId"` + InstanceType string `xml:"instanceType"` + AvailabilityZone string `xml:"availabilityZone"` + Start string `xml:"start"` + Duration uint64 `xml:"duration"` + End string `xml:"end"` + FixedPrice float32 `xml:"fixedPrice"` + UsagePrice float32 `xml:"usagePrice"` + InstanceCount int `xml:"instanceCount"` + ProductDescription string `xml:"productDescription"` + State string `xml:"state"` + Tags []Tag `xml:"tagSet->item"` + InstanceTenancy string `xml:"instanceTenancy"` + CurrencyCode string `xml:"currencyCode"` + OfferingType string `xml:"offeringType"` + RecurringCharges []RecurringCharge `xml:"recurringCharges>item"` +} + +// +// +// See +type RecurringCharge struct { + Frequency string `xml:"frequency"` + Amount float32 `xml:"amount"` +} + +// functions +// DescribeReservedInstances +// +// See +func (ec2 *EC2) DescribeReservedInstances(instIds []string, filter *Filter) (resp *DescribeReservedInstancesResponse, err error) { + params := makeParams("DescribeReservedInstances") + + for i, id := range instIds { + params["ReservedInstancesId."+strconv.Itoa(i+1)] = id + } + filter.addParams(params) + + resp = &DescribeReservedInstancesResponse{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// ---------------------------------------------------------------------------- +// Image and snapshot management functions and types. + +// The CreateImage request parameters. +// +// See http://goo.gl/cxU41 for more details. +type CreateImage struct { + InstanceId string + Name string + Description string + NoReboot bool + BlockDevices []BlockDeviceMapping +} + +// Response to a CreateImage request. +// +// See http://goo.gl/cxU41 for more details. +type CreateImageResp struct { + RequestId string `xml:"requestId"` + ImageId string `xml:"imageId"` +} + +// Response to a DescribeImages request. +// +// See http://goo.gl/hLnyg for more details. +type ImagesResp struct { + RequestId string `xml:"requestId"` + Images []Image `xml:"imagesSet>item"` +} + +// Response to a DescribeImageAttribute request. +// +// See http://goo.gl/bHO3zT for more details. +type ImageAttributeResp struct { + RequestId string `xml:"requestId"` + ImageId string `xml:"imageId"` + Kernel string `xml:"kernel>value"` + RamDisk string `xml:"ramdisk>value"` + Description string `xml:"description>value"` + Group string `xml:"launchPermission>item>group"` + UserIds []string `xml:"launchPermission>item>userId"` + ProductCodes []string `xml:"productCodes>item>productCode"` + BlockDevices []BlockDeviceMapping `xml:"blockDeviceMapping>item"` +} + +// The RegisterImage request parameters. +type RegisterImage struct { + ImageLocation string + Name string + Description string + Architecture string + KernelId string + RamdiskId string + RootDeviceName string + VirtType string + BlockDevices []BlockDeviceMapping +} + +// Response to a RegisterImage request. +type RegisterImageResp struct { + RequestId string `xml:"requestId"` + ImageId string `xml:"imageId"` +} + +// Response to a DegisterImage request. +// +// See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DeregisterImage.html +type DeregisterImageResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` +} + +// BlockDeviceMapping represents the association of a block device with an image. +// +// See http://goo.gl/wnDBf for more details. +type BlockDeviceMapping struct { + DeviceName string `xml:"deviceName"` + VirtualName string `xml:"virtualName"` + SnapshotId string `xml:"ebs>snapshotId"` + VolumeType string `xml:"ebs>volumeType"` + VolumeSize int64 `xml:"ebs>volumeSize"` + DeleteOnTermination bool `xml:"ebs>deleteOnTermination"` + NoDevice bool `xml:"noDevice"` + + // The number of I/O operations per second (IOPS) that the volume supports. + IOPS int64 `xml:"ebs>iops"` +} + +// Image represents details about an image. +// +// See http://goo.gl/iSqJG for more details. +type Image struct { + Id string `xml:"imageId"` + Name string `xml:"name"` + Description string `xml:"description"` + Type string `xml:"imageType"` + State string `xml:"imageState"` + Location string `xml:"imageLocation"` + Public bool `xml:"isPublic"` + Architecture string `xml:"architecture"` + Platform string `xml:"platform"` + ProductCodes []string `xml:"productCode>item>productCode"` + KernelId string `xml:"kernelId"` + RamdiskId string `xml:"ramdiskId"` + StateReason string `xml:"stateReason"` + OwnerId string `xml:"imageOwnerId"` + OwnerAlias string `xml:"imageOwnerAlias"` + RootDeviceType string `xml:"rootDeviceType"` + RootDeviceName string `xml:"rootDeviceName"` + VirtualizationType string `xml:"virtualizationType"` + Hypervisor string `xml:"hypervisor"` + BlockDevices []BlockDeviceMapping `xml:"blockDeviceMapping>item"` + Tags []Tag `xml:"tagSet>item"` +} + +// The ModifyImageAttribute request parameters. +type ModifyImageAttribute struct { + AddUsers []string + RemoveUsers []string + AddGroups []string + RemoveGroups []string + ProductCodes []string + Description string +} + +// The CopyImage request parameters. +// +// See http://goo.gl/hQwPCK for more details. +type CopyImage struct { + SourceRegion string + SourceImageId string + Name string + Description string + ClientToken string +} + +// Response to a CopyImage request. +// +// See http://goo.gl/hQwPCK for more details. +type CopyImageResp struct { + RequestId string `xml:"requestId"` + ImageId string `xml:"imageId"` +} + +// Creates an Amazon EBS-backed AMI from an Amazon EBS-backed instance +// that is either running or stopped. +// +// See http://goo.gl/cxU41 for more details. +func (ec2 *EC2) CreateImage(options *CreateImage) (resp *CreateImageResp, err error) { + params := makeParams("CreateImage") + params["InstanceId"] = options.InstanceId + params["Name"] = options.Name + if options.Description != "" { + params["Description"] = options.Description + } + if options.NoReboot { + params["NoReboot"] = "true" + } + addBlockDeviceParams("", params, options.BlockDevices) + + resp = &CreateImageResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + +// Images returns details about available images. +// The ids and filter parameters, if provided, will limit the images returned. +// For example, to get all the private images associated with this account set +// the boolean filter "is-public" to 0. +// For list of filters: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeImages.html +// +// Note: calling this function with nil ids and filter parameters will result in +// a very large number of images being returned. +// +// See http://goo.gl/SRBhW for more details. +func (ec2 *EC2) Images(ids []string, filter *Filter) (resp *ImagesResp, err error) { + params := makeParams("DescribeImages") + for i, id := range ids { + params["ImageId."+strconv.Itoa(i+1)] = id + } + filter.addParams(params) + + resp = &ImagesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// ImagesByOwners returns details about available images. +// The ids, owners, and filter parameters, if provided, will limit the images returned. +// For example, to get all the private images associated with this account set +// the boolean filter "is-public" to 0. +// For list of filters: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeImages.html +// +// Note: calling this function with nil ids and filter parameters will result in +// a very large number of images being returned. +// +// See http://goo.gl/SRBhW for more details. +func (ec2 *EC2) ImagesByOwners(ids []string, owners []string, filter *Filter) (resp *ImagesResp, err error) { + params := makeParams("DescribeImages") + for i, id := range ids { + params["ImageId."+strconv.Itoa(i+1)] = id + } + for i, owner := range owners { + params[fmt.Sprintf("Owner.%d", i+1)] = owner + } + + filter.addParams(params) + + resp = &ImagesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// ImageAttribute describes an attribute of an AMI. +// You can specify only one attribute at a time. +// Valid attributes are: +// description | kernel | ramdisk | launchPermission | productCodes | blockDeviceMapping +// +// See http://goo.gl/bHO3zT for more details. +func (ec2 *EC2) ImageAttribute(imageId, attribute string) (resp *ImageAttributeResp, err error) { + params := makeParams("DescribeImageAttribute") + params["ImageId"] = imageId + params["Attribute"] = attribute + + resp = &ImageAttributeResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// ModifyImageAttribute sets attributes for an image. +// +// See http://goo.gl/YUjO4G for more details. +func (ec2 *EC2) ModifyImageAttribute(imageId string, options *ModifyImageAttribute) (resp *SimpleResp, err error) { + params := makeParams("ModifyImageAttribute") + params["ImageId"] = imageId + if options.Description != "" { + params["Description.Value"] = options.Description + } + + if options.AddUsers != nil { + for i, user := range options.AddUsers { + p := fmt.Sprintf("LaunchPermission.Add.%d.UserId", i+1) + params[p] = user + } + } + + if options.RemoveUsers != nil { + for i, user := range options.RemoveUsers { + p := fmt.Sprintf("LaunchPermission.Remove.%d.UserId", i+1) + params[p] = user + } + } + + if options.AddGroups != nil { + for i, group := range options.AddGroups { + p := fmt.Sprintf("LaunchPermission.Add.%d.Group", i+1) + params[p] = group + } + } + + if options.RemoveGroups != nil { + for i, group := range options.RemoveGroups { + p := fmt.Sprintf("LaunchPermission.Remove.%d.Group", i+1) + params[p] = group + } + } + + if options.ProductCodes != nil { + addParamsList(params, "ProductCode", options.ProductCodes) + } + + resp = &SimpleResp{} + err = ec2.query(params, resp) + if err != nil { + resp = nil + } + + return +} + +// Registers a new AMI with EC2. +// +// See: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-RegisterImage.html +func (ec2 *EC2) RegisterImage(options *RegisterImage) (resp *RegisterImageResp, err error) { + params := makeParams("RegisterImage") + params["Name"] = options.Name + if options.ImageLocation != "" { + params["ImageLocation"] = options.ImageLocation + } + + if options.Description != "" { + params["Description"] = options.Description + } + + if options.Architecture != "" { + params["Architecture"] = options.Architecture + } + + if options.KernelId != "" { + params["KernelId"] = options.KernelId + } + + if options.RamdiskId != "" { + params["RamdiskId"] = options.RamdiskId + } + + if options.RootDeviceName != "" { + params["RootDeviceName"] = options.RootDeviceName + } + + if options.VirtType != "" { + params["VirtualizationType"] = options.VirtType + } + + addBlockDeviceParams("", params, options.BlockDevices) + + resp = &RegisterImageResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + +// Degisters an image. Note that this does not delete the backing stores of the AMI. +// +// See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DeregisterImage.html +func (ec2 *EC2) DeregisterImage(imageId string) (resp *DeregisterImageResp, err error) { + params := makeParams("DeregisterImage") + params["ImageId"] = imageId + + resp = &DeregisterImageResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + +// Copy and Image from one region to another. +// +// See http://goo.gl/hQwPCK for more details. +func (ec2 *EC2) CopyImage(options *CopyImage) (resp *CopyImageResp, err error) { + params := makeParams("CopyImage") + + if options.SourceRegion != "" { + params["SourceRegion"] = options.SourceRegion + } + + if options.SourceImageId != "" { + params["SourceImageId"] = options.SourceImageId + } + + if options.Name != "" { + params["Name"] = options.Name + } + + if options.Description != "" { + params["Description"] = options.Description + } + + if options.ClientToken != "" { + params["ClientToken"] = options.ClientToken + } + + resp = &CopyImageResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + +// Response to a CreateSnapshot request. +// +// See http://goo.gl/ttcda for more details. +type CreateSnapshotResp struct { + RequestId string `xml:"requestId"` + Snapshot +} + +// CreateSnapshot creates a volume snapshot and stores it in S3. +// +// See http://goo.gl/ttcda for more details. +func (ec2 *EC2) CreateSnapshot(volumeId, description string) (resp *CreateSnapshotResp, err error) { + params := makeParams("CreateSnapshot") + params["VolumeId"] = volumeId + params["Description"] = description + + resp = &CreateSnapshotResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// DeleteSnapshots deletes the volume snapshots with the given ids. +// +// Note: If you make periodic snapshots of a volume, the snapshots are +// incremental so that only the blocks on the device that have changed +// since your last snapshot are incrementally saved in the new snapshot. +// Even though snapshots are saved incrementally, the snapshot deletion +// process is designed so that you need to retain only the most recent +// snapshot in order to restore the volume. +// +// See http://goo.gl/vwU1y for more details. +func (ec2 *EC2) DeleteSnapshots(ids []string) (resp *SimpleResp, err error) { + params := makeParams("DeleteSnapshot") + for i, id := range ids { + params["SnapshotId."+strconv.Itoa(i+1)] = id + } + + resp = &SimpleResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + +// Response to a DescribeSnapshots request. +// +// See http://goo.gl/nClDT for more details. +type SnapshotsResp struct { + RequestId string `xml:"requestId"` + Snapshots []Snapshot `xml:"snapshotSet>item"` +} + +// Snapshot represents details about a volume snapshot. +// +// See http://goo.gl/nkovs for more details. +type Snapshot struct { + Id string `xml:"snapshotId"` + VolumeId string `xml:"volumeId"` + VolumeSize string `xml:"volumeSize"` + Status string `xml:"status"` + StartTime string `xml:"startTime"` + Description string `xml:"description"` + Progress string `xml:"progress"` + OwnerId string `xml:"ownerId"` + OwnerAlias string `xml:"ownerAlias"` + Tags []Tag `xml:"tagSet>item"` +} + +// Snapshots returns details about volume snapshots available to the user. +// The ids and filter parameters, if provided, limit the snapshots returned. +// +// See http://goo.gl/ogJL4 for more details. +func (ec2 *EC2) Snapshots(ids []string, filter *Filter) (resp *SnapshotsResp, err error) { + params := makeParams("DescribeSnapshots") + for i, id := range ids { + params["SnapshotId."+strconv.Itoa(i+1)] = id + } + filter.addParams(params) + + resp = &SnapshotsResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// ---------------------------------------------------------------------------- +// Volume management + +// The CreateVolume request parameters +// +// See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-CreateVolume.html +type CreateVolume struct { + AvailZone string + Size int64 + SnapshotId string + VolumeType string + IOPS int64 +} + +// Response to an AttachVolume request +type AttachVolumeResp struct { + RequestId string `xml:"requestId"` + VolumeId string `xml:"volumeId"` + InstanceId string `xml:"instanceId"` + Device string `xml:"device"` + Status string `xml:"status"` + AttachTime string `xml:"attachTime"` +} + +// Response to a CreateVolume request +type CreateVolumeResp struct { + RequestId string `xml:"requestId"` + VolumeId string `xml:"volumeId"` + Size int64 `xml:"size"` + SnapshotId string `xml:"snapshotId"` + AvailZone string `xml:"availabilityZone"` + Status string `xml:"status"` + CreateTime string `xml:"createTime"` + VolumeType string `xml:"volumeType"` + IOPS int64 `xml:"iops"` +} + +// Volume is a single volume. +type Volume struct { + VolumeId string `xml:"volumeId"` + Size string `xml:"size"` + SnapshotId string `xml:"snapshotId"` + AvailZone string `xml:"availabilityZone"` + Status string `xml:"status"` + Attachments []VolumeAttachment `xml:"attachmentSet>item"` + VolumeType string `xml:"volumeType"` + IOPS int64 `xml:"iops"` + Tags []Tag `xml:"tagSet>item"` +} + +type VolumeAttachment struct { + VolumeId string `xml:"volumeId"` + InstanceId string `xml:"instanceId"` + Device string `xml:"device"` + Status string `xml:"status"` +} + +// Response to a DescribeVolumes request +type VolumesResp struct { + RequestId string `xml:"requestId"` + Volumes []Volume `xml:"volumeSet>item"` +} + +// Attach a volume. +func (ec2 *EC2) AttachVolume(volumeId string, instanceId string, device string) (resp *AttachVolumeResp, err error) { + params := makeParams("AttachVolume") + params["VolumeId"] = volumeId + params["InstanceId"] = instanceId + params["Device"] = device + + resp = &AttachVolumeResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +// Create a new volume. +func (ec2 *EC2) CreateVolume(options *CreateVolume) (resp *CreateVolumeResp, err error) { + params := makeParams("CreateVolume") + params["AvailabilityZone"] = options.AvailZone + if options.Size > 0 { + params["Size"] = strconv.FormatInt(options.Size, 10) + } + + if options.SnapshotId != "" { + params["SnapshotId"] = options.SnapshotId + } + + if options.VolumeType != "" { + params["VolumeType"] = options.VolumeType + } + + if options.IOPS > 0 { + params["Iops"] = strconv.FormatInt(options.IOPS, 10) + } + + resp = &CreateVolumeResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// Delete an EBS volume. +func (ec2 *EC2) DeleteVolume(id string) (resp *SimpleResp, err error) { + params := makeParams("DeleteVolume") + params["VolumeId"] = id + + resp = &SimpleResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// Detaches an EBS volume. +func (ec2 *EC2) DetachVolume(id string) (resp *SimpleResp, err error) { + params := makeParams("DetachVolume") + params["VolumeId"] = id + + resp = &SimpleResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// Finds or lists all volumes. +func (ec2 *EC2) Volumes(volIds []string, filter *Filter) (resp *VolumesResp, err error) { + params := makeParams("DescribeVolumes") + addParamsList(params, "VolumeId", volIds) + filter.addParams(params) + resp = &VolumesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// ---------------------------------------------------------------------------- +// Security group management functions and types. + +// SimpleResp represents a response to an EC2 request which on success will +// return no other information besides a request id. +type SimpleResp struct { + XMLName xml.Name + RequestId string `xml:"requestId"` +} + +// CreateSecurityGroupResp represents a response to a CreateSecurityGroup request. +type CreateSecurityGroupResp struct { + SecurityGroup + RequestId string `xml:"requestId"` +} + +// CreateSecurityGroup run a CreateSecurityGroup request in EC2, with the provided +// name and description. +// +// See http://goo.gl/Eo7Yl for more details. +func (ec2 *EC2) CreateSecurityGroup(group SecurityGroup) (resp *CreateSecurityGroupResp, err error) { + params := makeParams("CreateSecurityGroup") + params["GroupName"] = group.Name + params["GroupDescription"] = group.Description + if group.VpcId != "" { + params["VpcId"] = group.VpcId + } + + resp = &CreateSecurityGroupResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + resp.Name = group.Name + return resp, nil +} + +// SecurityGroupsResp represents a response to a DescribeSecurityGroups +// request in EC2. +// +// See http://goo.gl/k12Uy for more details. +type SecurityGroupsResp struct { + RequestId string `xml:"requestId"` + Groups []SecurityGroupInfo `xml:"securityGroupInfo>item"` +} + +// SecurityGroup encapsulates details for a security group in EC2. +// +// See http://goo.gl/CIdyP for more details. +type SecurityGroupInfo struct { + SecurityGroup + OwnerId string `xml:"ownerId"` + Description string `xml:"groupDescription"` + IPPerms []IPPerm `xml:"ipPermissions>item"` + IPPermsEgress []IPPerm `xml:"ipPermissionsEgress>item"` +} + +// IPPerm represents an allowance within an EC2 security group. +// +// See http://goo.gl/4oTxv for more details. +type IPPerm struct { + Protocol string `xml:"ipProtocol"` + FromPort int `xml:"fromPort"` + ToPort int `xml:"toPort"` + SourceIPs []string `xml:"ipRanges>item>cidrIp"` + SourceGroups []UserSecurityGroup `xml:"groups>item"` +} + +// UserSecurityGroup holds a security group and the owner +// of that group. +type UserSecurityGroup struct { + Id string `xml:"groupId"` + Name string `xml:"groupName"` + OwnerId string `xml:"userId"` +} + +// SecurityGroup represents an EC2 security group. +// If SecurityGroup is used as a parameter, then one of Id or Name +// may be empty. If both are set, then Id is used. +type SecurityGroup struct { + Id string `xml:"groupId"` + Name string `xml:"groupName"` + Description string `xml:"groupDescription"` + VpcId string `xml:"vpcId"` +} + +// SecurityGroupNames is a convenience function that +// returns a slice of security groups with the given names. +func SecurityGroupNames(names ...string) []SecurityGroup { + g := make([]SecurityGroup, len(names)) + for i, name := range names { + g[i] = SecurityGroup{Name: name} + } + return g +} + +// SecurityGroupNames is a convenience function that +// returns a slice of security groups with the given ids. +func SecurityGroupIds(ids ...string) []SecurityGroup { + g := make([]SecurityGroup, len(ids)) + for i, id := range ids { + g[i] = SecurityGroup{Id: id} + } + return g +} + +// SecurityGroups returns details about security groups in EC2. Both parameters +// are optional, and if provided will limit the security groups returned to those +// matching the given groups or filtering rules. +// +// See http://goo.gl/k12Uy for more details. +func (ec2 *EC2) SecurityGroups(groups []SecurityGroup, filter *Filter) (resp *SecurityGroupsResp, err error) { + params := makeParams("DescribeSecurityGroups") + i, j := 1, 1 + for _, g := range groups { + if g.Id != "" { + params["GroupId."+strconv.Itoa(i)] = g.Id + i++ + } else { + params["GroupName."+strconv.Itoa(j)] = g.Name + j++ + } + } + filter.addParams(params) + + resp = &SecurityGroupsResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// DeleteSecurityGroup removes the given security group in EC2. +// +// See http://goo.gl/QJJDO for more details. +func (ec2 *EC2) DeleteSecurityGroup(group SecurityGroup) (resp *SimpleResp, err error) { + params := makeParams("DeleteSecurityGroup") + if group.Id != "" { + params["GroupId"] = group.Id + } else { + params["GroupName"] = group.Name + } + + resp = &SimpleResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// AuthorizeSecurityGroup creates an allowance for clients matching the provided +// rules to access instances within the given security group. +// +// See http://goo.gl/u2sDJ for more details. +func (ec2 *EC2) AuthorizeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) { + return ec2.authOrRevoke("AuthorizeSecurityGroupIngress", group, perms) +} + +// RevokeSecurityGroup revokes permissions from a group. +// +// See http://goo.gl/ZgdxA for more details. +func (ec2 *EC2) RevokeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) { + return ec2.authOrRevoke("RevokeSecurityGroupIngress", group, perms) +} + +// AuthorizeSecurityGroupEgress creates an allowance for instances within the +// given security group to access servers matching the provided rules. +// +// See http://goo.gl/R91LXY for more details. +func (ec2 *EC2) AuthorizeSecurityGroupEgress(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) { + return ec2.authOrRevoke("AuthorizeSecurityGroupEgress", group, perms) +} + +// RevokeSecurityGroupEgress revokes egress permissions from a group +// +// see http://goo.gl/Zv4wh8 +func (ec2 *EC2) RevokeSecurityGroupEgress(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) { + return ec2.authOrRevoke("RevokeSecurityGroupEgress", group, perms) +} + +func (ec2 *EC2) authOrRevoke(op string, group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) { + params := makeParams(op) + if group.Id != "" { + params["GroupId"] = group.Id + } else { + params["GroupName"] = group.Name + } + + for i, perm := range perms { + prefix := "IpPermissions." + strconv.Itoa(i+1) + params[prefix+".IpProtocol"] = perm.Protocol + params[prefix+".FromPort"] = strconv.Itoa(perm.FromPort) + params[prefix+".ToPort"] = strconv.Itoa(perm.ToPort) + for j, ip := range perm.SourceIPs { + params[prefix+".IpRanges."+strconv.Itoa(j+1)+".CidrIp"] = ip + } + for j, g := range perm.SourceGroups { + subprefix := prefix + ".Groups." + strconv.Itoa(j+1) + if g.OwnerId != "" { + params[subprefix+".UserId"] = g.OwnerId + } + if g.Id != "" { + params[subprefix+".GroupId"] = g.Id + } else { + params[subprefix+".GroupName"] = g.Name + } + } + } + + resp = &SimpleResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// ---------------------------------------------------------------------------- +// Elastic IP management functions and types. + +// Response to a DescribeAddresses request. +// +// See http://goo.gl/zW7J4p for more details. +type DescribeAddressesResp struct { + RequestId string `xml:"requestId"` + Addresses []Address `xml:"addressesSet>item"` +} + +// Address represents an Elastic IP Address +// See http://goo.gl/uxCjp7 for more details +type Address struct { + PublicIp string `xml:"publicIp"` + AllocationId string `xml:"allocationId"` + Domain string `xml:"domain"` + InstanceId string `xml:"instanceId"` + AssociationId string `xml:"associationId"` + NetworkInterfaceId string `xml:"networkInterfaceId"` + NetworkInterfaceOwnerId string `xml:"networkInterfaceOwnerId"` + PrivateIpAddress string `xml:"privateIpAddress"` +} + +// DescribeAddresses returns details about one or more +// Elastic IP Addresses. Returned addresses can be +// filtered by Public IP, Allocation ID or multiple filters +// +// See http://goo.gl/zW7J4p for more details. +func (ec2 *EC2) DescribeAddresses(publicIps []string, allocationIds []string, filter *Filter) (resp *DescribeAddressesResp, err error) { + params := makeParams("DescribeAddresses") + addParamsList(params, "PublicIp", publicIps) + addParamsList(params, "AllocationId", allocationIds) + filter.addParams(params) + resp = &DescribeAddressesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// AllocateAddressOptions are request parameters for allocating an Elastic IP Address +// +// See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-AllocateAddress.html +type AllocateAddressOptions struct { + Domain string +} + +// Response to an AllocateAddress request +// +// See http://goo.gl/aLPmbm for more details +type AllocateAddressResp struct { + RequestId string `xml:"requestId"` + PublicIp string `xml:"publicIp"` + Domain string `xml:"domain"` + AllocationId string `xml:"allocationId"` +} + +// Allocates a new Elastic IP address. +// The domain parameter is optional and is used for provisioning an ip address +// in EC2 or in VPC respectively +// +// See http://goo.gl/aLPmbm for more details +func (ec2 *EC2) AllocateAddress(options *AllocateAddressOptions) (resp *AllocateAddressResp, err error) { + params := makeParams("AllocateAddress") + params["Domain"] = options.Domain + resp = &AllocateAddressResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// Response to a ReleaseAddress request +// +// See http://goo.gl/Ciw2Z8 for more details +type ReleaseAddressResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` +} + +// Release existing elastic ip address from the account +// PublicIp = Required for EC2 +// AllocationId = Required for VPC +// +// See http://goo.gl/Ciw2Z8 for more details +func (ec2 *EC2) ReleaseAddress(publicIp, allocationId string) (resp *ReleaseAddressResp, err error) { + params := makeParams("ReleaseAddress") + + if publicIp != "" { + params["PublicIp"] = publicIp + + } + if allocationId != "" { + params["AllocationId"] = allocationId + } + + resp = &ReleaseAddressResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// Options set for AssociateAddress +// +// See http://goo.gl/hhj4z7 for more details +type AssociateAddressOptions struct { + PublicIp string + InstanceId string + AllocationId string + NetworkInterfaceId string + PrivateIpAddress string + AllowReassociation bool +} + +// Response to an AssociateAddress request +// +// See http://goo.gl/hhj4z7 for more details +type AssociateAddressResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` + AssociationId string `xml:"associationId"` +} + +// Associate an Elastic ip address to an instance id or a network interface +// +// See http://goo.gl/hhj4z7 for more details +func (ec2 *EC2) AssociateAddress(options *AssociateAddressOptions) (resp *AssociateAddressResp, err error) { + params := makeParams("AssociateAddress") + params["InstanceId"] = options.InstanceId + if options.PublicIp != "" { + params["PublicIp"] = options.PublicIp + } + if options.AllocationId != "" { + params["AllocationId"] = options.AllocationId + } + if options.NetworkInterfaceId != "" { + params["NetworkInterfaceId"] = options.NetworkInterfaceId + } + if options.PrivateIpAddress != "" { + params["PrivateIpAddress"] = options.PrivateIpAddress + } + if options.AllowReassociation { + params["AllowReassociation"] = "true" + } + + resp = &AssociateAddressResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} + +// Response to a Disassociate Address request +// +// See http://goo.gl/Dapkuzfor more details +type DisassociateAddressResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` +} + +// Disassociate an elastic ip address from an instance +// PublicIp - Required for EC2 +// AssociationId - Required for VPC +// See http://goo.gl/Dapkuz for more details +func (ec2 *EC2) DisassociateAddress(publicIp, associationId string) (resp *DisassociateAddressResp, err error) { + params := makeParams("DisassociateAddress") + if publicIp != "" { + params["PublicIp"] = publicIp + } + if associationId != "" { + params["AssociationId"] = associationId + } + + resp = &DisassociateAddressResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/github.com/goamz/goamz/ec2/ec2_test.go b/vendor/github.com/goamz/goamz/ec2/ec2_test.go new file mode 100644 index 000000000..3ca3a220d --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/ec2_test.go @@ -0,0 +1,1280 @@ +package ec2_test + +import ( + "testing" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/ec2" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + ec2 *ec2.EC2 +} + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.ec2 = ec2.NewWithClient( + auth, + aws.Region{EC2Endpoint: testServer.URL}, + testutil.DefaultClient, + ) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestRunInstancesErrorDump(c *C) { + testServer.Response(400, nil, ErrorDump) + + options := ec2.RunInstancesOptions{ + ImageId: "ami-a6f504cf", // Ubuntu Maverick, i386, instance store + InstanceType: "t1.micro", // Doesn't work with micro, results in 400. + } + + msg := `AMIs with an instance-store root device are not supported for the instance type 't1\.micro'\.` + + resp, err := s.ec2.RunInstances(&options) + + testServer.WaitRequest() + + c.Assert(resp, IsNil) + c.Assert(err, ErrorMatches, msg+` \(UnsupportedOperation\)`) + + ec2err, ok := err.(*ec2.Error) + c.Assert(ok, Equals, true) + c.Assert(ec2err.StatusCode, Equals, 400) + c.Assert(ec2err.Code, Equals, "UnsupportedOperation") + c.Assert(ec2err.Message, Matches, msg) + c.Assert(ec2err.RequestId, Equals, "0503f4e9-bbd6-483c-b54f-c4ae9f3b30f4") +} + +func (s *S) TestRequestSpotInstancesErrorDump(c *C) { + testServer.Response(400, nil, ErrorDump) + + options := ec2.RequestSpotInstances{ + SpotPrice: "0.01", + ImageId: "ami-a6f504cf", // Ubuntu Maverick, i386, instance store + InstanceType: "t1.micro", // Doesn't work with micro, results in 400. + } + + msg := `AMIs with an instance-store root device are not supported for the instance type 't1\.micro'\.` + + resp, err := s.ec2.RequestSpotInstances(&options) + + testServer.WaitRequest() + + c.Assert(resp, IsNil) + c.Assert(err, ErrorMatches, msg+` \(UnsupportedOperation\)`) + + ec2err, ok := err.(*ec2.Error) + c.Assert(ok, Equals, true) + c.Assert(ec2err.StatusCode, Equals, 400) + c.Assert(ec2err.Code, Equals, "UnsupportedOperation") + c.Assert(ec2err.Message, Matches, msg) + c.Assert(ec2err.RequestId, Equals, "0503f4e9-bbd6-483c-b54f-c4ae9f3b30f4") +} + +func (s *S) TestRunInstancesErrorWithoutXML(c *C) { + testServer.Responses(5, 500, nil, "") + options := ec2.RunInstancesOptions{ImageId: "image-id"} + + resp, err := s.ec2.RunInstances(&options) + + testServer.WaitRequest() + + c.Assert(resp, IsNil) + c.Assert(err, ErrorMatches, "500 Internal Server Error") + + ec2err, ok := err.(*ec2.Error) + c.Assert(ok, Equals, true) + c.Assert(ec2err.StatusCode, Equals, 500) + c.Assert(ec2err.Code, Equals, "") + c.Assert(ec2err.Message, Equals, "500 Internal Server Error") + c.Assert(ec2err.RequestId, Equals, "") +} + +func (s *S) TestRequestSpotInstancesErrorWithoutXML(c *C) { + testServer.Responses(5, 500, nil, "") + options := ec2.RequestSpotInstances{SpotPrice: "spot-price", ImageId: "image-id"} + + resp, err := s.ec2.RequestSpotInstances(&options) + + testServer.WaitRequest() + + c.Assert(resp, IsNil) + c.Assert(err, ErrorMatches, "500 Internal Server Error") + + ec2err, ok := err.(*ec2.Error) + c.Assert(ok, Equals, true) + c.Assert(ec2err.StatusCode, Equals, 500) + c.Assert(ec2err.Code, Equals, "") + c.Assert(ec2err.Message, Equals, "500 Internal Server Error") + c.Assert(ec2err.RequestId, Equals, "") +} + +func (s *S) TestRunInstancesExample(c *C) { + testServer.Response(200, nil, RunInstancesExample) + + options := ec2.RunInstancesOptions{ + KeyName: "my-keys", + ImageId: "image-id", + InstanceType: "inst-type", + SecurityGroups: []ec2.SecurityGroup{{Name: "g1"}, {Id: "g2"}, {Name: "g3"}, {Id: "g4"}}, + UserData: []byte("1234"), + KernelId: "kernel-id", + RamdiskId: "ramdisk-id", + AvailabilityZone: "zone", + PlacementGroupName: "group", + Monitoring: true, + SubnetId: "subnet-id", + DisableAPITermination: true, + ShutdownBehavior: "terminate", + PrivateIPAddress: "10.0.0.25", + BlockDevices: []ec2.BlockDeviceMapping{ + {DeviceName: "/dev/sdb", VirtualName: "ephemeral0"}, + {DeviceName: "/dev/sdc", SnapshotId: "snap-a08912c9", DeleteOnTermination: true}, + }, + } + resp, err := s.ec2.RunInstances(&options) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"RunInstances"}) + c.Assert(req.Form["ImageId"], DeepEquals, []string{"image-id"}) + c.Assert(req.Form["MinCount"], DeepEquals, []string{"1"}) + c.Assert(req.Form["MaxCount"], DeepEquals, []string{"1"}) + c.Assert(req.Form["KeyName"], DeepEquals, []string{"my-keys"}) + c.Assert(req.Form["InstanceType"], DeepEquals, []string{"inst-type"}) + c.Assert(req.Form["SecurityGroup.1"], DeepEquals, []string{"g1"}) + c.Assert(req.Form["SecurityGroup.2"], DeepEquals, []string{"g3"}) + c.Assert(req.Form["SecurityGroupId.1"], DeepEquals, []string{"g2"}) + c.Assert(req.Form["SecurityGroupId.2"], DeepEquals, []string{"g4"}) + c.Assert(req.Form["UserData"], DeepEquals, []string{"MTIzNA=="}) + c.Assert(req.Form["KernelId"], DeepEquals, []string{"kernel-id"}) + c.Assert(req.Form["RamdiskId"], DeepEquals, []string{"ramdisk-id"}) + c.Assert(req.Form["Placement.AvailabilityZone"], DeepEquals, []string{"zone"}) + c.Assert(req.Form["Placement.GroupName"], DeepEquals, []string{"group"}) + c.Assert(req.Form["Monitoring.Enabled"], DeepEquals, []string{"true"}) + c.Assert(req.Form["SubnetId"], DeepEquals, []string{"subnet-id"}) + c.Assert(req.Form["DisableApiTermination"], DeepEquals, []string{"true"}) + c.Assert(req.Form["InstanceInitiatedShutdownBehavior"], DeepEquals, []string{"terminate"}) + c.Assert(req.Form["PrivateIpAddress"], DeepEquals, []string{"10.0.0.25"}) + c.Assert(req.Form["BlockDeviceMapping.1.DeviceName"], DeepEquals, []string{"/dev/sdb"}) + c.Assert(req.Form["BlockDeviceMapping.1.VirtualName"], DeepEquals, []string{"ephemeral0"}) + c.Assert(req.Form["BlockDeviceMapping.2.Ebs.SnapshotId"], DeepEquals, []string{"snap-a08912c9"}) + c.Assert(req.Form["BlockDeviceMapping.2.Ebs.DeleteOnTermination"], DeepEquals, []string{"true"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.ReservationId, Equals, "r-47a5402e") + c.Assert(resp.OwnerId, Equals, "999988887777") + c.Assert(resp.SecurityGroups, DeepEquals, []ec2.SecurityGroup{{Name: "default", Id: "sg-67ad940e"}}) + c.Assert(resp.Instances, HasLen, 3) + + i0 := resp.Instances[0] + c.Assert(i0.InstanceId, Equals, "i-2ba64342") + c.Assert(i0.InstanceType, Equals, "m1.small") + c.Assert(i0.ImageId, Equals, "ami-60a54009") + c.Assert(i0.Monitoring, Equals, "enabled") + c.Assert(i0.KeyName, Equals, "example-key-name") + c.Assert(i0.AMILaunchIndex, Equals, 0) + c.Assert(i0.VirtualizationType, Equals, "paravirtual") + c.Assert(i0.Hypervisor, Equals, "xen") + + i1 := resp.Instances[1] + c.Assert(i1.InstanceId, Equals, "i-2bc64242") + c.Assert(i1.InstanceType, Equals, "m1.small") + c.Assert(i1.ImageId, Equals, "ami-60a54009") + c.Assert(i1.Monitoring, Equals, "enabled") + c.Assert(i1.KeyName, Equals, "example-key-name") + c.Assert(i1.AMILaunchIndex, Equals, 1) + c.Assert(i1.VirtualizationType, Equals, "paravirtual") + c.Assert(i1.Hypervisor, Equals, "xen") + + i2 := resp.Instances[2] + c.Assert(i2.InstanceId, Equals, "i-2be64332") + c.Assert(i2.InstanceType, Equals, "m1.small") + c.Assert(i2.ImageId, Equals, "ami-60a54009") + c.Assert(i2.Monitoring, Equals, "enabled") + c.Assert(i2.KeyName, Equals, "example-key-name") + c.Assert(i2.AMILaunchIndex, Equals, 2) + c.Assert(i2.VirtualizationType, Equals, "paravirtual") + c.Assert(i2.Hypervisor, Equals, "xen") +} + +func (s *S) TestRequestSpotInstancesExample(c *C) { + testServer.Response(200, nil, RequestSpotInstancesExample) + + options := ec2.RequestSpotInstances{ + SpotPrice: "0.5", + KeyName: "my-keys", + ImageId: "image-id", + InstanceType: "inst-type", + SecurityGroups: []ec2.SecurityGroup{{Name: "g1"}, {Id: "g2"}, {Name: "g3"}, {Id: "g4"}}, + UserData: []byte("1234"), + KernelId: "kernel-id", + RamdiskId: "ramdisk-id", + AvailZone: "zone", + PlacementGroupName: "group", + Monitoring: true, + SubnetId: "subnet-id", + PrivateIPAddress: "10.0.0.25", + BlockDevices: []ec2.BlockDeviceMapping{ + {DeviceName: "/dev/sdb", VirtualName: "ephemeral0"}, + {DeviceName: "/dev/sdc", SnapshotId: "snap-a08912c9", DeleteOnTermination: true}, + }, + } + resp, err := s.ec2.RequestSpotInstances(&options) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"RequestSpotInstances"}) + c.Assert(req.Form["SpotPrice"], DeepEquals, []string{"0.5"}) + c.Assert(req.Form["LaunchSpecification.ImageId"], DeepEquals, []string{"image-id"}) + c.Assert(req.Form["LaunchSpecification.KeyName"], DeepEquals, []string{"my-keys"}) + c.Assert(req.Form["LaunchSpecification.InstanceType"], DeepEquals, []string{"inst-type"}) + c.Assert(req.Form["LaunchSpecification.SecurityGroup.1"], DeepEquals, []string{"g1"}) + c.Assert(req.Form["LaunchSpecification.SecurityGroup.2"], DeepEquals, []string{"g3"}) + c.Assert(req.Form["LaunchSpecification.SecurityGroupId.1"], DeepEquals, []string{"g2"}) + c.Assert(req.Form["LaunchSpecification.SecurityGroupId.2"], DeepEquals, []string{"g4"}) + c.Assert(req.Form["LaunchSpecification.UserData"], DeepEquals, []string{"MTIzNA=="}) + c.Assert(req.Form["LaunchSpecification.KernelId"], DeepEquals, []string{"kernel-id"}) + c.Assert(req.Form["LaunchSpecification.RamdiskId"], DeepEquals, []string{"ramdisk-id"}) + c.Assert(req.Form["LaunchSpecification.Placement.AvailabilityZone"], DeepEquals, []string{"zone"}) + c.Assert(req.Form["LaunchSpecification.Placement.GroupName"], DeepEquals, []string{"group"}) + c.Assert(req.Form["LaunchSpecification.Monitoring.Enabled"], DeepEquals, []string{"true"}) + c.Assert(req.Form["LaunchSpecification.SubnetId"], DeepEquals, []string{"subnet-id"}) + c.Assert(req.Form["LaunchSpecification.PrivateIpAddress"], DeepEquals, []string{"10.0.0.25"}) + c.Assert(req.Form["LaunchSpecification.BlockDeviceMapping.1.DeviceName"], DeepEquals, []string{"/dev/sdb"}) + c.Assert(req.Form["LaunchSpecification.BlockDeviceMapping.1.VirtualName"], DeepEquals, []string{"ephemeral0"}) + c.Assert(req.Form["LaunchSpecification.BlockDeviceMapping.2.Ebs.SnapshotId"], DeepEquals, []string{"snap-a08912c9"}) + c.Assert(req.Form["LaunchSpecification.BlockDeviceMapping.2.Ebs.DeleteOnTermination"], DeepEquals, []string{"true"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.SpotRequestResults[0].SpotRequestId, Equals, "sir-1a2b3c4d") + c.Assert(resp.SpotRequestResults[0].SpotPrice, Equals, "0.5") + c.Assert(resp.SpotRequestResults[0].State, Equals, "open") + c.Assert(resp.SpotRequestResults[0].SpotLaunchSpec.ImageId, Equals, "ami-1a2b3c4d") +} + +func (s *S) TestCancelSpotRequestsExample(c *C) { + testServer.Response(200, nil, CancelSpotRequestsExample) + + resp, err := s.ec2.CancelSpotRequests([]string{"s-1", "s-2"}) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"CancelSpotInstanceRequests"}) + c.Assert(req.Form["SpotInstanceRequestId.1"], DeepEquals, []string{"s-1"}) + c.Assert(req.Form["SpotInstanceRequestId.2"], DeepEquals, []string{"s-2"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.CancelSpotRequestResults[0].SpotRequestId, Equals, "sir-1a2b3c4d") + c.Assert(resp.CancelSpotRequestResults[0].State, Equals, "cancelled") +} + +func (s *S) TestTerminateInstancesExample(c *C) { + testServer.Response(200, nil, TerminateInstancesExample) + + resp, err := s.ec2.TerminateInstances([]string{"i-1", "i-2"}) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"TerminateInstances"}) + c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-1"}) + c.Assert(req.Form["InstanceId.2"], DeepEquals, []string{"i-2"}) + c.Assert(req.Form["UserData"], IsNil) + c.Assert(req.Form["KernelId"], IsNil) + c.Assert(req.Form["RamdiskId"], IsNil) + c.Assert(req.Form["Placement.AvailabilityZone"], IsNil) + c.Assert(req.Form["Placement.GroupName"], IsNil) + c.Assert(req.Form["Monitoring.Enabled"], IsNil) + c.Assert(req.Form["SubnetId"], IsNil) + c.Assert(req.Form["DisableApiTermination"], IsNil) + c.Assert(req.Form["InstanceInitiatedShutdownBehavior"], IsNil) + c.Assert(req.Form["PrivateIpAddress"], IsNil) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.StateChanges, HasLen, 1) + c.Assert(resp.StateChanges[0].InstanceId, Equals, "i-3ea74257") + c.Assert(resp.StateChanges[0].CurrentState.Code, Equals, 32) + c.Assert(resp.StateChanges[0].CurrentState.Name, Equals, "shutting-down") + c.Assert(resp.StateChanges[0].PreviousState.Code, Equals, 16) + c.Assert(resp.StateChanges[0].PreviousState.Name, Equals, "running") +} + +func (s *S) TestDescribeSpotRequestsExample(c *C) { + testServer.Response(200, nil, DescribeSpotRequestsExample) + + filter := ec2.NewFilter() + filter.Add("key1", "value1") + filter.Add("key2", "value2", "value3") + + resp, err := s.ec2.DescribeSpotRequests([]string{"s-1", "s-2"}, filter) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSpotInstanceRequests"}) + c.Assert(req.Form["SpotInstanceRequestId.1"], DeepEquals, []string{"s-1"}) + c.Assert(req.Form["SpotInstanceRequestId.2"], DeepEquals, []string{"s-2"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "b1719f2a-5334-4479-b2f1-26926EXAMPLE") + c.Assert(resp.SpotRequestResults[0].SpotRequestId, Equals, "sir-1a2b3c4d") + c.Assert(resp.SpotRequestResults[0].State, Equals, "active") + c.Assert(resp.SpotRequestResults[0].SpotPrice, Equals, "0.5") + c.Assert(resp.SpotRequestResults[0].SpotLaunchSpec.ImageId, Equals, "ami-1a2b3c4d") +} + +func (s *S) TestDescribeInstancesExample1(c *C) { + testServer.Response(200, nil, DescribeInstancesExample1) + + filter := ec2.NewFilter() + filter.Add("key1", "value1") + filter.Add("key2", "value2", "value3") + + resp, err := s.ec2.DescribeInstances([]string{"i-1", "i-2"}, nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeInstances"}) + c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-1"}) + c.Assert(req.Form["InstanceId.2"], DeepEquals, []string{"i-2"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "98e3c9a4-848c-4d6d-8e8a-b1bdEXAMPLE") + c.Assert(resp.Reservations, HasLen, 2) + + r0 := resp.Reservations[0] + c.Assert(r0.ReservationId, Equals, "r-b27e30d9") + c.Assert(r0.OwnerId, Equals, "999988887777") + c.Assert(r0.RequesterId, Equals, "854251627541") + c.Assert(r0.SecurityGroups, DeepEquals, []ec2.SecurityGroup{{Name: "default", Id: "sg-67ad940e"}}) + c.Assert(r0.Instances, HasLen, 1) + + r0i := r0.Instances[0] + c.Assert(r0i.InstanceId, Equals, "i-c5cd56af") + c.Assert(r0i.PrivateDNSName, Equals, "domU-12-31-39-10-56-34.compute-1.internal") + c.Assert(r0i.DNSName, Equals, "ec2-174-129-165-232.compute-1.amazonaws.com") + c.Assert(r0i.AvailabilityZone, Equals, "us-east-1b") + c.Assert(r0i.IPAddress, Equals, "174.129.165.232") + c.Assert(r0i.PrivateIPAddress, Equals, "10.198.85.190") +} + +func (s *S) TestDescribeInstancesExample2(c *C) { + testServer.Response(200, nil, DescribeInstancesExample2) + + filter := ec2.NewFilter() + filter.Add("key1", "value1") + filter.Add("key2", "value2", "value3") + + resp, err := s.ec2.DescribeInstances([]string{"i-1", "i-2"}, filter) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeInstances"}) + c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-1"}) + c.Assert(req.Form["InstanceId.2"], DeepEquals, []string{"i-2"}) + c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"key1"}) + c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"value1"}) + c.Assert(req.Form["Filter.1.Value.2"], IsNil) + c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"key2"}) + c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"value2"}) + c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"value3"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Reservations, HasLen, 1) + + r0 := resp.Reservations[0] + r0i := r0.Instances[0] + c.Assert(r0i.State.Code, Equals, 16) + c.Assert(r0i.State.Name, Equals, "running") + + r0t0 := r0i.Tags[0] + r0t1 := r0i.Tags[1] + c.Assert(r0t0.Key, Equals, "webserver") + c.Assert(r0t0.Value, Equals, "") + c.Assert(r0t1.Key, Equals, "stack") + c.Assert(r0t1.Value, Equals, "Production") +} + +func (s *S) TestDescribeInstanceStatusExample(c *C) { + testServer.Response(200, nil, DescribeInstanceStatusExample) + + resp, err := s.ec2.DescribeInstanceStatus(&ec2.DescribeInstanceStatusOptions{}, nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeInstanceStatus"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.InstanceStatusSet, HasLen, 1) + c.Assert(resp.NextToken, Equals, "exampleToken") + + i0 := resp.InstanceStatusSet[0] + c.Assert(i0.InstanceId, Equals, "i-c7cd56ad") + c.Assert(i0.AvailabilityZone, Equals, "us-east-1b") + c.Assert(i0.Events, HasLen, 1) + + e0 := i0.Events[0] + c.Assert(e0.Code, Equals, "instance-reboot") + c.Assert(e0.Description, Equals, "example description") + c.Assert(e0.NotBefore, Equals, "2010-08-17T01:15:18.000Z") + c.Assert(e0.NotAfter, Equals, "2010-08-17T01:15:18.000Z") + + c.Assert(i0.InstanceState.Code, Equals, 16) + c.Assert(i0.InstanceState.Name, Equals, "running") + c.Assert(i0.SystemStatus.Status, Equals, "ok") + c.Assert(i0.SystemStatus.Details.Name, Equals, "reachability") + c.Assert(i0.SystemStatus.Details.Status, Equals, "passed") + c.Assert(i0.SystemStatus.Details.ImpairedSince, Equals, "2010-08-17T01:15:18.000Z") + c.Assert(i0.InstanceStatus.Status, Equals, "ok") + c.Assert(i0.InstanceStatus.Details.Name, Equals, "reachability") + c.Assert(i0.InstanceStatus.Details.Status, Equals, "passed") + c.Assert(i0.InstanceStatus.Details.ImpairedSince, Equals, "2010-08-17T01:15:18.000Z") +} + +func (s *S) TestDescribeAddressesPublicIPExample(c *C) { + testServer.Response(200, nil, DescribeAddressesExample) + + filter := ec2.NewFilter() + filter.Add("key1", "value1") + filter.Add("key2", "value2", "value3") + + resp, err := s.ec2.DescribeAddresses([]string{"192.0.2.1", "198.51.100.2", "203.0.113.41"}, []string{}, nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeAddresses"}) + c.Assert(req.Form["PublicIp.1"], DeepEquals, []string{"192.0.2.1"}) + c.Assert(req.Form["PublicIp.2"], DeepEquals, []string{"198.51.100.2"}) + c.Assert(req.Form["PublicIp.3"], DeepEquals, []string{"203.0.113.41"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Addresses, HasLen, 3) + + r0 := resp.Addresses[0] + c.Assert(r0.PublicIp, Equals, "192.0.2.1") + c.Assert(r0.Domain, Equals, "standard") + c.Assert(r0.InstanceId, Equals, "i-f15ebb98") + + r0i := resp.Addresses[1] + c.Assert(r0i.PublicIp, Equals, "198.51.100.2") + c.Assert(r0i.Domain, Equals, "standard") + c.Assert(r0i.InstanceId, Equals, "") + + r0ii := resp.Addresses[2] + c.Assert(r0ii.PublicIp, Equals, "203.0.113.41") + c.Assert(r0ii.Domain, Equals, "vpc") + c.Assert(r0ii.InstanceId, Equals, "i-64600030") + c.Assert(r0ii.AssociationId, Equals, "eipassoc-f0229899") + c.Assert(r0ii.AllocationId, Equals, "eipalloc-08229861") + c.Assert(r0ii.NetworkInterfaceOwnerId, Equals, "053230519467") + c.Assert(r0ii.NetworkInterfaceId, Equals, "eni-ef229886") + c.Assert(r0ii.PrivateIpAddress, Equals, "10.0.0.228") +} + +func (s *S) TestDescribeAddressesAllocationIDExample(c *C) { + testServer.Response(200, nil, DescribeAddressesAllocationIdExample) + + filter := ec2.NewFilter() + filter.Add("key1", "value1") + filter.Add("key2", "value2", "value3") + + resp, err := s.ec2.DescribeAddresses([]string{}, []string{"eipalloc-08229861", "eipalloc-08364752"}, nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeAddresses"}) + c.Assert(req.Form["AllocationId.1"], DeepEquals, []string{"eipalloc-08229861"}) + c.Assert(req.Form["AllocationId.2"], DeepEquals, []string{"eipalloc-08364752"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Addresses, HasLen, 2) + + r0 := resp.Addresses[0] + c.Assert(r0.PublicIp, Equals, "203.0.113.41") + c.Assert(r0.AllocationId, Equals, "eipalloc-08229861") + c.Assert(r0.Domain, Equals, "vpc") + c.Assert(r0.InstanceId, Equals, "i-64600030") + c.Assert(r0.AssociationId, Equals, "eipassoc-f0229899") + c.Assert(r0.NetworkInterfaceId, Equals, "eni-ef229886") + c.Assert(r0.NetworkInterfaceOwnerId, Equals, "053230519467") + c.Assert(r0.PrivateIpAddress, Equals, "10.0.0.228") + + r1 := resp.Addresses[1] + c.Assert(r1.PublicIp, Equals, "146.54.2.230") + c.Assert(r1.AllocationId, Equals, "eipalloc-08364752") + c.Assert(r1.Domain, Equals, "vpc") + c.Assert(r1.InstanceId, Equals, "i-64693456") + c.Assert(r1.AssociationId, Equals, "eipassoc-f0348693") + c.Assert(r1.NetworkInterfaceId, Equals, "eni-da764039") + c.Assert(r1.NetworkInterfaceOwnerId, Equals, "053230519467") + c.Assert(r1.PrivateIpAddress, Equals, "10.0.0.102") +} + +func (s *S) TestCreateImageExample(c *C) { + testServer.Response(200, nil, CreateImageExample) + + options := &ec2.CreateImage{ + InstanceId: "i-123456", + Name: "foo", + Description: "Test CreateImage", + NoReboot: true, + BlockDevices: []ec2.BlockDeviceMapping{ + {DeviceName: "/dev/sdb", VirtualName: "ephemeral0"}, + {DeviceName: "/dev/sdc", SnapshotId: "snap-a08912c9", DeleteOnTermination: true}, + }, + } + + resp, err := s.ec2.CreateImage(options) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"CreateImage"}) + c.Assert(req.Form["InstanceId"], DeepEquals, []string{options.InstanceId}) + c.Assert(req.Form["Name"], DeepEquals, []string{options.Name}) + c.Assert(req.Form["Description"], DeepEquals, []string{options.Description}) + c.Assert(req.Form["NoReboot"], DeepEquals, []string{"true"}) + c.Assert(req.Form["BlockDeviceMapping.1.DeviceName"], DeepEquals, []string{"/dev/sdb"}) + c.Assert(req.Form["BlockDeviceMapping.1.VirtualName"], DeepEquals, []string{"ephemeral0"}) + c.Assert(req.Form["BlockDeviceMapping.2.DeviceName"], DeepEquals, []string{"/dev/sdc"}) + c.Assert(req.Form["BlockDeviceMapping.2.Ebs.SnapshotId"], DeepEquals, []string{"snap-a08912c9"}) + c.Assert(req.Form["BlockDeviceMapping.2.Ebs.DeleteOnTermination"], DeepEquals, []string{"true"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.ImageId, Equals, "ami-4fa54026") +} + +func (s *S) TestDescribeImagesExample(c *C) { + testServer.Response(200, nil, DescribeImagesExample) + + filter := ec2.NewFilter() + filter.Add("key1", "value1") + filter.Add("key2", "value2", "value3") + + resp, err := s.ec2.Images([]string{"ami-1", "ami-2"}, filter) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeImages"}) + c.Assert(req.Form["ImageId.1"], DeepEquals, []string{"ami-1"}) + c.Assert(req.Form["ImageId.2"], DeepEquals, []string{"ami-2"}) + c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"key1"}) + c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"value1"}) + c.Assert(req.Form["Filter.1.Value.2"], IsNil) + c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"key2"}) + c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"value2"}) + c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"value3"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "4a4a27a2-2e7c-475d-b35b-ca822EXAMPLE") + c.Assert(resp.Images, HasLen, 1) + + i0 := resp.Images[0] + c.Assert(i0.Id, Equals, "ami-a2469acf") + c.Assert(i0.Type, Equals, "machine") + c.Assert(i0.Name, Equals, "example-marketplace-amzn-ami.1") + c.Assert(i0.Description, Equals, "Amazon Linux AMI i386 EBS") + c.Assert(i0.Location, Equals, "aws-marketplace/example-marketplace-amzn-ami.1") + c.Assert(i0.State, Equals, "available") + c.Assert(i0.Public, Equals, true) + c.Assert(i0.OwnerId, Equals, "123456789999") + c.Assert(i0.OwnerAlias, Equals, "aws-marketplace") + c.Assert(i0.Architecture, Equals, "i386") + c.Assert(i0.KernelId, Equals, "aki-805ea7e9") + c.Assert(i0.RootDeviceType, Equals, "ebs") + c.Assert(i0.RootDeviceName, Equals, "/dev/sda1") + c.Assert(i0.VirtualizationType, Equals, "paravirtual") + c.Assert(i0.Hypervisor, Equals, "xen") + + c.Assert(i0.Tags, HasLen, 1) + c.Assert(i0.Tags[0].Key, Equals, "Purpose") + c.Assert(i0.Tags[0].Value, Equals, "EXAMPLE") + + c.Assert(i0.BlockDevices, HasLen, 1) + c.Assert(i0.BlockDevices[0].DeviceName, Equals, "/dev/sda1") + c.Assert(i0.BlockDevices[0].SnapshotId, Equals, "snap-787e9403") + c.Assert(i0.BlockDevices[0].VolumeSize, Equals, int64(8)) + c.Assert(i0.BlockDevices[0].DeleteOnTermination, Equals, true) + + testServer.Response(200, nil, DescribeImagesExample) + resp2, err := s.ec2.ImagesByOwners([]string{"ami-1", "ami-2"}, []string{"123456789999", "id2"}, filter) + + req2 := testServer.WaitRequest() + c.Assert(req2.Form["Action"], DeepEquals, []string{"DescribeImages"}) + c.Assert(req2.Form["ImageId.1"], DeepEquals, []string{"ami-1"}) + c.Assert(req2.Form["ImageId.2"], DeepEquals, []string{"ami-2"}) + c.Assert(req2.Form["Owner.1"], DeepEquals, []string{"123456789999"}) + c.Assert(req2.Form["Owner.2"], DeepEquals, []string{"id2"}) + c.Assert(req2.Form["Filter.1.Name"], DeepEquals, []string{"key1"}) + c.Assert(req2.Form["Filter.1.Value.1"], DeepEquals, []string{"value1"}) + c.Assert(req2.Form["Filter.1.Value.2"], IsNil) + c.Assert(req2.Form["Filter.2.Name"], DeepEquals, []string{"key2"}) + c.Assert(req2.Form["Filter.2.Value.1"], DeepEquals, []string{"value2"}) + c.Assert(req2.Form["Filter.2.Value.2"], DeepEquals, []string{"value3"}) + + c.Assert(err, IsNil) + c.Assert(resp2.RequestId, Equals, "4a4a27a2-2e7c-475d-b35b-ca822EXAMPLE") + c.Assert(resp2.Images, HasLen, 1) + + i1 := resp2.Images[0] + c.Assert(i1.Id, Equals, "ami-a2469acf") + c.Assert(i1.Type, Equals, "machine") + c.Assert(i1.Name, Equals, "example-marketplace-amzn-ami.1") + c.Assert(i1.Description, Equals, "Amazon Linux AMI i386 EBS") + c.Assert(i1.Location, Equals, "aws-marketplace/example-marketplace-amzn-ami.1") + c.Assert(i1.State, Equals, "available") + c.Assert(i1.Public, Equals, true) + c.Assert(i1.OwnerId, Equals, "123456789999") + c.Assert(i1.OwnerAlias, Equals, "aws-marketplace") + c.Assert(i1.Architecture, Equals, "i386") + c.Assert(i1.KernelId, Equals, "aki-805ea7e9") + c.Assert(i1.RootDeviceType, Equals, "ebs") + c.Assert(i1.RootDeviceName, Equals, "/dev/sda1") + c.Assert(i1.VirtualizationType, Equals, "paravirtual") + c.Assert(i1.Hypervisor, Equals, "xen") + + c.Assert(i1.BlockDevices, HasLen, 1) + c.Assert(i1.BlockDevices[0].DeviceName, Equals, "/dev/sda1") + c.Assert(i1.BlockDevices[0].SnapshotId, Equals, "snap-787e9403") + c.Assert(i1.BlockDevices[0].VolumeSize, Equals, int64(8)) + c.Assert(i1.BlockDevices[0].DeleteOnTermination, Equals, true) +} + +func (s *S) TestImageAttributeExample(c *C) { + testServer.Response(200, nil, ImageAttributeExample) + + resp, err := s.ec2.ImageAttribute("ami-61a54008", "launchPermission") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeImageAttribute"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.ImageId, Equals, "ami-61a54008") + c.Assert(resp.Group, Equals, "all") + c.Assert(resp.UserIds[0], Equals, "495219933132") +} + +func (s *S) TestCreateSnapshotExample(c *C) { + testServer.Response(200, nil, CreateSnapshotExample) + + resp, err := s.ec2.CreateSnapshot("vol-4d826724", "Daily Backup") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"CreateSnapshot"}) + c.Assert(req.Form["VolumeId"], DeepEquals, []string{"vol-4d826724"}) + c.Assert(req.Form["Description"], DeepEquals, []string{"Daily Backup"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Snapshot.Id, Equals, "snap-78a54011") + c.Assert(resp.Snapshot.VolumeId, Equals, "vol-4d826724") + c.Assert(resp.Snapshot.Status, Equals, "pending") + c.Assert(resp.Snapshot.StartTime, Equals, "2008-05-07T12:51:50.000Z") + c.Assert(resp.Snapshot.Progress, Equals, "60%") + c.Assert(resp.Snapshot.OwnerId, Equals, "111122223333") + c.Assert(resp.Snapshot.VolumeSize, Equals, "10") + c.Assert(resp.Snapshot.Description, Equals, "Daily Backup") +} + +func (s *S) TestDeleteSnapshotsExample(c *C) { + testServer.Response(200, nil, DeleteSnapshotExample) + + resp, err := s.ec2.DeleteSnapshots([]string{"snap-78a54011"}) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteSnapshot"}) + c.Assert(req.Form["SnapshotId.1"], DeepEquals, []string{"snap-78a54011"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestDescribeSnapshotsExample(c *C) { + testServer.Response(200, nil, DescribeSnapshotsExample) + + filter := ec2.NewFilter() + filter.Add("key1", "value1") + filter.Add("key2", "value2", "value3") + + resp, err := s.ec2.Snapshots([]string{"snap-1", "snap-2"}, filter) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSnapshots"}) + c.Assert(req.Form["SnapshotId.1"], DeepEquals, []string{"snap-1"}) + c.Assert(req.Form["SnapshotId.2"], DeepEquals, []string{"snap-2"}) + c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"key1"}) + c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"value1"}) + c.Assert(req.Form["Filter.1.Value.2"], IsNil) + c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"key2"}) + c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"value2"}) + c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"value3"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Snapshots, HasLen, 1) + + s0 := resp.Snapshots[0] + c.Assert(s0.Id, Equals, "snap-1a2b3c4d") + c.Assert(s0.VolumeId, Equals, "vol-8875daef") + c.Assert(s0.VolumeSize, Equals, "15") + c.Assert(s0.Status, Equals, "pending") + c.Assert(s0.StartTime, Equals, "2010-07-29T04:12:01.000Z") + c.Assert(s0.Progress, Equals, "30%") + c.Assert(s0.OwnerId, Equals, "111122223333") + c.Assert(s0.Description, Equals, "Daily Backup") + + c.Assert(s0.Tags, HasLen, 1) + c.Assert(s0.Tags[0].Key, Equals, "Purpose") + c.Assert(s0.Tags[0].Value, Equals, "demo_db_14_backup") +} + +func (s *S) TestModifyImageAttributeExample(c *C) { + testServer.Response(200, nil, ModifyImageAttributeExample) + + options := ec2.ModifyImageAttribute{ + Description: "Test Description", + } + + resp, err := s.ec2.ModifyImageAttribute("ami-4fa54026", &options) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"ModifyImageAttribute"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestModifyImageAttributeExample_complex(c *C) { + testServer.Response(200, nil, ModifyImageAttributeExample) + + options := ec2.ModifyImageAttribute{ + AddUsers: []string{"u1", "u2"}, + RemoveUsers: []string{"u3"}, + AddGroups: []string{"g1", "g3"}, + RemoveGroups: []string{"g2"}, + Description: "Test Description", + } + + resp, err := s.ec2.ModifyImageAttribute("ami-4fa54026", &options) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"ModifyImageAttribute"}) + c.Assert(req.Form["LaunchPermission.Add.1.UserId"], DeepEquals, []string{"u1"}) + c.Assert(req.Form["LaunchPermission.Add.2.UserId"], DeepEquals, []string{"u2"}) + c.Assert(req.Form["LaunchPermission.Remove.1.UserId"], DeepEquals, []string{"u3"}) + c.Assert(req.Form["LaunchPermission.Add.1.Group"], DeepEquals, []string{"g1"}) + c.Assert(req.Form["LaunchPermission.Add.2.Group"], DeepEquals, []string{"g3"}) + c.Assert(req.Form["LaunchPermission.Remove.1.Group"], DeepEquals, []string{"g2"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestCopyImageExample(c *C) { + testServer.Response(200, nil, CopyImageExample) + + options := ec2.CopyImage{ + SourceRegion: "us-west-2", + SourceImageId: "ami-1a2b3c4d", + Description: "Test Description", + } + + resp, err := s.ec2.CopyImage(&options) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"CopyImage"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "60bc441d-fa2c-494d-b155-5d6a3EXAMPLE") +} + +func (s *S) TestCreateKeyPairExample(c *C) { + testServer.Response(200, nil, CreateKeyPairExample) + + resp, err := s.ec2.CreateKeyPair("foo") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"CreateKeyPair"}) + c.Assert(req.Form["KeyName"], DeepEquals, []string{"foo"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.KeyName, Equals, "foo") + c.Assert(resp.KeyFingerprint, Equals, "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00") +} + +func (s *S) TestDeleteKeyPairExample(c *C) { + testServer.Response(200, nil, DeleteKeyPairExample) + + resp, err := s.ec2.DeleteKeyPair("foo") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteKeyPair"}) + c.Assert(req.Form["KeyName"], DeepEquals, []string{"foo"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestCreateSecurityGroupExample(c *C) { + testServer.Response(200, nil, CreateSecurityGroupExample) + + resp, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: "websrv", Description: "Web Servers"}) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"CreateSecurityGroup"}) + c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) + c.Assert(req.Form["GroupDescription"], DeepEquals, []string{"Web Servers"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Name, Equals, "websrv") + c.Assert(resp.Id, Equals, "sg-67ad940e") +} + +func (s *S) TestDescribeSecurityGroupsExample(c *C) { + testServer.Response(200, nil, DescribeSecurityGroupsExample) + + resp, err := s.ec2.SecurityGroups([]ec2.SecurityGroup{{Name: "WebServers"}, {Name: "RangedPortsBySource"}}, nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSecurityGroups"}) + c.Assert(req.Form["GroupName.1"], DeepEquals, []string{"WebServers"}) + c.Assert(req.Form["GroupName.2"], DeepEquals, []string{"RangedPortsBySource"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Groups, HasLen, 2) + + g0 := resp.Groups[0] + c.Assert(g0.OwnerId, Equals, "999988887777") + c.Assert(g0.Name, Equals, "WebServers") + c.Assert(g0.Id, Equals, "sg-67ad940e") + c.Assert(g0.Description, Equals, "Web Servers") + c.Assert(g0.IPPerms, HasLen, 1) + c.Assert(g0.IPPermsEgress, HasLen, 1) + + g0ipp := g0.IPPerms[0] + c.Assert(g0ipp.Protocol, Equals, "tcp") + c.Assert(g0ipp.FromPort, Equals, 80) + c.Assert(g0ipp.ToPort, Equals, 80) + c.Assert(g0ipp.SourceIPs, DeepEquals, []string{"0.0.0.0/0"}) + + g0ippe := g0.IPPermsEgress[0] + c.Assert(g0ippe.Protocol, Equals, "tcp") + c.Assert(g0ippe.FromPort, Equals, 80) + c.Assert(g0ippe.ToPort, Equals, 80) + c.Assert(g0ippe.SourceIPs, DeepEquals, []string{"0.0.0.0/0"}) + + g1 := resp.Groups[1] + c.Assert(g1.OwnerId, Equals, "999988887777") + c.Assert(g1.Name, Equals, "RangedPortsBySource") + c.Assert(g1.Id, Equals, "sg-76abc467") + c.Assert(g1.Description, Equals, "Group A") + c.Assert(g1.IPPerms, HasLen, 1) + + g1ipp := g1.IPPerms[0] + c.Assert(g1ipp.Protocol, Equals, "tcp") + c.Assert(g1ipp.FromPort, Equals, 6000) + c.Assert(g1ipp.ToPort, Equals, 7000) + c.Assert(g1ipp.SourceIPs, IsNil) +} + +func (s *S) TestDescribeSecurityGroupsExampleWithFilter(c *C) { + testServer.Response(200, nil, DescribeSecurityGroupsExample) + + filter := ec2.NewFilter() + filter.Add("ip-permission.protocol", "tcp") + filter.Add("ip-permission.from-port", "22") + filter.Add("ip-permission.to-port", "22") + filter.Add("ip-permission.group-name", "app_server_group", "database_group") + + _, err := s.ec2.SecurityGroups(nil, filter) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSecurityGroups"}) + c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"ip-permission.from-port"}) + c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"22"}) + c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"ip-permission.group-name"}) + c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"app_server_group"}) + c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"database_group"}) + c.Assert(req.Form["Filter.3.Name"], DeepEquals, []string{"ip-permission.protocol"}) + c.Assert(req.Form["Filter.3.Value.1"], DeepEquals, []string{"tcp"}) + c.Assert(req.Form["Filter.4.Name"], DeepEquals, []string{"ip-permission.to-port"}) + c.Assert(req.Form["Filter.4.Value.1"], DeepEquals, []string{"22"}) + + c.Assert(err, IsNil) +} + +func (s *S) TestDescribeSecurityGroupsDumpWithGroup(c *C) { + testServer.Response(200, nil, DescribeSecurityGroupsDump) + + resp, err := s.ec2.SecurityGroups(nil, nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSecurityGroups"}) + c.Assert(err, IsNil) + c.Check(resp.Groups, HasLen, 1) + c.Check(resp.Groups[0].IPPerms, HasLen, 2) + + ipp0 := resp.Groups[0].IPPerms[0] + c.Assert(ipp0.SourceIPs, IsNil) + c.Check(ipp0.Protocol, Equals, "icmp") + c.Assert(ipp0.SourceGroups, HasLen, 1) + c.Check(ipp0.SourceGroups[0].OwnerId, Equals, "12345") + c.Check(ipp0.SourceGroups[0].Name, Equals, "default") + c.Check(ipp0.SourceGroups[0].Id, Equals, "sg-67ad940e") + + ipp1 := resp.Groups[0].IPPerms[1] + c.Check(ipp1.Protocol, Equals, "tcp") + c.Assert(ipp0.SourceIPs, IsNil) + c.Assert(ipp0.SourceGroups, HasLen, 1) + c.Check(ipp1.SourceGroups[0].Id, Equals, "sg-76abc467") + c.Check(ipp1.SourceGroups[0].OwnerId, Equals, "12345") + c.Check(ipp1.SourceGroups[0].Name, Equals, "other") +} + +func (s *S) TestDeleteSecurityGroupExample(c *C) { + testServer.Response(200, nil, DeleteSecurityGroupExample) + + resp, err := s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: "websrv"}) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteSecurityGroup"}) + c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) + c.Assert(req.Form["GroupId"], IsNil) + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestDeleteSecurityGroupExampleWithId(c *C) { + testServer.Response(200, nil, DeleteSecurityGroupExample) + + // ignore return and error - we're only want to check the parameter handling. + s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Id: "sg-67ad940e", Name: "ignored"}) + req := testServer.WaitRequest() + + c.Assert(req.Form["GroupName"], IsNil) + c.Assert(req.Form["GroupId"], DeepEquals, []string{"sg-67ad940e"}) +} + +func (s *S) TestAuthorizeSecurityGroupExample1(c *C) { + testServer.Response(200, nil, AuthorizeSecurityGroupIngressExample) + + perms := []ec2.IPPerm{{ + Protocol: "tcp", + FromPort: 80, + ToPort: 80, + SourceIPs: []string{"205.192.0.0/16", "205.159.0.0/16"}, + }} + resp, err := s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: "websrv"}, perms) + + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"AuthorizeSecurityGroupIngress"}) + c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) + c.Assert(req.Form["IpPermissions.1.IpProtocol"], DeepEquals, []string{"tcp"}) + c.Assert(req.Form["IpPermissions.1.FromPort"], DeepEquals, []string{"80"}) + c.Assert(req.Form["IpPermissions.1.ToPort"], DeepEquals, []string{"80"}) + c.Assert(req.Form["IpPermissions.1.IpRanges.1.CidrIp"], DeepEquals, []string{"205.192.0.0/16"}) + c.Assert(req.Form["IpPermissions.1.IpRanges.2.CidrIp"], DeepEquals, []string{"205.159.0.0/16"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestAuthorizeSecurityGroupExample1WithId(c *C) { + testServer.Response(200, nil, AuthorizeSecurityGroupIngressExample) + + perms := []ec2.IPPerm{{ + Protocol: "tcp", + FromPort: 80, + ToPort: 80, + SourceIPs: []string{"205.192.0.0/16", "205.159.0.0/16"}, + }} + // ignore return and error - we're only want to check the parameter handling. + s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Id: "sg-67ad940e", Name: "ignored"}, perms) + + req := testServer.WaitRequest() + + c.Assert(req.Form["GroupName"], IsNil) + c.Assert(req.Form["GroupId"], DeepEquals, []string{"sg-67ad940e"}) +} + +func (s *S) TestAuthorizeSecurityGroupExample2(c *C) { + testServer.Response(200, nil, AuthorizeSecurityGroupIngressExample) + + perms := []ec2.IPPerm{{ + Protocol: "tcp", + FromPort: 80, + ToPort: 81, + SourceGroups: []ec2.UserSecurityGroup{ + {OwnerId: "999988887777", Name: "OtherAccountGroup"}, + {Id: "sg-67ad940e"}, + }, + }} + resp, err := s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: "websrv"}, perms) + + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"AuthorizeSecurityGroupIngress"}) + c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) + c.Assert(req.Form["IpPermissions.1.IpProtocol"], DeepEquals, []string{"tcp"}) + c.Assert(req.Form["IpPermissions.1.FromPort"], DeepEquals, []string{"80"}) + c.Assert(req.Form["IpPermissions.1.ToPort"], DeepEquals, []string{"81"}) + c.Assert(req.Form["IpPermissions.1.Groups.1.UserId"], DeepEquals, []string{"999988887777"}) + c.Assert(req.Form["IpPermissions.1.Groups.1.GroupName"], DeepEquals, []string{"OtherAccountGroup"}) + c.Assert(req.Form["IpPermissions.1.Groups.2.UserId"], IsNil) + c.Assert(req.Form["IpPermissions.1.Groups.2.GroupName"], IsNil) + c.Assert(req.Form["IpPermissions.1.Groups.2.GroupId"], DeepEquals, []string{"sg-67ad940e"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestRevokeSecurityGroupExample(c *C) { + // RevokeSecurityGroup is implemented by the same code as AuthorizeSecurityGroup + // so there's no need to duplicate all the tests. + testServer.Response(200, nil, RevokeSecurityGroupIngressExample) + + resp, err := s.ec2.RevokeSecurityGroup(ec2.SecurityGroup{Name: "websrv"}, nil) + + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"RevokeSecurityGroupIngress"}) + c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestCreateTags(c *C) { + testServer.Response(200, nil, CreateTagsExample) + + resp, err := s.ec2.CreateTags([]string{"ami-1a2b3c4d", "i-7f4d3a2b"}, []ec2.Tag{{"webserver", ""}, {"stack", "Production"}}) + + req := testServer.WaitRequest() + c.Assert(req.Form["ResourceId.1"], DeepEquals, []string{"ami-1a2b3c4d"}) + c.Assert(req.Form["ResourceId.2"], DeepEquals, []string{"i-7f4d3a2b"}) + c.Assert(req.Form["Tag.1.Key"], DeepEquals, []string{"webserver"}) + c.Assert(req.Form["Tag.1.Value"], DeepEquals, []string{""}) + c.Assert(req.Form["Tag.2.Key"], DeepEquals, []string{"stack"}) + c.Assert(req.Form["Tag.2.Value"], DeepEquals, []string{"Production"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestStartInstances(c *C) { + testServer.Response(200, nil, StartInstancesExample) + + resp, err := s.ec2.StartInstances("i-10a64379") + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"StartInstances"}) + c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-10a64379"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + + s0 := resp.StateChanges[0] + c.Assert(s0.InstanceId, Equals, "i-10a64379") + c.Assert(s0.CurrentState.Code, Equals, 0) + c.Assert(s0.CurrentState.Name, Equals, "pending") + c.Assert(s0.PreviousState.Code, Equals, 80) + c.Assert(s0.PreviousState.Name, Equals, "stopped") +} + +func (s *S) TestStopInstances(c *C) { + testServer.Response(200, nil, StopInstancesExample) + + resp, err := s.ec2.StopInstances("i-10a64379") + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"StopInstances"}) + c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-10a64379"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + + s0 := resp.StateChanges[0] + c.Assert(s0.InstanceId, Equals, "i-10a64379") + c.Assert(s0.CurrentState.Code, Equals, 64) + c.Assert(s0.CurrentState.Name, Equals, "stopping") + c.Assert(s0.PreviousState.Code, Equals, 16) + c.Assert(s0.PreviousState.Name, Equals, "running") +} + +func (s *S) TestRebootInstances(c *C) { + testServer.Response(200, nil, RebootInstancesExample) + + resp, err := s.ec2.RebootInstances("i-10a64379") + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"RebootInstances"}) + c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-10a64379"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestSignatureWithEndpointPath(c *C) { + ec2.FakeTime(true) + defer ec2.FakeTime(false) + + testServer.Response(200, nil, RebootInstancesExample) + + // https://bugs.launchpad.net/goamz/+bug/1022749 + ec2 := ec2.NewWithClient(s.ec2.Auth, aws.Region{EC2Endpoint: testServer.URL + "/services/Cloud"}, testutil.DefaultClient) + + _, err := ec2.RebootInstances("i-10a64379") + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Signature"], DeepEquals, []string{"VVoC6Y6xfES+KvZo+789thP8+tye4F6fOKBiKmXk4S4="}) +} + +func (s *S) TestAllocateAddressExample(c *C) { + testServer.Response(200, nil, AllocateAddressExample) + + options := &ec2.AllocateAddressOptions{ + Domain: "vpc", + } + + resp, err := s.ec2.AllocateAddress(options) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"AllocateAddress"}) + c.Assert(req.Form["Domain"], DeepEquals, []string{"vpc"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.PublicIp, Equals, "198.51.100.1") + c.Assert(resp.Domain, Equals, "vpc") + c.Assert(resp.AllocationId, Equals, "eipalloc-5723d13e") +} + +func (s *S) TestReleaseAddressExample(c *C) { + testServer.Response(200, nil, ReleaseAddressExample) + + resp, err := s.ec2.ReleaseAddress("192.0.2.1", "eipalloc-5723d13e") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"ReleaseAddress"}) + c.Assert(req.Form["PublicIp"], DeepEquals, []string{"192.0.2.1"}) + c.Assert(req.Form["AllocationId"], DeepEquals, []string{"eipalloc-5723d13e"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Return, Equals, true) +} + +func (s *S) TestAssociateAddressExample(c *C) { + testServer.Response(200, nil, AssociateAddressExample) + + options := &ec2.AssociateAddressOptions{ + PublicIp: "192.0.2.1", + InstanceId: "i-4fd2431a", + AllocationId: "eipalloc-5723d13e", + AllowReassociation: true, + } + + resp, err := s.ec2.AssociateAddress(options) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"AssociateAddress"}) + c.Assert(req.Form["PublicIp"], DeepEquals, []string{"192.0.2.1"}) + c.Assert(req.Form["InstanceId"], DeepEquals, []string{"i-4fd2431a"}) + c.Assert(req.Form["AllocationId"], DeepEquals, []string{"eipalloc-5723d13e"}) + c.Assert(req.Form["AllowReassociation"], DeepEquals, []string{"true"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.AssociationId, Equals, "eipassoc-fc5ca095") + c.Assert(resp.Return, Equals, true) +} + +func (s *S) TestDisassociateAddressExample(c *C) { + testServer.Response(200, nil, DisassociateAddressExample) + + resp, err := s.ec2.DisassociateAddress("192.0.2.1", "eipassoc-aa7486c3") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DisassociateAddress"}) + c.Assert(req.Form["PublicIp"], DeepEquals, []string{"192.0.2.1"}) + c.Assert(req.Form["AssociationId"], DeepEquals, []string{"eipassoc-aa7486c3"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Return, Equals, true) +} + +func (s *S) TestModifyInstance(c *C) { + testServer.Response(200, nil, ModifyInstanceExample) + + options := ec2.ModifyInstance{ + InstanceType: "m1.small", + DisableAPITermination: true, + EbsOptimized: true, + SecurityGroups: []ec2.SecurityGroup{{Id: "g1"}, {Id: "g2"}}, + ShutdownBehavior: "terminate", + KernelId: "kernel-id", + RamdiskId: "ramdisk-id", + SourceDestCheck: true, + SriovNetSupport: true, + UserData: []byte("1234"), + BlockDevices: []ec2.BlockDeviceMapping{ + {DeviceName: "/dev/sda1", SnapshotId: "snap-a08912c9", DeleteOnTermination: true}, + }, + } + + resp, err := s.ec2.ModifyInstance("i-2ba64342", &options) + req := testServer.WaitRequest() + + c.Assert(req.Form["Action"], DeepEquals, []string{"ModifyInstanceAttribute"}) + c.Assert(req.Form["InstanceId"], DeepEquals, []string{"i-2ba64342"}) + c.Assert(req.Form["InstanceType.Value"], DeepEquals, []string{"m1.small"}) + c.Assert(req.Form["BlockDeviceMapping.1.DeviceName"], DeepEquals, []string{"/dev/sda1"}) + c.Assert(req.Form["BlockDeviceMapping.1.Ebs.SnapshotId"], DeepEquals, []string{"snap-a08912c9"}) + c.Assert(req.Form["BlockDeviceMapping.1.Ebs.DeleteOnTermination"], DeepEquals, []string{"true"}) + c.Assert(req.Form["DisableApiTermination.Value"], DeepEquals, []string{"true"}) + c.Assert(req.Form["EbsOptimized"], DeepEquals, []string{"true"}) + c.Assert(req.Form["GroupId.1"], DeepEquals, []string{"g1"}) + c.Assert(req.Form["GroupId.2"], DeepEquals, []string{"g2"}) + c.Assert(req.Form["InstanceInitiatedShutdownBehavior.Value"], DeepEquals, []string{"terminate"}) + c.Assert(req.Form["Kernel.Value"], DeepEquals, []string{"kernel-id"}) + c.Assert(req.Form["Ramdisk.Value"], DeepEquals, []string{"ramdisk-id"}) + c.Assert(req.Form["SourceDestCheck.Value"], DeepEquals, []string{"true"}) + c.Assert(req.Form["SriovNetSupport.Value"], DeepEquals, []string{"simple"}) + c.Assert(req.Form["UserData"], DeepEquals, []string{"MTIzNA=="}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") +} + +func (s *S) TestDescribeReservedInstancesExample(c *C) { + testServer.Response(200, nil, DescribeReservedInstancesExample) + + resp, err := s.ec2.DescribeReservedInstances([]string{"i-1", "i-2"}, nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeReservedInstances"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.ReservedInstances, HasLen, 1) + + r0 := resp.ReservedInstances[0] + c.Assert(r0.ReservedInstanceId, Equals, "e5a2ff3b-7d14-494f-90af-0b5d0EXAMPLE") + +} diff --git a/vendor/github.com/goamz/goamz/ec2/ec2i_test.go b/vendor/github.com/goamz/goamz/ec2/ec2i_test.go new file mode 100644 index 000000000..e8656f19f --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/ec2i_test.go @@ -0,0 +1,204 @@ +package ec2_test + +import ( + "crypto/rand" + "fmt" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/ec2" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +// AmazonServer represents an Amazon EC2 server. +type AmazonServer struct { + auth aws.Auth +} + +func (s *AmazonServer) SetUp(c *C) { + auth, err := aws.EnvAuth() + if err != nil { + c.Fatal(err.Error()) + } + s.auth = auth +} + +// Suite cost per run: 0.02 USD +var _ = Suite(&AmazonClientSuite{}) + +// AmazonClientSuite tests the client against a live EC2 server. +type AmazonClientSuite struct { + srv AmazonServer + ClientTests +} + +func (s *AmazonClientSuite) SetUpSuite(c *C) { + if !testutil.Amazon { + c.Skip("AmazonClientSuite tests not enabled") + } + s.srv.SetUp(c) + s.ec2 = ec2.NewWithClient(s.srv.auth, aws.USEast, testutil.DefaultClient) +} + +// ClientTests defines integration tests designed to test the client. +// It is not used as a test suite in itself, but embedded within +// another type. +type ClientTests struct { + ec2 *ec2.EC2 +} + +var imageId = "ami-ccf405a5" // Ubuntu Maverick, i386, EBS store + +// Cost: 0.00 USD +func (s *ClientTests) TestRunInstancesError(c *C) { + options := ec2.RunInstancesOptions{ + ImageId: "ami-a6f504cf", // Ubuntu Maverick, i386, instance store + InstanceType: "t1.micro", // Doesn't work with micro, results in 400. + } + + resp, err := s.ec2.RunInstances(&options) + + c.Assert(resp, IsNil) + c.Assert(err, ErrorMatches, "AMI.*root device.*not supported.*") + + ec2err, ok := err.(*ec2.Error) + c.Assert(ok, Equals, true) + c.Assert(ec2err.StatusCode, Equals, 400) + c.Assert(ec2err.Code, Equals, "UnsupportedOperation") + c.Assert(ec2err.Message, Matches, "AMI.*root device.*not supported.*") + c.Assert(ec2err.RequestId, Matches, ".+") +} + +// Cost: 0.02 USD +func (s *ClientTests) TestRunAndTerminate(c *C) { + options := ec2.RunInstancesOptions{ + ImageId: imageId, + InstanceType: "t1.micro", + } + resp1, err := s.ec2.RunInstances(&options) + c.Assert(err, IsNil) + c.Check(resp1.ReservationId, Matches, "r-[0-9a-f]*") + c.Check(resp1.OwnerId, Matches, "[0-9]+") + c.Check(resp1.Instances, HasLen, 1) + c.Check(resp1.Instances[0].InstanceType, Equals, "t1.micro") + + instId := resp1.Instances[0].InstanceId + + resp2, err := s.ec2.DescribeInstances([]string{instId}, nil) + c.Assert(err, IsNil) + if c.Check(resp2.Reservations, HasLen, 1) && c.Check(len(resp2.Reservations[0].Instances), Equals, 1) { + inst := resp2.Reservations[0].Instances[0] + c.Check(inst.InstanceId, Equals, instId) + } + + resp3, err := s.ec2.TerminateInstances([]string{instId}) + c.Assert(err, IsNil) + c.Check(resp3.StateChanges, HasLen, 1) + c.Check(resp3.StateChanges[0].InstanceId, Equals, instId) + c.Check(resp3.StateChanges[0].CurrentState.Name, Equals, "shutting-down") + c.Check(resp3.StateChanges[0].CurrentState.Code, Equals, 32) +} + +// Cost: 0.00 USD +func (s *ClientTests) TestSecurityGroups(c *C) { + name := "goamz-test" + descr := "goamz security group for tests" + + // Clean it up, if a previous test left it around and avoid leaving it around. + s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) + defer s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) + + resp1, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: name, Description: descr}) + c.Assert(err, IsNil) + c.Assert(resp1.RequestId, Matches, ".+") + c.Assert(resp1.Name, Equals, name) + c.Assert(resp1.Id, Matches, ".+") + + resp1, err = s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: name, Description: descr}) + ec2err, _ := err.(*ec2.Error) + c.Assert(resp1, IsNil) + c.Assert(ec2err, NotNil) + c.Assert(ec2err.Code, Equals, "InvalidGroup.Duplicate") + + perms := []ec2.IPPerm{{ + Protocol: "tcp", + FromPort: 0, + ToPort: 1024, + SourceIPs: []string{"127.0.0.1/24"}, + }} + + resp2, err := s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms) + c.Assert(err, IsNil) + c.Assert(resp2.RequestId, Matches, ".+") + + resp3, err := s.ec2.SecurityGroups(ec2.SecurityGroupNames(name), nil) + c.Assert(err, IsNil) + c.Assert(resp3.RequestId, Matches, ".+") + c.Assert(resp3.Groups, HasLen, 1) + + g0 := resp3.Groups[0] + c.Assert(g0.Name, Equals, name) + c.Assert(g0.Description, Equals, descr) + c.Assert(g0.IPPerms, HasLen, 1) + c.Assert(g0.IPPerms[0].Protocol, Equals, "tcp") + c.Assert(g0.IPPerms[0].FromPort, Equals, 0) + c.Assert(g0.IPPerms[0].ToPort, Equals, 1024) + c.Assert(g0.IPPerms[0].SourceIPs, DeepEquals, []string{"127.0.0.1/24"}) + + resp2, err = s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) + c.Assert(err, IsNil) + c.Assert(resp2.RequestId, Matches, ".+") +} + +var sessionId = func() string { + buf := make([]byte, 8) + // if we have no randomness, we'll just make do, so ignore the error. + rand.Read(buf) + return fmt.Sprintf("%x", buf) +}() + +// sessionName reutrns a name that is probably +// unique to this test session. +func sessionName(prefix string) string { + return prefix + "-" + sessionId +} + +var allRegions = []aws.Region{ + aws.USEast, + aws.USWest, + aws.EUWest, + aws.APSoutheast, + aws.APNortheast, +} + +// Communicate with all EC2 endpoints to see if they are alive. +func (s *ClientTests) TestRegions(c *C) { + name := sessionName("goamz-region-test") + perms := []ec2.IPPerm{{ + Protocol: "tcp", + FromPort: 80, + ToPort: 80, + SourceIPs: []string{"127.0.0.1/32"}, + }} + errs := make(chan error, len(allRegions)) + for _, region := range allRegions { + go func(r aws.Region) { + e := ec2.NewWithClient(s.ec2.Auth, r, testutil.DefaultClient) + _, err := e.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms) + errs <- err + }(region) + } + for _ = range allRegions { + err := <-errs + if err != nil { + ec2_err, ok := err.(*ec2.Error) + if ok { + c.Check(ec2_err.Code, Matches, "InvalidGroup.NotFound") + } else { + c.Errorf("Non-EC2 error: %s", err) + } + } else { + c.Errorf("Test should have errored but it seems to have succeeded") + } + } +} diff --git a/vendor/github.com/goamz/goamz/ec2/ec2t_test.go b/vendor/github.com/goamz/goamz/ec2/ec2t_test.go new file mode 100644 index 000000000..37c2e7eb7 --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/ec2t_test.go @@ -0,0 +1,581 @@ +package ec2_test + +import ( + "fmt" + "regexp" + "sort" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/ec2" + "github.com/goamz/goamz/ec2/ec2test" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +// LocalServer represents a local ec2test fake server. +type LocalServer struct { + auth aws.Auth + region aws.Region + srv *ec2test.Server +} + +func (s *LocalServer) SetUp(c *C) { + srv, err := ec2test.NewServer() + c.Assert(err, IsNil) + c.Assert(srv, NotNil) + + s.srv = srv + s.region = aws.Region{EC2Endpoint: srv.URL()} +} + +// LocalServerSuite defines tests that will run +// against the local ec2test server. It includes +// selected tests from ClientTests; +// when the ec2test functionality is sufficient, it should +// include all of them, and ClientTests can be simply embedded. +type LocalServerSuite struct { + srv LocalServer + ServerTests + clientTests ClientTests +} + +var _ = Suite(&LocalServerSuite{}) + +func (s *LocalServerSuite) SetUpSuite(c *C) { + s.srv.SetUp(c) + s.ServerTests.ec2 = ec2.NewWithClient(s.srv.auth, s.srv.region, testutil.DefaultClient) + s.clientTests.ec2 = ec2.NewWithClient(s.srv.auth, s.srv.region, testutil.DefaultClient) +} + +func (s *LocalServerSuite) TestRunAndTerminate(c *C) { + s.clientTests.TestRunAndTerminate(c) +} + +func (s *LocalServerSuite) TestSecurityGroups(c *C) { + s.clientTests.TestSecurityGroups(c) +} + +// TestUserData is not defined on ServerTests because it +// requires the ec2test server to function. +func (s *LocalServerSuite) TestUserData(c *C) { + data := make([]byte, 256) + for i := range data { + data[i] = byte(i) + } + inst, err := s.ec2.RunInstances(&ec2.RunInstancesOptions{ + ImageId: imageId, + InstanceType: "t1.micro", + UserData: data, + }) + c.Assert(err, IsNil) + c.Assert(inst, NotNil) + c.Assert(inst.Instances[0].DNSName, Equals, inst.Instances[0].InstanceId+".example.com") + + id := inst.Instances[0].InstanceId + + defer s.ec2.TerminateInstances([]string{id}) + + tinst := s.srv.srv.Instance(id) + c.Assert(tinst, NotNil) + c.Assert(tinst.UserData, DeepEquals, data) +} + +// AmazonServerSuite runs the ec2test server tests against a live EC2 server. +// It will only be activated if the -all flag is specified. +type AmazonServerSuite struct { + srv AmazonServer + ServerTests +} + +var _ = Suite(&AmazonServerSuite{}) + +func (s *AmazonServerSuite) SetUpSuite(c *C) { + if !testutil.Amazon { + c.Skip("AmazonServerSuite tests not enabled") + } + s.srv.SetUp(c) + s.ServerTests.ec2 = ec2.NewWithClient(s.srv.auth, aws.USEast, testutil.DefaultClient) +} + +// ServerTests defines a set of tests designed to test +// the ec2test local fake ec2 server. +// It is not used as a test suite in itself, but embedded within +// another type. +type ServerTests struct { + ec2 *ec2.EC2 +} + +func terminateInstances(c *C, e *ec2.EC2, insts []*ec2.Instance) { + var ids []string + for _, inst := range insts { + if inst != nil { + ids = append(ids, inst.InstanceId) + } + } + _, err := e.TerminateInstances(ids) + c.Check(err, IsNil, Commentf("%d INSTANCES LEFT RUNNING!!!", len(ids))) +} + +func (s *ServerTests) makeTestGroup(c *C, name, descr string) ec2.SecurityGroup { + // Clean it up if a previous test left it around. + _, err := s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) + if err != nil && err.(*ec2.Error).Code != "InvalidGroup.NotFound" { + c.Fatalf("delete security group: %v", err) + } + + resp, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: name, Description: descr}) + c.Assert(err, IsNil) + c.Assert(resp.Name, Equals, name) + return resp.SecurityGroup +} + +func (s *ServerTests) TestIPPerms(c *C) { + g0 := s.makeTestGroup(c, "goamz-test0", "ec2test group 0") + defer s.ec2.DeleteSecurityGroup(g0) + + g1 := s.makeTestGroup(c, "goamz-test1", "ec2test group 1") + defer s.ec2.DeleteSecurityGroup(g1) + + resp, err := s.ec2.SecurityGroups([]ec2.SecurityGroup{g0, g1}, nil) + c.Assert(err, IsNil) + c.Assert(resp.Groups, HasLen, 2) + c.Assert(resp.Groups[0].IPPerms, HasLen, 0) + c.Assert(resp.Groups[1].IPPerms, HasLen, 0) + + ownerId := resp.Groups[0].OwnerId + + // test some invalid parameters + // TODO more + _, err = s.ec2.AuthorizeSecurityGroup(g0, []ec2.IPPerm{{ + Protocol: "tcp", + FromPort: 0, + ToPort: 1024, + SourceIPs: []string{"z127.0.0.1/24"}, + }}) + c.Assert(err, NotNil) + c.Check(err.(*ec2.Error).Code, Equals, "InvalidPermission.Malformed") + + // Check that AuthorizeSecurityGroup adds the correct authorizations. + _, err = s.ec2.AuthorizeSecurityGroup(g0, []ec2.IPPerm{{ + Protocol: "tcp", + FromPort: 2000, + ToPort: 2001, + SourceIPs: []string{"127.0.0.0/24"}, + SourceGroups: []ec2.UserSecurityGroup{{ + Name: g1.Name, + }, { + Id: g0.Id, + }}, + }, { + Protocol: "tcp", + FromPort: 2000, + ToPort: 2001, + SourceIPs: []string{"200.1.1.34/32"}, + }}) + c.Assert(err, IsNil) + + resp, err = s.ec2.SecurityGroups([]ec2.SecurityGroup{g0}, nil) + c.Assert(err, IsNil) + c.Assert(resp.Groups, HasLen, 1) + c.Assert(resp.Groups[0].IPPerms, HasLen, 1) + + perm := resp.Groups[0].IPPerms[0] + srcg := perm.SourceGroups + c.Assert(srcg, HasLen, 2) + + // Normalize so we don't care about returned order. + if srcg[0].Name == g1.Name { + srcg[0], srcg[1] = srcg[1], srcg[0] + } + c.Check(srcg[0].Name, Equals, g0.Name) + c.Check(srcg[0].Id, Equals, g0.Id) + c.Check(srcg[0].OwnerId, Equals, ownerId) + c.Check(srcg[1].Name, Equals, g1.Name) + c.Check(srcg[1].Id, Equals, g1.Id) + c.Check(srcg[1].OwnerId, Equals, ownerId) + + sort.Strings(perm.SourceIPs) + c.Check(perm.SourceIPs, DeepEquals, []string{"127.0.0.0/24", "200.1.1.34/32"}) + + // Check that we can't delete g1 (because g0 is using it) + _, err = s.ec2.DeleteSecurityGroup(g1) + c.Assert(err, NotNil) + c.Check(err.(*ec2.Error).Code, Equals, "InvalidGroup.InUse") + + _, err = s.ec2.RevokeSecurityGroup(g0, []ec2.IPPerm{{ + Protocol: "tcp", + FromPort: 2000, + ToPort: 2001, + SourceGroups: []ec2.UserSecurityGroup{{Id: g1.Id}}, + }, { + Protocol: "tcp", + FromPort: 2000, + ToPort: 2001, + SourceIPs: []string{"200.1.1.34/32"}, + }}) + c.Assert(err, IsNil) + + resp, err = s.ec2.SecurityGroups([]ec2.SecurityGroup{g0}, nil) + c.Assert(err, IsNil) + c.Assert(resp.Groups, HasLen, 1) + c.Assert(resp.Groups[0].IPPerms, HasLen, 1) + + perm = resp.Groups[0].IPPerms[0] + srcg = perm.SourceGroups + c.Assert(srcg, HasLen, 1) + c.Check(srcg[0].Name, Equals, g0.Name) + c.Check(srcg[0].Id, Equals, g0.Id) + c.Check(srcg[0].OwnerId, Equals, ownerId) + + c.Check(perm.SourceIPs, DeepEquals, []string{"127.0.0.0/24"}) + + // We should be able to delete g1 now because we've removed its only use. + _, err = s.ec2.DeleteSecurityGroup(g1) + c.Assert(err, IsNil) + + _, err = s.ec2.DeleteSecurityGroup(g0) + c.Assert(err, IsNil) + + f := ec2.NewFilter() + f.Add("group-id", g0.Id, g1.Id) + resp, err = s.ec2.SecurityGroups(nil, f) + c.Assert(err, IsNil) + c.Assert(resp.Groups, HasLen, 0) +} + +func (s *ServerTests) TestDuplicateIPPerm(c *C) { + name := "goamz-test" + descr := "goamz security group for tests" + + // Clean it up, if a previous test left it around and avoid leaving it around. + s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) + defer s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) + + resp1, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: name, Description: descr}) + c.Assert(err, IsNil) + c.Assert(resp1.Name, Equals, name) + + perms := []ec2.IPPerm{{ + Protocol: "tcp", + FromPort: 200, + ToPort: 1024, + SourceIPs: []string{"127.0.0.1/24"}, + }, { + Protocol: "tcp", + FromPort: 0, + ToPort: 100, + SourceIPs: []string{"127.0.0.1/24"}, + }} + + _, err = s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms[0:1]) + c.Assert(err, IsNil) + + _, err = s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms[0:2]) + c.Assert(err, ErrorMatches, `.*\(InvalidPermission.Duplicate\)`) +} + +type filterSpec struct { + name string + values []string +} + +func (s *ServerTests) TestInstanceFiltering(c *C) { + groupResp, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: sessionName("testgroup1"), Description: "testgroup one description"}) + c.Assert(err, IsNil) + group1 := groupResp.SecurityGroup + defer s.ec2.DeleteSecurityGroup(group1) + + groupResp, err = s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: sessionName("testgroup2"), Description: "testgroup two description"}) + c.Assert(err, IsNil) + group2 := groupResp.SecurityGroup + defer s.ec2.DeleteSecurityGroup(group2) + + insts := make([]*ec2.Instance, 3) + inst, err := s.ec2.RunInstances(&ec2.RunInstancesOptions{ + MinCount: 2, + ImageId: imageId, + InstanceType: "t1.micro", + SecurityGroups: []ec2.SecurityGroup{group1}, + }) + c.Assert(err, IsNil) + insts[0] = &inst.Instances[0] + insts[1] = &inst.Instances[1] + defer terminateInstances(c, s.ec2, insts) + + imageId2 := "ami-e358958a" // Natty server, i386, EBS store + inst, err = s.ec2.RunInstances(&ec2.RunInstancesOptions{ + ImageId: imageId2, + InstanceType: "t1.micro", + SecurityGroups: []ec2.SecurityGroup{group2}, + }) + c.Assert(err, IsNil) + insts[2] = &inst.Instances[0] + + ids := func(indices ...int) (instIds []string) { + for _, index := range indices { + instIds = append(instIds, insts[index].InstanceId) + } + return + } + + tests := []struct { + about string + instanceIds []string // instanceIds argument to Instances method. + filters []filterSpec // filters argument to Instances method. + resultIds []string // set of instance ids of expected results. + allowExtra bool // resultIds may be incomplete. + err string // expected error. + }{ + { + about: "check that Instances returns all instances", + resultIds: ids(0, 1, 2), + allowExtra: true, + }, { + about: "check that specifying two instance ids returns them", + instanceIds: ids(0, 2), + resultIds: ids(0, 2), + }, { + about: "check that specifying a non-existent instance id gives an error", + instanceIds: append(ids(0), "i-deadbeef"), + err: `.*\(InvalidInstanceID\.NotFound\)`, + }, { + about: "check that a filter allowed both instances returns both of them", + filters: []filterSpec{ + {"instance-id", ids(0, 2)}, + }, + resultIds: ids(0, 2), + }, { + about: "check that a filter allowing only one instance returns it", + filters: []filterSpec{ + {"instance-id", ids(1)}, + }, + resultIds: ids(1), + }, { + about: "check that a filter allowing no instances returns none", + filters: []filterSpec{ + {"instance-id", []string{"i-deadbeef12345"}}, + }, + }, { + about: "check that filtering on group id works", + filters: []filterSpec{ + {"group-id", []string{group1.Id}}, + }, + resultIds: ids(0, 1), + }, { + about: "check that filtering on group name works", + filters: []filterSpec{ + {"group-name", []string{group1.Name}}, + }, + resultIds: ids(0, 1), + }, { + about: "check that filtering on image id works", + filters: []filterSpec{ + {"image-id", []string{imageId}}, + }, + resultIds: ids(0, 1), + allowExtra: true, + }, { + about: "combination filters 1", + filters: []filterSpec{ + {"image-id", []string{imageId, imageId2}}, + {"group-name", []string{group1.Name}}, + }, + resultIds: ids(0, 1), + }, { + about: "combination filters 2", + filters: []filterSpec{ + {"image-id", []string{imageId2}}, + {"group-name", []string{group1.Name}}, + }, + }, + } + for i, t := range tests { + c.Logf("%d. %s", i, t.about) + var f *ec2.Filter + if t.filters != nil { + f = ec2.NewFilter() + for _, spec := range t.filters { + f.Add(spec.name, spec.values...) + } + } + resp, err := s.ec2.DescribeInstances(t.instanceIds, f) + if t.err != "" { + c.Check(err, ErrorMatches, t.err) + continue + } + c.Assert(err, IsNil) + insts := make(map[string]*ec2.Instance) + for _, r := range resp.Reservations { + for j := range r.Instances { + inst := &r.Instances[j] + c.Check(insts[inst.InstanceId], IsNil, Commentf("duplicate instance id: %q", inst.InstanceId)) + insts[inst.InstanceId] = inst + } + } + if !t.allowExtra { + c.Check(insts, HasLen, len(t.resultIds), Commentf("expected %d instances got %#v", len(t.resultIds), insts)) + } + for j, id := range t.resultIds { + c.Check(insts[id], NotNil, Commentf("instance id %d (%q) not found; got %#v", j, id, insts)) + } + } +} + +func idsOnly(gs []ec2.SecurityGroup) []ec2.SecurityGroup { + for i := range gs { + gs[i].Name = "" + } + return gs +} + +func namesOnly(gs []ec2.SecurityGroup) []ec2.SecurityGroup { + for i := range gs { + gs[i].Id = "" + } + return gs +} + +func (s *ServerTests) TestGroupFiltering(c *C) { + g := make([]ec2.SecurityGroup, 4) + for i := range g { + resp, err := s.ec2.CreateSecurityGroup(ec2.SecurityGroup{Name: sessionName(fmt.Sprintf("testgroup%d", i)), Description: fmt.Sprintf("testdescription%d", i)}) + c.Assert(err, IsNil) + g[i] = resp.SecurityGroup + c.Logf("group %d: %v", i, g[i]) + defer s.ec2.DeleteSecurityGroup(g[i]) + } + + perms := [][]ec2.IPPerm{ + {{ + Protocol: "tcp", + FromPort: 100, + ToPort: 200, + SourceIPs: []string{"1.2.3.4/32"}, + }}, + {{ + Protocol: "tcp", + FromPort: 200, + ToPort: 300, + SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}}, + }}, + {{ + Protocol: "udp", + FromPort: 200, + ToPort: 400, + SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}}, + }}, + } + for i, ps := range perms { + _, err := s.ec2.AuthorizeSecurityGroup(g[i], ps) + c.Assert(err, IsNil) + } + + groups := func(indices ...int) (gs []ec2.SecurityGroup) { + for _, index := range indices { + gs = append(gs, g[index]) + } + return + } + + type groupTest struct { + about string + groups []ec2.SecurityGroup // groupIds argument to SecurityGroups method. + filters []filterSpec // filters argument to SecurityGroups method. + results []ec2.SecurityGroup // set of expected result groups. + allowExtra bool // specified results may be incomplete. + err string // expected error. + } + filterCheck := func(name, val string, gs []ec2.SecurityGroup) groupTest { + return groupTest{ + about: "filter check " + name, + filters: []filterSpec{{name, []string{val}}}, + results: gs, + allowExtra: true, + } + } + tests := []groupTest{ + { + about: "check that SecurityGroups returns all groups", + results: groups(0, 1, 2, 3), + allowExtra: true, + }, { + about: "check that specifying two group ids returns them", + groups: idsOnly(groups(0, 2)), + results: groups(0, 2), + }, { + about: "check that specifying names only works", + groups: namesOnly(groups(0, 2)), + results: groups(0, 2), + }, { + about: "check that specifying a non-existent group id gives an error", + groups: append(groups(0), ec2.SecurityGroup{Id: "sg-eeeeeeeee"}), + err: `.*\(InvalidGroup\.NotFound\)`, + }, { + about: "check that a filter allowed two groups returns both of them", + filters: []filterSpec{ + {"group-id", []string{g[0].Id, g[2].Id}}, + }, + results: groups(0, 2), + }, + { + about: "check that the previous filter works when specifying a list of ids", + groups: groups(1, 2), + filters: []filterSpec{ + {"group-id", []string{g[0].Id, g[2].Id}}, + }, + results: groups(2), + }, { + about: "check that a filter allowing no groups returns none", + filters: []filterSpec{ + {"group-id", []string{"sg-eeeeeeeee"}}, + }, + }, + filterCheck("description", "testdescription1", groups(1)), + filterCheck("group-name", g[2].Name, groups(2)), + filterCheck("ip-permission.cidr", "1.2.3.4/32", groups(0)), + filterCheck("ip-permission.group-name", g[1].Name, groups(1, 2)), + filterCheck("ip-permission.protocol", "udp", groups(2)), + filterCheck("ip-permission.from-port", "200", groups(1, 2)), + filterCheck("ip-permission.to-port", "200", groups(0)), + // TODO owner-id + } + for i, t := range tests { + c.Logf("%d. %s", i, t.about) + var f *ec2.Filter + if t.filters != nil { + f = ec2.NewFilter() + for _, spec := range t.filters { + f.Add(spec.name, spec.values...) + } + } + resp, err := s.ec2.SecurityGroups(t.groups, f) + if t.err != "" { + c.Check(err, ErrorMatches, t.err) + continue + } + c.Assert(err, IsNil) + groups := make(map[string]*ec2.SecurityGroup) + for j := range resp.Groups { + group := &resp.Groups[j].SecurityGroup + c.Check(groups[group.Id], IsNil, Commentf("duplicate group id: %q", group.Id)) + + groups[group.Id] = group + } + // If extra groups may be returned, eliminate all groups that + // we did not create in this session apart from the default group. + if t.allowExtra { + namePat := regexp.MustCompile(sessionName("testgroup[0-9]")) + for id, g := range groups { + if !namePat.MatchString(g.Name) { + delete(groups, id) + } + } + } + c.Check(groups, HasLen, len(t.results)) + for j, g := range t.results { + rg := groups[g.Id] + c.Assert(rg, NotNil, Commentf("group %d (%v) not found; got %#v", j, g, groups)) + c.Check(rg.Name, Equals, g.Name, Commentf("group %d (%v)", j, g)) + } + } +} diff --git a/vendor/github.com/goamz/goamz/ec2/ec2test/filter.go b/vendor/github.com/goamz/goamz/ec2/ec2test/filter.go new file mode 100644 index 000000000..1a0c04619 --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/ec2test/filter.go @@ -0,0 +1,84 @@ +package ec2test + +import ( + "fmt" + "net/url" + "strings" +) + +// filter holds an ec2 filter. A filter maps an attribute to a set of +// possible values for that attribute. For an item to pass through the +// filter, every attribute of the item mentioned in the filter must match +// at least one of its given values. +type filter map[string][]string + +// newFilter creates a new filter from the Filter fields in the url form. +// +// The filtering is specified through a map of name=>values, where the +// name is a well-defined key identifying the data to be matched, +// and the list of values holds the possible values the filtered +// item can take for the key to be included in the +// result set. For example: +// +// Filter.1.Name=instance-type +// Filter.1.Value.1=m1.small +// Filter.1.Value.2=m1.large +// +func newFilter(form url.Values) filter { + // TODO return an error if the fields are not well formed? + names := make(map[int]string) + values := make(map[int][]string) + maxId := 0 + for name, fvalues := range form { + var rest string + var id int + if x, _ := fmt.Sscanf(name, "Filter.%d.%s", &id, &rest); x != 2 { + continue + } + if id > maxId { + maxId = id + } + if rest == "Name" { + names[id] = fvalues[0] + continue + } + if !strings.HasPrefix(rest, "Value.") { + continue + } + values[id] = append(values[id], fvalues[0]) + } + + f := make(filter) + for id, name := range names { + f[name] = values[id] + } + return f +} + +func notDigit(r rune) bool { + return r < '0' || r > '9' +} + +// filterable represents an object that can be passed through a filter. +type filterable interface { + // matchAttr returns true if given attribute of the + // object matches value. It returns an error if the + // attribute is not recognised or the value is malformed. + matchAttr(attr, value string) (bool, error) +} + +// ok returns true if x passes through the filter. +func (f filter) ok(x filterable) (bool, error) { +next: + for a, vs := range f { + for _, v := range vs { + if ok, err := x.matchAttr(a, v); ok { + continue next + } else if err != nil { + return false, fmt.Errorf("bad attribute or value %q=%q for type %T: %v", a, v, x, err) + } + } + return false, nil + } + return true, nil +} diff --git a/vendor/github.com/goamz/goamz/ec2/ec2test/server.go b/vendor/github.com/goamz/goamz/ec2/ec2test/server.go new file mode 100644 index 000000000..e25d4ea20 --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/ec2test/server.go @@ -0,0 +1,993 @@ +// The ec2test package implements a fake EC2 provider with +// the capability of inducing errors on any given operation, +// and retrospectively determining what operations have been +// carried out. +package ec2test + +import ( + "encoding/base64" + "encoding/xml" + "fmt" + "github.com/goamz/goamz/ec2" + "io" + "net" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" + "sync" +) + +var b64 = base64.StdEncoding + +// Action represents a request that changes the ec2 state. +type Action struct { + RequestId string + + // Request holds the requested action as a url.Values instance + Request url.Values + + // If the action succeeded, Response holds the value that + // was marshalled to build the XML response for the request. + Response interface{} + + // If the action failed, Err holds an error giving details of the failure. + Err *ec2.Error +} + +// TODO possible other things: +// - some virtual time stamp interface, so a client +// can ask for all actions after a certain virtual time. + +// Server implements an EC2 simulator for use in testing. +type Server struct { + url string + listener net.Listener + mu sync.Mutex + reqs []*Action + + instances map[string]*Instance // id -> instance + reservations map[string]*reservation // id -> reservation + groups map[string]*securityGroup // id -> group + maxId counter + reqId counter + reservationId counter + groupId counter + initialInstanceState ec2.InstanceState +} + +// reservation holds a simulated ec2 reservation. +type reservation struct { + id string + instances map[string]*Instance + groups []*securityGroup +} + +// instance holds a simulated ec2 instance +type Instance struct { + // UserData holds the data that was passed to the RunInstances request + // when the instance was started. + UserData []byte + id string + imageId string + reservation *reservation + instType string + state ec2.InstanceState +} + +// permKey represents permission for a given security +// group or IP address (but not both) to access a given range of +// ports. Equality of permKeys is used in the implementation of +// permission sets, relying on the uniqueness of securityGroup +// instances. +type permKey struct { + protocol string + fromPort int + toPort int + group *securityGroup + ipAddr string +} + +// securityGroup holds a simulated ec2 security group. +// Instances of securityGroup should only be created through +// Server.createSecurityGroup to ensure that groups can be +// compared by pointer value. +type securityGroup struct { + id string + name string + description string + + perms map[permKey]bool +} + +func (g *securityGroup) ec2SecurityGroup() ec2.SecurityGroup { + return ec2.SecurityGroup{ + Name: g.name, + Id: g.id, + } +} + +func (g *securityGroup) matchAttr(attr, value string) (ok bool, err error) { + switch attr { + case "description": + return g.description == value, nil + case "group-id": + return g.id == value, nil + case "group-name": + return g.name == value, nil + case "ip-permission.cidr": + return g.hasPerm(func(k permKey) bool { return k.ipAddr == value }), nil + case "ip-permission.group-name": + return g.hasPerm(func(k permKey) bool { + return k.group != nil && k.group.name == value + }), nil + case "ip-permission.from-port": + port, err := strconv.Atoi(value) + if err != nil { + return false, err + } + return g.hasPerm(func(k permKey) bool { return k.fromPort == port }), nil + case "ip-permission.to-port": + port, err := strconv.Atoi(value) + if err != nil { + return false, err + } + return g.hasPerm(func(k permKey) bool { return k.toPort == port }), nil + case "ip-permission.protocol": + return g.hasPerm(func(k permKey) bool { return k.protocol == value }), nil + case "owner-id": + return value == ownerId, nil + } + return false, fmt.Errorf("unknown attribute %q", attr) +} + +func (g *securityGroup) hasPerm(test func(k permKey) bool) bool { + for k := range g.perms { + if test(k) { + return true + } + } + return false +} + +// ec2Perms returns the list of EC2 permissions granted +// to g. It groups permissions by port range and protocol. +func (g *securityGroup) ec2Perms() (perms []ec2.IPPerm) { + // The grouping is held in result. We use permKey for convenience, + // (ensuring that the group and ipAddr of each key is zero). For + // each protocol/port range combination, we build up the permission + // set in the associated value. + result := make(map[permKey]*ec2.IPPerm) + for k := range g.perms { + groupKey := k + groupKey.group = nil + groupKey.ipAddr = "" + + ec2p := result[groupKey] + if ec2p == nil { + ec2p = &ec2.IPPerm{ + Protocol: k.protocol, + FromPort: k.fromPort, + ToPort: k.toPort, + } + result[groupKey] = ec2p + } + if k.group != nil { + ec2p.SourceGroups = append(ec2p.SourceGroups, + ec2.UserSecurityGroup{ + Id: k.group.id, + Name: k.group.name, + OwnerId: ownerId, + }) + } else { + ec2p.SourceIPs = append(ec2p.SourceIPs, k.ipAddr) + } + } + for _, ec2p := range result { + perms = append(perms, *ec2p) + } + return +} + +var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) interface{}{ + "RunInstances": (*Server).runInstances, + "TerminateInstances": (*Server).terminateInstances, + "DescribeInstances": (*Server).describeInstances, + "CreateSecurityGroup": (*Server).createSecurityGroup, + "DescribeSecurityGroups": (*Server).describeSecurityGroups, + "DeleteSecurityGroup": (*Server).deleteSecurityGroup, + "AuthorizeSecurityGroupIngress": (*Server).authorizeSecurityGroupIngress, + "RevokeSecurityGroupIngress": (*Server).revokeSecurityGroupIngress, +} + +const ownerId = "9876" + +// newAction allocates a new action and adds it to the +// recorded list of server actions. +func (srv *Server) newAction() *Action { + srv.mu.Lock() + defer srv.mu.Unlock() + + a := new(Action) + srv.reqs = append(srv.reqs, a) + return a +} + +// NewServer returns a new server. +func NewServer() (*Server, error) { + srv := &Server{ + instances: make(map[string]*Instance), + groups: make(map[string]*securityGroup), + reservations: make(map[string]*reservation), + initialInstanceState: Pending, + } + + // Add default security group. + g := &securityGroup{ + name: "default", + description: "default group", + id: fmt.Sprintf("sg-%d", srv.groupId.next()), + } + g.perms = map[permKey]bool{ + permKey{ + protocol: "icmp", + fromPort: -1, + toPort: -1, + group: g, + }: true, + permKey{ + protocol: "tcp", + fromPort: 0, + toPort: 65535, + group: g, + }: true, + permKey{ + protocol: "udp", + fromPort: 0, + toPort: 65535, + group: g, + }: true, + } + srv.groups[g.id] = g + + l, err := net.Listen("tcp", "localhost:0") + if err != nil { + return nil, fmt.Errorf("cannot listen on localhost: %v", err) + } + srv.listener = l + + srv.url = "http://" + l.Addr().String() + + // we use HandlerFunc rather than *Server directly so that we + // can avoid exporting HandlerFunc from *Server. + go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + srv.serveHTTP(w, req) + })) + return srv, nil +} + +// Quit closes down the server. +func (srv *Server) Quit() { + srv.listener.Close() +} + +// SetInitialInstanceState sets the state that any new instances will be started in. +func (srv *Server) SetInitialInstanceState(state ec2.InstanceState) { + srv.mu.Lock() + srv.initialInstanceState = state + srv.mu.Unlock() +} + +// URL returns the URL of the server. +func (srv *Server) URL() string { + return srv.url +} + +// serveHTTP serves the EC2 protocol. +func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) { + req.ParseForm() + + a := srv.newAction() + a.RequestId = fmt.Sprintf("req%d", srv.reqId.next()) + a.Request = req.Form + + // Methods on Server that deal with parsing user data + // may fail. To save on error handling code, we allow these + // methods to call fatalf, which will panic with an *ec2.Error + // which will be caught here and returned + // to the client as a properly formed EC2 error. + defer func() { + switch err := recover().(type) { + case *ec2.Error: + a.Err = err + err.RequestId = a.RequestId + writeError(w, err) + case nil: + default: + panic(err) + } + }() + + f := actions[req.Form.Get("Action")] + if f == nil { + fatalf(400, "InvalidParameterValue", "Unrecognized Action") + } + + response := f(srv, w, req, a.RequestId) + a.Response = response + + w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`) + xmlMarshal(w, response) +} + +// Instance returns the instance for the given instance id. +// It returns nil if there is no such instance. +func (srv *Server) Instance(id string) *Instance { + srv.mu.Lock() + defer srv.mu.Unlock() + return srv.instances[id] +} + +// writeError writes an appropriate error response. +// TODO how should we deal with errors when the +// error itself is potentially generated by backend-agnostic +// code? +func writeError(w http.ResponseWriter, err *ec2.Error) { + // Error encapsulates an error returned by EC2. + // TODO merge with ec2.Error when xml supports ignoring a field. + type ec2error struct { + Code string // EC2 error code ("UnsupportedOperation", ...) + Message string // The human-oriented error message + RequestId string + } + + type Response struct { + RequestId string + Errors []ec2error `xml:"Errors>Error"` + } + + w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`) + w.WriteHeader(err.StatusCode) + xmlMarshal(w, Response{ + RequestId: err.RequestId, + Errors: []ec2error{{ + Code: err.Code, + Message: err.Message, + }}, + }) +} + +// xmlMarshal is the same as xml.Marshal except that +// it panics on error. The marshalling should not fail, +// but we want to know if it does. +func xmlMarshal(w io.Writer, x interface{}) { + if err := xml.NewEncoder(w).Encode(x); err != nil { + panic(fmt.Errorf("error marshalling %#v: %v", x, err)) + } +} + +// formToGroups parses a set of SecurityGroup form values +// as found in a RunInstances request, and returns the resulting +// slice of security groups. +// It calls fatalf if a group is not found. +func (srv *Server) formToGroups(form url.Values) []*securityGroup { + var groups []*securityGroup + for name, values := range form { + switch { + case strings.HasPrefix(name, "SecurityGroupId."): + if g := srv.groups[values[0]]; g != nil { + groups = append(groups, g) + } else { + fatalf(400, "InvalidGroup.NotFound", "unknown group id %q", values[0]) + } + case strings.HasPrefix(name, "SecurityGroup."): + var found *securityGroup + for _, g := range srv.groups { + if g.name == values[0] { + found = g + } + } + if found == nil { + fatalf(400, "InvalidGroup.NotFound", "unknown group name %q", values[0]) + } + groups = append(groups, found) + } + } + return groups +} + +// runInstances implements the EC2 RunInstances entry point. +func (srv *Server) runInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} { + min := atoi(req.Form.Get("MinCount")) + max := atoi(req.Form.Get("MaxCount")) + if min < 0 || max < 1 { + fatalf(400, "InvalidParameterValue", "bad values for MinCount or MaxCount") + } + if min > max { + fatalf(400, "InvalidParameterCombination", "MinCount is greater than MaxCount") + } + var userData []byte + if data := req.Form.Get("UserData"); data != "" { + var err error + userData, err = b64.DecodeString(data) + if err != nil { + fatalf(400, "InvalidParameterValue", "bad UserData value: %v", err) + } + } + + // TODO attributes still to consider: + // ImageId: accept anything, we can verify later + // KeyName ? + // InstanceType ? + // KernelId ? + // RamdiskId ? + // AvailabilityZone ? + // GroupName tag + // Monitoring ignore? + // SubnetId ? + // DisableAPITermination bool + // ShutdownBehavior string + // PrivateIPAddress string + + srv.mu.Lock() + defer srv.mu.Unlock() + + // make sure that form fields are correct before creating the reservation. + instType := req.Form.Get("InstanceType") + imageId := req.Form.Get("ImageId") + + r := srv.newReservation(srv.formToGroups(req.Form)) + + var resp ec2.RunInstancesResp + resp.RequestId = reqId + resp.ReservationId = r.id + resp.OwnerId = ownerId + + for i := 0; i < max; i++ { + inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState) + inst.UserData = userData + resp.Instances = append(resp.Instances, inst.ec2instance()) + } + return &resp +} + +func (srv *Server) group(group ec2.SecurityGroup) *securityGroup { + if group.Id != "" { + return srv.groups[group.Id] + } + for _, g := range srv.groups { + if g.name == group.Name { + return g + } + } + return nil +} + +// NewInstances creates n new instances in srv with the given instance type, +// image ID, initial state and security groups. If any group does not already +// exist, it will be created. NewInstances returns the ids of the new instances. +func (srv *Server) NewInstances(n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string { + srv.mu.Lock() + defer srv.mu.Unlock() + + rgroups := make([]*securityGroup, len(groups)) + for i, group := range groups { + g := srv.group(group) + if g == nil { + fatalf(400, "InvalidGroup.NotFound", "no such group %v", g) + } + rgroups[i] = g + } + r := srv.newReservation(rgroups) + + ids := make([]string, n) + for i := 0; i < n; i++ { + inst := srv.newInstance(r, instType, imageId, state) + ids[i] = inst.id + } + return ids +} + +func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState) *Instance { + inst := &Instance{ + id: fmt.Sprintf("i-%d", srv.maxId.next()), + instType: instType, + imageId: imageId, + state: state, + reservation: r, + } + srv.instances[inst.id] = inst + r.instances[inst.id] = inst + return inst +} + +func (srv *Server) newReservation(groups []*securityGroup) *reservation { + r := &reservation{ + id: fmt.Sprintf("r-%d", srv.reservationId.next()), + instances: make(map[string]*Instance), + groups: groups, + } + + srv.reservations[r.id] = r + return r +} + +func (srv *Server) terminateInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} { + srv.mu.Lock() + defer srv.mu.Unlock() + var resp ec2.TerminateInstancesResp + resp.RequestId = reqId + var insts []*Instance + for attr, vals := range req.Form { + if strings.HasPrefix(attr, "InstanceId.") { + id := vals[0] + inst := srv.instances[id] + if inst == nil { + fatalf(400, "InvalidInstanceID.NotFound", "no such instance id %q", id) + } + insts = append(insts, inst) + } + } + for _, inst := range insts { + resp.StateChanges = append(resp.StateChanges, inst.terminate()) + } + return &resp +} + +func (inst *Instance) terminate() (d ec2.InstanceStateChange) { + d.PreviousState = inst.state + inst.state = ShuttingDown + d.CurrentState = inst.state + d.InstanceId = inst.id + return d +} + +func (inst *Instance) ec2instance() ec2.Instance { + return ec2.Instance{ + InstanceId: inst.id, + InstanceType: inst.instType, + ImageId: inst.imageId, + DNSName: fmt.Sprintf("%s.example.com", inst.id), + // TODO the rest + } +} + +func (inst *Instance) matchAttr(attr, value string) (ok bool, err error) { + switch attr { + case "architecture": + return value == "i386", nil + case "instance-id": + return inst.id == value, nil + case "group-id": + for _, g := range inst.reservation.groups { + if g.id == value { + return true, nil + } + } + return false, nil + case "group-name": + for _, g := range inst.reservation.groups { + if g.name == value { + return true, nil + } + } + return false, nil + case "image-id": + return value == inst.imageId, nil + case "instance-state-code": + code, err := strconv.Atoi(value) + if err != nil { + return false, err + } + return code&0xff == inst.state.Code, nil + case "instance-state-name": + return value == inst.state.Name, nil + } + return false, fmt.Errorf("unknown attribute %q", attr) +} + +var ( + Pending = ec2.InstanceState{0, "pending"} + Running = ec2.InstanceState{16, "running"} + ShuttingDown = ec2.InstanceState{32, "shutting-down"} + Terminated = ec2.InstanceState{16, "terminated"} + Stopped = ec2.InstanceState{16, "stopped"} +) + +func (srv *Server) createSecurityGroup(w http.ResponseWriter, req *http.Request, reqId string) interface{} { + name := req.Form.Get("GroupName") + if name == "" { + fatalf(400, "InvalidParameterValue", "empty security group name") + } + srv.mu.Lock() + defer srv.mu.Unlock() + if srv.group(ec2.SecurityGroup{Name: name}) != nil { + fatalf(400, "InvalidGroup.Duplicate", "group %q already exists", name) + } + g := &securityGroup{ + name: name, + description: req.Form.Get("GroupDescription"), + id: fmt.Sprintf("sg-%d", srv.groupId.next()), + perms: make(map[permKey]bool), + } + srv.groups[g.id] = g + // we define a local type for this because ec2.CreateSecurityGroupResp + // contains SecurityGroup, but the response to this request + // should not contain the security group name. + type CreateSecurityGroupResponse struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` + GroupId string `xml:"groupId"` + } + r := &CreateSecurityGroupResponse{ + RequestId: reqId, + Return: true, + GroupId: g.id, + } + return r +} + +func (srv *Server) notImplemented(w http.ResponseWriter, req *http.Request, reqId string) interface{} { + fatalf(500, "InternalError", "not implemented") + panic("not reached") +} + +func (srv *Server) describeInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} { + srv.mu.Lock() + defer srv.mu.Unlock() + insts := make(map[*Instance]bool) + for name, vals := range req.Form { + if !strings.HasPrefix(name, "InstanceId.") { + continue + } + inst := srv.instances[vals[0]] + if inst == nil { + fatalf(400, "InvalidInstanceID.NotFound", "instance %q not found", vals[0]) + } + insts[inst] = true + } + + f := newFilter(req.Form) + + var resp ec2.DescribeInstancesResp + resp.RequestId = reqId + for _, r := range srv.reservations { + var instances []ec2.Instance + for _, inst := range r.instances { + if len(insts) > 0 && !insts[inst] { + continue + } + ok, err := f.ok(inst) + if ok { + instances = append(instances, inst.ec2instance()) + } else if err != nil { + fatalf(400, "InvalidParameterValue", "describe instances: %v", err) + } + } + if len(instances) > 0 { + var groups []ec2.SecurityGroup + for _, g := range r.groups { + groups = append(groups, g.ec2SecurityGroup()) + } + resp.Reservations = append(resp.Reservations, ec2.Reservation{ + ReservationId: r.id, + OwnerId: ownerId, + Instances: instances, + SecurityGroups: groups, + }) + } + } + return &resp +} + +func (srv *Server) describeSecurityGroups(w http.ResponseWriter, req *http.Request, reqId string) interface{} { + // BUG similar bug to describeInstances, but for GroupName and GroupId + srv.mu.Lock() + defer srv.mu.Unlock() + + var groups []*securityGroup + for name, vals := range req.Form { + var g ec2.SecurityGroup + switch { + case strings.HasPrefix(name, "GroupName."): + g.Name = vals[0] + case strings.HasPrefix(name, "GroupId."): + g.Id = vals[0] + default: + continue + } + sg := srv.group(g) + if sg == nil { + fatalf(400, "InvalidGroup.NotFound", "no such group %v", g) + } + groups = append(groups, sg) + } + if len(groups) == 0 { + for _, g := range srv.groups { + groups = append(groups, g) + } + } + + f := newFilter(req.Form) + var resp ec2.SecurityGroupsResp + resp.RequestId = reqId + for _, group := range groups { + ok, err := f.ok(group) + if ok { + resp.Groups = append(resp.Groups, ec2.SecurityGroupInfo{ + OwnerId: ownerId, + SecurityGroup: group.ec2SecurityGroup(), + Description: group.description, + IPPerms: group.ec2Perms(), + }) + } else if err != nil { + fatalf(400, "InvalidParameterValue", "describe security groups: %v", err) + } + } + return &resp +} + +func (srv *Server) authorizeSecurityGroupIngress(w http.ResponseWriter, req *http.Request, reqId string) interface{} { + srv.mu.Lock() + defer srv.mu.Unlock() + g := srv.group(ec2.SecurityGroup{ + Name: req.Form.Get("GroupName"), + Id: req.Form.Get("GroupId"), + }) + if g == nil { + fatalf(400, "InvalidGroup.NotFound", "group not found") + } + perms := srv.parsePerms(req) + + for _, p := range perms { + if g.perms[p] { + fatalf(400, "InvalidPermission.Duplicate", "Permission has already been authorized on the specified group") + } + } + for _, p := range perms { + g.perms[p] = true + } + return &ec2.SimpleResp{ + XMLName: xml.Name{"", "AuthorizeSecurityGroupIngressResponse"}, + RequestId: reqId, + } +} + +func (srv *Server) revokeSecurityGroupIngress(w http.ResponseWriter, req *http.Request, reqId string) interface{} { + srv.mu.Lock() + defer srv.mu.Unlock() + g := srv.group(ec2.SecurityGroup{ + Name: req.Form.Get("GroupName"), + Id: req.Form.Get("GroupId"), + }) + if g == nil { + fatalf(400, "InvalidGroup.NotFound", "group not found") + } + perms := srv.parsePerms(req) + + // Note EC2 does not give an error if asked to revoke an authorization + // that does not exist. + for _, p := range perms { + delete(g.perms, p) + } + return &ec2.SimpleResp{ + XMLName: xml.Name{"", "RevokeSecurityGroupIngressResponse"}, + RequestId: reqId, + } +} + +var secGroupPat = regexp.MustCompile(`^sg-[a-z0-9]+$`) +var ipPat = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$`) +var ownerIdPat = regexp.MustCompile(`^[0-9]+$`) + +// parsePerms returns a slice of permKey values extracted +// from the permission fields in req. +func (srv *Server) parsePerms(req *http.Request) []permKey { + // perms maps an index found in the form to its associated + // IPPerm. For instance, the form value with key + // "IpPermissions.3.FromPort" will be stored in perms[3].FromPort + perms := make(map[int]ec2.IPPerm) + + type subgroupKey struct { + id1, id2 int + } + // Each IPPerm can have many source security groups. The form key + // for a source security group contains two indices: the index + // of the IPPerm and the sub-index of the security group. The + // sourceGroups map maps from a subgroupKey containing these + // two indices to the associated security group. For instance, + // the form value with key "IPPermissions.3.Groups.2.GroupName" + // will be stored in sourceGroups[subgroupKey{3, 2}].Name. + sourceGroups := make(map[subgroupKey]ec2.UserSecurityGroup) + + // For each value in the form we store its associated information in the + // above maps. The maps are necessary because the form keys may + // arrive in any order, and the indices are not + // necessarily sequential or even small. + for name, vals := range req.Form { + val := vals[0] + var id1 int + var rest string + if x, _ := fmt.Sscanf(name, "IpPermissions.%d.%s", &id1, &rest); x != 2 { + continue + } + ec2p := perms[id1] + switch { + case rest == "FromPort": + ec2p.FromPort = atoi(val) + case rest == "ToPort": + ec2p.ToPort = atoi(val) + case rest == "IpProtocol": + switch val { + case "tcp", "udp", "icmp": + ec2p.Protocol = val + default: + // check it's a well formed number + atoi(val) + ec2p.Protocol = val + } + case strings.HasPrefix(rest, "Groups."): + k := subgroupKey{id1: id1} + if x, _ := fmt.Sscanf(rest[len("Groups."):], "%d.%s", &k.id2, &rest); x != 2 { + continue + } + g := sourceGroups[k] + switch rest { + case "UserId": + // BUG if the user id is blank, this does not conform to the + // way that EC2 handles it - a specified but blank owner id + // can cause RevokeSecurityGroupIngress to fail with + // "group not found" even if the security group id has been + // correctly specified. + // By failing here, we ensure that we fail early in this case. + if !ownerIdPat.MatchString(val) { + fatalf(400, "InvalidUserID.Malformed", "Invalid user ID: %q", val) + } + g.OwnerId = val + case "GroupName": + g.Name = val + case "GroupId": + if !secGroupPat.MatchString(val) { + fatalf(400, "InvalidGroupId.Malformed", "Invalid group ID: %q", val) + } + g.Id = val + default: + fatalf(400, "UnknownParameter", "unknown parameter %q", name) + } + sourceGroups[k] = g + case strings.HasPrefix(rest, "IpRanges."): + var id2 int + if x, _ := fmt.Sscanf(rest[len("IpRanges."):], "%d.%s", &id2, &rest); x != 2 { + continue + } + switch rest { + case "CidrIp": + if !ipPat.MatchString(val) { + fatalf(400, "InvalidPermission.Malformed", "Invalid IP range: %q", val) + } + ec2p.SourceIPs = append(ec2p.SourceIPs, val) + default: + fatalf(400, "UnknownParameter", "unknown parameter %q", name) + } + default: + fatalf(400, "UnknownParameter", "unknown parameter %q", name) + } + perms[id1] = ec2p + } + // Associate each set of source groups with its IPPerm. + for k, g := range sourceGroups { + p := perms[k.id1] + p.SourceGroups = append(p.SourceGroups, g) + perms[k.id1] = p + } + + // Now that we have built up the IPPerms we need, we check for + // parameter errors and build up a permKey for each permission, + // looking up security groups from srv as we do so. + var result []permKey + for _, p := range perms { + if p.FromPort > p.ToPort { + fatalf(400, "InvalidParameterValue", "invalid port range") + } + k := permKey{ + protocol: p.Protocol, + fromPort: p.FromPort, + toPort: p.ToPort, + } + for _, g := range p.SourceGroups { + if g.OwnerId != "" && g.OwnerId != ownerId { + fatalf(400, "InvalidGroup.NotFound", "group %q not found", g.Name) + } + var ec2g ec2.SecurityGroup + switch { + case g.Id != "": + ec2g.Id = g.Id + case g.Name != "": + ec2g.Name = g.Name + } + k.group = srv.group(ec2g) + if k.group == nil { + fatalf(400, "InvalidGroup.NotFound", "group %v not found", g) + } + result = append(result, k) + } + k.group = nil + for _, ip := range p.SourceIPs { + k.ipAddr = ip + result = append(result, k) + } + } + return result +} + +func (srv *Server) deleteSecurityGroup(w http.ResponseWriter, req *http.Request, reqId string) interface{} { + srv.mu.Lock() + defer srv.mu.Unlock() + g := srv.group(ec2.SecurityGroup{ + Name: req.Form.Get("GroupName"), + Id: req.Form.Get("GroupId"), + }) + if g == nil { + fatalf(400, "InvalidGroup.NotFound", "group not found") + } + for _, r := range srv.reservations { + for _, h := range r.groups { + if h == g && r.hasRunningMachine() { + fatalf(500, "InvalidGroup.InUse", "group is currently in use by a running instance") + } + } + } + for _, sg := range srv.groups { + // If a group refers to itself, it's ok to delete it. + if sg == g { + continue + } + for k := range sg.perms { + if k.group == g { + fatalf(500, "InvalidGroup.InUse", "group is currently in use by group %q", sg.id) + } + } + } + + delete(srv.groups, g.id) + return &ec2.SimpleResp{ + XMLName: xml.Name{"", "DeleteSecurityGroupResponse"}, + RequestId: reqId, + } +} + +func (r *reservation) hasRunningMachine() bool { + for _, inst := range r.instances { + if inst.state.Code != ShuttingDown.Code && inst.state.Code != Terminated.Code { + return true + } + } + return false +} + +type counter int + +func (c *counter) next() (i int) { + i = int(*c) + (*c)++ + return +} + +// atoi is like strconv.Atoi but is fatal if the +// string is not well formed. +func atoi(s string) int { + i, err := strconv.Atoi(s) + if err != nil { + fatalf(400, "InvalidParameterValue", "bad number: %v", err) + } + return i +} + +func fatalf(statusCode int, code string, f string, a ...interface{}) { + panic(&ec2.Error{ + StatusCode: statusCode, + Code: code, + Message: fmt.Sprintf(f, a...), + }) +} diff --git a/vendor/github.com/goamz/goamz/ec2/export_test.go b/vendor/github.com/goamz/goamz/ec2/export_test.go new file mode 100644 index 000000000..78da91a07 --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/export_test.go @@ -0,0 +1,22 @@ +package ec2 + +import ( + "github.com/goamz/goamz/aws" + "time" +) + +func Sign(auth aws.Auth, method, path string, params map[string]string, host string) { + sign(auth, method, path, params, host) +} + +func fixedTime() time.Time { + return time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC) +} + +func FakeTime(fakeIt bool) { + if fakeIt { + timeNow = fixedTime + } else { + timeNow = time.Now + } +} diff --git a/vendor/github.com/goamz/goamz/ec2/responses_test.go b/vendor/github.com/goamz/goamz/ec2/responses_test.go new file mode 100644 index 000000000..84186c1be --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/responses_test.go @@ -0,0 +1,1084 @@ +package ec2_test + +var ErrorDump = ` + +UnsupportedOperation +AMIs with an instance-store root device are not supported for the instance type 't1.micro'. +0503f4e9-bbd6-483c-b54f-c4ae9f3b30f4 +` + +// http://goo.gl/Mcm3b +var RunInstancesExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + r-47a5402e + 999988887777 + + + sg-67ad940e + default + + + + + i-2ba64342 + ami-60a54009 + + 0 + pending + + + + example-key-name + 0 + m1.small + 2007-08-07T11:51:50.000Z + + us-east-1b + + + enabled + + paravirtual + + + xen + + + i-2bc64242 + ami-60a54009 + + 0 + pending + + + + example-key-name + 1 + m1.small + 2007-08-07T11:51:50.000Z + + us-east-1b + + + enabled + + paravirtual + + + xen + + + i-2be64332 + ami-60a54009 + + 0 + pending + + + + example-key-name + 2 + m1.small + 2007-08-07T11:51:50.000Z + + us-east-1b + + + enabled + + paravirtual + + + xen + + + +` + +// http://goo.gl/GRZgCD +var RequestSpotInstancesExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + sir-1a2b3c4d + 0.5 + one-time + open + + pending-evaluation + YYYY-MM-DDTHH:MM:SS.000Z + Your Spot request has been submitted for review, and is pending evaluation. + + MyAzGroup + + ami-1a2b3c4d + gsg-keypair + + + sg-1a2b3c4d + websrv + + + m1.small + + + false + + false + + YYYY-MM-DDTHH:MM:SS.000Z + Linux/UNIX + + + +` + +// http://goo.gl/KsKJJk +var DescribeSpotRequestsExample = ` + + b1719f2a-5334-4479-b2f1-26926EXAMPLE + + + sir-1a2b3c4d + 0.5 + one-time + active + + fulfilled + YYYY-MM-DDTHH:MM:SS.000Z + Your Spot request is fulfilled. + + + ami-1a2b3c4d + gsg-keypair + + + sg-1a2b3c4d + websrv + + + m1.small + + false + + false + + i-1a2b3c4d + YYYY-MM-DDTHH:MM:SS.000Z + Linux/UNIX + us-east-1a + + + +` + +// http://goo.gl/DcfFgJ +var CancelSpotRequestsExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + sir-1a2b3c4d + cancelled + + + +` + +// http://goo.gl/3BKHj +var TerminateInstancesExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + i-3ea74257 + + 32 + shutting-down + + + 16 + running + + + + +` + +// http://goo.gl/mLbmw +var DescribeInstancesExample1 = ` + + 98e3c9a4-848c-4d6d-8e8a-b1bdEXAMPLE + + + r-b27e30d9 + 999988887777 + + + sg-67ad940e + default + + + + + i-c5cd56af + ami-1a2b3c4d + + 16 + running + + domU-12-31-39-10-56-34.compute-1.internal + ec2-174-129-165-232.compute-1.amazonaws.com + + GSG_Keypair + 0 + + m1.small + 2010-08-17T01:15:18.000Z + + us-east-1b + + + aki-94c527fd + ari-96c527ff + + disabled + + 10.198.85.190 + 174.129.165.232 + i386 + ebs + /dev/sda1 + + + /dev/sda1 + + vol-a082c1c9 + attached + 2010-08-17T01:15:21.000Z + false + + + + spot + sir-7a688402 + paravirtual + + + xen + + + 854251627541 + + + r-b67e30dd + 999988887777 + + + sg-67ad940e + default + + + + + i-d9cd56b3 + ami-1a2b3c4d + + 16 + running + + domU-12-31-39-10-54-E5.compute-1.internal + ec2-184-73-58-78.compute-1.amazonaws.com + + GSG_Keypair + 0 + + m1.large + 2010-08-17T01:15:19.000Z + + us-east-1b + + + aki-94c527fd + ari-96c527ff + + disabled + + 10.198.87.19 + 184.73.58.78 + i386 + ebs + /dev/sda1 + + + /dev/sda1 + + vol-a282c1cb + attached + 2010-08-17T01:15:23.000Z + false + + + + spot + sir-55a3aa02 + paravirtual + + + xen + + + 854251627541 + + + +` + +// http://goo.gl/mLbmw +var DescribeInstancesExample2 = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + r-bc7e30d7 + 999988887777 + + + sg-67ad940e + default + + + + + i-c7cd56ad + ami-b232d0db + + 16 + running + + domU-12-31-39-01-76-06.compute-1.internal + ec2-72-44-52-124.compute-1.amazonaws.com + GSG_Keypair + 0 + + m1.small + 2010-08-17T01:15:16.000Z + + us-east-1b + + aki-94c527fd + ari-96c527ff + + disabled + + 10.255.121.240 + 72.44.52.124 + i386 + ebs + /dev/sda1 + + + /dev/sda1 + + vol-a482c1cd + attached + 2010-08-17T01:15:26.000Z + true + + + + paravirtual + + + + webserver + + + + stack + Production + + + xen + + + + + +` + +// http://goo.gl/2FBTdS +var DescribeInstanceStatusExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + i-c7cd56ad + us-east-1b + + + instance-reboot + example description + 2010-08-17T01:15:18.000Z + 2010-08-17T01:15:18.000Z + + + + 16 + running + + + ok +
+ reachability + passed + 2010-08-17T01:15:18.000Z +
+
+ + ok +
+ reachability + passed + 2010-08-17T01:15:18.000Z +
+
+
+
+ exampleToken +
+` + +// http://goo.gl/icuXh5 +var ModifyInstanceExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/9rprDN +var AllocateAddressExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + 198.51.100.1 + vpc + eipalloc-5723d13e + +` + +// http://goo.gl/3Q0oCc +var ReleaseAddressExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/uOSQE +var AssociateAddressExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + eipassoc-fc5ca095 + +` + +// http://goo.gl/LrOa0 +var DisassociateAddressExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +//http://goo.gl/zW7J4p +var DescribeAddressesExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + 192.0.2.1 + standard + i-f15ebb98 + + + 198.51.100.2 + standard + + + + 203.0.113.41 + eipalloc-08229861 + vpc + i-64600030 + eipassoc-f0229899 + eni-ef229886 + 053230519467 + 10.0.0.228 + + + +` + +var DescribeAddressesAllocationIdExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + 203.0.113.41 + eipalloc-08229861 + vpc + i-64600030 + eipassoc-f0229899 + eni-ef229886 + 053230519467 + 10.0.0.228 + + + 146.54.2.230 + eipalloc-08364752 + vpc + i-64693456 + eipassoc-f0348693 + eni-da764039 + 053230519467 + 10.0.0.102 + + + +` + +// http://goo.gl/cxU41 +var CreateImageExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + ami-4fa54026 + +` + +// http://goo.gl/V0U25 +var DescribeImagesExample = ` + + 4a4a27a2-2e7c-475d-b35b-ca822EXAMPLE + + + ami-a2469acf + aws-marketplace/example-marketplace-amzn-ami.1 + available + 123456789999 + true + + + a1b2c3d4e5f6g7h8i9j10k11 + marketplace + + + i386 + machine + aki-805ea7e9 + aws-marketplace + example-marketplace-amzn-ami.1 + Amazon Linux AMI i386 EBS + ebs + /dev/sda1 + + + /dev/sda1 + + snap-787e9403 + 8 + true + + + + paravirtual + + + Purpose + EXAMPLE + + + xen + + + +` + +// http://goo.gl/bHO3z +var ImageAttributeExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + ami-61a54008 + + + all + + + 495219933132 + + + +` + +// http://goo.gl/ttcda +var CreateSnapshotExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + snap-78a54011 + vol-4d826724 + pending + 2008-05-07T12:51:50.000Z + 60% + 111122223333 + 10 + Daily Backup + +` + +// http://goo.gl/vwU1y +var DeleteSnapshotExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/nkovs +var DescribeSnapshotsExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + snap-1a2b3c4d + vol-8875daef + pending + 2010-07-29T04:12:01.000Z + 30% + 111122223333 + 15 + Daily Backup + + + Purpose + demo_db_14_backup + + + + + +` + +// http://goo.gl/YUjO4G +var ModifyImageAttributeExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/hQwPCK +var CopyImageExample = ` + + 60bc441d-fa2c-494d-b155-5d6a3EXAMPLE + ami-4d3c2b1a + +` + +var CreateKeyPairExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + foo + + 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 + + ---- BEGIN RSA PRIVATE KEY ---- +MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w0BAQUFADCBiDELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6 +b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAd +BgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wHhcNMTEwNDI1MjA0NTIxWhcN +MTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYD +VQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25z +b2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFt +YXpvbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ +21uUSfwfEvySWtC2XADZ4nB+BLYgVIk60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9T +rDHudUZg3qX4waLG5M43q7Wgc/MbQITxOUSQv7c7ugFFDzQGBzZswY6786m86gpE +Ibb3OhjZnzcvQAaRHhdlQWIMm2nrAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4 +nUhVVxYUntneD9+h8Mg9q6q+auNKyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0Fkb +FFBjvSfpJIlJ00zbhNYS5f6GuoEDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTb +NYiytVbZPQUQ5Yaxu2jXnimvw3rrszlaEXAMPLE= +-----END RSA PRIVATE KEY----- + + +` + +var DeleteKeyPairExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/Eo7Yl +var CreateSecurityGroupExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + sg-67ad940e + +` + +// http://goo.gl/k12Uy +var DescribeSecurityGroupsExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + 999988887777 + WebServers + sg-67ad940e + Web Servers + + + tcp + 80 + 80 + + + + 0.0.0.0/0 + + + + + + + tcp + 80 + 80 + + + + 0.0.0.0/0 + + + + + + + 999988887777 + RangedPortsBySource + sg-76abc467 + Group A + + + tcp + 6000 + 7000 + + + + + + + +` + +// A dump which includes groups within ip permissions. +var DescribeSecurityGroupsDump = ` + + + 87b92b57-cc6e-48b2-943f-f6f0e5c9f46c + + + 12345 + default + default group + + + icmp + -1 + -1 + + + 12345 + default + sg-67ad940e + + + + + + tcp + 0 + 65535 + + + 12345 + other + sg-76abc467 + + + + + + + + +` + +// http://goo.gl/QJJDO +var DeleteSecurityGroupExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/u2sDJ +var AuthorizeSecurityGroupIngressExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/Mz7xr +var RevokeSecurityGroupIngressExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/Vmkqc +var CreateTagsExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +// http://goo.gl/awKeF +var StartInstancesExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + i-10a64379 + + 0 + pending + + + 80 + stopped + + + + +` + +// http://goo.gl/436dJ +var StopInstancesExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + i-10a64379 + + 64 + stopping + + + 16 + running + + + + +` + +// http://goo.gl/baoUf +var RebootInstancesExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +var DescribeRouteTablesExample = ` + + 6f570b0b-9c18-4b07-bdec-73740dcf861aEXAMPLE + + + rtb-13ad487a + vpc-11ad4878 + + + 10.0.0.0/22 + local + active + CreateRouteTable + + + + + rtbassoc-12ad487b + rtb-13ad487a +
true
+
+
+ +
+ + rtb-f9ad4890 + vpc-11ad4878 + + + 10.0.0.0/22 + local + active + CreateRouteTable + + + 0.0.0.0/0 + igw-eaad4883 + active + + + + + rtbassoc-faad4893 + rtb-f9ad4890 + subnet-15ad487c + + + + +
+
+` + +var CreateRouteTableExample = ` + + 59abcd43-35bd-4eac-99ed-be587EXAMPLE + + rtb-f9ad4890 + vpc-11ad4878 + + + 10.0.0.0/22 + local + active + + + + + + +` + +var DeleteRouteTableExample = ` + + 49dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +var AssociateRouteTableExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + rtbassoc-f8ad4891 + +` + +var DisassociateRouteTableExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` + +var ReplaceRouteTableAssociationExample = ` + + 59dbff89-35bd-4eac-88ed-be587EXAMPLE + rtbassoc-faad2958 + +` +var DescribeReservedInstancesExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + + + e5a2ff3b-7d14-494f-90af-0b5d0EXAMPLE + m1.xlarge + us-east-1b + 31536000 + 61.0 + 0.034 + 3 + Linux/UNIX + active + default + USD + Light Utilization + + + + +` + +var DescribeVpcsExample = ` + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + + vpc-1a2b3c4d + available + 10.0.0.0/23 + dopt-7a8b9c2d + default + false + + + + +` + +var CreateVpcExample = ` + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + vpc-1a2b3c4d + pending + 10.0.0.0/16 + dopt-1a2b3c4d2 + default + + + +` + +var DeleteVpcExample = ` + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + true + +` + +var CreateRouteExample = ` + + b4998629-3000-437f-b382-cc96fEXAMPLE + true + +` + +var DeleteRouteExample = ` + + 59dbff89-35bd-4eac-99ed-be587EXAMPLE + true + +` diff --git a/vendor/github.com/goamz/goamz/ec2/sign.go b/vendor/github.com/goamz/goamz/ec2/sign.go new file mode 100644 index 000000000..1c30f13bb --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/sign.go @@ -0,0 +1,45 @@ +package ec2 + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "github.com/goamz/goamz/aws" + "sort" + "strings" +) + +// ---------------------------------------------------------------------------- +// EC2 signing (http://goo.gl/fQmAN) + +var b64 = base64.StdEncoding + +func sign(auth aws.Auth, method, path string, params map[string]string, host string) { + params["AWSAccessKeyId"] = auth.AccessKey + params["SignatureVersion"] = "2" + params["SignatureMethod"] = "HmacSHA256" + if auth.Token() != "" { + params["SecurityToken"] = auth.Token() + } + + // AWS specifies that the parameters in a signed request must + // be provided in the natural order of the keys. This is distinct + // from the natural order of the encoded value of key=value. + // Percent and equals affect the sorting order. + var keys, sarray []string + for k, _ := range params { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(params[k])) + } + joined := strings.Join(sarray, "&") + payload := method + "\n" + host + "\n" + path + "\n" + joined + hash := hmac.New(sha256.New, []byte(auth.SecretKey)) + hash.Write([]byte(payload)) + signature := make([]byte, b64.EncodedLen(hash.Size())) + b64.Encode(signature, hash.Sum(nil)) + + params["Signature"] = string(signature) +} diff --git a/vendor/github.com/goamz/goamz/ec2/sign_test.go b/vendor/github.com/goamz/goamz/ec2/sign_test.go new file mode 100644 index 000000000..bc6996cce --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/sign_test.go @@ -0,0 +1,68 @@ +package ec2_test + +import ( + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/ec2" + . "gopkg.in/check.v1" +) + +// EC2 ReST authentication docs: http://goo.gl/fQmAN + +var testAuth = aws.Auth{AccessKey: "user", SecretKey: "secret"} + +func (s *S) TestBasicSignature(c *C) { + params := map[string]string{} + ec2.Sign(testAuth, "GET", "/path", params, "localhost") + c.Assert(params["SignatureVersion"], Equals, "2") + c.Assert(params["SignatureMethod"], Equals, "HmacSHA256") + expected := "6lSe5QyXum0jMVc7cOUz32/52ZnL7N5RyKRk/09yiK4=" + c.Assert(params["Signature"], Equals, expected) +} + +func (s *S) TestParamSignature(c *C) { + params := map[string]string{ + "param1": "value1", + "param2": "value2", + "param3": "value3", + } + ec2.Sign(testAuth, "GET", "/path", params, "localhost") + expected := "XWOR4+0lmK8bD8CGDGZ4kfuSPbb2JibLJiCl/OPu1oU=" + c.Assert(params["Signature"], Equals, expected) +} + +func (s *S) TestManyParams(c *C) { + params := map[string]string{ + "param1": "value10", + "param2": "value2", + "param3": "value3", + "param4": "value4", + "param5": "value5", + "param6": "value6", + "param7": "value7", + "param8": "value8", + "param9": "value9", + "param10": "value1", + } + ec2.Sign(testAuth, "GET", "/path", params, "localhost") + expected := "di0sjxIvezUgQ1SIL6i+C/H8lL+U0CQ9frLIak8jkVg=" + c.Assert(params["Signature"], Equals, expected) +} + +func (s *S) TestEscaping(c *C) { + params := map[string]string{"Nonce": "+ +"} + ec2.Sign(testAuth, "GET", "/path", params, "localhost") + c.Assert(params["Nonce"], Equals, "+ +") + expected := "bqffDELReIqwjg/W0DnsnVUmfLK4wXVLO4/LuG+1VFA=" + c.Assert(params["Signature"], Equals, expected) +} + +func (s *S) TestSignatureExample1(c *C) { + params := map[string]string{ + "Timestamp": "2009-02-01T12:53:20+00:00", + "Version": "2007-11-07", + "Action": "ListDomains", + } + ec2.Sign(aws.Auth{AccessKey: "access", SecretKey: "secret"}, "GET", "/", params, "sdb.amazonaws.com") + expected := "okj96/5ucWBSc1uR2zXVfm6mDHtgfNv657rRtt/aunQ=" + c.Assert(params["Signature"], Equals, expected) +} diff --git a/vendor/github.com/goamz/goamz/ec2/vpc.go b/vendor/github.com/goamz/goamz/ec2/vpc.go new file mode 100644 index 000000000..3a2ff7c6e --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/vpc.go @@ -0,0 +1,399 @@ +package ec2 + +// RouteTable describes a route table which contains a set of rules, called routes +// that are used to determine where network traffic is directed. +// +// See http://goo.gl/bI9hkg for more details. +type RouteTable struct { + Id string `xml:"routeTableId"` + VpcId string `xml:"vpcId"` + Routes []Route `xml:"routeSet>item"` + Associations []RouteTableAssociation `xml:"associationSet>item"` + PropagatingVgws []PropagatingVgw `xml:"propagatingVgwSet>item"` + Tags []Tag `xml:"tagSet>item"` +} + +// Route describes a route in a route table. +// +// See http://goo.gl/hE5Kxe for more details. +type Route struct { + DestinationCidrBlock string `xml:"destinationCidrBlock"` // The CIDR block used for the destination match. + GatewayId string `xml:"gatewayId"` // The ID of a gateway attached to your VPC. + InstanceId string `xml:"instanceId"` // The ID of a NAT instance in your VPC. + InstanceOwnerId string `xml:"instanceOwnerId"` // The AWS account ID of the owner of the instance. + NetworkInterfaceId string `xml:"networkInterfaceId"` // The ID of the network interface. + State string `xml:"state"` // The state of the route. Valid values: active | blackhole + Origin string `xml:"origin"` // Describes how the route was created. Valid values: Valid values: CreateRouteTable | CreateRoute | EnableVgwRoutePropagation + VpcPeeringConnectionId string `xml:"vpcPeeringConnectionId"` // The ID of the VPC peering connection. +} + +// RouteTableAssociation describes an association between a route table and a subnet. +// +// See http://goo.gl/BZB8o8 for more details. +type RouteTableAssociation struct { + Id string `xml:"routeTableAssociationId"` // The ID of the association between a route table and a subnet. + RouteTableId string `xml:"routeTableId"` // The ID of the route table. + SubnetId string `xml:"subnetId"` // The ID of the subnet. + Main bool `xml:"main"` // Indicates whether this is the main route table. +} + +// PropagatingVgw describes a virtual private gateway propagating route. +// +// See http://goo.gl/myGQtG for more details. +type PropagatingVgw struct { + GatewayId string `xml:"gatewayID"` +} + +// CreateRouteTableResp represents a response from a CreateRouteTable request +// +// See http://goo.gl/LD0TqP for more details. +type CreateRouteTableResp struct { + RequestId string `xml:"requestId"` + RouteTable RouteTable `xml:"routeTable"` +} + +// CreateRouteTable creates a route table for the specified VPC. +// After you create a route table, you can add routes and associate the table with a subnet. +// +// See http://goo.gl/V9h6gE for more details.. +func (ec2 *EC2) CreateRouteTable(vpcId string) (resp *CreateRouteTableResp, err error) { + params := makeParams("CreateRouteTable") + params["VpcId"] = vpcId + resp = &CreateRouteTableResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// DescribeRouteTablesResp represents a response from a DescribeRouteTables call +// +// See http://goo.gl/T3tVsg for more details. +type DescribeRouteTablesResp struct { + RequestId string `xml:"requestId"` + RouteTables []RouteTable `xml:"routeTableSet>item"` +} + +// DescribeRouteTables describes one or more of your route tables +// +// See http://goo.gl/S0RVos for more details. +func (ec2 *EC2) DescribeRouteTables(routeTableIds []string, filter *Filter) (resp *DescribeRouteTablesResp, err error) { + params := makeParams("DescribeRouteTables") + addParamsList(params, "RouteTableId", routeTableIds) + filter.addParams(params) + resp = &DescribeRouteTablesResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// AssociateRouteTableResp represents a response from an AssociateRouteTable call +// +// See http://goo.gl/T4KlYk for more details. +type AssociateRouteTableResp struct { + RequestId string `xml:"requestId"` + AssociationId string `xml:"associationId"` +} + +// AssociateRouteTable associates a subnet with a route table. +// +// The subnet and route table must be in the same VPC. This association causes +// traffic originating from the subnet to be routed according to the routes +// in the route table. The action returns an association ID, which you need in +// order to disassociate the route table from the subnet later. +// A route table can be associated with multiple subnets. +// +// See http://goo.gl/bfnONU for more details. +func (ec2 *EC2) AssociateRouteTable(routeTableId, subnetId string) (resp *AssociateRouteTableResp, err error) { + params := makeParams("AssociateRouteTable") + params["RouteTableId"] = routeTableId + params["SubnetId"] = subnetId + resp = &AssociateRouteTableResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// DisassociateRouteTableResp represents the response from a DisassociateRouteTable request +// +// See http://goo.gl/1v4reT for more details. +type DisassociateRouteTableResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` // True if the request succeeds +} + +// DisassociateRouteTable disassociates a subnet from a route table. +// +// See http://goo.gl/A4NJum for more details. +func (ec2 *EC2) DisassociateRouteTable(associationId string) (resp *DisassociateRouteTableResp, err error) { + params := makeParams("DisassociateRouteTable") + params["AssociationId"] = associationId + resp = &DisassociateRouteTableResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// ReplaceRouteTableAssociationResp represents a response from a ReplaceRouteTableAssociation call +// +// See http://goo.gl/VhILGe for more details. +type ReplaceRouteTableAssociationResp struct { + RequestId string `xml:"requestId"` + NewAssociationId string `xml:"newAssociationId"` +} + +// ReplaceRouteTableAssociation changes the route table associated with a given subnet in a VPC. +// +// See http://goo.gl/kiit8j for more details. +func (ec2 *EC2) ReplaceRouteTableAssociation(associationId, routeTableId string) (resp *ReplaceRouteTableAssociationResp, err error) { + params := makeParams("ReplaceRouteTableAssociation") + params["AssociationId"] = associationId + params["RouteTableId"] = routeTableId + resp = &ReplaceRouteTableAssociationResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// DeleteRouteTableResp represents a response from a DeleteRouteTable request +// +// See http://goo.gl/b8usig for more details. +type DeleteRouteTableResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` // True if the request succeeds +} + +// DeleteRouteTable deletes the specified route table. +// You must disassociate the route table from any subnets before you can delete it. +// You can't delete the main route table. +// +// See http://goo.gl/crHxT2 for more details. +func (ec2 *EC2) DeleteRouteTable(routeTableId string) (resp *DeleteRouteTableResp, err error) { + params := makeParams("DeleteRouteTable") + params["RouteTableId"] = routeTableId + resp = &DeleteRouteTableResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// VPC describes a VPC. +// +// See http://goo.gl/WjX0Es for more details. +type VPC struct { + CidrBlock string `xml:"cidrBlock"` + DHCPOptionsID string `xml:"dhcpOptionsId"` + State string `xml:"state"` + VpcId string `xml:"vpcId"` + InstanceTenancy string `xml:"instanceTenancy"` + IsDefault bool `xml:"isDefault"` + Tags []Tag `xml:"tagSet>item"` +} + +// CreateVpcResp represents a response from a CreateVpcResp request +// +// See http://goo.gl/QoK11F for more details. +type CreateVpcResp struct { + RequestId string `xml:"requestId"` + VPC VPC `xml:"vpc"` // Information about the VPC. +} + +// CreateVpc creates a VPC with the specified CIDR block. +// +// The smallest VPC you can create uses a /28 netmask (16 IP addresses), +// and the largest uses a /16 netmask (65,536 IP addresses). +// +// By default, each instance you launch in the VPC has the default DHCP options, +// which includes only a default DNS server that Amazon provides (AmazonProvidedDNS). +// +// See http://goo.gl/QoK11F for more details. +func (ec2 *EC2) CreateVpc(cidrBlock, instanceTenancy string) (resp *CreateVpcResp, err error) { + params := makeParams("CreateVpc") + params["CidrBlock"] = cidrBlock + + if instanceTenancy != "" { + params["InstanceTenancy"] = instanceTenancy + } + + resp = &CreateVpcResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + +// DeleteVpcResp represents a response from a DeleteVpc request +// +// See http://goo.gl/qawyrz for more details. +type DeleteVpcResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` // True if the request succeeds +} + +// DeleteVpc deletes the specified VPC. +// +// You must detach or delete all gateways and resources that are associated with +// the VPC before you can delete it. For example, you must terminate all +// instances running in the VPC, delete all security groups associated with +// the VPC (except the default one), delete all route tables associated with +// the VPC (except the default one), and so on. +// +// See http://goo.gl/qawyrz for more details. +func (ec2 *EC2) DeleteVpc(vpcId string) (resp *DeleteVpcResp, err error) { + params := makeParams("DeleteVpc") + params["VpcId"] = vpcId + + resp = &DeleteVpcResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// DescribeVpcsResp represents a response from a DescribeVpcs request +// +// See http://goo.gl/DWQWvZ for more details. +type DescribeVpcsResp struct { + RequestId string `xml:"requestId"` + VPCs []VPC `xml:"vpcSet>item"` // Information about one or more VPCs. +} + +// DescribeVpcs describes one or more of your VPCs. +// +// See http://goo.gl/DWQWvZ for more details. +func (ec2 *EC2) DescribeVpcs(vpcIds []string, filter *Filter) (resp *DescribeVpcsResp, err error) { + params := makeParams("DescribeVpcs") + addParamsList(params, "VpcId", vpcIds) + filter.addParams(params) + resp = &DescribeVpcsResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} + +// DeleteRouteResp represents a response from a DeleteRoute request +// +// See http://goo.gl/Uqyt3w for more details. +type DeleteRouteResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` // True if the request succeeds +} + +// DeleteRoute deletes the specified route from the specified route table. +// +// See http://goo.gl/Uqyt3w for more details. +func (ec2 *EC2) DeleteRoute(routeTableId, destinationCidrBlock string) (resp *DeleteRouteResp, err error) { + params := makeParams("DeleteRoute") + params["RouteTableId"] = routeTableId + params["DestinationCidrBlock"] = destinationCidrBlock + resp = &DeleteRouteResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// CreateRouteResp represents a response from a CreateRoute request +// +// See http://goo.gl/c6Bg7e for more details. +type CreateRouteResp struct { + RequestId string `xml:"requestId"` + Return bool `xml:"return"` // True if the request succeeds +} + +// CreateRoute structure contains the options for a CreateRoute API call. +type CreateRoute struct { + DestinationCidrBlock string + GatewayId string + InstanceId string + NetworkInterfaceId string + VpcPeeringConnectionId string +} + +// CreateRoute creates a route in a route table within a VPC. +// You must specify one of the following targets: Internet gateway or virtual +// private gateway, NAT instance, VPC peering connection, or network interface. +// +// See http://goo.gl/c6Bg7e for more details. +func (ec2 *EC2) CreateRoute(routeTableId string, options *CreateRoute) (resp *CreateRouteResp, err error) { + params := makeParams("CreateRoute") + params["RouteTableId"] = routeTableId + if options.DestinationCidrBlock != "" { + params["DestinationCidrBlock"] = options.DestinationCidrBlock + } + if options.GatewayId != "" { + params["GatewayId"] = options.GatewayId + } + if options.InstanceId != "" { + params["InstanceId"] = options.InstanceId + } + if options.NetworkInterfaceId != "" { + params["NetworkInterfaceId"] = options.NetworkInterfaceId + } + if options.VpcPeeringConnectionId != "" { + params["VpcPeeringConnectionId"] = options.VpcPeeringConnectionId + } + resp = &CreateRouteResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + return +} + +// Subnet describes a subnet +// +// See http://goo.gl/bifW4R +type Subnet struct { + AvailabilityZone string `xml:"availabilityZone"` + AvailableIpAddressCount int `xml:"availableIpAddressCount"` + CidrBlock string `xml:"cidrBlock"` + DefaultForAZ bool `xml:"defaultForAz"` + MapPublicIpOnLaunch bool `xml:"mapPublicIpOnLaunch"` + State string `xml:"state"` + SubnetId string `xml:"subnetId"` + Tags []Tag `xml:"tagSet>item"` + VpcId string `xml:"vpcId"` +} + +// DescribeSubnetsResp represents a response from a DescribeSubnets request +// +// See https://goo.gl/1s0UQd for more details. +type DescribeSubnetsResp struct { + RequestId string `xml:"requestId"` + Subnets []Subnet `xml:"subnetSet>item"` +} + +// DescribeSubnets describes one or more Subnets. +// +// See https://goo.gl/1s0UQd for more details. +func (ec2 *EC2) DescribeSubnets(subnetIds []string, filter *Filter) (resp *DescribeSubnetsResp, err error) { + params := makeParams("DescribeSubnets") + addParamsList(params, "SubnetId", subnetIds) + filter.addParams(params) + resp = &DescribeSubnetsResp{} + err = ec2.query(params, resp) + if err != nil { + return nil, err + } + + return +} diff --git a/vendor/github.com/goamz/goamz/ec2/vpc_test.go b/vendor/github.com/goamz/goamz/ec2/vpc_test.go new file mode 100644 index 000000000..a6e67eeed --- /dev/null +++ b/vendor/github.com/goamz/goamz/ec2/vpc_test.go @@ -0,0 +1,224 @@ +package ec2_test + +import ( + "github.com/goamz/goamz/ec2" + . "gopkg.in/check.v1" +) + +func (s *S) TestCreateRouteTable(c *C) { + testServer.Response(200, nil, CreateRouteTableExample) + + resp, err := s.ec2.CreateRouteTable("vpc-11ad4878") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"CreateRouteTable"}) + c.Assert(req.Form["VpcId"], DeepEquals, []string{"vpc-11ad4878"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59abcd43-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.RouteTable.Id, Equals, "rtb-f9ad4890") + c.Assert(resp.RouteTable.VpcId, Equals, "vpc-11ad4878") + c.Assert(resp.RouteTable.Routes, HasLen, 1) + c.Assert(resp.RouteTable.Routes[0], DeepEquals, ec2.Route{ + DestinationCidrBlock: "10.0.0.0/22", + GatewayId: "local", + State: "active", + }) + c.Assert(resp.RouteTable.Associations, HasLen, 0) + c.Assert(resp.RouteTable.Tags, HasLen, 0) +} + +func (s *S) TestDescribeRouteTables(c *C) { + testServer.Response(200, nil, DescribeRouteTablesExample) + + filter := ec2.NewFilter() + filter.Add("key1", "value1") + filter.Add("key2", "value2", "value3") + + resp, err := s.ec2.DescribeRouteTables([]string{"rt1", "rt2"}, nil) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeRouteTables"}) + c.Assert(req.Form["RouteTableId.1"], DeepEquals, []string{"rt1"}) + c.Assert(req.Form["RouteTableId.2"], DeepEquals, []string{"rt2"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "6f570b0b-9c18-4b07-bdec-73740dcf861aEXAMPLE") + c.Assert(resp.RouteTables, HasLen, 2) + + rt1 := resp.RouteTables[0] + c.Assert(rt1.Id, Equals, "rtb-13ad487a") + c.Assert(rt1.VpcId, Equals, "vpc-11ad4878") + c.Assert(rt1.Routes, DeepEquals, []ec2.Route{ + {DestinationCidrBlock: "10.0.0.0/22", GatewayId: "local", State: "active", Origin: "CreateRouteTable"}, + }) + c.Assert(rt1.Associations, DeepEquals, []ec2.RouteTableAssociation{ + {Id: "rtbassoc-12ad487b", RouteTableId: "rtb-13ad487a", Main: true}, + }) + + rt2 := resp.RouteTables[1] + c.Assert(rt2.Id, Equals, "rtb-f9ad4890") + c.Assert(rt2.VpcId, Equals, "vpc-11ad4878") + c.Assert(rt2.Routes, DeepEquals, []ec2.Route{ + {DestinationCidrBlock: "10.0.0.0/22", GatewayId: "local", State: "active", Origin: "CreateRouteTable"}, + {DestinationCidrBlock: "0.0.0.0/0", GatewayId: "igw-eaad4883", State: "active"}, + }) + c.Assert(rt2.Associations, DeepEquals, []ec2.RouteTableAssociation{ + {Id: "rtbassoc-faad4893", RouteTableId: "rtb-f9ad4890", SubnetId: "subnet-15ad487c"}, + }) +} + +func (s *S) TestAssociateRouteTable(c *C) { + testServer.Response(200, nil, AssociateRouteTableExample) + + resp, err := s.ec2.AssociateRouteTable("rtb-e4ad488d", "subnet-15ad487c") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"AssociateRouteTable"}) + c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-e4ad488d"}) + c.Assert(req.Form["SubnetId"], DeepEquals, []string{"subnet-15ad487c"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.AssociationId, Equals, "rtbassoc-f8ad4891") +} + +func (s *S) TestDisassociateRouteTable(c *C) { + testServer.Response(200, nil, DisassociateRouteTableExample) + + resp, err := s.ec2.DisassociateRouteTable("rtbassoc-f8ad4891") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DisassociateRouteTable"}) + c.Assert(req.Form["AssociationId"], DeepEquals, []string{"rtbassoc-f8ad4891"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Return, Equals, true) +} + +func (s *S) TestReplaceRouteTableAssociation(c *C) { + testServer.Response(200, nil, ReplaceRouteTableAssociationExample) + + resp, err := s.ec2.ReplaceRouteTableAssociation("rtbassoc-f8ad4891", "rtb-f9ad4890") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"ReplaceRouteTableAssociation"}) + c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-f9ad4890"}) + c.Assert(req.Form["AssociationId"], DeepEquals, []string{"rtbassoc-f8ad4891"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-88ed-be587EXAMPLE") + c.Assert(resp.NewAssociationId, Equals, "rtbassoc-faad2958") +} + +func (s *S) TestDeleteRouteTable(c *C) { + testServer.Response(200, nil, DeleteRouteTableExample) + + resp, err := s.ec2.DeleteRouteTable("rtb-f9ad4890") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteRouteTable"}) + c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-f9ad4890"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "49dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Return, Equals, true) +} + +func (s *S) TestDescribeVpcs(c *C) { + testServer.Response(200, nil, DescribeVpcsExample) + + filter := ec2.NewFilter() + filter.Add("key1", "value1") + filter.Add("key2", "value2", "value3") + + resp, err := s.ec2.DescribeVpcs([]string{"id1", "id2"}, filter) + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeVpcs"}) + c.Assert(req.Form["VpcId.1"], DeepEquals, []string{"id1"}) + c.Assert(req.Form["VpcId.2"], DeepEquals, []string{"id2"}) + c.Assert(req.Form["VpcId.3"], IsNil) + c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"key1"}) + c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"value1"}) + c.Assert(req.Form["Filter.1.Value.2"], IsNil) + c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"key2"}) + c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"value2"}) + c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"value3"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") + c.Assert(resp.VPCs[0].VpcId, Equals, "vpc-1a2b3c4d") + c.Assert(resp.VPCs, HasLen, 1) +} + +func (s *S) TestCreateVpc(c *C) { + testServer.Response(200, nil, CreateVpcExample) + + resp, err := s.ec2.CreateVpc("foo", "bar") + + req := testServer.WaitRequest() + c.Assert(req.Form["CidrBlock"], DeepEquals, []string{"foo"}) + c.Assert(req.Form["InstanceTenancy"], DeepEquals, []string{"bar"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") + c.Assert(resp.VPC.VpcId, Equals, "vpc-1a2b3c4d") + c.Assert(resp.VPC.State, Equals, "pending") + c.Assert(resp.VPC.CidrBlock, Equals, "10.0.0.0/16") + c.Assert(resp.VPC.DHCPOptionsID, Equals, "dopt-1a2b3c4d2") + c.Assert(resp.VPC.InstanceTenancy, Equals, "default") +} + +func (s *S) TestDeleteVpc(c *C) { + testServer.Response(200, nil, DeleteVpcExample) + + resp, err := s.ec2.DeleteVpc("id1") + + req := testServer.WaitRequest() + c.Assert(req.Form["VpcId"], DeepEquals, []string{"id1"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestCreateRoute(c *C) { + testServer.Response(200, nil, CreateRouteExample) + + options := ec2.CreateRoute{ + DestinationCidrBlock: "12.34.56.78/90", + GatewayId: "foo", + InstanceId: "i-bar", + NetworkInterfaceId: "foobar", + VpcPeeringConnectionId: "barfoo", + } + + resp, err := s.ec2.CreateRoute("rtb-deadbeef", &options) + + req := testServer.WaitRequest() + c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-deadbeef"}) + c.Assert(req.Form["DestinationCidrBlock"], DeepEquals, []string{"12.34.56.78/90"}) + c.Assert(req.Form["GatewayId"], DeepEquals, []string{"foo"}) + c.Assert(req.Form["InstanceId"], DeepEquals, []string{"i-bar"}) + c.Assert(req.Form["NetworkInterfaceId"], DeepEquals, []string{"foobar"}) + c.Assert(req.Form["VpcPeeringConnectionId"], DeepEquals, []string{"barfoo"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "b4998629-3000-437f-b382-cc96fEXAMPLE") + c.Assert(resp.Return, Equals, true) +} + +func (s *S) TestDeleteRoute(c *C) { + testServer.Response(200, nil, DeleteRouteExample) + + resp, err := s.ec2.DeleteRoute("rtb-baddcafe", "foobar") + + req := testServer.WaitRequest() + c.Assert(req.Form["RouteTableId"], DeepEquals, []string{"rtb-baddcafe"}) + c.Assert(req.Form["DestinationCidrBlock"], DeepEquals, []string{"foobar"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") + c.Assert(resp.Return, Equals, true) +} diff --git a/vendor/github.com/goamz/goamz/ecs/ecs.go b/vendor/github.com/goamz/goamz/ecs/ecs.go new file mode 100644 index 000000000..89c7243d4 --- /dev/null +++ b/vendor/github.com/goamz/goamz/ecs/ecs.go @@ -0,0 +1,1075 @@ +// +// ecs: This package provides types and functions to interact with the AWS EC2 Container Service API +// +// Depends on https://github.com/goamz/goamz +// +// Author Boyan Dimitrov +// + +package ecs + +import ( + "encoding/xml" + "fmt" + "log" + "net/http" + "net/http/httputil" + "net/url" + "strconv" + "strings" + "time" + + "github.com/goamz/goamz/aws" +) + +const debug = false + +var timeNow = time.Now + +// ECS contains the details of the AWS region to perform operations against. +type ECS struct { + aws.Auth + aws.Region +} + +// New creates a new ECS Client. +func New(auth aws.Auth, region aws.Region) *ECS { + return &ECS{auth, region} +} + +// ---------------------------------------------------------------------------- +// Request dispatching logic. + +// Error encapsulates an error returned by the AWS ECS API. +// +// See http://goo.gl/VZGuC for more details. +type Error struct { + // HTTP status code (200, 403, ...) + StatusCode int + // ECS error code ("UnsupportedOperation", ...) + Code string + // The error type + Type string + // The human-oriented error message + Message string + RequestId string `xml:"RequestID"` +} + +func (err *Error) Error() string { + if err.Code == "" { + return err.Message + } + + return fmt.Sprintf("%s (%s)", err.Message, err.Code) +} + +type xmlErrors struct { + RequestId string `xml:"RequestId"` + Errors []Error `xml:"Error"` +} + +func (e *ECS) query(params map[string]string, resp interface{}) error { + params["Version"] = "2014-11-13" + data := strings.NewReader(multimap(params).Encode()) + + hreq, err := http.NewRequest("POST", e.Region.ECSEndpoint+"/", data) + if err != nil { + return err + } + + hreq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + + token := e.Auth.Token() + if token != "" { + hreq.Header.Set("X-Amz-Security-Token", token) + } + + signer := aws.NewV4Signer(e.Auth, "ecs", e.Region) + signer.Sign(hreq) + + if debug { + log.Printf("%v -> {\n", hreq) + } + r, err := http.DefaultClient.Do(hreq) + + if err != nil { + log.Printf("Error calling Amazon %v", err) + return err + } + + defer r.Body.Close() + + if debug { + dump, _ := httputil.DumpResponse(r, true) + log.Printf("response:\n") + log.Printf("%v\n}\n", string(dump)) + } + if r.StatusCode != 200 { + return buildError(r) + } + err = xml.NewDecoder(r.Body).Decode(resp) + return err +} + +func buildError(r *http.Response) error { + var ( + err Error + errors xmlErrors + ) + xml.NewDecoder(r.Body).Decode(&errors) + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } + + err.RequestId = errors.RequestId + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} + +func makeParams(action string) map[string]string { + params := make(map[string]string) + params["Action"] = action + return params +} + +func addParamsList(params map[string]string, label string, ids []string) { + for i, id := range ids { + params[label+"."+strconv.Itoa(i+1)] = id + } +} + +// ---------------------------------------------------------------------------- +// ECS types and related functions. + +// SimpleResp is the beic response from most actions. +type SimpleResp struct { + XMLName xml.Name + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// Cluster encapsulates the cluster datatype +// +// See +type Cluster struct { + ClusterArn string `xml:"clusterArn"` + ClusterName string `xml:"clusterName"` + Status string `xml:"status"` +} + +// CreateClusterReq encapsulates the createcluster req params +type CreateClusterReq struct { + ClusterName string +} + +// CreateClusterResp encapsulates the createcluster response +type CreateClusterResp struct { + Cluster Cluster `xml:"CreateClusterResult>cluster"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// CreateCluster creates a new Amazon ECS cluster. By default, your account +// will receive a default cluster when you launch your first container instance +func (e *ECS) CreateCluster(req *CreateClusterReq) (resp *CreateClusterResp, err error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("CreateCluster") + params["clusterName"] = req.ClusterName + + resp = new(CreateClusterResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Resource describes the resources available for a container instance. +type Resource struct { + DoubleValue float64 `xml:"doubleValue"` + IntegerValue int32 `xml:"integerValue"` + LongValue int64 `xml:"longValue"` + Name string `xml:"name"` + StringSetValue []string `xml:"stringSetValue>member"` + Type string `xml:"type"` +} + +// ContainerInstance represents n Amazon EC2 instance that is running +// the Amazon ECS agent and has been registered with a cluster +type ContainerInstance struct { + AgentConnected bool `xml:"agentConnected"` + ContainerInstanceArn string `xml:"containerInstanceArn"` + Ec2InstanceId string `xml:"ec2InstanceId"` + RegisteredResources []Resource `xml:"registeredResources>member"` + RemainingResources []Resource `xml:"remainingResources>member"` + Status string `xml:"status"` +} + +// DeregisterContainerInstanceReq encapsulates DeregisterContainerInstance request params +type DeregisterContainerInstanceReq struct { + Cluster string + // arn:aws:ecs:region:aws_account_id:container-instance/container_instance_UUID. + ContainerInstance string + Force bool +} + +// DeregisterContainerInstanceResp encapsulates DeregisterContainerInstance response +type DeregisterContainerInstanceResp struct { + ContainerInstance ContainerInstance `xml:"DeregisterContainerInstanceResult>containerInstance"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DeregisterContainerInstance deregisters an Amazon ECS container instance from the specified cluster +func (e *ECS) DeregisterContainerInstance(req *DeregisterContainerInstanceReq) ( + resp *DeregisterContainerInstanceResp, err error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("DeregisterContainerInstance") + params["containerInstance"] = req.ContainerInstance + params["force"] = strconv.FormatBool(req.Force) + + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + + resp = new(DeregisterContainerInstanceResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// PortMapping encapsulates the PortMapping data type +type PortMapping struct { + ContainerPort int32 `xml:containerPort` + HostPort int32 `xml:hostPort` +} + +// KeyValuePair encapsulates the KeyValuePair data type +type KeyValuePair struct { + Name string `xml:"name"` + Value string `xml:"value"` +} + +// MountPoint encapsulates the MountPoint data type +type MountPoint struct { + ContainerPath string `xml:"containerPath"` + ReadOnly bool `xml:"readOnly"` + SourceVolume string `xml:"sourceVolume"` +} + +// VolumeFrom encapsulates the VolumeFrom data type +type VolumeFrom struct { + ReadOnly bool `xml:"readOnly"` + SourceContainer string `xml:"sourceContainer"` +} + +// HostVolumeProperties encapsulates the HostVolumeProperties data type +type HostVolumeProperties struct { + SourcePath string `xml:"sourcePath"` +} + +// Volume encapsulates the Volume data type +type Volume struct { + Host HostVolumeProperties `xml:"host"` + Name string `xml:"name"` +} + +// ContainerDefinition encapsulates the container definition type +// Container definitions are used in task definitions to describe +// the different containers that are launched as part of a task +type ContainerDefinition struct { + Command []string `xml:"command>member"` + Cpu int32 `xml:"cpu"` + EntryPoint []string `xml:"entryPoint>member"` + Environment []KeyValuePair `xml:"environment>member"` + Essential bool `xml:"essential"` + Image string `xml:"image"` + Links []string `xml:"links>member"` + Memory int32 `xml:"memory"` + MountPoints []MountPoint `xml:"mountPoints>member"` + Name string `xml:"name"` + PortMappings []PortMapping `xml:"portMappings>member"` + VolumesFrom []VolumeFrom `xml:"volumesFrom>member"` +} + +// TaskDefinition encapsulates the task definition type +type TaskDefinition struct { + ContainerDefinitions []ContainerDefinition `xml:"containerDefinitions>member"` + Family string `xml:"family"` + Revision int32 `xml:"revision"` + TaskDefinitionArn string `xml:"taskDefinitionArn"` + Status string `xml:"status"` + Volumes []Volume `xml:"volumes>member"` +} + +// DeregisterTaskDefinitionReq encapsulates DeregisterTaskDefinition req params +type DeregisterTaskDefinitionReq struct { + TaskDefinition string +} + +// DeregisterTaskDefinitionResp encapsuates the DeregisterTaskDefinition response +type DeregisterTaskDefinitionResp struct { + TaskDefinition TaskDefinition `xml:"DeregisterTaskDefinitionResult>taskDefinition"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DeregisterTaskDefinition deregisters the specified task definition +func (e *ECS) DeregisterTaskDefinition(req *DeregisterTaskDefinitionReq) ( + *DeregisterTaskDefinitionResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("DeregisterTaskDefinition") + params["taskDefinition"] = req.TaskDefinition + + resp := new(DeregisterTaskDefinitionResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Failure encapsulates the failure type +type Failure struct { + Arn string `xml:"arn"` + Reason string `xml:"reason"` +} + +// DescribeClustersReq encapsulates DescribeClusters req params +type DescribeClustersReq struct { + Clusters []string +} + +// DescribeClustersResp encapsuates the DescribeClusters response +type DescribeClustersResp struct { + Clusters []Cluster `xml:"DescribeClustersResult>clusters>member"` + Failures []Failure `xml:"DescribeClustersResult>failures>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeClusters describes one or more of your clusters +func (e *ECS) DescribeClusters(req *DescribeClustersReq) (*DescribeClustersResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("DescribeClusters") + if len(req.Clusters) > 0 { + addParamsList(params, "clusters.member", req.Clusters) + } + + resp := new(DescribeClustersResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DescribeContainerInstancesReq ecapsulates DescribeContainerInstances req params +type DescribeContainerInstancesReq struct { + Cluster string + ContainerInstances []string +} + +// DescribeContainerInstancesResp ecapsulates DescribeContainerInstances response +type DescribeContainerInstancesResp struct { + ContainerInstances []ContainerInstance `xml:"DescribeContainerInstancesResult>containerInstances>member"` + Failures []Failure `xml:"DescribeContainerInstancesResult>failures>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeContainerInstances describes Amazon EC2 Container Service container instances +// Returns metadata about registered and remaining resources on each container instance requested +func (e *ECS) DescribeContainerInstances(req *DescribeContainerInstancesReq) ( + *DescribeContainerInstancesResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("DescribeContainerInstances") + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + if len(req.ContainerInstances) > 0 { + addParamsList(params, "containerInstances.member", req.ContainerInstances) + } + + resp := new(DescribeContainerInstancesResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DescribeTaskDefinitionReq encapsulates DescribeTaskDefinition req params +type DescribeTaskDefinitionReq struct { + TaskDefinition string +} + +// DescribeTaskDefinitionResp encapsuates the DescribeTaskDefinition response +type DescribeTaskDefinitionResp struct { + TaskDefinition TaskDefinition `xml:"DescribeTaskDefinitionResult>taskDefinition"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeTaskDefinition describes a task definition +func (e *ECS) DescribeTaskDefinition(req *DescribeTaskDefinitionReq) ( + *DescribeTaskDefinitionResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("DescribeTaskDefinition") + params["taskDefinition"] = req.TaskDefinition + + resp := new(DescribeTaskDefinitionResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// NetworkBinding encapsulates the network binding data type +type NetworkBinding struct { + BindIp string `xml:"bindIp"` + ContainerPort int32 `xml:"containerPort"` + HostPort int32 `xml:"hostPort"` +} + +// Container encapsulates the container data type +type Container struct { + ContainerArn string `xml:"containerArn"` + ExitCode int32 `xml:"exitCode"` + LastStatus string `xml:"lastStatus"` + Name string `xml:"name"` + NetworkBindings []NetworkBinding `xml:"networkBindings>member"` + Reason string `xml:"reason"` + TaskArn string `xml:"taskArn"` +} + +// ContainerOverride encapsulates the container override data type +type ContainerOverride struct { + Command []string `xml:"command>member"` + Environment []KeyValuePair `xml:"environment>member"` + Name string `xml:"name"` +} + +// TaskOverride encapsulates the task override data type +type TaskOverride struct { + ContainerOverrides []ContainerOverride `xml:"containerOverrides>member"` +} + +// Task encapsulates the task data type +type Task struct { + ClusterArn string `xml:"clusterArn"` + ContainerInstanceArn string `xml:"containerInstanceArn"` + Containers []Container `xml:"containers>member"` + DesiredStatus string `xml:"desiredStatus"` + LastStatus string `xml:"lastStatus"` + Overrides TaskOverride `xml:"overrides"` + TaskArn string `xml:"taskArn"` + TaskDefinitionArn string `xml:"taskDefinitionArn"` +} + +// DescribeTasksReq encapsulates DescribeTasks req params +type DescribeTasksReq struct { + Cluster string + Tasks []string +} + +// DescribeTasksResp encapsuates the DescribeTasks response +type DescribeTasksResp struct { + Tasks []Task `xml:"DescribeTasksResult>tasks>member"` + Failures []Failure `xml:"DescribeTasksResult>failures>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeTasks describes a task definition +func (e *ECS) DescribeTasks(req *DescribeTasksReq) (*DescribeTasksResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("DescribeTasks") + if len(req.Tasks) > 0 { + addParamsList(params, "tasks.member", req.Tasks) + } + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + + resp := new(DescribeTasksResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DiscoverPollEndpointReq encapsulates DiscoverPollEndpoint req params +type DiscoverPollEndpointReq struct { + ContainerInstance string +} + +// DiscoverPollEndpointResp encapsuates the DiscoverPollEndpoint response +type DiscoverPollEndpointResp struct { + Endpoint string `xml:"DiscoverPollEndpointResult>endpoint"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DiscoverPollEndpoint returns an endpoint for the Amazon EC2 Container Service agent +// to poll for updates +func (e *ECS) DiscoverPollEndpoint(req *DiscoverPollEndpointReq) ( + *DiscoverPollEndpointResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("DiscoverPollEndpoint") + if req.ContainerInstance != "" { + params["containerInstance"] = req.ContainerInstance + } + + resp := new(DiscoverPollEndpointResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ListClustersReq encapsulates ListClusters req params +type ListClustersReq struct { + MaxResults int32 + NextToken string +} + +// ListClustersResp encapsuates the ListClusters response +type ListClustersResp struct { + ClusterArns []string `xml:"ListClustersResult>clusterArns>member"` + NextToken string `xml:"ListClustersResult>nextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ListClusters returns a list of existing clusters +func (e *ECS) ListClusters(req *ListClustersReq) ( + *ListClustersResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("ListClusters") + if req.MaxResults > 0 { + params["maxResults"] = strconv.Itoa(int(req.MaxResults)) + } + if req.NextToken != "" { + params["nextToken"] = req.NextToken + } + + resp := new(ListClustersResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ListContainerInstancesReq encapsulates ListContainerInstances req params +type ListContainerInstancesReq struct { + Cluster string + MaxResults int32 + NextToken string +} + +// ListContainerInstancesResp encapsuates the ListContainerInstances response +type ListContainerInstancesResp struct { + ContainerInstanceArns []string `xml:"ListContainerInstancesResult>containerInstanceArns>member"` + NextToken string `xml:"ListContainerInstancesResult>nextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ListContainerInstances returns a list of container instances in a specified cluster. +func (e *ECS) ListContainerInstances(req *ListContainerInstancesReq) ( + *ListContainerInstancesResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("ListContainerInstances") + if req.MaxResults > 0 { + params["maxResults"] = strconv.Itoa(int(req.MaxResults)) + } + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + if req.NextToken != "" { + params["nextToken"] = req.NextToken + } + + resp := new(ListContainerInstancesResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ListTaskDefinitionsReq encapsulates ListTaskDefinitions req params +type ListTaskDefinitionsReq struct { + FamilyPrefix string + MaxResults int32 + NextToken string +} + +// ListTaskDefinitionsResp encapsuates the ListTaskDefinitions response +type ListTaskDefinitionsResp struct { + TaskDefinitionArns []string `xml:"ListTaskDefinitionsResult>taskDefinitionArns>member"` + NextToken string `xml:"ListTaskDefinitionsResult>nextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ListTaskDefinitions Returns a list of task definitions that are registered to your account. +func (e *ECS) ListTaskDefinitions(req *ListTaskDefinitionsReq) ( + *ListTaskDefinitionsResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("ListTaskDefinitions") + if req.MaxResults > 0 { + params["maxResults"] = strconv.Itoa(int(req.MaxResults)) + } + if req.FamilyPrefix != "" { + params["familyPrefix"] = req.FamilyPrefix + } + if req.NextToken != "" { + params["nextToken"] = req.NextToken + } + + resp := new(ListTaskDefinitionsResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ListTasksReq encapsulates ListTasks req params +type ListTasksReq struct { + Cluster string + ContainerInstance string + Family string + MaxResults int32 + NextToken string +} + +// ListTasksResp encapsuates the ListTasks response +type ListTasksResp struct { + TaskArns []string `xml:"ListTasksResult>taskArns>member"` + NextToken string `xml:"ListTasksResult>nextToken"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ListTasks Returns a list of tasks for a specified cluster. +// You can filter the results by family name or by a particular container instance +// with the family and containerInstance parameters. +func (e *ECS) ListTasks(req *ListTasksReq) ( + *ListTasksResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("ListTasks") + if req.MaxResults > 0 { + params["maxResults"] = strconv.Itoa(int(req.MaxResults)) + } + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + if req.ContainerInstance != "" { + params["containerInstance"] = req.ContainerInstance + } + if req.Family != "" { + params["family"] = req.Family + } + if req.NextToken != "" { + params["nextToken"] = req.NextToken + } + + resp := new(ListTasksResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// RegisterContainerInstanceReq encapsulates RegisterContainerInstance req params +type RegisterContainerInstanceReq struct { + Cluster string + InstanceIdentityDocument string + InstanceIdentityDocumentSignature string + TotalResources []Resource +} + +// DeregisterContainerInstanceResp encapsulates RegisterContainerInstance response +type RegisterContainerInstanceResp struct { + ContainerInstance ContainerInstance `xml:"RegisterContainerInstanceResult>containerInstance"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// RegisterContainerInstance registers an Amazon EC2 instance into the specified cluster. +// This instance will become available to place containers on. +func (e *ECS) RegisterContainerInstance(req *RegisterContainerInstanceReq) ( + resp *RegisterContainerInstanceResp, err error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("RegisterContainerInstance") + if req.InstanceIdentityDocument != "" { + params["instanceIdentityDocument"] = req.InstanceIdentityDocument + } + if req.InstanceIdentityDocumentSignature != "" { + params["instanceIdentityDocumentSignature"] = req.InstanceIdentityDocumentSignature + } + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + // Marshal Resources + for i, r := range req.TotalResources { + key := fmt.Sprintf("totalResources.member.%d", i+1) + params[fmt.Sprintf("%s.doubleValue", key)] = strconv.FormatFloat(r.DoubleValue, 'f', 1, 64) + params[fmt.Sprintf("%s.integerValue", key)] = strconv.Itoa(int(r.IntegerValue)) + params[fmt.Sprintf("%s.longValue", key)] = strconv.Itoa(int(r.LongValue)) + params[fmt.Sprintf("%s.name", key)] = r.Name + params[fmt.Sprintf("%s.type", key)] = r.Type + for k, sv := range r.StringSetValue { + params[fmt.Sprintf("%s.stringSetValue.member.%d", key, k+1)] = sv + } + } + + resp = new(RegisterContainerInstanceResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// RegisterTaskDefinitionReq encapsulates RegisterTaskDefinition req params +type RegisterTaskDefinitionReq struct { + Family string + ContainerDefinitions []ContainerDefinition + Volumes []Volume +} + +// RegisterTaskDefinitionResp encapsulates RegisterTaskDefinition response +type RegisterTaskDefinitionResp struct { + TaskDefinition TaskDefinition `xml:"RegisterTaskDefinitionResult>taskDefinition"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// RegisterTaskDefinition registers a new task definition from the supplied family and containerDefinitions. +func (e *ECS) RegisterTaskDefinition(req *RegisterTaskDefinitionReq) ( + resp *RegisterTaskDefinitionResp, err error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + params := makeParams("RegisterTaskDefinition") + if req.Family != "" { + params["family"] = req.Family + } + + // Marshal Container Definitions + for i, c := range req.ContainerDefinitions { + key := fmt.Sprintf("containerDefinitions.member.%d", i+1) + params[fmt.Sprintf("%s.cpu", key)] = strconv.Itoa(int(c.Cpu)) + params[fmt.Sprintf("%s.essential", key)] = strconv.FormatBool(c.Essential) + params[fmt.Sprintf("%s.image", key)] = c.Image + params[fmt.Sprintf("%s.memory", key)] = strconv.Itoa(int(c.Memory)) + params[fmt.Sprintf("%s.name", key)] = c.Name + + for k, cmd := range c.Command { + params[fmt.Sprintf("%s.command.member.%d", key, k+1)] = cmd + } + for k, ep := range c.EntryPoint { + params[fmt.Sprintf("%s.entryPoint.member.%d", key, k+1)] = ep + } + for k, env := range c.Environment { + params[fmt.Sprintf("%s.environment.member.%d.name", key, k+1)] = env.Name + params[fmt.Sprintf("%s.environment.member.%d.value", key, k+1)] = env.Value + } + for k, l := range c.Links { + params[fmt.Sprintf("%s.links.member.%d", key, k+1)] = l + } + for k, p := range c.PortMappings { + params[fmt.Sprintf("%s.portMappings.member.%d.containerPort", key, k+1)] = strconv.Itoa(int(p.ContainerPort)) + params[fmt.Sprintf("%s.portMappings.member.%d.hostPort", key, k+1)] = strconv.Itoa(int(p.HostPort)) + } + for k, m := range c.MountPoints { + params[fmt.Sprintf("%s.mountPoints.member.%d.containerPath", key, k+1)] = m.ContainerPath + params[fmt.Sprintf("%s.mountPoints.member.%d.readOnly", key, k+1)] = strconv.FormatBool(m.ReadOnly) + params[fmt.Sprintf("%s.mountPoints.member.%d.sourceVolume", key, k+1)] = m.SourceVolume + } + for k, v := range c.VolumesFrom { + params[fmt.Sprintf("%s.volumesFrom.member.%d.readOnly", key, k+1)] = strconv.FormatBool(v.ReadOnly) + params[fmt.Sprintf("%s.volumesFrom.member.%d.sourceContainer", key, k+1)] = v.SourceContainer + } + } + + for k, v := range req.Volumes { + params[fmt.Sprintf("volumes.member.%d.name", k+1)] = v.Name + params[fmt.Sprintf("volumes.member.%d.host.sourcePath", k+1)] = v.Host.SourcePath + } + + resp = new(RegisterTaskDefinitionResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// RunTaskReq encapsulates RunTask req params +type RunTaskReq struct { + Cluster string + Count int32 + Overrides TaskOverride + TaskDefinition string +} + +// RunTaskResp encapsuates the RunTask response +type RunTaskResp struct { + Tasks []Task `xml:"RunTaskResult>tasks>member"` + Failures []Failure `xml:"RunTaskResult>failures>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// RunTask Start a task using random placement and the default Amazon ECS scheduler. +// If you want to use your own scheduler or place a task on a specific container instance, +// use StartTask instead. +func (e *ECS) RunTask(req *RunTaskReq) (*RunTaskResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("RunTask") + if req.Count > 0 { + params["count"] = strconv.Itoa(int(req.Count)) + } + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + if req.TaskDefinition != "" { + params["taskDefinition"] = req.TaskDefinition + } + + for i, co := range req.Overrides.ContainerOverrides { + key := fmt.Sprintf("overrides.containerOverrides.member.%d", i+1) + params[fmt.Sprintf("%s.name", key)] = co.Name + for k, cmd := range co.Command { + params[fmt.Sprintf("%s.command.member.%d", key, k+1)] = cmd + } + for k, env := range co.Environment { + params[fmt.Sprintf("%s.environment.member.%d.name", key, k+1)] = env.Name + params[fmt.Sprintf("%s.environment.member.%d.value", key, k+1)] = env.Value + } + } + + resp := new(RunTaskResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// StartTaskReq encapsulates StartTask req params +type StartTaskReq struct { + Cluster string + ContainerInstances []string + Overrides TaskOverride + TaskDefinition string +} + +// StartTaskResp encapsuates the StartTask response +type StartTaskResp struct { + Tasks []Task `xml:"StartTaskResult>tasks>member"` + Failures []Failure `xml:"StartTaskResult>failures>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// StartTask Starts a new task from the specified task definition on the specified +// container instance or instances. If you want to use the default Amazon ECS scheduler +// to place your task, use RunTask instead. +func (e *ECS) StartTask(req *StartTaskReq) (*StartTaskResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("StartTask") + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + if req.TaskDefinition != "" { + params["taskDefinition"] = req.TaskDefinition + } + for i, ci := range req.ContainerInstances { + params[fmt.Sprintf("containerInstances.member.%d", i+1)] = ci + } + for i, co := range req.Overrides.ContainerOverrides { + key := fmt.Sprintf("overrides.containerOverrides.member.%d", i+1) + params[fmt.Sprintf("%s.name", key)] = co.Name + for k, cmd := range co.Command { + params[fmt.Sprintf("%s.command.member.%d", key, k+1)] = cmd + } + } + + resp := new(StartTaskResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// StopTaskReq encapsulates StopTask req params +type StopTaskReq struct { + Cluster string + Task string +} + +// StopTaskResp encapsuates the StopTask response +type StopTaskResp struct { + Task Task `xml:"StopTaskResult>task"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// StopTask stops a running task +func (e *ECS) StopTask(req *StopTaskReq) (*StopTaskResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("StopTask") + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + if req.Task != "" { + params["task"] = req.Task + } + + resp := new(StopTaskResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// SubmitContainerStateChangeReq encapsulates SubmitContainerStateChange req params +type SubmitContainerStateChangeReq struct { + Cluster string + ContainerName string + ExitCode int32 + NetworkBindings []NetworkBinding + Reason string + Status string + Task string +} + +// SubmitContainerStateChangeResp encapsuates the SubmitContainerStateChange response +type SubmitContainerStateChangeResp struct { + Acknowledgment string `xml:"SubmitContainerStateChangeResult>acknowledgment"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// SubmitContainerStateChange is used to acknowledge that a container changed states. +// Note: This action is only used by the Amazon EC2 Container Service agent, +// and it is not intended for use outside of the agent. +func (e *ECS) SubmitContainerStateChange(req *SubmitContainerStateChangeReq) ( + *SubmitContainerStateChangeResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("SubmitContainerStateChange") + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + if req.ContainerName != "" { + params["containerName"] = req.ContainerName + } + if req.Reason != "" { + params["reason"] = req.Reason + } + if req.Status != "" { + params["status"] = req.Status + } + if req.Task != "" { + params["task"] = req.Task + } + for i, nb := range req.NetworkBindings { + key := fmt.Sprintf("networkBindings.member.%d", i+1) + params[fmt.Sprintf("%s.bindIp", key)] = nb.BindIp + params[fmt.Sprintf("%s.containerPort", key)] = strconv.Itoa(int(nb.ContainerPort)) + params[fmt.Sprintf("%s.hostPort", key)] = strconv.Itoa(int(nb.HostPort)) + } + params["exitCode"] = strconv.Itoa(int(req.ExitCode)) + + resp := new(SubmitContainerStateChangeResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// SubmitTaskStateChangeReq encapsulates SubmitTaskStateChange req params +type SubmitTaskStateChangeReq struct { + Cluster string + Reason string + Status string + Task string +} + +// SubmitTaskStateChangeResp encapsuates the SubmitTaskStateChange response +type SubmitTaskStateChangeResp struct { + Acknowledgment string `xml:"SubmitTaskStateChangeResult>acknowledgment"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// SubmitTaskStateChange is used to acknowledge that a task changed states. +// Note: This action is only used by the Amazon EC2 Container Service agent, +// and it is not intended for use outside of the agent. +func (e *ECS) SubmitTaskStateChange(req *SubmitTaskStateChangeReq) ( + *SubmitTaskStateChangeResp, error) { + if req == nil { + return nil, fmt.Errorf("The req params cannot be nil") + } + + params := makeParams("SubmitTaskStateChange") + if req.Cluster != "" { + params["cluster"] = req.Cluster + } + + if req.Reason != "" { + params["reason"] = req.Reason + } + if req.Status != "" { + params["status"] = req.Status + } + if req.Task != "" { + params["task"] = req.Task + } + + resp := new(SubmitTaskStateChangeResp) + if err := e.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/github.com/goamz/goamz/ecs/ecs_test.go b/vendor/github.com/goamz/goamz/ecs/ecs_test.go new file mode 100644 index 000000000..7fe4a74ae --- /dev/null +++ b/vendor/github.com/goamz/goamz/ecs/ecs_test.go @@ -0,0 +1,806 @@ +package ecs + +import ( + "testing" + + . "gopkg.in/check.v1" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/testutil" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + ecs *ECS +} + +var testServer = testutil.NewHTTPServer() + +var mockTest bool + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.ecs = New(auth, aws.Region{ECSEndpoint: testServer.URL}) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +// -------------------------------------------------------------------------- +// Detailed Unit Tests + +func (s *S) TestCreateCluster(c *C) { + testServer.Response(200, nil, CreateClusterResponse) + req := &CreateClusterReq{ + ClusterName: "default", + } + resp, err := s.ecs.CreateCluster(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "CreateCluster") + c.Assert(values.Get("clusterName"), Equals, "default") + + c.Assert(resp.Cluster.ClusterArn, Equals, "arn:aws:ecs:region:aws_account_id:cluster/default") + c.Assert(resp.Cluster.ClusterName, Equals, "default") + c.Assert(resp.Cluster.Status, Equals, "ACTIVE") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestDeregisterContainerInstance(c *C) { + testServer.Response(200, nil, DeregisterContainerInstanceResponse) + req := &DeregisterContainerInstanceReq{ + Cluster: "default", + ContainerInstance: "uuid", + Force: true, + } + resp, err := s.ecs.DeregisterContainerInstance(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "DeregisterContainerInstance") + c.Assert(values.Get("cluster"), Equals, "default") + c.Assert(values.Get("containerInstance"), Equals, "uuid") + c.Assert(values.Get("force"), Equals, "true") + + expectedResource := []Resource{ + { + DoubleValue: 0.0, + IntegerValue: 2048, + LongValue: 0, + Name: "CPU", + Type: "INTEGER", + }, + { + DoubleValue: 0.0, + IntegerValue: 3955, + LongValue: 0, + Name: "MEMORY", + Type: "INTEGER", + }, + { + DoubleValue: 0.0, + IntegerValue: 0, + LongValue: 0, + Name: "PORTS", + StringSetValue: []string{"2376", "22", "51678", "2375"}, + Type: "STRINGSET", + }, + } + + c.Assert(resp.ContainerInstance.AgentConnected, Equals, false) + c.Assert(resp.ContainerInstance.ContainerInstanceArn, Equals, "arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID") + c.Assert(resp.ContainerInstance.Status, Equals, "INACTIVE") + c.Assert(resp.ContainerInstance.Ec2InstanceId, Equals, "instance_id") + c.Assert(resp.ContainerInstance.RegisteredResources, DeepEquals, expectedResource) + c.Assert(resp.ContainerInstance.RemainingResources, DeepEquals, expectedResource) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestDeregisterTaskDefinition(c *C) { + testServer.Response(200, nil, DeregisterTaskDefinitionResponse) + req := &DeregisterTaskDefinitionReq{ + TaskDefinition: "sleep360:2", + } + resp, err := s.ecs.DeregisterTaskDefinition(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "DeregisterTaskDefinition") + c.Assert(values.Get("taskDefinition"), Equals, "sleep360:2") + + expected := TaskDefinition{ + Family: "sleep360", + Revision: 2, + TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2", + ContainerDefinitions: []ContainerDefinition{ + { + Command: []string{"sleep", "360"}, + Cpu: 10, + EntryPoint: []string{"/bin/sh"}, + Environment: []KeyValuePair{ + { + Name: "envVar", + Value: "foo", + }, + }, + Essential: true, + Image: "busybox", + Memory: 10, + Name: "sleep", + }, + }, + } + + c.Assert(resp.TaskDefinition, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestDescribeClusters(c *C) { + testServer.Response(200, nil, DescribeClustersResponse) + req := &DescribeClustersReq{ + Clusters: []string{"test", "default"}, + } + resp, err := s.ecs.DescribeClusters(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "DescribeClusters") + c.Assert(values.Get("clusters.member.1"), Equals, "test") + c.Assert(values.Get("clusters.member.2"), Equals, "default") + + expected := []Cluster{ + { + ClusterName: "test", + ClusterArn: "arn:aws:ecs:us-east-1:aws_account_id:cluster/test", + Status: "ACTIVE", + }, + { + ClusterName: "default", + ClusterArn: "arn:aws:ecs:us-east-1:aws_account_id:cluster/default", + Status: "ACTIVE", + }, + } + + c.Assert(resp.Clusters, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestDescribeContainerInstances(c *C) { + testServer.Response(200, nil, DescribeContainerInstancesResponse) + req := &DescribeContainerInstancesReq{ + Cluster: "test", + ContainerInstances: []string{"arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID"}, + } + resp, err := s.ecs.DescribeContainerInstances(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "DescribeContainerInstances") + c.Assert(values.Get("cluster"), Equals, "test") + c.Assert(values.Get("containerInstances.member.1"), + Equals, "arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID") + + expected := []ContainerInstance{ + ContainerInstance{ + AgentConnected: true, + ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID", + Status: "ACTIVE", + Ec2InstanceId: "instance_id", + RegisteredResources: []Resource{ + { + DoubleValue: 0.0, + IntegerValue: 2048, + LongValue: 0, + Name: "CPU", + Type: "INTEGER", + }, + { + DoubleValue: 0.0, + IntegerValue: 3955, + LongValue: 0, + Name: "MEMORY", + Type: "INTEGER", + }, + { + DoubleValue: 0.0, + IntegerValue: 0, + LongValue: 0, + Name: "PORTS", + StringSetValue: []string{"2376", "22", "51678", "2375"}, + Type: "STRINGSET", + }, + }, + RemainingResources: []Resource{ + { + DoubleValue: 0.0, + IntegerValue: 2048, + LongValue: 0, + Name: "CPU", + Type: "INTEGER", + }, + { + DoubleValue: 0.0, + IntegerValue: 3955, + LongValue: 0, + Name: "MEMORY", + Type: "INTEGER", + }, + { + DoubleValue: 0.0, + IntegerValue: 0, + LongValue: 0, + Name: "PORTS", + StringSetValue: []string{"2376", "22", "51678", "2375"}, + Type: "STRINGSET", + }, + }, + }, + } + + c.Assert(resp.ContainerInstances, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestDescribeTaskDefinition(c *C) { + testServer.Response(200, nil, DescribeTaskDefinitionResponse) + req := &DescribeTaskDefinitionReq{ + TaskDefinition: "sleep360:2", + } + resp, err := s.ecs.DescribeTaskDefinition(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "DescribeTaskDefinition") + c.Assert(values.Get("taskDefinition"), Equals, "sleep360:2") + + expected := TaskDefinition{ + Family: "sleep360", + Revision: 2, + TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2", + ContainerDefinitions: []ContainerDefinition{ + { + Command: []string{"sleep", "360"}, + Cpu: 10, + EntryPoint: []string{"/bin/sh"}, + Environment: []KeyValuePair{ + { + Name: "envVar", + Value: "foo", + }, + }, + Essential: true, + Image: "busybox", + Memory: 10, + Name: "sleep", + }, + }, + } + + c.Assert(resp.TaskDefinition, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestDescribeTasks(c *C) { + testServer.Response(200, nil, DescribeTasksResponse) + req := &DescribeTasksReq{ + Cluster: "test", + Tasks: []string{"arn:aws:ecs:us-east-1:aws_account_id:task/UUID"}, + } + resp, err := s.ecs.DescribeTasks(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "DescribeTasks") + c.Assert(values.Get("cluster"), Equals, "test") + c.Assert(values.Get("tasks.member.1"), + Equals, "arn:aws:ecs:us-east-1:aws_account_id:task/UUID") + + expected := []Task{ + Task{ + Containers: []Container{ + { + TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID", + Name: "sleep", + ContainerArn: "arn:aws:ecs:us-east-1:aws_account_id:container/UUID", + LastStatus: "RUNNING", + }, + }, + Overrides: TaskOverride{ + ContainerOverrides: []ContainerOverride{ + { + Name: "sleep", + }, + }, + }, + DesiredStatus: "RUNNING", + TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID", + ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID", + LastStatus: "RUNNING", + TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2", + }, + } + + c.Assert(resp.Tasks, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestDiscoverPollEndpoint(c *C) { + testServer.Response(200, nil, DiscoverPollEndpointResponse) + req := &DiscoverPollEndpointReq{ + ContainerInstance: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID", + } + resp, err := s.ecs.DiscoverPollEndpoint(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "DiscoverPollEndpoint") + c.Assert(values.Get("containerInstance"), + Equals, "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID") + + c.Assert(resp.Endpoint, Equals, "https://ecs-x-1.us-east-1.amazonaws.com/") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestListClusters(c *C) { + testServer.Response(200, nil, ListClustersResponse) + req := &ListClustersReq{ + MaxResults: 2, + NextToken: "Token_UUID", + } + resp, err := s.ecs.ListClusters(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "ListClusters") + c.Assert(values.Get("maxResults"), Equals, "2") + c.Assert(values.Get("nextToken"), Equals, "Token_UUID") + + c.Assert(resp.ClusterArns, DeepEquals, []string{"arn:aws:ecs:us-east-1:aws_account_id:cluster/default", + "arn:aws:ecs:us-east-1:aws_account_id:cluster/test"}) + c.Assert(resp.NextToken, Equals, "token_UUID") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestListContainerInstances(c *C) { + testServer.Response(200, nil, ListContainerInstancesResponse) + req := &ListContainerInstancesReq{ + MaxResults: 2, + NextToken: "Token_UUID", + Cluster: "test", + } + resp, err := s.ecs.ListContainerInstances(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "ListContainerInstances") + c.Assert(values.Get("maxResults"), Equals, "2") + c.Assert(values.Get("cluster"), Equals, "test") + c.Assert(values.Get("nextToken"), Equals, "Token_UUID") + + c.Assert(resp.ContainerInstanceArns, DeepEquals, []string{ + "arn:aws:ecs:us-east-1:aws_account_id:container-instance/uuid-1", + "arn:aws:ecs:us-east-1:aws_account_id:container-instance/uuid-2"}) + c.Assert(resp.NextToken, Equals, "token_UUID") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestListTaskDefinitions(c *C) { + testServer.Response(200, nil, ListTaskDefinitionsResponse) + req := &ListTaskDefinitionsReq{ + MaxResults: 2, + NextToken: "Token_UUID", + FamilyPrefix: "sleep360", + } + resp, err := s.ecs.ListTaskDefinitions(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "ListTaskDefinitions") + c.Assert(values.Get("maxResults"), Equals, "2") + c.Assert(values.Get("familyPrefix"), Equals, "sleep360") + c.Assert(values.Get("nextToken"), Equals, "Token_UUID") + + c.Assert(resp.TaskDefinitionArns, DeepEquals, []string{ + "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:1", + "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2"}) + c.Assert(resp.NextToken, Equals, "token_UUID") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestListTasks(c *C) { + testServer.Response(200, nil, ListTasksResponse) + req := &ListTasksReq{ + MaxResults: 2, + NextToken: "Token_UUID", + Family: "sleep360", + Cluster: "test", + ContainerInstance: "container_uuid", + } + resp, err := s.ecs.ListTasks(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "ListTasks") + c.Assert(values.Get("maxResults"), Equals, "2") + c.Assert(values.Get("family"), Equals, "sleep360") + c.Assert(values.Get("containerInstance"), Equals, "container_uuid") + c.Assert(values.Get("cluster"), Equals, "test") + c.Assert(values.Get("nextToken"), Equals, "Token_UUID") + + c.Assert(resp.TaskArns, DeepEquals, []string{ + "arn:aws:ecs:us-east-1:aws_account_id:task/uuid_1", + "arn:aws:ecs:us-east-1:aws_account_id:task/uuid_2"}) + c.Assert(resp.NextToken, Equals, "token_UUID") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestRegisterContainerInstance(c *C) { + testServer.Response(200, nil, RegisterContainerInstanceResponse) + + resources := []Resource{ + { + DoubleValue: 0.0, + IntegerValue: 2048, + LongValue: 0, + Name: "CPU", + Type: "INTEGER", + }, + { + DoubleValue: 0.0, + IntegerValue: 3955, + LongValue: 0, + Name: "MEMORY", + Type: "INTEGER", + }, + { + DoubleValue: 0.0, + IntegerValue: 0, + LongValue: 0, + Name: "PORTS", + StringSetValue: []string{"2376", "22", "51678", "2375"}, + Type: "STRINGSET", + }, + } + + req := &RegisterContainerInstanceReq{ + Cluster: "default", + InstanceIdentityDocument: "foo", + InstanceIdentityDocumentSignature: "baz", + TotalResources: resources, + } + + resp, err := s.ecs.RegisterContainerInstance(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "RegisterContainerInstance") + c.Assert(values.Get("cluster"), Equals, "default") + c.Assert(values.Get("instanceIdentityDocument"), Equals, "foo") + c.Assert(values.Get("instanceIdentityDocumentSignature"), Equals, "baz") + c.Assert(values.Get("totalResources.member.1.doubleValue"), Equals, "0.0") + c.Assert(values.Get("totalResources.member.1.integerValue"), Equals, "2048") + c.Assert(values.Get("totalResources.member.1.longValue"), Equals, "0") + c.Assert(values.Get("totalResources.member.1.name"), Equals, "CPU") + c.Assert(values.Get("totalResources.member.1.type"), Equals, "INTEGER") + c.Assert(values.Get("totalResources.member.2.doubleValue"), Equals, "0.0") + c.Assert(values.Get("totalResources.member.2.integerValue"), Equals, "3955") + c.Assert(values.Get("totalResources.member.2.longValue"), Equals, "0") + c.Assert(values.Get("totalResources.member.2.name"), Equals, "MEMORY") + c.Assert(values.Get("totalResources.member.2.type"), Equals, "INTEGER") + c.Assert(values.Get("totalResources.member.3.doubleValue"), Equals, "0.0") + c.Assert(values.Get("totalResources.member.3.integerValue"), Equals, "0") + c.Assert(values.Get("totalResources.member.3.longValue"), Equals, "0") + c.Assert(values.Get("totalResources.member.3.name"), Equals, "PORTS") + c.Assert(values.Get("totalResources.member.3.stringSetValue.member.1"), Equals, "2376") + c.Assert(values.Get("totalResources.member.3.stringSetValue.member.2"), Equals, "22") + c.Assert(values.Get("totalResources.member.3.stringSetValue.member.3"), Equals, "51678") + c.Assert(values.Get("totalResources.member.3.stringSetValue.member.4"), Equals, "2375") + c.Assert(values.Get("totalResources.member.3.type"), Equals, "STRINGSET") + + c.Assert(resp.ContainerInstance.AgentConnected, Equals, true) + c.Assert(resp.ContainerInstance.ContainerInstanceArn, Equals, "arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID") + c.Assert(resp.ContainerInstance.Status, Equals, "ACTIVE") + c.Assert(resp.ContainerInstance.Ec2InstanceId, Equals, "instance_id") + c.Assert(resp.ContainerInstance.RegisteredResources, DeepEquals, resources) + c.Assert(resp.ContainerInstance.RemainingResources, DeepEquals, resources) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestRegisterTaskDefinition(c *C) { + testServer.Response(200, nil, RegisterTaskDefinitionResponse) + + CDefinitions := []ContainerDefinition{ + { + Command: []string{"sleep", "360"}, + Cpu: 10, + EntryPoint: []string{"/bin/sh"}, + Environment: []KeyValuePair{ + { + Name: "envVar", + Value: "foo", + }, + }, + Essential: true, + Image: "busybox", + Memory: 10, + Name: "sleep", + MountPoints: []MountPoint{ + { + ContainerPath: "/tmp/myfile", + ReadOnly: false, + SourceVolume: "/srv/myfile", + }, + { + ContainerPath: "/tmp/myfile2", + ReadOnly: true, + SourceVolume: "/srv/myfile2", + }, + }, + VolumesFrom: []VolumeFrom{ + { + ReadOnly: true, + SourceContainer: "foo", + }, + }, + }, + } + + req := &RegisterTaskDefinitionReq{ + Family: "sleep360", + ContainerDefinitions: CDefinitions, + Volumes: []Volume{ + { + Name: "/srv/myfile", + Host: HostVolumeProperties{ + SourcePath: "/srv/myfile", + }, + }, + }, + } + resp, err := s.ecs.RegisterTaskDefinition(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "RegisterTaskDefinition") + c.Assert(values.Get("containerDefinitions.member.1.command.member.1"), Equals, "sleep") + c.Assert(values.Get("containerDefinitions.member.1.command.member.2"), Equals, "360") + c.Assert(values.Get("containerDefinitions.member.1.cpu"), Equals, "10") + c.Assert(values.Get("containerDefinitions.member.1.memory"), Equals, "10") + c.Assert(values.Get("containerDefinitions.member.1.entryPoint.member.1"), Equals, "/bin/sh") + c.Assert(values.Get("containerDefinitions.member.1.environment.member.1.name"), Equals, "envVar") + c.Assert(values.Get("containerDefinitions.member.1.environment.member.1.value"), Equals, "foo") + c.Assert(values.Get("containerDefinitions.member.1.essential"), Equals, "true") + c.Assert(values.Get("containerDefinitions.member.1.image"), Equals, "busybox") + c.Assert(values.Get("containerDefinitions.member.1.memory"), Equals, "10") + c.Assert(values.Get("containerDefinitions.member.1.name"), Equals, "sleep") + c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.1.containerPath"), Equals, "/tmp/myfile") + c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.1.readOnly"), Equals, "false") + c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.1.sourceVolume"), Equals, "/srv/myfile") + c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.2.containerPath"), Equals, "/tmp/myfile2") + c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.2.readOnly"), Equals, "true") + c.Assert(values.Get("containerDefinitions.member.1.mountPoints.member.2.sourceVolume"), Equals, "/srv/myfile2") + c.Assert(values.Get("containerDefinitions.member.1.volumesFrom.member.1.readOnly"), Equals, "true") + c.Assert(values.Get("containerDefinitions.member.1.volumesFrom.member.1.sourceContainer"), Equals, "foo") + + c.Assert(values.Get("family"), Equals, "sleep360") + c.Assert(values.Get("volumes.member.1.name"), Equals, "/srv/myfile") + c.Assert(values.Get("volumes.member.1.host.sourcePath"), Equals, "/srv/myfile") + + expected := TaskDefinition{ + Family: "sleep360", + Revision: 2, + TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2", + ContainerDefinitions: CDefinitions, + Volumes: []Volume{ + { + Name: "/srv/myfile", + Host: HostVolumeProperties{ + SourcePath: "/srv/myfile", + }, + }, + }, + } + + c.Assert(resp.TaskDefinition, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestRunTask(c *C) { + testServer.Response(200, nil, RunTaskResponse) + req := &RunTaskReq{ + Cluster: "test", + Count: 1, + TaskDefinition: "sleep360:2", + } + resp, err := s.ecs.RunTask(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "RunTask") + c.Assert(values.Get("cluster"), Equals, "test") + c.Assert(values.Get("count"), Equals, "1") + c.Assert(values.Get("taskDefinition"), Equals, "sleep360:2") + + expected := []Task{ + Task{ + Containers: []Container{ + { + TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID", + Name: "sleep", + ContainerArn: "arn:aws:ecs:us-east-1:aws_account_id:container/UUID", + LastStatus: "RUNNING", + }, + }, + Overrides: TaskOverride{ + ContainerOverrides: []ContainerOverride{ + { + Name: "sleep", + }, + }, + }, + DesiredStatus: "RUNNING", + TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID", + ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID", + LastStatus: "PENDING", + TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2", + }, + } + + c.Assert(resp.Tasks, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestStartTask(c *C) { + testServer.Response(200, nil, StartTaskResponse) + req := &StartTaskReq{ + Cluster: "test", + ContainerInstances: []string{"containerUUID"}, + TaskDefinition: "sleep360:2", + } + resp, err := s.ecs.StartTask(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "StartTask") + c.Assert(values.Get("cluster"), Equals, "test") + c.Assert(values.Get("taskDefinition"), Equals, "sleep360:2") + c.Assert(values.Get("containerInstances.member.1"), Equals, "containerUUID") + + expected := []Task{ + Task{ + Containers: []Container{ + { + TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID", + Name: "sleep", + ContainerArn: "arn:aws:ecs:us-east-1:aws_account_id:container/UUID", + LastStatus: "RUNNING", + }, + }, + Overrides: TaskOverride{ + ContainerOverrides: []ContainerOverride{ + { + Name: "sleep", + }, + }, + }, + DesiredStatus: "RUNNING", + TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID", + ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID", + LastStatus: "PENDING", + TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2", + }, + } + + c.Assert(resp.Tasks, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestStopTask(c *C) { + testServer.Response(200, nil, StopTaskResponse) + req := &StopTaskReq{ + Cluster: "test", + Task: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID", + } + resp, err := s.ecs.StopTask(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "StopTask") + c.Assert(values.Get("cluster"), Equals, "test") + c.Assert(values.Get("task"), Equals, "arn:aws:ecs:us-east-1:aws_account_id:task/UUID") + + expected := Task{ + Containers: []Container{ + { + TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID", + Name: "sleep", + ContainerArn: "arn:aws:ecs:us-east-1:aws_account_id:container/UUID", + LastStatus: "RUNNING", + }, + }, + Overrides: TaskOverride{ + ContainerOverrides: []ContainerOverride{ + { + Name: "sleep", + }, + }, + }, + DesiredStatus: "STOPPED", + TaskArn: "arn:aws:ecs:us-east-1:aws_account_id:task/UUID", + ContainerInstanceArn: "arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID", + LastStatus: "RUNNING", + TaskDefinitionArn: "arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2", + } + + c.Assert(resp.Task, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestSubmitContainerStateChange(c *C) { + testServer.Response(200, nil, SubmitContainerStateChangeResponse) + networkBindings := []NetworkBinding{ + { + BindIp: "127.0.0.1", + ContainerPort: 80, + HostPort: 80, + }, + } + req := &SubmitContainerStateChangeReq{ + Cluster: "test", + ContainerName: "container", + ExitCode: 0, + Reason: "reason", + Status: "status", + Task: "taskUUID", + NetworkBindings: networkBindings, + } + + resp, err := s.ecs.SubmitContainerStateChange(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "SubmitContainerStateChange") + c.Assert(values.Get("cluster"), Equals, "test") + c.Assert(values.Get("containerName"), Equals, "container") + c.Assert(values.Get("exitCode"), Equals, "0") + c.Assert(values.Get("reason"), Equals, "reason") + c.Assert(values.Get("status"), Equals, "status") + c.Assert(values.Get("task"), Equals, "taskUUID") + c.Assert(values.Get("networkBindings.member.1.bindIp"), Equals, "127.0.0.1") + c.Assert(values.Get("networkBindings.member.1.containerPort"), Equals, "80") + c.Assert(values.Get("networkBindings.member.1.hostPort"), Equals, "80") + + c.Assert(resp.Acknowledgment, Equals, "ACK") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} + +func (s *S) TestSubmitTaskStateChange(c *C) { + testServer.Response(200, nil, SubmitTaskStateChangeResponse) + req := &SubmitTaskStateChangeReq{ + Cluster: "test", + Reason: "reason", + Status: "status", + Task: "taskUUID", + } + + resp, err := s.ecs.SubmitTaskStateChange(req) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + c.Assert(values.Get("Version"), Equals, "2014-11-13") + c.Assert(values.Get("Action"), Equals, "SubmitTaskStateChange") + c.Assert(values.Get("cluster"), Equals, "test") + c.Assert(values.Get("reason"), Equals, "reason") + c.Assert(values.Get("status"), Equals, "status") + c.Assert(values.Get("task"), Equals, "taskUUID") + + c.Assert(resp.Acknowledgment, Equals, "ACK") + c.Assert(resp.RequestId, Equals, "8d798a29-f083-11e1-bdfb-cb223EXAMPLE") +} diff --git a/vendor/github.com/goamz/goamz/ecs/responses_test.go b/vendor/github.com/goamz/goamz/ecs/responses_test.go new file mode 100644 index 000000000..6a4b0ce71 --- /dev/null +++ b/vendor/github.com/goamz/goamz/ecs/responses_test.go @@ -0,0 +1,637 @@ +package ecs + +var CreateClusterResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + + + arn:aws:ecs:region:aws_account_id:cluster/default + default + ACTIVE + + + +` +var DeregisterContainerInstanceResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + + + False + arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID + instance_id + INACTIVE + + + 2048 + 0 + INTEGER + CPU + 0.0 + + + 3955 + 0 + INTEGER + MEMORY + 0.0 + + + 0 + 0 + STRINGSET + + 2376 + 22 + 51678 + 2375 + + PORTS + 0.0 + + + + + 2048 + 0 + INTEGER + CPU + 0.0 + + + 3955 + 0 + INTEGER + MEMORY + 0.0 + + + 0 + 0 + STRINGSET + + 2376 + 22 + 51678 + 2375 + + PORTS + 0.0 + + + + + +` + +var DeregisterTaskDefinitionResponse = ` + + + + 2 + sleep360 + + + + true + + + envVar + foo + + + + /bin/sh + + sleep + + sleep + 360 + + 10 + busybox + 10 + + + arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2 + + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var DescribeClustersResponse = ` + + + + + + test + arn:aws:ecs:us-east-1:aws_account_id:cluster/test + ACTIVE + + + default + arn:aws:ecs:us-east-1:aws_account_id:cluster/default + ACTIVE + + + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var DescribeContainerInstancesResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + + + + + true + arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID + instance_id + ACTIVE + + + 2048 + 0 + INTEGER + CPU + 0.0 + + + 3955 + 0 + INTEGER + MEMORY + 0.0 + + + 0 + 0 + STRINGSET + + 2376 + 22 + 51678 + 2375 + + PORTS + 0.0 + + + + + 2048 + 0 + INTEGER + CPU + 0.0 + + + 3955 + 0 + INTEGER + MEMORY + 0.0 + + + 0 + 0 + STRINGSET + + 2376 + 22 + 51678 + 2375 + + PORTS + 0.0 + + + + + + +` + +var DescribeTaskDefinitionResponse = ` + + + + 2 + sleep360 + + + + true + + + envVar + foo + + + + /bin/sh + + sleep + + sleep + 360 + + 10 + busybox + 10 + + + arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2 + + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var DescribeTasksResponse = ` + + + + + + + + arn:aws:ecs:us-east-1:aws_account_id:task/UUID + sleep + arn:aws:ecs:us-east-1:aws_account_id:container/UUID + + RUNNING + + + + + + sleep + + + + RUNNING + arn:aws:ecs:us-east-1:aws_account_id:task/UUID + arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID + RUNNING + arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2 + + + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var DiscoverPollEndpointResponse = ` + + + https://ecs-x-1.us-east-1.amazonaws.com/ + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var ListClustersResponse = ` + + + + arn:aws:ecs:us-east-1:aws_account_id:cluster/default + arn:aws:ecs:us-east-1:aws_account_id:cluster/test + + token_UUID + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var ListContainerInstancesResponse = ` + + + + arn:aws:ecs:us-east-1:aws_account_id:container-instance/uuid-1 + arn:aws:ecs:us-east-1:aws_account_id:container-instance/uuid-2 + + token_UUID + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var ListTaskDefinitionsResponse = ` + + + + arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:1 + arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2 + + token_UUID + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var ListTasksResponse = ` + + + + arn:aws:ecs:us-east-1:aws_account_id:task/uuid_1 + arn:aws:ecs:us-east-1:aws_account_id:task/uuid_2 + + token_UUID + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var RegisterContainerInstanceResponse = ` + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + + + True + arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_UUID + instance_id + ACTIVE + + + 2048 + 0 + INTEGER + CPU + 0.0 + + + 3955 + 0 + INTEGER + MEMORY + 0.0 + + + 0 + 0 + STRINGSET + + 2376 + 22 + 51678 + 2375 + + PORTS + 0.0 + + + + + 2048 + 0 + INTEGER + CPU + 0.0 + + + 3955 + 0 + INTEGER + MEMORY + 0.0 + + + 0 + 0 + STRINGSET + + 2376 + 22 + 51678 + 2375 + + PORTS + 0.0 + + + + + +` + +var RegisterTaskDefinitionResponse = ` + + + + 2 + sleep360 + + + + true + + + envVar + foo + + + + /bin/sh + + sleep + + sleep + 360 + + 10 + busybox + 10 + + + /tmp/myfile + false + /srv/myfile + + + /tmp/myfile2 + true + /srv/myfile2 + + + + + true + foo + + + + + arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2 + + + /srv/myfile + + /srv/myfile + + + + + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var RunTaskResponse = ` + + + + + + + + arn:aws:ecs:us-east-1:aws_account_id:task/UUID + sleep + arn:aws:ecs:us-east-1:aws_account_id:container/UUID + + RUNNING + + + + + + sleep + + + + RUNNING + arn:aws:ecs:us-east-1:aws_account_id:task/UUID + arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID + PENDING + arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2 + + + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var StartTaskResponse = ` + + + + + + + + arn:aws:ecs:us-east-1:aws_account_id:task/UUID + sleep + arn:aws:ecs:us-east-1:aws_account_id:container/UUID + + RUNNING + + + + + + sleep + + + + RUNNING + arn:aws:ecs:us-east-1:aws_account_id:task/UUID + arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID + PENDING + arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2 + + + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var StopTaskResponse = ` + + + + + + arn:aws:ecs:us-east-1:aws_account_id:task/UUID + sleep + arn:aws:ecs:us-east-1:aws_account_id:container/UUID + + RUNNING + + + + + + sleep + + + + STOPPED + arn:aws:ecs:us-east-1:aws_account_id:task/UUID + arn:aws:ecs:us-east-1:aws_account_id:container-instance/UUID + RUNNING + arn:aws:ecs:us-east-1:aws_account_id:task-definition/sleep360:2 + + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var SubmitContainerStateChangeResponse = ` + + + ACK + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` + +var SubmitTaskStateChangeResponse = ` + + + ACK + + + 8d798a29-f083-11e1-bdfb-cb223EXAMPLE + + +` diff --git a/vendor/github.com/goamz/goamz/elb/elb.go b/vendor/github.com/goamz/goamz/elb/elb.go new file mode 100644 index 000000000..0127435a7 --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/elb.go @@ -0,0 +1,435 @@ +// This package provides types and functions to interact Elastic Load Balancing service +package elb + +import ( + "encoding/xml" + "fmt" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "github.com/goamz/goamz/aws" +) + +type ELB struct { + aws.Auth + aws.Region +} + +func New(auth aws.Auth, region aws.Region) *ELB { + return &ELB{auth, region} +} + +// The CreateLoadBalancer type encapsulates options for the respective request in AWS. +// The creation of a Load Balancer may differ inside EC2 and VPC. +// +// See http://goo.gl/4QFKi for more details. +type CreateLoadBalancer struct { + Name string + AvailabilityZones []string + Listeners []Listener + Scheme string + SecurityGroups []string + Subnets []string +} + +// Listener to configure in Load Balancer. +// +// See http://goo.gl/NJQCj for more details. +type Listener struct { + InstancePort int + InstanceProtocol string + LoadBalancerPort int + Protocol string + SSLCertificateId string +} + +// Response to a CreateLoadBalance request. +// +// See http://goo.gl/4QFKi for more details. +type CreateLoadBalancerResp struct { + DNSName string `xml:"CreateLoadBalancerResult>DNSName"` +} + +type SimpleResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// Creates a Load Balancer in Amazon. +// +// See http://goo.gl/4QFKi for more details. +func (elb *ELB) CreateLoadBalancer(options *CreateLoadBalancer) (resp *CreateLoadBalancerResp, err error) { + params := makeCreateParams(options) + resp = new(CreateLoadBalancerResp) + if err := elb.query(params, resp); err != nil { + return nil, err + } + return +} + +// Deletes a Load Balancer. +// +// See http://goo.gl/sDmPp for more details. +func (elb *ELB) DeleteLoadBalancer(name string) (resp *SimpleResp, err error) { + params := map[string]string{ + "Action": "DeleteLoadBalancer", + "LoadBalancerName": name, + } + resp = new(SimpleResp) + if err := elb.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +type RegisterInstancesResp struct { + InstanceIds []string `xml:"RegisterInstancesWithLoadBalancerResult>Instances>member>InstanceId"` +} + +// Register N instances with a given Load Balancer. +// +// See http://goo.gl/x9hru for more details. +func (elb *ELB) RegisterInstancesWithLoadBalancer(instanceIds []string, lbName string) (resp *RegisterInstancesResp, err error) { + // TODO: change params order and use ..., e.g (lbName string, instanceIds ...string) + params := map[string]string{ + "Action": "RegisterInstancesWithLoadBalancer", + "LoadBalancerName": lbName, + } + for i, instanceId := range instanceIds { + key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1) + params[key] = instanceId + } + resp = new(RegisterInstancesResp) + if err := elb.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Deregister N instances from a given Load Balancer. +// +// See http://goo.gl/Hgo4U for more details. +func (elb *ELB) DeregisterInstancesFromLoadBalancer(instanceIds []string, lbName string) (resp *SimpleResp, err error) { + // TODO: change params order and use ..., e.g (lbName string, instanceIds ...string) + params := map[string]string{ + "Action": "DeregisterInstancesFromLoadBalancer", + "LoadBalancerName": lbName, + } + for i, instanceId := range instanceIds { + key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1) + params[key] = instanceId + } + resp = new(SimpleResp) + if err := elb.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +type DescribeLoadBalancerResp struct { + LoadBalancerDescriptions []LoadBalancerDescription `xml:"DescribeLoadBalancersResult>LoadBalancerDescriptions>member"` +} + +type LoadBalancerDescription struct { + AvailabilityZones []string `xml:"AvailabilityZones>member"` + BackendServerDescriptions []BackendServerDescriptions `xml:"BackendServerDescriptions>member"` + CanonicalHostedZoneName string `xml:"CanonicalHostedZoneName"` + CanonicalHostedZoneNameId string `xml:"CanonicalHostedZoneNameID"` + CreatedTime time.Time `xml:"CreatedTime"` + DNSName string `xml:"DNSName"` + HealthCheck HealthCheck `xml:"HealthCheck"` + Instances []Instance `xml:"Instances>member"` + ListenerDescriptions []ListenerDescription `xml:"ListenerDescriptions>member"` + LoadBalancerName string `xml:"LoadBalancerName"` + Policies Policies `xml:"Policies"` + Scheme string `xml:"Scheme"` + SecurityGroups []string `xml:"SecurityGroups>member"` //vpc only + SourceSecurityGroup SourceSecurityGroup `xml:"SourceSecurityGroup"` + Subnets []string `xml:"Subnets>member"` + VPCId string `xml:"VPCId"` +} + +// Describe Load Balancers. +// It can be used to describe all Load Balancers or specific ones. +// +// See http://goo.gl/wofJA for more details. +func (elb *ELB) DescribeLoadBalancers(names ...string) (*DescribeLoadBalancerResp, error) { + params := map[string]string{"Action": "DescribeLoadBalancers"} + for i, name := range names { + index := fmt.Sprintf("LoadBalancerNames.member.%d", i+1) + params[index] = name + } + resp := new(DescribeLoadBalancerResp) + if err := elb.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +type BackendServerDescriptions struct { + InstancePort int `xml:"InstancePort"` + PolicyNames []string `xml:"PolicyNames>member"` +} + +type HealthCheck struct { + HealthyThreshold int `xml:"HealthyThreshold"` + Interval int `xml:"Interval"` + Target string `xml:"Target"` + Timeout int `xml:"Timeout"` + UnhealthyThreshold int `xml:"UnhealthyThreshold"` +} + +type Instance struct { + InstanceId string `xml:"InstanceId"` +} + +type ListenerDescription struct { + Listener Listener `xml:"Listener"` + PolicyNames []string `xml:"PolicyNames>member"` +} + +type Policies struct { + AppCookieStickinessPolicies []AppCookieStickinessPolicies `xml:"AppCookieStickinessPolicies>member"` + LBCookieStickinessPolicies []LBCookieStickinessPolicies `xml:"LBCookieStickinessPolicies>member"` + OtherPolicies []string `xml:"OtherPolicies>member"` +} + +// see http://goo.gl/clXGV for more information. +type AppCookieStickinessPolicies struct { + CookieName string `xml:"CookieName"` + PolicyName string `xml:"PolicyName"` +} + +type LBCookieStickinessPolicies struct { + CookieExpirationPeriod int `xml:"CookieExpirationPeriod"` + PolicyName string `xml:"PolicyName"` +} + +type SourceSecurityGroup struct { + GroupName string `xml:"GroupName"` + OwnerAlias string `xml:"OwnerAlias"` +} + +// Represents a XML response for DescribeInstanceHealth action +// +// See http://goo.gl/ovIB1 for more information. +type DescribeInstanceHealthResp struct { + InstanceStates []InstanceState `xml:"DescribeInstanceHealthResult>InstanceStates>member"` +} + +// See http://goo.gl/dzWfP for more information. +type InstanceState struct { + Description string `xml:"Description"` + InstanceId string `xml:"InstanceId"` + ReasonCode string `xml:"ReasonCode"` + State string `xml:"State"` +} + +// Describe instance health. +// +// See http://goo.gl/ovIB1 for more information. +func (elb *ELB) DescribeInstanceHealth(lbName string, instanceIds ...string) (*DescribeInstanceHealthResp, error) { + params := map[string]string{ + "Action": "DescribeInstanceHealth", + "LoadBalancerName": lbName, + } + for i, iId := range instanceIds { + key := fmt.Sprintf("Instances.member.%d.InstanceId", i+1) + params[key] = iId + } + resp := new(DescribeInstanceHealthResp) + if err := elb.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +type HealthCheckResp struct { + HealthCheck *HealthCheck `xml:"ConfigureHealthCheckResult>HealthCheck"` +} + +// Configure health check for a LB +// +// See http://goo.gl/2HE6a for more information +func (elb *ELB) ConfigureHealthCheck(lbName string, healthCheck *HealthCheck) (*HealthCheckResp, error) { + params := map[string]string{ + "Action": "ConfigureHealthCheck", + "LoadBalancerName": lbName, + "HealthCheck.HealthyThreshold": strconv.Itoa(healthCheck.HealthyThreshold), + "HealthCheck.Interval": strconv.Itoa(healthCheck.Interval), + "HealthCheck.Target": healthCheck.Target, + "HealthCheck.Timeout": strconv.Itoa(healthCheck.Timeout), + "HealthCheck.UnhealthyThreshold": strconv.Itoa(healthCheck.UnhealthyThreshold), + } + resp := new(HealthCheckResp) + if err := elb.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Add tags to the named ELB +// +// Note that AWS only accepts one ELB name at a time (even though it is sent as a list) +// +// See http://goo.gl/6JW4Wf for the rest of the details +func (elb *ELB) AddTags(elbName string, tags map[string]string) (*SimpleResp, error) { + var sortedKeys []string + params := make(map[string]string) + response := &SimpleResp{} + + for tagKey := range tags { + sortedKeys = append(sortedKeys, tagKey) + } + + sort.Strings(sortedKeys) + + for _, key := range sortedKeys { + number := len(tags) + params[fmt.Sprintf("Tags.member.%d.Key", number)] = key + params[fmt.Sprintf("Tags.member.%d.Value", number)] = tags[key] + delete(tags, key) + } + + params["Action"] = "AddTags" + params["LoadBalancerNames.member.1"] = elbName + + if err := elb.query(params, response); err != nil { + return nil, err + } + + return response, nil +} + +// Remove tags from the named ELB +// +// Note that AWS only accepts one ELB name at a time (even though it is sent as a list) +// +// see http://goo.gl/ochFqo for more details + +func (elb *ELB) RemoveTags(elbName string, tagKeys []string) (*SimpleResp, error) { + response := &SimpleResp{} + params := make(map[string]string) + + params["Action"] = "RemoveTags" + params["LoadBalancerNames.member.1"] = elbName + + for i, tagKey := range tagKeys { + params[fmt.Sprintf("Tags.member.%d.Key", i+1)] = tagKey + } + + if err := elb.query(params, response); err != nil { + return nil, err + } + + return response, nil +} + +func (elb *ELB) query(params map[string]string, resp interface{}) error { + params["Version"] = "2012-06-01" + params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339) + data := strings.NewReader(multimap(params).Encode()) + hreq, err := http.NewRequest("GET", elb.Region.ELBEndpoint+"/", data) + if err != nil { + return err + } + + hreq.URL.RawQuery = multimap(params).Encode() + token := elb.Auth.Token() + if token != "" { + hreq.Header.Set("X-Amz-Security-Token", token) + } + + signer := aws.NewV4Signer(elb.Auth, "elasticloadbalancing", elb.Region) + signer.Sign(hreq) + + r, err := http.DefaultClient.Do(hreq) + + if err != nil { + return err + } + defer r.Body.Close() + if r.StatusCode != 200 { + return buildError(r) + } + return xml.NewDecoder(r.Body).Decode(resp) +} + +// Error encapsulates an error returned by ELB. +type Error struct { + // HTTP status code + StatusCode int + // AWS error code + Code string + // The human-oriented error message + Message string +} + +func (err *Error) Error() string { + if err.Code == "" { + return err.Message + } + + return fmt.Sprintf("%s (%s)", err.Message, err.Code) +} + +type xmlErrors struct { + Errors []Error `xml:"Error"` +} + +func buildError(r *http.Response) error { + var ( + err Error + errors xmlErrors + ) + xml.NewDecoder(r.Body).Decode(&errors) + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} + +func makeCreateParams(createLB *CreateLoadBalancer) map[string]string { + params := make(map[string]string) + params["LoadBalancerName"] = createLB.Name + params["Action"] = "CreateLoadBalancer" + if createLB.Scheme != "" { + params["Scheme"] = createLB.Scheme + } + for i, s := range createLB.SecurityGroups { + key := fmt.Sprintf("SecurityGroups.member.%d", i+1) + params[key] = s + } + for i, s := range createLB.Subnets { + key := fmt.Sprintf("Subnets.member.%d", i+1) + params[key] = s + } + for i, l := range createLB.Listeners { + key := "Listeners.member.%d.%s" + index := i + 1 + params[fmt.Sprintf(key, index, "InstancePort")] = strconv.Itoa(l.InstancePort) + params[fmt.Sprintf(key, index, "InstanceProtocol")] = l.InstanceProtocol + params[fmt.Sprintf(key, index, "Protocol")] = l.Protocol + params[fmt.Sprintf(key, index, "LoadBalancerPort")] = strconv.Itoa(l.LoadBalancerPort) + } + for i, az := range createLB.AvailabilityZones { + key := fmt.Sprintf("AvailabilityZones.member.%d", i+1) + params[key] = az + } + return params +} diff --git a/vendor/github.com/goamz/goamz/elb/elb_test.go b/vendor/github.com/goamz/goamz/elb/elb_test.go new file mode 100644 index 000000000..db799fdfc --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/elb_test.go @@ -0,0 +1,369 @@ +package elb_test + +import ( + "time" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/elb" + . "gopkg.in/check.v1" +) + +type S struct { + HTTPSuite + elb *elb.ELB +} + +var _ = Suite(&S{}) + +func (s *S) SetUpSuite(c *C) { + s.HTTPSuite.SetUpSuite(c) + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.elb = elb.New(auth, aws.Region{ELBEndpoint: testServer.URL}) +} + +func (s *S) TestCreateLoadBalancer(c *C) { + testServer.PrepareResponse(200, nil, CreateLoadBalancer) + createLB := &elb.CreateLoadBalancer{ + Name: "testlb", + AvailabilityZones: []string{"us-east-1a", "us-east-1b"}, + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + Protocol: "http", + LoadBalancerPort: 80, + }, + }, + } + resp, err := s.elb.CreateLoadBalancer(createLB) + c.Assert(err, IsNil) + defer s.elb.DeleteLoadBalancer(createLB.Name) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Action"), Equals, "CreateLoadBalancer") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("LoadBalancerName"), Equals, "testlb") + c.Assert(values.Get("AvailabilityZones.member.1"), Equals, "us-east-1a") + c.Assert(values.Get("AvailabilityZones.member.2"), Equals, "us-east-1b") + c.Assert(values.Get("Listeners.member.1.InstancePort"), Equals, "80") + c.Assert(values.Get("Listeners.member.1.InstanceProtocol"), Equals, "http") + c.Assert(values.Get("Listeners.member.1.Protocol"), Equals, "http") + c.Assert(values.Get("Listeners.member.1.LoadBalancerPort"), Equals, "80") + c.Assert(resp.DNSName, Equals, "testlb-339187009.us-east-1.elb.amazonaws.com") +} + +func (s *S) TestCreateLoadBalancerWithSubnetsAndMoreListeners(c *C) { + testServer.PrepareResponse(200, nil, CreateLoadBalancer) + createLB := &elb.CreateLoadBalancer{ + Name: "testlb", + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + Protocol: "http", + LoadBalancerPort: 80, + }, + { + InstancePort: 8080, + InstanceProtocol: "http", + Protocol: "http", + LoadBalancerPort: 8080, + }, + }, + Subnets: []string{"subnetid-1", "subnetid-2"}, + SecurityGroups: []string{"sg-1", "sg-2"}, + } + _, err := s.elb.CreateLoadBalancer(createLB) + c.Assert(err, IsNil) + defer s.elb.DeleteLoadBalancer(createLB.Name) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Listeners.member.1.InstancePort"), Equals, "80") + c.Assert(values.Get("Listeners.member.1.LoadBalancerPort"), Equals, "80") + c.Assert(values.Get("Listeners.member.2.InstancePort"), Equals, "8080") + c.Assert(values.Get("Listeners.member.2.LoadBalancerPort"), Equals, "8080") + c.Assert(values.Get("Subnets.member.1"), Equals, "subnetid-1") + c.Assert(values.Get("Subnets.member.2"), Equals, "subnetid-2") + c.Assert(values.Get("SecurityGroups.member.1"), Equals, "sg-1") + c.Assert(values.Get("SecurityGroups.member.2"), Equals, "sg-2") +} + +func (s *S) TestCreateLoadBalancerWithWrongParamsCombination(c *C) { + testServer.PrepareResponse(400, nil, CreateLoadBalancerBadRequest) + createLB := &elb.CreateLoadBalancer{ + Name: "testlb", + AvailabilityZones: []string{"us-east-1a", "us-east-1b"}, + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + Protocol: "http", + LoadBalancerPort: 80, + }, + }, + Subnets: []string{"subnetid-1", "subnetid2"}, + } + resp, err := s.elb.CreateLoadBalancer(createLB) + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + e, ok := err.(*elb.Error) + c.Assert(ok, Equals, true) + c.Assert(e.Message, Equals, "Only one of SubnetIds or AvailabilityZones may be specified") + c.Assert(e.Code, Equals, "ValidationError") +} + +func (s *S) TestDeleteLoadBalancer(c *C) { + testServer.PrepareResponse(200, nil, DeleteLoadBalancer) + resp, err := s.elb.DeleteLoadBalancer("testlb") + c.Assert(err, IsNil) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("Action"), Equals, "DeleteLoadBalancer") + c.Assert(values.Get("LoadBalancerName"), Equals, "testlb") + c.Assert(resp.RequestId, Equals, "8d7223db-49d7-11e2-bba9-35ba56032fe1") +} + +func (s *S) TestRegisterInstancesWithLoadBalancer(c *C) { + testServer.PrepareResponse(200, nil, RegisterInstancesWithLoadBalancer) + resp, err := s.elb.RegisterInstancesWithLoadBalancer([]string{"i-b44db8ca", "i-461ecf38"}, "testlb") + c.Assert(err, IsNil) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("Action"), Equals, "RegisterInstancesWithLoadBalancer") + c.Assert(values.Get("LoadBalancerName"), Equals, "testlb") + c.Assert(values.Get("Instances.member.1.InstanceId"), Equals, "i-b44db8ca") + c.Assert(values.Get("Instances.member.2.InstanceId"), Equals, "i-461ecf38") + c.Assert(resp.InstanceIds, DeepEquals, []string{"i-b44db8ca", "i-461ecf38"}) +} + +func (s *S) TestRegisterInstancesWithLoadBalancerBadRequest(c *C) { + testServer.PrepareResponse(400, nil, RegisterInstancesWithLoadBalancerBadRequest) + resp, err := s.elb.RegisterInstancesWithLoadBalancer([]string{"i-b44db8ca", "i-461ecf38"}, "absentLB") + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + e, ok := err.(*elb.Error) + c.Assert(ok, Equals, true) + c.Assert(e.Message, Equals, "There is no ACTIVE Load Balancer named 'absentLB'") + c.Assert(e.Code, Equals, "LoadBalancerNotFound") +} + +func (s *S) TestDeregisterInstancesFromLoadBalancer(c *C) { + testServer.PrepareResponse(200, nil, DeregisterInstancesFromLoadBalancer) + resp, err := s.elb.DeregisterInstancesFromLoadBalancer([]string{"i-b44db8ca", "i-461ecf38"}, "testlb") + c.Assert(err, IsNil) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("Action"), Equals, "DeregisterInstancesFromLoadBalancer") + c.Assert(values.Get("LoadBalancerName"), Equals, "testlb") + c.Assert(values.Get("Instances.member.1.InstanceId"), Equals, "i-b44db8ca") + c.Assert(values.Get("Instances.member.2.InstanceId"), Equals, "i-461ecf38") + c.Assert(resp.RequestId, Equals, "d6490837-49fd-11e2-bba9-35ba56032fe1") +} + +func (s *S) TestDeregisterInstancesFromLoadBalancerBadRequest(c *C) { + testServer.PrepareResponse(400, nil, DeregisterInstancesFromLoadBalancerBadRequest) + resp, err := s.elb.DeregisterInstancesFromLoadBalancer([]string{"i-b44db8ca", "i-461ecf38"}, "testlb") + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + e, ok := err.(*elb.Error) + c.Assert(ok, Equals, true) + c.Assert(e.Message, Equals, "There is no ACTIVE Load Balancer named 'absentlb'") + c.Assert(e.Code, Equals, "LoadBalancerNotFound") +} + +func (s *S) TestDescribeLoadBalancers(c *C) { + testServer.PrepareResponse(200, nil, DescribeLoadBalancers) + resp, err := s.elb.DescribeLoadBalancers() + c.Assert(err, IsNil) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("Action"), Equals, "DescribeLoadBalancers") + t, _ := time.Parse(time.RFC3339, "2012-12-27T11:51:52.970Z") + expected := &elb.DescribeLoadBalancerResp{ + []elb.LoadBalancerDescription{ + { + AvailabilityZones: []string{"us-east-1a"}, + BackendServerDescriptions: []elb.BackendServerDescriptions(nil), + CanonicalHostedZoneName: "testlb-2087227216.us-east-1.elb.amazonaws.com", + CanonicalHostedZoneNameId: "Z3DZXE0Q79N41H", + CreatedTime: t, + DNSName: "testlb-2087227216.us-east-1.elb.amazonaws.com", + HealthCheck: elb.HealthCheck{ + HealthyThreshold: 10, + Interval: 30, + Target: "TCP:80", + Timeout: 5, + UnhealthyThreshold: 2, + }, + Instances: []elb.Instance(nil), + ListenerDescriptions: []elb.ListenerDescription{ + { + Listener: elb.Listener{ + Protocol: "HTTP", + LoadBalancerPort: 80, + InstanceProtocol: "HTTP", + InstancePort: 80, + }, + }, + }, + LoadBalancerName: "testlb", + //Policies: elb.Policies(nil), + Scheme: "internet-facing", + SecurityGroups: []string(nil), + SourceSecurityGroup: elb.SourceSecurityGroup{ + GroupName: "amazon-elb-sg", + OwnerAlias: "amazon-elb", + }, + Subnets: []string(nil), + }, + }, + } + c.Assert(resp, DeepEquals, expected) +} + +func (s *S) TestDescribeLoadBalancersByName(c *C) { + testServer.PrepareResponse(200, nil, DescribeLoadBalancers) + s.elb.DescribeLoadBalancers("somelb") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("Action"), Equals, "DescribeLoadBalancers") + c.Assert(values.Get("LoadBalancerNames.member.1"), Equals, "somelb") +} + +func (s *S) TestDescribeLoadBalancersBadRequest(c *C) { + testServer.PrepareResponse(400, nil, DescribeLoadBalancersBadRequest) + resp, err := s.elb.DescribeLoadBalancers() + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, `^Cannot find Load Balancer absentlb \(LoadBalancerNotFound\)$`) +} + +func (s *S) TestDescribeInstanceHealth(c *C) { + testServer.PrepareResponse(200, nil, DescribeInstanceHealth) + resp, err := s.elb.DescribeInstanceHealth("testlb", "i-b44db8ca") + c.Assert(err, IsNil) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("Action"), Equals, "DescribeInstanceHealth") + c.Assert(values.Get("LoadBalancerName"), Equals, "testlb") + c.Assert(values.Get("Instances.member.1.InstanceId"), Equals, "i-b44db8ca") + c.Assert(len(resp.InstanceStates) > 0, Equals, true) + c.Assert(resp.InstanceStates[0].Description, Equals, "Instance registration is still in progress.") + c.Assert(resp.InstanceStates[0].InstanceId, Equals, "i-b44db8ca") + c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService") + c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "ELB") +} + +func (s *S) TestDescribeInstanceHealthBadRequest(c *C) { + testServer.PrepareResponse(400, nil, DescribeInstanceHealthBadRequest) + resp, err := s.elb.DescribeInstanceHealth("testlb", "i-foooo") + c.Assert(err, NotNil) + c.Assert(resp, IsNil) + c.Assert(err, ErrorMatches, ".*i-foooo.*(InvalidInstance).*") +} + +func (s *S) TestConfigureHealthCheck(c *C) { + testServer.PrepareResponse(200, nil, ConfigureHealthCheck) + hc := elb.HealthCheck{ + HealthyThreshold: 10, + Interval: 30, + Target: "HTTP:80/", + Timeout: 5, + UnhealthyThreshold: 2, + } + resp, err := s.elb.ConfigureHealthCheck("testlb", &hc) + c.Assert(err, IsNil) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("Action"), Equals, "ConfigureHealthCheck") + c.Assert(values.Get("LoadBalancerName"), Equals, "testlb") + c.Assert(values.Get("HealthCheck.HealthyThreshold"), Equals, "10") + c.Assert(values.Get("HealthCheck.Interval"), Equals, "30") + c.Assert(values.Get("HealthCheck.Target"), Equals, "HTTP:80/") + c.Assert(values.Get("HealthCheck.Timeout"), Equals, "5") + c.Assert(values.Get("HealthCheck.UnhealthyThreshold"), Equals, "2") + c.Assert(resp.HealthCheck.HealthyThreshold, Equals, 10) + c.Assert(resp.HealthCheck.Interval, Equals, 30) + c.Assert(resp.HealthCheck.Target, Equals, "HTTP:80/") + c.Assert(resp.HealthCheck.Timeout, Equals, 5) + c.Assert(resp.HealthCheck.UnhealthyThreshold, Equals, 2) +} + +func (s *S) TestConfigureHealthCheckBadRequest(c *C) { + testServer.PrepareResponse(400, nil, ConfigureHealthCheckBadRequest) + hc := elb.HealthCheck{ + HealthyThreshold: 10, + Interval: 30, + Target: "HTTP:80/", + Timeout: 5, + UnhealthyThreshold: 2, + } + resp, err := s.elb.ConfigureHealthCheck("foolb", &hc) + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, ".*foolb.*(LoadBalancerNotFound).*") +} + +func (s *S) TestAddTags(c *C) { + testServer.PrepareResponse(200, nil, AddTagsSuccessResponse) + tagsToAdd := map[string]string{ + "my-key": "my-value", + "my-super-silly-tag": "its-another-valid-value", + } + + resp, err := s.elb.AddTags("my-elb", tagsToAdd) + c.Assert(err, IsNil) + + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Action"), Equals, "AddTags") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("LoadBalancerNames.member.1"), Equals, "my-elb") + c.Assert(values.Get("Tags.member.1.Key"), Equals, "my-super-silly-tag") + c.Assert(values.Get("Tags.member.1.Value"), Equals, "its-another-valid-value") + c.Assert(values.Get("Tags.member.2.Key"), Equals, "my-key") + c.Assert(values.Get("Tags.member.2.Value"), Equals, "my-value") + + c.Assert(resp.RequestId, Equals, "360e81f7-1100-11e4-b6ed-0f30SOME-SAUCY-EXAMPLE") +} + +func (s *S) TestAddBadTags(c *C) { + testServer.PrepareResponse(400, nil, TagsBadRequest) + tagsToAdd := map[string]string{ + "my-first-key": "an invalid value", + } + + resp, err := s.elb.AddTags("my-bad-elb", tagsToAdd) + c.Assert(resp, IsNil) + c.Assert(err, ErrorMatches, ".*(InvalidParameterValue).*") +} + +func (s *S) TestRemoveTags(c *C) { + testServer.PrepareResponse(200, nil, RemoveTagsSuccessResponse) + tagKeysToRemove := []string{"a-key-one", "a-key-two"} + + resp, err := s.elb.RemoveTags("my-test-elb-1", tagKeysToRemove) + c.Assert(err, IsNil) + + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Version"), Equals, "2012-06-01") + c.Assert(values.Get("Action"), Equals, "RemoveTags") + c.Assert(values.Get("Timestamp"), Not(Equals), "") + c.Assert(values.Get("LoadBalancerNames.member.1"), Equals, "my-test-elb-1") + c.Assert([]string{values.Get("Tags.member.1.Key"), values.Get("Tags.member.2.Key")}, DeepEquals, tagKeysToRemove) + c.Assert(resp.RequestId, Equals, "83c88b9d-12b7-11e3-8b82-87b12DIFFEXAMPLE") +} + +func (s *S) TestRemoveTagsFailure(c *C) { + testServer.PrepareResponse(400, nil, TagsBadRequest) + + resp, err := s.elb.RemoveTags("my-test-elb", []string{"non-existant-tag"}) + c.Assert(resp, IsNil) + c.Assert(err, ErrorMatches, ".*(InvalidParameterValue).*") +} diff --git a/vendor/github.com/goamz/goamz/elb/elbi_test.go b/vendor/github.com/goamz/goamz/elb/elbi_test.go new file mode 100644 index 000000000..4c21f9e38 --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/elbi_test.go @@ -0,0 +1,308 @@ +package elb_test + +import ( + "flag" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/ec2" + "github.com/goamz/goamz/elb" + . "gopkg.in/check.v1" +) + +var amazon = flag.Bool("amazon", false, "Enable tests against amazon server") + +// AmazonServer represents an Amazon AWS server. +type AmazonServer struct { + auth aws.Auth +} + +func (s *AmazonServer) SetUp(c *C) { + auth, err := aws.EnvAuth() + if err != nil { + c.Fatal(err) + } + s.auth = auth +} + +var _ = Suite(&AmazonClientSuite{}) + +// AmazonClientSuite tests the client against a live AWS server. +type AmazonClientSuite struct { + srv AmazonServer + ClientTests +} + +// ClientTests defines integration tests designed to test the client. +// It is not used as a test suite in itself, but embedded within +// another type. +type ClientTests struct { + elb *elb.ELB + ec2 *ec2.EC2 +} + +func (s *AmazonClientSuite) SetUpSuite(c *C) { + if !*amazon { + c.Skip("AmazonClientSuite tests not enabled") + } + s.srv.SetUp(c) + s.elb = elb.New(s.srv.auth, aws.USEast) + s.ec2 = ec2.New(s.srv.auth, aws.USEast) +} + +func (s *ClientTests) TestCreateAndDeleteLoadBalancer(c *C) { + createLBReq := elb.CreateLoadBalancer{ + Name: "testlb", + AvailabilityZones: []string{"us-east-1a"}, + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + LoadBalancerPort: 80, + Protocol: "http", + }, + }, + } + resp, err := s.elb.CreateLoadBalancer(&createLBReq) + c.Assert(err, IsNil) + defer s.elb.DeleteLoadBalancer(createLBReq.Name) + c.Assert(resp.DNSName, Not(Equals), "") + deleteResp, err := s.elb.DeleteLoadBalancer(createLBReq.Name) + c.Assert(err, IsNil) + c.Assert(deleteResp.RequestId, Not(Equals), "") +} + +func (s *ClientTests) TestCreateLoadBalancerError(c *C) { + createLBReq := elb.CreateLoadBalancer{ + Name: "testlb", + AvailabilityZones: []string{"us-east-1a"}, + Subnets: []string{"subnetid-1"}, + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + LoadBalancerPort: 80, + Protocol: "http", + }, + }, + } + resp, err := s.elb.CreateLoadBalancer(&createLBReq) + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + e, ok := err.(*elb.Error) + c.Assert(ok, Equals, true) + c.Assert(e.Message, Matches, "Only one of .* or .* may be specified") + c.Assert(e.Code, Equals, "ValidationError") +} + +func (s *ClientTests) createInstanceAndLB(c *C) (*elb.CreateLoadBalancer, string) { + options := ec2.RunInstancesOptions{ + ImageId: "ami-ccf405a5", + InstanceType: "t1.micro", + AvailabilityZone: "us-east-1c", + } + resp1, err := s.ec2.RunInstances(&options) + c.Assert(err, IsNil) + instId := resp1.Instances[0].InstanceId + createLBReq := elb.CreateLoadBalancer{ + Name: "testlb", + AvailabilityZones: []string{"us-east-1c"}, + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + LoadBalancerPort: 80, + Protocol: "http", + }, + }, + } + _, err = s.elb.CreateLoadBalancer(&createLBReq) + c.Assert(err, IsNil) + return &createLBReq, instId +} + +// Cost: 0.02 USD +func (s *ClientTests) TestCreateRegisterAndDeregisterInstanceWithLoadBalancer(c *C) { + createLBReq, instId := s.createInstanceAndLB(c) + defer func() { + _, err := s.elb.DeleteLoadBalancer(createLBReq.Name) + c.Check(err, IsNil) + _, err = s.ec2.TerminateInstances([]string{instId}) + c.Check(err, IsNil) + }() + resp, err := s.elb.RegisterInstancesWithLoadBalancer([]string{instId}, createLBReq.Name) + c.Assert(err, IsNil) + c.Assert(resp.InstanceIds, DeepEquals, []string{instId}) + resp2, err := s.elb.DeregisterInstancesFromLoadBalancer([]string{instId}, createLBReq.Name) + c.Assert(err, IsNil) + c.Assert(resp2, Not(Equals), "") +} + +func (s *ClientTests) TestDescribeLoadBalancers(c *C) { + createLBReq := elb.CreateLoadBalancer{ + Name: "testlb", + AvailabilityZones: []string{"us-east-1a"}, + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + LoadBalancerPort: 80, + Protocol: "http", + }, + }, + } + _, err := s.elb.CreateLoadBalancer(&createLBReq) + c.Assert(err, IsNil) + defer func() { + _, err := s.elb.DeleteLoadBalancer(createLBReq.Name) + c.Check(err, IsNil) + }() + resp, err := s.elb.DescribeLoadBalancers() + c.Assert(err, IsNil) + c.Assert(len(resp.LoadBalancerDescriptions) > 0, Equals, true) + c.Assert(resp.LoadBalancerDescriptions[0].AvailabilityZones, DeepEquals, []string{"us-east-1a"}) + c.Assert(resp.LoadBalancerDescriptions[0].LoadBalancerName, Equals, "testlb") + c.Assert(resp.LoadBalancerDescriptions[0].Scheme, Equals, "internet-facing") + hc := elb.HealthCheck{ + HealthyThreshold: 10, + Interval: 30, + Target: "TCP:80", + Timeout: 5, + UnhealthyThreshold: 2, + } + c.Assert(resp.LoadBalancerDescriptions[0].HealthCheck, DeepEquals, hc) + ld := []elb.ListenerDescription{ + { + Listener: elb.Listener{ + Protocol: "HTTP", + LoadBalancerPort: 80, + InstanceProtocol: "HTTP", + InstancePort: 80, + }, + }, + } + c.Assert(resp.LoadBalancerDescriptions[0].ListenerDescriptions, DeepEquals, ld) + ssg := elb.SourceSecurityGroup{ + GroupName: "amazon-elb-sg", + OwnerAlias: "amazon-elb", + } + c.Assert(resp.LoadBalancerDescriptions[0].SourceSecurityGroup, DeepEquals, ssg) +} + +func (s *ClientTests) TestDescribeLoadBalancersBadRequest(c *C) { + resp, err := s.elb.DescribeLoadBalancers("absentlb") + c.Assert(err, NotNil) + c.Assert(resp, IsNil) + c.Assert(err, ErrorMatches, ".*(LoadBalancerNotFound).*") +} + +func (s *ClientTests) TestDescribeInstanceHealth(c *C) { + createLBReq, instId := s.createInstanceAndLB(c) + defer func() { + _, err := s.elb.DeleteLoadBalancer(createLBReq.Name) + c.Check(err, IsNil) + _, err = s.ec2.TerminateInstances([]string{instId}) + c.Check(err, IsNil) + }() + _, err := s.elb.RegisterInstancesWithLoadBalancer([]string{instId}, createLBReq.Name) + c.Assert(err, IsNil) + resp, err := s.elb.DescribeInstanceHealth(createLBReq.Name, instId) + c.Assert(err, IsNil) + c.Assert(len(resp.InstanceStates) > 0, Equals, true) + c.Assert(resp.InstanceStates[0].Description, Equals, "Instance is in pending state.") + c.Assert(resp.InstanceStates[0].InstanceId, Equals, instId) + c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService") + c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "Instance") +} + +func (s *ClientTests) TestDescribeInstanceHealthBadRequest(c *C) { + createLBReq := elb.CreateLoadBalancer{ + Name: "testlb", + AvailabilityZones: []string{"us-east-1a"}, + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + LoadBalancerPort: 80, + Protocol: "http", + }, + }, + } + _, err := s.elb.CreateLoadBalancer(&createLBReq) + c.Assert(err, IsNil) + defer func() { + _, err := s.elb.DeleteLoadBalancer(createLBReq.Name) + c.Check(err, IsNil) + }() + resp, err := s.elb.DescribeInstanceHealth(createLBReq.Name, "i-foo") + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, ".*i-foo.*(InvalidInstance).*") +} + +func (s *ClientTests) TestConfigureHealthCheck(c *C) { + createLBReq := elb.CreateLoadBalancer{ + Name: "testlb", + AvailabilityZones: []string{"us-east-1a"}, + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + LoadBalancerPort: 80, + Protocol: "http", + }, + }, + } + _, err := s.elb.CreateLoadBalancer(&createLBReq) + c.Assert(err, IsNil) + defer func() { + _, err := s.elb.DeleteLoadBalancer(createLBReq.Name) + c.Check(err, IsNil) + }() + hc := elb.HealthCheck{ + HealthyThreshold: 10, + Interval: 30, + Target: "HTTP:80/", + Timeout: 5, + UnhealthyThreshold: 2, + } + resp, err := s.elb.ConfigureHealthCheck(createLBReq.Name, &hc) + c.Assert(err, IsNil) + c.Assert(resp.HealthCheck.HealthyThreshold, Equals, 10) + c.Assert(resp.HealthCheck.Interval, Equals, 30) + c.Assert(resp.HealthCheck.Target, Equals, "HTTP:80/") + c.Assert(resp.HealthCheck.Timeout, Equals, 5) + c.Assert(resp.HealthCheck.UnhealthyThreshold, Equals, 2) +} + +func (s *ClientTests) TestConfigureHealthCheckBadRequest(c *C) { + createLBReq := elb.CreateLoadBalancer{ + Name: "testlb", + AvailabilityZones: []string{"us-east-1a"}, + Listeners: []elb.Listener{ + { + InstancePort: 80, + InstanceProtocol: "http", + LoadBalancerPort: 80, + Protocol: "http", + }, + }, + } + _, err := s.elb.CreateLoadBalancer(&createLBReq) + c.Assert(err, IsNil) + defer func() { + _, err := s.elb.DeleteLoadBalancer(createLBReq.Name) + c.Check(err, IsNil) + }() + hc := elb.HealthCheck{ + HealthyThreshold: 10, + Interval: 30, + Target: "HTTP:80", + Timeout: 5, + UnhealthyThreshold: 2, + } + resp, err := s.elb.ConfigureHealthCheck(createLBReq.Name, &hc) + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + expected := "HealthCheck HTTP Target must specify a port followed by a path that begins with a slash. e.g. HTTP:80/ping/this/path (ValidationError)" + c.Assert(err.Error(), Equals, expected) +} diff --git a/vendor/github.com/goamz/goamz/elb/elbt_test.go b/vendor/github.com/goamz/goamz/elb/elbt_test.go new file mode 100644 index 000000000..2ea529452 --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/elbt_test.go @@ -0,0 +1,243 @@ +package elb_test + +import ( + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/elb" + "github.com/goamz/goamz/elb/elbtest" + . "gopkg.in/check.v1" +) + +// LocalServer represents a local elbtest fake server. +type LocalServer struct { + auth aws.Auth + region aws.Region + srv *elbtest.Server +} + +func (s *LocalServer) SetUp(c *C) { + srv, err := elbtest.NewServer() + c.Assert(err, IsNil) + c.Assert(srv, NotNil) + s.srv = srv + s.region = aws.Region{ELBEndpoint: srv.URL()} +} + +// LocalServerSuite defines tests that will run +// against the local elbtest server. It includes +// selected tests from ClientTests; +// when the elbtest functionality is sufficient, it should +// include all of them, and ClientTests can be simply embedded. +type LocalServerSuite struct { + srv LocalServer + ServerTests + clientTests ClientTests +} + +// ServerTests defines a set of tests designed to test +// the elbtest local fake elb server. +// It is not used as a test suite in itself, but embedded within +// another type. +type ServerTests struct { + elb *elb.ELB +} + +// AmazonServerSuite runs the elbtest server tests against a live ELB server. +// It will only be activated if the -all flag is specified. +type AmazonServerSuite struct { + srv AmazonServer + ServerTests +} + +var _ = Suite(&AmazonServerSuite{}) + +func (s *AmazonServerSuite) SetUpSuite(c *C) { + if !*amazon { + c.Skip("AmazonServerSuite tests not enabled") + } + s.srv.SetUp(c) + s.ServerTests.elb = elb.New(s.srv.auth, aws.USEast) +} + +var _ = Suite(&LocalServerSuite{}) + +func (s *LocalServerSuite) SetUpSuite(c *C) { + s.srv.SetUp(c) + s.ServerTests.elb = elb.New(s.srv.auth, s.srv.region) + s.clientTests.elb = elb.New(s.srv.auth, s.srv.region) +} + +func (s *LocalServerSuite) TestCreateLoadBalancer(c *C) { + s.clientTests.TestCreateAndDeleteLoadBalancer(c) +} + +func (s *LocalServerSuite) TestCreateLoadBalancerError(c *C) { + s.clientTests.TestCreateLoadBalancerError(c) +} + +func (s *LocalServerSuite) TestDescribeLoadBalancer(c *C) { + s.clientTests.TestDescribeLoadBalancers(c) +} + +func (s *LocalServerSuite) TestDescribeLoadBalancerListsAddedByNewLoadbalancerFunc(c *C) { + srv := s.srv.srv + srv.NewLoadBalancer("wierdlb") + defer srv.RemoveLoadBalancer("wierdlb") + resp, err := s.clientTests.elb.DescribeLoadBalancers() + c.Assert(err, IsNil) + isPresent := false + for _, desc := range resp.LoadBalancerDescriptions { + if desc.LoadBalancerName == "wierdlb" { + isPresent = true + } + } + c.Assert(isPresent, Equals, true) +} + +func (s *LocalServerSuite) TestDescribeLoadBalancerListsInstancesAddedByRegisterInstancesFunc(c *C) { + srv := s.srv.srv + lbName := "somelb" + srv.NewLoadBalancer(lbName) + defer srv.RemoveLoadBalancer(lbName) + instId := srv.NewInstance() + defer srv.RemoveInstance(instId) + srv.RegisterInstance(instId, lbName) // no need to deregister, since we're removing the lb + resp, err := s.clientTests.elb.DescribeLoadBalancers() + c.Assert(err, IsNil) + c.Assert(len(resp.LoadBalancerDescriptions) > 0, Equals, true) + c.Assert(len(resp.LoadBalancerDescriptions[0].Instances) > 0, Equals, true) + c.Assert(resp.LoadBalancerDescriptions[0].Instances, DeepEquals, []elb.Instance{{InstanceId: instId}}) + srv.DeregisterInstance(instId, lbName) + resp, err = s.clientTests.elb.DescribeLoadBalancers() + c.Assert(err, IsNil) + c.Assert(resp.LoadBalancerDescriptions[0].Instances, DeepEquals, []elb.Instance(nil)) +} + +func (s *LocalServerSuite) TestDescribeLoadBalancersBadRequest(c *C) { + s.clientTests.TestDescribeLoadBalancersBadRequest(c) +} + +func (s *LocalServerSuite) TestRegisterInstanceWithLoadBalancer(c *C) { + srv := s.srv.srv + instId := srv.NewInstance() + defer srv.RemoveInstance(instId) + srv.NewLoadBalancer("testlb") + defer srv.RemoveLoadBalancer("testlb") + resp, err := s.clientTests.elb.RegisterInstancesWithLoadBalancer([]string{instId}, "testlb") + c.Assert(err, IsNil) + c.Assert(resp.InstanceIds, DeepEquals, []string{instId}) +} + +func (s *LocalServerSuite) TestRegisterInstanceWithLoadBalancerWithAbsentInstance(c *C) { + srv := s.srv.srv + srv.NewLoadBalancer("testlb") + defer srv.RemoveLoadBalancer("testlb") + resp, err := s.clientTests.elb.RegisterInstancesWithLoadBalancer([]string{"i-212"}, "testlb") + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, `^InvalidInstance found in \[i-212\]. Invalid id: "i-212" \(InvalidInstance\)$`) + c.Assert(resp, IsNil) +} + +func (s *LocalServerSuite) TestRegisterInstanceWithLoadBalancerWithAbsentLoadBalancer(c *C) { + // the verification if the lb exists is done before the instances, so there is no need to create + // fixture instances for this test, it'll never get that far + resp, err := s.clientTests.elb.RegisterInstancesWithLoadBalancer([]string{"i-212"}, "absentlb") + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, `^There is no ACTIVE Load Balancer named 'absentlb' \(LoadBalancerNotFound\)$`) + c.Assert(resp, IsNil) +} + +func (s *LocalServerSuite) TestDeregisterInstanceWithLoadBalancer(c *C) { + // there is no need to register the instance first, amazon returns the same response + // in both cases (instance registered or not) + srv := s.srv.srv + instId := srv.NewInstance() + defer srv.RemoveInstance(instId) + srv.NewLoadBalancer("testlb") + defer srv.RemoveLoadBalancer("testlb") + resp, err := s.clientTests.elb.DeregisterInstancesFromLoadBalancer([]string{instId}, "testlb") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Not(Equals), "") +} + +func (s *LocalServerSuite) TestDeregisterInstanceWithLoadBalancerWithAbsentLoadBalancer(c *C) { + resp, err := s.clientTests.elb.DeregisterInstancesFromLoadBalancer([]string{"i-212"}, "absentlb") + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, `^There is no ACTIVE Load Balancer named 'absentlb' \(LoadBalancerNotFound\)$`) +} + +func (s *LocalServerSuite) TestDeregisterInstancewithLoadBalancerWithAbsentInstance(c *C) { + srv := s.srv.srv + srv.NewLoadBalancer("testlb") + defer srv.RemoveLoadBalancer("testlb") + resp, err := s.clientTests.elb.DeregisterInstancesFromLoadBalancer([]string{"i-212"}, "testlb") + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, `^InvalidInstance found in \[i-212\]. Invalid id: "i-212" \(InvalidInstance\)$`) +} + +func (s *LocalServerSuite) TestDescribeInstanceHealth(c *C) { + srv := s.srv.srv + instId := srv.NewInstance() + defer srv.RemoveInstance(instId) + srv.NewLoadBalancer("testlb") + defer srv.RemoveLoadBalancer("testlb") + resp, err := s.clientTests.elb.DescribeInstanceHealth("testlb", instId) + c.Assert(err, IsNil) + c.Assert(len(resp.InstanceStates) > 0, Equals, true) + c.Assert(resp.InstanceStates[0].Description, Equals, "Instance is in pending state.") + c.Assert(resp.InstanceStates[0].InstanceId, Equals, instId) + c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService") + c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "Instance") +} + +func (s *LocalServerSuite) TestDescribeInstanceHealthBadRequest(c *C) { + s.clientTests.TestDescribeInstanceHealthBadRequest(c) +} + +func (s *LocalServerSuite) TestDescribeInstanceHealthWithoutSpecifyingInstances(c *C) { + srv := s.srv.srv + instId := srv.NewInstance() + defer srv.RemoveInstance(instId) + srv.NewLoadBalancer("testlb") + defer srv.RemoveLoadBalancer("testlb") + srv.RegisterInstance(instId, "testlb") + resp, err := s.clientTests.elb.DescribeInstanceHealth("testlb") + c.Assert(err, IsNil) + c.Assert(len(resp.InstanceStates) > 0, Equals, true) + c.Assert(resp.InstanceStates[0].Description, Equals, "Instance is in pending state.") + c.Assert(resp.InstanceStates[0].InstanceId, Equals, instId) + c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService") + c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "Instance") +} + +func (s *LocalServerSuite) TestDescribeInstanceHealthChangingIt(c *C) { + srv := s.srv.srv + instId := srv.NewInstance() + defer srv.RemoveInstance(instId) + srv.NewLoadBalancer("somelb") + defer srv.RemoveLoadBalancer("somelb") + srv.RegisterInstance(instId, "somelb") + state := elb.InstanceState{ + Description: "Instance has failed at least the UnhealthyThreshold number of health checks consecutively", + InstanceId: instId, + State: "OutOfService", + ReasonCode: "Instance", + } + srv.ChangeInstanceState("somelb", state) + resp, err := s.clientTests.elb.DescribeInstanceHealth("somelb") + c.Assert(err, IsNil) + c.Assert(len(resp.InstanceStates) > 0, Equals, true) + c.Assert(resp.InstanceStates[0].Description, Equals, "Instance has failed at least the UnhealthyThreshold number of health checks consecutively") + c.Assert(resp.InstanceStates[0].InstanceId, Equals, instId) + c.Assert(resp.InstanceStates[0].State, Equals, "OutOfService") + c.Assert(resp.InstanceStates[0].ReasonCode, Equals, "Instance") +} + +func (s *LocalServerSuite) TestConfigureHealthCheck(c *C) { + s.clientTests.TestConfigureHealthCheck(c) +} + +func (s *LocalServerSuite) TestConfigureHealthCheckBadRequest(c *C) { + s.clientTests.TestConfigureHealthCheckBadRequest(c) +} diff --git a/vendor/github.com/goamz/goamz/elb/elbtest/server.go b/vendor/github.com/goamz/goamz/elb/elbtest/server.go new file mode 100644 index 000000000..9b8f79d4e --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/elbtest/server.go @@ -0,0 +1,551 @@ +// Package elbtest implements a fake ELB provider with the capability of +// inducing errors on any given operation, and retrospectively determining what +// operations have been carried out. +package elbtest + +import ( + "encoding/xml" + "fmt" + "github.com/goamz/goamz/elb" + "net" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" + "sync" +) + +// Server implements an ELB simulator for use in testing. +type Server struct { + url string + listener net.Listener + mutex sync.Mutex + reqId int + lbs map[string]*elb.LoadBalancerDescription + lbsReqs map[string]url.Values + instances []string + instanceStates map[string][]*elb.InstanceState + instCount int +} + +// Starts and returns a new server +func NewServer() (*Server, error) { + l, err := net.Listen("tcp", "localhost:0") + if err != nil { + return nil, fmt.Errorf("cannot listen on localhost: %v", err) + } + srv := &Server{ + listener: l, + url: "http://" + l.Addr().String(), + lbs: make(map[string]*elb.LoadBalancerDescription), + instanceStates: make(map[string][]*elb.InstanceState), + } + go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + srv.serveHTTP(w, req) + })) + return srv, nil +} + +// Quit closes down the server. +func (srv *Server) Quit() { + srv.listener.Close() +} + +// URL returns the URL of the server. +func (srv *Server) URL() string { + return srv.url +} + +type xmlErrors struct { + XMLName string `xml:"ErrorResponse"` + Error elb.Error +} + +func (srv *Server) error(w http.ResponseWriter, err *elb.Error) { + w.WriteHeader(err.StatusCode) + xmlErr := xmlErrors{Error: *err} + if e := xml.NewEncoder(w).Encode(xmlErr); e != nil { + panic(e) + } +} + +func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) { + req.ParseForm() + srv.mutex.Lock() + defer srv.mutex.Unlock() + f := actions[req.Form.Get("Action")] + if f == nil { + srv.error(w, &elb.Error{ + StatusCode: 400, + Code: "InvalidParameterValue", + Message: "Unrecognized Action", + }) + } + reqId := fmt.Sprintf("req%0X", srv.reqId) + srv.reqId++ + if resp, err := f(srv, w, req, reqId); err == nil { + if err := xml.NewEncoder(w).Encode(resp); err != nil { + panic(err) + } + } else { + switch err.(type) { + case *elb.Error: + srv.error(w, err.(*elb.Error)) + default: + panic(err) + } + } +} + +func (srv *Server) createLoadBalancer(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + composition := map[string]string{ + "AvailabilityZones.member.1": "Subnets.member.1", + } + if err := srv.validateComposition(req, composition); err != nil { + return nil, err + } + required := []string{ + "Listeners.member.1.InstancePort", + "Listeners.member.1.InstanceProtocol", + "Listeners.member.1.Protocol", + "Listeners.member.1.LoadBalancerPort", + "LoadBalancerName", + } + if err := srv.validate(req, required); err != nil { + return nil, err + } + path := req.FormValue("Path") + if path == "" { + path = "/" + } + lbName := req.FormValue("LoadBalancerName") + srv.lbs[lbName] = srv.makeLoadBalancerDescription(req.Form) + srv.lbs[lbName].DNSName = fmt.Sprintf("%s-some-aws-stuff.us-east-1.elb.amazonaws.com", lbName) + return elb.CreateLoadBalancerResp{ + DNSName: srv.lbs[lbName].DNSName, + }, nil +} + +func (srv *Server) deleteLoadBalancer(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"LoadBalancerName"}); err != nil { + return nil, err + } + srv.RemoveLoadBalancer(req.FormValue("LoadBalancerName")) + return elb.SimpleResp{RequestId: reqId}, nil +} + +func (srv *Server) registerInstancesWithLoadBalancer(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + required := []string{"LoadBalancerName", "Instances.member.1.InstanceId"} + if err := srv.validate(req, required); err != nil { + return nil, err + } + lbName := req.FormValue("LoadBalancerName") + if err := srv.lbExists(lbName); err != nil { + return nil, err + } + instIds := []string{} + instances := []elb.Instance{} + i := 1 + instId := req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i)) + for instId != "" { + if err := srv.instanceExists(instId); err != nil { + return nil, err + } + instIds = append(instIds, instId) + instances = append(instances, elb.Instance{InstanceId: instId}) + i++ + instId = req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i)) + } + srv.instanceStates[lbName] = append(srv.instanceStates[lbName], srv.makeInstanceState(instId)) + srv.lbs[lbName].Instances = append(srv.lbs[lbName].Instances, instances...) + return elb.RegisterInstancesResp{InstanceIds: instIds}, nil +} + +func (srv *Server) deregisterInstancesFromLoadBalancer(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + required := []string{"LoadBalancerName"} + if err := srv.validate(req, required); err != nil { + return nil, err + } + lbName := req.FormValue("LoadBalancerName") + if err := srv.lbExists(lbName); err != nil { + return nil, err + } + i := 1 + lb := srv.lbs[lbName] + instId := req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i)) + for instId != "" { + if err := srv.instanceExists(instId); err != nil { + return nil, err + } + i++ + removeInstanceFromLB(lb, instId) + instId = req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i)) + } + srv.lbs[lbName] = lb + srv.removeInstanceStatesFromLoadBalancer(lbName, instId) + return elb.SimpleResp{RequestId: reqId}, nil +} + +func (srv *Server) describeLoadBalancers(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + i := 1 + lbName := req.FormValue(fmt.Sprintf("LoadBalancerNames.member.%d", i)) + for lbName != "" { + key := fmt.Sprintf("LoadBalancerNames.member.%d", i) + if req.FormValue(key) != "" { + if err := srv.lbExists(req.FormValue(key)); err != nil { + return nil, err + } + } + i++ + lbName = req.FormValue(fmt.Sprintf("LoadBalancerNames.member.%d", i)) + } + lbsDesc := make([]elb.LoadBalancerDescription, len(srv.lbs)) + i = 0 + for _, lb := range srv.lbs { + lbsDesc[i] = *lb + i++ + } + resp := elb.DescribeLoadBalancerResp{ + LoadBalancerDescriptions: lbsDesc, + } + return resp, nil +} + +// getParameters returns the value all parameters from a request that matches a +// prefix. +// +// For example, for the prefix "Subnets.member.", it will return a slice +// containing the value of keys "Subnets.member.1", "Subnets.member.2" ... +// "Subnets.member.N". The prefix must include the trailing dot. +func (srv *Server) getParameters(prefix string, values url.Values) []string { + i, key := 1, "" + var k = func(n int) string { + return fmt.Sprintf(prefix+"%d", n) + } + var result []string + for i, key = 2, k(i); values.Get(key) != ""; i, key = i+1, k(i) { + result = append(result, values.Get(key)) + } + return result +} + +func (srv *Server) makeInstanceState(id string) *elb.InstanceState { + return &elb.InstanceState{ + Description: "Instance is in pending state.", + InstanceId: id, + State: "OutOfService", + ReasonCode: "Instance", + } +} + +func removeInstanceFromLB(lb *elb.LoadBalancerDescription, id string) { + index := -1 + for i, instance := range lb.Instances { + if instance.InstanceId == id { + index = i + break + } + } + if index > -1 { + copy(lb.Instances[index:], lb.Instances[index+1:]) + lb.Instances = lb.Instances[:len(lb.Instances)-1] + } +} + +func (srv *Server) removeInstanceStatesFromLoadBalancer(lb, id string) { + for i, state := range srv.instanceStates[lb] { + if state.InstanceId == id { + a := srv.instanceStates[lb] + a[i], a = a[len(a)-1], a[:len(a)-1] + srv.instanceStates[lb] = a + return + } + } +} + +func (srv *Server) makeLoadBalancerDescription(value url.Values) *elb.LoadBalancerDescription { + lds := []elb.ListenerDescription{} + i := 1 + protocol := value.Get(fmt.Sprintf("Listeners.member.%d.Protocol", i)) + for protocol != "" { + key := fmt.Sprintf("Listeners.member.%d.", i) + lInstPort, _ := strconv.Atoi(value.Get(key + "InstancePort")) + lLBPort, _ := strconv.Atoi(value.Get(key + "LoadBalancerPort")) + lDescription := elb.ListenerDescription{ + Listener: elb.Listener{ + Protocol: strings.ToUpper(protocol), + InstanceProtocol: strings.ToUpper(value.Get(key + "InstanceProtocol")), + LoadBalancerPort: lLBPort, + InstancePort: lInstPort, + }, + } + i++ + protocol = value.Get(fmt.Sprintf("Listeners.member.%d.Protocol", i)) + lds = append(lds, lDescription) + } + sourceSecGroup := srv.makeSourceSecGroup(value) + lbDesc := elb.LoadBalancerDescription{ + AvailabilityZones: srv.getParameters("AvailabilityZones.member.", value), + Subnets: srv.getParameters("Subnets.member.", value), + SecurityGroups: srv.getParameters("SecurityGroups.member.", value), + HealthCheck: srv.makeHealthCheck(value), + ListenerDescriptions: lds, + Scheme: value.Get("Scheme"), + SourceSecurityGroup: sourceSecGroup, + LoadBalancerName: value.Get("LoadBalancerName"), + } + if lbDesc.Scheme == "" { + lbDesc.Scheme = "internet-facing" + } + return &lbDesc +} + +func (srv *Server) makeHealthCheck(value url.Values) elb.HealthCheck { + ht := 10 + timeout := 5 + ut := 2 + interval := 30 + target := "TCP:80" + if v := value.Get("HealthCheck.HealthyThreshold"); v != "" { + ht, _ = strconv.Atoi(v) + } + if v := value.Get("HealthCheck.Timeout"); v != "" { + timeout, _ = strconv.Atoi(v) + } + if v := value.Get("HealthCheck.UnhealthyThreshold"); v != "" { + ut, _ = strconv.Atoi(v) + } + if v := value.Get("HealthCheck.Interval"); v != "" { + interval, _ = strconv.Atoi(v) + } + if v := value.Get("HealthCheck.Target"); v != "" { + target = v + } + return elb.HealthCheck{ + HealthyThreshold: ht, + Interval: interval, + Target: target, + Timeout: timeout, + UnhealthyThreshold: ut, + } +} + +func (srv *Server) makeSourceSecGroup(value url.Values) elb.SourceSecurityGroup { + name := "amazon-elb-sg" + alias := "amazon-elb" + if v := value.Get("SourceSecurityGroup.GroupName"); v != "" { + name = v + } + if v := value.Get("SourceSecurityGroup.OwnerAlias"); v != "" { + alias = v + } + return elb.SourceSecurityGroup{ + GroupName: name, + OwnerAlias: alias, + } +} + +func (srv *Server) describeInstanceHealth(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.lbExists(req.FormValue("LoadBalancerName")); err != nil { + return nil, err + } + resp := elb.DescribeInstanceHealthResp{ + InstanceStates: []elb.InstanceState{}, + } + for _, state := range srv.instanceStates[req.FormValue("LoadBalancerName")] { + resp.InstanceStates = append(resp.InstanceStates, *state) + } + i := 1 + instanceId := req.FormValue("Instances.member.1.InstanceId") + for instanceId != "" { + if err := srv.instanceExists(instanceId); err != nil { + return nil, err + } + is := elb.InstanceState{ + Description: "Instance is in pending state.", + InstanceId: instanceId, + State: "OutOfService", + ReasonCode: "Instance", + } + resp.InstanceStates = append(resp.InstanceStates, is) + i++ + instanceId = req.FormValue(fmt.Sprintf("Instances.member.%d.InstanceId", i)) + } + return resp, nil +} + +func (srv *Server) configureHealthCheck(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + required := []string{ + "LoadBalancerName", + "HealthCheck.HealthyThreshold", + "HealthCheck.Interval", + "HealthCheck.Target", + "HealthCheck.Timeout", + "HealthCheck.UnhealthyThreshold", + } + if err := srv.validate(req, required); err != nil { + return nil, err + } + target := req.FormValue("HealthCheck.Target") + r, err := regexp.Compile(`[\w]+:[\d]+\/+`) + if err != nil { + panic(err) + } + if m := r.FindStringSubmatch(target); m == nil { + return nil, &elb.Error{ + StatusCode: 400, + Code: "ValidationError", + Message: "HealthCheck HTTP Target must specify a port followed by a path that begins with a slash. e.g. HTTP:80/ping/this/path", + } + } + ht, _ := strconv.Atoi(req.FormValue("HealthCheck.HealthyThreshold")) + interval, _ := strconv.Atoi(req.FormValue("HealthCheck.Interval")) + timeout, _ := strconv.Atoi(req.FormValue("HealthCheck.Timeout")) + ut, _ := strconv.Atoi(req.FormValue("HealthCheck.UnhealthyThreshold")) + return elb.HealthCheckResp{ + HealthCheck: &elb.HealthCheck{ + HealthyThreshold: ht, + Interval: interval, + Target: target, + Timeout: timeout, + UnhealthyThreshold: ut, + }, + }, nil +} + +func (srv *Server) instanceExists(id string) error { + for _, instId := range srv.instances { + if instId == id { + return nil + } + } + return &elb.Error{ + StatusCode: 400, + Code: "InvalidInstance", + Message: fmt.Sprintf("InvalidInstance found in [%s]. Invalid id: \"%s\"", id, id), + } +} + +func (srv *Server) lbExists(name string) error { + if _, ok := srv.lbs[name]; !ok { + return &elb.Error{ + StatusCode: 400, + Code: "LoadBalancerNotFound", + Message: fmt.Sprintf("There is no ACTIVE Load Balancer named '%s'", name), + } + } + return nil +} + +func (srv *Server) validate(req *http.Request, required []string) error { + for _, field := range required { + if req.FormValue(field) == "" { + return &elb.Error{ + StatusCode: 400, + Code: "ValidationError", + Message: fmt.Sprintf("%s is required.", field), + } + } + } + return nil +} + +// Validates the composition of the fields. +// +// Some fields cannot be together in the same request, such as AvailabilityZones and Subnets. +// A sample map with the above requirement would be +// c := map[string]string{ +// "AvailabilityZones.member.1": "Subnets.member.1", +// } +// +// The server also requires that at least one of those fields are specified. +func (srv *Server) validateComposition(req *http.Request, composition map[string]string) error { + for k, v := range composition { + if req.FormValue(k) != "" && req.FormValue(v) != "" { + return &elb.Error{ + StatusCode: 400, + Code: "ValidationError", + Message: fmt.Sprintf("Only one of %s or %s may be specified", k, v), + } + } + if req.FormValue(k) == "" && req.FormValue(v) == "" { + return &elb.Error{ + StatusCode: 400, + Code: "ValidationError", + Message: fmt.Sprintf("Either %s or %s must be specified", k, v), + } + } + } + return nil +} + +// Creates a fake instance in the server +func (srv *Server) NewInstance() string { + srv.instCount++ + instId := fmt.Sprintf("i-%d", srv.instCount) + srv.instances = append(srv.instances, instId) + return instId +} + +// Removes a fake instance from the server +// +// If no instance is found it does nothing +func (srv *Server) RemoveInstance(instId string) { + for i, id := range srv.instances { + if id == instId { + srv.instances[i], srv.instances = srv.instances[len(srv.instances)-1], srv.instances[:len(srv.instances)-1] + } + } +} + +// Creates a fake load balancer in the fake server +func (srv *Server) NewLoadBalancer(name string) { + srv.lbs[name] = &elb.LoadBalancerDescription{ + LoadBalancerName: name, + DNSName: fmt.Sprintf("%s-some-aws-stuff.sa-east-1.amazonaws.com", name), + } +} + +// Removes a fake load balancer from the fake server +func (srv *Server) RemoveLoadBalancer(name string) { + delete(srv.lbs, name) +} + +// Register a fake instance with a fake Load Balancer +// +// If the Load Balancer does not exists it does nothing +func (srv *Server) RegisterInstance(instId, lbName string) { + lb, ok := srv.lbs[lbName] + if !ok { + fmt.Println("lb not found :/") + return + } + lb.Instances = append(lb.Instances, elb.Instance{InstanceId: instId}) + srv.instanceStates[lbName] = append(srv.instanceStates[lbName], srv.makeInstanceState(instId)) +} + +func (srv *Server) DeregisterInstance(instId, lbName string) { + removeInstanceFromLB(srv.lbs[lbName], instId) + srv.removeInstanceStatesFromLoadBalancer(lbName, instId) +} + +func (srv *Server) ChangeInstanceState(lb string, state elb.InstanceState) { + states := srv.instanceStates[lb] + for i, s := range states { + if s.InstanceId == state.InstanceId { + srv.instanceStates[lb][i] = &state + return + } + } +} + +var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) (interface{}, error){ + "CreateLoadBalancer": (*Server).createLoadBalancer, + "DeleteLoadBalancer": (*Server).deleteLoadBalancer, + "RegisterInstancesWithLoadBalancer": (*Server).registerInstancesWithLoadBalancer, + "DeregisterInstancesFromLoadBalancer": (*Server).deregisterInstancesFromLoadBalancer, + "DescribeLoadBalancers": (*Server).describeLoadBalancers, + "DescribeInstanceHealth": (*Server).describeInstanceHealth, + "ConfigureHealthCheck": (*Server).configureHealthCheck, +} diff --git a/vendor/github.com/goamz/goamz/elb/export_test.go b/vendor/github.com/goamz/goamz/elb/export_test.go new file mode 100644 index 000000000..49a2d50fe --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/export_test.go @@ -0,0 +1,9 @@ +package elb + +import ( + "github.com/goamz/goamz/aws" +) + +func Sign(auth aws.Auth, method, path string, params map[string]string, host string) { + sign(auth, method, path, params, host) +} diff --git a/vendor/github.com/goamz/goamz/elb/response_test.go b/vendor/github.com/goamz/goamz/elb/response_test.go new file mode 100644 index 000000000..637d1e140 --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/response_test.go @@ -0,0 +1,234 @@ +package elb_test + +var CreateLoadBalancer = ` + + + testlb-339187009.us-east-1.elb.amazonaws.com + + + 0c3a8e29-490e-11e2-8647-e14ad5151f1f + + +` + +var CreateLoadBalancerBadRequest = ` + + + Sender + ValidationError + Only one of SubnetIds or AvailabilityZones may be specified + + 159253fc-49dc-11e2-a47d-cde463c91a3c + +` + +var DeleteLoadBalancer = ` + + + + 8d7223db-49d7-11e2-bba9-35ba56032fe1 + + +` + +var RegisterInstancesWithLoadBalancer = ` + + + + + i-b44db8ca + + + i-461ecf38 + + + + + 0fc82478-49e1-11e2-b947-8768f15220aa + + +` + +var RegisterInstancesWithLoadBalancerBadRequest = ` + + + Sender + LoadBalancerNotFound + There is no ACTIVE Load Balancer named 'absentLB' + + 19a0bb97-49f7-11e2-90b4-6bb9ec8331bf + +` + +var DeregisterInstancesFromLoadBalancer = ` + + + + + + d6490837-49fd-11e2-bba9-35ba56032fe1 + + +` + +var DeregisterInstancesFromLoadBalancerBadRequest = ` + + + Sender + LoadBalancerNotFound + There is no ACTIVE Load Balancer named 'absentlb' + + 498e2b4a-4aa1-11e2-8839-d19a879f2eec + +` + +var DescribeLoadBalancers = ` + + + + + + 2012-12-27T11:51:52.970Z + testlb + + 30 + TCP:80 + 10 + 5 + 2 + + + + + + HTTP + 80 + HTTP + 80 + + + + + + + + + + + us-east-1a + + testlb-2087227216.us-east-1.elb.amazonaws.com + Z3DZXE0Q79N41H + internet-facing + + amazon-elb + amazon-elb-sg + + testlb-2087227216.us-east-1.elb.amazonaws.com + + + + + + + e2e81963-5055-11e2-99c7-434205631d9b + + +` + +var DescribeLoadBalancersBadRequest = ` + + + Sender + LoadBalancerNotFound + Cannot find Load Balancer absentlb + + f14f348e-50f7-11e2-9831-f770dd71c209 + +` + +var DescribeInstanceHealth = ` + + + + + Instance registration is still in progress. + i-b44db8ca + OutOfService + ELB + + + + + da0d0f9e-5669-11e2-9f81-319facce7423 + + +` + +var DescribeInstanceHealthBadRequest = ` + + + Sender + InvalidInstance + Could not find EC2 instance i-foooo. + + 352e00d6-566c-11e2-a46d-313272bbb522 + +` + +var ConfigureHealthCheck = ` + + + + 30 + HTTP:80/ + 10 + 5 + 2 + + + + a882d12c-5694-11e2-b647-594652c9487c + + +` + +var ConfigureHealthCheckBadRequest = ` + + + Sender + LoadBalancerNotFound + There is no ACTIVE Load Balancer named 'foolb' + + 2d9fe4a5-5697-11e2-9415-e325c02171d7 + +` + +var AddTagsSuccessResponse = ` + + + + 360e81f7-1100-11e4-b6ed-0f30SOME-SAUCY-EXAMPLE + + +` + +var TagsBadRequest = ` + + + Sender + InvalidParameterValue + An invalid or out-of-range value was supplied for the input parameter. + + terrible-request-id + +` + +var RemoveTagsSuccessResponse = ` + + + + 83c88b9d-12b7-11e3-8b82-87b12DIFFEXAMPLE + + +` diff --git a/vendor/github.com/goamz/goamz/elb/sign.go b/vendor/github.com/goamz/goamz/elb/sign.go new file mode 100644 index 000000000..b7da14772 --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/sign.go @@ -0,0 +1,35 @@ +package elb + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "github.com/goamz/goamz/aws" + "sort" + "strings" +) + +var b64 = base64.StdEncoding + +func sign(auth aws.Auth, method, path string, params map[string]string, host string) { + params["AWSAccessKeyId"] = auth.AccessKey + params["SignatureVersion"] = "2" + params["SignatureMethod"] = "HmacSHA256" + + var keys, sarray []string + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(params[k])) + } + joined := strings.Join(sarray, "&") + payload := method + "\n" + host + "\n" + path + "\n" + joined + hash := hmac.New(sha256.New, []byte(auth.SecretKey)) + hash.Write([]byte(payload)) + signature := make([]byte, b64.EncodedLen(hash.Size())) + b64.Encode(signature, hash.Sum(nil)) + + params["Signature"] = string(signature) +} diff --git a/vendor/github.com/goamz/goamz/elb/sign_test.go b/vendor/github.com/goamz/goamz/elb/sign_test.go new file mode 100644 index 000000000..0dda1667e --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/sign_test.go @@ -0,0 +1,66 @@ +package elb_test + +import ( + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/elb" + . "gopkg.in/check.v1" +) + +var testAuth = aws.Auth{AccessKey: "user", SecretKey: "secret"} + +func (s *S) TestBasicSignature(c *C) { + params := map[string]string{} + elb.Sign(testAuth, "GET", "/path", params, "localhost") + c.Assert(params["SignatureVersion"], Equals, "2") + c.Assert(params["SignatureMethod"], Equals, "HmacSHA256") + expected := "6lSe5QyXum0jMVc7cOUz32/52ZnL7N5RyKRk/09yiK4=" + c.Assert(params["Signature"], Equals, expected) +} + +func (s *S) TestParamSignature(c *C) { + params := map[string]string{ + "param1": "value1", + "param2": "value2", + "param3": "value3", + } + elb.Sign(testAuth, "GET", "/path", params, "localhost") + expected := "XWOR4+0lmK8bD8CGDGZ4kfuSPbb2JibLJiCl/OPu1oU=" + c.Assert(params["Signature"], Equals, expected) +} + +func (s *S) TestManyParams(c *C) { + params := map[string]string{ + "param1": "value10", + "param2": "value2", + "param3": "value3", + "param4": "value4", + "param5": "value5", + "param6": "value6", + "param7": "value7", + "param8": "value8", + "param9": "value9", + "param10": "value1", + } + elb.Sign(testAuth, "GET", "/path", params, "localhost") + expected := "di0sjxIvezUgQ1SIL6i+C/H8lL+U0CQ9frLIak8jkVg=" + c.Assert(params["Signature"], Equals, expected) +} + +func (s *S) TestEscaping(c *C) { + params := map[string]string{"Nonce": "+ +"} + elb.Sign(testAuth, "GET", "/path", params, "localhost") + c.Assert(params["Nonce"], Equals, "+ +") + expected := "bqffDELReIqwjg/W0DnsnVUmfLK4wXVLO4/LuG+1VFA=" + c.Assert(params["Signature"], Equals, expected) +} + +func (s *S) TestSignatureExample1(c *C) { + params := map[string]string{ + "Timestamp": "2009-02-01T12:53:20+00:00", + "Version": "2007-11-07", + "Action": "ListDomains", + } + elb.Sign(aws.Auth{AccessKey: "access", SecretKey: "secret"}, "GET", "/", params, "sdb.amazonaws.com") + expected := "okj96/5ucWBSc1uR2zXVfm6mDHtgfNv657rRtt/aunQ=" + c.Assert(params["Signature"], Equals, expected) +} diff --git a/vendor/github.com/goamz/goamz/elb/suite_test.go b/vendor/github.com/goamz/goamz/elb/suite_test.go new file mode 100644 index 000000000..fe4c81d47 --- /dev/null +++ b/vendor/github.com/goamz/goamz/elb/suite_test.go @@ -0,0 +1,119 @@ +package elb_test + +import ( + "fmt" + "net/http" + "net/url" + "os" + "testing" + "time" + + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +type HTTPSuite struct{} + +var testServer = NewTestHTTPServer("http://localhost:4444", 5*time.Second) + +func (s *HTTPSuite) SetUpSuite(c *C) { + testServer.Start() +} + +func (s *HTTPSuite) TearDownTest(c *C) { + testServer.FlushRequests() +} + +type TestHTTPServer struct { + URL string + Timeout time.Duration + started bool + request chan *http.Request + response chan *testResponse + pending chan bool +} + +type testResponse struct { + Status int + Headers map[string]string + Body string +} + +func NewTestHTTPServer(url string, timeout time.Duration) *TestHTTPServer { + return &TestHTTPServer{URL: url, Timeout: timeout} +} + +func (s *TestHTTPServer) Start() { + if s.started { + return + } + s.started = true + + s.request = make(chan *http.Request, 64) + s.response = make(chan *testResponse, 64) + s.pending = make(chan bool, 64) + + url, _ := url.Parse(s.URL) + go http.ListenAndServe(url.Host, s) + + s.PrepareResponse(202, nil, "Nothing.") + for { + // Wait for it to be up. + resp, err := http.Get(s.URL) + if err == nil && resp.StatusCode == 202 { + break + } + time.Sleep(1e8) + } + s.WaitRequest() // Consume dummy request. +} + +// FlushRequests discards requests which were not yet consumed by WaitRequest. +func (s *TestHTTPServer) FlushRequests() { + for { + select { + case <-s.request: + default: + return + } + } +} + +func (s *TestHTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s.request <- req + var resp *testResponse + select { + case resp = <-s.response: + case <-time.After(s.Timeout): + fmt.Fprintf(os.Stderr, "ERROR: Timeout waiting for test to provide response\n") + resp = &testResponse{500, nil, ""} + } + if resp.Headers != nil { + h := w.Header() + for k, v := range resp.Headers { + h.Set(k, v) + } + } + if resp.Status != 0 { + w.WriteHeader(resp.Status) + } + w.Write([]byte(resp.Body)) +} + +func (s *TestHTTPServer) WaitRequest() *http.Request { + select { + case req := <-s.request: + req.ParseForm() + return req + case <-time.After(s.Timeout): + panic("Timeout waiting for goamz request") + } + panic("unreached") +} + +func (s *TestHTTPServer) PrepareResponse(status int, headers map[string]string, body string) { + s.response <- &testResponse{status, headers, body} +} diff --git a/vendor/github.com/goamz/goamz/exp/mturk/example_test.go b/vendor/github.com/goamz/goamz/exp/mturk/example_test.go new file mode 100644 index 000000000..806ce95fd --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/mturk/example_test.go @@ -0,0 +1,66 @@ +package mturk_test + +import ( + "fmt" + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/exp/mturk" +) + +var turk *mturk.MTurk + +func ExampleNew() { + // These are your AWS tokens. Note that Turk do not support IAM. + // So you'll have to use your main profile's tokens. + var auth = aws.Auth{AccessKey: "", SecretKey: ""} + turk = mturk.New(auth, true) // true to use sandbox mode +} + +func Examplemturk_CreateHIT_withExternalQuestion() { + question := mturk.ExternalQuestion{ + ExternalURL: "http://www.amazon.com", + FrameHeight: 200, + } + reward := mturk.Price{ + Amount: "0.01", + CurrencyCode: "USD", + } + + hit, err := turk.CreateHIT("title", "description", question, reward, 30, 30, "key1,key2", 3, nil, "annotation") + + if err == nil { + fmt.Println(hit) + } +} + +func Examplemturk_CreateHIT_withHTMLQuestion() { + question := mturk.HTMLQuestion{ + HTMLContent: mturk.HTMLContent{` + + + + + + +
+ +

What's up?

+

+

+ + + +]]>`}, + FrameHeight: 200, + } + reward := mturk.Price{ + Amount: "0.01", + CurrencyCode: "USD", + } + + hit, err := turk.CreateHIT("title", "description", question, reward, 30, 30, "key1,key2", 3, nil, "") + + if err == nil { + fmt.Println(hit) + } +} diff --git a/vendor/github.com/goamz/goamz/exp/mturk/export_test.go b/vendor/github.com/goamz/goamz/exp/mturk/export_test.go new file mode 100644 index 000000000..736678a56 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/mturk/export_test.go @@ -0,0 +1,9 @@ +package mturk + +import ( + "github.com/goamz/goamz/aws" +) + +func Sign(auth aws.Auth, service, method, timestamp string, params map[string]string) { + sign(auth, service, method, timestamp, params) +} diff --git a/vendor/github.com/goamz/goamz/exp/mturk/mturk.go b/vendor/github.com/goamz/goamz/exp/mturk/mturk.go new file mode 100644 index 000000000..fbbf700c2 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/mturk/mturk.go @@ -0,0 +1,480 @@ +// +// goamz - Go packages to interact with the Amazon Web Services. +// +// https://wiki.ubuntu.com/goamz +// +// Copyright (c) 2011 Canonical Ltd. +// +// Written by Graham Miller + +// This package is in an experimental state, and does not currently +// follow conventions and style of the rest of goamz or common +// Go conventions. It must be polished before it's considered a +// first-class package in goamz. +package mturk + +import ( + "encoding/xml" + "errors" + "fmt" + "github.com/goamz/goamz/aws" + "net/http" + //"net/http/httputil" + "net/url" + "strconv" + "strings" + "time" +) + +type MTurk struct { + aws.Auth + URL *url.URL +} + +func New(auth aws.Auth, sandbox bool) *MTurk { + mt := &MTurk{Auth: auth} + var err error + if sandbox { + mt.URL, err = url.Parse("https://mechanicalturk.sandbox.amazonaws.com/") + } else { + mt.URL, err = url.Parse("https://mechanicalturk.amazonaws.com/") + } + if err != nil { + panic(err.Error()) + } + return mt +} + +// ---------------------------------------------------------------------------- +// Request dispatching logic. + +// Error encapsulates an error returned by MTurk. +type Error struct { + StatusCode int // HTTP status code (200, 403, ...) + Code string // EC2 error code ("UnsupportedOperation", ...) + Message string // The human-oriented error message + RequestId string +} + +func (err *Error) Error() string { + return err.Message +} + +// The request stanza included in several response types, for example +// in a "CreateHITResponse". http://goo.gl/qGeKf +type xmlRequest struct { + RequestId string + IsValid string + Errors []Error `xml:"Errors>Error"` +} + +/* +A Price represents an amount of money in a given currency. + +Reference: +http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_PriceDataStructureArticle.html +*/ +type Price struct { + // The amount of money, as a number. The amount is in the currency specified + // by the CurrencyCode. For example, if CurrencyCode is USD, the amount will + // be in United States dollars (e.g. 12.75 is $12.75 US). + Amount string + + // A code that represents the country and units of the currency. Its value is + // Type an ISO 4217 currency code, such as USD for United States dollars. + // + // Constraints: Currently only USD is supported. + CurrencyCode string + + // A textual representation of the price, using symbols and formatting + // appropriate for the currency. Symbols are represented using the Unicode + // character set. You do not need to specify FormattedPrice in a request. + // It is only provided by the service in responses, as a convenience to + // your application. + FormattedPrice string +} + +/* +Really just a country string. + +Reference: + +- http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_LocaleDataStructureArticle.html +- http://www.iso.org/iso/country_codes/country_codes +*/ +type Locale string + +/* +A QualificationRequirement describes a Qualification a Worker must +have before the Worker is allowed to accept a HIT. A requirement may optionally +state that a Worker must have the Qualification to preview the HIT. + +Reference: + +http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_QualificationRequirementDataStructureArticle.html +*/ +type QualificationRequirement struct { + // The ID of the Qualification type for the requirement. + // See http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_QualificationRequirementDataStructureArticle.html#ApiReference_QualificationType-IDs + QualificationTypeId string + + // The kind of comparison to make against a Qualification's value. + // Two values can be compared to see if one value is "LessThan", + // "LessThanOrEqualTo", "GreaterThan", "GreaterThanOrEqualTo", "EqualTo", or + // "NotEqualTo" the other. A Qualification requirement can also test if a + // Qualification "Exists" in the user's profile, regardless of its value. + Comparator string + + // The integer value to compare against the Qualification's value. + IntegerValue int + + // The locale value to compare against the Qualification's value, if the + // Qualification being compared is the locale Qualification. + LocaleValue Locale + + // If true, the question data for the HIT will not be shown when a Worker + // whose Qualifications do not meet this requirement tries to preview the HIT. + // That is, a Worker's Qualifications must meet all of the requirements for + // which RequiredToPreview is true in order to preview the HIT. + // + // If a Worker meets all of the requirements where RequiredToPreview is true + // (or if there are no such requirements), but does not meet all of the + // requirements for the HIT, the Worker will be allowed to preview the HIT's + // question data, but will not be allowed to accept and complete the HIT. + RequiredToPreview bool +} + +// Data structure holding the contents of an "external" +// question. http://goo.gl/NP8Aa +type ExternalQuestion struct { + XMLName xml.Name `xml:"http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd ExternalQuestion"` + ExternalURL string + FrameHeight int +} + +// Holds the html content of the HTMLQuestion. +type HTMLContent struct { + Content string `xml:",innerxml"` +} + +// Data structure holding the contents of an "html" +// question. http://goo.gl/hQn5An +type HTMLQuestion struct { + XMLName xml.Name `xml:"http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2011-11-11/HTMLQuestion.xsd HTMLQuestion"` + HTMLContent HTMLContent + + FrameHeight int +} + +// The data structure representing a "human interface task" (HIT) +// Currently only supports "external" questions, because Go +// structs don't support union types. http://goo.gl/NP8Aa +// This type is returned, for example, from SearchHITs +// http://goo.gl/PskcX +type HIT struct { + Request xmlRequest + + HITId string + HITTypeId string + CreationTime string + Title string + Description string + Keywords string + HITStatus string + Reward Price + LifetimeInSeconds uint + AssignmentDurationInSeconds uint + MaxAssignments uint + AutoApprovalDelayInSeconds uint + QualificationRequirement QualificationRequirement + Question interface{} + RequesterAnnotation string + NumberofSimilarHITs uint + HITReviewStatus string + NumberOfAssignmentsPending uint + NumberOfAssignmentsAvailable uint + NumberOfAssignmentsCompleted uint +} + +// The main data structure returned by SearchHITs +// http://goo.gl/PskcX +type SearchHITsResult struct { + NumResults uint + PageNumber uint + TotalNumResults uint + HITs []HIT `xml:"HIT"` +} + +// The wrapper data structure returned by SearchHITs +// http://goo.gl/PskcX +type SearchHITsResponse struct { + RequestId string `xml:"OperationRequest>RequestId"` + SearchHITsResult SearchHITsResult +} + +// The wrapper data structure returned by CreateHIT +// http://goo.gl/PskcX +type CreateHITResponse struct { + RequestId string `xml:"OperationRequest>RequestId"` + HIT HIT +} + +type Assignment struct { + AssignmentId string + WorkerId string + HITId string + AssignmentStatus string + AutoApprovalTime string + AcceptTime string + SubmitTime string + ApprovalTime string + Answer string +} + +func (a Assignment) Answers() (answers map[string]string) { + answers = make(map[string]string) + + decoder := xml.NewDecoder(strings.NewReader(a.Answer)) + + for { + token, _ := decoder.Token() + if token == nil { + break + } + switch startElement := token.(type) { + case xml.StartElement: + if startElement.Name.Local == "Answer" { + var answer struct { + QuestionIdentifier string + FreeText string + } + + decoder.DecodeElement(&answer, &startElement) + answers[answer.QuestionIdentifier] = answer.FreeText + } + } + } + + return +} + +type GetAssignmentsForHITResponse struct { + RequestId string `xml:"OperationRequest>RequestId"` + GetAssignmentsForHITResult struct { + Request xmlRequest + NumResults uint + TotalNumResults uint + PageNumber uint + Assignment Assignment + } +} + +/* +CreateHIT corresponds to the "CreateHIT" operation of the Mechanical Turk +API. Currently only supports "external" questions (see "HIT" struct above). + +Here are the detailed description for the parameters: + + title Required. A title should be short and descriptive about the + kind of task the HIT contains. On the Amazon Mechanical Turk + web site, the HIT title appears in search results, and + everywhere the HIT is mentioned. + description Required. A description includes detailed information about the + kind of task the HIT contains. On the Amazon Mechanical Turk + web site, the HIT description appears in the expanded view of + search results, and in the HIT and assignment screens. A good + description gives the user enough information to evaluate the + HIT before accepting it. + question Required. The data the person completing the HIT uses to produce + the results. Consstraints: Must be a QuestionForm data structure, + an ExternalQuestion data structure, or an HTMLQuestion data + structure. The XML question data must not be larger than 64 + kilobytes (65,535 bytes) in size, including whitespace. + reward Required. The amount of money the Requester will pay a Worker + for successfully completing the HIT. + assignmentDurationInSeconds Required. The amount of time, in seconds, that + a Worker has to complete the HIT after accepting + it. If a Worker does not complete the assignment + within the specified duration, the assignment is + considered abandoned. If the HIT is still + active (that is, its lifetime has not elapsed), + the assignment becomes available for other users + to find and accept. Valid Values: any integer + between 30 (30 seconds) and 31536000 (365 days). + lifetimeInSeconds Required. An amount of time, in seconds, after which the + HIT is no longer available for users to accept. After + the lifetime of the HIT elapses, the HIT no longer + appears in HIT searches, even if not all of the + assignments for the HIT have been accepted. Valid Values: + any integer between 30 (30 seconds) and 31536000 (365 days). + keywords One or more words or phrases that describe the HIT, + separated by commas. These words are used in searches to + find HITs. Constraints: cannot be more than 1,000 + characters. + maxAssignments The number of times the HIT can be accepted and completed + before the HIT becomes unavailable. Valid Values: any + integer between 1 and 1000000000 (1 billion). Default: 1 + qualificationRequirement A condition that a Worker's Qualifications must + meet before the Worker is allowed to accept and + complete the HIT. Constraints: no more than 10 + QualificationRequirement for each HIT. + requesterAnnotation An arbitrary data field. The RequesterAnnotation + parameter lets your application attach arbitrary data to + the HIT for tracking purposes. For example, the + RequesterAnnotation parameter could be an identifier + internal to the Requester's application that corresponds + with the HIT. Constraints: must not be longer than 255 + characters in length. + +Reference: +http://docs.aws.amazon.com/AWSMechTurk/latest/AWSMturkAPI/ApiReference_CreateHITOperation.html +*/ +func (mt *MTurk) CreateHIT( + title, description string, + question interface{}, + reward Price, + assignmentDurationInSeconds, + lifetimeInSeconds uint, + keywords string, + maxAssignments uint, + qualificationRequirement *QualificationRequirement, + requesterAnnotation string) (h *HIT, err error) { + + params := make(map[string]string) + params["Title"] = title + params["Description"] = description + params["Question"], err = xmlEncode(&question) + if err != nil { + return + } + params["Reward.1.Amount"] = reward.Amount + params["Reward.1.CurrencyCode"] = reward.CurrencyCode + params["AssignmentDurationInSeconds"] = strconv.FormatUint(uint64(assignmentDurationInSeconds), 10) + + params["LifetimeInSeconds"] = strconv.FormatUint(uint64(lifetimeInSeconds), 10) + if keywords != "" { + params["Keywords"] = keywords + } + if maxAssignments != 0 { + params["MaxAssignments"] = strconv.FormatUint(uint64(maxAssignments), 10) + } + if qualificationRequirement != nil { + params["QualificationRequirement"], err = xmlEncode(qualificationRequirement) + if err != nil { + return + } + } + if requesterAnnotation != "" { + params["RequesterAnnotation"] = requesterAnnotation + } + + var response CreateHITResponse + err = mt.query(params, "CreateHIT", &response) + if err == nil { + h = &response.HIT + } + return +} + +// Corresponds to the "CreateHIT" operation of the Mechanical Turk +// API, using an existing "hit type". http://goo.gl/cDBRc Currently only +// supports "external" questions (see "HIT" struct above). If +// "maxAssignments" or "requesterAnnotation" are the zero value for +// their types, they will not be included in the request. +func (mt *MTurk) CreateHITOfType(hitTypeId string, q ExternalQuestion, lifetimeInSeconds uint, maxAssignments uint, requesterAnnotation string) (h *HIT, err error) { + params := make(map[string]string) + params["HITTypeId"] = hitTypeId + params["Question"], err = xmlEncode(&q) + if err != nil { + return + } + params["LifetimeInSeconds"] = strconv.FormatUint(uint64(lifetimeInSeconds), 10) + if maxAssignments != 0 { + params["MaxAssignments"] = strconv.FormatUint(uint64(maxAssignments), 10) + } + if requesterAnnotation != "" { + params["RequesterAnnotation"] = requesterAnnotation + } + + var response CreateHITResponse + err = mt.query(params, "CreateHIT", &response) + if err == nil { + h = &response.HIT + } + return +} + +// Get the Assignments for a HIT. +func (mt *MTurk) GetAssignmentsForHIT(hitId string) (r *Assignment, err error) { + params := make(map[string]string) + params["HITId"] = hitId + var response GetAssignmentsForHITResponse + err = mt.query(params, "GetAssignmentsForHIT", &response) + if err == nil { + r = &response.GetAssignmentsForHITResult.Assignment + } + return +} + +// Corresponds to "SearchHITs" operation of Mechanical Turk. http://goo.gl/PskcX +// Currenlty supports none of the optional parameters. +func (mt *MTurk) SearchHITs() (s *SearchHITsResult, err error) { + params := make(map[string]string) + var response SearchHITsResponse + err = mt.query(params, "SearchHITs", &response) + if err == nil { + s = &response.SearchHITsResult + } + return +} + +// Adds common parameters to the "params" map, signs the request, +// adds the signature to the "params" map and sends the request +// to the server. It then unmarshals the response in to the "resp" +// parameter using xml.Unmarshal() +func (mt *MTurk) query(params map[string]string, operation string, resp interface{}) error { + service := "AWSMechanicalTurkRequester" + timestamp := time.Now().UTC().Format("2006-01-02T15:04:05Z") + + params["AWSAccessKeyId"] = mt.Auth.AccessKey + params["Service"] = service + params["Timestamp"] = timestamp + params["Operation"] = operation + + // make a copy + url := *mt.URL + + sign(mt.Auth, service, operation, timestamp, params) + url.RawQuery = multimap(params).Encode() + r, err := http.Get(url.String()) + if err != nil { + return err + } + //dump, _ := httputil.DumpResponse(r, true) + //println("DUMP:\n", string(dump)) + if r.StatusCode != 200 { + return errors.New(fmt.Sprintf("%d: unexpected status code", r.StatusCode)) + } + dec := xml.NewDecoder(r.Body) + err = dec.Decode(resp) + r.Body.Close() + return err +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} + +func xmlEncode(i interface{}) (s string, err error) { + var buf []byte + buf, err = xml.Marshal(i) + if err != nil { + return + } + s = string(buf) + return +} diff --git a/vendor/github.com/goamz/goamz/exp/mturk/mturk_test.go b/vendor/github.com/goamz/goamz/exp/mturk/mturk_test.go new file mode 100644 index 000000000..0c546571f --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/mturk/mturk_test.go @@ -0,0 +1,170 @@ +package mturk_test + +import ( + "net/url" + "testing" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/exp/mturk" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + mturk *mturk.MTurk +} + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + u, err := url.Parse(testServer.URL) + if err != nil { + panic(err.Error()) + } + + s.mturk = &mturk.MTurk{ + Auth: auth, + URL: u, + } +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestCreateHITExternalQuestion(c *C) { + testServer.Response(200, nil, BasicHitResponse) + + question := mturk.ExternalQuestion{ + ExternalURL: "http://www.amazon.com", + FrameHeight: 200, + } + reward := mturk.Price{ + Amount: "0.01", + CurrencyCode: "USD", + } + hit, err := s.mturk.CreateHIT("title", "description", question, reward, 1, 2, "key1,key2", 3, nil, "annotation") + + testServer.WaitRequest() + + c.Assert(err, IsNil) + c.Assert(hit, NotNil) + + c.Assert(hit.HITId, Equals, "28J4IXKO2L927XKJTHO34OCDNASCDW") + c.Assert(hit.HITTypeId, Equals, "2XZ7D1X3V0FKQVW7LU51S7PKKGFKDF") +} + +func (s *S) TestCreateHITHTMLQuestion(c *C) { + testServer.Response(200, nil, BasicHitResponse) + + question := mturk.HTMLQuestion{ + HTMLContent: mturk.HTMLContent{` + + + + + + +
+ +

What's up?

+

+

+ + + +]]>`}, + FrameHeight: 200, + } + reward := mturk.Price{ + Amount: "0.01", + CurrencyCode: "USD", + } + hit, err := s.mturk.CreateHIT("title", "description", question, reward, 1, 2, "key1,key2", 3, nil, "annotation") + + testServer.WaitRequest() + + c.Assert(err, IsNil) + c.Assert(hit, NotNil) + + c.Assert(hit.HITId, Equals, "28J4IXKO2L927XKJTHO34OCDNASCDW") + c.Assert(hit.HITTypeId, Equals, "2XZ7D1X3V0FKQVW7LU51S7PKKGFKDF") +} + +func (s *S) TestSearchHITs(c *C) { + testServer.Response(200, nil, SearchHITResponse) + + hitResult, err := s.mturk.SearchHITs() + + c.Assert(err, IsNil) + c.Assert(hitResult, NotNil) + + c.Assert(hitResult.NumResults, Equals, uint(1)) + c.Assert(hitResult.PageNumber, Equals, uint(1)) + c.Assert(hitResult.TotalNumResults, Equals, uint(1)) + + c.Assert(len(hitResult.HITs), Equals, 1) + c.Assert(hitResult.HITs[0].HITId, Equals, "2BU26DG67D1XTE823B3OQ2JF2XWF83") + c.Assert(hitResult.HITs[0].HITTypeId, Equals, "22OWJ5OPB0YV6IGL5727KP9U38P5XR") + c.Assert(hitResult.HITs[0].CreationTime, Equals, "2011-12-28T19:56:20Z") + c.Assert(hitResult.HITs[0].Title, Equals, "test hit") + c.Assert(hitResult.HITs[0].Description, Equals, "please disregard, testing only") + c.Assert(hitResult.HITs[0].HITStatus, Equals, "Reviewable") + c.Assert(hitResult.HITs[0].MaxAssignments, Equals, uint(1)) + c.Assert(hitResult.HITs[0].Reward.Amount, Equals, "0.01") + c.Assert(hitResult.HITs[0].Reward.CurrencyCode, Equals, "USD") + c.Assert(hitResult.HITs[0].AutoApprovalDelayInSeconds, Equals, uint(2592000)) + c.Assert(hitResult.HITs[0].AssignmentDurationInSeconds, Equals, uint(30)) + c.Assert(hitResult.HITs[0].NumberOfAssignmentsPending, Equals, uint(0)) + c.Assert(hitResult.HITs[0].NumberOfAssignmentsAvailable, Equals, uint(1)) + c.Assert(hitResult.HITs[0].NumberOfAssignmentsCompleted, Equals, uint(0)) +} + +func (s *S) TestGetAssignmentsForHIT_NoAnswer(c *C) { + testServer.Response(200, nil, GetAssignmentsForHITNoAnswerResponse) + + assignment, err := s.mturk.GetAssignmentsForHIT("emptyassignment") + + testServer.WaitRequest() + + c.Assert(err, IsNil) + c.Assert(assignment, NotNil) + + c.Assert(assignment.HITId, Equals, "") +} + +func (s *S) TestGetAssignmentsForHIT_Answer(c *C) { + testServer.Response(200, nil, GetAssignmentsForHITAnswerResponse) + + assignment, err := s.mturk.GetAssignmentsForHIT("emptyassignment") + + testServer.WaitRequest() + + c.Assert(err, IsNil) + c.Assert(assignment, NotNil) + + c.Assert(assignment.AssignmentId, Equals, "2QKNTL0XULRGFAQWUWDD05FP94V2O3") + c.Assert(assignment.WorkerId, Equals, "A1ZUQ2YDM61713") + c.Assert(assignment.HITId, Equals, "2W36VCPWZ9RN5DX1MBJ7VN3D6WEPAM") + c.Assert(assignment.AssignmentStatus, Equals, "Submitted") + c.Assert(assignment.AutoApprovalTime, Equals, "2014-02-26T09:39:48Z") + c.Assert(assignment.AcceptTime, Equals, "2014-01-27T09:39:38Z") + c.Assert(assignment.SubmitTime, Equals, "2014-01-27T09:39:48Z") + c.Assert(assignment.ApprovalTime, Equals, "") + + answers := assignment.Answers() + c.Assert(len(answers), Equals, 4) + c.Assert(answers["tags"], Equals, "asd") + c.Assert(answers["text_in_image"], Equals, "asd") + c.Assert(answers["is_pattern"], Equals, "yes") + c.Assert(answers["is_map"], Equals, "yes") +} diff --git a/vendor/github.com/goamz/goamz/exp/mturk/responses_test.go b/vendor/github.com/goamz/goamz/exp/mturk/responses_test.go new file mode 100644 index 000000000..abc483945 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/mturk/responses_test.go @@ -0,0 +1,36 @@ +package mturk_test + +var BasicHitResponse = ` +643b794b-66b6-4427-bb8a-4d3df5c9a20eTrue28J4IXKO2L927XKJTHO34OCDNASCDW2XZ7D1X3V0FKQVW7LU51S7PKKGFKDF +` + +var SearchHITResponse = ` +38862d9c-f015-4177-a2d3-924110a9d6f2True1112BU26DG67D1XTE823B3OQ2JF2XWF8322OWJ5OPB0YV6IGL5727KP9U38P5XR2011-12-28T19:56:20Ztest hitplease disregard, testing onlyReviewable10.01USD$0.0125920002011-12-28T19:56:50Z30010 +` + +var GetAssignmentsForHITNoAnswerResponse = ` +536934be-a35b-4e4e-9822-72fbf36d5862True001 +` + +var GetAssignmentsForHITAnswerResponse = ` +2f113bdf-2e3e-4c5a-a396-3ed01384ecb9True1112QKNTL0XULRGFAQWUWDD05FP94V2O3A1ZUQ2YDM617132W36VCPWZ9RN5DX1MBJ7VN3D6WEPAMSubmitted2014-02-26T09:39:48Z2014-01-27T09:39:38Z2014-01-27T09:39:48Z<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<QuestionFormAnswers xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionFormAnswers.xsd"> +<Answer> +<QuestionIdentifier>tags</QuestionIdentifier> +<FreeText>asd</FreeText> +</Answer> +<Answer> +<QuestionIdentifier>text_in_image</QuestionIdentifier> +<FreeText>asd</FreeText> +</Answer> +<Answer> +<QuestionIdentifier>is_pattern</QuestionIdentifier> +<FreeText>yes</FreeText> +</Answer> +<Answer> +<QuestionIdentifier>is_map</QuestionIdentifier> +<FreeText>yes</FreeText> +</Answer> +</QuestionFormAnswers> + +` diff --git a/vendor/github.com/goamz/goamz/exp/mturk/sign.go b/vendor/github.com/goamz/goamz/exp/mturk/sign.go new file mode 100644 index 000000000..f2ff8cbfe --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/mturk/sign.go @@ -0,0 +1,22 @@ +package mturk + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "github.com/goamz/goamz/aws" +) + +var b64 = base64.StdEncoding + +// ---------------------------------------------------------------------------- +// Mechanical Turk signing (http://goo.gl/wrzfn) +func sign(auth aws.Auth, service, method, timestamp string, params map[string]string) { + payload := service + method + timestamp + hash := hmac.New(sha1.New, []byte(auth.SecretKey)) + hash.Write([]byte(payload)) + signature := make([]byte, b64.EncodedLen(hash.Size())) + b64.Encode(signature, hash.Sum(nil)) + + params["Signature"] = string(signature) +} diff --git a/vendor/github.com/goamz/goamz/exp/mturk/sign_test.go b/vendor/github.com/goamz/goamz/exp/mturk/sign_test.go new file mode 100644 index 000000000..49296eb73 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/mturk/sign_test.go @@ -0,0 +1,19 @@ +package mturk_test + +import ( + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/exp/mturk" + . "gopkg.in/check.v1" +) + +// Mechanical Turk REST authentication docs: http://goo.gl/wrzfn + +var testAuth = aws.Auth{AccessKey: "user", SecretKey: "secret"} + +// == fIJy9wCApBNL2R4J2WjJGtIBFX4= +func (s *S) TestBasicSignature(c *C) { + params := map[string]string{} + mturk.Sign(testAuth, "AWSMechanicalTurkRequester", "CreateHIT", "2012-02-16T20:30:47Z", params) + expected := "b/TnvzrdeD/L/EyzdFrznPXhido=" + c.Assert(params["Signature"], Equals, expected) +} diff --git a/vendor/github.com/goamz/goamz/exp/sdb/export_test.go b/vendor/github.com/goamz/goamz/exp/sdb/export_test.go new file mode 100644 index 000000000..7807a914a --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sdb/export_test.go @@ -0,0 +1,9 @@ +package sdb + +import ( + "github.com/goamz/goamz/aws" +) + +func Sign(auth aws.Auth, method, path string, params map[string][]string, headers map[string][]string) { + sign(auth, method, path, params, headers) +} diff --git a/vendor/github.com/goamz/goamz/exp/sdb/responses_test.go b/vendor/github.com/goamz/goamz/exp/sdb/responses_test.go new file mode 100644 index 000000000..034c2b31c --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sdb/responses_test.go @@ -0,0 +1,120 @@ +package sdb_test + +var TestCreateDomainXmlOK = ` + + + + 63264005-7a5f-e01a-a224-395c63b89f6d + 0.0055590279 + + +` + +var TestListDomainsXmlOK = ` + + + + Account + Domain + Record + + + 15fcaf55-9914-63c2-21f3-951e31193790 + 0.0000071759 + + +` + +var TestListDomainsWithNextTokenXmlOK = ` + + + + Domain1-200706011651 + Domain2-200706011652 + TWV0ZXJpbmdUZXN0RG9tYWluMS0yMDA3MDYwMTE2NTY= + + + eb13162f-1b95-4511-8b12-489b86acfd28 + 0.0000219907 + + +` + +var TestDeleteDomainXmlOK = ` + + + + 039e1e25-9a64-2a74-93da-2fda36122a97 + 0.0055590278 + + +` + +var TestDomainMetadataXmlNoSuchDomain = ` + + + + + NoSuchDomain + The specified domain does not exist. + 0.0000071759 + + + e050cea2-a772-f90e-2cb0-98ebd42c2898 + +` + +var TestPutAttrsXmlOK = ` + + + + 490206ce-8292-456c-a00f-61b335eb202b + 0.0000219907 + + +` + +var TestAttrsXmlOK = ` + + + + ColorBlue + SizeMed + + + b1e8f1f7-42e9-494c-ad09-2674e557526d + 0.0000219942 + + +` + +var TestSelectXmlOK = ` + + + + + Item_03 + CategoryClothes + SubcategoryPants + NameSweatpants + ColorBlue + ColorYellow + ColorPink + SizeLarge + + + Item_06 + CategoryMotorcycle Parts + SubcategoryBodywork + NameFender Eliminator + ColorBlue + MakeYamaha + ModelR1 + + + + b1e8f1f7-42e9-494c-ad09-2674e557526d + 0.0000219907 + + +` diff --git a/vendor/github.com/goamz/goamz/exp/sdb/sdb.go b/vendor/github.com/goamz/goamz/exp/sdb/sdb.go new file mode 100644 index 000000000..0d13f4a9e --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sdb/sdb.go @@ -0,0 +1,413 @@ +// +// goamz - Go packages to interact with the Amazon Web Services. +// +// https://wiki.ubuntu.com/goamz +// +// Copyright (c) 2011 AppsAttic Ltd. +// +// sdb package written by: +// +// Andrew Chilton +// Brad Rydzewski + +// This package is in an experimental state, and does not currently +// follow conventions and style of the rest of goamz or common +// Go conventions. It must be polished before it's considered a +// first-class package in goamz. +package sdb + +// BUG: SelectResp isn't properly organized. It must change. + +// + +import ( + "encoding/xml" + "github.com/goamz/goamz/aws" + "log" + "net/http" + "net/http/httputil" + "net/url" + "strconv" + "time" +) + +const debug = false + +// The SDB type encapsulates operations with a specific SimpleDB region. +type SDB struct { + aws.Auth + aws.Region + private byte // Reserve the right of using private data. +} + +// New creates a new SDB. +func New(auth aws.Auth, region aws.Region) *SDB { + return &SDB{auth, region, 0} +} + +// The Domain type represents a collection of items that are described +// by name-value attributes. +type Domain struct { + *SDB + Name string +} + +// Domain returns a Domain with the given name. +func (sdb *SDB) Domain(name string) *Domain { + return &Domain{sdb, name} +} + +// The Item type represent individual objects that contain one or more +// name-value attributes stored within a SDB Domain as rows. +type Item struct { + *SDB + *Domain + Name string +} + +// Item returns an Item with the given name. +func (domain *Domain) Item(name string) *Item { + return &Item{domain.SDB, domain, name} +} + +// The Attr type represent categories of data that can be assigned to items. +type Attr struct { + Name string + Value string +} + +// ---------------------------------------------------------------------------- +// Service-level operations. + +// --- ListDomains + +// Response to a ListDomains request. +// +// See http://goo.gl/3u0Cf for more details. +type ListDomainsResp struct { + Domains []string `xml:"ListDomainsResult>DomainName"` + NextToken string `xml:"ListDomainsResult>NextToken"` + ResponseMetadata ResponseMetadata +} + +// ListDomains lists all domains in sdb. +// +// See http://goo.gl/Dsw15 for more details. +func (sdb *SDB) ListDomains() (resp *ListDomainsResp, err error) { + return sdb.ListDomainsN(0, "") +} + +// ListDomainsN lists domains in sdb up to maxDomains. +// If nextToken is not empty, domains listed will start at the given token. +// +// See http://goo.gl/Dsw15 for more details. +func (sdb *SDB) ListDomainsN(maxDomains int, nextToken string) (resp *ListDomainsResp, err error) { + params := makeParams("ListDomains") + if maxDomains != 0 { + params["MaxNumberOfDomains"] = []string{strconv.Itoa(maxDomains)} + } + if nextToken != "" { + params["NextToken"] = []string{nextToken} + } + resp = &ListDomainsResp{} + err = sdb.query(nil, nil, params, nil, resp) + return +} + +// --- SelectExpression + +// Response to a Select request. +// +// See http://goo.gl/GTsSZ for more details. +type SelectResp struct { + Items []struct { + Name string + Attrs []Attr `xml:"Attribute"` + } `xml:"SelectResult>Item"` + ResponseMetadata ResponseMetadata +} + +// Select returns a set of items and attributes that match expr. +// Select is similar to the standard SQL SELECT statement. +// +// See http://goo.gl/GTsSZ for more details. +func (sdb *SDB) Select(expr string, consistent bool) (resp *SelectResp, err error) { + resp = &SelectResp{} + params := makeParams("Select") + params["SelectExpression"] = []string{expr} + if consistent { + params["ConsistentRead"] = []string{"true"} + } + err = sdb.query(nil, nil, params, nil, resp) + return +} + +// ---------------------------------------------------------------------------- +// Domain-level operations. + +// --- CreateDomain + +// CreateDomain creates a new domain. +// +// See http://goo.gl/jDjGH for more details. +func (domain *Domain) CreateDomain() (resp *SimpleResp, err error) { + params := makeParams("CreateDomain") + resp = &SimpleResp{} + err = domain.SDB.query(domain, nil, params, nil, resp) + return +} + +// DeleteDomain deletes an existing domain. +// +// See http://goo.gl/S0dCL for more details. +func (domain *Domain) DeleteDomain() (resp *SimpleResp, err error) { + params := makeParams("DeleteDomain") + resp = &SimpleResp{} + err = domain.SDB.query(domain, nil, params, nil, resp) + return +} + +// ---------------------------------------------------------------------------- +// Item-level operations. + +type PutAttrs struct { + attrs []Attr + expected []Attr + replace map[string]bool + missing map[string]bool +} + +func (pa *PutAttrs) Add(name, value string) { + pa.attrs = append(pa.attrs, Attr{name, value}) +} + +func (pa *PutAttrs) Replace(name, value string) { + pa.Add(name, value) + if pa.replace == nil { + pa.replace = make(map[string]bool) + } + pa.replace[name] = true +} + +// The PutAttrs request will only succeed if the existing +// item in SimpleDB contains a matching name / value pair. +func (pa *PutAttrs) IfValue(name, value string) { + pa.expected = append(pa.expected, Attr{name, value}) +} + +// Flag to test the existence of an attribute while performing +// conditional updates. X can be any positive integer or 0. +// +// This should set Expected.N.Name=name and Expected.N.Exists=false +func (pa *PutAttrs) IfMissing(name string) { + if pa.missing == nil { + pa.missing = make(map[string]bool) + } + pa.missing[name] = true +} + +// PutAttrs adds attrs to item. +// +// See http://goo.gl/yTAV4 for more details. +func (item *Item) PutAttrs(attrs *PutAttrs) (resp *SimpleResp, err error) { + params := makeParams("PutAttributes") + resp = &SimpleResp{} + + // copy these attrs over to the parameters + itemNum := 1 + for _, attr := range attrs.attrs { + itemNumStr := strconv.Itoa(itemNum) + + // do the name, value and replace + params["Attribute."+itemNumStr+".Name"] = []string{attr.Name} + params["Attribute."+itemNumStr+".Value"] = []string{attr.Value} + + if _, ok := attrs.replace[attr.Name]; ok { + params["Attribute."+itemNumStr+".Replace"] = []string{"true"} + } + + itemNum++ + } + + //append expected values to params + expectedNum := 1 + for _, attr := range attrs.expected { + expectedNumStr := strconv.Itoa(expectedNum) + params["Expected."+expectedNumStr+".Name"] = []string{attr.Name} + params["Expected."+expectedNumStr+".Value"] = []string{attr.Value} + + if attrs.missing[attr.Name] { + params["Expected."+expectedNumStr+".Exists"] = []string{"false"} + } + expectedNum++ + } + + err = item.query(params, nil, resp) + if err != nil { + return nil, err + } + return +} + +// Response to an Attrs request. +// +// See http://goo.gl/45X1M for more details. +type AttrsResp struct { + Attrs []Attr `xml:"GetAttributesResult>Attribute"` + ResponseMetadata ResponseMetadata +} + +// Attrs returns one or more of the named attributes, or +// all of item's attributes if names is nil. +// If consistent is true, previous writes will necessarily +// be observed. +// +// See http://goo.gl/45X1M for more details. +func (item *Item) Attrs(names []string, consistent bool) (resp *AttrsResp, err error) { + params := makeParams("GetAttributes") + params["ItemName"] = []string{item.Name} + if consistent { + params["ConsistentRead"] = []string{"true"} + } + + // Copy these attributes over to the parameters + for i, name := range names { + params["AttributeName."+strconv.Itoa(i+1)] = []string{name} + } + + resp = &AttrsResp{} + err = item.query(params, nil, resp) + if err != nil { + return nil, err + } + return +} + +// ---------------------------------------------------------------------------- +// Generic data structures for all requests/responses. + +// Error encapsulates an error returned by SDB. +type Error struct { + StatusCode int // HTTP status code (200, 403, ...) + StatusMsg string // HTTP status message ("Service Unavailable", "Bad Request", ...) + Code string // SimpleDB error code ("InvalidParameterValue", ...) + Message string // The human-oriented error message + RequestId string // A unique ID for this request + BoxUsage float64 // The measure of machine utilization for this request. +} + +func (err *Error) Error() string { + return err.Message +} + +// SimpleResp represents a response to an SDB request which on success +// will return no other information besides ResponseMetadata. +type SimpleResp struct { + ResponseMetadata ResponseMetadata +} + +// ResponseMetadata +type ResponseMetadata struct { + RequestId string // A unique ID for tracking the request + BoxUsage float64 // The measure of machine utilization for this request. +} + +func buildError(r *http.Response) error { + err := Error{} + err.StatusCode = r.StatusCode + err.StatusMsg = r.Status + xml.NewDecoder(r.Body).Decode(&err) + return &err +} + +// ---------------------------------------------------------------------------- +// Request dispatching logic. + +func (item *Item) query(params url.Values, headers http.Header, resp interface{}) error { + return item.Domain.SDB.query(item.Domain, item, params, headers, resp) +} + +func (domain *Domain) query(item *Item, params url.Values, headers http.Header, resp interface{}) error { + return domain.SDB.query(domain, item, params, headers, resp) +} + +func (sdb *SDB) query(domain *Domain, item *Item, params url.Values, headers http.Header, resp interface{}) error { + // all SimpleDB operations have path="/" + method := "GET" + path := "/" + + // if we have been given no headers or params, create them + if headers == nil { + headers = map[string][]string{} + } + if params == nil { + params = map[string][]string{} + } + + // setup some default parameters + params["Version"] = []string{"2009-04-15"} + params["Timestamp"] = []string{time.Now().UTC().Format(time.RFC3339)} + + // set the DomainName param (every request must have one) + if domain != nil { + params["DomainName"] = []string{domain.Name} + } + + // set the ItemName if we have one + if item != nil { + params["ItemName"] = []string{item.Name} + } + + // check the endpoint URL + u, err := url.Parse(sdb.Region.SDBEndpoint) + if err != nil { + return err + } + headers["Host"] = []string{u.Host} + sign(sdb.Auth, method, path, params, headers) + + u.Path = path + if len(params) > 0 { + u.RawQuery = params.Encode() + } + req := http.Request{ + URL: u, + Method: method, + ProtoMajor: 1, + ProtoMinor: 1, + Close: true, + Header: headers, + } + + if v, ok := headers["Content-Length"]; ok { + req.ContentLength, _ = strconv.ParseInt(v[0], 10, 64) + delete(headers, "Content-Length") + } + + r, err := http.DefaultClient.Do(&req) + if err != nil { + return err + } + defer r.Body.Close() + + if debug { + dump, _ := httputil.DumpResponse(r, true) + log.Printf("response:\n") + log.Printf("%v\n}\n", string(dump)) + } + + // status code is always 200 when successful (since we're always doing a GET) + if r.StatusCode != 200 { + return buildError(r) + } + + // everything was fine, so unmarshal the XML and return what it's err is (if any) + err = xml.NewDecoder(r.Body).Decode(resp) + return err +} + +func makeParams(action string) map[string][]string { + params := make(map[string][]string) + params["Action"] = []string{action} + return params +} diff --git a/vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go b/vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go new file mode 100644 index 000000000..83e7f9f86 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go @@ -0,0 +1,219 @@ +package sdb_test + +import ( + "testing" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/exp/sdb" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + sdb *sdb.SDB +} + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.sdb = sdb.New(auth, aws.Region{SDBEndpoint: testServer.URL}) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestCreateDomainOK(c *C) { + testServer.Response(200, nil, TestCreateDomainXmlOK) + + domain := s.sdb.Domain("domain") + resp, err := domain.CreateDomain() + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "63264005-7a5f-e01a-a224-395c63b89f6d") + c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0055590279) + + c.Assert(err, IsNil) +} + +func (s *S) TestListDomainsOK(c *C) { + testServer.Response(200, nil, TestListDomainsXmlOK) + + resp, err := s.sdb.ListDomains() + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "15fcaf55-9914-63c2-21f3-951e31193790") + c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000071759) + c.Assert(resp.Domains, DeepEquals, []string{"Account", "Domain", "Record"}) + + c.Assert(err, IsNil) +} + +func (s *S) TestListDomainsWithNextTokenXmlOK(c *C) { + testServer.Response(200, nil, TestListDomainsWithNextTokenXmlOK) + + resp, err := s.sdb.ListDomains() + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "eb13162f-1b95-4511-8b12-489b86acfd28") + c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907) + c.Assert(resp.Domains, DeepEquals, []string{"Domain1-200706011651", "Domain2-200706011652"}) + c.Assert(resp.NextToken, Equals, "TWV0ZXJpbmdUZXN0RG9tYWluMS0yMDA3MDYwMTE2NTY=") + + c.Assert(err, IsNil) +} + +func (s *S) TestDeleteDomainOK(c *C) { + testServer.Response(200, nil, TestDeleteDomainXmlOK) + + domain := s.sdb.Domain("domain") + resp, err := domain.DeleteDomain() + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "039e1e25-9a64-2a74-93da-2fda36122a97") + c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0055590278) + + c.Assert(err, IsNil) +} + +func (s *S) TestPutAttrsOK(c *C) { + testServer.Response(200, nil, TestPutAttrsXmlOK) + + domain := s.sdb.Domain("MyDomain") + item := domain.Item("Item123") + + putAttrs := new(sdb.PutAttrs) + putAttrs.Add("FirstName", "john") + putAttrs.Add("LastName", "smith") + putAttrs.Replace("MiddleName", "jacob") + + putAttrs.IfValue("FirstName", "john") + putAttrs.IfMissing("FirstName") + + resp, err := item.PutAttrs(putAttrs) + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Form["Action"], DeepEquals, []string{"PutAttributes"}) + c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"}) + c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"}) + c.Assert(req.Form["Attribute.1.Name"], DeepEquals, []string{"FirstName"}) + c.Assert(req.Form["Attribute.1.Value"], DeepEquals, []string{"john"}) + c.Assert(req.Form["Attribute.2.Name"], DeepEquals, []string{"LastName"}) + c.Assert(req.Form["Attribute.2.Value"], DeepEquals, []string{"smith"}) + c.Assert(req.Form["Attribute.3.Name"], DeepEquals, []string{"MiddleName"}) + c.Assert(req.Form["Attribute.3.Value"], DeepEquals, []string{"jacob"}) + c.Assert(req.Form["Attribute.3.Replace"], DeepEquals, []string{"true"}) + + c.Assert(req.Form["Expected.1.Name"], DeepEquals, []string{"FirstName"}) + c.Assert(req.Form["Expected.1.Value"], DeepEquals, []string{"john"}) + c.Assert(req.Form["Expected.1.Exists"], DeepEquals, []string{"false"}) + + c.Assert(err, IsNil) + c.Assert(resp.ResponseMetadata.RequestId, Equals, "490206ce-8292-456c-a00f-61b335eb202b") + c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907) + +} + +func (s *S) TestAttrsOK(c *C) { + testServer.Response(200, nil, TestAttrsXmlOK) + + domain := s.sdb.Domain("MyDomain") + item := domain.Item("Item123") + + resp, err := item.Attrs(nil, true) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + c.Assert(req.Form["Action"], DeepEquals, []string{"GetAttributes"}) + c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"}) + c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"}) + c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"}) + + c.Assert(resp.Attrs[0].Name, Equals, "Color") + c.Assert(resp.Attrs[0].Value, Equals, "Blue") + c.Assert(resp.Attrs[1].Name, Equals, "Size") + c.Assert(resp.Attrs[1].Value, Equals, "Med") + c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d") + c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219942) + + c.Assert(err, IsNil) +} + +func (s *S) TestAttrsSelectOK(c *C) { + testServer.Response(200, nil, TestAttrsXmlOK) + + domain := s.sdb.Domain("MyDomain") + item := domain.Item("Item123") + + resp, err := item.Attrs([]string{"Color", "Size"}, true) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + c.Assert(req.Form["Action"], DeepEquals, []string{"GetAttributes"}) + c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"}) + c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"}) + c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"}) + c.Assert(req.Form["AttributeName.1"], DeepEquals, []string{"Color"}) + c.Assert(req.Form["AttributeName.2"], DeepEquals, []string{"Size"}) + + c.Assert(resp.Attrs[0].Name, Equals, "Color") + c.Assert(resp.Attrs[0].Value, Equals, "Blue") + c.Assert(resp.Attrs[1].Name, Equals, "Size") + c.Assert(resp.Attrs[1].Value, Equals, "Med") + c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d") + c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219942) + + c.Assert(err, IsNil) +} + +func (s *S) TestSelectOK(c *C) { + testServer.Response(200, nil, TestSelectXmlOK) + + resp, err := s.sdb.Select("select Color from MyDomain where Color like 'Blue%'", true) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + c.Assert(req.Form["Action"], DeepEquals, []string{"Select"}) + c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"}) + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d") + c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907) + c.Assert(len(resp.Items), Equals, 2) + c.Assert(resp.Items[0].Name, Equals, "Item_03") + c.Assert(resp.Items[1].Name, Equals, "Item_06") + c.Assert(resp.Items[0].Attrs[0].Name, Equals, "Category") + c.Assert(resp.Items[0].Attrs[0].Value, Equals, "Clothes") + + c.Assert(err, IsNil) +} diff --git a/vendor/github.com/goamz/goamz/exp/sdb/sign.go b/vendor/github.com/goamz/goamz/exp/sdb/sign.go new file mode 100644 index 000000000..040ed5385 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sdb/sign.go @@ -0,0 +1,54 @@ +package sdb + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "github.com/goamz/goamz/aws" + "net/http" + "net/url" + "sort" + "strings" +) + +var b64 = base64.StdEncoding + +// ---------------------------------------------------------------------------- +// SimpleDB signing (http://goo.gl/CaY81) + +func sign(auth aws.Auth, method, path string, params url.Values, headers http.Header) { + var host string + for k, v := range headers { + k = strings.ToLower(k) + switch k { + case "host": + host = v[0] + } + } + + // set up some defaults used for signing the request + params["AWSAccessKeyId"] = []string{auth.AccessKey} + params["SignatureVersion"] = []string{"2"} + params["SignatureMethod"] = []string{"HmacSHA256"} + if auth.Token() != "" { + params["SecurityToken"] = []string{auth.Token()} + } + + // join up all the incoming params + var sarray []string + for k, v := range params { + sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v[0])) + } + sort.StringSlice(sarray).Sort() + joined := strings.Join(sarray, "&") + + // create the payload, sign it and create the signature + payload := strings.Join([]string{method, host, "/", joined}, "\n") + hash := hmac.New(sha256.New, []byte(auth.SecretKey)) + hash.Write([]byte(payload)) + signature := make([]byte, b64.EncodedLen(hash.Size())) + b64.Encode(signature, hash.Sum(nil)) + + // add the signature to the outgoing params + params["Signature"] = []string{string(signature)} +} diff --git a/vendor/github.com/goamz/goamz/exp/sdb/sign_test.go b/vendor/github.com/goamz/goamz/exp/sdb/sign_test.go new file mode 100644 index 000000000..f45ad15e1 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sdb/sign_test.go @@ -0,0 +1,29 @@ +package sdb_test + +import ( + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/exp/sdb" + . "gopkg.in/check.v1" +) + +// SimpleDB ReST authentication docs: http://goo.gl/CaY81 + +var testAuth = aws.Auth{AccessKey: "access-key-id-s8eBOWuU", SecretKey: "secret-access-key-UkQjTLd9"} + +func (s *S) TestSignExampleDomainCreate(c *C) { + method := "GET" + params := map[string][]string{ + "Action": {"CreateDomain"}, + "DomainName": {"MyDomain"}, + "Timestamp": {"2011-08-20T07:23:57+12:00"}, + "Version": {"2009-04-15"}, + } + headers := map[string][]string{ + "Host": {"sdb.amazonaws.com"}, + } + sdb.Sign(testAuth, method, "", params, headers) + expected := "ot2JaeeqMRJqgAqW67hkzUlffgxdOz4RykbrECB+tDU=" + c.Assert(params["Signature"], DeepEquals, []string{expected}) +} + +// Do a few test methods which takes combinations of params diff --git a/vendor/github.com/goamz/goamz/exp/ses/ses.go b/vendor/github.com/goamz/goamz/exp/ses/ses.go new file mode 100644 index 000000000..7c9c7c352 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/ses/ses.go @@ -0,0 +1,145 @@ +// ses.go +package ses + +import ( + "encoding/xml" + "github.com/goamz/goamz/aws" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" +) + +type SES struct { + auth aws.Auth + region aws.Region + client *http.Client +} + +// Initializes a pointer to an SES struct which can be used +// to perform SES API calls. +func NewSES(auth aws.Auth, region aws.Region) *SES { + ses := SES{auth, region, nil} + return &ses +} + +// Sends an email to the specifications stored in the Email struct. +func (ses *SES) SendEmail(email *Email) error { + data := make(url.Values) + + index := 0 + for i := range email.destination.bccAddresses { + if len(email.destination.bccAddresses[i]) > 0 { + index += 1 + key := "Destination.BccAddresses.member." + strconv.Itoa(index) + data.Add(key, email.destination.bccAddresses[i]) + } + } + + index = 0 + for i := range email.destination.ccAddresses { + if len(email.destination.ccAddresses[i]) > 0 { + index += 1 + key := "Destination.CcAddresses.member." + strconv.Itoa(index) + data.Add(key, email.destination.ccAddresses[i]) + } + } + + index = 0 + for i := range email.destination.toAddresses { + if len(email.destination.toAddresses[i]) > 0 { + index += 1 + key := "Destination.ToAddresses.member." + strconv.Itoa(index) + data.Add(key, email.destination.toAddresses[i]) + } + } + + index = 0 + for i := range email.replyTo { + if len(email.replyTo[i]) > 0 { + index += 1 + key := "ReplyToAddresses.member." + strconv.Itoa(index) + data.Add(key, email.replyTo[i]) + } + } + + if len(email.message.Subject.Data) > 0 { + if len(email.message.Subject.Charset) > 0 { + data.Add("Message.Subject.Charset", email.message.Subject.Charset) + } + data.Add("Message.Subject.Data", email.message.Subject.Data) + } + + if len(email.message.Body.Html.Data) > 0 { + if len(email.message.Body.Html.Charset) > 0 { + data.Add("Message.Body.Html.Charset", email.message.Body.Html.Charset) + } + data.Add("Message.Body.Html.Data", email.message.Body.Html.Data) + } + + if len(email.message.Body.Text.Data) > 0 { + if len(email.message.Body.Text.Charset) > 0 { + data.Add("Message.Body.Text.Charset", email.message.Body.Text.Charset) + } + data.Add("Message.Body.Text.Data", email.message.Body.Text.Data) + } + + if len(email.returnPath) > 0 { + data.Add("ReturnPath", email.returnPath) + } + + if len(email.source) > 0 { + data.Add("Source", email.source) + } + + return ses.doPost("SendEmail", data) +} + +// Do an SES POST action. +func (ses *SES) doPost(action string, data url.Values) error { + req := http.Request{ + Method: "POST", + ProtoMajor: 1, + ProtoMinor: 1, + Close: true, + Header: http.Header{}} + + URL, err := url.Parse(ses.region.SESEndpoint) + if err != nil { + return err + } + URL.Path = "/" + + req.URL = URL + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + sign(ses.auth, "POST", req.Header) + + data.Add("AWSAccessKeyId", ses.auth.AccessKey) + data.Add("Action", action) + + body := data.Encode() + req.Header.Add("Content-Length", strconv.Itoa(len(body))) + req.Body = ioutil.NopCloser(strings.NewReader(body)) + + if ses.client == nil { + ses.client = &http.Client{} + } + + resp, err := ses.client.Do(&req) + if err != nil { + return err + } + if resp.StatusCode > 204 { + defer resp.Body.Close() + return buildError(resp) + } + + return nil +} + +func buildError(r *http.Response) *SESError { + err := SESError{} + xml.NewDecoder(r.Body).Decode(&err) + return &err +} diff --git a/vendor/github.com/goamz/goamz/exp/ses/ses_test.go b/vendor/github.com/goamz/goamz/exp/ses/ses_test.go new file mode 100644 index 000000000..ae93215ed --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/ses/ses_test.go @@ -0,0 +1,69 @@ +// ses_test +package ses_test + +import ( + "bytes" + "net/url" + "testing" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/exp/ses" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +type S struct { + ses *ses.SES +} + +var _ = Suite(&S{}) + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.ses = ses.NewSES(auth, aws.Region{Name: "faux-region-1", S3Endpoint: testServer.URL}) +} + +func (s *S) TearDownStrategy(c *C) { + +} + +func (s *S) SetUpTest(c *C) { + +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestSendEmail(c *C) { + testServer.Response(200, nil, "") + + email := ses.NewEmail() + email.AddTo("test@test.com") + email.AddSource("test@test.com") + email.SetSubject("test") + email.SetBodyHtml("test") + + s.ses.SendEmail(email) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "POST") + c.Assert(req.Header["Date"], Not(Equals), "") + + buf := new(bytes.Buffer) + buf.ReadFrom(req.Body) + body, _ := url.ParseQuery(buf.String()) + + c.Assert(body, Not(IsNil)) + c.Assert(body["Destination.ToAddresses.member.1"], Equals, "test@test.com") + c.Assert(body["Source"], Equals, "test@test.com") + c.Assert(body["Message.Subject.Data"], Equals, "test") + c.Assert(body["Message.Body.Html.Data"], Equals, "test") +} diff --git a/vendor/github.com/goamz/goamz/exp/ses/ses_types.go b/vendor/github.com/goamz/goamz/exp/ses/ses_types.go new file mode 100644 index 000000000..698fa7623 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/ses/ses_types.go @@ -0,0 +1,156 @@ +// ses_types +package ses + +// Private internal representation of message body. +type Body struct { + Html Content + Text Content +} + +// Content data structure with charset and payload (Data). +type Content struct { + Charset string + Data string +} + +// Email data structure. Should be main data structure used +// for sending emails using SES. +type Email struct { + destination *destination + message Message + replyTo []string + returnPath string + source string +} + +// Private internal representation for email destinations. +type destination struct { + bccAddresses []string + ccAddresses []string + toAddresses []string +} + +// Message data structure. +type Message struct { + Body Body + Subject Content +} + +// SES Error structure. +type SESError struct { + Type string + Code string + Message string + Detail string + RequestId string +} + +func (err *SESError) Error() string { + return err.Message +} + +// Returns a pointer to an empty but initialized Email. +func NewEmail() *Email { + return &Email{ + destination: newDestination(), + replyTo: make([]string, 5)} +} + +// Private function to return an initialized destination. +func newDestination() *destination { + return &destination{ + bccAddresses: make([]string, 5), + ccAddresses: make([]string, 5), + toAddresses: make([]string, 5)} +} + +// Add a BCC destination to Email. +func (em *Email) AddBCC(address string) { + em.destination.bccAddresses = append(em.destination.bccAddresses, address) +} + +// Add multiple BCC destinations to Email. +func (em *Email) AddBCCs(addresses []string) { + em.destination.bccAddresses = append(em.destination.bccAddresses, addresses...) +} + +// Add a CC destination to Email. +func (em *Email) AddCC(address string) { + em.destination.ccAddresses = append(em.destination.ccAddresses, address) +} + +// Add multiple CC destinations to Email. +func (em *Email) AddCCs(addresses []string) { + em.destination.ccAddresses = append(em.destination.ccAddresses, addresses...) +} + +// Add a To destination to Email. +func (em *Email) AddTo(address string) { + em.destination.toAddresses = append(em.destination.toAddresses, address) +} + +// Add multiple To destinations to Email. +func (em *Email) AddTos(addresses []string) { + em.destination.toAddresses = append(em.destination.toAddresses, addresses...) +} + +// Add a reply-to address to Email. +func (em *Email) AddReplyTo(address string) { + em.replyTo = append(em.replyTo, address) +} + +// Add multiple reply-to addresses to Email. +func (em *Email) AddReplyTos(addresses []string) { + em.replyTo = append(em.replyTo, addresses...) +} + +// Set the return path for Email. +func (em *Email) SetReturnPath(path string) { + em.returnPath = path +} + +// Set the source address for Email. +func (em *Email) SetSource(source string) { + em.source = source +} + +// Set the Email message. +func (em *Email) SetMessage(message Message) { + em.message = message +} + +// Set the subject for the Email message. +// Uses ASCII as charset. +func (em *Email) SetSubject(subject string) { + em.message.Subject = Content{Data: subject} +} + +// Set the subject for the Email message. +// Uses the charset (or ASCII if none) of Content. +func (em *Email) SetSubjectAsContent(subject Content) { + em.message.Subject = subject +} + +// Sets the HTML body of the Email message. +// Uses ASCII as charset. +func (em *Email) SetBodyHtml(html string) { + em.message.Body.Html = Content{Data: html} +} + +// Sets the HTML body of the Email message. +// Uses the charset (or ASCII if none) of Content. +func (em *Email) SetBodyHtmlAsContent(html Content) { + em.message.Body.Html = html +} + +// Sets the raw text body of the Email message. +// Uses ASCII as charset. +func (em *Email) SetBodyRawText(text string) { + em.message.Body.Text = Content{Data: text} +} + +// Sets the raw test body of the Email message. +// Uses the charset (or ASCII if none) of Content. +func (em *Email) SetBodyRawTextAsContent(text Content) { + em.message.Body.Text = text +} diff --git a/vendor/github.com/goamz/goamz/exp/ses/sign.go b/vendor/github.com/goamz/goamz/exp/ses/sign.go new file mode 100644 index 000000000..5c9c840c8 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/ses/sign.go @@ -0,0 +1,26 @@ +// sign +package ses + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "fmt" + "github.com/goamz/goamz/aws" + "time" +) + +const ( + AMZ_DATE_STYLE = "Mon, 02 Jan 2006 15:04:05 -0700" +) + +// Sign SES request as dictated by Amazon's Version 3 signature method. +func sign(auth aws.Auth, method string, headers map[string][]string) { + date := time.Now().UTC().Format(AMZ_DATE_STYLE) + h := hmac.New(sha256.New, []byte(auth.SecretKey)) + h.Write([]byte(date)) + signature := base64.StdEncoding.EncodeToString(h.Sum(nil)) + authHeader := fmt.Sprintf("AWS3-HTTPS AWSAccessKeyId=%s, Algorithm=HmacSHA256, Signature=%s", auth.AccessKey, signature) + headers["Date"] = []string{date} + headers["X-Amzn-Authorization"] = []string{authHeader} +} diff --git a/vendor/github.com/goamz/goamz/exp/sns/Makefile b/vendor/github.com/goamz/goamz/exp/sns/Makefile new file mode 100644 index 000000000..1e5b9da3b --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/Makefile @@ -0,0 +1,21 @@ +include $(GOROOT)/src/Make.inc + +TARG=launchpad.net/goamz/sns + +GOFILES=\ + sns.go\ + sign.go\ + +include $(GOROOT)/src/Make.pkg + +GOFMT=gofmt +BADFMT=$(shell $(GOFMT) -l $(GOFILES) 2> /dev/null) + +gofmt: $(BADFMT) + @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done + +ifneq ($(BADFMT),) +ifneq ($(MAKECMDGOALS), gofmt) +#$(warning WARNING: make gofmt: $(BADFMT)) +endif +endif diff --git a/vendor/github.com/goamz/goamz/exp/sns/README b/vendor/github.com/goamz/goamz/exp/sns/README new file mode 100644 index 000000000..87770adb5 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/README @@ -0,0 +1 @@ +Amazon Simple Notification Service API for Golang. diff --git a/vendor/github.com/goamz/goamz/exp/sns/endpoint.go b/vendor/github.com/goamz/goamz/exp/sns/endpoint.go new file mode 100644 index 000000000..c6e6e4433 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/endpoint.go @@ -0,0 +1,132 @@ +package sns + +import ( + "fmt" + "strconv" +) + +type DeleteEndpointResponse struct { + ResponseMetadata +} + +type GetEndpointAttributesResponse struct { + Attributes []AttributeEntry `xml:"GetEndpointAttributesResult>Attributes>entry"` + ResponseMetadata +} + +type PlatformEndpointOpt struct { + Attributes []AttributeEntry + PlatformApplicationArn string + CustomUserData string + Token string +} + +type CreatePlatformEndpointResponse struct { + EndpointArn string `xml:"CreatePlatformEndpointResult>EndpointArn"` + ResponseMetadata +} + +type PlatformEndpoints struct { + EndpointArn string `xml:"EndpointArn"` + Attributes []AttributeEntry `xml:"Attributes>entry"` +} + +type ListEndpointsByPlatformApplicationResponse struct { + Endpoints []PlatformEndpoints `xml:"ListEndpointsByPlatformApplicationResult>Endpoints>member"` + ResponseMetadata +} + +type SetEndpointAttributesOpt struct { + Attributes []AttributeEntry + EndpointArn string +} + +type SetEndpointAttributesResponse struct { + ResponseMetadata +} + +// DeleteEndpoint +// +// See http://goo.gl/9SlUD9 for more details. +func (sns *SNS) DeleteEndpoint(endpointArn string) (resp *DeleteEndpointResponse, err error) { + resp = &DeleteEndpointResponse{} + params := makeParams("DeleteEndpoint") + + params["EndpointArn"] = endpointArn + + err = sns.query(params, resp) + + return +} + +// GetEndpointAttributes +// +// See http://goo.gl/c8E5X1 for more details. +func (sns *SNS) GetEndpointAttributes(endpointArn string) (resp *GetEndpointAttributesResponse, err error) { + resp = &GetEndpointAttributesResponse{} + + params := makeParams("GetEndpointAttributes") + + params["EndpointArn"] = endpointArn + + err = sns.query(params, resp) + + return +} + +// CreatePlatformEndpoint +// +// See http://goo.gl/4tnngi for more details. +func (sns *SNS) CreatePlatformEndpoint(options *PlatformEndpointOpt) (resp *CreatePlatformEndpointResponse, err error) { + + resp = &CreatePlatformEndpointResponse{} + params := makeParams("CreatePlatformEndpoint") + + params["PlatformApplicationArn"] = options.PlatformApplicationArn + params["Token"] = options.Token + + if options.CustomUserData != "" { + params["CustomUserData"] = options.CustomUserData + } + + err = sns.query(params, resp) + + return +} + +// ListEndpointsByPlatformApplication +// +// See http://goo.gl/L7ioyR for more detail. +func (sns *SNS) ListEndpointsByPlatformApplication(platformApplicationArn, nextToken string) (resp *ListEndpointsByPlatformApplicationResponse, err error) { + resp = &ListEndpointsByPlatformApplicationResponse{} + + params := makeParams("ListEndpointsByPlatformApplication") + + params["PlatformApplicationArn"] = platformApplicationArn + + if nextToken != "" { + params["NextToken"] = nextToken + } + + err = sns.query(params, resp) + return + +} + +// SetEndpointAttributes +// +// See http://goo.gl/GTktCj for more detail. +func (sns *SNS) SetEndpointAttributes(options *SetEndpointAttributesOpt) (resp *SetEndpointAttributesResponse, err error) { + resp = &SetEndpointAttributesResponse{} + params := makeParams("SetEndpointAttributes") + + params["EndpointArn"] = options.EndpointArn + + for i, attr := range options.Attributes { + params[fmt.Sprintf("Attributes.entry.%s.key", strconv.Itoa(i+1))] = attr.Key + params[fmt.Sprintf("Attributes.entry.%s.value", strconv.Itoa(i+1))] = attr.Value + } + + err = sns.query(params, resp) + return +} diff --git a/vendor/github.com/goamz/goamz/exp/sns/permissions.go b/vendor/github.com/goamz/goamz/exp/sns/permissions.go new file mode 100644 index 000000000..e7c73629f --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/permissions.go @@ -0,0 +1,51 @@ +package sns + +import ( + "strconv" +) + +type Permission struct { + ActionName string + AccountId string +} + +type AddPermissionResponse struct { + ResponseMetadata +} + +// AddPermission +// +// See http://goo.gl/mbY4a for more details. +func (sns *SNS) AddPermission(permissions []Permission, Label, TopicArn string) (resp *AddPermissionResponse, err error) { + resp = &AddPermissionResponse{} + params := makeParams("AddPermission") + + for i, p := range permissions { + params["AWSAccountId.member."+strconv.Itoa(i+1)] = p.AccountId + params["ActionName.member."+strconv.Itoa(i+1)] = p.ActionName + } + + params["Label"] = Label + params["TopicArn"] = TopicArn + + err = sns.query(params, resp) + return +} + +type RemovePermissionResponse struct { + ResponseMetadata +} + +// RemovePermission +// +// See http://goo.gl/wGl5j for more details. +func (sns *SNS) RemovePermission(Label, TopicArn string) (resp *RemovePermissionResponse, err error) { + resp = &RemovePermissionResponse{} + params := makeParams("RemovePermission") + + params["Label"] = Label + params["TopicArn"] = TopicArn + + err = sns.query(params, resp) + return +} diff --git a/vendor/github.com/goamz/goamz/exp/sns/platform.go b/vendor/github.com/goamz/goamz/exp/sns/platform.go new file mode 100644 index 000000000..b650cdda7 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/platform.go @@ -0,0 +1,135 @@ +package sns + +import ( + "fmt" + "strconv" +) + +type CreatePlatformApplicationResponse struct { + PlatformApplicationArn string `xml:"CreatePlatformApplicationResult>PlatformApplicationArn"` + ResponseMetadata +} + +type PlatformApplicationOpt struct { + Attributes []AttributeEntry + Name string + Platform string +} + +type DeletePlatformApplicationResponse struct { + ResponseMetadata +} + +type GetPlatformApplicationAttributesResponse struct { + Attributes []AttributeEntry `xml:"GetPlatformApplicationAttributesResult>Attributes>entry"` + ResponseMetadata +} + +type SetPlatformApplicationAttributesOpt struct { + Attributes []AttributeEntry + PlatformApplicationArn string +} + +type SetPlatformApplicationAttributesResponse struct { + ResponseMetadata +} + +type PlatformApplication struct { + Attributes []AttributeEntry `xml:"Attributes>entry"` + PlatformApplicationArn string +} + +type ListPlatformApplicationsResponse struct { + NextToken string + PlatformApplications []PlatformApplication `xml:"ListPlatformApplicationsResult>PlatformApplications>member"` + ResponseMetadata +} + +// CreatePlatformApplication +// +// See http://goo.gl/Mbbl6Z for more details. + +func (sns *SNS) CreatePlatformApplication(options *PlatformApplicationOpt) (resp *CreatePlatformApplicationResponse, err error) { + resp = &CreatePlatformApplicationResponse{} + params := makeParams("CreatePlatformApplication") + + params["Platform"] = options.Platform + params["Name"] = options.Name + + for i, attr := range options.Attributes { + params[fmt.Sprintf("Attributes.entry.%s.key", strconv.Itoa(i+1))] = attr.Key + params[fmt.Sprintf("Attributes.entry.%s.value", strconv.Itoa(i+1))] = attr.Value + } + + err = sns.query(params, resp) + + return + +} + +// DeletePlatformApplication +// +// See http://goo.gl/6GB3DN for more details. +func (sns *SNS) DeletePlatformApplication(platformApplicationArn string) (resp *DeletePlatformApplicationResponse, err error) { + resp = &DeletePlatformApplicationResponse{} + + params := makeParams("DeletePlatformApplication") + + params["PlatformApplicationArn"] = platformApplicationArn + + err = sns.query(params, resp) + + return +} + +// GetPlatformApplicationAttributes +// +// See http://goo.gl/GswJ8I for more details. +func (sns *SNS) GetPlatformApplicationAttributes(platformApplicationArn, nextToken string) (resp *GetPlatformApplicationAttributesResponse, err error) { + resp = &GetPlatformApplicationAttributesResponse{} + + params := makeParams("GetPlatformApplicationAttributes") + + params["PlatformApplicationArn"] = platformApplicationArn + + if nextToken != "" { + params["NextToken"] = nextToken + } + + err = sns.query(params, resp) + + return +} + +// ListPlatformApplications +// +// See http://goo.gl/vQ3ooV for more detail. +func (sns *SNS) ListPlatformApplications(nextToken string) (resp *ListPlatformApplicationsResponse, err error) { + resp = &ListPlatformApplicationsResponse{} + params := makeParams("ListPlatformApplications") + + if nextToken != "" { + params["NextToken"] = nextToken + } + + err = sns.query(params, resp) + return +} + +// SetPlatformApplicationAttributes +// +// See http://goo.gl/RWnzzb for more detail. +func (sns *SNS) SetPlatformApplicationAttributes(options *SetPlatformApplicationAttributesOpt) (resp *SetPlatformApplicationAttributesResponse, err error) { + resp = &SetPlatformApplicationAttributesResponse{} + params := makeParams("SetPlatformApplicationAttributes") + + params["PlatformApplicationArn"] = options.PlatformApplicationArn + + for i, attr := range options.Attributes { + params[fmt.Sprintf("Attributes.entry.%s.key", strconv.Itoa(i+1))] = attr.Key + params[fmt.Sprintf("Attributes.entry.%s.value", strconv.Itoa(i+1))] = attr.Value + } + + err = sns.query(params, resp) + return +} diff --git a/vendor/github.com/goamz/goamz/exp/sns/responses_test.go b/vendor/github.com/goamz/goamz/exp/sns/responses_test.go new file mode 100644 index 000000000..53a06429d --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/responses_test.go @@ -0,0 +1,317 @@ +package sns_test + +var TestListTopicsXmlOK = ` + + + + + + arn:aws:sns:us-west-1:331995417492:Transcoding + + + + + bd10b26c-e30e-11e0-ba29-93c3aca2f103 + + +` + +var TestCreateTopicXmlOK = ` + + + + arn:aws:sns:us-east-1:123456789012:My-Topic + + + a8dec8b3-33a4-11df-8963-01868b7c937a + + +` + +var TestDeleteTopicXmlOK = ` + + + f3aa9ac9-3c3d-11df-8235-9dab105e9c32 + + +` + +var TestListSubscriptionsXmlOK = ` + + + + + arn:aws:sns:us-east-1:698519295917:My-Topic + email + arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca + 123456789012 + example@amazon.com + + + + + 384ac68d-3775-11df-8963-01868b7c937a + + +` + +var TestGetTopicAttributesXmlOK = ` + + + + + Owner + 123456789012 + + + Policy + {"Version":"2008-10-17","Id":"us-east-1/698519295917/test__default_policy_ID","Statement" : [{"Effect":"Allow","Sid":"us-east-1/698519295917/test__default_statement_ID","Principal" : {"AWS": "*"},"Action":["SNS:GetTopicAttributes","SNS:SetTopicAttributes","SNS:AddPermission","SNS:RemovePermission","SNS:DeleteTopic","SNS:Subscribe","SNS:ListSubscriptionsByTopic","SNS:Publish","SNS:Receive"],"Resource":"arn:aws:sns:us-east-1:698519295917:test","Condition" : {"StringLike" : {"AWS:SourceArn": "arn:aws:*:*:698519295917:*"}}}]} + + + TopicArn + arn:aws:sns:us-east-1:123456789012:My-Topic + + + + + 057f074c-33a7-11df-9540-99d0768312d3 + + +` + +var TestPublishXmlOK = ` + + + 94f20ce6-13c5-43a0-9a9e-ca52d816e90b + + + f187a3c1-376f-11df-8963-01868b7c937a + + +` + +var TestSetTopicAttributesXmlOK = ` + + + a8763b99-33a7-11df-a9b7-05d48da6f042 + + +` + +var TestSubscribeXmlOK = ` + + + pending confirmation + + + a169c740-3766-11df-8963-01868b7c937a + + +` + +var TestUnsubscribeXmlOK = ` + + + 18e0ac39-3776-11df-84c0-b93cc1666b84 + + +` + +var TestConfirmSubscriptionXmlOK = ` + + + arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca + + + 7a50221f-3774-11df-a9b7-05d48da6f042 + + +` + +var TestAddPermissionXmlOK = ` + + + 6a213e4e-33a8-11df-9540-99d0768312d3 + + +` + +var TestRemovePermissionXmlOK = ` + + + d170b150-33a8-11df-995a-2d6fbe836cc1 + + +` + +var TestListSubscriptionsByTopicXmlOK = ` + + + + + arn:aws:sns:us-east-1:123456789012:My-Topic + email + arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca + 123456789012 + example@amazon.com + + + + + b9275252-3774-11df-9540-99d0768312d3 + + +` + +var TestCreatePlatformApplicationXmlOK = ` + + + arn:aws:sns:us-west-2:123456789012:app/GCM/gcmpushapp + + + b6f0e78b-e9d4-5a0e-b973-adc04e8a4ff9 + + +` + +var TestCreatePlatformEndpointXmlOK = ` + + + arn:aws:sns:us-west-2:123456789012:endpoint/GCM/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3 + + + 6613341d-3e15-53f7-bf3c-7e56994ba278 + + +` + +var DeleteEndpointXmlOK = ` + + + c1d2b191-353c-5a5f-8969-fbdd3900afa8 + + +` + +var TestDeletePlatformApplicationXmlOK = ` + + + 097dac18-7a77-5823-a8dd-e65476dcb037 + + +` + +var TestGetEndpointAttributesXmlOK = ` + + + + + Enabled + true + + + CustomUserData + UserId=01234567 + + + Token + APA91bGi7fFachkC1xjlqT66VYEucGHochmf1VQAr9k...jsM0PKPxKhddCzx6paEsyay9Zn3D4wNUJb8m6HZrBEXAMPLE + + + + + 6c725a19-a142-5b77-94f9-1055a9ea04e7 + + +` + +var TestGetPlatformApplicationAttributesXmlOK = ` + + + + + AllowEndpointPolicies + false + + + + + 74848df2-87f6-55ed-890c-c7be80442462 + + +` + +var TestListEndpointsByPlatformApplicationXmlOK = ` + + + + + arn:aws:sns:us-west-2:123456789012:endpoint/GCM/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3 + + + Enabled + true + + + CustomUserData + UserId=27576823 + + + Token + APA91bGi7fFachkC1xjlqT66VYEucGHochmf1VQAr9k...jsM0PKPxKhddCzx6paEsyay9Zn3D4wNUJb8m6HZrBEXAMPLE + + + + + + + 9a48768c-dac8-5a60-aec0-3cc27ea08d96 + + +` + +var TestListPlatformApplicationsXmlOK = ` + + + + + arn:aws:sns:us-west-2:123456789012:app/APNS_SANDBOX/apnspushapp + + + AllowEndpointPolicies + false + + + + + arn:aws:sns:us-west-2:123456789012:app/GCM/gcmpushapp + + + AllowEndpointPolicies + false + + + + + + + 315a335e-85d8-52df-9349-791283cbb529 + + +` + +var TestSetEndpointAttributesXmlOK = ` + + + 2fe0bfc7-3e85-5ee5-a9e2-f58b35e85f6a + + +` + +var TestSetPlatformApplicationAttributesXmlOK = ` + + + cf577bcc-b3dc-5463-88f1-3180b9412395 + + +` diff --git a/vendor/github.com/goamz/goamz/exp/sns/sign.go b/vendor/github.com/goamz/goamz/exp/sns/sign.go new file mode 100644 index 000000000..0c35f05ae --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/sign.go @@ -0,0 +1,72 @@ +package sns + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "github.com/goamz/goamz/aws" + "sort" + "strings" +) + +var b64 = base64.StdEncoding + +/* +func sign(auth aws.Auth, method, path string, params url.Values, headers http.Header) { + var host string + for k, v := range headers { + k = strings.ToLower(k) + switch k { + case "host": + host = v[0] + } + } + + params["AWSAccessKeyId"] = []string{auth.AccessKey} + params["SignatureVersion"] = []string{"2"} + params["SignatureMethod"] = []string{"HmacSHA256"} + if auth.Token() != "" { + params["SecurityToken"] = auth.Token() + } + + var sarry []string + for k, v := range params { + sarry = append(sarry, aws.Encode(k) + "=" + aws.Encode(v[0])) + } + + sort.StringSlice(sarry).Sort() + joined := strings.Join(sarry, "&") + + payload := strings.Join([]string{method, host, "/", joined}, "\n") + hash := hmac.NewSHA256([]byte(auth.SecretKey)) + hash.Write([]byte(payload)) + signature := make([]byte, b64.EncodedLen(hash.Size())) + b64.Encode(signature, hash.Sum()) + + params["Signature"] = []string{"AWS " + string(signature)} + println("Payload:", payload) + println("Signature:", strings.Join(params["Signature"], "|")) +}*/ + +func sign(auth aws.Auth, method, path string, params map[string]string, host string) { + params["AWSAccessKeyId"] = auth.AccessKey + if auth.Token() != "" { + params["SecurityToken"] = auth.Token() + } + params["SignatureVersion"] = "2" + params["SignatureMethod"] = "HmacSHA256" + + var sarray []string + for k, v := range params { + sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v)) + } + sort.StringSlice(sarray).Sort() + joined := strings.Join(sarray, "&") + payload := method + "\n" + host + "\n" + path + "\n" + joined + hash := hmac.New(sha256.New, []byte(auth.SecretKey)) + hash.Write([]byte(payload)) + signature := make([]byte, b64.EncodedLen(hash.Size())) + b64.Encode(signature, hash.Sum(nil)) + + params["Signature"] = string(signature) +} diff --git a/vendor/github.com/goamz/goamz/exp/sns/sns.go b/vendor/github.com/goamz/goamz/exp/sns/sns.go new file mode 100644 index 000000000..4d4622211 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/sns.go @@ -0,0 +1,113 @@ +// +// goamz - Go packages to interact with the Amazon Web Services. +// +// https://wiki.ubuntu.com/goamz +// +// Copyright (c) 2011 Memeo Inc. +// +// Written by Prudhvi Krishna Surapaneni + +// This package is in an experimental state, and does not currently +// follow conventions and style of the rest of goamz or common +// Go conventions. It must be polished before it's considered a +// first-class package in goamz. +package sns + +// BUG(niemeyer): Package needs documentation. + +import ( + "encoding/xml" + "net/http" + "net/url" + "time" + + "github.com/goamz/goamz/aws" +) + +// The SNS type encapsulates operation with an SNS region. +type SNS struct { + aws.Auth + aws.Region + private byte // Reserve the right of using private data. +} + +type AttributeEntry struct { + Key string `xml:"key"` + Value string `xml:"value"` +} + +type ResponseMetadata struct { + RequestId string `xml:"ResponseMetadata>RequestId"` + BoxUsage float64 `xml:"ResponseMetadata>BoxUsage"` +} + +func New(auth aws.Auth, region aws.Region) *SNS { + return &SNS{auth, region, 0} +} + +func makeParams(action string) map[string]string { + params := make(map[string]string) + params["Action"] = action + return params +} + +type Error struct { + StatusCode int + Code string + Message string + RequestId string +} + +func (err *Error) Error() string { + return err.Message +} + +type xmlErrors struct { + RequestId string + Errors []Error `xml:"Errors>Error"` +} + +func (sns *SNS) query(params map[string]string, resp interface{}) error { + params["Timestamp"] = time.Now().UTC().Format(time.RFC3339) + u, err := url.Parse(sns.Region.SNSEndpoint) + if err != nil { + return err + } + + sign(sns.Auth, "GET", "/", params, u.Host) + u.RawQuery = multimap(params).Encode() + r, err := http.Get(u.String()) + if err != nil { + return err + } + defer r.Body.Close() + + if r.StatusCode != 200 { + return buildError(r) + } + err = xml.NewDecoder(r.Body).Decode(resp) + return err +} + +func buildError(r *http.Response) error { + errors := xmlErrors{} + xml.NewDecoder(r.Body).Decode(&errors) + var err Error + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } + err.RequestId = errors.RequestId + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} diff --git a/vendor/github.com/goamz/goamz/exp/sns/sns_test.go b/vendor/github.com/goamz/goamz/exp/sns/sns_test.go new file mode 100644 index 000000000..054db13d3 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/sns_test.go @@ -0,0 +1,455 @@ +package sns_test + +import ( + "testing" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/exp/sns" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + sns *sns.SNS +} + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.sns = sns.New(auth, aws.Region{SNSEndpoint: testServer.URL}) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestListTopicsOK(c *C) { + testServer.Response(200, nil, TestListTopicsXmlOK) + + resp, err := s.sns.ListTopics(nil) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.Topics[0].SNS, Equals, s.sns) + c.Assert(resp.ResponseMetadata.RequestId, Equals, "bd10b26c-e30e-11e0-ba29-93c3aca2f103") + c.Assert(err, IsNil) +} + +func (s *S) TestCreateTopic(c *C) { + testServer.Response(200, nil, TestCreateTopicXmlOK) + + resp, err := s.sns.CreateTopic("My-Topic") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.Topic.SNS, Equals, s.sns) + c.Assert(resp.Topic.TopicArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic") + c.Assert(resp.ResponseMetadata.RequestId, Equals, "a8dec8b3-33a4-11df-8963-01868b7c937a") + c.Assert(err, IsNil) +} + +func (s *S) TestDeleteTopic(c *C) { + testServer.Response(200, nil, TestDeleteTopicXmlOK) + + t := sns.Topic{s.sns, "arn:aws:sns:us-east-1:123456789012:My-Topic"} + resp, err := t.Delete() + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "f3aa9ac9-3c3d-11df-8235-9dab105e9c32") + c.Assert(err, IsNil) +} + +func (s *S) TestListSubscriptions(c *C) { + testServer.Response(200, nil, TestListSubscriptionsXmlOK) + + resp, err := s.sns.ListSubscriptions(nil) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(len(resp.Subscriptions), Not(Equals), 0) + c.Assert(resp.Subscriptions[0].Protocol, Equals, "email") + c.Assert(resp.Subscriptions[0].Endpoint, Equals, "example@amazon.com") + c.Assert(resp.Subscriptions[0].SubscriptionArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca") + c.Assert(resp.Subscriptions[0].TopicArn, Equals, "arn:aws:sns:us-east-1:698519295917:My-Topic") + c.Assert(resp.Subscriptions[0].Owner, Equals, "123456789012") + c.Assert(err, IsNil) +} + +func (s *S) TestGetTopicAttributes(c *C) { + testServer.Response(200, nil, TestGetTopicAttributesXmlOK) + + resp, err := s.sns.GetTopicAttributes("arn:aws:sns:us-east-1:123456789012:My-Topic") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(len(resp.Attributes), Not(Equals), 0) + c.Assert(resp.Attributes[0].Key, Equals, "Owner") + c.Assert(resp.Attributes[0].Value, Equals, "123456789012") + c.Assert(resp.Attributes[1].Key, Equals, "Policy") + c.Assert(resp.Attributes[1].Value, Equals, `{"Version":"2008-10-17","Id":"us-east-1/698519295917/test__default_policy_ID","Statement" : [{"Effect":"Allow","Sid":"us-east-1/698519295917/test__default_statement_ID","Principal" : {"AWS": "*"},"Action":["SNS:GetTopicAttributes","SNS:SetTopicAttributes","SNS:AddPermission","SNS:RemovePermission","SNS:DeleteTopic","SNS:Subscribe","SNS:ListSubscriptionsByTopic","SNS:Publish","SNS:Receive"],"Resource":"arn:aws:sns:us-east-1:698519295917:test","Condition" : {"StringLike" : {"AWS:SourceArn": "arn:aws:*:*:698519295917:*"}}}]}`) + c.Assert(resp.ResponseMetadata.RequestId, Equals, "057f074c-33a7-11df-9540-99d0768312d3") + c.Assert(err, IsNil) +} + +func (s *S) TestPublish(c *C) { + testServer.Response(200, nil, TestPublishXmlOK) + + pubOpt := &sns.PublishOpt{"foobar", "", "subject", "arn:aws:sns:us-east-1:123456789012:My-Topic"} + resp, err := s.sns.Publish(pubOpt) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.MessageId, Equals, "94f20ce6-13c5-43a0-9a9e-ca52d816e90b") + c.Assert(resp.ResponseMetadata.RequestId, Equals, "f187a3c1-376f-11df-8963-01868b7c937a") + c.Assert(err, IsNil) +} + +func (s *S) TestSetTopicAttributes(c *C) { + testServer.Response(200, nil, TestSetTopicAttributesXmlOK) + + resp, err := s.sns.SetTopicAttributes("DisplayName", "MyTopicName", "arn:aws:sns:us-east-1:123456789012:My-Topic") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "a8763b99-33a7-11df-a9b7-05d48da6f042") + c.Assert(err, IsNil) +} + +func (s *S) TestSubscribe(c *C) { + testServer.Response(200, nil, TestSubscribeXmlOK) + + resp, err := s.sns.Subscribe("example@amazon.com", "email", "arn:aws:sns:us-east-1:123456789012:My-Topic") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.SubscriptionArn, Equals, "pending confirmation") + c.Assert(resp.ResponseMetadata.RequestId, Equals, "a169c740-3766-11df-8963-01868b7c937a") + c.Assert(err, IsNil) +} + +func (s *S) TestUnsubscribe(c *C) { + testServer.Response(200, nil, TestUnsubscribeXmlOK) + + resp, err := s.sns.Unsubscribe("arn:aws:sns:us-east-1:123456789012:My-Topic:a169c740-3766-11df-8963-01868b7c937a") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "18e0ac39-3776-11df-84c0-b93cc1666b84") + c.Assert(err, IsNil) +} + +func (s *S) TestConfirmSubscription(c *C) { + testServer.Response(200, nil, TestConfirmSubscriptionXmlOK) + + opt := &sns.ConfirmSubscriptionOpt{"", "51b2ff3edb475b7d91550e0ab6edf0c1de2a34e6ebaf6", "arn:aws:sns:us-east-1:123456789012:My-Topic"} + resp, err := s.sns.ConfirmSubscription(opt) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.SubscriptionArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca") + c.Assert(resp.ResponseMetadata.RequestId, Equals, "7a50221f-3774-11df-a9b7-05d48da6f042") + c.Assert(err, IsNil) +} + +func (s *S) TestAddPermission(c *C) { + testServer.Response(200, nil, TestAddPermissionXmlOK) + perm := make([]sns.Permission, 2) + perm[0].ActionName = "Publish" + perm[1].ActionName = "GetTopicAttributes" + perm[0].AccountId = "987654321000" + perm[1].AccountId = "876543210000" + + resp, err := s.sns.AddPermission(perm, "NewPermission", "arn:aws:sns:us-east-1:123456789012:My-Topic") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.RequestId, Equals, "6a213e4e-33a8-11df-9540-99d0768312d3") + c.Assert(err, IsNil) +} + +func (s *S) TestRemovePermission(c *C) { + testServer.Response(200, nil, TestRemovePermissionXmlOK) + + resp, err := s.sns.RemovePermission("NewPermission", "arn:aws:sns:us-east-1:123456789012:My-Topic") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.RequestId, Equals, "d170b150-33a8-11df-995a-2d6fbe836cc1") + c.Assert(err, IsNil) +} + +func (s *S) TestListSubscriptionByTopic(c *C) { + testServer.Response(200, nil, TestListSubscriptionsByTopicXmlOK) + + opt := &sns.ListSubscriptionByTopicOpt{"", "arn:aws:sns:us-east-1:123456789012:My-Topic"} + resp, err := s.sns.ListSubscriptionByTopic(opt) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(len(resp.Subscriptions), Not(Equals), 0) + c.Assert(resp.Subscriptions[0].TopicArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic") + c.Assert(resp.Subscriptions[0].SubscriptionArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca") + c.Assert(resp.Subscriptions[0].Owner, Equals, "123456789012") + c.Assert(resp.Subscriptions[0].Endpoint, Equals, "example@amazon.com") + c.Assert(resp.Subscriptions[0].Protocol, Equals, "email") + c.Assert(err, IsNil) +} + +func (s *S) TestCreatePlatformApplication(c *C) { + testServer.Response(200, nil, TestCreatePlatformApplicationXmlOK) + + attrs := []sns.AttributeEntry{ + sns.AttributeEntry{Key: "PlatformCredential", Value: "AIzaSyClE2lcV2zEKTLYYo645zfk2jhQPFeyxDo"}, + sns.AttributeEntry{Key: "PlatformPrincipal", Value: "There is no principal for GCM"}, + } + opt := &sns.PlatformApplicationOpt{Name: "gcmpushapp", Platform: "GCM", Attributes: attrs} + resp, err := s.sns.CreatePlatformApplication(opt) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.PlatformApplicationArn, Equals, "arn:aws:sns:us-west-2:123456789012:app/GCM/gcmpushapp") + c.Assert(resp.RequestId, Equals, "b6f0e78b-e9d4-5a0e-b973-adc04e8a4ff9") + + c.Assert(err, IsNil) +} + +func (s *S) TestCreatePlatformEndpoint(c *C) { + testServer.Response(200, nil, TestCreatePlatformEndpointXmlOK) + + opt := &sns.PlatformEndpointOpt{PlatformApplicationArn: "arn:aws:sns:us-west-2:123456789012:app/GCM/gcmpushapp", + CustomUserData: "UserId=27576823", Token: "APA91bGi7fFachkC1xjlqT66VYEucGHochmf1VQAr9k...jsM0PKPxKhddCzx6paEsyay9Zn3D4wNUJb8m6HZrBEXAMPLE"} + + resp, err := s.sns.CreatePlatformEndpoint(opt) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.EndpointArn, Equals, "arn:aws:sns:us-west-2:123456789012:endpoint/GCM/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3") + c.Assert(resp.RequestId, Equals, "6613341d-3e15-53f7-bf3c-7e56994ba278") + + c.Assert(err, IsNil) +} + +func (s *S) TestDeleteEndpoint(c *C) { + testServer.Response(200, nil, DeleteEndpointXmlOK) + + resp, err := s.sns.DeleteEndpoint("arn:aws:sns:us-west-2:123456789012:endpoint/GCM%/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.RequestId, Equals, "c1d2b191-353c-5a5f-8969-fbdd3900afa8") + + c.Assert(err, IsNil) +} + +func (s *S) TestDeletePlatformApplication(c *C) { + testServer.Response(200, nil, TestDeletePlatformApplicationXmlOK) + + resp, err := s.sns.DeletePlatformApplication("arn:aws:sns:us-west-2:123456789012:app/GCM/gcmpushapp") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.RequestId, Equals, "097dac18-7a77-5823-a8dd-e65476dcb037") + + c.Assert(err, IsNil) +} + +func (s *S) TestGetEndpointAttributes(c *C) { + testServer.Response(200, nil, TestGetEndpointAttributesXmlOK) + + resp, err := s.sns.GetEndpointAttributes("arn:aws:sns:us-west-2:123456789012:endpoint/GCM/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(len(resp.Attributes), Equals, 3) + c.Assert(resp.Attributes[0].Key, Equals, "Enabled") + c.Assert(resp.Attributes[0].Value, Equals, "true") + + c.Assert(resp.Attributes[1].Key, Equals, "CustomUserData") + c.Assert(resp.Attributes[1].Value, Equals, "UserId=01234567") + + c.Assert(resp.Attributes[2].Key, Equals, "Token") + c.Assert(resp.Attributes[2].Value, Equals, "APA91bGi7fFachkC1xjlqT66VYEucGHochmf1VQAr9k...jsM0PKPxKhddCzx6paEsyay9Zn3D4wNUJb8m6HZrBEXAMPLE") + + c.Assert(resp.RequestId, Equals, "6c725a19-a142-5b77-94f9-1055a9ea04e7") + + c.Assert(err, IsNil) +} + +func (s *S) TestGetPlatformApplicationAttributes(c *C) { + testServer.Response(200, nil, TestGetPlatformApplicationAttributesXmlOK) + + resp, err := s.sns.GetPlatformApplicationAttributes("arn:aws:sns:us-west-2:123456789012:app/GCM/gcmpushapp", "") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(len(resp.Attributes), Not(Equals), 0) + c.Assert(resp.Attributes[0].Key, Equals, "AllowEndpointPolicies") + c.Assert(resp.Attributes[0].Value, Equals, "false") + c.Assert(resp.RequestId, Equals, "74848df2-87f6-55ed-890c-c7be80442462") + + c.Assert(err, IsNil) +} + +func (s *S) TestListEndpointsByPlatformApplication(c *C) { + testServer.Response(200, nil, TestListEndpointsByPlatformApplicationXmlOK) + + resp, err := s.sns.ListEndpointsByPlatformApplication("arn:aws:sns:us-west-2:123456789012:app/GCM/gcmpushapp", "") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(len(resp.Endpoints), Not(Equals), 0) + c.Assert(resp.Endpoints[0].EndpointArn, Equals, "arn:aws:sns:us-west-2:123456789012:endpoint/GCM/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3") + c.Assert(len(resp.Endpoints[0].Attributes), Equals, 3) + c.Assert(resp.Endpoints[0].Attributes[0].Key, Equals, "Enabled") + c.Assert(resp.Endpoints[0].Attributes[0].Value, Equals, "true") + c.Assert(resp.Endpoints[0].Attributes[1].Key, Equals, "CustomUserData") + c.Assert(resp.Endpoints[0].Attributes[1].Value, Equals, "UserId=27576823") + c.Assert(resp.Endpoints[0].Attributes[2].Key, Equals, "Token") + c.Assert(resp.Endpoints[0].Attributes[2].Value, Equals, "APA91bGi7fFachkC1xjlqT66VYEucGHochmf1VQAr9k...jsM0PKPxKhddCzx6paEsyay9Zn3D4wNUJb8m6HZrBEXAMPLE") + + c.Assert(resp.RequestId, Equals, "9a48768c-dac8-5a60-aec0-3cc27ea08d96") + + c.Assert(err, IsNil) +} + +func (s *S) TestListPlatformApplications(c *C) { + testServer.Response(200, nil, TestListPlatformApplicationsXmlOK) + + resp, err := s.sns.ListPlatformApplications("") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(len(resp.PlatformApplications), Not(Equals), 0) + + c.Assert(resp.PlatformApplications[0].PlatformApplicationArn, Equals, "arn:aws:sns:us-west-2:123456789012:app/APNS_SANDBOX/apnspushapp") + c.Assert(len(resp.PlatformApplications[0].Attributes), Equals, 1) + c.Assert(resp.PlatformApplications[0].Attributes[0].Key, Equals, "AllowEndpointPolicies") + c.Assert(resp.PlatformApplications[0].Attributes[0].Value, Equals, "false") + + c.Assert(resp.PlatformApplications[1].PlatformApplicationArn, Equals, "arn:aws:sns:us-west-2:123456789012:app/GCM/gcmpushapp") + c.Assert(len(resp.PlatformApplications[1].Attributes), Equals, 1) + c.Assert(resp.PlatformApplications[1].Attributes[0].Key, Equals, "AllowEndpointPolicies") + c.Assert(resp.PlatformApplications[1].Attributes[0].Value, Equals, "false") + + c.Assert(resp.RequestId, Equals, "315a335e-85d8-52df-9349-791283cbb529") + + c.Assert(err, IsNil) +} + +func (s *S) TestSetEndpointAttributes(c *C) { + testServer.Response(200, nil, TestSetEndpointAttributesXmlOK) + + attrs := []sns.AttributeEntry{ + sns.AttributeEntry{Key: "CustomUserData", Value: "My custom userdata"}, + } + + opts := &sns.SetEndpointAttributesOpt{ + EndpointArn: "arn:aws:sns:us-west-2:123456789012:endpoint/GCM/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3", + Attributes: attrs} + + resp, err := s.sns.SetEndpointAttributes(opts) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.RequestId, Equals, "2fe0bfc7-3e85-5ee5-a9e2-f58b35e85f6a") + + c.Assert(err, IsNil) +} + +func (s *S) TestSetPlatformApplicationAttributes(c *C) { + testServer.Response(200, nil, TestSetPlatformApplicationAttributesXmlOK) + + attrs := []sns.AttributeEntry{ + sns.AttributeEntry{Key: "EventEndpointCreated", Value: "arn:aws:sns:us-west-2:123456789012:topicarn"}, + } + + opts := &sns.SetPlatformApplicationAttributesOpt{ + PlatformApplicationArn: "arn:aws:sns:us-west-2:123456789012:app/GCM/gcmpushapp", + Attributes: attrs} + + resp, err := s.sns.SetPlatformApplicationAttributes(opts) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.RequestId, Equals, "cf577bcc-b3dc-5463-88f1-3180b9412395") + + c.Assert(err, IsNil) +} diff --git a/vendor/github.com/goamz/goamz/exp/sns/subscription.go b/vendor/github.com/goamz/goamz/exp/sns/subscription.go new file mode 100644 index 000000000..cbfef8b2c --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/subscription.go @@ -0,0 +1,165 @@ +package sns + +type Subscription struct { + Endpoint string + Owner string + Protocol string + SubscriptionArn string + TopicArn string +} + +type ListSubscriptionsResp struct { + Subscriptions []Subscription `xml:"ListSubscriptionsResult>Subscriptions>member"` + NextToken string + ResponseMetadata +} + +type PublishOpt struct { + Message string + MessageStructure string + Subject string + TopicArn string + TargetArn string +} + +type PublishResp struct { + MessageId string `xml:"PublishResult>MessageId"` + ResponseMetadata +} + +type SubscribeResponse struct { + SubscriptionArn string `xml:"SubscribeResult>SubscriptionArn"` + ResponseMetadata +} + +type UnsubscribeResponse struct { + ResponseMetadata +} + +type ConfirmSubscriptionResponse struct { + SubscriptionArn string `xml:"ConfirmSubscriptionResult>SubscriptionArn"` + ResponseMetadata +} + +type ConfirmSubscriptionOpt struct { + AuthenticateOnUnsubscribe string + Token string + TopicArn string +} + +type ListSubscriptionByTopicResponse struct { + Subscriptions []Subscription `xml:"ListSubscriptionsByTopicResult>Subscriptions>member"` + ResponseMetadata +} + +type ListSubscriptionByTopicOpt struct { + NextToken string + TopicArn string +} + +// Publish +// +// See http://goo.gl/AY2D8 for more details. +func (sns *SNS) Publish(options *PublishOpt) (resp *PublishResp, err error) { + resp = &PublishResp{} + params := makeParams("Publish") + + if options.Subject != "" { + params["Subject"] = options.Subject + } + + if options.MessageStructure != "" { + params["MessageStructure"] = options.MessageStructure + } + + if options.Message != "" { + params["Message"] = options.Message + } + + if options.TopicArn != "" { + params["TopicArn"] = options.TopicArn + } + + if options.TargetArn != "" { + params["TargetArn"] = options.TargetArn + } + + err = sns.query(params, resp) + return +} + +// Subscribe +// +// See http://goo.gl/c3iGS for more details. +func (sns *SNS) Subscribe(Endpoint, Protocol, TopicArn string) (resp *SubscribeResponse, err error) { + resp = &SubscribeResponse{} + params := makeParams("Subscribe") + + params["Endpoint"] = Endpoint + params["Protocol"] = Protocol + params["TopicArn"] = TopicArn + + err = sns.query(params, resp) + return +} + +// Unsubscribe +// +// See http://goo.gl/4l5Ge for more details. +func (sns *SNS) Unsubscribe(SubscriptionArn string) (resp *UnsubscribeResponse, err error) { + resp = &UnsubscribeResponse{} + params := makeParams("Unsubscribe") + + params["SubscriptionArn"] = SubscriptionArn + + err = sns.query(params, resp) + return +} + +// ConfirmSubscription +// +// See http://goo.gl/3hXzH for more details. +func (sns *SNS) ConfirmSubscription(options *ConfirmSubscriptionOpt) (resp *ConfirmSubscriptionResponse, err error) { + resp = &ConfirmSubscriptionResponse{} + params := makeParams("ConfirmSubscription") + + if options.AuthenticateOnUnsubscribe != "" { + params["AuthenticateOnUnsubscribe"] = options.AuthenticateOnUnsubscribe + } + + params["Token"] = options.Token + params["TopicArn"] = options.TopicArn + + err = sns.query(params, resp) + return +} + +// ListSubscriptions +// +// See http://goo.gl/k3aGn for more details. +func (sns *SNS) ListSubscriptions(NextToken *string) (resp *ListSubscriptionsResp, err error) { + resp = &ListSubscriptionsResp{} + params := makeParams("ListSubscriptions") + if NextToken != nil { + params["NextToken"] = *NextToken + } + err = sns.query(params, resp) + return +} + +// ListSubscriptionByTopic +// +// See http://goo.gl/LaVcC for more details. +func (sns *SNS) ListSubscriptionByTopic(options *ListSubscriptionByTopicOpt) (resp *ListSubscriptionByTopicResponse, err error) { + resp = &ListSubscriptionByTopicResponse{} + params := makeParams("ListSbubscriptionByTopic") + + if options.NextToken != "" { + params["NextToken"] = options.NextToken + } + + params["TopicArn"] = options.TopicArn + + err = sns.query(params, resp) + return +} diff --git a/vendor/github.com/goamz/goamz/exp/sns/topic.go b/vendor/github.com/goamz/goamz/exp/sns/topic.go new file mode 100644 index 000000000..601325ec9 --- /dev/null +++ b/vendor/github.com/goamz/goamz/exp/sns/topic.go @@ -0,0 +1,104 @@ +package sns + +import ( + "errors" +) + +type Topic struct { + SNS *SNS + TopicArn string +} + +type ListTopicsResp struct { + Topics []Topic `xml:"ListTopicsResult>Topics>member"` + NextToken string + ResponseMetadata +} + +type CreateTopicResp struct { + Topic Topic `xml:"CreateTopicResult"` + ResponseMetadata +} + +type DeleteTopicResp struct { + ResponseMetadata +} + +type GetTopicAttributesResp struct { + Attributes []AttributeEntry `xml:"GetTopicAttributesResult>Attributes>entry"` + ResponseMetadata +} + +type SetTopicAttributesResponse struct { + ResponseMetadata +} + +// ListTopics +// +// See http://goo.gl/lfrMK for more details. +func (sns *SNS) ListTopics(NextToken *string) (resp *ListTopicsResp, err error) { + resp = &ListTopicsResp{} + params := makeParams("ListTopics") + if NextToken != nil { + params["NextToken"] = *NextToken + } + + err = sns.query(params, resp) + for i, _ := range resp.Topics { + resp.Topics[i].SNS = sns + } + return +} + +// CreateTopic +// +// See http://goo.gl/m9aAt for more details. +func (sns *SNS) CreateTopic(Name string) (resp *CreateTopicResp, err error) { + resp = &CreateTopicResp{} + params := makeParams("CreateTopic") + params["Name"] = Name + err = sns.query(params, resp) + resp.Topic.SNS = sns + return +} + +// Delete +// +// Helper function for deleting a topic +func (topic *Topic) Delete() (resp *DeleteTopicResp, err error) { + resp = &DeleteTopicResp{} + params := makeParams("DeleteTopic") + params["TopicArn"] = topic.TopicArn + err = topic.SNS.query(params, resp) + return +} + +// GetTopicAttributes +// +// See http://goo.gl/WXRoX for more details. +func (sns *SNS) GetTopicAttributes(TopicArn string) (resp *GetTopicAttributesResp, err error) { + resp = &GetTopicAttributesResp{} + params := makeParams("GetTopicAttributes") + params["TopicArn"] = TopicArn + err = sns.query(params, resp) + return +} + +// SetTopicAttributes +// +// See http://goo.gl/oVYW7 for more details. +func (sns *SNS) SetTopicAttributes(AttributeName, AttributeValue, TopicArn string) (resp *SetTopicAttributesResponse, err error) { + resp = &SetTopicAttributesResponse{} + params := makeParams("SetTopicAttributes") + + if AttributeName == "" || TopicArn == "" { + return nil, errors.New("Invalid Attribute Name or TopicArn") + } + + params["AttributeName"] = AttributeName + params["AttributeValue"] = AttributeValue + params["TopicArn"] = TopicArn + + err = sns.query(params, resp) + return +} diff --git a/vendor/github.com/goamz/goamz/iam/iam.go b/vendor/github.com/goamz/goamz/iam/iam.go new file mode 100644 index 000000000..7271f1bf6 --- /dev/null +++ b/vendor/github.com/goamz/goamz/iam/iam.go @@ -0,0 +1,643 @@ +// The iam package provides types and functions for interaction with the AWS +// Identity and Access Management (IAM) service. +package iam + +import ( + "encoding/xml" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/goamz/goamz/aws" +) + +// The IAM type encapsulates operations operations with the IAM endpoint. +type IAM struct { + aws.Auth + aws.Region + httpClient *http.Client +} + +// New creates a new IAM instance. +func New(auth aws.Auth, region aws.Region) *IAM { + return NewWithClient(auth, region, aws.RetryingClient) +} + +func NewWithClient(auth aws.Auth, region aws.Region, httpClient *http.Client) *IAM { + return &IAM{auth, region, httpClient} +} + +func (iam *IAM) query(params map[string]string, resp interface{}) error { + params["Version"] = "2010-05-08" + params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339) + endpoint, err := url.Parse(iam.IAMEndpoint) + if err != nil { + return err + } + sign(iam.Auth, "GET", "/", params, endpoint.Host) + endpoint.RawQuery = multimap(params).Encode() + r, err := iam.httpClient.Get(endpoint.String()) + if err != nil { + return err + } + defer r.Body.Close() + if r.StatusCode > 200 { + return buildError(r) + } + + return xml.NewDecoder(r.Body).Decode(resp) +} + +func (iam *IAM) postQuery(params map[string]string, resp interface{}) error { + endpoint, err := url.Parse(iam.IAMEndpoint) + if err != nil { + return err + } + params["Version"] = "2010-05-08" + params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339) + sign(iam.Auth, "POST", "/", params, endpoint.Host) + encoded := multimap(params).Encode() + body := strings.NewReader(encoded) + req, err := http.NewRequest("POST", endpoint.String(), body) + if err != nil { + return err + } + req.Header.Set("Host", endpoint.Host) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Content-Length", strconv.Itoa(len(encoded))) + r, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer r.Body.Close() + if r.StatusCode > 200 { + return buildError(r) + } + return xml.NewDecoder(r.Body).Decode(resp) +} + +func buildError(r *http.Response) error { + var ( + err Error + errors xmlErrors + ) + xml.NewDecoder(r.Body).Decode(&errors) + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} + +// Response to a CreateUser request. +// +// See http://goo.gl/JS9Gz for more details. +type CreateUserResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` + User User `xml:"CreateUserResult>User"` +} + +// User encapsulates a user managed by IAM. +// +// See http://goo.gl/BwIQ3 for more details. +type User struct { + Arn string + Path string + Id string `xml:"UserId"` + Name string `xml:"UserName"` +} + +// CreateUser creates a new user in IAM. +// +// See http://goo.gl/JS9Gz for more details. +func (iam *IAM) CreateUser(name, path string) (*CreateUserResp, error) { + params := map[string]string{ + "Action": "CreateUser", + "Path": path, + "UserName": name, + } + resp := new(CreateUserResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response for GetUser requests. +// +// See http://goo.gl/ZnzRN for more details. +type GetUserResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` + User User `xml:"GetUserResult>User"` +} + +// GetUser gets a user from IAM. +// +// See http://goo.gl/ZnzRN for more details. +func (iam *IAM) GetUser(name string) (*GetUserResp, error) { + params := map[string]string{ + "Action": "GetUser", + "UserName": name, + } + resp := new(GetUserResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteUser deletes a user from IAM. +// +// See http://goo.gl/jBuCG for more details. +func (iam *IAM) DeleteUser(name string) (*SimpleResp, error) { + params := map[string]string{ + "Action": "DeleteUser", + "UserName": name, + } + resp := new(SimpleResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response to a CreateGroup request. +// +// See http://goo.gl/n7NNQ for more details. +type CreateGroupResp struct { + Group Group `xml:"CreateGroupResult>Group"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// Group encapsulates a group managed by IAM. +// +// See http://goo.gl/ae7Vs for more details. +type Group struct { + Arn string + Id string `xml:"GroupId"` + Name string `xml:"GroupName"` + Path string +} + +// CreateGroup creates a new group in IAM. +// +// The path parameter can be used to identify which division or part of the +// organization the user belongs to. +// +// If path is unset ("") it defaults to "/". +// +// See http://goo.gl/n7NNQ for more details. +func (iam *IAM) CreateGroup(name string, path string) (*CreateGroupResp, error) { + params := map[string]string{ + "Action": "CreateGroup", + "GroupName": name, + } + if path != "" { + params["Path"] = path + } + resp := new(CreateGroupResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response to a ListGroups request. +// +// See http://goo.gl/W2TRj for more details. +type GroupsResp struct { + Groups []Group `xml:"ListGroupsResult>Groups>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// Groups list the groups that have the specified path prefix. +// +// The parameter pathPrefix is optional. If pathPrefix is "", all groups are +// returned. +// +// See http://goo.gl/W2TRj for more details. +func (iam *IAM) Groups(pathPrefix string) (*GroupsResp, error) { + params := map[string]string{ + "Action": "ListGroups", + } + if pathPrefix != "" { + params["PathPrefix"] = pathPrefix + } + resp := new(GroupsResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteGroup deletes a group from IAM. +// +// See http://goo.gl/d5i2i for more details. +func (iam *IAM) DeleteGroup(name string) (*SimpleResp, error) { + params := map[string]string{ + "Action": "DeleteGroup", + "GroupName": name, + } + resp := new(SimpleResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response to a CreateAccessKey request. +// +// See http://goo.gl/L46Py for more details. +type CreateAccessKeyResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` + AccessKey AccessKey `xml:"CreateAccessKeyResult>AccessKey"` +} + +// AccessKey encapsulates an access key generated for a user. +// +// See http://goo.gl/LHgZR for more details. +type AccessKey struct { + UserName string + Id string `xml:"AccessKeyId"` + Secret string `xml:"SecretAccessKey,omitempty"` + Status string +} + +// CreateAccessKey creates a new access key in IAM. +// +// See http://goo.gl/L46Py for more details. +func (iam *IAM) CreateAccessKey(userName string) (*CreateAccessKeyResp, error) { + params := map[string]string{ + "Action": "CreateAccessKey", + "UserName": userName, + } + resp := new(CreateAccessKeyResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response to AccessKeys request. +// +// See http://goo.gl/Vjozx for more details. +type AccessKeysResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` + AccessKeys []AccessKey `xml:"ListAccessKeysResult>AccessKeyMetadata>member"` +} + +// AccessKeys lists all acccess keys associated with a user. +// +// The userName parameter is optional. If set to "", the userName is determined +// implicitly based on the AWS Access Key ID used to sign the request. +// +// See http://goo.gl/Vjozx for more details. +func (iam *IAM) AccessKeys(userName string) (*AccessKeysResp, error) { + params := map[string]string{ + "Action": "ListAccessKeys", + } + if userName != "" { + params["UserName"] = userName + } + resp := new(AccessKeysResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteAccessKey deletes an access key from IAM. +// +// The userName parameter is optional. If set to "", the userName is determined +// implicitly based on the AWS Access Key ID used to sign the request. +// +// See http://goo.gl/hPGhw for more details. +func (iam *IAM) DeleteAccessKey(id, userName string) (*SimpleResp, error) { + params := map[string]string{ + "Action": "DeleteAccessKey", + "AccessKeyId": id, + } + if userName != "" { + params["UserName"] = userName + } + resp := new(SimpleResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response to a GetUserPolicy request. +// +// See http://goo.gl/BH04O for more details. +type GetUserPolicyResp struct { + Policy UserPolicy `xml:"GetUserPolicyResult"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// UserPolicy encapsulates an IAM group policy. +// +// See http://goo.gl/C7hgS for more details. +type UserPolicy struct { + Name string `xml:"PolicyName"` + UserName string `xml:"UserName"` + Document string `xml:"PolicyDocument"` +} + +// GetUserPolicy gets a user policy in IAM. +// +// See http://goo.gl/BH04O for more details. +func (iam *IAM) GetUserPolicy(userName, policyName string) (*GetUserPolicyResp, error) { + params := map[string]string{ + "Action": "GetUserPolicy", + "UserName": userName, + "PolicyName": policyName, + } + resp := new(GetUserPolicyResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil + return nil, nil +} + +// PutUserPolicy creates a user policy in IAM. +// +// See http://goo.gl/ldCO8 for more details. +func (iam *IAM) PutUserPolicy(userName, policyName, policyDocument string) (*SimpleResp, error) { + params := map[string]string{ + "Action": "PutUserPolicy", + "UserName": userName, + "PolicyName": policyName, + "PolicyDocument": policyDocument, + } + resp := new(SimpleResp) + if err := iam.postQuery(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// DeleteUserPolicy deletes a user policy from IAM. +// +// See http://goo.gl/7Jncn for more details. +func (iam *IAM) DeleteUserPolicy(userName, policyName string) (*SimpleResp, error) { + params := map[string]string{ + "Action": "DeleteUserPolicy", + "PolicyName": policyName, + "UserName": userName, + } + resp := new(SimpleResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response for AddUserToGroup requests. +// +// See http://goo.gl/ZnzRN for more details. +type AddUserToGroupResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// AddUserToGroup adds a user to a specific group +// +// See http://goo.gl/ZnzRN for more details. +func (iam *IAM) AddUserToGroup(name, group string) (*AddUserToGroupResp, error) { + + params := map[string]string{ + "Action": "AddUserToGroup", + "GroupName": group, + "UserName": name} + resp := new(AddUserToGroupResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response for a ListAccountAliases request. +// +// See http://goo.gl/MMN79v for more details. +type ListAccountAliasesResp struct { + AccountAliases []string `xml:"ListAccountAliasesResult>AccountAliases>member"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// ListAccountAliases lists the account aliases associated with the account +// +// See http://goo.gl/MMN79v for more details. +func (iam *IAM) ListAccountAliases() (*ListAccountAliasesResp, error) { + params := map[string]string{ + "Action": "ListAccountAliases", + } + resp := new(ListAccountAliasesResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response for a CreateAccountAlias request. +// +// See http://goo.gl/oU5C4H for more details. +type CreateAccountAliasResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// CreateAccountAlias creates an alias for your AWS account. +// +// See http://goo.gl/oU5C4H for more details. +func (iam *IAM) CreateAccountAlias(alias string) (*CreateAccountAliasResp, error) { + params := map[string]string{ + "Action": "CreateAccountAlias", + "AccountAlias": alias, + } + resp := new(CreateAccountAliasResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Response for a DeleteAccountAlias request. +// +// See http://goo.gl/hKalgg for more details. +type DeleteAccountAliasResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DeleteAccountAlias deletes the specified AWS account alias. +// +// See http://goo.gl/hKalgg for more details. +func (iam *IAM) DeleteAccountAlias(alias string) (*DeleteAccountAliasResp, error) { + params := map[string]string{ + "Action": "DeleteAccountAlias", + "AccountAlias": alias, + } + resp := new(DeleteAccountAliasResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +type SimpleResp struct { + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +type xmlErrors struct { + Errors []Error `xml:"Error"` +} + +// ServerCertificateMetadata represents a ServerCertificateMetadata object +// +// See http://goo.gl/Rfu7LD for more details. +type ServerCertificateMetadata struct { + Arn string `xml:"Arn"` + Expiration time.Time `xml:"Expiration"` + Path string `xml:"Path"` + ServerCertificateId string `xml:"ServerCertificateId"` + ServerCertificateName string `xml:"ServerCertificateName"` + UploadDate time.Time `xml:"UploadDate"` +} + +// UploadServerCertificateResponse wraps up for UploadServerCertificate request. +// +// See http://goo.gl/bomzce for more details. +type UploadServerCertificateResponse struct { + ServerCertificateMetadata ServerCertificateMetadata `xml:"UploadServerCertificateResult>ServerCertificateMetadata"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// UploadServerCertificateParams wraps up the params to be passed for the UploadServerCertificate request +// +// See http://goo.gl/bomzce for more details. +type UploadServerCertificateParams struct { + ServerCertificateName string + PrivateKey string + CertificateBody string + CertificateChain string + Path string +} + +// UploadServerCertificate uploads a server certificate entity for the AWS account. +// +// Required Params: ServerCertificateName, PrivateKey, CertificateBody +// +// See http://goo.gl/bomzce for more details. +func (iam *IAM) UploadServerCertificate(options *UploadServerCertificateParams) ( + *UploadServerCertificateResponse, error) { + params := map[string]string{ + "Action": "UploadServerCertificate", + "ServerCertificateName": options.ServerCertificateName, + "PrivateKey": options.PrivateKey, + "CertificateBody": options.CertificateBody, + } + if options.CertificateChain != "" { + params["CertificateChain"] = options.CertificateChain + } + if options.Path != "" { + params["Path"] = options.Path + } + + resp := new(UploadServerCertificateResponse) + if err := iam.postQuery(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// ListServerCertificates lists all available certificates for the AWS account specified +// +// Required Params: None +// +// Optional Params: Marker, and, PathPrefix +// +// See http://goo.gl/bwn0Nb for specifics + +type ListServerCertificatesParams struct { + Marker string + PathPrefix string +} + +type ListServerCertificatesResp struct { + ServerCertificates []ServerCertificateMetadata `xml:"ListServerCertificatesResult>ServerCertificateMetadataList>member>ServerCertificateMetadata"` + RequestId string `xml:"ResponseMetadata>RequestId"` + IsTruncated bool `xml:"ListServerCertificatesResult>IsTruncated"` +} + +func (iam *IAM) ListServerCertificates(options *ListServerCertificatesParams) ( + *ListServerCertificatesResp, error) { + params := map[string]string{ + "Action": "ListServerCertificates", + } + + if options.Marker != "" { + params["Marker"] = options.Marker + } + + if options.PathPrefix != "" { + params["PathPrefix"] = options.PathPrefix + } + + resp := new(ListServerCertificatesResp) + if err := iam.query(params, resp); err != nil { + return nil, err + } + + return resp, nil +} + +// DeleteServerCertificate deletes the specified server certificate. +// +// See http://goo.gl/W4nmxQ for more details. +func (iam *IAM) DeleteServerCertificate(serverCertificateName string) (*SimpleResp, error) { + params := map[string]string{ + "Action": "DeleteServerCertificate", + "ServerCertificateName": serverCertificateName, + } + + resp := new(SimpleResp) + if err := iam.postQuery(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// Error encapsulates an IAM error. +type Error struct { + // HTTP status code of the error. + StatusCode int + + // AWS code of the error. + Code string + + // Message explaining the error. + Message string +} + +func (e *Error) Error() string { + var prefix string + if e.Code != "" { + prefix = e.Code + ": " + } + if prefix == "" && e.StatusCode > 0 { + prefix = strconv.Itoa(e.StatusCode) + ": " + } + return prefix + e.Message +} diff --git a/vendor/github.com/goamz/goamz/iam/iam_test.go b/vendor/github.com/goamz/goamz/iam/iam_test.go new file mode 100644 index 000000000..e73935670 --- /dev/null +++ b/vendor/github.com/goamz/goamz/iam/iam_test.go @@ -0,0 +1,450 @@ +package iam_test + +import ( + "strings" + "testing" + "time" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/iam" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +type S struct { + iam *iam.IAM +} + +var _ = Suite(&S{}) + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.iam = iam.NewWithClient(auth, aws.Region{IAMEndpoint: testServer.URL}, testutil.DefaultClient) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestCreateUser(c *C) { + testServer.Response(200, nil, CreateUserExample) + resp, err := s.iam.CreateUser("Bob", "/division_abc/subdivision_xyz/") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "CreateUser") + c.Assert(values.Get("UserName"), Equals, "Bob") + c.Assert(values.Get("Path"), Equals, "/division_abc/subdivision_xyz/") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") + expected := iam.User{ + Path: "/division_abc/subdivision_xyz/", + Name: "Bob", + Id: "AIDACKCEVSQ6C2EXAMPLE", + Arn: "arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob", + } + c.Assert(resp.User, DeepEquals, expected) +} + +func (s *S) TestCreateUserConflict(c *C) { + testServer.Response(409, nil, DuplicateUserExample) + resp, err := s.iam.CreateUser("Bob", "/division_abc/subdivision_xyz/") + testServer.WaitRequest() + c.Assert(resp, IsNil) + c.Assert(err, NotNil) + e, ok := err.(*iam.Error) + c.Assert(ok, Equals, true) + c.Assert(e.Message, Equals, "User with name Bob already exists.") + c.Assert(e.Code, Equals, "EntityAlreadyExists") +} + +func (s *S) TestGetUser(c *C) { + testServer.Response(200, nil, GetUserExample) + resp, err := s.iam.GetUser("Bob") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "GetUser") + c.Assert(values.Get("UserName"), Equals, "Bob") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") + expected := iam.User{ + Path: "/division_abc/subdivision_xyz/", + Name: "Bob", + Id: "AIDACKCEVSQ6C2EXAMPLE", + Arn: "arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob", + } + c.Assert(resp.User, DeepEquals, expected) +} + +func (s *S) TestDeleteUser(c *C) { + testServer.Response(200, nil, RequestIdExample) + resp, err := s.iam.DeleteUser("Bob") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "DeleteUser") + c.Assert(values.Get("UserName"), Equals, "Bob") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestCreateGroup(c *C) { + testServer.Response(200, nil, CreateGroupExample) + resp, err := s.iam.CreateGroup("Admins", "/admins/") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "CreateGroup") + c.Assert(values.Get("GroupName"), Equals, "Admins") + c.Assert(values.Get("Path"), Equals, "/admins/") + c.Assert(err, IsNil) + c.Assert(resp.Group.Path, Equals, "/admins/") + c.Assert(resp.Group.Name, Equals, "Admins") + c.Assert(resp.Group.Id, Equals, "AGPACKCEVSQ6C2EXAMPLE") + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestCreateGroupWithoutPath(c *C) { + testServer.Response(200, nil, CreateGroupExample) + _, err := s.iam.CreateGroup("Managers", "") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "CreateGroup") + c.Assert(err, IsNil) + _, ok := map[string][]string(values)["Path"] + c.Assert(ok, Equals, false) +} + +func (s *S) TestDeleteGroup(c *C) { + testServer.Response(200, nil, RequestIdExample) + resp, err := s.iam.DeleteGroup("Admins") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "DeleteGroup") + c.Assert(values.Get("GroupName"), Equals, "Admins") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestListGroups(c *C) { + testServer.Response(200, nil, ListGroupsExample) + resp, err := s.iam.Groups("/division_abc/") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "ListGroups") + c.Assert(values.Get("PathPrefix"), Equals, "/division_abc/") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") + expected := []iam.Group{ + { + Path: "/division_abc/subdivision_xyz/", + Name: "Admins", + Id: "AGPACKCEVSQ6C2EXAMPLE", + Arn: "arn:aws:iam::123456789012:group/Admins", + }, + { + Path: "/division_abc/subdivision_xyz/product_1234/engineering/", + Name: "Test", + Id: "AGP2MAB8DPLSRHEXAMPLE", + Arn: "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/engineering/Test", + }, + { + Path: "/division_abc/subdivision_xyz/product_1234/", + Name: "Managers", + Id: "AGPIODR4TAW7CSEXAMPLE", + Arn: "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/Managers", + }, + } + c.Assert(resp.Groups, DeepEquals, expected) +} + +func (s *S) TestListGroupsWithoutPathPrefix(c *C) { + testServer.Response(200, nil, ListGroupsExample) + _, err := s.iam.Groups("") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "ListGroups") + c.Assert(err, IsNil) + _, ok := map[string][]string(values)["PathPrefix"] + c.Assert(ok, Equals, false) +} + +func (s *S) TestCreateAccessKey(c *C) { + testServer.Response(200, nil, CreateAccessKeyExample) + resp, err := s.iam.CreateAccessKey("Bob") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "CreateAccessKey") + c.Assert(values.Get("UserName"), Equals, "Bob") + c.Assert(err, IsNil) + c.Assert(resp.AccessKey.UserName, Equals, "Bob") + c.Assert(resp.AccessKey.Id, Equals, "AKIAIOSFODNN7EXAMPLE") + c.Assert(resp.AccessKey.Secret, Equals, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY") + c.Assert(resp.AccessKey.Status, Equals, "Active") +} + +func (s *S) TestDeleteAccessKey(c *C) { + testServer.Response(200, nil, RequestIdExample) + resp, err := s.iam.DeleteAccessKey("ysa8hasdhasdsi", "Bob") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "DeleteAccessKey") + c.Assert(values.Get("AccessKeyId"), Equals, "ysa8hasdhasdsi") + c.Assert(values.Get("UserName"), Equals, "Bob") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestDeleteAccessKeyBlankUserName(c *C) { + testServer.Response(200, nil, RequestIdExample) + _, err := s.iam.DeleteAccessKey("ysa8hasdhasdsi", "") + c.Assert(err, IsNil) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "DeleteAccessKey") + c.Assert(values.Get("AccessKeyId"), Equals, "ysa8hasdhasdsi") + _, ok := map[string][]string(values)["UserName"] + c.Assert(ok, Equals, false) +} + +func (s *S) TestAccessKeys(c *C) { + testServer.Response(200, nil, ListAccessKeyExample) + resp, err := s.iam.AccessKeys("Bob") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "ListAccessKeys") + c.Assert(values.Get("UserName"), Equals, "Bob") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") + c.Assert(resp.AccessKeys, HasLen, 2) + c.Assert(resp.AccessKeys[0].Id, Equals, "AKIAIOSFODNN7EXAMPLE") + c.Assert(resp.AccessKeys[0].UserName, Equals, "Bob") + c.Assert(resp.AccessKeys[0].Status, Equals, "Active") + c.Assert(resp.AccessKeys[1].Id, Equals, "AKIAI44QH8DHBEXAMPLE") + c.Assert(resp.AccessKeys[1].UserName, Equals, "Bob") + c.Assert(resp.AccessKeys[1].Status, Equals, "Inactive") +} + +func (s *S) TestAccessKeysBlankUserName(c *C) { + testServer.Response(200, nil, ListAccessKeyExample) + _, err := s.iam.AccessKeys("") + c.Assert(err, IsNil) + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "ListAccessKeys") + _, ok := map[string][]string(values)["UserName"] + c.Assert(ok, Equals, false) +} + +func (s *S) TestGetUserPolicy(c *C) { + testServer.Response(200, nil, GetUserPolicyExample) + resp, err := s.iam.GetUserPolicy("Bob", "AllAccessPolicy") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "GetUserPolicy") + c.Assert(values.Get("UserName"), Equals, "Bob") + c.Assert(values.Get("PolicyName"), Equals, "AllAccessPolicy") + c.Assert(err, IsNil) + c.Assert(resp.Policy.UserName, Equals, "Bob") + c.Assert(resp.Policy.Name, Equals, "AllAccessPolicy") + c.Assert(strings.TrimSpace(resp.Policy.Document), Equals, `{"Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestPutUserPolicy(c *C) { + document := `{ + "Statement": [ + { + "Action": [ + "s3:*" + ], + "Effect": "Allow", + "Resource": [ + "arn:aws:s3:::8shsns19s90ajahadsj/*", + "arn:aws:s3:::8shsns19s90ajahadsj" + ] + }] + }` + testServer.Response(200, nil, RequestIdExample) + resp, err := s.iam.PutUserPolicy("Bob", "AllAccessPolicy", document) + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "POST") + c.Assert(req.FormValue("Action"), Equals, "PutUserPolicy") + c.Assert(req.FormValue("PolicyName"), Equals, "AllAccessPolicy") + c.Assert(req.FormValue("UserName"), Equals, "Bob") + c.Assert(req.FormValue("PolicyDocument"), Equals, document) + c.Assert(req.FormValue("Version"), Equals, "2010-05-08") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestDeleteUserPolicy(c *C) { + testServer.Response(200, nil, RequestIdExample) + resp, err := s.iam.DeleteUserPolicy("Bob", "AllAccessPolicy") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "DeleteUserPolicy") + c.Assert(values.Get("PolicyName"), Equals, "AllAccessPolicy") + c.Assert(values.Get("UserName"), Equals, "Bob") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestAddUserToGroup(c *C) { + testServer.Response(200, nil, AddUserToGroupExample) + resp, err := s.iam.AddUserToGroup("admin1", "Admins") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "AddUserToGroup") + c.Assert(values.Get("GroupName"), Equals, "Admins") + c.Assert(values.Get("UserName"), Equals, "admin1") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestListAccountAliases(c *C) { + testServer.Response(200, nil, ListAccountAliasesExample) + resp, err := s.iam.ListAccountAliases() + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "ListAccountAliases") + c.Assert(err, IsNil) + c.Assert(resp.AccountAliases[0], Equals, "foocorporation") + c.Assert(resp.RequestId, Equals, "c5a076e9-f1b0-11df-8fbe-45274EXAMPLE") +} + +func (s *S) TestCreateAccountAlias(c *C) { + testServer.Response(200, nil, CreateAccountAliasExample) + resp, err := s.iam.CreateAccountAlias("foobaz") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "CreateAccountAlias") + c.Assert(values.Get("AccountAlias"), Equals, "foobaz") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "36b5db08-f1b0-11df-8fbe-45274EXAMPLE") +} + +func (s *S) TestDeleteAccountAlias(c *C) { + testServer.Response(200, nil, DeleteAccountAliasExample) + resp, err := s.iam.DeleteAccountAlias("foobaz") + values := testServer.WaitRequest().URL.Query() + c.Assert(values.Get("Action"), Equals, "DeleteAccountAlias") + c.Assert(values.Get("AccountAlias"), Equals, "foobaz") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestUploadServerCertificate(c *C) { + testServer.Response(200, nil, UploadServerCertificateExample) + + certificateBody := ` +-----BEGIN CERTIFICATE----- +MIICdzCCAeCgAwIBAgIGANc+Ha2wMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNVBAYT +AlVTMRMwEQYDVQQKEwpBbWF6b24uY29tMQwwCgYDVQQLEwNBV1MxITAfBgNVBAMT +GEFXUyBMaW1pdGVkLUFzc3VyYW5jZSBDQTAeFw0wOTAyMDQxNzE5MjdaFw0xMDAy +MDQxNzE5MjdaMFIxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpBbWF6b24uY29tMRcw +FQYDVQQLEw5BV1MtRGV2ZWxvcGVyczEVMBMGA1UEAxMMNTdxNDl0c3ZwYjRtMIGf +MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpB/vsOwmT/O0td1RqzKjttSBaPjbr +dqwNe9BrOyB08fw2+Ch5oonZYXfGUrT6mkYXH5fQot9HvASrzAKHO596FdJA6DmL +ywdWe1Oggk7zFSXO1Xv+3vPrJtaYxYo3eRIp7w80PMkiOv6M0XK8ubcTouODeJbf +suDqcLnLDxwsvwIDAQABo1cwVTAOBgNVHQ8BAf8EBAMCBaAwFgYDVR0lAQH/BAww +CgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQULGNaBphBumaKbDRK +CAi0mH8B3mowDQYJKoZIhvcNAQEFBQADgYEAuKxhkXaCLGcqDuweKtO/AEw9ZePH +wr0XqsaIK2HZboqruebXEGsojK4Ks0WzwgrEynuHJwTn760xe39rSqXWIOGrOBaX +wFpWHVjTFMKk+tSDG1lssLHyYWWdFFU4AnejRGORJYNaRHgVTKjHphc5jEhHm0BX +AEaHzTpmEXAMPLE= +-----END CERTIFICATE----- +` + privateKey := ` +-----BEGIN DSA PRIVATE KEY----- +MIIBugIBTTKBgQD33xToSXPJ6hr37L3+KNi3/7DgywlBcvlFPPSHIw3ORuO/22mT +8Cy5fT89WwNvZ3BPKWU6OZ38TQv3eWjNc/3U3+oqVNG2poX5nCPOtO1b96HYX2mR +3FTdH6FRKbQEhpDzZ6tRrjTHjMX6sT3JRWkBd2c4bGu+HUHO1H7QvrCTeQIVTKMs +TCKCyrLiGhUWuUGNJUMU6y6zToGTHl84Tz7TPwDGDXuy/Dk5s4jTVr+xibROC/gS +Qrs4Dzz3T1ze6lvU8S1KT9UsOB5FUJNTTPCPey+Lo4mmK6b23XdTyCIT8e2fsm2j +jHHC1pIPiTkdLS3j6ZYjF8LY6TENFng+LDY/xwPOl7TJVoD3J/WXC2J9CEYq9o34 +kq6WWn3CgYTuo54nXUgnoCb3xdG8COFrg+oTbIkHTSzs3w5o/GGgKK7TDF3UlJjq +vHNyJQ6kWBrQRR1Xp5KYQ4c/Dm5kef+62mH53HpcCELguWVcffuVQpmq3EWL9Zp9 +jobTJQ2VHjb5IVxiO6HRSd27di3njyrzUuJCyHSDTqwLJmTThpd6OTIUTL3Tc4m2 +62TITdw53KWJEXAMPLE= +-----END DSA PRIVATE KEY----- +` + params := &iam.UploadServerCertificateParams{ + ServerCertificateName: "ProdServerCert", + Path: "/company/servercerts/", + PrivateKey: privateKey, + CertificateBody: certificateBody, + } + + resp, err := s.iam.UploadServerCertificate(params) + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "POST") + c.Assert(req.FormValue("Action"), Equals, "UploadServerCertificate") + c.Assert(req.FormValue("CertificateBody"), Equals, certificateBody) + c.Assert(req.FormValue("PrivateKey"), Equals, privateKey) + c.Assert(req.FormValue("ServerCertificateName"), Equals, "ProdServerCert") + c.Assert(req.FormValue("CertificateChain"), Equals, "") + c.Assert(req.FormValue("Path"), Equals, "/company/servercerts/") + c.Assert(req.FormValue("Version"), Equals, "2010-05-08") + c.Assert(err, IsNil) + + ud, _ := time.Parse(time.RFC3339, "2010-05-08T01:02:03.004Z") + exp, _ := time.Parse(time.RFC3339, "2012-05-08T01:02:03.004Z") + expected := iam.ServerCertificateMetadata{ + Arn: "arn:aws:iam::123456789012:server-certificate/company/servercerts/ProdServerCert", + ServerCertificateName: "ProdServerCert", + ServerCertificateId: "ASCACKCEVSQ6C2EXAMPLE", + Path: "/company/servercerts/", + UploadDate: ud, + Expiration: exp, + } + c.Assert(resp.ServerCertificateMetadata, DeepEquals, expected) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} + +func (s *S) TestListServerCertificates(c *C) { + testServer.Response(200, nil, ListServerCertificatesExample) + params := &iam.ListServerCertificatesParams{ + Marker: "my-fake-marker", + PathPrefix: "/some/fake/path", + } + + resp, err := s.iam.ListServerCertificates(params) + req := testServer.WaitRequest() + + c.Assert(err, IsNil) + c.Assert(req.Method, Equals, "GET") + c.Assert(req.FormValue("Action"), Equals, "ListServerCertificates") + c.Assert(req.FormValue("Marker"), Equals, "my-fake-marker") + c.Assert(req.FormValue("PathPrefix"), Equals, "/some/fake/path") + c.Assert(req.FormValue("Version"), Equals, "2010-05-08") + + uploadDate, _ := time.Parse(time.RFC3339, "2010-05-08T01:02:03.004Z") + expirationDate, _ := time.Parse(time.RFC3339, "2012-05-08T01:02:03.004Z") + expected := []iam.ServerCertificateMetadata{ + { + Arn: "arn:aws:iam::123456789012:server-certificate/company/servercerts/ProdServerCert", + ServerCertificateName: "ProdServerCert", + ServerCertificateId: "ASCACKCEVSQ6C2EXAMPLE1", + Path: "/some/fake/path", + UploadDate: uploadDate, + Expiration: expirationDate, + }, + { + Arn: "arn:aws:iam::123456789012:server-certificate/company/servercerts/BetaServerCert", + ServerCertificateName: "BetaServerCert", + ServerCertificateId: "ASCACKCEVSQ6C2EXAMPLE2", + Path: "/some/fake/path", + UploadDate: uploadDate, + Expiration: expirationDate, + }, + { + Arn: "arn:aws:iam::123456789012:server-certificate/company/servercerts/TestServerCert", + ServerCertificateName: "TestServerCert", + ServerCertificateId: "ASCACKCEVSQ6C2EXAMPLE3", + Path: "/some/fake/path", + UploadDate: uploadDate, + Expiration: expirationDate, + }, + } + + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eTHISDIFFERENTTEST") + c.Assert(resp.IsTruncated, Equals, false) + c.Assert(resp.ServerCertificates, DeepEquals, expected) +} + +func (s *S) TestDeleteServerCertificate(c *C) { + testServer.Response(200, nil, DeleteServerCertificateExample) + resp, err := s.iam.DeleteServerCertificate("ProdServerCert") + req := testServer.WaitRequest() + c.Assert(req.FormValue("Action"), Equals, "DeleteServerCertificate") + c.Assert(req.FormValue("ServerCertificateName"), Equals, "ProdServerCert") + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") +} diff --git a/vendor/github.com/goamz/goamz/iam/iami_test.go b/vendor/github.com/goamz/goamz/iam/iami_test.go new file mode 100644 index 000000000..26f32386f --- /dev/null +++ b/vendor/github.com/goamz/goamz/iam/iami_test.go @@ -0,0 +1,209 @@ +package iam_test + +import ( + "net/url" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/iam" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +// AmazonServer represents an Amazon AWS server. +type AmazonServer struct { + auth aws.Auth +} + +func (s *AmazonServer) SetUp(c *C) { + auth, err := aws.EnvAuth() + if err != nil { + c.Fatal(err) + } + s.auth = auth +} + +var _ = Suite(&AmazonClientSuite{}) + +// AmazonClientSuite tests the client against a live AWS server. +type AmazonClientSuite struct { + srv AmazonServer + ClientTests +} + +func (s *AmazonClientSuite) SetUpSuite(c *C) { + if !testutil.Amazon { + c.Skip("AmazonClientSuite tests not enabled") + } + s.srv.SetUp(c) + s.iam = iam.New(s.srv.auth, aws.USEast) +} + +// ClientTests defines integration tests designed to test the client. +// It is not used as a test suite in itself, but embedded within +// another type. +type ClientTests struct { + iam *iam.IAM +} + +func (s *ClientTests) TestCreateAndDeleteUser(c *C) { + createResp, err := s.iam.CreateUser("gopher", "/gopher/") + c.Assert(err, IsNil) + getResp, err := s.iam.GetUser("gopher") + c.Assert(err, IsNil) + c.Assert(createResp.User, DeepEquals, getResp.User) + _, err = s.iam.DeleteUser("gopher") + c.Assert(err, IsNil) +} + +func (s *ClientTests) TestCreateUserError(c *C) { + _, err := s.iam.CreateUser("gopher", "/gopher/") + c.Assert(err, IsNil) + defer s.iam.DeleteUser("gopher") + _, err = s.iam.CreateUser("gopher", "/") + iamErr, ok := err.(*iam.Error) + c.Assert(ok, Equals, true) + c.Assert(iamErr.StatusCode, Equals, 409) + c.Assert(iamErr.Code, Equals, "EntityAlreadyExists") + c.Assert(iamErr.Message, Equals, "User with name gopher already exists.") +} + +func (s *ClientTests) TestDeleteUserError(c *C) { + _, err := s.iam.DeleteUser("gopher") + iamErr, ok := err.(*iam.Error) + c.Assert(ok, Equals, true) + c.Assert(iamErr.StatusCode, Equals, 404) + c.Assert(iamErr.Code, Equals, "NoSuchEntity") + c.Assert(iamErr.Message, Equals, "The user with name gopher cannot be found.") +} + +func (s *ClientTests) TestGetUserError(c *C) { + _, err := s.iam.GetUser("gopher") + iamErr, ok := err.(*iam.Error) + c.Assert(ok, Equals, true) + c.Assert(iamErr.StatusCode, Equals, 404) + c.Assert(iamErr.Code, Equals, "NoSuchEntity") + c.Assert(iamErr.Message, Equals, "The user with name gopher cannot be found.") +} + +func (s *ClientTests) TestCreateListAndDeleteAccessKey(c *C) { + createUserResp, err := s.iam.CreateUser("gopher", "/gopher/") + c.Assert(err, IsNil) + defer s.iam.DeleteUser(createUserResp.User.Name) + createKeyResp, err := s.iam.CreateAccessKey(createUserResp.User.Name) + c.Assert(err, IsNil) + listKeyResp, err := s.iam.AccessKeys(createUserResp.User.Name) + c.Assert(err, IsNil) + c.Assert(listKeyResp.AccessKeys, HasLen, 1) + createKeyResp.AccessKey.Secret = "" + c.Assert(listKeyResp.AccessKeys[0], DeepEquals, createKeyResp.AccessKey) + _, err = s.iam.DeleteAccessKey(createKeyResp.AccessKey.Id, createUserResp.User.Name) + c.Assert(err, IsNil) +} + +func (s *ClientTests) TestCreateAccessKeyError(c *C) { + _, err := s.iam.CreateAccessKey("unknowngopher") + c.Assert(err, NotNil) + iamErr, ok := err.(*iam.Error) + c.Assert(ok, Equals, true) + c.Assert(iamErr.StatusCode, Equals, 404) + c.Assert(iamErr.Code, Equals, "NoSuchEntity") + c.Assert(iamErr.Message, Equals, "The user with name unknowngopher cannot be found.") +} + +func (s *ClientTests) TestListAccessKeysUserNotFound(c *C) { + _, err := s.iam.AccessKeys("unknowngopher") + c.Assert(err, NotNil) + iamErr, ok := err.(*iam.Error) + c.Assert(ok, Equals, true) + c.Assert(iamErr.StatusCode, Equals, 404) + c.Assert(iamErr.Code, Equals, "NoSuchEntity") + c.Assert(iamErr.Message, Equals, "The user with name unknowngopher cannot be found.") +} + +func (s *ClientTests) TestListAccessKeysUserWithoutKeys(c *C) { + createUserResp, err := s.iam.CreateUser("gopher", "/") + c.Assert(err, IsNil) + defer s.iam.DeleteUser(createUserResp.User.Name) + resp, err := s.iam.AccessKeys(createUserResp.User.Name) + c.Assert(err, IsNil) + c.Assert(resp.AccessKeys, HasLen, 0) +} + +func (s *ClientTests) TestCreateListAndDeleteGroup(c *C) { + cResp1, err := s.iam.CreateGroup("Finances", "/finances/") + c.Assert(err, IsNil) + cResp2, err := s.iam.CreateGroup("DevelopmentManagers", "/development/managers/") + c.Assert(err, IsNil) + lResp, err := s.iam.Groups("/development/") + c.Assert(err, IsNil) + c.Assert(lResp.Groups, HasLen, 1) + c.Assert(cResp2.Group, DeepEquals, lResp.Groups[0]) + lResp, err = s.iam.Groups("") + c.Assert(err, IsNil) + c.Assert(lResp.Groups, HasLen, 2) + if lResp.Groups[0].Name == cResp1.Group.Name { + c.Assert([]iam.Group{cResp1.Group, cResp2.Group}, DeepEquals, lResp.Groups) + } else { + c.Assert([]iam.Group{cResp2.Group, cResp1.Group}, DeepEquals, lResp.Groups) + } + _, err = s.iam.DeleteGroup("DevelopmentManagers") + c.Assert(err, IsNil) + lResp, err = s.iam.Groups("/development/") + c.Assert(err, IsNil) + c.Assert(lResp.Groups, HasLen, 0) + _, err = s.iam.DeleteGroup("Finances") + c.Assert(err, IsNil) +} + +func (s *ClientTests) TestCreateGroupError(c *C) { + _, err := s.iam.CreateGroup("Finances", "/finances/") + c.Assert(err, IsNil) + defer s.iam.DeleteGroup("Finances") + _, err = s.iam.CreateGroup("Finances", "/something-else/") + iamErr, ok := err.(*iam.Error) + c.Assert(ok, Equals, true) + c.Assert(iamErr.StatusCode, Equals, 409) + c.Assert(iamErr.Code, Equals, "EntityAlreadyExists") + c.Assert(iamErr.Message, Equals, "Group with name Finances already exists.") +} + +func (s *ClientTests) TestDeleteGroupError(c *C) { + _, err := s.iam.DeleteGroup("Finances") + iamErr, ok := err.(*iam.Error) + c.Assert(ok, Equals, true) + c.Assert(iamErr.StatusCode, Equals, 404) + c.Assert(iamErr.Code, Equals, "NoSuchEntity") + c.Assert(iamErr.Message, Equals, "The group with name Finances cannot be found.") +} + +func (s *ClientTests) TestPutGetAndDeleteUserPolicy(c *C) { + userResp, err := s.iam.CreateUser("gopher", "/gopher/") + c.Assert(err, IsNil) + defer s.iam.DeleteUser(userResp.User.Name) + document := `{ + "Statement": [ + { + "Action": [ + "s3:*" + ], + "Effect": "Allow", + "Resource": [ + "arn:aws:s3:::8shsns19s90ajahadsj/*", + "arn:aws:s3:::8shsns19s90ajahadsj" + ] + }] + }` + _, err = s.iam.PutUserPolicy(userResp.User.Name, "EverythingS3", document) + c.Assert(err, IsNil) + resp, err := s.iam.GetUserPolicy(userResp.User.Name, "EverythingS3") + c.Assert(err, IsNil) + c.Assert(resp.Policy.Name, Equals, "EverythingS3") + c.Assert(resp.Policy.UserName, Equals, userResp.User.Name) + gotDocument, err := url.QueryUnescape(resp.Policy.Document) + c.Assert(err, IsNil) + c.Assert(gotDocument, Equals, document) + _, err = s.iam.DeleteUserPolicy(userResp.User.Name, "EverythingS3") + c.Assert(err, IsNil) + _, err = s.iam.GetUserPolicy(userResp.User.Name, "EverythingS3") + c.Assert(err, NotNil) +} diff --git a/vendor/github.com/goamz/goamz/iam/iamt_test.go b/vendor/github.com/goamz/goamz/iam/iamt_test.go new file mode 100644 index 000000000..9d89f43e3 --- /dev/null +++ b/vendor/github.com/goamz/goamz/iam/iamt_test.go @@ -0,0 +1,39 @@ +package iam_test + +import ( + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/iam" + "github.com/goamz/goamz/iam/iamtest" + . "gopkg.in/check.v1" +) + +// LocalServer represents a local ec2test fake server. +type LocalServer struct { + auth aws.Auth + region aws.Region + srv *iamtest.Server +} + +func (s *LocalServer) SetUp(c *C) { + srv, err := iamtest.NewServer() + c.Assert(err, IsNil) + c.Assert(srv, NotNil) + + s.srv = srv + s.region = aws.Region{IAMEndpoint: srv.URL()} +} + +// LocalServerSuite defines tests that will run +// against the local iamtest server. It includes +// tests from ClientTests. +type LocalServerSuite struct { + srv LocalServer + ClientTests +} + +var _ = Suite(&LocalServerSuite{}) + +func (s *LocalServerSuite) SetUpSuite(c *C) { + s.srv.SetUp(c) + s.ClientTests.iam = iam.New(s.srv.auth, s.srv.region) +} diff --git a/vendor/github.com/goamz/goamz/iam/iamtest/server.go b/vendor/github.com/goamz/goamz/iam/iamtest/server.go new file mode 100644 index 000000000..08991d2f8 --- /dev/null +++ b/vendor/github.com/goamz/goamz/iam/iamtest/server.go @@ -0,0 +1,432 @@ +// Package iamtest implements a fake IAM provider with the capability of +// inducing errors on any given operation, and retrospectively determining what +// operations have been carried out. +package iamtest + +import ( + "encoding/json" + "encoding/xml" + "fmt" + "github.com/goamz/goamz/iam" + "net" + "net/http" + "strings" + "sync" +) + +type action struct { + srv *Server + w http.ResponseWriter + req *http.Request + reqId string +} + +// Server implements an IAM simulator for use in tests. +type Server struct { + reqId int + url string + listener net.Listener + users []iam.User + groups []iam.Group + accessKeys []iam.AccessKey + userPolicies []iam.UserPolicy + mutex sync.Mutex +} + +func NewServer() (*Server, error) { + l, err := net.Listen("tcp", "localhost:0") + if err != nil { + return nil, fmt.Errorf("cannot listen on localhost: %v", err) + } + srv := &Server{ + listener: l, + url: "http://" + l.Addr().String(), + } + go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + srv.serveHTTP(w, req) + })) + return srv, nil +} + +// Quit closes down the server. +func (srv *Server) Quit() error { + return srv.listener.Close() +} + +// URL returns a URL for the server. +func (srv *Server) URL() string { + return srv.url +} + +type xmlErrors struct { + XMLName string `xml:"ErrorResponse"` + Error iam.Error +} + +func (srv *Server) error(w http.ResponseWriter, err *iam.Error) { + w.WriteHeader(err.StatusCode) + xmlErr := xmlErrors{Error: *err} + if e := xml.NewEncoder(w).Encode(xmlErr); e != nil { + panic(e) + } +} + +func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) { + req.ParseForm() + srv.mutex.Lock() + defer srv.mutex.Unlock() + action := req.FormValue("Action") + if action == "" { + srv.error(w, &iam.Error{ + StatusCode: 400, + Code: "MissingAction", + Message: "Missing action", + }) + } + if a, ok := actions[action]; ok { + reqId := fmt.Sprintf("req%0X", srv.reqId) + srv.reqId++ + if resp, err := a(srv, w, req, reqId); err == nil { + if err := xml.NewEncoder(w).Encode(resp); err != nil { + panic(err) + } + } else { + switch err.(type) { + case *iam.Error: + srv.error(w, err.(*iam.Error)) + default: + panic(err) + } + } + } else { + srv.error(w, &iam.Error{ + StatusCode: 400, + Code: "InvalidAction", + Message: "Invalid action: " + action, + }) + } +} + +func (srv *Server) createUser(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"UserName"}); err != nil { + return nil, err + } + path := req.FormValue("Path") + if path == "" { + path = "/" + } + name := req.FormValue("UserName") + for _, user := range srv.users { + if user.Name == name { + return nil, &iam.Error{ + StatusCode: 409, + Code: "EntityAlreadyExists", + Message: fmt.Sprintf("User with name %s already exists.", name), + } + } + } + user := iam.User{ + Id: "USER" + reqId + "EXAMPLE", + Arn: fmt.Sprintf("arn:aws:iam:::123456789012:user%s%s", path, name), + Name: name, + Path: path, + } + srv.users = append(srv.users, user) + return iam.CreateUserResp{ + RequestId: reqId, + User: user, + }, nil +} + +func (srv *Server) getUser(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"UserName"}); err != nil { + return nil, err + } + name := req.FormValue("UserName") + index, err := srv.findUser(name) + if err != nil { + return nil, err + } + return iam.GetUserResp{RequestId: reqId, User: srv.users[index]}, nil +} + +func (srv *Server) deleteUser(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"UserName"}); err != nil { + return nil, err + } + name := req.FormValue("UserName") + index, err := srv.findUser(name) + if err != nil { + return nil, err + } + copy(srv.users[index:], srv.users[index+1:]) + srv.users = srv.users[:len(srv.users)-1] + return iam.SimpleResp{RequestId: reqId}, nil +} + +func (srv *Server) createAccessKey(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"UserName"}); err != nil { + return nil, err + } + userName := req.FormValue("UserName") + if _, err := srv.findUser(userName); err != nil { + return nil, err + } + key := iam.AccessKey{ + Id: fmt.Sprintf("%s%d", userName, len(srv.accessKeys)), + Secret: "", + UserName: userName, + Status: "Active", + } + srv.accessKeys = append(srv.accessKeys, key) + return iam.CreateAccessKeyResp{RequestId: reqId, AccessKey: key}, nil +} + +func (srv *Server) deleteAccessKey(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"AccessKeyId", "UserName"}); err != nil { + return nil, err + } + key := req.FormValue("AccessKeyId") + index := -1 + for i, ak := range srv.accessKeys { + if ak.Id == key { + index = i + break + } + } + if index < 0 { + return nil, &iam.Error{ + StatusCode: 404, + Code: "NoSuchEntity", + Message: "No such key.", + } + } + copy(srv.accessKeys[index:], srv.accessKeys[index+1:]) + srv.accessKeys = srv.accessKeys[:len(srv.accessKeys)-1] + return iam.SimpleResp{RequestId: reqId}, nil +} + +func (srv *Server) listAccessKeys(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"UserName"}); err != nil { + return nil, err + } + userName := req.FormValue("UserName") + if _, err := srv.findUser(userName); err != nil { + return nil, err + } + var keys []iam.AccessKey + for _, k := range srv.accessKeys { + if k.UserName == userName { + keys = append(keys, k) + } + } + return iam.AccessKeysResp{ + RequestId: reqId, + AccessKeys: keys, + }, nil +} + +func (srv *Server) createGroup(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"GroupName"}); err != nil { + return nil, err + } + name := req.FormValue("GroupName") + path := req.FormValue("Path") + for _, group := range srv.groups { + if group.Name == name { + return nil, &iam.Error{ + StatusCode: 409, + Code: "EntityAlreadyExists", + Message: fmt.Sprintf("Group with name %s already exists.", name), + } + } + } + group := iam.Group{ + Id: "GROUP " + reqId + "EXAMPLE", + Arn: fmt.Sprintf("arn:aws:iam:::123456789012:group%s%s", path, name), + Name: name, + Path: path, + } + srv.groups = append(srv.groups, group) + return iam.CreateGroupResp{ + RequestId: reqId, + Group: group, + }, nil +} + +func (srv *Server) listGroups(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + pathPrefix := req.FormValue("PathPrefix") + if pathPrefix == "" { + return iam.GroupsResp{ + RequestId: reqId, + Groups: srv.groups, + }, nil + } + var groups []iam.Group + for _, group := range srv.groups { + if strings.HasPrefix(group.Path, pathPrefix) { + groups = append(groups, group) + } + } + return iam.GroupsResp{ + RequestId: reqId, + Groups: groups, + }, nil +} + +func (srv *Server) deleteGroup(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"GroupName"}); err != nil { + return nil, err + } + name := req.FormValue("GroupName") + index := -1 + for i, group := range srv.groups { + if group.Name == name { + index = i + break + } + } + if index == -1 { + return nil, &iam.Error{ + StatusCode: 404, + Code: "NoSuchEntity", + Message: fmt.Sprintf("The group with name %s cannot be found.", name), + } + } + copy(srv.groups[index:], srv.groups[index+1:]) + srv.groups = srv.groups[:len(srv.groups)-1] + return iam.SimpleResp{RequestId: reqId}, nil +} + +func (srv *Server) putUserPolicy(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"UserName", "PolicyDocument", "PolicyName"}); err != nil { + return nil, err + } + var exists bool + policyName := req.FormValue("PolicyName") + userName := req.FormValue("UserName") + for _, policy := range srv.userPolicies { + if policyName == policy.Name && userName == policy.UserName { + exists = true + break + } + } + if !exists { + policy := iam.UserPolicy{ + Name: policyName, + UserName: userName, + Document: req.FormValue("PolicyDocument"), + } + var dumb interface{} + if err := json.Unmarshal([]byte(policy.Document), &dumb); err != nil { + return nil, &iam.Error{ + StatusCode: 400, + Code: "MalformedPolicyDocument", + Message: "Malformed policy document", + } + } + srv.userPolicies = append(srv.userPolicies, policy) + } + return iam.SimpleResp{RequestId: reqId}, nil +} + +func (srv *Server) deleteUserPolicy(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"UserName", "PolicyName"}); err != nil { + return nil, err + } + policyName := req.FormValue("PolicyName") + userName := req.FormValue("UserName") + index := -1 + for i, policy := range srv.userPolicies { + if policyName == policy.Name && userName == policy.UserName { + index = i + break + } + } + if index < 0 { + return nil, &iam.Error{ + StatusCode: 404, + Code: "NoSuchEntity", + Message: "No such user policy", + } + } + copy(srv.userPolicies[index:], srv.userPolicies[index+1:]) + srv.userPolicies = srv.userPolicies[:len(srv.userPolicies)-1] + return iam.SimpleResp{RequestId: reqId}, nil +} + +func (srv *Server) getUserPolicy(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { + if err := srv.validate(req, []string{"UserName", "PolicyName"}); err != nil { + return nil, err + } + policyName := req.FormValue("PolicyName") + userName := req.FormValue("UserName") + index := -1 + for i, policy := range srv.userPolicies { + if policyName == policy.Name && userName == policy.UserName { + index = i + break + } + } + if index < 0 { + return nil, &iam.Error{ + StatusCode: 404, + Code: "NoSuchEntity", + Message: "No such user policy", + } + } + return iam.GetUserPolicyResp{ + Policy: srv.userPolicies[index], + RequestId: reqId, + }, nil +} + +func (srv *Server) findUser(userName string) (int, error) { + var ( + err error + index = -1 + ) + for i, user := range srv.users { + if user.Name == userName { + index = i + break + } + } + if index < 0 { + err = &iam.Error{ + StatusCode: 404, + Code: "NoSuchEntity", + Message: fmt.Sprintf("The user with name %s cannot be found.", userName), + } + } + return index, err +} + +// Validates the presence of required request parameters. +func (srv *Server) validate(req *http.Request, required []string) error { + for _, r := range required { + if req.FormValue(r) == "" { + return &iam.Error{ + StatusCode: 400, + Code: "InvalidParameterCombination", + Message: fmt.Sprintf("%s is required.", r), + } + } + } + return nil +} + +var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) (interface{}, error){ + "CreateUser": (*Server).createUser, + "DeleteUser": (*Server).deleteUser, + "GetUser": (*Server).getUser, + "CreateAccessKey": (*Server).createAccessKey, + "DeleteAccessKey": (*Server).deleteAccessKey, + "ListAccessKeys": (*Server).listAccessKeys, + "PutUserPolicy": (*Server).putUserPolicy, + "DeleteUserPolicy": (*Server).deleteUserPolicy, + "GetUserPolicy": (*Server).getUserPolicy, + "CreateGroup": (*Server).createGroup, + "DeleteGroup": (*Server).deleteGroup, + "ListGroups": (*Server).listGroups, +} diff --git a/vendor/github.com/goamz/goamz/iam/responses_test.go b/vendor/github.com/goamz/goamz/iam/responses_test.go new file mode 100644 index 000000000..d8a0b2c4c --- /dev/null +++ b/vendor/github.com/goamz/goamz/iam/responses_test.go @@ -0,0 +1,261 @@ +package iam_test + +// http://goo.gl/EUIvl +var CreateUserExample = ` + + + + /division_abc/subdivision_xyz/ + Bob + AIDACKCEVSQ6C2EXAMPLE + arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var DuplicateUserExample = ` + + + Sender + EntityAlreadyExists + User with name Bob already exists. + + 1d5f5000-1316-11e2-a60f-91a8e6fb6d21 + +` + +var GetUserExample = ` + + + + /division_abc/subdivision_xyz/ + Bob + AIDACKCEVSQ6C2EXAMPLE + arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var CreateGroupExample = ` + + + + /admins/ + Admins + AGPACKCEVSQ6C2EXAMPLE + arn:aws:iam::123456789012:group/Admins + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var ListGroupsExample = ` + + + + + /division_abc/subdivision_xyz/ + Admins + AGPACKCEVSQ6C2EXAMPLE + arn:aws:iam::123456789012:group/Admins + + + /division_abc/subdivision_xyz/product_1234/engineering/ + Test + AGP2MAB8DPLSRHEXAMPLE + arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/engineering/Test + + + /division_abc/subdivision_xyz/product_1234/ + Managers + AGPIODR4TAW7CSEXAMPLE + arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/Managers + + + false + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var RequestIdExample = ` + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var CreateAccessKeyExample = ` + + + + Bob + AKIAIOSFODNN7EXAMPLE + Active + wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var ListAccessKeyExample = ` + + + Bob + + + Bob + AKIAIOSFODNN7EXAMPLE + Active + + + Bob + AKIAI44QH8DHBEXAMPLE + Inactive + + + false + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var GetUserPolicyExample = ` + + + Bob + AllAccessPolicy + + {"Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]} + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var AddUserToGroupExample = ` + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var ListAccountAliasesExample = ` + + + false + + foocorporation + + + + c5a076e9-f1b0-11df-8fbe-45274EXAMPLE + + +` + +var CreateAccountAliasExample = ` + + + 36b5db08-f1b0-11df-8fbe-45274EXAMPLE + + +` + +var DeleteAccountAliasExample = ` + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` + +var UploadServerCertificateExample = ` + + + + ProdServerCert + /company/servercerts/ + arn:aws:iam::123456789012:server-certificate/company/servercerts/ProdServerCert + 2010-05-08T01:02:03.004Z + ASCACKCEVSQ6C2EXAMPLE + 2012-05-08T01:02:03.004Z + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` +var ListServerCertificatesExample = ` + + + false + + + + ProdServerCert + /some/fake/path + arn:aws:iam::123456789012:server-certificate/company/servercerts/ProdServerCert + 2010-05-08T01:02:03.004Z + ASCACKCEVSQ6C2EXAMPLE1 + 2012-05-08T01:02:03.004Z + + + + + BetaServerCert + /some/fake/path + arn:aws:iam::123456789012:server-certificate/company/servercerts/BetaServerCert + 2010-05-08T01:02:03.004Z + ASCACKCEVSQ6C2EXAMPLE2 + 2012-05-08T01:02:03.004Z + + + + + TestServerCert + /some/fake/path + arn:aws:iam::123456789012:server-certificate/company/servercerts/TestServerCert + 2010-05-08T01:02:03.004Z + ASCACKCEVSQ6C2EXAMPLE3 + 2012-05-08T01:02:03.004Z + + + + + + 7a62c49f-347e-4fc4-9331-6e8eTHISDIFFERENTTEST + + +` + +var DeleteServerCertificateExample = ` + + +7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + +` diff --git a/vendor/github.com/goamz/goamz/iam/sign.go b/vendor/github.com/goamz/goamz/iam/sign.go new file mode 100644 index 000000000..b704fd8d6 --- /dev/null +++ b/vendor/github.com/goamz/goamz/iam/sign.go @@ -0,0 +1,38 @@ +package iam + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "github.com/goamz/goamz/aws" + "sort" + "strings" +) + +// ---------------------------------------------------------------------------- +// Version 2 signing (http://goo.gl/RSRp5) + +var b64 = base64.StdEncoding + +func sign(auth aws.Auth, method, path string, params map[string]string, host string) { + params["AWSAccessKeyId"] = auth.AccessKey + params["SignatureVersion"] = "2" + params["SignatureMethod"] = "HmacSHA256" + if auth.Token() != "" { + params["SecurityToken"] = auth.Token() + } + + var sarray []string + for k, v := range params { + sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v)) + } + sort.StringSlice(sarray).Sort() + joined := strings.Join(sarray, "&") + payload := method + "\n" + host + "\n" + path + "\n" + joined + hash := hmac.New(sha256.New, []byte(auth.SecretKey)) + hash.Write([]byte(payload)) + signature := make([]byte, b64.EncodedLen(hash.Size())) + b64.Encode(signature, hash.Sum(nil)) + + params["Signature"] = string(signature) +} diff --git a/vendor/github.com/goamz/goamz/rds/rds.go b/vendor/github.com/goamz/goamz/rds/rds.go new file mode 100644 index 000000000..f00b52c64 --- /dev/null +++ b/vendor/github.com/goamz/goamz/rds/rds.go @@ -0,0 +1,96 @@ +package rds + +import ( + "encoding/xml" + "github.com/goamz/goamz/aws" + "log" + "net/http/httputil" + "strconv" +) + +const debug = false + +const ( + ServiceName = "rds" + ApiVersion = "2013-09-09" +) + +// The RDS type encapsulates operations within a specific EC2 region. +type RDS struct { + Service aws.AWSService +} + +// New creates a new RDS Client. +func New(auth aws.Auth, region aws.Region) (*RDS, error) { + service, err := aws.NewService(auth, region.RDSEndpoint) + if err != nil { + return nil, err + } + return &RDS{ + Service: service, + }, nil +} + +// ---------------------------------------------------------------------------- +// Request dispatching logic. + +// query dispatches a request to the RDS API signed with a version 2 signature +func (rds *RDS) query(method, path string, params map[string]string, resp interface{}) error { + // Add basic RDS param + params["Version"] = ApiVersion + + r, err := rds.Service.Query(method, path, params) + if err != nil { + return err + } + defer r.Body.Close() + + if debug { + dump, _ := httputil.DumpResponse(r, true) + log.Printf("response:\n") + log.Printf("%v\n}\n", string(dump)) + } + + if r.StatusCode != 200 { + return rds.Service.BuildError(r) + } + err = xml.NewDecoder(r.Body).Decode(resp) + return err +} + +// ---------------------------------------------------------------------------- +// API methods and corresponding response types. + +// Response to a DescribeDBInstances request +// +// See http://goo.gl/KSPlAl for more details. +type DescribeDBInstancesResponse struct { + DBInstances []DBInstance `xml:"DescribeDBInstancesResult>DBInstances>DBInstance"` // The list of database instances + Marker string `xml:"DescribeDBInstancesResult>Marker"` // An optional pagination token provided by a previous request + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// DescribeDBInstances - Returns a description of each Database Instance +// Supports pagination by using the "Marker" parameter, and "maxRecords" for subsequent calls +// Unfortunately RDS does not currently support filtering +// +// See http://goo.gl/lzZMyz for more details. +func (rds *RDS) DescribeDBInstances(id string, maxRecords int, marker string) (*DescribeDBInstancesResponse, error) { + + params := aws.MakeParams("DescribeDBInstances") + + if id != "" { + params["DBInstanceIdentifier"] = id + } + + if maxRecords != 0 { + params["MaxRecords"] = strconv.Itoa(maxRecords) + } + if marker != "" { + params["Marker"] = marker + } + + resp := &DescribeDBInstancesResponse{} + err := rds.query("POST", "/", params, resp) + return resp, err +} diff --git a/vendor/github.com/goamz/goamz/rds/rds_test.go b/vendor/github.com/goamz/goamz/rds/rds_test.go new file mode 100644 index 000000000..ac277d4d7 --- /dev/null +++ b/vendor/github.com/goamz/goamz/rds/rds_test.go @@ -0,0 +1,77 @@ +package rds_test + +import ( + "testing" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/rds" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + rds *rds.RDS +} + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + var err error + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.rds, err = rds.New(auth, aws.Region{RDSEndpoint: aws.ServiceInfo{testServer.URL, aws.V2Signature}}) + c.Assert(err, IsNil) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestDescribeDBInstancesExample1(c *C) { + testServer.Response(200, nil, DescribeDBInstancesExample1) + + resp, err := s.rds.DescribeDBInstances("simcoprod01", 0, "") + + req := testServer.WaitRequest() + c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeDBInstances"}) + c.Assert(req.Form["DBInstanceIdentifier"], DeepEquals, []string{"simcoprod01"}) + + c.Assert(err, IsNil) + c.Assert(resp.RequestId, Equals, "9135fff3-8509-11e0-bd9b-a7b1ece36d51") + c.Assert(resp.DBInstances, HasLen, 1) + + db0 := resp.DBInstances[0] + c.Assert(db0.AllocatedStorage, Equals, 10) + c.Assert(db0.AutoMinorVersionUpgrade, Equals, true) + c.Assert(db0.AvailabilityZone, Equals, "us-east-1a") + c.Assert(db0.BackupRetentionPeriod, Equals, 1) + + c.Assert(db0.DBInstanceClass, Equals, "db.m1.large") + c.Assert(db0.DBInstanceIdentifier, Equals, "simcoprod01") + c.Assert(db0.DBInstanceStatus, Equals, "available") + c.Assert(db0.DBName, Equals, "simcoprod") + + c.Assert(db0.Endpoint.Address, Equals, "simcoprod01.cu7u2t4uz396.us-east-1.rds.amazonaws.com") + c.Assert(db0.Endpoint.Port, Equals, 3306) + c.Assert(db0.Engine, Equals, "mysql") + c.Assert(db0.EngineVersion, Equals, "5.1.50") + c.Assert(db0.InstanceCreateTime, Equals, "2011-05-23T06:06:43.110Z") + + c.Assert(db0.LatestRestorableTime, Equals, "2011-05-23T06:50:00Z") + c.Assert(db0.LicenseModel, Equals, "general-public-license") + c.Assert(db0.MasterUsername, Equals, "master") + c.Assert(db0.MultiAZ, Equals, false) + c.Assert(db0.OptionGroupMemberships, HasLen, 1) + c.Assert(db0.OptionGroupMemberships[0].Name, Equals, "default.mysql5.1") + c.Assert(db0.OptionGroupMemberships[0].Status, Equals, "in-sync") + + c.Assert(db0.PreferredBackupWindow, Equals, "00:00-00:30") + c.Assert(db0.PreferredMaintenanceWindow, Equals, "sat:07:30-sat:08:00") + c.Assert(db0.PubliclyAccessible, Equals, false) +} diff --git a/vendor/github.com/goamz/goamz/rds/responses_test.go b/vendor/github.com/goamz/goamz/rds/responses_test.go new file mode 100644 index 000000000..b7912f84b --- /dev/null +++ b/vendor/github.com/goamz/goamz/rds/responses_test.go @@ -0,0 +1,57 @@ +package rds_test + +var DescribeDBInstancesExample1 = ` + + + + + + 2011-05-23T06:50:00Z + mysql + + 1 + false + general-public-license + available + 5.1.50 + + 3306 +
simcoprod01.cu7u2t4uz396.us-east-1.rds.amazonaws.com
+
+ simcoprod01 + simcoprod + + + in-sync + default.mysql5.1 + + + + + active + default + + + 00:00-00:30 + true + sat:07:30-sat:08:00 + us-east-1a + 2011-05-23T06:06:43.110Z + 10 + + + default.mysql5.1 + in-sync + + + db.m1.large + master + false +
+
+
+ + 9135fff3-8509-11e0-bd9b-a7b1ece36d51 + +
+` diff --git a/vendor/github.com/goamz/goamz/rds/types.go b/vendor/github.com/goamz/goamz/rds/types.go new file mode 100644 index 000000000..aa9c4024f --- /dev/null +++ b/vendor/github.com/goamz/goamz/rds/types.go @@ -0,0 +1,388 @@ +package rds + +// AvailabilityZone contains Availability Zone information +// See http://goo.gl/GWF4zF for more details. +type AvailabilityZone struct { + Name string `xml:"Name"` + ProvisionedIopsCapable bool `xml:"ProvisionedIopsCapable"` +} + +// CharacterSet represents a character set used by a Database Engine +// See http://goo.gl/0BXwFp for more details. +type CharacterSet struct { + Name string `xml:"CharacterSetName"` + Description string `xml:"CharacterSetDescription"` +} + +// DBEngineVersion describes a version of a Database Engine +// See http://goo.gl/a5l6cv for more details. +type DBEngineVersion struct { + DBEngineDescription string `xml:"DBEngineDescription"` // The description of the database engine + DBEngineVersionDescription string `xml:"DBEngineVersionDescription"` // The description of the database engine version + DBParameterGroupFamily string `xml:"DBParameterGroupFamily"` // The name of the DB parameter group family for the database engine + DefaultCharacterSet CharacterSet `xml:"DefaultCharacterSet"` // The default character set for new instances of this engine version, if the CharacterSetName parameter of the CreateDBInstance API is not specified + Engine string `xml:"Engine"` // The name of the database engine + EngineVersion string `xml:"EngineVersion"` // The version number of the database engine + SupportedCharacterSets []CharacterSet `xml:"SupportedCharacterSets"` // A list of the character sets supported by this engine for the CharacterSetName parameter of the CreateDBInstance API +} + +// DBInstance encapsulates an instance of a Database +// See http://goo.gl/rQFpAe for more details. +type DBInstance struct { + AllocatedStorage int `xml:"AllocatedStorage"` // Specifies the allocated storage size specified in gigabytes. + AutoMinorVersionUpgrade bool `xml:"AutoMinorVersionUpgrade"` // Indicates that minor version patches are applied automatically. + AvailabilityZone string `xml:"AvailabilityZone"` // Specifies the name of the Availability Zone the DB instance is located in. + BackupRetentionPeriod int `xml:"BackupRetentionPeriod"` // Specifies the number of days for which automatic DB snapshots are retained. + CharacterSetName string `xml:"CharacterSetName"` // If present, specifies the name of the character set that this instance is associated with. + DBInstanceClass string `xml:"DBInstanceClass"` // Contains the name of the compute and memory capacity class of the DB instance. + DBInstanceIdentifier string `xml:"DBInstanceIdentifier"` // Contains a user-supplied database identifier. This is the unique key that identifies a DB instance. + DBInstanceStatus string `xml:"DBInstanceStatus"` // Specifies the current state of this database. + DBName string `xml:"DBName"` // The meaning of this parameter differs according to the database engine you use. + DBParameterGroups []DBParameterGroupStatus `xml:"DBParameterGroups>DBParameterGroup"` // Provides the list of DB parameter groups applied to this DB instance. + DBSecurityGroups []DBSecurityGroupMembership `xml:"DBSecurityGroups>DBSecurityGroup"` // Provides List of DB security group elements containing only DBSecurityGroup.Name and DBSecurityGroup.Status subelements. + DBSubnetGroup DBSubnetGroup `xml:"DBSubnetGroup"` // Specifies information on the subnet group associated with the DB instance, including the name, description, and subnets in the subnet group. + Endpoint Endpoint `xml:"Endpoint"` // Specifies the connection endpoint. + Engine string `xml:"Engine"` // Provides the name of the database engine to be used for this DB instance. + EngineVersion string `xml:"EngineVersion"` // Indicates the database engine version. + InstanceCreateTime string `xml:"InstanceCreateTime"` // Provides the date and time the DB instance was created. + Iops int `xml:"Iops"` // Specifies the Provisioned IOPS (I/O operations per second) value. + LatestRestorableTime string `xml:"LatestRestorableTime"` // Specifies the latest time to which a database can be restored with point-in-time restore. + LicenseModel string `xml:"LicenseModel"` // License model information for this DB instance. + MasterUsername string `xml:"MasterUsername"` // Contains the master username for the DB instance. + MultiAZ bool `xml:"MultiAZ"` // Specifies if the DB instance is a Multi-AZ deployment. + OptionGroupMemberships []OptionGroupMembership `xml:"OptionGroupMemberships>OptionGroupMembership"` // Provides the list of option group memberships for this DB instance. + PendingModifiedValues PendingModifiedValues `xml:"PendingModifiedValues"` // Specifies that changes to the DB instance are pending. This element is only included when changes are pending. Specific changes are identified by subelements. + PreferredBackupWindow string `xml:"PreferredBackupWindow"` // Specifies the daily time range during which automated backups are created if automated backups are enabled, as determined by the BackupRetentionPeriod. + PreferredMaintenanceWindow string `xml:"PreferredMaintenanceWindow"` // Specifies the weekly time range (in UTC) during which system maintenance can occur. + PubliclyAccessible bool `xml:"PubliclyAccessible"` // Specifies the accessibility options for the DB instance. A value of true specifies an Internet-facing instance with a publicly resolvable DNS name, which resolves to a public IP address. A value of false specifies an internal instance with a DNS name that resolves to a private IP address. + ReadReplicaDBInstanceIdentifiers []string `xml:"ReadReplicaDBInstanceIdentifiers"` // Contains one or more identifiers of the read replicas associated with this DB instance. + ReadReplicaSourceDBInstanceIdentifier string `xml:"ReadReplicaSourceDBInstanceIdentifier"` // Contains the identifier of the source DB instance if this DB instance is a read replica. + SecondaryAvailabilityZone string `xml:"SecondaryAvailabilityZone"` // If present, specifies the name of the secondary Availability Zone for a DB instance with multi-AZ support. + StatusInfos []DBInstanceStatusInfo `xml:"StatusInfos"` // The status of a read replica. If the instance is not a read replica, this will be blank. + VpcSecurityGroups []VpcSecurityGroupMembership `xml:"VpcSecurityGroups"` // Provides List of VPC security group elements that the DB instance belongs to. +} + +// DBInstanceStatusInfo provides a list of status information for a DB instance +// See http://goo.gl/WuePdz for more details. +type DBInstanceStatusInfo struct { + Message string `xml:"Message"` // Details of the error if there is an error for the instance. If the instance is not in an error state, this value is blank. + Normal bool `xml:"Normal"` // Boolean value that is true if the instance is operating normally, or false if the instance is in an error state. + Status string `xml:"Status"` // Status of the DB instance. For a StatusType of read replica, the values can be replicating, error, stopped, or terminated. + StatusType string `xml:"StatusType"` // This value is currently "read replication." +} + +// DBParameterGroup contains the result of a successful invocation of the CreateDBParameterGroup action +// See http://goo.gl/a8BCTy for more details. +type DBParameterGroup struct { + Name string `xml:"DBParameterGroupName"` + Description string `xml:"Description"` + Family string `xml:"DBParameterGroupFamily"` +} + +// DBParameterGroupStatus represents the status of the DB parameter group +// See http://goo.gl/X318cI for more details. +type DBParameterGroupStatus struct { + Name string `xml:"DBParameterGroupName"` + Status string `xml:"ParameterApplyStatus"` +} + +// DBSecurityGroup represents a RDS DB Security Group which controls network access to a DB instance that is not inside a VPC +// See http://goo.gl/JF5oJy for more details. +type DBSecurityGroup struct { + Name string `xml:"DBSecurityGroupName"` + Description string `xml:"DBSecurityGroupDescription"` + EC2SecurityGroups []EC2SecurityGroup `xml:"EC2SecurityGroups"` + IPRanges []IPRange `xml:"IPRanges"` + OwnerId string `xml:"OwnerId"` + VpcId string `xml:"VpcId"` +} + +// DBSecurityGroupMembership represents a DBSecurityGroup which a Database Instance belongs to +// See http://goo.gl/QjTK0b for more details. +type DBSecurityGroupMembership struct { + Name string `xml:"DBSecurityGroupName"` + Status string `xml:"Status"` +} + +// DBSnapshot represents a snapshot of a Database (a backup of the Instance data) +// See http://goo.gl/wkf0L9 for more details. +type DBSnapshot struct { + AllocatedStorage int `xml:"AllocatedStorage"` // Specifies the allocated storage size in gigabytes (GB) + AvailabilityZone string `xml:"AvailabilityZone"` + DBInstanceIdentifier string `xml:"DBInstanceIdentifier"` + DBSnapshotIdentifier string `xml:"DBSnapshotIdentifier"` + Engine string `xml:"Engine"` + EngineVersion string `xml:"EngineVersion"` + InstanceCreateTime string `xml:"InstanceCreateTime"` + Iops int `xml:"Iops"` + LicenseModel string `xml:"LicenseModel"` + MasterUsername string `xml:"MasterUsername"` + OptionGroupName string `xml:"OptionGroupName"` + PercentProgress int `xml:"PercentProgress"` + Port int `xml:"Port"` + SnapshotCreateTime string `xml:"SnapshotCreateTime"` + SnapshotType string `xml:"SnapshotType"` + SourceRegion string `xml:"SourceRegion"` + Status string `xml:"Status"` + VpcId string `xml:"VpcId"` +} + +// DBSubnetGroup is a collection of subnets that is designated for an RDS DB Instance in a VPC +// See http://goo.gl/8vMPkE for more details. +type DBSubnetGroup struct { + Name string `xml:"DBSubnetGroupName"` + Description string `xml:"DBSubnetGroupDescription"` + Status string `xml:"SubnetGroupStatus"` + Subnets []Subnet `xml:"Subnets>Subnet"` + VpcId string `xml:"VpcId"` +} + +// EC2SecurityGroup a standard EC2 Security Group which can be assigned to a DB Instance +// See http://goo.gl/AWavZ2 for more details. +type EC2SecurityGroup struct { + Id string `xml:"EC2SecurityGroupId"` + Name string `xml:"EC2SecurityGroupName"` + OwnerId string `xml:"EC2SecurityGroupOwnerId"` // The AWS ID of the owner of the EC2 security group + Status string `xml:"Status"` // Status can be "authorizing", "authorized", "revoking", and "revoked" +} + +// Endpoint encapsulates the connection endpoint for a DB Instance +// See http://goo.gl/jefsJ4 for more details. +type Endpoint struct { + Address string `xml:"Address"` + Port int `xml:"Port"` +} + +// EngineDefaults describes the system parameter information for a given database engine +// See http://goo.gl/XFy7Wv for more details. +type EngineDefaults struct { + DBParameterGroupFamily string `xml:"DBParameterGroupFamily"` + Marker string `xml:"Marker"` + Parameters []Parameter `xml:"Parameters"` +} + +// Event encapsulates events related to DB instances, DB security groups, DB snapshots, and DB parameter groups +// See http://goo.gl/6fUQow for more details. +type Event struct { + Date string `xml:"Date"` // Specifies the date and time of the event + EventCategories []string `xml:"EventCategories"` // Specifies the category for the event + Message string `xml:"Message"` // Provides the text of this event + SourceIdentifier string `xml:"SourceIdentifier"` // Provides the identifier for the source of the event + SourceType string `xml:"SourceType"` // Valid Values: db-instance | db-parameter-group | db-security-group | db-snapshot +} + +// EventCategoriesMap encapsulates event categories for the specified source type +// See http://goo.gl/9VY3aS for more details. +type EventCategoriesMap struct { + EventCategories []string `xml:"EventCategories"` + SourceType string `xml:"SourceType"` +} + +// EventSubscription describes a subscription, for a customer account, to a series of events +// See http://goo.gl/zgNdXw for more details. +type EventSubscription struct { + CustSubscriptionId string `xml:"CustSubscriptionId"` // The RDS event notification subscription Id + CustomerAwsId string `xml:"CustomerAwsId"` // The AWS customer account associated with the RDS event notification subscription + Enabled bool `xml:"Enabled"` // True indicates the subscription is enabled + EventCategoriesList []string `xml:"EventCategoriesList"` // A list of event categories for the RDS event notification subscription + SnsTopicArn string `xml:"SnsTopicArn"` // The topic ARN of the RDS event notification subscription + SourceIdsList []string `xml:"SourceIdsList"` // A list of source Ids for the RDS event notification subscription + SourceType string `xml:"SourceType"` // The source type for the RDS event notification subscription + Status string `xml:"Status"` // Can be one of the following: creating | modifying | deleting | active | no-permission | topic-not-exist + SubscriptionCreationTime string `xml:"SubscriptionCreationTime"` // The time the RDS event notification subscription was created +} + +// IPRange encapsulates an IP range (and its status) used by a DB Security Group +// See http://goo.gl/VfntNm for more details. +type IPRange struct { + CIDRIP string `xml:"CIDRIP"` + Status string `xml:"Status"` // Specifies the status of the IP range. Status can be "authorizing", "authorized", "revoking", and "revoked". +} + +// Option describes a feature available for an RDS instance along with any settings applicable to it +// See http://goo.gl/8DYY0J for more details. +type Option struct { + Name string `xml:"OptionName"` + Description string `xml:"OptionDescription"` + Settings []OptionSetting `xml:"OptionSettings"` + Permanent bool `xml:"Permanent"` + Persistent bool `xml:"Persistent"` + Port int `xml:"Port"` + DBSecurityGroupMemberships []DBSecurityGroupMembership `xml:"DBSecurityGroupMemberships"` // If the option requires access to a port, then this DB security group allows access to the port + VpcSecurityGroupMemberships []VpcSecurityGroupMembership `xml:"VpcSecurityGroupMemberships"` // If the option requires access to a port, then this VPC security group allows access to the port +} + +// OptionConfiguration is a list of all available options +// See http://goo.gl/kkEzw1 for more details. +type OptionConfiguration struct { + OptionName string `xml:"OptionName"` + OptionSettings []OptionSetting `xml:"OptionSettings"` + Port int `xml:"Port"` + DBSecurityGroupMemberships []string `xml:"DBSecurityGroupMemberships"` + VpcSecurityGroupMemberships []string `xml:"VpcSecurityGroupMemberships"` +} + +// OptionGroup represents a set of features, called options, that are available for a particular Amazon RDS DB instance +// See http://goo.gl/NedBJl for more details. +type OptionGroup struct { + Name string `xml:"OptionGroupName"` + Description string `xml:"OptionGroupDescription"` + VpcId string `xml:"VpcId"` + AllowsVpcAndNonVpcInstanceMemberships bool `xml:"AllowsVpcAndNonVpcInstanceMemberships"` + EngineName string `xml:"EngineName"` + MajorEngineVersion string `xml:"MajorEngineVersion"` + Options []Option `xml:"Options"` +} + +// OptionGroupMembership provides information on the option groups the DB instance is a member of +// See http://goo.gl/XBW6j4 for more details. +type OptionGroupMembership struct { + Name string `xml:"OptionGroupName"` // The name of the option group that the instance belongs to + Status string `xml:"Status"` // The status of the option group membership, e.g. in-sync, pending, pending-maintenance, applying +} + +// OptionGroupOption represents an option within an option group +// See http://goo.gl/jQYL0U for more details. +type OptionGroupOption struct { + DefaultPort int `xml:"DefaultPort"` + Description string `xml:"Description"` + EngineName string `xml:"EngineName"` + MajorEngineVersion string `xml:"MajorEngineVersion"` + MinimumRequiredMinorEngineVersion string `xml:"MinimumRequiredMinorEngineVersion"` + Name string `xml:"Name"` + OptionGroupOptionSettings []OptionGroupOptionSetting `xml:"OptionGroupOptionSettings"` + OptionsDependedOn string `xml:"OptionsDependedOn"` + Permanent bool `xml:"Permanent"` + Persistent bool `xml:"Persistent"` + PortRequired bool `xml:"PortRequired"` +} + +// OptionGroupOptionSetting are used to display settings available for each option with their default values and other information +// See http://goo.gl/9aIwNX for more details. +type OptionGroupOptionSetting struct { + AllowedValues string `xml:"AllowedValues"` + ApplyType string `xml:"ApplyType"` + DefaultValue string `xml:"DefaultValue"` + IsModifiable bool `xml:"IsModifiable"` + SettingDescription string `xml:"SettingDescription"` + SettingName string `xml:"SettingName"` +} + +// OptionSetting encapsulates modifiable settings for a particular option (a feature available for a Database Instance) +// See http://goo.gl/VjOJmW for more details. +type OptionSetting struct { + Name string `xml:"Name"` + Value string `xml:"Value"` + Description string `xml:"Description"` + AllowedValues string `xml:"AllowedValues"` + ApplyType string `xml:"ApplyType"` + DataType string `xml:"DataType"` + DefaultValue string `xml:"DefaultValue"` + IsCollection bool `xml:"IsCollection"` + IsModifiable bool `xml:"IsModifiable"` +} + +// OrderableDBInstanceOption contains a list of available options for a DB instance +// See http://goo.gl/FVPeVC for more details. +type OrderableDBInstanceOption struct { + AvailabilityZones []AvailabilityZone `xml:"AvailabilityZones"` + DBInstanceClass string `xml:"DBInstanceClass"` + Engine string `xml:"Engine"` + EngineVersion string `xml:"EngineVersion"` + LicenseModel string `xml:"LicenseModel"` + MultiAZCapable bool `xml:"MultiAZCapable"` + ReadReplicaCapable bool `xml:"ReadReplicaCapable"` + Vpc bool `xml:"Vpc"` +} + +// Parameter is used as a request parameter in various actions +// See http://goo.gl/cJmvVT for more details. +type Parameter struct { + AllowedValues string `xml:"AllowedValues"` + ApplyMethod string `xml:"ApplyMethod"` // Valid Values: immediate | pending-reboot + ApplyType string `xml:"ApplyType"` + DataType string `xml:"DataType"` + Description string `xml:"Description"` + IsModifiable bool `xml:"IsModifiable"` + MinimumEngineVersion string `xml:"MinimumEngineVersion"` + ParameterName string `xml:"ParameterName"` + ParameterValue string `xml:"ParameterValue"` + Source string `xml:"Source"` +} + +// PendingModifiedValues represents values modified in a ModifyDBInstance action +// See http://goo.gl/UoXhLH for more details. +type PendingModifiedValues struct { + AllocatedStorage int `xml:"AllocatedStorage"` + BackupRetentionPeriod int `xml:"BackupRetentionPeriod"` + DBInstanceClass string `xml:"DBInstanceClass"` + DBInstanceIdentifier string `xml:"DBInstanceIdentifier"` + EngineVersion string `xml:"EngineVersion"` + Iops int `xml:"Iops"` + MasterUserPassword string `xml:"MasterUserPassword"` + MultiAZ bool `xml:"MultiAZ"` + Port string `xml:"Port"` +} + +// RecurringCharge describes an amount that will be charged on a recurring basis with a given frequency +// See http://goo.gl/3GDplh for more details. +type RecurringCharge struct { + Amount float64 `xml:"RecurringChargeAmount"` + Frequency string `xml:"RecurringChargeFrequency"` +} + +// ReservedDBInstance encapsulates a reserved Database Instance +// See http://goo.gl/mjLhNI for more details. +type ReservedDBInstance struct { + CurrencyCode string `xml:"CurrencyCode"` + DBInstanceClass string `xml:"DBInstanceClass"` + DBInstanceCount int `xml:"DBInstanceCount"` + Duration int `xml:"Duration"` + FixedPrice float64 `xml:"FixedPrice"` + MultiAZ bool `xml:"MultiAZ"` + OfferingType string `xml:"OfferingType"` + ProductDescription string `xml:"ProductDescription"` + RecurringCharges []RecurringCharge `xml:"RecurringCharges"` + ReservedDBInstanceId string `xml:"ReservedDBInstanceId"` + ReservedDBInstancesOfferingId string `xml:"ReservedDBInstancesOfferingId"` + StartTime string `xml:"StartTime"` + State string `xml:"State"` + UsagePrice float64 `xml:"UsagePrice"` +} + +// ReservedDBInstancesOffering describes an available Reserved DB instance offering which can be purchased +// See http://goo.gl/h5s8e6 for more details. +type ReservedDBInstancesOffering struct { + CurrencyCode string `xml:"CurrencyCode"` + DBInstanceClass string `xml:"DBInstanceClass"` + Duration int `xml:"Duration"` + FixedPrice float64 `xml:"FixedPrice"` + MultiAZ bool `xml:"MultiAZ"` + OfferingType string `xml:"OfferingType"` + ProductDescription string `xml:"ProductDescription"` + RecurringCharges []RecurringCharge `xml:"RecurringCharges"` + ReservedDBInstancesOfferingId string `xml:"ReservedDBInstancesOfferingId"` + UsagePrice float64 `xml:"UsagePrice"` +} + +// Subnet describes an EC2 subnet, along with its status and location +// See http://goo.gl/Nc8ymd for more details. +type Subnet struct { + Id string `xml:"SubnetIdentifier"` + Status string `xml:"SubnetStatus"` + AvailabilityZone AvailabilityZone `xml:"SubnetAvailabilityZone"` +} + +// Tag represents metadata assigned to an Amazon RDS resource consisting of a key-value pair +// See http://goo.gl/YnXRrE for more details. +type Tag struct { + Key string `xml:"Key"` + Value string `xml:"Value"` +} + +// VpcSecurityGroupMembership describes a standard VPC Security Group which has been assigned to a DB Instance located in a VPC +// See http://goo.gl/UIvmlS for more details. +type VpcSecurityGroupMembership struct { + Id string `xml:"VpcSecurityGroupId"` + Status string `xml:"Status"` +} diff --git a/vendor/github.com/goamz/goamz/route53/route53.go b/vendor/github.com/goamz/goamz/route53/route53.go new file mode 100644 index 000000000..0e26a6f73 --- /dev/null +++ b/vendor/github.com/goamz/goamz/route53/route53.go @@ -0,0 +1,254 @@ +package route53 + +import ( + "bytes" + "encoding/xml" + "fmt" + "github.com/goamz/goamz/aws" + "io" + "net/http" +) + +type Route53 struct { + Auth aws.Auth + Endpoint string + Signer *aws.Route53Signer + Service *aws.Service +} + +const route53_host = "https://route53.amazonaws.com" + +// Factory for the route53 type +func NewRoute53(auth aws.Auth) (*Route53, error) { + signer := aws.NewRoute53Signer(auth) + + return &Route53{ + Auth: auth, + Signer: signer, + Endpoint: route53_host + "/2013-04-01/hostedzone", + }, nil +} + +// General Structs used in all types of requests +type HostedZone struct { + XMLName xml.Name `xml:"HostedZone"` + Id string + Name string + CallerReference string + Config Config + ResourceRecordSetCount int +} + +type Config struct { + XMLName xml.Name `xml:"Config"` + Comment string +} + +// Structs for getting the existing Hosted Zones +type ListHostedZonesResponse struct { + XMLName xml.Name `xml:"ListHostedZonesResponse"` + HostedZones []HostedZone `xml:"HostedZones>HostedZone"` + Marker string + IsTruncated bool + NextMarker string + MaxItems int +} + +// Structs for Creating a New Host +type CreateHostedZoneRequest struct { + XMLName xml.Name `xml:"CreateHostedZoneRequest"` + Xmlns string `xml:"xmlns,attr"` + Name string + CallerReference string + HostedZoneConfig HostedZoneConfig +} + +type ResourceRecordValue struct { + Value string `xml:"Value"` +} + +type AliasTarget struct { + HostedZoneId string `xml:"HostedZoneId"` + DNSName string `xml:"DNSName"` + EvaluateTargetHealth bool `xml:"EvaluateTargetHealth"` +} + +// Wrapper for all the different resource record sets +type ResourceRecordSet interface{} + +// Basic Change +type Change struct { + Action string `xml:"Action"` + Name string `xml:"ResourceRecordSet>Name"` + Type string `xml:"ResourceRecordSet>Type"` + TTL int `xml:"ResourceRecordSet>TTL,omitempty"` + Values []ResourceRecordValue `xml:"ResourceRecordSet>ResourceRecords>ResourceRecord"` + HealthCheckId string `xml:"ResourceRecordSet>HealthCheckId,omitempty"` +} + +// Basic Resource Recod Set +type BasicResourceRecordSet struct { + Action string `xml:"Action"` + Name string `xml:"ResourceRecordSet>Name"` + Type string `xml:"ResourceRecordSet>Type"` + TTL int `xml:"ResourceRecordSet>TTL,omitempty"` + Values []ResourceRecordValue `xml:"ResourceRecordSet>ResourceRecords>ResourceRecord"` + HealthCheckId string `xml:"ResourceRecordSet>HealthCheckId,omitempty"` +} + +// Alias Resource Record Set +type AliasResourceRecordSet struct { + Action string `xml:"Action"` + Name string `xml:"ResourceRecordSet>Name"` + Type string `xml:"ResourceRecordSet>Type"` + AliasTarget AliasTarget `xml:"ResourceRecordSet>AliasTarget"` + HealthCheckId string `xml:"ResourceRecordSet>HealthCheckId,omitempty"` +} + +type ChangeResourceRecordSetsRequest struct { + XMLName xml.Name `xml:"ChangeResourceRecordSetsRequest"` + Xmlns string `xml:"xmlns,attr"` + Changes []ResourceRecordSet `xml:"ChangeBatch>Changes>Change"` +} + +type HostedZoneConfig struct { + XMLName xml.Name `xml:"HostedZoneConfig"` + Comment string +} + +type CreateHostedZoneResponse struct { + XMLName xml.Name `xml:"CreateHostedZoneResponse"` + HostedZone HostedZone + ChangeInfo ChangeInfo + DelegationSet DelegationSet +} + +type ChangeResourceRecordSetsResponse struct { + XMLName xml.Name `xml:"ChangeResourceRecordSetsResponse"` + Id string `xml:"ChangeInfo>Id"` + Status string `xml:"ChangeInfo>Status"` + SubmittedAt string `xml:"ChangeInfo>SubmittedAt"` +} + +type ChangeInfo struct { + XMLName xml.Name `xml:"ChangeInfo"` + Id string + Status string + SubmittedAt string +} + +type DelegationSet struct { + XMLName xml.Name `xml:"DelegationSet` + NameServers NameServers +} + +type NameServers struct { + XMLName xml.Name `xml:"NameServers` + NameServer []string +} + +type GetHostedZoneResponse struct { + XMLName xml.Name `xml:"GetHostedZoneResponse"` + HostedZone HostedZone + DelegationSet DelegationSet +} + +type DeleteHostedZoneResponse struct { + XMLName xml.Name `xml:"DeleteHostedZoneResponse"` + Xmlns string `xml:"xmlns,attr"` + ChangeInfo ChangeInfo +} + +// query sends the specified HTTP request to the path and signs the request +// with the required authentication and headers based on the Auth. +// +// Automatically decodes the response into the the result interface +func (r *Route53) query(method string, path string, body io.Reader, result interface{}) error { + var err error + + // Create the POST request and sign the headers + req, err := http.NewRequest(method, path, body) + r.Signer.Sign(req) + + // Send the request and capture the response + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + return err + } + + if method == "POST" { + defer req.Body.Close() + } + + if res.StatusCode != 201 && res.StatusCode != 200 { + err = r.Service.BuildError(res) + return err + } + + err = xml.NewDecoder(res.Body).Decode(result) + + return err +} + +// CreateHostedZone send a creation request to the AWS Route53 API +func (r *Route53) CreateHostedZone(hostedZoneReq *CreateHostedZoneRequest) (*CreateHostedZoneResponse, error) { + xmlBytes, err := xml.Marshal(hostedZoneReq) + if err != nil { + return nil, err + } + + result := new(CreateHostedZoneResponse) + err = r.query("POST", r.Endpoint, bytes.NewBuffer(xmlBytes), result) + + return result, err +} + +// ChangeResourceRecordSet send a change resource record request to the AWS Route53 API +func (r *Route53) ChangeResourceRecordSet(req *ChangeResourceRecordSetsRequest, zoneId string) (*ChangeResourceRecordSetsResponse, error) { + xmlBytes, err := xml.Marshal(req) + if err != nil { + return nil, err + } + xmlBytes = []byte(xml.Header + string(xmlBytes)) + + result := new(ChangeResourceRecordSetsResponse) + path := fmt.Sprintf("%s/%s/rrset", r.Endpoint, zoneId) + err = r.query("POST", path, bytes.NewBuffer(xmlBytes), result) + + return result, err +} + +// ListedHostedZones fetches a collection of HostedZones through the AWS Route53 API +func (r *Route53) ListHostedZones(marker string, maxItems int) (result *ListHostedZonesResponse, err error) { + path := "" + + if marker == "" { + path = fmt.Sprintf("%s?maxitems=%d", r.Endpoint, maxItems) + } else { + path = fmt.Sprintf("%s?marker=%v&maxitems=%d", r.Endpoint, marker, maxItems) + } + + result = new(ListHostedZonesResponse) + err = r.query("GET", path, nil, result) + + return +} + +// GetHostedZone fetches a particular hostedzones DelegationSet by id +func (r *Route53) GetHostedZone(id string) (result *GetHostedZoneResponse, err error) { + result = new(GetHostedZoneResponse) + err = r.query("GET", fmt.Sprintf("%s/%v", r.Endpoint, id), nil, result) + + return +} + +// DeleteHostedZone deletes the hosted zone with the given id +func (r *Route53) DeleteHostedZone(id string) (result *DeleteHostedZoneResponse, err error) { + path := fmt.Sprintf("%s/%s", r.Endpoint, id) + + result = new(DeleteHostedZoneResponse) + err = r.query("DELETE", path, nil, result) + + return +} diff --git a/vendor/github.com/goamz/goamz/s3/export_test.go b/vendor/github.com/goamz/goamz/s3/export_test.go new file mode 100644 index 000000000..4ff913cde --- /dev/null +++ b/vendor/github.com/goamz/goamz/s3/export_test.go @@ -0,0 +1,17 @@ +package s3 + +import ( + "github.com/goamz/goamz/aws" +) + +func Sign(auth aws.Auth, method, path string, params, headers map[string][]string) { + sign(auth, method, path, params, headers) +} + +func SetListPartsMax(n int) { + listPartsMax = n +} + +func SetListMultiMax(n int) { + listMultiMax = n +} diff --git a/vendor/github.com/goamz/goamz/s3/multi_test.go b/vendor/github.com/goamz/goamz/s3/multi_test.go new file mode 100644 index 000000000..5c788d9cc --- /dev/null +++ b/vendor/github.com/goamz/goamz/s3/multi_test.go @@ -0,0 +1,373 @@ +package s3_test + +import ( + "encoding/xml" + "io" + "io/ioutil" + "strings" + + "github.com/goamz/goamz/s3" + . "gopkg.in/check.v1" +) + +func (s *S) TestInitMulti(c *C) { + testServer.Response(200, nil, InitMultiResultDump) + + b := s.s3.Bucket("sample") + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "POST") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Header["Content-Type"], DeepEquals, []string{"text/plain"}) + c.Assert(req.Header["X-Amz-Acl"], DeepEquals, []string{"private"}) + c.Assert(req.Form["uploads"], DeepEquals, []string{""}) + + c.Assert(multi.UploadId, Matches, "JNbR_[A-Za-z0-9.]+QQ--") +} + +func (s *S) TestMultiNoPreviousUpload(c *C) { + // Don't retry the NoSuchUpload error. + s.DisableRetries() + + testServer.Response(404, nil, NoSuchUploadErrorDump) + testServer.Response(200, nil, InitMultiResultDump) + + b := s.s3.Bucket("sample") + + multi, err := b.Multi("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/sample/") + c.Assert(req.Form["uploads"], DeepEquals, []string{""}) + c.Assert(req.Form["prefix"], DeepEquals, []string{"multi"}) + + req = testServer.WaitRequest() + c.Assert(req.Method, Equals, "POST") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form["uploads"], DeepEquals, []string{""}) + + c.Assert(multi.UploadId, Matches, "JNbR_[A-Za-z0-9.]+QQ--") +} + +func (s *S) TestMultiReturnOld(c *C) { + testServer.Response(200, nil, ListMultiResultDump) + + b := s.s3.Bucket("sample") + + multi, err := b.Multi("multi1", "text/plain", s3.Private) + c.Assert(err, IsNil) + c.Assert(multi.Key, Equals, "multi1") + c.Assert(multi.UploadId, Equals, "iUVug89pPvSswrikD") + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/sample/") + c.Assert(req.Form["uploads"], DeepEquals, []string{""}) + c.Assert(req.Form["prefix"], DeepEquals, []string{"multi1"}) +} + +func (s *S) TestListParts(c *C) { + testServer.Response(200, nil, InitMultiResultDump) + testServer.Response(200, nil, ListPartsResultDump1) + testServer.Response(404, nil, NoSuchUploadErrorDump) // :-( + testServer.Response(200, nil, ListPartsResultDump2) + + b := s.s3.Bucket("sample") + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + + parts, err := multi.ListParts() + c.Assert(err, IsNil) + c.Assert(parts, HasLen, 3) + c.Assert(parts[0].N, Equals, 1) + c.Assert(parts[0].Size, Equals, int64(5)) + c.Assert(parts[0].ETag, Equals, `"ffc88b4ca90a355f8ddba6b2c3b2af5c"`) + c.Assert(parts[1].N, Equals, 2) + c.Assert(parts[1].Size, Equals, int64(5)) + c.Assert(parts[1].ETag, Equals, `"d067a0fa9dc61a6e7195ca99696b5a89"`) + c.Assert(parts[2].N, Equals, 3) + c.Assert(parts[2].Size, Equals, int64(5)) + c.Assert(parts[2].ETag, Equals, `"49dcd91231f801159e893fb5c6674985"`) + testServer.WaitRequest() + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") + c.Assert(req.Form["max-parts"], DeepEquals, []string{"1000"}) + + testServer.WaitRequest() // The internal error. + req = testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") + c.Assert(req.Form["max-parts"], DeepEquals, []string{"1000"}) + c.Assert(req.Form["part-number-marker"], DeepEquals, []string{"2"}) +} + +func (s *S) TestPutPart(c *C) { + headers := map[string]string{ + "ETag": `"26f90efd10d614f100252ff56d88dad8"`, + } + testServer.Response(200, nil, InitMultiResultDump) + testServer.Response(200, headers, "") + + b := s.s3.Bucket("sample") + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + + part, err := multi.PutPart(1, strings.NewReader("")) + c.Assert(err, IsNil) + c.Assert(part.N, Equals, 1) + c.Assert(part.Size, Equals, int64(8)) + c.Assert(part.ETag, Equals, headers["ETag"]) + + testServer.WaitRequest() + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") + c.Assert(req.Form["partNumber"], DeepEquals, []string{"1"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"8"}) + c.Assert(req.Header["Content-Md5"], DeepEquals, []string{"JvkO/RDWFPEAJS/1bYja2A=="}) +} + +func readAll(r io.Reader) string { + data, err := ioutil.ReadAll(r) + if err != nil { + panic(err) + } + return string(data) +} + +func (s *S) TestPutAllNoPreviousUpload(c *C) { + // Don't retry the NoSuchUpload error. + s.DisableRetries() + + etag1 := map[string]string{"ETag": `"etag1"`} + etag2 := map[string]string{"ETag": `"etag2"`} + etag3 := map[string]string{"ETag": `"etag3"`} + testServer.Response(200, nil, InitMultiResultDump) + testServer.Response(404, nil, NoSuchUploadErrorDump) + testServer.Response(200, etag1, "") + testServer.Response(200, etag2, "") + testServer.Response(200, etag3, "") + + b := s.s3.Bucket("sample") + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + + parts, err := multi.PutAll(strings.NewReader("part1part2last"), 5) + c.Assert(parts, HasLen, 3) + c.Assert(parts[0].ETag, Equals, `"etag1"`) + c.Assert(parts[1].ETag, Equals, `"etag2"`) + c.Assert(parts[2].ETag, Equals, `"etag3"`) + c.Assert(err, IsNil) + + // Init + testServer.WaitRequest() + + // List old parts. Won't find anything. + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/sample/multi") + + // Send part 1. + req = testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form["partNumber"], DeepEquals, []string{"1"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"5"}) + c.Assert(readAll(req.Body), Equals, "part1") + + // Send part 2. + req = testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form["partNumber"], DeepEquals, []string{"2"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"5"}) + c.Assert(readAll(req.Body), Equals, "part2") + + // Send part 3 with shorter body. + req = testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form["partNumber"], DeepEquals, []string{"3"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"4"}) + c.Assert(readAll(req.Body), Equals, "last") +} + +func (s *S) TestPutAllZeroSizeFile(c *C) { + // Don't retry the NoSuchUpload error. + s.DisableRetries() + + etag1 := map[string]string{"ETag": `"etag1"`} + testServer.Response(200, nil, InitMultiResultDump) + testServer.Response(404, nil, NoSuchUploadErrorDump) + testServer.Response(200, etag1, "") + + b := s.s3.Bucket("sample") + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + + // Must send at least one part, so that completing it will work. + parts, err := multi.PutAll(strings.NewReader(""), 5) + c.Assert(parts, HasLen, 1) + c.Assert(parts[0].ETag, Equals, `"etag1"`) + c.Assert(err, IsNil) + + // Init + testServer.WaitRequest() + + // List old parts. Won't find anything. + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/sample/multi") + + // Send empty part. + req = testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form["partNumber"], DeepEquals, []string{"1"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"0"}) + c.Assert(readAll(req.Body), Equals, "") +} + +func (s *S) TestPutAllResume(c *C) { + etag2 := map[string]string{"ETag": `"etag2"`} + testServer.Response(200, nil, InitMultiResultDump) + testServer.Response(200, nil, ListPartsResultDump1) + testServer.Response(200, nil, ListPartsResultDump2) + testServer.Response(200, etag2, "") + + b := s.s3.Bucket("sample") + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + + // "part1" and "part3" match the checksums in ResultDump1. + // The middle one is a mismatch (it refers to "part2"). + parts, err := multi.PutAll(strings.NewReader("part1partXpart3"), 5) + c.Assert(parts, HasLen, 3) + c.Assert(parts[0].N, Equals, 1) + c.Assert(parts[0].Size, Equals, int64(5)) + c.Assert(parts[0].ETag, Equals, `"ffc88b4ca90a355f8ddba6b2c3b2af5c"`) + c.Assert(parts[1].N, Equals, 2) + c.Assert(parts[1].Size, Equals, int64(5)) + c.Assert(parts[1].ETag, Equals, `"etag2"`) + c.Assert(parts[2].N, Equals, 3) + c.Assert(parts[2].Size, Equals, int64(5)) + c.Assert(parts[2].ETag, Equals, `"49dcd91231f801159e893fb5c6674985"`) + c.Assert(err, IsNil) + + // Init + testServer.WaitRequest() + + // List old parts, broken in two requests. + for i := 0; i < 2; i++ { + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/sample/multi") + } + + // Send part 2, as it didn't match the checksum. + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form["partNumber"], DeepEquals, []string{"2"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"5"}) + c.Assert(readAll(req.Body), Equals, "partX") +} + +func (s *S) TestMultiComplete(c *C) { + testServer.Response(200, nil, InitMultiResultDump) + // Note the 200 response. Completing will hold the connection on some + // kind of long poll, and may return a late error even after a 200. + testServer.Response(200, nil, InternalErrorDump) + testServer.Response(200, nil, "") + + b := s.s3.Bucket("sample") + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + + err = multi.Complete([]s3.Part{{2, `"ETag2"`, 32}, {1, `"ETag1"`, 64}}) + // returns InternalErrorDump in the payload, which should manifest as + // an error. + c.Assert(err, NotNil) + + testServer.WaitRequest() + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "POST") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") + + var payload struct { + XMLName xml.Name + Part []struct { + PartNumber int + ETag string + } + } + + dec := xml.NewDecoder(req.Body) + err = dec.Decode(&payload) + c.Assert(err, IsNil) + + c.Assert(payload.XMLName.Local, Equals, "CompleteMultipartUpload") + c.Assert(len(payload.Part), Equals, 2) + c.Assert(payload.Part[0].PartNumber, Equals, 1) + c.Assert(payload.Part[0].ETag, Equals, `"ETag1"`) + c.Assert(payload.Part[1].PartNumber, Equals, 2) + c.Assert(payload.Part[1].ETag, Equals, `"ETag2"`) +} + +func (s *S) TestMultiAbort(c *C) { + testServer.Response(200, nil, InitMultiResultDump) + testServer.Response(200, nil, "") + + b := s.s3.Bucket("sample") + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + + err = multi.Abort() + c.Assert(err, IsNil) + + testServer.WaitRequest() + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "DELETE") + c.Assert(req.URL.Path, Equals, "/sample/multi") + c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") +} + +func (s *S) TestListMulti(c *C) { + testServer.Response(200, nil, ListMultiResultDump) + + b := s.s3.Bucket("sample") + + multis, prefixes, err := b.ListMulti("", "/") + c.Assert(err, IsNil) + c.Assert(prefixes, DeepEquals, []string{"a/", "b/"}) + c.Assert(multis, HasLen, 2) + c.Assert(multis[0].Key, Equals, "multi1") + c.Assert(multis[0].UploadId, Equals, "iUVug89pPvSswrikD") + c.Assert(multis[1].Key, Equals, "multi2") + c.Assert(multis[1].UploadId, Equals, "DkirwsSvPp98guVUi") + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/sample/") + c.Assert(req.Form["uploads"], DeepEquals, []string{""}) + c.Assert(req.Form["prefix"], DeepEquals, []string{""}) + c.Assert(req.Form["delimiter"], DeepEquals, []string{"/"}) + c.Assert(req.Form["max-uploads"], DeepEquals, []string{"1000"}) +} diff --git a/vendor/github.com/goamz/goamz/s3/responses_test.go b/vendor/github.com/goamz/goamz/s3/responses_test.go new file mode 100644 index 000000000..414ede0a7 --- /dev/null +++ b/vendor/github.com/goamz/goamz/s3/responses_test.go @@ -0,0 +1,202 @@ +package s3_test + +var GetObjectErrorDump = ` + + + NoSuchBucket + The specified bucket does not exist + non-existent-bucket + 3F1B667FAD71C3D8 + L4ee/zrm1irFXY5F45fKXIRdOf9ktsKY/8TDVawuMK2jWRb1RF84i1uBzkdNqS5D + +` + +var GetListResultDump1 = ` + + + quotes + N + false + + Nelson + 2006-01-01T12:00:00.000Z + "828ef3fdfa96f00ad9f27c383fc9ac7f" + 5 + STANDARD + + bcaf161ca5fb16fd081034f + webfile + + + + Neo + 2006-01-01T12:00:00.000Z + "828ef3fdfa96f00ad9f27c383fc9ac7f" + 4 + STANDARD + + bcaf1ffd86a5fb16fd081034f + webfile + + + +` + +var GetListResultDump2 = ` + + example-bucket + photos/2006/ + some-marker + 1000 + / + false + + + photos/2006/feb/ + + + photos/2006/jan/ + + +` + +var InitMultiResultDump = ` + + + sample + multi + JNbR_cMdwnGiD12jKAd6WK2PUkfj2VxA7i4nCwjE6t71nI9Tl3eVDPFlU0nOixhftH7I17ZPGkV3QA.l7ZD.QQ-- + +` + +var ListPartsResultDump1 = ` + + + sample + multi + JNbR_cMdwnGiD12jKAd6WK2PUkfj2VxA7i4nCwjE6t71nI9Tl3eVDPFlU0nOixhftH7I17ZPGkV3QA.l7ZD.QQ-- + + bb5c0f63b0b25f2d099c + joe + + + bb5c0f63b0b25f2d099c + joe + + STANDARD + 0 + 2 + 2 + true + + 1 + 2013-01-30T13:45:51.000Z + "ffc88b4ca90a355f8ddba6b2c3b2af5c" + 5 + + + 2 + 2013-01-30T13:45:52.000Z + "d067a0fa9dc61a6e7195ca99696b5a89" + 5 + + +` + +var ListPartsResultDump2 = ` + + + sample + multi + JNbR_cMdwnGiD12jKAd6WK2PUkfj2VxA7i4nCwjE6t71nI9Tl3eVDPFlU0nOixhftH7I17ZPGkV3QA.l7ZD.QQ-- + + bb5c0f63b0b25f2d099c + joe + + + bb5c0f63b0b25f2d099c + joe + + STANDARD + 2 + 3 + 2 + false + + 3 + 2013-01-30T13:46:50.000Z + "49dcd91231f801159e893fb5c6674985" + 5 + + +` + +var ListMultiResultDump = ` + + + goamz-test-bucket-us-east-1-akiajk3wyewhctyqbf7a + + + multi1 + iUVug89pPvSswrikD72p8uO62EzhNtpDxRmwC5WSiWDdK9SfzmDqe3xpP1kMWimyimSnz4uzFc3waVM5ufrKYQ-- + / + 1000 + false + + multi1 + iUVug89pPvSswrikD + + bb5c0f63b0b25f2d0 + gustavoniemeyer + + + bb5c0f63b0b25f2d0 + gustavoniemeyer + + STANDARD + 2013-01-30T18:15:47.000Z + + + multi2 + DkirwsSvPp98guVUi + + bb5c0f63b0b25f2d0 + joe + + + bb5c0f63b0b25f2d0 + joe + + STANDARD + 2013-01-30T18:15:47.000Z + + + a/ + + + b/ + + +` + +var NoSuchUploadErrorDump = ` + + + NoSuchUpload + Not relevant + sample + 3F1B667FAD71C3D8 + kjhwqk + +` + +var InternalErrorDump = ` + + + InternalError + Not relevant + sample + 3F1B667FAD71C3D8 + kjhwqk + +` diff --git a/vendor/github.com/goamz/goamz/s3/s3_test.go b/vendor/github.com/goamz/goamz/s3/s3_test.go new file mode 100644 index 000000000..24d4dfcc0 --- /dev/null +++ b/vendor/github.com/goamz/goamz/s3/s3_test.go @@ -0,0 +1,427 @@ +package s3_test + +import ( + "bytes" + "io/ioutil" + "net/http" + "testing" + "time" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/s3" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +type S struct { + s3 *s3.S3 +} + +var _ = Suite(&S{}) + +var testServer = testutil.NewHTTPServer() + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.s3 = s3.New(auth, aws.Region{Name: "faux-region-1", S3Endpoint: testServer.URL}) +} + +func (s *S) TearDownSuite(c *C) { + s.s3.AttemptStrategy = s3.DefaultAttemptStrategy +} + +func (s *S) SetUpTest(c *C) { + s.s3.AttemptStrategy = aws.AttemptStrategy{ + Total: 300 * time.Millisecond, + Delay: 100 * time.Millisecond, + } +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) DisableRetries() { + s.s3.AttemptStrategy = aws.AttemptStrategy{} +} + +// PutBucket docs: http://goo.gl/kBTCu + +func (s *S) TestPutBucket(c *C) { + testServer.Response(200, nil, "") + + b := s.s3.Bucket("bucket") + err := b.PutBucket(s3.Private) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/bucket/") + c.Assert(req.Header["Date"], Not(Equals), "") +} + +// Head docs: http://bit.ly/17K1ylI + +func (s *S) TestHead(c *C) { + testServer.Response(200, nil, "content") + + b := s.s3.Bucket("bucket") + resp, err := b.Head("name", nil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "HEAD") + c.Assert(req.URL.Path, Equals, "/bucket/name") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(err, IsNil) + c.Assert(resp.ContentLength, FitsTypeOf, int64(0)) + c.Assert(resp, FitsTypeOf, &http.Response{}) +} + +// DeleteBucket docs: http://goo.gl/GoBrY + +func (s *S) TestDelBucket(c *C) { + testServer.Response(204, nil, "") + + b := s.s3.Bucket("bucket") + err := b.DelBucket() + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "DELETE") + c.Assert(req.URL.Path, Equals, "/bucket/") + c.Assert(req.Header["Date"], Not(Equals), "") +} + +// GetObject docs: http://goo.gl/isCO7 + +func (s *S) TestGet(c *C) { + testServer.Response(200, nil, "content") + + b := s.s3.Bucket("bucket") + data, err := b.Get("name") + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/bucket/name") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "content") +} + +func (s *S) TestURL(c *C) { + testServer.Response(200, nil, "content") + + b := s.s3.Bucket("bucket") + url := b.URL("name") + r, err := http.Get(url) + c.Assert(err, IsNil) + data, err := ioutil.ReadAll(r.Body) + r.Body.Close() + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "content") + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/bucket/name") +} + +func (s *S) TestGetReader(c *C) { + testServer.Response(200, nil, "content") + + b := s.s3.Bucket("bucket") + rc, err := b.GetReader("name") + c.Assert(err, IsNil) + data, err := ioutil.ReadAll(rc) + rc.Close() + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "content") + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/bucket/name") + c.Assert(req.Header["Date"], Not(Equals), "") +} + +func (s *S) TestGetNotFound(c *C) { + for i := 0; i < 10; i++ { + testServer.Response(404, nil, GetObjectErrorDump) + } + + b := s.s3.Bucket("non-existent-bucket") + data, err := b.Get("non-existent") + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/non-existent-bucket/non-existent") + c.Assert(req.Header["Date"], Not(Equals), "") + + s3err, _ := err.(*s3.Error) + c.Assert(s3err, NotNil) + c.Assert(s3err.StatusCode, Equals, 404) + c.Assert(s3err.BucketName, Equals, "non-existent-bucket") + c.Assert(s3err.RequestId, Equals, "3F1B667FAD71C3D8") + c.Assert(s3err.HostId, Equals, "L4ee/zrm1irFXY5F45fKXIRdOf9ktsKY/8TDVawuMK2jWRb1RF84i1uBzkdNqS5D") + c.Assert(s3err.Code, Equals, "NoSuchBucket") + c.Assert(s3err.Message, Equals, "The specified bucket does not exist") + c.Assert(s3err.Error(), Equals, "The specified bucket does not exist") + c.Assert(data, IsNil) +} + +// PutObject docs: http://goo.gl/FEBPD + +func (s *S) TestPutObject(c *C) { + testServer.Response(200, nil, "") + + b := s.s3.Bucket("bucket") + err := b.Put("name", []byte("content"), "content-type", s3.Private, s3.Options{}) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/bucket/name") + c.Assert(req.Header["Date"], Not(DeepEquals), []string{""}) + c.Assert(req.Header["Content-Type"], DeepEquals, []string{"content-type"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"7"}) + //c.Assert(req.Header["Content-MD5"], DeepEquals, "...") + c.Assert(req.Header["X-Amz-Acl"], DeepEquals, []string{"private"}) +} + +func (s *S) TestPutObjectReadTimeout(c *C) { + s.s3.ReadTimeout = 50 * time.Millisecond + defer func() { + s.s3.ReadTimeout = 0 + }() + + b := s.s3.Bucket("bucket") + err := b.Put("name", []byte("content"), "content-type", s3.Private, s3.Options{}) + + // Make sure that we get a timeout error. + c.Assert(err, NotNil) + + // Set the response after the request times out so that the next request will work. + testServer.Response(200, nil, "") + + // This time set the response within our timeout period so that we expect the call + // to return successfully. + go func() { + time.Sleep(25 * time.Millisecond) + testServer.Response(200, nil, "") + }() + err = b.Put("name", []byte("content"), "content-type", s3.Private, s3.Options{}) + c.Assert(err, IsNil) +} + +func (s *S) TestPutObjectHeader(c *C) { + testServer.Response(200, nil, "") + + b := s.s3.Bucket("bucket") + err := b.PutHeader( + "name", + []byte("content"), + map[string][]string{"Content-Type": {"content-type"}}, + s3.Private, + ) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/bucket/name") + c.Assert(req.Header["Date"], Not(DeepEquals), []string{""}) + c.Assert(req.Header["Content-Type"], DeepEquals, []string{"content-type"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"7"}) + //c.Assert(req.Header["Content-MD5"], DeepEquals, "...") + c.Assert(req.Header["X-Amz-Acl"], DeepEquals, []string{"private"}) +} + +func (s *S) TestPutReader(c *C) { + testServer.Response(200, nil, "") + + b := s.s3.Bucket("bucket") + buf := bytes.NewBufferString("content") + err := b.PutReader("name", buf, int64(buf.Len()), "content-type", s3.Private, s3.Options{}) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/bucket/name") + c.Assert(req.Header["Date"], Not(DeepEquals), []string{""}) + c.Assert(req.Header["Content-Type"], DeepEquals, []string{"content-type"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"7"}) + //c.Assert(req.Header["Content-MD5"], Equals, "...") + c.Assert(req.Header["X-Amz-Acl"], DeepEquals, []string{"private"}) +} + +func (s *S) TestPutReaderHeader(c *C) { + testServer.Response(200, nil, "") + + b := s.s3.Bucket("bucket") + buf := bytes.NewBufferString("content") + err := b.PutReaderHeader( + "name", + buf, + int64(buf.Len()), + map[string][]string{"Content-Type": {"content-type"}}, + s3.Private, + ) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "PUT") + c.Assert(req.URL.Path, Equals, "/bucket/name") + c.Assert(req.Header["Date"], Not(DeepEquals), []string{""}) + c.Assert(req.Header["Content-Type"], DeepEquals, []string{"content-type"}) + c.Assert(req.Header["Content-Length"], DeepEquals, []string{"7"}) + //c.Assert(req.Header["Content-MD5"], Equals, "...") + c.Assert(req.Header["X-Amz-Acl"], DeepEquals, []string{"private"}) +} + +// DelObject docs: http://goo.gl/APeTt + +func (s *S) TestDelObject(c *C) { + testServer.Response(200, nil, "") + + b := s.s3.Bucket("bucket") + err := b.Del("name") + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "DELETE") + c.Assert(req.URL.Path, Equals, "/bucket/name") + c.Assert(req.Header["Date"], Not(Equals), "") +} + +func (s *S) TestDelMultiObjects(c *C) { + testServer.Response(200, nil, "") + + b := s.s3.Bucket("bucket") + objects := []s3.Object{s3.Object{Key: "test"}} + err := b.DelMulti(s3.Delete{ + Quiet: false, + Objects: objects, + }) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "POST") + c.Assert(req.URL.RawQuery, Equals, "delete=") + c.Assert(req.Header["Date"], Not(Equals), "") + c.Assert(req.Header["Content-MD5"], Not(Equals), "") + c.Assert(req.Header["Content-Type"], Not(Equals), "") + c.Assert(req.ContentLength, Not(Equals), "") +} + +// Bucket List Objects docs: http://goo.gl/YjQTc + +func (s *S) TestList(c *C) { + testServer.Response(200, nil, GetListResultDump1) + + b := s.s3.Bucket("quotes") + + data, err := b.List("N", "", "", 0) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/quotes/") + c.Assert(req.Header["Date"], Not(Equals), "") + c.Assert(req.Form["prefix"], DeepEquals, []string{"N"}) + c.Assert(req.Form["delimiter"], DeepEquals, []string{""}) + c.Assert(req.Form["marker"], DeepEquals, []string{""}) + c.Assert(req.Form["max-keys"], DeepEquals, []string(nil)) + + c.Assert(data.Name, Equals, "quotes") + c.Assert(data.Prefix, Equals, "N") + c.Assert(data.IsTruncated, Equals, false) + c.Assert(len(data.Contents), Equals, 2) + + c.Assert(data.Contents[0].Key, Equals, "Nelson") + c.Assert(data.Contents[0].LastModified, Equals, "2006-01-01T12:00:00.000Z") + c.Assert(data.Contents[0].ETag, Equals, `"828ef3fdfa96f00ad9f27c383fc9ac7f"`) + c.Assert(data.Contents[0].Size, Equals, int64(5)) + c.Assert(data.Contents[0].StorageClass, Equals, "STANDARD") + c.Assert(data.Contents[0].Owner.ID, Equals, "bcaf161ca5fb16fd081034f") + c.Assert(data.Contents[0].Owner.DisplayName, Equals, "webfile") + + c.Assert(data.Contents[1].Key, Equals, "Neo") + c.Assert(data.Contents[1].LastModified, Equals, "2006-01-01T12:00:00.000Z") + c.Assert(data.Contents[1].ETag, Equals, `"828ef3fdfa96f00ad9f27c383fc9ac7f"`) + c.Assert(data.Contents[1].Size, Equals, int64(4)) + c.Assert(data.Contents[1].StorageClass, Equals, "STANDARD") + c.Assert(data.Contents[1].Owner.ID, Equals, "bcaf1ffd86a5fb16fd081034f") + c.Assert(data.Contents[1].Owner.DisplayName, Equals, "webfile") +} + +func (s *S) TestListWithDelimiter(c *C) { + testServer.Response(200, nil, GetListResultDump2) + + b := s.s3.Bucket("quotes") + + data, err := b.List("photos/2006/", "/", "some-marker", 1000) + c.Assert(err, IsNil) + + req := testServer.WaitRequest() + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/quotes/") + c.Assert(req.Header["Date"], Not(Equals), "") + c.Assert(req.Form["prefix"], DeepEquals, []string{"photos/2006/"}) + c.Assert(req.Form["delimiter"], DeepEquals, []string{"/"}) + c.Assert(req.Form["marker"], DeepEquals, []string{"some-marker"}) + c.Assert(req.Form["max-keys"], DeepEquals, []string{"1000"}) + + c.Assert(data.Name, Equals, "example-bucket") + c.Assert(data.Prefix, Equals, "photos/2006/") + c.Assert(data.Delimiter, Equals, "/") + c.Assert(data.Marker, Equals, "some-marker") + c.Assert(data.IsTruncated, Equals, false) + c.Assert(len(data.Contents), Equals, 0) + c.Assert(data.CommonPrefixes, DeepEquals, []string{"photos/2006/feb/", "photos/2006/jan/"}) +} + +func (s *S) TestExists(c *C) { + testServer.Response(200, nil, "") + + b := s.s3.Bucket("bucket") + result, err := b.Exists("name") + + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "HEAD") + + c.Assert(err, IsNil) + c.Assert(result, Equals, true) +} + +func (s *S) TestExistsNotFound404(c *C) { + testServer.Response(404, nil, "") + + b := s.s3.Bucket("bucket") + result, err := b.Exists("name") + + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "HEAD") + + c.Assert(err, IsNil) + c.Assert(result, Equals, false) +} + +func (s *S) TestExistsNotFound403(c *C) { + testServer.Response(403, nil, "") + + b := s.s3.Bucket("bucket") + result, err := b.Exists("name") + + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "HEAD") + + c.Assert(err, IsNil) + c.Assert(result, Equals, false) +} diff --git a/vendor/github.com/goamz/goamz/s3/s3i_test.go b/vendor/github.com/goamz/goamz/s3/s3i_test.go new file mode 100644 index 000000000..1b898efc4 --- /dev/null +++ b/vendor/github.com/goamz/goamz/s3/s3i_test.go @@ -0,0 +1,590 @@ +package s3_test + +import ( + "bytes" + "crypto/md5" + "fmt" + "io/ioutil" + "net" + "net/http" + "sort" + "strings" + "time" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/s3" + "github.com/goamz/goamz/testutil" + . "gopkg.in/check.v1" +) + +// AmazonServer represents an Amazon S3 server. +type AmazonServer struct { + auth aws.Auth +} + +func (s *AmazonServer) SetUp(c *C) { + auth, err := aws.EnvAuth() + if err != nil { + c.Fatal(err.Error()) + } + s.auth = auth +} + +var _ = Suite(&AmazonClientSuite{Region: aws.USEast}) +var _ = Suite(&AmazonClientSuite{Region: aws.EUWest}) +var _ = Suite(&AmazonDomainClientSuite{Region: aws.USEast}) + +// AmazonClientSuite tests the client against a live S3 server. +type AmazonClientSuite struct { + aws.Region + srv AmazonServer + ClientTests +} + +func (s *AmazonClientSuite) SetUpSuite(c *C) { + if !testutil.Amazon { + c.Skip("live tests against AWS disabled (no -amazon)") + } + s.srv.SetUp(c) + s.s3 = s3.New(s.srv.auth, s.Region) + // In case tests were interrupted in the middle before. + s.ClientTests.Cleanup() +} + +func (s *AmazonClientSuite) TearDownTest(c *C) { + s.ClientTests.Cleanup() +} + +// AmazonDomainClientSuite tests the client against a live S3 +// server using bucket names in the endpoint domain name rather +// than the request path. +type AmazonDomainClientSuite struct { + aws.Region + srv AmazonServer + ClientTests +} + +func (s *AmazonDomainClientSuite) SetUpSuite(c *C) { + if !testutil.Amazon { + c.Skip("live tests against AWS disabled (no -amazon)") + } + s.srv.SetUp(c) + region := s.Region + region.S3BucketEndpoint = "https://${bucket}.s3.amazonaws.com" + s.s3 = s3.New(s.srv.auth, region) + s.ClientTests.Cleanup() +} + +func (s *AmazonDomainClientSuite) TearDownTest(c *C) { + s.ClientTests.Cleanup() +} + +// ClientTests defines integration tests designed to test the client. +// It is not used as a test suite in itself, but embedded within +// another type. +type ClientTests struct { + s3 *s3.S3 + authIsBroken bool +} + +func (s *ClientTests) Cleanup() { + killBucket(testBucket(s.s3)) +} + +func testBucket(s *s3.S3) *s3.Bucket { + // Watch out! If this function is corrupted and made to match with something + // people own, killBucket will happily remove *everything* inside the bucket. + key := s.Auth.AccessKey + if len(key) >= 8 { + key = s.Auth.AccessKey[:8] + } + return s.Bucket(fmt.Sprintf("goamz-%s-%s", s.Region.Name, key)) +} + +var attempts = aws.AttemptStrategy{ + Min: 5, + Total: 20 * time.Second, + Delay: 100 * time.Millisecond, +} + +func killBucket(b *s3.Bucket) { + var err error + for attempt := attempts.Start(); attempt.Next(); { + err = b.DelBucket() + if err == nil { + return + } + if _, ok := err.(*net.DNSError); ok { + return + } + e, ok := err.(*s3.Error) + if ok && e.Code == "NoSuchBucket" { + return + } + if ok && e.Code == "BucketNotEmpty" { + // Errors are ignored here. Just retry. + resp, err := b.List("", "", "", 1000) + if err == nil { + for _, key := range resp.Contents { + _ = b.Del(key.Key) + } + } + multis, _, _ := b.ListMulti("", "") + for _, m := range multis { + _ = m.Abort() + } + } + } + message := "cannot delete test bucket" + if err != nil { + message += ": " + err.Error() + } + panic(message) +} + +func get(url string) ([]byte, error) { + for attempt := attempts.Start(); attempt.Next(); { + resp, err := http.Get(url) + if err != nil { + if attempt.HasNext() { + continue + } + return nil, err + } + data, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + if attempt.HasNext() { + continue + } + return nil, err + } + return data, err + } + panic("unreachable") +} + +func (s *ClientTests) TestBasicFunctionality(c *C) { + b := testBucket(s.s3) + err := b.PutBucket(s3.PublicRead) + c.Assert(err, IsNil) + + err = b.Put("name", []byte("yo!"), "text/plain", s3.PublicRead, s3.Options{}) + c.Assert(err, IsNil) + defer b.Del("name") + + data, err := b.Get("name") + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "yo!") + + data, err = get(b.URL("name")) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "yo!") + + buf := bytes.NewBufferString("hey!") + err = b.PutReader("name2", buf, int64(buf.Len()), "text/plain", s3.Private, s3.Options{}) + c.Assert(err, IsNil) + defer b.Del("name2") + + rc, err := b.GetReader("name2") + c.Assert(err, IsNil) + data, err = ioutil.ReadAll(rc) + c.Check(err, IsNil) + c.Check(string(data), Equals, "hey!") + rc.Close() + + data, err = get(b.SignedURL("name2", time.Now().Add(time.Hour))) + c.Assert(err, IsNil) + c.Assert(string(data), Equals, "hey!") + + if !s.authIsBroken { + data, err = get(b.SignedURL("name2", time.Now().Add(-time.Hour))) + c.Assert(err, IsNil) + c.Assert(string(data), Matches, "(?s).*AccessDenied.*") + } + + err = b.DelBucket() + c.Assert(err, NotNil) + + s3err, ok := err.(*s3.Error) + c.Assert(ok, Equals, true) + c.Assert(s3err.Code, Equals, "BucketNotEmpty") + c.Assert(s3err.BucketName, Equals, b.Name) + c.Assert(s3err.Message, Equals, "The bucket you tried to delete is not empty") + + err = b.Del("name") + c.Assert(err, IsNil) + err = b.Del("name2") + c.Assert(err, IsNil) + + err = b.DelBucket() + c.Assert(err, IsNil) +} + +func (s *ClientTests) TestGetNotFound(c *C) { + b := s.s3.Bucket("goamz-" + s.s3.Auth.AccessKey) + data, err := b.Get("non-existent") + + s3err, _ := err.(*s3.Error) + c.Assert(s3err, NotNil) + c.Assert(s3err.StatusCode, Equals, 404) + c.Assert(s3err.Code, Equals, "NoSuchBucket") + c.Assert(s3err.Message, Equals, "The specified bucket does not exist") + c.Assert(data, IsNil) +} + +// Communicate with all endpoints to see if they are alive. +func (s *ClientTests) TestRegions(c *C) { + errs := make(chan error, len(aws.Regions)) + for _, region := range aws.Regions { + go func(r aws.Region) { + s := s3.New(s.s3.Auth, r) + b := s.Bucket("goamz-" + s.Auth.AccessKey) + _, err := b.Get("non-existent") + errs <- err + }(region) + } + for _ = range aws.Regions { + err := <-errs + if err != nil { + s3_err, ok := err.(*s3.Error) + if ok { + c.Check(s3_err.Code, Matches, "NoSuchBucket") + } else if _, ok = err.(*net.DNSError); ok { + // Okay as well. + } else { + c.Errorf("Non-S3 error: %s", err) + } + } else { + c.Errorf("Test should have errored but it seems to have succeeded") + } + } +} + +var objectNames = []string{ + "index.html", + "index2.html", + "photos/2006/February/sample2.jpg", + "photos/2006/February/sample3.jpg", + "photos/2006/February/sample4.jpg", + "photos/2006/January/sample.jpg", + "test/bar", + "test/foo", +} + +func keys(names ...string) []s3.Key { + ks := make([]s3.Key, len(names)) + for i, name := range names { + ks[i].Key = name + } + return ks +} + +// As the ListResp specifies all the parameters to the +// request too, we use it to specify request parameters +// and expected results. The Contents field is +// used only for the key names inside it. +var listTests = []s3.ListResp{ + // normal list. + { + Contents: keys(objectNames...), + }, { + Marker: objectNames[0], + Contents: keys(objectNames[1:]...), + }, { + Marker: objectNames[0] + "a", + Contents: keys(objectNames[1:]...), + }, { + Marker: "z", + }, + + // limited results. + { + MaxKeys: 2, + Contents: keys(objectNames[0:2]...), + IsTruncated: true, + }, { + MaxKeys: 2, + Marker: objectNames[0], + Contents: keys(objectNames[1:3]...), + IsTruncated: true, + }, { + MaxKeys: 2, + Marker: objectNames[len(objectNames)-2], + Contents: keys(objectNames[len(objectNames)-1:]...), + }, + + // with delimiter + { + Delimiter: "/", + CommonPrefixes: []string{"photos/", "test/"}, + Contents: keys("index.html", "index2.html"), + }, { + Delimiter: "/", + Prefix: "photos/2006/", + CommonPrefixes: []string{"photos/2006/February/", "photos/2006/January/"}, + }, { + Delimiter: "/", + Prefix: "t", + CommonPrefixes: []string{"test/"}, + }, { + Delimiter: "/", + MaxKeys: 1, + Contents: keys("index.html"), + IsTruncated: true, + }, { + Delimiter: "/", + MaxKeys: 1, + Marker: "index2.html", + CommonPrefixes: []string{"photos/"}, + IsTruncated: true, + }, { + Delimiter: "/", + MaxKeys: 1, + Marker: "photos/", + CommonPrefixes: []string{"test/"}, + IsTruncated: false, + }, { + Delimiter: "Feb", + CommonPrefixes: []string{"photos/2006/Feb"}, + Contents: keys("index.html", "index2.html", "photos/2006/January/sample.jpg", "test/bar", "test/foo"), + }, +} + +func (s *ClientTests) TestDoublePutBucket(c *C) { + b := testBucket(s.s3) + err := b.PutBucket(s3.PublicRead) + c.Assert(err, IsNil) + + err = b.PutBucket(s3.PublicRead) + if err != nil { + c.Assert(err, FitsTypeOf, new(s3.Error)) + c.Assert(err.(*s3.Error).Code, Equals, "BucketAlreadyOwnedByYou") + } +} + +func (s *ClientTests) TestBucketList(c *C) { + b := testBucket(s.s3) + err := b.PutBucket(s3.Private) + c.Assert(err, IsNil) + + objData := make(map[string][]byte) + for i, path := range objectNames { + data := []byte(strings.Repeat("a", i)) + err := b.Put(path, data, "text/plain", s3.Private, s3.Options{}) + c.Assert(err, IsNil) + defer b.Del(path) + objData[path] = data + } + + for i, t := range listTests { + c.Logf("test %d", i) + resp, err := b.List(t.Prefix, t.Delimiter, t.Marker, t.MaxKeys) + c.Assert(err, IsNil) + c.Check(resp.Name, Equals, b.Name) + c.Check(resp.Delimiter, Equals, t.Delimiter) + c.Check(resp.IsTruncated, Equals, t.IsTruncated) + c.Check(resp.CommonPrefixes, DeepEquals, t.CommonPrefixes) + checkContents(c, resp.Contents, objData, t.Contents) + } +} + +func etag(data []byte) string { + sum := md5.New() + sum.Write(data) + return fmt.Sprintf(`"%x"`, sum.Sum(nil)) +} + +func checkContents(c *C, contents []s3.Key, data map[string][]byte, expected []s3.Key) { + c.Assert(contents, HasLen, len(expected)) + for i, k := range contents { + c.Check(k.Key, Equals, expected[i].Key) + // TODO mtime + c.Check(k.Size, Equals, int64(len(data[k.Key]))) + c.Check(k.ETag, Equals, etag(data[k.Key])) + } +} + +func (s *ClientTests) TestMultiInitPutList(c *C) { + b := testBucket(s.s3) + err := b.PutBucket(s3.Private) + c.Assert(err, IsNil) + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + c.Assert(multi.UploadId, Matches, ".+") + defer multi.Abort() + + var sent []s3.Part + + for i := 0; i < 5; i++ { + p, err := multi.PutPart(i+1, strings.NewReader(fmt.Sprintf("", i+1))) + c.Assert(err, IsNil) + c.Assert(p.N, Equals, i+1) + c.Assert(p.Size, Equals, int64(8)) + c.Assert(p.ETag, Matches, ".+") + sent = append(sent, p) + } + + s3.SetListPartsMax(2) + + parts, err := multi.ListParts() + c.Assert(err, IsNil) + c.Assert(parts, HasLen, len(sent)) + for i := range parts { + c.Assert(parts[i].N, Equals, sent[i].N) + c.Assert(parts[i].Size, Equals, sent[i].Size) + c.Assert(parts[i].ETag, Equals, sent[i].ETag) + } + + err = multi.Complete(parts) + s3err, failed := err.(*s3.Error) + c.Assert(failed, Equals, true) + c.Assert(s3err.Code, Equals, "EntityTooSmall") + + err = multi.Abort() + c.Assert(err, IsNil) + _, err = multi.ListParts() + s3err, ok := err.(*s3.Error) + c.Assert(ok, Equals, true) + c.Assert(s3err.Code, Equals, "NoSuchUpload") +} + +// This may take a minute or more due to the minimum size accepted S3 +// on multipart upload parts. +func (s *ClientTests) TestMultiComplete(c *C) { + b := testBucket(s.s3) + err := b.PutBucket(s3.Private) + c.Assert(err, IsNil) + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + c.Assert(multi.UploadId, Matches, ".+") + defer multi.Abort() + + // Minimum size S3 accepts for all but the last part is 5MB. + data1 := make([]byte, 5*1024*1024) + data2 := []byte("") + + part1, err := multi.PutPart(1, bytes.NewReader(data1)) + c.Assert(err, IsNil) + part2, err := multi.PutPart(2, bytes.NewReader(data2)) + c.Assert(err, IsNil) + + // Purposefully reversed. The order requirement must be handled. + err = multi.Complete([]s3.Part{part2, part1}) + c.Assert(err, IsNil) + + data, err := b.Get("multi") + c.Assert(err, IsNil) + + c.Assert(len(data), Equals, len(data1)+len(data2)) + for i := range data1 { + if data[i] != data1[i] { + c.Fatalf("uploaded object at byte %d: want %d, got %d", data1[i], data[i]) + } + } + c.Assert(string(data[len(data1):]), Equals, string(data2)) +} + +type multiList []*s3.Multi + +func (l multiList) Len() int { return len(l) } +func (l multiList) Less(i, j int) bool { return l[i].Key < l[j].Key } +func (l multiList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } + +func (s *ClientTests) TestListMulti(c *C) { + b := testBucket(s.s3) + err := b.PutBucket(s3.Private) + c.Assert(err, IsNil) + + // Ensure an empty state before testing its behavior. + multis, _, err := b.ListMulti("", "") + for _, m := range multis { + err := m.Abort() + c.Assert(err, IsNil) + } + + keys := []string{ + "a/multi2", + "a/multi3", + "b/multi4", + "multi1", + } + for _, key := range keys { + m, err := b.InitMulti(key, "", s3.Private) + c.Assert(err, IsNil) + defer m.Abort() + } + + // Amazon's implementation of the multiple-request listing for + // multipart uploads in progress seems broken in multiple ways. + // (next tokens are not provided, etc). + //s3.SetListMultiMax(2) + + multis, prefixes, err := b.ListMulti("", "") + c.Assert(err, IsNil) + for attempt := attempts.Start(); attempt.Next() && len(multis) < len(keys); { + multis, prefixes, err = b.ListMulti("", "") + c.Assert(err, IsNil) + } + sort.Sort(multiList(multis)) + c.Assert(prefixes, IsNil) + var gotKeys []string + for _, m := range multis { + gotKeys = append(gotKeys, m.Key) + } + c.Assert(gotKeys, DeepEquals, keys) + for _, m := range multis { + c.Assert(m.Bucket, Equals, b) + c.Assert(m.UploadId, Matches, ".+") + } + + multis, prefixes, err = b.ListMulti("", "/") + for attempt := attempts.Start(); attempt.Next() && len(prefixes) < 2; { + multis, prefixes, err = b.ListMulti("", "") + c.Assert(err, IsNil) + } + c.Assert(err, IsNil) + c.Assert(prefixes, DeepEquals, []string{"a/", "b/"}) + c.Assert(multis, HasLen, 1) + c.Assert(multis[0].Bucket, Equals, b) + c.Assert(multis[0].Key, Equals, "multi1") + c.Assert(multis[0].UploadId, Matches, ".+") + + for attempt := attempts.Start(); attempt.Next() && len(multis) < 2; { + multis, prefixes, err = b.ListMulti("", "") + c.Assert(err, IsNil) + } + multis, prefixes, err = b.ListMulti("a/", "/") + c.Assert(err, IsNil) + c.Assert(prefixes, IsNil) + c.Assert(multis, HasLen, 2) + c.Assert(multis[0].Bucket, Equals, b) + c.Assert(multis[0].Key, Equals, "a/multi2") + c.Assert(multis[0].UploadId, Matches, ".+") + c.Assert(multis[1].Bucket, Equals, b) + c.Assert(multis[1].Key, Equals, "a/multi3") + c.Assert(multis[1].UploadId, Matches, ".+") +} + +func (s *ClientTests) TestMultiPutAllZeroLength(c *C) { + b := testBucket(s.s3) + err := b.PutBucket(s3.Private) + c.Assert(err, IsNil) + + multi, err := b.InitMulti("multi", "text/plain", s3.Private) + c.Assert(err, IsNil) + defer multi.Abort() + + // This tests an edge case. Amazon requires at least one + // part for multiprat uploads to work, even the part is empty. + parts, err := multi.PutAll(strings.NewReader(""), 5*1024*1024) + c.Assert(err, IsNil) + c.Assert(parts, HasLen, 1) + c.Assert(parts[0].Size, Equals, int64(0)) + c.Assert(parts[0].ETag, Equals, `"d41d8cd98f00b204e9800998ecf8427e"`) + + err = multi.Complete(parts) + c.Assert(err, IsNil) +} diff --git a/vendor/github.com/goamz/goamz/s3/s3t_test.go b/vendor/github.com/goamz/goamz/s3/s3t_test.go new file mode 100644 index 000000000..4e6f61fdb --- /dev/null +++ b/vendor/github.com/goamz/goamz/s3/s3t_test.go @@ -0,0 +1,83 @@ +package s3_test + +import ( + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/s3" + "github.com/goamz/goamz/s3/s3test" + . "gopkg.in/check.v1" +) + +type LocalServer struct { + auth aws.Auth + region aws.Region + srv *s3test.Server + config *s3test.Config +} + +func (s *LocalServer) SetUp(c *C) { + srv, err := s3test.NewServer(s.config) + c.Assert(err, IsNil) + c.Assert(srv, NotNil) + + s.srv = srv + s.region = aws.Region{ + Name: "faux-region-1", + S3Endpoint: srv.URL(), + S3LocationConstraint: true, // s3test server requires a LocationConstraint + } +} + +// LocalServerSuite defines tests that will run +// against the local s3test server. It includes +// selected tests from ClientTests; +// when the s3test functionality is sufficient, it should +// include all of them, and ClientTests can be simply embedded. +type LocalServerSuite struct { + srv LocalServer + clientTests ClientTests +} + +var ( + // run tests twice, once in us-east-1 mode, once not. + _ = Suite(&LocalServerSuite{ + srv: LocalServer{ + config: &s3test.Config{}, + }, + }) + _ = Suite(&LocalServerSuite{ + srv: LocalServer{ + config: &s3test.Config{ + Send409Conflict: true, + }, + }, + }) +) + +func (s *LocalServerSuite) SetUpSuite(c *C) { + s.srv.SetUp(c) + s.clientTests.s3 = s3.New(s.srv.auth, s.srv.region) + + // TODO Sadly the fake server ignores auth completely right now. :-( + s.clientTests.authIsBroken = true + s.clientTests.Cleanup() +} + +func (s *LocalServerSuite) TearDownTest(c *C) { + s.clientTests.Cleanup() +} + +func (s *LocalServerSuite) TestBasicFunctionality(c *C) { + s.clientTests.TestBasicFunctionality(c) +} + +func (s *LocalServerSuite) TestGetNotFound(c *C) { + s.clientTests.TestGetNotFound(c) +} + +func (s *LocalServerSuite) TestBucketList(c *C) { + s.clientTests.TestBucketList(c) +} + +func (s *LocalServerSuite) TestDoublePutBucket(c *C) { + s.clientTests.TestDoublePutBucket(c) +} diff --git a/vendor/github.com/goamz/goamz/s3/s3test/server.go b/vendor/github.com/goamz/goamz/s3/s3test/server.go new file mode 100644 index 000000000..bf4dd8a3c --- /dev/null +++ b/vendor/github.com/goamz/goamz/s3/s3test/server.go @@ -0,0 +1,640 @@ +package s3test + +import ( + "bytes" + "crypto/md5" + "encoding/base64" + "encoding/hex" + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/http" + "net/url" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/goamz/goamz/s3" +) + +const debug = false + +type s3Error struct { + statusCode int + XMLName struct{} `xml:"Error"` + Code string + Message string + BucketName string + RequestId string + HostId string +} + +type action struct { + srv *Server + w http.ResponseWriter + req *http.Request + reqId string +} + +// Config controls the internal behaviour of the Server. A nil config is the default +// and behaves as if all configurations assume their default behaviour. Once passed +// to NewServer, the configuration must not be modified. +type Config struct { + // Send409Conflict controls how the Server will respond to calls to PUT on a + // previously existing bucket. The default is false, and corresponds to the + // us-east-1 s3 enpoint. Setting this value to true emulates the behaviour of + // all other regions. + // http://docs.amazonwebservices.com/AmazonS3/latest/API/ErrorResponses.html + Send409Conflict bool + // Set the host string on which to serve s3test server. + Host string +} + +func (c *Config) send409Conflict() bool { + if c != nil { + return c.Send409Conflict + } + return false +} + +// Server is a fake S3 server for testing purposes. +// All of the data for the server is kept in memory. +type Server struct { + url string + reqId int + listener net.Listener + mu sync.Mutex + buckets map[string]*bucket + config *Config +} + +type bucket struct { + name string + acl s3.ACL + ctime time.Time + objects map[string]*object +} + +type object struct { + name string + mtime time.Time + meta http.Header // metadata to return with requests. + checksum []byte // also held as Content-MD5 in meta. + data []byte +} + +// A resource encapsulates the subject of an HTTP request. +// The resource referred to may or may not exist +// when the request is made. +type resource interface { + put(a *action) interface{} + get(a *action) interface{} + post(a *action) interface{} + delete(a *action) interface{} +} + +func NewServer(config *Config) (*Server, error) { + if config.Host == "" { + config.Host = "localhost:0" + } + + l, err := net.Listen("tcp", config.Host) + if err != nil { + return nil, fmt.Errorf("cannot listen on localhost: %v", err) + } + srv := &Server{ + listener: l, + url: "http://" + l.Addr().String(), + buckets: make(map[string]*bucket), + config: config, + } + go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + srv.serveHTTP(w, req) + })) + return srv, nil +} + +// Quit closes down the server. +func (srv *Server) Quit() { + srv.listener.Close() +} + +// URL returns a URL for the server. +func (srv *Server) URL() string { + return srv.url +} + +func fatalf(code int, codeStr string, errf string, a ...interface{}) { + panic(&s3Error{ + statusCode: code, + Code: codeStr, + Message: fmt.Sprintf(errf, a...), + }) +} + +// serveHTTP serves the S3 protocol. +func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) { + // ignore error from ParseForm as it's usually spurious. + req.ParseForm() + + srv.mu.Lock() + defer srv.mu.Unlock() + + if debug { + log.Printf("s3test %q %q", req.Method, req.URL) + } + a := &action{ + srv: srv, + w: w, + req: req, + reqId: fmt.Sprintf("%09X", srv.reqId), + } + srv.reqId++ + + var r resource + defer func() { + switch err := recover().(type) { + case *s3Error: + switch r := r.(type) { + case objectResource: + err.BucketName = r.bucket.name + case bucketResource: + err.BucketName = r.name + } + err.RequestId = a.reqId + // TODO HostId + w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`) + w.WriteHeader(err.statusCode) + xmlMarshal(w, err) + case nil: + default: + panic(err) + } + }() + + r = srv.resourceForURL(req.URL) + + var resp interface{} + switch req.Method { + case "PUT": + resp = r.put(a) + case "GET", "HEAD": + resp = r.get(a) + case "DELETE": + resp = r.delete(a) + case "POST": + resp = r.post(a) + default: + fatalf(400, "MethodNotAllowed", "unknown http request method %q", req.Method) + } + if resp != nil && req.Method != "HEAD" { + xmlMarshal(w, resp) + } +} + +// xmlMarshal is the same as xml.Marshal except that +// it panics on error. The marshalling should not fail, +// but we want to know if it does. +func xmlMarshal(w io.Writer, x interface{}) { + if err := xml.NewEncoder(w).Encode(x); err != nil { + panic(fmt.Errorf("error marshalling %#v: %v", x, err)) + } +} + +// In a fully implemented test server, each of these would have +// its own resource type. +var unimplementedBucketResourceNames = map[string]bool{ + "acl": true, + "lifecycle": true, + "policy": true, + "location": true, + "logging": true, + "notification": true, + "versions": true, + "requestPayment": true, + "versioning": true, + "website": true, + "uploads": true, +} + +var unimplementedObjectResourceNames = map[string]bool{ + "uploadId": true, + "acl": true, + "torrent": true, + "uploads": true, +} + +var pathRegexp = regexp.MustCompile("/(([^/]+)(/(.*))?)?") + +// resourceForURL returns a resource object for the given URL. +func (srv *Server) resourceForURL(u *url.URL) (r resource) { + m := pathRegexp.FindStringSubmatch(u.Path) + if m == nil { + fatalf(404, "InvalidURI", "Couldn't parse the specified URI") + } + bucketName := m[2] + objectName := m[4] + if bucketName == "" { + return nullResource{} // root + } + b := bucketResource{ + name: bucketName, + bucket: srv.buckets[bucketName], + } + q := u.Query() + if objectName == "" { + for name := range q { + if unimplementedBucketResourceNames[name] { + return nullResource{} + } + } + return b + + } + if b.bucket == nil { + fatalf(404, "NoSuchBucket", "The specified bucket does not exist") + } + objr := objectResource{ + name: objectName, + version: q.Get("versionId"), + bucket: b.bucket, + } + for name := range q { + if unimplementedObjectResourceNames[name] { + return nullResource{} + } + } + if obj := objr.bucket.objects[objr.name]; obj != nil { + objr.object = obj + } + return objr +} + +// nullResource has error stubs for all resource methods. +type nullResource struct{} + +func notAllowed() interface{} { + fatalf(400, "MethodNotAllowed", "The specified method is not allowed against this resource") + return nil +} + +func (nullResource) put(a *action) interface{} { return notAllowed() } +func (nullResource) get(a *action) interface{} { return notAllowed() } +func (nullResource) post(a *action) interface{} { return notAllowed() } +func (nullResource) delete(a *action) interface{} { return notAllowed() } + +const timeFormat = "2006-01-02T15:04:05.000Z07:00" + +type bucketResource struct { + name string + bucket *bucket // non-nil if the bucket already exists. +} + +// GET on a bucket lists the objects in the bucket. +// http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGET.html +func (r bucketResource) get(a *action) interface{} { + if r.bucket == nil { + fatalf(404, "NoSuchBucket", "The specified bucket does not exist") + } + delimiter := a.req.Form.Get("delimiter") + marker := a.req.Form.Get("marker") + maxKeys := -1 + if s := a.req.Form.Get("max-keys"); s != "" { + i, err := strconv.Atoi(s) + if err != nil || i < 0 { + fatalf(400, "invalid value for max-keys: %q", s) + } + maxKeys = i + } + prefix := a.req.Form.Get("prefix") + a.w.Header().Set("Content-Type", "application/xml") + + if a.req.Method == "HEAD" { + return nil + } + + var objs orderedObjects + + // first get all matching objects and arrange them in alphabetical order. + for name, obj := range r.bucket.objects { + if strings.HasPrefix(name, prefix) { + objs = append(objs, obj) + } + } + sort.Sort(objs) + + if maxKeys <= 0 { + maxKeys = 1000 + } + resp := &s3.ListResp{ + Name: r.bucket.name, + Prefix: prefix, + Delimiter: delimiter, + Marker: marker, + MaxKeys: maxKeys, + } + + var prefixes []string + for _, obj := range objs { + if !strings.HasPrefix(obj.name, prefix) { + continue + } + name := obj.name + isPrefix := false + if delimiter != "" { + if i := strings.Index(obj.name[len(prefix):], delimiter); i >= 0 { + name = obj.name[:len(prefix)+i+len(delimiter)] + if prefixes != nil && prefixes[len(prefixes)-1] == name { + continue + } + isPrefix = true + } + } + if name <= marker { + continue + } + if len(resp.Contents)+len(prefixes) >= maxKeys { + resp.IsTruncated = true + break + } + if isPrefix { + prefixes = append(prefixes, name) + } else { + // Contents contains only keys not found in CommonPrefixes + resp.Contents = append(resp.Contents, obj.s3Key()) + } + } + resp.CommonPrefixes = prefixes + return resp +} + +// orderedObjects holds a slice of objects that can be sorted +// by name. +type orderedObjects []*object + +func (s orderedObjects) Len() int { + return len(s) +} +func (s orderedObjects) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s orderedObjects) Less(i, j int) bool { + return s[i].name < s[j].name +} + +func (obj *object) s3Key() s3.Key { + return s3.Key{ + Key: obj.name, + LastModified: obj.mtime.Format(timeFormat), + Size: int64(len(obj.data)), + ETag: fmt.Sprintf(`"%x"`, obj.checksum), + // TODO StorageClass + // TODO Owner + } +} + +// DELETE on a bucket deletes the bucket if it's not empty. +func (r bucketResource) delete(a *action) interface{} { + b := r.bucket + if b == nil { + fatalf(404, "NoSuchBucket", "The specified bucket does not exist") + } + if len(b.objects) > 0 { + fatalf(400, "BucketNotEmpty", "The bucket you tried to delete is not empty") + } + delete(a.srv.buckets, b.name) + return nil +} + +// PUT on a bucket creates the bucket. +// http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUT.html +func (r bucketResource) put(a *action) interface{} { + var created bool + if r.bucket == nil { + if !validBucketName(r.name) { + fatalf(400, "InvalidBucketName", "The specified bucket is not valid") + } + if loc := locationConstraint(a); loc == "" { + fatalf(400, "InvalidRequets", "The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.") + } + // TODO validate acl + r.bucket = &bucket{ + name: r.name, + // TODO default acl + objects: make(map[string]*object), + } + a.srv.buckets[r.name] = r.bucket + created = true + } + if !created && a.srv.config.send409Conflict() { + fatalf(409, "BucketAlreadyOwnedByYou", "Your previous request to create the named bucket succeeded and you already own it.") + } + r.bucket.acl = s3.ACL(a.req.Header.Get("x-amz-acl")) + return nil +} + +func (bucketResource) post(a *action) interface{} { + fatalf(400, "Method", "bucket POST method not available") + return nil +} + +// validBucketName returns whether name is a valid bucket name. +// Here are the rules, from: +// http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev/BucketRestrictions.html +// +// Can contain lowercase letters, numbers, periods (.), underscores (_), +// and dashes (-). You can use uppercase letters for buckets only in the +// US Standard region. +// +// Must start with a number or letter +// +// Must be between 3 and 255 characters long +// +// There's one extra rule (Must not be formatted as an IP address (e.g., 192.168.5.4) +// but the real S3 server does not seem to check that rule, so we will not +// check it either. +// +func validBucketName(name string) bool { + if len(name) < 3 || len(name) > 255 { + return false + } + r := name[0] + if !(r >= '0' && r <= '9' || r >= 'a' && r <= 'z') { + return false + } + for _, r := range name { + switch { + case r >= '0' && r <= '9': + case r >= 'a' && r <= 'z': + case r == '_' || r == '-': + case r == '.': + default: + return false + } + } + return true +} + +var responseParams = map[string]bool{ + "content-type": true, + "content-language": true, + "expires": true, + "cache-control": true, + "content-disposition": true, + "content-encoding": true, +} + +type objectResource struct { + name string + version string + bucket *bucket // always non-nil. + object *object // may be nil. +} + +// GET on an object gets the contents of the object. +// http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html +func (objr objectResource) get(a *action) interface{} { + obj := objr.object + if obj == nil { + fatalf(404, "NoSuchKey", "The specified key does not exist.") + } + h := a.w.Header() + // add metadata + for name, d := range obj.meta { + h[name] = d + } + // override header values in response to request parameters. + for name, vals := range a.req.Form { + if strings.HasPrefix(name, "response-") { + name = name[len("response-"):] + if !responseParams[name] { + continue + } + h.Set(name, vals[0]) + } + } + if r := a.req.Header.Get("Range"); r != "" { + fatalf(400, "NotImplemented", "range unimplemented") + } + // TODO Last-Modified-Since + // TODO If-Modified-Since + // TODO If-Unmodified-Since + // TODO If-Match + // TODO If-None-Match + // TODO Connection: close ?? + // TODO x-amz-request-id + h.Set("Content-Length", fmt.Sprint(len(obj.data))) + h.Set("ETag", hex.EncodeToString(obj.checksum)) + h.Set("Last-Modified", obj.mtime.Format(time.RFC1123)) + if a.req.Method == "HEAD" { + return nil + } + // TODO avoid holding the lock when writing data. + _, err := a.w.Write(obj.data) + if err != nil { + // we can't do much except just log the fact. + log.Printf("error writing data: %v", err) + } + return nil +} + +var metaHeaders = map[string]bool{ + "Content-MD5": true, + "x-amz-acl": true, + "Content-Type": true, + "Content-Encoding": true, + "Content-Disposition": true, +} + +// PUT on an object creates the object. +func (objr objectResource) put(a *action) interface{} { + // TODO Cache-Control header + // TODO Expires header + // TODO x-amz-server-side-encryption + // TODO x-amz-storage-class + + // TODO is this correct, or should we erase all previous metadata? + obj := objr.object + if obj == nil { + obj = &object{ + name: objr.name, + meta: make(http.Header), + } + } + + var expectHash []byte + if c := a.req.Header.Get("Content-MD5"); c != "" { + var err error + expectHash, err = base64.StdEncoding.DecodeString(c) + if err != nil || len(expectHash) != md5.Size { + fatalf(400, "InvalidDigest", "The Content-MD5 you specified was invalid") + } + } + sum := md5.New() + // TODO avoid holding lock while reading data. + data, err := ioutil.ReadAll(io.TeeReader(a.req.Body, sum)) + if err != nil { + fatalf(400, "TODO", "read error") + } + gotHash := sum.Sum(nil) + if expectHash != nil && bytes.Compare(gotHash, expectHash) != 0 { + fatalf(400, "BadDigest", "The Content-MD5 you specified did not match what we received") + } + if a.req.ContentLength >= 0 && int64(len(data)) != a.req.ContentLength { + fatalf(400, "IncompleteBody", "You did not provide the number of bytes specified by the Content-Length HTTP header") + } + + // PUT request has been successful - save data and metadata + for key, values := range a.req.Header { + key = http.CanonicalHeaderKey(key) + if metaHeaders[key] || strings.HasPrefix(key, "X-Amz-Meta-") { + obj.meta[key] = values + } + } + obj.data = data + obj.checksum = gotHash + obj.mtime = time.Now() + objr.bucket.objects[objr.name] = obj + + h := a.w.Header() + h.Set("ETag", fmt.Sprintf(`"%s"`, hex.EncodeToString(obj.checksum))) + + return nil +} + +func (objr objectResource) delete(a *action) interface{} { + delete(objr.bucket.objects, objr.name) + return nil +} + +func (objr objectResource) post(a *action) interface{} { + fatalf(400, "MethodNotAllowed", "The specified method is not allowed against this resource") + return nil +} + +type CreateBucketConfiguration struct { + LocationConstraint string +} + +// locationConstraint parses the request body (if present). +// If there is no body, an empty string will be returned. +func locationConstraint(a *action) string { + var body bytes.Buffer + if _, err := io.Copy(&body, a.req.Body); err != nil { + fatalf(400, "InvalidRequest", err.Error()) + } + if body.Len() == 0 { + return "" + } + var loc CreateBucketConfiguration + if err := xml.NewDecoder(&body).Decode(&loc); err != nil { + fatalf(400, "InvalidRequest", err.Error()) + } + return loc.LocationConstraint +} diff --git a/vendor/github.com/goamz/goamz/s3/sign_test.go b/vendor/github.com/goamz/goamz/s3/sign_test.go new file mode 100644 index 000000000..112e1ca3e --- /dev/null +++ b/vendor/github.com/goamz/goamz/s3/sign_test.go @@ -0,0 +1,132 @@ +package s3_test + +import ( + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/s3" + . "gopkg.in/check.v1" +) + +// S3 ReST authentication docs: http://goo.gl/G1LrK + +var testAuth = aws.Auth{AccessKey: "0PN5J17HBGZHT7JJ3X82", SecretKey: "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o"} + +func (s *S) TestSignExampleObjectGet(c *C) { + method := "GET" + path := "/johnsmith/photos/puppy.jpg" + headers := map[string][]string{ + "Host": {"johnsmith.s3.amazonaws.com"}, + "Date": {"Tue, 27 Mar 2007 19:36:42 +0000"}, + } + s3.Sign(testAuth, method, path, nil, headers) + expected := "AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbA=" + c.Assert(headers["Authorization"], DeepEquals, []string{expected}) +} + +func (s *S) TestSignExampleObjectPut(c *C) { + method := "PUT" + path := "/johnsmith/photos/puppy.jpg" + headers := map[string][]string{ + "Host": {"johnsmith.s3.amazonaws.com"}, + "Date": {"Tue, 27 Mar 2007 21:15:45 +0000"}, + "Content-Type": {"image/jpeg"}, + "Content-Length": {"94328"}, + } + s3.Sign(testAuth, method, path, nil, headers) + expected := "AWS 0PN5J17HBGZHT7JJ3X82:hcicpDDvL9SsO6AkvxqmIWkmOuQ=" + c.Assert(headers["Authorization"], DeepEquals, []string{expected}) +} + +func (s *S) TestSignExampleList(c *C) { + method := "GET" + path := "/johnsmith/" + params := map[string][]string{ + "prefix": {"photos"}, + "max-keys": {"50"}, + "marker": {"puppy"}, + } + headers := map[string][]string{ + "Host": {"johnsmith.s3.amazonaws.com"}, + "Date": {"Tue, 27 Mar 2007 19:42:41 +0000"}, + "User-Agent": {"Mozilla/5.0"}, + } + s3.Sign(testAuth, method, path, params, headers) + expected := "AWS 0PN5J17HBGZHT7JJ3X82:jsRt/rhG+Vtp88HrYL706QhE4w4=" + c.Assert(headers["Authorization"], DeepEquals, []string{expected}) +} + +func (s *S) TestSignExampleFetch(c *C) { + method := "GET" + path := "/johnsmith/" + params := map[string][]string{ + "acl": {""}, + } + headers := map[string][]string{ + "Host": {"johnsmith.s3.amazonaws.com"}, + "Date": {"Tue, 27 Mar 2007 19:44:46 +0000"}, + } + s3.Sign(testAuth, method, path, params, headers) + expected := "AWS 0PN5J17HBGZHT7JJ3X82:thdUi9VAkzhkniLj96JIrOPGi0g=" + c.Assert(headers["Authorization"], DeepEquals, []string{expected}) +} + +func (s *S) TestSignExampleDelete(c *C) { + method := "DELETE" + path := "/johnsmith/photos/puppy.jpg" + params := map[string][]string{} + headers := map[string][]string{ + "Host": {"s3.amazonaws.com"}, + "Date": {"Tue, 27 Mar 2007 21:20:27 +0000"}, + "User-Agent": {"dotnet"}, + "x-amz-date": {"Tue, 27 Mar 2007 21:20:26 +0000"}, + } + s3.Sign(testAuth, method, path, params, headers) + expected := "AWS 0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5Ip83xlYzk=" + c.Assert(headers["Authorization"], DeepEquals, []string{expected}) +} + +func (s *S) TestSignExampleUpload(c *C) { + method := "PUT" + path := "/static.johnsmith.net/db-backup.dat.gz" + params := map[string][]string{} + headers := map[string][]string{ + "Host": {"static.johnsmith.net:8080"}, + "Date": {"Tue, 27 Mar 2007 21:06:08 +0000"}, + "User-Agent": {"curl/7.15.5"}, + "x-amz-acl": {"public-read"}, + "content-type": {"application/x-download"}, + "Content-MD5": {"4gJE4saaMU4BqNR0kLY+lw=="}, + "X-Amz-Meta-ReviewedBy": {"joe@johnsmith.net,jane@johnsmith.net"}, + "X-Amz-Meta-FileChecksum": {"0x02661779"}, + "X-Amz-Meta-ChecksumAlgorithm": {"crc32"}, + "Content-Disposition": {"attachment; filename=database.dat"}, + "Content-Encoding": {"gzip"}, + "Content-Length": {"5913339"}, + } + s3.Sign(testAuth, method, path, params, headers) + expected := "AWS 0PN5J17HBGZHT7JJ3X82:C0FlOtU8Ylb9KDTpZqYkZPX91iI=" + c.Assert(headers["Authorization"], DeepEquals, []string{expected}) +} + +func (s *S) TestSignExampleListAllMyBuckets(c *C) { + method := "GET" + path := "/" + headers := map[string][]string{ + "Host": {"s3.amazonaws.com"}, + "Date": {"Wed, 28 Mar 2007 01:29:59 +0000"}, + } + s3.Sign(testAuth, method, path, nil, headers) + expected := "AWS 0PN5J17HBGZHT7JJ3X82:Db+gepJSUbZKwpx1FR0DLtEYoZA=" + c.Assert(headers["Authorization"], DeepEquals, []string{expected}) +} + +func (s *S) TestSignExampleUnicodeKeys(c *C) { + method := "GET" + path := "/dictionary/fran%C3%A7ais/pr%c3%a9f%c3%a8re" + headers := map[string][]string{ + "Host": {"s3.amazonaws.com"}, + "Date": {"Wed, 28 Mar 2007 01:49:49 +0000"}, + } + s3.Sign(testAuth, method, path, nil, headers) + expected := "AWS 0PN5J17HBGZHT7JJ3X82:dxhSBHoI6eVSPcXJqEghlUzZMnY=" + c.Assert(headers["Authorization"], DeepEquals, []string{expected}) +} diff --git a/vendor/github.com/goamz/goamz/sqs/Makefile b/vendor/github.com/goamz/goamz/sqs/Makefile new file mode 100644 index 000000000..1219acfb9 --- /dev/null +++ b/vendor/github.com/goamz/goamz/sqs/Makefile @@ -0,0 +1,20 @@ +include $(GOROOT)/src/Make.inc + +TARG=launchpad.net/goamz/sqs + +GOFILES=\ + sqs.go\ + +include $(GOROOT)/src/Make.pkg + +GOFMT=gofmt +BADFMT=$(shell $(GOFMT) -l $(GOFILES) 2> /dev/null) + +gofmt: $(BADFMT) + @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done + +ifneq ($(BADFMT),) +ifneq ($(MAKECMDGOALS), gofmt) +#$(warning WARNING: make gofmt: $(BADFMT)) +endif +endif diff --git a/vendor/github.com/goamz/goamz/sqs/README.md b/vendor/github.com/goamz/goamz/sqs/README.md new file mode 100644 index 000000000..a283a4eec --- /dev/null +++ b/vendor/github.com/goamz/goamz/sqs/README.md @@ -0,0 +1,38 @@ +Amazon Simple Queue Service API Client Written in Golang. +========================================================= + +Merged from https://github.com/Mistobaan/sqs + +Installation +------------ + + go get github.com/goamz/goamz/sqs + +Documentation +------------- + +http://godoc.org/github.com/goamz/goamz/sqs + + +Sample Usage +------------ + + var auth = aws.Auth{ + AccessKey: os.Getenv("AWS_ACCESS_KEY_ID"), + SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"), + } + + conn := sqs.New(auth, aws.USEast) + + q, err := conn.CreateQueue(queueName) + if err != nil { + log.Fatalf(err.Error()) + } + + q.SendMessage(batch) + + +Testing +------- + + go test . diff --git a/vendor/github.com/goamz/goamz/sqs/responses_test.go b/vendor/github.com/goamz/goamz/sqs/responses_test.go new file mode 100644 index 000000000..857fb9104 --- /dev/null +++ b/vendor/github.com/goamz/goamz/sqs/responses_test.go @@ -0,0 +1,196 @@ +package sqs + +var TestCreateQueueXmlOK = ` + + + http://sqs.us-east-1.amazonaws.com/123456789012/testQueue + + + 7a62c49f-347e-4fc4-9331-6e8e7a96aa73 + + +` + +var TestListQueuesXmlOK = ` + + + http://sqs.us-east-1.amazonaws.com/123456789012/testQueue + + + 725275ae-0b9b-4762-b238-436d7c65a1ac + + +` + +var TestDeleteQueueXmlOK = ` + + + 6fde8d1e-52cd-4581-8cd9-c512f4c64223 + + +` + +var TestPurgeQueueXmlOK = ` + + + 6fde8d1e-52cd-4581-8cd9-c512f4c64223 + + +` + +var TestSendMessageXmlOK = ` + + + fafb00f5732ab283681e124bf8747ed1 + 5fea7756-0ea4-451a-a703-a558b933e274 + ba056227cfd9533dba1f72ad9816d233 + + + 27daac76-34dd-47df-bd01-1f6e873584a0 + + +` + +var TestSendMessageBatchXmlOk = ` + + + + test_msg_001 + 0a5231c7-8bff-4955-be2e-8dc7c50a25fa + 0e024d309850c78cba5eabbeff7cae71 + + + test_msg_002 + 15ee1ed3-87e7-40c1-bdaa-2e49968ea7e9 + 7fb8146a82f95e0af155278f406862c2 + + + + ca1ad5d0-8271-408b-8d0f-1351bf547e74 + + +` + +var TestReceiveMessageXmlOK = ` + + + + 5fea7756-0ea4-451a-a703-a558b933e274 + MbZj6wDWli+JvwwJaBV+3dcjk2YW2vA3+STFFljTM8tJJg6HRG6PYSasuWXPJB+CwLj1FjgXUv1uSj1gUPAWV66FU/WeR4mq2OKpEGYWbnLmpRCJVAyeMjeU5ZBdtcQ+QEauMZc8ZRv37sIW2iJKq3M9MFx1YvV11A2x/KSbkJ0= + fafb00f5732ab283681e124bf8747ed1 + This is a test message + + SenderId + 195004372649 + + + SentTimestamp + 1238099229000 + + + ApproximateReceiveCount + 5 + + + ApproximateFirstReceiveTimestamp + 1250700979248 + + + CustomAttribute + + String + Testing, testing, 1, 2, 3 + + + + BinaryCustomAttribute + + Binary + iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAABA0lEQVQ4T72UrQ4CMRCEewhyiiBPopBgcfAUSIICB88CDhRB8hTgsCBRyJMEdUFwZJpMs/3LHQlhVdPufJ1ut03UjyKJcR5zVc4umbW87eeqvVFBjTdJwP54D+4xGXVUCGiBxoOsJOCd9IKgRnnV8wAezrnRmwGcpKtCJ8UgJBNWLFNzVAOimyqIhElXGkQ3LmQ6fKrdqaW1cixhdKVBcEOBLEwViBugVv8B1elVuLYcoTea624drcl5LW4KTRsFhQpLtVzzQKGCh2DuHI8FvdVH7vGQKEPerHRjgegKMESsXgAgWBtu5D1a9BQWCXSrzx9BvjPPkRQR6IJcQNTRV/cvkj93DqUTWzVDIQAAAABJRU5ErkJggg== + + + + + + b6633655-283d-45b4-aee4-4e84e0ae6afa + + +` + +var TestChangeMessageVisibilityXmlOK = ` + + + 6a7a282a-d013-4a59-aba9-335b0fa48bed + + +` + +var TestDeleteMessageBatchXmlOK = ` + + + + msg1 + + + msg2 + + + + d6f86b7a-74d1-4439-b43f-196a1e29cd85 + + +` + +var TestDeleteMessageUsingReceiptXmlOK = ` + + + d6d86b7a-74d1-4439-b43f-196a1e29cd85 + + +` + +var TestGetQueueAttributesXmlOK = ` + + + + ReceiveMessageWaitTimeSeconds + 2 + + + VisibilityTimeout + 30 + + + ApproximateNumberOfMessages + 0 + + + ApproximateNumberOfMessagesNotVisible + 0 + + + CreatedTimestamp + 1286771522 + + + LastModifiedTimestamp + 1286771522 + + + QueueArn + arn:aws:sqs:us-east-1:123456789012:qfoo + + + MaximumMessageSize + 8192 + + + MessageRetentionPeriod + 345600 + + + + 1ea71be5-b5a2-4f9d-b85a-945d8d08cd0b + + +` diff --git a/vendor/github.com/goamz/goamz/sqs/sqs.go b/vendor/github.com/goamz/goamz/sqs/sqs.go new file mode 100644 index 000000000..4005b1b37 --- /dev/null +++ b/vendor/github.com/goamz/goamz/sqs/sqs.go @@ -0,0 +1,570 @@ +// +// gosqs - Go packages to interact with the Amazon SQS Web Services. +// +// depends on https://wiki.ubuntu.com/goamz +// +// +// Written by Prudhvi Krishna Surapaneni +// Extended by Fabrizio Milo +// +package sqs + +import ( + "encoding/xml" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "net/http/httputil" + "net/url" + "strconv" + "strings" + "time" + + "github.com/goamz/goamz/aws" +) + +const API_VERSION = "2012-11-05" + +const debug = false + +// The SQS type encapsulates operation with an SQS region. +type SQS struct { + aws.Auth + aws.Region + private byte // Reserve the right of using private data. +} + +// NewFrom Create A new SQS Client given an access and secret Key +// region must be one of "us.east, us.west, eu.west" +func NewFrom(accessKey, secretKey, region string) (*SQS, error) { + + auth := aws.Auth{AccessKey: accessKey, SecretKey: secretKey} + aws_region := aws.USEast + + switch region { + case "us.east", "us.east.1": + aws_region = aws.USEast + case "us.west", "us.west.1": + aws_region = aws.USWest + case "us.west.2": + aws_region = aws.USWest2 + case "eu.west": + aws_region = aws.EUWest + case "ap.southeast", "ap.southeast.1": + aws_region = aws.APSoutheast + case "ap.southeast.2": + aws_region = aws.APSoutheast2 + case "ap.northeast", "ap.northeast.1": + aws_region = aws.APNortheast + case "sa.east", "sa.east.1": + aws_region = aws.SAEast + case "cn.north", "cn.north.1": + aws_region = aws.CNNorth + default: + return nil, errors.New(fmt.Sprintf("Unknown/Unsupported region %s", region)) + } + + aws_sqs := New(auth, aws_region) + return aws_sqs, nil +} + +// NewFrom Create A new SQS Client from an exisisting aws.Auth +func New(auth aws.Auth, region aws.Region) *SQS { + return &SQS{auth, region, 0} +} + +// Queue Reference to a Queue +type Queue struct { + *SQS + Url string +} + +type CreateQueueResponse struct { + QueueUrl string `xml:"CreateQueueResult>QueueUrl"` + ResponseMetadata ResponseMetadata +} + +type GetQueueUrlResponse struct { + QueueUrl string `xml:"GetQueueUrlResult>QueueUrl"` + ResponseMetadata ResponseMetadata +} + +type ListQueuesResponse struct { + QueueUrl []string `xml:"ListQueuesResult>QueueUrl"` + ResponseMetadata ResponseMetadata +} + +type DeleteMessageResponse struct { + ResponseMetadata ResponseMetadata +} + +type DeleteQueueResponse struct { + ResponseMetadata ResponseMetadata +} + +type PurgeQueueResponse struct { + ResponseMetadata ResponseMetadata +} + +type SendMessageResponse struct { + MD5 string `xml:"SendMessageResult>MD5OfMessageBody"` + MD5OfMessageAttributes string `xml:"SendMessageResult>MD5OfMessageAttributes"` + Id string `xml:"SendMessageResult>MessageId"` + ResponseMetadata ResponseMetadata +} + +type ReceiveMessageResponse struct { + Messages []Message `xml:"ReceiveMessageResult>Message"` + ResponseMetadata ResponseMetadata +} + +type Message struct { + MessageId string `xml:"MessageId"` + Body string `xml:"Body"` + MD5OfBody string `xml:"MD5OfBody"` + ReceiptHandle string `xml:"ReceiptHandle"` + Attribute []Attribute `xml:"Attribute"` + MessageAttribute []MessageAttribute `xml:"MessageAttribute"` + MD5OfMessageAttributes string `xml:"MD5OfMessageAttributes"` +} + +type Attribute struct { + Name string `xml:"Name"` + Value string `xml:"Value"` +} + +type MessageAttribute struct { + Name string `xml:"Name"` + Value MessageAttributeValue `xml:"Value"` +} + +type MessageAttributeValue struct { + DataType string `xml:"DataType"` + BinaryValue []byte `xml:"BinaryValue"` + StringValue string `xml:"StringValue"` + + // Not yet implemented (Reserved for future use) + BinaryListValues [][]byte `xml:"BinaryListValues"` + StringListValues []string `xml:"StringListValues"` +} + +type ChangeMessageVisibilityResponse struct { + ResponseMetadata ResponseMetadata +} + +type GetQueueAttributesResponse struct { + Attributes []Attribute `xml:"GetQueueAttributesResult>Attribute"` + ResponseMetadata ResponseMetadata +} + +type ResponseMetadata struct { + RequestId string + BoxUsage float64 +} + +type Error struct { + StatusCode int + Code string + Message string + RequestId string +} + +func (err *Error) Error() string { + if err.Code == "" { + return err.Message + } + return fmt.Sprintf("%s (%s)", err.Message, err.Code) +} + +func (err *Error) String() string { + return err.Message +} + +type xmlErrors struct { + RequestId string + Errors []Error `xml:"Errors>Error"` + Error Error +} + +// CreateQueue create a queue with a specific name +func (s *SQS) CreateQueue(queueName string) (*Queue, error) { + return s.CreateQueueWithTimeout(queueName, 30) +} + +// CreateQueue create a queue with a specific name and a timeout +func (s *SQS) CreateQueueWithTimeout(queueName string, timeout int) (*Queue, error) { + params := map[string]string{ + "VisibilityTimeout": strconv.Itoa(timeout), + } + return s.CreateQueueWithAttributes(queueName, params) +} + +func (s *SQS) CreateQueueWithAttributes(queueName string, attrs map[string]string) (q *Queue, err error) { + resp, err := s.newQueue(queueName, attrs) + if err != nil { + return nil, err + } + q = &Queue{s, resp.QueueUrl} + return +} + +// GetQueue get a reference to the given quename +func (s *SQS) GetQueue(queueName string) (*Queue, error) { + var q *Queue + resp, err := s.getQueueUrl(queueName) + if err != nil { + return q, err + } + q = &Queue{s, resp.QueueUrl} + return q, nil +} + +func (s *SQS) QueueFromArn(queueUrl string) (q *Queue) { + q = &Queue{s, queueUrl} + return +} + +func (s *SQS) getQueueUrl(queueName string) (resp *GetQueueUrlResponse, err error) { + resp = &GetQueueUrlResponse{} + params := makeParams("GetQueueUrl") + params["QueueName"] = queueName + err = s.query("", params, resp) + return resp, err +} + +func (s *SQS) newQueue(queueName string, attrs map[string]string) (resp *CreateQueueResponse, err error) { + resp = &CreateQueueResponse{} + params := makeParams("CreateQueue") + params["QueueName"] = queueName + + i := 1 + for k, v := range attrs { + nameParam := fmt.Sprintf("Attribute.%d.Name", i) + valParam := fmt.Sprintf("Attribute.%d.Value", i) + params[nameParam] = k + params[valParam] = v + i++ + } + + err = s.query("", params, resp) + return +} + +func (s *SQS) ListQueues(QueueNamePrefix string) (resp *ListQueuesResponse, err error) { + resp = &ListQueuesResponse{} + params := makeParams("ListQueues") + + if QueueNamePrefix != "" { + params["QueueNamePrefix"] = QueueNamePrefix + } + + err = s.query("", params, resp) + return +} + +func (q *Queue) Delete() (resp *DeleteQueueResponse, err error) { + resp = &DeleteQueueResponse{} + params := makeParams("DeleteQueue") + + err = q.SQS.query(q.Url, params, resp) + return +} + +func (q *Queue) Purge() (resp *PurgeQueueResponse, err error) { + resp = &PurgeQueueResponse{} + params := makeParams("PurgeQueue") + + err = q.SQS.query(q.Url, params, resp) + return +} + +func (q *Queue) SendMessageWithDelay(MessageBody string, DelaySeconds int64) (resp *SendMessageResponse, err error) { + resp = &SendMessageResponse{} + params := makeParams("SendMessage") + + params["MessageBody"] = MessageBody + params["DelaySeconds"] = strconv.Itoa(int(DelaySeconds)) + + err = q.SQS.query(q.Url, params, resp) + return +} + +func (q *Queue) SendMessage(MessageBody string) (resp *SendMessageResponse, err error) { + resp = &SendMessageResponse{} + params := makeParams("SendMessage") + + params["MessageBody"] = MessageBody + + err = q.SQS.query(q.Url, params, resp) + return +} + +func (q *Queue) SendMessageWithAttributes(MessageBody string, attrs map[string]string) (resp *SendMessageResponse, err error) { + resp = &SendMessageResponse{} + params := makeParams("SendMessage") + + params["MessageBody"] = MessageBody + + i := 1 + for k, v := range attrs { + nameParam := fmt.Sprintf("MessageAttribute.%d.Name", i) + valParam := fmt.Sprintf("MessageAttribute.%d.Value.StringValue", i) + typeParam := fmt.Sprintf("MessageAttribute.%d.Value.DataType", i) + params[nameParam] = k + params[valParam] = v + params[typeParam] = "String" + i++ + } + + err = q.SQS.query(q.Url, params, resp) + return +} + +// ReceiveMessageWithVisibilityTimeout +func (q *Queue) ReceiveMessageWithVisibilityTimeout(MaxNumberOfMessages, VisibilityTimeoutSec int) (*ReceiveMessageResponse, error) { + params := map[string]string{ + "MaxNumberOfMessages": strconv.Itoa(MaxNumberOfMessages), + "VisibilityTimeout": strconv.Itoa(VisibilityTimeoutSec), + } + return q.ReceiveMessageWithParameters(params) +} + +// ReceiveMessage +func (q *Queue) ReceiveMessage(MaxNumberOfMessages int) (*ReceiveMessageResponse, error) { + params := map[string]string{ + "MaxNumberOfMessages": strconv.Itoa(MaxNumberOfMessages), + } + return q.ReceiveMessageWithParameters(params) +} + +func (q *Queue) ReceiveMessageWithParameters(p map[string]string) (resp *ReceiveMessageResponse, err error) { + resp = &ReceiveMessageResponse{} + params := makeParams("ReceiveMessage") + params["AttributeName"] = "All" + params["MessageAttributeNames"] = "All" + + for k, v := range p { + params[k] = v + } + + err = q.SQS.query(q.Url, params, resp) + return +} + +func (q *Queue) ChangeMessageVisibility(M *Message, VisibilityTimeout int) (resp *ChangeMessageVisibilityResponse, err error) { + resp = &ChangeMessageVisibilityResponse{} + params := makeParams("ChangeMessageVisibility") + params["VisibilityTimeout"] = strconv.Itoa(VisibilityTimeout) + params["ReceiptHandle"] = M.ReceiptHandle + + err = q.SQS.query(q.Url, params, resp) + return +} + +func (q *Queue) GetQueueAttributes(A string) (resp *GetQueueAttributesResponse, err error) { + resp = &GetQueueAttributesResponse{} + params := makeParams("GetQueueAttributes") + params["AttributeName"] = A + + err = q.SQS.query(q.Url, params, resp) + return +} + +func (q *Queue) DeleteMessage(M *Message) (resp *DeleteMessageResponse, err error) { + return q.DeleteMessageUsingReceiptHandle(M.ReceiptHandle) +} + +func (q *Queue) DeleteMessageUsingReceiptHandle(receiptHandle string) (resp *DeleteMessageResponse, err error) { + resp = &DeleteMessageResponse{} + params := makeParams("DeleteMessage") + params["ReceiptHandle"] = receiptHandle + + err = q.SQS.query(q.Url, params, resp) + return +} + +type SendMessageBatchResultEntry struct { + Id string `xml:"Id"` + MessageId string `xml:"MessageId"` + MD5OfMessageBody string `xml:"MD5OfMessageBody"` +} + +type SendMessageBatchResponse struct { + SendMessageBatchResult []SendMessageBatchResultEntry `xml:"SendMessageBatchResult>SendMessageBatchResultEntry"` + ResponseMetadata ResponseMetadata +} + +/* SendMessageBatch + */ +func (q *Queue) SendMessageBatch(msgList []Message) (resp *SendMessageBatchResponse, err error) { + resp = &SendMessageBatchResponse{} + params := makeParams("SendMessageBatch") + + for idx, msg := range msgList { + count := idx + 1 + params[fmt.Sprintf("SendMessageBatchRequestEntry.%d.Id", count)] = fmt.Sprintf("msg-%d", count) + params[fmt.Sprintf("SendMessageBatchRequestEntry.%d.MessageBody", count)] = msg.Body + } + + err = q.SQS.query(q.Url, params, resp) + return +} + +/* SendMessageBatchString + */ +func (q *Queue) SendMessageBatchString(msgList []string) (resp *SendMessageBatchResponse, err error) { + resp = &SendMessageBatchResponse{} + params := makeParams("SendMessageBatch") + + for idx, msg := range msgList { + count := idx + 1 + params[fmt.Sprintf("SendMessageBatchRequestEntry.%d.Id", count)] = fmt.Sprintf("msg-%d", count) + params[fmt.Sprintf("SendMessageBatchRequestEntry.%d.MessageBody", count)] = msg + } + + err = q.SQS.query(q.Url, params, resp) + return +} + +type DeleteMessageBatchResponse struct { + DeleteMessageBatchResult []struct { + Id string + SenderFault bool + Code string + Message string + } `xml:"DeleteMessageBatchResult>DeleteMessageBatchResultEntry"` + ResponseMetadata ResponseMetadata +} + +/* DeleteMessageBatch */ +func (q *Queue) DeleteMessageBatch(msgList []Message) (resp *DeleteMessageBatchResponse, err error) { + resp = &DeleteMessageBatchResponse{} + params := makeParams("DeleteMessageBatch") + + lutMsg := make(map[string]Message) + + for idx := range msgList { + params[fmt.Sprintf("DeleteMessageBatchRequestEntry.%d.Id", idx+1)] = msgList[idx].MessageId + params[fmt.Sprintf("DeleteMessageBatchRequestEntry.%d.ReceiptHandle", idx+1)] = msgList[idx].ReceiptHandle + + lutMsg[string(msgList[idx].MessageId)] = msgList[idx] + } + + err = q.SQS.query(q.Url, params, resp) + + messageWithErrors := make([]Message, 0, len(msgList)) + + for idx := range resp.DeleteMessageBatchResult { + if resp.DeleteMessageBatchResult[idx].SenderFault { + msg, ok := lutMsg[resp.DeleteMessageBatchResult[idx].Id] + if ok { + messageWithErrors = append(messageWithErrors, msg) + } + } + } + + if len(messageWithErrors) > 0 { + log.Printf("%d Message have not been sent", len(messageWithErrors)) + } + + return +} + +func (s *SQS) query(queueUrl string, params map[string]string, resp interface{}) (err error) { + params["Version"] = API_VERSION + params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339) + var url_ *url.URL + + switch { + // fully qualified queueUrl + case strings.HasPrefix(queueUrl, "http"): + url_, err = url.Parse(queueUrl) + // relative queueUrl + case strings.HasPrefix(queueUrl, "/"): + url_, err = url.Parse(s.Region.SQSEndpoint + queueUrl) + // zero-value for queueUrl + default: + url_, err = url.Parse(s.Region.SQSEndpoint) + } + + if err != nil { + return err + } + + if s.Auth.Token() != "" { + params["SecurityToken"] = s.Auth.Token() + } + + var r *http.Response + + var sarray []string + for k, v := range params { + sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v)) + } + + req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", url_, strings.Join(sarray, "&")), nil) + if err != nil { + return err + } + signer := aws.NewV4Signer(s.Auth, "sqs", s.Region) + signer.Sign(req) + client := http.Client{} + r, err = client.Do(req) + + if debug { + log.Printf("GET ", url_.String()) + } + + if err != nil { + return err + } + + defer r.Body.Close() + + if debug { + dump, _ := httputil.DumpResponse(r, true) + log.Printf("DUMP:\n", string(dump)) + } + + if r.StatusCode != 200 { + return buildError(r) + } + err = xml.NewDecoder(r.Body).Decode(resp) + io.Copy(ioutil.Discard, r.Body) + + return err +} + +func buildError(r *http.Response) error { + errors := xmlErrors{} + xml.NewDecoder(r.Body).Decode(&errors) + var err Error + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } else { + err = errors.Error + } + err.RequestId = errors.RequestId + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +func makeParams(action string) map[string]string { + params := make(map[string]string) + params["Action"] = action + return params +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} diff --git a/vendor/github.com/goamz/goamz/sqs/sqs_test.go b/vendor/github.com/goamz/goamz/sqs/sqs_test.go new file mode 100644 index 000000000..a06433535 --- /dev/null +++ b/vendor/github.com/goamz/goamz/sqs/sqs_test.go @@ -0,0 +1,414 @@ +package sqs + +import ( + "crypto/md5" + "encoding/binary" + "fmt" + "hash" + + "github.com/goamz/goamz/aws" + . "gopkg.in/check.v1" +) + +var _ = Suite(&S{}) + +type S struct { + HTTPSuite + sqs *SQS +} + +func (s *S) SetUpSuite(c *C) { + s.HTTPSuite.SetUpSuite(c) + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.sqs = New(auth, aws.Region{SQSEndpoint: testServer.URL}) +} + +func (s *S) TestCreateQueue(c *C) { + testServer.PrepareResponse(200, nil, TestCreateQueueXmlOK) + + resp, err := s.sqs.CreateQueue("testQueue") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + fmt.Printf("%+v\n", req) + c.Assert(req.Form["Action"], DeepEquals, []string{"CreateQueue"}) + c.Assert(req.Form["Attribute.1.Name"], DeepEquals, []string{"VisibilityTimeout"}) + c.Assert(req.Form["Attribute.1.Value"], DeepEquals, []string{"30"}) + + c.Assert(resp.Url, Equals, "http://sqs.us-east-1.amazonaws.com/123456789012/testQueue") + c.Assert(err, IsNil) +} + +func (s *S) TestCreateQueueWithTimeout(c *C) { + testServer.PrepareResponse(200, nil, TestCreateQueueXmlOK) + + s.sqs.CreateQueueWithTimeout("testQueue", 180) + req := testServer.WaitRequest() + + // TestCreateQueue() tests the core functionality, just check the timeout in this test + c.Assert(req.Form["Attribute.1.Name"], DeepEquals, []string{"VisibilityTimeout"}) + c.Assert(req.Form["Attribute.1.Value"], DeepEquals, []string{"180"}) +} + +func (s *S) TestCreateQueueWithAttributes(c *C) { + testServer.PrepareResponse(200, nil, TestCreateQueueXmlOK) + + s.sqs.CreateQueueWithAttributes("testQueue", map[string]string{ + "ReceiveMessageWaitTimeSeconds": "20", + "VisibilityTimeout": "240", + }) + req := testServer.WaitRequest() + + // TestCreateQueue() tests the core functionality, just check the timeout in this test + var receiveMessageWaitSet bool + var visibilityTimeoutSet bool + + for i := 1; i <= 2; i++ { + prefix := fmt.Sprintf("Attribute.%d.", i) + attr := req.FormValue(prefix + "Name") + value := req.FormValue(prefix + "Value") + switch attr { + case "ReceiveMessageWaitTimeSeconds": + c.Assert(value, DeepEquals, "20") + receiveMessageWaitSet = true + case "VisibilityTimeout": + c.Assert(value, DeepEquals, "240") + visibilityTimeoutSet = true + } + } + c.Assert(receiveMessageWaitSet, Equals, true) + c.Assert(visibilityTimeoutSet, Equals, true) +} + +func (s *S) TestListQueues(c *C) { + testServer.PrepareResponse(200, nil, TestListQueuesXmlOK) + + resp, err := s.sqs.ListQueues("") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(len(resp.QueueUrl), Not(Equals), 0) + c.Assert(resp.QueueUrl[0], Equals, "http://sqs.us-east-1.amazonaws.com/123456789012/testQueue") + c.Assert(resp.ResponseMetadata.RequestId, Equals, "725275ae-0b9b-4762-b238-436d7c65a1ac") + c.Assert(err, IsNil) +} + +func (s *S) TestDeleteQueue(c *C) { + testServer.PrepareResponse(200, nil, TestDeleteQueueXmlOK) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + resp, err := q.Delete() + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "6fde8d1e-52cd-4581-8cd9-c512f4c64223") + c.Assert(err, IsNil) +} + +func (s *S) TestPurgeQueue(c *C) { + testServer.PrepareResponse(200, nil, TestPurgeQueueXmlOK) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + resp, err := q.Purge() + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "6fde8d1e-52cd-4581-8cd9-c512f4c64223") + c.Assert(err, IsNil) +} + +func (s *S) TestSendMessage(c *C) { + testServer.PrepareResponse(200, nil, TestSendMessageXmlOK) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + resp, err := q.SendMessage("This is a test message") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + msg := "This is a test message" + var h hash.Hash = md5.New() + h.Write([]byte(msg)) + c.Assert(resp.MD5, Equals, fmt.Sprintf("%x", h.Sum(nil))) + c.Assert(resp.Id, Equals, "5fea7756-0ea4-451a-a703-a558b933e274") + c.Assert(err, IsNil) +} + +func (s *S) TestSendMessageRelativePath(c *C) { + testServer.PrepareResponse(200, nil, TestSendMessageXmlOK) + + q := &Queue{s.sqs, "/123456789012/testQueue/"} + resp, err := q.SendMessage("This is a test message") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + msg := "This is a test message" + var h hash.Hash = md5.New() + h.Write([]byte(msg)) + c.Assert(resp.MD5, Equals, fmt.Sprintf("%x", h.Sum(nil))) + c.Assert(resp.Id, Equals, "5fea7756-0ea4-451a-a703-a558b933e274") + c.Assert(err, IsNil) +} + +func encodeMessageAttribute(str string) []byte { + bstr := []byte(str) + bs := make([]byte, 4+len(bstr)) + binary.BigEndian.PutUint32(bs, uint32(len(bstr))) + copy(bs[4:len(bs)], bstr) + return bs +} + +func (s *S) TestSendMessageWithAttributes(c *C) { + testServer.PrepareResponse(200, nil, TestSendMessageXmlOK) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + attrs := map[string]string{ + "test_attribute_name_1": "test_attribute_value_1", + } + resp, err := q.SendMessageWithAttributes("This is a test message", attrs) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + var attrsHash = md5.New() + attrsHash.Write(encodeMessageAttribute("test_attribute_name_1")) + attrsHash.Write(encodeMessageAttribute("String")) + attrsHash.Write([]byte{1}) + attrsHash.Write(encodeMessageAttribute("test_attribute_value_1")) + c.Assert(resp.MD5OfMessageAttributes, Equals, fmt.Sprintf("%x", attrsHash.Sum(nil))) + + msg := "This is a test message" + var h hash.Hash = md5.New() + h.Write([]byte(msg)) + c.Assert(resp.MD5, Equals, fmt.Sprintf("%x", h.Sum(nil))) + c.Assert(resp.Id, Equals, "5fea7756-0ea4-451a-a703-a558b933e274") + c.Assert(err, IsNil) +} + +func (s *S) TestSendMessageBatch(c *C) { + testServer.PrepareResponse(200, nil, TestSendMessageBatchXmlOk) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + + msgList := []string{"test message body 1", "test message body 2"} + resp, err := q.SendMessageBatchString(msgList) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + for idx, msg := range msgList { + var h hash.Hash = md5.New() + h.Write([]byte(msg)) + c.Assert(resp.SendMessageBatchResult[idx].MD5OfMessageBody, Equals, fmt.Sprintf("%x", h.Sum(nil))) + c.Assert(err, IsNil) + } +} + +func (s *S) TestDeleteMessageBatch(c *C) { + testServer.PrepareResponse(200, nil, TestDeleteMessageBatchXmlOK) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + + msgList := []Message{*(&Message{ReceiptHandle: "gfk0T0R0waama4fVFffkjPQrrvzMrOg0fTFk2LxT33EuB8wR0ZCFgKWyXGWFoqqpCIiprQUEhir%2F5LeGPpYTLzjqLQxyQYaQALeSNHb0us3uE84uujxpBhsDkZUQkjFFkNqBXn48xlMcVhTcI3YLH%2Bd%2BIqetIOHgBCZAPx6r%2B09dWaBXei6nbK5Ygih21DCDdAwFV68Jo8DXhb3ErEfoDqx7vyvC5nCpdwqv%2BJhU%2FTNGjNN8t51v5c%2FAXvQsAzyZVNapxUrHIt4NxRhKJ72uICcxruyE8eRXlxIVNgeNP8ZEDcw7zZU1Zw%3D%3D"}), + *(&Message{ReceiptHandle: "gfk0T0R0waama4fVFffkjKzmhMCymjQvfTFk2LxT33G4ms5subrE0deLKWSscPU1oD3J9zgeS4PQQ3U30qOumIE6AdAv3w%2F%2Fa1IXW6AqaWhGsEPaLm3Vf6IiWqdM8u5imB%2BNTwj3tQRzOWdTOePjOjPcTpRxBtXix%2BEvwJOZUma9wabv%2BSw6ZHjwmNcVDx8dZXJhVp16Bksiox%2FGrUvrVTCJRTWTLc59oHLLF8sEkKzRmGNzTDGTiV%2BYjHfQj60FD3rVaXmzTsoNxRhKJ72uIHVMGVQiAGgB%2BqAbSqfKHDQtVOmJJgkHug%3D%3D"}), + } + + resp, err := q.DeleteMessageBatch(msgList) + c.Assert(err, IsNil) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + for idx, _ := range msgList { + c.Assert(resp.DeleteMessageBatchResult[idx].Id, Equals, fmt.Sprintf("msg%d", idx+1)) + } +} + +func (s *S) TestDeleteMessageUsingReceiptHandle(c *C) { + testServer.PrepareResponse(200, nil, TestDeleteMessageUsingReceiptXmlOK) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + + msg := &Message{ReceiptHandle: "gfk0T0R0waama4fVFffkjRQrrvzMrOg0fTFk2LxT33EuB8wR0ZCFgKWyXGWFoqqpCIiprQUEhir%2F5LeGPpYTLzjqLQxyQYaQALeSNHb0us3uE84uujxpBhsDkZUQkjFFkNqBXn48xlMcVhTcI3YLH%2Bd%2BIqetIOHgBCZAPx6r%2B09dWaBXei6nbK5Ygih21DCDdAwFV68Jo8DXhb3ErEfoDqx7vyvC5nCpdwqv%2BJhU%2FTNGjNN8t51v5c%2FAXvQsAzyZVNapxUrHIt4NxRhKJ72uICcxruyE8eRXlxIVNgeNP8ZEDcw7zZU1Zw%3D%3D"} + + resp, err := q.DeleteMessageUsingReceiptHandle(msg.ReceiptHandle) + c.Assert(err, IsNil) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "d6d86b7a-74d1-4439-b43f-196a1e29cd85") +} + +func (s *S) TestReceiveMessage(c *C) { + testServer.PrepareResponse(200, nil, TestReceiveMessageXmlOK) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + resp, err := q.ReceiveMessage(5) + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(len(resp.Messages), Not(Equals), 0) + c.Assert(resp.Messages[0].MessageId, Equals, "5fea7756-0ea4-451a-a703-a558b933e274") + c.Assert(resp.Messages[0].MD5OfBody, Equals, "fafb00f5732ab283681e124bf8747ed1") + c.Assert(resp.Messages[0].ReceiptHandle, Equals, "MbZj6wDWli+JvwwJaBV+3dcjk2YW2vA3+STFFljTM8tJJg6HRG6PYSasuWXPJB+CwLj1FjgXUv1uSj1gUPAWV66FU/WeR4mq2OKpEGYWbnLmpRCJVAyeMjeU5ZBdtcQ+QEauMZc8ZRv37sIW2iJKq3M9MFx1YvV11A2x/KSbkJ0=") + c.Assert(resp.Messages[0].Body, Equals, "This is a test message") + + c.Assert(len(resp.Messages[0].Attribute), Not(Equals), 0) + + expectedAttributeResults := []struct { + Name string + Value string + }{ + {Name: "SenderId", Value: "195004372649"}, + {Name: "SentTimestamp", Value: "1238099229000"}, + {Name: "ApproximateReceiveCount", Value: "5"}, + {Name: "ApproximateFirstReceiveTimestamp", Value: "1250700979248"}, + } + + for i, expected := range expectedAttributeResults { + c.Assert(resp.Messages[0].Attribute[i].Name, Equals, expected.Name) + c.Assert(resp.Messages[0].Attribute[i].Value, Equals, expected.Value) + } + + c.Assert(len(resp.Messages[0].MessageAttribute), Not(Equals), 0) + + expectedMessageAttributeResults := []struct { + Name string + Value struct { + DataType string + BinaryValue []byte + StringValue string + + // Not yet implemented (Reserved for future use) + BinaryListValues [][]byte + StringListValues []string + } + }{ + { + Name: "CustomAttribute", + Value: struct { + DataType string + BinaryValue []byte + StringValue string + + // Not yet implemented (Reserved for future use) + BinaryListValues [][]byte + StringListValues []string + }{ + DataType: "String", + StringValue: "Testing, testing, 1, 2, 3", + }, + }, + { + Name: "BinaryCustomAttribute", + Value: struct { + DataType string + BinaryValue []byte + StringValue string + + // Not yet implemented (Reserved for future use) + BinaryListValues [][]byte + StringListValues []string + }{ + DataType: "Binary", + BinaryValue: []byte("iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAABA0lEQVQ4T72UrQ4CMRCEewhyiiBPopBgcfAUSIICB88CDhRB8hTgsCBRyJMEdUFwZJpMs/3LHQlhVdPufJ1ut03UjyKJcR5zVc4umbW87eeqvVFBjTdJwP54D+4xGXVUCGiBxoOsJOCd9IKgRnnV8wAezrnRmwGcpKtCJ8UgJBNWLFNzVAOimyqIhElXGkQ3LmQ6fKrdqaW1cixhdKVBcEOBLEwViBugVv8B1elVuLYcoTea624drcl5LW4KTRsFhQpLtVzzQKGCh2DuHI8FvdVH7vGQKEPerHRjgegKMESsXgAgWBtu5D1a9BQWCXSrzx9BvjPPkRQR6IJcQNTRV/cvkj93DqUTWzVDIQAAAABJRU5ErkJggg=="), + }, + }, + } + + for i, expected := range expectedMessageAttributeResults { + c.Assert(resp.Messages[0].MessageAttribute[i].Name, Equals, expected.Name) + c.Assert(resp.Messages[0].MessageAttribute[i].Value.DataType, Equals, expected.Value.DataType) + c.Assert(string(resp.Messages[0].MessageAttribute[i].Value.BinaryValue), Equals, string(expected.Value.BinaryValue)) + c.Assert(resp.Messages[0].MessageAttribute[i].Value.StringValue, Equals, expected.Value.StringValue) + } + + c.Assert(err, IsNil) +} + +func (s *S) TestChangeMessageVisibility(c *C) { + testServer.PrepareResponse(200, nil, TestReceiveMessageXmlOK) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + + resp1, err := q.ReceiveMessage(1) + req := testServer.WaitRequest() + + testServer.PrepareResponse(200, nil, TestChangeMessageVisibilityXmlOK) + + resp, err := q.ChangeMessageVisibility(&resp1.Messages[0], 50) + req = testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + c.Assert(req.Header["Date"], Not(Equals), "") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "6a7a282a-d013-4a59-aba9-335b0fa48bed") + c.Assert(err, IsNil) +} + +func (s *S) TestGetQueueAttributes(c *C) { + testServer.PrepareResponse(200, nil, TestGetQueueAttributesXmlOK) + + q := &Queue{s.sqs, testServer.URL + "/123456789012/testQueue/"} + + resp, err := q.GetQueueAttributes("All") + req := testServer.WaitRequest() + + c.Assert(req.Method, Equals, "GET") + c.Assert(req.URL.Path, Equals, "/123456789012/testQueue/") + + c.Assert(resp.ResponseMetadata.RequestId, Equals, "1ea71be5-b5a2-4f9d-b85a-945d8d08cd0b") + + c.Assert(len(resp.Attributes), Equals, 9) + + expectedResults := []struct { + Name string + Value string + }{ + {Name: "ReceiveMessageWaitTimeSeconds", Value: "2"}, + {Name: "VisibilityTimeout", Value: "30"}, + {Name: "ApproximateNumberOfMessages", Value: "0"}, + {Name: "ApproximateNumberOfMessagesNotVisible", Value: "0"}, + {Name: "CreatedTimestamp", Value: "1286771522"}, + {Name: "LastModifiedTimestamp", Value: "1286771522"}, + {Name: "QueueArn", Value: "arn:aws:sqs:us-east-1:123456789012:qfoo"}, + {Name: "MaximumMessageSize", Value: "8192"}, + {Name: "MessageRetentionPeriod", Value: "345600"}, + } + + for i, expected := range expectedResults { + c.Assert(resp.Attributes[i].Name, Equals, expected.Name) + c.Assert(resp.Attributes[i].Value, Equals, expected.Value) + } + + c.Assert(err, IsNil) +} diff --git a/vendor/github.com/goamz/goamz/sqs/suite_test.go b/vendor/github.com/goamz/goamz/sqs/suite_test.go new file mode 100644 index 000000000..8de1bc04f --- /dev/null +++ b/vendor/github.com/goamz/goamz/sqs/suite_test.go @@ -0,0 +1,145 @@ +package sqs + +import ( + "flag" + "fmt" + "net/http" + "net/url" + "os" + "testing" + "time" + + "github.com/goamz/goamz/aws" + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var integration = flag.Bool("i", false, "Enable integration tests") + +type SuiteI struct { + auth aws.Auth +} + +func (s *SuiteI) SetUpSuite(c *C) { + if !*integration { + c.Skip("Integration tests not enabled (-i flag)") + } + auth, err := aws.EnvAuth() + if err != nil { + c.Fatal(err.Error()) + } + s.auth = auth +} + +type HTTPSuite struct{} + +var testServer = NewTestHTTPServer("http://localhost:4455", 5e9) + +func (s *HTTPSuite) SetUpSuite(c *C) { + testServer.Start() +} + +func (s *HTTPSuite) TearDownTest(c *C) { + testServer.FlushRequests() +} + +type TestHTTPServer struct { + URL string + Timeout time.Duration + started bool + request chan *http.Request + response chan *testResponse + pending chan bool +} + +type testResponse struct { + Status int + Headers map[string]string + Body string +} + +func NewTestHTTPServer(url string, timeout time.Duration) *TestHTTPServer { + return &TestHTTPServer{URL: url, Timeout: timeout} +} + +func (s *TestHTTPServer) Start() { + if s.started { + return + } + s.started = true + + s.request = make(chan *http.Request, 64) + s.response = make(chan *testResponse, 64) + s.pending = make(chan bool, 64) + + url, _ := url.Parse(s.URL) + go func() { + err := http.ListenAndServe(url.Host, s) + if err != nil { + panic(err) + } + }() + + s.PrepareResponse(202, nil, "Nothing.") + for { + // Wait for it to be up. + resp, err := http.Get(s.URL) + if err == nil && resp.StatusCode == 202 { + break + } + fmt.Fprintf(os.Stderr, "\nWaiting for fake server to be up... ") + time.Sleep(1e8) + } + fmt.Fprintf(os.Stderr, "done\n\n") + s.WaitRequest() // Consume dummy request. +} + +// FlushRequests discards requests which were not yet consumed by WaitRequest. +func (s *TestHTTPServer) FlushRequests() { + for { + select { + case <-s.request: + default: + return + } + } +} + +func (s *TestHTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { + s.request <- req + var resp *testResponse + select { + case resp = <-s.response: + case <-time.After(s.Timeout): + fmt.Fprintf(os.Stderr, "ERROR: Timeout waiting for test to provide response\n") + resp = &testResponse{500, nil, ""} + } + if resp.Headers != nil { + h := w.Header() + for k, v := range resp.Headers { + h.Set(k, v) + } + } + if resp.Status != 0 { + w.WriteHeader(resp.Status) + } + w.Write([]byte(resp.Body)) +} + +func (s *TestHTTPServer) WaitRequest() *http.Request { + select { + case req := <-s.request: + req.ParseForm() + return req + case <-time.After(s.Timeout): + panic("Timeout waiting for goamz request") + } + panic("unreached") +} + +func (s *TestHTTPServer) PrepareResponse(status int, headers map[string]string, body string) { + s.response <- &testResponse{status, headers, body} +} diff --git a/vendor/github.com/goamz/goamz/sts/responses_test.go b/vendor/github.com/goamz/goamz/sts/responses_test.go new file mode 100644 index 000000000..b25f495e2 --- /dev/null +++ b/vendor/github.com/goamz/goamz/sts/responses_test.go @@ -0,0 +1,84 @@ +package sts_test + +var AssumeRoleResponse = ` + + + + + AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQW + LWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGd + QrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU + 9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz + +scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== + + + wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY + + 2011-07-15T23:28:33.359Z + AKIAIOSFODNN7EXAMPLE + + + arn:aws:sts::123456789012:assumed-role/demo/Bob + ARO123EXAMPLE123:Bob + + 6 + + + c6104cbe-af31-11e0-8154-cbc7ccf896c7 + + +` + +var GetFederationTokenResponse = ` + + + + + AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQW + LWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGd + QrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU + 9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz + +scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== + + + wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY + + 2011-07-15T23:28:33.359Z + AKIAIOSFODNN7EXAMPLE + + + arn:aws:sts::123456789012:federated-user/Bob + 123456789012:Bob + + 6 + + + c6104cbe-af31-11e0-8154-cbc7ccf896c7 + + +` + +var GetSessionTokenResponse = ` + + + + + AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/L + To6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3z + rkuWJOgQs8IZZaIv2BXIa2R4OlgkBN9bkUDNCJiBeb/AXlzBBko7b15fjrBs2+cTQtp + Z3CYWFXG8C5zqx37wnOE49mRl/+OtkIKGO7fAE + + + wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY + + 2011-07-11T19:55:29.611Z + AKIAIOSFODNN7EXAMPLE + + + + 58c5dbae-abef-11e0-8cfe-09039844ac7d + + +` diff --git a/vendor/github.com/goamz/goamz/sts/sts.go b/vendor/github.com/goamz/goamz/sts/sts.go new file mode 100644 index 000000000..973969223 --- /dev/null +++ b/vendor/github.com/goamz/goamz/sts/sts.go @@ -0,0 +1,273 @@ +// +// sts: This package provides types and functions to interact with the AWS STS API +// +// Depends on https://github.com/goamz/goamz +// + +package sts + +import ( + "encoding/xml" + "fmt" + "log" + "net/http" + "net/http/httputil" + "net/url" + "strconv" + "strings" + "time" + + "github.com/goamz/goamz/aws" +) + +// The STS type encapsulates operations within a specific EC2 region. +type STS struct { + aws.Auth + aws.Region + private byte // Reserve the right of using private data. +} + +// New creates a new STS Client. +// We can only use us-east for region because AWS.. +func New(auth aws.Auth, region aws.Region) *STS { + // Make sure we can run the package tests + if region.Name == "" { + return &STS{auth, region, 0} + } + return &STS{auth, aws.Regions["us-east-1"], 0} +} + +const debug = false + +// ---------------------------------------------------------------------------- +// Request dispatching logic. + +// Error encapsulates an error returned by the AWS STS API. +// +// See http://goo.gl/zDZbuQ for more details. +type Error struct { + // HTTP status code (200, 403, ...) + StatusCode int + // STS error code + Code string + // The human-oriented error message + Message string + RequestId string `xml:"RequestID"` +} + +func (err *Error) Error() string { + if err.Code == "" { + return err.Message + } + + return fmt.Sprintf("%s (%s)", err.Message, err.Code) +} + +type xmlErrors struct { + RequestId string `xml:"RequestId"` + Errors []Error `xml:"Error"` +} + +func (sts *STS) query(params map[string]string, resp interface{}) error { + params["Version"] = "2011-06-15" + + data := strings.NewReader(multimap(params).Encode()) + + hreq, err := http.NewRequest("POST", sts.Region.STSEndpoint+"/", data) + if err != nil { + return err + } + + hreq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + + token := sts.Auth.Token() + if token != "" { + hreq.Header.Set("X-Amz-Security-Token", token) + } + + signer := aws.NewV4Signer(sts.Auth, "sts", sts.Region) + signer.Sign(hreq) + + if debug { + log.Printf("%v -> {\n", hreq) + } + r, err := http.DefaultClient.Do(hreq) + + if err != nil { + log.Printf("Error calling Amazon") + return err + } + + defer r.Body.Close() + + if debug { + dump, _ := httputil.DumpResponse(r, true) + log.Printf("response:\n") + log.Printf("%v\n}\n", string(dump)) + } + if r.StatusCode != 200 { + return buildError(r) + } + err = xml.NewDecoder(r.Body).Decode(resp) + return err +} + +func buildError(r *http.Response) error { + var ( + err Error + errors xmlErrors + ) + xml.NewDecoder(r.Body).Decode(&errors) + if len(errors.Errors) > 0 { + err = errors.Errors[0] + } + + err.RequestId = errors.RequestId + err.StatusCode = r.StatusCode + if err.Message == "" { + err.Message = r.Status + } + return &err +} + +func makeParams(action string) map[string]string { + params := make(map[string]string) + params["Action"] = action + return params +} + +func multimap(p map[string]string) url.Values { + q := make(url.Values, len(p)) + for k, v := range p { + q[k] = []string{v} + } + return q +} + +// options for the AssumeRole function +// +// See http://goo.gl/Ld6Dbk for details +type AssumeRoleParams struct { + DurationSeconds int + ExternalId string + Policy string + RoleArn string + RoleSessionName string +} + +type AssumedRoleUser struct { + Arn string `xml:"Arn"` + AssumedRoleId string `xml:"AssumedRoleId"` +} + +type Credentials struct { + AccessKeyId string `xml:"AccessKeyId"` + Expiration time.Time `xml:"Expiration"` + SecretAccessKey string `xml:"SecretAccessKey"` + SessionToken string `xml:"SessionToken"` +} + +type AssumeRoleResult struct { + AssumedRoleUser AssumedRoleUser `xml:"AssumeRoleResult>AssumedRoleUser"` + Credentials Credentials `xml:"AssumeRoleResult>Credentials"` + PackedPolicySize int `xml:"AssumeRoleResult>PackedPolicySize"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// AssumeRole assumes the specified role +// +// See http://goo.gl/zDZbuQ for more details. +func (sts *STS) AssumeRole(options *AssumeRoleParams) (resp *AssumeRoleResult, err error) { + params := makeParams("AssumeRole") + + params["RoleArn"] = options.RoleArn + params["RoleSessionName"] = options.RoleSessionName + + if options.DurationSeconds != 0 { + params["DurationSeconds"] = strconv.Itoa(options.DurationSeconds) + } + if options.ExternalId != "" { + params["ExternalId"] = options.ExternalId + } + if options.Policy != "" { + params["Policy"] = options.Policy + } + + resp = new(AssumeRoleResult) + if err := sts.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// FederatedUser presents dentifiers for the federated user that is associated with the credentials. +// +// See http://goo.gl/uPtr7V for more details +type FederatedUser struct { + Arn string `xml:"Arn"` + FederatedUserId string `xml:"FederatedUserId"` +} + +// GetFederationToken wraps GetFederationToken response +// +// See http://goo.gl/Iujjeg for more details +type GetFederationTokenResult struct { + Credentials Credentials `xml:"GetFederationTokenResult>Credentials"` + FederatedUser FederatedUser `xml:"GetFederationTokenResult>FederatedUser"` + PackedPolicySize int `xml:"GetFederationTokenResult>PackedPolicySize"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// GetFederationToken returns a set of temporary credentials for an AWS account or IAM user +// +// See http://goo.gl/Iujjeg for more details +func (sts *STS) GetFederationToken(name, policy string, durationSeconds int) ( + resp *GetFederationTokenResult, err error) { + params := makeParams("GetFederationToken") + params["Name"] = name + + if durationSeconds != 0 { + params["DurationSeconds"] = strconv.Itoa(durationSeconds) + } + if policy != "" { + params["Policy"] = policy + } + + resp = new(GetFederationTokenResult) + if err := sts.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} + +// GetSessionToken wraps GetSessionToken response +// +// See http://goo.gl/v8s5Y for more details +type GetSessionTokenResult struct { + Credentials Credentials `xml:"GetSessionTokenResult>Credentials"` + RequestId string `xml:"ResponseMetadata>RequestId"` +} + +// GetSessionToken returns a set of temporary credentials for an AWS account or IAM user +// +// See http://goo.gl/v8s5Y for more details +func (sts *STS) GetSessionToken(durationSeconds int, serialnNumber, tokenCode string) ( + resp *GetSessionTokenResult, err error) { + params := makeParams("GetSessionToken") + + if durationSeconds != 0 { + params["DurationSeconds"] = strconv.Itoa(durationSeconds) + } + if serialnNumber != "" { + params["SerialNumber"] = serialnNumber + } + if tokenCode != "" { + params["TokenCode"] = tokenCode + } + + resp = new(GetSessionTokenResult) + if err := sts.query(params, resp); err != nil { + return nil, err + } + return resp, nil +} diff --git a/vendor/github.com/goamz/goamz/sts/sts_test.go b/vendor/github.com/goamz/goamz/sts/sts_test.go new file mode 100644 index 000000000..354c6272f --- /dev/null +++ b/vendor/github.com/goamz/goamz/sts/sts_test.go @@ -0,0 +1,151 @@ +package sts_test + +import ( + "testing" + "time" + + . "gopkg.in/check.v1" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/sts" + "github.com/goamz/goamz/testutil" +) + +func Test(t *testing.T) { + TestingT(t) +} + +var _ = Suite(&S{}) + +type S struct { + sts *sts.STS +} + +var testServer = testutil.NewHTTPServer() + +var mockTest bool + +func (s *S) SetUpSuite(c *C) { + testServer.Start() + auth := aws.Auth{AccessKey: "abc", SecretKey: "123"} + s.sts = sts.New(auth, aws.Region{STSEndpoint: testServer.URL}) +} + +func (s *S) TearDownTest(c *C) { + testServer.Flush() +} + +func (s *S) TestAssumeRole(c *C) { + testServer.Response(200, nil, AssumeRoleResponse) + request := &sts.AssumeRoleParams{ + DurationSeconds: 3600, + ExternalId: "123ABC", + Policy: `{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1","Effect":"Allow","Action":"s3:*","Resource":"*"}]}`, + RoleArn: "arn:aws:iam::123456789012:role/demo", + RoleSessionName: "Bob", + } + resp, err := s.sts.AssumeRole(request) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2011-06-15") + c.Assert(values.Get("Action"), Equals, "AssumeRole") + c.Assert(values.Get("DurationSeconds"), Equals, "3600") + c.Assert(values.Get("ExternalId"), Equals, "123ABC") + c.Assert(values.Get("Policy"), Equals, `{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1","Effect":"Allow","Action":"s3:*","Resource":"*"}]}`) + c.Assert(values.Get("RoleArn"), Equals, "arn:aws:iam::123456789012:role/demo") + c.Assert(values.Get("RoleSessionName"), Equals, "Bob") + // Response test + exp, _ := time.Parse(time.RFC3339, "2011-07-15T23:28:33.359Z") + c.Assert(resp.RequestId, Equals, "c6104cbe-af31-11e0-8154-cbc7ccf896c7") + c.Assert(resp.PackedPolicySize, Equals, 6) + c.Assert(resp.AssumedRoleUser, DeepEquals, sts.AssumedRoleUser{ + Arn: "arn:aws:sts::123456789012:assumed-role/demo/Bob", + AssumedRoleId: "ARO123EXAMPLE123:Bob", + }) + c.Assert(resp.Credentials, DeepEquals, sts.Credentials{ + SessionToken: ` + AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQW + LWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGd + QrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU + 9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz + +scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== + `, + SecretAccessKey: ` + wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY + `, + AccessKeyId: "AKIAIOSFODNN7EXAMPLE", + Expiration: exp, + }) + +} + +func (s *S) TestGetFederationToken(c *C) { + testServer.Response(200, nil, GetFederationTokenResponse) + resp, err := s.sts.GetFederationToken( + "Bob", + `{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1","Effect":"Allow","Action":"s3:*","Resource":"*"}]}`, + 3600, + ) + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2011-06-15") + c.Assert(values.Get("Action"), Equals, "GetFederationToken") + c.Assert(values.Get("DurationSeconds"), Equals, "3600") + c.Assert(values.Get("Policy"), Equals, `{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1","Effect":"Allow","Action":"s3:*","Resource":"*"}]}`) + c.Assert(values.Get("Name"), Equals, "Bob") + // Response test + exp, _ := time.Parse(time.RFC3339, "2011-07-15T23:28:33.359Z") + c.Assert(resp.RequestId, Equals, "c6104cbe-af31-11e0-8154-cbc7ccf896c7") + c.Assert(resp.PackedPolicySize, Equals, 6) + c.Assert(resp.FederatedUser, DeepEquals, sts.FederatedUser{ + Arn: "arn:aws:sts::123456789012:federated-user/Bob", + FederatedUserId: "123456789012:Bob", + }) + c.Assert(resp.Credentials, DeepEquals, sts.Credentials{ + SessionToken: ` + AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQW + LWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGd + QrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU + 9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz + +scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== + `, + SecretAccessKey: ` + wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY + `, + AccessKeyId: "AKIAIOSFODNN7EXAMPLE", + Expiration: exp, + }) + +} + +func (s *S) TestGetSessionToken(c *C) { + testServer.Response(200, nil, GetSessionTokenResponse) + resp, err := s.sts.GetSessionToken(3600, "YourMFADeviceSerialNumber", "123456") + c.Assert(err, IsNil) + values := testServer.WaitRequest().PostForm + // Post request test + c.Assert(values.Get("Version"), Equals, "2011-06-15") + c.Assert(values.Get("Action"), Equals, "GetSessionToken") + c.Assert(values.Get("DurationSeconds"), Equals, "3600") + c.Assert(values.Get("SerialNumber"), Equals, "YourMFADeviceSerialNumber") + c.Assert(values.Get("TokenCode"), Equals, "123456") + // Response test + exp, _ := time.Parse(time.RFC3339, "2011-07-11T19:55:29.611Z") + c.Assert(resp.RequestId, Equals, "58c5dbae-abef-11e0-8cfe-09039844ac7d") + c.Assert(resp.Credentials, DeepEquals, sts.Credentials{ + SessionToken: ` + AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKwRcOIfrRh3c/L + To6UDdyJwOOvEVPvLXCrrrUtdnniCEXAMPLE/IvU1dYUg2RVAJBanLiHb4IgRmpRV3z + rkuWJOgQs8IZZaIv2BXIa2R4OlgkBN9bkUDNCJiBeb/AXlzBBko7b15fjrBs2+cTQtp + Z3CYWFXG8C5zqx37wnOE49mRl/+OtkIKGO7fAE + `, + SecretAccessKey: ` + wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY + `, + AccessKeyId: "AKIAIOSFODNN7EXAMPLE", + Expiration: exp, + }) + +} diff --git a/vendor/github.com/goamz/goamz/testutil/http.go b/vendor/github.com/goamz/goamz/testutil/http.go new file mode 100644 index 000000000..ccc570cdd --- /dev/null +++ b/vendor/github.com/goamz/goamz/testutil/http.go @@ -0,0 +1,180 @@ +package testutil + +import ( + "bytes" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/url" + "os" + "time" +) + +type HTTPServer struct { + URL string + Timeout time.Duration + started bool + request chan *http.Request + response chan ResponseFunc +} + +type Response struct { + Status int + Headers map[string]string + Body string +} + +var DefaultClient = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + }, +} + +func NewHTTPServer() *HTTPServer { + return &HTTPServer{URL: "http://localhost:4444", Timeout: 5 * time.Second} +} + +type ResponseFunc func(path string) Response + +func (s *HTTPServer) Start() { + if s.started { + return + } + s.started = true + s.request = make(chan *http.Request, 1024) + s.response = make(chan ResponseFunc, 1024) + u, err := url.Parse(s.URL) + if err != nil { + panic(err) + } + l, err := net.Listen("tcp", u.Host) + if err != nil { + panic(err) + } + go http.Serve(l, s) + + s.Response(203, nil, "") + for { + // Wait for it to be up. + resp, err := http.Get(s.URL) + if err == nil && resp.StatusCode == 203 { + break + } + time.Sleep(1e8) + } + s.WaitRequest() // Consume dummy request. +} + +// Flush discards all pending requests and responses. +func (s *HTTPServer) Flush() { + for { + select { + case <-s.request: + case <-s.response: + default: + return + } + } +} + +func body(req *http.Request) string { + data, err := ioutil.ReadAll(req.Body) + if err != nil { + panic(err) + } + return string(data) +} + +func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { + req.ParseMultipartForm(1e6) + data, err := ioutil.ReadAll(req.Body) + if err != nil { + panic(err) + } + req.Body = ioutil.NopCloser(bytes.NewBuffer(data)) + s.request <- req + var resp Response + select { + case respFunc := <-s.response: + resp = respFunc(req.URL.Path) + case <-time.After(s.Timeout): + const msg = "ERROR: Timeout waiting for test to prepare a response\n" + fmt.Fprintf(os.Stderr, msg) + resp = Response{500, nil, msg} + } + if resp.Headers != nil { + h := w.Header() + for k, v := range resp.Headers { + h.Set(k, v) + } + } + if resp.Status != 0 { + w.WriteHeader(resp.Status) + } + w.Write([]byte(resp.Body)) +} + +// WaitRequests returns the next n requests made to the http server from +// the queue. If not enough requests were previously made, it waits until +// the timeout value for them to be made. +func (s *HTTPServer) WaitRequests(n int) []*http.Request { + reqs := make([]*http.Request, 0, n) + for i := 0; i < n; i++ { + select { + case req := <-s.request: + reqs = append(reqs, req) + case <-time.After(s.Timeout): + panic("Timeout waiting for request") + } + } + return reqs +} + +// WaitRequest returns the next request made to the http server from +// the queue. If no requests were previously made, it waits until the +// timeout value for one to be made. +func (s *HTTPServer) WaitRequest() *http.Request { + return s.WaitRequests(1)[0] +} + +// ResponseFunc prepares the test server to respond the following n +// requests using f to build each response. +func (s *HTTPServer) ResponseFunc(n int, f ResponseFunc) { + for i := 0; i < n; i++ { + s.response <- f + } +} + +// ResponseMap maps request paths to responses. +type ResponseMap map[string]Response + +// ResponseMap prepares the test server to respond the following n +// requests using the m to obtain the responses. +func (s *HTTPServer) ResponseMap(n int, m ResponseMap) { + f := func(path string) Response { + for rpath, resp := range m { + if rpath == path { + return resp + } + } + body := "Path not found in response map: " + path + return Response{Status: 500, Body: body} + } + s.ResponseFunc(n, f) +} + +// Responses prepares the test server to respond the following n requests +// using the provided response parameters. +func (s *HTTPServer) Responses(n int, status int, headers map[string]string, body string) { + f := func(path string) Response { + return Response{status, headers, body} + } + s.ResponseFunc(n, f) +} + +// Response prepares the test server to respond the following request +// using the provided response parameters. +func (s *HTTPServer) Response(status int, headers map[string]string, body string) { + s.Responses(1, status, headers, body) +} diff --git a/vendor/github.com/goamz/goamz/testutil/suite.go b/vendor/github.com/goamz/goamz/testutil/suite.go new file mode 100644 index 000000000..f4519aa4e --- /dev/null +++ b/vendor/github.com/goamz/goamz/testutil/suite.go @@ -0,0 +1,31 @@ +package testutil + +import ( + "flag" + + "github.com/goamz/goamz/aws" + . "gopkg.in/check.v1" +) + +// Amazon must be used by all tested packages to determine whether to +// run functional tests against the real AWS servers. +var Amazon bool + +func init() { + flag.BoolVar(&Amazon, "amazon", false, "Enable tests against amazon server") +} + +type LiveSuite struct { + auth aws.Auth +} + +func (s *LiveSuite) SetUpSuite(c *C) { + if !Amazon { + c.Skip("amazon tests not enabled (-amazon flag)") + } + auth, err := aws.EnvAuth() + if err != nil { + c.Fatal(err.Error()) + } + s.auth = auth +} diff --git a/vendor/github.com/golang/freetype/cmd/print-glyph-points/main.c b/vendor/github.com/golang/freetype/cmd/print-glyph-points/main.c new file mode 100644 index 000000000..6e821e892 --- /dev/null +++ b/vendor/github.com/golang/freetype/cmd/print-glyph-points/main.c @@ -0,0 +1,87 @@ +/* +gcc main.c -I/usr/include/freetype2 -lfreetype && ./a.out 12 ../../testdata/luxisr.ttf with_hinting +*/ + +#include +#include +#include FT_FREETYPE_H + +void usage(char** argv) { + fprintf(stderr, "usage: %s font_size font_file [with_hinting|sans_hinting]\n", argv[0]); +} + +int main(int argc, char** argv) { + FT_Error error; + FT_Library library; + FT_Face face; + FT_Glyph_Metrics* m; + FT_Outline* o; + FT_Int major, minor, patch; + int i, j, font_size, no_hinting; + + if (argc != 4) { + usage(argv); + return 1; + } + font_size = atoi(argv[1]); + if (font_size <= 0) { + fprintf(stderr, "invalid font_size\n"); + usage(argv); + return 1; + } + if (!strcmp(argv[3], "with_hinting")) { + no_hinting = 0; + } else if (!strcmp(argv[3], "sans_hinting")) { + no_hinting = 1; + } else { + fprintf(stderr, "neither \"with_hinting\" nor \"sans_hinting\"\n"); + usage(argv); + return 1; + }; + error = FT_Init_FreeType(&library); + if (error) { + fprintf(stderr, "FT_Init_FreeType: error #%d\n", error); + return 1; + } + FT_Library_Version(library, &major, &minor, &patch); + printf("freetype version %d.%d.%d\n", major, minor, patch); + error = FT_New_Face(library, argv[2], 0, &face); + if (error) { + fprintf(stderr, "FT_New_Face: error #%d\n", error); + return 1; + } + error = FT_Set_Char_Size(face, 0, font_size*64, 0, 0); + if (error) { + fprintf(stderr, "FT_Set_Char_Size: error #%d\n", error); + return 1; + } + for (i = 0; i < face->num_glyphs; i++) { + error = FT_Load_Glyph(face, i, no_hinting ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT); + if (error) { + fprintf(stderr, "FT_Load_Glyph: glyph %d: error #%d\n", i, error); + return 1; + } + if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) { + fprintf(stderr, "glyph format for glyph %d is not FT_GLYPH_FORMAT_OUTLINE\n", i); + return 1; + } + m = &face->glyph->metrics; + /* Print what Go calls the AdvanceWidth, and then: XMin, YMin, XMax, YMax. */ + printf("%ld %ld %ld %ld %ld;", + m->horiAdvance, + m->horiBearingX, + m->horiBearingY - m->height, + m->horiBearingX + m->width, + m->horiBearingY); + /* Print the glyph points. */ + o = &face->glyph->outline; + for (j = 0; j < o->n_points; j++) { + if (j != 0) { + printf(", "); + } + printf("%ld %ld %d", o->points[j].x, o->points[j].y, o->tags[j] & 0x01); + } + printf("\n"); + } + return 0; +} diff --git a/vendor/github.com/golang/freetype/example/capjoin/main.go b/vendor/github.com/golang/freetype/example/capjoin/main.go new file mode 100644 index 000000000..71f3356c7 --- /dev/null +++ b/vendor/github.com/golang/freetype/example/capjoin/main.go @@ -0,0 +1,85 @@ +// Copyright 2016 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +// +build example +// +// This build tag means that "go install github.com/golang/freetype/..." +// doesn't install this example program. Use "go run main.go" to run it or "go +// install -tags=example" to install it. + +package main + +import ( + "bufio" + "fmt" + "image" + "image/color" + "image/draw" + "image/png" + "log" + "os" + + "github.com/golang/freetype/raster" + "golang.org/x/image/math/fixed" +) + +func main() { + const ( + w = 400 + h = 400 + ) + r := raster.NewRasterizer(w, h) + r.UseNonZeroWinding = true + + cjs := []struct { + c raster.Capper + j raster.Joiner + }{ + {raster.RoundCapper, raster.RoundJoiner}, + {raster.ButtCapper, raster.BevelJoiner}, + {raster.SquareCapper, raster.BevelJoiner}, + } + + for i, cj := range cjs { + var path raster.Path + path.Start(fixed.P(30+100*i, 30+120*i)) + path.Add1(fixed.P(180+100*i, 80+120*i)) + path.Add1(fixed.P(50+100*i, 130+120*i)) + raster.Stroke(r, path, fixed.I(20), cj.c, cj.j) + } + + rgba := image.NewRGBA(image.Rect(0, 0, w, h)) + draw.Draw(rgba, rgba.Bounds(), image.Black, image.Point{}, draw.Src) + p := raster.NewRGBAPainter(rgba) + p.SetColor(color.RGBA{0x7f, 0x7f, 0x7f, 0xff}) + r.Rasterize(p) + + white := color.RGBA{0xff, 0xff, 0xff, 0xff} + for i := range cjs { + rgba.SetRGBA(30+100*i, 30+120*i, white) + rgba.SetRGBA(180+100*i, 80+120*i, white) + rgba.SetRGBA(50+100*i, 130+120*i, white) + } + + // Save that RGBA image to disk. + outFile, err := os.Create("out.png") + if err != nil { + log.Println(err) + os.Exit(1) + } + defer outFile.Close() + b := bufio.NewWriter(outFile) + err = png.Encode(b, rgba) + if err != nil { + log.Println(err) + os.Exit(1) + } + err = b.Flush() + if err != nil { + log.Println(err) + os.Exit(1) + } + fmt.Println("Wrote out.png OK.") +} diff --git a/vendor/github.com/golang/freetype/example/drawer/main.go b/vendor/github.com/golang/freetype/example/drawer/main.go new file mode 100644 index 000000000..d26d066d9 --- /dev/null +++ b/vendor/github.com/golang/freetype/example/drawer/main.go @@ -0,0 +1,158 @@ +// Copyright 2015 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +// +build example +// +// This build tag means that "go install github.com/golang/freetype/..." +// doesn't install this example program. Use "go run main.go" to run it or "go +// install -tags=example" to install it. + +package main + +import ( + "bufio" + "flag" + "fmt" + "image" + "image/color" + "image/draw" + "image/png" + "io/ioutil" + "log" + "math" + "os" + + "github.com/golang/freetype/truetype" + "golang.org/x/image/font" + "golang.org/x/image/math/fixed" +) + +var ( + dpi = flag.Float64("dpi", 72, "screen resolution in Dots Per Inch") + fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename of the ttf font") + hinting = flag.String("hinting", "none", "none | full") + size = flag.Float64("size", 12, "font size in points") + spacing = flag.Float64("spacing", 1.5, "line spacing (e.g. 2 means double spaced)") + wonb = flag.Bool("whiteonblack", false, "white text on a black background") +) + +const title = "Jabberwocky" + +var text = []string{ + "’Twas brillig, and the slithy toves", + "Did gyre and gimble in the wabe;", + "All mimsy were the borogoves,", + "And the mome raths outgrabe.", + "", + "“Beware the Jabberwock, my son!", + "The jaws that bite, the claws that catch!", + "Beware the Jubjub bird, and shun", + "The frumious Bandersnatch!”", + "", + "He took his vorpal sword in hand:", + "Long time the manxome foe he sought—", + "So rested he by the Tumtum tree,", + "And stood awhile in thought.", + "", + "And as in uffish thought he stood,", + "The Jabberwock, with eyes of flame,", + "Came whiffling through the tulgey wood,", + "And burbled as it came!", + "", + "One, two! One, two! and through and through", + "The vorpal blade went snicker-snack!", + "He left it dead, and with its head", + "He went galumphing back.", + "", + "“And hast thou slain the Jabberwock?", + "Come to my arms, my beamish boy!", + "O frabjous day! Callooh! Callay!”", + "He chortled in his joy.", + "", + "’Twas brillig, and the slithy toves", + "Did gyre and gimble in the wabe;", + "All mimsy were the borogoves,", + "And the mome raths outgrabe.", +} + +func main() { + flag.Parse() + + // Read the font data. + fontBytes, err := ioutil.ReadFile(*fontfile) + if err != nil { + log.Println(err) + return + } + f, err := truetype.Parse(fontBytes) + if err != nil { + log.Println(err) + return + } + + // Draw the background and the guidelines. + fg, bg := image.Black, image.White + ruler := color.RGBA{0xdd, 0xdd, 0xdd, 0xff} + if *wonb { + fg, bg = image.White, image.Black + ruler = color.RGBA{0x22, 0x22, 0x22, 0xff} + } + const imgW, imgH = 640, 480 + rgba := image.NewRGBA(image.Rect(0, 0, imgW, imgH)) + draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) + for i := 0; i < 200; i++ { + rgba.Set(10, 10+i, ruler) + rgba.Set(10+i, 10, ruler) + } + + // Draw the text. + h := font.HintingNone + switch *hinting { + case "full": + h = font.HintingFull + } + d := &font.Drawer{ + Dst: rgba, + Src: fg, + Face: truetype.NewFace(f, &truetype.Options{ + Size: *size, + DPI: *dpi, + Hinting: h, + }), + } + y := 10 + int(math.Ceil(*size**dpi/72)) + dy := int(math.Ceil(*size * *spacing * *dpi / 72)) + d.Dot = fixed.Point26_6{ + X: (fixed.I(imgW) - d.MeasureString(title)) / 2, + Y: fixed.I(y), + } + d.DrawString(title) + y += dy + for _, s := range text { + d.Dot = fixed.P(10, y) + d.DrawString(s) + y += dy + } + + // Save that RGBA image to disk. + outFile, err := os.Create("out.png") + if err != nil { + log.Println(err) + os.Exit(1) + } + defer outFile.Close() + b := bufio.NewWriter(outFile) + err = png.Encode(b, rgba) + if err != nil { + log.Println(err) + os.Exit(1) + } + err = b.Flush() + if err != nil { + log.Println(err) + os.Exit(1) + } + fmt.Println("Wrote out.png OK.") +} diff --git a/vendor/github.com/golang/freetype/example/freetype/main.go b/vendor/github.com/golang/freetype/example/freetype/main.go new file mode 100644 index 000000000..dfbde9a2f --- /dev/null +++ b/vendor/github.com/golang/freetype/example/freetype/main.go @@ -0,0 +1,150 @@ +// Copyright 2010 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +// +build example +// +// This build tag means that "go install github.com/golang/freetype/..." +// doesn't install this example program. Use "go run main.go" to run it or "go +// install -tags=example" to install it. + +package main + +import ( + "bufio" + "flag" + "fmt" + "image" + "image/color" + "image/draw" + "image/png" + "io/ioutil" + "log" + "os" + + "github.com/golang/freetype" + "golang.org/x/image/font" +) + +var ( + dpi = flag.Float64("dpi", 72, "screen resolution in Dots Per Inch") + fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename of the ttf font") + hinting = flag.String("hinting", "none", "none | full") + size = flag.Float64("size", 12, "font size in points") + spacing = flag.Float64("spacing", 1.5, "line spacing (e.g. 2 means double spaced)") + wonb = flag.Bool("whiteonblack", false, "white text on a black background") +) + +var text = []string{ + "’Twas brillig, and the slithy toves", + "Did gyre and gimble in the wabe;", + "All mimsy were the borogoves,", + "And the mome raths outgrabe.", + "", + "“Beware the Jabberwock, my son!", + "The jaws that bite, the claws that catch!", + "Beware the Jubjub bird, and shun", + "The frumious Bandersnatch!”", + "", + "He took his vorpal sword in hand:", + "Long time the manxome foe he sought—", + "So rested he by the Tumtum tree,", + "And stood awhile in thought.", + "", + "And as in uffish thought he stood,", + "The Jabberwock, with eyes of flame,", + "Came whiffling through the tulgey wood,", + "And burbled as it came!", + "", + "One, two! One, two! and through and through", + "The vorpal blade went snicker-snack!", + "He left it dead, and with its head", + "He went galumphing back.", + "", + "“And hast thou slain the Jabberwock?", + "Come to my arms, my beamish boy!", + "O frabjous day! Callooh! Callay!”", + "He chortled in his joy.", + "", + "’Twas brillig, and the slithy toves", + "Did gyre and gimble in the wabe;", + "All mimsy were the borogoves,", + "And the mome raths outgrabe.", +} + +func main() { + flag.Parse() + + // Read the font data. + fontBytes, err := ioutil.ReadFile(*fontfile) + if err != nil { + log.Println(err) + return + } + f, err := freetype.ParseFont(fontBytes) + if err != nil { + log.Println(err) + return + } + + // Initialize the context. + fg, bg := image.Black, image.White + ruler := color.RGBA{0xdd, 0xdd, 0xdd, 0xff} + if *wonb { + fg, bg = image.White, image.Black + ruler = color.RGBA{0x22, 0x22, 0x22, 0xff} + } + rgba := image.NewRGBA(image.Rect(0, 0, 640, 480)) + draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) + c := freetype.NewContext() + c.SetDPI(*dpi) + c.SetFont(f) + c.SetFontSize(*size) + c.SetClip(rgba.Bounds()) + c.SetDst(rgba) + c.SetSrc(fg) + switch *hinting { + default: + c.SetHinting(font.HintingNone) + case "full": + c.SetHinting(font.HintingFull) + } + + // Draw the guidelines. + for i := 0; i < 200; i++ { + rgba.Set(10, 10+i, ruler) + rgba.Set(10+i, 10, ruler) + } + + // Draw the text. + pt := freetype.Pt(10, 10+int(c.PointToFixed(*size)>>6)) + for _, s := range text { + _, err = c.DrawString(s, pt) + if err != nil { + log.Println(err) + return + } + pt.Y += c.PointToFixed(*size * *spacing) + } + + // Save that RGBA image to disk. + outFile, err := os.Create("out.png") + if err != nil { + log.Println(err) + os.Exit(1) + } + defer outFile.Close() + b := bufio.NewWriter(outFile) + err = png.Encode(b, rgba) + if err != nil { + log.Println(err) + os.Exit(1) + } + err = b.Flush() + if err != nil { + log.Println(err) + os.Exit(1) + } + fmt.Println("Wrote out.png OK.") +} diff --git a/vendor/github.com/golang/freetype/example/gamma/main.go b/vendor/github.com/golang/freetype/example/gamma/main.go new file mode 100644 index 000000000..cdd50bc3b --- /dev/null +++ b/vendor/github.com/golang/freetype/example/gamma/main.go @@ -0,0 +1,86 @@ +// Copyright 2010 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +// +build example +// +// This build tag means that "go install github.com/golang/freetype/..." +// doesn't install this example program. Use "go run main.go" to run it or "go +// install -tags=example" to install it. + +package main + +import ( + "bufio" + "fmt" + "image" + "image/draw" + "image/png" + "log" + "os" + + "github.com/golang/freetype/raster" + "golang.org/x/image/math/fixed" +) + +func p(x, y int) fixed.Point26_6 { + return fixed.Point26_6{ + X: fixed.Int26_6(x * 64), + Y: fixed.Int26_6(y * 64), + } +} + +func main() { + // Draw a rounded corner that is one pixel wide. + r := raster.NewRasterizer(50, 50) + r.Start(p(5, 5)) + r.Add1(p(5, 25)) + r.Add2(p(5, 45), p(25, 45)) + r.Add1(p(45, 45)) + r.Add1(p(45, 44)) + r.Add1(p(26, 44)) + r.Add2(p(6, 44), p(6, 24)) + r.Add1(p(6, 5)) + r.Add1(p(5, 5)) + + // Rasterize that curve multiple times at different gammas. + const ( + w = 600 + h = 200 + ) + rgba := image.NewRGBA(image.Rect(0, 0, w, h)) + draw.Draw(rgba, image.Rect(0, 0, w, h/2), image.Black, image.ZP, draw.Src) + draw.Draw(rgba, image.Rect(0, h/2, w, h), image.White, image.ZP, draw.Src) + mask := image.NewAlpha(image.Rect(0, 0, 50, 50)) + painter := raster.NewAlphaSrcPainter(mask) + gammas := []float64{1.0 / 10.0, 1.0 / 3.0, 1.0 / 2.0, 2.0 / 3.0, 4.0 / 5.0, 1.0, 5.0 / 4.0, 3.0 / 2.0, 2.0, 3.0, 10.0} + for i, g := range gammas { + draw.Draw(mask, mask.Bounds(), image.Transparent, image.ZP, draw.Src) + r.Rasterize(raster.NewGammaCorrectionPainter(painter, g)) + x, y := 50*i+25, 25 + draw.DrawMask(rgba, image.Rect(x, y, x+50, y+50), image.White, image.ZP, mask, image.ZP, draw.Over) + y += 100 + draw.DrawMask(rgba, image.Rect(x, y, x+50, y+50), image.Black, image.ZP, mask, image.ZP, draw.Over) + } + + // Save that RGBA image to disk. + outFile, err := os.Create("out.png") + if err != nil { + log.Println(err) + os.Exit(1) + } + defer outFile.Close() + b := bufio.NewWriter(outFile) + err = png.Encode(b, rgba) + if err != nil { + log.Println(err) + os.Exit(1) + } + err = b.Flush() + if err != nil { + log.Println(err) + os.Exit(1) + } + fmt.Println("Wrote out.png OK.") +} diff --git a/vendor/github.com/golang/freetype/example/raster/main.go b/vendor/github.com/golang/freetype/example/raster/main.go new file mode 100644 index 000000000..3e572e1c5 --- /dev/null +++ b/vendor/github.com/golang/freetype/example/raster/main.go @@ -0,0 +1,185 @@ +// Copyright 2010 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +// +build example +// +// This build tag means that "go install github.com/golang/freetype/..." +// doesn't install this example program. Use "go run main.go" to run it or "go +// install -tags=example" to install it. + +package main + +import ( + "bufio" + "fmt" + "image" + "image/color" + "image/draw" + "image/png" + "log" + "os" + + "github.com/golang/freetype/raster" + "golang.org/x/image/math/fixed" +) + +type node struct { + x, y, degree int +} + +// These contours "outside" and "inside" are from the 'A' glyph from the Droid +// Serif Regular font. + +var outside = []node{ + node{414, 489, 1}, + node{336, 274, 2}, + node{327, 250, 0}, + node{322, 226, 2}, + node{317, 203, 0}, + node{317, 186, 2}, + node{317, 134, 0}, + node{350, 110, 2}, + node{384, 86, 0}, + node{453, 86, 1}, + node{500, 86, 1}, + node{500, 0, 1}, + node{0, 0, 1}, + node{0, 86, 1}, + node{39, 86, 2}, + node{69, 86, 0}, + node{90, 92, 2}, + node{111, 99, 0}, + node{128, 117, 2}, + node{145, 135, 0}, + node{160, 166, 2}, + node{176, 197, 0}, + node{195, 246, 1}, + node{649, 1462, 1}, + node{809, 1462, 1}, + node{1272, 195, 2}, + node{1284, 163, 0}, + node{1296, 142, 2}, + node{1309, 121, 0}, + node{1326, 108, 2}, + node{1343, 96, 0}, + node{1365, 91, 2}, + node{1387, 86, 0}, + node{1417, 86, 1}, + node{1444, 86, 1}, + node{1444, 0, 1}, + node{881, 0, 1}, + node{881, 86, 1}, + node{928, 86, 2}, + node{1051, 86, 0}, + node{1051, 184, 2}, + node{1051, 201, 0}, + node{1046, 219, 2}, + node{1042, 237, 0}, + node{1034, 260, 1}, + node{952, 489, 1}, + node{414, 489, -1}, +} + +var inside = []node{ + node{686, 1274, 1}, + node{453, 592, 1}, + node{915, 592, 1}, + node{686, 1274, -1}, +} + +func p(n node) fixed.Point26_6 { + x, y := 20+n.x/4, 380-n.y/4 + return fixed.Point26_6{ + X: fixed.Int26_6(x << 6), + Y: fixed.Int26_6(y << 6), + } +} + +func contour(r *raster.Rasterizer, ns []node) { + if len(ns) == 0 { + return + } + i := 0 + r.Start(p(ns[i])) + for { + switch ns[i].degree { + case -1: + // -1 signifies end-of-contour. + return + case 1: + i += 1 + r.Add1(p(ns[i])) + case 2: + i += 2 + r.Add2(p(ns[i-1]), p(ns[i])) + default: + panic("bad degree") + } + } +} + +func showNodes(m *image.RGBA, ns []node) { + for _, n := range ns { + p := p(n) + x, y := int(p.X)/64, int(p.Y)/64 + if !(image.Point{x, y}).In(m.Bounds()) { + continue + } + var c color.Color + switch n.degree { + case 0: + c = color.RGBA{0, 255, 255, 255} + case 1: + c = color.RGBA{255, 0, 0, 255} + case 2: + c = color.RGBA{255, 0, 0, 255} + } + if c != nil { + m.Set(x, y, c) + } + } +} + +func main() { + // Rasterize the contours to a mask image. + const ( + w = 400 + h = 400 + ) + r := raster.NewRasterizer(w, h) + contour(r, outside) + contour(r, inside) + mask := image.NewAlpha(image.Rect(0, 0, w, h)) + p := raster.NewAlphaSrcPainter(mask) + r.Rasterize(p) + + // Draw the mask image (in gray) onto an RGBA image. + rgba := image.NewRGBA(image.Rect(0, 0, w, h)) + gray := image.NewUniform(color.Alpha{0x1f}) + draw.Draw(rgba, rgba.Bounds(), image.Black, image.ZP, draw.Src) + draw.DrawMask(rgba, rgba.Bounds(), gray, image.ZP, mask, image.ZP, draw.Over) + showNodes(rgba, outside) + showNodes(rgba, inside) + + // Save that RGBA image to disk. + outFile, err := os.Create("out.png") + if err != nil { + log.Println(err) + os.Exit(1) + } + defer outFile.Close() + b := bufio.NewWriter(outFile) + err = png.Encode(b, rgba) + if err != nil { + log.Println(err) + os.Exit(1) + } + err = b.Flush() + if err != nil { + log.Println(err) + os.Exit(1) + } + fmt.Println("Wrote out.png OK.") +} diff --git a/vendor/github.com/golang/freetype/example/round/main.go b/vendor/github.com/golang/freetype/example/round/main.go new file mode 100644 index 000000000..2920e8335 --- /dev/null +++ b/vendor/github.com/golang/freetype/example/round/main.go @@ -0,0 +1,110 @@ +// Copyright 2010 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +// +build example +// +// This build tag means that "go install github.com/golang/freetype/..." +// doesn't install this example program. Use "go run main.go" to run it or "go +// install -tags=example" to install it. + +// This program visualizes the quadratic approximation to the circle, used to +// implement round joins when stroking paths. The approximation is used in the +// stroking code for arcs between 0 and 45 degrees, but is visualized here +// between 0 and 90 degrees. The discrepancy between the approximation and the +// true circle is clearly visible at angles above 65 degrees. +package main + +import ( + "bufio" + "fmt" + "image" + "image/color" + "image/draw" + "image/png" + "log" + "math" + "os" + + "github.com/golang/freetype/raster" + "golang.org/x/image/math/fixed" +) + +// pDot returns the dot product p·q. +func pDot(p, q fixed.Point26_6) fixed.Int52_12 { + px, py := int64(p.X), int64(p.Y) + qx, qy := int64(q.X), int64(q.Y) + return fixed.Int52_12(px*qx + py*qy) +} + +func main() { + const ( + n = 17 + r = 64 * 80 + ) + s := fixed.Int26_6(r * math.Sqrt(2) / 2) + t := fixed.Int26_6(r * math.Tan(math.Pi/8)) + + m := image.NewRGBA(image.Rect(0, 0, 800, 600)) + draw.Draw(m, m.Bounds(), image.NewUniform(color.RGBA{63, 63, 63, 255}), image.ZP, draw.Src) + mp := raster.NewRGBAPainter(m) + mp.SetColor(image.Black) + z := raster.NewRasterizer(800, 600) + + for i := 0; i < n; i++ { + cx := fixed.Int26_6(6400 + 12800*(i%4)) + cy := fixed.Int26_6(640 + 8000*(i/4)) + c := fixed.Point26_6{X: cx, Y: cy} + theta := math.Pi * (0.5 + 0.5*float64(i)/(n-1)) + dx := fixed.Int26_6(r * math.Cos(theta)) + dy := fixed.Int26_6(r * math.Sin(theta)) + d := fixed.Point26_6{X: dx, Y: dy} + // Draw a quarter-circle approximated by two quadratic segments, + // with each segment spanning 45 degrees. + z.Start(c) + z.Add1(c.Add(fixed.Point26_6{X: r, Y: 0})) + z.Add2(c.Add(fixed.Point26_6{X: r, Y: t}), c.Add(fixed.Point26_6{X: s, Y: s})) + z.Add2(c.Add(fixed.Point26_6{X: t, Y: r}), c.Add(fixed.Point26_6{X: 0, Y: r})) + // Add another quadratic segment whose angle ranges between 0 and 90 + // degrees. For an explanation of the magic constants 128, 150, 181 and + // 256, read the comments in the freetype/raster package. + dot := 256 * pDot(d, fixed.Point26_6{X: 0, Y: r}) / (r * r) + multiple := fixed.Int26_6(150-(150-128)*(dot-181)/(256-181)) >> 2 + z.Add2(c.Add(fixed.Point26_6{X: dx, Y: r + dy}.Mul(multiple)), c.Add(d)) + // Close the curve. + z.Add1(c) + } + z.Rasterize(mp) + + for i := 0; i < n; i++ { + cx := fixed.Int26_6(6400 + 12800*(i%4)) + cy := fixed.Int26_6(640 + 8000*(i/4)) + for j := 0; j < n; j++ { + theta := math.Pi * float64(j) / (n - 1) + dx := fixed.Int26_6(r * math.Cos(theta)) + dy := fixed.Int26_6(r * math.Sin(theta)) + m.Set(int((cx+dx)/64), int((cy+dy)/64), color.RGBA{255, 255, 0, 255}) + } + } + + // Save that RGBA image to disk. + outFile, err := os.Create("out.png") + if err != nil { + log.Println(err) + os.Exit(1) + } + defer outFile.Close() + b := bufio.NewWriter(outFile) + err = png.Encode(b, m) + if err != nil { + log.Println(err) + os.Exit(1) + } + err = b.Flush() + if err != nil { + log.Println(err) + os.Exit(1) + } + fmt.Println("Wrote out.png OK.") +} diff --git a/vendor/github.com/golang/freetype/example/truetype/main.go b/vendor/github.com/golang/freetype/example/truetype/main.go new file mode 100644 index 000000000..e7db2d0ca --- /dev/null +++ b/vendor/github.com/golang/freetype/example/truetype/main.go @@ -0,0 +1,89 @@ +// Copyright 2010 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +// +build example +// +// This build tag means that "go install github.com/golang/freetype/..." +// doesn't install this example program. Use "go run main.go" to run it or "go +// install -tags=example" to install it. + +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + + "github.com/golang/freetype/truetype" + "golang.org/x/image/font" + "golang.org/x/image/math/fixed" +) + +var fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename of the ttf font") + +func printBounds(b fixed.Rectangle26_6) { + fmt.Printf("Min.X:%d Min.Y:%d Max.X:%d Max.Y:%d\n", b.Min.X, b.Min.Y, b.Max.X, b.Max.Y) +} + +func printGlyph(g *truetype.GlyphBuf) { + printBounds(g.Bounds) + fmt.Print("Points:\n---\n") + e := 0 + for i, p := range g.Points { + fmt.Printf("%4d, %4d", p.X, p.Y) + if p.Flags&0x01 != 0 { + fmt.Print(" on\n") + } else { + fmt.Print(" off\n") + } + if i+1 == int(g.Ends[e]) { + fmt.Print("---\n") + e++ + } + } +} + +func main() { + flag.Parse() + fmt.Printf("Loading fontfile %q\n", *fontfile) + b, err := ioutil.ReadFile(*fontfile) + if err != nil { + log.Println(err) + return + } + f, err := truetype.Parse(b) + if err != nil { + log.Println(err) + return + } + fupe := fixed.Int26_6(f.FUnitsPerEm()) + printBounds(f.Bounds(fupe)) + fmt.Printf("FUnitsPerEm:%d\n\n", fupe) + + c0, c1 := 'A', 'V' + + i0 := f.Index(c0) + hm := f.HMetric(fupe, i0) + g := &truetype.GlyphBuf{} + err = g.Load(f, fupe, i0, font.HintingNone) + if err != nil { + log.Println(err) + return + } + fmt.Printf("'%c' glyph\n", c0) + fmt.Printf("AdvanceWidth:%d LeftSideBearing:%d\n", hm.AdvanceWidth, hm.LeftSideBearing) + printGlyph(g) + i1 := f.Index(c1) + fmt.Printf("\n'%c', '%c' Kern:%d\n", c0, c1, f.Kern(fupe, i0, i1)) + + fmt.Printf("\nThe numbers above are in FUnits.\n" + + "The numbers below are in 26.6 fixed point pixels, at 12pt and 72dpi.\n\n") + a := truetype.NewFace(f, &truetype.Options{ + Size: 12, + DPI: 72, + }) + fmt.Printf("%#v\n", a.Metrics()) +} diff --git a/vendor/github.com/golang/freetype/freetype.go b/vendor/github.com/golang/freetype/freetype.go index bec01f6b7..96035866c 100644 --- a/vendor/github.com/golang/freetype/freetype.go +++ b/vendor/github.com/golang/freetype/freetype.go @@ -6,7 +6,7 @@ // The freetype package provides a convenient API to draw text onto an image. // Use the freetype/raster and freetype/truetype packages for lower level // control over rasterization and TrueType parsing. -package freetype +package freetype // import "github.com/golang/freetype" import ( "errors" diff --git a/vendor/github.com/golang/freetype/freetype_test.go b/vendor/github.com/golang/freetype/freetype_test.go new file mode 100644 index 000000000..348c411ab --- /dev/null +++ b/vendor/github.com/golang/freetype/freetype_test.go @@ -0,0 +1,59 @@ +// Copyright 2012 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +package freetype + +import ( + "image" + "image/draw" + "io/ioutil" + "runtime" + "strings" + "testing" +) + +func BenchmarkDrawString(b *testing.B) { + data, err := ioutil.ReadFile("licenses/gpl.txt") + if err != nil { + b.Fatal(err) + } + lines := strings.Split(string(data), "\n") + + data, err = ioutil.ReadFile("testdata/luxisr.ttf") + if err != nil { + b.Fatal(err) + } + f, err := ParseFont(data) + if err != nil { + b.Fatal(err) + } + + dst := image.NewRGBA(image.Rect(0, 0, 800, 600)) + draw.Draw(dst, dst.Bounds(), image.White, image.ZP, draw.Src) + + c := NewContext() + c.SetDst(dst) + c.SetClip(dst.Bounds()) + c.SetSrc(image.Black) + c.SetFont(f) + + var ms runtime.MemStats + runtime.ReadMemStats(&ms) + mallocs := ms.Mallocs + + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j, line := range lines { + _, err := c.DrawString(line, Pt(0, (j*16)%600)) + if err != nil { + b.Fatal(err) + } + } + } + b.StopTimer() + runtime.ReadMemStats(&ms) + mallocs = ms.Mallocs - mallocs + b.Logf("%d iterations, %d mallocs per iteration\n", b.N, int(mallocs)/b.N) +} diff --git a/vendor/github.com/golang/freetype/licenses/ftl.txt b/vendor/github.com/golang/freetype/licenses/ftl.txt new file mode 100644 index 000000000..bbaba33f4 --- /dev/null +++ b/vendor/github.com/golang/freetype/licenses/ftl.txt @@ -0,0 +1,169 @@ + The FreeType Project LICENSE + ---------------------------- + + 2006-Jan-27 + + Copyright 1996-2002, 2006 by + David Turner, Robert Wilhelm, and Werner Lemberg + + + +Introduction +============ + + The FreeType Project is distributed in several archive packages; + some of them may contain, in addition to the FreeType font engine, + various tools and contributions which rely on, or relate to, the + FreeType Project. + + This license applies to all files found in such packages, and + which do not fall under their own explicit license. The license + affects thus the FreeType font engine, the test programs, + documentation and makefiles, at the very least. + + This license was inspired by the BSD, Artistic, and IJG + (Independent JPEG Group) licenses, which all encourage inclusion + and use of free software in commercial and freeware products + alike. As a consequence, its main points are that: + + o We don't promise that this software works. However, we will be + interested in any kind of bug reports. (`as is' distribution) + + o You can use this software for whatever you want, in parts or + full form, without having to pay us. (`royalty-free' usage) + + o You may not pretend that you wrote this software. If you use + it, or only parts of it, in a program, you must acknowledge + somewhere in your documentation that you have used the + FreeType code. (`credits') + + We specifically permit and encourage the inclusion of this + software, with or without modifications, in commercial products. + We disclaim all warranties covering The FreeType Project and + assume no liability related to The FreeType Project. + + + Finally, many people asked us for a preferred form for a + credit/disclaimer to use in compliance with this license. We thus + encourage you to use the following text: + + """ + Portions of this software are copyright The FreeType + Project (www.freetype.org). All rights reserved. + """ + + Please replace with the value from the FreeType version you + actually use. + + +Legal Terms +=========== + +0. Definitions +-------------- + + Throughout this license, the terms `package', `FreeType Project', + and `FreeType archive' refer to the set of files originally + distributed by the authors (David Turner, Robert Wilhelm, and + Werner Lemberg) as the `FreeType Project', be they named as alpha, + beta or final release. + + `You' refers to the licensee, or person using the project, where + `using' is a generic term including compiling the project's source + code as well as linking it to form a `program' or `executable'. + This program is referred to as `a program using the FreeType + engine'. + + This license applies to all files distributed in the original + FreeType Project, including all source code, binaries and + documentation, unless otherwise stated in the file in its + original, unmodified form as distributed in the original archive. + If you are unsure whether or not a particular file is covered by + this license, you must contact us to verify this. + + The FreeType Project is copyright (C) 1996-2000 by David Turner, + Robert Wilhelm, and Werner Lemberg. All rights reserved except as + specified below. + +1. No Warranty +-------------- + + THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO + USE, OF THE FREETYPE PROJECT. + +2. Redistribution +----------------- + + This license grants a worldwide, royalty-free, perpetual and + irrevocable right and license to use, execute, perform, compile, + display, copy, create derivative works of, distribute and + sublicense the FreeType Project (in both source and object code + forms) and derivative works thereof for any purpose; and to + authorize others to exercise some or all of the rights granted + herein, subject to the following conditions: + + o Redistribution of source code must retain this license file + (`FTL.TXT') unaltered; any additions, deletions or changes to + the original files must be clearly indicated in accompanying + documentation. The copyright notices of the unaltered, + original files must be preserved in all copies of source + files. + + o Redistribution in binary form must provide a disclaimer that + states that the software is based in part of the work of the + FreeType Team, in the distribution documentation. We also + encourage you to put an URL to the FreeType web page in your + documentation, though this isn't mandatory. + + These conditions apply to any software derived from or based on + the FreeType Project, not just the unmodified files. If you use + our work, you must acknowledge us. However, no fee need be paid + to us. + +3. Advertising +-------------- + + Neither the FreeType authors and contributors nor you shall use + the name of the other for commercial, advertising, or promotional + purposes without specific prior written permission. + + We suggest, but do not require, that you use one or more of the + following phrases to refer to this software in your documentation + or advertising materials: `FreeType Project', `FreeType Engine', + `FreeType library', or `FreeType Distribution'. + + As you have not signed this license, you are not required to + accept it. However, as the FreeType Project is copyrighted + material, only this license, or another one contracted with the + authors, grants you the right to use, distribute, and modify it. + Therefore, by using, distributing, or modifying the FreeType + Project, you indicate that you understand and accept all the terms + of this license. + +4. Contacts +----------- + + There are two mailing lists related to FreeType: + + o freetype@nongnu.org + + Discusses general use and applications of FreeType, as well as + future and wanted additions to the library and distribution. + If you are looking for support, start in this list if you + haven't found anything to help you in the documentation. + + o freetype-devel@nongnu.org + + Discusses bugs, as well as engine internals, design issues, + specific licenses, porting, etc. + + Our home page can be found at + + http://www.freetype.org + + +--- end of FTL.TXT --- diff --git a/vendor/github.com/golang/freetype/licenses/gpl.txt b/vendor/github.com/golang/freetype/licenses/gpl.txt new file mode 100644 index 000000000..b2fe7b6af --- /dev/null +++ b/vendor/github.com/golang/freetype/licenses/gpl.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/vendor/github.com/golang/freetype/raster/raster.go b/vendor/github.com/golang/freetype/raster/raster.go index 995925e2a..7e6cd4e2b 100644 --- a/vendor/github.com/golang/freetype/raster/raster.go +++ b/vendor/github.com/golang/freetype/raster/raster.go @@ -13,7 +13,7 @@ // the Freetype "smooth" module, and the Anti-Grain Geometry library. A // description of the area/coverage algorithm is at // http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm -package raster +package raster // import "github.com/golang/freetype/raster" import ( "strconv" diff --git a/vendor/github.com/golang/freetype/testdata/COPYING b/vendor/github.com/golang/freetype/testdata/COPYING new file mode 100644 index 000000000..78c606533 --- /dev/null +++ b/vendor/github.com/golang/freetype/testdata/COPYING @@ -0,0 +1,42 @@ +Luxi fonts copyright (c) 2001 by Bigelow & Holmes Inc. Luxi font +instruction code copyright (c) 2001 by URW++ GmbH. All Rights +Reserved. Luxi is a registered trademark of Bigelow & Holmes Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of these Fonts and associated documentation files (the "Font +Software"), to deal in the Font Software, including without +limitation the rights to use, copy, merge, publish, distribute, +sublicense, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software. + +The Font Software may not be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may not +be modified nor may additional glyphs or characters be added to the +Fonts. This License becomes null and void when the Fonts or Font +Software have been modified. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL +BIGELOW & HOLMES INC. OR URW++ GMBH. BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, +INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR +INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT +SOFTWARE. + +Except as contained in this notice, the names of Bigelow & Holmes +Inc. and URW++ GmbH. shall not be used in advertising or otherwise to +promote the sale, use or other dealings in this Font Software without +prior written authorization from Bigelow & Holmes Inc. and URW++ GmbH. + +For further information, contact: + +info@urwpp.de +or +design@bigelowandholmes.com diff --git a/vendor/github.com/golang/freetype/testdata/README b/vendor/github.com/golang/freetype/testdata/README new file mode 100644 index 000000000..bae438269 --- /dev/null +++ b/vendor/github.com/golang/freetype/testdata/README @@ -0,0 +1,13 @@ +The luxi*.ttf and COPYING files in this directory were copied from the X.org +project, specifically +http://xorg.freedesktop.org/releases/individual/font/font-bh-ttf-1.0.0.tar.bz2 + +There are three Luxi fonts: sans (s), serif (r) and monospaced (m). For example, +luxisr.ttf is Luxi Sans. The 'r' here means regular, as opposed to bold. + +The *.ttx files in this directory were generated from the *.ttf files +by the ttx command-line tool. +http://www.letterror.com/code/ttx/index.html + +The *-hinting.txt files in this directory were generated from the *.ttf files +by the ../cmd/print-glyph-points command-line tool. diff --git a/vendor/github.com/golang/freetype/testdata/luximr.ttf b/vendor/github.com/golang/freetype/testdata/luximr.ttf new file mode 100644 index 000000000..6ad6e1266 Binary files /dev/null and b/vendor/github.com/golang/freetype/testdata/luximr.ttf differ diff --git a/vendor/github.com/golang/freetype/testdata/luximr.ttx b/vendor/github.com/golang/freetype/testdata/luximr.ttx new file mode 100644 index 000000000..e60ebca45 --- /dev/null +++ b/vendor/github.com/golang/freetype/testdata/luximr.ttx @@ -0,0 +1,24616 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 15 values pushed */ + 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + FDEF[ ] + SLOOP[ ] + MDAP[1] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + MDAP[1] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP1[ ] + SRP2[ ] + IP[ ] + ENDF[ ] + FDEF[ ] + SRP1[ ] + SRP2[ ] + SLOOP[ ] + IP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MIRP[11101] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MIRP[10100] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MDRP[11101] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MDRP[10100] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MIRP[11101] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MIRP[10100] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MDRP[11101] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MDRP[10100] + ENDF[ ] + FDEF[ ] + MDRP[00100] + ENDF[ ] + FDEF[ ] + MDRP[00000] + ENDF[ ] + FDEF[ ] + SVTCA[0] + NPUSHB[ ] /* 10 values pushed */ + 1 0 0 1 1 2 2 3 3 0 + SZPS[ ] + MIAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SZPS[ ] + ENDF[ ] + + + + + + PUSHB[ ] /* 2 values pushed */ + 48 1 + PUSHW[ ] /* 1 value pushed */ + 329 + RTG[ ] + SCANCTRL[ ] + SCANTYPE[ ] + SCVTCI[ ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 16 values pushed */ + 5 6 2 1 4 7 3 0 5 4 2 3 6 7 1 0 + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + SVTCA[0] + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 19 18 2 7 16 3 0 0 17 16 6 1 0 14 13 10 9 6 5 2 1 6 7 3 2 + 4 48 200 15 0 1 12 11 4 3 3 2 0 8 7 0 14 19 18 17 16 15 14 13 12 + 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 133 values pushed */ + 33 32 26 25 20 19 6 21 23 3 28 27 2 0 30 3 4 3 2 9 1 3 0 0 22 + 21 6 1 17 29 0 6 1 23 31 30 6 1 9 16 15 12 11 8 7 2 1 6 7 5 + 4 4 48 200 24 23 1 10 9 1 14 13 6 5 3 3 0 18 17 0 14 29 28 25 24 + 4 20 0 3 33 30 16 15 14 13 12 11 10 7 6 11 13 8 0 0 3 2 6 1 4 + 21 20 6 1 18 32 31 17 9 8 19 4 0 3 4 48 200 5 4 1 19 18 1 27 26 + 1 23 22 1 0 3 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 23 20 2 21 7 3 19 18 2 7 16 3 0 0 17 16 6 1 0 14 13 10 9 6 5 + 2 1 6 7 3 2 4 48 200 22 21 1 15 0 1 12 11 4 3 3 3 0 8 7 0 + 14 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 23 14 30 48 200 19 18 2 7 16 3 26 25 21 20 4 13 30 7 0 0 17 16 + 6 1 0 14 13 10 9 6 5 2 1 6 7 3 2 4 48 200 15 0 1 12 11 4 3 + 3 2 0 8 7 0 14 26 25 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 + 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 27 26 25 24 23 20 6 21 7 3 19 18 2 7 16 3 0 0 17 16 6 1 0 14 13 + 10 9 6 5 2 1 6 7 3 2 4 48 200 22 21 1 15 0 1 12 11 4 3 3 3 + 0 8 7 0 14 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 + 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 116 values pushed */ + 19 18 2 7 16 3 0 0 27 24 23 20 10 3 21 17 16 6 1 0 14 13 10 9 6 + 5 2 1 6 7 3 3 4 48 200 26 25 22 21 3 15 0 1 12 11 4 3 3 3 0 + 8 7 0 14 17 15 14 13 12 5 26 24 3 19 18 8 7 4 24 22 3 16 3 2 0 + 4 22 20 3 11 10 9 3 13 26 6 5 4 1 4 13 20 0 0 25 24 10 1 26 23 + 22 10 1 20 2 4 48 200 27 26 1 21 20 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 21 20 2 22 7 3 19 18 2 7 16 3 0 0 17 16 6 1 0 14 13 10 9 6 5 + 2 1 6 7 3 2 4 48 200 23 22 1 15 0 1 12 11 4 3 3 3 0 8 7 0 + 14 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 19 18 2 7 16 3 0 0 23 20 6 1 21 17 16 6 1 0 14 13 10 9 6 5 2 + 1 6 7 3 3 4 48 200 22 21 1 15 0 1 12 11 4 3 3 3 0 8 7 0 14 + 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 25 17 30 48 200 19 18 2 7 16 3 28 27 30 3 0 0 17 16 6 1 0 14 + 13 10 9 6 5 2 1 6 7 3 2 4 48 200 15 0 1 21 20 12 11 4 3 5 2 + 0 8 7 0 14 0 0 23 20 32 48 200 32 28 27 21 20 19 18 17 16 15 14 13 12 + 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 88 values pushed */ + 0 0 44 32 28 36 32 20 48 200 28 0 19 18 2 7 16 3 20 7 0 0 17 16 6 + 1 0 14 13 10 9 6 5 2 1 6 7 3 2 4 48 200 15 0 1 12 11 4 3 3 + 2 0 8 7 0 14 0 0 48 32 24 40 32 32 48 200 32 24 19 18 17 16 15 14 13 + 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 41 5 24 29 5 36 48 200 19 18 2 7 16 3 43 32 31 20 4 13 36 24 7 + 0 0 17 16 6 1 0 14 13 10 9 6 5 2 1 6 7 3 2 4 48 200 15 0 1 + 12 11 4 3 3 2 0 8 7 0 14 43 32 31 20 19 18 17 16 15 14 13 12 11 10 + 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 29 28 27 26 10 5 1 0 3 0 0 34 33 2 1 6 3 3 20 19 18 0 6 3 16 + 2 4 48 200 17 16 1 0 4 3 0 14 0 0 31 39 6 22 39 14 48 200 33 29 26 + 20 16 10 4 7 13 14 6 19 18 17 3 2 4 13 0 0 0 34 28 27 19 10 3 0 + 1 4 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 55 values pushed */ + 0 0 25 20 2 17 5 10 48 200 10 0 2 2 1 1 27 15 14 13 12 0 6 0 2 + 3 0 0 14 0 0 21 39 6 48 200 15 14 6 12 0 0 13 12 29 1 0 1 5 48 + 200 27 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 0 0 25 20 2 17 5 10 48 200 10 0 2 2 1 31 28 2 29 0 3 0 1 1 27 + 15 14 13 12 0 6 0 2 3 0 0 30 29 1 0 14 0 0 21 39 6 48 200 31 30 + 29 28 15 14 6 13 6 12 0 0 13 12 29 1 0 1 5 48 200 27 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 78 values pushed */ + 0 0 25 20 2 17 5 10 48 200 10 0 2 2 1 1 27 15 14 13 12 0 6 0 2 + 3 0 0 35 34 33 32 31 28 6 13 29 30 29 1 0 14 0 0 21 39 6 48 200 35 + 34 33 32 31 30 29 28 15 14 10 13 6 12 0 0 13 12 29 1 0 1 5 48 200 27 + 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 0 0 43 32 38 25 20 2 17 5 10 48 200 10 0 2 2 1 1 27 15 14 13 12 0 + 6 0 2 3 0 0 1 47 41 40 30 29 28 6 13 38 2 0 14 0 0 45 20 34 21 + 39 6 48 200 47 41 40 30 29 28 15 14 8 13 34 6 12 0 0 13 12 29 1 0 1 + 5 48 200 27 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 25 20 2 17 5 10 48 200 10 0 2 2 1 35 34 33 32 31 28 6 29 0 3 + 0 1 1 27 15 14 13 12 0 6 0 2 3 0 0 30 29 1 0 14 0 0 21 39 6 + 48 200 35 34 33 32 31 30 29 28 15 14 10 13 6 12 0 0 13 12 29 1 0 1 5 + 48 200 27 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 25 20 2 17 5 10 48 200 10 0 2 2 1 1 27 15 14 13 12 0 6 0 2 + 3 0 0 0 0 31 28 10 1 29 1 4 48 200 30 29 1 0 14 0 0 21 39 6 48 + 200 15 14 2 12 30 3 6 28 0 0 31 30 10 1 28 1 4 13 12 29 1 0 1 5 + 48 200 29 28 1 27 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 21 20 4 3 6 3 5 14 13 2 1 6 3 0 2 4 48 200 12 0 1 0 6 + 5 0 14 0 0 16 39 8 48 200 20 14 12 6 4 13 8 13 5 4 1 0 4 13 2 + 0 0 21 13 10 1 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 89 values pushed */ + 29 28 27 26 25 22 6 13 23 0 0 21 20 4 3 6 3 5 14 13 2 1 6 3 0 + 2 4 48 200 24 23 1 12 0 1 2 0 6 5 0 14 0 0 16 39 8 48 200 26 13 + 2 2 29 28 27 24 23 22 20 14 12 6 10 13 8 13 25 5 4 1 0 5 13 2 0 + 0 21 13 10 1 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 0 0 29 17 8 7 6 3 9 21 20 4 3 6 3 5 23 22 2 1 6 3 0 3 4 + 48 200 19 18 6 5 3 16 0 1 2 0 10 9 0 14 0 0 25 39 12 48 200 29 23 + 20 19 16 10 6 13 12 17 9 8 5 4 1 0 6 13 2 0 0 22 21 18 17 10 3 + 2 1 4 48 200 7 6 3 2 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 108 values pushed */ + 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 3 0 0 10 9 4 3 6 3 5 + 18 17 6 1 11 20 19 20 1 0 2 1 6 1 0 4 4 48 200 12 11 1 23 0 1 + 2 0 6 5 0 14 17 16 13 12 4 14 10 3 5 4 1 0 4 13 2 0 0 21 20 + 6 1 22 9 8 6 1 6 19 18 11 10 10 3 2 3 4 48 200 23 22 1 7 6 1 + 15 14 1 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 124 values pushed */ + 27 24 2 25 5 3 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 3 0 0 10 + 9 4 3 6 3 5 18 17 6 1 11 20 19 20 1 0 2 1 6 1 0 4 4 48 200 + 26 25 1 12 11 1 23 0 1 3 0 6 5 0 14 26 8 14 2 27 25 24 17 16 13 + 12 7 14 10 3 5 4 1 0 4 13 2 0 0 21 20 6 1 22 9 8 6 1 6 19 + 18 11 10 10 3 2 3 4 48 200 23 22 1 7 6 1 15 14 1 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 133 values pushed */ + 0 0 27 14 34 48 200 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 3 30 29 + 25 24 4 13 34 5 0 0 10 9 4 3 6 3 5 18 17 6 1 11 20 19 20 1 0 + 2 1 6 1 0 4 4 48 200 12 11 1 23 0 1 2 0 6 5 0 14 30 20 8 2 + 29 17 16 13 12 5 14 10 3 25 10 2 2 5 4 1 0 4 13 2 0 0 21 20 6 + 1 22 9 8 6 1 6 19 18 11 10 10 3 2 3 4 48 200 23 22 1 7 6 1 15 + 14 1 24 3 2 2 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 134 values pushed */ + 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 3 31 30 29 28 27 24 6 13 25 + 0 0 10 9 4 3 6 3 5 18 17 6 1 11 20 19 20 1 0 2 1 6 1 0 4 + 4 48 200 26 25 1 12 11 1 23 0 1 3 0 6 5 0 14 24 20 8 2 31 30 29 + 26 25 17 16 13 12 9 14 10 3 28 10 2 2 27 5 4 1 0 5 13 2 0 0 21 + 20 6 1 22 9 8 6 1 6 19 18 11 10 10 3 2 3 4 48 200 23 22 1 7 6 + 1 15 14 1 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 135 values pushed */ + 31 30 29 28 27 24 6 25 5 3 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 + 3 0 0 10 9 4 3 6 3 5 18 17 6 1 11 20 19 20 1 0 2 1 6 1 0 + 4 4 48 200 26 25 1 12 11 1 23 0 1 3 0 6 5 0 14 27 6 20 2 30 29 + 28 26 25 17 16 13 12 9 14 10 3 31 10 2 2 5 4 1 0 4 13 2 0 0 21 + 20 6 1 22 9 8 6 1 6 19 18 11 10 10 3 2 3 4 48 200 23 22 1 7 6 + 1 15 14 1 24 3 2 2 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 135 values pushed */ + 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 3 0 0 31 28 27 24 10 3 25 + 10 9 4 3 6 3 5 18 17 6 1 11 20 19 20 1 0 2 1 6 1 0 5 4 48 + 200 30 29 26 25 3 12 11 1 23 0 1 3 0 6 5 0 14 17 16 13 12 4 14 28 + 3 5 4 1 0 4 13 2 0 0 29 28 10 1 30 27 26 10 1 2 21 20 6 1 22 + 9 8 6 1 6 19 18 11 10 10 3 2 5 4 48 200 31 30 1 23 22 1 7 6 1 + 15 14 1 25 24 3 2 3 5 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 124 values pushed */ + 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 3 0 0 27 24 10 1 25 10 9 + 4 3 6 3 5 18 17 6 1 11 20 19 20 1 0 2 1 6 1 0 5 4 48 200 26 + 25 1 12 11 1 23 0 1 3 0 6 5 0 14 17 16 13 12 4 14 26 3 5 4 1 + 0 4 13 2 0 0 27 26 10 1 24 21 20 6 1 22 9 8 6 1 6 19 18 11 10 + 10 3 2 4 4 48 200 25 24 1 23 22 1 7 6 1 15 14 1 3 2 1 5 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 124 values pushed */ + 25 24 2 26 5 3 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 3 0 0 10 + 9 4 3 6 3 5 18 17 6 1 11 20 19 20 1 0 2 1 6 1 0 4 4 48 200 + 27 26 1 12 11 1 23 0 1 3 0 6 5 0 14 27 25 24 17 16 13 12 7 14 10 + 3 26 10 2 2 5 4 1 0 4 13 2 0 0 21 20 6 1 22 9 8 6 1 6 19 + 18 11 10 10 3 2 3 4 48 200 23 22 1 7 6 1 15 14 1 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 128 values pushed */ + 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 3 0 0 27 24 6 1 25 10 9 + 4 3 6 3 5 18 17 6 1 11 20 19 20 1 0 2 1 6 1 0 5 4 48 200 26 + 25 1 12 11 1 23 0 1 3 0 6 5 0 14 27 26 2 20 8 3 17 16 13 12 4 + 14 10 3 25 24 2 10 2 3 5 4 1 0 4 13 2 0 0 21 20 6 1 22 9 8 + 6 1 6 19 18 11 10 10 3 2 3 4 48 200 23 22 1 7 6 1 15 14 1 3 2 + 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 108 values pushed */ + 0 0 26 17 19 48 200 37 36 35 34 14 13 10 9 8 7 4 3 2 1 14 5 0 3 + 24 23 22 21 4 13 19 0 38 33 15 0 3 0 12 11 6 5 0 3 14 30 30 14 8 + 2 24 23 11 10 7 5 8 21 3 38 37 34 3 21 6 3 13 12 2 13 14 5 4 1 + 0 4 13 2 0 0 33 9 8 6 2 14 36 35 6 6 2 2 2 4 48 200 15 14 1 + 22 21 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MDAP[1] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 141 values pushed */ + 0 0 29 17 34 48 200 14 13 8 7 4 3 11 3 22 21 16 15 4 17 19 3 32 31 + 34 0 0 0 10 9 4 3 6 3 5 18 17 6 1 11 20 19 20 1 0 2 1 6 1 + 0 4 4 48 200 12 11 1 25 24 23 0 3 2 0 6 5 0 14 0 0 27 20 36 48 + 200 32 31 2 6 20 3 25 20 8 2 36 36 24 17 16 13 12 6 14 10 3 5 4 1 + 0 4 13 2 0 0 21 20 6 1 22 9 8 6 1 6 19 18 11 10 10 3 2 3 4 + 48 200 23 22 1 7 6 1 15 14 1 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 0 0 29 17 8 7 6 3 9 21 20 4 3 6 3 5 23 22 2 1 6 3 0 3 4 + 48 200 19 18 6 5 3 16 0 1 2 0 10 9 0 14 0 0 25 39 12 48 200 29 23 + 20 19 16 10 6 13 12 17 9 8 5 4 1 0 6 13 2 0 0 22 21 18 17 10 3 + 2 1 4 48 200 7 6 3 2 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 114 values pushed */ + 0 0 37 20 42 14 5 7 48 200 42 2 7 0 1 12 11 10 9 4 0 2 3 0 52 + 0 30 2 1 40 39 2 32 2 3 0 0 0 21 20 1 0 6 3 2 49 48 33 32 6 + 3 30 2 4 48 200 19 18 3 2 3 51 50 31 30 3 2 0 14 0 0 27 36 53 48 + 200 52 51 50 49 48 33 32 31 30 21 20 19 18 12 11 3 2 1 0 19 13 53 9 0 + 0 10 9 29 1 39 1 5 48 200 40 39 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 114 values pushed */ + 0 0 37 20 42 14 5 7 48 200 42 2 7 0 1 12 11 10 9 4 0 2 3 0 52 + 0 30 2 1 40 39 2 32 2 3 0 0 0 21 20 1 0 6 3 2 49 48 33 32 6 + 3 30 2 4 48 200 19 18 3 2 3 51 50 31 30 3 2 0 14 0 0 27 36 53 48 + 200 52 51 50 49 48 33 32 31 30 21 20 19 18 12 11 3 2 1 0 19 13 53 9 0 + 0 10 9 29 1 39 1 5 48 200 40 39 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 97 values pushed */ + 17 16 11 10 4 6 14 3 19 18 2 20 0 3 0 0 13 12 7 6 6 3 8 21 20 + 6 1 14 5 4 1 0 6 3 2 3 4 48 200 15 14 1 3 2 1 2 0 9 8 0 + 14 20 19 16 15 2 1 6 17 0 3 8 7 4 3 4 13 5 0 0 12 11 6 1 9 + 21 14 13 0 10 3 5 2 4 48 200 10 9 1 18 17 1 6 5 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 0 0 23 20 2 17 5 10 48 200 10 0 2 2 1 15 14 13 12 4 0 28 3 0 1 + 25 0 2 26 2 3 0 0 0 27 26 6 1 28 1 4 48 200 29 28 1 0 14 0 0 + 19 39 6 48 200 15 14 2 12 25 3 28 27 6 25 0 0 26 25 10 1 0 1 4 48 + 200 29 0 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 0 0 33 14 40 23 20 2 17 5 10 48 200 10 0 2 2 1 15 14 13 12 4 0 28 + 3 0 1 25 0 2 26 2 3 0 1 36 35 31 30 4 13 40 0 0 0 0 27 26 6 + 1 28 1 4 48 200 29 28 1 0 14 0 0 19 39 6 48 200 36 35 15 14 4 12 25 + 3 31 30 28 27 4 13 6 25 0 0 26 25 10 1 0 1 4 48 200 29 0 1 13 12 + 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 110 values pushed */ + 0 0 23 20 2 17 5 10 48 200 10 0 2 2 1 37 36 35 34 33 30 6 31 0 3 + 0 1 15 14 13 12 4 0 28 3 0 1 25 0 2 26 2 3 0 0 0 27 26 6 1 + 28 1 4 48 200 32 31 1 29 28 1 2 0 14 0 0 19 39 6 48 200 34 33 15 14 + 4 12 25 3 37 36 35 32 31 30 28 27 8 13 6 25 0 0 26 25 10 1 0 1 4 + 48 200 29 0 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 107 values pushed */ + 0 0 33 32 44 23 20 2 17 5 10 48 200 10 0 2 2 1 15 14 13 12 4 0 28 + 3 0 1 25 0 2 26 2 3 0 1 38 37 31 30 4 13 44 2 0 0 0 27 26 6 + 1 28 1 4 48 200 29 28 1 0 14 0 0 35 20 42 19 39 6 48 200 15 14 2 12 + 25 3 38 37 31 30 28 27 6 13 42 6 25 0 0 26 25 10 1 0 1 4 48 200 29 + 0 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 105 values pushed */ + 0 0 23 20 2 17 5 10 48 200 10 0 2 2 1 15 14 13 12 4 0 28 3 0 1 + 25 0 2 26 2 3 0 0 0 33 30 10 1 31 27 26 6 1 28 2 4 48 200 32 31 + 1 29 28 1 2 0 14 0 0 19 39 6 48 200 15 14 2 12 25 3 28 27 2 32 30 + 3 6 30 0 0 33 32 10 1 30 26 25 10 1 0 2 4 48 200 31 30 1 29 0 1 + 13 12 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 106 values pushed */ + 20 19 16 15 12 11 8 7 8 9 13 3 26 25 22 21 6 5 2 1 8 0 3 3 0 + 0 27 0 6 1 13 1 4 48 200 14 13 1 24 23 4 3 3 2 0 18 17 10 9 0 + 3 14 25 24 17 16 11 10 3 2 8 14 0 3 23 22 19 18 4 13 20 9 8 5 4 + 4 13 6 0 0 27 26 15 14 10 3 20 13 12 1 0 10 3 6 2 4 48 200 21 20 + 1 7 6 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 143 values pushed */ + 24 23 20 19 16 15 12 11 8 13 17 3 26 25 10 9 4 17 2 3 38 37 34 33 30 + 29 6 5 8 35 4 3 18 17 1 36 35 1 39 32 31 4 3 1 0 1 4 0 22 21 + 14 13 0 3 28 27 8 7 3 2 1 5 14 39 38 33 32 21 20 15 14 8 1 0 3 + 31 30 27 26 23 22 6 13 24 13 12 9 8 5 4 6 13 6 0 0 35 34 19 18 2 + 1 10 5 24 37 36 17 16 3 0 10 5 6 2 4 48 200 29 28 25 24 3 11 10 7 + 6 3 35 34 19 18 2 1 5 37 36 17 16 3 0 5 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 133 values pushed */ + 35 34 33 32 31 28 6 29 9 3 20 19 16 15 12 11 8 7 8 9 13 3 26 25 22 + 21 6 5 2 1 8 0 3 3 0 0 27 0 6 1 13 1 4 48 200 30 29 1 14 13 + 1 24 23 4 3 3 3 0 18 17 10 9 0 3 14 31 20 14 2 35 34 33 32 30 29 + 25 24 17 16 11 10 3 2 14 14 0 3 28 0 6 2 23 22 19 18 4 13 20 9 8 + 5 4 4 13 6 0 0 27 26 15 14 10 3 20 13 12 1 0 10 3 6 2 4 48 200 + 21 20 1 7 6 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 0 0 8 7 4 3 6 3 5 10 9 2 1 6 3 0 2 4 48 200 11 0 1 0 6 + 5 0 14 11 10 7 6 4 13 8 5 4 1 0 4 13 2 0 0 9 8 10 1 2 1 + 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 100 values pushed */ + 0 0 17 5 28 48 200 28 2 24 19 15 14 13 10 9 2 1 9 3 0 3 12 0 0 + 0 21 20 8 7 4 3 6 5 5 1 4 48 200 11 0 1 0 23 22 6 5 0 3 14 + 22 21 15 14 4 19 12 3 11 10 7 6 4 12 8 3 5 4 1 0 4 13 2 0 0 + 20 19 10 1 23 9 8 10 1 2 2 4 48 200 24 23 1 13 12 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 15 12 2 13 5 3 0 0 8 7 4 3 6 3 5 10 9 2 1 6 3 0 2 4 48 + 200 14 13 1 11 0 1 2 0 6 5 0 14 15 8 2 2 14 13 11 10 7 6 6 13 + 8 5 4 1 0 4 13 2 0 0 9 8 10 1 2 1 4 48 200 12 3 2 2 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 76 values pushed */ + 0 0 15 14 22 48 200 18 17 13 12 4 13 22 5 0 0 8 7 4 3 6 3 5 10 + 9 2 1 6 3 0 2 4 48 200 11 0 1 0 6 5 0 14 18 17 11 10 7 6 6 + 13 8 13 12 5 4 1 0 6 13 2 0 0 9 8 10 1 2 1 4 48 200 3 2 1 + 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 19 18 17 16 15 12 6 13 5 3 0 0 8 7 4 3 6 3 5 10 9 2 1 6 3 + 0 2 4 48 200 14 13 1 11 0 1 2 0 6 5 0 14 18 17 2 8 2 3 16 15 + 14 11 10 7 6 7 13 8 19 13 12 5 4 1 0 7 13 2 0 0 9 8 10 1 2 + 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 19 16 15 12 10 3 13 8 7 4 3 6 3 5 10 9 2 1 6 3 0 3 4 + 48 200 18 17 14 13 3 11 0 1 2 0 6 5 0 14 11 10 7 6 4 13 18 5 4 + 1 0 4 13 12 0 0 17 16 10 1 18 15 14 10 1 12 9 8 10 1 2 3 4 48 + 200 19 18 1 13 12 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 0 0 15 12 10 1 13 8 7 4 3 6 3 5 10 9 2 1 6 3 0 3 4 48 200 + 14 13 1 11 0 1 2 0 6 5 0 14 11 10 7 6 4 13 8 5 4 1 0 4 13 + 2 0 0 15 14 10 1 2 9 8 10 1 2 2 4 48 200 13 12 3 2 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 13 12 2 14 5 3 0 0 8 7 4 3 6 3 5 10 9 2 1 6 3 0 2 4 48 + 200 15 14 1 11 0 1 2 0 6 5 0 14 13 12 2 8 2 3 11 10 7 6 4 13 + 8 15 14 5 4 1 0 6 13 2 0 0 9 8 10 1 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 15 12 6 1 13 8 7 4 3 6 3 5 10 9 2 1 6 3 0 3 4 48 200 + 14 13 1 11 0 1 2 0 6 5 0 14 15 14 11 10 7 6 6 13 8 13 12 5 4 + 1 0 6 13 2 0 0 9 8 10 1 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 0 0 17 17 22 48 200 20 19 22 0 0 0 8 7 4 3 6 3 5 10 9 2 1 6 + 3 0 2 4 48 200 13 12 11 0 3 0 6 5 0 14 0 0 15 20 24 48 200 24 24 + 8 2 2 20 19 13 12 11 10 7 6 8 13 8 5 4 1 0 4 13 2 0 0 9 8 + 10 1 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 33 5 16 21 5 28 48 200 35 24 23 12 4 13 28 16 5 0 0 8 7 4 3 + 6 3 5 10 9 2 1 6 3 0 2 4 48 200 11 0 1 0 6 5 0 14 24 23 11 + 10 7 6 6 13 8 35 12 5 4 1 0 6 13 2 0 0 9 8 10 1 2 1 4 48 + 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 5 5 20 48 200 20 2 1 16 9 3 2 1 0 6 10 2 3 0 0 0 15 14 + 11 10 6 3 12 1 4 48 200 13 12 0 14 12 11 3 2 4 9 0 3 14 13 2 13 + 15 0 0 10 9 10 1 15 1 4 48 200 16 15 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 96 values pushed */ + 0 0 5 5 20 48 200 20 2 29 28 27 26 25 22 6 23 12 3 1 16 9 3 2 1 + 0 6 10 2 3 0 0 0 15 14 11 10 6 3 12 1 4 48 200 24 23 1 0 13 12 + 0 14 28 27 24 3 15 9 3 29 23 22 12 11 3 2 7 9 0 3 26 25 14 13 4 + 13 15 0 0 10 9 10 1 15 1 4 48 200 16 15 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 27 26 25 24 23 22 19 18 17 16 15 12 11 10 9 8 7 4 3 2 1 21 5 0 3 + 28 21 20 0 3 0 14 13 6 5 0 3 14 28 27 24 23 22 21 20 19 18 17 16 15 + 14 13 12 11 10 7 6 19 13 8 5 4 1 0 4 13 2 0 0 26 25 9 8 10 3 + 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 111 values pushed */ + 0 0 32 32 43 48 200 27 26 25 24 23 22 19 18 17 16 15 12 11 10 9 8 7 4 + 3 2 1 21 5 0 3 37 36 30 29 4 13 43 0 28 21 20 0 3 0 14 13 6 5 + 0 3 14 0 0 34 20 41 48 200 37 36 30 29 28 27 24 23 22 21 20 19 18 17 16 + 15 14 13 12 11 10 7 6 23 13 41 8 5 4 1 0 4 13 2 0 0 26 25 9 8 + 10 3 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 13 12 2 4 10 3 0 0 9 8 5 4 6 3 6 11 10 20 1 0 3 2 6 1 0 + 3 4 48 200 1 0 1 0 7 6 0 14 8 7 2 11 9 3 6 5 2 1 4 13 3 + 0 0 12 11 6 1 0 10 9 10 1 3 2 4 48 200 13 0 1 4 3 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 91 values pushed */ + 17 14 2 15 6 3 13 12 2 4 10 3 0 0 9 8 5 4 6 3 6 11 10 20 1 + 0 3 2 6 1 0 3 4 48 200 16 15 1 1 0 1 2 0 7 6 0 14 16 15 8 + 7 4 11 9 3 14 9 3 2 6 5 2 1 4 13 3 0 0 12 11 6 1 0 17 10 + 9 10 2 3 2 4 48 200 13 0 1 4 3 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 99 values pushed */ + 22 19 18 17 15 14 13 12 8 4 10 3 0 0 9 8 5 4 6 3 6 11 10 20 1 + 0 3 2 6 1 0 3 4 48 200 1 0 1 0 21 20 7 6 0 3 14 18 17 2 11 + 14 3 8 7 2 14 9 3 6 5 2 1 4 13 3 0 0 20 19 15 14 10 3 21 12 + 11 6 1 0 10 9 10 1 3 3 4 48 200 22 21 1 13 0 1 4 3 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 106 values pushed */ + 0 0 17 32 28 48 200 13 12 2 4 10 3 22 21 15 14 4 13 28 0 0 0 9 8 + 5 4 6 3 6 11 10 20 1 0 3 2 6 1 0 3 4 48 200 1 0 1 0 7 6 + 0 14 0 0 19 20 26 48 200 26 26 22 21 8 7 5 11 9 3 15 14 2 9 3 3 + 6 5 2 1 4 13 3 0 0 12 11 6 1 0 10 9 10 1 3 2 4 48 200 13 0 + 1 4 3 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 13 12 2 14 10 3 0 0 17 14 10 1 15 9 8 5 4 6 3 6 11 10 20 1 0 + 3 2 6 1 0 4 4 48 200 16 15 1 1 0 1 2 0 7 6 0 14 8 7 2 14 + 9 3 6 5 2 1 4 13 3 0 0 15 14 10 1 0 12 11 6 1 0 10 9 10 1 + 3 3 4 48 200 17 16 13 0 3 4 3 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 89 values pushed */ + 20 19 16 15 14 13 6 5 4 3 10 7 17 3 0 0 12 11 8 7 6 3 9 18 17 + 20 1 0 2 1 6 1 0 3 4 48 200 21 0 1 0 10 9 0 14 11 10 2 14 12 + 3 0 0 19 18 6 1 20 17 16 13 12 10 3 2 2 4 48 200 21 20 1 15 14 1 + 7 6 3 2 3 9 8 5 4 1 0 5 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 91 values pushed */ + 26 25 24 23 22 21 20 19 18 17 14 13 12 11 8 7 4 3 2 1 20 5 0 3 27 + 16 15 0 3 0 10 9 6 5 0 3 14 27 26 23 22 21 20 17 16 9 8 7 6 12 + 18 24 3 15 14 11 10 4 13 12 5 4 1 0 4 13 2 0 0 19 18 34 1 12 25 + 24 6 1 2 2 4 48 200 13 12 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 79 values pushed */ + 20 19 18 17 14 13 10 9 8 7 4 3 2 1 14 5 0 3 21 16 15 0 3 0 12 + 11 6 5 0 3 14 21 20 17 11 10 7 6 8 6 3 13 12 2 13 14 5 4 1 0 + 4 13 2 0 0 16 9 8 6 2 14 19 18 6 6 2 2 2 4 48 200 15 14 1 3 + 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 96 values pushed */ + 25 22 2 23 5 3 20 19 18 17 14 13 10 9 8 7 4 3 2 1 14 5 0 3 24 + 23 1 21 16 15 0 3 2 0 12 11 6 5 0 3 14 24 14 8 2 25 23 22 21 20 + 17 11 10 7 9 8 6 3 13 12 2 13 14 5 4 1 0 4 13 2 0 0 16 9 8 + 6 2 14 19 18 6 6 2 2 2 4 48 200 15 14 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 106 values pushed */ + 20 19 18 17 14 13 10 9 8 7 4 3 2 1 14 5 0 3 29 28 27 26 25 22 6 + 13 23 24 23 1 21 16 15 0 3 2 0 12 11 6 5 0 3 14 22 14 8 2 29 28 + 27 26 24 23 21 20 17 11 10 7 12 8 6 3 25 6 2 2 13 12 2 13 14 5 4 + 1 0 4 13 2 0 0 16 9 8 6 2 14 19 18 6 6 2 2 2 4 48 200 15 14 + 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 107 values pushed */ + 0 0 25 32 36 48 200 20 19 18 17 14 13 10 9 8 7 4 3 2 1 14 5 0 3 + 30 29 23 22 4 13 36 0 21 16 15 0 3 0 12 11 6 5 0 3 14 0 0 27 20 + 34 48 200 34 34 30 29 23 22 21 20 17 11 10 7 11 8 6 3 13 12 2 13 14 5 + 4 1 0 4 13 2 0 0 16 9 8 6 2 14 19 18 6 6 2 2 2 4 48 200 15 + 14 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 108 values pushed */ + 0 0 43 5 26 31 5 38 48 200 20 19 18 17 14 13 10 9 8 7 4 3 2 1 14 + 5 0 3 45 34 33 22 4 13 38 26 5 21 16 15 0 3 0 12 11 6 5 0 3 14 + 34 14 8 2 45 33 21 20 17 11 10 7 8 8 6 3 22 6 2 2 13 12 2 13 14 + 5 4 1 0 4 13 2 0 0 16 9 8 6 2 14 19 18 6 6 2 2 2 4 48 200 + 15 14 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 24 5 8 16 5 0 48 200 8 2 0 0 14 0 0 28 39 4 20 39 12 48 200 + 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 131 values pushed */ + 0 0 42 5 3 36 5 11 48 200 11 0 3 2 13 14 18 2 34 23 22 17 16 5 18 + 20 3 33 31 30 25 24 5 26 28 3 1 28 0 2 0 0 19 18 6 1 14 27 26 6 + 1 20 29 28 20 1 0 3 4 48 200 21 20 1 32 0 1 2 0 15 14 0 14 0 0 + 38 24 7 48 200 26 25 22 21 4 17 19 3 7 0 0 0 30 29 6 1 31 18 17 6 + 1 15 34 33 14 13 1 0 19 5 19 3 4 48 200 32 31 1 16 15 1 24 23 1 28 + 27 20 19 3 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 24 5 8 16 5 0 48 200 8 2 0 0 1 35 32 2 33 0 3 0 34 33 1 + 0 14 0 0 28 39 4 20 39 12 48 200 35 34 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 35 14 42 24 5 8 16 5 0 48 200 8 2 0 0 1 38 37 33 32 4 13 42 + 0 0 14 0 0 28 36 4 20 36 12 48 200 38 37 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 51 values pushed */ + 0 0 24 5 8 16 5 0 48 200 8 2 0 0 1 39 38 37 36 35 32 6 33 0 3 + 0 34 33 1 0 14 0 0 28 39 4 20 39 12 48 200 39 38 37 36 35 34 33 32 12 + 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 0 0 24 5 8 16 5 0 48 200 8 2 0 0 0 0 39 36 35 32 10 3 33 1 4 + 48 200 38 37 34 33 3 0 14 0 0 28 39 4 20 39 12 48 200 4 38 12 32 0 0 + 37 36 10 1 38 35 34 10 1 32 2 4 48 200 39 38 1 33 32 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 24 5 8 16 5 0 48 200 8 2 0 0 1 33 32 2 34 0 3 0 35 34 1 + 0 14 0 0 28 39 4 20 39 12 48 200 35 34 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 51 values pushed */ + 0 0 24 5 8 16 5 0 48 200 8 2 0 0 1 39 36 35 32 4 33 0 3 0 38 + 37 34 33 3 0 14 0 0 28 39 4 20 39 12 48 200 39 38 37 36 35 34 33 32 12 + 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 0 0 24 5 8 16 5 0 48 200 8 2 0 0 0 0 35 32 6 1 33 1 4 48 200 + 34 33 1 0 14 0 0 28 39 4 20 39 12 48 200 35 34 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 61 values pushed */ + 36 2 25 0 3 1 1 30 27 17 3 1 5 0 1 3 0 0 11 1 1 38 19 11 9 + 0 5 1 2 3 0 0 1 29 28 2 13 0 0 1 39 18 2 0 14 39 38 32 30 29 + 28 27 21 19 18 17 15 9 7 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 48 values pushed */ + 0 0 53 5 36 41 5 48 24 5 8 16 5 0 48 200 8 2 0 0 1 55 44 43 32 + 4 13 48 36 0 0 14 0 0 28 39 4 20 39 12 48 200 55 44 43 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 18 17 16 0 4 7 1 3 0 0 23 22 8 7 6 3 9 6 5 2 1 6 3 3 2 + 4 48 200 4 3 1 0 10 9 0 14 0 0 20 39 12 48 200 22 18 16 10 3 2 6 + 13 12 0 9 8 5 4 4 13 6 0 0 23 17 1 0 10 3 6 1 4 48 200 7 6 + 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 36 values pushed */ + 0 0 24 5 10 48 200 10 0 1 22 20 18 0 4 13 32 0 0 14 0 0 36 39 14 + 28 39 6 48 200 22 20 18 14 6 0 + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 25 24 14 3 3 19 3 22 21 16 15 2 1 6 19 0 3 0 0 30 29 4 3 6 3 + 5 1 4 48 200 20 19 1 23 18 17 0 3 2 0 6 5 0 14 0 0 27 39 10 48 + 200 29 25 23 22 19 18 17 16 15 14 6 11 13 10 20 5 4 1 0 4 13 2 0 0 + 30 24 21 20 10 3 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 34 31 2 32 5 3 25 24 14 3 3 19 3 22 21 16 15 2 1 6 19 0 3 0 0 + 30 29 4 3 6 3 5 1 4 48 200 33 32 1 20 19 1 23 18 17 0 3 3 0 6 + 5 0 14 0 0 27 39 10 48 200 34 33 32 31 29 25 23 22 19 18 17 16 15 14 6 + 15 13 10 20 5 4 1 0 4 13 2 0 0 30 24 21 20 10 3 2 1 4 48 200 3 + 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 113 values pushed */ + 25 24 14 3 3 19 3 22 21 16 15 2 1 6 19 0 3 38 37 36 35 34 31 6 13 + 32 0 0 30 29 4 3 6 3 5 1 4 48 200 33 32 1 20 19 1 23 18 17 0 3 + 3 0 6 5 0 14 0 0 27 39 10 48 200 35 20 2 2 38 37 36 33 32 31 29 25 + 23 22 19 18 17 16 15 14 6 17 13 10 20 34 5 4 1 0 5 13 2 0 0 30 24 + 21 20 10 3 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 113 values pushed */ + 0 0 34 32 45 48 200 25 24 14 3 3 19 3 22 21 16 15 2 1 6 19 0 3 39 + 38 32 31 4 13 45 0 0 0 30 29 4 3 6 3 5 1 4 48 200 20 19 1 23 18 + 17 0 3 2 0 6 5 0 14 0 0 36 20 43 27 39 10 48 200 39 38 32 31 29 25 + 23 22 19 18 17 16 15 14 6 15 13 43 10 20 5 4 1 0 4 13 2 0 0 30 24 + 21 20 10 3 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 61 values pushed */ + 0 0 25 5 18 5 5 40 48 200 40 2 18 0 1 1 23 22 21 20 3 2 1 0 8 + 0 2 3 0 0 14 0 0 27 19 16 9 28 36 48 200 16 23 22 16 3 2 5 20 0 + 3 36 20 21 20 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 25 5 18 5 5 40 48 200 40 2 18 0 1 45 42 2 43 0 3 0 1 1 23 + 22 21 20 3 2 1 0 8 0 2 3 0 0 44 43 1 0 14 0 0 27 19 16 9 28 + 36 48 200 16 45 44 43 42 23 22 16 3 2 9 20 0 3 36 20 21 20 1 1 0 1 + 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 0 0 25 5 18 5 5 40 48 200 40 2 18 0 1 1 23 22 21 20 3 2 1 0 8 + 0 2 3 0 0 49 48 47 46 45 42 6 13 43 44 43 1 0 14 0 0 27 19 16 9 + 28 36 48 200 16 49 48 47 46 45 44 43 42 23 22 16 3 2 13 20 0 3 36 20 21 + 20 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 57 32 52 25 5 18 5 5 40 48 200 40 2 18 0 1 1 23 22 21 20 3 2 + 1 0 8 0 2 3 0 0 1 61 55 54 44 43 42 6 13 52 2 0 14 0 0 59 20 + 48 27 19 16 9 28 36 48 200 48 16 61 55 54 48 44 43 42 23 22 16 3 2 12 20 + 0 3 36 20 21 20 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 25 5 18 5 5 40 48 200 40 2 18 0 1 49 48 47 46 45 42 6 43 0 3 + 0 1 1 23 22 21 20 3 2 1 0 8 0 2 3 0 0 44 43 1 0 14 0 0 27 + 19 16 9 28 36 48 200 16 49 48 47 46 45 44 43 42 23 22 16 3 2 13 20 0 3 + 36 20 21 20 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 0 0 45 32 56 25 5 18 5 5 40 48 200 40 2 18 0 1 1 23 22 21 20 3 2 + 1 0 8 0 2 3 0 0 1 50 49 43 42 4 13 56 2 0 14 0 0 47 20 54 27 + 19 16 9 28 36 48 200 54 16 54 50 49 43 42 23 22 16 3 2 10 20 0 3 36 20 + 21 20 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 10 9 6 5 4 3 1 3 0 0 12 11 4 3 6 3 7 14 13 2 1 6 3 0 2 + 4 48 200 15 0 1 0 8 7 0 14 15 14 2 10 12 3 1 0 2 2 4 3 0 0 + 11 10 6 1 8 13 12 10 1 2 5 4 6 1 6 3 4 48 200 9 8 1 3 2 1 + 7 6 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 101 values pushed */ + 14 13 10 9 4 7 5 3 0 0 16 15 8 7 6 3 11 20 19 4 3 33 3 5 22 + 21 2 1 6 3 0 3 4 48 200 18 17 6 5 3 23 0 1 2 0 12 11 0 14 23 + 22 19 18 4 14 16 3 5 4 1 0 4 2 8 3 0 0 15 14 6 1 12 21 20 17 + 16 10 3 2 9 8 6 1 10 3 4 48 200 13 12 1 7 6 3 2 3 11 10 1 3 + 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 105 values pushed */ + 10 9 6 5 4 3 1 3 23 22 21 20 19 16 6 13 17 0 0 12 11 4 3 6 3 + 7 14 13 2 1 6 3 0 2 4 48 200 18 17 1 15 0 1 2 0 8 7 0 14 23 + 17 16 15 14 5 10 12 3 22 21 2 12 2 3 20 19 18 1 0 5 2 4 3 0 0 + 11 10 6 1 8 13 12 10 1 2 5 4 6 1 6 3 4 48 200 9 8 1 3 2 1 + 7 6 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 116 values pushed */ + 0 0 31 32 26 48 200 10 9 6 5 4 3 1 3 35 29 28 18 4 13 26 0 0 0 + 12 11 4 3 6 3 7 14 13 2 1 6 3 0 2 4 48 200 17 16 15 0 3 0 8 + 7 0 14 0 0 33 20 22 48 200 22 22 15 14 3 10 12 3 18 17 16 3 12 2 3 + 35 29 28 1 0 5 2 4 3 0 0 11 10 6 1 8 13 12 10 1 2 5 4 6 1 + 6 3 4 48 200 9 8 1 3 2 1 7 6 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 112 values pushed */ + 0 0 19 32 30 48 200 10 9 6 5 4 3 1 3 24 23 17 16 4 13 30 0 0 0 + 12 11 4 3 6 3 7 14 13 2 1 6 3 0 2 4 48 200 15 0 1 0 8 7 0 + 14 0 0 21 20 28 48 200 28 28 15 14 3 10 12 3 24 23 2 12 2 3 17 16 1 + 0 4 2 4 3 0 0 11 10 6 1 8 13 12 10 1 2 5 4 6 1 6 3 4 48 + 200 9 8 1 3 2 1 7 6 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 29 28 22 21 8 7 6 0 9 3 0 0 20 19 16 15 6 3 17 14 13 10 9 6 3 + 11 2 4 48 200 1 0 1 12 11 1 2 0 18 17 0 14 0 0 26 39 3 48 200 28 + 22 19 18 11 10 7 1 8 13 3 0 17 16 13 12 4 13 14 0 0 29 21 20 9 8 + 0 10 5 14 1 4 48 200 15 14 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 0 0 10 5 23 48 200 23 2 1 25 19 18 17 14 13 12 6 5 4 1 0 12 2 2 + 3 0 16 15 3 2 0 3 14 15 14 4 3 4 12 5 3 17 16 2 13 18 2 1 0 + 0 0 13 12 6 1 18 6 5 10 1 0 2 4 48 200 19 18 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 91 values pushed */ + 0 0 10 5 23 48 200 23 2 29 26 2 27 2 3 1 25 19 18 17 14 13 12 6 5 + 4 1 0 12 2 2 3 0 28 27 1 0 16 15 3 2 0 3 14 28 18 12 2 29 27 + 26 15 14 4 3 7 12 5 3 17 16 2 13 18 2 1 0 0 0 13 12 6 1 18 6 + 5 10 1 0 2 4 48 200 19 18 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 95 values pushed */ + 0 0 29 14 36 10 5 23 48 200 23 2 1 25 19 18 17 14 13 12 6 5 4 1 0 + 12 2 2 3 0 32 31 27 26 4 13 36 2 16 15 3 2 0 3 14 32 18 12 2 31 + 27 15 14 4 3 6 12 5 3 26 5 0 2 17 16 2 13 18 2 1 0 0 0 13 12 + 6 1 18 6 5 10 1 0 2 4 48 200 19 18 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 102 values pushed */ + 0 0 10 5 23 48 200 23 2 33 32 31 30 29 26 6 27 2 3 1 25 19 18 17 14 + 13 12 6 5 4 1 0 12 2 2 3 0 28 27 1 0 16 15 3 2 0 3 14 29 18 + 12 2 33 32 31 30 28 27 15 14 4 3 10 12 5 3 26 5 0 2 17 16 2 13 18 + 2 1 0 0 0 13 12 6 1 18 6 5 10 1 0 2 4 48 200 19 18 1 25 0 1 + 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 109 values pushed */ + 0 0 10 5 23 48 200 23 2 1 25 19 18 17 14 13 12 6 5 4 1 0 12 2 2 + 3 0 0 0 33 30 29 26 10 3 27 1 4 48 200 32 31 28 27 3 0 16 15 3 2 + 0 3 14 15 14 4 3 4 30 28 3 17 16 2 13 18 2 1 0 0 0 31 30 10 1 + 32 29 28 10 1 26 13 12 6 1 18 6 5 10 1 0 4 4 48 200 33 32 1 27 26 + 1 19 18 1 25 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 91 values pushed */ + 0 0 10 5 23 48 200 23 2 27 26 2 28 2 3 1 25 19 18 17 14 13 12 6 5 + 4 1 0 12 2 2 3 0 29 28 1 0 16 15 3 2 0 3 14 29 27 26 15 14 4 + 3 7 12 5 3 28 5 0 2 17 16 2 13 18 2 1 0 0 0 13 12 6 1 18 6 + 5 10 1 0 2 4 48 200 19 18 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 99 values pushed */ + 0 0 10 5 23 48 200 23 2 33 30 29 26 4 27 2 3 1 25 19 18 17 14 13 12 + 6 5 4 1 0 12 2 2 3 0 32 31 28 27 3 0 16 15 3 2 0 3 14 31 18 + 12 2 33 30 29 28 27 26 15 14 4 3 10 12 5 3 32 17 16 3 13 18 2 1 0 + 0 0 13 12 6 1 18 6 5 10 1 0 2 4 48 200 19 18 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 101 values pushed */ + 0 0 10 5 23 48 200 23 2 1 25 19 18 17 14 13 12 6 5 4 1 0 12 2 2 + 3 0 0 0 29 26 6 1 27 1 4 48 200 28 27 1 0 16 15 3 2 0 3 14 29 + 28 2 18 12 3 15 14 4 3 4 12 5 3 27 26 2 5 0 3 17 16 2 13 18 2 + 1 0 0 0 13 12 6 1 18 6 5 10 1 0 2 4 48 200 19 18 1 25 0 1 2 + 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 0 0 3 32 14 48 200 14 8 7 1 0 14 0 0 5 20 12 48 200 12 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 100 values pushed */ + 0 0 31 17 36 10 5 23 48 200 23 2 1 25 19 18 17 14 13 12 6 5 4 1 0 + 12 2 2 3 0 1 34 33 27 26 4 13 36 2 0 16 15 3 2 0 3 14 0 0 29 + 20 38 48 200 38 38 34 33 27 26 15 14 4 3 9 12 5 3 17 16 2 13 18 2 1 + 0 0 0 13 12 6 1 18 6 5 10 1 0 2 4 48 200 19 18 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 100 values pushed */ + 0 0 50 32 34 42 32 26 10 5 23 48 200 23 2 1 25 19 18 17 14 13 12 6 5 + 4 1 0 12 2 2 3 0 34 26 2 16 15 3 2 0 3 14 0 0 54 32 30 46 32 + 38 48 200 30 30 15 14 4 3 5 12 5 3 38 38 5 0 2 17 16 2 13 18 2 1 + 0 0 0 13 12 6 1 18 6 5 10 1 0 2 4 48 200 19 18 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 99 values pushed */ + 0 0 47 5 30 35 5 42 10 5 23 48 200 23 2 1 25 19 18 17 14 13 12 6 5 + 4 1 0 12 2 2 3 0 49 38 37 26 4 13 42 30 2 16 15 3 2 0 3 14 38 + 18 12 2 49 37 15 14 4 3 6 12 5 3 26 5 0 2 17 16 2 13 18 2 1 0 + 0 0 13 12 6 1 18 6 5 10 1 0 2 4 48 200 19 18 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 8 7 2 1 0 3 0 0 14 13 10 9 6 5 2 1 6 7 3 1 4 48 200 15 0 + 1 0 12 11 4 3 0 3 14 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 18 17 14 13 6 5 2 1 8 3 9 3 22 21 12 11 8 7 6 9 0 3 10 9 1 + 23 20 19 0 3 2 0 16 15 4 3 0 3 14 23 22 21 20 19 18 17 16 15 14 13 + 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 31 30 29 28 27 24 6 25 3 3 18 17 14 13 6 5 2 1 8 3 9 3 22 21 12 + 11 8 7 6 9 0 3 26 25 1 10 9 1 23 20 19 0 3 3 0 16 15 4 3 0 + 3 14 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 + 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 24 17 16 15 12 11 10 9 8 5 4 3 12 6 1 3 0 0 26 25 23 22 19 18 2 + 1 6 7 0 1 4 48 200 27 21 20 0 3 0 14 13 7 6 0 3 14 27 26 25 24 + 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 18 11 10 3 4 4 1 3 0 0 17 16 13 12 9 8 5 4 6 7 6 20 19 2 1 + 6 3 0 2 4 48 200 21 0 1 0 15 14 7 6 0 3 14 11 10 2 18 2 3 21 + 20 17 16 15 14 13 12 8 13 18 9 8 7 6 5 4 1 0 8 13 2 0 0 19 18 + 10 1 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 100 values pushed */ + 25 22 2 23 6 3 18 11 10 3 4 4 1 3 0 0 17 16 13 12 9 8 5 4 6 + 7 6 20 19 2 1 6 3 0 2 4 48 200 24 23 1 21 0 1 2 0 15 14 7 6 + 0 3 14 25 22 11 10 4 18 2 3 24 23 21 20 17 16 15 14 13 12 10 13 18 9 + 8 7 6 5 4 1 0 8 13 2 0 0 19 18 10 1 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 108 values pushed */ + 29 28 27 26 25 22 6 23 6 3 18 11 10 3 4 4 1 3 0 0 17 16 13 12 9 + 8 5 4 6 7 6 20 19 2 1 6 3 0 2 4 48 200 24 23 1 21 0 1 2 0 + 15 14 7 6 0 3 14 28 27 23 11 10 5 18 2 3 26 25 24 21 20 17 16 15 14 + 13 12 11 13 18 29 22 9 8 7 6 5 4 1 0 10 13 2 0 0 19 18 10 1 2 + 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 124 values pushed */ + 18 11 10 3 4 4 1 3 0 0 29 26 25 22 10 3 23 17 16 13 12 9 8 5 4 + 6 7 6 20 19 2 1 6 3 0 3 4 48 200 28 27 24 23 3 21 0 1 2 0 15 + 14 7 6 0 3 14 21 20 14 13 12 5 28 26 3 11 10 2 18 24 3 9 8 7 3 + 2 22 3 17 16 15 3 13 28 6 5 4 1 0 5 13 22 0 0 27 26 10 1 28 25 + 24 10 1 22 19 18 10 1 2 3 4 48 200 29 28 1 23 22 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 12 11 5 4 4 2 1 3 0 0 8 3 2 6 2 6 10 9 1 20 2 0 2 4 48 + 200 13 0 1 0 7 6 0 14 8 7 2 12 10 3 9 2 2 10 3 3 1 0 5 0 + 0 11 10 6 1 12 4 3 6 1 5 2 4 48 200 13 12 1 6 5 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 17 14 2 15 6 3 12 11 5 4 4 2 1 3 0 0 8 3 2 6 2 6 10 9 1 + 20 2 0 2 4 48 200 16 15 1 13 0 1 2 0 7 6 0 14 8 7 2 12 10 3 + 17 16 15 14 9 2 6 10 3 3 1 0 5 0 0 11 10 6 1 12 4 3 6 1 5 + 2 4 48 200 13 12 1 6 5 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 97 values pushed */ + 12 11 5 4 4 2 1 3 21 20 19 18 17 14 6 13 15 0 0 8 3 2 6 2 6 + 10 9 1 20 2 0 2 4 48 200 16 15 1 13 0 1 2 0 7 6 0 14 14 8 7 + 3 12 10 3 21 20 19 18 16 15 9 2 8 10 3 3 17 3 5 2 1 0 5 0 0 + 11 10 6 1 12 4 3 6 1 5 2 4 48 200 13 12 1 6 5 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 12 11 5 4 4 2 1 3 0 0 17 14 10 1 15 8 3 2 6 2 6 10 9 1 20 + 2 0 3 4 48 200 16 15 1 13 0 1 2 0 7 6 0 14 8 7 2 12 10 3 2 + 10 16 2 9 14 3 2 1 0 5 0 0 17 16 10 1 14 11 10 6 1 12 4 3 6 + 1 5 3 4 48 200 15 14 1 13 12 1 6 5 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 38 14 2 14 5 21 48 200 21 1 2 2 1 32 31 30 27 26 25 19 18 17 16 + 12 11 10 0 14 1 28 3 0 29 28 1 0 14 0 0 36 39 6 48 200 29 25 0 2 + 32 17 16 10 4 0 18 3 28 27 2 13 25 6 18 0 0 31 30 12 11 0 10 4 25 + 1 4 48 200 26 25 1 19 18 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 12 values pushed */ + 3 0 1 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 111 values pushed */ + 0 0 60 5 31 53 30 4 40 14 45 18 5 25 48 200 45 2 31 1 25 1 4 2 1 + 29 23 22 21 20 14 6 1 12 3 0 35 12 36 2 1 47 43 42 0 4 36 2 3 0 + 0 0 49 48 37 36 6 3 12 1 4 48 200 56 55 13 12 3 0 14 0 0 51 24 8 + 48 200 56 55 37 36 35 29 0 7 42 13 3 49 21 20 12 4 13 22 3 8 22 43 42 + 1 48 47 14 13 3 23 22 1 3 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 6 1 1 1 4 48 200 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 0 0 59 5 17 51 14 3 48 200 17 0 3 2 1 53 25 11 3 0 32 3 0 45 43 + 41 40 39 35 34 31 30 29 27 1 12 32 0 3 33 32 1 42 0 1 2 0 14 0 0 + 61 13 13 57 13 21 47 13 7 48 200 53 45 43 42 41 40 39 35 34 33 32 31 30 29 + 27 25 21 13 11 7 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 2 1 4 1 0 1 4 48 200 3 0 1 0 14 0 0 3 2 4 1 0 1 4 + 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 18 values pushed */ + 4 1 2 13 0 5 3 2 0 3 0 14 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 24 16 5 11 16 18 48 200 5 13 18 0 14 13 1 1 0 1 2 0 14 14 13 + 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 17 1 32 31 29 28 26 25 24 22 21 20 19 17 15 12 11 7 6 5 4 2 1 0 22 + 13 2 3 0 14 13 0 14 32 31 29 28 26 25 24 22 21 20 19 15 14 13 12 11 9 + 7 6 5 4 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 0 0 58 14 22 50 17 30 44 5 2 36 5 10 48 200 10 0 2 2 30 1 48 47 32 + 30 18 17 14 13 12 9 0 15 3 0 22 1 46 22 0 3 15 2 3 0 16 15 1 0 + 14 0 0 54 16 26 40 6 6 48 200 15 14 2 13 12 32 18 26 6 0 0 0 48 47 + 46 17 16 0 6 5 12 1 4 48 200 13 12 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 28 30 10 22 14 18 48 200 18 2 10 1 1 3 2 2 4 1 3 0 1 30 20 + 6 3 1 0 3 0 5 4 1 1 0 1 2 0 14 0 0 26 39 14 48 200 14 0 4 + 3 1 0 0 30 20 6 5 0 10 4 1 1 4 48 200 2 1 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 3 2 1 1 0 1 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 2 1 1 3 0 1 2 0 14 0 0 3 2 16 1 0 1 4 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 52 51 40 29 28 27 26 15 14 13 12 1 0 14 0 0 49 19 5 31 19 22 17 13 36 + 10 13 44 48 200 52 51 44 40 36 29 28 27 26 22 15 14 13 12 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 52 51 40 29 28 27 26 15 14 13 12 1 0 14 0 0 49 19 5 31 19 22 17 13 36 + 10 13 44 48 200 52 51 44 40 36 29 28 27 26 22 15 14 13 12 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 0 0 4 3 6 1 1 6 5 6 1 0 2 4 48 200 2 1 1 7 0 1 2 0 14 + 7 6 3 2 4 13 4 0 0 5 4 12 1 0 1 4 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 0 0 5 4 6 1 6 3 2 6 1 0 2 4 48 200 7 6 1 1 0 1 2 0 14 + 6 5 2 1 4 13 3 0 0 4 3 12 1 0 1 4 48 200 7 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 17 values pushed */ + 0 0 3 14 10 48 200 10 6 5 1 0 14 6 5 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 34 values pushed */ + 6 5 1 7 4 1 2 1 1 3 0 1 4 0 14 0 0 7 6 3 2 16 3 0 1 + 4 48 200 5 4 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 15 values pushed */ + 0 0 0 8 48 200 8 14 0 0 4 12 48 200 12 + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 48 values pushed */ + 0 0 25 14 2 17 5 10 48 200 10 1 2 2 1 1 27 15 14 13 12 0 6 1 2 + 3 0 0 14 0 0 21 39 6 48 200 15 14 6 12 27 0 1 13 12 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 22 values pushed */ + 7 6 5 4 3 0 6 13 1 2 1 1 0 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 0 0 25 14 2 17 5 10 48 200 10 1 2 2 1 35 34 33 32 31 28 6 29 1 3 + 0 1 1 27 15 14 13 12 0 6 1 2 3 0 0 30 29 1 0 14 0 0 21 39 6 + 48 200 31 0 35 34 33 32 30 29 28 15 14 9 13 6 12 27 0 1 13 12 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 25 14 2 17 5 10 48 200 10 1 2 2 1 1 27 15 14 13 12 0 6 1 2 + 3 0 0 0 0 31 28 10 1 29 1 4 48 200 30 29 0 14 0 0 21 39 6 48 200 + 15 14 2 12 30 3 6 28 0 0 31 30 10 1 28 1 4 48 200 29 28 1 27 0 1 + 13 12 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 29 values pushed */ + 0 0 15 32 10 48 200 19 13 12 10 2 1 0 14 0 0 17 20 6 48 200 19 13 12 + 6 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 64 values pushed */ + 28 27 26 25 23 22 20 19 17 16 15 14 12 11 10 9 1 0 14 0 0 32 39 5 48 + 200 17 16 2 14 11 3 5 0 0 0 28 27 10 9 1 0 6 5 11 1 4 48 200 23 + 22 1 15 14 1 26 25 20 19 12 11 5 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 22 values pushed */ + 7 6 5 4 3 0 6 13 1 2 1 1 0 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 7 4 8 1 5 2 1 8 1 0 2 4 48 200 3 0 1 0 6 5 1 14 0 + 0 7 6 3 2 8 3 0 1 4 48 200 5 4 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 34 values pushed */ + 10 6 5 3 0 5 13 1 2 1 1 0 14 10 2 0 2 0 0 3 2 8 1 0 1 + 4 48 200 6 5 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 0 0 59 40 35 51 17 43 24 40 8 16 40 0 48 200 8 2 0 0 43 35 1 1 61 + 49 48 47 46 43 35 32 8 0 2 3 0 0 14 0 0 55 6 39 28 6 4 20 6 12 + 48 200 61 32 2 13 4 46 49 48 39 12 46 47 46 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 68 values pushed */ + 0 0 36 14 26 28 14 12 48 200 12 1 1 16 15 9 8 4 13 1 0 1 24 23 22 + 21 17 14 10 7 3 2 1 0 12 13 26 1 0 14 0 0 40 16 19 32 16 5 48 200 + 24 23 22 21 19 17 16 15 14 10 9 8 7 5 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 31 30 12 25 14 20 48 200 20 1 12 2 1 33 23 22 8 5 4 6 1 6 3 + 0 0 0 1 0 6 1 2 1 4 48 200 3 2 1 7 6 1 2 0 14 0 0 29 39 + 16 48 200 6 5 2 13 3 2 1 16 0 0 0 33 23 22 8 7 0 10 5 3 1 4 + 48 200 4 3 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 42 values pushed */ + 1 10 9 8 7 4 3 2 1 8 5 2 3 0 11 0 1 0 6 5 0 14 11 10 7 + 6 5 4 1 0 8 8 2 3 9 8 1 3 2 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 1 18 17 16 15 14 13 12 11 8 7 6 5 4 3 2 1 16 9 2 3 0 19 0 1 + 0 10 9 0 14 19 10 2 12 11 3 9 0 2 1 2 3 0 0 18 15 14 11 14 3 + 1 1 4 48 200 17 16 13 12 3 8 5 4 1 3 7 6 3 2 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 109 values pushed */ + 0 0 31 30 5 25 14 13 48 200 13 1 5 2 1 42 39 38 37 35 34 6 16 1 3 + 0 1 33 23 21 20 15 1 6 1 0 3 0 0 0 17 16 6 1 18 1 4 48 200 41 + 40 19 18 3 22 0 1 2 0 14 0 0 29 39 9 48 200 38 37 22 21 4 41 34 3 + 18 17 9 0 0 0 40 39 35 34 10 3 41 33 23 16 15 1 0 10 5 19 2 4 48 + 200 42 41 1 20 19 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 0 0 39 30 5 33 14 13 48 200 13 1 5 2 1 41 31 29 28 15 1 6 1 0 3 + 0 0 0 21 20 6 1 22 27 26 17 16 6 3 18 2 4 48 200 23 22 1 25 24 19 + 18 3 30 0 1 3 0 14 0 0 37 39 9 48 200 30 29 26 25 4 13 23 22 21 18 + 17 4 13 9 0 0 0 41 31 20 19 16 15 1 0 10 7 23 1 4 48 200 28 27 24 + 23 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 29 values pushed */ + 0 0 24 5 8 16 5 0 48 200 0 0 1 8 0 0 14 0 0 28 6 4 20 6 12 + 48 200 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 7 4 3 0 10 3 1 1 4 48 200 6 5 2 1 0 3 14 0 0 5 4 10 + 1 6 3 2 10 1 0 2 4 48 200 7 6 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 59 values pushed */ + 0 0 11 8 4 1 9 6 5 4 1 4 3 0 7 1 1 3 4 48 200 10 9 1 7 + 4 1 2 1 1 3 0 14 0 0 11 10 7 6 4 3 4 1 4 48 200 9 8 5 4 + 3 3 2 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 91 values pushed */ + 48 47 46 39 38 37 28 27 26 24 23 22 21 19 18 17 16 9 8 6 5 4 3 1 0 + 14 0 0 52 13 12 43 13 33 48 200 26 24 23 3 21 18 3 12 12 6 5 3 0 3 + 3 33 21 0 0 28 27 19 18 6 3 16 46 39 38 37 6 3 0 2 4 48 200 22 21 + 1 48 47 17 16 3 9 8 1 0 3 4 3 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 0 0 3 0 10 1 1 1 4 48 200 2 1 0 14 0 0 3 2 10 1 0 1 4 48 + 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 53 values pushed */ + 0 0 4 3 6 1 5 8 7 2 1 6 3 0 2 4 48 200 9 0 1 0 6 5 1 + 14 9 8 2 13 6 5 4 1 0 4 13 2 0 0 7 6 10 1 2 1 4 48 200 3 + 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 0 0 5 5 18 48 200 1 14 9 2 10 2 3 0 1 3 2 1 0 4 13 18 2 0 + 0 0 11 10 6 1 12 1 4 48 200 13 12 1 14 12 11 3 2 4 9 0 3 0 0 + 10 9 10 1 13 1 4 48 200 14 13 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 2 1 4 1 0 1 4 48 200 3 0 1 0 14 0 0 3 2 4 1 0 1 4 + 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 25 5 18 5 14 10 48 200 18 1 10 2 1 23 1 21 2 0 20 21 0 2 1 + 8 7 2 0 2 3 0 0 0 1 0 6 1 21 1 4 48 200 22 21 1 0 14 20 0 + 2 13 7 23 22 21 1 4 13 14 7 8 7 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 0 0 32 14 39 25 5 18 5 14 10 48 200 18 1 10 2 1 23 1 21 2 0 20 21 + 0 2 1 8 7 2 0 2 3 0 1 35 34 30 29 4 13 39 1 0 0 0 1 0 6 + 1 21 1 4 48 200 22 21 1 0 14 20 0 2 13 7 35 34 30 29 23 22 21 1 8 + 13 14 7 8 7 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 53 values pushed */ + 0 0 54 5 25 38 5 7 48 200 25 2 7 0 1 1 46 45 32 16 0 5 0 2 3 + 0 0 14 0 0 58 39 21 50 28 29 42 19 3 34 19 11 48 200 46 45 32 29 21 16 + 11 3 0 + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 10 9 6 5 2 1 4 5 0 1 4 48 200 11 8 7 4 3 0 5 0 14 0 + 0 9 8 4 1 10 7 6 4 1 4 3 2 4 1 0 3 4 48 200 11 10 1 5 4 + 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 6 1 1 1 4 48 200 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 200 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 28 30 11 23 17 16 48 200 11 1 32 31 30 26 7 4 3 2 1 9 5 0 3 + 25 21 20 19 18 5 13 16 0 33 0 1 0 6 5 1 14 33 32 21 20 4 25 18 3 + 5 4 1 0 4 13 2 0 0 31 30 7 6 10 3 2 1 4 48 200 26 25 1 19 18 + 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 0 0 7 4 7 1 5 2 1 7 1 0 2 4 48 200 6 5 1 3 0 1 2 0 14 + 7 6 3 2 3 5 4 1 0 3 2 0 + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 25 5 18 5 14 10 48 200 18 1 10 2 1 23 1 21 2 0 20 21 0 2 1 + 8 7 2 0 2 3 0 0 0 1 0 6 1 21 1 4 48 200 22 21 1 0 14 20 0 + 2 13 7 23 22 21 1 4 13 14 7 8 7 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 40 5 22 32 5 30 48 200 30 1 22 2 1 1 14 11 7 5 4 3 2 7 0 + 1 3 0 0 1 1 0 1 2 2 0 0 1 13 12 9 8 4 13 0 0 14 0 0 44 + 39 18 36 39 26 48 200 26 18 14 13 12 11 9 8 7 5 4 3 2 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 9 8 5 4 4 6 1 3 0 0 2 1 9 1 0 1 4 48 200 3 0 1 0 7 6 + 0 14 9 4 2 7 5 3 0 0 8 7 10 1 5 3 2 4 1 0 2 4 48 200 6 + 5 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 1 9 4 2 1 2 3 0 1 8 5 2 2 6 3 0 0 0 2 1 9 1 0 1 4 + 48 200 7 6 1 0 3 0 1 14 9 4 2 5 7 3 0 0 6 5 10 1 7 1 0 + 4 1 2 2 4 48 200 8 7 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 88 values pushed */ + 0 0 18 5 11 48 200 22 16 15 14 13 7 6 13 11 5 0 0 26 25 4 3 20 3 + 5 28 27 2 1 6 3 0 2 4 48 200 24 23 6 5 3 29 0 1 2 0 14 29 28 + 25 24 16 15 6 13 22 3 5 4 1 0 4 13 2 0 0 27 26 23 22 10 3 2 1 + 4 48 200 14 13 1 7 6 3 2 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 129 values pushed */ + 0 0 18 40 11 48 200 16 35 14 2 20 7 2 14 5 3 32 31 28 27 24 23 2 1 + 8 3 0 3 13 11 35 0 0 37 34 15 14 10 3 35 30 29 4 3 20 3 5 2 4 + 48 200 36 35 1 22 21 6 5 3 33 26 25 0 3 3 0 14 27 26 2 28 13 3 33 + 32 16 15 4 13 20 3 25 24 2 13 22 5 4 1 0 4 13 2 0 0 35 34 29 28 + 10 3 22 31 30 21 20 10 3 2 2 4 48 200 37 36 23 22 3 14 13 1 7 6 3 + 2 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 129 values pushed */ + 0 0 18 40 11 48 200 16 35 14 2 20 7 2 14 5 3 32 31 28 27 24 23 2 1 + 8 3 0 3 13 11 35 0 0 37 34 15 14 10 3 35 30 29 4 3 20 3 5 2 4 + 48 200 36 35 1 22 21 6 5 3 33 26 25 0 3 3 0 14 27 26 2 28 13 3 33 + 32 16 15 4 13 20 3 25 24 2 13 22 5 4 1 0 4 13 2 0 0 35 34 29 28 + 10 3 22 31 30 21 20 10 3 2 2 4 48 200 37 36 23 22 3 14 13 1 7 6 3 + 2 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 200 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 13 5 22 5 5 30 48 200 30 2 22 1 22 20 15 3 2 1 6 18 2 3 0 + 1 0 2 0 0 0 19 18 22 1 16 1 4 48 200 17 16 0 14 0 0 9 39 26 48 + 200 3 2 2 19 15 3 26 17 0 0 20 19 14 1 15 1 4 48 200 18 17 1 16 15 + 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 108 values pushed */ + 0 0 29 40 16 48 200 33 27 14 3 19 0 3 26 25 22 21 9 8 5 4 8 2 6 + 3 16 19 0 0 11 10 3 2 20 3 0 1 4 48 200 20 19 1 13 12 1 0 3 24 + 23 7 6 3 3 0 14 25 24 6 5 2 1 6 19 0 3 23 22 2 13 20 12 11 8 + 7 4 13 9 0 0 27 26 19 10 2 20 33 4 3 0 10 3 9 2 4 48 200 21 20 + 1 14 13 10 9 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 108 values pushed */ + 0 0 29 40 16 48 200 33 27 14 3 19 0 3 26 25 22 21 9 8 5 4 8 2 6 + 3 16 19 0 0 11 10 3 2 20 3 0 1 4 48 200 20 19 1 13 12 1 0 3 24 + 23 7 6 3 3 0 14 25 24 6 5 2 1 6 19 0 3 23 22 2 13 20 12 11 8 + 7 4 13 9 0 0 27 26 19 10 2 20 33 4 3 0 10 3 9 2 4 48 200 21 20 + 1 14 13 10 9 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 14 25 7 48 200 7 0 1 12 11 10 3 0 3 3 0 1 9 0 0 0 0 20 + 19 2 1 6 3 3 1 4 48 200 18 17 4 3 3 21 0 1 2 0 14 21 20 19 18 + 17 12 11 4 3 2 1 0 12 13 9 10 9 1 0 + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 79 values pushed */ + 17 3 2 2 0 0 16 15 6 5 2 7 4 0 14 13 10 9 6 3 11 2 4 48 200 + 8 7 1 0 3 12 11 1 2 0 4 3 0 14 11 10 2 6 4 3 15 13 12 3 4 + 0 1 3 0 0 17 16 14 0 12 3 4 1 4 48 200 7 6 1 9 8 5 4 3 2 + 1 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 13 5 4 2 0 0 10 9 3 2 6 3 4 1 4 48 200 6 5 1 12 11 8 7 4 + 4 1 0 1 3 0 14 9 8 2 13 0 11 5 4 3 4 13 1 0 0 13 12 2 1 + 16 3 0 1 4 48 200 10 7 6 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 2 1 1 3 0 1 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 2 1 1 3 0 1 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 2 1 1 3 0 1 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 89 values pushed */ + 0 0 61 5 24 53 5 13 46 5 36 48 200 13 1 24 1 26 24 18 17 15 5 6 1 + 31 3 0 0 31 41 2 1 16 1 0 36 41 0 0 32 31 7 1 41 1 4 48 200 52 + 41 1 0 14 0 0 65 19 20 57 28 9 50 28 34 44 28 38 29 15 2 48 200 52 41 + 38 34 32 31 26 20 18 17 16 15 9 5 2 0 + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 110 values pushed */ + 0 0 61 5 24 53 5 13 46 5 36 48 200 13 1 1 76 75 74 73 72 69 16 7 70 + 1 3 0 24 1 26 24 18 17 15 5 6 1 31 3 0 0 31 41 2 36 41 0 0 32 + 31 7 1 41 1 4 48 200 71 70 1 52 41 1 2 0 14 0 0 65 19 20 57 28 9 + 50 28 34 44 28 38 29 15 2 48 200 76 75 74 73 72 71 70 69 52 41 38 34 32 31 + 26 20 18 17 16 15 9 5 2 0 + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 135 values pushed */ + 0 0 61 5 24 53 5 13 46 5 36 48 200 13 1 1 16 75 1 2 0 24 1 26 24 + 18 17 15 5 6 1 31 3 0 0 31 41 2 77 74 73 72 70 69 6 13 75 36 41 0 + 0 32 31 7 1 41 1 4 48 200 76 75 1 52 41 1 2 0 14 0 0 65 19 20 57 + 28 9 50 28 34 44 28 38 29 15 2 48 200 73 72 31 3 69 76 3 52 32 18 17 16 + 15 6 13 34 20 69 41 26 5 0 4 13 38 9 2 3 12 76 0 0 75 74 70 69 10 + 3 76 1 4 48 200 77 76 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 125 values pushed */ + 0 0 61 5 24 53 5 13 46 5 36 48 200 13 1 1 16 69 1 2 0 24 1 26 24 + 18 17 15 5 6 1 31 3 0 0 31 41 2 36 41 0 0 72 69 10 1 70 32 31 7 + 1 41 2 4 48 200 52 41 1 0 71 70 0 14 0 0 65 19 20 57 28 9 50 28 34 + 44 28 38 29 15 2 48 200 31 71 69 2 52 32 18 17 16 15 6 13 34 20 71 41 26 + 5 0 4 13 38 9 2 3 12 69 0 0 72 71 10 1 69 1 4 48 200 70 69 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 53 5 9 36 5 29 48 200 29 2 59 58 57 34 33 32 31 3 2 1 10 13 9 + 0 60 0 1 0 14 0 0 51 28 13 38 15 25 18 13 46 48 200 46 60 59 46 3 31 + 57 3 34 33 2 13 25 13 31 1 0 2 0 0 58 57 10 1 2 1 4 48 200 32 31 + 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 12 values pushed */ + 1 0 2 3 2 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 18 values pushed */ + 5 4 3 2 1 0 14 5 2 2 13 0 4 3 1 0 3 0 + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 11 10 9 8 7 6 5 4 3 2 1 0 14 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 11 10 9 8 7 6 5 4 3 2 1 0 14 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 5 4 3 2 1 0 14 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 5 4 3 2 1 0 14 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 0 0 22 30 11 48 200 11 1 1 4 3 2 5 1 3 0 1 26 25 24 20 19 18 15 + 14 13 7 2 1 12 1 0 3 0 6 5 1 27 17 16 0 3 2 0 14 27 26 18 17 + 4 19 6 3 16 15 2 13 13 5 4 1 0 4 13 2 0 0 20 19 10 1 13 25 24 + 7 6 10 3 2 2 4 48 200 14 13 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 116 values pushed */ + 0 0 15 30 4 48 200 4 1 29 28 2 30 26 3 1 23 22 19 18 17 13 12 11 8 + 7 6 0 12 1 9 3 0 0 0 35 34 25 24 33 3 26 1 4 48 200 31 30 1 33 + 32 27 26 3 21 20 10 9 3 3 0 14 34 33 20 19 11 10 6 12 0 3 9 8 2 + 13 6 30 29 26 25 22 21 6 13 23 0 0 13 12 10 1 6 35 32 31 18 17 0 10 + 5 23 2 4 48 200 7 6 1 28 27 24 23 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 121 values pushed */ + 0 0 22 35 11 48 200 11 1 35 34 33 32 31 28 6 29 5 3 1 4 3 2 5 1 + 3 0 1 26 25 24 20 19 18 15 14 13 7 2 1 12 1 0 3 0 30 29 1 6 5 + 1 27 17 16 0 3 3 0 14 32 31 2 13 19 3 34 33 30 29 27 26 18 17 8 19 + 6 3 35 28 2 6 2 3 16 15 2 13 13 5 4 1 0 4 13 2 0 0 20 19 10 + 1 13 25 24 7 6 10 3 2 2 4 48 200 14 13 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 22 values pushed */ + 7 4 3 0 4 13 1 6 5 2 1 3 0 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 200 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 200 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 13 10 4 1 11 4 3 6 1 5 8 7 2 1 6 3 0 3 4 48 200 12 11 + 1 9 0 1 2 0 6 5 1 14 9 8 2 13 12 5 4 1 0 4 13 10 0 0 13 + 12 9 1 10 7 6 10 1 2 2 4 48 200 11 10 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 0 0 13 14 20 48 200 16 15 11 10 4 13 20 5 0 0 4 3 6 1 5 8 7 2 + 1 6 3 0 2 4 48 200 9 0 1 0 6 5 1 14 16 15 9 8 4 13 6 11 10 + 5 4 1 0 6 13 2 0 0 7 6 10 1 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 0 0 17 14 13 10 10 3 11 4 3 6 1 5 8 7 2 1 6 3 0 3 4 48 200 + 9 0 1 0 16 15 12 11 0 3 6 5 1 14 9 8 2 13 16 5 4 1 0 4 13 + 10 0 0 15 14 10 1 16 13 12 10 1 10 7 6 10 1 2 3 4 48 200 17 16 1 + 11 10 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 120 values pushed */ + 0 0 20 17 13 48 200 11 1 0 2 22 18 17 16 15 5 13 13 0 0 0 33 30 29 + 26 4 3 27 24 23 4 3 6 3 5 8 7 2 1 6 3 0 3 4 48 200 32 31 28 + 27 3 9 0 1 2 0 25 10 6 5 1 3 14 25 24 18 17 9 8 6 30 15 3 5 + 4 1 0 4 13 26 0 0 31 30 9 1 10 23 22 10 1 10 29 28 7 6 10 3 2 + 3 4 48 200 27 26 1 33 32 11 10 3 16 15 1 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 0 0 31 5 14 19 5 26 48 200 14 0 26 1 33 26 10 3 0 5 3 0 1 22 21 + 2 13 0 0 0 0 4 3 6 1 5 8 7 2 1 6 3 0 2 4 48 200 9 0 1 + 0 6 5 1 14 22 21 9 8 4 13 6 33 10 5 4 1 0 6 13 2 0 0 7 6 + 10 1 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 0 0 5 5 18 48 200 1 14 9 2 10 2 3 0 1 3 2 1 0 4 13 18 2 0 + 0 0 23 20 4 1 21 11 10 6 1 12 2 4 48 200 22 21 1 0 13 12 1 14 12 + 11 3 2 4 20 0 3 0 0 21 20 9 1 22 10 9 10 1 13 2 4 48 200 23 22 + 1 14 13 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 96 values pushed */ + 0 0 5 5 18 48 200 27 26 25 24 23 20 6 21 12 3 1 14 9 2 10 2 3 0 + 1 3 2 1 0 4 13 18 2 0 0 0 11 10 6 1 12 1 4 48 200 22 21 1 0 + 13 12 1 14 26 25 22 3 13 9 3 27 21 20 12 11 3 2 7 9 0 3 24 23 2 + 13 13 0 0 10 9 10 1 13 1 4 48 200 14 13 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 11 10 2 12 18 3 24 23 22 15 14 9 8 5 4 3 2 1 12 16 0 3 0 0 21 + 20 17 16 6 3 18 1 4 48 200 13 12 1 25 7 6 0 3 2 0 19 18 1 14 25 + 24 23 22 21 20 19 18 17 16 15 6 5 2 1 0 16 13 3 12 11 8 7 4 13 9 + 0 0 14 13 4 3 10 3 9 1 4 48 200 10 9 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 24 23 22 15 14 9 8 5 4 3 2 1 12 10 0 3 0 0 21 20 17 16 11 10 6 + 5 12 1 4 48 200 25 7 6 0 3 0 19 18 13 12 1 3 14 25 24 23 22 21 20 + 19 18 17 16 15 6 5 2 1 0 16 13 3 12 11 8 7 4 13 9 0 0 14 13 4 + 3 10 3 9 1 4 48 200 10 9 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 54 values pushed */ + 0 0 4 3 6 1 5 8 7 2 1 6 3 0 2 4 48 200 6 5 1 9 0 1 2 + 0 14 9 8 2 13 6 5 4 1 0 4 13 2 0 0 7 6 10 1 2 1 4 48 200 + 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 18 15 14 13 11 10 6 3 1 3 0 0 4 3 6 1 5 8 7 2 1 6 3 0 2 + 4 48 200 17 16 6 5 3 9 0 1 2 0 14 14 13 9 8 4 17 10 3 5 4 1 + 0 4 13 2 0 0 16 15 11 10 10 3 17 7 6 10 1 2 2 4 48 200 18 17 1 + 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 0 0 13 10 10 1 11 4 3 6 1 5 8 7 2 1 6 3 0 3 4 48 200 12 11 + 1 6 5 1 9 0 1 3 0 14 9 8 2 12 10 3 5 4 1 0 4 13 2 0 0 + 11 10 10 1 12 7 6 10 1 2 2 4 48 200 13 12 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 16 values pushed */ + 5 4 3 2 1 0 14 4 1 0 5 3 2 0 3 0 + CALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 6 5 2 1 16 3 3 1 4 48 200 4 3 1 7 0 1 2 0 14 0 0 1 + 0 16 1 4 1 4 48 200 7 6 5 4 3 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 18 5 11 48 200 22 16 15 14 13 7 6 13 11 5 0 0 4 3 20 1 5 24 + 23 2 1 6 3 0 2 4 48 200 6 5 1 25 0 1 2 0 14 25 24 16 15 4 13 + 22 3 5 4 1 0 4 13 2 0 0 23 22 10 1 2 1 4 48 200 14 13 1 7 6 + 3 2 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 14 13 12 11 6 5 4 3 8 7 1 3 0 0 8 7 6 1 9 16 15 2 1 6 3 + 0 2 4 48 200 10 9 1 17 0 1 2 0 14 17 16 2 13 12 9 8 1 0 4 13 + 4 0 0 15 14 11 10 10 3 2 1 4 48 200 13 12 1 7 6 3 2 3 5 4 1 + 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 109 values pushed */ + 0 0 41 30 11 28 30 19 48 200 19 1 11 1 45 44 43 39 36 35 34 26 23 22 21 + 15 7 4 3 2 1 17 5 0 3 46 38 37 25 24 0 5 0 6 5 1 14 37 36 2 + 25 34 3 15 34 38 2 46 45 2 38 6 3 24 23 2 13 21 5 4 1 0 4 13 2 + 0 0 26 25 12 1 21 35 34 12 1 38 44 43 7 6 12 3 2 3 4 48 200 22 21 + 1 39 38 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 6 1 1 1 4 48 200 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 2 16 1 0 1 4 48 200 1 0 1 0 14 2 1 1 3 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 200 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 6 30 21 48 200 21 2 26 17 14 13 10 9 8 4 1 0 10 2 15 3 23 15 + 24 2 16 15 1 25 24 1 2 0 12 11 3 2 1 3 14 11 10 2 8 3 3 15 14 + 2 13 12 2 1 0 0 0 17 16 9 8 10 3 12 24 23 4 3 10 3 0 2 4 48 + 200 13 12 1 26 25 0 2 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 33 values pushed */ + 1 10 8 7 6 5 4 3 2 1 0 10 13 2 0 1 11 9 2 0 14 11 10 9 8 + 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 0 0 22 30 11 48 200 11 1 26 25 24 20 19 18 15 14 13 7 4 3 2 1 14 5 + 0 3 27 17 16 0 3 0 6 5 1 14 27 26 18 17 4 19 6 3 16 15 2 13 13 + 5 4 1 0 4 13 2 0 0 20 19 10 1 13 25 24 7 6 10 3 2 2 4 48 200 + 14 13 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 110 values pushed */ + 0 0 22 30 11 48 200 11 1 36 33 32 31 29 28 6 34 5 3 26 25 24 20 19 18 + 15 14 13 7 4 3 2 1 14 5 0 3 35 34 1 27 17 16 0 3 2 0 6 5 1 + 14 27 26 18 17 4 19 6 3 32 31 5 4 1 0 6 2 28 3 16 15 2 13 13 0 + 0 36 35 10 1 28 20 19 10 1 13 25 24 7 6 10 3 2 3 4 48 200 34 33 29 + 28 3 14 13 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 200 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 37 25 4 31 5 12 27 5 20 48 200 20 2 12 0 4 1 1 25 24 23 22 4 + 0 6 0 2 3 0 0 14 0 0 41 26 16 35 24 8 48 200 25 24 0 3 13 16 22 + 8 22 23 22 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 14 13 10 9 4 13 7 27 24 23 0 4 13 1 0 0 31 30 18 17 6 5 6 5 7 + 29 28 20 19 4 3 6 5 1 2 4 48 200 16 15 12 11 8 7 5 26 25 22 21 2 + 1 5 2 0 14 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 + 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 20 5 8 16 5 0 48 200 8 2 0 1 14 0 0 22 39 4 18 39 12 48 200 + 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 27 14 34 20 5 8 16 5 0 48 200 8 2 0 1 1 30 29 25 24 4 13 34 + 1 0 14 0 0 22 36 4 18 36 12 48 200 30 29 25 24 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 44 5 14 32 5 2 28 5 10 21 14 26 48 200 26 2 14 1 10 1 2 2 1 + 42 12 2 1 40 3 0 16 40 17 2 1 24 23 19 0 4 17 2 3 0 0 0 18 17 + 6 1 40 1 4 48 200 41 40 1 0 14 0 0 30 19 6 48 200 42 41 40 19 18 12 + 0 7 13 36 6 16 24 23 17 16 3 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 0 0 5 17 10 48 200 10 8 7 1 0 14 0 0 3 20 12 48 200 12 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 6 5 4 3 4 13 1 0 0 8 7 2 1 6 3 0 1 4 48 200 9 0 1 0 14 + 9 8 7 6 4 13 2 5 4 1 0 4 13 2 3 2 1 0 + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 108 values pushed */ + 0 0 15 22 48 200 22 43 42 41 22 20 5 1 40 3 28 27 19 18 17 10 5 7 40 + 37 3 0 0 38 37 16 1 4 1 4 48 200 45 40 1 39 4 1 44 2 1 2 3 0 + 1 4 0 14 0 0 13 34 24 48 200 37 28 27 18 17 10 2 1 8 24 4 3 3 44 + 40 2 43 42 0 3 13 40 0 0 45 44 16 1 40 1 4 48 200 41 40 1 39 38 24 + 2 20 19 5 4 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 105 values pushed */ + 3 2 1 3 4 15 3 23 0 14 2 0 0 22 21 18 17 14 6 4 12 1 4 48 200 + 16 15 1 20 19 13 12 3 11 10 1 8 7 4 2 9 6 1 5 0 1 6 0 14 8 + 10 11 2 21 15 14 13 7 5 11 4 3 9 4 0 2 19 18 2 13 10 6 3 2 3 + 13 0 0 0 23 22 12 11 16 3 10 5 4 16 1 0 2 4 48 200 20 17 16 10 3 + 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 52 values pushed */ + 6 5 4 3 4 13 1 0 0 9 0 6 1 1 1 4 48 200 8 7 2 1 3 0 14 + 9 8 2 13 6 5 4 1 0 4 13 2 0 0 7 6 37 1 2 1 4 48 200 3 2 + 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 4 3 2 1 4 13 0 5 0 1 0 14 3 2 0 0 0 1 0 16 1 4 1 4 48 + 200 5 4 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 105 values pushed */ + 0 0 36 5 2 14 5 21 48 200 21 0 1 25 19 16 3 0 17 3 0 32 31 30 10 + 9 8 6 17 26 3 0 26 28 2 2 28 0 0 29 28 6 1 26 1 4 48 200 18 17 + 1 27 26 1 2 0 14 0 0 34 19 6 48 200 29 25 0 2 32 17 16 8 4 0 18 + 3 28 27 2 13 25 6 18 0 0 31 30 10 9 0 12 4 25 1 4 48 200 26 25 1 + 19 18 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 29 values pushed */ + 0 0 24 5 8 16 5 0 48 200 0 0 1 8 0 0 14 0 0 28 19 4 20 19 12 + 48 200 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 0 0 31 5 18 24 5 7 48 200 18 2 7 1 1 1 35 29 28 22 20 12 9 1 8 + 1 2 3 0 0 1 11 10 2 13 1 0 1 21 0 2 0 14 0 0 33 39 14 26 39 + 3 48 200 35 29 28 22 21 20 14 12 11 10 9 3 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 91 values pushed */ + 0 0 33 30 14 27 14 22 48 200 22 2 14 1 1 35 25 10 7 6 5 8 2 3 0 + 1 24 2 0 2 0 0 0 5 4 1 0 6 3 2 1 4 48 200 3 2 1 0 9 8 + 1 14 0 0 31 39 18 48 200 2 1 2 13 18 0 8 7 4 3 4 13 5 0 0 35 + 25 24 10 9 0 10 5 5 1 4 48 200 6 5 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 7 0 1 1 16 2 2 0 1 18 15 14 0 4 13 2 0 0 0 17 16 6 1 12 1 + 4 48 200 13 12 0 14 12 15 17 2 5 0 0 0 16 15 6 1 13 1 0 6 1 17 + 2 4 48 200 14 13 1 18 17 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 17 values pushed */ + 21 13 12 0 14 0 0 17 39 6 48 200 21 13 12 6 0 + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 17 values pushed */ + 10 9 1 0 14 0 0 5 39 16 48 200 16 10 9 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 69 values pushed */ + 0 0 48 5 36 44 5 28 24 5 12 20 5 4 48 200 36 2 4 0 28 12 1 1 28 + 12 2 0 2 3 0 0 1 2 1 2 13 0 0 1 3 0 2 0 14 0 0 50 20 32 + 46 20 40 26 20 8 22 20 16 48 200 40 32 16 8 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 2 1 8 1 0 1 4 48 200 3 0 1 0 14 0 0 3 2 8 1 0 1 4 + 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 2 1 4 1 0 1 4 48 200 3 0 1 0 14 0 0 3 2 4 1 0 1 4 + 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 2 1 4 1 0 1 4 48 200 3 0 1 0 14 0 0 3 2 4 1 0 1 4 + 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 0 0 72 17 60 68 17 52 48 17 36 44 17 28 20 17 8 16 17 0 48 200 60 2 36 + 2 0 0 52 28 8 1 1 52 28 26 25 8 5 0 2 3 0 0 1 27 24 2 0 14 + 0 0 74 21 56 70 21 64 50 21 32 46 21 40 22 21 4 18 21 12 48 200 64 56 40 + 32 27 26 25 24 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 53 values pushed */ + 0 0 10 9 2 1 7 3 3 1 4 48 200 6 5 1 8 7 4 3 3 11 0 1 3 + 0 14 0 0 11 10 7 6 14 3 0 1 4 48 200 9 8 1 5 4 1 0 3 3 2 + 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 15 14 7 1 12 10 9 2 1 7 3 3 2 4 48 200 13 12 1 6 5 1 8 + 7 4 3 3 11 0 1 4 0 14 0 0 11 10 7 6 14 3 0 1 4 48 200 15 12 + 9 8 3 5 4 1 0 3 14 13 3 2 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 0 0 29 30 4 23 14 12 48 200 12 1 4 2 1 31 21 0 3 13 2 3 0 16 15 + 2 19 17 3 0 0 20 19 6 1 17 1 4 48 200 18 17 1 0 14 13 1 14 0 0 + 27 39 8 48 200 17 16 2 13 14 19 18 8 0 0 0 31 21 20 13 0 10 4 14 1 + 4 48 200 15 14 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 14 5 21 48 200 21 0 1 36 34 19 18 17 16 5 7 0 4 3 0 0 0 2 + 1 9 1 0 1 4 48 200 37 4 1 3 0 1 2 0 14 0 0 10 39 23 48 200 37 + 36 5 4 4 2 0 3 17 16 2 0 18 3 34 23 2 0 0 3 2 4 1 0 1 4 + 48 200 19 18 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 0 0 14 5 21 48 200 1 32 30 18 17 5 5 4 2 3 0 1 19 16 21 2 0 0 + 0 2 1 9 1 0 1 4 48 200 33 4 1 0 3 0 1 14 0 0 10 39 23 48 200 + 17 16 2 18 0 3 33 32 5 4 4 0 2 3 30 23 2 0 0 1 0 4 1 2 1 + 4 48 200 19 18 1 3 2 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 21 values pushed */ + 6 5 2 1 3 7 4 3 0 3 2 0 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 18 17 16 14 13 11 8 7 6 4 3 1 12 13 0 19 10 9 0 3 0 14 7 6 2 + 0 3 3 17 16 2 10 13 3 0 0 11 10 4 1 13 9 8 4 3 4 3 0 2 4 + 48 200 19 18 14 13 3 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 18 17 16 14 13 11 8 7 6 4 3 1 12 13 0 19 10 9 0 3 0 14 17 16 2 + 13 10 3 7 6 2 3 0 3 0 0 11 10 4 1 13 9 8 4 3 4 3 0 2 4 + 48 200 19 18 14 13 3 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 18 17 16 14 13 11 8 7 6 4 3 1 12 13 0 19 10 9 0 3 0 14 7 6 2 + 0 3 3 17 16 2 10 13 3 0 0 11 10 4 1 13 9 8 4 3 4 3 0 2 4 + 48 200 19 18 14 13 3 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 34 values pushed */ + 10 6 5 3 0 5 13 1 2 1 1 0 14 10 0 2 2 0 0 6 5 1 0 8 3 + 2 1 4 48 200 3 2 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 34 values pushed */ + 10 6 5 3 0 5 13 1 2 1 1 0 14 10 2 0 2 0 0 3 2 8 1 0 1 + 4 48 200 6 5 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 34 values pushed */ + 10 6 5 3 0 5 13 1 2 1 1 0 14 10 2 0 2 0 0 3 2 8 1 0 1 + 4 48 200 6 5 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 2 1 1 3 0 1 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 0 0 22 22 15 48 200 15 1 17 9 7 2 20 19 18 11 0 5 7 1 3 0 0 8 + 7 6 1 9 6 5 2 1 6 3 3 2 4 48 200 4 3 1 0 10 9 1 14 20 19 + 3 2 4 17 0 3 9 8 5 4 4 13 6 0 0 11 10 1 0 10 3 6 1 4 48 + 200 18 17 1 7 6 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 2 16 1 0 1 4 48 200 1 0 1 0 14 2 1 1 3 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 97 values pushed */ + 0 0 24 40 8 16 40 0 48 200 8 2 0 0 60 59 53 52 50 49 48 47 44 43 42 + 36 35 34 33 15 37 32 3 38 37 1 51 46 45 32 3 2 0 14 0 0 55 6 40 28 + 6 4 20 6 12 48 200 59 53 51 50 47 46 45 44 43 42 38 11 13 40 4 48 37 36 + 33 32 4 13 12 34 0 0 60 52 49 48 3 34 1 6 48 200 35 34 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 0 0 24 32 8 16 32 0 48 200 8 0 14 0 0 28 32 4 20 32 12 48 200 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 61 values pushed */ + 0 0 25 5 18 5 5 40 48 200 40 2 18 1 1 1 23 22 21 20 3 2 1 0 8 + 1 2 3 0 0 14 0 0 29 19 16 7 28 36 48 200 16 23 22 16 3 2 5 20 0 + 3 36 20 21 20 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 25 5 18 5 5 40 48 200 40 2 18 1 1 49 48 47 46 45 42 6 43 1 3 + 0 1 1 23 22 21 20 3 2 1 0 8 1 2 3 0 0 44 43 1 0 14 0 0 29 + 19 16 7 28 36 48 200 16 49 48 47 46 45 44 43 42 23 22 16 3 2 13 20 0 3 + 36 20 21 20 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 0 0 31 5 24 5 5 50 48 200 24 0 1 58 52 44 29 28 27 26 18 3 2 1 0 + 12 13 50 0 0 14 0 0 60 15 16 54 16 42 35 15 20 9 15 46 48 200 20 16 58 + 52 44 29 28 20 18 16 3 2 10 26 0 3 46 42 26 27 26 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 59 values pushed */ + 1 3 1 2 2 0 1 10 6 5 0 4 13 2 0 0 0 14 11 8 1 12 1 4 48 + 200 2 1 1 0 13 12 1 14 10 2 0 2 0 0 14 13 3 2 8 3 0 1 4 48 + 200 12 11 6 5 1 0 5 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 59 values pushed */ + 1 3 1 2 2 0 1 10 6 5 0 4 13 2 0 0 0 14 11 8 1 12 1 4 48 + 200 2 1 1 0 13 12 1 14 10 2 0 2 0 0 14 13 3 2 8 3 0 1 4 48 + 200 12 11 6 5 1 0 5 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 35 values pushed */ + 0 0 8 5 4 27 2 6 1 4 48 200 13 0 1 0 7 6 0 14 13 4 0 3 7 + 5 3 8 7 1 6 5 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 200 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 39 5 12 31 25 4 27 5 20 48 200 20 0 12 2 4 1 1 25 24 23 22 4 + 0 6 0 2 3 0 0 14 0 0 43 24 8 35 26 16 48 200 8 22 25 24 0 3 13 + 16 22 23 22 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 2 1 1 3 0 1 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 104 values pushed */ + 0 0 19 5 12 48 200 12 0 1 21 17 16 15 14 8 6 0 6 3 0 26 3 1 3 + 4 28 3 0 0 25 24 5 4 6 3 6 29 28 22 1 0 2 4 48 200 23 22 7 6 + 3 30 0 1 2 0 14 24 23 17 16 4 14 21 3 28 21 3 2 6 5 2 3 0 3 + 0 0 26 25 22 21 10 3 3 1 4 48 200 30 29 1 15 14 1 8 7 4 3 3 1 + 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 0 0 21 14 2 48 200 2 2 1 23 17 6 0 4 7 2 3 0 0 0 16 15 8 7 + 20 3 9 1 4 48 200 12 11 1 14 13 10 9 3 2 0 14 15 14 2 0 12 3 9 + 8 6 0 0 17 16 13 12 10 3 6 1 4 48 200 23 0 1 11 10 7 6 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 97 values pushed */ + 0 0 30 14 3 48 200 3 2 1 26 7 1 0 4 8 2 3 0 0 0 21 20 13 12 + 20 3 14 25 24 9 8 33 3 10 2 4 48 200 17 16 1 19 18 15 14 3 23 22 11 + 10 3 3 0 14 24 23 20 19 4 0 17 3 14 13 10 9 4 13 7 0 0 26 25 22 + 21 18 17 10 5 7 1 4 48 200 1 0 1 16 15 12 11 8 7 5 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 108 values pushed */ + 0 0 21 14 2 48 200 2 2 32 29 28 27 4 30 11 3 25 24 2 11 9 3 1 23 + 17 6 0 4 7 2 3 0 0 0 16 15 8 7 20 3 9 1 4 48 200 31 30 1 12 + 11 1 14 13 10 9 3 3 0 14 15 14 2 0 31 3 28 27 2 31 24 3 9 8 6 + 0 0 30 29 25 24 10 3 31 17 16 13 12 10 3 6 2 4 48 200 32 31 1 23 0 + 1 11 10 7 6 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 109 values pushed */ + 0 0 39 32 34 21 14 2 48 200 2 2 1 23 17 6 0 4 7 2 3 0 1 43 37 + 36 26 25 24 6 13 34 2 0 0 0 16 15 8 7 20 3 9 1 4 48 200 12 11 1 + 14 13 10 9 3 2 0 14 0 0 41 20 30 48 200 30 30 26 25 24 15 14 6 0 12 + 3 43 37 36 3 12 6 3 9 8 6 0 0 17 16 13 12 10 3 6 1 4 48 200 23 + 0 1 11 10 7 6 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 100 values pushed */ + 0 0 31 30 9 27 14 17 48 200 17 2 9 1 1 2 1 2 3 1 3 0 1 1 33 + 25 5 3 1 2 3 0 0 1 19 2 0 2 0 0 0 24 21 20 0 6 3 22 1 4 + 48 200 4 3 1 23 22 1 2 0 14 0 0 29 39 13 48 200 22 21 2 13 13 4 24 + 23 3 2 4 13 0 0 0 33 25 20 19 5 4 10 5 0 1 4 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 0 0 20 5 27 5 5 41 48 200 41 2 27 0 1 1 33 25 24 23 22 14 13 12 11 + 3 2 1 0 13 0 2 3 0 0 14 0 0 16 24 29 9 39 37 48 200 33 23 22 14 + 13 12 11 3 2 9 13 37 29 24 0 0 25 24 23 1 0 1 5 48 200 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 139 values pushed */ + 0 0 20 31 27 9 31 37 48 200 31 25 24 23 22 16 15 14 13 7 6 5 12 1 45 + 3 37 53 37 4 3 45 44 3 27 1 0 0 52 51 48 47 44 6 4 42 1 4 48 200 + 46 45 1 50 49 43 42 3 41 40 1 2 1 1 3 0 1 5 0 14 0 0 18 38 29 + 11 38 33 48 200 2 1 2 40 41 3 33 29 51 45 44 43 33 31 29 23 22 16 15 14 + 13 7 6 3 0 17 41 4 3 49 48 2 13 40 0 0 53 52 42 41 16 3 40 1 4 + 48 200 50 47 46 40 3 25 24 5 4 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + MDAP[1] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 20 6 27 5 6 35 48 200 35 31 27 25 24 23 22 14 13 12 11 3 2 1 0 + 14 0 0 18 13 29 7 13 33 48 200 31 23 22 14 13 12 11 3 2 9 13 33 29 0 + 25 24 1 0 3 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 16 31 23 5 31 35 48 200 35 27 23 21 20 19 18 12 11 10 9 3 2 1 0 + 14 0 0 14 38 25 7 38 31 48 200 27 19 18 12 11 10 9 3 2 9 13 31 25 0 + 21 20 1 0 3 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 0 0 21 5 4 9 5 16 48 200 4 0 1 12 11 2 13 0 0 1 23 0 16 0 0 + 14 23 12 11 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 129 values pushed */ + 37 36 35 34 33 32 29 28 27 26 23 20 19 18 17 14 13 12 11 10 9 6 5 4 3 + 2 1 27 7 0 3 38 31 30 16 15 0 5 0 25 24 22 21 8 7 0 5 14 36 35 + 34 33 24 23 22 7 31 37 3 21 20 17 16 4 18 8 3 15 14 11 10 4 8 12 3 + 5 4 1 0 4 2 6 3 30 29 26 25 4 13 27 0 0 32 31 18 1 27 38 37 18 + 1 18 2 4 13 12 1 2 1 6 48 200 28 27 1 19 18 1 9 8 1 3 2 1 7 + 6 1 5 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 0 0 11 5 18 48 200 18 0 1 16 15 14 13 4 0 1 3 0 0 0 32 31 1 22 + 2 0 1 4 48 200 33 0 1 0 14 0 0 7 39 22 48 200 22 31 22 14 13 4 32 + 15 3 33 32 1 16 15 1 1 0 1 3 0 + LOOPCALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 0 0 10 6 18 48 200 15 14 13 12 4 13 18 1 0 0 27 0 16 1 1 1 4 48 + 200 26 25 1 2 0 14 0 0 8 13 20 48 200 20 25 20 13 12 4 26 0 3 27 26 + 1 15 14 1 0 3 2 0 + LOOPCALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 0 0 11 18 48 200 24 23 16 15 14 13 1 7 13 18 31 0 0 33 0 16 1 31 1 + 4 48 200 32 31 1 0 14 0 0 9 34 20 48 200 31 24 23 14 13 5 20 0 3 33 + 32 20 2 16 15 1 0 3 2 0 + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 0 0 19 30 10 48 200 10 2 23 22 21 17 14 13 12 6 3 2 10 0 4 3 5 4 + 1 0 16 15 1 0 1 3 14 23 0 2 5 16 3 4 3 2 13 1 15 14 12 0 0 + 22 21 6 5 10 3 1 17 16 10 1 12 2 4 48 200 2 1 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 96 values pushed */ + 0 0 27 14 34 19 30 10 48 200 10 2 23 22 21 17 14 13 12 6 3 2 10 0 4 + 3 30 29 25 24 4 13 34 0 5 4 1 0 16 15 1 0 1 3 14 30 1 5 2 29 + 23 0 3 5 16 3 25 24 2 16 12 3 4 3 2 13 1 15 14 12 0 0 22 21 6 + 5 10 3 1 17 16 10 1 12 2 4 48 200 2 1 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 2 16 1 0 1 4 48 200 1 0 1 0 14 2 1 1 3 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 110 values pushed */ + 0 0 45 5 28 33 5 40 19 30 10 48 200 28 0 10 2 40 1 47 40 24 3 0 0 + 3 0 23 22 21 17 14 13 12 6 3 2 10 0 4 3 1 36 35 2 13 0 0 5 4 + 1 0 16 15 1 0 1 3 14 36 1 5 2 35 23 0 3 5 16 3 47 24 2 16 12 + 3 4 3 2 13 1 15 14 12 0 0 22 21 6 5 10 3 1 17 16 10 1 12 2 4 + 48 200 2 1 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 8 7 2 1 0 3 0 0 14 13 10 9 6 5 2 1 6 7 3 1 4 48 200 15 0 + 1 0 12 11 4 3 1 3 14 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 18 17 14 13 6 5 2 1 8 3 9 3 22 21 12 11 8 7 6 9 0 3 10 9 1 + 23 20 19 0 3 2 0 16 15 4 3 1 3 14 23 22 21 20 19 18 17 16 15 14 13 + 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 31 30 29 28 27 24 6 25 3 3 18 17 14 13 6 5 2 1 8 3 9 3 22 21 12 + 11 8 7 6 9 0 3 26 25 1 10 9 1 23 20 19 0 3 3 0 16 15 4 3 1 + 3 14 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 + 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 26 25 24 23 22 19 18 17 16 15 12 11 10 9 8 5 4 3 2 1 20 6 0 3 27 + 21 20 0 3 0 14 13 7 6 1 3 14 27 26 25 24 23 22 21 20 19 18 17 16 15 + 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 1 8 7 2 1 2 3 0 1 22 0 2 2 16 3 0 0 0 14 13 10 9 6 5 2 + 1 6 7 3 21 20 17 16 6 3 18 2 4 48 200 19 18 1 0 12 11 4 3 1 3 + 14 22 21 20 19 18 17 16 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 95 values pushed */ + 30 29 28 27 26 23 6 24 3 3 1 8 7 2 1 2 3 0 1 22 0 2 2 16 3 + 0 0 0 14 13 10 9 6 5 2 1 6 7 3 21 20 17 16 6 3 18 2 4 48 200 + 25 24 1 19 18 1 2 0 12 11 4 3 1 3 14 30 29 28 27 26 25 24 23 22 21 + 20 19 18 17 16 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 131 values pushed */ + 19 18 2 10 8 3 30 7 2 8 5 3 0 0 25 24 21 20 17 16 13 12 6 7 14 + 29 28 9 8 6 3 10 32 31 6 5 6 3 3 36 35 2 1 6 3 0 4 4 48 200 + 27 26 11 10 3 34 33 4 3 3 37 0 1 3 0 23 22 15 14 0 3 14 19 18 2 + 30 2 3 37 36 33 32 29 28 27 26 25 24 23 22 21 20 14 13 30 17 16 15 14 13 + 12 11 10 9 8 5 4 1 0 14 13 2 0 0 35 34 31 30 10 3 2 1 4 48 200 + 7 6 3 2 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 12 11 5 4 4 2 1 3 0 0 8 3 2 6 2 6 10 9 1 7 2 0 2 4 48 + 200 13 0 1 0 7 6 1 14 8 7 2 12 10 3 9 2 2 10 3 3 0 0 11 10 + 6 1 12 4 3 6 1 5 2 4 48 200 13 12 1 6 5 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 24 5 8 16 5 0 48 200 8 2 0 0 14 0 0 26 39 4 20 39 12 48 200 + 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + Copyright (c) 2001 by Bigelow & Holmes Inc. Instructions copyright (c) 2001 by URW++. + + + Luxi Mono + + + Regular + + + Luxi Mono Regular: B&H + + + Luxi Mono Regular + + + 1.2 : October 12, 2001 + + + LuxiMono + + + Luxi is a registered trademark of Bigelow & Holmes Inc. + + + Bigelow & Holmes Inc. + + + Kris Holmes and Charles Bigelow + + + http://www.urwpp.de + + + design@bigelowandholmes.com + + + Copyright (c) 2001 by Bigelow & Holmes Inc. Instructions copyright (c) 2001 by URW++. + + + Luxi Mono + + + Regular + + + Luxi Mono Regular: B&H + + + Luxi Mono Regular + + + 1.2 : October 12, 2001 + + + LuxiMono + + + Luxi is a registered trademark of Bigelow & Holmes Inc. + + + Bigelow & Holmes Inc. + + + Kris Holmes and Charles Bigelow + + + http://www.urwpp.de + + + design@bigelowandholmes.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/golang/freetype/testdata/luxirr.ttf b/vendor/github.com/golang/freetype/testdata/luxirr.ttf new file mode 100644 index 000000000..daa8ad8cc Binary files /dev/null and b/vendor/github.com/golang/freetype/testdata/luxirr.ttf differ diff --git a/vendor/github.com/golang/freetype/testdata/luxirr.ttx b/vendor/github.com/golang/freetype/testdata/luxirr.ttx new file mode 100644 index 000000000..27191d0a7 --- /dev/null +++ b/vendor/github.com/golang/freetype/testdata/luxirr.ttx @@ -0,0 +1,30264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 15 values pushed */ + 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + FDEF[ ] + SLOOP[ ] + MDAP[1] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + MDAP[1] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP1[ ] + SRP2[ ] + IP[ ] + ENDF[ ] + FDEF[ ] + SRP1[ ] + SRP2[ ] + SLOOP[ ] + IP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MIRP[11101] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MIRP[10100] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MDRP[11101] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MDRP[10100] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MIRP[11101] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MIRP[10100] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MDRP[11101] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MDRP[10100] + ENDF[ ] + FDEF[ ] + MDRP[00100] + ENDF[ ] + FDEF[ ] + MDRP[00000] + ENDF[ ] + FDEF[ ] + SVTCA[0] + NPUSHB[ ] /* 10 values pushed */ + 1 0 0 1 1 2 2 3 3 0 + SZPS[ ] + MIAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SZPS[ ] + ENDF[ ] + + + + + + PUSHB[ ] /* 2 values pushed */ + 48 1 + PUSHW[ ] /* 1 value pushed */ + 329 + RTG[ ] + SCANCTRL[ ] + SCANTYPE[ ] + SCVTCI[ ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 16 values pushed */ + 5 6 2 1 4 7 3 0 5 4 2 3 6 7 1 0 + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + SVTCA[0] + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 61 values pushed */ + 33 32 18 17 14 10 7 1 8 23 0 3 37 9 8 3 13 35 0 0 24 23 7 1 35 + 1 4 48 84 36 35 1 34 16 15 0 3 2 0 14 37 36 35 34 33 32 30 24 23 20 + 18 17 16 15 14 10 9 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 147 values pushed */ + 87 32 31 29 28 11 9 8 8 16 18 3 34 33 2 39 85 3 83 81 80 78 77 62 55 + 52 51 49 47 46 45 41 1 15 63 0 3 0 0 17 16 21 1 6 40 39 21 1 18 64 + 63 7 1 85 3 4 48 84 19 18 1 86 85 1 84 54 53 0 3 3 0 7 6 0 14 + 51 49 2 7 32 3 26 25 47 46 45 39 34 31 29 28 26 25 19 16 11 9 14 32 17 + 3 85 84 83 81 80 78 77 64 55 54 6 1 0 13 13 75 62 0 0 87 86 63 62 32 + 3 17 1 4 48 196 53 52 1 8 7 1 33 32 1 41 40 18 17 3 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + MDAP[1] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 41 38 37 9 8 5 39 35 3 33 32 18 17 14 10 7 1 8 23 0 3 0 0 24 23 + 7 1 35 1 4 48 84 40 39 1 36 35 1 34 16 15 0 3 3 0 14 41 40 39 38 + 37 36 35 34 33 32 30 24 23 20 18 17 16 15 14 10 9 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 43 20 52 48 84 33 32 18 17 14 10 7 1 8 23 0 3 48 47 39 38 37 9 + 8 7 13 52 35 0 0 24 23 7 1 35 1 4 48 84 36 35 1 34 16 15 0 3 2 + 0 14 48 47 39 38 37 36 35 34 33 32 30 24 23 20 18 17 16 15 14 10 9 8 7 + 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 44 43 42 41 38 37 9 8 8 39 35 3 33 32 18 17 14 10 7 1 8 23 0 3 0 + 0 24 23 7 1 35 1 4 48 84 40 39 1 36 35 1 34 16 15 0 3 3 0 14 44 + 43 42 41 40 39 38 37 36 35 34 33 32 30 24 23 20 18 17 16 15 14 10 9 8 7 + 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 112 values pushed */ + 37 9 8 3 38 35 3 33 32 18 17 14 10 7 1 8 23 0 3 0 0 45 42 41 38 + 13 3 39 24 23 7 1 35 2 4 48 84 44 43 40 39 3 36 35 1 34 16 15 0 3 + 3 0 14 36 23 18 17 16 5 44 42 3 37 9 8 3 42 40 3 15 14 10 3 13 20 + 44 35 34 33 32 24 7 1 0 8 13 30 38 0 0 43 42 13 1 44 41 40 13 1 38 + 2 4 48 196 45 44 1 39 38 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 39 38 37 9 8 5 40 35 3 33 32 18 17 14 10 7 1 8 23 0 3 0 0 24 23 + 7 1 35 1 4 48 84 41 40 1 36 35 1 34 16 15 0 3 3 0 14 41 40 39 38 + 37 36 35 34 33 32 30 24 23 20 18 17 16 15 14 10 9 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 37 9 8 3 38 35 3 33 32 18 17 14 10 7 1 8 23 0 3 0 0 41 38 12 1 + 39 24 23 7 1 35 2 4 48 84 40 39 1 36 35 1 34 16 15 0 3 3 0 14 41 + 40 39 38 37 36 35 34 33 32 30 24 23 20 18 17 16 15 14 10 9 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 0 0 50 7 41 48 84 33 32 18 17 14 10 7 1 8 23 0 3 37 9 8 3 13 35 + 39 38 41 0 0 0 24 23 7 1 35 1 4 48 84 36 35 1 46 45 34 16 15 0 5 + 2 0 14 0 0 48 48 43 48 196 46 45 43 39 38 37 36 35 34 33 32 30 24 23 20 + 18 17 16 15 14 10 9 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 93 values pushed */ + 0 0 62 17 46 54 17 38 48 84 46 0 1 37 0 35 2 0 33 32 18 17 14 10 7 + 1 8 23 0 3 1 9 8 2 13 38 0 0 0 0 24 23 7 1 35 1 4 48 84 36 + 35 1 34 16 15 0 3 2 0 14 0 0 66 17 42 58 17 50 48 196 50 42 37 36 35 + 34 33 32 30 24 23 20 18 17 16 15 14 10 9 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 59 20 42 47 20 54 48 84 33 32 18 17 14 10 7 1 8 23 0 3 61 50 49 + 38 37 9 8 7 13 54 42 35 0 0 24 23 7 1 35 1 4 48 84 36 35 1 34 16 + 15 0 3 2 0 14 61 50 49 38 37 36 35 34 33 32 30 24 23 20 18 17 16 15 14 + 10 9 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 17 26 16 9 43 59 58 50 49 43 38 37 0 8 26 8 3 9 8 1 0 27 26 0 14 + 0 0 54 10 31 45 33 4 48 196 26 17 9 16 58 50 27 8 0 5 13 31 4 37 0 + 0 59 49 38 37 32 3 16 1 4 48 196 17 16 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 0 0 32 9 2 24 17 10 48 84 10 0 2 2 1 1 34 20 19 18 17 14 13 12 0 + 9 0 2 3 0 0 14 0 0 28 34 6 48 196 34 0 2 13 12 20 19 18 17 14 5 + 13 6 12 13 12 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 0 0 32 9 2 24 17 10 48 84 10 0 2 2 1 38 35 2 36 0 3 0 1 1 34 + 20 19 18 17 14 13 12 0 9 0 2 3 0 0 37 36 1 0 14 0 0 28 34 6 48 + 196 34 0 2 13 12 38 37 36 35 20 19 18 17 14 9 13 6 12 13 12 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 76 values pushed */ + 0 0 32 9 2 24 17 10 48 84 10 0 2 2 1 1 34 20 19 18 17 14 13 12 0 + 9 0 2 3 0 0 41 40 39 38 35 5 13 36 37 36 1 0 14 0 0 28 34 6 48 + 196 34 0 2 13 12 41 40 39 38 37 36 35 20 19 18 17 14 12 13 6 12 13 12 1 + 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 0 0 38 17 51 32 9 2 24 17 10 48 84 10 0 2 2 1 1 34 20 19 18 17 14 + 13 12 0 9 0 2 3 0 0 1 45 44 43 42 36 35 6 13 51 2 0 14 0 0 40 + 48 47 28 34 6 48 196 34 0 2 13 12 45 44 43 42 36 35 20 19 18 17 14 11 13 + 47 6 12 13 12 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 79 values pushed */ + 0 0 32 9 2 24 17 10 48 84 10 0 2 2 1 41 40 39 38 35 5 36 0 3 0 + 1 1 34 20 19 18 17 14 13 12 0 9 0 2 3 0 0 37 36 1 0 14 0 0 28 + 34 6 48 196 34 0 2 13 12 41 40 39 38 37 36 35 20 19 18 17 14 12 13 6 12 + 13 12 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 89 values pushed */ + 0 0 32 9 2 24 17 10 48 84 10 0 2 2 1 1 34 20 19 18 17 14 13 12 0 + 9 0 2 3 0 0 0 0 38 35 5 1 36 1 4 48 84 37 36 1 0 14 0 0 28 + 34 6 48 196 20 19 18 17 14 5 12 37 3 34 0 2 13 12 6 35 0 0 36 35 4 + 1 37 1 4 48 196 38 37 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 54 values pushed */ + 20 29 19 12 36 44 36 31 30 4 0 11 3 12 11 1 0 29 0 0 14 0 0 40 34 + 7 48 196 29 20 12 19 44 11 0 3 13 7 30 0 0 31 30 32 1 19 1 4 48 196 + 20 19 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 20 29 19 12 36 44 36 31 30 4 0 11 3 51 50 49 48 45 5 13 46 47 46 1 12 + 11 1 2 0 29 0 0 14 0 0 40 34 7 48 196 29 20 12 19 49 48 2 30 19 3 + 51 50 47 46 45 44 11 0 8 13 7 30 0 0 31 30 32 1 19 1 4 48 196 20 19 + 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 33 26 4 13 49 48 2 13 2 3 40 40 35 2 0 25 3 0 0 52 34 1 0 14 3 + 2 1 4 48 84 51 50 3 2 3 26 25 1 2 0 14 13 0 14 0 0 44 34 21 48 + 196 26 33 13 4 52 51 48 25 14 5 13 21 34 2 1 0 0 0 50 49 35 34 32 3 + 0 1 4 48 196 33 4 3 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 101 values pushed */ + 52 61 51 44 21 20 12 11 2 1 6 9 28 3 42 41 30 26 23 22 6 28 34 3 0 + 0 10 9 21 1 0 35 34 7 1 43 2 4 48 84 29 28 1 44 43 1 2 0 61 0 + 0 14 61 52 44 51 41 2 2 0 21 3 35 34 28 26 23 20 12 9 8 21 10 3 0 + 0 30 29 11 10 32 3 51 1 4 48 196 43 42 1 1 0 1 22 21 1 52 51 1 4 + 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 117 values pushed */ + 52 61 51 44 65 62 2 63 0 3 21 20 12 11 2 1 6 9 28 3 42 41 30 26 23 + 22 6 28 34 3 0 0 10 9 21 1 0 35 34 7 1 43 2 4 48 84 64 63 1 29 + 28 1 44 43 1 3 0 61 0 0 14 61 52 44 51 41 2 2 0 21 3 65 64 63 35 + 34 28 26 23 20 12 9 11 21 10 3 62 10 51 2 0 0 30 29 11 10 32 3 51 1 + 4 48 196 43 42 1 1 0 1 22 21 1 52 51 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 124 values pushed */ + 0 0 67 20 76 48 84 52 61 51 44 21 20 12 11 2 1 6 9 28 3 42 41 30 26 + 23 22 6 28 34 3 72 71 63 62 4 13 76 0 0 0 10 9 21 1 0 35 34 7 1 + 43 2 4 48 84 29 28 1 44 43 1 2 0 61 0 0 14 61 52 44 51 41 2 2 0 + 21 3 72 71 35 34 28 26 23 20 12 9 10 21 10 3 63 62 2 10 51 3 0 0 30 + 29 11 10 32 3 51 1 4 48 196 43 42 1 1 0 1 22 21 1 52 51 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 123 values pushed */ + 52 61 51 44 21 20 12 11 2 1 6 9 28 3 42 41 30 26 23 22 6 28 34 3 68 + 67 66 65 62 5 13 63 0 0 10 9 21 1 0 35 34 7 1 43 2 4 48 84 64 63 + 1 29 28 1 44 43 1 3 0 61 0 0 14 61 52 44 51 62 41 2 3 0 21 3 68 + 67 64 63 35 34 28 26 23 20 12 9 12 21 10 3 66 65 2 10 51 3 0 0 30 29 + 11 10 32 3 51 1 4 48 196 43 42 1 1 0 1 22 21 1 52 51 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 124 values pushed */ + 52 61 51 44 68 67 66 65 62 5 63 0 3 21 20 12 11 2 1 6 9 28 3 42 41 + 30 26 23 22 6 28 34 3 0 0 10 9 21 1 0 35 34 7 1 43 2 4 48 84 64 + 63 1 29 28 1 44 43 1 3 0 61 0 0 14 61 52 44 51 41 2 2 0 21 3 67 + 66 65 64 63 35 34 28 26 23 20 12 9 13 21 10 3 68 62 2 10 51 3 0 0 30 + 29 11 10 32 3 51 1 4 48 196 43 42 1 1 0 1 22 21 1 52 51 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 136 values pushed */ + 52 61 51 44 21 20 12 11 2 1 6 9 28 3 42 41 30 26 23 22 6 28 34 3 0 + 0 69 66 65 62 13 3 63 10 9 21 1 0 35 34 7 1 43 3 4 48 84 68 67 64 + 63 3 29 28 1 44 43 1 3 0 61 0 0 14 61 52 44 51 41 2 2 0 21 3 26 + 23 20 3 21 68 3 35 28 12 9 4 68 66 3 34 66 64 2 0 0 67 66 13 1 68 + 65 64 13 1 62 30 29 11 10 32 3 51 3 4 48 196 69 68 1 63 62 1 43 42 1 + 1 0 1 22 21 1 52 51 1 6 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 120 values pushed */ + 52 61 51 44 21 20 12 11 2 1 6 9 28 3 42 41 30 26 23 22 6 28 34 3 0 + 0 65 62 5 1 63 10 9 21 1 0 35 34 7 1 43 3 4 48 84 64 63 1 29 28 + 1 44 43 1 3 0 61 0 0 14 61 52 44 51 41 2 2 0 21 3 35 28 26 23 20 + 12 9 7 21 64 3 34 64 62 2 0 0 65 64 4 1 62 30 29 11 10 32 3 51 2 + 4 48 196 63 62 1 43 42 1 1 0 1 22 21 1 52 51 1 5 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 117 values pushed */ + 52 61 51 44 63 62 2 64 0 3 21 20 12 11 2 1 6 9 28 3 42 41 30 26 23 + 22 6 28 34 3 0 0 10 9 21 1 0 35 34 7 1 43 2 4 48 84 65 64 1 29 + 28 1 44 43 1 3 0 61 0 0 14 61 52 44 51 41 2 2 0 21 3 65 63 62 35 + 34 28 26 23 20 12 9 11 21 10 3 64 10 51 2 0 0 30 29 11 10 32 3 51 1 + 4 48 196 43 42 1 1 0 1 22 21 1 52 51 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 117 values pushed */ + 52 61 51 44 21 20 12 11 2 1 6 9 28 3 42 41 30 26 23 22 6 28 34 3 0 + 0 65 62 12 1 63 10 9 21 1 0 35 34 7 1 43 3 4 48 84 64 63 1 29 28 + 1 44 43 1 3 0 61 0 0 14 61 52 44 51 41 2 2 0 21 3 65 64 35 34 28 + 26 23 20 12 9 10 21 10 3 63 62 2 10 51 3 0 0 30 29 11 10 32 3 51 1 + 4 48 196 43 42 1 1 0 1 22 21 1 52 51 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 97 values pushed */ + 0 0 53 21 39 48 84 7 14 64 58 57 34 27 24 17 16 6 1 10 14 0 3 55 43 + 42 41 4 13 39 0 65 56 35 0 3 0 26 25 15 14 0 3 14 14 7 56 55 43 25 + 24 5 16 41 3 65 64 15 3 41 57 3 27 26 2 13 34 1 0 6 0 0 17 16 35 + 1 34 58 57 35 1 6 2 4 48 196 35 34 1 42 41 1 7 6 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 127 values pushed */ + 0 0 74 7 65 48 84 52 61 51 44 21 20 12 11 2 1 6 9 28 3 42 41 30 26 + 23 22 6 28 34 3 63 62 65 43 0 0 10 9 21 1 0 35 34 7 1 43 2 4 48 + 84 29 28 1 70 69 44 43 3 2 0 61 0 0 14 0 0 72 48 67 48 196 61 52 44 + 51 70 63 62 41 2 5 0 21 3 67 69 67 35 34 28 26 23 20 12 9 10 21 10 3 + 0 0 30 29 11 10 32 3 51 1 4 48 196 43 42 1 1 0 1 22 21 1 52 51 1 + 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 33 26 4 13 49 48 2 13 2 3 40 40 35 2 0 25 3 0 0 52 34 1 0 14 3 + 2 1 4 48 84 51 50 3 2 3 26 25 1 2 0 14 13 0 14 0 0 44 34 21 48 + 196 26 33 13 4 52 51 48 25 14 5 13 21 34 2 1 0 0 0 50 49 35 34 32 3 + 0 1 4 48 196 33 4 3 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 98 values pushed */ + 0 0 40 9 45 22 38 15 48 84 45 2 15 0 1 19 18 17 3 0 10 3 0 1 43 + 42 2 0 2 3 0 0 0 29 28 9 8 11 3 10 49 38 37 0 11 3 1 2 4 48 + 84 27 26 11 10 3 36 35 2 1 3 2 0 14 0 0 31 5 4 48 196 49 43 42 38 + 37 36 35 29 28 27 26 19 11 10 9 8 2 1 0 19 13 4 17 18 17 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 98 values pushed */ + 0 0 40 9 45 22 38 15 48 84 45 2 15 0 1 19 18 17 3 0 10 3 0 1 43 + 42 2 0 2 3 0 0 0 29 28 9 8 11 3 10 49 38 37 0 11 3 1 2 4 48 + 84 27 26 11 10 3 36 35 2 1 3 2 0 14 0 0 31 5 4 48 196 49 43 42 38 + 37 36 35 29 28 27 26 19 11 10 9 8 2 1 0 19 13 4 17 18 17 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 91 values pushed */ + 48 57 47 40 30 39 21 20 2 1 4 9 11 3 23 22 2 28 39 3 0 0 10 9 21 + 1 0 29 28 7 1 11 2 4 48 84 12 11 1 40 39 1 2 0 57 0 0 14 57 48 + 40 47 39 30 2 0 21 2 28 23 20 12 9 5 21 10 3 0 0 30 29 11 10 32 3 + 47 1 4 48 196 1 0 1 22 21 1 48 47 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 0 0 30 7 2 22 17 10 48 84 10 0 2 2 1 18 17 14 13 12 5 0 40 3 0 + 1 49 42 39 33 32 0 6 40 2 3 0 41 40 1 0 14 0 0 26 34 6 48 196 18 + 17 14 3 12 32 3 42 41 2 13 0 40 39 6 32 0 0 33 32 32 1 0 1 4 48 + 196 49 0 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 0 0 55 20 64 30 7 2 22 17 10 48 84 10 0 2 2 1 18 17 14 13 12 5 0 + 40 3 0 1 49 42 39 33 32 0 6 40 2 3 0 1 60 59 51 50 4 13 64 0 0 + 41 40 1 0 14 0 0 26 34 6 48 196 60 59 18 17 14 5 12 32 3 42 41 2 13 + 0 51 50 40 39 4 13 6 32 0 0 33 32 32 1 0 1 4 48 196 49 0 1 13 12 + 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 108 values pushed */ + 0 0 30 7 2 22 17 10 48 84 10 0 2 2 1 56 55 54 53 50 5 51 0 3 0 + 1 18 17 14 13 12 5 0 40 3 0 1 49 42 39 33 32 0 6 40 2 3 0 52 51 + 1 41 40 1 2 0 14 0 0 26 34 6 48 196 54 53 18 17 14 5 12 32 3 42 41 + 2 13 0 56 55 52 51 50 40 39 7 13 6 32 0 0 33 32 32 1 0 1 4 48 196 + 49 0 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 107 values pushed */ + 0 0 53 17 64 30 7 2 22 17 10 48 84 10 0 2 2 1 18 17 14 13 12 5 0 + 40 3 0 1 49 42 39 33 32 0 6 40 2 3 0 1 58 57 51 50 4 13 64 2 0 + 41 40 1 0 14 0 0 55 48 62 26 34 6 48 196 18 17 14 3 12 32 3 42 41 2 + 13 0 58 57 51 50 40 39 6 13 62 6 32 0 0 33 32 32 1 0 1 4 48 196 49 + 0 1 13 12 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 111 values pushed */ + 0 0 30 7 2 22 17 10 48 84 10 0 2 2 1 18 17 14 13 12 5 0 40 3 0 + 1 49 42 39 33 32 0 6 40 2 3 0 0 0 53 50 5 1 51 1 4 48 84 52 51 + 1 41 40 1 2 0 14 0 0 26 34 6 48 196 18 17 14 3 12 32 3 40 39 2 52 + 50 3 42 41 2 13 0 6 50 0 0 53 52 4 1 50 33 32 32 1 0 2 4 48 196 + 51 50 1 49 0 1 13 12 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 88 values pushed */ + 74 67 57 66 56 50 40 49 37 30 20 29 19 12 2 11 0 0 39 38 7 1 0 1 4 + 48 84 1 0 1 50 49 30 29 3 2 0 67 66 12 11 0 3 14 67 74 66 57 50 56 + 49 40 30 37 29 20 12 19 11 2 0 0 38 37 2 1 32 3 19 74 40 39 0 32 3 + 56 2 4 48 196 20 19 1 57 56 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 121 values pushed */ + 82 75 65 74 60 53 43 52 40 33 23 32 18 12 2 11 0 0 62 61 42 41 22 21 21 + 5 19 1 0 7 1 83 2 4 48 84 84 83 1 75 74 12 11 3 2 0 53 52 33 32 + 0 3 86 85 64 63 20 19 1 5 14 75 82 74 65 53 60 52 43 33 40 32 23 12 18 + 11 2 63 62 2 13 60 21 20 18 0 0 85 84 82 43 42 0 32 5 60 86 83 41 40 + 2 1 32 5 18 2 4 48 196 65 64 61 60 3 23 22 19 18 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 117 values pushed */ + 74 67 57 66 56 50 40 49 37 30 20 29 19 12 2 11 81 80 79 78 75 5 76 11 3 + 0 0 39 38 7 1 0 1 4 48 84 77 76 1 1 0 1 50 49 30 29 3 3 0 67 + 66 12 11 0 3 14 67 74 66 57 50 56 49 40 30 37 29 20 12 19 11 2 78 19 1 + 2 81 80 79 77 76 5 1 0 3 75 0 56 2 0 0 38 37 2 1 32 3 19 74 40 + 39 0 32 3 56 2 4 48 196 20 19 1 57 56 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 39 values pushed */ + 0 29 19 28 18 11 1 10 29 28 1 0 11 10 0 14 29 0 28 19 11 18 10 1 0 + 0 19 18 32 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 0 29 19 28 18 11 1 10 39 36 2 37 10 3 38 37 1 29 28 1 2 0 11 10 0 + 14 29 0 28 19 11 18 10 1 37 18 0 2 38 18 39 36 0 0 0 19 18 32 1 0 + 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 0 0 41 20 50 48 84 0 29 19 28 18 11 1 10 46 45 37 36 4 13 50 10 29 28 + 1 0 11 10 0 14 29 0 28 19 11 18 10 1 46 45 2 13 18 37 36 0 0 0 19 + 18 37 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 68 values pushed */ + 0 29 19 28 18 11 1 10 42 41 40 39 36 5 37 10 3 38 37 1 29 28 1 2 0 + 11 10 0 14 29 0 28 19 11 18 10 1 41 18 0 2 40 39 38 3 13 18 42 37 36 + 3 13 0 0 0 19 18 32 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 0 29 19 28 18 11 1 10 0 0 43 40 39 36 13 3 37 1 4 48 84 42 41 38 37 + 3 29 28 1 2 0 11 10 0 14 29 0 28 19 11 18 10 1 0 0 41 40 13 1 42 + 39 38 13 1 36 19 18 32 1 0 3 4 48 196 43 42 1 37 36 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 0 29 19 28 18 11 1 10 0 0 39 36 5 1 37 1 4 48 84 38 37 1 29 28 1 + 2 0 11 10 0 14 29 0 28 19 11 18 10 1 0 0 39 38 4 1 36 19 18 32 1 + 0 2 4 48 196 37 36 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 0 29 19 28 18 11 1 10 37 36 2 38 10 3 39 38 1 29 28 1 2 0 11 10 0 + 14 29 0 28 19 11 18 10 1 39 18 0 2 36 18 38 0 0 0 37 19 18 32 2 0 + 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 0 29 19 28 18 11 1 10 0 0 39 36 12 1 37 1 4 48 84 38 37 1 29 28 1 + 2 0 11 10 0 14 29 0 28 19 11 18 10 1 39 38 2 13 18 37 36 0 0 0 19 + 18 32 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 48 7 39 48 84 0 29 19 28 18 11 1 10 37 36 39 28 44 43 29 28 3 0 + 11 10 0 14 0 0 46 48 41 48 196 29 0 28 19 11 18 10 1 43 18 0 2 44 37 + 36 3 13 18 41 0 0 0 19 18 32 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 0 0 57 20 40 45 20 52 48 84 0 29 19 28 18 11 1 10 59 48 47 36 4 13 52 + 40 10 29 28 1 0 11 10 0 14 29 0 28 19 11 18 10 1 48 47 2 13 18 59 36 + 0 0 0 19 18 37 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 0 0 7 7 33 48 84 12 19 1 29 28 21 11 4 19 2 3 0 1 2 1 0 3 13 + 33 2 0 20 19 0 14 19 12 2 11 0 2 21 20 2 13 28 0 0 29 28 32 1 11 + 1 4 48 196 12 11 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 0 0 7 7 33 48 84 12 19 41 40 39 38 35 5 36 19 3 1 29 28 21 11 4 19 + 2 3 0 1 2 1 0 3 13 33 2 0 37 36 1 0 20 19 0 14 19 12 40 28 11 + 2 41 36 35 2 4 11 0 3 39 38 37 21 20 5 13 28 0 0 29 28 32 1 11 1 + 4 48 196 12 11 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 71 64 54 63 53 46 44 38 37 32 31 28 22 21 14 11 10 0 12 12 29 3 46 45 30 + 29 3 0 64 63 13 12 0 3 14 64 71 63 54 46 53 45 44 32 31 30 29 28 22 21 + 14 13 12 11 10 14 13 34 7 0 0 0 71 38 37 0 32 3 53 1 4 48 196 54 53 + 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 106 values pushed */ + 0 0 75 17 86 48 84 71 64 54 63 53 46 44 38 37 32 31 28 22 21 14 11 10 0 + 12 12 29 3 80 79 73 72 4 13 86 29 46 45 30 29 3 0 64 63 13 12 0 3 14 + 0 0 77 48 84 48 196 64 71 63 54 46 53 80 79 73 72 45 44 32 31 30 29 28 22 + 21 14 13 12 11 10 18 13 84 34 7 3 12 0 0 0 71 38 37 0 32 3 53 1 4 + 48 196 54 53 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 29 22 12 21 11 4 32 30 2 1 4 21 34 3 0 0 35 34 7 1 3 1 4 48 84 + 4 3 1 0 22 21 0 14 22 29 21 12 4 11 35 34 32 1 4 2 29 3 0 0 30 + 29 32 1 11 1 4 48 196 3 2 1 12 11 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 29 22 12 21 11 4 43 40 2 41 21 3 32 30 2 1 4 21 34 3 0 0 35 34 7 + 1 3 1 4 48 84 42 41 1 4 3 1 2 0 22 21 0 14 22 29 21 12 4 11 42 + 41 35 34 32 1 6 2 29 3 43 40 2 29 11 3 0 0 30 29 32 1 11 1 4 48 + 196 3 2 1 12 11 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 94 values pushed */ + 29 22 12 21 11 4 51 49 48 44 41 40 32 30 2 1 10 21 34 3 0 0 35 34 7 + 1 3 1 4 48 84 4 3 1 0 43 42 22 21 0 3 14 22 29 21 12 4 11 1 2 + 43 2 51 40 35 3 43 41 3 34 32 2 41 29 3 0 0 49 48 42 41 4 3 43 30 + 29 32 1 11 2 4 48 196 44 43 1 3 2 1 12 11 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 94 values pushed */ + 0 0 43 17 54 48 84 29 22 12 21 11 4 32 30 2 1 4 21 34 3 48 47 41 40 + 4 13 54 3 0 0 35 34 7 1 3 1 4 48 84 4 3 1 0 22 21 0 14 0 0 + 45 48 52 48 196 22 29 21 12 4 11 52 52 48 47 35 34 32 1 7 2 29 3 0 0 + 41 40 30 29 32 3 11 1 4 48 196 3 2 1 12 11 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 29 22 12 21 11 4 32 30 2 1 4 40 34 3 0 0 43 40 5 1 41 35 34 7 1 + 3 2 4 48 84 42 41 1 4 3 1 2 0 22 21 0 14 22 29 21 12 4 11 1 2 + 42 2 35 34 32 3 40 29 3 0 0 41 40 4 1 42 30 29 32 1 11 2 4 48 196 + 43 42 1 3 2 1 12 11 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 47 40 21 14 4 13 38 37 28 26 25 24 23 22 3 2 1 0 12 13 30 3 0 0 31 + 30 7 1 39 1 4 48 84 40 39 1 0 14 13 0 14 40 47 14 21 13 4 37 31 30 + 28 24 23 6 38 21 3 2 1 0 0 0 26 25 22 21 32 3 0 1 4 48 196 39 38 + 1 47 4 3 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 51 58 50 44 34 43 33 32 31 30 29 22 19 12 11 4 1 11 0 20 3 44 43 21 20 + 3 0 58 3 2 0 0 3 14 58 51 44 50 43 34 2 11 29 2 32 31 22 21 1 0 + 6 29 33 3 20 19 4 3 4 13 11 51 50 33 0 0 30 29 4 1 11 1 4 48 196 + 12 11 1 34 33 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 76 values pushed */ + 38 45 37 31 23 30 22 19 12 9 2 1 6 0 30 3 21 20 30 31 30 1 0 45 11 + 10 0 0 3 14 45 38 31 37 30 23 21 19 1 2 10 9 0 3 1 22 3 12 11 2 + 13 19 0 0 2 1 35 1 19 23 22 35 1 37 2 4 48 196 20 19 1 38 37 1 2 + 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 38 45 37 31 23 30 49 46 2 47 0 3 22 19 12 9 2 1 6 0 30 3 21 20 30 + 48 47 1 31 30 1 2 0 45 11 10 0 0 3 14 45 38 31 37 30 23 21 19 1 2 + 49 48 47 46 10 9 0 7 1 22 3 12 11 2 13 19 0 0 2 1 35 1 19 23 22 + 35 1 37 2 4 48 196 20 19 1 38 37 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 95 values pushed */ + 38 45 37 31 23 30 22 19 12 9 2 1 6 0 30 3 52 51 50 49 46 5 13 47 21 + 20 30 48 47 1 31 30 1 2 0 45 11 10 0 0 3 14 45 38 31 37 30 23 21 19 + 1 2 52 51 50 49 48 47 46 10 9 0 10 1 22 3 12 11 2 13 19 0 0 2 1 + 35 1 19 23 22 35 1 37 2 4 48 196 20 19 1 38 37 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 0 0 49 17 60 48 84 38 45 37 31 23 30 22 19 12 9 2 1 6 0 30 3 54 53 + 47 46 21 20 6 13 60 30 31 30 1 0 45 11 10 0 0 3 14 0 0 51 48 58 48 + 196 45 38 31 37 30 23 21 19 1 2 58 58 54 53 47 46 10 9 0 8 1 22 3 12 + 11 2 13 19 0 0 2 1 35 1 19 23 22 35 1 37 2 4 48 196 20 19 1 38 37 + 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 99 values pushed */ + 0 0 67 20 50 55 20 62 48 84 38 45 37 31 23 30 22 19 12 9 2 1 6 0 30 + 3 69 58 57 46 4 13 62 50 0 21 20 30 31 30 1 0 45 11 10 0 0 3 14 45 + 38 31 37 30 23 21 19 1 2 69 58 57 46 10 9 0 7 1 22 3 12 11 2 13 19 + 0 0 2 1 35 1 19 23 22 35 1 37 2 4 48 196 20 19 1 38 37 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 24 17 8 16 17 0 48 84 8 2 0 0 14 0 0 28 34 4 20 34 12 48 196 + 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 133 values pushed */ + 0 0 85 17 60 77 17 70 48 84 70 0 60 2 0 1 14 2 73 31 30 24 23 22 16 + 9 8 4 3 11 14 38 3 72 54 51 50 45 44 42 40 36 33 32 11 38 52 3 0 0 + 15 14 21 1 1 1 4 48 84 39 38 1 53 52 1 2 0 2 1 0 14 0 0 81 33 + 66 48 196 50 2 31 2 45 44 42 38 36 33 30 24 23 22 14 9 8 4 14 31 15 3 + 66 0 0 0 73 72 54 53 1 0 36 5 15 1 4 48 196 52 51 1 3 2 1 32 31 + 1 40 39 16 15 3 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 24 17 8 16 17 0 48 84 8 2 0 0 1 35 32 2 33 0 3 0 34 33 1 + 0 14 0 0 28 34 4 20 34 12 48 196 35 34 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 37 20 46 24 38 8 16 38 0 48 84 8 2 0 0 1 42 41 33 32 4 13 46 + 0 0 14 0 0 28 34 4 20 34 12 48 196 42 41 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 49 values pushed */ + 0 0 24 17 8 16 17 0 48 84 8 2 0 0 1 38 37 36 35 32 5 33 0 3 0 + 34 33 1 0 14 0 0 28 34 4 20 34 12 48 196 38 37 36 35 34 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 0 0 24 17 8 16 17 0 48 84 8 2 0 0 0 0 39 36 35 32 13 3 33 1 4 + 48 84 38 37 34 33 3 0 14 0 0 28 34 4 20 34 12 48 196 4 38 12 32 0 0 + 37 36 13 1 38 35 34 13 1 32 2 4 48 196 39 38 1 33 32 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 24 17 8 16 17 0 48 84 8 2 0 0 1 33 32 2 34 0 3 0 35 34 1 + 0 14 0 0 28 34 4 20 34 12 48 196 35 34 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 51 values pushed */ + 0 0 24 17 8 16 17 0 48 84 8 2 0 0 1 39 36 35 32 4 33 0 3 0 38 + 37 34 33 3 0 14 0 0 28 34 4 20 34 12 48 196 39 38 37 36 35 34 33 32 12 + 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 0 0 24 17 8 16 17 0 48 84 8 2 0 0 0 0 35 32 12 1 33 1 4 48 84 + 34 33 1 0 14 0 0 28 34 4 20 34 12 48 196 35 34 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 0 0 41 17 9 30 17 22 48 84 22 2 9 0 1 1 47 37 36 26 24 14 11 1 8 + 0 2 3 0 0 1 13 12 2 13 0 0 1 25 0 2 0 14 0 0 45 34 5 34 34 + 18 48 196 47 37 36 26 25 24 18 14 13 12 11 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 48 values pushed */ + 0 0 53 20 36 41 20 48 24 17 8 16 17 0 48 84 8 2 0 0 1 55 44 43 32 + 4 13 48 36 0 0 14 0 0 28 34 4 20 34 12 48 196 55 44 43 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 26 35 25 18 10 17 44 43 37 36 9 8 6 0 17 3 18 17 1 0 35 0 0 14 0 + 0 39 33 6 48 196 35 26 18 25 17 10 43 37 8 0 4 13 6 9 0 0 44 36 10 + 9 32 3 25 1 4 48 196 26 25 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 0 0 32 17 6 24 17 14 48 84 14 0 6 2 1 1 22 0 2 2 0 0 1 4 1 + 0 3 13 2 0 14 0 0 36 34 18 28 34 10 48 196 22 18 10 4 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 38 47 37 30 49 48 11 3 55 20 3 28 22 17 12 4 20 18 3 56 55 1 21 20 1 + 30 29 19 18 3 3 0 47 0 0 14 0 0 51 10 7 48 196 47 38 30 37 0 21 37 + 2 55 49 29 28 20 19 18 17 12 11 10 13 7 21 0 0 56 48 22 21 32 3 37 1 + 4 48 196 38 37 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 95 values pushed */ + 38 47 37 30 60 57 2 58 0 3 49 48 11 3 55 20 3 28 22 17 12 4 20 18 3 + 59 58 1 56 55 1 21 20 1 30 29 19 18 3 4 0 47 0 0 14 0 0 51 10 7 + 48 196 47 38 30 37 0 21 37 2 60 59 58 57 55 49 29 28 20 19 18 17 12 11 14 + 13 7 21 0 0 56 48 22 21 32 3 37 1 4 48 196 38 37 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 101 values pushed */ + 38 47 37 30 49 48 11 3 55 20 3 28 22 17 12 4 20 18 3 63 62 61 60 57 5 + 13 58 59 58 1 56 55 1 21 20 1 30 29 19 18 3 4 0 47 0 0 14 0 0 51 + 10 7 48 196 47 38 30 37 61 60 0 3 21 37 3 63 62 59 58 57 55 49 29 28 20 + 19 18 17 12 11 15 13 7 21 0 0 56 48 22 21 32 3 37 1 4 48 196 38 37 1 + 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 105 values pushed */ + 0 0 60 17 71 48 84 38 47 37 30 49 48 11 3 55 20 3 28 22 17 12 4 20 18 + 3 65 64 58 57 4 13 71 18 56 55 1 21 20 1 30 29 19 18 3 3 0 47 0 0 + 14 0 0 62 48 69 51 10 7 48 196 47 38 30 37 0 21 37 2 65 64 58 57 55 49 + 29 28 20 19 18 17 12 11 14 13 69 7 21 0 0 56 48 22 21 32 3 37 1 4 48 + 196 38 37 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 35 17 23 10 21 50 48 84 50 2 23 0 1 1 31 30 27 26 25 6 5 2 1 + 0 10 0 2 3 0 0 14 0 0 37 5 19 12 10 46 48 196 19 31 30 27 19 6 5 + 2 7 25 0 3 46 25 26 25 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 35 17 23 10 21 50 48 84 50 2 23 0 1 55 52 2 53 0 3 0 1 1 31 + 30 27 26 25 6 5 2 1 0 10 0 2 3 0 0 54 53 1 0 14 0 0 37 5 19 + 12 10 46 48 196 19 55 53 52 31 30 27 19 6 5 2 10 25 0 3 54 46 25 26 25 + 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 0 0 35 17 23 10 21 50 48 84 50 2 23 0 1 1 31 30 27 26 25 6 5 2 1 + 0 10 0 2 3 0 0 58 57 56 55 52 5 13 53 54 53 1 0 14 0 0 37 5 19 + 12 10 46 48 196 19 58 57 56 55 54 53 52 31 30 27 19 6 5 2 14 25 0 3 46 + 25 26 25 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 91 values pushed */ + 0 0 55 17 68 35 17 23 10 21 50 48 84 50 2 23 0 1 1 31 30 27 26 25 6 + 5 2 1 0 10 0 2 3 0 0 1 62 61 60 59 53 52 6 13 68 2 0 14 0 0 + 57 48 64 37 5 19 12 10 46 48 196 64 19 64 62 61 60 59 53 52 31 30 27 19 6 + 5 2 14 25 0 3 46 25 26 25 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 35 17 23 10 21 50 48 84 50 2 23 0 1 58 57 56 55 52 5 53 0 3 0 + 1 1 31 30 27 26 25 6 5 2 1 0 10 0 2 3 0 0 54 53 1 0 14 0 0 + 37 5 19 12 10 46 48 196 19 58 57 56 55 54 53 52 31 30 27 19 6 5 2 14 25 + 0 3 46 25 26 25 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 55 17 66 35 17 23 10 21 50 48 84 50 2 23 0 1 1 31 30 27 26 25 6 + 5 2 1 0 10 0 2 3 0 0 1 60 59 53 52 4 13 66 2 0 14 0 0 57 48 + 64 37 5 19 12 10 46 48 196 64 19 64 60 59 53 52 31 30 27 19 6 5 2 12 25 + 0 3 46 25 26 25 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 44 37 27 36 24 20 19 16 15 12 11 7 6 9 0 36 3 0 0 26 25 1 0 21 3 + 13 1 4 48 84 37 36 1 0 14 13 0 14 37 44 36 27 25 24 20 19 16 5 14 26 + 3 11 7 6 1 4 0 12 3 0 0 27 26 32 1 0 1 4 48 196 15 14 1 44 0 + 1 13 12 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 102 values pushed */ + 52 45 35 44 28 24 23 20 19 16 15 11 10 9 4 2 3 0 0 30 29 5 4 21 3 + 17 34 33 1 0 21 3 2 2 4 48 84 32 31 3 2 3 45 44 1 2 0 18 17 0 + 14 45 52 44 35 33 32 29 28 24 23 20 7 18 30 3 15 11 10 5 2 1 6 0 16 + 3 0 0 35 34 31 30 32 3 0 1 4 48 196 19 18 1 52 4 3 0 3 17 16 1 + 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 44 37 27 36 24 20 19 16 15 12 11 7 6 9 0 36 3 51 50 49 48 45 5 13 46 + 0 0 26 25 1 0 21 3 13 1 4 48 84 47 46 1 37 36 1 2 0 14 13 0 14 + 37 44 36 27 51 46 45 25 24 20 19 16 8 14 26 3 50 26 0 2 49 48 47 11 7 + 6 1 7 0 12 3 0 0 27 26 32 1 0 1 4 48 196 15 14 1 44 0 1 13 12 + 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 117 values pushed */ + 0 0 48 17 61 48 84 44 37 27 36 24 20 19 16 15 12 11 7 6 9 0 36 3 55 + 52 46 45 4 13 61 36 0 0 26 25 1 0 21 3 13 1 4 48 84 54 53 37 36 3 + 0 14 13 0 14 0 0 50 48 57 48 196 37 44 36 27 57 57 25 24 20 19 16 6 14 + 26 3 55 54 53 3 26 0 3 52 46 45 11 7 6 1 7 0 12 3 0 0 27 26 32 + 1 0 1 4 48 196 15 14 1 44 0 1 13 12 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 109 values pushed */ + 0 0 48 38 59 48 84 44 37 27 36 24 20 19 16 15 12 11 7 6 9 0 36 3 53 + 52 46 45 4 13 59 36 0 0 26 25 1 0 42 3 13 1 4 48 84 37 36 1 0 14 + 13 0 14 0 0 50 23 57 48 196 37 44 36 27 57 57 25 24 20 19 16 6 14 26 3 + 46 45 11 7 6 1 6 0 12 3 0 0 27 26 37 1 0 1 4 48 196 15 14 1 53 + 52 44 0 3 13 12 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 68 values pushed */ + 44 37 27 36 26 19 9 18 55 54 46 45 8 7 0 7 36 18 3 19 18 1 0 37 36 + 0 14 0 0 50 33 5 48 196 37 44 36 27 19 26 18 9 54 46 7 3 13 5 0 0 + 0 55 45 44 9 8 0 32 5 26 1 4 48 196 27 26 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 15 17 44 48 84 44 2 47 56 37 30 22 29 1 46 38 21 9 8 1 6 0 2 + 3 0 56 30 29 0 0 3 14 56 47 30 37 29 22 1 0 2 21 8 3 0 0 22 21 + 9 1 37 9 8 32 1 46 2 4 48 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 0 0 15 17 44 48 84 44 2 47 56 37 30 22 29 60 57 2 58 0 3 1 46 38 21 + 9 8 1 6 0 2 3 0 59 58 1 0 56 30 29 0 0 3 14 56 47 30 37 29 22 + 60 59 58 57 1 0 6 21 8 3 0 0 22 21 9 1 37 9 8 32 1 46 2 4 48 + 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 62 20 71 15 38 44 48 84 44 2 47 56 37 30 22 29 1 46 38 21 9 8 1 + 6 0 2 3 0 67 66 58 57 4 13 71 0 56 30 29 0 0 3 14 56 47 30 37 29 + 22 67 66 58 57 1 0 6 21 8 3 0 0 22 21 41 1 37 9 8 37 1 46 2 4 + 48 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 0 0 15 17 44 48 84 44 2 47 56 37 30 22 29 63 62 61 60 57 5 58 0 3 1 + 46 38 21 9 8 1 6 0 2 3 0 59 58 1 0 56 30 29 0 0 3 14 56 47 30 + 37 29 22 63 62 61 60 59 58 57 1 0 9 21 8 3 0 0 22 21 9 1 37 9 8 + 32 1 46 2 4 48 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 105 values pushed */ + 0 0 15 17 44 48 84 44 2 47 56 37 30 22 29 1 46 38 21 9 8 1 6 0 2 + 3 0 0 0 64 61 60 57 13 3 58 1 4 48 84 63 62 59 58 3 0 56 30 29 0 + 0 3 14 56 47 30 37 29 22 1 0 2 59 57 3 0 0 62 61 13 1 63 60 59 13 + 1 57 22 21 9 1 37 9 8 32 1 46 4 4 48 196 64 63 1 58 57 1 38 37 1 + 47 46 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 84 values pushed */ + 0 0 15 17 44 48 84 44 2 47 56 37 30 22 29 58 57 2 59 0 3 1 46 38 21 + 9 8 1 6 0 2 3 0 60 59 1 0 56 30 29 0 0 3 14 56 47 30 37 29 22 + 60 59 58 57 1 0 6 21 8 3 0 0 22 21 9 1 37 9 8 32 1 46 2 4 48 + 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 95 values pushed */ + 0 0 15 17 44 48 84 44 2 47 56 37 30 22 29 64 61 60 57 4 58 0 3 1 46 + 38 21 9 8 1 6 0 2 3 0 63 62 59 58 3 0 56 30 29 0 0 3 14 56 47 + 30 37 29 22 63 37 21 2 64 62 61 60 59 58 57 1 0 9 21 8 3 0 0 22 21 + 9 1 37 9 8 32 1 46 2 4 48 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 89 values pushed */ + 0 0 15 17 44 48 84 44 2 47 56 37 30 22 29 1 46 38 21 9 8 1 6 0 2 + 3 0 0 0 60 57 12 1 58 1 4 48 84 59 58 1 0 56 30 29 0 0 3 14 56 + 47 30 37 29 22 60 59 58 57 1 0 6 21 8 3 0 0 22 21 9 1 37 9 8 32 + 1 46 2 4 48 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 0 0 3 17 14 48 84 14 8 7 1 0 14 0 0 5 48 12 48 196 12 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 96 values pushed */ + 0 0 69 7 60 15 17 44 48 84 44 2 47 56 37 30 22 29 1 46 38 21 9 8 1 + 6 0 2 3 0 1 65 64 58 57 4 13 60 2 0 56 30 29 0 0 3 14 0 0 67 + 48 62 48 196 56 47 30 37 29 22 62 65 64 62 58 57 1 0 7 21 8 3 0 0 22 + 21 9 1 37 9 8 32 1 46 2 4 48 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 93 values pushed */ + 0 0 81 17 65 73 17 57 15 17 44 48 84 44 2 47 56 37 30 22 29 1 46 38 21 + 9 8 1 6 0 2 3 0 65 57 0 56 30 29 0 0 3 14 0 0 85 17 61 77 17 + 69 48 196 56 47 30 37 29 22 69 61 69 61 1 0 4 21 8 3 0 0 22 21 9 1 + 37 9 8 32 1 46 2 4 48 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 89 values pushed */ + 0 0 78 20 61 66 20 73 15 38 44 48 84 44 2 47 56 37 30 22 29 1 46 38 21 + 9 8 1 6 0 2 3 0 80 69 68 57 4 13 73 61 0 56 30 29 0 0 3 14 56 + 47 30 37 29 22 80 69 68 57 1 0 6 21 8 3 0 0 22 21 41 1 37 9 8 37 + 1 46 2 4 48 196 38 37 1 47 46 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 1 28 25 24 17 16 11 10 7 1 9 8 2 3 0 1 35 0 2 0 27 26 9 8 0 + 3 14 35 28 27 26 25 24 21 17 16 13 11 10 9 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 1 38 30 27 26 19 18 11 10 7 9 8 1 3 0 1 1 20 17 2 1 2 3 0 0 + 1 39 37 36 0 4 13 2 0 29 28 9 8 0 3 14 39 38 37 36 30 29 28 27 26 + 23 20 19 18 17 14 11 10 9 8 7 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 46 45 44 43 40 5 41 8 3 1 38 30 27 26 19 18 11 10 7 9 8 1 3 0 1 + 1 20 17 2 1 2 3 0 0 1 39 37 36 0 4 13 2 0 42 41 1 0 29 28 9 + 8 0 3 14 46 45 44 43 42 41 40 39 38 37 36 30 29 28 27 26 23 20 19 18 17 + 14 11 10 9 8 7 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 70 69 59 58 52 51 48 43 42 36 33 32 26 17 16 13 8 7 1 19 14 0 3 71 50 + 49 0 3 0 35 34 15 14 0 3 14 71 70 69 66 59 58 55 52 51 50 49 48 43 42 + 36 35 34 33 32 29 26 20 17 16 15 14 13 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 58 51 43 50 33 32 30 42 33 32 30 27 24 23 17 10 9 6 4 0 13 7 50 3 51 + 50 1 0 26 25 8 7 0 3 14 51 58 50 43 17 42 0 2 27 26 25 24 23 5 13 + 20 42 10 9 8 7 6 4 6 13 13 0 0 0 43 42 32 1 0 1 4 48 196 58 0 + 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MDAP[1] + MDAP[1] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 58 51 43 50 62 59 2 60 7 3 33 32 30 42 33 32 30 27 24 23 17 10 9 6 4 + 0 13 7 50 3 61 60 1 51 50 1 2 0 26 25 8 7 0 3 14 51 58 50 43 62 + 59 17 3 42 0 3 61 60 27 26 25 24 23 7 13 20 42 10 9 8 7 6 4 6 13 + 13 0 0 0 43 42 32 1 0 1 4 48 196 58 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + MDAP[1] + MDAP[1] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 98 values pushed */ + 58 51 43 50 65 64 63 62 59 5 60 7 3 33 32 30 42 33 32 30 27 24 23 17 10 + 9 6 4 0 13 7 50 3 61 60 1 51 50 1 2 0 26 25 8 7 0 3 14 51 58 + 50 43 64 60 17 3 42 0 3 63 62 61 27 26 25 24 23 8 13 20 42 65 59 10 9 + 8 7 6 4 8 13 13 0 0 0 43 42 32 1 0 1 4 48 196 58 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + MDAP[1] + MDAP[1] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 121 values pushed */ + 58 51 43 50 33 32 30 42 33 32 30 27 24 23 17 10 9 6 4 0 13 7 50 3 0 + 0 66 63 62 59 13 3 60 1 4 48 84 65 64 61 60 3 51 50 1 2 0 26 25 8 + 7 0 3 14 51 58 50 43 25 24 23 3 65 63 3 17 42 61 2 10 9 8 3 0 59 + 3 27 26 2 13 20 65 7 6 4 3 13 13 59 0 0 64 63 13 1 65 62 61 13 1 + 59 43 42 32 1 0 3 4 48 196 66 65 1 60 59 1 58 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + MDAP[1] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 14 12 2 2 22 21 11 10 4 2 15 3 1 15 0 2 0 0 3 2 21 1 12 16 15 + 35 1 0 2 4 48 84 23 0 1 0 13 12 0 14 21 16 15 14 13 10 3 2 8 22 + 11 3 1 0 11 23 22 1 12 11 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 27 24 2 25 12 3 14 12 2 2 22 21 11 10 4 2 15 3 1 15 0 2 0 0 3 + 2 21 1 12 16 15 35 1 0 2 4 48 84 26 25 1 23 0 1 2 0 13 12 0 14 + 27 26 25 24 21 16 15 14 13 10 3 2 12 22 11 3 1 0 11 23 22 1 12 11 1 + 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 14 12 2 2 22 21 11 10 4 2 15 3 1 15 0 2 30 29 28 27 24 5 13 25 0 + 0 3 2 21 1 12 16 15 35 1 0 2 4 48 84 26 25 1 23 0 1 2 0 13 12 + 0 14 30 29 28 27 26 25 24 21 16 15 14 13 10 3 2 15 22 11 3 1 0 11 23 + 22 1 12 11 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 14 12 2 2 22 21 11 10 4 2 15 3 1 15 0 2 0 0 27 24 5 1 25 3 2 + 21 1 12 16 15 35 1 0 3 4 48 84 26 25 1 23 0 1 2 0 13 12 0 14 21 + 16 14 13 2 5 22 26 3 15 10 3 3 24 11 3 1 0 11 0 0 27 26 4 1 24 + 1 4 48 196 25 24 1 23 22 1 12 11 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 45 22 2 14 21 23 48 84 37 2 23 1 2 2 28 35 1 1 41 40 39 27 21 + 20 19 18 10 9 8 0 12 1 2 3 0 0 1 35 2 0 14 0 0 43 10 6 48 196 + 35 28 0 27 9 2 41 19 18 8 4 9 20 3 6 20 0 0 40 39 10 9 4 3 27 + 1 4 48 196 28 27 1 21 20 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 12 values pushed */ + 3 0 1 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 112 values pushed */ + 0 0 60 17 29 52 22 4 37 9 42 16 21 25 48 84 42 2 29 1 25 1 4 2 1 + 56 27 23 22 21 20 12 7 1 54 3 0 31 11 10 3 54 32 3 1 48 47 46 40 39 + 0 6 32 2 3 0 0 0 33 32 14 1 54 1 4 48 84 55 54 1 0 14 0 0 50 + 10 8 48 196 48 21 20 10 4 11 22 3 56 55 54 40 39 33 32 31 27 0 10 13 11 + 8 22 47 46 12 11 3 23 22 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 9 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 83 17 17 75 15 3 48 84 17 0 3 2 1 77 67 25 11 4 0 39 3 0 65 + 63 49 41 38 31 29 1 8 39 0 3 40 39 1 64 0 1 2 0 14 0 0 87 5 13 + 81 16 21 71 5 7 48 196 77 67 65 64 63 49 41 40 39 38 31 29 25 21 13 11 7 + 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 3 0 6 1 1 1 4 48 84 2 1 1 0 14 0 0 3 2 6 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 109 values pushed */ + 0 0 71 17 55 63 17 47 45 22 2 14 21 23 48 84 37 2 23 1 2 2 28 35 1 + 1 41 40 39 27 21 20 19 18 10 9 8 0 12 1 2 3 0 0 1 55 47 1 0 1 + 35 2 0 14 0 0 75 17 51 67 17 59 43 10 6 48 196 35 28 51 51 0 2 27 9 + 3 59 59 41 19 18 8 5 9 20 3 6 20 0 0 40 39 10 9 4 3 27 1 4 48 + 196 28 27 1 21 20 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + CALL[ ] + MDAP[1] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 5 4 3 2 1 0 14 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 21 values pushed */ + 0 0 24 41 5 10 41 19 48 84 19 15 14 5 1 0 14 15 14 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 33 1 33 31 28 26 25 24 22 21 20 7 6 5 2 1 0 15 29 1 3 0 1 1 19 + 17 16 15 14 12 11 10 9 3 10 1 2 3 0 0 30 29 0 14 31 30 29 28 26 25 + 24 22 21 20 19 17 16 15 14 12 11 10 9 7 6 5 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 76 values pushed */ + 0 0 78 22 29 72 9 35 66 11 2 58 11 10 50 21 18 48 84 10 0 2 2 35 29 + 18 1 1 70 69 68 45 44 35 29 25 24 23 18 0 12 0 2 3 0 0 14 0 0 76 + 47 31 62 11 6 54 11 14 48 48 20 48 196 70 69 68 45 44 31 25 24 23 20 14 6 + 0 + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 0 0 33 29 14 27 21 22 48 84 22 2 14 1 1 7 1 2 8 1 3 0 1 1 35 + 25 10 3 1 2 3 0 0 1 24 0 2 0 9 8 1 0 14 0 0 31 10 18 48 196 + 18 9 8 7 0 0 0 35 25 24 10 9 4 4 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 9 values pushed */ + 3 2 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 3 2 1 0 14 0 0 3 2 9 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 45 values pushed */ + 56 44 43 42 41 29 22 17 16 15 14 2 1 0 14 0 0 39 24 48 19 24 10 48 196 + 56 54 48 44 43 42 41 33 29 25 22 17 16 15 14 10 4 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 56 44 43 42 41 36 29 22 17 16 15 14 2 1 0 14 0 0 39 24 48 19 24 10 48 + 196 56 54 48 44 43 42 41 36 33 29 25 22 17 16 15 14 10 4 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 0 0 4 3 14 1 1 6 5 14 1 0 2 4 48 84 2 1 1 7 0 1 2 0 14 + 7 6 3 2 4 13 4 0 0 5 4 13 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 0 0 4 3 14 1 5 2 1 14 1 0 2 4 48 84 6 5 1 7 0 1 2 0 14 + 5 4 1 0 4 13 2 0 0 3 2 13 1 6 1 4 48 196 7 6 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 17 values pushed */ + 0 0 5 20 14 48 84 14 10 9 1 0 14 10 9 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 28 values pushed */ + 7 6 5 4 3 2 1 0 14 0 0 7 6 3 2 9 3 0 1 4 48 196 5 4 1 + 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 15 values pushed */ + 0 0 0 8 48 84 8 14 0 0 4 12 48 196 12 + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 0 0 25 9 2 17 17 10 48 84 10 1 2 2 1 1 27 15 14 13 12 0 6 1 2 + 3 0 0 14 0 0 21 10 6 48 196 15 14 6 0 27 13 12 0 3 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 6 5 4 3 0 5 13 1 2 1 1 0 14 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 78 values pushed */ + 0 0 25 9 2 17 17 10 48 84 10 1 2 2 1 33 29 0 2 0 1 1 34 32 31 + 28 4 0 1 3 0 0 1 1 27 15 14 13 12 0 6 1 2 3 0 0 30 29 1 0 + 14 0 0 21 10 6 48 196 31 0 34 33 32 30 29 28 15 14 8 13 6 0 27 13 12 + 0 3 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 79 values pushed */ + 0 0 25 9 2 17 17 10 48 84 10 1 2 2 1 1 27 15 14 13 12 0 6 1 2 + 3 0 0 0 0 31 28 5 1 29 1 4 48 84 30 29 0 14 0 0 21 10 6 48 196 + 15 14 2 0 30 3 6 28 0 0 29 28 4 1 30 1 4 48 196 31 30 1 27 13 12 + 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 29 values pushed */ + 0 0 3 17 18 48 84 18 10 9 8 7 1 0 14 0 0 5 48 14 48 196 14 10 9 + 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 61 values pushed */ + 40 39 38 37 35 34 32 30 29 18 17 16 14 13 12 11 1 0 14 0 0 44 10 5 48 + 196 18 16 13 2 5 0 0 0 40 39 12 11 1 0 11 5 13 1 4 48 196 35 34 17 + 16 3 38 37 30 29 14 13 5 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 1 5 1 0 2 0 1 6 4 3 0 4 13 0 0 2 1 1 0 14 6 5 4 3 2 + 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 7 4 6 1 5 2 1 6 1 0 2 4 48 84 3 0 1 0 6 5 1 14 0 + 0 7 6 3 2 6 3 0 1 4 48 196 5 4 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 13 11 10 4 1 0 6 13 2 3 2 1 0 14 13 0 2 3 1 3 0 0 4 3 6 + 1 1 1 4 48 196 11 10 2 1 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 0 0 55 17 34 51 25 42 24 11 8 16 11 0 48 84 8 2 0 0 42 34 1 1 57 + 47 46 45 44 42 34 32 8 0 2 3 0 0 14 0 0 53 16 38 28 11 4 20 11 12 + 48 196 57 32 2 13 4 44 47 46 38 12 44 45 44 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 68 values pushed */ + 0 0 36 9 26 28 9 12 48 84 12 1 1 16 15 9 8 4 13 1 0 1 24 23 22 + 21 17 14 10 7 3 2 1 0 12 13 26 1 0 14 0 0 40 9 19 32 9 5 48 196 + 24 23 22 21 19 17 16 15 14 10 9 8 7 5 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 78 values pushed */ + 0 0 41 29 5 35 21 13 48 84 13 1 5 2 1 22 16 2 23 1 3 0 1 43 33 + 31 25 15 1 6 1 0 3 0 24 23 1 32 0 1 2 0 14 0 0 39 10 9 48 196 + 32 31 2 13 24 23 22 9 0 0 0 43 33 16 15 1 0 4 5 24 1 4 48 196 25 + 24 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 42 values pushed */ + 1 10 9 8 7 4 3 2 1 8 5 2 3 0 11 0 1 0 6 5 0 14 11 10 7 + 6 5 4 1 0 8 8 2 3 9 8 1 3 2 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 1 18 17 16 15 14 13 12 11 8 7 6 5 4 3 2 1 16 9 2 3 0 19 0 1 + 0 10 9 0 14 19 10 2 12 11 3 9 0 2 1 2 3 0 0 18 15 14 11 9 3 + 1 1 4 48 196 17 16 13 12 3 8 5 4 1 3 7 6 3 2 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 104 values pushed */ + 0 0 41 29 4 35 21 12 48 84 12 1 4 2 1 55 53 52 48 45 44 21 15 8 22 + 1 3 0 1 43 33 30 24 14 0 6 1 31 3 0 47 46 23 22 3 32 31 1 2 0 + 14 0 0 39 10 8 48 196 55 44 2 47 45 3 31 30 2 45 23 3 22 21 8 0 0 + 0 53 52 46 45 4 3 47 43 33 32 15 14 0 4 5 23 2 4 48 196 48 47 1 24 + 23 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 104 values pushed */ + 0 0 49 29 5 43 21 13 48 84 13 1 5 2 26 20 2 27 18 3 1 51 41 39 33 + 15 1 6 1 0 3 0 0 0 32 31 17 16 14 3 18 1 4 48 84 28 27 1 30 29 + 19 18 3 40 0 1 3 0 14 0 0 47 10 9 48 196 40 39 31 30 4 13 28 27 26 + 18 17 4 13 9 0 0 0 51 41 20 19 16 15 1 0 4 7 28 1 4 48 196 33 32 + 29 28 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 28 values pushed */ + 0 0 24 8 16 7 0 48 84 0 0 1 8 0 0 14 0 0 28 8 4 20 8 12 48 + 196 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 7 4 3 0 13 3 1 1 4 48 84 6 5 2 1 3 0 14 0 0 5 4 13 + 1 6 3 2 13 1 0 2 4 48 196 7 6 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 0 0 11 8 5 1 9 7 4 9 1 5 2 1 5 1 0 3 4 48 84 6 5 1 3 + 0 1 2 0 10 9 1 14 7 6 2 13 2 5 4 0 0 0 11 10 3 2 4 3 0 + 1 4 48 196 9 8 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 94 values pushed */ + 68 67 66 59 58 57 45 44 43 39 38 32 31 30 28 27 26 25 15 14 9 8 5 4 3 + 1 0 14 0 0 72 18 19 63 18 53 48 196 43 39 38 32 4 30 28 3 27 28 0 2 + 19 19 9 8 5 4 0 3 3 53 30 0 0 66 59 58 57 45 44 28 11 6 0 1 4 + 48 196 31 30 1 68 67 26 25 15 14 1 0 7 4 3 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 0 0 3 0 5 1 1 1 4 48 84 2 1 0 14 0 0 3 2 4 1 0 1 4 48 + 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 49 values pushed */ + 19 16 13 10 9 6 3 0 8 17 1 3 2 1 1 0 18 17 1 14 1 0 2 13 18 + 17 16 13 6 3 2 6 13 9 0 0 19 18 4 1 9 1 4 48 196 10 9 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 0 0 7 11 22 48 84 1 20 17 11 3 18 2 3 0 1 10 9 2 1 0 5 13 22 + 2 0 19 18 1 14 18 17 2 3 9 0 3 0 0 11 10 9 4 2 19 1 4 48 196 + 20 19 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 3 0 6 1 1 1 4 48 84 2 1 1 0 14 0 0 3 2 6 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 0 0 29 17 10 20 9 2 48 84 10 1 2 2 14 23 15 2 1 22 0 2 15 2 3 + 0 0 0 16 15 14 1 23 1 4 48 84 24 23 1 0 14 24 23 22 16 15 14 6 0 + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 0 0 38 20 47 29 17 10 20 9 2 48 84 10 1 2 2 14 23 15 2 1 22 0 2 + 15 2 3 0 1 43 42 34 33 4 13 47 1 0 0 0 16 15 14 1 23 1 4 48 84 + 24 23 1 0 14 43 42 34 33 24 23 22 16 15 14 6 0 + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 51 values pushed */ + 0 0 54 17 25 39 17 8 48 84 25 2 8 0 1 1 48 33 16 0 4 0 2 3 0 + 0 14 0 0 58 5 21 50 5 29 43 18 4 35 18 12 48 196 48 33 29 21 16 12 4 + 0 + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 10 9 6 5 2 1 5 5 0 1 4 48 84 11 8 7 4 3 0 5 0 14 0 + 0 9 8 4 1 10 7 6 4 1 4 3 2 4 1 0 3 4 48 196 11 10 1 5 4 + 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 9 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 88 values pushed */ + 0 0 46 29 21 37 11 28 48 84 21 1 55 49 48 41 26 25 17 14 8 7 1 11 15 + 0 3 40 39 32 31 30 5 13 28 0 56 0 1 0 16 15 1 14 56 55 32 3 39 16 + 3 15 14 1 0 4 13 7 0 0 41 40 39 4 2 25 49 48 17 16 4 3 7 2 4 + 48 196 26 25 1 31 30 1 8 7 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 33 values pushed */ + 0 0 7 4 9 1 5 2 1 9 1 0 2 4 48 84 6 5 1 3 0 1 2 0 14 + 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 0 0 29 17 10 20 9 2 48 84 10 1 2 2 14 23 15 2 1 22 0 2 15 2 3 + 0 0 0 16 15 14 1 23 1 4 48 84 24 23 1 0 14 24 23 22 16 15 14 6 0 + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 69 values pushed */ + 0 0 39 17 21 31 17 29 48 84 29 1 21 2 1 1 13 10 5 4 3 2 6 0 1 + 3 0 0 1 1 0 1 2 2 0 0 1 12 11 8 7 4 13 0 0 14 0 0 43 10 + 17 35 10 25 48 196 25 17 13 12 11 10 8 7 5 4 3 2 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 52 values pushed */ + 9 8 5 4 4 6 1 3 0 0 2 1 5 1 0 1 4 48 84 3 0 1 0 7 6 + 0 14 9 4 2 2 0 3 0 0 8 7 3 2 4 3 0 1 4 48 196 6 5 1 0 + 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 1 9 4 2 1 2 3 0 1 8 5 2 2 6 3 0 0 0 2 1 5 1 0 1 4 + 48 84 7 6 1 0 3 0 1 14 9 4 2 0 2 3 0 0 6 5 1 0 4 3 2 + 1 4 48 196 8 7 3 2 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 95 values pushed */ + 0 0 26 11 16 48 84 40 34 7 1 4 8 0 3 29 24 23 20 19 18 12 7 13 16 + 10 0 0 33 32 7 1 10 1 4 9 8 1 10 1 6 48 84 41 0 1 0 31 30 11 + 10 1 3 14 41 40 32 31 24 23 20 7 18 29 3 10 9 1 0 4 13 7 0 0 34 + 33 30 29 4 3 7 1 4 48 196 19 18 1 12 11 8 7 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 138 values pushed */ + 0 0 31 11 20 48 84 49 45 24 23 2 1 0 3 34 16 2 0 14 3 59 53 50 43 + 37 11 5 7 12 4 3 22 20 1 0 0 52 51 7 1 14 1 4 13 12 1 14 1 6 + 3 0 5 1 1 1 4 48 84 60 45 44 4 3 0 36 35 15 14 1 3 2 1 0 14 + 45 49 49 50 22 2 60 59 24 3 22 34 3 44 43 2 13 2 14 13 5 4 4 13 11 + 0 0 51 50 4 1 2 53 52 35 34 4 3 11 1 0 13 1 2 3 4 48 196 23 22 + 1 16 15 12 11 3 37 36 3 2 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 138 values pushed */ + 0 0 31 11 20 48 84 49 45 24 23 2 1 0 3 34 16 2 0 14 3 59 53 50 43 + 37 11 5 7 12 4 3 22 20 1 0 0 52 51 7 1 14 1 4 13 12 1 14 1 6 + 3 0 5 1 1 1 4 48 84 60 45 44 4 3 0 36 35 15 14 1 3 2 1 0 14 + 45 49 49 50 22 2 60 59 24 3 22 34 3 44 43 2 13 2 14 13 5 4 4 13 11 + 0 0 51 50 4 1 2 53 52 35 34 4 3 11 1 0 13 1 2 3 4 48 196 23 22 + 1 16 15 12 11 3 37 36 3 2 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 6 11 27 48 84 27 2 1 19 18 13 12 2 1 6 16 2 3 0 1 0 2 0 + 0 0 17 16 13 1 14 1 4 48 84 15 14 0 14 0 0 10 10 23 48 196 19 2 2 + 15 17 3 12 17 13 2 23 15 0 0 18 17 11 1 13 1 4 48 196 16 15 1 14 13 + 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 115 values pushed */ + 0 0 38 11 18 48 84 34 30 41 36 12 3 20 10 3 52 46 35 28 22 7 1 7 8 + 0 3 18 20 0 0 45 44 7 1 10 1 4 9 8 1 10 1 6 48 84 21 20 1 53 + 30 29 0 3 2 0 43 42 11 10 1 3 14 30 34 53 52 44 43 34 20 6 35 41 3 + 29 28 2 13 21 10 9 1 0 4 13 7 0 0 36 35 4 1 21 46 45 42 41 4 3 + 7 2 4 48 196 22 21 1 12 11 8 7 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 115 values pushed */ + 0 0 38 11 18 48 84 34 30 41 36 12 3 20 10 3 52 46 35 28 22 7 1 7 8 + 0 3 18 20 0 0 45 44 7 1 10 1 4 9 8 1 10 1 6 48 84 21 20 1 53 + 30 29 0 3 2 0 43 42 11 10 1 3 14 30 34 53 52 44 43 34 20 6 35 41 3 + 29 28 2 13 21 10 9 1 0 4 13 7 0 0 36 35 4 1 21 46 45 42 41 4 3 + 7 2 4 48 196 22 21 1 12 11 8 7 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 0 0 17 11 9 48 84 9 0 1 13 12 11 3 0 3 3 0 0 0 25 24 2 1 14 + 3 3 1 4 48 84 23 22 4 3 3 26 0 1 2 0 14 26 25 24 23 22 13 4 3 + 2 1 0 11 13 11 12 11 1 0 + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 25 18 2 5 0 2 16 9 2 0 17 3 28 4 3 3 13 5 0 0 27 26 6 5 19 + 3 0 1 4 48 84 8 7 1 0 3 18 17 1 2 0 14 18 25 17 16 2 6 4 3 + 3 4 0 2 26 2 1 3 13 0 0 0 28 27 25 0 13 3 4 1 4 48 196 7 6 + 1 9 8 5 4 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 10 13 8 2 23 17 7 1 4 8 0 3 27 12 11 3 13 13 0 0 16 15 9 8 46 + 3 13 1 4 48 84 26 25 14 13 3 24 0 1 2 0 14 11 12 7 2 24 23 15 14 + 4 13 12 25 10 9 1 0 5 13 7 0 0 27 26 8 7 15 3 12 1 4 48 196 17 + 16 13 12 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 9 values pushed */ + 3 2 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 9 values pushed */ + 3 2 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 0 0 61 17 41 51 17 24 47 17 13 48 84 13 1 24 1 33 32 26 24 18 17 15 5 + 0 9 1 55 3 0 1 16 1 0 41 55 67 55 1 0 14 0 0 65 30 37 57 30 45 + 53 5 20 49 5 9 48 196 67 55 45 37 33 32 28 26 20 18 17 16 15 9 5 2 0 + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 101 values pushed */ + 0 0 61 17 41 51 17 24 47 17 13 48 84 13 1 1 73 69 0 2 0 1 1 74 72 + 71 68 16 5 0 1 3 0 0 24 1 33 32 26 24 18 17 15 5 0 9 1 55 3 0 + 41 55 70 69 1 67 55 1 2 0 14 0 0 65 30 37 57 30 45 53 5 20 49 5 9 + 48 196 74 73 72 71 70 69 68 67 55 45 37 33 32 28 26 20 18 17 16 15 9 5 2 + 0 + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 121 values pushed */ + 0 0 61 17 41 51 17 24 47 17 13 48 84 13 1 1 16 70 1 2 0 24 1 33 32 + 26 24 18 17 15 5 0 9 1 55 3 0 79 77 76 72 69 68 6 13 70 41 55 71 70 + 1 67 55 1 2 0 14 0 0 65 30 37 57 30 45 53 5 20 49 5 9 48 196 79 68 + 67 33 32 26 6 69 71 3 18 17 16 15 4 13 37 20 69 55 5 0 3 13 45 28 9 + 2 4 12 71 0 0 77 76 70 69 4 3 71 1 4 48 196 72 71 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 120 values pushed */ + 0 0 61 17 41 51 17 24 47 17 13 48 84 13 1 1 16 68 1 2 0 24 1 33 32 + 26 24 18 17 15 5 0 9 1 55 3 0 1 41 2 0 0 0 71 68 5 1 69 1 4 + 48 84 67 55 1 0 70 69 0 14 0 0 65 30 37 57 30 45 53 5 20 49 5 9 48 + 196 67 33 32 26 4 70 68 3 18 17 16 15 4 13 37 20 70 55 5 0 3 13 45 28 + 9 2 4 12 68 0 0 71 70 4 1 68 1 4 48 196 69 68 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 22 11 47 6 11 69 48 84 69 2 43 42 36 33 27 26 2 1 0 9 13 47 34 + 35 34 1 0 14 0 0 20 16 51 10 24 65 48 196 15 34 33 15 3 0 26 3 2 65 + 57 51 3 12 0 36 35 42 0 0 27 26 4 1 42 1 4 48 196 1 0 1 43 42 1 + 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + MDRP[00000] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 12 values pushed */ + 1 0 2 3 2 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + NPUSHB[ ] /* 15 values pushed */ + 6 5 4 3 2 1 0 14 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 11 10 9 8 7 6 5 4 3 2 1 0 14 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 11 10 9 8 7 6 5 4 3 2 1 0 14 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 5 4 3 2 1 0 14 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 5 4 3 2 1 0 14 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 94 values pushed */ + 0 0 47 29 21 48 84 21 1 51 57 1 14 8 2 15 1 3 0 1 50 49 42 41 35 + 32 26 25 17 7 1 11 1 0 3 0 16 15 1 57 34 33 0 3 2 0 14 57 51 51 + 35 34 3 41 16 3 33 32 2 13 25 15 14 1 0 4 13 7 0 0 42 41 4 1 25 + 50 49 17 16 4 3 7 2 4 48 196 26 25 1 8 7 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 118 values pushed */ + 0 0 55 29 29 48 84 29 1 59 65 18 12 2 19 10 3 1 58 57 50 49 43 40 34 + 33 25 7 1 11 1 0 3 0 0 0 24 23 9 8 14 3 10 1 4 48 84 20 19 1 + 22 21 11 10 3 65 42 41 0 3 3 0 14 65 59 59 43 42 23 22 5 49 20 3 41 + 40 2 13 33 19 18 10 9 1 0 6 13 7 0 0 50 49 4 1 33 58 57 25 24 21 + 20 4 5 7 2 4 48 196 34 33 1 12 11 8 7 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 121 values pushed */ + 0 0 47 39 21 48 84 21 1 51 57 64 63 62 61 58 5 59 15 3 1 14 8 2 15 + 1 3 0 1 50 49 42 41 35 32 26 25 17 7 1 11 1 0 3 0 60 59 1 16 15 + 1 57 34 33 0 3 3 0 14 57 51 62 61 2 25 41 3 63 60 59 51 35 34 6 41 + 16 3 64 58 2 16 7 3 33 32 2 13 25 15 14 1 0 4 13 7 0 0 42 41 5 + 1 25 50 49 17 16 5 3 7 2 4 48 196 26 25 1 8 7 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 22 values pushed */ + 7 4 3 0 4 13 1 6 5 2 1 3 0 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 27 21 18 15 12 11 8 5 8 19 4 3 0 0 3 0 5 1 1 1 4 48 84 28 4 + 1 0 20 19 1 2 1 0 14 28 27 2 13 2 19 18 15 8 5 4 6 13 0 0 0 + 21 20 3 2 4 3 0 1 4 48 196 12 11 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 0 0 30 20 39 48 84 19 16 13 10 9 3 0 7 17 1 3 35 34 26 25 4 13 39 + 17 2 1 1 0 18 17 1 14 35 34 1 0 4 13 18 26 25 17 16 13 3 2 7 13 + 9 0 0 19 18 4 1 9 1 4 48 196 10 9 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 19 16 13 10 9 3 0 7 17 1 3 0 0 32 29 28 25 13 3 26 1 4 48 84 31 + 30 27 26 3 2 1 1 2 0 18 17 1 14 1 0 2 31 29 3 17 16 13 3 2 5 + 27 25 3 0 0 30 29 13 1 31 28 27 13 1 25 19 18 4 1 9 3 4 48 196 32 + 31 1 26 25 1 10 9 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 46 20 29 34 20 41 48 84 29 0 41 1 48 41 25 3 0 17 3 0 19 16 13 + 10 9 3 0 7 17 1 3 1 37 36 2 13 0 0 2 1 1 0 18 17 1 14 37 36 + 1 0 4 13 18 48 25 17 16 13 3 2 7 13 9 0 0 19 18 5 1 9 1 4 48 + 196 10 9 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 76 values pushed */ + 0 0 7 17 22 48 84 1 20 17 11 3 18 2 3 0 1 10 9 2 1 0 5 13 22 + 2 0 0 0 27 24 5 1 25 1 4 48 84 26 25 0 19 18 1 14 18 17 2 3 9 + 0 3 0 0 25 24 11 10 9 4 4 19 1 4 48 196 27 26 20 19 3 1 0 1 2 + 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 0 0 7 17 22 48 84 1 29 25 0 2 0 1 30 28 27 24 4 0 18 3 0 1 20 + 17 11 3 18 2 3 0 1 10 9 2 1 0 5 13 22 2 0 26 25 1 0 19 18 1 + 14 29 26 2 19 9 3 30 25 24 18 17 2 6 9 0 3 28 27 2 13 19 0 0 20 + 19 4 1 9 1 4 48 196 11 10 9 2 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 14 8 2 15 35 3 61 55 54 53 50 44 37 34 33 32 31 28 27 26 25 18 17 7 1 + 19 35 0 3 16 15 1 62 52 51 0 3 2 0 36 35 1 14 62 61 53 52 51 50 44 + 37 36 35 34 33 32 31 28 27 26 25 18 19 13 23 16 15 14 1 0 4 13 7 0 0 + 55 54 17 16 4 3 7 1 4 48 196 8 7 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 55 54 53 50 49 46 39 36 35 34 33 30 29 28 27 20 19 16 10 9 3 0 22 17 1 + 3 52 51 2 1 3 0 38 37 18 17 1 3 14 53 52 51 50 49 46 39 38 37 36 35 + 34 33 30 29 28 27 20 1 0 20 13 25 18 17 16 3 2 4 13 9 0 0 55 54 19 + 18 4 3 9 1 4 48 196 10 9 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 48 values pushed */ + 23 17 14 8 7 4 1 7 15 0 3 16 15 1 24 0 1 2 0 14 24 23 2 13 16 + 15 14 4 1 0 5 13 7 0 0 17 16 4 1 7 1 4 48 196 8 7 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 36 34 33 29 26 25 19 16 10 9 6 3 0 13 17 1 3 28 27 18 17 3 2 1 1 + 2 0 14 36 25 2 28 26 3 1 0 2 26 18 3 17 16 6 3 2 5 13 9 0 0 + 34 33 27 26 4 3 28 19 18 4 1 9 2 4 48 196 29 28 1 10 9 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 76 values pushed */ + 16 10 2 17 26 3 19 9 6 3 0 5 25 1 3 0 0 28 25 5 1 26 1 4 48 + 84 27 26 1 18 17 1 2 1 1 3 0 14 1 0 2 25 18 3 17 16 6 3 2 5 + 13 9 0 0 26 25 4 1 27 19 18 4 1 9 2 4 48 196 28 27 1 10 9 1 2 + 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + NPUSHB[ ] /* 15 values pushed */ + 6 5 4 3 2 1 0 14 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 41 values pushed */ + 7 0 1 0 0 6 5 2 1 9 3 3 1 4 48 84 4 3 1 0 14 3 2 0 0 + 0 1 0 9 1 4 1 4 48 196 7 6 5 4 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 0 0 18 38 8 48 84 37 31 28 22 4 0 29 3 21 16 15 12 11 10 4 7 13 8 + 2 0 0 1 0 8 1 2 1 4 48 84 30 29 1 0 3 2 1 14 29 28 16 15 12 + 5 10 21 3 31 30 2 1 4 13 0 0 0 22 21 5 1 0 1 4 48 196 11 10 1 + 37 4 3 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 64 values pushed */ + 31 25 24 23 22 21 18 12 11 10 9 8 7 4 1 15 19 0 3 20 19 1 32 0 1 + 2 0 14 32 31 23 22 4 13 20 19 18 10 9 4 1 0 7 13 7 0 0 25 24 21 + 20 4 3 7 1 4 48 196 12 11 8 7 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 114 values pushed */ + 0 0 79 29 21 55 29 29 48 84 29 1 21 1 88 82 81 74 73 67 64 58 57 50 49 + 43 40 34 33 25 17 14 8 7 1 21 15 0 3 89 66 65 42 41 0 5 0 16 15 1 + 14 65 64 43 42 4 49 25 3 89 88 67 66 4 73 16 3 41 40 2 13 33 15 14 1 + 0 4 13 7 0 0 50 49 4 1 33 58 57 25 4 2 73 82 81 17 16 4 3 7 3 + 4 48 196 34 33 1 74 73 1 8 7 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 9 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 18 29 41 48 84 41 2 39 36 30 27 21 20 13 10 4 3 10 11 37 3 0 37 + 1 2 38 37 1 2 1 1 2 0 29 28 12 11 1 3 14 28 27 2 20 0 3 37 36 + 2 13 29 11 10 2 0 0 39 38 21 20 4 3 29 13 12 1 0 4 3 2 2 4 48 + 196 30 29 1 4 3 2 2 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 1 11 10 9 8 7 6 5 4 3 2 1 0 12 13 1 0 14 11 10 9 8 7 6 5 + 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 47 29 21 48 84 21 1 51 57 50 49 42 41 35 32 26 25 17 14 8 7 1 13 + 15 0 3 57 34 33 0 3 0 16 15 1 14 57 51 51 35 34 3 41 16 3 33 32 2 + 13 25 15 14 1 0 4 13 7 0 0 42 41 4 1 25 50 49 17 16 4 3 7 2 4 + 48 196 26 25 1 8 7 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 116 values pushed */ + 0 0 47 29 21 48 84 21 1 51 57 69 67 66 62 59 58 6 60 15 3 50 49 42 41 + 35 32 26 25 17 14 8 7 1 13 15 0 3 61 60 1 57 34 33 0 3 2 0 16 15 + 1 14 57 51 51 35 34 3 41 16 3 15 14 1 0 4 7 61 3 69 58 2 61 59 3 + 33 32 2 13 25 0 0 62 61 4 1 59 42 41 4 1 25 50 49 17 16 4 3 7 3 + 4 48 196 67 66 60 59 3 26 25 1 8 7 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 54 values pushed */ + 0 0 39 17 4 33 17 12 29 17 20 48 84 20 2 12 0 4 1 1 24 23 22 4 0 + 5 0 2 3 0 0 14 0 0 43 10 16 37 5 8 48 196 24 0 2 13 16 22 8 22 + 23 22 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 14 13 10 9 4 13 7 27 24 23 0 4 13 1 0 0 31 30 18 17 6 5 9 5 7 + 26 25 22 21 2 1 9 5 3 2 4 48 84 16 15 12 11 8 7 5 29 28 20 19 4 + 3 5 2 0 14 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 + 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 22 17 8 16 17 0 48 84 8 2 0 1 14 0 0 26 10 4 20 10 12 48 196 + 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 35 20 44 22 38 8 16 38 0 48 84 8 2 0 1 1 40 39 31 30 4 13 44 + 1 0 14 0 0 26 40 4 20 40 12 48 196 40 39 31 30 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 53 11 2 41 11 25 37 11 33 12 9 17 48 84 33 1 25 2 17 2 2 1 1 + 51 0 2 1 49 3 0 6 49 7 2 1 21 15 14 3 7 2 3 0 0 0 8 7 14 + 1 49 1 4 48 84 50 49 1 0 14 0 0 39 10 29 48 196 51 50 49 45 29 21 15 + 14 8 7 6 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 0 0 12 7 3 48 84 8 7 3 1 0 14 0 0 10 48 5 48 196 8 7 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 42 values pushed */ + 17 9 25 24 18 7 1 0 6 13 21 8 9 8 1 0 14 9 17 8 7 2 13 0 25 + 24 17 0 0 1 0 4 1 17 1 4 48 196 18 17 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 106 values pushed */ + 0 0 37 45 44 48 84 44 44 42 41 40 39 5 4 55 3 30 55 29 2 27 21 20 19 + 18 12 11 5 2 1 10 13 15 4 3 0 29 0 0 56 55 43 1 29 1 4 48 84 57 + 29 1 28 4 1 2 0 14 0 0 35 44 48 48 196 57 56 55 42 41 40 39 30 29 28 + 27 2 1 13 13 48 20 19 18 5 4 3 0 6 13 11 0 0 21 20 12 1 11 1 4 + 48 196 12 11 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 129 values pushed */ + 31 16 15 3 32 17 3 14 17 12 2 27 26 21 11 6 5 6 12 4 3 55 49 48 47 + 46 40 39 33 2 1 10 13 43 32 3 0 4 0 0 30 29 18 17 46 3 12 1 4 48 + 84 56 32 1 20 19 13 12 3 28 4 1 3 0 14 15 1 2 16 11 3 56 55 29 14 + 13 6 5 4 8 11 48 3 28 27 26 19 18 2 6 13 16 47 46 33 32 3 0 6 13 + 39 0 0 49 48 12 1 39 31 30 12 11 15 3 16 2 4 48 196 40 39 1 21 20 17 + 16 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 23 17 16 15 14 8 7 1 8 13 11 0 24 0 1 0 14 24 23 2 13 16 15 14 1 + 0 4 13 7 0 0 17 16 12 1 7 1 4 48 196 8 7 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 78 values pushed */ + 0 0 39 7 2 12 25 19 48 84 19 0 1 35 34 33 29 28 24 23 17 16 15 14 8 + 7 6 0 15 13 31 26 2 3 12 0 0 14 0 0 37 47 4 48 196 29 28 2 13 23 + 35 17 16 15 14 6 6 13 4 0 0 0 34 33 8 7 0 23 4 23 1 4 48 196 24 + 23 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 29 values pushed */ + 0 0 20 25 8 16 25 0 48 84 0 0 1 8 0 0 14 0 0 22 24 4 18 24 12 + 48 196 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 0 0 33 17 18 24 17 7 48 84 18 2 7 1 1 1 39 31 30 22 20 12 9 1 8 + 1 2 3 0 0 1 11 10 2 13 1 0 1 21 0 2 0 14 0 0 37 10 14 28 10 + 3 48 196 39 31 30 22 21 20 14 12 11 10 9 3 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 0 0 48 29 21 42 21 29 48 84 29 2 21 1 1 50 40 17 14 8 5 15 2 3 0 + 1 38 32 31 7 1 5 2 0 3 0 39 0 1 0 16 15 1 14 0 0 46 10 25 48 + 196 39 38 2 13 25 16 15 14 1 0 4 13 7 0 0 50 40 32 31 17 16 4 5 7 + 1 4 48 196 8 7 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 1 1 16 2 2 0 1 18 15 14 0 4 13 2 0 0 0 17 16 9 1 12 1 4 48 + 84 13 12 0 14 12 15 17 2 5 0 0 0 16 15 21 1 13 1 0 21 1 17 2 4 + 48 196 14 13 1 18 17 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 17 values pushed */ + 10 9 1 0 14 0 0 14 5 5 48 196 10 9 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 17 values pushed */ + 10 9 1 0 14 0 0 14 5 5 48 196 10 9 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 69 values pushed */ + 0 0 48 11 36 44 11 28 24 11 12 20 11 4 48 84 36 2 4 0 28 12 1 1 28 + 12 2 0 2 3 0 0 1 2 1 2 13 0 0 1 3 0 2 0 14 0 0 50 47 32 + 46 47 40 26 47 8 22 47 16 48 196 40 32 16 8 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 2 1 6 1 0 1 4 48 84 3 0 1 0 14 0 0 3 2 6 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 3 0 6 1 1 1 4 48 84 2 1 1 0 14 0 0 3 2 6 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 3 0 6 1 1 1 4 48 84 2 1 1 0 14 0 0 3 2 6 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 68 11 56 64 11 48 44 11 32 40 11 24 20 11 8 16 11 0 48 84 56 2 32 + 2 0 0 48 24 8 1 1 48 24 8 3 0 2 3 0 0 1 74 73 2 13 0 0 1 + 75 72 2 0 14 0 0 70 47 52 66 47 60 46 47 28 42 47 36 22 47 4 18 47 12 + 48 196 75 74 73 72 60 52 36 28 12 4 + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 55 values pushed */ + 6 5 2 13 3 11 0 1 0 0 10 9 2 1 9 3 3 1 4 48 84 8 7 4 3 + 3 0 14 9 8 2 13 6 3 2 0 0 0 11 10 7 6 9 3 0 1 4 48 196 5 + 4 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 15 4 2 5 1 3 10 9 2 13 7 0 0 14 13 6 5 9 3 7 2 1 9 1 0 + 2 4 48 84 12 11 8 7 3 3 0 1 2 0 14 13 12 3 2 4 13 10 7 6 1 + 0 4 13 4 0 0 15 14 11 10 9 3 4 1 4 48 196 9 8 5 4 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 0 0 40 29 12 34 21 20 48 84 20 1 12 2 1 42 32 8 3 22 2 3 0 1 30 + 24 7 1 4 2 0 3 0 31 0 1 0 23 22 1 14 0 0 38 10 16 48 196 31 30 + 2 13 23 1 0 16 7 0 0 42 32 22 8 7 4 4 23 1 4 48 196 24 23 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 14 11 22 48 84 22 0 1 33 20 19 18 5 5 0 4 3 0 0 0 2 1 5 + 1 0 1 4 48 84 34 4 1 3 0 1 2 0 14 0 0 10 10 26 48 196 18 0 19 + 2 26 2 0 0 34 33 3 2 4 3 0 1 4 48 196 34 33 3 2 3 20 19 1 5 + 4 1 0 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 0 0 14 11 22 48 84 22 2 1 35 20 19 18 5 5 4 2 3 0 0 0 2 1 5 + 1 0 1 4 48 84 36 4 1 3 0 0 14 0 0 10 10 26 48 196 18 19 0 2 26 + 2 0 0 5 4 1 0 4 3 2 1 4 48 196 20 19 1 36 35 3 2 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 22 values pushed */ + 7 4 3 0 4 13 1 6 5 2 1 3 0 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 27 25 24 18 15 14 13 11 10 4 1 0 12 13 2 17 16 3 2 3 0 14 13 0 2 + 3 1 3 27 14 2 17 15 3 0 0 18 17 6 1 15 11 10 2 1 6 3 3 2 4 + 48 196 25 24 16 15 3 4 3 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 27 25 24 18 15 14 13 11 10 4 1 0 12 13 2 17 16 3 2 3 0 14 27 14 2 + 15 17 3 13 0 2 1 3 3 0 0 18 17 6 1 15 11 10 2 1 6 3 3 2 4 + 48 196 25 24 16 15 3 4 3 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 27 25 24 18 15 14 13 11 10 4 1 0 12 13 2 17 16 3 2 3 0 14 13 0 2 + 3 1 3 27 14 2 17 15 3 0 0 18 17 6 1 15 11 10 2 1 6 3 3 2 4 + 48 196 25 24 16 15 3 4 3 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 13 11 10 4 1 0 6 13 2 3 2 1 0 14 13 0 2 1 3 3 0 0 11 10 2 + 1 6 3 3 1 4 48 196 4 3 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 13 11 10 4 1 0 6 13 2 3 2 1 0 14 13 0 2 3 1 3 0 0 4 3 6 + 1 1 1 4 48 196 11 10 2 1 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 13 11 10 4 1 0 6 13 2 3 2 1 0 14 13 0 2 3 1 3 0 0 4 3 6 + 1 1 1 4 48 196 11 10 2 1 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 12 values pushed */ + 3 0 1 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 68 values pushed */ + 0 0 31 13 19 48 84 19 1 40 34 33 23 22 17 14 8 7 1 10 15 0 3 21 15 + 41 0 1 0 16 15 1 14 41 40 23 3 21 16 3 15 14 1 0 4 13 7 0 0 34 + 33 17 16 4 3 7 1 4 48 196 22 21 1 8 7 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 101 values pushed */ + 0 0 24 11 8 16 11 0 48 84 8 2 0 0 75 74 70 69 68 65 62 61 50 46 45 + 41 38 33 32 15 51 39 3 52 51 1 67 66 40 39 3 2 0 14 0 0 72 18 59 28 + 11 4 20 11 12 48 196 52 32 45 2 74 70 68 67 66 65 62 61 39 38 10 13 59 4 + 32 51 50 41 40 4 13 12 45 0 0 75 69 33 32 26 3 45 1 4 48 196 46 45 1 + 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 0 0 24 17 8 16 17 0 48 84 8 0 14 0 0 28 17 4 20 17 12 48 196 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 59 values pushed */ + 0 0 26 17 19 6 17 40 48 84 40 2 19 1 1 1 24 23 22 21 2 1 0 7 1 + 2 3 0 0 14 0 0 28 16 17 8 5 36 48 196 17 24 23 17 2 4 21 0 3 36 + 21 22 21 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 91 values pushed */ + 0 0 26 17 19 6 17 40 48 84 40 2 19 1 1 47 43 0 2 0 1 1 48 46 45 + 42 4 0 1 3 0 0 1 1 24 23 22 21 2 1 0 7 1 2 3 0 0 44 43 1 + 0 14 0 0 28 16 17 8 5 36 48 196 17 48 47 44 43 24 23 17 2 8 21 0 3 + 46 45 2 13 36 21 42 0 22 21 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 41 11 31 8 11 64 48 84 31 0 1 72 66 56 35 34 33 23 2 1 0 10 13 + 64 0 0 14 0 0 74 18 21 68 47 54 45 18 27 12 18 60 48 196 27 21 72 66 56 + 35 27 23 21 2 8 33 0 3 60 54 33 34 33 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 1 4 2 2 2 0 1 13 11 10 1 0 5 13 2 0 0 0 17 14 6 1 15 1 4 + 48 84 3 2 1 0 16 15 1 14 13 0 2 3 1 3 0 0 17 16 4 3 6 3 1 + 1 4 48 196 15 14 11 10 2 1 5 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 1 4 2 2 2 0 1 13 11 10 1 0 5 13 2 0 0 0 17 14 6 1 15 1 4 + 48 84 3 2 1 0 16 15 1 14 13 0 2 3 1 3 0 0 17 16 4 3 6 3 1 + 1 4 48 196 15 14 11 10 2 1 5 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 35 values pushed */ + 7 5 3 2 0 0 4 3 28 1 5 1 4 48 84 11 0 1 0 6 5 0 14 11 7 + 6 3 0 5 13 4 5 4 1 0 + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 52 values pushed */ + 0 0 41 17 12 33 17 4 29 17 20 48 84 20 0 12 2 4 1 1 24 23 22 4 0 + 5 0 2 3 0 0 14 0 0 45 5 8 37 10 16 48 196 8 22 24 0 16 22 23 22 + 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 9 values pushed */ + 3 2 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 98 values pushed */ + 0 0 20 11 12 48 84 12 0 1 24 16 15 14 10 5 0 8 3 0 29 5 2 6 1 + 3 0 0 28 27 7 6 14 3 8 32 31 1 19 2 0 2 4 48 84 26 25 9 8 3 + 33 0 1 2 0 14 27 26 16 3 14 24 3 31 8 7 3 5 0 3 0 0 29 28 25 + 24 4 3 5 1 4 48 196 33 32 15 14 3 10 9 6 5 3 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 18 2 11 16 1 22 16 10 3 0 2 3 0 5 4 2 13 2 0 0 9 8 1 0 3 + 2 1 6 48 84 7 6 3 2 1 3 14 16 11 8 7 2 13 5 2 1 0 0 0 11 + 10 9 6 5 4 4 0 1 4 48 196 22 4 3 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 26 2 19 24 1 30 24 18 3 0 2 3 0 9 8 2 13 6 0 0 13 12 5 4 3 + 6 1 6 17 16 1 0 21 3 2 1 4 48 84 15 14 3 2 3 0 11 10 7 6 1 + 3 14 24 19 16 15 12 11 4 13 9 6 5 2 1 4 13 0 0 0 19 18 17 14 13 + 10 9 4 6 0 1 4 48 196 30 8 7 4 3 0 5 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 18 2 11 16 34 32 31 27 24 23 5 4 8 25 2 3 1 22 16 10 3 0 2 3 0 + 0 0 9 8 1 0 3 2 1 6 48 84 26 25 1 0 7 6 3 2 1 3 14 16 11 + 34 23 8 7 4 26 24 3 2 1 0 0 0 32 31 25 24 4 3 26 11 10 9 6 5 + 4 4 0 2 4 48 196 27 26 1 22 4 3 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 104 values pushed */ + 0 0 26 17 41 48 84 18 2 11 16 1 22 16 10 3 0 2 3 0 5 4 2 13 2 + 1 33 32 31 30 24 23 6 13 41 2 0 0 0 9 8 1 0 3 2 1 6 48 84 7 + 6 3 2 1 3 14 0 0 28 48 37 48 196 16 11 33 31 30 24 23 5 5 0 3 32 + 8 7 3 13 37 5 2 1 0 0 0 11 10 9 6 5 4 4 0 1 4 48 196 22 4 + 3 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 47 29 20 41 21 28 48 84 28 2 20 1 1 13 14 1 2 0 1 1 49 39 16 + 3 1 2 3 0 0 1 37 31 30 7 1 5 2 0 3 0 15 14 1 38 0 1 2 0 + 14 0 0 45 10 24 48 196 38 37 2 13 24 15 14 13 7 1 0 5 13 15 49 39 31 + 30 16 15 5 0 + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 64 values pushed */ + 0 0 23 17 34 8 17 50 48 84 50 2 34 0 1 1 42 32 31 30 19 18 17 16 2 + 1 0 11 0 2 3 0 0 14 0 0 21 5 38 12 10 46 48 196 42 30 19 18 17 16 + 2 7 13 46 38 31 32 31 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 150 values pushed */ + 0 0 53 45 61 42 45 71 48 84 14 17 12 2 27 26 21 11 6 5 6 12 4 3 65 + 59 58 57 49 48 47 46 40 39 37 36 35 34 33 32 31 16 15 2 1 21 13 71 61 17 + 3 0 4 0 0 30 29 18 17 46 3 12 1 4 48 84 20 19 13 12 3 28 4 1 2 + 0 14 0 0 51 44 63 44 44 67 48 196 15 1 2 16 11 3 28 27 26 19 18 2 6 + 13 16 65 59 58 57 49 48 47 46 40 39 37 36 35 34 33 32 29 14 13 6 5 4 3 + 0 24 13 67 63 11 0 0 31 30 12 11 15 3 16 1 4 48 196 21 20 17 16 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 0 0 21 45 29 10 45 41 48 84 41 33 29 27 26 25 17 16 15 14 8 7 5 4 3 + 2 1 0 14 0 0 19 44 31 12 44 37 48 196 37 33 31 27 26 25 17 16 15 14 8 + 7 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 0 0 21 20 4 9 20 16 48 84 4 0 1 12 11 2 13 0 0 1 23 0 16 0 0 + 14 23 12 11 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 133 values pushed */ + 36 35 32 31 26 21 20 17 16 15 14 11 10 7 6 3 2 17 0 12 3 0 0 30 29 + 23 22 9 8 1 0 14 7 4 1 4 48 84 34 33 19 18 13 12 5 0 28 27 25 24 + 5 4 0 5 14 36 27 26 25 16 5 34 17 3 24 23 20 19 4 21 5 3 12 11 8 + 7 4 5 9 3 14 13 2 1 4 0 3 3 33 32 29 28 4 13 30 0 0 35 34 23 + 1 30 22 21 9 1 17 10 9 23 1 0 3 4 48 196 31 30 1 18 17 1 6 5 1 + 15 0 1 4 3 1 5 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 64 values pushed */ + 0 0 12 17 23 48 84 23 0 1 21 20 19 3 0 35 3 0 1 35 0 2 0 0 36 + 35 13 1 0 1 4 48 84 37 0 1 0 14 0 0 10 10 25 48 196 35 19 2 36 20 + 3 25 36 1 0 20 37 36 1 21 20 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 52 values pushed */ + 0 0 8 45 15 48 84 1 28 0 2 13 12 11 10 4 13 15 28 0 0 30 0 43 1 + 28 1 4 48 84 29 28 1 0 14 0 0 6 44 19 48 196 30 29 28 19 13 12 11 10 + 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 0 0 24 29 5 48 84 5 2 42 36 33 27 26 19 16 10 9 1 10 17 0 3 43 0 + 1 0 35 34 18 17 1 3 14 34 33 2 0 18 3 43 42 2 13 35 17 16 9 0 0 + 27 26 1 0 4 3 35 19 18 4 1 9 2 4 48 196 36 35 1 10 9 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 97 values pushed */ + 0 0 49 20 58 24 29 5 48 84 5 2 42 36 33 27 26 19 16 10 9 1 10 17 0 + 3 54 53 45 44 4 13 58 17 43 0 1 0 35 34 18 17 1 3 14 54 53 2 35 0 + 3 34 33 2 0 18 3 45 44 2 18 9 3 43 42 2 13 35 17 16 9 0 0 27 26 + 1 0 4 3 35 19 18 4 1 9 2 4 48 196 36 35 1 10 9 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 12 1 1 1 4 48 84 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 0 0 68 17 52 60 17 44 24 29 5 48 84 5 2 42 36 33 27 26 19 16 10 9 1 + 10 17 0 3 52 44 17 43 0 1 0 35 34 18 17 1 3 14 0 0 72 17 48 64 17 + 56 48 196 48 48 35 0 2 34 33 2 0 18 3 56 56 18 9 2 43 42 2 13 35 17 + 16 9 0 0 27 26 1 0 4 3 35 19 18 4 1 9 2 4 48 196 36 35 1 10 9 + 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 111 values pushed */ + 0 0 65 20 48 53 20 60 24 29 5 48 84 48 0 5 2 60 1 67 60 44 3 0 17 + 3 0 42 36 33 27 26 19 16 10 9 1 10 17 0 3 1 56 55 2 13 0 0 43 0 + 1 0 35 34 18 17 1 3 14 56 55 2 35 0 3 34 33 2 0 18 3 67 44 2 18 + 9 3 43 42 2 13 35 17 16 9 0 0 27 26 1 0 4 3 35 19 18 4 1 9 2 + 4 48 196 36 35 1 10 9 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 40 values pushed */ + 1 25 22 21 15 8 7 4 7 5 2 3 0 1 30 0 2 0 24 23 6 5 1 3 14 + 30 25 24 23 22 21 19 15 11 8 7 6 5 4 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 54 values pushed */ + 1 37 30 27 26 18 17 16 15 10 9 6 11 7 2 3 0 1 38 36 35 0 4 13 2 + 0 29 28 8 7 1 3 14 38 37 36 35 30 29 28 27 26 23 18 17 16 15 12 10 9 + 8 7 6 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 1 44 40 0 2 0 1 45 43 42 39 4 0 7 3 0 1 37 30 27 26 18 17 16 15 + 10 9 6 11 7 2 3 0 1 38 36 35 0 4 13 2 0 41 40 1 0 29 28 8 7 + 1 3 14 45 44 43 42 41 40 39 38 37 36 35 30 29 28 27 26 23 18 17 16 15 12 + 10 9 8 7 6 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 75 74 65 55 54 51 45 39 36 35 29 28 16 15 12 6 1 17 13 0 3 76 53 52 0 + 3 0 38 37 14 13 1 3 14 76 75 74 71 65 58 55 54 53 52 51 45 39 38 37 36 + 35 32 29 28 19 16 15 14 13 12 6 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 1 25 22 21 15 8 7 4 7 5 2 3 0 1 0 2 30 2 0 31 30 1 0 24 23 + 6 5 1 3 14 31 30 25 24 23 22 21 19 15 11 8 7 6 5 4 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 1 37 33 0 2 0 1 38 36 35 32 4 0 5 3 0 1 25 22 21 15 8 7 4 7 + 5 2 3 0 1 0 2 30 2 0 34 33 1 31 30 1 2 0 24 23 6 5 1 3 14 + 38 37 36 35 34 33 32 31 30 25 24 23 22 21 19 15 11 8 7 6 5 4 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 119 values pushed */ + 68 61 51 60 32 29 38 35 32 29 24 19 18 17 15 12 10 13 6 3 0 0 46 45 5 + 4 14 3 6 48 47 3 2 14 3 0 2 4 48 84 44 43 7 6 3 50 49 1 0 3 + 61 60 1 3 0 37 36 14 13 0 3 14 61 68 60 51 24 19 18 17 15 14 6 43 0 + 3 49 48 45 44 38 37 36 35 8 13 27 43 13 12 6 5 2 1 6 13 21 0 0 0 + 51 50 47 46 43 4 4 0 1 4 48 196 68 7 4 3 0 4 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + MDAP[1] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 17 15 2 2 25 24 23 22 14 13 10 9 8 9 2 18 3 1 18 0 2 0 0 3 2 + 14 1 15 19 18 21 1 0 2 4 48 84 26 0 1 0 16 15 1 14 24 23 22 19 18 + 17 16 13 10 9 8 3 2 13 25 14 3 1 0 14 26 25 1 15 14 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 20 11 8 16 11 0 48 84 8 2 0 0 14 0 0 22 10 4 18 10 12 48 196 + 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright (c) 2001 by Bigelow & Holmes Inc. Instructions copyright (c) 2001 by URW++. + + + Luxi Serif + + + Regular + + + Luxi Serif Regular: B&H + + + Luxi Serif Regular + + + 1.2 : October 12, 2001 + + + LuxiSerif + + + Luxi is a registered trademark of Bigelow & Holmes Inc. + + + Bigelow & Holmes Inc. + + + Kris Holmes and Charles Bigelow + + + http://www.urwpp.de + + + design@bigelowandholmes.com + + + Copyright (c) 2001 by Bigelow & Holmes Inc. Instructions copyright (c) 2001 by URW++. + + + Luxi Serif + + + Regular + + + Luxi Serif Regular: B&H + + + Luxi Serif Regular + + + 1.2 : October 12, 2001 + + + LuxiSerif + + + Luxi is a registered trademark of Bigelow & Holmes Inc. + + + Bigelow & Holmes Inc. + + + Kris Holmes and Charles Bigelow + + + http://www.urwpp.de + + + design@bigelowandholmes.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/golang/freetype/testdata/luxisr-12pt-sans-hinting.txt b/vendor/github.com/golang/freetype/testdata/luxisr-12pt-sans-hinting.txt new file mode 100644 index 000000000..e2761641d --- /dev/null +++ b/vendor/github.com/golang/freetype/testdata/luxisr-12pt-sans-hinting.txt @@ -0,0 +1,392 @@ +freetype version 2.5.1 +213 21 0 192 555;21 0 1, 21 555 1, 192 555 1, 192 0 1, 171 21 1, 171 534 1, 43 534 1, 43 21 1 +0 0 0 0 0; +213 0 0 0 0; +213 0 0 0 0; +213 70 0 144 555;70 0 1, 70 74 1, 144 74 1, 144 0 1, 79 148 1, 70 444 1, 70 555 1, 144 555 1, 144 444 1, 135 148 1 +273 35 407 238 592;44 407 1, 35 592 1, 108 592 1, 99 407 1, 173 407 1, 164 592 1, 238 592 1, 229 407 1 +427 9 0 418 555;47 0 1, 89 167 1, 9 167 1, 18 213 1, 100 213 1, 133 342 1, 44 342 1, 54 389 1, 144 389 1, 186 555 1, 234 555 1, 192 389 1, 291 389 1, 332 555 1, 380 555 1, 339 389 1, 418 389 1, 409 342 1, 327 342 1, 294 213 1, 383 213 1, 374 167 1, 283 167 1, 242 0 1, 194 0 1, 235 167 1, 137 167 1, 95 0 1, 148 213 1, 247 213 1, 279 342 1, 180 342 1 +427 39 -46 353 602;187 -46 1, 187 0 1, 121 0 0, 39 31 1, 39 95 1, 123 56 0, 187 56 1, 187 255 1, 117 298 0, 88 330 1, 55 368 0, 55 422 1, 55 486 0, 103 524 1, 135 550 0, 187 555 1, 187 602 1, 224 602 1, 224 555 1, 278 555 0, 344 530 1, 344 470 1, 273 501 0, 224 504 1, 224 307 1, 228 304 1, 238 298 0, 247 293 1, 251 290 1, 299 262 0, 322 237 1, 353 205 0, 353 155 1, 353 87 0, 308 42 1, 276 12 0, 224 0 1, 224 -46 1, 224 60 1, 288 85 0, 288 144 1, 288 175 0, 270 195 1, 257 210 0, 224 233 1, 187 331 1, 187 502 1, 120 479 0, 120 425 1, 120 376 0 +683 42 -14 641 569;94 -14 1, 531 569 1, 589 569 1, 152 -14 1, 161 555 1, 216 555 0, 248 518 1, 280 480 0, 280 416 1, 280 352 0, 248 315 1, 216 278 0, 161 278 1, 106 278 0, 74 315 1, 42 353 0, 42 418 1, 42 475 0, 68 511 1, 101 555 0, 161 518 1, 134 518 0, 117 491 1, 100 462 0, 100 419 1, 100 375 0, 114 348 1, 131 315 0, 161 315 1, 189 315 0, 206 343 1, 222 371 0, 222 416 1, 222 462 0, 206 490 1, 188 518 0, 522 278 1, 577 278 0, 609 240 1, 641 203 0, 641 139 1, 641 75 0, 609 38 1, 577 0 0, 522 0 1, 467 0 0, 435 38 1, 403 75 0, 403 141 1, 403 198 0, 429 233 1, 462 278 0, 522 241 1, 494 241 0, 477 213 1, 461 185 0, 461 141 1, 461 98 0, 474 71 1, 491 37 0, 522 37 1, 549 37 0, 566 65 1, 583 93 0, 583 139 1, 583 185 0, 566 213 1, 549 241 0 +512 21 -14 485 569;384 0 1, 357 33 1, 282 -14 0, 214 -14 1, 132 -14 0, 77 37 1, 21 88 0, 21 166 1, 21 243 0, 69 290 1, 98 318 0, 152 339 1, 119 400 0, 119 445 1, 119 501 0, 153 535 1, 188 569 0, 247 569 1, 303 569 0, 336 539 1, 368 508 0, 368 457 1, 368 401 0, 325 360 1, 298 335 0, 248 312 1, 311 198 0, 373 123 1, 410 171 0, 410 265 1, 410 295 1, 483 295 1, 483 165 0, 408 83 1, 441 41 0, 485 0 1, 325 76 1, 251 160 0, 178 296 1, 141 279 0, 123 257 1, 95 225 0, 95 179 1, 95 122 0, 134 82 1, 172 42 0, 226 42 1, 268 42 0, 220 359 1, 256 374 0, 273 392 1, 299 419 0, 299 456 1, 299 513 0, 246 513 1, 191 513 0, 191 453 1, 191 416 0, 217 365 1 +147 27 389 120 592;45 389 1, 27 592 1, 120 592 1, 101 389 1 +256 49 -111 225 592;225 -60 1, 225 -111 1, 150 -58 0, 107 21 1, 49 123 0, 49 241 1, 49 364 0, 111 470 1, 154 542 0, 225 592 1, 225 541 1, 174 485 0, 152 426 1, 123 353 0, 123 241 1, 123 124 0, 154 48 1, 177 -7 0 +256 31 -111 207 592;31 541 1, 31 592 1, 106 539 0, 150 461 1, 207 359 0, 207 241 1, 207 117 0, 144 12 1, 102 -60 0, 31 -111 1, 31 -60 1, 81 -3 0, 104 56 1, 132 129 0, 132 241 1, 132 357 0, 101 433 1, 79 487 0 +299 15 282 284 555;267 483 1, 284 431 1, 180 406 1, 180 407 1, 180 411 0, 180 411 1, 180 411 1, 180 427 0, 170 437 1, 255 314 1, 210 282 1, 152 382 1, 170 384 0, 178 401 1, 88 282 1, 43 314 1, 120 401 1, 128 384 0, 147 382 1, 15 431 1, 32 483 1, 129 437 1, 118 427 0, 118 411 1, 118 411 1, 118 411 0, 118 409 1, 119 408 1, 119 407 0, 119 406 1, 122 555 1, 177 555 1, 165 440 1, 157 444 0, 149 444 1, 141 444 0, 133 440 1 +449 39 37 409 407;196 37 1, 196 194 1, 39 194 1, 39 250 1, 196 250 1, 196 407 1, 252 407 1, 252 250 1, 409 250 1, 409 194 1, 252 194 1, 252 37 1 +213 60 -120 153 93;60 -120 1, 60 -93 1, 96 -83 0, 96 -8 1, 96 0 1, 60 0 1, 60 93 1, 153 93 1, 153 12 1, 153 -110 0 +256 33 194 223 250;33 194 1, 33 250 1, 223 250 1, 223 194 1 +213 60 0 153 93;60 0 1, 60 93 1, 153 93 1, 153 0 1 +213 -22 -111 236 555;-22 -111 1, 178 555 1, 236 555 1, 36 -111 1 +427 30 -14 397 569;213 569 1, 298 569 0, 347 491 1, 397 413 0, 397 279 1, 397 142 0, 347 64 1, 298 -14 0, 211 -14 1, 137 -14 0, 90 50 1, 30 130 0, 30 278 1, 30 413 0, 80 491 1, 129 569 0, 213 513 1, 163 513 0, 136 452 1, 109 390 0, 109 278 1, 109 167 0, 136 104 1, 163 42 0, 214 42 1, 260 42 0, 285 87 1, 318 148 0, 318 279 1, 318 392 0, 291 452 1, 263 513 0 +427 86 0 383 569;86 0 1, 86 56 1, 197 56 1, 197 494 1, 86 466 1, 86 523 1, 272 569 1, 272 56 1, 383 56 1, 383 0 1 +427 38 0 353 569;38 0 1, 38 65 1, 64 125 0, 125 188 1, 165 230 1, 202 267 1, 274 341 0, 274 415 1, 274 468 0, 246 493 1, 224 514 0, 184 514 1, 132 514 0, 52 469 1, 52 534 1, 128 569 0, 194 569 1, 267 569 0, 310 527 1, 353 485 0, 353 413 1, 353 364 0, 331 326 1, 308 287 0, 247 233 1, 221 209 1, 143 139 0, 128 65 1, 350 65 1, 350 0 1 +427 57 -14 362 569;57 4 1, 57 73 1, 60 72 1, 70 68 0, 74 67 1, 115 52 0, 129 48 1, 154 42 0, 174 42 1, 230 42 0, 258 73 1, 283 100 0, 283 150 1, 283 208 0, 245 239 1, 208 270 0, 138 270 1, 109 270 1, 109 320 1, 134 320 1, 199 321 0, 234 350 1, 270 379 0, 270 430 1, 270 513 0, 180 513 1, 134 513 0, 65 482 1, 65 547 1, 133 569 0, 185 569 1, 275 569 0, 315 526 1, 344 494 0, 344 441 1, 344 381 0, 302 343 1, 277 320 0, 228 301 1, 271 290 0, 293 277 1, 362 237 0, 362 153 1, 362 77 0, 312 32 1, 263 -14 0, 181 -14 1, 137 -14 0 +427 12 0 402 555;258 0 1, 258 157 1, 12 157 1, 12 213 1, 258 555 1, 327 555 1, 327 218 1, 402 218 1, 402 157 1, 327 157 1, 327 0 1, 84 218 1, 263 218 1, 263 464 1 +427 61 -14 362 555;61 -2 1, 61 64 1, 118 42 0, 166 42 1, 221 42 0, 252 74 1, 284 107 0, 284 162 1, 284 288 0, 113 288 1, 92 288 0, 71 285 1, 71 555 1, 351 555 1, 351 491 1, 135 491 1, 135 344 1, 234 343 0, 291 304 1, 362 255 0, 362 159 1, 362 78 0, 308 32 1, 253 -14 0, 158 -14 1, 116 -14 0 +427 32 -14 385 569;112 292 1, 164 356 0, 238 356 1, 306 356 0, 346 310 1, 385 264 0, 385 182 1, 385 92 0, 339 39 1, 292 -14 0, 214 -14 1, 129 -14 0, 80 59 1, 32 132 0, 32 260 1, 32 405 0, 90 487 1, 148 569 0, 251 569 1, 298 569 0, 355 548 1, 355 484 1, 287 514 0, 249 514 1, 166 514 0, 133 431 1, 120 398 0, 115 357 1, 113 335 0, 218 303 1, 172 303 0, 143 271 1, 114 239 0, 114 183 1, 114 122 0, 144 82 1, 174 42 0, 221 42 1, 312 42 0, 312 167 1, 312 303 0 +427 51 0 407 555;83 0 1, 94 65 0, 114 112 1, 134 159 0, 185 242 1, 337 486 1, 51 486 1, 51 555 1, 407 555 1, 407 486 1, 194 171 0, 168 0 1 +427 37 -14 408 569;147 302 1, 111 329 0, 92 353 1, 66 389 0, 66 429 1, 66 491 0, 112 530 1, 158 569 0, 233 569 1, 302 569 0, 344 536 1, 386 503 0, 386 449 1, 386 400 0, 349 357 1, 326 331 0, 283 302 1, 339 273 0, 368 243 1, 408 199 0, 408 143 1, 408 74 0, 356 30 1, 303 -14 0, 219 -14 1, 137 -14 0, 87 28 1, 37 69 0, 37 138 1, 37 198 0, 78 245 1, 102 274 0, 242 326 1, 319 379 0, 319 437 1, 319 472 0, 294 492 1, 268 513 0, 225 513 1, 183 513 0, 158 494 1, 133 474 0, 133 441 1, 133 402 0, 176 368 1, 197 351 0, 188 272 1, 146 240 0, 130 216 1, 111 189 0, 111 149 1, 111 101 0, 142 71 1, 173 42 0, 223 42 1, 271 42 0, 302 67 1, 332 92 0, 332 132 1, 332 168 0, 309 192 1, 289 212 0, 240 241 1 +427 32 -14 385 569;305 263 1, 253 199 0, 179 199 1, 110 199 0, 71 245 1, 32 292 0, 32 373 1, 32 463 0, 78 516 1, 124 569 0, 202 569 1, 288 569 0, 336 496 1, 385 423 0, 385 296 1, 385 150 0, 327 68 1, 269 -14 0, 166 -14 1, 118 -14 0, 61 7 1, 61 71 1, 130 42 0, 168 42 1, 251 42 0, 284 125 1, 297 158 0, 302 198 1, 304 220 0, 196 513 1, 104 513 0, 104 389 1, 104 252 0, 198 252 1, 245 252 0, 273 284 1, 302 317 0, 302 372 1, 302 433 0, 273 473 1, 243 513 0 +213 70 0 144 407;70 0 1, 70 74 1, 144 74 1, 144 0 1, 70 333 1, 70 407 1, 144 407 1, 144 333 1 +213 70 -120 144 407;70 -120 1, 70 -93 1, 96 -79 0, 96 -9 1, 96 0 1, 70 0 1, 70 74 1, 144 74 1, 144 12 1, 143 -102 0, 70 333 1, 70 407 1, 144 407 1, 144 333 1 +449 39 37 409 407;409 37 1, 39 222 1, 409 407 1, 409 345 1, 164 222 1, 164 222 1, 409 99 1 +449 39 125 409 319;39 125 1, 39 180 1, 409 180 1, 409 125 1, 39 264 1, 39 319 1, 409 319 1, 409 264 1 +449 39 37 409 407;39 407 1, 409 222 1, 39 37 1, 39 99 1, 284 222 1, 284 222 1, 39 345 1 +427 52 0 380 569;141 0 1, 141 74 1, 215 74 1, 215 0 1, 141 148 1, 141 168 1, 141 260 0, 202 308 1, 236 334 1, 302 385 0, 302 440 1, 302 513 0, 199 513 1, 136 513 0, 52 486 1, 52 548 1, 135 569 0, 204 569 1, 281 569 0, 324 544 1, 380 512 0, 380 441 1, 380 371 0, 309 327 1, 278 308 1, 241 285 0, 228 260 1, 215 236 0, 215 189 1, 215 148 1 +780 95 -14 692 569;470 17 1, 400 -14 0, 336 -14 1, 232 -14 0, 164 51 1, 95 116 0, 95 218 1, 95 356 0, 201 462 1, 306 569 0, 445 569 1, 551 569 0, 621 501 1, 692 434 0, 692 333 1, 692 241 0, 635 176 1, 578 111 0, 498 111 1, 435 111 0, 435 154 1, 435 169 0, 443 194 1, 458 241 1, 453 241 1, 422 182 0, 393 153 1, 352 111 0, 307 111 1, 236 111 0, 236 196 1, 236 290 0, 297 366 1, 357 442 0, 434 442 1, 443 442 0, 458 441 1, 462 441 0, 467 440 1, 482 440 0, 492 440 1, 543 440 1, 496 201 1, 494 190 0, 494 178 1, 494 153 0, 522 153 1, 572 153 0, 611 206 1, 650 260 0, 650 328 1, 650 414 0, 590 471 1, 531 528 0, 440 528 1, 321 528 0, 229 434 1, 137 341 0, 137 222 1, 137 135 0, 195 82 1, 252 28 0, 342 28 1, 400 28 0, 456 55 1, 462 312 1, 477 386 1, 442 398 0, 417 398 1, 364 398 0, 329 344 1, 294 291 0, 294 213 1, 294 157 0, 324 157 1, 374 157 0 +512 7 0 503 555;7 0 1, 218 555 1, 296 555 1, 503 0 1, 419 0 1, 361 154 1, 138 154 1, 80 0 1, 161 212 1, 339 212 1, 250 450 1 +512 62 0 462 555;62 0 1, 62 555 1, 240 555 1, 349 555 0, 394 527 1, 440 498 0, 440 431 1, 440 369 0, 395 330 1, 368 307 0, 317 289 1, 382 270 0, 415 242 1, 462 202 0, 462 138 1, 462 80 0, 423 41 1, 396 13 0, 353 6 1, 318 0 0, 259 0 1, 141 59 1, 199 59 1, 309 59 0, 344 75 1, 378 92 0, 378 144 1, 378 201 0, 334 231 1, 289 261 0, 205 261 1, 141 261 1, 141 311 1, 208 311 1, 359 311 0, 359 417 1, 359 471 0, 315 485 1, 282 496 0, 213 496 1, 141 496 1 +555 44 -14 507 569;507 29 1, 421 -14 0, 323 -14 1, 186 -14 0, 115 60 1, 44 135 0, 44 277 1, 44 419 0, 116 494 1, 189 569 0, 327 569 1, 404 569 0, 506 545 1, 506 471 1, 390 510 0, 323 510 1, 228 510 0, 178 450 1, 128 390 0, 128 278 1, 128 167 0, 182 108 1, 235 48 0, 332 48 1, 414 48 0, 507 96 1 +555 62 0 520 555;62 0 1, 62 555 1, 240 555 1, 520 555 0, 520 290 1, 520 152 0, 447 76 1, 374 0 0, 241 0 1, 141 59 1, 235 59 1, 435 59 0, 435 281 1, 435 412 0, 356 466 1, 333 482 0, 301 488 1, 263 496 0, 199 496 1, 141 496 1 +512 72 0 491 555;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1 +469 72 0 449 555;72 0 1, 72 555 1, 449 555 1, 449 496 1, 150 496 1, 150 310 1, 411 310 1, 411 252 1, 150 252 1, 150 0 1 +597 35 -14 527 569;527 258 1, 527 15 1, 424 -14 0, 327 -14 1, 35 -14 0, 35 276 1, 35 417 0, 110 493 1, 186 569 0, 329 569 1, 421 569 0, 526 544 1, 526 471 1, 406 510 0, 326 510 1, 119 510 0, 119 279 1, 119 165 0, 176 105 1, 233 45 0, 338 45 1, 381 45 0, 449 59 1, 449 200 1, 356 200 1, 356 258 1 +555 62 0 492 555;62 0 1, 62 555 1, 141 555 1, 141 321 1, 414 321 1, 414 555 1, 492 555 1, 492 0 1, 414 0 1, 414 262 1, 141 262 1, 141 0 1 +213 68 0 146 555;68 0 1, 68 555 1, 146 555 1, 146 0 1 +384 18 -111 315 555;18 -87 1, 18 -19 1, 83 -48 0, 138 -48 1, 203 -48 0, 221 -18 1, 236 7 0, 236 68 1, 236 555 1, 315 555 1, 315 70 1, 315 -111 0, 135 -111 1, 74 -111 0 +512 72 0 494 555;72 0 1, 72 555 1, 146 555 1, 146 282 1, 376 555 1, 455 555 1, 232 290 1, 494 0 1, 394 0 1, 146 281 1, 146 0 1 +427 62 0 413 555;62 0 1, 62 555 1, 141 555 1, 141 59 1, 413 59 1, 413 0 1 +640 62 0 578 555;62 0 1, 62 555 1, 171 555 1, 324 126 1, 480 555 1, 578 555 1, 578 0 1, 504 0 1, 504 451 1, 353 37 1, 277 37 1, 130 453 1, 130 0 1 +555 62 0 492 555;62 0 1, 62 555 1, 139 555 1, 425 126 1, 425 555 1, 492 555 1, 492 0 1, 415 0 1, 129 429 1, 129 0 1 +597 35 -14 563 569;299 569 1, 419 569 0, 491 490 1, 563 410 0, 563 278 1, 563 144 0, 491 65 1, 419 -14 0, 295 -14 1, 189 -14 0, 121 51 1, 35 132 0, 35 278 1, 35 411 0, 107 490 1, 179 569 0, 299 510 1, 214 510 0, 167 449 1, 119 388 0, 119 278 1, 119 169 0, 167 107 1, 213 45 0, 297 45 1, 375 45 0, 421 95 1, 479 156 0, 479 278 1, 479 388 0, 431 449 1, 383 510 0 +512 63 0 494 555;63 0 1, 63 555 1, 280 555 1, 366 555 0, 403 545 1, 441 535 0, 465 507 1, 494 471 0, 494 408 1, 494 221 0, 257 221 1, 141 221 1, 141 0 1, 141 280 1, 254 280 1, 411 280 0, 411 404 1, 411 464 0, 370 481 1, 335 496 0, 255 496 1, 141 496 1 +597 35 -111 615 569;615 -48 1, 565 -111 1, 434 -68 0, 346 -10 1, 307 -14 0, 287 -14 1, 177 -14 0, 106 68 1, 35 149 0, 35 278 1, 35 410 0, 107 489 1, 178 569 0, 298 569 1, 419 569 0, 491 489 1, 563 410 0, 563 277 1, 563 160 0, 509 87 1, 488 58 0, 460 38 1, 446 27 0, 418 11 1, 510 -30 0, 297 510 1, 214 510 0, 167 448 1, 119 387 0, 119 278 1, 119 169 0, 167 107 1, 214 45 0, 297 45 1, 382 45 0, 430 106 1, 479 167 0, 479 275 1, 479 376 0, 440 436 1, 392 510 0 +555 62 0 538 555;62 0 1, 62 555 1, 294 555 1, 465 555 0, 465 417 1, 465 350 0, 423 306 1, 399 281 0, 353 260 1, 538 0 1, 441 0 1, 283 235 1, 141 235 1, 141 0 1, 141 294 1, 229 294 1, 309 294 0, 346 321 1, 384 350 0, 384 408 1, 384 456 0, 353 476 1, 323 496 0, 253 496 1, 141 496 1 +512 45 -14 466 569;45 20 1, 45 98 1, 156 45 0, 264 45 1, 385 45 0, 385 135 1, 385 181 0, 352 203 1, 326 220 0, 269 239 1, 193 264 1, 48 311 0, 48 421 1, 48 569 0, 251 569 1, 338 569 0, 432 545 1, 432 473 1, 334 510 0, 246 510 1, 124 510 0, 124 427 1, 124 394 0, 147 374 1, 171 354 0, 230 334 1, 308 309 1, 395 281 0, 431 244 1, 466 207 0, 466 146 1, 466 72 0, 411 29 1, 357 -14 0, 261 -14 1, 167 -14 0 +469 8 0 461 555;195 0 1, 195 496 1, 8 496 1, 8 555 1, 461 555 1, 461 496 1, 274 496 1, 274 0 1 +555 62 -14 492 555;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1 +512 14 0 508 555;228 0 1, 14 555 1, 95 555 1, 272 99 1, 441 555 1, 508 555 1, 302 0 1 +725 9 0 716 555;152 0 1, 9 555 1, 85 555 1, 199 117 1, 329 555 1, 405 555 1, 530 121 1, 651 555 1, 716 555 1, 560 0 1, 482 0 1, 358 428 1, 230 0 1 +512 11 0 502 555;11 0 1, 215 276 1, 20 555 1, 113 555 1, 263 339 1, 423 555 1, 498 555 1, 299 289 1, 502 0 1, 409 0 1, 251 224 1, 85 0 1 +512 11 0 501 555;210 0 1, 210 231 1, 11 555 1, 101 555 1, 259 298 1, 428 555 1, 501 555 1, 289 233 1, 289 0 1 +469 38 0 431 555;38 0 1, 38 63 1, 336 496 1, 56 496 1, 56 555 1, 431 555 1, 431 496 1, 132 63 1, 431 63 1, 431 0 1 +213 56 -111 204 592;56 -111 1, 56 592 1, 204 592 1, 204 537 1, 121 537 1, 121 -56 1, 204 -56 1, 204 -111 1 +213 -22 -111 236 555;236 -111 1, 178 -111 1, -22 555 1, 36 555 1 +213 10 -111 158 592;158 592 1, 158 -111 1, 10 -111 1, 10 -56 1, 93 -56 1, 93 537 1, 10 537 1, 10 592 1 +360 14 222 347 555;180 431 1, 75 222 1, 14 222 1, 180 555 1, 347 222 1, 284 222 1 +427 0 -56 427 0;0 -56 1, 0 0 1, 427 0 1, 427 -56 1 +256 40 481 216 602;216 481 1, 160 481 1, 40 602 1, 125 602 1 +427 36 -9 412 416;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0 +427 58 -9 395 592;132 264 1, 132 56 1, 183 46 0, 209 46 1, 315 46 0, 315 207 1, 315 275 0, 294 313 1, 273 352 0, 238 352 1, 191 352 0, 132 331 1, 153 369 0, 176 389 1, 209 416 0, 254 416 1, 317 416 0, 356 361 1, 395 306 0, 395 215 1, 395 108 0, 344 49 1, 294 -9 0, 203 -9 1, 168 -9 0, 132 0 1, 58 -5 1, 58 592 1, 132 592 1 +384 32 -9 347 416;347 12 1, 279 -9 0, 219 -9 1, 135 -9 0, 84 50 1, 32 109 0, 32 204 1, 32 303 0, 85 360 1, 137 416 0, 231 416 1, 278 416 0, 343 403 1, 343 341 1, 281 360 0, 245 360 1, 116 360 0, 116 204 1, 116 130 0, 149 90 1, 182 50 0, 242 50 1, 287 50 0, 347 76 1 +427 32 -9 369 592;295 143 1, 295 351 1, 243 361 0, 218 361 1, 112 361 0, 112 200 1, 112 133 0, 133 94 1, 154 56 0, 189 56 1, 236 56 0, 295 76 1, 274 38 0, 251 18 1, 218 -9 0, 173 -9 1, 110 -9 0, 71 46 1, 32 101 0, 32 193 1, 32 299 0, 83 358 1, 133 416 0, 224 416 1, 259 416 0, 295 407 1, 295 592 1, 369 592 1, 369 0 1, 295 0 1 +427 32 -9 383 416;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0 +213 12 0 236 602;66 0 1, 66 352 1, 12 352 1, 12 407 1, 66 407 1, 66 456 1, 66 602 0, 181 602 1, 206 602 0, 236 592 1, 236 533 1, 209 546 0, 189 546 1, 162 546 0, 151 528 1, 140 510 0, 140 464 1, 140 407 1, 213 407 1, 213 352 1, 140 352 1, 140 0 1 +427 35 -158 372 416;298 162 1, 298 351 1, 245 361 0, 222 361 1, 115 361 0, 115 215 1, 115 150 0, 136 112 1, 157 74 0, 192 74 1, 239 74 0, 298 95 1, 277 57 0, 254 37 1, 221 9 0, 176 9 1, 113 9 0, 74 64 1, 35 119 0, 35 207 1, 35 306 0, 85 361 1, 135 416 0, 226 416 1, 261 416 0, 298 407 1, 372 407 1, 372 111 1, 372 15 0, 362 -31 1, 334 -158 0, 174 -158 1, 106 -158 0, 38 -135 1, 38 -71 1, 118 -102 0, 173 -102 1, 298 -102 0, 298 31 1 +427 58 0 374 592;58 0 1, 58 592 1, 132 592 1, 132 331 1, 159 369 0, 186 388 1, 224 416 0, 270 416 1, 374 416 0, 374 293 1, 374 0 1, 300 0 1, 300 269 1, 300 318 0, 290 335 1, 279 353 0, 251 353 1, 190 353 0, 132 264 1, 132 0 1 +171 48 0 122 555;48 0 1, 48 407 1, 122 407 1, 122 0 1, 48 481 1, 48 555 1, 122 555 1, 122 481 1 +171 -58 -157 124 555;-58 -145 1, -58 -87 1, -28 -102 0, -2 -102 1, 35 -102 0, 43 -74 1, 50 -51 0, 50 0 1, 50 407 1, 124 407 1, 124 0 1, 124 -157 0, 4 -157 1, -29 -157 0, 50 481 1, 50 555 1, 124 555 1, 124 481 1 +384 58 0 377 592;58 0 1, 58 592 1, 132 592 1, 132 210 1, 268 407 1, 339 407 1, 209 215 1, 377 0 1, 287 0 1, 132 209 1, 132 0 1 +171 48 0 122 592;48 0 1, 48 592 1, 122 592 1, 122 0 1 +640 58 0 587 416;58 0 1, 58 407 1, 132 407 1, 132 331 1, 164 380 0, 185 397 1, 210 416 0, 249 416 1, 298 416 0, 329 385 1, 346 366 0, 359 331 1, 392 380 0, 413 397 1, 437 416 0, 477 416 1, 587 416 0, 587 296 1, 587 0 1, 513 0 1, 512 285 1, 512 355 0, 458 355 1, 410 355 0, 359 273 1, 359 0 1, 285 0 1, 285 285 1, 285 355 0, 231 355 1, 183 355 0, 132 273 1, 132 0 1 +427 58 0 374 416;58 0 1, 58 407 1, 132 407 1, 132 331 1, 159 369 0, 186 388 1, 224 416 0, 270 416 1, 374 416 0, 374 293 1, 374 0 1, 300 0 1, 300 269 1, 300 318 0, 290 335 1, 279 353 0, 251 353 1, 190 353 0, 132 264 1, 132 0 1 +427 32 -9 395 416;213 416 1, 297 416 0, 346 359 1, 395 303 0, 395 204 1, 395 104 0, 346 47 1, 297 -9 0, 211 -9 1, 137 -9 0, 91 38 1, 32 96 0, 32 204 1, 32 302 0, 81 359 1, 130 416 0, 213 361 1, 112 361 0, 112 204 1, 112 46 0, 213 46 1, 315 46 0, 315 205 1, 315 361 0 +427 58 -148 395 416;132 -148 1, 58 -148 1, 58 407 1, 132 407 1, 132 331 1, 153 369 0, 176 389 1, 209 416 0, 254 416 1, 317 416 0, 356 361 1, 395 306 0, 395 215 1, 395 108 0, 344 49 1, 294 -9 0, 203 -9 1, 168 -9 0, 132 0 1, 132 264 1, 132 56 1, 183 46 0, 209 46 1, 315 46 0, 315 207 1, 315 275 0, 294 313 1, 273 352 0, 238 352 1, 191 352 0 +427 32 -148 369 416;295 407 1, 369 407 1, 369 -148 1, 295 -148 1, 295 76 1, 274 38 0, 251 18 1, 218 -9 0, 173 -9 1, 110 -9 0, 71 46 1, 32 101 0, 32 193 1, 32 299 0, 83 358 1, 133 416 0, 224 416 1, 259 416 0, 295 143 1, 295 351 1, 243 361 0, 218 361 1, 112 361 0, 112 200 1, 112 133 0, 133 94 1, 154 56 0, 189 56 1, 236 56 0 +256 58 0 251 416;58 0 1, 58 407 1, 132 407 1, 132 331 1, 148 369 0, 166 389 1, 193 416 0, 230 416 1, 237 416 0, 251 414 1, 251 345 1, 231 352 0, 219 352 1, 178 352 0, 132 269 1, 132 0 1 +384 44 -9 341 416;44 14 1, 44 82 1, 118 46 0, 181 46 1, 266 46 0, 266 106 1, 266 147 0, 207 167 1, 141 189 1, 46 220 0, 46 303 1, 46 416 0, 201 416 1, 246 416 0, 309 404 1, 309 342 1, 253 361 0, 196 361 1, 119 361 0, 119 310 1, 119 273 0, 172 256 1, 231 237 1, 341 201 0, 341 113 1, 341 57 0, 297 24 1, 254 -9 0, 178 -9 1, 119 -9 0 +213 11 -9 210 488;199 -2 1, 176 -9 0, 156 -9 1, 57 -9 0, 57 113 1, 57 352 1, 11 352 1, 11 407 1, 57 407 1, 57 481 1, 131 488 1, 131 407 1, 210 407 1, 210 352 1, 131 352 1, 131 126 1, 131 78 0, 139 62 1, 147 46 0, 174 46 1, 188 46 0, 199 50 1 +427 53 -9 369 407;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1 +384 7 0 380 407;152 0 1, 7 407 1, 82 407 1, 195 90 1, 314 407 1, 380 407 1, 225 0 1 +555 4 0 549 407;102 0 1, 4 407 1, 77 407 1, 150 101 1, 244 407 1, 318 407 1, 400 99 1, 486 407 1, 549 407 1, 435 0 1, 361 0 1, 275 315 1, 177 0 1 +384 11 0 372 407;11 0 1, 143 215 1, 15 407 1, 101 407 1, 203 255 1, 294 407 1, 362 407 1, 238 202 1, 372 0 1, 287 0 1, 177 164 1, 79 0 1 +384 7 -148 380 407;152 0 1, 7 407 1, 82 407 1, 193 95 1, 314 407 1, 380 407 1, 164 -148 1, 87 -148 1 +384 28 0 356 407;28 0 1, 28 56 1, 261 352 1, 39 352 1, 39 407 1, 352 407 1, 352 352 1, 119 56 1, 356 56 1, 356 0 1 +257 9 -111 213 592;9 269 1, 32 269 1, 90 269 0, 90 330 1, 90 354 0, 84 381 1, 77 414 1, 69 447 0, 69 476 1, 69 537 0, 120 569 1, 156 591 0, 213 592 1, 213 537 1, 193 537 1, 167 537 0, 151 524 1, 134 510 0, 134 490 1, 134 482 0, 139 455 1, 145 416 1, 149 391 0, 149 361 1, 149 290 0, 94 241 1, 149 192 0, 149 120 1, 149 90 0, 145 65 1, 139 27 1, 134 -1 0, 134 -9 1, 134 -29 0, 151 -42 1, 168 -56 0, 193 -56 1, 213 -56 1, 213 -111 1, 153 -110 0, 117 -85 1, 69 -53 0, 69 6 1, 69 35 0, 77 67 1, 84 100 1, 90 127 0, 90 152 1, 90 213 0, 32 213 1, 9 213 1 +200 72 -111 128 592;72 -111 1, 72 592 1, 128 592 1, 128 -111 1 +257 44 -111 247 592;247 213 1, 224 213 1, 167 213 0, 167 152 1, 167 124 0, 173 100 1, 180 67 1, 187 36 0, 187 6 1, 187 -56 0, 135 -88 1, 100 -110 0, 44 -111 1, 44 -56 1, 63 -56 1, 89 -56 0, 105 -42 1, 122 -29 0, 122 -9 1, 122 1 0, 118 27 1, 111 65 1, 107 88 0, 107 120 1, 107 192 0, 162 241 1, 137 263 0, 125 285 1, 107 318 0, 107 361 1, 107 393 0, 111 416 1, 118 455 1, 122 480 0, 122 491 1, 122 510 0, 105 524 1, 88 537 0, 63 537 1, 44 537 1, 44 592 1, 104 591 0, 140 566 1, 187 534 0, 187 475 1, 187 445 0, 180 414 1, 173 381 1, 167 357 0, 167 329 1, 167 269 0, 224 269 1, 247 269 1 +449 39 155 409 290;95 167 1, 39 167 1, 40 206 0, 47 227 1, 69 290 0, 139 290 1, 176 290 0, 213 264 1, 255 235 1, 280 218 1, 291 210 0, 309 210 1, 352 210 0, 354 278 1, 409 278 1, 408 238 0, 401 217 1, 379 155 0, 310 155 1, 273 155 0, 235 180 1, 193 209 1, 168 227 1, 157 234 0, 140 234 1, 96 234 0 +512 7 0 503 666;7 0 1, 218 555 1, 296 555 1, 503 0 1, 419 0 1, 361 154 1, 138 154 1, 80 0 1, 161 212 1, 339 212 1, 250 450 1, 150 602 1, 150 666 1, 215 666 1, 215 602 1, 299 602 1, 299 666 1, 363 666 1, 363 602 1 +512 7 0 503 726;7 0 1, 218 555 1, 296 555 1, 503 0 1, 419 0 1, 361 154 1, 138 154 1, 80 0 1, 161 212 1, 339 212 1, 250 450 1, 257 726 1, 292 726 0, 317 701 1, 342 676 0, 342 641 1, 342 605 0, 317 580 1, 292 555 0, 256 555 1, 225 555 0, 202 576 1, 172 602 0, 172 641 1, 172 676 0, 197 701 1, 222 726 0, 257 693 1, 235 693 0, 219 678 1, 204 663 0, 204 641 1, 204 619 0, 219 603 1, 235 588 0, 256 588 1, 276 588 0, 291 600 1, 310 616 0, 310 641 1, 310 663 0, 294 678 1, 279 693 0 +555 44 -162 507 569;507 29 1, 421 -14 0, 323 -14 1, 186 -14 0, 115 60 1, 44 135 0, 44 277 1, 44 419 0, 116 494 1, 189 569 0, 327 569 1, 404 569 0, 506 545 1, 506 471 1, 390 510 0, 323 510 1, 228 510 0, 178 450 1, 128 390 0, 128 278 1, 128 167 0, 182 108 1, 235 48 0, 332 48 1, 414 48 0, 507 96 1, 288 0 1, 324 0 1, 302 -41 1, 329 -42 0, 348 -56 1, 374 -74 0, 374 -101 1, 374 -126 0, 352 -144 1, 330 -162 0, 298 -162 1, 273 -162 0, 244 -154 1, 244 -124 1, 263 -129 0, 283 -129 1, 322 -129 0, 322 -102 1, 322 -67 0, 252 -66 1 +512 72 0 491 722;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1, 224 602 1, 315 722 1, 400 722 1, 280 602 1 +555 62 0 492 689;62 0 1, 62 555 1, 139 555 1, 425 126 1, 425 555 1, 492 555 1, 492 0 1, 415 0 1, 129 429 1, 129 0 1, 162 602 1, 165 637 0, 174 656 1, 191 689 0, 232 689 1, 259 689 0, 282 675 1, 305 661 1, 326 648 0, 337 648 1, 362 648 0, 366 689 1, 412 689 1, 409 654 0, 400 635 1, 383 602 0, 342 602 1, 315 602 0, 292 616 1, 269 630 1, 249 643 0, 237 643 1, 212 643 0, 208 602 1 +597 35 -14 563 666;299 569 1, 418 569 0, 491 490 1, 563 410 0, 563 278 1, 563 144 0, 491 65 1, 418 -14 0, 295 -14 1, 189 -14 0, 120 51 1, 35 132 0, 35 278 1, 35 411 0, 107 490 1, 179 569 0, 299 510 1, 213 510 0, 166 449 1, 119 388 0, 119 278 1, 119 169 0, 166 107 1, 213 45 0, 297 45 1, 374 45 0, 420 95 1, 478 156 0, 478 278 1, 478 388 0, 431 449 1, 383 510 0, 192 602 1, 192 666 1, 257 666 1, 257 602 1, 340 602 1, 340 666 1, 405 666 1, 405 602 1 +555 62 -14 492 666;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 176 602 1, 176 666 1, 240 666 1, 240 602 1, 324 602 1, 324 666 1, 389 666 1, 389 602 1 +427 36 -9 412 602;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0, 168 481 1, 259 602 1, 344 602 1, 224 481 1 +427 36 -9 412 602;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0, 280 481 1, 224 481 1, 104 602 1, 189 602 1 +427 36 -9 412 602;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0, 61 481 1, 151 602 1, 233 602 1, 323 481 1, 268 481 1, 192 557 1, 192 557 1, 116 481 1 +427 36 -9 412 546;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0, 85 481 1, 85 546 1, 150 546 1, 150 481 1, 234 481 1, 234 546 1, 298 546 1, 298 481 1 +427 36 -9 412 569;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0, 67 481 1, 70 516 0, 79 535 1, 96 569 0, 137 569 1, 164 569 0, 187 555 1, 210 541 1, 231 528 0, 242 528 1, 267 528 0, 271 569 1, 317 569 1, 314 534 0, 305 515 1, 288 481 0, 247 481 1, 220 481 0, 197 496 1, 174 510 1, 154 522 0, 142 522 1, 117 522 0, 113 481 1 +427 36 -9 412 651;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0, 192 651 1, 227 651 0, 252 627 1, 277 602 0, 277 567 1, 277 531 0, 252 506 1, 227 481 0, 191 481 1, 160 481 0, 137 501 1, 107 527 0, 107 566 1, 107 602 0, 132 626 1, 156 651 0, 192 619 1, 170 619 0, 154 603 1, 139 588 0, 139 566 1, 139 545 0, 154 529 1, 170 513 0, 191 513 1, 211 513 0, 226 526 1, 245 542 0, 245 567 1, 245 588 0, 229 603 1, 214 619 0 +384 32 -162 347 416;347 12 1, 279 -9 0, 219 -9 1, 135 -9 0, 84 50 1, 32 109 0, 32 204 1, 32 303 0, 85 360 1, 137 416 0, 231 416 1, 278 416 0, 343 403 1, 343 341 1, 281 360 0, 245 360 1, 116 360 0, 116 204 1, 116 130 0, 149 90 1, 182 50 0, 242 50 1, 287 50 0, 347 76 1, 235 0 1, 271 0 1, 248 -41 1, 275 -42 0, 295 -56 1, 321 -74 0, 321 -101 1, 321 -126 0, 299 -144 1, 277 -162 0, 245 -162 1, 220 -162 0, 191 -154 1, 191 -124 1, 210 -129 0, 230 -129 1, 269 -129 0, 269 -102 1, 269 -67 0, 199 -66 1 +427 32 -9 383 602;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0, 168 481 1, 259 602 1, 344 602 1, 224 481 1 +427 32 -9 383 602;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0, 280 481 1, 224 481 1, 104 602 1, 189 602 1 +427 32 -9 383 602;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0, 61 481 1, 151 602 1, 233 602 1, 323 481 1, 268 481 1, 192 557 1, 192 557 1, 116 481 1 +427 32 -9 383 546;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0, 85 481 1, 85 546 1, 150 546 1, 150 481 1, 234 481 1, 234 546 1, 298 546 1, 298 481 1 +171 40 0 216 602;48 0 1, 48 407 1, 122 407 1, 122 0 1, 40 481 1, 131 602 1, 216 602 1, 96 481 1 +171 -24 0 152 602;48 0 1, 48 407 1, 122 407 1, 122 0 1, 152 481 1, 96 481 1, -24 602 1, 61 602 1 +171 -67 0 195 602;48 0 1, 48 407 1, 122 407 1, 122 0 1, -67 481 1, 23 602 1, 105 602 1, 195 481 1, 140 481 1, 64 557 1, 64 557 1, -12 481 1 +171 -21 0 192 546;48 0 1, 48 407 1, 122 407 1, 122 0 1, -21 481 1, -21 546 1, 44 546 1, 44 481 1, 127 481 1, 127 546 1, 192 546 1, 192 481 1 +427 58 0 374 569;58 0 1, 58 407 1, 132 407 1, 132 331 1, 159 369 0, 186 388 1, 224 416 0, 270 416 1, 374 416 0, 374 293 1, 374 0 1, 300 0 1, 300 269 1, 300 318 0, 290 335 1, 279 353 0, 251 353 1, 190 353 0, 132 264 1, 132 0 1, 67 481 1, 70 516 0, 79 535 1, 96 569 0, 137 569 1, 164 569 0, 187 555 1, 210 541 1, 231 528 0, 242 528 1, 267 528 0, 271 569 1, 317 569 1, 314 534 0, 305 515 1, 288 481 0, 247 481 1, 220 481 0, 197 496 1, 174 510 1, 154 522 0, 142 522 1, 117 522 0, 113 481 1 +427 32 -9 395 602;213 416 1, 297 416 0, 346 359 1, 395 303 0, 395 204 1, 395 104 0, 346 47 1, 297 -9 0, 211 -9 1, 137 -9 0, 91 38 1, 32 96 0, 32 204 1, 32 302 0, 81 359 1, 130 416 0, 213 361 1, 112 361 0, 112 204 1, 112 46 0, 213 46 1, 315 46 0, 315 205 1, 315 361 0, 168 481 1, 259 602 1, 344 602 1, 224 481 1 +427 32 -9 395 602;213 416 1, 297 416 0, 346 359 1, 395 303 0, 395 204 1, 395 104 0, 346 47 1, 297 -9 0, 211 -9 1, 137 -9 0, 91 38 1, 32 96 0, 32 204 1, 32 302 0, 81 359 1, 130 416 0, 213 361 1, 112 361 0, 112 204 1, 112 46 0, 213 46 1, 315 46 0, 315 205 1, 315 361 0, 280 481 1, 224 481 1, 104 602 1, 189 602 1 +427 32 -9 395 602;213 416 1, 297 416 0, 346 359 1, 395 303 0, 395 204 1, 395 104 0, 346 47 1, 297 -9 0, 211 -9 1, 137 -9 0, 91 38 1, 32 96 0, 32 204 1, 32 302 0, 81 359 1, 130 416 0, 213 361 1, 112 361 0, 112 204 1, 112 46 0, 213 46 1, 315 46 0, 315 205 1, 315 361 0, 61 481 1, 151 602 1, 233 602 1, 323 481 1, 268 481 1, 192 557 1, 192 557 1, 116 481 1 +427 32 -9 395 546;213 416 1, 297 416 0, 346 359 1, 395 303 0, 395 204 1, 395 104 0, 346 47 1, 297 -9 0, 211 -9 1, 137 -9 0, 91 38 1, 32 96 0, 32 204 1, 32 302 0, 81 359 1, 130 416 0, 213 361 1, 112 361 0, 112 204 1, 112 46 0, 213 46 1, 315 46 0, 315 205 1, 315 361 0, 85 481 1, 85 546 1, 150 546 1, 150 481 1, 234 481 1, 234 546 1, 298 546 1, 298 481 1 +427 32 -9 395 569;213 416 1, 297 416 0, 346 359 1, 395 303 0, 395 204 1, 395 104 0, 346 47 1, 297 -9 0, 211 -9 1, 137 -9 0, 91 38 1, 32 96 0, 32 204 1, 32 302 0, 81 359 1, 130 416 0, 213 361 1, 112 361 0, 112 204 1, 112 46 0, 213 46 1, 315 46 0, 315 205 1, 315 361 0, 67 481 1, 70 516 0, 79 535 1, 96 569 0, 137 569 1, 164 569 0, 187 555 1, 210 541 1, 231 528 0, 242 528 1, 267 528 0, 271 569 1, 317 569 1, 314 534 0, 305 515 1, 288 481 0, 247 481 1, 220 481 0, 197 496 1, 174 510 1, 154 522 0, 142 522 1, 117 522 0, 113 481 1 +427 53 -9 369 602;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 168 481 1, 259 602 1, 344 602 1, 224 481 1 +427 53 -9 369 602;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 280 481 1, 224 481 1, 104 602 1, 189 602 1 +427 53 -9 369 602;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 61 481 1, 151 602 1, 233 602 1, 323 481 1, 268 481 1, 192 557 1, 192 557 1, 116 481 1 +427 53 -9 369 546;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 85 481 1, 85 546 1, 150 546 1, 150 481 1, 234 481 1, 234 546 1, 298 546 1, 298 481 1 +427 56 -111 371 555;177 -111 1, 186 315 1, 56 305 1, 56 361 1, 186 352 1, 177 555 1, 251 555 1, 241 352 1, 371 361 1, 371 305 1, 241 315 1, 251 -111 1 +307 43 347 265 569;154 569 1, 199 569 0, 232 536 1, 265 504 0, 265 458 1, 265 412 0, 232 380 1, 199 347 0, 152 347 1, 113 347 0, 82 373 1, 43 408 0, 43 458 1, 43 504 0, 75 536 1, 108 569 0, 154 523 1, 127 523 0, 108 504 1, 89 485 0, 89 458 1, 89 432 0, 108 413 1, 127 393 0, 153 393 1, 177 393 0, 195 409 1, 219 428 0, 219 458 1, 219 485 0, 200 504 1, 180 523 0 +427 65 0 380 555;237 0 1, 237 65 1, 166 74 0, 123 120 1, 65 181 0, 65 278 1, 65 379 0, 124 435 1, 165 475 0, 237 486 1, 237 555 1, 274 555 1, 274 486 1, 324 483 0, 380 468 1, 380 406 1, 314 429 0, 274 432 1, 274 117 1, 325 117 0, 380 143 1, 380 87 1, 324 65 0, 274 65 1, 274 0 1, 237 429 1, 215 426 0, 203 420 1, 146 390 0, 146 277 1, 146 199 0, 180 158 1, 200 135 0, 237 122 1 +427 45 0 362 569;45 0 1, 45 65 1, 124 90 0, 124 183 1, 124 269 1, 57 269 1, 57 324 1, 124 324 1, 124 405 1, 124 485 0, 162 527 1, 200 569 0, 272 569 1, 310 569 0, 357 558 1, 357 495 1, 308 513 0, 269 513 1, 198 513 0, 198 427 1, 198 324 1, 279 324 1, 279 269 1, 198 269 1, 198 221 1, 198 153 0, 180 120 1, 166 92 0, 133 65 1, 362 65 1, 362 0 1 +427 48 -125 379 569;48 -98 1, 48 -30 1, 142 -69 0, 203 -69 1, 250 -69 0, 281 -51 1, 311 -32 0, 311 -1 1, 311 27 0, 288 43 1, 270 56 0, 225 75 1, 159 104 1, 50 150 0, 50 230 1, 50 284 0, 105 342 1, 52 379 0, 52 434 1, 52 494 0, 101 531 1, 149 569 0, 229 569 1, 284 569 0, 358 551 1, 358 491 1, 280 513 0, 228 513 1, 179 513 0, 149 494 1, 119 475 0, 119 445 1, 119 404 0, 185 377 1, 236 357 1, 314 325 0, 343 296 1, 373 266 0, 373 221 1, 373 168 0, 317 104 1, 379 66 0, 379 3 1, 379 -56 0, 329 -90 1, 279 -125 0, 196 -125 1, 138 -125 0, 281 128 1, 308 166 0, 308 200 1, 308 228 0, 290 245 1, 272 263 0, 226 282 1, 143 317 1, 115 282 0, 115 249 1, 115 198 0, 201 162 1 +269 30 208 239 416;135 416 1, 178 416 0, 208 386 1, 239 355 0, 239 312 1, 239 269 0, 208 239 1, 177 208 0, 133 208 1, 96 208 0, 67 233 1, 30 265 0, 30 312 1, 30 356 0, 61 386 1, 92 416 0 +413 33 -111 338 555;190 -111 1, 190 278 1, 122 284 0, 83 316 1, 33 358 0, 33 433 1, 33 499 0, 69 527 1, 105 555 0, 190 555 1, 338 555 1, 338 -111 1, 292 -111 1, 292 509 1, 237 509 1, 237 -111 1 +469 48 -9 437 602;48 0 1, 48 432 1, 48 527 0, 83 564 1, 117 602 0, 206 602 1, 349 602 0, 349 500 1, 349 451 0, 300 399 1, 261 357 0, 261 337 1, 261 312 0, 303 281 1, 372 230 1, 437 182 0, 437 111 1, 437 -9 0, 296 -9 1, 234 -9 0, 183 11 1, 183 76 1, 253 46 0, 296 46 1, 368 46 0, 368 104 1, 368 141 0, 324 174 1, 244 236 1, 195 273 0, 195 315 1, 195 351 0, 238 407 1, 275 455 0, 275 487 1, 275 546 0, 201 546 1, 157 546 0, 140 528 1, 122 509 0, 122 463 1, 122 0 1 +566 6 0 561 555;283 555 1, 398 555 0, 479 474 1, 561 393 0, 561 278 1, 561 162 0, 479 81 1, 398 0 0, 280 0 1, 179 0 0, 104 66 1, 6 152 0, 6 278 1, 6 393 0, 87 474 1, 169 555 0, 283 516 1, 185 516 0, 115 446 1, 45 376 0, 45 278 1, 45 181 0, 115 110 1, 184 40 0, 281 40 1, 370 40 0, 437 96 1, 521 168 0, 521 278 1, 521 376 0, 451 446 1, 381 516 0, 192 126 1, 192 426 1, 286 426 1, 377 426 0, 377 353 1, 377 301 0, 324 266 1, 415 126 1, 359 126 1, 278 252 1, 240 252 1, 240 126 1, 237 289 1, 251 289 1, 330 289 0, 330 347 1, 330 396 0, 264 396 1, 237 396 1 +566 6 0 561 555;283 555 1, 398 555 0, 479 474 1, 561 393 0, 561 278 1, 561 162 0, 479 81 1, 398 0 0, 280 0 1, 179 0 0, 104 66 1, 6 152 0, 6 278 1, 6 393 0, 87 474 1, 169 555 0, 283 516 1, 185 516 0, 115 446 1, 45 376 0, 45 278 1, 45 181 0, 115 110 1, 184 40 0, 281 40 1, 370 40 0, 437 96 1, 521 168 0, 521 278 1, 521 376 0, 451 446 1, 381 516 0, 384 137 1, 333 119 0, 293 119 1, 226 119 0, 183 163 1, 140 207 0, 140 276 1, 140 348 0, 182 391 1, 224 434 0, 295 434 1, 332 434 0, 375 425 1, 384 423 1, 384 379 1, 336 399 0, 298 399 1, 251 399 0, 222 365 1, 193 332 0, 193 277 1, 193 222 0, 223 191 1, 253 159 0, 303 159 1, 343 159 0, 384 181 1 +768 83 278 662 555;176 278 1, 176 509 1, 83 509 1, 83 555 1, 333 555 1, 333 509 1, 240 509 1, 240 278 1, 380 278 1, 380 555 1, 467 555 1, 525 395 1, 582 555 1, 662 555 1, 662 278 1, 597 278 1, 597 479 1, 536 301 1, 491 301 1, 430 463 1, 430 278 1 +256 40 481 216 602;40 481 1, 131 602 1, 216 602 1, 96 481 1 +256 21 481 234 546;21 481 1, 21 546 1, 86 546 1, 86 481 1, 170 481 1, 170 546 1, 234 546 1, 234 481 1 +213 0 0 0 0; +768 7 0 746 555;224 213 1, 381 213 1, 381 460 1, 7 0 1, 360 555 1, 730 555 1, 730 496 1, 459 496 1, 459 318 1, 693 318 1, 693 260 1, 459 260 1, 459 59 1, 746 59 1, 746 0 1, 381 0 1, 381 155 1, 187 155 1, 89 0 1 +597 35 -14 563 569;39 -14 1, 104 69 1, 74 107 0, 58 146 1, 35 204 0, 35 278 1, 35 410 0, 107 490 1, 179 569 0, 298 569 1, 389 569 0, 459 519 1, 498 569 1, 563 569 1, 496 484 1, 525 446 0, 541 407 1, 563 350 0, 563 277 1, 563 144 0, 491 65 1, 419 -14 0, 299 -14 1, 211 -14 0, 142 34 1, 104 -14 1, 184 88 1, 233 45 0, 299 45 1, 384 45 0, 431 106 1, 479 167 0, 479 276 1, 479 363 0, 448 423 1, 416 465 1, 366 510 0, 299 510 1, 214 510 0, 167 449 1, 119 388 0, 119 279 1, 119 190 0, 152 129 1 +213 0 0 0 0; +449 39 0 409 444;196 111 1, 196 250 1, 39 250 1, 39 305 1, 196 305 1, 196 444 1, 252 444 1, 252 305 1, 409 305 1, 409 250 1, 252 250 1, 252 111 1, 39 0 1, 39 56 1, 409 56 1, 409 0 1 +213 0 0 0 0; +213 0 0 0 0; +427 9 0 404 555;170 0 1, 170 129 1, 59 129 1, 59 176 1, 170 176 1, 170 231 1, 59 231 1, 59 278 1, 170 278 1, 9 555 1, 95 555 1, 216 346 1, 216 346 1, 338 555 1, 404 555 1, 244 278 1, 355 278 1, 355 231 1, 244 231 1, 244 176 1, 355 176 1, 355 129 1, 244 129 1, 244 0 1 +427 53 -148 369 407;53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 295 0 1, 295 76 1, 236 -7 0, 176 -7 1, 152 -7 0, 127 9 1, 127 -148 1, 53 -148 1 +213 0 0 0 0; +213 0 0 0 0; +213 0 0 0 0; +213 0 0 0 0; +213 0 0 0 0; +284 32 308 267 569;182 343 1, 144 308 0, 105 308 1, 74 308 0, 53 328 1, 32 348 0, 32 378 1, 32 464 0, 161 464 1, 179 464 1, 179 490 1, 179 531 0, 133 531 1, 97 531 0, 54 509 1, 54 551 1, 102 569 0, 144 569 1, 234 569 0, 234 492 1, 234 380 1, 234 345 0, 255 346 1, 257 346 1, 258 346 0, 261 346 1, 263 346 0, 265 347 1, 267 315 1, 248 308 0, 231 308 1, 194 308 0, 184 343 1, 179 374 1, 179 431 1, 164 431 1, 88 431 0, 88 385 1, 88 350 0, 124 350 1, 150 350 0 +281 28 308 252 569;140 569 1, 192 569 0, 222 534 1, 252 499 0, 252 439 1, 252 378 0, 222 343 1, 192 308 0, 139 308 1, 93 308 0, 64 337 1, 28 373 0, 28 439 1, 28 499 0, 58 534 1, 89 569 0, 140 531 1, 87 531 0, 87 439 1, 87 347 0, 140 347 1, 194 347 0, 194 440 1, 194 531 0 +213 0 0 0 0; +683 36 -9 638 416;288 92 1, 288 197 1, 260 198 1, 235 199 0, 207 195 1, 110 181 0, 110 114 1, 110 51 0, 184 51 1, 236 51 0, 344 369 1, 400 416 0, 472 416 1, 638 416 0, 638 215 1, 638 192 1, 360 192 1, 365 148 0, 375 125 1, 408 47 0, 512 47 1, 568 47 0, 636 72 1, 636 13 1, 559 -9 0, 494 -9 1, 423 -9 0, 374 23 1, 347 41 0, 322 77 1, 279 31 0, 248 12 1, 211 -9 0, 160 -9 1, 104 -9 0, 70 21 1, 36 53 0, 36 102 1, 36 241 0, 270 241 1, 288 241 1, 288 290 1, 288 329 0, 269 345 1, 251 361 0, 205 361 1, 141 361 0, 71 325 1, 71 386 1, 148 416 0, 218 416 1, 303 416 0, 364 248 1, 564 248 1, 563 284 0, 556 302 1, 536 361 0, 469 361 1, 422 361 0, 396 333 1, 371 308 0 +469 54 -9 416 416;131 22 1, 108 -9 1, 54 -9 1, 99 53 1, 54 116 0, 54 204 1, 54 303 0, 102 360 1, 151 416 0, 236 416 1, 296 416 0, 339 385 1, 362 416 1, 416 416 1, 371 354 1, 416 291 0, 416 203 1, 416 105 0, 367 48 1, 319 -9 0, 234 -9 1, 174 -9 0, 169 75 1, 170 75 1, 185 59 0, 198 53 1, 215 46 0, 234 46 1, 336 46 0, 336 204 1, 336 251 0, 325 291 1, 301 332 1, 300 333 1, 272 361 0, 235 361 1, 134 361 0, 134 205 1, 134 152 0, 145 116 1 +469 69 -162 398 407;309 407 1, 309 333 1, 235 333 1, 235 407 1, 309 259 1, 309 239 1, 309 147 0, 248 99 1, 214 73 1, 148 22 0, 148 -33 1, 148 -107 0, 251 -107 1, 314 -107 0, 398 -78 1, 398 -141 1, 316 -162 0, 246 -162 1, 169 -162 0, 125 -137 1, 69 -105 0, 69 -34 1, 69 36 0, 141 81 1, 171 99 1, 209 122 0, 222 147 1, 235 171 0, 235 218 1, 235 259 1 +256 91 -148 165 407;165 407 1, 165 333 1, 91 333 1, 91 407 1, 156 259 1, 165 -37 1, 165 -148 1, 91 -148 1, 91 -37 1, 100 259 1 +449 32 111 402 333;32 278 1, 32 333 1, 402 333 1, 402 111 1, 347 111 1, 347 278 1 +213 0 0 0 0; +427 18 -111 384 569;18 -111 1, 94 269 1, 32 269 1, 32 324 1, 105 324 1, 113 363 1, 154 569 0, 300 569 1, 339 569 0, 384 558 1, 373 500 1, 332 514 0, 299 514 1, 218 514 0, 195 398 1, 180 324 1, 251 324 1, 251 269 1, 169 269 1, 94 -111 1 +213 0 0 0 0; +213 0 0 0 0; +427 43 37 376 370;376 342 1, 265 204 1, 376 65 1, 339 37 1, 191 204 1, 339 370 1, 228 342 1, 117 204 1, 228 65 1, 191 37 1, 43 204 1, 191 370 1 +427 51 37 384 370;51 65 1, 162 204 1, 51 342 1, 88 370 1, 236 204 1, 88 37 1, 199 65 1, 310 204 1, 199 342 1, 236 370 1, 384 204 1, 236 37 1 +768 93 0 676 74;93 0 1, 93 74 1, 167 74 1, 167 0 1, 347 0 1, 347 74 1, 421 74 1, 421 0 1, 602 0 1, 602 74 1, 676 74 1, 676 0 1 +427 0 0 0 0; +512 7 0 503 722;7 0 1, 218 555 1, 296 555 1, 503 0 1, 419 0 1, 361 154 1, 138 154 1, 80 0 1, 161 212 1, 339 212 1, 250 450 1, 312 602 1, 257 602 1, 137 722 1, 222 722 1 +512 7 0 503 689;7 0 1, 218 555 1, 296 555 1, 503 0 1, 419 0 1, 361 154 1, 138 154 1, 80 0 1, 161 212 1, 339 212 1, 250 450 1, 132 602 1, 135 636 0, 144 656 1, 161 689 0, 202 689 1, 229 689 0, 252 675 1, 275 661 1, 296 648 0, 307 648 1, 332 648 0, 336 689 1, 382 689 1, 379 654 0, 370 635 1, 353 602 0, 312 602 1, 285 602 0, 262 616 1, 239 630 1, 219 643 0, 207 643 1, 182 643 0, 178 602 1 +597 35 -14 563 689;299 569 1, 418 569 0, 491 490 1, 563 410 0, 563 278 1, 563 144 0, 491 65 1, 418 -14 0, 295 -14 1, 189 -14 0, 120 51 1, 35 132 0, 35 278 1, 35 411 0, 107 490 1, 179 569 0, 299 510 1, 213 510 0, 166 449 1, 119 388 0, 119 278 1, 119 169 0, 166 107 1, 213 45 0, 297 45 1, 374 45 0, 420 95 1, 478 156 0, 478 278 1, 478 388 0, 431 449 1, 383 510 0, 174 602 1, 176 636 0, 186 656 1, 203 689 0, 244 689 1, 271 689 0, 294 675 1, 316 661 1, 338 648 0, 349 648 1, 373 648 0, 377 689 1, 423 689 1, 421 654 0, 411 635 1, 395 602 0, 354 602 1, 327 602 0, 303 616 1, 281 630 1, 260 643 0, 248 643 1, 224 643 0, 220 602 1 +768 35 -14 747 569;423 0 1, 423 22 1, 362 -14 0, 291 -14 1, 176 -14 0, 105 66 1, 35 147 0, 35 278 1, 35 411 0, 106 490 1, 177 569 0, 294 569 1, 363 569 0, 423 534 1, 423 555 1, 730 555 1, 730 496 1, 502 496 1, 502 318 1, 693 318 1, 693 260 1, 502 260 1, 502 59 1, 747 59 1, 747 0 1, 423 213 1, 423 342 1, 423 430 0, 393 470 1, 363 510 0, 296 510 1, 212 510 0, 165 449 1, 119 388 0, 119 278 1, 119 167 0, 165 106 1, 213 45 0, 296 45 1, 423 45 0 +725 32 -9 680 416;371 341 1, 395 375 0, 422 392 1, 461 416 0, 516 416 1, 612 416 0, 650 348 1, 678 297 0, 680 192 1, 412 192 1, 419 120 0, 447 87 1, 481 46 0, 560 46 1, 618 46 0, 680 73 1, 680 14 1, 608 -9 0, 542 -9 1, 474 -9 0, 434 12 1, 404 29 0, 373 65 1, 350 32 0, 323 15 1, 283 -9 0, 227 -9 1, 138 -9 0, 85 48 1, 32 106 0, 32 204 1, 32 302 0, 86 359 1, 138 416 0, 228 416 1, 287 416 0, 327 388 1, 350 372 0, 228 361 1, 111 361 0, 111 205 1, 111 137 0, 134 98 1, 163 46 0, 229 46 1, 337 46 0, 337 204 1, 337 276 0, 314 315 1, 288 361 0, 414 243 1, 601 243 1, 600 291 0, 588 317 1, 567 361 0, 514 361 1, 462 361 0, 437 321 1, 420 295 0 +427 38 204 390 250;38 204 1, 38 250 1, 390 250 1, 390 204 1 +768 37 204 731 241;37 204 1, 37 241 1, 731 241 1, 731 204 1 +256 21 398 225 592;225 592 1, 225 564 1, 198 551 0, 198 480 1, 198 472 1, 225 472 1, 225 398 1, 151 398 1, 151 460 1, 151 573 0, 95 592 1, 95 564 1, 69 551 0, 69 480 1, 69 472 1, 95 472 1, 95 398 1, 21 398 1, 21 460 1, 22 573 0 +256 31 398 234 592;31 398 1, 31 426 1, 57 440 0, 57 510 1, 57 518 1, 31 518 1, 31 592 1, 105 592 1, 105 530 1, 104 417 0, 160 398 1, 160 426 1, 187 440 0, 187 510 1, 187 518 1, 160 518 1, 160 592 1, 234 592 1, 234 530 1, 234 417 0 +171 35 380 127 592;127 592 1, 127 564 1, 91 554 0, 91 480 1, 91 472 1, 127 472 1, 127 380 1, 35 380 1, 35 460 1, 35 582 0 +171 44 380 136 592;44 380 1, 44 407 1, 80 417 0, 80 492 1, 80 500 1, 44 500 1, 44 592 1, 136 592 1, 136 512 1, 136 389 0 +449 39 0 409 444;39 194 1, 39 250 1, 409 250 1, 409 194 1, 178 352 1, 178 444 1, 270 444 1, 270 352 1, 178 0 1, 178 93 1, 270 93 1, 270 0 1 +213 0 0 0 0; +384 7 -148 380 546;152 0 1, 7 407 1, 82 407 1, 193 95 1, 314 407 1, 380 407 1, 164 -148 1, 87 -148 1, 85 481 1, 85 546 1, 150 546 1, 150 481 1, 234 481 1, 234 546 1, 298 546 1, 298 481 1 +512 11 0 501 666;210 0 1, 210 231 1, 11 555 1, 101 555 1, 259 298 1, 428 555 1, 501 555 1, 289 233 1, 289 0 1, 158 602 1, 158 666 1, 223 666 1, 223 602 1, 306 602 1, 306 666 1, 371 666 1, 371 602 1 +128 -165 -14 293 569;-165 -14 1, 243 569 1, 293 569 1, -114 -14 1 +427 46 110 381 446;137 168 1, 78 110 1, 46 143 1, 104 201 1, 80 240 0, 80 278 1, 80 315 0, 104 354 1, 46 413 1, 78 446 1, 137 387 1, 174 411 0, 213 411 1, 253 411 0, 290 387 1, 348 446 1, 381 413 1, 323 354 1, 347 315 0, 347 278 1, 347 240 0, 323 201 1, 381 143 1, 348 110 1, 290 168 1, 253 144 0, 213 144 1, 174 144 0, 213 365 1, 177 365 0, 152 339 1, 126 314 0, 126 277 1, 126 241 0, 152 216 1, 176 191 0, 212 191 1, 246 191 0, 270 211 1, 300 237 0, 300 278 1, 300 314 0, 275 339 1, 250 365 0 +256 28 37 213 370;213 342 1, 102 204 1, 213 65 1, 176 37 1, 28 204 1, 176 370 1 +256 43 37 228 370;43 65 1, 154 204 1, 43 342 1, 80 370 1, 228 204 1, 80 37 1 +384 12 0 336 602;66 0 1, 66 352 1, 12 352 1, 12 407 1, 66 407 1, 66 456 1, 66 602 0, 181 602 1, 206 602 0, 236 592 1, 236 533 1, 209 546 0, 189 546 1, 162 546 0, 151 528 1, 140 510 0, 140 464 1, 140 407 1, 336 407 1, 336 0 1, 262 0 1, 262 352 1, 140 352 1, 140 0 1, 262 481 1, 262 555 1, 336 555 1, 336 481 1 +384 12 0 336 602;66 0 1, 66 352 1, 12 352 1, 12 407 1, 66 407 1, 66 456 1, 66 602 0, 179 602 1, 262 592 1, 336 592 1, 336 0 1, 262 0 1, 262 537 1, 246 540 1, 214 546 0, 192 546 1, 159 546 0, 148 524 1, 140 505 0, 140 464 1, 140 407 1, 206 407 1, 206 352 1, 140 352 1, 140 0 1 +427 56 -111 371 555;177 -111 1, 186 93 1, 56 83 1, 56 139 1, 186 129 1, 186 315 1, 56 305 1, 56 361 1, 186 352 1, 177 555 1, 251 555 1, 241 352 1, 371 361 1, 371 305 1, 241 315 1, 241 129 1, 371 139 1, 371 83 1, 241 93 1, 251 -111 1 +213 60 184 153 277;60 184 1, 60 277 1, 153 277 1, 153 184 1 +171 39 -111 132 93;39 -111 1, 39 -83 1, 75 -73 0, 75 -8 1, 75 0 1, 39 0 1, 39 93 1, 132 93 1, 132 12 1, 131 -101 0 +256 26 -120 230 74;26 -120 1, 26 -93 1, 53 -79 0, 53 -9 1, 53 0 1, 26 0 1, 26 74 1, 100 74 1, 100 12 1, 100 -102 0, 156 -120 1, 156 -93 1, 182 -78 0, 182 -9 1, 182 0 1, 156 0 1, 156 74 1, 230 74 1, 230 12 1, 230 -102 0 +768 9 -14 759 569;128 555 1, 181 555 0, 213 518 1, 245 480 0, 245 417 1, 245 352 0, 213 315 1, 182 278 0, 126 278 1, 78 278 0, 48 308 1, 9 347 0, 9 416 1, 9 480 0, 41 518 1, 74 555 0, 127 518 1, 65 518 0, 65 417 1, 65 315 0, 128 315 1, 190 315 0, 190 416 1, 190 463 0, 173 491 1, 156 518 0, 377 278 1, 431 278 0, 463 240 1, 495 203 0, 495 139 1, 495 74 0, 463 37 1, 431 0 0, 376 0 1, 328 0 0, 297 30 1, 259 69 0, 259 139 1, 259 203 0, 291 240 1, 323 278 0, 377 241 1, 315 241 0, 315 139 1, 315 37 0, 377 37 1, 440 37 0, 440 138 1, 440 186 0, 423 213 1, 405 241 0, 641 278 1, 695 278 0, 727 240 1, 759 203 0, 759 140 1, 759 74 0, 727 37 1, 695 0 0, 640 0 1, 592 0 0, 561 31 1, 523 69 0, 523 139 1, 523 203 0, 555 240 1, 587 278 0, 640 241 1, 578 241 0, 578 139 1, 578 37 0, 641 37 1, 704 37 0, 704 139 1, 704 186 0, 686 213 1, 669 241 0, 23 -14 1, 431 569 1, 482 569 1, 74 -14 1 +512 7 0 503 722;7 0 1, 218 555 1, 296 555 1, 503 0 1, 419 0 1, 361 154 1, 138 154 1, 80 0 1, 161 212 1, 339 212 1, 250 450 1, 126 602 1, 216 722 1, 298 722 1, 388 602 1, 333 602 1, 257 677 1, 257 677 1, 181 602 1 +512 72 0 491 722;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1, 150 602 1, 240 722 1, 322 722 1, 412 602 1, 357 602 1, 281 677 1, 281 677 1, 205 602 1 +512 7 0 503 722;7 0 1, 218 555 1, 296 555 1, 503 0 1, 419 0 1, 361 154 1, 138 154 1, 80 0 1, 161 212 1, 339 212 1, 250 450 1, 201 602 1, 292 722 1, 377 722 1, 257 602 1 +512 72 0 491 666;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1, 169 602 1, 169 666 1, 234 666 1, 234 602 1, 317 602 1, 317 666 1, 382 666 1, 382 602 1 +512 72 0 491 722;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1, 335 602 1, 280 602 1, 159 722 1, 245 722 1 +213 51 0 227 722;68 0 1, 68 555 1, 146 555 1, 146 0 1, 51 602 1, 142 722 1, 227 722 1, 107 602 1 +213 -24 0 238 722;68 0 1, 68 555 1, 146 555 1, 146 0 1, -24 602 1, 66 722 1, 148 722 1, 238 602 1, 183 602 1, 107 677 1, 107 677 1, 31 602 1 +213 0 0 213 666;68 0 1, 68 555 1, 146 555 1, 146 0 1, 0 602 1, 0 666 1, 65 666 1, 65 602 1, 149 602 1, 149 666 1, 213 666 1, 213 602 1 +213 -14 0 162 722;68 0 1, 68 555 1, 146 555 1, 146 0 1, 162 602 1, 107 602 1, -14 722 1, 72 722 1 +597 35 -14 563 722;299 569 1, 418 569 0, 491 490 1, 563 410 0, 563 278 1, 563 144 0, 491 65 1, 418 -14 0, 295 -14 1, 189 -14 0, 120 51 1, 35 132 0, 35 278 1, 35 411 0, 107 490 1, 179 569 0, 299 510 1, 213 510 0, 166 449 1, 119 388 0, 119 278 1, 119 169 0, 166 107 1, 213 45 0, 297 45 1, 374 45 0, 420 95 1, 478 156 0, 478 278 1, 478 388 0, 431 449 1, 383 510 0, 243 602 1, 333 722 1, 419 722 1, 299 602 1 +597 35 -14 563 722;299 569 1, 418 569 0, 491 490 1, 563 410 0, 563 278 1, 563 144 0, 491 65 1, 418 -14 0, 295 -14 1, 189 -14 0, 120 51 1, 35 132 0, 35 278 1, 35 411 0, 107 490 1, 179 569 0, 299 510 1, 213 510 0, 166 449 1, 119 388 0, 119 278 1, 119 169 0, 166 107 1, 213 45 0, 297 45 1, 374 45 0, 420 95 1, 478 156 0, 478 278 1, 478 388 0, 431 449 1, 383 510 0, 167 602 1, 258 722 1, 339 722 1, 430 602 1, 374 602 1, 299 677 1, 298 677 1, 223 602 1 +427 0 -14 405 568;141 201 1, 156 141 0, 178 108 1, 218 48 0, 290 48 1, 337 48 0, 405 75 1, 405 10 1, 332 -14 0, 281 -14 1, 194 -14 0, 140 38 1, 101 74 0, 82 134 1, 75 156 0, 66 201 1, 0 201 1, 19 248 1, 61 248 1, 60 276 1, 60 277 0, 60 284 1, 61 300 0, 62 321 1, 0 321 1, 19 368 1, 69 368 1, 84 430 0, 102 462 1, 161 568 0, 293 568 1, 341 568 0, 405 552 1, 405 483 1, 342 510 0, 294 510 1, 228 510 0, 188 462 1, 166 434 0, 154 400 1, 149 386 0, 144 368 1, 356 368 1, 337 321 1, 136 321 1, 134 294 0, 134 276 1, 135 248 1, 307 248 1, 288 201 1 +597 35 -14 563 722;299 569 1, 418 569 0, 491 490 1, 563 410 0, 563 278 1, 563 144 0, 491 65 1, 418 -14 0, 295 -14 1, 189 -14 0, 120 51 1, 35 132 0, 35 278 1, 35 411 0, 107 490 1, 179 569 0, 299 510 1, 213 510 0, 166 449 1, 119 388 0, 119 278 1, 119 169 0, 166 107 1, 213 45 0, 297 45 1, 374 45 0, 420 95 1, 478 156 0, 478 278 1, 478 388 0, 431 449 1, 383 510 0, 354 602 1, 299 602 1, 178 722 1, 264 722 1 +555 62 -14 492 722;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 227 602 1, 317 722 1, 402 722 1, 282 602 1 +555 62 -14 492 722;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 151 602 1, 241 722 1, 323 722 1, 413 602 1, 358 602 1, 282 677 1, 282 677 1, 206 602 1 +555 62 -14 492 722;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 338 602 1, 282 602 1, 162 722 1, 247 722 1 +171 48 0 122 407;48 0 1, 48 407 1, 122 407 1, 122 0 1 +256 -3 481 259 602;-3 481 1, 87 602 1, 169 602 1, 259 481 1, 204 481 1, 128 557 1, 128 557 1, 52 481 1 +256 3 481 253 569;3 481 1, 6 516 0, 15 535 1, 32 569 0, 73 569 1, 100 569 0, 123 555 1, 146 541 1, 167 528 0, 178 528 1, 203 528 0, 207 569 1, 253 569 1, 250 534 0, 241 515 1, 224 481 0, 183 481 1, 156 481 0, 133 496 1, 110 510 1, 90 522 0, 78 522 1, 53 522 0, 49 481 1 +256 8 481 248 537;8 481 1, 8 537 1, 248 537 1, 248 481 1 +256 3 481 253 602;3 602 1, 49 602 1, 57 566 0, 77 551 1, 97 537 0, 128 537 1, 163 537 0, 183 555 1, 200 570 0, 207 602 1, 253 602 1, 247 551 0, 219 521 1, 184 481 0, 128 481 1, 69 481 0, 33 524 1, 9 554 0 +256 91 481 165 555;91 481 1, 91 555 1, 165 555 1, 165 481 1 +256 43 481 213 651;128 651 1, 163 651 0, 188 627 1, 213 602 0, 213 567 1, 213 531 0, 188 506 1, 163 481 0, 127 481 1, 96 481 0, 73 501 1, 43 527 0, 43 566 1, 43 602 0, 68 626 1, 92 651 0, 128 619 1, 106 619 0, 90 603 1, 75 588 0, 75 566 1, 75 545 0, 90 529 1, 106 513 0, 127 513 1, 147 513 0, 162 526 1, 181 542 0, 181 567 1, 181 588 0, 165 603 1, 150 619 0 +256 63 -162 193 0;107 0 1, 143 0 1, 120 -41 1, 147 -42 0, 167 -56 1, 193 -74 0, 193 -101 1, 193 -126 0, 171 -144 1, 149 -162 0, 117 -162 1, 92 -162 0, 63 -154 1, 63 -124 1, 82 -129 0, 102 -129 1, 141 -129 0, 141 -102 1, 141 -67 0, 71 -66 1 +256 -19 481 275 602;-19 481 1, 71 602 1, 143 602 1, 23 481 1, 113 481 1, 203 602 1, 275 602 1, 155 481 1 +256 64 -139 192 0;123 0 1, 163 0 1, 115 -30 0, 115 -67 1, 115 -103 0, 158 -103 1, 178 -103 0, 192 -98 1, 192 -128 1, 169 -139 0, 140 -139 1, 64 -139 0, 64 -80 1, 64 -34 0 +256 -3 481 259 602;259 602 1, 169 481 1, 87 481 1, -3 602 1, 52 602 1, 128 526 1, 128 526 1, 204 602 1 +427 0 -14 405 568;141 201 1, 156 141 0, 178 108 1, 218 48 0, 290 48 1, 337 48 0, 405 75 1, 405 10 1, 332 -14 0, 281 -14 1, 194 -14 0, 140 38 1, 101 74 0, 82 134 1, 75 156 0, 66 201 1, 0 201 1, 19 248 1, 61 248 1, 60 276 1, 60 277 0, 60 284 1, 61 300 0, 62 321 1, 0 321 1, 19 368 1, 69 368 1, 84 430 0, 102 462 1, 161 568 0, 293 568 1, 341 568 0, 405 552 1, 405 483 1, 342 510 0, 294 510 1, 228 510 0, 188 462 1, 166 434 0, 154 400 1, 149 386 0, 144 368 1, 356 368 1, 337 321 1, 136 321 1, 134 294 0, 134 276 1, 135 248 1, 307 248 1, 288 201 1 +213 0 0 0 0; +200 72 -111 128 592;72 -111 1, 72 167 1, 128 167 1, 128 -111 1, 72 315 1, 72 592 1, 128 592 1, 128 315 1 +256 33 194 223 250;33 194 1, 33 250 1, 223 250 1, 223 194 1 +427 37 546 390 602;37 546 1, 37 602 1, 390 602 1, 390 546 1 +256 28 222 240 564;28 222 1, 28 268 1, 53 312 0, 96 350 1, 122 373 1, 181 425 0, 181 471 1, 181 525 0, 120 525 1, 85 525 0, 36 500 1, 36 543 1, 85 564 0, 130 564 1, 179 564 0, 210 538 1, 240 513 0, 240 473 1, 240 420 0, 171 363 1, 151 346 1, 101 305 0, 92 268 1, 238 268 1, 238 222 1 +256 28 214 234 564;32 507 1, 32 549 1, 75 564 0, 117 564 1, 223 564 0, 223 486 1, 223 450 0, 197 426 1, 182 413 0, 151 402 1, 199 389 0, 218 364 1, 234 343 0, 234 312 1, 234 267 0, 201 240 1, 169 214 0, 112 214 1, 74 214 0, 28 225 1, 28 270 1, 78 251 0, 108 251 1, 175 251 0, 175 312 1, 175 381 0, 78 381 1, 59 381 1, 59 416 1, 75 416 1, 167 416 0, 167 478 1, 167 526 0, 108 526 1, 74 526 0 +213 60 184 153 277;60 184 1, 60 277 1, 153 277 1, 153 184 1 +256 46 222 176 564;120 222 1, 120 507 1, 46 488 1, 46 531 1, 176 564 1, 176 222 1 +641 44 -14 594 569;117 222 1, 117 507 1, 44 488 1, 44 531 1, 173 564 1, 173 222 1, 497 0 1, 497 91 1, 344 91 1, 344 133 1, 495 333 1, 548 333 1, 548 135 1, 594 135 1, 594 91 1, 548 91 1, 548 0 1, 393 135 1, 497 135 1, 497 271 1, 72 -14 1, 480 569 1, 530 569 1, 122 -14 1 +641 44 -14 594 569;382 0 1, 382 46 1, 406 88 0, 450 128 1, 477 151 1, 535 203 0, 535 249 1, 535 303 0, 475 303 1, 440 303 0, 390 278 1, 390 321 1, 440 341 0, 485 341 1, 534 341 0, 564 316 1, 594 291 0, 594 250 1, 594 196 0, 525 140 1, 505 124 1, 455 83 0, 447 46 1, 593 46 1, 593 0 1, 53 -14 1, 461 569 1, 512 569 1, 104 -14 1, 117 222 1, 117 507 1, 44 488 1, 44 531 1, 173 564 1, 173 222 1 +641 42 -14 599 569;46 507 1, 46 549 1, 89 564 0, 131 564 1, 237 564 0, 237 486 1, 237 450 0, 211 426 1, 195 413 0, 165 402 1, 213 389 0, 232 364 1, 248 343 0, 248 312 1, 248 267 0, 215 240 1, 183 214 0, 126 214 1, 87 214 0, 42 225 1, 42 270 1, 92 251 0, 122 251 1, 189 251 0, 189 312 1, 189 381 0, 92 381 1, 72 381 1, 72 416 1, 89 416 1, 180 416 0, 180 478 1, 180 526 0, 122 526 1, 88 526 0, 502 0 1, 502 91 1, 349 91 1, 349 133 1, 500 333 1, 552 333 1, 552 135 1, 599 135 1, 599 91 1, 552 91 1, 552 0 1, 398 135 1, 502 135 1, 502 271 1, 107 -14 1, 515 569 1, 565 569 1, 157 -14 1 +555 2 0 520 555;62 0 1, 62 255 1, 2 255 1, 2 314 1, 62 314 1, 62 555 1, 240 555 1, 520 555 0, 520 290 1, 520 152 0, 447 76 1, 374 0 0, 241 0 1, 141 59 1, 235 59 1, 435 59 0, 435 281 1, 435 412 0, 356 466 1, 333 482 0, 301 488 1, 263 496 0, 199 496 1, 141 496 1, 141 314 1, 266 314 1, 266 255 1, 141 255 1 +449 44 42 404 402;44 81 1, 185 222 1, 44 363 1, 84 402 1, 224 261 1, 365 402 1, 404 363 1, 264 222 1, 404 81 1, 365 42 1, 224 183 1, 84 42 1 +512 11 0 501 722;210 0 1, 210 231 1, 11 555 1, 101 555 1, 259 298 1, 428 555 1, 501 555 1, 289 233 1, 289 0 1, 204 602 1, 294 722 1, 380 722 1, 259 602 1 +512 63 0 494 555;63 0 1, 63 555 1, 141 555 1, 141 450 1, 280 450 1, 366 450 0, 403 440 1, 441 431 0, 465 402 1, 494 366 0, 494 304 1, 494 116 0, 257 116 1, 141 116 1, 141 0 1, 141 175 1, 254 175 1, 411 175 0, 411 299 1, 411 359 0, 370 376 1, 335 391 0, 255 391 1, 141 391 1 +427 32 -9 395 629;47 538 1, 47 594 1, 122 594 0, 183 565 1, 241 629 1, 270 596 1, 222 542 1, 269 506 0, 294 478 1, 395 366 0, 395 206 1, 395 105 0, 347 48 1, 299 -9 0, 216 -9 1, 132 -9 0, 82 48 1, 32 105 0, 32 201 1, 32 297 0, 81 352 1, 129 407 0, 214 407 1, 234 407 0, 259 402 1, 230 456 0, 177 494 1, 122 432 1, 92 465 1, 140 518 1, 102 538 0, 212 352 1, 165 352 0, 138 312 1, 111 272 0, 111 199 1, 111 46 0, 214 46 1, 316 46 0, 316 199 1, 316 352 0 +384 7 -148 380 602;152 0 1, 7 407 1, 82 407 1, 193 95 1, 314 407 1, 380 407 1, 164 -148 1, 87 -148 1, 168 481 1, 259 602 1, 344 602 1, 224 481 1 +427 58 -148 395 592;132 -148 1, 58 -148 1, 58 592 1, 132 592 1, 132 331 1, 153 369 0, 176 389 1, 209 416 0, 254 416 1, 317 416 0, 356 361 1, 395 306 0, 395 215 1, 395 108 0, 344 49 1, 294 -9 0, 203 -9 1, 168 -9 0, 132 0 1, 132 264 1, 132 56 1, 183 46 0, 209 46 1, 315 46 0, 315 207 1, 315 275 0, 294 313 1, 273 352 0, 238 352 1, 191 352 0 +514 8 0 504 657;8 0 1, 219 555 1, 297 555 1, 504 0 1, 419 0 1, 362 154 1, 139 154 1, 81 0 1, 161 212 1, 340 212 1, 251 450 1, 137 602 1, 137 657 1, 378 657 1, 378 602 1 +432 36 -9 412 537;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0, 72 481 1, 72 537 1, 312 537 1, 312 481 1 +514 8 0 504 722;8 0 1, 219 555 1, 297 555 1, 504 0 1, 419 0 1, 362 154 1, 139 154 1, 81 0 1, 161 212 1, 340 212 1, 251 450 1, 133 722 1, 179 722 1, 187 687 0, 207 672 1, 227 657 0, 258 657 1, 293 657 0, 313 675 1, 329 690 0, 336 722 1, 383 722 1, 377 672 0, 349 641 1, 314 602 0, 258 602 1, 199 602 0, 163 645 1, 139 674 0 +432 36 -9 412 602;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0, 67 602 1, 113 602 1, 121 566 0, 141 551 1, 161 537 0, 192 537 1, 227 537 0, 247 555 1, 264 570 0, 271 602 1, 317 602 1, 311 551 0, 283 521 1, 248 481 0, 192 481 1, 133 481 0, 97 524 1, 73 554 0 +512 7 -139 503 555;7 0 1, 218 555 1, 296 555 1, 503 0 1, 419 0 1, 361 154 1, 138 154 1, 80 0 1, 161 212 1, 339 212 1, 250 450 1, 419 0 1, 459 0 1, 411 -30 0, 411 -67 1, 411 -103 0, 454 -103 1, 474 -103 0, 488 -98 1, 488 -128 1, 464 -139 0, 435 -139 1, 359 -139 0, 359 -80 1, 359 -34 0 +427 36 -139 412 416;290 52 1, 221 -9 0, 155 -9 1, 102 -9 0, 69 22 1, 36 53 0, 36 102 1, 36 241 0, 262 241 1, 279 241 1, 279 290 1, 279 361 0, 201 361 1, 140 361 0, 71 325 1, 71 386 1, 148 416 0, 215 416 1, 287 416 0, 320 386 1, 353 356 0, 353 290 1, 353 105 1, 353 42 0, 392 42 1, 398 42 0, 407 44 1, 412 3 1, 384 -9 0, 357 -9 1, 332 -9 0, 315 5 1, 299 19 0, 279 92 1, 279 197 1, 255 198 1, 233 199 0, 205 195 1, 112 182 0, 112 114 1, 112 51 0, 180 51 1, 227 51 0, 315 0 1, 355 0 1, 307 -30 0, 307 -67 1, 307 -103 0, 350 -103 1, 370 -103 0, 384 -98 1, 384 -128 1, 361 -139 0, 332 -139 1, 256 -139 0, 256 -80 1, 256 -34 0 +555 44 -14 507 722;507 29 1, 421 -14 0, 323 -14 1, 186 -14 0, 115 60 1, 44 135 0, 44 277 1, 44 419 0, 116 494 1, 189 569 0, 327 569 1, 404 569 0, 506 545 1, 506 471 1, 390 510 0, 323 510 1, 228 510 0, 178 450 1, 128 390 0, 128 278 1, 128 167 0, 182 108 1, 235 48 0, 332 48 1, 414 48 0, 507 96 1, 264 602 1, 355 722 1, 440 722 1, 320 602 1 +384 32 -9 347 602;347 12 1, 279 -9 0, 219 -9 1, 135 -9 0, 84 50 1, 32 109 0, 32 204 1, 32 303 0, 85 360 1, 137 416 0, 231 416 1, 278 416 0, 343 403 1, 343 341 1, 281 360 0, 245 360 1, 116 360 0, 116 204 1, 116 130 0, 149 90 1, 182 50 0, 242 50 1, 287 50 0, 347 76 1, 168 481 1, 259 602 1, 344 602 1, 224 481 1 +555 44 -14 507 722;507 29 1, 421 -14 0, 323 -14 1, 186 -14 0, 115 60 1, 44 135 0, 44 277 1, 44 419 0, 116 494 1, 189 569 0, 327 569 1, 404 569 0, 506 545 1, 506 471 1, 390 510 0, 323 510 1, 228 510 0, 178 450 1, 128 390 0, 128 278 1, 128 167 0, 182 108 1, 235 48 0, 332 48 1, 414 48 0, 507 96 1, 189 602 1, 279 722 1, 361 722 1, 451 602 1, 396 602 1, 320 677 1, 320 677 1, 244 602 1 +384 32 -9 359 602;347 12 1, 279 -9 0, 219 -9 1, 135 -9 0, 84 50 1, 32 109 0, 32 204 1, 32 303 0, 85 360 1, 137 416 0, 231 416 1, 278 416 0, 343 403 1, 343 341 1, 281 360 0, 245 360 1, 116 360 0, 116 204 1, 116 130 0, 149 90 1, 182 50 0, 242 50 1, 287 50 0, 347 76 1, 96 481 1, 187 602 1, 269 602 1, 359 481 1, 303 481 1, 228 557 1, 227 557 1, 152 481 1 +555 44 -14 507 675;507 29 1, 421 -14 0, 323 -14 1, 186 -14 0, 115 60 1, 44 135 0, 44 277 1, 44 419 0, 116 494 1, 189 569 0, 327 569 1, 404 569 0, 506 545 1, 506 471 1, 390 510 0, 323 510 1, 228 510 0, 178 450 1, 128 390 0, 128 278 1, 128 167 0, 182 108 1, 235 48 0, 332 48 1, 414 48 0, 507 96 1, 283 602 1, 283 675 1, 357 675 1, 357 602 1 +384 32 -9 347 555;347 12 1, 279 -9 0, 219 -9 1, 135 -9 0, 84 50 1, 32 109 0, 32 204 1, 32 303 0, 85 360 1, 137 416 0, 231 416 1, 278 416 0, 343 403 1, 343 341 1, 281 360 0, 245 360 1, 116 360 0, 116 204 1, 116 130 0, 149 90 1, 182 50 0, 242 50 1, 287 50 0, 347 76 1, 191 481 1, 191 555 1, 264 555 1, 264 481 1 +555 44 -14 507 722;507 29 1, 421 -14 0, 323 -14 1, 186 -14 0, 115 60 1, 44 135 0, 44 277 1, 44 419 0, 116 494 1, 189 569 0, 327 569 1, 404 569 0, 506 545 1, 506 471 1, 390 510 0, 323 510 1, 228 510 0, 178 450 1, 128 390 0, 128 278 1, 128 167 0, 182 108 1, 235 48 0, 332 48 1, 414 48 0, 507 96 1, 451 722 1, 361 602 1, 279 602 1, 189 722 1, 244 722 1, 320 646 1, 320 646 1, 396 722 1 +384 32 -9 387 602;347 12 1, 279 -9 0, 219 -9 1, 135 -9 0, 84 50 1, 32 109 0, 32 204 1, 32 303 0, 85 360 1, 137 416 0, 231 416 1, 278 416 0, 343 403 1, 343 341 1, 281 360 0, 245 360 1, 116 360 0, 116 204 1, 116 130 0, 149 90 1, 182 50 0, 242 50 1, 287 50 0, 347 76 1, 387 602 1, 297 481 1, 215 481 1, 125 602 1, 180 602 1, 256 526 1, 256 526 1, 332 602 1 +555 62 0 520 722;62 0 1, 62 555 1, 240 555 1, 520 555 0, 520 290 1, 520 152 0, 447 76 1, 374 0 0, 241 0 1, 141 59 1, 235 59 1, 435 59 0, 435 281 1, 435 412 0, 356 466 1, 333 482 0, 301 488 1, 263 496 0, 199 496 1, 141 496 1, 387 722 1, 297 602 1, 215 602 1, 125 722 1, 180 722 1, 255 646 1, 256 646 1, 332 722 1 +472 32 -9 472 592;295 143 1, 295 351 1, 243 361 0, 218 361 1, 112 361 0, 112 200 1, 112 133 0, 133 94 1, 154 56 0, 189 56 1, 236 56 0, 295 76 1, 274 38 0, 251 18 1, 218 -9 0, 173 -9 1, 110 -9 0, 71 46 1, 32 101 0, 32 193 1, 32 299 0, 83 358 1, 133 416 0, 224 416 1, 259 416 0, 295 407 1, 295 592 1, 369 592 1, 369 0 1, 295 0 1, 398 422 1, 398 444 1, 427 452 0, 427 512 1, 427 518 1, 398 518 1, 398 592 1, 472 592 1, 472 528 1, 472 430 0 +555 2 0 520 555;62 0 1, 62 255 1, 2 255 1, 2 314 1, 62 314 1, 62 555 1, 240 555 1, 520 555 0, 520 290 1, 520 152 0, 447 76 1, 374 0 0, 241 0 1, 141 59 1, 235 59 1, 435 59 0, 435 281 1, 435 412 0, 356 466 1, 333 482 0, 301 488 1, 263 496 0, 199 496 1, 141 496 1, 141 314 1, 266 314 1, 266 255 1, 141 255 1 +427 32 -9 425 592;295 472 1, 175 472 1, 175 518 1, 295 518 1, 295 592 1, 369 592 1, 369 518 1, 425 518 1, 425 472 1, 369 472 1, 369 0 1, 295 0 1, 295 76 1, 274 38 0, 251 18 1, 218 -9 0, 173 -9 1, 110 -9 0, 71 46 1, 32 101 0, 32 193 1, 32 299 0, 83 358 1, 133 416 0, 224 416 1, 259 416 0, 295 407 1, 295 143 1, 295 351 1, 243 361 0, 218 361 1, 112 361 0, 112 200 1, 112 133 0, 133 94 1, 154 56 0, 189 56 1, 236 56 0 +512 72 0 491 657;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1, 150 602 1, 150 657 1, 391 657 1, 391 602 1 +427 32 -9 383 537;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0, 72 481 1, 72 537 1, 312 537 1, 312 481 1 +512 72 0 491 722;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1, 152 722 1, 198 722 1, 206 687 0, 226 672 1, 245 657 0, 276 657 1, 312 657 0, 332 675 1, 348 690 0, 355 722 1, 401 722 1, 395 672 0, 368 641 1, 333 602 0, 276 602 1, 218 602 0, 182 645 1, 158 674 0 +427 32 -9 383 602;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0, 89 602 1, 135 602 1, 143 567 0, 163 551 1, 182 537 0, 213 537 1, 249 537 0, 269 555 1, 285 570 0, 292 602 1, 338 602 1, 332 551 0, 305 521 1, 269 481 0, 213 481 1, 155 481 0, 119 524 1, 95 553 0 +512 72 0 491 675;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1, 238 602 1, 238 675 1, 312 675 1, 312 602 1 +427 32 -9 383 555;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0, 155 481 1, 155 555 1, 229 555 1, 229 481 1 +512 72 -139 491 555;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1, 403 0 1, 443 0 1, 395 -30 0, 395 -67 1, 395 -103 0, 438 -103 1, 458 -103 0, 472 -98 1, 472 -128 1, 449 -139 0, 420 -139 1, 344 -139 0, 344 -80 1, 344 -34 0 +427 32 -139 383 416;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0, 251 0 1, 291 0 1, 243 -30 0, 243 -67 1, 243 -103 0, 286 -103 1, 306 -103 0, 320 -98 1, 320 -128 1, 297 -139 0, 268 -139 1, 192 -139 0, 192 -80 1, 192 -34 0 +512 72 0 491 722;72 0 1, 72 555 1, 474 555 1, 474 496 1, 150 496 1, 150 318 1, 437 318 1, 437 260 1, 150 260 1, 150 59 1, 491 59 1, 491 0 1, 406 722 1, 315 602 1, 234 602 1, 143 722 1, 199 722 1, 274 646 1, 275 646 1, 350 722 1 +427 32 -9 383 602;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0, 323 602 1, 233 481 1, 151 481 1, 61 602 1, 116 602 1, 192 526 1, 192 526 1, 268 602 1 +597 35 -14 527 722;527 258 1, 527 15 1, 424 -14 0, 327 -14 1, 35 -14 0, 35 276 1, 35 417 0, 110 493 1, 186 569 0, 329 569 1, 421 569 0, 526 544 1, 526 471 1, 406 510 0, 326 510 1, 119 510 0, 119 279 1, 119 165 0, 176 105 1, 233 45 0, 338 45 1, 381 45 0, 449 59 1, 449 200 1, 356 200 1, 356 258 1, 194 602 1, 284 722 1, 366 722 1, 456 602 1, 401 602 1, 326 677 1, 325 677 1, 249 602 1 +427 35 -158 372 602;298 162 1, 298 351 1, 245 361 0, 222 361 1, 115 361 0, 115 215 1, 115 150 0, 136 112 1, 157 74 0, 192 74 1, 239 74 0, 298 95 1, 277 57 0, 254 37 1, 221 9 0, 176 9 1, 113 9 0, 74 64 1, 35 119 0, 35 207 1, 35 306 0, 85 361 1, 135 416 0, 226 416 1, 261 416 0, 298 407 1, 372 407 1, 372 111 1, 372 15 0, 362 -31 1, 334 -158 0, 174 -158 1, 106 -158 0, 38 -135 1, 38 -71 1, 118 -102 0, 173 -102 1, 298 -102 0, 298 31 1, 93 481 1, 184 602 1, 266 602 1, 356 481 1, 300 481 1, 225 557 1, 224 557 1, 149 481 1 +597 35 -14 527 722;527 258 1, 527 15 1, 424 -14 0, 327 -14 1, 35 -14 0, 35 276 1, 35 417 0, 110 493 1, 186 569 0, 329 569 1, 421 569 0, 526 544 1, 526 471 1, 406 510 0, 326 510 1, 119 510 0, 119 279 1, 119 165 0, 176 105 1, 233 45 0, 338 45 1, 381 45 0, 449 59 1, 449 200 1, 356 200 1, 356 258 1, 200 722 1, 246 722 1, 254 687 0, 275 672 1, 294 657 0, 325 657 1, 360 657 0, 381 675 1, 397 690 0, 404 722 1, 450 722 1, 444 672 0, 417 641 1, 381 602 0, 325 602 1, 266 602 0, 231 645 1, 206 674 0 +427 35 -158 381 602;298 162 1, 298 351 1, 245 361 0, 222 361 1, 115 361 0, 115 215 1, 115 150 0, 136 112 1, 157 74 0, 192 74 1, 239 74 0, 298 95 1, 277 57 0, 254 37 1, 221 9 0, 176 9 1, 113 9 0, 74 64 1, 35 119 0, 35 207 1, 35 306 0, 85 361 1, 135 416 0, 226 416 1, 261 416 0, 298 407 1, 372 407 1, 372 111 1, 372 15 0, 362 -31 1, 334 -158 0, 174 -158 1, 106 -158 0, 38 -135 1, 38 -71 1, 118 -102 0, 173 -102 1, 298 -102 0, 298 31 1, 131 602 1, 177 602 1, 185 566 0, 205 551 1, 225 537 0, 256 537 1, 291 537 0, 311 555 1, 328 570 0, 335 602 1, 381 602 1, 375 551 0, 347 521 1, 312 481 0, 256 481 1, 197 481 0, 161 524 1, 137 554 0 +597 35 -14 527 675;527 258 1, 527 15 1, 424 -14 0, 327 -14 1, 35 -14 0, 35 276 1, 35 417 0, 110 493 1, 186 569 0, 329 569 1, 421 569 0, 526 544 1, 526 471 1, 406 510 0, 326 510 1, 119 510 0, 119 279 1, 119 165 0, 176 105 1, 233 45 0, 338 45 1, 381 45 0, 449 59 1, 449 200 1, 356 200 1, 356 258 1, 288 602 1, 288 675 1, 362 675 1, 362 602 1 +427 35 -158 372 555;298 162 1, 298 351 1, 245 361 0, 222 361 1, 115 361 0, 115 215 1, 115 150 0, 136 112 1, 157 74 0, 192 74 1, 239 74 0, 298 95 1, 277 57 0, 254 37 1, 221 9 0, 176 9 1, 113 9 0, 74 64 1, 35 119 0, 35 207 1, 35 306 0, 85 361 1, 135 416 0, 226 416 1, 261 416 0, 298 407 1, 372 407 1, 372 111 1, 372 15 0, 362 -31 1, 334 -158 0, 174 -158 1, 106 -158 0, 38 -135 1, 38 -71 1, 118 -102 0, 173 -102 1, 298 -102 0, 298 31 1, 183 481 1, 183 555 1, 257 555 1, 257 481 1 +597 35 -162 527 569;527 258 1, 527 15 1, 423 -14 0, 327 -14 1, 35 -14 0, 35 276 1, 35 417 0, 110 493 1, 186 569 0, 329 569 1, 421 569 0, 526 544 1, 526 471 1, 406 510 0, 326 510 1, 119 510 0, 119 279 1, 119 165 0, 176 105 1, 233 45 0, 338 45 1, 381 45 0, 449 59 1, 449 200 1, 356 200 1, 356 258 1, 263 -158 1, 263 -126 1, 284 -129 0, 299 -129 1, 340 -129 0, 340 -104 1, 340 -77 0, 281 -71 1, 281 -42 1, 331 -43 0, 356 -54 1, 391 -69 0, 391 -105 1, 391 -162 0, 309 -162 1, 287 -162 0 +427 35 -158 372 651;298 162 1, 298 351 1, 245 361 0, 222 361 1, 115 361 0, 115 215 1, 115 150 0, 136 112 1, 157 74 0, 192 74 1, 239 74 0, 298 95 1, 277 57 0, 254 37 1, 221 9 0, 176 9 1, 113 9 0, 74 64 1, 35 119 0, 35 207 1, 35 306 0, 85 361 1, 135 416 0, 226 416 1, 261 416 0, 298 407 1, 372 407 1, 372 111 1, 372 15 0, 362 -31 1, 334 -158 0, 174 -158 1, 106 -158 0, 38 -135 1, 38 -71 1, 118 -102 0, 173 -102 1, 298 -102 0, 298 31 1, 257 651 1, 257 629 1, 228 621 0, 228 561 1, 228 555 1, 257 555 1, 257 481 1, 183 481 1, 183 545 1, 183 644 0 +555 62 0 492 722;62 0 1, 62 555 1, 141 555 1, 141 321 1, 414 321 1, 414 555 1, 492 555 1, 492 0 1, 414 0 1, 414 262 1, 141 262 1, 141 0 1, 146 602 1, 236 722 1, 318 722 1, 408 602 1, 353 602 1, 278 677 1, 277 677 1, 201 602 1 +427 58 0 374 750;58 0 1, 58 592 1, 132 592 1, 132 331 1, 159 369 0, 186 388 1, 224 416 0, 270 416 1, 374 416 0, 374 293 1, 374 0 1, 300 0 1, 300 269 1, 300 318 0, 290 335 1, 279 353 0, 251 353 1, 190 353 0, 132 264 1, 132 0 1, 86 629 1, 176 750 1, 258 750 1, 348 629 1, 293 629 1, 217 705 1, 216 705 1, 141 629 1 +555 6 0 548 555;141 321 1, 414 321 1, 414 416 1, 141 416 1, 62 0 1, 62 416 1, 6 416 1, 6 463 1, 62 463 1, 62 555 1, 141 555 1, 141 463 1, 414 463 1, 414 555 1, 492 555 1, 492 463 1, 548 463 1, 548 416 1, 492 416 1, 492 0 1, 414 0 1, 414 262 1, 141 262 1, 141 0 1 +427 2 0 374 592;58 0 1, 58 472 1, 2 472 1, 2 518 1, 58 518 1, 58 592 1, 132 592 1, 132 518 1, 243 518 1, 243 472 1, 132 472 1, 132 331 1, 159 369 0, 186 388 1, 224 416 0, 270 416 1, 374 416 0, 374 293 1, 374 0 1, 300 0 1, 300 269 1, 300 318 0, 290 335 1, 279 353 0, 251 353 1, 190 353 0, 132 264 1, 132 0 1 +213 -18 0 232 689;68 0 1, 68 555 1, 146 555 1, 146 0 1, -18 602 1, -15 636 0, -6 656 1, 11 689 0, 52 689 1, 79 689 0, 102 675 1, 125 661 1, 146 648 0, 157 648 1, 182 648 0, 186 689 1, 232 689 1, 229 654 0, 220 635 1, 203 602 0, 162 602 1, 135 602 0, 112 616 1, 89 630 1, 69 643 0, 57 643 1, 32 643 0, 28 602 1 +171 -40 0 210 569;48 0 1, 48 407 1, 122 407 1, 122 0 1, -40 481 1, -37 516 0, -28 535 1, -11 569 0, 30 569 1, 57 569 0, 80 555 1, 103 541 1, 124 528 0, 135 528 1, 160 528 0, 164 569 1, 210 569 1, 207 534 0, 198 515 1, 181 481 0, 141 481 1, 114 481 0, 90 496 1, 68 510 1, 47 522 0, 35 522 1, 11 522 0, 6 481 1 +213 -14 0 227 657;68 0 1, 68 555 1, 146 555 1, 146 0 1, -14 602 1, -14 657 1, 227 657 1, 227 602 1 +171 -56 0 184 537;48 0 1, 48 407 1, 122 407 1, 122 0 1, -56 481 1, -56 537 1, 184 537 1, 184 481 1 +213 -18 0 232 722;68 0 1, 68 555 1, 146 555 1, 146 0 1, -18 722 1, 28 722 1, 36 687 0, 56 672 1, 76 657 0, 107 657 1, 142 657 0, 162 675 1, 179 690 0, 186 722 1, 232 722 1, 226 672 0, 198 641 1, 163 602 0, 107 602 1, 48 602 0, 12 645 1, -12 674 0 +171 -40 0 210 602;48 0 1, 48 407 1, 122 407 1, 122 0 1, -40 602 1, 6 602 1, 14 566 0, 35 551 1, 54 537 0, 85 537 1, 120 537 0, 141 555 1, 157 570 0, 164 602 1, 210 602 1, 204 551 0, 177 521 1, 141 481 0, 85 481 1, 26 481 0, -9 524 1, -34 554 0 +213 45 -139 173 555;68 0 1, 68 555 1, 146 555 1, 146 0 1, 104 0 1, 144 0 1, 96 -30 0, 96 -67 1, 96 -103 0, 140 -103 1, 159 -103 0, 173 -98 1, 173 -128 1, 150 -139 0, 121 -139 1, 45 -139 0, 45 -80 1, 45 -34 0 +171 0 -139 128 555;48 0 1, 48 407 1, 122 407 1, 122 0 1, 48 481 1, 48 555 1, 122 555 1, 122 481 1, 59 0 1, 99 0 1, 51 -30 0, 51 -67 1, 51 -103 0, 94 -103 1, 114 -103 0, 128 -98 1, 128 -128 1, 105 -139 0, 76 -139 1, 0 -139 0, 0 -80 1, 0 -34 0 +213 68 0 146 675;68 0 1, 68 555 1, 146 555 1, 146 0 1, 70 602 1, 70 675 1, 144 675 1, 144 602 1 +564 68 -111 507 555;68 0 1, 68 555 1, 146 555 1, 146 0 1, 210 -87 1, 210 -19 1, 275 -48 0, 330 -48 1, 395 -48 0, 413 -18 1, 428 7 0, 428 68 1, 428 555 1, 507 555 1, 507 70 1, 507 -111 0, 327 -111 1, 266 -111 0 +341 48 -157 316 555;48 0 1, 48 407 1, 122 407 1, 122 0 1, 48 481 1, 48 555 1, 122 555 1, 122 481 1, 134 -145 1, 134 -87 1, 164 -102 0, 190 -102 1, 227 -102 0, 235 -74 1, 242 -51 0, 242 0 1, 242 407 1, 316 407 1, 316 0 1, 316 -157 0, 196 -157 1, 163 -157 0, 242 481 1, 242 555 1, 316 555 1, 316 481 1 +384 18 -111 398 722;18 -87 1, 18 -19 1, 83 -48 0, 138 -48 1, 203 -48 0, 221 -18 1, 236 7 0, 236 68 1, 236 555 1, 315 555 1, 315 70 1, 315 -111 0, 135 -111 1, 74 -111 0, 135 602 1, 226 722 1, 308 722 1, 398 602 1, 342 602 1, 267 677 1, 266 677 1, 191 602 1 +171 -58 -157 212 602;-58 -145 1, -58 -87 1, -28 -102 0, -2 -102 1, 35 -102 0, 43 -74 1, 50 -51 0, 50 0 1, 50 407 1, 124 407 1, 124 0 1, 124 -157 0, 4 -157 1, -29 -157 0, -50 481 1, 40 602 1, 122 602 1, 212 481 1, 156 481 1, 81 557 1, 80 557 1, 5 481 1 +512 72 -162 494 555;72 0 1, 72 555 1, 146 555 1, 146 282 1, 376 555 1, 455 555 1, 232 290 1, 494 0 1, 394 0 1, 146 281 1, 146 0 1, 183 -158 1, 183 -126 1, 204 -129 0, 219 -129 1, 260 -129 0, 260 -104 1, 260 -77 0, 201 -71 1, 201 -42 1, 251 -43 0, 276 -54 1, 311 -69 0, 311 -105 1, 311 -162 0, 230 -162 1, 207 -162 0 +384 58 -162 377 592;58 0 1, 58 592 1, 132 592 1, 132 210 1, 268 407 1, 339 407 1, 209 215 1, 377 0 1, 287 0 1, 132 209 1, 132 0 1, 128 -158 1, 128 -126 1, 149 -129 0, 164 -129 1, 205 -129 0, 205 -104 1, 205 -77 0, 146 -71 1, 146 -42 1, 196 -43 0, 221 -54 1, 256 -69 0, 256 -105 1, 256 -162 0, 175 -162 1, 153 -162 0 +384 58 0 377 407;58 0 1, 58 407 1, 132 407 1, 132 210 1, 268 407 1, 339 407 1, 209 215 1, 377 0 1, 287 0 1, 132 209 1, 132 0 1 +427 62 0 413 722;62 0 1, 62 555 1, 141 555 1, 141 59 1, 413 59 1, 413 0 1, 66 602 1, 157 722 1, 242 722 1, 122 602 1 +171 40 0 216 730;48 0 1, 48 592 1, 122 592 1, 122 0 1, 40 609 1, 131 730 1, 216 730 1, 96 609 1 +427 62 -162 413 555;62 0 1, 62 555 1, 141 555 1, 141 59 1, 413 59 1, 413 0 1, 168 -158 1, 168 -126 1, 190 -129 0, 205 -129 1, 246 -129 0, 246 -104 1, 246 -77 0, 187 -71 1, 187 -42 1, 237 -43 0, 262 -54 1, 297 -69 0, 297 -105 1, 297 -162 0, 215 -162 1, 193 -162 0 +171 0 -162 128 592;48 0 1, 48 592 1, 122 592 1, 122 0 1, 0 -158 1, 0 -126 1, 21 -129 0, 36 -129 1, 77 -129 0, 77 -104 1, 77 -77 0, 18 -71 1, 18 -42 1, 68 -43 0, 93 -54 1, 128 -69 0, 128 -105 1, 128 -162 0, 47 -162 1, 25 -162 0 +427 62 0 413 555;62 0 1, 62 555 1, 141 555 1, 141 59 1, 413 59 1, 413 0 1, 252 385 1, 252 407 1, 280 415 0, 280 475 1, 280 481 1, 252 481 1, 252 555 1, 326 555 1, 326 491 1, 325 393 0 +224 48 0 229 592;48 0 1, 48 592 1, 122 592 1, 122 0 1, 155 422 1, 155 444 1, 183 452 0, 183 512 1, 183 518 1, 155 518 1, 155 592 1, 229 592 1, 229 528 1, 228 430 0 +427 62 0 413 555;62 0 1, 62 555 1, 141 555 1, 141 59 1, 413 59 1, 413 0 1, 279 241 1, 279 315 1, 353 315 1, 353 241 1 +257 48 0 252 592;48 0 1, 48 592 1, 122 592 1, 122 0 1, 178 241 1, 178 315 1, 252 315 1, 252 241 1 +427 6 0 413 555;62 0 1, 62 260 1, 6 230 1, 6 293 1, 62 323 1, 62 555 1, 141 555 1, 141 366 1, 233 416 1, 233 353 1, 141 303 1, 141 59 1, 413 59 1, 413 0 1 +171 -3 0 173 592;48 0 1, 48 270 1, -3 243 1, -3 305 1, 48 333 1, 48 592 1, 123 592 1, 123 374 1, 173 399 1, 173 338 1, 123 311 1, 123 0 1 +555 62 0 492 722;62 0 1, 62 555 1, 139 555 1, 425 126 1, 425 555 1, 492 555 1, 492 0 1, 415 0 1, 129 429 1, 129 0 1, 222 602 1, 312 722 1, 398 722 1, 277 602 1 +427 58 0 374 602;58 0 1, 58 407 1, 132 407 1, 132 331 1, 159 369 0, 186 388 1, 224 416 0, 270 416 1, 374 416 0, 374 293 1, 374 0 1, 300 0 1, 300 269 1, 300 318 0, 290 335 1, 279 353 0, 251 353 1, 190 353 0, 132 264 1, 132 0 1, 168 481 1, 259 602 1, 344 602 1, 224 481 1 +555 62 -162 492 555;62 0 1, 62 555 1, 139 555 1, 425 126 1, 425 555 1, 492 555 1, 492 0 1, 415 0 1, 129 429 1, 129 0 1, 194 -158 1, 194 -126 1, 216 -129 0, 231 -129 1, 272 -129 0, 272 -104 1, 272 -77 0, 213 -71 1, 213 -42 1, 263 -43 0, 288 -54 1, 323 -69 0, 323 -105 1, 323 -162 0, 241 -162 1, 219 -162 0 +427 58 -162 374 416;58 0 1, 58 407 1, 132 407 1, 132 331 1, 159 369 0, 186 388 1, 224 416 0, 270 416 1, 374 416 0, 374 293 1, 374 0 1, 300 0 1, 300 269 1, 300 318 0, 290 335 1, 279 353 0, 251 353 1, 190 353 0, 132 264 1, 132 0 1, 128 -158 1, 128 -126 1, 149 -129 0, 164 -129 1, 205 -129 0, 205 -104 1, 205 -77 0, 146 -71 1, 146 -42 1, 196 -43 0, 221 -54 1, 256 -69 0, 256 -105 1, 256 -162 0, 175 -162 1, 153 -162 0 +555 62 0 492 722;62 0 1, 62 555 1, 139 555 1, 425 126 1, 425 555 1, 492 555 1, 492 0 1, 415 0 1, 129 429 1, 129 0 1, 408 722 1, 318 602 1, 236 602 1, 146 722 1, 201 722 1, 277 646 1, 278 646 1, 353 722 1 +427 58 0 374 602;58 0 1, 58 407 1, 132 407 1, 132 331 1, 159 369 0, 186 388 1, 224 416 0, 270 416 1, 374 416 0, 374 293 1, 374 0 1, 300 0 1, 300 269 1, 300 318 0, 290 335 1, 279 353 0, 251 353 1, 190 353 0, 132 264 1, 132 0 1, 323 602 1, 233 481 1, 151 481 1, 61 602 1, 116 602 1, 192 526 1, 192 526 1, 268 602 1 +464 0 0 411 592;95 0 1, 95 407 1, 168 407 1, 168 331 1, 196 369 0, 222 388 1, 261 416 0, 307 416 1, 411 416 0, 411 293 1, 411 0 1, 336 0 1, 336 269 1, 336 318 0, 326 335 1, 315 353 0, 287 353 1, 227 353 0, 168 264 1, 168 0 1, 0 422 1, 0 444 1, 29 452 0, 29 512 1, 29 518 1, 0 518 1, 0 592 1, 74 592 1, 74 528 1, 74 430 0 +555 62 -158 492 555;62 0 1, 62 555 1, 139 555 1, 425 126 1, 425 555 1, 492 555 1, 492 -35 1, 492 -158 0, 373 -158 1, 345 -158 0, 317 -150 1, 317 -92 1, 341 -102 0, 369 -102 1, 425 -102 0, 425 -21 1, 425 -15 1, 129 429 1, 129 0 1 +427 58 -158 374 416;58 0 1, 58 407 1, 132 407 1, 132 331 1, 159 369 0, 186 388 1, 224 416 0, 270 416 1, 374 416 0, 374 293 1, 374 -35 1, 374 -158 0, 255 -158 1, 227 -158 0, 198 -150 1, 198 -92 1, 222 -102 0, 244 -102 1, 300 -102 0, 300 -21 1, 300 269 1, 300 318 0, 290 335 1, 279 353 0, 251 353 1, 190 353 0, 132 264 1, 132 0 1 +597 35 -14 563 657;299 569 1, 419 569 0, 491 490 1, 563 410 0, 563 278 1, 563 144 0, 491 65 1, 419 -14 0, 295 -14 1, 189 -14 0, 121 51 1, 35 132 0, 35 278 1, 35 411 0, 107 490 1, 179 569 0, 299 510 1, 214 510 0, 167 449 1, 119 388 0, 119 278 1, 119 169 0, 167 107 1, 213 45 0, 297 45 1, 375 45 0, 421 95 1, 479 156 0, 479 278 1, 479 388 0, 431 449 1, 383 510 0, 179 602 1, 179 657 1, 419 657 1, 419 602 1 +427 32 -9 395 537;213 416 1, 297 416 0, 346 359 1, 395 303 0, 395 204 1, 395 104 0, 346 47 1, 297 -9 0, 211 -9 1, 137 -9 0, 91 38 1, 32 96 0, 32 204 1, 32 302 0, 81 359 1, 130 416 0, 213 361 1, 112 361 0, 112 204 1, 112 46 0, 213 46 1, 315 46 0, 315 205 1, 315 361 0, 72 481 1, 72 537 1, 312 537 1, 312 481 1 +597 35 -14 563 722;299 569 1, 419 569 0, 491 490 1, 563 410 0, 563 278 1, 563 144 0, 491 65 1, 419 -14 0, 295 -14 1, 189 -14 0, 121 51 1, 35 132 0, 35 278 1, 35 411 0, 107 490 1, 179 569 0, 299 510 1, 214 510 0, 167 449 1, 119 388 0, 119 278 1, 119 169 0, 167 107 1, 213 45 0, 297 45 1, 375 45 0, 421 95 1, 479 156 0, 479 278 1, 479 388 0, 431 449 1, 383 510 0, 174 722 1, 220 722 1, 228 687 0, 248 672 1, 268 657 0, 299 657 1, 334 657 0, 354 675 1, 371 690 0, 378 722 1, 424 722 1, 418 672 0, 390 641 1, 355 602 0, 299 602 1, 240 602 0, 204 645 1, 180 674 0 +427 32 -9 395 602;213 416 1, 297 416 0, 346 359 1, 395 303 0, 395 204 1, 395 104 0, 346 47 1, 297 -9 0, 211 -9 1, 137 -9 0, 91 38 1, 32 96 0, 32 204 1, 32 302 0, 81 359 1, 130 416 0, 213 361 1, 112 361 0, 112 204 1, 112 46 0, 213 46 1, 315 46 0, 315 205 1, 315 361 0, 89 602 1, 135 602 1, 143 567 0, 163 551 1, 182 537 0, 213 537 1, 249 537 0, 269 555 1, 285 570 0, 292 602 1, 338 602 1, 332 551 0, 305 521 1, 269 481 0, 213 481 1, 155 481 0, 119 524 1, 95 553 0 +597 35 -14 563 722;299 569 1, 419 569 0, 491 490 1, 563 410 0, 563 278 1, 563 144 0, 491 65 1, 419 -14 0, 295 -14 1, 189 -14 0, 121 51 1, 35 132 0, 35 278 1, 35 411 0, 107 490 1, 179 569 0, 299 510 1, 214 510 0, 167 449 1, 119 388 0, 119 278 1, 119 169 0, 167 107 1, 213 45 0, 297 45 1, 375 45 0, 421 95 1, 479 156 0, 479 278 1, 479 388 0, 431 449 1, 383 510 0, 212 602 1, 303 722 1, 374 722 1, 254 602 1, 344 602 1, 434 722 1, 506 722 1, 386 602 1 +427 32 -9 403 602;213 416 1, 297 416 0, 346 359 1, 395 303 0, 395 204 1, 395 104 0, 346 47 1, 297 -9 0, 211 -9 1, 137 -9 0, 91 38 1, 32 96 0, 32 204 1, 32 302 0, 81 359 1, 130 416 0, 213 361 1, 112 361 0, 112 204 1, 112 46 0, 213 46 1, 315 46 0, 315 205 1, 315 361 0, 109 481 1, 199 602 1, 271 602 1, 151 481 1, 241 481 1, 331 602 1, 403 602 1, 283 481 1 +555 62 0 538 722;62 0 1, 62 555 1, 294 555 1, 465 555 0, 465 417 1, 465 350 0, 423 306 1, 399 281 0, 353 260 1, 538 0 1, 441 0 1, 283 235 1, 141 235 1, 141 0 1, 141 294 1, 229 294 1, 309 294 0, 346 321 1, 384 350 0, 384 408 1, 384 456 0, 353 476 1, 323 496 0, 253 496 1, 141 496 1, 196 602 1, 287 722 1, 372 722 1, 252 602 1 +256 58 0 280 602;58 0 1, 58 407 1, 132 407 1, 132 331 1, 148 369 0, 166 389 1, 193 416 0, 230 416 1, 237 416 0, 251 414 1, 251 345 1, 231 352 0, 219 352 1, 178 352 0, 132 269 1, 132 0 1, 104 481 1, 195 602 1, 280 602 1, 160 481 1 +555 62 -162 538 555;62 0 1, 62 555 1, 294 555 1, 465 555 0, 465 417 1, 465 350 0, 423 306 1, 399 281 0, 353 260 1, 538 0 1, 441 0 1, 283 235 1, 141 235 1, 141 0 1, 141 294 1, 229 294 1, 309 294 0, 346 321 1, 384 350 0, 384 408 1, 384 456 0, 353 476 1, 323 496 0, 253 496 1, 141 496 1, 196 -158 1, 196 -126 1, 218 -129 0, 233 -129 1, 274 -129 0, 274 -104 1, 274 -77 0, 215 -71 1, 215 -42 1, 265 -43 0, 290 -54 1, 324 -69 0, 324 -105 1, 324 -162 0, 243 -162 1, 221 -162 0 +256 58 -162 251 416;58 0 1, 58 407 1, 132 407 1, 132 331 1, 148 369 0, 166 389 1, 193 416 0, 230 416 1, 237 416 0, 251 414 1, 251 345 1, 231 352 0, 219 352 1, 178 352 0, 132 269 1, 132 0 1, 64 -158 1, 64 -126 1, 85 -129 0, 100 -129 1, 141 -129 0, 141 -104 1, 141 -77 0, 82 -71 1, 82 -42 1, 132 -43 0, 157 -54 1, 192 -69 0, 192 -105 1, 192 -162 0, 111 -162 1, 89 -162 0 +555 62 0 538 722;62 0 1, 62 555 1, 294 555 1, 465 555 0, 465 417 1, 465 350 0, 423 306 1, 399 281 0, 353 260 1, 538 0 1, 441 0 1, 283 235 1, 141 235 1, 141 0 1, 141 294 1, 229 294 1, 309 294 0, 346 321 1, 384 350 0, 384 408 1, 384 456 0, 353 476 1, 323 496 0, 253 496 1, 141 496 1, 368 722 1, 278 602 1, 196 602 1, 106 722 1, 161 722 1, 237 646 1, 237 646 1, 313 722 1 +256 -3 0 259 602;58 0 1, 58 407 1, 132 407 1, 132 331 1, 148 369 0, 166 389 1, 193 416 0, 230 416 1, 237 416 0, 251 414 1, 251 345 1, 231 352 0, 219 352 1, 178 352 0, 132 269 1, 132 0 1, 259 602 1, 169 481 1, 87 481 1, -3 602 1, 52 602 1, 128 526 1, 128 526 1, 204 602 1 +512 45 -14 466 722;45 20 1, 45 98 1, 156 45 0, 264 45 1, 385 45 0, 385 135 1, 385 181 0, 352 203 1, 326 220 0, 269 239 1, 193 264 1, 48 311 0, 48 421 1, 48 569 0, 251 569 1, 338 569 0, 432 545 1, 432 473 1, 334 510 0, 246 510 1, 124 510 0, 124 427 1, 124 394 0, 147 374 1, 171 354 0, 230 334 1, 308 309 1, 395 281 0, 431 244 1, 466 207 0, 466 146 1, 466 72 0, 411 29 1, 357 -14 0, 261 -14 1, 167 -14 0, 196 602 1, 287 722 1, 372 722 1, 252 602 1 +384 44 -9 344 602;44 14 1, 44 82 1, 118 46 0, 181 46 1, 266 46 0, 266 106 1, 266 147 0, 207 167 1, 141 189 1, 46 220 0, 46 303 1, 46 416 0, 201 416 1, 246 416 0, 309 404 1, 309 342 1, 253 361 0, 196 361 1, 119 361 0, 119 310 1, 119 273 0, 172 256 1, 231 237 1, 341 201 0, 341 113 1, 341 57 0, 297 24 1, 254 -9 0, 178 -9 1, 119 -9 0, 168 481 1, 259 602 1, 344 602 1, 224 481 1 +512 45 -14 466 722;45 20 1, 45 98 1, 156 45 0, 264 45 1, 385 45 0, 385 135 1, 385 181 0, 352 203 1, 326 220 0, 269 239 1, 193 264 1, 48 311 0, 48 421 1, 48 569 0, 251 569 1, 338 569 0, 432 545 1, 432 473 1, 334 510 0, 246 510 1, 124 510 0, 124 427 1, 124 394 0, 147 374 1, 171 354 0, 230 334 1, 308 309 1, 395 281 0, 431 244 1, 466 207 0, 466 146 1, 466 72 0, 411 29 1, 357 -14 0, 261 -14 1, 167 -14 0, 120 602 1, 211 722 1, 293 722 1, 383 602 1, 327 602 1, 252 677 1, 251 677 1, 176 602 1 +384 44 -9 341 602;44 14 1, 44 82 1, 118 46 0, 181 46 1, 266 46 0, 266 106 1, 266 147 0, 207 167 1, 141 189 1, 46 220 0, 46 303 1, 46 416 0, 201 416 1, 246 416 0, 309 404 1, 309 342 1, 253 361 0, 196 361 1, 119 361 0, 119 310 1, 119 273 0, 172 256 1, 231 237 1, 341 201 0, 341 113 1, 341 57 0, 297 24 1, 254 -9 0, 178 -9 1, 119 -9 0, 75 481 1, 165 602 1, 247 602 1, 337 481 1, 282 481 1, 206 557 1, 206 557 1, 130 481 1 +512 45 -162 466 569;45 20 1, 45 98 1, 156 45 0, 264 45 1, 385 45 0, 385 135 1, 385 181 0, 352 203 1, 326 220 0, 269 239 1, 193 264 1, 48 311 0, 48 421 1, 48 569 0, 251 569 1, 338 569 0, 432 545 1, 432 473 1, 334 510 0, 246 510 1, 124 510 0, 124 427 1, 124 394 0, 147 374 1, 171 354 0, 230 334 1, 308 309 1, 395 281 0, 431 244 1, 466 207 0, 466 147 1, 466 72 0, 411 29 1, 357 -14 0, 261 -14 1, 167 -14 0, 225 0 1, 262 0 1, 239 -41 1, 266 -42 0, 286 -56 1, 312 -74 0, 312 -101 1, 312 -126 0, 290 -144 1, 268 -162 0, 236 -162 1, 211 -162 0, 182 -154 1, 182 -124 1, 201 -129 0, 221 -129 1, 260 -129 0, 260 -102 1, 260 -67 0, 190 -66 1 +384 44 -162 341 416;44 14 1, 44 82 1, 118 46 0, 181 46 1, 266 46 0, 266 106 1, 266 147 0, 207 167 1, 141 189 1, 46 220 0, 46 303 1, 46 416 0, 201 416 1, 246 416 0, 309 404 1, 309 342 1, 253 361 0, 196 361 1, 119 361 0, 119 310 1, 119 273 0, 172 256 1, 231 237 1, 341 201 0, 341 113 1, 341 57 0, 297 24 1, 254 -9 0, 178 -9 1, 119 -9 0, 171 0 1, 207 0 1, 184 -41 1, 211 -42 0, 231 -56 1, 257 -74 0, 257 -101 1, 257 -126 0, 235 -144 1, 213 -162 0, 181 -162 1, 156 -162 0, 127 -154 1, 127 -124 1, 146 -129 0, 166 -129 1, 205 -129 0, 205 -102 1, 205 -67 0, 135 -66 1 +512 45 -14 466 722;45 20 1, 45 98 1, 156 45 0, 264 45 1, 385 45 0, 385 135 1, 385 181 0, 352 203 1, 326 220 0, 269 239 1, 193 264 1, 48 311 0, 48 421 1, 48 569 0, 251 569 1, 338 569 0, 432 545 1, 432 473 1, 334 510 0, 246 510 1, 124 510 0, 124 427 1, 124 394 0, 147 374 1, 171 354 0, 230 334 1, 308 309 1, 395 281 0, 431 244 1, 466 207 0, 466 146 1, 466 72 0, 411 29 1, 357 -14 0, 261 -14 1, 167 -14 0, 383 722 1, 293 602 1, 211 602 1, 120 722 1, 176 722 1, 251 646 1, 252 646 1, 327 722 1 +384 44 -9 341 602;44 14 1, 44 82 1, 118 46 0, 181 46 1, 266 46 0, 266 106 1, 266 147 0, 207 167 1, 141 189 1, 46 220 0, 46 303 1, 46 416 0, 201 416 1, 246 416 0, 309 404 1, 309 342 1, 253 361 0, 196 361 1, 119 361 0, 119 310 1, 119 273 0, 172 256 1, 231 237 1, 341 201 0, 341 113 1, 341 57 0, 297 24 1, 254 -9 0, 178 -9 1, 119 -9 0, 323 602 1, 233 481 1, 151 481 1, 61 602 1, 116 602 1, 192 526 1, 192 526 1, 268 602 1 +469 8 -162 461 555;195 0 1, 195 496 1, 8 496 1, 8 555 1, 461 555 1, 461 496 1, 274 496 1, 274 0 1, 220 0 1, 256 0 1, 234 -41 1, 261 -42 0, 280 -56 1, 306 -74 0, 306 -101 1, 306 -126 0, 285 -144 1, 263 -162 0, 230 -162 1, 205 -162 0, 176 -154 1, 176 -124 1, 195 -129 0, 215 -129 1, 254 -129 0, 254 -102 1, 254 -67 0, 184 -66 1 +213 11 -162 210 488;199 -2 1, 176 -9 0, 156 -9 1, 57 -9 0, 57 113 1, 57 352 1, 11 352 1, 11 407 1, 57 407 1, 57 481 1, 131 488 1, 131 407 1, 210 407 1, 210 352 1, 131 352 1, 131 126 1, 131 78 0, 139 62 1, 147 46 0, 174 46 1, 188 46 0, 199 50 1, 112 0 1, 149 0 1, 126 -41 1, 153 -42 0, 173 -56 1, 199 -74 0, 199 -101 1, 199 -126 0, 177 -144 1, 155 -162 0, 123 -162 1, 97 -162 0, 69 -154 1, 69 -124 1, 87 -129 0, 108 -129 1, 147 -129 0, 147 -102 1, 147 -67 0, 77 -66 1 +469 8 0 461 722;195 0 1, 195 496 1, 8 496 1, 8 555 1, 461 555 1, 461 496 1, 274 496 1, 274 0 1, 366 722 1, 275 602 1, 194 602 1, 103 722 1, 159 722 1, 234 646 1, 235 646 1, 310 722 1 +288 11 -9 270 633;199 -2 1, 176 -9 0, 156 -9 1, 57 -9 0, 57 113 1, 57 352 1, 11 352 1, 11 407 1, 57 407 1, 57 481 1, 131 488 1, 131 407 1, 210 407 1, 210 352 1, 131 352 1, 131 126 1, 131 78 0, 139 62 1, 147 46 0, 174 46 1, 188 46 0, 199 50 1, 196 463 1, 196 485 1, 224 493 0, 224 553 1, 224 559 1, 196 559 1, 196 633 1, 270 633 1, 270 569 1, 269 471 0 +469 8 0 461 555;195 0 1, 195 268 1, 81 268 1, 81 324 1, 195 324 1, 195 496 1, 8 496 1, 8 555 1, 461 555 1, 461 496 1, 274 496 1, 274 324 1, 387 324 1, 387 268 1, 274 268 1, 274 0 1 +213 11 -9 210 488;57 213 1, 11 213 1, 11 259 1, 57 259 1, 57 352 1, 11 352 1, 11 407 1, 57 407 1, 57 481 1, 131 488 1, 131 407 1, 210 407 1, 210 352 1, 131 352 1, 131 259 1, 210 259 1, 210 213 1, 131 213 1, 131 126 1, 131 78 0, 139 62 1, 147 46 0, 174 46 1, 188 46 0, 199 50 1, 199 -2 1, 176 -9 0, 156 -9 1, 57 -9 0, 57 113 1 +555 62 -14 492 689;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 157 602 1, 160 636 0, 169 656 1, 186 689 0, 227 689 1, 254 689 0, 277 675 1, 300 661 1, 321 648 0, 332 648 1, 357 648 0, 361 689 1, 407 689 1, 404 654 0, 395 635 1, 378 602 0, 338 602 1, 311 602 0, 287 616 1, 264 630 1, 244 643 0, 232 643 1, 207 643 0, 203 602 1 +427 53 -9 369 569;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 86 481 1, 89 516 0, 98 535 1, 116 569 0, 156 569 1, 183 569 0, 206 555 1, 229 541 1, 250 528 0, 261 528 1, 286 528 0, 290 569 1, 336 569 1, 333 534 0, 324 515 1, 307 481 0, 267 481 1, 240 481 0, 216 496 1, 194 510 1, 173 522 0, 161 522 1, 137 522 0, 132 481 1 +555 62 -14 492 657;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 162 602 1, 162 657 1, 402 657 1, 402 602 1 +427 53 -9 369 537;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 72 481 1, 72 537 1, 312 537 1, 312 481 1 +555 62 -14 492 722;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 157 722 1, 203 722 1, 211 687 0, 231 672 1, 251 657 0, 282 657 1, 317 657 0, 338 675 1, 354 690 0, 361 722 1, 407 722 1, 401 672 0, 374 641 1, 338 602 0, 282 602 1, 223 602 0, 188 645 1, 163 674 0 +427 53 -9 369 602;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 86 602 1, 132 602 1, 140 567 0, 161 551 1, 180 537 0, 211 537 1, 246 537 0, 267 555 1, 283 570 0, 290 602 1, 336 602 1, 330 551 0, 303 521 1, 267 481 0, 211 481 1, 152 481 0, 117 524 1, 93 553 0 +555 62 -14 492 762;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 282 762 1, 317 762 0, 342 738 1, 367 713 0, 367 678 1, 367 642 0, 342 617 1, 317 592 0, 281 592 1, 250 592 0, 227 612 1, 197 638 0, 197 677 1, 197 713 0, 222 737 1, 247 762 0, 282 730 1, 260 730 0, 245 714 1, 229 699 0, 229 677 1, 229 656 0, 245 640 1, 260 624 0, 281 624 1, 302 624 0, 316 637 1, 335 653 0, 335 678 1, 335 699 0, 319 714 1, 304 730 0 +427 53 -9 369 651;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 192 651 1, 227 651 0, 252 627 1, 277 602 0, 277 567 1, 277 531 0, 252 506 1, 227 481 0, 191 481 1, 160 481 0, 137 501 1, 107 527 0, 107 566 1, 107 602 0, 132 626 1, 156 651 0, 192 619 1, 170 619 0, 154 603 1, 139 588 0, 139 566 1, 139 545 0, 154 529 1, 170 513 0, 191 513 1, 211 513 0, 226 526 1, 245 542 0, 245 567 1, 245 588 0, 229 603 1, 214 619 0 +555 62 -14 492 722;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 187 602 1, 278 722 1, 349 722 1, 229 602 1, 319 602 1, 409 722 1, 481 722 1, 361 602 1 +427 53 -9 403 602;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 109 481 1, 199 602 1, 271 602 1, 151 481 1, 241 481 1, 331 602 1, 403 602 1, 283 481 1 +555 62 -139 492 555;62 555 1, 141 555 1, 141 205 1, 141 146 0, 151 118 1, 160 90 0, 187 71 1, 225 45 0, 288 45 1, 363 45 0, 393 80 1, 423 115 0, 423 202 1, 423 555 1, 492 555 1, 492 203 1, 492 129 0, 477 91 1, 463 52 0, 423 24 1, 370 -14 0, 281 -14 1, 168 -14 0, 115 39 1, 62 91 0, 62 206 1, 300 0 1, 340 0 1, 292 -30 0, 292 -67 1, 292 -103 0, 335 -103 1, 355 -103 0, 369 -98 1, 369 -128 1, 346 -139 0, 317 -139 1, 241 -139 0, 241 -80 1, 241 -34 0 +427 53 -139 384 407;295 0 1, 295 76 1, 268 38 0, 242 19 1, 203 -9 0, 157 -9 1, 53 -9 0, 53 115 1, 53 407 1, 127 407 1, 127 139 1, 127 90 0, 137 72 1, 148 54 0, 176 54 1, 237 54 0, 295 143 1, 295 407 1, 369 407 1, 369 0 1, 315 0 1, 355 0 1, 307 -30 0, 307 -67 1, 307 -103 0, 350 -103 1, 370 -103 0, 384 -98 1, 384 -128 1, 361 -139 0, 332 -139 1, 256 -139 0, 256 -80 1, 256 -34 0 +725 9 0 716 722;152 0 1, 9 555 1, 85 555 1, 199 117 1, 329 555 1, 405 555 1, 530 121 1, 651 555 1, 716 555 1, 560 0 1, 482 0 1, 358 428 1, 230 0 1, 236 602 1, 326 722 1, 408 722 1, 498 602 1, 443 602 1, 367 677 1, 366 677 1, 291 602 1 +555 4 0 549 602;102 0 1, 4 407 1, 77 407 1, 150 101 1, 244 407 1, 318 407 1, 400 99 1, 486 407 1, 549 407 1, 435 0 1, 361 0 1, 275 315 1, 177 0 1, 149 481 1, 240 602 1, 321 602 1, 412 481 1, 356 481 1, 281 557 1, 280 557 1, 205 481 1 +512 11 0 501 722;210 0 1, 210 231 1, 11 555 1, 101 555 1, 259 298 1, 428 555 1, 501 555 1, 289 233 1, 289 0 1, 134 602 1, 224 722 1, 306 722 1, 396 602 1, 341 602 1, 265 677 1, 264 677 1, 189 602 1 +384 7 -148 380 602;152 0 1, 7 407 1, 82 407 1, 193 95 1, 314 407 1, 380 407 1, 164 -148 1, 87 -148 1, 67 481 1, 157 602 1, 239 602 1, 329 481 1, 274 481 1, 198 557 1, 198 557 1, 122 481 1 +469 38 0 431 722;38 0 1, 38 63 1, 336 496 1, 56 496 1, 56 555 1, 431 555 1, 431 496 1, 132 63 1, 431 63 1, 431 0 1, 179 602 1, 269 722 1, 355 722 1, 234 602 1 +384 28 0 356 602;28 0 1, 28 56 1, 261 352 1, 39 352 1, 39 407 1, 352 407 1, 352 352 1, 119 56 1, 356 56 1, 356 0 1, 168 481 1, 259 602 1, 344 602 1, 224 481 1 +469 38 0 431 675;38 0 1, 38 63 1, 336 496 1, 56 496 1, 56 555 1, 431 555 1, 431 496 1, 132 63 1, 431 63 1, 431 0 1, 202 602 1, 202 675 1, 276 675 1, 276 602 1 +384 28 0 356 555;28 0 1, 28 56 1, 261 352 1, 39 352 1, 39 407 1, 352 407 1, 352 352 1, 119 56 1, 356 56 1, 356 0 1, 155 481 1, 155 555 1, 229 555 1, 229 481 1 +469 38 0 431 722;38 0 1, 38 63 1, 336 496 1, 56 496 1, 56 555 1, 431 555 1, 431 496 1, 132 63 1, 431 63 1, 431 0 1, 370 722 1, 280 602 1, 198 602 1, 108 722 1, 163 722 1, 239 646 1, 239 646 1, 315 722 1 +384 28 0 356 602;28 0 1, 28 56 1, 261 352 1, 39 352 1, 39 407 1, 352 407 1, 352 352 1, 119 56 1, 356 56 1, 356 0 1, 323 602 1, 233 481 1, 151 481 1, 61 602 1, 116 602 1, 192 526 1, 192 526 1, 268 602 1 +171 3 0 192 602;54 0 1, 54 352 1, 3 352 1, 3 407 1, 54 407 1, 54 456 1, 54 525 0, 84 563 1, 114 602 0, 167 602 1, 175 602 0, 192 600 1, 192 545 1, 181 546 0, 175 546 1, 128 546 0, 128 464 1, 128 0 1 +512 45 -162 466 569;45 20 1, 45 98 1, 156 45 0, 264 45 1, 385 45 0, 385 135 1, 385 181 0, 352 203 1, 326 220 0, 269 239 1, 193 264 1, 48 311 0, 48 421 1, 48 569 0, 251 569 1, 338 569 0, 432 545 1, 432 473 1, 334 510 0, 246 510 1, 124 510 0, 124 427 1, 124 394 0, 147 374 1, 171 354 0, 230 334 1, 308 309 1, 395 281 0, 431 244 1, 466 207 0, 466 147 1, 466 72 0, 411 29 1, 357 -14 0, 261 -14 1, 167 -14 0, 197 -158 1, 197 -126 1, 218 -129 0, 233 -129 1, 274 -129 0, 274 -104 1, 274 -77 0, 215 -71 1, 215 -42 1, 265 -43 0, 290 -54 1, 325 -69 0, 325 -105 1, 325 -162 0, 243 -162 1, 221 -162 0 +384 44 -162 341 416;44 14 1, 44 82 1, 118 46 0, 181 46 1, 266 46 0, 266 106 1, 266 147 0, 207 167 1, 141 189 1, 46 220 0, 46 303 1, 46 416 0, 201 416 1, 246 416 0, 309 404 1, 309 342 1, 253 361 0, 196 361 1, 119 361 0, 119 310 1, 119 273 0, 172 256 1, 231 237 1, 341 201 0, 341 113 1, 341 57 0, 297 24 1, 254 -9 0, 178 -9 1, 119 -9 0, 128 -158 1, 128 -126 1, 149 -129 0, 164 -129 1, 205 -129 0, 205 -104 1, 205 -77 0, 146 -71 1, 146 -42 1, 196 -43 0, 221 -54 1, 256 -69 0, 256 -105 1, 256 -162 0, 175 -162 1, 153 -162 0 +469 8 -162 461 555;195 0 1, 195 496 1, 8 496 1, 8 555 1, 461 555 1, 461 496 1, 274 496 1, 274 0 1, 177 -158 1, 177 -126 1, 198 -129 0, 213 -129 1, 254 -129 0, 254 -104 1, 254 -77 0, 195 -71 1, 195 -42 1, 245 -43 0, 270 -54 1, 305 -69 0, 305 -105 1, 305 -162 0, 224 -162 1, 201 -162 0 +213 11 -162 210 488;199 -2 1, 176 -9 0, 156 -9 1, 57 -9 0, 57 113 1, 57 352 1, 11 352 1, 11 407 1, 57 407 1, 57 481 1, 131 488 1, 131 407 1, 210 407 1, 210 352 1, 131 352 1, 131 126 1, 131 78 0, 139 62 1, 147 46 0, 174 46 1, 188 46 0, 199 50 1, 64 -158 1, 64 -126 1, 85 -129 0, 100 -129 1, 141 -129 0, 141 -104 1, 141 -77 0, 82 -71 1, 82 -42 1, 132 -43 0, 157 -54 1, 192 -69 0, 192 -105 1, 192 -162 0, 111 -162 1, 89 -162 0 +256 64 -162 192 -42;64 -158 1, 64 -126 1, 85 -129 0, 100 -129 1, 141 -129 0, 141 -104 1, 141 -77 0, 82 -71 1, 82 -42 1, 132 -43 0, 157 -54 1, 192 -69 0, 192 -105 1, 192 -162 0, 111 -162 1, 89 -162 0 +213 70 -120 144 407;70 -120 1, 70 -93 1, 96 -79 0, 96 -9 1, 96 0 1, 70 0 1, 70 74 1, 144 74 1, 144 12 1, 143 -102 0, 70 333 1, 70 407 1, 144 407 1, 144 333 1 +213 60 184 153 277;60 184 1, 60 277 1, 153 277 1, 153 184 1 +256 33 194 223 250;33 194 1, 33 250 1, 223 250 1, 223 194 1 +256 33 194 223 250;33 194 1, 33 250 1, 223 250 1, 223 194 1 +427 38 204 390 250;38 204 1, 38 250 1, 390 250 1, 390 204 1 +768 37 204 731 241;37 204 1, 37 241 1, 731 241 1, 731 204 1 +213 0 0 0 0; +427 37 546 390 602;37 546 1, 37 602 1, 390 602 1, 390 546 1 +427 32 -9 383 416;307 248 1, 306 284 0, 299 303 1, 280 361 0, 215 361 1, 169 361 0, 143 334 1, 117 308 0, 111 248 1, 380 72 1, 380 13 1, 304 -9 0, 240 -9 1, 145 -9 0, 89 50 1, 32 109 0, 32 209 1, 32 304 0, 82 360 1, 132 416 0, 216 416 1, 314 416 0, 354 347 1, 383 296 0, 382 215 1, 382 192 1, 110 192 1, 114 147 0, 125 124 1, 158 47 0, 256 47 1, 312 47 0 +449 39 194 409 250;39 194 1, 39 250 1, 409 250 1, 409 194 1 +128 -165 -14 293 569;-165 -14 1, 243 569 1, 293 569 1, -114 -14 1 +213 60 184 153 277;60 184 1, 60 277 1, 153 277 1, 153 184 1 +384 12 0 336 602;66 0 1, 66 352 1, 12 352 1, 12 407 1, 66 407 1, 66 456 1, 66 602 0, 181 602 1, 206 602 0, 236 592 1, 236 533 1, 209 546 0, 189 546 1, 162 546 0, 151 528 1, 140 510 0, 140 464 1, 140 407 1, 336 407 1, 336 0 1, 262 0 1, 262 352 1, 140 352 1, 140 0 1, 262 481 1, 262 555 1, 336 555 1, 336 481 1 +384 12 0 336 602;66 0 1, 66 352 1, 12 352 1, 12 407 1, 66 407 1, 66 456 1, 66 602 0, 179 602 1, 262 592 1, 336 592 1, 336 0 1, 262 0 1, 262 537 1, 246 540 1, 214 546 0, 192 546 1, 159 546 0, 148 524 1, 140 505 0, 140 464 1, 140 407 1, 206 407 1, 206 352 1, 140 352 1, 140 0 1 +256 1 222 251 555;154 222 1, 154 313 1, 1 313 1, 1 356 1, 152 555 1, 205 555 1, 205 357 1, 251 357 1, 251 313 1, 205 313 1, 205 222 1, 50 357 1, 154 357 1, 154 493 1 +171 -58 -157 124 407;-58 -145 1, -58 -87 1, -28 -102 0, -2 -102 1, 35 -102 0, 43 -74 1, 50 -51 0, 50 0 1, 50 407 1, 124 407 1, 124 0 1, 124 -157 0, 4 -157 1, -29 -157 0 +213 0 0 0 0; diff --git a/vendor/github.com/golang/freetype/testdata/luxisr-12pt-with-hinting.txt b/vendor/github.com/golang/freetype/testdata/luxisr-12pt-with-hinting.txt new file mode 100644 index 000000000..9c30f6773 --- /dev/null +++ b/vendor/github.com/golang/freetype/testdata/luxisr-12pt-with-hinting.txt @@ -0,0 +1,392 @@ +freetype version 2.5.1 +192 0 0 192 576;0 0 1, 0 576 1, 192 576 1, 192 0 1, 128 64 1, 128 512 1, 64 512 1, 64 64 1 +0 0 0 0 0; +192 0 0 0 0; +192 0 0 0 0; +192 64 0 128 576;64 0 1, 64 64 1, 128 64 1, 128 0 1, 72 128 1, 64 454 1, 64 576 1, 128 576 1, 128 454 1, 120 128 1 +256 0 384 256 576;44 384 1, 35 576 1, 108 576 1, 99 384 1, 173 384 1, 164 576 1, 238 576 1, 229 384 1 +448 0 0 448 576;47 25 1, 89 192 1, 9 192 1, 18 256 1, 100 256 1, 133 320 1, 44 320 1, 54 384 1, 144 384 1, 186 551 1, 234 551 1, 192 384 1, 291 384 1, 332 551 1, 380 551 1, 339 384 1, 418 384 1, 409 320 1, 327 320 1, 294 256 1, 383 256 1, 374 192 1, 283 192 1, 242 25 1, 194 25 1, 235 192 1, 137 192 1, 95 25 1, 148 256 1, 247 256 1, 279 320 1, 180 320 1 +448 64 -64 320 640;192 -46 1, 192 0 1, 135 0 0, 64 31 1, 64 95 1, 137 56 0, 192 56 1, 192 255 1, 124 298 0, 96 331 1, 64 368 0, 64 422 1, 64 486 0, 110 524 1, 142 550 0, 192 555 1, 192 602 1, 256 602 1, 256 555 1, 285 555 0, 320 530 1, 320 470 1, 282 500 0, 256 504 1, 256 307 1, 258 304 1, 263 298 0, 267 293 1, 270 290 1, 293 262 0, 305 237 1, 320 205 0, 320 155 1, 320 87 0, 297 42 1, 282 12 0, 256 0 1, 256 -46 1, 256 60 1, 256 85 0, 256 144 1, 256 175 0, 256 195 1, 256 210 0, 256 233 1, 192 331 1, 192 502 1, 128 479 0, 128 425 1, 128 376 0 +704 64 0 640 576;94 0 1, 531 576 1, 589 576 1, 152 0 1, 160 576 1, 204 576 0, 230 542 1, 256 507 0, 256 448 1, 256 389 0, 230 355 1, 204 320 0, 160 320 1, 115 320 0, 90 355 1, 64 389 0, 64 450 1, 64 502 0, 85 535 1, 112 576 0, 160 512 1, 146 512 0, 137 495 1, 128 477 0, 128 449 1, 128 422 0, 135 405 1, 144 384 0, 160 384 1, 174 384 0, 183 402 1, 192 419 0, 192 448 1, 192 477 0, 183 494 1, 174 512 0, 512 320 1, 571 320 0, 606 277 1, 640 234 0, 640 160 1, 640 86 0, 606 43 1, 571 0 0, 512 0 1, 453 0 0, 418 43 1, 384 86 0, 384 162 1, 384 228 0, 412 269 1, 448 320 0, 512 256 1, 483 256 0, 466 230 1, 448 203 0, 448 162 1, 448 122 0, 462 96 1, 480 64 0, 512 64 1, 541 64 0, 558 91 1, 576 117 0, 576 160 1, 576 203 0, 558 229 1, 541 256 0 +512 0 0 512 576;384 0 1, 357 35 1, 277 0 0, 205 0 1, 118 0 0, 59 52 1, 0 105 0, 0 185 1, 0 264 0, 56 313 1, 89 341 0, 152 363 1, 128 420 0, 128 461 1, 128 513 0, 163 545 1, 199 576 0, 260 576 1, 317 576 0, 351 548 1, 384 519 0, 384 472 1, 384 419 0, 335 381 1, 305 358 0, 248 337 1, 311 214 0, 373 133 1, 410 185 0, 410 287 1, 410 320 1, 483 320 1, 483 179 0, 408 90 1, 441 44 0, 485 0 1, 325 82 1, 251 173 0, 178 321 1, 127 303 0, 102 281 1, 64 249 0, 64 203 1, 64 145 0, 107 104 1, 151 64 0, 212 64 1, 260 64 0, 220 383 1, 266 396 0, 288 411 1, 320 433 0, 320 464 1, 320 512 0, 258 512 1, 192 512 0, 192 461 1, 192 431 0, 217 388 1 +128 0 384 128 576;45 384 1, 27 576 1, 120 576 1, 101 384 1 +256 64 -128 256 640;225 -60 1, 225 -111 1, 156 -58 0, 117 21 1, 64 123 0, 64 241 1, 64 364 0, 121 469 1, 160 541 0, 225 592 1, 225 541 1, 177 485 0, 155 426 1, 128 353 0, 128 241 1, 128 124 0, 158 48 1, 180 -7 0 +256 0 -128 192 640;31 541 1, 31 592 1, 100 539 0, 140 460 1, 192 358 0, 192 241 1, 192 117 0, 135 12 1, 96 -60 0, 31 -111 1, 31 -60 1, 79 -3 0, 101 55 1, 128 129 0, 128 241 1, 128 357 0, 98 433 1, 77 487 0 +320 0 256 320 576;267 483 1, 284 415 1, 180 383 1, 180 384 1, 180 389 0, 192 390 1, 192 390 1, 192 411 0, 170 423 1, 255 296 1, 210 266 1, 152 360 1, 171 362 0, 178 378 1, 88 266 1, 43 296 1, 120 378 1, 128 362 0, 147 360 1, 15 415 1, 32 483 1, 129 423 1, 118 410 0, 118 389 1, 118 389 1, 118 389 0, 118 387 1, 119 386 1, 119 384 0, 119 383 1, 122 576 1, 177 576 1, 165 427 1, 157 448 0, 149 448 1, 140 448 0, 133 427 1 +448 64 64 384 384;192 64 1, 192 192 1, 64 192 1, 64 256 1, 192 256 1, 192 384 1, 256 384 1, 256 256 1, 384 256 1, 384 192 1, 256 192 1, 256 64 1 +192 64 -192 128 64;64 -149 1, 64 -121 1, 89 -111 0, 89 -36 1, 89 -29 1, 64 -29 1, 64 64 1, 128 64 1, 128 -16 1, 128 -139 0 +256 64 192 192 256;64 192 1, 64 256 1, 192 256 1, 192 192 1 +192 64 0 128 64;64 0 1, 64 64 1, 128 64 1, 128 0 1 +192 -64 -128 256 576;-22 -128 1, 178 576 1, 236 576 1, 36 -128 1 +448 0 0 384 576;192 576 1, 280 576 0, 332 499 1, 384 422 0, 384 289 1, 384 154 0, 332 77 1, 280 0 0, 190 0 1, 112 0 0, 63 63 1, 0 142 0, 0 289 1, 0 422 0, 52 499 1, 104 576 0, 192 512 1, 130 512 0, 97 454 1, 64 395 0, 64 288 1, 64 183 0, 97 123 1, 130 64 0, 192 64 1, 248 64 0, 279 107 1, 320 165 0, 320 289 1, 320 397 0, 287 454 1, 252 512 0 +448 64 0 384 640;64 0 1, 64 64 1, 192 64 1, 192 502 1, 64 474 1, 64 531 1, 256 577 1, 256 64 1, 384 64 1, 384 0 1 +448 64 0 320 576;64 0 1, 64 64 1, 85 124 0, 134 187 1, 167 228 1, 197 266 1, 256 339 0, 256 414 1, 256 466 0, 232 491 1, 213 512 0, 178 512 1, 133 512 0, 64 486 1, 64 554 1, 129 576 0, 185 576 1, 247 576 0, 283 533 1, 320 490 0, 320 418 1, 320 368 0, 301 330 1, 282 290 0, 230 234 1, 208 211 1, 142 139 0, 129 64 1, 320 64 1, 320 0 1 +448 64 0 384 576;64 4 1, 64 76 1, 67 76 1, 78 74 0, 83 74 1, 129 68 0, 146 66 1, 173 64 0, 196 64 1, 260 64 0, 292 94 1, 320 119 0, 320 167 1, 320 222 0, 272 251 1, 225 280 0, 137 280 1, 108 280 1, 108 332 1, 131 332 1, 191 333 0, 223 360 1, 256 387 0, 256 435 1, 256 512 0, 172 512 1, 129 512 0, 64 500 1, 64 568 1, 126 576 0, 174 576 1, 257 576 0, 293 534 1, 320 502 0, 320 450 1, 320 391 0, 287 353 1, 267 331 0, 228 312 1, 278 301 0, 303 288 1, 384 248 0, 384 165 1, 384 90 0, 332 45 1, 279 0 0, 194 0 1, 148 0 0 +448 0 0 384 576;256 0 1, 256 128 1, 0 128 1, 0 187 1, 256 576 1, 320 576 1, 320 192 1, 384 192 1, 384 128 1, 320 128 1, 320 0 1, 75 192 1, 256 192 1, 256 473 1 +448 64 -64 320 576;64 -2 1, 64 67 1, 113 64 0, 154 64 1, 202 64 0, 229 98 1, 256 132 0, 256 189 1, 256 320 0, 102 320 1, 83 320 0, 64 297 1, 64 576 1, 320 576 1, 320 512 1, 128 512 1, 128 359 1, 212 357 0, 259 319 1, 320 269 0, 320 173 1, 320 92 0, 274 46 1, 227 0 0, 146 0 1, 111 0 0 +448 64 0 384 576;141 303 1, 187 384 0, 253 384 1, 314 384 0, 349 336 1, 384 288 0, 384 203 1, 384 110 0, 342 55 1, 300 0 0, 229 0 1, 152 0 0, 108 72 1, 64 144 0, 64 270 1, 64 414 0, 121 495 1, 179 576 0, 281 576 1, 327 576 0, 384 569 1, 384 502 1, 316 512 0, 278 512 1, 195 512 0, 162 434 1, 149 403 0, 144 364 1, 142 344 0, 229 320 1, 184 320 0, 156 288 1, 128 257 0, 128 203 1, 128 143 0, 157 103 1, 185 64 0, 231 64 1, 320 64 0, 320 187 1, 320 320 0 +448 64 0 384 576;93 0 1, 103 68 0, 121 118 1, 139 168 0, 185 255 1, 321 512 1, 64 512 1, 64 576 1, 384 576 1, 384 512 1, 192 180 0, 169 0 1 +448 64 0 384 576;147 313 1, 110 339 0, 91 363 1, 64 398 0, 64 438 1, 64 499 0, 110 537 1, 156 576 0, 230 576 1, 299 576 0, 342 543 1, 384 511 0, 384 457 1, 384 410 0, 347 367 1, 325 341 0, 283 313 1, 328 284 0, 351 254 1, 384 211 0, 384 155 1, 384 87 0, 338 43 1, 293 0 0, 221 0 1, 150 0 0, 107 41 1, 64 82 0, 64 150 1, 64 210 0, 94 257 1, 113 285 0, 242 338 1, 320 387 0, 320 441 1, 320 473 0, 294 493 1, 267 512 0, 222 512 1, 180 512 0, 154 494 1, 128 476 0, 128 445 1, 128 409 0, 173 378 1, 195 362 0, 188 282 1, 156 252 0, 143 229 1, 128 204 0, 128 165 1, 128 120 0, 155 92 1, 181 64 0, 225 64 1, 267 64 0, 293 88 1, 320 111 0, 320 149 1, 320 183 0, 298 206 1, 280 225 0, 236 253 1 +448 64 0 384 576;308 273 1, 262 192 0, 195 192 1, 134 192 0, 99 240 1, 64 288 0, 64 373 1, 64 466 0, 106 521 1, 148 576 0, 218 576 1, 296 576 0, 340 504 1, 384 432 0, 384 306 1, 384 162 0, 327 81 1, 269 0 0, 168 0 1, 120 0 0, 64 7 1, 64 74 1, 133 64 0, 171 64 1, 254 64 0, 287 143 1, 300 173 0, 305 212 1, 307 232 0, 217 512 1, 128 512 0, 128 390 1, 128 256 0, 219 256 1, 264 256 0, 292 288 1, 320 320 0, 320 374 1, 320 433 0, 291 473 1, 262 512 0 +192 64 0 128 384;64 0 1, 64 64 1, 128 64 1, 128 0 1, 64 320 1, 64 384 1, 128 384 1, 128 320 1 +192 64 -128 128 384;64 -120 1, 64 -93 1, 87 -79 0, 87 -9 1, 87 0 1, 64 0 1, 64 64 1, 128 64 1, 128 10 1, 128 -102 0, 64 320 1, 64 384 1, 128 384 1, 128 320 1 +448 0 0 384 448;384 37 1, 14 222 1, 384 407 1, 384 345 1, 139 222 1, 139 222 1, 384 99 1 +448 64 128 384 320;64 128 1, 64 192 1, 384 192 1, 384 128 1, 64 256 1, 64 320 1, 384 320 1, 384 256 1 +448 64 0 448 448;64 407 1, 434 222 1, 64 37 1, 64 99 1, 309 222 1, 309 222 1, 64 345 1 +448 64 0 384 576;128 0 1, 128 64 1, 192 64 1, 192 0 1, 128 128 1, 128 150 1, 128 246 0, 201 297 1, 241 324 1, 320 377 0, 320 435 1, 320 512 0, 215 512 1, 150 512 0, 64 500 1, 64 568 1, 145 576 0, 213 576 1, 287 576 0, 330 550 1, 384 515 0, 384 441 1, 384 366 0, 301 319 1, 266 299 1, 222 275 0, 207 248 1, 192 222 0, 192 173 1, 192 128 1 +768 64 0 704 576;470 17 1, 394 0 0, 325 0 1, 213 0 0, 138 64 1, 64 129 0, 64 229 1, 64 365 0, 177 471 1, 291 576 0, 439 576 1, 553 576 0, 628 510 1, 704 444 0, 704 346 1, 704 255 0, 647 191 1, 591 128 0, 511 128 1, 448 128 0, 448 167 1, 448 180 0, 451 202 1, 458 245 1, 453 245 1, 424 192 0, 398 166 1, 361 128 0, 320 128 1, 256 128 0, 256 210 1, 256 301 0, 312 374 1, 368 448 0, 439 448 1, 447 448 0, 461 448 1, 465 448 0, 469 448 1, 483 448 0, 492 448 1, 543 448 1, 513 235 1, 512 225 0, 512 215 1, 512 192 0, 536 192 1, 576 192 0, 608 237 1, 640 283 0, 640 341 1, 640 415 0, 581 463 1, 521 512 0, 430 512 1, 312 512 0, 220 428 1, 128 344 0, 128 238 1, 128 160 0, 188 112 1, 247 64 0, 339 64 1, 398 64 0, 456 56 1, 462 318 1, 477 393 1, 447 384 0, 426 384 1, 380 384 0, 350 341 1, 320 298 0, 320 237 1, 320 192 0, 345 192 1, 387 192 0 +512 0 0 512 576;7 0 1, 218 576 1, 296 576 1, 503 0 1, 419 0 1, 361 128 1, 138 128 1, 80 0 1, 161 192 1, 339 192 1, 250 458 1 +512 64 0 448 576;64 0 1, 64 576 1, 227 576 1, 348 576 0, 398 546 1, 448 516 0, 448 443 1, 448 377 0, 396 336 1, 364 311 0, 304 292 1, 369 272 0, 401 244 1, 448 204 0, 448 140 1, 448 80 0, 410 41 1, 383 13 0, 340 6 1, 305 0 0, 247 0 1, 128 64 1, 186 64 1, 308 64 0, 346 80 1, 384 95 0, 384 144 1, 384 199 0, 335 227 1, 285 256 0, 192 256 1, 128 256 1, 128 320 1, 195 320 1, 384 320 0, 384 430 1, 384 486 0, 330 501 1, 288 512 0, 201 512 1, 128 512 1 +576 64 0 512 576;512 30 1, 429 0 0, 334 0 1, 202 0 0, 133 73 1, 64 147 0, 64 287 1, 64 427 0, 134 502 1, 205 576 0, 338 576 1, 413 576 0, 512 566 1, 512 489 1, 394 512 0, 326 512 1, 230 512 0, 179 454 1, 128 396 0, 128 287 1, 128 180 0, 182 122 1, 237 64 0, 335 64 1, 417 64 0, 512 100 1 +576 64 0 512 576;64 0 1, 64 576 1, 227 576 1, 512 576 0, 512 301 1, 512 158 0, 438 79 1, 364 0 0, 228 0 1, 128 64 1, 223 64 1, 448 64 0, 448 292 1, 448 426 0, 360 481 1, 335 497 0, 299 504 1, 257 512 0, 186 512 1, 128 512 1 +512 64 0 512 576;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1 +448 64 0 448 576;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 384 320 1, 384 256 1, 128 256 1, 128 0 1 +576 64 0 512 576;512 256 1, 512 14 1, 418 0 0, 330 0 1, 64 0 0, 64 286 1, 64 426 0, 133 501 1, 202 576 0, 332 576 1, 416 576 0, 512 564 1, 512 485 1, 398 512 0, 323 512 1, 128 512 0, 128 289 1, 128 180 0, 183 122 1, 238 64 0, 340 64 1, 382 64 0, 448 57 1, 448 192 1, 384 192 1, 384 256 1 +576 64 0 512 576;64 0 1, 64 576 1, 128 576 1, 128 320 1, 448 320 1, 448 576 1, 512 576 1, 512 0 1, 448 0 1, 448 256 1, 128 256 1, 128 0 1 +192 64 0 128 576;64 0 1, 64 576 1, 128 576 1, 128 0 1 +384 0 -128 320 576;0 -87 1, 0 -19 1, 75 -64 0, 141 -64 1, 216 -64 0, 238 -29 1, 256 0 0, 256 71 1, 256 576 1, 320 576 1, 320 73 1, 320 -128 0, 125 -128 1, 60 -128 0 +512 64 0 512 576;64 0 1, 64 576 1, 128 576 1, 128 293 1, 359 576 1, 438 576 1, 214 301 1, 476 0 1, 377 0 1, 128 292 1, 128 0 1 +448 64 0 384 576;64 0 1, 64 576 1, 128 576 1, 128 64 1, 384 64 1, 384 0 1 +640 64 0 576 576;64 0 1, 64 576 1, 170 576 1, 327 151 1, 487 576 1, 576 576 1, 576 0 1, 512 0 1, 512 473 1, 357 64 1, 279 64 1, 128 475 1, 128 0 1 +576 64 0 512 576;64 0 1, 64 576 1, 138 576 1, 448 131 1, 448 576 1, 512 576 1, 512 0 1, 437 0 1, 128 445 1, 128 0 1 +576 64 0 576 576;320 576 1, 436 576 0, 506 498 1, 576 419 0, 576 289 1, 576 156 0, 506 78 1, 436 0 0, 316 0 1, 214 0 0, 147 64 1, 64 145 0, 64 288 1, 64 420 0, 134 498 1, 204 576 0, 320 512 1, 229 512 0, 179 453 1, 128 394 0, 128 288 1, 128 183 0, 179 124 1, 229 64 0, 318 64 1, 401 64 0, 450 112 1, 512 171 0, 512 289 1, 512 394 0, 461 453 1, 410 512 0 +512 64 0 512 576;64 0 1, 64 576 1, 267 576 1, 365 576 0, 408 565 1, 451 553 0, 478 520 1, 512 479 0, 512 408 1, 512 192 0, 244 192 1, 128 192 1, 128 0 1, 128 256 1, 240 256 1, 448 256 0, 448 402 1, 448 473 0, 394 494 1, 348 512 0, 242 512 1, 128 512 1 +576 64 -128 640 576;615 -48 1, 565 -111 1, 434 -68 0, 346 -10 1, 311 0 0, 293 0 1, 193 0 0, 128 81 1, 64 161 0, 64 289 1, 64 419 0, 133 497 1, 203 576 0, 319 576 1, 436 576 0, 506 495 1, 576 415 0, 576 280 1, 576 162 0, 517 87 1, 494 58 0, 464 38 1, 449 27 0, 418 11 1, 510 -30 0, 318 512 1, 229 512 0, 179 452 1, 128 393 0, 128 288 1, 128 183 0, 179 124 1, 229 64 0, 318 64 1, 409 64 0, 460 123 1, 512 181 0, 512 286 1, 512 383 0, 471 441 1, 420 512 0 +576 64 0 576 576;64 0 1, 64 576 1, 281 576 1, 448 576 0, 448 439 1, 448 372 0, 408 329 1, 384 303 0, 340 283 1, 525 0 1, 428 0 1, 271 256 1, 128 256 1, 128 0 1, 128 320 1, 216 320 1, 303 320 0, 343 346 1, 384 373 0, 384 429 1, 384 474 0, 351 493 1, 318 512 0, 241 512 1, 128 512 1 +512 64 0 512 576;64 21 1, 64 102 1, 189 64 0, 311 64 1, 448 64 0, 448 152 1, 448 197 0, 410 218 1, 381 235 0, 315 253 1, 229 278 1, 64 324 0, 64 431 1, 64 576 0, 267 576 1, 355 576 0, 448 566 1, 448 491 1, 347 512 0, 255 512 1, 128 512 0, 128 431 1, 128 399 0, 154 379 1, 180 359 0, 247 340 1, 334 316 1, 432 288 0, 472 252 1, 512 216 0, 512 156 1, 512 84 0, 454 42 1, 396 0 0, 294 0 1, 193 0 0 +448 0 0 448 576;192 0 1, 192 512 1, 0 512 1, 0 576 1, 448 576 1, 448 512 1, 256 512 1, 256 0 1 +576 64 0 512 576;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1 +512 0 0 512 576;228 0 1, 14 576 1, 95 576 1, 272 103 1, 441 576 1, 508 576 1, 302 0 1 +704 0 0 768 576;152 0 1, 9 576 1, 85 576 1, 199 121 1, 329 576 1, 405 576 1, 530 125 1, 651 576 1, 716 576 1, 560 0 1, 482 0 1, 358 444 1, 230 0 1 +512 0 0 512 576;11 0 1, 215 286 1, 20 576 1, 113 576 1, 263 352 1, 423 576 1, 498 576 1, 299 300 1, 502 0 1, 409 0 1, 251 233 1, 85 0 1 +512 -64 0 512 576;192 0 1, 192 240 1, -7 576 1, 83 576 1, 232 309 1, 395 576 1, 468 576 1, 256 242 1, 256 0 1 +448 64 0 448 576;64 0 1, 64 64 1, 351 512 1, 64 512 1, 64 576 1, 448 576 1, 448 512 1, 142 64 1, 448 64 1, 448 0 1 +192 64 -128 192 576;64 -128 1, 64 576 1, 192 576 1, 192 512 1, 128 512 1, 128 -64 1, 192 -64 1, 192 -128 1 +192 -64 -128 256 576;236 -128 1, 178 -128 1, -22 576 1, 36 576 1 +192 0 -128 128 576;128 576 1, 128 -128 1, 0 -128 1, 0 -64 1, 64 -64 1, 64 512 1, 0 512 1, 0 576 1 +384 0 192 384 576;180 401 1, 75 192 1, 14 192 1, 180 525 1, 347 192 1, 284 192 1 +448 0 -64 448 0;0 -64 1, 0 0 1, 448 0 1, 448 -64 1 +256 0 512 256 576;216 512 1, 160 512 1, 40 576 1, 125 576 1 +448 0 0 448 384;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0 +448 64 -64 384 576;128 249 1, 128 53 1, 182 64 0, 209 64 1, 320 64 0, 320 199 1, 320 256 0, 298 288 1, 276 320 0, 239 320 1, 190 320 0, 128 312 1, 148 344 0, 171 361 1, 204 384 0, 247 384 1, 308 384 0, 346 334 1, 384 285 0, 384 202 1, 384 106 0, 335 53 1, 286 0 0, 197 0 1, 164 0 0, 128 0 1, 64 -5 1, 64 576 1, 128 576 1 +384 0 0 320 384;320 11 1, 251 0 0, 190 0 1, 104 0 0, 52 53 1, 0 107 0, 0 192 1, 0 282 0, 54 333 1, 108 384 0, 205 384 1, 254 384 0, 320 380 1, 320 322 1, 251 320 0, 209 320 1, 64 320 0, 64 191 1, 64 130 0, 101 97 1, 137 64 0, 203 64 1, 253 64 0, 320 72 1 +448 64 0 384 576;320 135 1, 320 331 1, 265 320 0, 239 320 1, 128 320 0, 128 185 1, 128 129 0, 150 96 1, 172 64 0, 209 64 1, 258 64 0, 320 72 1, 300 40 0, 277 23 1, 245 0 0, 201 0 1, 140 0 0, 102 50 1, 64 100 0, 64 182 1, 64 278 0, 113 331 1, 162 384 0, 251 384 1, 285 384 0, 320 384 1, 320 576 1, 384 576 1, 384 0 1, 320 0 1 +448 64 0 448 384;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0 +192 0 0 256 576;64 0 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 433 1, 64 576 0, 194 576 1, 221 576 0, 256 569 1, 256 510 1, 219 512 0, 193 512 1, 157 512 0, 142 496 1, 128 481 0, 128 441 1, 128 384 1, 192 384 1, 192 320 1, 128 320 1, 128 0 1 +448 64 -192 384 384;320 153 1, 320 331 1, 265 320 0, 240 320 1, 128 320 0, 128 190 1, 128 132 0, 150 98 1, 172 64 0, 209 64 1, 258 64 0, 320 90 1, 300 50 0, 277 29 1, 245 0 0, 202 0 1, 140 0 0, 102 52 1, 64 103 0, 64 186 1, 64 280 0, 113 332 1, 162 384 0, 250 384 1, 285 384 0, 320 384 1, 384 384 1, 384 105 1, 384 22 0, 374 -18 1, 348 -128 0, 194 -128 1, 130 -128 0, 64 -135 1, 64 -71 1, 143 -64 0, 198 -64 1, 320 -64 0, 320 29 1 +448 64 0 384 576;64 0 1, 64 576 1, 128 576 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 0 1, 320 0 1, 320 254 1, 320 293 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1 +192 64 0 128 576;64 0 1, 64 384 1, 128 384 1, 128 0 1, 64 512 1, 64 576 1, 128 576 1, 128 512 1 +192 -64 -192 128 576;-64 -145 1, -64 -87 1, -28 -64 0, 3 -64 1, 46 -64 0, 56 -47 1, 64 -32 0, 64 0 1, 64 384 1, 128 384 1, 128 0 1, 128 -128 0, 2 -128 1, -33 -128 0, 64 512 1, 64 576 1, 128 576 1, 128 512 1 +384 64 0 384 576;64 0 1, 64 576 1, 128 576 1, 128 198 1, 265 384 1, 335 384 1, 205 203 1, 374 0 1, 284 0 1, 128 197 1, 128 0 1 +192 64 0 128 576;64 0 1, 64 576 1, 128 576 1, 128 0 1 +640 64 0 576 384;64 0 1, 64 384 1, 128 384 1, 128 312 1, 156 353 0, 173 368 1, 194 384 0, 227 384 1, 268 384 0, 294 357 1, 309 342 0, 320 312 1, 357 354 0, 380 368 1, 408 384 0, 453 384 1, 576 384 0, 576 279 1, 576 0 1, 493 0 1, 493 257 1, 493 320 0, 432 320 1, 378 320 0, 320 257 1, 320 0 1, 256 0 1, 256 268 1, 256 320 0, 211 320 1, 171 320 0, 128 257 1, 128 0 1 +448 64 0 384 384;64 0 1, 64 384 1, 128 384 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 0 1, 320 0 1, 320 253 1, 320 292 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1 +448 64 0 384 384;224 384 1, 298 384 0, 341 333 1, 384 281 0, 384 193 1, 384 102 0, 341 51 1, 298 0 0, 222 0 1, 156 0 0, 116 42 1, 64 95 0, 64 192 1, 64 281 0, 107 333 1, 150 384 0, 224 320 1, 128 320 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 193 1, 320 320 0 +448 64 -128 384 384;128 -128 1, 64 -128 1, 64 384 1, 128 384 1, 128 312 1, 148 344 0, 171 361 1, 203 384 0, 247 384 1, 308 384 0, 346 334 1, 384 285 0, 384 202 1, 384 106 0, 335 53 1, 286 0 0, 197 0 1, 164 0 0, 128 0 1, 128 249 1, 128 53 1, 182 64 0, 209 64 1, 320 64 0, 320 199 1, 320 256 0, 298 288 1, 276 320 0, 239 320 1, 190 320 0 +448 64 -128 384 384;320 384 1, 384 384 1, 384 -128 1, 320 -128 1, 320 72 1, 300 40 0, 277 23 1, 245 0 0, 201 0 1, 140 0 0, 102 50 1, 64 100 0, 64 182 1, 64 278 0, 113 331 1, 162 384 0, 251 384 1, 285 384 0, 320 135 1, 320 331 1, 265 320 0, 239 320 1, 128 320 0, 128 185 1, 128 129 0, 150 96 1, 172 64 0, 209 64 1, 258 64 0 +256 64 0 256 448;64 0 1, 64 384 1, 128 384 1, 128 312 1, 145 345 0, 165 361 1, 194 384 0, 233 384 1, 241 384 0, 256 391 1, 256 326 1, 235 320 0, 222 320 1, 178 320 0, 128 253 1, 128 0 1 +384 64 0 320 384;64 13 1, 64 77 1, 128 64 0, 183 64 1, 256 64 0, 256 116 1, 256 152 0, 204 168 1, 147 187 1, 64 214 0, 64 286 1, 64 384 0, 215 384 1, 258 384 0, 320 381 1, 320 323 1, 263 320 0, 206 320 1, 128 320 0, 128 276 1, 128 244 0, 174 230 1, 225 213 1, 320 182 0, 320 106 1, 320 57 0, 283 29 1, 245 0 0, 180 0 1, 129 0 0 +192 0 -64 192 512;192 -2 1, 172 0 0, 154 0 1, 64 0 0, 64 103 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 458 1, 128 465 1, 128 384 1, 192 384 1, 192 320 1, 128 320 1, 128 115 1, 128 84 0, 136 74 1, 144 64 0, 168 64 1, 182 64 0, 192 45 1 +448 64 0 384 384;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1 +384 0 0 384 384;152 0 1, 7 384 1, 82 384 1, 195 85 1, 314 384 1, 380 384 1, 225 0 1 +576 0 0 576 384;102 0 1, 4 384 1, 77 384 1, 150 95 1, 244 384 1, 318 384 1, 400 94 1, 486 384 1, 549 384 1, 435 0 1, 361 0 1, 275 297 1, 177 0 1 +384 0 0 384 384;11 0 1, 143 203 1, 15 384 1, 101 384 1, 203 240 1, 294 384 1, 362 384 1, 238 191 1, 372 0 1, 287 0 1, 177 154 1, 79 0 1 +384 0 -128 384 384;152 0 1, 7 384 1, 82 384 1, 193 90 1, 314 384 1, 380 384 1, 164 -128 1, 87 -128 1 +384 0 0 384 384;0 0 1, 0 64 1, 291 320 1, 64 320 1, 64 384 1, 384 384 1, 384 320 1, 145 64 1, 384 64 1, 384 0 1 +256 0 -128 192 640;0 269 1, 22 269 1, 64 269 0, 64 330 1, 64 354 0, 64 382 1, 64 414 1, 64 447 0, 64 476 1, 64 537 0, 109 569 1, 141 591 0, 192 592 1, 192 537 1, 173 537 1, 153 537 0, 141 524 1, 128 510 0, 128 490 1, 128 483 0, 128 455 1, 128 417 1, 128 391 0, 128 361 1, 128 290 0, 80 241 1, 128 192 0, 128 120 1, 128 90 0, 128 65 1, 128 26 1, 128 -1 0, 128 -9 1, 128 -29 0, 141 -43 1, 154 -56 0, 173 -56 1, 192 -56 1, 192 -111 1, 139 -110 0, 106 -85 1, 64 -52 0, 64 6 1, 64 35 0, 64 67 1, 64 100 1, 64 127 0, 64 152 1, 64 213 0, 22 213 1, 0 213 1 +192 64 -128 128 576;64 -128 1, 64 576 1, 128 576 1, 128 -128 1 +256 64 -128 256 640;256 213 1, 234 213 1, 192 213 0, 192 152 1, 192 124 0, 192 100 1, 192 67 1, 192 36 0, 192 6 1, 192 -55 0, 146 -88 1, 114 -110 0, 64 -111 1, 64 -56 1, 82 -56 1, 102 -56 0, 115 -43 1, 128 -29 0, 128 -9 1, 128 1 0, 128 26 1, 128 65 1, 128 88 0, 128 120 1, 128 192 0, 176 241 1, 154 263 0, 144 285 1, 128 318 0, 128 361 1, 128 393 0, 128 417 1, 128 455 1, 128 480 0, 128 491 1, 128 510 0, 115 524 1, 102 537 0, 82 537 1, 64 537 1, 64 592 1, 117 591 0, 150 566 1, 192 534 0, 192 475 1, 192 445 0, 192 414 1, 192 382 1, 192 357 0, 192 329 1, 192 269 0, 234 269 1, 256 269 1 +448 0 192 448 256;95 192 1, 39 192 1, 40 213 0, 47 223 1, 69 256 0, 139 256 1, 176 256 0, 214 256 1, 256 256 1, 280 256 1, 291 256 0, 309 256 1, 352 256 0, 354 256 1, 409 256 1, 408 235 0, 401 225 1, 379 192 0, 310 192 1, 273 192 0, 235 192 1, 193 192 1, 168 192 1, 158 192 0, 140 192 1, 96 192 0 +512 -64 0 576 640;-15 0 1, 196 576 1, 316 576 1, 524 0 1, 439 0 1, 381 128 1, 116 128 1, 58 0 1, 138 192 1, 360 192 1, 246 458 1, 128 576 1, 128 640 1, 192 640 1, 192 576 1, 320 576 1, 320 640 1, 384 640 1, 384 576 1 +512 0 0 512 768;7 0 1, 218 576 1, 296 576 1, 503 0 1, 419 0 1, 361 128 1, 138 128 1, 80 0 1, 161 192 1, 339 192 1, 250 458 1, 256 768 1, 283 768 0, 301 740 1, 320 712 0, 320 672 1, 320 632 0, 301 604 1, 282 576 0, 255 576 1, 232 576 0, 215 599 1, 192 628 0, 192 672 1, 192 712 0, 211 740 1, 229 768 0, 256 704 1, 256 704 0, 256 695 1, 256 686 0, 256 672 1, 256 659 0, 256 650 1, 256 640 0, 256 640 1, 256 640 0, 256 648 1, 256 657 0, 256 672 1, 256 686 0, 256 695 1, 256 704 0 +576 64 -192 512 576;512 30 1, 429 0 0, 334 0 1, 202 0 0, 133 73 1, 64 147 0, 64 287 1, 64 427 0, 134 502 1, 205 576 0, 338 576 1, 413 576 0, 512 566 1, 512 489 1, 394 512 0, 326 512 1, 230 512 0, 179 454 1, 128 396 0, 128 287 1, 128 180 0, 182 122 1, 237 64 0, 335 64 1, 417 64 0, 512 100 1, 293 0 1, 330 0 1, 307 -41 1, 336 -42 0, 356 -59 1, 384 -82 0, 384 -116 1, 384 -148 0, 361 -170 1, 338 -192 0, 306 -192 1, 280 -192 0, 250 -154 1, 250 -124 1, 267 -128 0, 285 -128 1, 320 -128 0, 320 -101 1, 320 -67 0, 258 -66 1 +512 64 0 512 704;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1, 211 576 1, 311 704 1, 407 704 1, 273 576 1 +576 64 0 512 768;64 0 1, 64 576 1, 138 576 1, 448 131 1, 448 576 1, 512 576 1, 512 0 1, 437 0 1, 128 445 1, 128 0 1, 163 623 1, 166 656 0, 176 673 1, 195 704 0, 239 704 1, 268 704 0, 293 704 1, 318 704 1, 341 704 0, 353 704 1, 380 704 0, 384 710 1, 434 710 1, 431 682 0, 421 667 1, 403 640 0, 359 640 1, 329 640 0, 304 640 1, 279 640 1, 257 640 0, 244 640 1, 217 640 0, 213 623 1 +576 64 0 512 640;288 576 1, 389 576 0, 451 498 1, 512 419 0, 512 289 1, 512 156 0, 451 78 1, 389 0 0, 285 0 1, 195 0 0, 137 64 1, 64 145 0, 64 288 1, 64 420 0, 125 498 1, 186 576 0, 288 512 1, 212 512 0, 170 453 1, 128 394 0, 128 288 1, 128 183 0, 170 124 1, 212 64 0, 286 64 1, 355 64 0, 397 112 1, 448 171 0, 448 289 1, 448 394 0, 406 453 1, 363 512 0, 192 576 1, 192 640 1, 256 640 1, 256 576 1, 320 576 1, 320 640 1, 384 640 1, 384 576 1 +576 64 0 512 640;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 192 576 1, 192 640 1, 256 640 1, 256 576 1, 320 576 1, 320 640 1, 384 640 1, 384 576 1 +448 0 0 448 576;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0, 168 512 1, 259 576 1, 344 576 1, 224 512 1 +448 0 0 448 576;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0, 280 512 1, 224 512 1, 104 576 1, 189 576 1 +448 0 0 448 576;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0, 61 512 1, 151 576 1, 233 576 1, 323 512 1, 268 512 1, 192 552 1, 192 552 1, 116 512 1 +448 0 0 448 576;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0, 64 512 1, 64 576 1, 128 576 1, 128 512 1, 256 512 1, 256 576 1, 320 576 1, 320 512 1 +448 0 0 448 640;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0, 67 502 1, 70 531 0, 79 548 1, 96 576 0, 137 576 1, 164 576 0, 187 576 1, 210 576 1, 231 576 0, 242 576 1, 267 576 0, 271 590 1, 317 590 1, 314 559 0, 305 542 1, 288 512 0, 247 512 1, 220 512 0, 197 512 1, 174 512 1, 154 512 0, 142 512 1, 117 512 0, 113 502 1 +448 0 0 448 640;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0, 192 640 1, 219 640 0, 237 621 1, 256 603 0, 256 576 1, 256 549 0, 237 531 1, 219 512 0, 191 512 1, 168 512 0, 151 527 1, 128 547 0, 128 576 1, 128 603 0, 147 621 1, 165 640 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0 +384 0 -192 320 384;320 11 1, 251 0 0, 190 0 1, 104 0 0, 52 53 1, 0 107 0, 0 192 1, 0 282 0, 54 333 1, 108 384 0, 205 384 1, 254 384 0, 320 380 1, 320 322 1, 251 320 0, 209 320 1, 64 320 0, 64 191 1, 64 130 0, 101 97 1, 137 64 0, 203 64 1, 253 64 0, 320 72 1, 235 0 1, 271 0 1, 248 -41 1, 275 -42 0, 294 -59 1, 320 -82 0, 320 -116 1, 320 -148 0, 298 -170 1, 277 -192 0, 244 -192 1, 219 -192 0, 191 -154 1, 191 -124 1, 207 -128 0, 224 -128 1, 256 -128 0, 256 -101 1, 256 -67 0, 199 -66 1 +448 64 0 448 576;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0, 168 512 1, 259 576 1, 344 576 1, 224 512 1 +448 64 0 448 576;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0, 280 512 1, 224 512 1, 104 576 1, 189 576 1 +448 0 0 448 576;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0, 61 512 1, 151 576 1, 233 576 1, 323 512 1, 268 512 1, 192 552 1, 192 552 1, 116 512 1 +448 64 0 448 576;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0, 64 512 1, 64 576 1, 128 576 1, 128 512 1, 256 512 1, 256 576 1, 320 576 1, 320 512 1 +192 0 0 256 576;64 0 1, 64 384 1, 128 384 1, 128 0 1, 40 512 1, 131 576 1, 216 576 1, 96 512 1 +192 -64 0 192 576;64 0 1, 64 384 1, 128 384 1, 128 0 1, 152 512 1, 96 512 1, -24 576 1, 61 576 1 +192 -128 0 256 576;64 0 1, 64 384 1, 128 384 1, 128 0 1, -67 512 1, 23 576 1, 105 576 1, 195 512 1, 140 512 1, 64 552 1, 64 552 1, -12 512 1 +192 0 0 192 576;64 0 1, 64 384 1, 128 384 1, 128 0 1, 0 512 1, 0 576 1, 64 576 1, 64 512 1, 128 512 1, 128 576 1, 192 576 1, 192 512 1 +448 64 0 384 640;64 0 1, 64 384 1, 128 384 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 0 1, 320 0 1, 320 253 1, 320 292 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1, 67 502 1, 70 531 0, 79 548 1, 96 576 0, 137 576 1, 164 576 0, 187 576 1, 210 576 1, 231 576 0, 242 576 1, 267 576 0, 271 590 1, 317 590 1, 314 559 0, 305 542 1, 288 512 0, 247 512 1, 220 512 0, 197 512 1, 174 512 1, 154 512 0, 142 512 1, 117 512 0, 113 502 1 +448 64 0 384 576;224 384 1, 298 384 0, 341 333 1, 384 281 0, 384 193 1, 384 102 0, 341 51 1, 298 0 0, 222 0 1, 156 0 0, 116 42 1, 64 95 0, 64 192 1, 64 281 0, 107 333 1, 150 384 0, 224 320 1, 128 320 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 193 1, 320 320 0, 168 512 1, 259 576 1, 344 576 1, 224 512 1 +448 64 0 384 576;224 384 1, 298 384 0, 341 333 1, 384 281 0, 384 193 1, 384 102 0, 341 51 1, 298 0 0, 222 0 1, 156 0 0, 116 42 1, 64 95 0, 64 192 1, 64 281 0, 107 333 1, 150 384 0, 224 320 1, 128 320 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 193 1, 320 320 0, 280 512 1, 224 512 1, 104 576 1, 189 576 1 +448 0 0 384 576;224 384 1, 298 384 0, 341 333 1, 384 281 0, 384 193 1, 384 102 0, 341 51 1, 298 0 0, 222 0 1, 156 0 0, 116 42 1, 64 95 0, 64 192 1, 64 281 0, 107 333 1, 150 384 0, 224 320 1, 128 320 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 193 1, 320 320 0, 61 512 1, 151 576 1, 233 576 1, 323 512 1, 268 512 1, 192 552 1, 192 552 1, 116 512 1 +448 64 0 384 576;224 384 1, 298 384 0, 341 333 1, 384 281 0, 384 193 1, 384 102 0, 341 51 1, 298 0 0, 222 0 1, 156 0 0, 116 42 1, 64 95 0, 64 192 1, 64 281 0, 107 333 1, 150 384 0, 224 320 1, 128 320 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 193 1, 320 320 0, 64 512 1, 64 576 1, 128 576 1, 128 512 1, 256 512 1, 256 576 1, 320 576 1, 320 512 1 +448 64 0 384 640;224 384 1, 298 384 0, 341 333 1, 384 281 0, 384 193 1, 384 102 0, 341 51 1, 298 0 0, 222 0 1, 156 0 0, 116 42 1, 64 95 0, 64 192 1, 64 281 0, 107 333 1, 150 384 0, 224 320 1, 128 320 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 193 1, 320 320 0, 67 502 1, 70 531 0, 79 548 1, 96 576 0, 137 576 1, 164 576 0, 187 576 1, 210 576 1, 231 576 0, 242 576 1, 267 576 0, 271 590 1, 317 590 1, 314 559 0, 305 542 1, 288 512 0, 247 512 1, 220 512 0, 197 512 1, 174 512 1, 154 512 0, 142 512 1, 117 512 0, 113 502 1 +448 64 0 384 576;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 168 512 1, 259 576 1, 344 576 1, 224 512 1 +448 64 0 384 576;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 280 512 1, 224 512 1, 104 576 1, 189 576 1 +448 0 0 384 576;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 61 512 1, 151 576 1, 233 576 1, 323 512 1, 268 512 1, 192 552 1, 192 552 1, 116 512 1 +448 64 0 384 576;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 64 512 1, 64 576 1, 128 576 1, 128 512 1, 256 512 1, 256 576 1, 320 576 1, 320 512 1 +448 64 -128 384 576;186 -128 1, 196 327 1, 64 317 1, 64 375 1, 196 365 1, 186 576 1, 262 576 1, 252 365 1, 384 375 1, 384 317 1, 252 327 1, 262 -128 1 +320 64 384 256 576;160 576 1, 199 576 0, 228 548 1, 256 520 0, 256 480 1, 256 440 0, 228 412 1, 199 384 0, 159 384 1, 124 384 0, 98 407 1, 64 437 0, 64 480 1, 64 520 0, 92 548 1, 121 576 0, 160 512 1, 147 512 0, 137 503 1, 128 493 0, 128 480 1, 128 467 0, 137 457 1, 147 448 0, 160 448 1, 172 448 0, 181 456 1, 192 465 0, 192 480 1, 192 493 0, 183 503 1, 173 512 0 +448 0 0 384 576;192 0 1, 192 65 1, 113 74 0, 65 120 1, 0 181 0, 0 278 1, 0 379 0, 66 436 1, 112 475 0, 192 486 1, 192 555 1, 256 555 1, 256 486 1, 316 484 0, 384 468 1, 384 406 1, 304 428 0, 256 432 1, 256 117 1, 317 117 0, 384 143 1, 384 87 1, 317 65 0, 256 65 1, 256 0 1, 192 429 1, 161 426 0, 145 420 1, 64 390 0, 64 277 1, 64 199 0, 112 159 1, 140 136 0, 192 122 1 +448 64 0 384 640;64 0 1, 64 64 1, 128 88 0, 128 176 1, 128 256 1, 64 256 1, 64 320 1, 128 320 1, 128 410 1, 128 491 0, 170 533 1, 211 576 0, 291 576 1, 333 576 0, 384 579 1, 384 510 1, 325 512 0, 277 512 1, 192 512 0, 192 434 1, 192 320 1, 256 320 1, 256 256 1, 192 256 1, 192 211 1, 192 147 0, 176 116 1, 164 89 0, 136 64 1, 384 64 1, 384 0 1 +448 64 -128 384 576;64 -77 1, 64 -9 1, 155 -64 0, 214 -64 1, 260 -64 0, 290 -45 1, 320 -25 0, 320 7 1, 320 36 0, 298 52 1, 279 66 0, 235 86 1, 170 116 1, 64 164 0, 64 247 1, 64 303 0, 123 363 1, 64 398 0, 64 449 1, 64 506 0, 115 541 1, 166 576 0, 249 576 1, 306 576 0, 384 572 1, 384 512 1, 301 512 0, 245 512 1, 192 512 0, 160 494 1, 128 476 0, 128 447 1, 128 409 0, 194 383 1, 246 364 1, 324 334 0, 354 306 1, 384 278 0, 384 236 1, 384 186 0, 342 125 1, 384 83 0, 384 14 1, 384 -51 0, 335 -89 1, 287 -128 0, 207 -128 1, 151 -128 0, 304 149 1, 320 187 0, 320 221 1, 320 249 0, 303 266 1, 285 284 0, 241 303 1, 161 338 1, 128 302 0, 128 270 1, 128 219 0, 220 183 1 +256 0 192 192 384;96 384 1, 136 384 0, 164 356 1, 192 327 0, 192 287 1, 192 248 0, 164 220 1, 135 192 0, 94 192 1, 60 192 0, 34 215 1, 0 245 0, 0 288 1, 0 328 0, 28 356 1, 56 384 0 +384 64 -128 320 576;192 -111 1, 192 280 1, 136 287 0, 105 321 1, 64 366 0, 64 446 1, 64 516 0, 93 546 1, 123 576 0, 192 576 1, 320 576 1, 320 -111 1, 256 -111 1, 256 512 1, 256 512 1, 256 -111 1 +448 64 0 448 576;64 0 1, 64 432 1, 64 513 0, 100 545 1, 138 576 0, 232 576 1, 384 576 0, 384 480 1, 384 434 0, 313 385 1, 256 345 0, 256 327 1, 256 303 0, 302 274 1, 377 225 1, 448 180 0, 448 113 1, 448 0 0, 306 0 1, 244 0 0, 192 11 1, 192 76 1, 265 64 0, 310 64 1, 384 64 0, 384 115 1, 384 149 0, 336 179 1, 246 234 1, 192 267 0, 192 305 1, 192 337 0, 261 387 1, 320 430 0, 320 459 1, 320 512 0, 227 512 1, 172 512 0, 150 501 1, 128 490 0, 128 463 1, 128 0 1 +576 0 0 576 576;288 576 1, 407 576 0, 492 492 1, 576 407 0, 576 288 1, 576 168 0, 491 84 1, 407 0 0, 284 0 1, 180 0 0, 102 68 1, 0 157 0, 0 288 1, 0 407 0, 84 492 1, 169 576 0, 288 512 1, 196 512 0, 130 446 1, 64 380 0, 64 288 1, 64 197 0, 129 130 1, 195 64 0, 285 64 1, 370 64 0, 432 117 1, 512 185 0, 512 288 1, 512 381 0, 446 446 1, 380 512 0, 192 128 1, 192 448 1, 302 448 1, 384 448 0, 384 370 1, 384 315 0, 340 277 1, 431 128 1, 375 128 1, 294 262 1, 256 262 1, 256 128 1, 256 301 1, 267 301 1, 320 301 0, 320 363 1, 320 415 0, 280 415 1, 256 415 1 +576 0 0 576 576;288 576 1, 407 576 0, 492 492 1, 576 407 0, 576 288 1, 576 168 0, 491 84 1, 407 0 0, 284 0 1, 180 0 0, 102 68 1, 0 157 0, 0 288 1, 0 407 0, 84 492 1, 169 576 0, 288 512 1, 196 512 0, 130 446 1, 64 380 0, 64 288 1, 64 197 0, 129 130 1, 195 64 0, 285 64 1, 370 64 0, 432 117 1, 512 185 0, 512 288 1, 512 381 0, 446 446 1, 380 512 0, 384 142 1, 330 128 0, 288 128 1, 218 128 0, 173 173 1, 128 217 0, 128 288 1, 128 360 0, 172 404 1, 216 448 0, 291 448 1, 329 448 0, 375 441 1, 384 439 1, 384 393 1, 335 384 0, 297 384 1, 250 384 0, 221 357 1, 192 330 0, 192 287 1, 192 243 0, 222 218 1, 252 192 0, 303 192 1, 342 192 0, 384 188 1 +768 64 256 640 576;192 256 1, 192 512 1, 99 512 1, 99 576 1, 341 576 1, 341 512 1, 256 512 1, 256 256 1, 384 256 1, 384 576 1, 476 576 1, 521 372 1, 564 576 1, 640 576 1, 640 256 1, 576 256 1, 576 474 1, 529 279 1, 495 279 1, 448 454 1, 448 256 1 +256 0 512 256 576;40 512 1, 131 576 1, 216 576 1, 96 512 1 +256 0 512 256 576;0 512 1, 0 576 1, 64 576 1, 64 512 1, 192 512 1, 192 576 1, 256 576 1, 256 512 1 +192 0 0 0 0; +768 0 0 768 576;227 192 1, 384 192 1, 384 473 1, 10 0 1, 363 576 1, 704 576 1, 704 512 1, 448 512 1, 448 320 1, 704 320 1, 704 256 1, 448 256 1, 448 64 1, 768 64 1, 768 0 1, 384 0 1, 384 128 1, 190 128 1, 92 0 1 +576 0 0 576 576;39 0 1, 104 65 1, 86 104 0, 77 144 1, 64 203 0, 64 279 1, 64 414 0, 131 495 1, 198 576 0, 309 576 1, 394 576 0, 459 529 1, 498 576 1, 563 576 1, 496 484 1, 531 447 0, 550 409 1, 576 353 0, 576 283 1, 576 154 0, 502 77 1, 428 0 0, 304 0 1, 213 0 0, 142 32 1, 104 0 1, 184 83 1, 238 64 0, 312 64 1, 406 64 0, 459 119 1, 512 174 0, 512 273 1, 512 351 0, 448 405 1, 416 459 1, 367 512 0, 302 512 1, 220 512 0, 174 449 1, 128 387 0, 128 275 1, 128 184 0, 152 122 1 +192 0 0 0 0; +448 64 0 384 448;192 128 1, 192 256 1, 64 256 1, 64 320 1, 192 320 1, 192 448 1, 256 448 1, 256 320 1, 384 320 1, 384 256 1, 256 256 1, 256 128 1, 64 0 1, 64 64 1, 384 64 1, 384 0 1 +192 0 0 0 0; +192 0 0 0 0; +448 0 0 448 576;192 0 1, 192 128 1, 81 128 1, 81 192 1, 192 192 1, 192 192 1, 81 192 1, 81 256 1, 192 256 1, 31 576 1, 117 576 1, 232 335 1, 232 335 1, 350 576 1, 416 576 1, 256 256 1, 367 256 1, 367 192 1, 256 192 1, 256 192 1, 367 192 1, 367 128 1, 256 128 1, 256 0 1 +448 64 -128 384 384;64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 320 0 1, 320 72 1, 252 0 0, 184 0 1, 156 0 0, 128 9 1, 128 -128 1, 64 -128 1 +192 0 0 0 0; +192 0 0 0 0; +192 0 0 0 0; +192 0 0 0 0; +192 0 0 0 0; +256 64 320 320 576;196 364 1, 162 320 0, 129 320 1, 100 320 0, 82 341 1, 64 363 0, 64 394 1, 64 485 0, 174 485 1, 192 485 1, 192 511 1, 192 512 0, 147 512 1, 110 512 0, 68 530 1, 68 572 1, 118 576 0, 161 576 1, 256 576 0, 256 513 1, 256 401 1, 256 383 0, 276 384 1, 279 384 1, 280 384 0, 282 384 1, 284 384 0, 286 368 1, 289 336 1, 268 320 0, 250 320 1, 209 320 0, 198 364 1, 192 395 1, 192 452 1, 178 452 1, 128 452 0, 128 413 1, 128 384 0, 154 384 1, 172 384 0 +256 0 320 256 576;128 576 1, 187 576 0, 222 542 1, 256 507 0, 256 449 1, 256 389 0, 222 354 1, 187 320 0, 127 320 1, 74 320 0, 41 348 1, 0 384 0, 0 448 1, 0 507 0, 35 541 1, 69 576 0, 128 512 1, 64 512 0, 64 448 1, 64 384 0, 128 384 1, 192 384 0, 192 449 1, 192 512 0 +192 0 0 0 0; +704 0 0 704 384;320 92 1, 320 198 1, 290 199 1, 263 200 0, 233 196 1, 128 183 0, 128 121 1, 128 64 0, 208 64 1, 264 64 0, 371 353 1, 423 384 0, 489 384 1, 642 384 0, 642 218 1, 642 192 1, 387 192 1, 391 153 0, 400 133 1, 431 64 0, 526 64 1, 578 64 0, 640 72 1, 640 13 1, 569 0 0, 510 0 1, 444 0 0, 399 29 1, 374 45 0, 351 77 1, 298 36 0, 260 19 1, 215 0 0, 153 0 1, 84 0 0, 42 30 1, 0 61 0, 0 110 1, 0 248 0, 300 248 1, 320 248 1, 320 290 1, 320 307 0, 298 313 1, 277 320 0, 223 320 1, 147 320 0, 64 318 1, 64 367 1, 151 384 0, 230 384 1, 325 384 0, 390 256 1, 574 256 1, 573 276 0, 567 287 1, 549 320 0, 487 320 1, 444 320 0, 419 305 1, 396 290 0 +448 0 0 448 448;131 21 1, 108 0 1, 54 0 1, 99 50 1, 64 108 0, 64 189 1, 64 280 0, 111 332 1, 158 384 0, 240 384 1, 298 384 0, 339 363 1, 362 448 1, 416 448 1, 371 334 1, 448 276 0, 448 196 1, 448 105 0, 394 53 1, 340 0 0, 246 0 1, 180 0 0, 169 71 1, 170 71 1, 190 67 0, 207 66 1, 229 64 0, 253 64 1, 384 64 0, 384 200 1, 384 241 0, 325 275 1, 301 313 1, 300 313 1, 271 320 0, 233 320 1, 128 320 0, 128 186 1, 128 140 0, 145 109 1 +448 64 -192 384 384;320 384 1, 320 320 1, 256 320 1, 256 384 1, 320 256 1, 320 236 1, 320 139 0, 247 89 1, 207 62 1, 128 7 0, 128 -50 1, 128 -128 0, 234 -128 1, 298 -128 0, 384 -78 1, 384 -141 1, 304 -192 0, 236 -192 1, 161 -192 0, 118 -165 1, 64 -131 0, 64 -55 1, 64 21 0, 147 68 1, 182 88 1, 226 113 0, 241 139 1, 256 166 0, 256 215 1, 256 256 1 +256 64 -128 128 384;128 384 1, 128 320 1, 64 320 1, 64 384 1, 120 256 1, 128 -32 1, 128 -128 1, 64 -128 1, 64 -32 1, 72 256 1 +448 64 128 384 320;64 256 1, 64 320 1, 384 320 1, 384 128 1, 320 128 1, 320 256 1 +192 0 0 0 0; +448 0 -128 448 640;51 -128 1, 118 256 1, 64 256 1, 64 320 1, 128 320 1, 135 361 1, 174 576 0, 310 576 1, 346 576 0, 389 579 1, 378 515 1, 339 512 0, 307 512 1, 230 512 0, 208 394 1, 194 320 1, 256 320 1, 256 256 1, 184 256 1, 118 -128 1 +192 0 0 0 0; +192 0 0 0 0; +448 0 0 384 384;376 342 1, 265 204 1, 376 65 1, 339 37 1, 191 204 1, 339 370 1, 228 342 1, 117 204 1, 228 65 1, 191 37 1, 43 204 1, 191 370 1 +448 0 0 384 384;51 65 1, 162 204 1, 51 342 1, 88 370 1, 236 204 1, 88 37 1, 199 65 1, 310 204 1, 199 342 1, 236 370 1, 384 204 1, 236 37 1 +768 64 0 704 64;64 0 1, 64 64 1, 128 64 1, 128 0 1, 320 0 1, 320 64 1, 384 64 1, 384 0 1, 640 0 1, 640 64 1, 704 64 1, 704 0 1 +448 0 0 0 0; +512 0 0 512 704;7 0 1, 218 576 1, 296 576 1, 503 0 1, 419 0 1, 361 128 1, 138 128 1, 80 0 1, 161 192 1, 339 192 1, 250 458 1, 312 576 1, 257 576 1, 137 704 1, 222 704 1 +512 0 0 512 768;7 0 1, 218 576 1, 296 576 1, 503 0 1, 419 0 1, 361 128 1, 138 128 1, 80 0 1, 161 192 1, 339 192 1, 250 458 1, 132 623 1, 135 655 0, 144 673 1, 161 704 0, 202 704 1, 229 704 0, 252 704 1, 275 704 1, 296 704 0, 307 704 1, 332 704 0, 336 710 1, 382 710 1, 379 682 0, 370 667 1, 353 640 0, 312 640 1, 285 640 0, 262 640 1, 239 640 1, 219 640 0, 207 640 1, 182 640 0, 178 623 1 +576 64 0 576 768;320 576 1, 436 576 0, 506 498 1, 576 419 0, 576 289 1, 576 156 0, 506 78 1, 436 0 0, 316 0 1, 214 0 0, 147 64 1, 64 145 0, 64 288 1, 64 420 0, 134 498 1, 204 576 0, 320 512 1, 229 512 0, 179 453 1, 128 394 0, 128 288 1, 128 183 0, 179 124 1, 229 64 0, 318 64 1, 401 64 0, 450 112 1, 512 171 0, 512 289 1, 512 394 0, 461 453 1, 410 512 0, 174 623 1, 177 655 0, 186 673 1, 203 704 0, 244 704 1, 271 704 0, 294 704 1, 316 704 1, 337 704 0, 349 704 1, 373 704 0, 377 710 1, 423 710 1, 420 682 0, 411 667 1, 394 640 0, 354 640 1, 327 640 0, 303 640 1, 281 640 1, 260 640 0, 248 640 1, 224 640 0, 220 623 1 +768 64 0 768 576;448 0 1, 448 24 1, 387 0 0, 317 0 1, 203 0 0, 134 79 1, 64 159 0, 64 288 1, 64 420 0, 134 498 1, 204 576 0, 320 576 1, 388 576 0, 448 553 1, 448 576 1, 704 576 1, 704 512 1, 512 512 1, 512 320 1, 704 320 1, 704 256 1, 512 256 1, 512 64 1, 768 64 1, 768 0 1, 448 212 1, 448 345 1, 448 433 0, 416 472 1, 384 512 0, 314 512 1, 226 512 0, 177 453 1, 128 394 0, 128 288 1, 128 182 0, 177 123 1, 227 64 0, 314 64 1, 448 64 0 +704 64 0 704 384;396 332 1, 420 355 0, 447 367 1, 486 384 0, 540 384 1, 637 384 0, 675 325 1, 703 282 0, 704 192 1, 437 192 1, 444 129 0, 471 100 1, 506 64 0, 585 64 1, 643 64 0, 704 73 1, 704 14 1, 632 0 0, 566 0 1, 499 0 0, 459 19 1, 429 33 0, 398 65 1, 376 36 0, 349 22 1, 310 0 0, 255 0 1, 168 0 0, 116 52 1, 64 104 0, 64 192 1, 64 281 0, 116 332 1, 168 384 0, 256 384 1, 313 384 0, 353 364 1, 375 353 0, 261 320 1, 128 320 0, 128 193 1, 128 138 0, 154 106 1, 187 64 0, 262 64 1, 384 64 0, 384 192 1, 384 251 0, 359 283 1, 329 320 0, 439 256 1, 625 256 1, 624 282 0, 612 296 1, 592 320 0, 539 320 1, 487 320 0, 462 299 1, 445 284 0 +448 0 192 448 256;38 192 1, 38 256 1, 390 256 1, 390 192 1 +768 0 192 768 256;37 192 1, 37 256 1, 731 256 1, 731 192 1 +256 0 384 256 640;256 578 1, 256 551 1, 233 537 0, 233 467 1, 233 458 1, 256 458 1, 256 384 1, 192 384 1, 192 446 1, 192 559 0, 64 578 1, 64 551 1, 41 537 0, 41 467 1, 41 458 1, 64 458 1, 64 384 1, 0 384 1, 0 446 1, 0 559 0 +256 0 320 256 576;0 382 1, 0 409 1, 23 423 0, 23 493 1, 23 502 1, 0 502 1, 0 576 1, 64 576 1, 64 514 1, 64 401 0, 192 382 1, 192 409 1, 215 423 0, 215 493 1, 215 502 1, 192 502 1, 192 576 1, 256 576 1, 256 514 1, 256 401 0 +192 64 384 128 640;128 597 1, 128 569 1, 103 559 0, 103 484 1, 103 476 1, 128 476 1, 128 384 1, 64 384 1, 64 464 1, 64 586 0 +192 64 320 128 576;64 363 1, 64 391 1, 89 401 0, 89 476 1, 89 483 1, 64 483 1, 64 576 1, 128 576 1, 128 496 1, 128 372 0 +448 64 0 384 448;64 192 1, 64 256 1, 384 256 1, 384 192 1, 192 384 1, 192 448 1, 256 448 1, 256 384 1, 192 0 1, 192 64 1, 256 64 1, 256 0 1 +192 0 0 0 0; +384 0 -128 384 576;152 0 1, 7 384 1, 82 384 1, 193 90 1, 314 384 1, 380 384 1, 164 -128 1, 87 -128 1, 64 512 1, 64 576 1, 128 576 1, 128 512 1, 256 512 1, 256 576 1, 320 576 1, 320 512 1 +512 -64 0 576 640;192 0 1, 192 240 1, -19 576 1, 71 576 1, 227 309 1, 441 576 1, 514 576 1, 256 242 1, 256 0 1, 128 576 1, 128 640 1, 192 640 1, 192 576 1, 320 576 1, 320 640 1, 384 640 1, 384 576 1 +128 -192 -64 320 576;-165 -14 1, 243 569 1, 293 569 1, -114 -14 1 +448 0 64 384 448;137 145 1, 78 87 1, 46 120 1, 104 178 1, 64 217 0, 64 255 1, 64 292 0, 104 331 1, 46 390 1, 78 423 1, 137 364 1, 174 384 0, 214 384 1, 253 384 0, 290 364 1, 348 423 1, 381 390 1, 323 331 1, 320 292 0, 320 255 1, 320 217 0, 323 178 1, 381 120 1, 348 87 1, 290 145 1, 253 128 0, 214 128 1, 174 128 0, 192 320 1, 165 320 0, 146 302 1, 128 283 0, 128 256 1, 128 229 0, 146 211 1, 165 192 0, 191 192 1, 216 192 0, 233 207 1, 256 226 0, 256 256 1, 256 283 0, 238 302 1, 219 320 0 +256 0 0 256 384;213 342 1, 102 204 1, 213 65 1, 176 37 1, 28 204 1, 176 370 1 +256 0 0 256 384;43 65 1, 154 204 1, 43 342 1, 80 370 1, 228 204 1, 80 37 1 +384 0 0 320 640;64 0 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 468 1, 64 640 0, 194 640 1, 221 640 0, 256 613 1, 256 557 1, 219 576 0, 193 576 1, 157 576 0, 142 555 1, 128 534 0, 128 482 1, 128 384 1, 320 384 1, 320 0 1, 256 0 1, 256 320 1, 128 320 1, 128 0 1, 256 512 1, 256 576 1, 320 576 1, 320 512 1 +384 0 0 320 576;64 0 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 435 1, 64 576 0, 175 576 1, 256 576 1, 320 576 1, 320 0 1, 256 0 1, 256 518 1, 240 516 1, 206 512 0, 183 512 1, 148 512 0, 137 493 1, 128 477 0, 128 443 1, 128 384 1, 192 384 1, 192 320 1, 128 320 1, 128 0 1 +448 64 -128 384 576;183 -128 1, 192 97 1, 64 86 1, 64 144 1, 192 134 1, 192 327 1, 64 317 1, 64 375 1, 192 365 1, 183 576 1, 265 576 1, 256 365 1, 384 375 1, 384 317 1, 256 327 1, 256 134 1, 384 144 1, 384 86 1, 256 97 1, 265 -128 1 +192 64 192 128 256;64 192 1, 64 256 1, 128 256 1, 128 192 1 +192 64 -192 128 64;64 -140 1, 64 -112 1, 89 -102 0, 89 -36 1, 89 -29 1, 64 -29 1, 64 64 1, 128 64 1, 128 -16 1, 128 -130 0 +256 0 -192 256 64;0 -130 1, 0 -103 1, 23 -89 0, 23 -19 1, 23 -10 1, 0 -10 1, 0 64 1, 64 64 1, 64 2 1, 64 -111 0, 192 -130 1, 192 -103 1, 215 -89 0, 215 -19 1, 215 -10 1, 192 -10 1, 192 64 1, 256 64 1, 256 2 1, 256 -111 0 +768 0 -64 768 640;128 576 1, 186 576 0, 221 541 1, 256 507 0, 256 448 1, 256 388 0, 221 354 1, 187 320 0, 126 320 1, 75 320 0, 42 348 1, 0 384 0, 0 448 1, 0 507 0, 35 541 1, 70 576 0, 127 512 1, 64 512 0, 64 448 1, 64 384 0, 128 384 1, 192 384 0, 192 448 1, 192 477 0, 175 495 1, 157 512 0, 384 320 1, 443 320 0, 477 277 1, 512 234 0, 512 160 1, 512 86 0, 477 43 1, 443 0 0, 383 0 1, 330 0 0, 298 35 1, 256 80 0, 256 160 1, 256 234 0, 291 277 1, 326 320 0, 383 256 1, 320 256 0, 320 160 1, 320 64 0, 384 64 1, 448 64 0, 448 159 1, 448 204 0, 431 230 1, 413 256 0, 640 320 1, 699 320 0, 733 277 1, 768 234 0, 768 161 1, 768 86 0, 733 43 1, 698 0 0, 639 0 1, 587 0 0, 553 35 1, 512 80 0, 512 160 1, 512 234 0, 547 277 1, 581 320 0, 639 256 1, 576 256 0, 576 160 1, 576 64 0, 640 64 1, 704 64 0, 704 160 1, 704 204 0, 686 230 1, 669 256 0, 23 -14 1, 431 590 1, 482 590 1, 74 -14 1 +512 0 0 512 704;7 0 1, 218 576 1, 296 576 1, 503 0 1, 419 0 1, 361 128 1, 138 128 1, 80 0 1, 161 192 1, 339 192 1, 250 458 1, 126 576 1, 216 704 1, 298 704 1, 388 576 1, 333 576 1, 257 657 1, 257 657 1, 181 576 1 +512 64 0 512 704;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1, 127 576 1, 228 704 1, 319 704 1, 420 576 1, 358 576 1, 274 657 1, 273 657 1, 189 576 1 +512 0 0 512 704;7 0 1, 218 576 1, 296 576 1, 503 0 1, 419 0 1, 361 128 1, 138 128 1, 80 0 1, 161 192 1, 339 192 1, 250 458 1, 201 576 1, 292 704 1, 377 704 1, 257 576 1 +512 64 0 512 640;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1, 192 576 1, 192 640 1, 256 640 1, 256 576 1, 320 576 1, 320 640 1, 384 640 1, 384 576 1 +512 64 0 512 704;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1, 334 576 1, 273 576 1, 138 704 1, 234 704 1 +192 0 0 256 704;64 0 1, 64 576 1, 128 576 1, 128 0 1, 48 576 1, 124 704 1, 209 704 1, 96 576 1 +192 -64 0 256 704;64 0 1, 64 576 1, 128 576 1, 128 0 1, -28 576 1, 62 704 1, 130 704 1, 220 576 1, 164 576 1, 96 657 1, 96 657 1, 28 576 1 +192 0 0 192 640;64 0 1, 64 576 1, 128 576 1, 128 0 1, 0 576 1, 0 640 1, 64 640 1, 64 576 1, 128 576 1, 128 640 1, 192 640 1, 192 576 1 +192 -64 0 192 704;64 0 1, 64 576 1, 128 576 1, 128 0 1, 144 576 1, 96 576 1, -17 704 1, 68 704 1 +576 64 0 576 704;320 576 1, 436 576 0, 506 498 1, 576 419 0, 576 289 1, 576 156 0, 506 78 1, 436 0 0, 316 0 1, 214 0 0, 147 64 1, 64 145 0, 64 288 1, 64 420 0, 134 498 1, 204 576 0, 320 512 1, 229 512 0, 179 453 1, 128 394 0, 128 288 1, 128 183 0, 179 124 1, 229 64 0, 318 64 1, 401 64 0, 450 112 1, 512 171 0, 512 289 1, 512 394 0, 461 453 1, 410 512 0, 243 576 1, 333 704 1, 419 704 1, 299 576 1 +576 64 0 576 704;320 576 1, 436 576 0, 506 498 1, 576 419 0, 576 289 1, 576 156 0, 506 78 1, 436 0 0, 316 0 1, 214 0 0, 147 64 1, 64 145 0, 64 288 1, 64 420 0, 134 498 1, 204 576 0, 320 512 1, 229 512 0, 179 453 1, 128 394 0, 128 288 1, 128 183 0, 179 124 1, 229 64 0, 318 64 1, 401 64 0, 450 112 1, 512 171 0, 512 289 1, 512 394 0, 461 453 1, 410 512 0, 167 576 1, 258 704 1, 339 704 1, 430 576 1, 374 576 1, 299 657 1, 298 657 1, 223 576 1 +448 -64 0 384 576;121 192 1, 136 142 0, 158 114 1, 198 64 0, 269 64 1, 316 64 0, 384 72 1, 384 10 1, 311 0 0, 260 0 1, 173 0 0, 119 46 1, 81 78 0, 62 132 1, 55 151 0, 46 192 1, -21 192 1, -2 256 1, 40 256 1, 39 280 1, 39 282 0, 39 288 1, 40 302 0, 41 320 1, -21 320 1, -2 384 1, 48 384 1, 63 444 0, 81 474 1, 140 576 0, 273 576 1, 320 576 0, 384 573 1, 384 502 1, 321 512 0, 273 512 1, 207 512 0, 168 469 1, 145 444 0, 133 413 1, 128 401 0, 123 384 1, 336 384 1, 316 320 1, 115 320 1, 113 296 0, 113 281 1, 114 256 1, 286 256 1, 267 192 1 +576 64 0 576 704;320 576 1, 436 576 0, 506 498 1, 576 419 0, 576 289 1, 576 156 0, 506 78 1, 436 0 0, 316 0 1, 214 0 0, 147 64 1, 64 145 0, 64 288 1, 64 420 0, 134 498 1, 204 576 0, 320 512 1, 229 512 0, 179 453 1, 128 394 0, 128 288 1, 128 183 0, 179 124 1, 229 64 0, 318 64 1, 401 64 0, 450 112 1, 512 171 0, 512 289 1, 512 394 0, 461 453 1, 410 512 0, 354 576 1, 299 576 1, 178 704 1, 264 704 1 +576 64 0 512 704;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 225 576 1, 328 704 1, 425 704 1, 288 576 1 +576 64 0 512 704;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 139 576 1, 242 704 1, 334 704 1, 437 576 1, 374 576 1, 288 657 1, 288 657 1, 202 576 1 +576 64 0 512 704;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 351 576 1, 288 576 1, 151 704 1, 248 704 1 +192 64 0 128 384;64 0 1, 64 384 1, 128 384 1, 128 0 1 +256 -64 512 320 576;-3 512 1, 87 576 1, 169 576 1, 259 512 1, 204 512 1, 128 552 1, 128 552 1, 52 512 1 +256 0 448 256 640;3 502 1, 6 531 0, 15 548 1, 32 576 0, 73 576 1, 100 576 0, 123 576 1, 146 576 1, 167 576 0, 178 576 1, 203 576 0, 207 590 1, 253 590 1, 250 559 0, 241 542 1, 224 512 0, 183 512 1, 156 512 0, 133 512 1, 110 512 1, 90 512 0, 78 512 1, 53 512 0, 49 502 1 +256 0 448 256 512;0 448 1, 0 512 1, 256 512 1, 256 448 1 +256 0 512 256 640;3 602 1, 49 602 1, 57 588 0, 77 582 1, 97 576 0, 128 576 1, 163 576 0, 184 583 1, 200 589 0, 207 602 1, 253 602 1, 247 564 0, 220 542 1, 184 512 0, 128 512 1, 69 512 0, 33 544 1, 9 566 0 +256 64 512 128 576;64 512 1, 64 576 1, 128 576 1, 128 512 1 +256 64 512 192 640;128 640 1, 155 640 0, 173 621 1, 192 603 0, 192 576 1, 192 549 0, 173 531 1, 155 512 0, 127 512 1, 104 512 0, 87 527 1, 64 547 0, 64 576 1, 64 603 0, 83 621 1, 101 640 0, 128 576 1, 128 576 0, 128 576 1, 128 576 0, 128 576 1, 128 576 0, 128 576 1, 128 576 0, 128 576 1, 128 576 0, 128 576 1, 128 576 0, 128 576 1, 128 576 0, 128 576 1, 128 576 0 +256 0 -192 192 0;107 0 1, 143 0 1, 120 -41 1, 147 -42 0, 166 -59 1, 192 -82 0, 192 -116 1, 192 -148 0, 170 -170 1, 149 -192 0, 116 -192 1, 91 -192 0, 63 -154 1, 63 -124 1, 79 -128 0, 96 -128 1, 128 -128 0, 128 -101 1, 128 -67 0, 71 -66 1 +256 -64 448 320 576;-19 456 1, 71 576 1, 143 576 1, 23 456 1, 113 456 1, 203 576 1, 275 576 1, 155 456 1 +256 64 -128 192 0;123 0 1, 163 0 1, 128 -19 0, 128 -42 1, 128 -64 0, 164 -64 1, 180 -64 0, 192 -98 1, 192 -128 1, 169 -128 0, 140 -128 1, 64 -128 0, 64 -73 1, 64 -31 0 +256 -64 512 320 576;259 576 1, 169 512 1, 87 512 1, -3 576 1, 52 576 1, 128 536 1, 128 536 1, 204 576 1 +448 -64 0 384 576;121 192 1, 136 142 0, 158 114 1, 198 64 0, 269 64 1, 316 64 0, 384 72 1, 384 10 1, 311 0 0, 260 0 1, 173 0 0, 119 46 1, 81 78 0, 62 132 1, 55 151 0, 46 192 1, -21 192 1, -2 256 1, 40 256 1, 39 280 1, 39 282 0, 39 288 1, 40 302 0, 41 320 1, -21 320 1, -2 384 1, 48 384 1, 63 444 0, 81 474 1, 140 576 0, 273 576 1, 320 576 0, 384 573 1, 384 502 1, 321 512 0, 273 512 1, 207 512 0, 168 469 1, 145 444 0, 133 413 1, 128 401 0, 123 384 1, 336 384 1, 316 320 1, 115 320 1, 113 296 0, 113 281 1, 114 256 1, 286 256 1, 267 192 1 +192 0 0 0 0; +192 64 -128 128 576;64 -128 1, 64 192 1, 128 192 1, 128 -128 1, 64 320 1, 64 576 1, 128 576 1, 128 320 1 +256 64 192 192 256;64 192 1, 64 256 1, 192 256 1, 192 192 1 +448 64 512 384 576;64 512 1, 64 576 1, 384 576 1, 384 512 1 +256 0 192 256 576;28 192 1, 28 256 1, 55 300 0, 101 338 1, 129 361 1, 192 412 0, 192 458 1, 192 512 0, 127 512 1, 89 512 0, 36 488 1, 36 532 1, 89 576 0, 138 576 1, 191 576 0, 224 548 1, 256 521 0, 256 477 1, 256 421 0, 179 359 1, 157 341 1, 102 296 0, 92 256 1, 238 256 1, 238 192 1 +256 0 192 256 576;32 507 1, 32 549 1, 68 576 0, 103 576 1, 192 576 0, 192 492 1, 192 454 0, 177 429 1, 168 414 0, 151 402 1, 212 388 0, 236 360 1, 256 336 0, 256 302 1, 256 251 0, 220 222 1, 184 192 0, 121 192 1, 79 192 0, 28 225 1, 28 270 1, 84 256 0, 117 256 1, 192 256 0, 192 314 1, 192 381 0, 78 381 1, 59 381 1, 59 416 1, 75 416 1, 128 416 0, 128 470 1, 128 512 0, 86 512 1, 62 512 0 +192 64 192 128 256;64 192 1, 64 256 1, 128 256 1, 128 192 1 +256 0 192 192 576;128 192 1, 128 477 1, 54 458 1, 54 501 1, 192 534 1, 192 192 1 +640 0 -64 640 576;128 192 1, 128 494 1, 54 475 1, 54 518 1, 192 551 1, 192 192 1, 512 -27 1, 512 64 1, 361 64 1, 361 126 1, 511 320 1, 576 320 1, 576 128 1, 622 128 1, 622 64 1, 576 64 1, 576 -27 1, 409 128 1, 512 128 1, 512 249 1, 82 -41 1, 495 556 1, 554 556 1, 134 -41 1 +640 0 -64 640 576;401 0 1, 401 64 1, 428 95 0, 479 125 1, 509 143 1, 576 181 0, 576 215 1, 576 256 0, 507 256 1, 467 256 0, 410 248 1, 410 291 1, 466 320 0, 516 320 1, 572 320 0, 606 298 1, 640 276 0, 640 241 1, 640 194 0, 559 146 1, 535 132 1, 476 96 0, 466 64 1, 612 64 1, 612 0 1, 63 -14 1, 480 539 1, 531 539 1, 114 -14 1, 128 192 1, 128 477 1, 54 458 1, 54 501 1, 192 534 1, 192 192 1 +640 0 -64 640 576;56 494 1, 56 536 1, 101 576 0, 145 576 1, 256 576 0, 256 486 1, 256 444 0, 227 418 1, 209 402 0, 175 389 1, 222 376 0, 241 349 1, 256 327 0, 256 295 1, 256 247 0, 224 220 1, 192 192 0, 135 192 1, 97 192 0, 52 215 1, 52 259 1, 100 256 0, 128 256 1, 192 256 0, 192 308 1, 192 368 0, 102 368 1, 83 368 1, 83 403 1, 99 403 1, 192 403 0, 192 465 1, 192 512 0, 133 512 1, 99 512 0, 512 -27 1, 512 64 1, 359 64 1, 359 126 1, 510 320 1, 576 320 1, 576 128 1, 623 128 1, 623 64 1, 576 64 1, 576 -27 1, 408 128 1, 512 128 1, 512 260 1, 117 -41 1, 528 556 1, 589 556 1, 167 -41 1 +576 0 0 512 576;64 0 1, 64 256 1, 0 256 1, 0 320 1, 64 320 1, 64 576 1, 229 576 1, 512 576 0, 512 301 1, 512 158 0, 438 79 1, 365 0 0, 231 0 1, 128 64 1, 225 64 1, 448 64 0, 448 292 1, 448 426 0, 361 481 1, 336 497 0, 300 504 1, 258 512 0, 188 512 1, 128 512 1, 128 320 1, 256 320 1, 256 256 1, 128 256 1 +448 0 0 448 384;44 58 1, 185 199 1, 44 340 1, 84 379 1, 224 238 1, 365 379 1, 404 340 1, 264 199 1, 404 58 1, 365 19 1, 224 160 1, 84 19 1 +512 -64 0 512 704;192 0 1, 192 240 1, -7 576 1, 83 576 1, 232 309 1, 395 576 1, 468 576 1, 256 242 1, 256 0 1, 185 576 1, 261 704 1, 346 704 1, 232 576 1 +512 64 0 512 576;64 0 1, 64 576 1, 128 576 1, 128 448 1, 267 448 1, 365 448 0, 408 438 1, 451 429 0, 478 401 1, 512 367 0, 512 308 1, 512 128 0, 244 128 1, 128 128 1, 128 0 1, 128 192 1, 240 192 1, 448 192 0, 448 302 1, 448 355 0, 394 371 1, 348 384 0, 242 384 1, 128 384 1 +448 64 0 384 704;64 554 1, 64 615 1, 139 615 0, 200 586 1, 258 650 1, 287 617 1, 238 559 1, 278 523 0, 299 494 1, 384 380 0, 384 219 1, 384 116 0, 342 58 1, 299 0 0, 226 0 1, 152 0 0, 108 53 1, 64 105 0, 64 194 1, 64 283 0, 110 333 1, 155 384 0, 234 384 1, 253 384 0, 276 379 1, 247 449 0, 194 497 1, 138 416 1, 109 459 1, 157 528 1, 119 554 0, 223 320 1, 178 320 0, 153 287 1, 128 253 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 192 1, 320 320 0 +384 0 -128 384 576;152 0 1, 7 384 1, 82 384 1, 193 90 1, 314 384 1, 380 384 1, 164 -128 1, 87 -128 1, 168 512 1, 259 576 1, 344 576 1, 224 512 1 +448 64 -128 384 576;128 -128 1, 64 -128 1, 64 576 1, 128 576 1, 128 312 1, 148 344 0, 171 361 1, 203 384 0, 247 384 1, 308 384 0, 346 334 1, 384 285 0, 384 202 1, 384 106 0, 335 53 1, 286 0 0, 197 0 1, 164 0 0, 128 0 1, 128 249 1, 128 53 1, 182 64 0, 209 64 1, 320 64 0, 320 199 1, 320 256 0, 298 288 1, 276 320 0, 239 320 1, 190 320 0 +512 -64 0 512 640;-1 0 1, 215 576 1, 297 576 1, 510 0 1, 425 0 1, 366 128 1, 130 128 1, 72 0 1, 154 192 1, 343 192 1, 249 458 1, 128 576 1, 128 640 1, 384 640 1, 384 576 1 +448 0 0 448 512;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0, 64 448 1, 64 512 1, 320 512 1, 320 448 1 +512 0 0 512 768;8 0 1, 219 576 1, 297 576 1, 504 0 1, 419 0 1, 362 128 1, 139 128 1, 81 0 1, 161 192 1, 340 192 1, 251 458 1, 133 743 1, 179 743 1, 187 722 0, 207 713 1, 226 704 0, 258 704 1, 293 704 0, 313 715 1, 329 724 0, 336 743 1, 383 743 1, 377 700 0, 350 674 1, 314 640 0, 258 640 1, 199 640 0, 163 677 1, 139 702 0 +448 0 0 448 640;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0, 67 602 1, 113 602 1, 121 588 0, 141 582 1, 161 576 0, 192 576 1, 227 576 0, 248 583 1, 264 589 0, 271 602 1, 317 602 1, 311 564 0, 284 542 1, 248 512 0, 192 512 1, 133 512 0, 97 544 1, 73 566 0 +512 0 -128 512 576;7 0 1, 218 576 1, 296 576 1, 503 0 1, 419 0 1, 361 128 1, 138 128 1, 80 0 1, 161 192 1, 339 192 1, 250 458 1, 419 0 1, 459 0 1, 448 -19 0, 448 -42 1, 448 -64 0, 470 -64 1, 481 -64 0, 488 -98 1, 488 -128 1, 469 -128 0, 446 -128 1, 384 -128 0, 384 -73 1, 384 -31 0 +448 0 -128 448 384;329 49 1, 239 0 0, 155 0 1, 86 0 0, 43 28 1, 0 57 0, 0 101 1, 0 227 0, 299 227 1, 320 227 1, 320 274 1, 320 320 0, 224 320 1, 149 320 0, 64 307 1, 64 364 1, 151 384 0, 228 384 1, 309 384 0, 347 358 1, 384 331 0, 384 274 1, 384 99 1, 384 64 0, 423 64 1, 428 64 0, 437 42 1, 443 3 1, 417 0 0, 392 0 1, 368 0 0, 352 11 1, 337 23 0, 320 87 1, 320 186 1, 283 187 1, 248 188 0, 206 184 1, 64 173 0, 64 116 1, 64 64 0, 168 64 1, 240 64 0, 315 0 1, 355 0 1, 320 -19 0, 320 -42 1, 320 -64 0, 356 -64 1, 372 -64 0, 384 -98 1, 384 -128 1, 361 -128 0, 332 -128 1, 256 -128 0, 256 -73 1, 256 -31 0 +576 64 0 512 704;512 30 1, 429 0 0, 334 0 1, 202 0 0, 133 73 1, 64 147 0, 64 287 1, 64 427 0, 134 502 1, 205 576 0, 338 576 1, 413 576 0, 512 566 1, 512 489 1, 394 512 0, 326 512 1, 230 512 0, 179 454 1, 128 396 0, 128 287 1, 128 180 0, 182 122 1, 237 64 0, 335 64 1, 417 64 0, 512 100 1, 270 576 1, 360 704 1, 446 704 1, 326 576 1 +384 0 0 384 576;320 11 1, 251 0 0, 190 0 1, 104 0 0, 52 53 1, 0 107 0, 0 192 1, 0 282 0, 54 333 1, 108 384 0, 205 384 1, 254 384 0, 320 380 1, 320 322 1, 251 320 0, 209 320 1, 64 320 0, 64 191 1, 64 130 0, 101 97 1, 137 64 0, 203 64 1, 253 64 0, 320 72 1, 168 512 1, 259 576 1, 344 576 1, 224 512 1 +576 64 0 512 704;512 30 1, 429 0 0, 334 0 1, 202 0 0, 133 73 1, 64 147 0, 64 287 1, 64 427 0, 134 502 1, 205 576 0, 338 576 1, 413 576 0, 512 566 1, 512 489 1, 394 512 0, 326 512 1, 230 512 0, 179 454 1, 128 396 0, 128 287 1, 128 180 0, 182 122 1, 237 64 0, 335 64 1, 417 64 0, 512 100 1, 194 576 1, 285 704 1, 366 704 1, 457 576 1, 401 576 1, 326 657 1, 325 657 1, 250 576 1 +384 0 0 384 576;320 11 1, 251 0 0, 190 0 1, 104 0 0, 52 53 1, 0 107 0, 0 192 1, 0 282 0, 54 333 1, 108 384 0, 205 384 1, 254 384 0, 320 380 1, 320 322 1, 251 320 0, 209 320 1, 64 320 0, 64 191 1, 64 130 0, 101 97 1, 137 64 0, 203 64 1, 253 64 0, 320 72 1, 74 512 1, 164 576 1, 246 576 1, 332 512 1, 281 512 1, 205 552 1, 204 552 1, 129 512 1 +576 64 0 512 704;512 30 1, 429 0 0, 334 0 1, 202 0 0, 133 73 1, 64 147 0, 64 287 1, 64 427 0, 134 502 1, 205 576 0, 338 576 1, 413 576 0, 512 566 1, 512 489 1, 394 512 0, 326 512 1, 230 512 0, 179 454 1, 128 396 0, 128 287 1, 128 180 0, 182 122 1, 237 64 0, 335 64 1, 417 64 0, 512 100 1, 320 640 1, 320 704 1, 384 704 1, 384 640 1 +384 64 0 320 576;320 11 1, 265 0 0, 216 0 1, 148 0 0, 106 53 1, 64 107 0, 64 192 1, 64 282 0, 107 333 1, 150 384 0, 228 384 1, 267 384 0, 320 380 1, 320 322 1, 268 320 0, 237 320 1, 128 320 0, 128 191 1, 128 130 0, 156 97 1, 183 64 0, 232 64 1, 270 64 0, 320 72 1, 192 512 1, 192 576 1, 256 576 1, 256 512 1 +576 64 0 512 704;512 30 1, 429 0 0, 334 0 1, 202 0 0, 133 73 1, 64 147 0, 64 287 1, 64 427 0, 134 502 1, 205 576 0, 338 576 1, 413 576 0, 512 566 1, 512 489 1, 394 512 0, 326 512 1, 230 512 0, 179 454 1, 128 396 0, 128 287 1, 128 180 0, 182 122 1, 237 64 0, 335 64 1, 417 64 0, 512 100 1, 457 704 1, 366 576 1, 285 576 1, 194 704 1, 250 704 1, 325 623 1, 326 623 1, 401 704 1 +384 0 0 448 576;320 11 1, 251 0 0, 190 0 1, 104 0 0, 52 53 1, 0 107 0, 0 192 1, 0 282 0, 54 333 1, 108 384 0, 205 384 1, 254 384 0, 320 380 1, 320 322 1, 251 320 0, 209 320 1, 64 320 0, 64 191 1, 64 130 0, 101 97 1, 137 64 0, 203 64 1, 253 64 0, 320 72 1, 387 576 1, 297 512 1, 215 512 1, 125 576 1, 180 576 1, 256 536 1, 256 536 1, 332 576 1 +576 64 0 512 704;64 0 1, 64 576 1, 227 576 1, 512 576 0, 512 301 1, 512 158 0, 438 79 1, 364 0 0, 228 0 1, 128 64 1, 223 64 1, 448 64 0, 448 292 1, 448 426 0, 360 481 1, 335 497 0, 299 504 1, 257 512 0, 186 512 1, 128 512 1, 374 704 1, 284 576 1, 202 576 1, 115 704 1, 167 704 1, 243 623 1, 244 623 1, 319 704 1 +448 64 0 448 576;320 135 1, 320 331 1, 265 320 0, 239 320 1, 128 320 0, 128 185 1, 128 129 0, 150 96 1, 172 64 0, 209 64 1, 258 64 0, 320 72 1, 300 40 0, 277 23 1, 245 0 0, 201 0 1, 140 0 0, 102 50 1, 64 100 0, 64 182 1, 64 278 0, 113 331 1, 162 384 0, 251 384 1, 285 384 0, 320 384 1, 320 576 1, 384 576 1, 384 0 1, 320 0 1, 384 400 1, 384 422 1, 409 430 0, 409 493 1, 409 499 1, 384 499 1, 384 576 1, 448 576 1, 448 510 1, 448 408 0 +576 0 0 512 576;64 0 1, 64 256 1, 0 256 1, 0 320 1, 64 320 1, 64 576 1, 229 576 1, 512 576 0, 512 301 1, 512 158 0, 438 79 1, 365 0 0, 231 0 1, 128 64 1, 225 64 1, 448 64 0, 448 292 1, 448 426 0, 361 481 1, 336 497 0, 300 504 1, 258 512 0, 188 512 1, 128 512 1, 128 320 1, 256 320 1, 256 256 1, 128 256 1 +448 64 0 448 576;320 448 1, 200 448 1, 200 512 1, 320 512 1, 320 576 1, 384 576 1, 384 512 1, 440 512 1, 440 448 1, 384 448 1, 384 0 1, 320 0 1, 320 72 1, 300 40 0, 277 23 1, 245 0 0, 201 0 1, 140 0 0, 102 50 1, 64 100 0, 64 182 1, 64 278 0, 113 331 1, 162 384 0, 251 384 1, 285 384 0, 320 384 1, 320 135 1, 320 331 1, 265 320 0, 239 320 1, 128 320 0, 128 185 1, 128 129 0, 150 96 1, 172 64 0, 209 64 1, 258 64 0 +512 64 0 512 640;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1, 128 576 1, 128 640 1, 384 640 1, 384 576 1 +448 64 0 448 512;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0, 64 448 1, 64 512 1, 320 512 1, 320 448 1 +512 64 0 512 768;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1, 129 743 1, 181 743 1, 190 722 0, 212 713 1, 234 704 0, 269 704 1, 308 704 0, 331 715 1, 349 724 0, 357 743 1, 408 743 1, 401 700 0, 371 674 1, 331 640 0, 268 640 1, 203 640 0, 163 677 1, 136 702 0 +448 64 0 448 640;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0, 93 579 1, 139 579 1, 147 543 0, 167 527 1, 186 512 0, 218 512 1, 253 512 0, 273 531 1, 289 546 0, 296 579 1, 342 579 1, 336 524 0, 309 491 1, 273 448 0, 217 448 1, 159 448 0, 123 495 1, 99 526 0 +512 64 0 512 704;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1, 256 640 1, 256 704 1, 320 704 1, 320 640 1 +448 64 0 448 576;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0, 128 512 1, 128 576 1, 192 576 1, 192 512 1 +512 64 -128 512 576;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1, 410 0 1, 448 0 1, 384 -19 0, 384 -42 1, 384 -64 0, 420 -64 1, 436 -64 0, 448 -98 1, 448 -128 1, 425 -128 0, 396 -128 1, 320 -128 0, 320 -73 1, 320 -31 0 +448 64 -128 448 384;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0, 251 0 1, 291 0 1, 256 -19 0, 256 -42 1, 256 -64 0, 292 -64 1, 308 -64 0, 320 -98 1, 320 -128 1, 297 -128 0, 268 -128 1, 192 -128 0, 192 -73 1, 192 -31 0 +512 64 0 512 704;64 0 1, 64 576 1, 448 576 1, 448 512 1, 128 512 1, 128 320 1, 448 320 1, 448 256 1, 128 256 1, 128 64 1, 512 64 1, 512 0 1, 413 704 1, 312 576 1, 221 576 1, 122 704 1, 182 704 1, 266 623 1, 267 623 1, 351 704 1 +448 0 0 448 576;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0, 323 576 1, 233 512 1, 151 512 1, 61 576 1, 116 576 1, 192 536 1, 192 536 1, 268 576 1 +576 64 0 512 704;512 256 1, 512 14 1, 418 0 0, 330 0 1, 64 0 0, 64 286 1, 64 426 0, 133 501 1, 202 576 0, 332 576 1, 416 576 0, 512 564 1, 512 485 1, 398 512 0, 323 512 1, 128 512 0, 128 289 1, 128 180 0, 183 122 1, 238 64 0, 340 64 1, 382 64 0, 448 57 1, 448 192 1, 384 192 1, 384 256 1, 222 576 1, 312 704 1, 391 704 1, 454 576 1, 415 576 1, 354 657 1, 353 657 1, 277 576 1 +448 64 -192 384 576;320 153 1, 320 331 1, 265 320 0, 240 320 1, 128 320 0, 128 190 1, 128 132 0, 150 98 1, 172 64 0, 209 64 1, 258 64 0, 320 90 1, 300 50 0, 277 29 1, 245 0 0, 202 0 1, 140 0 0, 102 52 1, 64 103 0, 64 186 1, 64 280 0, 113 332 1, 162 384 0, 250 384 1, 285 384 0, 320 384 1, 384 384 1, 384 105 1, 384 22 0, 374 -18 1, 348 -128 0, 194 -128 1, 130 -128 0, 64 -135 1, 64 -71 1, 143 -64 0, 198 -64 1, 320 -64 0, 320 29 1, 119 512 1, 208 576 1, 288 576 1, 370 512 1, 322 512 1, 248 552 1, 248 552 1, 174 512 1 +576 64 0 512 768;512 256 1, 512 14 1, 418 0 0, 330 0 1, 64 0 0, 64 286 1, 64 426 0, 133 501 1, 202 576 0, 332 576 1, 416 576 0, 512 564 1, 512 485 1, 398 512 0, 323 512 1, 128 512 0, 128 289 1, 128 180 0, 183 122 1, 238 64 0, 340 64 1, 382 64 0, 448 57 1, 448 192 1, 384 192 1, 384 256 1, 228 743 1, 274 743 1, 281 722 0, 300 713 1, 317 704 0, 345 704 1, 378 704 0, 396 715 1, 411 724 0, 417 743 1, 449 743 1, 444 700 0, 419 674 1, 388 640 0, 339 640 1, 286 640 0, 255 677 1, 233 702 0 +448 64 -192 384 640;320 153 1, 320 331 1, 265 320 0, 240 320 1, 128 320 0, 128 190 1, 128 132 0, 150 98 1, 172 64 0, 209 64 1, 258 64 0, 320 90 1, 300 50 0, 277 29 1, 245 0 0, 202 0 1, 140 0 0, 102 52 1, 64 103 0, 64 186 1, 64 280 0, 113 332 1, 162 384 0, 250 384 1, 285 384 0, 320 384 1, 384 384 1, 384 105 1, 384 22 0, 374 -18 1, 348 -128 0, 194 -128 1, 130 -128 0, 64 -135 1, 64 -71 1, 143 -64 0, 198 -64 1, 320 -64 0, 320 29 1, 131 602 1, 177 602 1, 185 588 0, 205 582 1, 225 576 0, 256 576 1, 291 576 0, 312 583 1, 328 589 0, 335 602 1, 381 602 1, 375 564 0, 348 542 1, 312 512 0, 256 512 1, 197 512 0, 161 544 1, 137 566 0 +576 64 0 512 704;512 256 1, 512 14 1, 418 0 0, 330 0 1, 64 0 0, 64 286 1, 64 426 0, 133 501 1, 202 576 0, 332 576 1, 416 576 0, 512 564 1, 512 485 1, 398 512 0, 323 512 1, 128 512 0, 128 289 1, 128 180 0, 183 122 1, 238 64 0, 340 64 1, 382 64 0, 448 57 1, 448 192 1, 384 192 1, 384 256 1, 320 640 1, 320 704 1, 384 704 1, 384 640 1 +448 64 -192 384 576;320 153 1, 320 331 1, 265 320 0, 240 320 1, 128 320 0, 128 190 1, 128 132 0, 150 98 1, 172 64 0, 209 64 1, 258 64 0, 320 90 1, 300 50 0, 277 29 1, 245 0 0, 202 0 1, 140 0 0, 102 52 1, 64 103 0, 64 186 1, 64 280 0, 113 332 1, 162 384 0, 250 384 1, 285 384 0, 320 384 1, 384 384 1, 384 105 1, 384 22 0, 374 -18 1, 348 -128 0, 194 -128 1, 130 -128 0, 64 -135 1, 64 -71 1, 143 -64 0, 198 -64 1, 320 -64 0, 320 29 1, 192 512 1, 192 576 1, 256 576 1, 256 512 1 +576 64 -192 512 576;512 256 1, 512 14 1, 417 0 0, 330 0 1, 64 0 0, 64 286 1, 64 426 0, 133 501 1, 202 576 0, 332 576 1, 416 576 0, 512 564 1, 512 485 1, 398 512 0, 323 512 1, 128 512 0, 128 289 1, 128 180 0, 183 122 1, 238 64 0, 340 64 1, 382 64 0, 448 57 1, 448 192 1, 384 192 1, 384 256 1, 291 -158 1, 291 -126 1, 299 -128 0, 305 -128 1, 320 -128 0, 320 -104 1, 320 -77 0, 309 -71 1, 309 -42 1, 343 -43 0, 360 -57 1, 384 -77 0, 384 -121 1, 384 -192 0, 325 -192 1, 309 -192 0 +448 64 -192 384 704;320 153 1, 320 331 1, 265 320 0, 240 320 1, 128 320 0, 128 190 1, 128 132 0, 150 98 1, 172 64 0, 209 64 1, 258 64 0, 320 90 1, 300 50 0, 277 29 1, 245 0 0, 202 0 1, 140 0 0, 102 52 1, 64 103 0, 64 186 1, 64 280 0, 113 332 1, 162 384 0, 250 384 1, 285 384 0, 320 384 1, 384 384 1, 384 105 1, 384 22 0, 374 -18 1, 348 -128 0, 194 -128 1, 130 -128 0, 64 -135 1, 64 -71 1, 143 -64 0, 198 -64 1, 320 -64 0, 320 29 1, 256 682 1, 256 660 1, 231 652 0, 231 592 1, 231 586 1, 256 586 1, 256 512 1, 192 512 1, 192 576 1, 192 674 0 +576 64 0 512 704;64 0 1, 64 576 1, 128 576 1, 128 320 1, 448 320 1, 448 576 1, 512 576 1, 512 0 1, 448 0 1, 448 256 1, 128 256 1, 128 0 1, 134 576 1, 240 704 1, 336 704 1, 441 576 1, 376 576 1, 288 657 1, 287 657 1, 199 576 1 +448 64 0 384 768;64 0 1, 64 576 1, 128 576 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 0 1, 320 0 1, 320 254 1, 320 293 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1, 88 640 1, 179 768 1, 272 768 1, 362 640 1, 312 640 1, 226 721 1, 225 721 1, 139 640 1 +576 0 0 576 576;128 320 1, 448 320 1, 448 384 1, 128 384 1, 64 0 1, 64 384 1, 8 384 1, 8 448 1, 64 448 1, 64 576 1, 128 576 1, 128 448 1, 448 448 1, 448 576 1, 512 576 1, 512 448 1, 568 448 1, 568 384 1, 512 384 1, 512 0 1, 448 0 1, 448 256 1, 128 256 1, 128 0 1 +448 0 0 384 576;64 0 1, 64 448 1, 8 448 1, 8 512 1, 64 512 1, 64 576 1, 128 576 1, 128 512 1, 255 512 1, 255 448 1, 128 448 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 0 1, 320 0 1, 320 254 1, 320 293 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1 +192 -64 0 256 768;64 0 1, 64 576 1, 128 576 1, 128 0 1, -22 623 1, -20 655 0, -11 673 1, 5 704 0, 43 704 1, 68 704 0, 89 704 1, 110 704 1, 130 704 0, 141 704 1, 163 704 0, 167 710 1, 214 710 1, 212 682 0, 203 667 1, 187 640 0, 150 640 1, 125 640 0, 103 640 1, 82 640 1, 63 640 0, 51 640 1, 29 640 0, 25 623 1 +192 -64 0 256 640;64 0 1, 64 384 1, 128 384 1, 128 0 1, -24 480 1, -21 518 0, -13 539 1, 4 576 0, 43 576 1, 69 576 0, 90 576 1, 112 576 1, 132 576 0, 143 576 1, 166 576 0, 170 590 1, 216 590 1, 213 559 0, 205 542 1, 188 512 0, 150 512 1, 124 512 0, 102 512 1, 80 512 1, 61 512 0, 49 512 1, 26 512 0, 22 480 1 +192 0 0 256 640;64 0 1, 64 576 1, 128 576 1, 128 0 1, 0 576 1, 0 640 1, 256 640 1, 256 576 1 +192 -64 0 192 512;64 0 1, 64 384 1, 128 384 1, 128 0 1, -64 448 1, -64 512 1, 192 512 1, 192 448 1 +192 -64 0 256 768;64 0 1, 64 576 1, 128 576 1, 128 0 1, -22 743 1, 25 743 1, 32 722 0, 50 713 1, 68 704 0, 96 704 1, 128 704 0, 146 715 1, 161 724 0, 167 743 1, 214 743 1, 208 700 0, 182 674 1, 149 640 0, 96 640 1, 40 640 0, 7 677 1, -16 702 0 +192 -64 0 256 640;64 0 1, 64 384 1, 128 384 1, 128 0 1, -24 578 1, 22 578 1, 29 542 0, 48 527 1, 67 512 0, 96 512 1, 129 512 0, 148 531 1, 163 546 0, 170 578 1, 216 578 1, 210 524 0, 184 491 1, 150 448 0, 96 448 1, 39 448 0, 5 495 1, -18 526 0 +192 64 -128 192 576;64 0 1, 64 576 1, 128 576 1, 128 0 1, 94 0 1, 126 0 1, 128 -19 0, 128 -42 1, 128 -64 0, 143 -64 1, 150 -64 0, 155 -98 1, 155 -128 1, 139 -128 0, 118 -128 1, 64 -128 0, 64 -73 1, 64 -31 0 +192 0 -128 128 576;64 0 1, 64 384 1, 128 384 1, 128 0 1, 64 512 1, 64 576 1, 128 576 1, 128 512 1, 59 0 1, 99 0 1, 64 -19 0, 64 -42 1, 64 -64 0, 100 -64 1, 116 -64 0, 128 -98 1, 128 -128 1, 105 -128 0, 76 -128 1, 0 -128 0, 0 -73 1, 0 -31 0 +192 64 0 128 704;64 0 1, 64 576 1, 128 576 1, 128 0 1, 64 640 1, 64 704 1, 128 704 1, 128 640 1 +576 64 -128 512 576;64 0 1, 64 576 1, 128 576 1, 128 0 1, 192 -87 1, 192 -19 1, 267 -64 0, 333 -64 1, 408 -64 0, 430 -29 1, 448 0 0, 448 71 1, 448 576 1, 512 576 1, 512 73 1, 512 -128 0, 317 -128 1, 252 -128 0 +320 64 -192 320 576;64 0 1, 64 384 1, 128 384 1, 128 0 1, 64 512 1, 64 576 1, 128 576 1, 128 512 1, 128 -145 1, 128 -87 1, 164 -64 0, 195 -64 1, 238 -64 0, 248 -47 1, 256 -32 0, 256 0 1, 256 384 1, 320 384 1, 320 0 1, 320 -128 0, 194 -128 1, 159 -128 0, 256 512 1, 256 576 1, 320 576 1, 320 512 1 +384 0 -128 448 704;0 -87 1, 0 -19 1, 75 -64 0, 141 -64 1, 216 -64 0, 238 -29 1, 256 0 0, 256 71 1, 256 576 1, 320 576 1, 320 73 1, 320 -128 0, 125 -128 1, 60 -128 0, 137 576 1, 244 704 1, 314 704 1, 403 576 1, 347 576 1, 281 657 1, 280 657 1, 203 576 1 +192 -64 -192 256 576;-64 -145 1, -64 -87 1, -28 -64 0, 3 -64 1, 46 -64 0, 56 -47 1, 64 -32 0, 64 0 1, 64 384 1, 128 384 1, 128 0 1, 128 -128 0, 2 -128 1, -33 -128 0, -55 512 1, 52 576 1, 126 576 1, 216 512 1, 160 512 1, 91 552 1, 90 552 1, 11 512 1 +512 64 -192 512 576;64 0 1, 64 576 1, 128 576 1, 128 293 1, 359 576 1, 438 576 1, 214 301 1, 476 0 1, 377 0 1, 128 292 1, 128 0 1, 165 -158 1, 165 -126 1, 190 -128 0, 208 -128 1, 256 -128 0, 256 -104 1, 256 -77 0, 184 -71 1, 184 -42 1, 246 -43 0, 277 -57 1, 320 -77 0, 320 -121 1, 320 -192 0, 222 -192 1, 195 -192 0 +384 64 -192 384 576;64 0 1, 64 576 1, 128 576 1, 128 198 1, 265 384 1, 335 384 1, 205 203 1, 374 0 1, 284 0 1, 128 197 1, 128 0 1, 128 -158 1, 128 -126 1, 146 -128 0, 158 -128 1, 192 -128 0, 192 -104 1, 192 -77 0, 146 -71 1, 146 -42 1, 196 -43 0, 221 -57 1, 256 -77 0, 256 -121 1, 256 -192 0, 175 -192 1, 153 -192 0 +384 64 0 384 384;64 0 1, 64 384 1, 128 384 1, 128 198 1, 265 384 1, 335 384 1, 205 203 1, 374 0 1, 284 0 1, 128 197 1, 128 0 1 +448 64 0 384 704;64 0 1, 64 576 1, 128 576 1, 128 64 1, 384 64 1, 384 0 1, 68 576 1, 143 704 1, 224 704 1, 113 576 1 +192 0 0 256 704;64 0 1, 64 576 1, 128 576 1, 128 0 1, 40 640 1, 131 704 1, 216 704 1, 96 640 1 +448 64 -192 384 576;64 0 1, 64 576 1, 128 576 1, 128 64 1, 384 64 1, 384 0 1, 154 -158 1, 154 -126 1, 164 -128 0, 172 -128 1, 192 -128 0, 192 -104 1, 192 -77 0, 171 -71 1, 171 -42 1, 210 -43 0, 229 -57 1, 256 -77 0, 256 -121 1, 256 -192 0, 191 -192 1, 174 -192 0 +192 0 -192 128 576;64 0 1, 64 576 1, 128 576 1, 128 0 1, 0 -158 1, 0 -126 1, 18 -128 0, 30 -128 1, 64 -128 0, 64 -104 1, 64 -77 0, 18 -71 1, 18 -42 1, 68 -43 0, 93 -57 1, 128 -77 0, 128 -121 1, 128 -192 0, 47 -192 1, 25 -192 0 +448 64 0 384 576;64 0 1, 64 576 1, 128 576 1, 128 64 1, 384 64 1, 384 0 1, 256 400 1, 256 423 1, 281 431 0, 281 493 1, 281 500 1, 256 500 1, 256 576 1, 320 576 1, 320 510 1, 320 408 0 +256 64 0 256 576;64 0 1, 64 576 1, 128 576 1, 128 0 1, 192 410 1, 192 432 1, 217 440 0, 217 498 1, 217 504 1, 192 504 1, 192 576 1, 256 576 1, 256 514 1, 256 418 0 +448 64 0 384 576;64 0 1, 64 576 1, 128 576 1, 128 64 1, 384 64 1, 384 0 1, 320 256 1, 320 320 1, 384 320 1, 384 256 1 +256 64 0 256 576;64 0 1, 64 576 1, 128 576 1, 128 0 1, 192 256 1, 192 320 1, 256 320 1, 256 256 1 +448 0 0 384 576;64 0 1, 64 271 1, 0 240 1, 0 305 1, 64 337 1, 64 576 1, 128 576 1, 128 381 1, 256 433 1, 256 367 1, 128 316 1, 128 64 1, 384 64 1, 384 0 1 +192 0 0 192 576;64 0 1, 64 263 1, 0 237 1, 0 297 1, 64 324 1, 64 576 1, 128 576 1, 128 364 1, 192 388 1, 192 329 1, 128 303 1, 128 0 1 +576 64 0 512 704;64 0 1, 64 576 1, 138 576 1, 448 131 1, 448 576 1, 512 576 1, 512 0 1, 437 0 1, 128 445 1, 128 0 1, 228 576 1, 326 704 1, 418 704 1, 288 576 1 +448 64 0 384 576;64 0 1, 64 384 1, 128 384 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 0 1, 320 0 1, 320 253 1, 320 292 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1, 168 512 1, 259 576 1, 344 576 1, 224 512 1 +576 64 -192 512 576;64 0 1, 64 576 1, 138 576 1, 448 131 1, 448 576 1, 512 576 1, 512 0 1, 437 0 1, 128 445 1, 128 0 1, 198 -158 1, 198 -126 1, 214 -128 0, 225 -128 1, 256 -128 0, 256 -104 1, 256 -77 0, 218 -71 1, 218 -42 1, 265 -43 0, 288 -57 1, 320 -77 0, 320 -121 1, 320 -192 0, 243 -192 1, 222 -192 0 +448 64 -192 384 384;64 0 1, 64 384 1, 128 384 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 0 1, 320 0 1, 320 253 1, 320 292 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1, 128 -158 1, 128 -126 1, 146 -128 0, 158 -128 1, 192 -128 0, 192 -104 1, 192 -77 0, 146 -71 1, 146 -42 1, 196 -43 0, 221 -57 1, 256 -77 0, 256 -121 1, 256 -192 0, 175 -192 1, 153 -192 0 +576 64 0 512 704;64 0 1, 64 576 1, 138 576 1, 448 131 1, 448 576 1, 512 576 1, 512 0 1, 437 0 1, 128 445 1, 128 0 1, 430 704 1, 332 576 1, 244 576 1, 146 704 1, 206 704 1, 288 623 1, 288 623 1, 370 704 1 +448 0 0 384 576;64 0 1, 64 384 1, 128 384 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 0 1, 320 0 1, 320 253 1, 320 292 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1, 323 576 1, 233 512 1, 151 512 1, 61 576 1, 116 576 1, 192 536 1, 192 536 1, 268 576 1 +448 0 0 384 576;64 0 1, 64 384 1, 128 384 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 0 1, 320 0 1, 320 253 1, 320 292 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1, 0 399 1, 0 422 1, 25 430 0, 25 493 1, 25 499 1, 0 499 1, 0 576 1, 64 576 1, 64 509 1, 64 407 0 +576 64 -192 512 576;64 0 1, 64 576 1, 138 576 1, 448 131 1, 448 576 1, 512 576 1, 512 -35 1, 512 -128 0, 382 -128 1, 352 -128 0, 320 -150 1, 320 -92 1, 349 -64 0, 382 -64 1, 448 -64 0, 448 -21 1, 448 -15 1, 128 445 1, 128 0 1 +448 64 -192 384 384;64 0 1, 64 384 1, 128 384 1, 128 312 1, 157 344 0, 185 360 1, 226 384 0, 275 384 1, 384 384 0, 384 276 1, 384 -35 1, 384 -128 0, 254 -128 1, 224 -128 0, 192 -150 1, 192 -92 1, 223 -64 0, 250 -64 1, 320 -64 0, 320 -21 1, 320 253 1, 320 292 0, 308 306 1, 296 320 0, 264 320 1, 194 320 0, 128 249 1, 128 0 1 +576 64 0 576 640;320 576 1, 436 576 0, 506 498 1, 576 419 0, 576 289 1, 576 156 0, 506 78 1, 436 0 0, 316 0 1, 214 0 0, 147 64 1, 64 145 0, 64 288 1, 64 420 0, 134 498 1, 204 576 0, 320 512 1, 229 512 0, 179 453 1, 128 394 0, 128 288 1, 128 183 0, 179 124 1, 229 64 0, 318 64 1, 401 64 0, 450 112 1, 512 171 0, 512 289 1, 512 394 0, 461 453 1, 410 512 0, 192 576 1, 192 640 1, 448 640 1, 448 576 1 +448 64 0 384 512;224 384 1, 298 384 0, 341 333 1, 384 281 0, 384 193 1, 384 102 0, 341 51 1, 298 0 0, 222 0 1, 156 0 0, 116 42 1, 64 95 0, 64 192 1, 64 281 0, 107 333 1, 150 384 0, 224 320 1, 128 320 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 193 1, 320 320 0, 64 448 1, 64 512 1, 320 512 1, 320 448 1 +576 64 0 576 768;320 576 1, 436 576 0, 506 498 1, 576 419 0, 576 289 1, 576 156 0, 506 78 1, 436 0 0, 316 0 1, 214 0 0, 147 64 1, 64 145 0, 64 288 1, 64 420 0, 134 498 1, 204 576 0, 320 512 1, 229 512 0, 179 453 1, 128 394 0, 128 288 1, 128 183 0, 179 124 1, 229 64 0, 318 64 1, 401 64 0, 450 112 1, 512 171 0, 512 289 1, 512 394 0, 461 453 1, 410 512 0, 174 743 1, 220 743 1, 228 722 0, 248 713 1, 268 704 0, 299 704 1, 334 704 0, 355 715 1, 371 724 0, 378 743 1, 424 743 1, 418 700 0, 391 674 1, 355 640 0, 299 640 1, 240 640 0, 204 677 1, 180 702 0 +448 64 0 384 640;224 384 1, 298 384 0, 341 333 1, 384 281 0, 384 193 1, 384 102 0, 341 51 1, 298 0 0, 222 0 1, 156 0 0, 116 42 1, 64 95 0, 64 192 1, 64 281 0, 107 333 1, 150 384 0, 224 320 1, 128 320 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 193 1, 320 320 0, 89 579 1, 135 579 1, 143 543 0, 163 527 1, 182 512 0, 214 512 1, 249 512 0, 269 531 1, 285 546 0, 292 579 1, 338 579 1, 332 524 0, 305 491 1, 269 448 0, 213 448 1, 155 448 0, 119 495 1, 95 526 0 +576 64 0 576 704;320 576 1, 436 576 0, 506 498 1, 576 419 0, 576 289 1, 576 156 0, 506 78 1, 436 0 0, 316 0 1, 214 0 0, 147 64 1, 64 145 0, 64 288 1, 64 420 0, 134 498 1, 204 576 0, 320 512 1, 229 512 0, 179 453 1, 128 394 0, 128 288 1, 128 183 0, 179 124 1, 229 64 0, 318 64 1, 401 64 0, 450 112 1, 512 171 0, 512 289 1, 512 394 0, 461 453 1, 410 512 0, 212 612 1, 303 704 1, 374 704 1, 254 612 1, 344 612 1, 434 704 1, 506 704 1, 386 612 1 +448 64 0 448 576;224 384 1, 298 384 0, 341 333 1, 384 281 0, 384 193 1, 384 102 0, 341 51 1, 298 0 0, 222 0 1, 156 0 0, 116 42 1, 64 95 0, 64 192 1, 64 281 0, 107 333 1, 150 384 0, 224 320 1, 128 320 0, 128 192 1, 128 64 0, 224 64 1, 320 64 0, 320 193 1, 320 320 0, 109 456 1, 199 576 1, 271 576 1, 151 456 1, 241 456 1, 331 576 1, 403 576 1, 283 456 1 +576 64 0 576 704;64 0 1, 64 576 1, 281 576 1, 448 576 0, 448 439 1, 448 372 0, 408 329 1, 384 303 0, 340 283 1, 525 0 1, 428 0 1, 271 256 1, 128 256 1, 128 0 1, 128 320 1, 216 320 1, 303 320 0, 343 346 1, 384 373 0, 384 429 1, 384 474 0, 351 493 1, 318 512 0, 241 512 1, 128 512 1, 184 576 1, 274 704 1, 359 704 1, 239 576 1 +256 64 0 320 576;64 0 1, 64 384 1, 128 384 1, 128 312 1, 145 345 0, 165 361 1, 194 384 0, 233 384 1, 241 384 0, 256 391 1, 256 326 1, 235 320 0, 222 320 1, 178 320 0, 128 253 1, 128 0 1, 104 512 1, 195 576 1, 280 576 1, 160 512 1 +576 64 -192 576 576;64 0 1, 64 576 1, 281 576 1, 448 576 0, 448 439 1, 448 372 0, 408 329 1, 384 303 0, 340 283 1, 525 0 1, 428 0 1, 271 256 1, 128 256 1, 128 0 1, 128 320 1, 216 320 1, 303 320 0, 343 346 1, 384 373 0, 384 429 1, 384 474 0, 351 493 1, 318 512 0, 241 512 1, 128 512 1, 184 -158 1, 184 -126 1, 204 -128 0, 218 -128 1, 256 -128 0, 256 -104 1, 256 -77 0, 202 -71 1, 202 -42 1, 256 -43 0, 283 -57 1, 320 -77 0, 320 -121 1, 320 -192 0, 234 -192 1, 210 -192 0 +256 64 -192 256 448;64 0 1, 64 384 1, 128 384 1, 128 312 1, 145 345 0, 165 361 1, 194 384 0, 233 384 1, 241 384 0, 256 391 1, 256 326 1, 235 320 0, 222 320 1, 178 320 0, 128 253 1, 128 0 1, 64 -158 1, 64 -126 1, 82 -128 0, 94 -128 1, 128 -128 0, 128 -104 1, 128 -77 0, 82 -71 1, 82 -42 1, 132 -43 0, 157 -57 1, 192 -77 0, 192 -121 1, 192 -192 0, 111 -192 1, 89 -192 0 +576 64 0 576 704;64 0 1, 64 576 1, 281 576 1, 448 576 0, 448 439 1, 448 372 0, 408 329 1, 384 303 0, 340 283 1, 525 0 1, 428 0 1, 271 256 1, 128 256 1, 128 0 1, 128 320 1, 216 320 1, 303 320 0, 343 346 1, 384 373 0, 384 429 1, 384 474 0, 351 493 1, 318 512 0, 241 512 1, 128 512 1, 356 704 1, 265 576 1, 184 576 1, 100 704 1, 149 704 1, 224 623 1, 225 623 1, 300 704 1 +256 -64 0 320 576;64 0 1, 64 384 1, 128 384 1, 128 312 1, 145 345 0, 165 361 1, 194 384 0, 233 384 1, 241 384 0, 256 391 1, 256 326 1, 235 320 0, 222 320 1, 178 320 0, 128 253 1, 128 0 1, 259 576 1, 169 512 1, 87 512 1, -3 576 1, 52 576 1, 128 536 1, 128 536 1, 204 576 1 +512 64 0 512 704;64 21 1, 64 102 1, 189 64 0, 311 64 1, 448 64 0, 448 152 1, 448 197 0, 410 218 1, 381 235 0, 315 253 1, 229 278 1, 64 324 0, 64 431 1, 64 576 0, 267 576 1, 355 576 0, 448 566 1, 448 491 1, 347 512 0, 255 512 1, 128 512 0, 128 431 1, 128 399 0, 154 379 1, 180 359 0, 247 340 1, 334 316 1, 432 288 0, 472 252 1, 512 216 0, 512 156 1, 512 84 0, 454 42 1, 396 0 0, 294 0 1, 193 0 0, 214 576 1, 304 704 1, 389 704 1, 269 576 1 +384 64 0 384 576;64 13 1, 64 77 1, 128 64 0, 183 64 1, 256 64 0, 256 116 1, 256 152 0, 204 168 1, 147 187 1, 64 214 0, 64 286 1, 64 384 0, 215 384 1, 258 384 0, 320 381 1, 320 323 1, 263 320 0, 206 320 1, 128 320 0, 128 276 1, 128 244 0, 174 230 1, 225 213 1, 320 182 0, 320 106 1, 320 57 0, 283 29 1, 245 0 0, 180 0 1, 129 0 0, 168 512 1, 259 576 1, 344 576 1, 224 512 1 +512 64 0 512 704;64 21 1, 64 102 1, 189 64 0, 311 64 1, 448 64 0, 448 152 1, 448 197 0, 410 218 1, 381 235 0, 315 253 1, 229 278 1, 64 324 0, 64 431 1, 64 576 0, 267 576 1, 355 576 0, 448 566 1, 448 491 1, 347 512 0, 255 512 1, 128 512 0, 128 431 1, 128 399 0, 154 379 1, 180 359 0, 247 340 1, 334 316 1, 432 288 0, 472 252 1, 512 216 0, 512 156 1, 512 84 0, 454 42 1, 396 0 0, 294 0 1, 193 0 0, 139 576 1, 229 704 1, 310 704 1, 400 576 1, 344 576 1, 270 657 1, 269 657 1, 194 576 1 +384 64 0 384 576;64 13 1, 64 77 1, 128 64 0, 183 64 1, 256 64 0, 256 116 1, 256 152 0, 204 168 1, 147 187 1, 64 214 0, 64 286 1, 64 384 0, 215 384 1, 258 384 0, 320 381 1, 320 323 1, 263 320 0, 206 320 1, 128 320 0, 128 276 1, 128 244 0, 174 230 1, 225 213 1, 320 182 0, 320 106 1, 320 57 0, 283 29 1, 245 0 0, 180 0 1, 129 0 0, 94 512 1, 181 576 1, 260 576 1, 348 512 1, 293 512 1, 221 552 1, 220 552 1, 147 512 1 +512 64 -192 512 576;64 21 1, 64 102 1, 189 64 0, 311 64 1, 448 64 0, 448 152 1, 448 197 0, 410 218 1, 381 235 0, 315 253 1, 229 278 1, 64 324 0, 64 431 1, 64 576 0, 267 576 1, 355 576 0, 448 566 1, 448 491 1, 347 512 0, 255 512 1, 128 512 0, 128 431 1, 128 399 0, 154 379 1, 180 359 0, 247 340 1, 334 316 1, 432 288 0, 472 252 1, 512 216 0, 512 157 1, 512 84 0, 454 42 1, 396 0 0, 294 0 1, 193 0 0, 243 0 1, 279 0 1, 257 -41 1, 280 -42 0, 297 -59 1, 320 -82 0, 320 -116 1, 320 -148 0, 300 -170 1, 279 -192 0, 250 -192 1, 227 -192 0, 200 -154 1, 200 -124 1, 213 -128 0, 228 -128 1, 256 -128 0, 256 -101 1, 256 -67 0, 208 -66 1 +384 64 -192 320 384;64 13 1, 64 77 1, 128 64 0, 183 64 1, 256 64 0, 256 116 1, 256 152 0, 204 168 1, 147 187 1, 64 214 0, 64 286 1, 64 384 0, 215 384 1, 258 384 0, 320 381 1, 320 323 1, 263 320 0, 206 320 1, 128 320 0, 128 276 1, 128 244 0, 174 230 1, 225 213 1, 320 182 0, 320 106 1, 320 57 0, 283 29 1, 245 0 0, 180 0 1, 129 0 0, 171 0 1, 207 0 1, 184 -41 1, 211 -42 0, 230 -59 1, 256 -82 0, 256 -116 1, 256 -148 0, 234 -170 1, 213 -192 0, 180 -192 1, 155 -192 0, 127 -154 1, 127 -124 1, 143 -128 0, 160 -128 1, 192 -128 0, 192 -101 1, 192 -67 0, 135 -66 1 +512 64 0 512 704;64 21 1, 64 102 1, 189 64 0, 311 64 1, 448 64 0, 448 152 1, 448 197 0, 410 218 1, 381 235 0, 315 253 1, 229 278 1, 64 324 0, 64 431 1, 64 576 0, 267 576 1, 355 576 0, 448 566 1, 448 491 1, 347 512 0, 255 512 1, 128 512 0, 128 431 1, 128 399 0, 154 379 1, 180 359 0, 247 340 1, 334 316 1, 432 288 0, 472 252 1, 512 216 0, 512 156 1, 512 84 0, 454 42 1, 396 0 0, 294 0 1, 193 0 0, 400 704 1, 310 576 1, 229 576 1, 139 704 1, 194 704 1, 269 623 1, 270 623 1, 344 704 1 +384 0 0 384 576;64 13 1, 64 77 1, 128 64 0, 183 64 1, 256 64 0, 256 116 1, 256 152 0, 204 168 1, 147 187 1, 64 214 0, 64 286 1, 64 384 0, 215 384 1, 258 384 0, 320 381 1, 320 323 1, 263 320 0, 206 320 1, 128 320 0, 128 276 1, 128 244 0, 174 230 1, 225 213 1, 320 182 0, 320 106 1, 320 57 0, 283 29 1, 245 0 0, 180 0 1, 129 0 0, 323 576 1, 233 512 1, 151 512 1, 61 576 1, 116 576 1, 192 536 1, 192 536 1, 268 576 1 +448 0 -192 448 576;192 0 1, 192 512 1, 0 512 1, 0 576 1, 448 576 1, 448 512 1, 256 512 1, 256 0 1, 212 0 1, 242 0 1, 223 -41 1, 259 -42 0, 285 -59 1, 320 -82 0, 320 -115 1, 320 -148 0, 295 -170 1, 270 -192 0, 234 -192 1, 205 -192 0, 173 -154 1, 173 -124 1, 193 -128 0, 214 -128 1, 256 -128 0, 256 -101 1, 256 -67 0, 181 -66 1 +192 0 -192 192 512;192 -2 1, 172 0 0, 154 0 1, 64 0 0, 64 103 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 458 1, 128 465 1, 128 384 1, 192 384 1, 192 320 1, 128 320 1, 128 115 1, 128 84 0, 136 74 1, 144 64 0, 168 64 1, 182 64 0, 192 45 1, 112 0 1, 145 0 1, 124 -41 1, 149 -42 0, 167 -59 1, 192 -82 0, 192 -115 1, 192 -148 0, 172 -170 1, 152 -192 0, 123 -192 1, 100 -192 0, 74 -154 1, 74 -124 1, 87 -128 0, 101 -128 1, 128 -128 0, 128 -101 1, 128 -67 0, 81 -66 1 +448 0 0 448 704;192 0 1, 192 512 1, 0 512 1, 0 576 1, 448 576 1, 448 512 1, 256 512 1, 256 0 1, 350 704 1, 258 576 1, 190 576 1, 98 704 1, 155 704 1, 224 623 1, 224 623 1, 293 704 1 +320 0 -64 256 640;192 -2 1, 172 0 0, 154 0 1, 64 0 0, 64 103 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 468 1, 128 476 1, 128 384 1, 192 384 1, 192 320 1, 128 320 1, 128 115 1, 128 84 0, 136 74 1, 144 64 0, 168 64 1, 182 64 0, 192 45 1, 192 447 1, 192 472 1, 208 481 0, 208 549 1, 208 556 1, 192 556 1, 192 640 1, 256 640 1, 256 567 1, 256 456 0 +448 0 0 448 576;192 0 1, 192 256 1, 64 256 1, 64 320 1, 192 320 1, 192 512 1, 0 512 1, 0 576 1, 448 576 1, 448 512 1, 256 512 1, 256 320 1, 384 320 1, 384 256 1, 256 256 1, 256 0 1 +192 0 -64 192 512;64 192 1, 0 192 1, 0 256 1, 64 256 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 458 1, 128 465 1, 128 384 1, 192 384 1, 192 320 1, 128 320 1, 128 256 1, 192 256 1, 192 192 1, 128 192 1, 128 114 1, 128 84 0, 136 74 1, 144 64 0, 168 64 1, 182 64 0, 192 45 1, 192 -2 1, 172 0 0, 154 0 1, 64 0 0, 64 102 1 +576 64 0 512 768;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 146 623 1, 149 655 0, 160 673 1, 179 704 0, 226 704 1, 256 704 0, 282 704 1, 308 704 1, 332 704 0, 345 704 1, 372 704 0, 377 710 1, 430 710 1, 427 682 0, 416 667 1, 397 640 0, 351 640 1, 321 640 0, 294 640 1, 268 640 1, 245 640 0, 231 640 1, 204 640 0, 199 623 1 +448 64 0 384 640;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 93 480 1, 96 519 0, 106 539 1, 125 576 0, 169 576 1, 198 576 0, 223 576 1, 248 576 1, 271 576 0, 283 576 1, 310 576 0, 314 590 1, 355 590 1, 352 559 0, 342 542 1, 324 512 0, 280 512 1, 250 512 0, 225 512 1, 200 512 1, 178 512 0, 165 512 1, 138 512 0, 134 480 1 +576 64 0 512 640;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 192 576 1, 192 640 1, 384 640 1, 384 576 1 +448 64 0 384 512;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 64 448 1, 64 512 1, 320 512 1, 320 448 1 +576 64 0 512 768;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 146 743 1, 199 743 1, 208 722 0, 231 713 1, 253 704 0, 288 704 1, 328 704 0, 351 715 1, 369 724 0, 377 743 1, 430 743 1, 423 700 0, 392 674 1, 352 640 0, 288 640 1, 221 640 0, 181 677 1, 153 702 0 +448 64 0 384 640;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 93 578 1, 134 578 1, 143 543 0, 166 527 1, 188 512 0, 224 512 1, 264 512 0, 287 531 1, 306 546 0, 314 578 1, 355 578 1, 349 524 0, 320 491 1, 283 448 0, 224 448 1, 162 448 0, 125 495 1, 100 526 0 +576 64 0 512 768;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 288 768 1, 328 768 0, 356 749 1, 384 731 0, 384 704 1, 384 677 0, 356 659 1, 328 640 0, 287 640 1, 252 640 0, 226 655 1, 192 675 0, 192 704 1, 192 731 0, 220 749 1, 248 768 0, 288 704 1, 275 704 0, 265 704 1, 256 704 0, 256 704 1, 256 704 0, 265 704 1, 275 704 0, 288 704 1, 300 704 0, 309 704 1, 320 704 0, 320 704 1, 320 704 0, 310 704 1, 301 704 0 +448 64 0 384 640;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 192 640 1, 219 640 0, 237 621 1, 256 603 0, 256 576 1, 256 549 0, 237 531 1, 219 512 0, 191 512 1, 168 512 0, 151 527 1, 128 547 0, 128 576 1, 128 603 0, 147 621 1, 165 640 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0, 192 576 1, 192 576 0 +576 64 0 512 704;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 180 612 1, 283 704 1, 364 704 1, 228 612 1, 330 612 1, 432 704 1, 502 704 1, 377 612 1 +448 64 0 448 576;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 109 456 1, 199 576 1, 271 576 1, 151 456 1, 241 456 1, 331 576 1, 403 576 1, 283 456 1 +576 64 -128 512 576;64 576 1, 128 576 1, 128 213 1, 128 158 0, 139 132 1, 150 106 0, 180 88 1, 223 64 0, 295 64 1, 379 64 0, 414 97 1, 448 129 0, 448 210 1, 448 576 1, 512 576 1, 512 211 1, 512 139 0, 497 102 1, 482 64 0, 440 37 1, 385 0 0, 292 0 1, 174 0 0, 119 51 1, 64 102 0, 64 214 1, 308 0 1, 354 0 1, 320 -19 0, 320 -42 1, 320 -64 0, 358 -64 1, 375 -64 0, 387 -98 1, 387 -128 1, 363 -128 0, 333 -128 1, 256 -128 0, 256 -73 1, 256 -31 0 +448 64 -128 384 384;320 0 1, 320 72 1, 291 40 0, 263 24 1, 222 0 0, 174 0 1, 64 0 0, 64 108 1, 64 384 1, 128 384 1, 128 131 1, 128 92 0, 140 78 1, 152 64 0, 184 64 1, 254 64 0, 320 135 1, 320 384 1, 384 384 1, 384 0 1, 315 0 1, 355 0 1, 320 -19 0, 320 -42 1, 320 -64 0, 356 -64 1, 372 -64 0, 384 -98 1, 384 -128 1, 361 -128 0, 332 -128 1, 256 -128 0, 256 -73 1, 256 -31 0 +704 0 0 768 704;152 0 1, 9 576 1, 85 576 1, 199 121 1, 329 576 1, 405 576 1, 530 125 1, 651 576 1, 716 576 1, 560 0 1, 482 0 1, 358 444 1, 230 0 1, 236 576 1, 326 704 1, 408 704 1, 498 576 1, 443 576 1, 367 657 1, 366 657 1, 291 576 1 +576 0 0 576 576;102 0 1, 4 384 1, 77 384 1, 150 95 1, 244 384 1, 318 384 1, 400 94 1, 486 384 1, 549 384 1, 435 0 1, 361 0 1, 275 297 1, 177 0 1, 149 512 1, 240 576 1, 321 576 1, 412 512 1, 356 512 1, 281 552 1, 280 552 1, 205 512 1 +512 -64 0 512 704;192 0 1, 192 240 1, -7 576 1, 83 576 1, 232 309 1, 395 576 1, 468 576 1, 256 242 1, 256 0 1, 115 576 1, 203 704 1, 273 704 1, 363 576 1, 307 576 1, 236 657 1, 236 657 1, 171 576 1 +384 0 -128 384 576;152 0 1, 7 384 1, 82 384 1, 193 90 1, 314 384 1, 380 384 1, 164 -128 1, 87 -128 1, 67 512 1, 157 576 1, 239 576 1, 329 512 1, 274 512 1, 198 552 1, 198 552 1, 122 512 1 +448 64 0 448 704;64 0 1, 64 64 1, 351 512 1, 64 512 1, 64 576 1, 448 576 1, 448 512 1, 142 64 1, 448 64 1, 448 0 1, 190 576 1, 283 704 1, 370 704 1, 247 576 1 +384 0 0 384 576;0 0 1, 0 64 1, 291 320 1, 64 320 1, 64 384 1, 384 384 1, 384 320 1, 145 64 1, 384 64 1, 384 0 1, 168 512 1, 259 576 1, 344 576 1, 224 512 1 +448 64 0 448 704;64 0 1, 64 64 1, 331 512 1, 64 512 1, 64 576 1, 448 576 1, 448 512 1, 131 64 1, 448 64 1, 448 0 1, 192 640 1, 192 704 1, 256 704 1, 256 640 1 +384 0 0 384 576;0 0 1, 0 64 1, 291 320 1, 64 320 1, 64 384 1, 384 384 1, 384 320 1, 145 64 1, 384 64 1, 384 0 1, 128 512 1, 128 576 1, 192 576 1, 192 512 1 +448 64 0 448 704;64 0 1, 64 64 1, 351 512 1, 64 512 1, 64 576 1, 448 576 1, 448 512 1, 142 64 1, 448 64 1, 448 0 1, 386 704 1, 293 576 1, 209 576 1, 117 704 1, 174 704 1, 251 623 1, 252 623 1, 329 704 1 +384 0 0 384 576;0 0 1, 0 64 1, 291 320 1, 64 320 1, 64 384 1, 384 384 1, 384 320 1, 145 64 1, 384 64 1, 384 0 1, 323 576 1, 233 512 1, 151 512 1, 61 576 1, 116 576 1, 192 536 1, 192 536 1, 268 576 1 +192 0 0 192 640;64 0 1, 64 352 1, 0 352 1, 0 407 1, 64 407 1, 64 456 1, 64 513 0, 91 544 1, 119 576 0, 169 576 1, 176 576 0, 192 600 1, 192 545 1, 181 512 0, 175 512 1, 128 512 0, 128 464 1, 128 0 1 +512 64 -192 512 576;64 21 1, 64 102 1, 189 64 0, 311 64 1, 448 64 0, 448 152 1, 448 197 0, 410 218 1, 381 235 0, 315 253 1, 229 278 1, 64 324 0, 64 431 1, 64 576 0, 267 576 1, 355 576 0, 448 566 1, 448 491 1, 347 512 0, 255 512 1, 128 512 0, 128 431 1, 128 399 0, 154 379 1, 180 359 0, 247 340 1, 334 316 1, 432 288 0, 472 252 1, 512 216 0, 512 157 1, 512 84 0, 454 42 1, 396 0 0, 294 0 1, 193 0 0, 214 -158 1, 214 -126 1, 226 -128 0, 234 -128 1, 256 -128 0, 256 -104 1, 256 -77 0, 233 -71 1, 233 -42 1, 273 -43 0, 292 -57 1, 320 -77 0, 320 -121 1, 320 -192 0, 253 -192 1, 234 -192 0 +384 64 -192 320 384;64 13 1, 64 77 1, 128 64 0, 183 64 1, 256 64 0, 256 116 1, 256 152 0, 204 168 1, 147 187 1, 64 214 0, 64 286 1, 64 384 0, 215 384 1, 258 384 0, 320 381 1, 320 323 1, 263 320 0, 206 320 1, 128 320 0, 128 276 1, 128 244 0, 174 230 1, 225 213 1, 320 182 0, 320 106 1, 320 57 0, 283 29 1, 245 0 0, 180 0 1, 129 0 0, 128 -158 1, 128 -126 1, 146 -128 0, 158 -128 1, 192 -128 0, 192 -104 1, 192 -77 0, 146 -71 1, 146 -42 1, 196 -43 0, 221 -57 1, 256 -77 0, 256 -121 1, 256 -192 0, 175 -192 1, 153 -192 0 +448 0 -192 448 576;192 0 1, 192 512 1, 0 512 1, 0 576 1, 448 576 1, 448 512 1, 256 512 1, 256 0 1, 173 -158 1, 173 -126 1, 196 -128 0, 212 -128 1, 256 -128 0, 256 -104 1, 256 -77 0, 192 -71 1, 192 -42 1, 251 -43 0, 279 -57 1, 320 -77 0, 320 -121 1, 320 -192 0, 227 -192 1, 201 -192 0 +192 0 -192 192 512;192 -2 1, 172 0 0, 154 0 1, 64 0 0, 64 103 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 458 1, 128 465 1, 128 384 1, 192 384 1, 192 320 1, 128 320 1, 128 115 1, 128 84 0, 136 74 1, 144 64 0, 168 64 1, 182 64 0, 192 45 1, 64 -158 1, 64 -126 1, 82 -128 0, 94 -128 1, 128 -128 0, 128 -104 1, 128 -77 0, 82 -71 1, 82 -42 1, 132 -43 0, 157 -57 1, 192 -77 0, 192 -121 1, 192 -192 0, 111 -192 1, 89 -192 0 +256 64 -192 192 0;64 -158 1, 64 -126 1, 82 -128 0, 94 -128 1, 128 -128 0, 128 -104 1, 128 -77 0, 82 -71 1, 82 -42 1, 132 -43 0, 157 -57 1, 192 -77 0, 192 -121 1, 192 -192 0, 111 -192 1, 89 -192 0 +192 64 -128 128 384;64 -120 1, 64 -93 1, 87 -79 0, 87 -9 1, 87 0 1, 64 0 1, 64 64 1, 128 64 1, 128 10 1, 128 -102 0, 64 320 1, 64 384 1, 128 384 1, 128 320 1 +192 64 192 128 256;64 192 1, 64 256 1, 128 256 1, 128 192 1 +256 64 192 192 256;64 192 1, 64 256 1, 192 256 1, 192 192 1 +256 64 192 192 256;64 192 1, 64 256 1, 192 256 1, 192 192 1 +448 0 192 448 256;38 192 1, 38 256 1, 390 256 1, 390 192 1 +768 0 192 768 256;37 192 1, 37 256 1, 731 256 1, 731 192 1 +192 0 0 0 0; +448 64 512 384 576;64 512 1, 64 576 1, 384 576 1, 384 512 1 +448 64 0 448 384;311 256 1, 310 277 0, 303 287 1, 284 320 0, 219 320 1, 173 320 0, 147 305 1, 121 290 0, 115 256 1, 384 72 1, 384 13 1, 314 0 0, 256 0 1, 168 0 0, 116 53 1, 64 107 0, 64 197 1, 64 283 0, 110 333 1, 156 384 0, 234 384 1, 323 384 0, 360 325 1, 387 281 0, 386 212 1, 386 192 1, 114 192 1, 119 153 0, 129 132 1, 162 64 0, 260 64 1, 316 64 0 +448 64 192 384 256;64 192 1, 64 256 1, 384 256 1, 384 192 1 +128 -192 -64 320 576;-165 -14 1, 243 569 1, 293 569 1, -114 -14 1 +192 64 192 128 256;64 192 1, 64 256 1, 128 256 1, 128 192 1 +384 0 0 320 640;64 0 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 468 1, 64 640 0, 194 640 1, 221 640 0, 256 613 1, 256 557 1, 219 576 0, 193 576 1, 157 576 0, 142 555 1, 128 534 0, 128 482 1, 128 384 1, 320 384 1, 320 0 1, 256 0 1, 256 320 1, 128 320 1, 128 0 1, 256 512 1, 256 576 1, 320 576 1, 320 512 1 +384 0 0 320 576;64 0 1, 64 320 1, 0 320 1, 0 384 1, 64 384 1, 64 435 1, 64 576 0, 175 576 1, 256 576 1, 320 576 1, 320 0 1, 256 0 1, 256 518 1, 240 516 1, 206 512 0, 183 512 1, 148 512 0, 137 493 1, 128 477 0, 128 443 1, 128 384 1, 192 384 1, 192 320 1, 128 320 1, 128 0 1 +256 -64 192 256 576;128 229 1, 128 320 1, -25 320 1, -25 382 1, 126 576 1, 192 576 1, 192 384 1, 238 384 1, 238 320 1, 192 320 1, 192 229 1, 24 384 1, 128 384 1, 128 516 1 +192 -64 -192 128 384;-64 -145 1, -64 -87 1, -28 -64 0, 3 -64 1, 46 -64 0, 56 -47 1, 64 -32 0, 64 0 1, 64 384 1, 128 384 1, 128 0 1, 128 -128 0, 2 -128 1, -33 -128 0 +192 0 0 0 0; diff --git a/vendor/github.com/golang/freetype/testdata/luxisr.ttf b/vendor/github.com/golang/freetype/testdata/luxisr.ttf new file mode 100644 index 000000000..c47fd20be Binary files /dev/null and b/vendor/github.com/golang/freetype/testdata/luxisr.ttf differ diff --git a/vendor/github.com/golang/freetype/testdata/luxisr.ttx b/vendor/github.com/golang/freetype/testdata/luxisr.ttx new file mode 100644 index 000000000..98eea53e4 --- /dev/null +++ b/vendor/github.com/golang/freetype/testdata/luxisr.ttx @@ -0,0 +1,22503 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 15 values pushed */ + 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + FDEF[ ] + SLOOP[ ] + MDAP[1] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + MDAP[1] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP1[ ] + SRP2[ ] + IP[ ] + ENDF[ ] + FDEF[ ] + SRP1[ ] + SRP2[ ] + SLOOP[ ] + IP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MIRP[11101] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MIRP[10100] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MDRP[11101] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + SLOOP[ ] + MDRP[10100] + ALIGNRP[ ] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MIRP[11101] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MIRP[10100] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MDRP[11101] + ENDF[ ] + FDEF[ ] + SRP0[ ] + MDRP[10100] + ENDF[ ] + FDEF[ ] + MDRP[00100] + ENDF[ ] + FDEF[ ] + MDRP[00000] + ENDF[ ] + FDEF[ ] + SVTCA[0] + NPUSHB[ ] /* 10 values pushed */ + 1 0 0 1 1 2 2 3 3 0 + SZPS[ ] + MIAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SZPS[ ] + ENDF[ ] + + + + + + PUSHB[ ] /* 2 values pushed */ + 48 1 + PUSHW[ ] /* 1 value pushed */ + 329 + RTG[ ] + SCANCTRL[ ] + SCANTYPE[ ] + SCVTCI[ ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 16 values pushed */ + 5 6 2 1 4 7 3 0 5 4 2 3 6 7 1 0 + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + SVTCA[0] + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + MDAP[1] + ALIGNRP[ ] + MDRP[11100] + ALIGNRP[ ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 40 values pushed */ + 10 1 8 2 0 0 9 8 15 1 5 1 4 48 196 6 5 1 7 4 3 0 3 2 0 + 2 1 0 14 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 2 6 8 2 0 0 7 6 15 1 4 11 10 15 1 8 13 12 15 1 3 3 4 48 196 + 9 8 1 17 16 1 18 15 14 3 3 1 0 1 4 0 5 4 0 14 18 17 4 3 0 + 5 13 1 0 0 16 15 2 1 33 3 7 1 4 48 196 14 13 1 6 5 1 10 9 1 + 12 11 8 7 3 16 15 2 1 3 5 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 10 1 8 2 0 0 9 8 15 1 5 1 4 48 196 13 12 1 14 11 1 6 5 1 7 + 4 3 0 3 4 0 2 1 0 14 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 59 values pushed */ + 0 0 16 40 25 48 196 10 1 8 2 21 20 12 11 4 13 25 1 0 0 9 8 15 1 + 5 1 4 48 196 6 5 1 7 4 3 0 3 2 0 2 1 0 14 21 20 12 11 10 9 + 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 17 16 2 12 11 3 10 1 8 2 0 0 9 8 15 1 5 1 4 48 196 13 12 1 18 + 15 14 11 3 6 5 1 7 4 3 0 3 4 0 2 1 0 14 18 17 16 15 14 13 12 + 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 93 values pushed */ + 10 1 8 2 0 0 18 15 14 11 13 3 12 9 8 15 1 5 2 4 48 196 17 16 13 + 12 3 6 5 1 7 4 3 0 3 3 0 2 1 0 14 9 5 2 17 15 3 10 2 1 + 3 15 13 3 8 13 11 2 4 3 2 13 17 7 6 0 3 13 11 0 0 16 15 13 1 + 17 14 13 13 1 11 2 4 48 196 18 17 1 12 11 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 10 1 8 2 0 0 9 8 15 1 5 1 4 48 196 14 13 1 12 11 1 6 5 1 7 + 4 3 0 3 4 0 2 1 0 14 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 64 values pushed */ + 10 1 8 2 0 0 14 11 7 1 12 9 8 15 1 5 2 4 48 196 13 12 1 6 5 + 1 7 4 3 0 3 3 0 2 1 0 14 10 9 8 6 5 2 1 7 13 11 3 4 3 + 2 13 13 7 0 11 14 13 1 12 11 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 16 21 21 48 196 10 1 8 2 19 18 21 0 0 0 9 8 15 1 5 1 4 48 + 196 6 5 1 12 11 7 4 3 0 5 2 0 2 1 0 14 0 0 14 42 23 48 196 23 + 19 18 12 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 0 0 35 44 19 27 44 11 48 196 19 0 10 1 8 2 11 1 0 0 9 8 15 1 5 + 1 4 48 196 6 5 1 7 4 3 0 3 2 0 2 1 0 14 0 0 39 32 15 31 32 + 23 48 196 23 15 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 0 0 32 6 15 20 6 27 48 196 10 1 8 2 34 23 22 11 4 13 27 15 1 0 0 + 9 8 15 1 5 1 4 48 196 6 5 1 7 4 3 0 3 2 0 2 1 0 14 34 23 + 22 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 76 values pushed */ + 10 32 30 2 0 0 40 39 15 1 1 31 30 31 1 32 22 21 15 1 0 3 4 48 196 + 33 32 1 20 0 1 2 0 2 1 0 14 0 0 35 9 6 26 26 14 48 196 39 33 30 + 22 20 10 2 7 13 14 6 21 0 0 40 32 31 21 33 3 0 1 4 48 196 1 0 1 + 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 23 29 2 15 29 10 48 196 10 0 2 2 1 1 25 13 12 0 4 0 2 3 0 + 0 14 0 0 19 26 6 48 196 6 12 25 0 1 13 12 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 0 0 23 29 2 15 29 10 48 196 10 0 2 2 1 1 25 13 12 0 4 0 2 3 0 + 0 28 27 1 29 26 1 2 0 14 0 0 19 26 6 48 196 29 28 27 26 4 13 6 12 + 25 0 1 13 12 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 23 29 2 15 29 10 48 196 10 0 2 2 32 31 2 26 27 3 1 1 25 13 12 + 0 4 0 2 3 0 0 33 30 29 26 3 28 27 1 2 0 14 0 0 19 26 6 48 196 + 33 32 31 30 29 28 27 26 8 13 6 12 25 0 1 13 12 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 41 44 36 23 29 2 15 29 10 48 196 10 0 2 2 1 1 25 13 12 0 4 0 + 2 3 0 0 1 45 39 38 28 27 26 6 13 36 2 0 14 0 0 43 42 32 19 26 6 + 48 196 45 39 38 28 27 26 6 13 32 6 12 25 0 1 13 12 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 23 29 2 15 29 10 48 196 10 0 2 2 32 31 2 27 26 3 1 1 25 13 12 + 0 4 0 2 3 0 0 28 27 1 33 30 29 26 3 2 0 14 0 0 19 26 6 48 196 + 33 32 31 30 29 28 27 26 8 13 6 12 25 0 1 13 12 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 0 0 23 29 2 15 29 10 48 196 10 0 2 2 1 1 25 13 12 0 4 0 2 3 0 + 0 0 0 29 26 5 1 27 1 4 48 196 28 27 1 0 14 0 0 19 26 6 48 196 6 + 26 0 0 27 26 4 1 28 1 4 48 196 29 28 1 25 0 1 13 12 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 54 values pushed */ + 0 0 19 18 15 1 1 10 9 15 1 0 2 4 48 196 8 0 1 0 2 1 0 14 0 + 0 12 26 4 48 196 18 10 8 2 4 13 4 9 0 0 19 9 33 1 0 1 4 48 196 + 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 26 25 2 20 21 3 0 0 19 18 15 1 1 10 9 15 1 0 2 4 48 196 27 24 23 + 20 3 22 21 1 8 0 1 3 0 2 1 0 14 0 0 12 26 4 48 196 23 9 0 2 + 27 26 25 24 22 21 20 18 10 8 2 11 13 4 9 0 0 19 9 33 1 0 1 4 48 + 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 23 22 15 1 5 27 26 2 1 15 3 3 14 13 15 1 0 3 4 48 196 25 24 + 4 3 3 12 0 1 2 0 6 5 0 14 0 0 16 26 8 48 196 22 14 12 6 4 25 + 13 3 8 25 0 0 27 24 23 13 33 3 0 1 4 48 196 26 25 1 5 4 1 0 3 + 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 0 0 4 3 15 1 1 8 7 15 1 5 10 9 15 1 0 3 4 48 196 6 5 1 11 + 0 1 2 0 2 1 0 14 0 0 9 8 5 4 33 3 0 1 4 48 196 11 10 1 3 + 2 1 7 6 1 1 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 0 0 4 3 15 1 1 8 7 15 1 5 10 9 15 1 0 3 4 48 196 14 13 1 15 + 12 1 6 5 1 11 0 1 4 0 2 1 0 14 15 14 13 12 4 6 4 3 0 0 9 + 8 5 4 33 3 0 1 4 48 196 11 10 1 3 2 1 7 6 1 1 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 0 0 17 40 26 48 196 22 21 13 12 4 13 26 1 0 0 4 3 15 1 1 8 7 15 + 1 5 10 9 15 1 0 3 4 48 196 6 5 1 11 0 1 2 0 2 1 0 14 22 21 + 13 12 4 6 4 3 0 0 9 8 5 4 33 3 0 1 4 48 196 11 10 1 3 2 1 + 7 6 1 1 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 89 values pushed */ + 18 17 2 12 13 3 0 0 4 3 15 1 1 8 7 15 1 5 10 9 15 1 0 3 4 + 48 196 19 16 15 12 3 14 13 1 6 5 1 11 0 1 4 0 2 1 0 14 19 18 17 + 16 14 13 12 7 6 4 3 15 4 0 2 0 0 9 8 5 4 33 3 0 1 4 48 196 + 11 10 1 3 2 1 7 6 1 1 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 89 values pushed */ + 18 17 2 13 12 3 0 0 4 3 15 1 1 8 7 15 1 5 10 9 15 1 0 3 4 + 48 196 14 13 1 19 16 15 12 3 6 5 1 11 0 1 4 0 2 1 0 14 19 18 17 + 16 15 14 13 7 6 4 3 12 4 0 2 0 0 9 8 5 4 33 3 0 1 4 48 196 + 11 10 1 3 2 1 7 6 1 1 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 88 values pushed */ + 0 0 19 16 15 12 13 3 13 4 3 15 1 1 8 7 15 1 5 10 9 15 1 0 4 + 4 48 196 18 17 14 13 3 6 5 1 11 0 1 3 0 2 1 0 14 0 0 17 16 13 + 1 18 15 14 13 1 12 9 8 5 4 33 3 0 3 4 48 196 19 18 1 13 12 1 11 + 10 1 3 2 1 7 6 1 1 0 1 6 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 76 values pushed */ + 0 0 15 12 5 1 13 4 3 15 1 1 8 7 15 1 5 10 9 15 1 0 4 4 48 + 196 14 13 1 6 5 1 11 0 1 3 0 2 1 0 14 0 0 15 14 4 1 12 9 8 + 5 4 33 3 0 2 4 48 196 13 12 1 11 10 1 3 2 1 7 6 1 1 0 1 5 + 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 74 values pushed */ + 0 0 4 3 15 1 1 8 7 15 1 5 10 9 15 1 0 3 4 48 196 15 14 1 13 + 12 1 6 5 1 11 0 1 4 0 2 1 0 14 15 14 13 12 4 6 4 3 0 0 9 + 8 5 4 33 3 0 1 4 48 196 11 10 1 3 2 1 7 6 1 1 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 15 12 7 1 13 4 3 15 1 1 8 7 15 1 5 10 9 15 1 0 4 4 48 + 196 14 13 1 6 5 1 11 0 1 3 0 2 1 0 14 0 0 13 12 9 8 5 4 33 + 5 0 1 4 48 196 15 14 1 13 12 9 8 5 4 5 11 10 1 3 2 1 7 6 1 + 1 0 1 6 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 0 0 13 40 8 48 196 17 3 2 1 0 3 16 15 11 10 6 5 13 8 0 18 0 1 + 0 5 4 2 1 0 3 14 2 10 17 2 0 0 16 15 4 3 24 3 5 18 17 24 1 + 0 2 4 48 196 6 5 1 11 10 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 94 values pushed */ + 0 0 17 21 22 48 196 20 19 22 0 0 0 4 3 15 1 1 8 7 15 1 5 10 9 + 15 1 0 3 4 48 196 6 5 1 13 12 11 0 3 2 0 2 1 0 14 0 0 15 42 + 24 48 196 20 19 13 3 2 6 3 24 24 12 2 6 4 3 0 0 9 8 5 4 33 3 + 0 1 4 48 196 11 10 1 3 2 1 7 6 1 1 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 23 22 15 1 5 27 26 2 1 15 3 3 14 13 15 1 0 3 4 48 196 25 24 + 4 3 3 12 0 1 2 0 6 5 0 14 0 0 16 26 8 48 196 22 14 12 6 4 25 + 13 3 8 25 0 0 27 24 23 13 33 3 0 1 4 48 196 26 25 1 5 4 1 0 3 + 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 0 0 35 35 30 4 45 9 48 196 30 0 9 2 1 33 32 2 0 25 3 0 1 7 6 + 2 0 2 3 0 0 0 44 43 24 23 6 3 25 49 16 15 0 6 3 17 2 4 48 196 + 42 41 26 25 3 48 47 18 17 3 2 0 14 49 48 47 44 43 42 41 26 25 24 23 18 + 17 16 15 0 16 13 32 7 6 1 33 32 1 2 0 + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 0 0 35 35 30 4 45 9 48 196 30 0 9 2 1 33 32 2 0 25 3 0 1 7 6 + 2 0 2 3 0 0 0 44 43 24 23 6 3 25 49 16 15 0 6 3 17 2 4 48 196 + 42 41 26 25 3 48 47 18 17 3 2 0 14 49 48 47 44 43 42 41 26 25 24 23 18 + 17 16 15 0 16 13 32 7 6 1 33 32 1 2 0 + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 52 values pushed */ + 0 0 4 3 15 1 1 8 7 15 1 5 2 4 48 196 6 5 1 9 0 1 2 0 2 + 1 0 14 0 0 9 8 5 4 33 3 0 1 4 48 196 3 2 1 7 6 1 1 0 1 + 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 20 29 3 14 29 9 48 196 9 0 3 2 1 12 11 2 0 0 3 0 1 22 1 + 2 23 2 3 0 0 0 24 23 15 1 0 1 4 48 196 25 0 1 0 14 0 0 16 26 + 5 48 196 5 24 0 0 23 22 33 1 0 1 4 48 196 1 0 1 12 11 1 25 24 1 + 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 100 values pushed */ + 0 0 31 40 40 20 29 3 14 29 9 48 196 9 0 3 2 1 12 11 2 0 0 3 0 + 1 22 1 2 23 2 3 0 1 36 35 27 26 4 13 40 0 0 0 0 24 23 15 1 0 + 1 4 48 196 25 0 1 0 14 0 0 16 26 5 48 196 36 11 22 2 35 22 24 2 27 + 26 5 24 0 0 23 22 33 1 0 1 4 48 196 1 0 1 12 11 1 25 24 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 109 values pushed */ + 0 0 20 29 3 14 29 9 48 196 9 0 3 2 32 31 2 27 26 3 1 12 11 2 0 + 0 3 0 1 22 1 2 23 2 3 0 0 0 24 23 15 1 0 1 4 48 196 28 27 1 + 33 30 29 26 3 25 0 1 3 0 14 0 0 16 26 5 48 196 29 11 22 2 30 28 2 + 22 24 3 33 32 31 27 26 5 13 5 24 0 0 23 22 33 1 0 1 4 48 196 1 0 + 1 12 11 1 25 24 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 104 values pushed */ + 0 0 29 44 40 20 29 3 14 29 9 48 196 9 0 3 2 1 12 11 2 0 0 3 0 + 1 22 1 2 23 2 3 0 1 34 33 27 26 4 13 40 2 0 0 0 24 23 15 1 0 + 1 4 48 196 25 0 1 0 14 0 0 31 42 38 16 26 5 48 196 38 38 22 24 2 34 + 33 27 26 4 13 5 24 0 0 23 22 33 1 0 1 4 48 196 1 0 1 12 11 1 25 + 24 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 94 values pushed */ + 0 0 20 29 3 14 29 9 48 196 9 0 3 2 1 12 11 2 0 0 3 0 1 22 1 + 2 23 2 3 0 0 0 29 26 5 1 27 24 23 15 1 0 2 4 48 196 28 27 1 25 + 0 1 2 0 14 0 0 16 26 5 48 196 5 26 0 0 27 26 4 1 28 23 22 33 1 + 0 2 4 48 196 29 28 1 1 0 1 12 11 1 25 24 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 10 9 15 1 3 1 4 48 196 4 3 1 11 8 7 0 3 2 0 6 5 2 1 + 0 3 14 0 0 9 8 5 4 33 3 6 11 10 3 2 33 3 0 2 4 48 196 7 6 + 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 107 values pushed */ + 0 0 18 17 6 5 3 2 6 5 7 1 4 48 196 16 15 12 11 8 7 5 22 21 1 + 23 20 19 4 3 18 17 6 5 3 2 5 1 0 1 5 0 14 13 10 9 0 3 14 17 + 16 2 13 14 7 6 4 0 0 21 20 13 12 2 1 33 5 14 23 22 11 10 3 0 33 + 5 4 2 4 48 196 19 18 15 14 3 9 8 5 4 3 21 20 13 12 2 1 5 23 22 + 11 10 3 0 5 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 18 17 2 13 12 3 0 0 10 9 15 1 3 1 4 48 196 14 13 1 19 16 15 12 3 + 4 3 1 11 8 7 0 3 4 0 6 5 2 1 0 3 14 19 18 17 16 15 14 13 12 + 8 4 2 3 0 0 9 8 5 4 33 3 6 11 10 3 2 33 3 0 2 4 48 196 7 + 6 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 23 values pushed */ + 3 0 1 0 2 1 0 14 0 0 3 2 33 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 40 values pushed */ + 6 5 1 7 4 1 3 0 1 3 0 2 1 0 14 7 5 2 2 0 3 6 2 4 0 + 0 0 3 2 33 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 0 0 9 7 18 48 196 14 13 5 4 4 13 18 1 3 0 1 0 2 1 0 14 14 13 + 2 13 2 5 4 0 0 0 3 2 9 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 10 9 2 5 4 3 6 5 1 11 8 7 4 3 3 0 1 3 0 2 1 0 14 10 9 + 2 2 0 3 8 7 6 3 13 2 11 5 4 3 13 0 0 0 3 2 33 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 59 values pushed */ + 0 0 11 8 7 4 13 3 5 1 4 48 196 10 9 6 5 3 3 0 1 2 0 2 1 + 0 14 0 0 9 8 13 1 10 7 6 13 1 4 3 2 33 1 0 3 4 48 196 11 10 + 1 5 4 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 0 0 7 4 5 1 5 1 4 48 196 6 5 1 3 0 1 2 0 2 1 0 14 0 0 + 7 6 4 1 4 3 2 33 1 0 2 4 48 196 5 4 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 40 values pushed */ + 7 6 1 5 4 1 3 0 1 3 0 2 1 0 14 7 5 2 2 0 3 4 2 6 0 + 0 0 3 2 33 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + MDRP[00000] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 45 values pushed */ + 0 0 7 4 7 1 5 1 4 48 196 6 5 1 3 0 1 2 0 2 1 0 14 0 0 + 3 2 33 1 0 1 4 48 196 7 6 1 5 4 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 9 21 14 48 196 12 11 14 0 5 4 3 0 3 0 2 1 0 14 0 0 7 42 + 16 48 196 5 4 2 2 0 3 12 11 2 13 2 16 0 0 0 3 2 33 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 0 0 25 6 8 13 6 20 48 196 27 16 15 4 4 13 20 8 1 3 0 1 0 2 1 + 0 14 16 15 2 13 2 27 4 0 0 0 3 2 9 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 3 30 12 48 196 1 10 7 2 8 2 3 0 1 1 0 12 2 0 9 8 0 14 + 0 0 8 7 33 1 9 1 4 48 196 10 9 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 79 values pushed */ + 0 0 3 30 12 48 196 20 19 2 15 14 3 1 10 7 2 8 2 3 0 1 1 0 12 + 2 0 16 15 1 21 18 17 14 3 2 0 9 8 0 14 20 19 16 3 9 7 3 21 15 + 14 3 7 0 3 18 17 2 13 9 0 0 8 7 33 1 9 1 4 48 196 10 9 1 1 + 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 45 values pushed */ + 9 6 3 3 1 0 3 10 8 7 0 3 0 5 4 2 1 0 3 14 8 7 6 5 4 + 5 13 2 0 0 10 9 3 2 4 3 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 0 0 14 44 25 48 196 9 6 3 3 1 0 3 19 18 12 11 4 13 25 0 10 8 7 + 0 3 0 5 4 2 1 0 3 14 0 0 16 42 23 48 196 19 18 12 11 8 7 6 5 + 4 9 13 23 2 0 0 10 9 3 2 4 3 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 38 values pushed */ + 0 0 4 3 15 1 0 1 4 48 196 5 0 1 0 2 1 0 14 0 0 3 2 33 1 + 0 1 4 48 196 5 4 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 0 0 4 3 15 1 0 1 4 48 196 8 7 1 9 6 1 5 0 1 3 0 2 1 0 + 14 8 7 2 4 2 3 9 6 2 2 0 3 0 0 3 2 33 1 0 1 4 48 196 5 + 4 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 14 11 10 9 7 6 6 1 3 3 0 0 4 3 15 1 0 1 4 48 196 5 0 1 0 + 13 12 2 1 0 3 14 10 9 2 13 6 3 0 0 12 11 7 6 4 3 13 3 2 33 + 1 0 2 4 48 196 14 13 1 5 4 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 9 44 20 48 196 14 13 7 6 4 13 20 0 0 0 4 3 15 1 0 1 4 48 + 196 5 0 1 0 2 1 0 14 0 0 11 42 18 48 196 18 18 14 13 7 6 5 4 2 + 3 0 0 3 2 33 1 0 1 4 48 196 5 4 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 55 values pushed */ + 0 0 9 6 5 1 7 4 3 15 1 0 2 4 48 196 8 7 1 5 0 1 2 0 2 + 1 0 14 0 0 7 6 4 1 8 3 2 33 1 0 2 4 48 196 9 8 1 5 4 1 + 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 10 9 8 7 4 3 2 1 8 5 11 3 0 0 12 11 15 1 0 1 4 48 196 13 0 + 1 0 6 5 0 14 0 0 11 10 7 6 33 3 0 1 4 48 196 13 12 1 9 8 1 + 5 4 1 0 3 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 11 8 3 3 1 9 3 10 9 1 12 7 6 0 3 2 0 5 4 2 1 0 3 14 10 + 9 4 3 2 5 7 11 3 0 0 8 7 4 1 5 12 11 24 1 0 2 4 48 196 6 + 5 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 49 values pushed */ + 8 3 2 1 0 3 9 7 6 0 3 0 5 4 2 1 0 3 14 7 2 2 3 8 3 + 0 0 4 3 24 1 5 9 8 24 1 0 2 4 48 196 6 5 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 8 3 2 1 0 3 12 11 1 13 10 1 9 7 6 0 3 3 0 5 4 2 1 0 3 + 14 13 12 11 10 7 2 6 3 8 3 0 0 4 3 24 1 5 9 8 24 1 0 2 4 + 48 196 6 5 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 16 15 2 10 11 3 8 3 2 1 0 3 17 14 13 10 3 12 11 1 9 7 6 0 3 + 3 0 5 4 2 1 0 3 14 17 16 15 14 13 12 11 10 7 2 10 3 8 3 0 0 + 4 3 24 1 5 9 8 24 1 0 2 4 48 196 6 5 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 13 44 24 48 196 8 3 2 1 0 3 18 17 11 10 4 13 24 0 9 7 6 0 + 3 0 5 4 2 1 0 3 14 0 0 15 42 22 48 196 22 22 18 17 11 10 7 2 7 + 3 8 3 0 0 4 3 24 1 5 9 8 24 1 0 2 4 48 196 6 5 1 1 0 1 + 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 0 0 31 6 14 19 6 26 48 196 8 3 2 1 0 3 33 22 21 10 4 13 26 14 1 + 9 7 6 0 3 0 5 4 2 1 0 3 14 33 22 21 10 7 2 6 3 8 3 0 0 + 4 3 24 1 5 9 8 24 1 0 2 4 48 196 6 5 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 24 29 8 16 29 0 48 196 8 2 0 0 14 0 0 28 26 4 20 26 12 48 196 + 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 0 0 38 29 3 30 29 11 48 196 11 0 3 2 13 14 16 2 26 16 18 2 25 20 22 + 2 1 22 0 2 0 0 17 16 15 1 14 21 20 15 1 18 23 22 15 1 0 3 4 48 + 196 19 18 1 24 0 1 2 0 15 14 0 14 0 0 34 26 7 48 196 7 0 0 0 26 + 25 14 13 1 0 33 5 17 1 4 48 196 24 23 1 16 15 1 20 19 1 22 21 18 17 + 3 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 39 values pushed */ + 0 0 24 29 8 16 29 0 48 196 8 2 0 0 34 33 1 35 32 1 2 0 14 0 0 + 28 26 4 20 26 12 48 196 35 34 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 37 7 46 24 35 8 16 35 0 48 196 8 2 0 0 1 42 41 33 32 4 13 46 + 0 0 14 0 0 28 36 4 20 36 12 48 196 42 41 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 51 values pushed */ + 0 0 24 29 8 16 29 0 48 196 8 2 0 0 38 37 2 33 32 3 34 33 1 39 36 + 35 32 3 2 0 14 0 0 28 26 4 20 26 12 48 196 39 38 37 36 35 34 33 32 12 + 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 0 0 24 29 8 16 29 0 48 196 8 2 0 0 0 0 39 36 35 32 13 3 33 1 4 + 48 196 38 37 34 33 3 0 14 0 0 28 26 4 20 26 12 48 196 4 38 12 32 0 0 + 37 36 13 1 38 35 34 13 1 32 2 4 48 196 39 38 1 33 32 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 39 values pushed */ + 0 0 24 29 8 16 29 0 48 196 8 2 0 0 35 34 1 33 32 1 2 0 14 0 0 + 28 26 4 20 26 12 48 196 35 34 33 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 51 values pushed */ + 0 0 24 29 8 16 29 0 48 196 8 2 0 0 1 39 36 35 32 4 33 0 3 0 38 + 37 34 33 3 0 14 0 0 28 26 4 20 26 12 48 196 39 38 37 36 35 34 33 32 12 + 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 52 values pushed */ + 0 0 24 29 8 16 29 0 48 196 8 2 0 0 0 0 35 32 7 1 33 1 4 48 196 + 34 33 1 0 14 0 0 28 26 4 20 26 12 48 196 4 34 12 32 35 34 1 33 32 1 + 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 37 29 9 28 29 22 48 196 22 2 9 0 1 1 35 34 14 11 4 0 1 3 0 + 0 1 1 43 26 24 1 4 1 2 3 0 0 13 12 1 25 0 1 2 0 14 0 0 41 + 26 5 32 26 18 48 196 43 35 34 26 25 24 18 14 13 12 11 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 48 values pushed */ + 0 0 53 6 36 41 6 48 24 29 8 16 29 0 48 196 8 2 0 0 1 55 44 43 32 + 4 13 48 36 0 0 14 0 0 28 26 4 20 26 12 48 196 55 44 43 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 0 0 21 20 15 1 1 11 10 15 1 13 2 4 48 196 14 13 1 12 0 1 2 0 2 + 1 0 14 0 0 16 26 8 48 196 20 14 10 2 4 13 8 11 0 0 21 13 12 11 33 + 3 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 0 0 33 29 5 25 29 13 48 196 13 0 5 2 1 1 23 0 2 2 0 0 1 3 1 + 0 3 13 2 0 14 0 0 37 26 17 29 26 9 48 196 23 17 9 3 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 69 values pushed */ + 8 14 11 2 0 0 24 23 15 1 1 12 11 15 1 14 2 4 48 196 15 14 1 13 10 + 9 0 3 2 0 2 1 0 14 0 0 19 9 4 48 196 23 15 11 10 9 8 2 7 13 + 4 12 0 0 24 14 13 12 33 3 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 79 values pushed */ + 8 14 11 2 0 0 24 23 15 1 1 12 11 15 1 14 2 4 48 196 27 26 1 28 25 + 1 15 14 1 13 10 9 0 3 4 0 2 1 0 14 0 0 19 9 4 48 196 28 27 26 + 25 23 15 11 10 9 8 2 11 13 4 12 0 0 24 14 13 12 33 3 0 1 4 48 196 + 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 94 values pushed */ + 31 30 2 25 26 3 8 14 11 2 0 0 24 23 15 1 1 12 11 15 1 14 2 4 48 + 196 32 29 28 25 3 27 26 1 15 14 1 13 10 9 0 3 4 0 2 1 0 14 0 0 + 19 9 4 48 196 28 12 0 2 32 31 30 29 27 26 25 23 15 11 10 9 8 2 14 13 + 4 12 0 0 24 14 13 12 33 3 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 0 0 28 44 39 48 196 8 14 11 2 33 32 26 25 4 13 39 0 0 0 24 23 15 1 + 1 12 11 15 1 14 2 4 48 196 15 14 1 13 10 9 0 3 2 0 2 1 0 14 0 + 0 30 42 37 19 9 4 48 196 33 32 26 25 23 15 11 10 9 8 2 11 13 37 4 12 + 0 0 24 14 13 12 33 3 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 52 values pushed */ + 0 0 19 29 14 3 29 34 48 196 34 2 14 0 1 1 17 16 1 0 4 0 2 3 0 + 0 14 0 0 21 43 12 5 9 30 48 196 12 12 16 0 2 30 16 17 16 1 1 0 1 + 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 19 29 14 3 29 34 48 196 34 2 14 0 1 1 17 16 1 0 4 0 2 3 0 + 0 38 37 1 39 36 1 2 0 14 0 0 21 43 12 5 9 30 48 196 12 39 38 37 36 + 12 5 16 0 3 30 16 17 16 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 19 29 14 3 29 34 48 196 34 2 14 0 42 41 2 36 37 3 1 1 17 16 1 + 0 4 0 2 3 0 0 43 40 39 36 3 38 37 1 2 0 14 0 0 21 43 12 5 9 + 30 48 196 12 43 42 41 40 39 38 37 36 12 9 16 0 3 30 16 17 16 1 1 0 1 + 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 79 values pushed */ + 0 0 51 44 46 19 29 14 3 29 34 48 196 34 2 14 0 1 1 17 16 1 0 4 0 + 2 3 0 0 1 55 49 48 38 37 36 6 13 46 2 0 14 0 0 53 42 42 21 43 12 + 5 9 30 48 196 42 12 55 49 48 42 38 37 36 12 8 16 0 3 30 16 17 16 1 1 + 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 19 29 14 3 29 34 48 196 34 2 14 0 42 41 2 37 36 3 1 1 17 16 1 + 0 4 0 2 3 0 0 38 37 1 43 40 39 36 3 2 0 14 0 0 21 43 12 5 9 + 30 48 196 12 43 42 41 40 39 38 37 36 12 9 16 0 3 30 16 17 16 1 1 0 1 + 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 0 0 39 44 50 19 29 14 3 29 34 48 196 34 2 14 0 1 1 17 16 1 0 4 0 + 2 3 0 0 1 44 43 37 36 4 13 50 2 0 14 0 0 41 42 48 21 43 12 5 9 + 30 48 196 48 12 48 44 43 37 36 12 6 16 0 3 30 16 17 16 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 6 5 2 1 15 3 3 1 4 48 196 7 0 1 0 4 3 0 14 0 0 7 6 + 33 1 0 1 4 48 196 5 4 1 1 0 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 0 0 10 9 6 5 15 3 7 14 13 2 1 7 3 3 2 4 48 196 12 11 4 3 3 + 15 0 1 2 0 8 7 0 14 0 0 15 14 11 10 33 3 0 1 4 48 196 9 8 1 + 13 12 1 5 4 1 0 3 3 2 1 7 6 1 5 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 78 values pushed */ + 14 13 2 8 9 3 0 0 6 5 2 1 15 3 3 1 4 48 196 15 12 11 8 3 10 + 9 1 7 0 1 3 0 4 3 0 14 15 9 8 3 4 6 3 14 13 2 6 0 3 12 + 11 10 3 0 2 3 0 0 7 6 33 1 0 1 4 48 196 5 4 1 1 0 1 3 2 + 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 0 0 23 44 18 48 196 27 21 20 10 4 13 18 0 0 0 6 5 2 1 15 3 3 1 + 4 48 196 9 8 7 0 3 0 4 3 0 14 0 0 25 42 14 48 196 14 14 4 6 2 + 10 9 8 3 6 0 3 27 21 20 3 0 2 3 0 0 7 6 33 1 0 1 4 48 196 + 5 4 1 1 0 1 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 78 values pushed */ + 0 0 11 32 22 48 196 16 15 9 8 4 13 22 0 0 0 6 5 2 1 37 3 3 1 + 4 48 196 7 0 1 0 4 3 0 14 0 0 13 22 20 48 196 20 20 4 6 2 9 8 + 2 0 2 3 0 0 7 6 9 1 0 1 4 48 196 5 4 1 16 15 1 0 3 3 2 + 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 68 values pushed */ + 0 0 23 22 15 1 3 16 15 15 1 12 2 4 48 196 4 3 1 13 12 1 14 0 1 + 3 0 2 1 0 14 0 0 18 26 10 48 196 22 16 12 4 4 13 10 2 0 0 3 2 + 33 1 0 23 15 14 13 33 3 0 2 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 0 0 8 29 21 48 196 21 2 1 25 15 12 2 4 0 2 3 0 14 13 1 0 0 3 + 14 0 0 13 12 24 1 14 2 1 33 1 0 2 4 48 196 15 14 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 0 0 8 29 21 48 196 21 2 1 25 15 12 2 4 0 2 3 0 28 27 1 29 26 1 + 2 0 14 13 1 0 0 3 14 29 28 27 26 4 12 1 3 0 0 13 12 24 1 14 2 + 1 33 1 0 2 4 48 196 15 14 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 69 values pushed */ + 0 0 31 7 40 8 35 21 48 196 21 2 1 25 15 12 2 4 0 2 3 0 36 35 27 + 26 4 13 40 0 14 13 1 0 0 3 14 36 35 27 26 4 12 1 3 0 0 13 12 27 + 1 14 2 1 9 1 0 2 4 48 196 15 14 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 78 values pushed */ + 0 0 8 29 21 48 196 21 2 32 31 2 27 26 3 1 25 15 12 2 4 0 2 3 0 + 28 27 1 33 30 29 26 3 2 0 14 13 1 0 0 3 14 33 32 31 30 29 28 27 26 + 8 12 1 3 0 0 13 12 24 1 14 2 1 33 1 0 2 4 48 196 15 14 1 25 0 + 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 8 29 21 48 196 21 2 1 25 15 12 2 4 0 2 3 0 0 0 33 30 29 26 + 13 3 27 1 4 48 196 32 31 28 27 3 0 14 13 1 0 0 3 14 0 0 31 30 13 + 1 32 29 28 13 1 26 13 12 24 1 14 2 1 33 1 0 4 4 48 196 33 32 1 27 + 26 1 15 14 1 25 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 0 0 8 29 21 48 196 21 2 1 25 15 12 2 4 0 2 3 0 29 28 1 27 26 1 + 2 0 14 13 1 0 0 3 14 29 28 27 26 4 12 1 3 0 0 13 12 24 1 14 2 + 1 33 1 0 2 4 48 196 15 14 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 79 values pushed */ + 0 0 8 29 21 48 196 21 2 33 30 29 26 4 27 0 3 1 25 15 12 2 4 0 2 + 3 0 32 31 28 27 3 0 14 13 1 0 0 3 14 32 14 12 2 33 31 30 29 28 27 + 26 7 12 1 3 0 0 13 12 24 1 14 2 1 33 1 0 2 4 48 196 15 14 1 25 + 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 8 29 21 48 196 21 2 1 25 15 12 2 4 0 2 3 0 0 0 29 26 7 1 + 27 1 4 48 196 28 27 1 0 14 13 1 0 0 3 14 0 0 13 12 24 1 14 2 1 + 33 1 0 2 4 48 196 29 28 1 27 26 1 15 14 1 25 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 0 0 3 44 14 48 196 14 8 7 1 0 14 0 0 5 42 12 48 196 12 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 31 21 36 8 29 21 48 196 21 2 1 25 15 12 2 4 0 2 3 0 1 34 33 + 27 26 4 13 36 2 0 14 13 1 0 0 3 14 0 0 29 42 38 48 196 38 38 34 33 + 27 26 5 12 1 3 0 0 13 12 24 1 14 2 1 33 1 0 2 4 48 196 15 14 1 + 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 50 44 34 42 44 26 8 29 21 48 196 21 2 1 25 15 12 2 4 0 2 3 0 + 34 26 0 14 13 1 0 0 3 14 0 0 54 32 30 46 32 38 48 196 38 30 38 30 2 + 12 1 3 0 0 13 12 24 1 14 2 1 33 1 0 2 4 48 196 15 14 1 25 0 1 + 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 73 values pushed */ + 0 0 47 6 30 35 6 42 8 35 21 48 196 21 2 1 25 15 12 2 4 0 2 3 0 + 49 38 37 26 4 13 42 30 0 14 13 1 0 0 3 14 49 38 37 26 4 12 1 3 0 + 0 13 12 27 1 14 2 1 9 1 0 2 4 48 196 15 14 1 25 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + NPUSHB[ ] /* 22 values pushed */ + 3 1 0 2 6 0 1 0 5 4 2 1 0 3 14 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 35 values pushed */ + 11 6 3 3 1 0 3 12 10 9 0 3 0 8 7 5 4 2 1 0 5 14 12 11 10 + 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 19 18 2 14 13 3 11 6 3 3 1 0 3 15 14 1 20 17 16 13 3 12 10 9 0 + 3 3 0 8 7 5 4 2 1 0 5 14 20 19 18 17 16 15 14 13 12 11 10 9 8 + 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 33 values pushed */ + 10 7 4 1 4 2 0 3 11 9 8 0 3 0 6 5 3 2 0 3 14 11 10 9 8 + 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 45 values pushed */ + 7 4 1 3 2 0 3 8 0 1 0 6 5 3 2 0 3 14 4 7 0 2 6 5 2 + 13 7 3 2 0 0 0 8 7 33 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 59 values pushed */ + 7 4 1 3 2 0 3 11 10 1 12 9 1 8 0 1 3 0 6 5 3 2 0 3 14 + 12 4 2 7 0 3 11 10 6 5 4 13 7 9 3 2 3 13 0 0 0 8 7 33 1 + 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 15 14 2 10 9 3 7 4 1 3 2 0 3 11 10 1 16 13 12 9 3 8 0 1 3 + 0 6 5 3 2 0 3 14 15 14 10 4 4 7 0 3 13 12 11 6 5 5 13 7 16 + 9 3 2 4 13 0 0 0 8 7 33 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 7 4 1 3 2 0 3 0 0 16 13 12 9 13 3 10 1 4 48 196 15 14 11 10 3 + 8 0 1 2 0 6 5 3 2 0 3 14 4 7 11 2 6 5 2 13 15 3 2 9 0 + 0 14 13 13 1 15 12 11 13 1 9 8 7 33 1 0 3 4 48 196 16 15 1 10 9 + 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 0 0 6 3 2 15 2 4 1 4 8 7 1 2 0 1 6 48 196 9 0 1 0 5 4 + 0 14 7 2 2 5 3 3 9 8 6 5 3 4 3 1 1 0 1 3 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 0 0 6 3 2 15 2 4 1 4 8 7 1 2 0 1 6 48 196 12 11 1 13 10 1 + 9 0 1 3 0 5 4 0 14 13 12 11 10 7 2 6 5 3 3 9 8 6 5 3 4 + 3 1 1 0 1 3 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 69 values pushed */ + 16 15 2 10 11 3 0 0 6 3 2 15 2 4 1 4 8 7 1 2 0 1 6 48 196 + 17 14 13 10 3 12 11 1 9 0 1 3 0 5 4 0 14 17 16 15 14 13 12 11 10 + 7 2 10 5 3 3 9 8 6 5 3 4 3 1 1 0 1 3 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 13 10 5 1 11 6 3 2 15 2 4 2 4 8 7 1 2 0 1 6 48 196 12 + 11 1 9 0 1 2 0 5 4 0 14 2 5 12 2 7 10 3 2 0 0 13 12 4 1 + 10 1 4 48 196 11 10 1 9 8 6 5 3 4 3 1 1 0 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 82 values pushed */ + 0 0 41 29 2 12 40 17 48 196 29 2 17 1 2 2 24 1 1 34 33 27 26 24 22 + 21 15 14 10 9 8 0 13 1 2 3 0 0 14 0 0 39 43 6 48 196 0 21 9 2 + 8 9 14 2 27 26 2 13 21 6 14 0 0 34 33 10 9 4 3 21 1 4 48 196 22 + 21 1 15 14 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 2 1 1 3 0 1 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 0 0 57 40 12 45 40 50 20 40 25 48 196 50 1 33 2 25 2 12 1 1 48 47 41 + 10 4 1 52 3 0 40 39 14 1 4 52 15 3 8 1 29 23 22 8 0 5 15 2 3 + 0 0 0 16 15 7 1 52 1 4 48 196 53 52 1 0 14 53 52 29 16 10 5 22 0 + 3 6 39 6 2 0 47 3 15 14 2 13 22 37 47 23 22 1 48 47 1 41 40 1 0 + 3 3 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[1] + CALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 11 1 1 1 4 48 196 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 54 40 17 46 40 3 48 196 17 0 3 2 1 48 38 25 11 4 0 30 3 0 36 + 33 29 27 1 5 30 0 3 31 30 1 35 0 1 2 0 14 0 0 56 41 13 52 14 21 + 42 41 7 48 196 48 38 36 35 33 31 30 29 27 25 21 13 11 7 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 3 0 8 1 1 1 4 48 196 2 1 1 0 14 0 0 3 2 8 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 18 values pushed */ + 3 0 2 13 1 5 4 2 1 3 0 14 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 24 7 5 11 7 18 48 196 5 13 18 0 14 13 1 1 0 1 2 0 14 14 13 + 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 34 1 36 34 32 24 23 21 20 19 8 1 0 11 30 1 3 0 1 1 29 18 16 15 14 + 13 11 10 9 3 2 11 1 2 3 0 0 31 30 0 14 36 32 31 30 29 24 23 21 20 + 19 18 16 15 14 13 11 10 9 8 5 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 74 6 28 68 16 34 62 16 2 54 16 10 46 16 18 48 196 10 0 2 2 34 1 + 34 0 40 2 0 28 18 1 66 65 64 28 24 23 18 0 8 40 2 3 0 41 40 1 0 + 14 0 0 72 15 30 58 17 6 50 17 14 48 196 66 65 64 44 41 40 30 24 23 20 14 + 6 0 + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + MDAP[1] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 23 2 15 1 9 3 1 1 11 9 3 1 0 5 1 2 3 0 0 1 26 25 2 0 28 + 27 1 0 14 19 5 0 0 0 28 25 11 1 0 4 4 26 1 4 48 196 27 26 1 28 + 25 11 1 0 4 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + CALL[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 12 values pushed */ + 1 0 1 0 3 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 2 1 1 3 0 1 2 0 14 0 0 3 2 7 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 55 values pushed */ + 50 49 38 37 36 25 14 13 12 1 0 14 0 0 47 15 27 32 13 42 18 13 8 3 15 + 23 48 196 42 27 23 8 49 42 36 27 25 23 14 8 1 9 12 0 3 38 37 13 12 3 + 50 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 55 values pushed */ + 52 51 40 39 38 25 14 13 12 1 0 14 0 0 49 15 29 34 13 44 18 13 8 3 15 + 23 48 196 44 29 23 8 51 44 38 29 25 23 14 8 1 9 0 12 3 52 0 1 40 39 + 13 12 3 2 0 + LOOPCALL[ ] + CALL[ ] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 0 0 4 3 7 1 1 6 5 7 1 0 2 4 48 196 2 1 1 7 0 1 2 0 14 + 0 0 5 4 13 1 0 1 4 48 196 7 6 3 2 3 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 0 0 6 5 7 1 0 4 3 7 1 1 2 4 48 196 7 0 1 2 1 1 2 0 14 + 0 0 5 4 13 1 0 1 4 48 196 1 0 1 7 6 3 2 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 17 values pushed */ + 0 0 5 40 14 48 196 14 10 9 1 0 14 10 9 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 34 values pushed */ + 6 5 1 7 4 1 2 1 1 3 0 1 4 0 14 0 0 7 6 3 2 7 3 0 1 + 4 48 196 5 4 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 15 values pushed */ + 0 0 0 8 48 196 8 14 0 0 4 12 48 196 12 + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 21 29 2 15 40 10 48 196 10 1 2 2 1 1 23 13 12 0 4 1 2 3 0 + 0 14 0 0 17 26 6 48 196 6 12 23 0 1 13 12 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 6 5 2 0 1 3 7 4 3 0 3 2 1 1 2 0 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 21 29 2 15 40 10 48 196 10 1 2 2 30 29 2 25 24 3 1 1 23 13 12 + 0 4 1 2 3 0 0 26 25 1 31 28 27 24 3 2 0 14 0 0 17 26 6 48 196 + 27 0 31 30 29 28 26 25 24 7 13 6 12 23 0 1 13 12 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 0 0 21 29 2 15 40 10 48 196 10 1 2 2 1 1 23 13 12 0 4 1 2 3 0 + 0 0 0 27 24 5 1 25 1 4 48 196 26 25 0 14 0 0 17 26 6 48 196 6 24 + 0 0 25 24 4 1 26 1 4 48 196 27 26 1 23 0 1 13 12 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 29 values pushed */ + 0 0 15 44 10 48 196 19 13 12 10 2 1 0 14 0 0 17 42 6 48 196 19 13 12 + 6 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 55 values pushed */ + 33 25 24 23 21 20 18 17 15 14 12 11 10 9 1 0 14 0 0 29 9 5 48 196 5 + 0 0 0 33 25 10 9 1 0 11 5 11 1 4 48 196 21 20 15 14 3 24 23 18 17 + 12 11 5 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 6 5 2 1 0 3 2 1 1 7 4 3 0 3 2 0 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 7 4 5 1 5 2 1 5 1 0 2 4 48 196 3 0 1 0 6 5 1 14 0 + 0 7 6 3 2 4 3 0 1 4 48 196 5 4 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 8 5 4 3 1 0 6 13 6 7 6 1 0 14 4 3 2 7 0 3 0 0 8 7 8 + 1 0 1 4 48 196 6 5 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 0 0 56 20 34 48 21 42 24 20 8 16 20 0 48 196 8 2 0 0 42 34 1 1 58 + 46 45 42 34 32 6 0 2 3 0 0 14 0 0 52 42 38 28 17 4 20 17 12 48 196 + 58 46 45 38 32 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 68 values pushed */ + 0 0 36 6 26 28 6 12 48 196 12 1 1 16 15 9 8 4 13 1 0 1 24 23 22 + 21 17 14 10 7 3 2 1 0 12 13 26 1 0 14 0 0 40 6 19 32 6 5 48 196 + 24 23 22 21 19 17 16 15 14 10 9 8 7 5 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 23 1 15 2 9 3 1 25 11 9 3 1 0 6 1 28 3 0 27 26 1 29 28 1 2 + 0 14 19 5 0 0 0 29 26 25 11 1 0 4 5 27 1 4 48 196 28 27 1 29 26 + 25 11 1 0 5 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 42 values pushed */ + 1 10 9 8 7 4 3 2 1 8 5 2 3 0 11 0 1 0 6 5 0 14 11 10 7 + 6 5 4 1 0 8 8 2 3 9 8 1 3 2 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 1 18 17 16 15 14 13 12 11 8 7 6 5 4 3 2 1 16 9 2 3 0 19 0 1 + 0 10 9 0 14 19 10 2 12 11 3 9 0 2 1 2 3 0 0 18 15 14 11 7 3 + 1 1 4 48 196 17 16 13 12 3 8 5 4 1 3 7 6 3 2 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 23 1 15 2 1 38 35 34 33 31 30 6 26 1 3 0 9 3 1 25 11 9 3 1 0 + 6 1 28 3 0 37 36 27 26 3 29 28 1 2 0 14 34 33 2 37 30 3 19 5 0 + 0 0 36 35 31 30 4 3 37 29 26 25 11 1 0 4 5 27 2 4 48 196 38 37 1 + 28 27 1 29 26 25 11 1 0 5 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 90 values pushed */ + 0 0 36 30 16 30 40 24 48 196 24 1 16 2 1 28 27 26 12 4 1 10 3 0 0 + 0 9 8 1 0 6 3 2 1 4 48 196 5 4 1 7 6 3 2 3 11 10 1 3 0 + 14 0 0 32 9 20 48 196 8 7 2 13 5 2 1 20 0 0 0 28 27 26 12 11 4 + 3 0 4 7 5 1 4 48 196 10 9 6 5 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 29 values pushed */ + 0 0 24 6 8 16 6 0 48 196 0 0 1 8 0 0 14 0 0 28 6 4 20 6 12 + 48 196 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 7 4 3 0 13 3 1 1 4 48 196 6 5 2 1 3 0 14 0 0 5 4 13 + 1 6 3 2 13 1 0 2 4 48 196 7 6 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 59 values pushed */ + 0 0 10 9 8 1 8 7 4 8 1 5 3 0 7 1 1 3 4 48 196 11 8 1 6 + 5 1 2 1 1 3 0 14 0 0 11 10 7 6 8 3 4 1 4 48 196 9 8 5 4 + 3 3 2 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 46 45 44 38 37 36 24 23 21 20 18 17 16 15 7 6 4 3 1 0 14 0 0 48 13 + 11 40 13 32 48 196 11 11 0 3 2 32 20 0 0 44 38 37 36 24 23 18 17 11 7 + 0 1 4 48 196 21 20 1 46 45 16 15 7 6 1 0 7 4 3 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 0 0 3 0 5 1 1 1 4 48 196 2 1 0 14 0 0 3 2 4 1 0 1 4 48 + 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 23 values pushed */ + 3 0 1 0 2 1 1 14 0 0 3 2 4 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 40 values pushed */ + 0 0 3 40 12 48 196 1 10 7 1 0 4 13 12 2 0 9 8 1 14 0 0 8 7 + 4 1 9 1 4 48 196 10 9 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 3 0 8 1 1 1 4 48 196 2 1 1 0 14 0 0 3 2 8 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 48 values pushed */ + 0 0 30 40 12 48 196 20 1 12 2 4 1 4 1 0 2 0 1 10 9 2 25 2 3 + 0 26 25 1 8 0 1 2 0 14 25 9 26 8 0 3 13 16 9 10 9 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 37 40 46 30 40 12 48 196 20 1 12 2 4 1 4 1 0 2 0 1 10 9 2 + 25 2 3 0 1 42 41 33 32 4 13 46 1 0 26 25 1 8 0 1 2 0 14 25 9 + 42 41 33 32 26 8 0 7 13 16 9 10 9 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 51 values pushed */ + 0 0 54 40 24 38 40 8 48 196 24 2 8 0 1 1 46 32 16 0 4 0 2 3 0 + 0 14 0 0 58 43 20 50 41 28 42 14 4 34 14 12 48 196 46 32 28 20 16 12 4 + 0 + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 10 9 6 5 2 1 5 5 0 1 4 48 196 11 8 7 4 3 0 5 0 14 0 + 0 9 8 4 1 10 7 6 4 1 4 3 2 4 1 0 3 4 48 196 11 10 1 5 4 + 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 11 1 1 1 4 48 196 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 6 1 1 1 4 48 196 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 24 30 7 17 40 12 48 196 7 1 26 20 9 3 4 1 0 3 19 15 14 10 4 + 13 12 0 27 0 1 0 2 1 1 14 0 0 20 19 4 1 9 27 26 3 2 4 3 0 + 2 4 48 196 10 9 1 15 14 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 0 0 7 4 7 1 5 2 1 7 1 0 2 4 48 196 6 5 1 3 0 1 2 0 14 + 7 6 3 2 3 5 4 1 0 3 2 0 + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 48 values pushed */ + 0 0 30 40 12 48 196 20 1 12 2 4 1 4 1 0 2 0 1 10 9 2 25 2 3 + 0 26 25 1 8 0 1 2 0 14 25 9 26 8 0 3 13 16 9 10 9 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 0 0 37 40 14 31 40 22 48 196 22 1 14 2 1 1 29 28 27 26 6 0 6 0 1 + 3 0 0 1 1 24 1 2 2 0 0 1 5 4 3 1 4 13 0 0 14 0 0 39 9 + 10 35 9 18 48 196 29 28 27 26 24 6 5 4 3 9 13 10 0 18 0 1 0 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 54 values pushed */ + 8 5 2 6 4 3 0 0 2 1 5 1 0 1 4 48 196 9 4 1 3 0 1 2 0 + 7 6 0 14 9 4 2 2 0 3 0 0 8 7 3 2 4 3 0 1 4 48 196 6 5 + 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 1 8 5 2 2 6 3 0 0 0 2 1 5 1 0 1 4 48 196 9 4 1 7 6 1 + 2 0 3 0 1 14 9 4 2 0 2 3 0 0 6 5 1 0 4 3 2 1 4 48 196 + 8 7 3 2 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 68 values pushed */ + 0 0 12 40 7 48 196 16 10 9 5 4 13 7 3 0 0 20 19 2 1 7 3 3 1 + 4 48 196 21 0 1 0 18 17 4 3 1 3 14 0 0 21 20 17 16 4 3 0 1 4 + 48 196 10 9 1 19 18 1 5 4 1 0 3 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 0 0 12 40 7 48 196 10 25 24 2 16 5 2 24 3 3 9 7 25 0 0 27 24 5 + 1 25 22 21 2 1 7 3 3 2 4 48 196 23 20 19 0 3 0 26 25 0 18 17 4 + 3 1 3 14 0 0 25 24 21 20 4 3 18 23 22 17 16 4 3 0 2 4 48 196 27 + 26 19 18 3 10 9 1 5 4 1 0 3 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 0 0 12 40 7 48 196 10 25 24 2 16 5 2 24 3 3 9 7 25 0 0 27 24 5 + 1 25 22 21 2 1 7 3 3 2 4 48 196 23 20 19 0 3 0 26 25 0 18 17 4 + 3 1 3 14 0 0 25 24 21 20 4 3 18 23 22 17 16 4 3 0 2 4 48 196 27 + 26 19 18 3 10 9 1 5 4 1 0 3 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 0 0 3 0 6 1 1 1 4 48 196 2 1 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 3 40 24 48 196 24 2 9 1 16 11 9 1 4 14 2 3 0 1 0 2 0 0 + 0 15 14 13 1 12 1 4 48 196 13 12 0 14 0 0 7 9 20 48 196 20 13 0 0 + 16 15 13 1 11 1 4 48 196 14 13 1 12 11 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 15 40 7 48 196 19 12 5 3 8 3 3 7 8 0 0 23 22 2 1 7 3 3 + 1 4 48 196 9 8 1 24 11 10 0 3 2 0 21 20 4 3 1 3 14 0 0 12 11 + 8 4 2 9 24 23 20 19 4 3 0 2 4 48 196 10 9 1 22 21 1 5 4 1 0 + 3 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 81 values pushed */ + 0 0 15 40 7 48 196 19 12 5 3 8 3 3 7 8 0 0 23 22 2 1 7 3 3 + 1 4 48 196 9 8 1 24 11 10 0 3 2 0 21 20 4 3 1 3 14 0 0 12 11 + 8 4 2 9 24 23 20 19 4 3 0 2 4 48 196 10 9 1 22 21 1 5 4 1 0 + 3 3 2 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 67 values pushed */ + 0 0 12 40 7 48 196 7 0 1 10 0 3 2 0 1 9 0 0 0 0 18 17 2 1 + 7 3 3 1 4 48 196 16 15 4 3 3 19 0 1 2 0 14 19 18 15 4 1 5 16 + 2 3 10 9 2 13 16 0 2 17 16 1 3 2 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 69 values pushed */ + 13 4 6 2 3 6 1 2 0 0 12 11 7 6 15 3 1 1 4 48 196 9 8 2 1 + 3 10 0 1 2 0 5 4 0 14 11 0 2 2 0 0 4 1 0 24 2 5 13 12 13 + 1 5 2 4 48 196 8 7 1 10 9 6 5 3 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 13 4 6 2 3 6 1 2 10 0 1 0 0 9 8 2 1 38 3 6 1 4 48 196 5 + 4 1 12 11 7 6 3 2 0 14 8 7 2 13 5 11 4 3 2 4 13 0 0 0 13 + 12 1 0 22 3 5 1 4 48 196 10 9 6 5 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 9 values pushed */ + 3 2 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 9 values pushed */ + 3 2 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 0 0 36 40 31 48 196 23 1 15 2 9 3 1 38 27 11 9 3 1 0 7 25 2 3 + 0 1 34 33 31 2 0 26 25 1 14 5 5 0 33 2 19 33 27 26 1 34 33 1 38 + 25 11 1 0 4 3 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 86 values pushed */ + 0 0 36 40 31 48 196 23 1 15 2 45 44 2 40 39 3 9 3 1 38 27 11 9 3 + 1 0 7 25 2 3 0 1 34 33 31 2 0 41 40 1 46 43 42 39 3 2 0 26 25 + 1 14 43 42 2 26 0 3 5 46 45 44 41 40 39 5 7 0 33 3 19 33 27 26 1 + 34 33 1 38 25 11 1 0 4 3 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + MDAP[1] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 92 values pushed */ + 0 0 36 40 31 48 196 23 1 15 2 9 3 1 38 27 11 9 3 1 0 7 25 2 3 + 0 47 44 43 42 40 39 6 13 45 1 34 33 31 2 0 46 45 1 0 26 25 1 14 43 + 42 2 39 46 3 5 5 46 33 2 19 33 0 0 45 44 40 39 4 3 46 1 4 48 196 + 47 46 1 27 26 1 34 33 1 38 25 11 1 0 4 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 36 40 31 48 196 23 1 15 2 9 3 1 38 27 11 9 3 1 0 7 25 2 3 + 0 1 34 33 31 2 0 0 0 42 39 5 1 40 1 4 48 196 41 40 0 26 25 1 14 + 5 5 39 33 2 19 33 0 0 42 41 4 1 39 1 4 48 196 40 39 1 27 26 1 34 + 33 1 38 25 11 1 0 4 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 0 0 36 40 5 23 40 18 48 196 18 2 40 21 20 1 4 13 5 0 41 0 1 0 14 + 0 0 34 41 7 25 14 16 11 14 30 48 196 30 16 7 3 12 20 0 0 41 40 4 1 + 0 1 4 48 196 21 20 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 3 2 1 1 0 1 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 6 5 4 3 2 1 0 14 5 4 1 3 13 0 6 3 2 0 3 0 + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 11 10 9 8 7 6 5 4 3 2 1 0 14 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 11 10 9 8 7 6 5 4 3 2 1 0 14 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 5 4 3 2 1 0 14 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 5 4 3 2 1 0 14 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 56 values pushed */ + 0 0 16 30 7 48 196 7 1 1 18 12 9 3 4 1 0 3 0 2 1 1 19 11 10 + 0 3 2 0 14 0 0 12 11 4 1 9 19 18 3 2 4 3 0 2 4 48 196 10 9 + 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 24 30 15 48 196 15 1 1 26 20 17 11 4 1 0 3 0 0 0 10 9 2 1 + 6 3 3 1 4 48 196 6 5 1 8 7 4 3 3 27 19 18 0 3 3 0 14 9 8 + 2 19 6 3 3 2 0 0 0 20 19 4 1 17 27 26 11 10 7 6 4 5 0 2 4 + 48 196 18 17 1 5 4 1 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 16 7 48 196 7 1 26 25 2 21 20 3 1 18 12 9 3 4 1 0 3 0 22 + 21 1 27 24 23 20 3 2 1 1 19 11 10 0 3 4 0 14 23 9 11 2 27 26 25 + 24 22 21 6 11 2 3 20 2 0 2 0 0 12 11 5 1 9 19 18 3 2 5 3 0 + 2 4 48 196 10 9 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 22 values pushed */ + 7 4 3 0 4 13 1 6 5 2 1 3 0 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 196 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 196 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 41 values pushed */ + 0 0 7 4 5 1 5 1 4 48 196 3 0 1 0 6 5 0 2 1 1 14 0 0 7 + 6 3 2 4 3 0 1 4 48 196 5 4 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 0 0 9 40 18 48 196 14 13 5 4 4 13 18 1 3 0 1 0 2 1 1 14 14 13 + 2 13 2 5 4 0 0 0 3 2 4 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 59 values pushed */ + 0 0 11 8 7 4 13 3 5 1 4 48 196 10 9 6 5 3 3 0 1 2 0 2 1 + 1 14 0 0 9 8 13 1 10 7 6 13 1 4 3 2 4 1 0 3 4 48 196 11 10 + 1 5 4 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 0 0 25 6 8 13 6 20 48 196 8 0 20 1 27 20 4 3 0 1 3 0 1 16 15 + 2 13 0 0 3 0 1 0 2 1 1 14 16 15 2 13 2 27 4 0 0 0 3 2 5 + 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 0 0 3 40 12 48 196 1 10 7 1 0 4 13 12 2 0 0 0 17 14 5 1 15 1 + 4 48 196 16 15 0 9 8 1 14 0 0 15 14 8 7 4 3 9 1 4 48 196 17 16 + 10 9 3 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 75 values pushed */ + 0 0 3 40 12 48 196 20 19 2 15 14 3 1 10 7 1 0 4 13 12 2 0 16 15 + 1 21 18 17 14 3 2 0 9 8 1 14 20 19 16 3 9 7 3 21 15 14 3 7 0 + 3 18 17 2 13 9 0 0 10 9 4 1 7 1 4 48 196 8 7 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 9 6 3 3 4 0 3 2 1 1 10 8 7 0 3 2 0 5 4 1 14 8 7 6 5 + 4 5 13 2 0 0 10 9 3 2 4 3 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 45 values pushed */ + 9 6 3 3 1 0 3 10 8 7 0 3 0 5 4 2 1 1 3 14 8 7 6 5 4 + 5 13 2 0 0 10 9 3 2 4 3 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 2 1 1 3 0 1 2 0 14 0 0 3 2 4 1 0 1 4 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 53 values pushed */ + 12 9 8 7 5 4 6 1 0 3 11 10 2 1 3 3 0 1 2 0 14 8 7 2 11 + 4 3 0 0 10 9 5 4 4 3 11 3 2 4 1 0 2 4 48 196 12 11 1 1 0 + 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 0 0 7 4 5 1 5 1 4 48 196 6 5 1 2 1 1 3 0 1 3 0 14 0 0 + 5 4 4 1 6 3 2 4 1 0 2 4 48 196 7 6 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + NPUSHB[ ] /* 20 values pushed */ + 6 5 4 3 2 1 0 14 5 4 1 3 13 0 6 3 2 0 3 0 + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 39 values pushed */ + 0 0 5 0 7 1 1 1 4 48 196 2 1 1 4 3 1 2 0 14 0 0 5 4 7 + 1 2 1 4 48 196 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 48 values pushed */ + 0 0 14 7 9 48 196 16 12 11 5 4 3 2 1 8 13 9 0 17 0 1 0 14 0 + 0 17 16 5 1 0 1 4 48 196 12 11 1 5 4 1 0 3 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 10 9 8 7 4 3 2 1 8 5 0 3 6 5 1 11 0 1 2 0 14 0 0 11 10 + 7 6 4 3 0 1 4 48 196 9 8 1 5 4 1 0 3 3 2 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 70 values pushed */ + 0 0 29 29 7 22 29 15 48 196 15 1 7 1 31 27 24 17 11 3 6 1 0 3 32 + 26 25 19 18 0 5 0 2 1 1 14 19 17 11 2 0 0 25 24 11 4 2 26 32 31 + 3 2 4 3 0 2 4 48 196 18 17 1 27 26 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 196 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 196 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 196 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 60 values pushed */ + 0 0 6 29 15 48 196 15 2 17 13 8 2 4 0 11 3 12 11 1 19 18 1 2 0 + 10 9 1 0 1 3 14 0 0 13 12 9 8 4 3 10 18 17 2 1 4 3 0 2 4 + 48 196 11 10 1 19 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 1 11 10 9 8 7 6 5 4 3 2 1 0 12 13 1 0 14 11 10 9 8 7 6 5 + 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 53 values pushed */ + 0 0 16 30 7 48 196 7 1 18 12 9 3 4 1 0 3 19 11 10 0 3 0 2 1 + 1 14 0 0 12 11 4 1 9 19 18 3 2 4 3 0 2 4 48 196 10 9 1 1 0 + 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 0 0 16 30 7 48 196 7 1 28 25 24 23 21 20 6 26 1 3 18 12 9 3 4 1 + 0 3 27 26 1 19 11 10 0 3 2 0 2 1 1 14 24 23 2 27 20 3 0 0 28 + 27 4 1 20 12 11 4 1 9 19 18 3 2 4 3 0 3 4 48 196 26 25 21 20 3 + 10 9 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 196 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 0 0 33 40 2 29 40 10 23 40 18 48 196 18 2 10 0 2 1 1 21 20 2 0 4 + 0 2 3 0 0 14 0 0 37 26 14 31 41 6 48 196 0 14 20 6 20 21 20 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + MDRP[00000] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 14 13 10 9 4 13 7 27 24 23 0 4 13 1 0 0 31 30 18 17 6 5 6 5 7 + 29 28 20 19 4 3 6 5 1 2 4 48 196 16 15 12 11 8 7 5 26 25 22 21 2 + 1 5 2 0 14 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 + 11 10 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 20 40 8 16 40 0 48 196 8 2 0 1 14 0 0 22 9 4 18 9 12 48 196 + 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 44 values pushed */ + 0 0 29 7 38 20 7 8 16 7 0 48 196 8 2 0 1 1 34 33 25 24 4 13 38 + 1 0 14 0 0 22 9 4 18 9 12 48 196 34 33 25 24 12 4 + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 55 40 4 44 40 26 38 40 34 13 40 18 48 196 34 1 26 2 18 2 4 1 1 + 0 1 50 2 0 1 22 16 15 3 8 2 3 0 0 0 9 8 31 1 50 1 4 48 196 + 51 50 1 0 14 0 0 40 43 30 48 196 51 50 22 9 0 5 13 46 30 8 16 15 8 + 2 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 0 0 5 21 10 48 196 10 8 7 1 0 14 0 0 3 42 12 48 196 12 8 7 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 49 values pushed */ + 6 5 4 3 4 13 1 0 0 8 7 2 1 7 3 0 1 4 48 196 9 0 1 0 14 + 0 0 7 6 4 1 2 1 4 48 196 9 8 1 3 2 1 5 4 1 0 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 85 values pushed */ + 0 0 8 11 13 48 196 33 32 31 30 27 26 11 10 8 13 13 29 28 25 0 0 0 23 + 22 1 38 2 0 1 4 48 196 34 29 1 24 0 1 2 0 14 0 0 6 37 17 48 196 + 27 26 24 23 22 11 10 1 0 9 13 17 33 32 31 28 25 4 13 29 0 0 34 33 7 + 1 29 1 4 48 196 30 29 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 107 values pushed */ + 19 10 0 2 9 12 7 2 22 21 4 3 2 1 6 13 10 23 20 16 6 4 13 7 0 + 0 18 17 13 12 38 3 7 1 4 48 196 11 10 1 15 14 8 7 3 5 0 1 3 0 + 14 22 11 6 2 21 17 10 9 8 5 6 4 3 23 4 0 2 14 13 2 13 11 20 3 + 2 3 13 0 0 0 19 18 7 6 22 3 11 5 4 7 1 0 2 4 48 196 16 15 12 + 11 3 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 4 3 2 1 4 13 0 5 0 1 0 14 3 2 0 0 0 1 0 7 1 4 1 4 48 + 196 5 4 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 80 values pushed */ + 0 0 39 16 2 12 20 17 48 196 17 0 1 35 34 33 32 28 27 20 19 15 14 10 9 + 8 0 14 13 30 22 2 3 12 0 0 14 0 0 37 7 6 48 196 32 0 2 19 9 3 + 28 27 2 13 19 35 15 14 8 4 13 6 9 0 0 34 33 10 9 7 3 19 1 4 48 + 196 20 19 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 29 values pushed */ + 0 0 20 20 8 16 20 0 48 196 0 0 1 8 0 0 14 0 0 22 15 4 18 15 12 + 48 196 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 63 values pushed */ + 0 0 35 40 9 27 40 20 48 196 20 2 9 1 1 1 39 32 31 22 14 11 3 0 8 + 1 2 3 0 0 13 12 1 2 1 1 2 0 14 0 0 37 9 5 29 9 16 48 196 39 + 32 31 22 16 14 13 12 11 5 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 28 30 8 22 40 16 48 196 16 2 8 1 1 20 19 4 3 2 2 3 0 1 18 + 2 0 2 0 1 0 1 0 3 2 1 14 0 0 24 9 12 48 196 12 0 0 0 20 19 + 18 4 3 0 4 5 1 1 4 48 196 2 1 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 57 values pushed */ + 1 1 13 2 2 0 1 15 12 11 0 4 13 2 0 0 0 14 13 6 1 9 1 4 48 + 196 10 9 0 14 5 0 0 0 13 12 19 1 10 9 1 0 19 2 14 2 4 48 196 11 + 10 1 15 14 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 17 values pushed */ + 10 9 1 0 14 0 0 14 41 5 48 196 10 9 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 17 values pushed */ + 10 9 1 0 14 0 0 14 41 5 48 196 10 9 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 60 21 44 52 21 36 28 21 12 20 21 4 48 196 44 2 4 0 36 12 1 1 36 + 12 2 0 2 3 0 0 2 1 1 3 0 1 2 0 14 0 0 64 15 40 56 15 48 32 + 15 8 24 15 16 48 196 48 40 16 8 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 2 1 8 1 0 1 4 48 196 3 0 1 0 14 0 0 3 2 8 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 3 0 8 1 1 1 4 48 196 2 1 1 0 14 0 0 3 2 8 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 0 0 3 0 8 1 1 1 4 48 196 2 1 1 0 14 0 0 3 2 8 1 0 1 4 + 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 87 values pushed */ + 0 0 72 21 60 68 21 52 46 21 34 42 21 26 20 21 8 16 21 0 48 196 60 2 34 + 2 0 0 52 26 8 1 1 52 26 8 3 0 2 3 0 0 1 80 79 2 13 0 0 1 + 81 78 2 0 14 0 0 74 7 56 70 7 64 48 7 30 44 7 38 22 7 4 18 7 12 + 48 196 81 80 79 78 64 56 38 30 12 4 + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MDAP[1] + MDAP[1] + MIAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 53 values pushed */ + 0 0 10 9 2 1 7 3 3 1 4 48 196 8 7 4 3 3 11 0 1 2 0 6 5 + 1 14 0 0 11 10 7 6 7 3 0 1 4 48 196 9 8 1 5 4 1 0 3 3 2 + 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 14 13 7 1 12 10 9 2 1 7 3 3 2 4 48 196 15 12 1 6 5 1 8 + 7 4 3 3 11 0 1 4 0 14 0 0 11 10 7 6 7 3 0 1 4 48 196 15 14 + 9 8 3 5 4 1 0 3 13 12 3 2 3 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 0 0 27 30 8 21 40 16 48 196 16 1 8 2 1 19 18 4 3 0 2 3 0 3 2 + 1 0 1 0 1 14 0 0 23 9 12 48 196 12 0 0 0 19 18 4 3 0 4 4 1 + 1 4 48 196 2 1 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 71 values pushed */ + 0 0 12 40 17 48 196 17 0 1 28 15 14 5 4 0 4 3 0 0 0 2 1 5 1 + 0 1 4 48 196 29 4 1 3 0 1 2 0 14 0 0 10 9 21 48 196 21 2 0 0 + 29 28 3 2 4 3 0 1 4 48 196 15 14 1 5 4 1 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 72 values pushed */ + 0 0 12 40 17 48 196 1 28 5 2 4 2 3 0 1 15 14 17 2 0 0 0 2 1 + 5 1 0 1 4 48 196 29 4 1 0 3 0 1 14 0 0 10 9 21 48 196 21 2 0 + 0 29 28 3 2 4 3 0 1 4 48 196 15 14 1 5 4 1 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 21 values pushed */ + 6 5 2 1 3 0 7 4 3 0 1 3 14 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 18 15 14 13 11 10 8 5 4 3 1 0 12 13 6 17 16 7 6 3 0 14 14 13 2 + 17 10 3 4 3 2 7 0 3 0 0 16 15 11 10 4 3 17 8 7 4 1 0 2 4 + 48 196 18 17 1 6 5 1 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 18 15 14 13 11 10 8 5 4 3 1 0 12 13 6 17 16 7 6 3 0 14 4 3 2 + 0 7 3 14 13 2 10 17 3 0 0 16 15 11 10 4 3 17 8 7 4 1 0 2 4 + 48 196 18 17 1 6 5 1 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 18 15 14 13 11 10 8 5 4 3 1 0 12 13 6 17 16 7 6 3 0 14 14 13 2 + 17 10 3 4 3 2 7 0 3 0 0 16 15 11 10 4 3 17 8 7 4 1 0 2 4 + 48 196 18 17 1 6 5 1 0 3 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 8 5 4 3 1 0 6 13 6 7 6 1 0 14 4 3 2 0 7 3 0 0 6 5 1 + 0 8 3 7 1 4 48 196 8 7 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 8 5 4 3 1 0 6 13 6 7 6 1 0 14 4 3 2 7 0 3 0 0 8 7 8 + 1 0 1 4 48 196 6 5 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 37 values pushed */ + 8 5 4 3 1 0 6 13 6 7 6 1 0 14 4 3 2 7 0 3 0 0 8 7 8 + 1 0 1 4 48 196 6 5 1 0 3 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SRP0[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 13 values pushed */ + 2 1 1 3 0 1 2 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + LOOPCALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 47 values pushed */ + 0 0 12 30 7 48 196 7 1 14 10 3 3 1 0 3 9 1 15 0 1 0 2 1 1 + 14 0 0 15 14 3 2 4 3 0 1 4 48 196 10 9 1 1 0 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00000] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 196 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 83 values pushed */ + 0 0 24 20 8 16 20 0 48 196 8 2 0 0 50 49 45 44 42 41 38 7 33 32 3 + 34 33 1 43 40 39 32 3 2 0 14 0 0 47 6 36 28 17 4 20 17 12 48 196 49 + 45 41 40 39 38 34 7 13 36 4 42 12 32 0 0 43 42 18 1 32 50 44 19 1 32 + 2 4 48 196 33 32 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 25 values pushed */ + 0 0 24 44 8 16 44 0 48 196 8 0 14 0 0 28 32 4 20 32 12 48 196 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 52 values pushed */ + 0 0 17 40 12 3 40 28 48 196 28 2 12 1 1 1 15 14 1 0 4 1 2 3 0 + 0 14 0 0 19 41 10 5 41 24 48 196 10 10 14 0 2 24 14 15 14 1 1 0 1 + 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 77 values pushed */ + 0 0 17 40 12 3 40 28 48 196 28 2 12 1 36 35 2 31 30 3 1 1 15 14 1 + 0 4 1 2 3 0 0 32 31 1 37 34 33 30 3 2 0 14 0 0 19 41 10 5 41 + 24 48 196 10 37 36 35 34 32 31 30 10 8 14 0 3 33 24 14 15 14 1 1 0 1 + 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 66 values pushed */ + 0 0 27 40 22 3 40 46 48 196 22 0 1 55 48 40 25 24 16 1 0 8 13 46 0 + 0 14 0 0 57 13 14 50 13 38 31 14 18 7 14 42 48 196 18 14 55 48 40 18 16 + 14 6 24 0 3 42 38 24 25 24 1 1 0 1 2 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + CALL[ ] + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 1 8 6 2 2 0 1 5 4 3 1 0 5 13 2 0 0 0 13 10 5 1 11 1 4 + 48 196 7 6 1 0 12 11 1 14 4 3 2 7 0 3 0 0 13 12 8 7 4 3 0 + 1 4 48 196 11 10 6 5 1 0 5 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 62 values pushed */ + 1 8 6 2 2 0 1 5 4 3 1 0 5 13 2 0 0 0 13 10 5 1 11 1 4 + 48 196 7 6 1 0 12 11 1 14 4 3 2 7 0 3 0 0 13 12 8 7 4 3 0 + 1 4 48 196 11 10 6 5 1 0 5 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 35 values pushed */ + 0 0 9 6 5 27 2 7 1 4 48 196 11 0 1 0 8 7 0 14 11 5 0 3 8 + 6 3 9 8 1 7 6 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 196 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 50 values pushed */ + 0 0 37 40 10 29 40 2 23 40 18 48 196 18 0 10 2 2 1 1 21 20 2 0 4 + 0 2 3 0 0 14 0 0 39 41 6 33 26 14 48 196 6 20 0 14 20 21 20 1 0 + CALL[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + NPUSHB[ ] /* 12 values pushed */ + 3 0 1 0 2 1 0 14 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 97 values pushed */ + 0 0 17 40 12 48 196 12 0 1 19 15 8 3 0 6 3 0 24 3 2 4 1 3 1 + 14 0 0 0 0 23 22 5 4 7 3 6 29 28 1 13 2 0 2 4 48 196 21 20 7 + 6 3 30 0 1 2 0 14 28 19 3 2 0 0 24 23 20 19 4 3 3 1 4 48 196 + 30 29 1 15 14 1 22 21 1 8 7 4 3 3 6 5 1 1 0 1 6 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 76 values pushed */ + 0 0 19 40 2 48 196 2 2 1 21 15 4 3 5 2 3 0 10 9 2 13 7 1 0 + 2 0 0 0 14 13 6 5 7 3 7 1 4 48 196 12 11 8 7 1 3 14 0 0 15 + 14 11 10 4 3 4 1 4 48 196 13 12 1 21 0 1 9 8 5 4 3 7 6 1 4 + 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 97 values pushed */ + 0 0 22 40 27 48 196 27 2 1 29 24 18 3 0 2 3 0 9 8 2 13 6 1 25 + 2 0 0 0 13 12 5 4 7 3 6 17 16 1 0 6 3 2 2 4 48 196 15 14 3 + 2 3 0 11 10 7 6 1 3 14 0 0 18 17 14 13 10 9 4 5 0 1 4 48 196 + 16 15 12 11 3 25 24 1 29 8 7 4 3 0 5 6 5 2 1 3 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 103 values pushed */ + 0 0 19 40 2 48 196 2 2 30 27 26 25 23 22 10 9 8 28 7 3 1 21 15 4 + 3 5 2 3 0 1 0 2 0 0 0 14 13 6 5 7 3 7 1 4 48 196 29 28 1 + 0 12 11 8 7 1 3 14 26 25 2 29 12 3 0 0 28 27 23 22 4 3 29 15 14 + 11 10 4 3 4 2 4 48 196 30 29 1 13 12 1 21 0 1 9 8 5 4 3 7 6 + 1 5 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00000] + SZP0[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 109 values pushed */ + 0 0 37 44 32 19 40 2 48 196 2 2 1 21 15 4 3 5 2 3 0 10 9 2 13 + 7 1 41 35 34 24 23 22 0 7 13 32 2 0 0 0 14 13 6 5 7 3 7 1 4 + 48 196 12 11 8 7 1 3 14 0 0 39 42 28 48 196 23 0 10 2 41 35 34 24 22 + 5 10 4 3 0 0 15 14 11 10 4 3 4 1 4 48 196 13 12 1 28 21 0 2 9 + 8 5 4 3 7 6 1 4 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 68 values pushed */ + 0 0 28 30 8 22 40 16 48 196 16 2 8 1 1 1 20 19 4 3 1 2 3 0 0 + 1 18 2 0 2 0 3 2 1 1 0 1 2 0 14 0 0 24 9 12 48 196 12 0 0 + 0 20 19 18 4 3 0 4 5 1 1 4 48 196 2 1 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 65 values pushed */ + 0 0 25 40 30 8 40 46 48 196 46 2 30 0 1 1 38 28 27 18 17 16 1 0 8 + 0 2 3 0 0 14 0 0 23 41 34 12 9 42 48 196 38 18 17 16 4 13 42 34 27 + 0 0 28 27 23 1 0 1 5 48 196 1 0 1 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + CALL[ ] + SZP0[ ] + SZP1[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 126 values pushed */ + 0 0 33 11 3 22 11 17 48 196 17 48 20 19 17 4 39 41 3 38 41 36 2 51 50 + 29 28 27 26 9 1 0 9 13 3 39 52 49 45 35 4 13 36 0 0 47 46 42 41 38 + 3 36 1 4 48 196 40 39 1 44 43 37 36 3 2 0 14 0 0 31 39 5 24 37 13 + 48 196 50 40 35 2 51 43 42 3 13 40 52 49 46 39 38 37 29 28 27 26 20 19 9 + 1 0 15 13 13 5 35 0 0 48 47 36 35 22 3 40 1 4 48 196 45 44 41 40 3 + 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + MDRP[00100] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 43 values pushed */ + 0 0 33 11 3 22 11 17 48 196 29 28 27 26 20 19 17 9 3 1 0 14 0 0 31 + 39 5 24 37 13 48 196 29 28 27 26 20 19 13 9 5 1 0 + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 30 values pushed */ + 0 0 21 6 4 9 6 16 48 196 4 0 1 12 11 2 13 0 0 1 23 0 16 0 0 + 14 23 12 11 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SZP0[ ] + SRP0[ ] + MDRP[00100] + MDRP[00000] + MDRP[00000] + SZP0[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 97 values pushed */ + 1 19 16 2 1 1 3 0 1 18 17 11 3 1 0 3 0 0 0 6 5 2 1 6 3 + 3 1 4 48 196 20 15 14 8 7 0 5 0 13 12 10 9 4 3 0 5 14 18 17 12 + 11 10 5 15 19 3 5 4 2 8 6 3 3 2 0 0 0 16 15 13 1 13 9 8 18 + 1 19 7 6 13 1 0 3 4 48 196 14 13 1 20 19 1 1 0 1 3 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00000] + MDRP[00000] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 0 0 11 40 16 48 196 16 0 1 14 13 2 0 1 3 0 0 0 28 27 1 13 2 0 + 1 4 48 196 29 0 1 0 14 0 0 7 9 20 48 196 27 28 13 2 20 28 29 28 1 + 14 13 1 1 0 1 3 0 + LOOPCALL[ ] + SRP0[ ] + MDRP[00100] + CALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 45 values pushed */ + 0 0 8 11 13 48 196 11 10 2 13 13 1 0 0 24 0 38 1 1 1 4 48 196 23 + 22 1 2 0 14 0 0 6 37 17 48 196 24 23 22 17 11 10 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[1] + MDAP[0] + MDAP[0] + MDAP[0] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 54 values pushed */ + 0 0 14 30 5 48 196 5 2 16 10 7 1 4 8 0 3 19 0 1 0 18 17 9 8 + 1 3 14 0 0 17 16 1 0 4 3 18 10 9 4 1 7 2 4 48 196 19 18 1 8 + 7 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 79 values pushed */ + 0 0 25 40 34 14 30 5 48 196 5 2 16 10 7 1 4 8 0 3 30 29 21 20 4 + 13 34 8 19 0 1 0 18 17 9 8 1 3 14 30 18 0 2 29 21 2 0 9 3 20 + 9 7 2 0 0 17 16 1 0 4 3 18 10 9 4 1 7 2 4 48 196 19 18 1 8 + 7 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SRP0[ ] + MDRP[00100] + LOOPCALL[ ] + CALL[ ] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 24 values pushed */ + 0 0 3 0 7 1 1 1 4 48 196 2 1 1 0 14 3 2 1 1 0 1 2 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 93 values pushed */ + 0 0 41 6 24 29 6 36 14 30 5 48 196 24 0 5 2 36 1 43 36 20 3 0 8 + 3 0 16 10 7 1 4 8 0 3 1 32 31 2 13 0 0 19 0 1 0 18 17 9 8 + 1 3 14 32 18 0 2 43 31 2 0 9 3 20 9 7 2 0 0 17 16 1 0 4 3 + 18 10 9 4 1 7 2 4 48 196 19 18 1 8 7 1 2 0 + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + CALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP0[ ] + SRP0[ ] + LOOPCALL[ ] + SZP0[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + MDAP[1] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + NPUSHB[ ] /* 22 values pushed */ + 3 1 0 2 6 0 1 0 5 4 2 1 1 3 14 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 35 values pushed */ + 11 6 3 3 1 0 3 12 10 9 0 3 0 8 7 5 4 2 1 1 5 14 12 11 10 + 9 8 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 58 values pushed */ + 19 18 2 14 13 3 11 6 3 3 1 0 3 15 14 1 20 17 16 13 3 12 10 9 0 + 3 3 0 8 7 5 4 2 1 1 5 14 20 19 18 17 16 15 14 13 12 11 10 9 8 + 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 33 values pushed */ + 10 7 4 1 4 2 0 3 11 9 8 0 3 0 6 5 3 2 1 3 14 11 10 9 8 + 7 6 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + NPUSHB[ ] /* 31 values pushed */ + 1 3 1 2 2 0 1 0 2 6 2 0 7 6 1 0 5 4 2 1 1 3 14 7 6 + 5 4 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 54 values pushed */ + 14 13 2 9 8 3 1 3 1 2 2 0 1 0 2 6 2 0 10 9 1 15 12 11 8 + 3 7 6 1 3 0 5 4 2 1 1 3 14 15 14 13 12 11 10 9 8 7 6 5 4 + 3 2 1 0 + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + MDAP[0] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SZP1[ ] + CALL[ ] + SZP1[ ] + SZP0[ ] + CALL[ ] + SZP0[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 93 values pushed */ + 12 11 2 9 7 3 0 0 18 17 6 5 6 3 7 20 19 4 3 6 3 1 2 4 48 + 196 16 15 8 7 3 22 21 2 1 3 23 0 1 3 0 14 13 10 9 0 3 14 12 11 + 2 15 0 3 21 20 17 16 14 13 6 13 15 10 9 7 6 3 2 6 13 0 0 0 23 + 22 19 18 15 4 4 0 1 4 48 196 8 5 4 1 0 4 0 + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + SRP0[ ] + LOOPCALL[ ] + SRP0[ ] + LOOPCALL[ ] + CALL[ ] + CALL[ ] + SLOOP[ ] + MIAP[1] + ALIGNRP[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + CALL[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 46 values pushed */ + 0 0 6 3 2 7 2 4 8 7 1 7 2 0 2 4 48 196 9 0 1 0 5 4 1 + 14 7 2 2 5 3 3 9 8 1 6 5 1 4 3 1 1 0 1 4 0 + LOOPCALL[ ] + CALL[ ] + CALL[ ] + MIAP[1] + ALIGNRP[ ] + CALL[ ] + SSW[ ] + SSWCI[ ] + LOOPCALL[ ] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NPUSHB[ ] /* 27 values pushed */ + 0 0 24 40 8 16 40 0 48 196 8 2 0 0 14 0 0 28 43 4 20 9 12 48 196 + 12 4 + MDAP[1] + MDAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + CALL[ ] + MIAP[1] + MIAP[1] + SSW[ ] + SSWCI[ ] + SRP0[ ] + MIRP[01101] + SRP0[ ] + MIRP[01101] + SSW[ ] + SSWCI[ ] + IUP[1] + IUP[0] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright (c) 2001 by Bigelow & Holmes Inc. Instructions copyright (c) 2001 by URW++. + + + Luxi Sans + + + Regular + + + Luxi Sans Regular: B&H + + + Luxi Sans Regular + + + 1.2 : October 12, 2001 + + + LuxiSans + + + Luxi is a registered trademark of Bigelow & Holmes Inc. + + + Bigelow & Holmes Inc. + + + Kris Holmes and Charles Bigelow + + + http://www.urwpp.de + + + design@bigelowandholmes.com + + + Copyright (c) 2001 by Bigelow & Holmes Inc. Instructions copyright (c) 2001 by URW++. + + + Luxi Sans + + + Regular + + + Luxi Sans Regular: B&H + + + Luxi Sans Regular + + + 1.2 : October 12, 2001 + + + LuxiSans + + + Luxi is a registered trademark of Bigelow & Holmes Inc. + + + Bigelow & Holmes Inc. + + + Kris Holmes and Charles Bigelow + + + http://www.urwpp.de + + + design@bigelowandholmes.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/github.com/golang/freetype/testdata/make-other-hinting-txts.sh b/vendor/github.com/golang/freetype/testdata/make-other-hinting-txts.sh new file mode 100755 index 000000000..afee131e8 --- /dev/null +++ b/vendor/github.com/golang/freetype/testdata/make-other-hinting-txts.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# +# This script creates the optional x-*-hinting.txt files from fonts that are +# not checked in for copyright or file size reasons. +# +# Run it from this directory (testdata). +# +# It has only been tested on an Ubuntu 14.04 system. + +set -e + +: ${FONTDIR:=/usr/share/fonts/truetype} + +ln -sf $FONTDIR/droid/DroidSansJapanese.ttf x-droid-sans-japanese.ttf +ln -sf $FONTDIR/msttcorefonts/Arial_Bold.ttf x-arial-bold.ttf +ln -sf $FONTDIR/msttcorefonts/Times_New_Roman.ttf x-times-new-roman.ttf +ln -sf $FONTDIR/ttf-dejavu/DejaVuSans-Oblique.ttf x-deja-vu-sans-oblique.ttf + +${CC:=gcc} ../cmd/print-glyph-points/main.c $(pkg-config --cflags --libs freetype2) -o print-glyph-points + +# Uncomment these lines to also recreate the luxisr-*-hinting.txt files. +# ./print-glyph-points 12 luxisr.ttf sans_hinting > luxisr-12pt-sans-hinting.txt +# ./print-glyph-points 12 luxisr.ttf with_hinting > luxisr-12pt-with-hinting.txt + +./print-glyph-points 9 x-droid-sans-japanese.ttf sans_hinting > x-droid-sans-japanese-9pt-sans-hinting.txt +./print-glyph-points 9 x-droid-sans-japanese.ttf with_hinting > x-droid-sans-japanese-9pt-with-hinting.txt +./print-glyph-points 11 x-arial-bold.ttf sans_hinting > x-arial-bold-11pt-sans-hinting.txt +./print-glyph-points 11 x-arial-bold.ttf with_hinting > x-arial-bold-11pt-with-hinting.txt +./print-glyph-points 13 x-times-new-roman.ttf sans_hinting > x-times-new-roman-13pt-sans-hinting.txt +./print-glyph-points 13 x-times-new-roman.ttf with_hinting > x-times-new-roman-13pt-with-hinting.txt +./print-glyph-points 17 x-deja-vu-sans-oblique.ttf sans_hinting > x-deja-vu-sans-oblique-17pt-sans-hinting.txt +./print-glyph-points 17 x-deja-vu-sans-oblique.ttf with_hinting > x-deja-vu-sans-oblique-17pt-with-hinting.txt + +rm print-glyph-points diff --git a/vendor/github.com/golang/freetype/truetype/face_test.go b/vendor/github.com/golang/freetype/truetype/face_test.go new file mode 100644 index 000000000..856581dff --- /dev/null +++ b/vendor/github.com/golang/freetype/truetype/face_test.go @@ -0,0 +1,48 @@ +// Copyright 2015 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +package truetype + +import ( + "image" + "image/draw" + "io/ioutil" + "strings" + "testing" + + "golang.org/x/image/font" + "golang.org/x/image/math/fixed" +) + +func BenchmarkDrawString(b *testing.B) { + data, err := ioutil.ReadFile("../licenses/gpl.txt") + if err != nil { + b.Fatal(err) + } + lines := strings.Split(string(data), "\n") + data, err = ioutil.ReadFile("../testdata/luxisr.ttf") + if err != nil { + b.Fatal(err) + } + f, err := Parse(data) + if err != nil { + b.Fatal(err) + } + dst := image.NewRGBA(image.Rect(0, 0, 800, 600)) + draw.Draw(dst, dst.Bounds(), image.White, image.ZP, draw.Src) + d := &font.Drawer{ + Dst: dst, + Src: image.Black, + Face: NewFace(f, nil), + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j, line := range lines { + d.Dot = fixed.P(0, (j*16)%600) + d.DrawString(line) + } + } +} diff --git a/vendor/github.com/golang/freetype/truetype/hint_test.go b/vendor/github.com/golang/freetype/truetype/hint_test.go new file mode 100644 index 000000000..7eb43dde0 --- /dev/null +++ b/vendor/github.com/golang/freetype/truetype/hint_test.go @@ -0,0 +1,675 @@ +// Copyright 2012 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +package truetype + +import ( + "reflect" + "strings" + "testing" + + "golang.org/x/image/math/fixed" +) + +func TestBytecode(t *testing.T) { + testCases := []struct { + desc string + prog []byte + want []int32 + errStr string + }{ + { + "underflow", + []byte{ + opDUP, + }, + nil, + "underflow", + }, + { + "infinite loop", + []byte{ + opPUSHW000, // [-1] + 0xff, + 0xff, + opDUP, // [-1, -1] + opJMPR, // [-1] + }, + nil, + "too many steps", + }, + { + "unbalanced if/else", + []byte{ + opPUSHB000, // [0] + 0, + opIF, + }, + nil, + "unbalanced", + }, + { + "vector set/gets", + []byte{ + opSVTCA1, // [] + opGPV, // [0x4000, 0] + opSVTCA0, // [0x4000, 0] + opGFV, // [0x4000, 0, 0, 0x4000] + opNEG, // [0x4000, 0, 0, -0x4000] + opSPVFS, // [0x4000, 0] + opSFVTPV, // [0x4000, 0] + opPUSHB000, // [0x4000, 0, 1] + 1, + opGFV, // [0x4000, 0, 1, 0, -0x4000] + opPUSHB000, // [0x4000, 0, 1, 0, -0x4000, 2] + 2, + }, + []int32{0x4000, 0, 1, 0, -0x4000, 2}, + "", + }, + { + "jumps", + []byte{ + opPUSHB001, // [10, 2] + 10, + 2, + opJMPR, // [10] + opDUP, // not executed + opDUP, // [10, 10] + opPUSHB010, // [10, 10, 20, 2, 1] + 20, + 2, + 1, + opJROT, // [10, 10, 20] + opDUP, // not executed + opDUP, // [10, 10, 20, 20] + opPUSHB010, // [10, 10, 20, 20, 30, 2, 1] + 30, + 2, + 1, + opJROF, // [10, 10, 20, 20, 30] + opDUP, // [10, 10, 20, 20, 30, 30] + opDUP, // [10, 10, 20, 20, 30, 30, 30] + }, + []int32{10, 10, 20, 20, 30, 30, 30}, + "", + }, + { + "stack ops", + []byte{ + opPUSHB010, // [10, 20, 30] + 10, + 20, + 30, + opCLEAR, // [] + opPUSHB010, // [40, 50, 60] + 40, + 50, + 60, + opSWAP, // [40, 60, 50] + opDUP, // [40, 60, 50, 50] + opDUP, // [40, 60, 50, 50, 50] + opPOP, // [40, 60, 50, 50] + opDEPTH, // [40, 60, 50, 50, 4] + opCINDEX, // [40, 60, 50, 50, 40] + opPUSHB000, // [40, 60, 50, 50, 40, 4] + 4, + opMINDEX, // [40, 50, 50, 40, 60] + }, + []int32{40, 50, 50, 40, 60}, + "", + }, + { + "push ops", + []byte{ + opPUSHB000, // [255] + 255, + opPUSHW001, // [255, -2, 253] + 255, + 254, + 0, + 253, + opNPUSHB, // [1, -2, 253, 1, 2] + 2, + 1, + 2, + opNPUSHW, // [1, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809] + 3, + 4, + 5, + 6, + 7, + 8, + 9, + }, + []int32{255, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809}, + "", + }, + { + "store ops", + []byte{ + opPUSHB011, // [1, 22, 3, 44] + 1, + 22, + 3, + 44, + opWS, // [1, 22] + opWS, // [] + opPUSHB000, // [3] + 3, + opRS, // [44] + }, + []int32{44}, + "", + }, + { + "comparison ops", + []byte{ + opPUSHB001, // [10, 20] + 10, + 20, + opLT, // [1] + opPUSHB001, // [1, 10, 20] + 10, + 20, + opLTEQ, // [1, 1] + opPUSHB001, // [1, 1, 10, 20] + 10, + 20, + opGT, // [1, 1, 0] + opPUSHB001, // [1, 1, 0, 10, 20] + 10, + 20, + opGTEQ, // [1, 1, 0, 0] + opEQ, // [1, 1, 1] + opNEQ, // [1, 0] + }, + []int32{1, 0}, + "", + }, + { + "odd/even", + // Calculate odd(2+31/64), odd(2+32/64), even(2), even(1). + []byte{ + opPUSHB000, // [159] + 159, + opODD, // [0] + opPUSHB000, // [0, 160] + 160, + opODD, // [0, 1] + opPUSHB000, // [0, 1, 128] + 128, + opEVEN, // [0, 1, 1] + opPUSHB000, // [0, 1, 1, 64] + 64, + opEVEN, // [0, 1, 1, 0] + }, + []int32{0, 1, 1, 0}, + "", + }, + { + "if true", + []byte{ + opPUSHB001, // [255, 1] + 255, + 1, + opIF, + opPUSHB000, // [255, 2] + 2, + opEIF, + opPUSHB000, // [255, 2, 254] + 254, + }, + []int32{255, 2, 254}, + "", + }, + { + "if false", + []byte{ + opPUSHB001, // [255, 0] + 255, + 0, + opIF, + opPUSHB000, // [255] + 2, + opEIF, + opPUSHB000, // [255, 254] + 254, + }, + []int32{255, 254}, + "", + }, + { + "if/else true", + []byte{ + opPUSHB000, // [1] + 1, + opIF, + opPUSHB000, // [2] + 2, + opELSE, + opPUSHB000, // not executed + 3, + opEIF, + }, + []int32{2}, + "", + }, + { + "if/else false", + []byte{ + opPUSHB000, // [0] + 0, + opIF, + opPUSHB000, // not executed + 2, + opELSE, + opPUSHB000, // [3] + 3, + opEIF, + }, + []int32{3}, + "", + }, + { + "if/else true if/else false", + // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data. + []byte{ + opPUSHB010, // [255, 0, 1] + 255, + 0, + 1, + opIF, + opIF, + opPUSHB001, // not executed + 0x58, + 0x58, + opELSE, + opPUSHW000, // [255, 0x5858] + 0x58, + 0x58, + opEIF, + opELSE, + opIF, + opNPUSHB, // not executed + 3, + 0x58, + 0x58, + 0x58, + opELSE, + opNPUSHW, // not executed + 2, + 0x58, + 0x58, + 0x58, + 0x58, + opEIF, + opEIF, + opPUSHB000, // [255, 0x5858, 254] + 254, + }, + []int32{255, 0x5858, 254}, + "", + }, + { + "if/else false if/else true", + // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data. + []byte{ + opPUSHB010, // [255, 1, 0] + 255, + 1, + 0, + opIF, + opIF, + opPUSHB001, // not executed + 0x58, + 0x58, + opELSE, + opPUSHW000, // not executed + 0x58, + 0x58, + opEIF, + opELSE, + opIF, + opNPUSHB, // [255, 0x58, 0x58, 0x58] + 3, + 0x58, + 0x58, + 0x58, + opELSE, + opNPUSHW, // not executed + 2, + 0x58, + 0x58, + 0x58, + 0x58, + opEIF, + opEIF, + opPUSHB000, // [255, 0x58, 0x58, 0x58, 254] + 254, + }, + []int32{255, 0x58, 0x58, 0x58, 254}, + "", + }, + { + "logical ops", + []byte{ + opPUSHB010, // [0, 10, 20] + 0, + 10, + 20, + opAND, // [0, 1] + opOR, // [1] + opNOT, // [0] + }, + []int32{0}, + "", + }, + { + "arithmetic ops", + // Calculate abs((-(1 - (2*3)))/2 + 1/64). + // The answer is 5/2 + 1/64 in ideal numbers, or 161 in 26.6 fixed point math. + []byte{ + opPUSHB010, // [64, 128, 192] + 1 << 6, + 2 << 6, + 3 << 6, + opMUL, // [64, 384] + opSUB, // [-320] + opNEG, // [320] + opPUSHB000, // [320, 128] + 2 << 6, + opDIV, // [160] + opPUSHB000, // [160, 1] + 1, + opADD, // [161] + opABS, // [161] + }, + []int32{161}, + "", + }, + { + "floor, ceiling", + []byte{ + opPUSHB000, // [96] + 96, + opFLOOR, // [64] + opPUSHB000, // [64, 96] + 96, + opCEILING, // [64, 128] + }, + []int32{64, 128}, + "", + }, + { + "rounding", + // Round 1.40625 (which is 90/64) under various rounding policies. + // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding + []byte{ + opROFF, // [] + opPUSHB000, // [90] + 90, + opROUND00, // [90] + opRTG, // [90] + opPUSHB000, // [90, 90] + 90, + opROUND00, // [90, 64] + opRTHG, // [90, 64] + opPUSHB000, // [90, 64, 90] + 90, + opROUND00, // [90, 64, 96] + opRDTG, // [90, 64, 96] + opPUSHB000, // [90, 64, 96, 90] + 90, + opROUND00, // [90, 64, 96, 64] + opRUTG, // [90, 64, 96, 64] + opPUSHB000, // [90, 64, 96, 64, 90] + 90, + opROUND00, // [90, 64, 96, 64, 128] + opRTDG, // [90, 64, 96, 64, 128] + opPUSHB000, // [90, 64, 96, 64, 128, 90] + 90, + opROUND00, // [90, 64, 96, 64, 128, 96] + }, + []int32{90, 64, 96, 64, 128, 96}, + "", + }, + { + "super-rounding", + // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding + // and the sign preservation steps of the "Order of rounding operations" section. + []byte{ + opPUSHB000, // [0x58] + 0x58, + opSROUND, // [] + opPUSHW000, // [-81] + 0xff, + 0xaf, + opROUND00, // [-80] + opPUSHW000, // [-80, -80] + 0xff, + 0xb0, + opROUND00, // [-80, -80] + opPUSHW000, // [-80, -80, -17] + 0xff, + 0xef, + opROUND00, // [-80, -80, -16] + opPUSHW000, // [-80, -80, -16, -16] + 0xff, + 0xf0, + opROUND00, // [-80, -80, -16, -16] + opPUSHB000, // [-80, -80, -16, -16, 0] + 0, + opROUND00, // [-80, -80, -16, -16, 16] + opPUSHB000, // [-80, -80, -16, -16, 16, 16] + 16, + opROUND00, // [-80, -80, -16, -16, 16, 16] + opPUSHB000, // [-80, -80, -16, -16, 16, 16, 47] + 47, + opROUND00, // [-80, -80, -16, -16, 16, 16, 16] + opPUSHB000, // [-80, -80, -16, -16, 16, 16, 16, 48] + 48, + opROUND00, // [-80, -80, -16, -16, 16, 16, 16, 80] + }, + []int32{-80, -80, -16, -16, 16, 16, 16, 80}, + "", + }, + { + "roll", + []byte{ + opPUSHB010, // [1, 2, 3] + 1, + 2, + 3, + opROLL, // [2, 3, 1] + }, + []int32{2, 3, 1}, + "", + }, + { + "max/min", + []byte{ + opPUSHW001, // [-2, -3] + 0xff, + 0xfe, + 0xff, + 0xfd, + opMAX, // [-2] + opPUSHW001, // [-2, -4, -5] + 0xff, + 0xfc, + 0xff, + 0xfb, + opMIN, // [-2, -5] + }, + []int32{-2, -5}, + "", + }, + { + "functions", + []byte{ + opPUSHB011, // [3, 7, 0, 3] + 3, + 7, + 0, + 3, + + opFDEF, // Function #3 (not called) + opPUSHB000, + 98, + opENDF, + + opFDEF, // Function #0 + opDUP, + opADD, + opENDF, + + opFDEF, // Function #7 + opPUSHB001, + 10, + 0, + opCALL, + opDUP, + opENDF, + + opFDEF, // Function #3 (again) + opPUSHB000, + 99, + opENDF, + + opPUSHB001, // [2, 0] + 2, + 0, + opCALL, // [4] + opPUSHB000, // [4, 3] + 3, + opLOOPCALL, // [99, 99, 99, 99] + opPUSHB000, // [99, 99, 99, 99, 7] + 7, + opCALL, // [99, 99, 99, 99, 20, 20] + }, + []int32{99, 99, 99, 99, 20, 20}, + "", + }, + } + + for _, tc := range testCases { + h := &hinter{} + h.init(&Font{ + maxStorage: 32, + maxStackElements: 100, + }, 768) + err, errStr := h.run(tc.prog, nil, nil, nil, nil), "" + if err != nil { + errStr = err.Error() + } + if tc.errStr != "" { + if errStr == "" { + t.Errorf("%s: got no error, want %q", tc.desc, tc.errStr) + } else if !strings.Contains(errStr, tc.errStr) { + t.Errorf("%s: got error %q, want one containing %q", tc.desc, errStr, tc.errStr) + } + continue + } + if errStr != "" { + t.Errorf("%s: got error %q, want none", tc.desc, errStr) + continue + } + got := h.stack[:len(tc.want)] + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("%s: got %v, want %v", tc.desc, got, tc.want) + continue + } + } +} + +// TestMove tests that the hinter.move method matches the output of the C +// Freetype implementation. +func TestMove(t *testing.T) { + h, p := hinter{}, Point{} + testCases := []struct { + pvX, pvY, fvX, fvY f2dot14 + wantX, wantY fixed.Int26_6 + }{ + {+0x4000, +0x0000, +0x4000, +0x0000, +1000, +0}, + {+0x4000, +0x0000, -0x4000, +0x0000, +1000, +0}, + {-0x4000, +0x0000, +0x4000, +0x0000, -1000, +0}, + {-0x4000, +0x0000, -0x4000, +0x0000, -1000, +0}, + {+0x0000, +0x4000, +0x0000, +0x4000, +0, +1000}, + {+0x0000, +0x4000, +0x0000, -0x4000, +0, +1000}, + {+0x4000, +0x0000, +0x2d41, +0x2d41, +1000, +1000}, + {+0x4000, +0x0000, -0x2d41, +0x2d41, +1000, -1000}, + {+0x4000, +0x0000, +0x2d41, -0x2d41, +1000, -1000}, + {+0x4000, +0x0000, -0x2d41, -0x2d41, +1000, +1000}, + {-0x4000, +0x0000, +0x2d41, +0x2d41, -1000, -1000}, + {-0x4000, +0x0000, -0x2d41, +0x2d41, -1000, +1000}, + {-0x4000, +0x0000, +0x2d41, -0x2d41, -1000, +1000}, + {-0x4000, +0x0000, -0x2d41, -0x2d41, -1000, -1000}, + {+0x376d, +0x2000, +0x2d41, +0x2d41, +732, +732}, + {-0x376d, +0x2000, +0x2d41, +0x2d41, -2732, -2732}, + {+0x376d, +0x2000, +0x2d41, -0x2d41, +2732, -2732}, + {-0x376d, +0x2000, +0x2d41, -0x2d41, -732, +732}, + {-0x376d, -0x2000, +0x2d41, +0x2d41, -732, -732}, + {+0x376d, +0x2000, +0x4000, +0x0000, +1155, +0}, + {+0x376d, +0x2000, +0x0000, +0x4000, +0, +2000}, + } + for _, tc := range testCases { + p = Point{} + h.gs.pv = [2]f2dot14{tc.pvX, tc.pvY} + h.gs.fv = [2]f2dot14{tc.fvX, tc.fvY} + h.move(&p, 1000, true) + tx := p.Flags&flagTouchedX != 0 + ty := p.Flags&flagTouchedY != 0 + wantTX := tc.fvX != 0 + wantTY := tc.fvY != 0 + if p.X != tc.wantX || p.Y != tc.wantY || tx != wantTX || ty != wantTY { + t.Errorf("pv=%v, fv=%v\ngot %d, %d, %t, %t\nwant %d, %d, %t, %t", + h.gs.pv, h.gs.fv, p.X, p.Y, tx, ty, tc.wantX, tc.wantY, wantTX, wantTY) + continue + } + + // Check that p is aligned with the freedom vector. + a := int64(p.X) * int64(tc.fvY) + b := int64(p.Y) * int64(tc.fvX) + if a != b { + t.Errorf("pv=%v, fv=%v, p=%v not aligned with fv", h.gs.pv, h.gs.fv, p) + continue + } + + // Check that the projected p is 1000 away from the origin. + dotProd := (int64(p.X)*int64(tc.pvX) + int64(p.Y)*int64(tc.pvY) + 1<<13) >> 14 + if dotProd != 1000 { + t.Errorf("pv=%v, fv=%v, p=%v not 1000 from origin", h.gs.pv, h.gs.fv, p) + continue + } + } +} + +// TestNormalize tests that the normalize function matches the output of the C +// Freetype implementation. +func TestNormalize(t *testing.T) { + testCases := [][2]f2dot14{ + {-15895, 3974}, + {-15543, 5181}, + {-14654, 7327}, + {-11585, 11585}, + {0, 16384}, + {11585, 11585}, + {14654, 7327}, + {15543, 5181}, + {15895, 3974}, + {16066, 3213}, + {16161, 2694}, + {16219, 2317}, + {16257, 2032}, + {16284, 1809}, + } + for i, want := range testCases { + got := normalize(f2dot14(i)-4, 1) + if got != want { + t.Errorf("i=%d: got %v, want %v", i, got, want) + } + } +} diff --git a/vendor/github.com/golang/freetype/truetype/truetype.go b/vendor/github.com/golang/freetype/truetype/truetype.go index 9e943d3ec..613fc17e2 100644 --- a/vendor/github.com/golang/freetype/truetype/truetype.go +++ b/vendor/github.com/golang/freetype/truetype/truetype.go @@ -15,7 +15,7 @@ // // To measure a TrueType font in ideal FUnit space, use scale equal to // font.FUnitsPerEm(). -package truetype +package truetype // import "github.com/golang/freetype/truetype" import ( "fmt" diff --git a/vendor/github.com/golang/freetype/truetype/truetype_test.go b/vendor/github.com/golang/freetype/truetype/truetype_test.go new file mode 100644 index 000000000..bd62d1da1 --- /dev/null +++ b/vendor/github.com/golang/freetype/truetype/truetype_test.go @@ -0,0 +1,400 @@ +// Copyright 2012 The Freetype-Go Authors. All rights reserved. +// Use of this source code is governed by your choice of either the +// FreeType License or the GNU General Public License version 2 (or +// any later version), both of which can be found in the LICENSE file. + +package truetype + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "strconv" + "strings" + "testing" + + "golang.org/x/image/font" + "golang.org/x/image/math/fixed" +) + +func parseTestdataFont(name string) (f *Font, testdataIsOptional bool, err error) { + b, err := ioutil.ReadFile(fmt.Sprintf("../testdata/%s.ttf", name)) + if err != nil { + // The "x-foo" fonts are optional tests, as they are not checked + // in for copyright or file size reasons. + return nil, strings.HasPrefix(name, "x-"), fmt.Errorf("%s: ReadFile: %v", name, err) + } + f, err = Parse(b) + if err != nil { + return nil, true, fmt.Errorf("%s: Parse: %v", name, err) + } + return f, false, nil +} + +func mkBounds(minX, minY, maxX, maxY fixed.Int26_6) fixed.Rectangle26_6 { + return fixed.Rectangle26_6{ + Min: fixed.Point26_6{ + X: minX, + Y: minY, + }, + Max: fixed.Point26_6{ + X: maxX, + Y: maxY, + }, + } +} + +// TestParse tests that the luxisr.ttf metrics and glyphs are parsed correctly. +// The numerical values can be manually verified by examining luxisr.ttx. +func TestParse(t *testing.T) { + f, _, err := parseTestdataFont("luxisr") + if err != nil { + t.Fatal(err) + } + if got, want := f.FUnitsPerEm(), int32(2048); got != want { + t.Errorf("FUnitsPerEm: got %v, want %v", got, want) + } + fupe := fixed.Int26_6(f.FUnitsPerEm()) + if got, want := f.Bounds(fupe), mkBounds(-441, -432, 2024, 2033); got != want { + t.Errorf("Bounds: got %v, want %v", got, want) + } + + i0 := f.Index('A') + i1 := f.Index('V') + if i0 != 36 || i1 != 57 { + t.Fatalf("Index: i0, i1 = %d, %d, want 36, 57", i0, i1) + } + if got, want := f.HMetric(fupe, i0), (HMetric{1366, 19}); got != want { + t.Errorf("HMetric: got %v, want %v", got, want) + } + if got, want := f.VMetric(fupe, i0), (VMetric{2465, 553}); got != want { + t.Errorf("VMetric: got %v, want %v", got, want) + } + if got, want := f.Kern(fupe, i0, i1), fixed.Int26_6(-144); got != want { + t.Errorf("Kern: got %v, want %v", got, want) + } + + g := &GlyphBuf{} + err = g.Load(f, fupe, i0, font.HintingNone) + if err != nil { + t.Fatalf("Load: %v", err) + } + g0 := &GlyphBuf{ + Bounds: g.Bounds, + Points: g.Points, + Ends: g.Ends, + } + g1 := &GlyphBuf{ + Bounds: mkBounds(19, 0, 1342, 1480), + Points: []Point{ + {19, 0, 51}, + {581, 1480, 1}, + {789, 1480, 51}, + {1342, 0, 1}, + {1116, 0, 35}, + {962, 410, 3}, + {368, 410, 33}, + {214, 0, 3}, + {428, 566, 19}, + {904, 566, 33}, + {667, 1200, 3}, + }, + Ends: []int{8, 11}, + } + if got, want := fmt.Sprint(g0), fmt.Sprint(g1); got != want { + t.Errorf("GlyphBuf:\ngot %v\nwant %v", got, want) + } +} + +func TestIndex(t *testing.T) { + testCases := map[string]map[rune]Index{ + "luxisr": { + ' ': 3, + '!': 4, + 'A': 36, + 'V': 57, + 'É': 101, + 'fl': 193, + '\u22c5': 385, + '中': 0, + }, + + // The x-etc test cases use those versions of the .ttf files provided + // by Ubuntu 14.04. See testdata/make-other-hinting-txts.sh for details. + + "x-arial-bold": { + ' ': 3, + '+': 14, + '0': 19, + '_': 66, + 'w': 90, + '~': 97, + 'Ä': 98, + 'fl': 192, + '½': 242, + 'σ': 305, + 'λ': 540, + 'ỹ': 1275, + '\u04e9': 1319, + '中': 0, + }, + "x-deja-vu-sans-oblique": { + ' ': 3, + '*': 13, + 'Œ': 276, + 'ω': 861, + '‡': 2571, + '⊕': 3110, + 'fl': 4728, + '\ufb03': 4729, + '\ufffd': 4813, + // TODO: '\U0001f640': ???, + '中': 0, + }, + "x-droid-sans-japanese": { + ' ': 0, + '\u3000': 3, + '\u3041': 25, + '\u30fe': 201, + '\uff61': 202, + '\uff67': 208, + '\uff9e': 263, + '\uff9f': 264, + '\u4e00': 265, + '\u557e': 1000, + '\u61b6': 2024, + '\u6ede': 3177, + '\u7505': 3555, + '\u81e3': 4602, + '\u81e5': 4603, + '\u81e7': 4604, + '\u81e8': 4605, + '\u81ea': 4606, + '\u81ed': 4607, + '\u81f3': 4608, + '\u81f4': 4609, + '\u91c7': 5796, + '\u9fa0': 6620, + '\u203e': 12584, + }, + "x-times-new-roman": { + ' ': 3, + ':': 29, + 'fl': 192, + 'Ŀ': 273, + '♠': 388, + 'Ŗ': 451, + 'Σ': 520, + '\u200D': 745, + 'Ẽ': 1216, + '\u04e9': 1319, + '中': 0, + }, + } + for name, wants := range testCases { + f, testdataIsOptional, err := parseTestdataFont(name) + if err != nil { + if testdataIsOptional { + t.Log(err) + } else { + t.Fatal(err) + } + continue + } + for r, want := range wants { + if got := f.Index(r); got != want { + t.Errorf("%s: Index of %q, aka %U: got %d, want %d", name, r, r, got, want) + } + } + } +} + +func TestName(t *testing.T) { + testCases := map[string]string{ + "luximr": "Luxi Mono", + "luxirr": "Luxi Serif", + "luxisr": "Luxi Sans", + } + + for name, want := range testCases { + f, testdataIsOptional, err := parseTestdataFont(name) + if err != nil { + if testdataIsOptional { + t.Log(err) + } else { + t.Fatal(err) + } + continue + } + if got := f.Name(NameIDFontFamily); got != want { + t.Errorf("%s: got %q, want %q", name, got, want) + } + } +} + +type scalingTestData struct { + advanceWidth fixed.Int26_6 + bounds fixed.Rectangle26_6 + points []Point +} + +// scalingTestParse parses a line of points like +// 213 -22 -111 236 555;-22 -111 1, 178 555 1, 236 555 1, 36 -111 1 +// The line will not have a trailing "\n". +func scalingTestParse(line string) (ret scalingTestData) { + next := func(s string) (string, fixed.Int26_6) { + t, i := "", strings.Index(s, " ") + if i != -1 { + s, t = s[:i], s[i+1:] + } + x, _ := strconv.Atoi(s) + return t, fixed.Int26_6(x) + } + + i := strings.Index(line, ";") + prefix, line := line[:i], line[i+1:] + + prefix, ret.advanceWidth = next(prefix) + prefix, ret.bounds.Min.X = next(prefix) + prefix, ret.bounds.Min.Y = next(prefix) + prefix, ret.bounds.Max.X = next(prefix) + prefix, ret.bounds.Max.Y = next(prefix) + + ret.points = make([]Point, 0, 1+strings.Count(line, ",")) + for len(line) > 0 { + s := line + if i := strings.Index(line, ","); i != -1 { + s, line = line[:i], line[i+1:] + for len(line) > 0 && line[0] == ' ' { + line = line[1:] + } + } else { + line = "" + } + s, x := next(s) + s, y := next(s) + s, f := next(s) + ret.points = append(ret.points, Point{X: x, Y: y, Flags: uint32(f)}) + } + return ret +} + +// scalingTestEquals is equivalent to, but faster than, calling +// reflect.DeepEquals(a, b), and also returns the index of the first non-equal +// element. It also treats a nil []Point and an empty non-nil []Point as equal. +// a and b must have equal length. +func scalingTestEquals(a, b []Point) (index int, equals bool) { + for i, p := range a { + if p != b[i] { + return i, false + } + } + return 0, true +} + +var scalingTestCases = []struct { + name string + size int +}{ + {"luxisr", 12}, + {"x-arial-bold", 11}, + {"x-deja-vu-sans-oblique", 17}, + {"x-droid-sans-japanese", 9}, + {"x-times-new-roman", 13}, +} + +func testScaling(t *testing.T, h font.Hinting) { + for _, tc := range scalingTestCases { + f, testdataIsOptional, err := parseTestdataFont(tc.name) + if err != nil { + if testdataIsOptional { + t.Log(err) + } else { + t.Error(err) + } + continue + } + hintingStr := "sans" + if h != font.HintingNone { + hintingStr = "with" + } + testFile, err := os.Open(fmt.Sprintf( + "../testdata/%s-%dpt-%s-hinting.txt", tc.name, tc.size, hintingStr)) + if err != nil { + t.Errorf("%s: Open: %v", tc.name, err) + continue + } + defer testFile.Close() + + wants := []scalingTestData{} + scanner := bufio.NewScanner(testFile) + if scanner.Scan() { + major, minor, patch := 0, 0, 0 + _, err := fmt.Sscanf(scanner.Text(), "freetype version %d.%d.%d", &major, &minor, &patch) + if err != nil { + t.Errorf("%s: version information: %v", tc.name, err) + } + if (major < 2) || (major == 2 && minor < 5) || (major == 2 && minor == 5 && patch < 1) { + t.Errorf("%s: need freetype version >= 2.5.1.\n"+ + "Try setting LD_LIBRARY_PATH=/path/to/freetype_built_from_src/objs/.libs/\n"+ + "and re-running testdata/make-other-hinting-txts.sh", + tc.name) + continue + } + } else { + t.Errorf("%s: no version information", tc.name) + continue + } + for scanner.Scan() { + wants = append(wants, scalingTestParse(scanner.Text())) + } + if err := scanner.Err(); err != nil && err != io.EOF { + t.Errorf("%s: Scanner: %v", tc.name, err) + continue + } + + glyphBuf := &GlyphBuf{} + for i, want := range wants { + if err = glyphBuf.Load(f, fixed.I(tc.size), Index(i), h); err != nil { + t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err) + continue + } + got := scalingTestData{ + advanceWidth: glyphBuf.AdvanceWidth, + bounds: glyphBuf.Bounds, + points: glyphBuf.Points, + } + + if got.advanceWidth != want.advanceWidth { + t.Errorf("%s: glyph #%d advance width:\ngot %v\nwant %v", + tc.name, i, got.advanceWidth, want.advanceWidth) + continue + } + + if got.bounds != want.bounds { + t.Errorf("%s: glyph #%d bounds:\ngot %v\nwant %v", + tc.name, i, got.bounds, want.bounds) + continue + } + + for i := range got.points { + got.points[i].Flags &= 0x01 + } + if len(got.points) != len(want.points) { + t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\ndifferent slice lengths: %d versus %d", + tc.name, i, got.points, want.points, len(got.points), len(want.points)) + continue + } + if j, equals := scalingTestEquals(got.points, want.points); !equals { + t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\nat index %d: %v versus %v", + tc.name, i, got.points, want.points, j, got.points[j], want.points[j]) + continue + } + } + } +} + +func TestScalingHintingNone(t *testing.T) { testScaling(t, font.HintingNone) } +func TestScalingHintingFull(t *testing.T) { testScaling(t, font.HintingFull) } diff --git a/vendor/github.com/golang/groupcache/.gitignore b/vendor/github.com/golang/groupcache/.gitignore new file mode 100644 index 000000000..b25c15b81 --- /dev/null +++ b/vendor/github.com/golang/groupcache/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/vendor/github.com/golang/groupcache/README.md b/vendor/github.com/golang/groupcache/README.md new file mode 100644 index 000000000..70c29da16 --- /dev/null +++ b/vendor/github.com/golang/groupcache/README.md @@ -0,0 +1,73 @@ +# groupcache + +## Summary + +groupcache is a caching and cache-filling library, intended as a +replacement for memcached in many cases. + +For API docs and examples, see http://godoc.org/github.com/golang/groupcache + +## Comparison to memcached + +### **Like memcached**, groupcache: + + * shards by key to select which peer is responsible for that key + +### **Unlike memcached**, groupcache: + + * does not require running a separate set of servers, thus massively + reducing deployment/configuration pain. groupcache is a client + library as well as a server. It connects to its own peers. + + * comes with a cache filling mechanism. Whereas memcached just says + "Sorry, cache miss", often resulting in a thundering herd of + database (or whatever) loads from an unbounded number of clients + (which has resulted in several fun outages), groupcache coordinates + cache fills such that only one load in one process of an entire + replicated set of processes populates the cache, then multiplexes + the loaded value to all callers. + + * does not support versioned values. If key "foo" is value "bar", + key "foo" must always be "bar". There are neither cache expiration + times, nor explicit cache evictions. Thus there is also no CAS, + nor Increment/Decrement. This also means that groupcache.... + + * ... supports automatic mirroring of super-hot items to multiple + processes. This prevents memcached hot spotting where a machine's + CPU and/or NIC are overloaded by very popular keys/values. + + * is currently only available for Go. It's very unlikely that I + (bradfitz@) will port the code to any other language. + +## Loading process + +In a nutshell, a groupcache lookup of **Get("foo")** looks like: + +(On machine #5 of a set of N machines running the same code) + + 1. Is the value of "foo" in local memory because it's super hot? If so, use it. + + 2. Is the value of "foo" in local memory because peer #5 (the current + peer) is the owner of it? If so, use it. + + 3. Amongst all the peers in my set of N, am I the owner of the key + "foo"? (e.g. does it consistent hash to 5?) If so, load it. If + other callers come in, via the same process or via RPC requests + from peers, they block waiting for the load to finish and get the + same answer. If not, RPC to the peer that's the owner and get + the answer. If the RPC fails, just load it locally (still with + local dup suppression). + +## Users + +groupcache is in production use by dl.google.com (its original user), +parts of Blogger, parts of Google Code, parts of Google Fiber, parts +of Google production monitoring systems, etc. + +## Presentations + +See http://talks.golang.org/2013/oscon-dl.slide + +## Help + +Use the golang-nuts mailing list for any discussion or questions. diff --git a/vendor/github.com/golang/groupcache/byteview.go b/vendor/github.com/golang/groupcache/byteview.go new file mode 100644 index 000000000..035a9ee44 --- /dev/null +++ b/vendor/github.com/golang/groupcache/byteview.go @@ -0,0 +1,160 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package groupcache + +import ( + "bytes" + "errors" + "io" + "strings" +) + +// A ByteView holds an immutable view of bytes. +// Internally it wraps either a []byte or a string, +// but that detail is invisible to callers. +// +// A ByteView is meant to be used as a value type, not +// a pointer (like a time.Time). +type ByteView struct { + // If b is non-nil, b is used, else s is used. + b []byte + s string +} + +// Len returns the view's length. +func (v ByteView) Len() int { + if v.b != nil { + return len(v.b) + } + return len(v.s) +} + +// ByteSlice returns a copy of the data as a byte slice. +func (v ByteView) ByteSlice() []byte { + if v.b != nil { + return cloneBytes(v.b) + } + return []byte(v.s) +} + +// String returns the data as a string, making a copy if necessary. +func (v ByteView) String() string { + if v.b != nil { + return string(v.b) + } + return v.s +} + +// At returns the byte at index i. +func (v ByteView) At(i int) byte { + if v.b != nil { + return v.b[i] + } + return v.s[i] +} + +// Slice slices the view between the provided from and to indices. +func (v ByteView) Slice(from, to int) ByteView { + if v.b != nil { + return ByteView{b: v.b[from:to]} + } + return ByteView{s: v.s[from:to]} +} + +// SliceFrom slices the view from the provided index until the end. +func (v ByteView) SliceFrom(from int) ByteView { + if v.b != nil { + return ByteView{b: v.b[from:]} + } + return ByteView{s: v.s[from:]} +} + +// Copy copies b into dest and returns the number of bytes copied. +func (v ByteView) Copy(dest []byte) int { + if v.b != nil { + return copy(dest, v.b) + } + return copy(dest, v.s) +} + +// Equal returns whether the bytes in b are the same as the bytes in +// b2. +func (v ByteView) Equal(b2 ByteView) bool { + if b2.b == nil { + return v.EqualString(b2.s) + } + return v.EqualBytes(b2.b) +} + +// EqualString returns whether the bytes in b are the same as the bytes +// in s. +func (v ByteView) EqualString(s string) bool { + if v.b == nil { + return v.s == s + } + l := v.Len() + if len(s) != l { + return false + } + for i, bi := range v.b { + if bi != s[i] { + return false + } + } + return true +} + +// EqualBytes returns whether the bytes in b are the same as the bytes +// in b2. +func (v ByteView) EqualBytes(b2 []byte) bool { + if v.b != nil { + return bytes.Equal(v.b, b2) + } + l := v.Len() + if len(b2) != l { + return false + } + for i, bi := range b2 { + if bi != v.s[i] { + return false + } + } + return true +} + +// Reader returns an io.ReadSeeker for the bytes in v. +func (v ByteView) Reader() io.ReadSeeker { + if v.b != nil { + return bytes.NewReader(v.b) + } + return strings.NewReader(v.s) +} + +// ReadAt implements io.ReaderAt on the bytes in v. +func (v ByteView) ReadAt(p []byte, off int64) (n int, err error) { + if off < 0 { + return 0, errors.New("view: invalid offset") + } + if off >= int64(v.Len()) { + return 0, io.EOF + } + n = v.SliceFrom(int(off)).Copy(p) + if n < len(p) { + err = io.EOF + } + return +} diff --git a/vendor/github.com/golang/groupcache/byteview_test.go b/vendor/github.com/golang/groupcache/byteview_test.go new file mode 100644 index 000000000..9ece00f45 --- /dev/null +++ b/vendor/github.com/golang/groupcache/byteview_test.go @@ -0,0 +1,142 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package groupcache + +import ( + "fmt" + "io" + "io/ioutil" + "testing" +) + +func TestByteView(t *testing.T) { + for _, s := range []string{"", "x", "yy"} { + for _, v := range []ByteView{of([]byte(s)), of(s)} { + name := fmt.Sprintf("string %q, view %+v", s, v) + if v.Len() != len(s) { + t.Errorf("%s: Len = %d; want %d", name, v.Len(), len(s)) + } + if v.String() != s { + t.Errorf("%s: String = %q; want %q", name, v.String(), s) + } + var longDest [3]byte + if n := v.Copy(longDest[:]); n != len(s) { + t.Errorf("%s: long Copy = %d; want %d", name, n, len(s)) + } + var shortDest [1]byte + if n := v.Copy(shortDest[:]); n != min(len(s), 1) { + t.Errorf("%s: short Copy = %d; want %d", name, n, min(len(s), 1)) + } + if got, err := ioutil.ReadAll(v.Reader()); err != nil || string(got) != s { + t.Errorf("%s: Reader = %q, %v; want %q", name, got, err, s) + } + if got, err := ioutil.ReadAll(io.NewSectionReader(v, 0, int64(len(s)))); err != nil || string(got) != s { + t.Errorf("%s: SectionReader of ReaderAt = %q, %v; want %q", name, got, err, s) + } + } + } +} + +// of returns a byte view of the []byte or string in x. +func of(x interface{}) ByteView { + if bytes, ok := x.([]byte); ok { + return ByteView{b: bytes} + } + return ByteView{s: x.(string)} +} + +func TestByteViewEqual(t *testing.T) { + tests := []struct { + a interface{} // string or []byte + b interface{} // string or []byte + want bool + }{ + {"x", "x", true}, + {"x", "y", false}, + {"x", "yy", false}, + {[]byte("x"), []byte("x"), true}, + {[]byte("x"), []byte("y"), false}, + {[]byte("x"), []byte("yy"), false}, + {[]byte("x"), "x", true}, + {[]byte("x"), "y", false}, + {[]byte("x"), "yy", false}, + {"x", []byte("x"), true}, + {"x", []byte("y"), false}, + {"x", []byte("yy"), false}, + } + for i, tt := range tests { + va := of(tt.a) + if bytes, ok := tt.b.([]byte); ok { + if got := va.EqualBytes(bytes); got != tt.want { + t.Errorf("%d. EqualBytes = %v; want %v", i, got, tt.want) + } + } else { + if got := va.EqualString(tt.b.(string)); got != tt.want { + t.Errorf("%d. EqualString = %v; want %v", i, got, tt.want) + } + } + if got := va.Equal(of(tt.b)); got != tt.want { + t.Errorf("%d. Equal = %v; want %v", i, got, tt.want) + } + } +} + +func TestByteViewSlice(t *testing.T) { + tests := []struct { + in string + from int + to interface{} // nil to mean the end (SliceFrom); else int + want string + }{ + { + in: "abc", + from: 1, + to: 2, + want: "b", + }, + { + in: "abc", + from: 1, + want: "bc", + }, + { + in: "abc", + to: 2, + want: "ab", + }, + } + for i, tt := range tests { + for _, v := range []ByteView{of([]byte(tt.in)), of(tt.in)} { + name := fmt.Sprintf("test %d, view %+v", i, v) + if tt.to != nil { + v = v.Slice(tt.from, tt.to.(int)) + } else { + v = v.SliceFrom(tt.from) + } + if v.String() != tt.want { + t.Errorf("%s: got %q; want %q", name, v.String(), tt.want) + } + } + } +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/vendor/github.com/golang/groupcache/consistenthash/consistenthash.go b/vendor/github.com/golang/groupcache/consistenthash/consistenthash.go new file mode 100644 index 000000000..a9c56f076 --- /dev/null +++ b/vendor/github.com/golang/groupcache/consistenthash/consistenthash.go @@ -0,0 +1,81 @@ +/* +Copyright 2013 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package consistenthash provides an implementation of a ring hash. +package consistenthash + +import ( + "hash/crc32" + "sort" + "strconv" +) + +type Hash func(data []byte) uint32 + +type Map struct { + hash Hash + replicas int + keys []int // Sorted + hashMap map[int]string +} + +func New(replicas int, fn Hash) *Map { + m := &Map{ + replicas: replicas, + hash: fn, + hashMap: make(map[int]string), + } + if m.hash == nil { + m.hash = crc32.ChecksumIEEE + } + return m +} + +// Returns true if there are no items available. +func (m *Map) IsEmpty() bool { + return len(m.keys) == 0 +} + +// Adds some keys to the hash. +func (m *Map) Add(keys ...string) { + for _, key := range keys { + for i := 0; i < m.replicas; i++ { + hash := int(m.hash([]byte(strconv.Itoa(i) + key))) + m.keys = append(m.keys, hash) + m.hashMap[hash] = key + } + } + sort.Ints(m.keys) +} + +// Gets the closest item in the hash to the provided key. +func (m *Map) Get(key string) string { + if m.IsEmpty() { + return "" + } + + hash := int(m.hash([]byte(key))) + + // Binary search for appropriate replica. + idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash }) + + // Means we have cycled back to the first replica. + if idx == len(m.keys) { + idx = 0 + } + + return m.hashMap[m.keys[idx]] +} diff --git a/vendor/github.com/golang/groupcache/consistenthash/consistenthash_test.go b/vendor/github.com/golang/groupcache/consistenthash/consistenthash_test.go new file mode 100644 index 000000000..1a37fd7ff --- /dev/null +++ b/vendor/github.com/golang/groupcache/consistenthash/consistenthash_test.go @@ -0,0 +1,110 @@ +/* +Copyright 2013 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package consistenthash + +import ( + "fmt" + "strconv" + "testing" +) + +func TestHashing(t *testing.T) { + + // Override the hash function to return easier to reason about values. Assumes + // the keys can be converted to an integer. + hash := New(3, func(key []byte) uint32 { + i, err := strconv.Atoi(string(key)) + if err != nil { + panic(err) + } + return uint32(i) + }) + + // Given the above hash function, this will give replicas with "hashes": + // 2, 4, 6, 12, 14, 16, 22, 24, 26 + hash.Add("6", "4", "2") + + testCases := map[string]string{ + "2": "2", + "11": "2", + "23": "4", + "27": "2", + } + + for k, v := range testCases { + if hash.Get(k) != v { + t.Errorf("Asking for %s, should have yielded %s", k, v) + } + } + + // Adds 8, 18, 28 + hash.Add("8") + + // 27 should now map to 8. + testCases["27"] = "8" + + for k, v := range testCases { + if hash.Get(k) != v { + t.Errorf("Asking for %s, should have yielded %s", k, v) + } + } + +} + +func TestConsistency(t *testing.T) { + hash1 := New(1, nil) + hash2 := New(1, nil) + + hash1.Add("Bill", "Bob", "Bonny") + hash2.Add("Bob", "Bonny", "Bill") + + if hash1.Get("Ben") != hash2.Get("Ben") { + t.Errorf("Fetching 'Ben' from both hashes should be the same") + } + + hash2.Add("Becky", "Ben", "Bobby") + + if hash1.Get("Ben") != hash2.Get("Ben") || + hash1.Get("Bob") != hash2.Get("Bob") || + hash1.Get("Bonny") != hash2.Get("Bonny") { + t.Errorf("Direct matches should always return the same entry") + } + +} + +func BenchmarkGet8(b *testing.B) { benchmarkGet(b, 8) } +func BenchmarkGet32(b *testing.B) { benchmarkGet(b, 32) } +func BenchmarkGet128(b *testing.B) { benchmarkGet(b, 128) } +func BenchmarkGet512(b *testing.B) { benchmarkGet(b, 512) } + +func benchmarkGet(b *testing.B, shards int) { + + hash := New(50, nil) + + var buckets []string + for i := 0; i < shards; i++ { + buckets = append(buckets, fmt.Sprintf("shard-%d", i)) + } + + hash.Add(buckets...) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + hash.Get(buckets[i&(shards-1)]) + } +} diff --git a/vendor/github.com/golang/groupcache/groupcache.go b/vendor/github.com/golang/groupcache/groupcache.go new file mode 100644 index 000000000..40410a0cc --- /dev/null +++ b/vendor/github.com/golang/groupcache/groupcache.go @@ -0,0 +1,489 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package groupcache provides a data loading mechanism with caching +// and de-duplication that works across a set of peer processes. +// +// Each data Get first consults its local cache, otherwise delegates +// to the requested key's canonical owner, which then checks its cache +// or finally gets the data. In the common case, many concurrent +// cache misses across a set of peers for the same key result in just +// one cache fill. +package groupcache + +import ( + "errors" + "math/rand" + "strconv" + "sync" + "sync/atomic" + + pb "github.com/golang/groupcache/groupcachepb" + "github.com/golang/groupcache/lru" + "github.com/golang/groupcache/singleflight" +) + +// A Getter loads data for a key. +type Getter interface { + // Get returns the value identified by key, populating dest. + // + // The returned data must be unversioned. That is, key must + // uniquely describe the loaded data, without an implicit + // current time, and without relying on cache expiration + // mechanisms. + Get(ctx Context, key string, dest Sink) error +} + +// A GetterFunc implements Getter with a function. +type GetterFunc func(ctx Context, key string, dest Sink) error + +func (f GetterFunc) Get(ctx Context, key string, dest Sink) error { + return f(ctx, key, dest) +} + +var ( + mu sync.RWMutex + groups = make(map[string]*Group) + + initPeerServerOnce sync.Once + initPeerServer func() +) + +// GetGroup returns the named group previously created with NewGroup, or +// nil if there's no such group. +func GetGroup(name string) *Group { + mu.RLock() + g := groups[name] + mu.RUnlock() + return g +} + +// NewGroup creates a coordinated group-aware Getter from a Getter. +// +// The returned Getter tries (but does not guarantee) to run only one +// Get call at once for a given key across an entire set of peer +// processes. Concurrent callers both in the local process and in +// other processes receive copies of the answer once the original Get +// completes. +// +// The group name must be unique for each getter. +func NewGroup(name string, cacheBytes int64, getter Getter) *Group { + return newGroup(name, cacheBytes, getter, nil) +} + +// If peers is nil, the peerPicker is called via a sync.Once to initialize it. +func newGroup(name string, cacheBytes int64, getter Getter, peers PeerPicker) *Group { + if getter == nil { + panic("nil Getter") + } + mu.Lock() + defer mu.Unlock() + initPeerServerOnce.Do(callInitPeerServer) + if _, dup := groups[name]; dup { + panic("duplicate registration of group " + name) + } + g := &Group{ + name: name, + getter: getter, + peers: peers, + cacheBytes: cacheBytes, + loadGroup: &singleflight.Group{}, + } + if fn := newGroupHook; fn != nil { + fn(g) + } + groups[name] = g + return g +} + +// newGroupHook, if non-nil, is called right after a new group is created. +var newGroupHook func(*Group) + +// RegisterNewGroupHook registers a hook that is run each time +// a group is created. +func RegisterNewGroupHook(fn func(*Group)) { + if newGroupHook != nil { + panic("RegisterNewGroupHook called more than once") + } + newGroupHook = fn +} + +// RegisterServerStart registers a hook that is run when the first +// group is created. +func RegisterServerStart(fn func()) { + if initPeerServer != nil { + panic("RegisterServerStart called more than once") + } + initPeerServer = fn +} + +func callInitPeerServer() { + if initPeerServer != nil { + initPeerServer() + } +} + +// A Group is a cache namespace and associated data loaded spread over +// a group of 1 or more machines. +type Group struct { + name string + getter Getter + peersOnce sync.Once + peers PeerPicker + cacheBytes int64 // limit for sum of mainCache and hotCache size + + // mainCache is a cache of the keys for which this process + // (amongst its peers) is authorative. That is, this cache + // contains keys which consistent hash on to this process's + // peer number. + mainCache cache + + // hotCache contains keys/values for which this peer is not + // authorative (otherwise they would be in mainCache), but + // are popular enough to warrant mirroring in this process to + // avoid going over the network to fetch from a peer. Having + // a hotCache avoids network hotspotting, where a peer's + // network card could become the bottleneck on a popular key. + // This cache is used sparingly to maximize the total number + // of key/value pairs that can be stored globally. + hotCache cache + + // loadGroup ensures that each key is only fetched once + // (either locally or remotely), regardless of the number of + // concurrent callers. + loadGroup flightGroup + + // Stats are statistics on the group. + Stats Stats +} + +// flightGroup is defined as an interface which flightgroup.Group +// satisfies. We define this so that we may test with an alternate +// implementation. +type flightGroup interface { + // Done is called when Do is done. + Do(key string, fn func() (interface{}, error)) (interface{}, error) +} + +// Stats are per-group statistics. +type Stats struct { + Gets AtomicInt // any Get request, including from peers + CacheHits AtomicInt // either cache was good + PeerLoads AtomicInt // either remote load or remote cache hit (not an error) + PeerErrors AtomicInt + Loads AtomicInt // (gets - cacheHits) + LoadsDeduped AtomicInt // after singleflight + LocalLoads AtomicInt // total good local loads + LocalLoadErrs AtomicInt // total bad local loads + ServerRequests AtomicInt // gets that came over the network from peers +} + +// Name returns the name of the group. +func (g *Group) Name() string { + return g.name +} + +func (g *Group) initPeers() { + if g.peers == nil { + g.peers = getPeers() + } +} + +func (g *Group) Get(ctx Context, key string, dest Sink) error { + g.peersOnce.Do(g.initPeers) + g.Stats.Gets.Add(1) + if dest == nil { + return errors.New("groupcache: nil dest Sink") + } + value, cacheHit := g.lookupCache(key) + + if cacheHit { + g.Stats.CacheHits.Add(1) + return setSinkView(dest, value) + } + + // Optimization to avoid double unmarshalling or copying: keep + // track of whether the dest was already populated. One caller + // (if local) will set this; the losers will not. The common + // case will likely be one caller. + destPopulated := false + value, destPopulated, err := g.load(ctx, key, dest) + if err != nil { + return err + } + if destPopulated { + return nil + } + return setSinkView(dest, value) +} + +// load loads key either by invoking the getter locally or by sending it to another machine. +func (g *Group) load(ctx Context, key string, dest Sink) (value ByteView, destPopulated bool, err error) { + g.Stats.Loads.Add(1) + viewi, err := g.loadGroup.Do(key, func() (interface{}, error) { + // Check the cache again because singleflight can only dedup calls + // that overlap concurrently. It's possible for 2 concurrent + // requests to miss the cache, resulting in 2 load() calls. An + // unfortunate goroutine scheduling would result in this callback + // being run twice, serially. If we don't check the cache again, + // cache.nbytes would be incremented below even though there will + // be only one entry for this key. + // + // Consider the following serialized event ordering for two + // goroutines in which this callback gets called twice for hte + // same key: + // 1: Get("key") + // 2: Get("key") + // 1: lookupCache("key") + // 2: lookupCache("key") + // 1: load("key") + // 2: load("key") + // 1: loadGroup.Do("key", fn) + // 1: fn() + // 2: loadGroup.Do("key", fn) + // 2: fn() + if value, cacheHit := g.lookupCache(key); cacheHit { + g.Stats.CacheHits.Add(1) + return value, nil + } + g.Stats.LoadsDeduped.Add(1) + var value ByteView + var err error + if peer, ok := g.peers.PickPeer(key); ok { + value, err = g.getFromPeer(ctx, peer, key) + if err == nil { + g.Stats.PeerLoads.Add(1) + return value, nil + } + g.Stats.PeerErrors.Add(1) + // TODO(bradfitz): log the peer's error? keep + // log of the past few for /groupcachez? It's + // probably boring (normal task movement), so not + // worth logging I imagine. + } + value, err = g.getLocally(ctx, key, dest) + if err != nil { + g.Stats.LocalLoadErrs.Add(1) + return nil, err + } + g.Stats.LocalLoads.Add(1) + destPopulated = true // only one caller of load gets this return value + g.populateCache(key, value, &g.mainCache) + return value, nil + }) + if err == nil { + value = viewi.(ByteView) + } + return +} + +func (g *Group) getLocally(ctx Context, key string, dest Sink) (ByteView, error) { + err := g.getter.Get(ctx, key, dest) + if err != nil { + return ByteView{}, err + } + return dest.view() +} + +func (g *Group) getFromPeer(ctx Context, peer ProtoGetter, key string) (ByteView, error) { + req := &pb.GetRequest{ + Group: &g.name, + Key: &key, + } + res := &pb.GetResponse{} + err := peer.Get(ctx, req, res) + if err != nil { + return ByteView{}, err + } + value := ByteView{b: res.Value} + // TODO(bradfitz): use res.MinuteQps or something smart to + // conditionally populate hotCache. For now just do it some + // percentage of the time. + if rand.Intn(10) == 0 { + g.populateCache(key, value, &g.hotCache) + } + return value, nil +} + +func (g *Group) lookupCache(key string) (value ByteView, ok bool) { + if g.cacheBytes <= 0 { + return + } + value, ok = g.mainCache.get(key) + if ok { + return + } + value, ok = g.hotCache.get(key) + return +} + +func (g *Group) populateCache(key string, value ByteView, cache *cache) { + if g.cacheBytes <= 0 { + return + } + cache.add(key, value) + + // Evict items from cache(s) if necessary. + for { + mainBytes := g.mainCache.bytes() + hotBytes := g.hotCache.bytes() + if mainBytes+hotBytes <= g.cacheBytes { + return + } + + // TODO(bradfitz): this is good-enough-for-now logic. + // It should be something based on measurements and/or + // respecting the costs of different resources. + victim := &g.mainCache + if hotBytes > mainBytes/8 { + victim = &g.hotCache + } + victim.removeOldest() + } +} + +// CacheType represents a type of cache. +type CacheType int + +const ( + // The MainCache is the cache for items that this peer is the + // owner for. + MainCache CacheType = iota + 1 + + // The HotCache is the cache for items that seem popular + // enough to replicate to this node, even though it's not the + // owner. + HotCache +) + +// CacheStats returns stats about the provided cache within the group. +func (g *Group) CacheStats(which CacheType) CacheStats { + switch which { + case MainCache: + return g.mainCache.stats() + case HotCache: + return g.hotCache.stats() + default: + return CacheStats{} + } +} + +// cache is a wrapper around an *lru.Cache that adds synchronization, +// makes values always be ByteView, and counts the size of all keys and +// values. +type cache struct { + mu sync.RWMutex + nbytes int64 // of all keys and values + lru *lru.Cache + nhit, nget int64 + nevict int64 // number of evictions +} + +func (c *cache) stats() CacheStats { + c.mu.RLock() + defer c.mu.RUnlock() + return CacheStats{ + Bytes: c.nbytes, + Items: c.itemsLocked(), + Gets: c.nget, + Hits: c.nhit, + Evictions: c.nevict, + } +} + +func (c *cache) add(key string, value ByteView) { + c.mu.Lock() + defer c.mu.Unlock() + if c.lru == nil { + c.lru = &lru.Cache{ + OnEvicted: func(key lru.Key, value interface{}) { + val := value.(ByteView) + c.nbytes -= int64(len(key.(string))) + int64(val.Len()) + c.nevict++ + }, + } + } + c.lru.Add(key, value) + c.nbytes += int64(len(key)) + int64(value.Len()) +} + +func (c *cache) get(key string) (value ByteView, ok bool) { + c.mu.Lock() + defer c.mu.Unlock() + c.nget++ + if c.lru == nil { + return + } + vi, ok := c.lru.Get(key) + if !ok { + return + } + c.nhit++ + return vi.(ByteView), true +} + +func (c *cache) removeOldest() { + c.mu.Lock() + defer c.mu.Unlock() + if c.lru != nil { + c.lru.RemoveOldest() + } +} + +func (c *cache) bytes() int64 { + c.mu.RLock() + defer c.mu.RUnlock() + return c.nbytes +} + +func (c *cache) items() int64 { + c.mu.RLock() + defer c.mu.RUnlock() + return c.itemsLocked() +} + +func (c *cache) itemsLocked() int64 { + if c.lru == nil { + return 0 + } + return int64(c.lru.Len()) +} + +// An AtomicInt is an int64 to be accessed atomically. +type AtomicInt int64 + +// Add atomically adds n to i. +func (i *AtomicInt) Add(n int64) { + atomic.AddInt64((*int64)(i), n) +} + +// Get atomically gets the value of i. +func (i *AtomicInt) Get() int64 { + return atomic.LoadInt64((*int64)(i)) +} + +func (i *AtomicInt) String() string { + return strconv.FormatInt(i.Get(), 10) +} + +// CacheStats are returned by stats accessors on Group. +type CacheStats struct { + Bytes int64 + Items int64 + Gets int64 + Hits int64 + Evictions int64 +} diff --git a/vendor/github.com/golang/groupcache/groupcache_test.go b/vendor/github.com/golang/groupcache/groupcache_test.go new file mode 100644 index 000000000..3a4ecc2cc --- /dev/null +++ b/vendor/github.com/golang/groupcache/groupcache_test.go @@ -0,0 +1,447 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Tests for groupcache. + +package groupcache + +import ( + "errors" + "fmt" + "hash/crc32" + "math/rand" + "reflect" + "sync" + "testing" + "time" + + "github.com/golang/protobuf/proto" + + pb "github.com/golang/groupcache/groupcachepb" + testpb "github.com/golang/groupcache/testpb" +) + +var ( + once sync.Once + stringGroup, protoGroup Getter + + stringc = make(chan string) + + dummyCtx Context + + // cacheFills is the number of times stringGroup or + // protoGroup's Getter have been called. Read using the + // cacheFills function. + cacheFills AtomicInt +) + +const ( + stringGroupName = "string-group" + protoGroupName = "proto-group" + testMessageType = "google3/net/groupcache/go/test_proto.TestMessage" + fromChan = "from-chan" + cacheSize = 1 << 20 +) + +func testSetup() { + stringGroup = NewGroup(stringGroupName, cacheSize, GetterFunc(func(_ Context, key string, dest Sink) error { + if key == fromChan { + key = <-stringc + } + cacheFills.Add(1) + return dest.SetString("ECHO:" + key) + })) + + protoGroup = NewGroup(protoGroupName, cacheSize, GetterFunc(func(_ Context, key string, dest Sink) error { + if key == fromChan { + key = <-stringc + } + cacheFills.Add(1) + return dest.SetProto(&testpb.TestMessage{ + Name: proto.String("ECHO:" + key), + City: proto.String("SOME-CITY"), + }) + })) +} + +// tests that a Getter's Get method is only called once with two +// outstanding callers. This is the string variant. +func TestGetDupSuppressString(t *testing.T) { + once.Do(testSetup) + // Start two getters. The first should block (waiting reading + // from stringc) and the second should latch on to the first + // one. + resc := make(chan string, 2) + for i := 0; i < 2; i++ { + go func() { + var s string + if err := stringGroup.Get(dummyCtx, fromChan, StringSink(&s)); err != nil { + resc <- "ERROR:" + err.Error() + return + } + resc <- s + }() + } + + // Wait a bit so both goroutines get merged together via + // singleflight. + // TODO(bradfitz): decide whether there are any non-offensive + // debug/test hooks that could be added to singleflight to + // make a sleep here unnecessary. + time.Sleep(250 * time.Millisecond) + + // Unblock the first getter, which should unblock the second + // as well. + stringc <- "foo" + + for i := 0; i < 2; i++ { + select { + case v := <-resc: + if v != "ECHO:foo" { + t.Errorf("got %q; want %q", v, "ECHO:foo") + } + case <-time.After(5 * time.Second): + t.Errorf("timeout waiting on getter #%d of 2", i+1) + } + } +} + +// tests that a Getter's Get method is only called once with two +// outstanding callers. This is the proto variant. +func TestGetDupSuppressProto(t *testing.T) { + once.Do(testSetup) + // Start two getters. The first should block (waiting reading + // from stringc) and the second should latch on to the first + // one. + resc := make(chan *testpb.TestMessage, 2) + for i := 0; i < 2; i++ { + go func() { + tm := new(testpb.TestMessage) + if err := protoGroup.Get(dummyCtx, fromChan, ProtoSink(tm)); err != nil { + tm.Name = proto.String("ERROR:" + err.Error()) + } + resc <- tm + }() + } + + // Wait a bit so both goroutines get merged together via + // singleflight. + // TODO(bradfitz): decide whether there are any non-offensive + // debug/test hooks that could be added to singleflight to + // make a sleep here unnecessary. + time.Sleep(250 * time.Millisecond) + + // Unblock the first getter, which should unblock the second + // as well. + stringc <- "Fluffy" + want := &testpb.TestMessage{ + Name: proto.String("ECHO:Fluffy"), + City: proto.String("SOME-CITY"), + } + for i := 0; i < 2; i++ { + select { + case v := <-resc: + if !reflect.DeepEqual(v, want) { + t.Errorf(" Got: %v\nWant: %v", proto.CompactTextString(v), proto.CompactTextString(want)) + } + case <-time.After(5 * time.Second): + t.Errorf("timeout waiting on getter #%d of 2", i+1) + } + } +} + +func countFills(f func()) int64 { + fills0 := cacheFills.Get() + f() + return cacheFills.Get() - fills0 +} + +func TestCaching(t *testing.T) { + once.Do(testSetup) + fills := countFills(func() { + for i := 0; i < 10; i++ { + var s string + if err := stringGroup.Get(dummyCtx, "TestCaching-key", StringSink(&s)); err != nil { + t.Fatal(err) + } + } + }) + if fills != 1 { + t.Errorf("expected 1 cache fill; got %d", fills) + } +} + +func TestCacheEviction(t *testing.T) { + once.Do(testSetup) + testKey := "TestCacheEviction-key" + getTestKey := func() { + var res string + for i := 0; i < 10; i++ { + if err := stringGroup.Get(dummyCtx, testKey, StringSink(&res)); err != nil { + t.Fatal(err) + } + } + } + fills := countFills(getTestKey) + if fills != 1 { + t.Fatalf("expected 1 cache fill; got %d", fills) + } + + g := stringGroup.(*Group) + evict0 := g.mainCache.nevict + + // Trash the cache with other keys. + var bytesFlooded int64 + // cacheSize/len(testKey) is approximate + for bytesFlooded < cacheSize+1024 { + var res string + key := fmt.Sprintf("dummy-key-%d", bytesFlooded) + stringGroup.Get(dummyCtx, key, StringSink(&res)) + bytesFlooded += int64(len(key) + len(res)) + } + evicts := g.mainCache.nevict - evict0 + if evicts <= 0 { + t.Errorf("evicts = %v; want more than 0", evicts) + } + + // Test that the key is gone. + fills = countFills(getTestKey) + if fills != 1 { + t.Fatalf("expected 1 cache fill after cache trashing; got %d", fills) + } +} + +type fakePeer struct { + hits int + fail bool +} + +func (p *fakePeer) Get(_ Context, in *pb.GetRequest, out *pb.GetResponse) error { + p.hits++ + if p.fail { + return errors.New("simulated error from peer") + } + out.Value = []byte("got:" + in.GetKey()) + return nil +} + +type fakePeers []ProtoGetter + +func (p fakePeers) PickPeer(key string) (peer ProtoGetter, ok bool) { + if len(p) == 0 { + return + } + n := crc32.Checksum([]byte(key), crc32.IEEETable) % uint32(len(p)) + return p[n], p[n] != nil +} + +// tests that peers (virtual, in-process) are hit, and how much. +func TestPeers(t *testing.T) { + once.Do(testSetup) + rand.Seed(123) + peer0 := &fakePeer{} + peer1 := &fakePeer{} + peer2 := &fakePeer{} + peerList := fakePeers([]ProtoGetter{peer0, peer1, peer2, nil}) + const cacheSize = 0 // disabled + localHits := 0 + getter := func(_ Context, key string, dest Sink) error { + localHits++ + return dest.SetString("got:" + key) + } + testGroup := newGroup("TestPeers-group", cacheSize, GetterFunc(getter), peerList) + run := func(name string, n int, wantSummary string) { + // Reset counters + localHits = 0 + for _, p := range []*fakePeer{peer0, peer1, peer2} { + p.hits = 0 + } + + for i := 0; i < n; i++ { + key := fmt.Sprintf("key-%d", i) + want := "got:" + key + var got string + err := testGroup.Get(dummyCtx, key, StringSink(&got)) + if err != nil { + t.Errorf("%s: error on key %q: %v", name, key, err) + continue + } + if got != want { + t.Errorf("%s: for key %q, got %q; want %q", name, key, got, want) + } + } + summary := func() string { + return fmt.Sprintf("localHits = %d, peers = %d %d %d", localHits, peer0.hits, peer1.hits, peer2.hits) + } + if got := summary(); got != wantSummary { + t.Errorf("%s: got %q; want %q", name, got, wantSummary) + } + } + resetCacheSize := func(maxBytes int64) { + g := testGroup + g.cacheBytes = maxBytes + g.mainCache = cache{} + g.hotCache = cache{} + } + + // Base case; peers all up, with no problems. + resetCacheSize(1 << 20) + run("base", 200, "localHits = 49, peers = 51 49 51") + + // Verify cache was hit. All localHits are gone, and some of + // the peer hits (the ones randomly selected to be maybe hot) + run("cached_base", 200, "localHits = 0, peers = 49 47 48") + resetCacheSize(0) + + // With one of the peers being down. + // TODO(bradfitz): on a peer number being unavailable, the + // consistent hashing should maybe keep trying others to + // spread the load out. Currently it fails back to local + // execution if the first consistent-hash slot is unavailable. + peerList[0] = nil + run("one_peer_down", 200, "localHits = 100, peers = 0 49 51") + + // Failing peer + peerList[0] = peer0 + peer0.fail = true + run("peer0_failing", 200, "localHits = 100, peers = 51 49 51") +} + +func TestTruncatingByteSliceTarget(t *testing.T) { + var buf [100]byte + s := buf[:] + if err := stringGroup.Get(dummyCtx, "short", TruncatingByteSliceSink(&s)); err != nil { + t.Fatal(err) + } + if want := "ECHO:short"; string(s) != want { + t.Errorf("short key got %q; want %q", s, want) + } + + s = buf[:6] + if err := stringGroup.Get(dummyCtx, "truncated", TruncatingByteSliceSink(&s)); err != nil { + t.Fatal(err) + } + if want := "ECHO:t"; string(s) != want { + t.Errorf("truncated key got %q; want %q", s, want) + } +} + +func TestAllocatingByteSliceTarget(t *testing.T) { + var dst []byte + sink := AllocatingByteSliceSink(&dst) + + inBytes := []byte("some bytes") + sink.SetBytes(inBytes) + if want := "some bytes"; string(dst) != want { + t.Errorf("SetBytes resulted in %q; want %q", dst, want) + } + v, err := sink.view() + if err != nil { + t.Fatalf("view after SetBytes failed: %v", err) + } + if &inBytes[0] == &dst[0] { + t.Error("inBytes and dst share memory") + } + if &inBytes[0] == &v.b[0] { + t.Error("inBytes and view share memory") + } + if &dst[0] == &v.b[0] { + t.Error("dst and view share memory") + } +} + +// orderedFlightGroup allows the caller to force the schedule of when +// orig.Do will be called. This is useful to serialize calls such +// that singleflight cannot dedup them. +type orderedFlightGroup struct { + mu sync.Mutex + stage1 chan bool + stage2 chan bool + orig flightGroup +} + +func (g *orderedFlightGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) { + <-g.stage1 + <-g.stage2 + g.mu.Lock() + defer g.mu.Unlock() + return g.orig.Do(key, fn) +} + +// TestNoDedup tests invariants on the cache size when singleflight is +// unable to dedup calls. +func TestNoDedup(t *testing.T) { + const testkey = "testkey" + const testval = "testval" + g := newGroup("testgroup", 1024, GetterFunc(func(_ Context, key string, dest Sink) error { + return dest.SetString(testval) + }), nil) + + orderedGroup := &orderedFlightGroup{ + stage1: make(chan bool), + stage2: make(chan bool), + orig: g.loadGroup, + } + // Replace loadGroup with our wrapper so we can control when + // loadGroup.Do is entered for each concurrent request. + g.loadGroup = orderedGroup + + // Issue two idential requests concurrently. Since the cache is + // empty, it will miss. Both will enter load(), but we will only + // allow one at a time to enter singleflight.Do, so the callback + // function will be called twice. + resc := make(chan string, 2) + for i := 0; i < 2; i++ { + go func() { + var s string + if err := g.Get(dummyCtx, testkey, StringSink(&s)); err != nil { + resc <- "ERROR:" + err.Error() + return + } + resc <- s + }() + } + + // Ensure both goroutines have entered the Do routine. This implies + // both concurrent requests have checked the cache, found it empty, + // and called load(). + orderedGroup.stage1 <- true + orderedGroup.stage1 <- true + orderedGroup.stage2 <- true + orderedGroup.stage2 <- true + + for i := 0; i < 2; i++ { + if s := <-resc; s != testval { + t.Errorf("result is %s want %s", s, testval) + } + } + + const wantItems = 1 + if g.mainCache.items() != wantItems { + t.Errorf("mainCache has %d items, want %d", g.mainCache.items(), wantItems) + } + + // If the singleflight callback doesn't double-check the cache again + // upon entry, we would increment nbytes twice but the entry would + // only be in the cache once. + const wantBytes = int64(len(testkey) + len(testval)) + if g.mainCache.nbytes != wantBytes { + t.Errorf("cache has %d bytes, want %d", g.mainCache.nbytes, wantBytes) + } +} + +// TODO(bradfitz): port the Google-internal full integration test into here, +// using HTTP requests instead of our RPC system. diff --git a/vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go b/vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go new file mode 100644 index 000000000..520d1ee9a --- /dev/null +++ b/vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go @@ -0,0 +1,65 @@ +// Code generated by protoc-gen-go. +// source: groupcache.proto +// DO NOT EDIT! + +package groupcachepb + +import proto "github.com/golang/protobuf/proto" +import json "encoding/json" +import math "math" + +// Reference proto, json, and math imports to suppress error if they are not otherwise used. +var _ = proto.Marshal +var _ = &json.SyntaxError{} +var _ = math.Inf + +type GetRequest struct { + Group *string `protobuf:"bytes,1,req,name=group" json:"group,omitempty"` + Key *string `protobuf:"bytes,2,req,name=key" json:"key,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetRequest) Reset() { *m = GetRequest{} } +func (m *GetRequest) String() string { return proto.CompactTextString(m) } +func (*GetRequest) ProtoMessage() {} + +func (m *GetRequest) GetGroup() string { + if m != nil && m.Group != nil { + return *m.Group + } + return "" +} + +func (m *GetRequest) GetKey() string { + if m != nil && m.Key != nil { + return *m.Key + } + return "" +} + +type GetResponse struct { + Value []byte `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` + MinuteQps *float64 `protobuf:"fixed64,2,opt,name=minute_qps" json:"minute_qps,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *GetResponse) Reset() { *m = GetResponse{} } +func (m *GetResponse) String() string { return proto.CompactTextString(m) } +func (*GetResponse) ProtoMessage() {} + +func (m *GetResponse) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *GetResponse) GetMinuteQps() float64 { + if m != nil && m.MinuteQps != nil { + return *m.MinuteQps + } + return 0 +} + +func init() { +} diff --git a/vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto b/vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto new file mode 100644 index 000000000..b5bdff94f --- /dev/null +++ b/vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto @@ -0,0 +1,34 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +syntax = "proto2"; + +package groupcachepb; + +message GetRequest { + required string group = 1; + required string key = 2; // not actually required/guaranteed to be UTF-8 +} + +message GetResponse { + optional bytes value = 1; + optional double minute_qps = 2; +} + +service GroupCache { + rpc Get(GetRequest) returns (GetResponse) { + }; +} diff --git a/vendor/github.com/golang/groupcache/http.go b/vendor/github.com/golang/groupcache/http.go new file mode 100644 index 000000000..14eb345a8 --- /dev/null +++ b/vendor/github.com/golang/groupcache/http.go @@ -0,0 +1,227 @@ +/* +Copyright 2013 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package groupcache + +import ( + "bytes" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync" + + "github.com/golang/groupcache/consistenthash" + pb "github.com/golang/groupcache/groupcachepb" + "github.com/golang/protobuf/proto" +) + +const defaultBasePath = "/_groupcache/" + +const defaultReplicas = 50 + +// HTTPPool implements PeerPicker for a pool of HTTP peers. +type HTTPPool struct { + // Context optionally specifies a context for the server to use when it + // receives a request. + // If nil, the server uses a nil Context. + Context func(*http.Request) Context + + // Transport optionally specifies an http.RoundTripper for the client + // to use when it makes a request. + // If nil, the client uses http.DefaultTransport. + Transport func(Context) http.RoundTripper + + // this peer's base URL, e.g. "https://example.net:8000" + self string + + // opts specifies the options. + opts HTTPPoolOptions + + mu sync.Mutex // guards peers and httpGetters + peers *consistenthash.Map + httpGetters map[string]*httpGetter // keyed by e.g. "http://10.0.0.2:8008" +} + +// HTTPPoolOptions are the configurations of a HTTPPool. +type HTTPPoolOptions struct { + // BasePath specifies the HTTP path that will serve groupcache requests. + // If blank, it defaults to "/_groupcache/". + BasePath string + + // Replicas specifies the number of key replicas on the consistent hash. + // If blank, it defaults to 50. + Replicas int + + // HashFn specifies the hash function of the consistent hash. + // If blank, it defaults to crc32.ChecksumIEEE. + HashFn consistenthash.Hash +} + +// NewHTTPPool initializes an HTTP pool of peers, and registers itself as a PeerPicker. +// For convenience, it also registers itself as an http.Handler with http.DefaultServeMux. +// The self argument be a valid base URL that points to the current server, +// for example "http://example.net:8000". +func NewHTTPPool(self string) *HTTPPool { + p := NewHTTPPoolOpts(self, nil) + http.Handle(p.opts.BasePath, p) + return p +} + +var httpPoolMade bool + +// NewHTTPPoolOpts initializes an HTTP pool of peers with the given options. +// Unlike NewHTTPPool, this function does not register the created pool as an HTTP handler. +// The returned *HTTPPool implements http.Handler and must be registered using http.Handle. +func NewHTTPPoolOpts(self string, o *HTTPPoolOptions) *HTTPPool { + if httpPoolMade { + panic("groupcache: NewHTTPPool must be called only once") + } + httpPoolMade = true + + p := &HTTPPool{ + self: self, + httpGetters: make(map[string]*httpGetter), + } + if o != nil { + p.opts = *o + } + if p.opts.BasePath == "" { + p.opts.BasePath = defaultBasePath + } + if p.opts.Replicas == 0 { + p.opts.Replicas = defaultReplicas + } + p.peers = consistenthash.New(p.opts.Replicas, p.opts.HashFn) + + RegisterPeerPicker(func() PeerPicker { return p }) + return p +} + +// Set updates the pool's list of peers. +// Each peer value should be a valid base URL, +// for example "http://example.net:8000". +func (p *HTTPPool) Set(peers ...string) { + p.mu.Lock() + defer p.mu.Unlock() + p.peers = consistenthash.New(p.opts.Replicas, p.opts.HashFn) + p.peers.Add(peers...) + p.httpGetters = make(map[string]*httpGetter, len(peers)) + for _, peer := range peers { + p.httpGetters[peer] = &httpGetter{transport: p.Transport, baseURL: peer + p.opts.BasePath} + } +} + +func (p *HTTPPool) PickPeer(key string) (ProtoGetter, bool) { + p.mu.Lock() + defer p.mu.Unlock() + if p.peers.IsEmpty() { + return nil, false + } + if peer := p.peers.Get(key); peer != p.self { + return p.httpGetters[peer], true + } + return nil, false +} + +func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Parse request. + if !strings.HasPrefix(r.URL.Path, p.opts.BasePath) { + panic("HTTPPool serving unexpected path: " + r.URL.Path) + } + parts := strings.SplitN(r.URL.Path[len(p.opts.BasePath):], "/", 2) + if len(parts) != 2 { + http.Error(w, "bad request", http.StatusBadRequest) + return + } + groupName := parts[0] + key := parts[1] + + // Fetch the value for this group/key. + group := GetGroup(groupName) + if group == nil { + http.Error(w, "no such group: "+groupName, http.StatusNotFound) + return + } + var ctx Context + if p.Context != nil { + ctx = p.Context(r) + } + + group.Stats.ServerRequests.Add(1) + var value []byte + err := group.Get(ctx, key, AllocatingByteSliceSink(&value)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Write the value to the response body as a proto message. + body, err := proto.Marshal(&pb.GetResponse{Value: value}) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/x-protobuf") + w.Write(body) +} + +type httpGetter struct { + transport func(Context) http.RoundTripper + baseURL string +} + +var bufferPool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, +} + +func (h *httpGetter) Get(context Context, in *pb.GetRequest, out *pb.GetResponse) error { + u := fmt.Sprintf( + "%v%v/%v", + h.baseURL, + url.QueryEscape(in.GetGroup()), + url.QueryEscape(in.GetKey()), + ) + req, err := http.NewRequest("GET", u, nil) + if err != nil { + return err + } + tr := http.DefaultTransport + if h.transport != nil { + tr = h.transport(context) + } + res, err := tr.RoundTrip(req) + if err != nil { + return err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return fmt.Errorf("server returned: %v", res.Status) + } + b := bufferPool.Get().(*bytes.Buffer) + b.Reset() + defer bufferPool.Put(b) + _, err = io.Copy(b, res.Body) + if err != nil { + return fmt.Errorf("reading response body: %v", err) + } + err = proto.Unmarshal(b.Bytes(), out) + if err != nil { + return fmt.Errorf("decoding response body: %v", err) + } + return nil +} diff --git a/vendor/github.com/golang/groupcache/http_test.go b/vendor/github.com/golang/groupcache/http_test.go new file mode 100644 index 000000000..b42edd7f0 --- /dev/null +++ b/vendor/github.com/golang/groupcache/http_test.go @@ -0,0 +1,166 @@ +/* +Copyright 2013 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package groupcache + +import ( + "errors" + "flag" + "log" + "net" + "net/http" + "os" + "os/exec" + "strconv" + "strings" + "sync" + "testing" + "time" +) + +var ( + peerAddrs = flag.String("test_peer_addrs", "", "Comma-separated list of peer addresses; used by TestHTTPPool") + peerIndex = flag.Int("test_peer_index", -1, "Index of which peer this child is; used by TestHTTPPool") + peerChild = flag.Bool("test_peer_child", false, "True if running as a child process; used by TestHTTPPool") +) + +func TestHTTPPool(t *testing.T) { + if *peerChild { + beChildForTestHTTPPool() + os.Exit(0) + } + + const ( + nChild = 4 + nGets = 100 + ) + + var childAddr []string + for i := 0; i < nChild; i++ { + childAddr = append(childAddr, pickFreeAddr(t)) + } + + var cmds []*exec.Cmd + var wg sync.WaitGroup + for i := 0; i < nChild; i++ { + cmd := exec.Command(os.Args[0], + "--test.run=TestHTTPPool", + "--test_peer_child", + "--test_peer_addrs="+strings.Join(childAddr, ","), + "--test_peer_index="+strconv.Itoa(i), + ) + cmds = append(cmds, cmd) + wg.Add(1) + if err := cmd.Start(); err != nil { + t.Fatal("failed to start child process: ", err) + } + go awaitAddrReady(t, childAddr[i], &wg) + } + defer func() { + for i := 0; i < nChild; i++ { + if cmds[i].Process != nil { + cmds[i].Process.Kill() + } + } + }() + wg.Wait() + + // Use a dummy self address so that we don't handle gets in-process. + p := NewHTTPPool("should-be-ignored") + p.Set(addrToURL(childAddr)...) + + // Dummy getter function. Gets should go to children only. + // The only time this process will handle a get is when the + // children can't be contacted for some reason. + getter := GetterFunc(func(ctx Context, key string, dest Sink) error { + return errors.New("parent getter called; something's wrong") + }) + g := NewGroup("httpPoolTest", 1<<20, getter) + + for _, key := range testKeys(nGets) { + var value string + if err := g.Get(nil, key, StringSink(&value)); err != nil { + t.Fatal(err) + } + if suffix := ":" + key; !strings.HasSuffix(value, suffix) { + t.Errorf("Get(%q) = %q, want value ending in %q", key, value, suffix) + } + t.Logf("Get key=%q, value=%q (peer:key)", key, value) + } +} + +func testKeys(n int) (keys []string) { + keys = make([]string, n) + for i := range keys { + keys[i] = strconv.Itoa(i) + } + return +} + +func beChildForTestHTTPPool() { + addrs := strings.Split(*peerAddrs, ",") + + p := NewHTTPPool("http://" + addrs[*peerIndex]) + p.Set(addrToURL(addrs)...) + + getter := GetterFunc(func(ctx Context, key string, dest Sink) error { + dest.SetString(strconv.Itoa(*peerIndex) + ":" + key) + return nil + }) + NewGroup("httpPoolTest", 1<<20, getter) + + log.Fatal(http.ListenAndServe(addrs[*peerIndex], p)) +} + +// This is racy. Another process could swoop in and steal the port between the +// call to this function and the next listen call. Should be okay though. +// The proper way would be to pass the l.File() as ExtraFiles to the child +// process, and then close your copy once the child starts. +func pickFreeAddr(t *testing.T) string { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer l.Close() + return l.Addr().String() +} + +func addrToURL(addr []string) []string { + url := make([]string, len(addr)) + for i := range addr { + url[i] = "http://" + addr[i] + } + return url +} + +func awaitAddrReady(t *testing.T, addr string, wg *sync.WaitGroup) { + defer wg.Done() + const max = 1 * time.Second + tries := 0 + for { + tries++ + c, err := net.Dial("tcp", addr) + if err == nil { + c.Close() + return + } + delay := time.Duration(tries) * 25 * time.Millisecond + if delay > max { + delay = max + } + time.Sleep(delay) + } +} diff --git a/vendor/github.com/golang/groupcache/lru/lru_test.go b/vendor/github.com/golang/groupcache/lru/lru_test.go new file mode 100644 index 000000000..98a2656e8 --- /dev/null +++ b/vendor/github.com/golang/groupcache/lru/lru_test.go @@ -0,0 +1,73 @@ +/* +Copyright 2013 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package lru + +import ( + "testing" +) + +type simpleStruct struct { + int + string +} + +type complexStruct struct { + int + simpleStruct +} + +var getTests = []struct { + name string + keyToAdd interface{} + keyToGet interface{} + expectedOk bool +}{ + {"string_hit", "myKey", "myKey", true}, + {"string_miss", "myKey", "nonsense", false}, + {"simple_struct_hit", simpleStruct{1, "two"}, simpleStruct{1, "two"}, true}, + {"simeple_struct_miss", simpleStruct{1, "two"}, simpleStruct{0, "noway"}, false}, + {"complex_struct_hit", complexStruct{1, simpleStruct{2, "three"}}, + complexStruct{1, simpleStruct{2, "three"}}, true}, +} + +func TestGet(t *testing.T) { + for _, tt := range getTests { + lru := New(0) + lru.Add(tt.keyToAdd, 1234) + val, ok := lru.Get(tt.keyToGet) + if ok != tt.expectedOk { + t.Fatalf("%s: cache hit = %v; want %v", tt.name, ok, !ok) + } else if ok && val != 1234 { + t.Fatalf("%s expected get to return 1234 but got %v", tt.name, val) + } + } +} + +func TestRemove(t *testing.T) { + lru := New(0) + lru.Add("myKey", 1234) + if val, ok := lru.Get("myKey"); !ok { + t.Fatal("TestRemove returned no match") + } else if val != 1234 { + t.Fatalf("TestRemove failed. Expected %d, got %v", 1234, val) + } + + lru.Remove("myKey") + if _, ok := lru.Get("myKey"); ok { + t.Fatal("TestRemove returned a removed entry") + } +} diff --git a/vendor/github.com/golang/groupcache/peers.go b/vendor/github.com/golang/groupcache/peers.go new file mode 100644 index 000000000..a74a79b8f --- /dev/null +++ b/vendor/github.com/golang/groupcache/peers.go @@ -0,0 +1,71 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// peers.go defines how processes find and communicate with their peers. + +package groupcache + +import ( + pb "github.com/golang/groupcache/groupcachepb" +) + +// Context is an opaque value passed through calls to the +// ProtoGetter. It may be nil if your ProtoGetter implementation does +// not require a context. +type Context interface{} + +// ProtoGetter is the interface that must be implemented by a peer. +type ProtoGetter interface { + Get(context Context, in *pb.GetRequest, out *pb.GetResponse) error +} + +// PeerPicker is the interface that must be implemented to locate +// the peer that owns a specific key. +type PeerPicker interface { + // PickPeer returns the peer that owns the specific key + // and true to indicate that a remote peer was nominated. + // It returns nil, false if the key owner is the current peer. + PickPeer(key string) (peer ProtoGetter, ok bool) +} + +// NoPeers is an implementation of PeerPicker that never finds a peer. +type NoPeers struct{} + +func (NoPeers) PickPeer(key string) (peer ProtoGetter, ok bool) { return } + +var ( + portPicker func() PeerPicker +) + +// RegisterPeerPicker registers the peer initialization function. +// It is called once, when the first group is created. +func RegisterPeerPicker(fn func() PeerPicker) { + if portPicker != nil { + panic("RegisterPeerPicker called more than once") + } + portPicker = fn +} + +func getPeers() PeerPicker { + if portPicker == nil { + return NoPeers{} + } + pk := portPicker() + if pk == nil { + pk = NoPeers{} + } + return pk +} diff --git a/vendor/github.com/golang/groupcache/singleflight/singleflight.go b/vendor/github.com/golang/groupcache/singleflight/singleflight.go new file mode 100644 index 000000000..ff2c2ee4f --- /dev/null +++ b/vendor/github.com/golang/groupcache/singleflight/singleflight.go @@ -0,0 +1,64 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package singleflight provides a duplicate function call suppression +// mechanism. +package singleflight + +import "sync" + +// call is an in-flight or completed Do call +type call struct { + wg sync.WaitGroup + val interface{} + err error +} + +// Group represents a class of work and forms a namespace in which +// units of work can be executed with duplicate suppression. +type Group struct { + mu sync.Mutex // protects m + m map[string]*call // lazily initialized +} + +// Do executes and returns the results of the given function, making +// sure that only one execution is in-flight for a given key at a +// time. If a duplicate comes in, the duplicate caller waits for the +// original to complete and receives the same results. +func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) { + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call) + } + if c, ok := g.m[key]; ok { + g.mu.Unlock() + c.wg.Wait() + return c.val, c.err + } + c := new(call) + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + + c.val, c.err = fn() + c.wg.Done() + + g.mu.Lock() + delete(g.m, key) + g.mu.Unlock() + + return c.val, c.err +} diff --git a/vendor/github.com/golang/groupcache/singleflight/singleflight_test.go b/vendor/github.com/golang/groupcache/singleflight/singleflight_test.go new file mode 100644 index 000000000..47b4d3dc0 --- /dev/null +++ b/vendor/github.com/golang/groupcache/singleflight/singleflight_test.go @@ -0,0 +1,85 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package singleflight + +import ( + "errors" + "fmt" + "sync" + "sync/atomic" + "testing" + "time" +) + +func TestDo(t *testing.T) { + var g Group + v, err := g.Do("key", func() (interface{}, error) { + return "bar", nil + }) + if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want { + t.Errorf("Do = %v; want %v", got, want) + } + if err != nil { + t.Errorf("Do error = %v", err) + } +} + +func TestDoErr(t *testing.T) { + var g Group + someErr := errors.New("Some error") + v, err := g.Do("key", func() (interface{}, error) { + return nil, someErr + }) + if err != someErr { + t.Errorf("Do error = %v; want someErr", err) + } + if v != nil { + t.Errorf("unexpected non-nil value %#v", v) + } +} + +func TestDoDupSuppress(t *testing.T) { + var g Group + c := make(chan string) + var calls int32 + fn := func() (interface{}, error) { + atomic.AddInt32(&calls, 1) + return <-c, nil + } + + const n = 10 + var wg sync.WaitGroup + for i := 0; i < n; i++ { + wg.Add(1) + go func() { + v, err := g.Do("key", fn) + if err != nil { + t.Errorf("Do error: %v", err) + } + if v.(string) != "bar" { + t.Errorf("got %q; want %q", v, "bar") + } + wg.Done() + }() + } + time.Sleep(100 * time.Millisecond) // let goroutines above block + c <- "bar" + wg.Wait() + if got := atomic.LoadInt32(&calls); got != 1 { + t.Errorf("number of calls = %d; want 1", got) + } +} diff --git a/vendor/github.com/golang/groupcache/sinks.go b/vendor/github.com/golang/groupcache/sinks.go new file mode 100644 index 000000000..cb42b41b4 --- /dev/null +++ b/vendor/github.com/golang/groupcache/sinks.go @@ -0,0 +1,322 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package groupcache + +import ( + "errors" + + "github.com/golang/protobuf/proto" +) + +// A Sink receives data from a Get call. +// +// Implementation of Getter must call exactly one of the Set methods +// on success. +type Sink interface { + // SetString sets the value to s. + SetString(s string) error + + // SetBytes sets the value to the contents of v. + // The caller retains ownership of v. + SetBytes(v []byte) error + + // SetProto sets the value to the encoded version of m. + // The caller retains ownership of m. + SetProto(m proto.Message) error + + // view returns a frozen view of the bytes for caching. + view() (ByteView, error) +} + +func cloneBytes(b []byte) []byte { + c := make([]byte, len(b)) + copy(c, b) + return c +} + +func setSinkView(s Sink, v ByteView) error { + // A viewSetter is a Sink that can also receive its value from + // a ByteView. This is a fast path to minimize copies when the + // item was already cached locally in memory (where it's + // cached as a ByteView) + type viewSetter interface { + setView(v ByteView) error + } + if vs, ok := s.(viewSetter); ok { + return vs.setView(v) + } + if v.b != nil { + return s.SetBytes(v.b) + } + return s.SetString(v.s) +} + +// StringSink returns a Sink that populates the provided string pointer. +func StringSink(sp *string) Sink { + return &stringSink{sp: sp} +} + +type stringSink struct { + sp *string + v ByteView + // TODO(bradfitz): track whether any Sets were called. +} + +func (s *stringSink) view() (ByteView, error) { + // TODO(bradfitz): return an error if no Set was called + return s.v, nil +} + +func (s *stringSink) SetString(v string) error { + s.v.b = nil + s.v.s = v + *s.sp = v + return nil +} + +func (s *stringSink) SetBytes(v []byte) error { + return s.SetString(string(v)) +} + +func (s *stringSink) SetProto(m proto.Message) error { + b, err := proto.Marshal(m) + if err != nil { + return err + } + s.v.b = b + *s.sp = string(b) + return nil +} + +// ByteViewSink returns a Sink that populates a ByteView. +func ByteViewSink(dst *ByteView) Sink { + if dst == nil { + panic("nil dst") + } + return &byteViewSink{dst: dst} +} + +type byteViewSink struct { + dst *ByteView + + // if this code ever ends up tracking that at least one set* + // method was called, don't make it an error to call set + // methods multiple times. Lorry's payload.go does that, and + // it makes sense. The comment at the top of this file about + // "exactly one of the Set methods" is overly strict. We + // really care about at least once (in a handler), but if + // multiple handlers fail (or multiple functions in a program + // using a Sink), it's okay to re-use the same one. +} + +func (s *byteViewSink) setView(v ByteView) error { + *s.dst = v + return nil +} + +func (s *byteViewSink) view() (ByteView, error) { + return *s.dst, nil +} + +func (s *byteViewSink) SetProto(m proto.Message) error { + b, err := proto.Marshal(m) + if err != nil { + return err + } + *s.dst = ByteView{b: b} + return nil +} + +func (s *byteViewSink) SetBytes(b []byte) error { + *s.dst = ByteView{b: cloneBytes(b)} + return nil +} + +func (s *byteViewSink) SetString(v string) error { + *s.dst = ByteView{s: v} + return nil +} + +// ProtoSink returns a sink that unmarshals binary proto values into m. +func ProtoSink(m proto.Message) Sink { + return &protoSink{ + dst: m, + } +} + +type protoSink struct { + dst proto.Message // authorative value + typ string + + v ByteView // encoded +} + +func (s *protoSink) view() (ByteView, error) { + return s.v, nil +} + +func (s *protoSink) SetBytes(b []byte) error { + err := proto.Unmarshal(b, s.dst) + if err != nil { + return err + } + s.v.b = cloneBytes(b) + s.v.s = "" + return nil +} + +func (s *protoSink) SetString(v string) error { + b := []byte(v) + err := proto.Unmarshal(b, s.dst) + if err != nil { + return err + } + s.v.b = b + s.v.s = "" + return nil +} + +func (s *protoSink) SetProto(m proto.Message) error { + b, err := proto.Marshal(m) + if err != nil { + return err + } + // TODO(bradfitz): optimize for same-task case more and write + // right through? would need to document ownership rules at + // the same time. but then we could just assign *dst = *m + // here. This works for now: + err = proto.Unmarshal(b, s.dst) + if err != nil { + return err + } + s.v.b = b + s.v.s = "" + return nil +} + +// AllocatingByteSliceSink returns a Sink that allocates +// a byte slice to hold the received value and assigns +// it to *dst. The memory is not retained by groupcache. +func AllocatingByteSliceSink(dst *[]byte) Sink { + return &allocBytesSink{dst: dst} +} + +type allocBytesSink struct { + dst *[]byte + v ByteView +} + +func (s *allocBytesSink) view() (ByteView, error) { + return s.v, nil +} + +func (s *allocBytesSink) setView(v ByteView) error { + if v.b != nil { + *s.dst = cloneBytes(v.b) + } else { + *s.dst = []byte(v.s) + } + s.v = v + return nil +} + +func (s *allocBytesSink) SetProto(m proto.Message) error { + b, err := proto.Marshal(m) + if err != nil { + return err + } + return s.setBytesOwned(b) +} + +func (s *allocBytesSink) SetBytes(b []byte) error { + return s.setBytesOwned(cloneBytes(b)) +} + +func (s *allocBytesSink) setBytesOwned(b []byte) error { + if s.dst == nil { + return errors.New("nil AllocatingByteSliceSink *[]byte dst") + } + *s.dst = cloneBytes(b) // another copy, protecting the read-only s.v.b view + s.v.b = b + s.v.s = "" + return nil +} + +func (s *allocBytesSink) SetString(v string) error { + if s.dst == nil { + return errors.New("nil AllocatingByteSliceSink *[]byte dst") + } + *s.dst = []byte(v) + s.v.b = nil + s.v.s = v + return nil +} + +// TruncatingByteSliceSink returns a Sink that writes up to len(*dst) +// bytes to *dst. If more bytes are available, they're silently +// truncated. If fewer bytes are available than len(*dst), *dst +// is shrunk to fit the number of bytes available. +func TruncatingByteSliceSink(dst *[]byte) Sink { + return &truncBytesSink{dst: dst} +} + +type truncBytesSink struct { + dst *[]byte + v ByteView +} + +func (s *truncBytesSink) view() (ByteView, error) { + return s.v, nil +} + +func (s *truncBytesSink) SetProto(m proto.Message) error { + b, err := proto.Marshal(m) + if err != nil { + return err + } + return s.setBytesOwned(b) +} + +func (s *truncBytesSink) SetBytes(b []byte) error { + return s.setBytesOwned(cloneBytes(b)) +} + +func (s *truncBytesSink) setBytesOwned(b []byte) error { + if s.dst == nil { + return errors.New("nil TruncatingByteSliceSink *[]byte dst") + } + n := copy(*s.dst, b) + if n < len(*s.dst) { + *s.dst = (*s.dst)[:n] + } + s.v.b = b + s.v.s = "" + return nil +} + +func (s *truncBytesSink) SetString(v string) error { + if s.dst == nil { + return errors.New("nil TruncatingByteSliceSink *[]byte dst") + } + n := copy(*s.dst, v) + if n < len(*s.dst) { + *s.dst = (*s.dst)[:n] + } + s.v.b = nil + s.v.s = v + return nil +} diff --git a/vendor/github.com/golang/groupcache/testpb/test.pb.go b/vendor/github.com/golang/groupcache/testpb/test.pb.go new file mode 100644 index 000000000..038040d15 --- /dev/null +++ b/vendor/github.com/golang/groupcache/testpb/test.pb.go @@ -0,0 +1,235 @@ +// Code generated by protoc-gen-go. +// source: test.proto +// DO NOT EDIT! + +package testpb + +import proto "github.com/golang/protobuf/proto" +import json "encoding/json" +import math "math" + +// Reference proto, json, and math imports to suppress error if they are not otherwise used. +var _ = proto.Marshal +var _ = &json.SyntaxError{} +var _ = math.Inf + +type TestMessage struct { + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + City *string `protobuf:"bytes,2,opt,name=city" json:"city,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TestMessage) Reset() { *m = TestMessage{} } +func (m *TestMessage) String() string { return proto.CompactTextString(m) } +func (*TestMessage) ProtoMessage() {} + +func (m *TestMessage) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *TestMessage) GetCity() string { + if m != nil && m.City != nil { + return *m.City + } + return "" +} + +type TestRequest struct { + Lower *string `protobuf:"bytes,1,req,name=lower" json:"lower,omitempty"` + RepeatCount *int32 `protobuf:"varint,2,opt,name=repeat_count,def=1" json:"repeat_count,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TestRequest) Reset() { *m = TestRequest{} } +func (m *TestRequest) String() string { return proto.CompactTextString(m) } +func (*TestRequest) ProtoMessage() {} + +const Default_TestRequest_RepeatCount int32 = 1 + +func (m *TestRequest) GetLower() string { + if m != nil && m.Lower != nil { + return *m.Lower + } + return "" +} + +func (m *TestRequest) GetRepeatCount() int32 { + if m != nil && m.RepeatCount != nil { + return *m.RepeatCount + } + return Default_TestRequest_RepeatCount +} + +type TestResponse struct { + Value *string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *TestResponse) Reset() { *m = TestResponse{} } +func (m *TestResponse) String() string { return proto.CompactTextString(m) } +func (*TestResponse) ProtoMessage() {} + +func (m *TestResponse) GetValue() string { + if m != nil && m.Value != nil { + return *m.Value + } + return "" +} + +type CacheStats struct { + Items *int64 `protobuf:"varint,1,opt,name=items" json:"items,omitempty"` + Bytes *int64 `protobuf:"varint,2,opt,name=bytes" json:"bytes,omitempty"` + Gets *int64 `protobuf:"varint,3,opt,name=gets" json:"gets,omitempty"` + Hits *int64 `protobuf:"varint,4,opt,name=hits" json:"hits,omitempty"` + Evicts *int64 `protobuf:"varint,5,opt,name=evicts" json:"evicts,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *CacheStats) Reset() { *m = CacheStats{} } +func (m *CacheStats) String() string { return proto.CompactTextString(m) } +func (*CacheStats) ProtoMessage() {} + +func (m *CacheStats) GetItems() int64 { + if m != nil && m.Items != nil { + return *m.Items + } + return 0 +} + +func (m *CacheStats) GetBytes() int64 { + if m != nil && m.Bytes != nil { + return *m.Bytes + } + return 0 +} + +func (m *CacheStats) GetGets() int64 { + if m != nil && m.Gets != nil { + return *m.Gets + } + return 0 +} + +func (m *CacheStats) GetHits() int64 { + if m != nil && m.Hits != nil { + return *m.Hits + } + return 0 +} + +func (m *CacheStats) GetEvicts() int64 { + if m != nil && m.Evicts != nil { + return *m.Evicts + } + return 0 +} + +type StatsResponse struct { + Gets *int64 `protobuf:"varint,1,opt,name=gets" json:"gets,omitempty"` + CacheHits *int64 `protobuf:"varint,12,opt,name=cache_hits" json:"cache_hits,omitempty"` + Fills *int64 `protobuf:"varint,2,opt,name=fills" json:"fills,omitempty"` + TotalAlloc *uint64 `protobuf:"varint,3,opt,name=total_alloc" json:"total_alloc,omitempty"` + MainCache *CacheStats `protobuf:"bytes,4,opt,name=main_cache" json:"main_cache,omitempty"` + HotCache *CacheStats `protobuf:"bytes,5,opt,name=hot_cache" json:"hot_cache,omitempty"` + ServerIn *int64 `protobuf:"varint,6,opt,name=server_in" json:"server_in,omitempty"` + Loads *int64 `protobuf:"varint,8,opt,name=loads" json:"loads,omitempty"` + PeerLoads *int64 `protobuf:"varint,9,opt,name=peer_loads" json:"peer_loads,omitempty"` + PeerErrors *int64 `protobuf:"varint,10,opt,name=peer_errors" json:"peer_errors,omitempty"` + LocalLoads *int64 `protobuf:"varint,11,opt,name=local_loads" json:"local_loads,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *StatsResponse) Reset() { *m = StatsResponse{} } +func (m *StatsResponse) String() string { return proto.CompactTextString(m) } +func (*StatsResponse) ProtoMessage() {} + +func (m *StatsResponse) GetGets() int64 { + if m != nil && m.Gets != nil { + return *m.Gets + } + return 0 +} + +func (m *StatsResponse) GetCacheHits() int64 { + if m != nil && m.CacheHits != nil { + return *m.CacheHits + } + return 0 +} + +func (m *StatsResponse) GetFills() int64 { + if m != nil && m.Fills != nil { + return *m.Fills + } + return 0 +} + +func (m *StatsResponse) GetTotalAlloc() uint64 { + if m != nil && m.TotalAlloc != nil { + return *m.TotalAlloc + } + return 0 +} + +func (m *StatsResponse) GetMainCache() *CacheStats { + if m != nil { + return m.MainCache + } + return nil +} + +func (m *StatsResponse) GetHotCache() *CacheStats { + if m != nil { + return m.HotCache + } + return nil +} + +func (m *StatsResponse) GetServerIn() int64 { + if m != nil && m.ServerIn != nil { + return *m.ServerIn + } + return 0 +} + +func (m *StatsResponse) GetLoads() int64 { + if m != nil && m.Loads != nil { + return *m.Loads + } + return 0 +} + +func (m *StatsResponse) GetPeerLoads() int64 { + if m != nil && m.PeerLoads != nil { + return *m.PeerLoads + } + return 0 +} + +func (m *StatsResponse) GetPeerErrors() int64 { + if m != nil && m.PeerErrors != nil { + return *m.PeerErrors + } + return 0 +} + +func (m *StatsResponse) GetLocalLoads() int64 { + if m != nil && m.LocalLoads != nil { + return *m.LocalLoads + } + return 0 +} + +type Empty struct { + XXX_unrecognized []byte `json:"-"` +} + +func (m *Empty) Reset() { *m = Empty{} } +func (m *Empty) String() string { return proto.CompactTextString(m) } +func (*Empty) ProtoMessage() {} + +func init() { +} diff --git a/vendor/github.com/golang/groupcache/testpb/test.proto b/vendor/github.com/golang/groupcache/testpb/test.proto new file mode 100644 index 000000000..b9dc6c9a0 --- /dev/null +++ b/vendor/github.com/golang/groupcache/testpb/test.proto @@ -0,0 +1,63 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +syntax = "proto2"; + +package testpb; + +message TestMessage { + optional string name = 1; + optional string city = 2; +} + +message TestRequest { + required string lower = 1; // to be returned upper case + optional int32 repeat_count = 2 [default = 1]; // .. this many times +} + +message TestResponse { + optional string value = 1; +} + +message CacheStats { + optional int64 items = 1; + optional int64 bytes = 2; + optional int64 gets = 3; + optional int64 hits = 4; + optional int64 evicts = 5; +} + +message StatsResponse { + optional int64 gets = 1; + optional int64 cache_hits = 12; + optional int64 fills = 2; + optional uint64 total_alloc = 3; + optional CacheStats main_cache = 4; + optional CacheStats hot_cache = 5; + optional int64 server_in = 6; + optional int64 loads = 8; + optional int64 peer_loads = 9; + optional int64 peer_errors = 10; + optional int64 local_loads = 11; +} + +message Empty {} + +service GroupCacheTest { + rpc InitPeers(Empty) returns (Empty) {}; + rpc Get(TestRequest) returns (TestResponse) {}; + rpc GetStats(Empty) returns (StatsResponse) {}; +} diff --git a/vendor/github.com/gorilla/context/context_test.go b/vendor/github.com/gorilla/context/context_test.go new file mode 100644 index 000000000..9814c501e --- /dev/null +++ b/vendor/github.com/gorilla/context/context_test.go @@ -0,0 +1,161 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context + +import ( + "net/http" + "testing" +) + +type keyType int + +const ( + key1 keyType = iota + key2 +) + +func TestContext(t *testing.T) { + assertEqual := func(val interface{}, exp interface{}) { + if val != exp { + t.Errorf("Expected %v, got %v.", exp, val) + } + } + + r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + + // Get() + assertEqual(Get(r, key1), nil) + + // Set() + Set(r, key1, "1") + assertEqual(Get(r, key1), "1") + assertEqual(len(data[r]), 1) + + Set(r, key2, "2") + assertEqual(Get(r, key2), "2") + assertEqual(len(data[r]), 2) + + //GetOk + value, ok := GetOk(r, key1) + assertEqual(value, "1") + assertEqual(ok, true) + + value, ok = GetOk(r, "not exists") + assertEqual(value, nil) + assertEqual(ok, false) + + Set(r, "nil value", nil) + value, ok = GetOk(r, "nil value") + assertEqual(value, nil) + assertEqual(ok, true) + + // GetAll() + values := GetAll(r) + assertEqual(len(values), 3) + + // GetAll() for empty request + values = GetAll(emptyR) + if values != nil { + t.Error("GetAll didn't return nil value for invalid request") + } + + // GetAllOk() + values, ok = GetAllOk(r) + assertEqual(len(values), 3) + assertEqual(ok, true) + + // GetAllOk() for empty request + values, ok = GetAllOk(emptyR) + assertEqual(value, nil) + assertEqual(ok, false) + + // Delete() + Delete(r, key1) + assertEqual(Get(r, key1), nil) + assertEqual(len(data[r]), 2) + + Delete(r, key2) + assertEqual(Get(r, key2), nil) + assertEqual(len(data[r]), 1) + + // Clear() + Clear(r) + assertEqual(len(data), 0) +} + +func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) { + <-wait + for i := 0; i < iterations; i++ { + Get(r, key) + } + done <- struct{}{} + +} + +func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) { + <-wait + for i := 0; i < iterations; i++ { + Set(r, key, value) + } + done <- struct{}{} + +} + +func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) { + + b.StopTimer() + r, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + done := make(chan struct{}) + b.StartTimer() + + for i := 0; i < b.N; i++ { + wait := make(chan struct{}) + + for i := 0; i < numReaders; i++ { + go parallelReader(r, "test", iterations, wait, done) + } + + for i := 0; i < numWriters; i++ { + go parallelWriter(r, "test", "123", iterations, wait, done) + } + + close(wait) + + for i := 0; i < numReaders+numWriters; i++ { + <-done + } + + } + +} + +func BenchmarkMutexSameReadWrite1(b *testing.B) { + benchmarkMutex(b, 1, 1, 32) +} +func BenchmarkMutexSameReadWrite2(b *testing.B) { + benchmarkMutex(b, 2, 2, 32) +} +func BenchmarkMutexSameReadWrite4(b *testing.B) { + benchmarkMutex(b, 4, 4, 32) +} +func BenchmarkMutex1(b *testing.B) { + benchmarkMutex(b, 2, 8, 32) +} +func BenchmarkMutex2(b *testing.B) { + benchmarkMutex(b, 16, 4, 64) +} +func BenchmarkMutex3(b *testing.B) { + benchmarkMutex(b, 1, 2, 128) +} +func BenchmarkMutex4(b *testing.B) { + benchmarkMutex(b, 128, 32, 256) +} +func BenchmarkMutex5(b *testing.B) { + benchmarkMutex(b, 1024, 2048, 64) +} +func BenchmarkMutex6(b *testing.B) { + benchmarkMutex(b, 2048, 1024, 512) +} diff --git a/vendor/github.com/gorilla/handlers/canonical_test.go b/vendor/github.com/gorilla/handlers/canonical_test.go new file mode 100644 index 000000000..615e4b056 --- /dev/null +++ b/vendor/github.com/gorilla/handlers/canonical_test.go @@ -0,0 +1,127 @@ +package handlers + +import ( + "bufio" + "bytes" + "log" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" +) + +func TestCleanHost(t *testing.T) { + tests := []struct { + in, want string + }{ + {"www.google.com", "www.google.com"}, + {"www.google.com foo", "www.google.com"}, + {"www.google.com/foo", "www.google.com"}, + {" first character is a space", ""}, + } + for _, tt := range tests { + got := cleanHost(tt.in) + if tt.want != got { + t.Errorf("cleanHost(%q) = %q, want %q", tt.in, got, tt.want) + } + } +} + +func TestCanonicalHost(t *testing.T) { + gorilla := "http://www.gorillatoolkit.org" + + rr := httptest.NewRecorder() + r := newRequest("GET", "http://www.example.com/") + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + // Test a re-direct: should return a 302 Found. + CanonicalHost(gorilla, http.StatusFound)(testHandler).ServeHTTP(rr, r) + + if rr.Code != http.StatusFound { + t.Fatalf("bad status: got %v want %v", rr.Code, http.StatusFound) + } + + if rr.Header().Get("Location") != gorilla+r.URL.Path { + t.Fatalf("bad re-direct: got %q want %q", rr.Header().Get("Location"), gorilla+r.URL.Path) + } + +} + +func TestKeepsQueryString(t *testing.T) { + google := "https://www.google.com" + + rr := httptest.NewRecorder() + querystring := url.Values{"q": {"golang"}, "format": {"json"}}.Encode() + r := newRequest("GET", "http://www.example.com/search?"+querystring) + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + CanonicalHost(google, http.StatusFound)(testHandler).ServeHTTP(rr, r) + + want := google + r.URL.Path + "?" + querystring + if rr.Header().Get("Location") != want { + t.Fatalf("bad re-direct: got %q want %q", rr.Header().Get("Location"), want) + } +} + +func TestBadDomain(t *testing.T) { + rr := httptest.NewRecorder() + r := newRequest("GET", "http://www.example.com/") + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + // Test a bad domain - should return 200 OK. + CanonicalHost("%", http.StatusFound)(testHandler).ServeHTTP(rr, r) + + if rr.Code != http.StatusOK { + t.Fatalf("bad status: got %v want %v", rr.Code, http.StatusOK) + } +} + +func TestEmptyHost(t *testing.T) { + rr := httptest.NewRecorder() + r := newRequest("GET", "http://www.example.com/") + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + // Test a domain that returns an empty url.Host from url.Parse. + CanonicalHost("hello.com", http.StatusFound)(testHandler).ServeHTTP(rr, r) + + if rr.Code != http.StatusOK { + t.Fatalf("bad status: got %v want %v", rr.Code, http.StatusOK) + } +} + +func TestHeaderWrites(t *testing.T) { + gorilla := "http://www.gorillatoolkit.org" + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + }) + + // Catch the log output to ensure we don't write multiple headers. + var b bytes.Buffer + buf := bufio.NewWriter(&b) + tl := log.New(buf, "test: ", log.Lshortfile) + + srv := httptest.NewServer( + CanonicalHost(gorilla, http.StatusFound)(testHandler)) + defer srv.Close() + srv.Config.ErrorLog = tl + + _, err := http.Get(srv.URL) + if err != nil { + t.Fatal(err) + } + + err = buf.Flush() + if err != nil { + t.Fatal(err) + } + + // We rely on the error not changing: net/http does not export it. + if strings.Contains(b.String(), "multiple response.WriteHeader calls") { + t.Fatalf("re-direct did not return early: multiple header writes") + } +} diff --git a/vendor/github.com/gorilla/handlers/compress_test.go b/vendor/github.com/gorilla/handlers/compress_test.go new file mode 100644 index 000000000..6f07f440d --- /dev/null +++ b/vendor/github.com/gorilla/handlers/compress_test.go @@ -0,0 +1,154 @@ +// Copyright 2013 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package handlers + +import ( + "bufio" + "io" + "net" + "net/http" + "net/http/httptest" + "strconv" + "testing" +) + +var contentType = "text/plain; charset=utf-8" + +func compressedRequest(w *httptest.ResponseRecorder, compression string) { + CompressHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Length", strconv.Itoa(9*1024)) + w.Header().Set("Content-Type", contentType) + for i := 0; i < 1024; i++ { + io.WriteString(w, "Gorilla!\n") + } + })).ServeHTTP(w, &http.Request{ + Method: "GET", + Header: http.Header{ + "Accept-Encoding": []string{compression}, + }, + }) + +} + +func TestCompressHandlerNoCompression(t *testing.T) { + w := httptest.NewRecorder() + compressedRequest(w, "") + if enc := w.HeaderMap.Get("Content-Encoding"); enc != "" { + t.Errorf("wrong content encoding, got %q want %q", enc, "") + } + if ct := w.HeaderMap.Get("Content-Type"); ct != contentType { + t.Errorf("wrong content type, got %q want %q", ct, contentType) + } + if w.Body.Len() != 1024*9 { + t.Errorf("wrong len, got %d want %d", w.Body.Len(), 1024*9) + } + if l := w.HeaderMap.Get("Content-Length"); l != "9216" { + t.Errorf("wrong content-length. got %q expected %d", l, 1024*9) + } +} + +func TestCompressHandlerGzip(t *testing.T) { + w := httptest.NewRecorder() + compressedRequest(w, "gzip") + if w.HeaderMap.Get("Content-Encoding") != "gzip" { + t.Errorf("wrong content encoding, got %q want %q", w.HeaderMap.Get("Content-Encoding"), "gzip") + } + if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" { + t.Errorf("wrong content type, got %s want %s", w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") + } + if w.Body.Len() != 72 { + t.Errorf("wrong len, got %d want %d", w.Body.Len(), 72) + } + if l := w.HeaderMap.Get("Content-Length"); l != "" { + t.Errorf("wrong content-length. got %q expected %q", l, "") + } +} + +func TestCompressHandlerDeflate(t *testing.T) { + w := httptest.NewRecorder() + compressedRequest(w, "deflate") + if w.HeaderMap.Get("Content-Encoding") != "deflate" { + t.Fatalf("wrong content encoding, got %q want %q", w.HeaderMap.Get("Content-Encoding"), "deflate") + } + if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" { + t.Fatalf("wrong content type, got %s want %s", w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") + } + if w.Body.Len() != 54 { + t.Fatalf("wrong len, got %d want %d", w.Body.Len(), 54) + } +} + +func TestCompressHandlerGzipDeflate(t *testing.T) { + w := httptest.NewRecorder() + compressedRequest(w, "gzip, deflate ") + if w.HeaderMap.Get("Content-Encoding") != "gzip" { + t.Fatalf("wrong content encoding, got %q want %q", w.HeaderMap.Get("Content-Encoding"), "gzip") + } + if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" { + t.Fatalf("wrong content type, got %s want %s", w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") + } +} + +type fullyFeaturedResponseWriter struct{} + +// Header/Write/WriteHeader implement the http.ResponseWriter interface. +func (fullyFeaturedResponseWriter) Header() http.Header { + return http.Header{} +} +func (fullyFeaturedResponseWriter) Write([]byte) (int, error) { + return 0, nil +} +func (fullyFeaturedResponseWriter) WriteHeader(int) {} + +// Flush implements the http.Flusher interface. +func (fullyFeaturedResponseWriter) Flush() {} + +// Hijack implements the http.Hijacker interface. +func (fullyFeaturedResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + return nil, nil, nil +} + +// CloseNotify implements the http.CloseNotifier interface. +func (fullyFeaturedResponseWriter) CloseNotify() <-chan bool { + return nil +} + +func TestCompressHandlerPreserveInterfaces(t *testing.T) { + // Compile time validation fullyFeaturedResponseWriter implements all the + // interfaces we're asserting in the test case below. + var ( + _ http.Flusher = fullyFeaturedResponseWriter{} + _ http.CloseNotifier = fullyFeaturedResponseWriter{} + _ http.Hijacker = fullyFeaturedResponseWriter{} + ) + var h http.Handler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + comp := r.Header.Get("Accept-Encoding") + if _, ok := rw.(*compressResponseWriter); !ok { + t.Fatalf("ResponseWriter wasn't wrapped by compressResponseWriter, got %T type", rw) + } + if _, ok := rw.(http.Flusher); !ok { + t.Errorf("ResponseWriter lost http.Flusher interface for %q", comp) + } + if _, ok := rw.(http.CloseNotifier); !ok { + t.Errorf("ResponseWriter lost http.CloseNotifier interface for %q", comp) + } + if _, ok := rw.(http.Hijacker); !ok { + t.Errorf("ResponseWriter lost http.Hijacker interface for %q", comp) + } + }) + h = CompressHandler(h) + var ( + rw fullyFeaturedResponseWriter + ) + r, err := http.NewRequest("GET", "/", nil) + if err != nil { + t.Fatalf("Failed to create test request: %v", err) + } + r.Header.Set("Accept-Encoding", "gzip") + h.ServeHTTP(rw, r) + + r.Header.Set("Accept-Encoding", "deflate") + h.ServeHTTP(rw, r) +} diff --git a/vendor/github.com/gorilla/handlers/cors_test.go b/vendor/github.com/gorilla/handlers/cors_test.go new file mode 100644 index 000000000..c63913eee --- /dev/null +++ b/vendor/github.com/gorilla/handlers/cors_test.go @@ -0,0 +1,336 @@ +package handlers + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func TestDefaultCORSHandlerReturnsOk(t *testing.T) { + r := newRequest("GET", "http://www.example.com/") + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS()(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusFound) + } +} + +func TestDefaultCORSHandlerReturnsOkWithOrigin(t *testing.T) { + r := newRequest("GET", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS()(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusFound) + } +} + +func TestCORSHandlerIgnoreOptionsFallsThrough(t *testing.T) { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusTeapot) + }) + + CORS(IgnoreOptions())(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusTeapot { + t.Fatalf("bad status: got %v want %v", status, http.StatusTeapot) + } +} + +func TestCORSHandlerSetsExposedHeaders(t *testing.T) { + // Test default configuration. + r := newRequest("GET", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS(ExposedHeaders([]string{"X-CORS-TEST"}))(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusOK) + } + + header := rr.HeaderMap.Get(corsExposeHeadersHeader) + if header != "X-Cors-Test" { + t.Fatal("bad header: expected X-Cors-Test header, got empty header for method.") + } +} + +func TestCORSHandlerUnsetRequestMethodForPreflightBadRequest(t *testing.T) { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS(AllowedMethods([]string{"DELETE"}))(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusBadRequest { + t.Fatalf("bad status: got %v want %v", status, http.StatusBadRequest) + } +} + +func TestCORSHandlerInvalidRequestMethodForPreflightMethodNotAllowed(t *testing.T) { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + r.Header.Set(corsRequestMethodHeader, "DELETE") + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS()(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusMethodNotAllowed { + t.Fatalf("bad status: got %v want %v", status, http.StatusMethodNotAllowed) + } +} + +func TestCORSHandlerOptionsRequestMustNotBePassedToNextHandler(t *testing.T) { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + r.Header.Set(corsRequestMethodHeader, "GET") + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Fatal("Options request must not be passed to next handler") + }) + + CORS()(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusOK) + } +} + +func TestCORSHandlerAllowedMethodForPreflight(t *testing.T) { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + r.Header.Set(corsRequestMethodHeader, "DELETE") + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS(AllowedMethods([]string{"DELETE"}))(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusOK) + } + + header := rr.HeaderMap.Get(corsAllowMethodsHeader) + if header != "DELETE" { + t.Fatalf("bad header: expected DELETE method header, got empty header.") + } +} + +func TestCORSHandlerAllowMethodsNotSetForSimpleRequestPreflight(t *testing.T) { + for _, method := range defaultCorsMethods { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + r.Header.Set(corsRequestMethodHeader, method) + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS()(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusOK) + } + + header := rr.HeaderMap.Get(corsAllowMethodsHeader) + if header != "" { + t.Fatalf("bad header: expected empty method header, got %s.", header) + } + } +} + +func TestCORSHandlerAllowedHeaderNotSetForSimpleRequestPreflight(t *testing.T) { + for _, simpleHeader := range defaultCorsHeaders { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + r.Header.Set(corsRequestMethodHeader, "GET") + r.Header.Set(corsRequestHeadersHeader, simpleHeader) + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS()(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusOK) + } + + header := rr.HeaderMap.Get(corsAllowHeadersHeader) + if header != "" { + t.Fatalf("bad header: expected empty header, got %s.", header) + } + } +} + +func TestCORSHandlerAllowedHeaderForPreflight(t *testing.T) { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + r.Header.Set(corsRequestMethodHeader, "POST") + r.Header.Set(corsRequestHeadersHeader, "Content-Type") + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS(AllowedHeaders([]string{"Content-Type"}))(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusOK) + } + + header := rr.HeaderMap.Get(corsAllowHeadersHeader) + if header != "Content-Type" { + t.Fatalf("bad header: expected Content-Type header, got empty header.") + } +} + +func TestCORSHandlerInvalidHeaderForPreflightForbidden(t *testing.T) { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + r.Header.Set(corsRequestMethodHeader, "POST") + r.Header.Set(corsRequestHeadersHeader, "Content-Type") + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS()(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusForbidden { + t.Fatalf("bad status: got %v want %v", status, http.StatusForbidden) + } +} + +func TestCORSHandlerMaxAgeForPreflight(t *testing.T) { + r := newRequest("OPTIONS", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + r.Header.Set(corsRequestMethodHeader, "POST") + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS(MaxAge(3500))(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusOK) + } + + header := rr.HeaderMap.Get(corsMaxAgeHeader) + if header != "600" { + t.Fatalf("bad header: expected %s to be %s, got %s.", corsMaxAgeHeader, "600", header) + } +} + +func TestCORSHandlerAllowedCredentials(t *testing.T) { + r := newRequest("GET", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS(AllowCredentials())(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusOK) + } + + header := rr.HeaderMap.Get(corsAllowCredentialsHeader) + if header != "true" { + t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowCredentialsHeader, "true", header) + } +} + +func TestCORSHandlerMultipleAllowOriginsSetsVaryHeader(t *testing.T) { + r := newRequest("GET", "http://www.example.com/") + r.Header.Set("Origin", r.URL.String()) + + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + CORS(AllowedOrigins([]string{r.URL.String(), "http://google.com"}))(testHandler).ServeHTTP(rr, r) + + if status := rr.Code; status != http.StatusOK { + t.Fatalf("bad status: got %v want %v", status, http.StatusOK) + } + + header := rr.HeaderMap.Get(corsVaryHeader) + if header != corsOriginHeader { + t.Fatalf("bad header: expected %s to be %s, got %s.", corsVaryHeader, corsOriginHeader, header) + } +} + +func TestCORSWithMultipleHandlers(t *testing.T) { + var lastHandledBy string + corsMiddleware := CORS() + + testHandler1 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + lastHandledBy = "testHandler1" + }) + testHandler2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + lastHandledBy = "testHandler2" + }) + + r1 := newRequest("GET", "http://www.example.com/") + rr1 := httptest.NewRecorder() + handler1 := corsMiddleware(testHandler1) + + corsMiddleware(testHandler2) + + handler1.ServeHTTP(rr1, r1) + if lastHandledBy != "testHandler1" { + t.Fatalf("bad CORS() registration: Handler served should be Handler registered") + } +} + +func TestCORSHandlerWithCustomValidator(t *testing.T) { + r := newRequest("GET", "http://a.example.com") + r.Header.Set("Origin", r.URL.String()) + rr := httptest.NewRecorder() + + testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + originValidator := func(origin string) bool { + if strings.HasSuffix(origin, ".example.com") { + return true + } + return false + } + + CORS(AllowedOriginValidator(originValidator))(testHandler).ServeHTTP(rr, r) + header := rr.HeaderMap.Get(corsAllowOriginHeader) + if header != r.URL.String() { + t.Fatalf("bad header: expected %s to be %s, got %s.", corsAllowOriginHeader, r.URL.String(), header) + } + +} diff --git a/vendor/github.com/gorilla/handlers/handlers_test.go b/vendor/github.com/gorilla/handlers/handlers_test.go new file mode 100644 index 000000000..6ea7c7fa6 --- /dev/null +++ b/vendor/github.com/gorilla/handlers/handlers_test.go @@ -0,0 +1,354 @@ +// Copyright 2013 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package handlers + +import ( + "bytes" + "net" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" +) + +const ( + ok = "ok\n" + notAllowed = "Method not allowed\n" +) + +var okHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Write([]byte(ok)) +}) + +func newRequest(method, url string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + return req +} + +func TestMethodHandler(t *testing.T) { + tests := []struct { + req *http.Request + handler http.Handler + code int + allow string // Contents of the Allow header + body string + }{ + // No handlers + {newRequest("GET", "/foo"), MethodHandler{}, http.StatusMethodNotAllowed, "", notAllowed}, + {newRequest("OPTIONS", "/foo"), MethodHandler{}, http.StatusOK, "", ""}, + + // A single handler + {newRequest("GET", "/foo"), MethodHandler{"GET": okHandler}, http.StatusOK, "", ok}, + {newRequest("POST", "/foo"), MethodHandler{"GET": okHandler}, http.StatusMethodNotAllowed, "GET", notAllowed}, + + // Multiple handlers + {newRequest("GET", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusOK, "", ok}, + {newRequest("POST", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusOK, "", ok}, + {newRequest("DELETE", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusMethodNotAllowed, "GET, POST", notAllowed}, + {newRequest("OPTIONS", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusOK, "GET, POST", ""}, + + // Override OPTIONS + {newRequest("OPTIONS", "/foo"), MethodHandler{"OPTIONS": okHandler}, http.StatusOK, "", ok}, + } + + for i, test := range tests { + rec := httptest.NewRecorder() + test.handler.ServeHTTP(rec, test.req) + if rec.Code != test.code { + t.Fatalf("%d: wrong code, got %d want %d", i, rec.Code, test.code) + } + if allow := rec.HeaderMap.Get("Allow"); allow != test.allow { + t.Fatalf("%d: wrong Allow, got %s want %s", i, allow, test.allow) + } + if body := rec.Body.String(); body != test.body { + t.Fatalf("%d: wrong body, got %q want %q", i, body, test.body) + } + } +} + +func TestWriteLog(t *testing.T) { + loc, err := time.LoadLocation("Europe/Warsaw") + if err != nil { + panic(err) + } + ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc) + + // A typical request with an OK response + req := newRequest("GET", "http://example.com") + req.RemoteAddr = "192.168.100.5" + + buf := new(bytes.Buffer) + writeLog(buf, req, *req.URL, ts, http.StatusOK, 100) + log := buf.String() + + expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100\n" + if log != expected { + t.Fatalf("wrong log, got %q want %q", log, expected) + } + + // CONNECT request over http/2.0 + req = &http.Request{ + Method: "CONNECT", + Proto: "HTTP/2.0", + ProtoMajor: 2, + ProtoMinor: 0, + URL: &url.URL{Host: "www.example.com:443"}, + Host: "www.example.com:443", + RemoteAddr: "192.168.100.5", + } + + buf = new(bytes.Buffer) + writeLog(buf, req, *req.URL, ts, http.StatusOK, 100) + log = buf.String() + + expected = "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"CONNECT www.example.com:443 HTTP/2.0\" 200 100\n" + if log != expected { + t.Fatalf("wrong log, got %q want %q", log, expected) + } + + // Request with an unauthorized user + req = newRequest("GET", "http://example.com") + req.RemoteAddr = "192.168.100.5" + req.URL.User = url.User("kamil") + + buf.Reset() + writeLog(buf, req, *req.URL, ts, http.StatusUnauthorized, 500) + log = buf.String() + + expected = "192.168.100.5 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 401 500\n" + if log != expected { + t.Fatalf("wrong log, got %q want %q", log, expected) + } + + // Request with url encoded parameters + req = newRequest("GET", "http://example.com/test?abc=hello%20world&a=b%3F") + req.RemoteAddr = "192.168.100.5" + + buf.Reset() + writeLog(buf, req, *req.URL, ts, http.StatusOK, 100) + log = buf.String() + + expected = "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET /test?abc=hello%20world&a=b%3F HTTP/1.1\" 200 100\n" + if log != expected { + t.Fatalf("wrong log, got %q want %q", log, expected) + } +} + +func TestWriteCombinedLog(t *testing.T) { + loc, err := time.LoadLocation("Europe/Warsaw") + if err != nil { + panic(err) + } + ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc) + + // A typical request with an OK response + req := newRequest("GET", "http://example.com") + req.RemoteAddr = "192.168.100.5" + req.Header.Set("Referer", "http://example.com") + req.Header.Set( + "User-Agent", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.33 "+ + "(KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33", + ) + + buf := new(bytes.Buffer) + writeCombinedLog(buf, req, *req.URL, ts, http.StatusOK, 100) + log := buf.String() + + expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " + + "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " + + "AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n" + if log != expected { + t.Fatalf("wrong log, got %q want %q", log, expected) + } + + // CONNECT request over http/2.0 + req1 := &http.Request{ + Method: "CONNECT", + Host: "www.example.com:443", + Proto: "HTTP/2.0", + ProtoMajor: 2, + ProtoMinor: 0, + RemoteAddr: "192.168.100.5", + Header: http.Header{}, + URL: &url.URL{Host: "www.example.com:443"}, + } + req1.Header.Set("Referer", "http://example.com") + req1.Header.Set( + "User-Agent", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.33 "+ + "(KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33", + ) + + buf = new(bytes.Buffer) + writeCombinedLog(buf, req1, *req1.URL, ts, http.StatusOK, 100) + log = buf.String() + + expected = "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"CONNECT www.example.com:443 HTTP/2.0\" 200 100 \"http://example.com\" " + + "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " + + "AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n" + if log != expected { + t.Fatalf("wrong log, got %q want %q", log, expected) + } + + // Request with an unauthorized user + req.URL.User = url.User("kamil") + + buf.Reset() + writeCombinedLog(buf, req, *req.URL, ts, http.StatusUnauthorized, 500) + log = buf.String() + + expected = "192.168.100.5 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 401 500 \"http://example.com\" " + + "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " + + "AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n" + if log != expected { + t.Fatalf("wrong log, got %q want %q", log, expected) + } + + // Test with remote ipv6 address + req.RemoteAddr = "::1" + + buf.Reset() + writeCombinedLog(buf, req, *req.URL, ts, http.StatusOK, 100) + log = buf.String() + + expected = "::1 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " + + "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " + + "AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n" + if log != expected { + t.Fatalf("wrong log, got %q want %q", log, expected) + } + + // Test remote ipv6 addr, with port + req.RemoteAddr = net.JoinHostPort("::1", "65000") + + buf.Reset() + writeCombinedLog(buf, req, *req.URL, ts, http.StatusOK, 100) + log = buf.String() + + expected = "::1 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " + + "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " + + "AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n" + if log != expected { + t.Fatalf("wrong log, got %q want %q", log, expected) + } +} + +func TestLogPathRewrites(t *testing.T) { + var buf bytes.Buffer + + handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + req.URL.Path = "/" // simulate http.StripPrefix and friends + w.WriteHeader(200) + }) + logger := LoggingHandler(&buf, handler) + + logger.ServeHTTP(httptest.NewRecorder(), newRequest("GET", "/subdir/asdf")) + + if !strings.Contains(buf.String(), "GET /subdir/asdf HTTP") { + t.Fatalf("Got log %#v, wanted substring %#v", buf.String(), "GET /subdir/asdf HTTP") + } +} + +func BenchmarkWriteLog(b *testing.B) { + loc, err := time.LoadLocation("Europe/Warsaw") + if err != nil { + b.Fatalf(err.Error()) + } + ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc) + + req := newRequest("GET", "http://example.com") + req.RemoteAddr = "192.168.100.5" + + b.ResetTimer() + + buf := &bytes.Buffer{} + for i := 0; i < b.N; i++ { + buf.Reset() + writeLog(buf, req, *req.URL, ts, http.StatusUnauthorized, 500) + } +} + +func TestContentTypeHandler(t *testing.T) { + tests := []struct { + Method string + AllowContentTypes []string + ContentType string + Code int + }{ + {"POST", []string{"application/json"}, "application/json", http.StatusOK}, + {"POST", []string{"application/json", "application/xml"}, "application/json", http.StatusOK}, + {"POST", []string{"application/json"}, "application/json; charset=utf-8", http.StatusOK}, + {"POST", []string{"application/json"}, "application/json+xxx", http.StatusUnsupportedMediaType}, + {"POST", []string{"application/json"}, "text/plain", http.StatusUnsupportedMediaType}, + {"GET", []string{"application/json"}, "", http.StatusOK}, + {"GET", []string{}, "", http.StatusOK}, + } + for _, test := range tests { + r, err := http.NewRequest(test.Method, "/", nil) + if err != nil { + t.Error(err) + continue + } + + h := ContentTypeHandler(okHandler, test.AllowContentTypes...) + r.Header.Set("Content-Type", test.ContentType) + w := httptest.NewRecorder() + h.ServeHTTP(w, r) + if w.Code != test.Code { + t.Errorf("expected %d, got %d", test.Code, w.Code) + } + } +} + +func TestHTTPMethodOverride(t *testing.T) { + var tests = []struct { + Method string + OverrideMethod string + ExpectedMethod string + }{ + {"POST", "PUT", "PUT"}, + {"POST", "PATCH", "PATCH"}, + {"POST", "DELETE", "DELETE"}, + {"PUT", "DELETE", "PUT"}, + {"GET", "GET", "GET"}, + {"HEAD", "HEAD", "HEAD"}, + {"GET", "PUT", "GET"}, + {"HEAD", "DELETE", "HEAD"}, + } + + for _, test := range tests { + h := HTTPMethodOverrideHandler(okHandler) + reqs := make([]*http.Request, 0, 2) + + rHeader, err := http.NewRequest(test.Method, "/", nil) + if err != nil { + t.Error(err) + } + rHeader.Header.Set(HTTPMethodOverrideHeader, test.OverrideMethod) + reqs = append(reqs, rHeader) + + f := url.Values{HTTPMethodOverrideFormKey: []string{test.OverrideMethod}} + rForm, err := http.NewRequest(test.Method, "/", strings.NewReader(f.Encode())) + if err != nil { + t.Error(err) + } + rForm.Header.Set("Content-Type", "application/x-www-form-urlencoded") + reqs = append(reqs, rForm) + + for _, r := range reqs { + w := httptest.NewRecorder() + h.ServeHTTP(w, r) + if r.Method != test.ExpectedMethod { + t.Errorf("Expected %s, got %s", test.ExpectedMethod, r.Method) + } + } + } +} diff --git a/vendor/github.com/gorilla/handlers/proxy_headers_test.go b/vendor/github.com/gorilla/handlers/proxy_headers_test.go new file mode 100644 index 000000000..85282ef7d --- /dev/null +++ b/vendor/github.com/gorilla/handlers/proxy_headers_test.go @@ -0,0 +1,100 @@ +package handlers + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +type headerTable struct { + key string // header key + val string // header val + expected string // expected result +} + +func TestGetIP(t *testing.T) { + headers := []headerTable{ + {xForwardedFor, "8.8.8.8", "8.8.8.8"}, // Single address + {xForwardedFor, "8.8.8.8, 8.8.4.4", "8.8.8.8"}, // Multiple + {xForwardedFor, "[2001:db8:cafe::17]:4711", "[2001:db8:cafe::17]:4711"}, // IPv6 address + {xForwardedFor, "", ""}, // None + {xRealIP, "8.8.8.8", "8.8.8.8"}, // Single address + {xRealIP, "8.8.8.8, 8.8.4.4", "8.8.8.8, 8.8.4.4"}, // Multiple + {xRealIP, "[2001:db8:cafe::17]:4711", "[2001:db8:cafe::17]:4711"}, // IPv6 address + {xRealIP, "", ""}, // None + {forwarded, `for="_gazonk"`, "_gazonk"}, // Hostname + {forwarded, `For="[2001:db8:cafe::17]:4711`, `[2001:db8:cafe::17]:4711`}, // IPv6 address + {forwarded, `for=192.0.2.60;proto=http;by=203.0.113.43`, `192.0.2.60`}, // Multiple params + {forwarded, `for=192.0.2.43, for=198.51.100.17`, "192.0.2.43"}, // Multiple params + {forwarded, `for="workstation.local",for=198.51.100.17`, "workstation.local"}, // Hostname + } + + for _, v := range headers { + req := &http.Request{ + Header: http.Header{ + v.key: []string{v.val}, + }} + res := getIP(req) + if res != v.expected { + t.Fatalf("wrong header for %s: got %s want %s", v.key, res, + v.expected) + } + } +} + +func TestGetScheme(t *testing.T) { + headers := []headerTable{ + {xForwardedProto, "https", "https"}, + {xForwardedProto, "http", "http"}, + {xForwardedProto, "HTTP", "http"}, + {forwarded, `For="[2001:db8:cafe::17]:4711`, ""}, // No proto + {forwarded, `for=192.0.2.43, for=198.51.100.17;proto=https`, "https"}, // Multiple params before proto + {forwarded, `for=172.32.10.15; proto=https;by=127.0.0.1`, "https"}, // Space before proto + {forwarded, `for=192.0.2.60;proto=http;by=203.0.113.43`, "http"}, // Multiple params + } + + for _, v := range headers { + req := &http.Request{ + Header: http.Header{ + v.key: []string{v.val}, + }, + } + res := getScheme(req) + if res != v.expected { + t.Fatalf("wrong header for %s: got %s want %s", v.key, res, + v.expected) + } + } +} + +// Test the middleware end-to-end +func TestProxyHeaders(t *testing.T) { + rr := httptest.NewRecorder() + r := newRequest("GET", "/") + + r.Header.Set(xForwardedFor, "8.8.8.8") + r.Header.Set(xForwardedProto, "https") + + var addr string + var proto string + ProxyHeaders(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + addr = r.RemoteAddr + proto = r.URL.Scheme + })).ServeHTTP(rr, r) + + if rr.Code != http.StatusOK { + t.Fatalf("bad status: got %d want %d", rr.Code, http.StatusOK) + } + + if addr != r.Header.Get(xForwardedFor) { + t.Fatalf("wrong address: got %s want %s", addr, + r.Header.Get(xForwardedFor)) + } + + if proto != r.Header.Get(xForwardedProto) { + t.Fatalf("wrong address: got %s want %s", proto, + r.Header.Get(xForwardedProto)) + } + +} diff --git a/vendor/github.com/gorilla/handlers/recovery_test.go b/vendor/github.com/gorilla/handlers/recovery_test.go new file mode 100644 index 000000000..1ae0e5805 --- /dev/null +++ b/vendor/github.com/gorilla/handlers/recovery_test.go @@ -0,0 +1,44 @@ +package handlers + +import ( + "bytes" + "log" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func TestRecoveryLoggerWithDefaultOptions(t *testing.T) { + var buf bytes.Buffer + log.SetOutput(&buf) + + handler := RecoveryHandler() + handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + panic("Unexpected error!") + }) + + recovery := handler(handlerFunc) + recovery.ServeHTTP(httptest.NewRecorder(), newRequest("GET", "/subdir/asdf")) + + if !strings.Contains(buf.String(), "Unexpected error!") { + t.Fatalf("Got log %#v, wanted substring %#v", buf.String(), "Unexpected error!") + } +} + +func TestRecoveryLoggerWithCustomLogger(t *testing.T) { + var buf bytes.Buffer + var logger = log.New(&buf, "", log.LstdFlags) + + handler := RecoveryHandler(RecoveryLogger(logger), PrintRecoveryStack(false)) + handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + panic("Unexpected error!") + }) + + recovery := handler(handlerFunc) + recovery.ServeHTTP(httptest.NewRecorder(), newRequest("GET", "/subdir/asdf")) + + if !strings.Contains(buf.String(), "Unexpected error!") { + t.Fatalf("Got log %#v, wanted substring %#v", buf.String(), "Unexpected error!") + } +} diff --git a/vendor/github.com/gorilla/mux/bench_test.go b/vendor/github.com/gorilla/mux/bench_test.go new file mode 100644 index 000000000..946289b92 --- /dev/null +++ b/vendor/github.com/gorilla/mux/bench_test.go @@ -0,0 +1,49 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func BenchmarkMux(b *testing.B) { + router := new(Router) + handler := func(w http.ResponseWriter, r *http.Request) {} + router.HandleFunc("/v1/{v1}", handler) + + request, _ := http.NewRequest("GET", "/v1/anything", nil) + for i := 0; i < b.N; i++ { + router.ServeHTTP(nil, request) + } +} + +func BenchmarkMuxAlternativeInRegexp(b *testing.B) { + router := new(Router) + handler := func(w http.ResponseWriter, r *http.Request) {} + router.HandleFunc("/v1/{v1:(a|b)}", handler) + + requestA, _ := http.NewRequest("GET", "/v1/a", nil) + requestB, _ := http.NewRequest("GET", "/v1/b", nil) + for i := 0; i < b.N; i++ { + router.ServeHTTP(nil, requestA) + router.ServeHTTP(nil, requestB) + } +} + +func BenchmarkManyPathVariables(b *testing.B) { + router := new(Router) + handler := func(w http.ResponseWriter, r *http.Request) {} + router.HandleFunc("/v1/{v1}/{v2}/{v3}/{v4}/{v5}", handler) + + matchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4/5", nil) + notMatchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4", nil) + recorder := httptest.NewRecorder() + for i := 0; i < b.N; i++ { + router.ServeHTTP(nil, matchingRequest) + router.ServeHTTP(recorder, notMatchingRequest) + } +} diff --git a/vendor/github.com/gorilla/mux/mux_test.go b/vendor/github.com/gorilla/mux/mux_test.go new file mode 100644 index 000000000..777d063c0 --- /dev/null +++ b/vendor/github.com/gorilla/mux/mux_test.go @@ -0,0 +1,1471 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "fmt" + "net/http" + "strings" + "testing" + + "github.com/gorilla/context" +) + +func (r *Route) GoString() string { + matchers := make([]string, len(r.matchers)) + for i, m := range r.matchers { + matchers[i] = fmt.Sprintf("%#v", m) + } + return fmt.Sprintf("&Route{matchers:[]matcher{%s}}", strings.Join(matchers, ", ")) +} + +func (r *routeRegexp) GoString() string { + return fmt.Sprintf("&routeRegexp{template: %q, matchHost: %t, matchQuery: %t, strictSlash: %t, regexp: regexp.MustCompile(%q), reverse: %q, varsN: %v, varsR: %v", r.template, r.matchHost, r.matchQuery, r.strictSlash, r.regexp.String(), r.reverse, r.varsN, r.varsR) +} + +type routeTest struct { + title string // title of the test + route *Route // the route being tested + request *http.Request // a request to test the route + vars map[string]string // the expected vars of the match + host string // the expected host of the match + path string // the expected path of the match + path_template string // the expected path template to match + host_template string // the expected host template to match + shouldMatch bool // whether the request is expected to match the route at all + shouldRedirect bool // whether the request should result in a redirect +} + +func TestHost(t *testing.T) { + // newRequestHost a new request with a method, url, and host header + newRequestHost := func(method, url, host string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + req.Host = host + return req + } + + tests := []routeTest{ + { + title: "Host route match", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: true, + }, + { + title: "Host route, wrong host in request URL", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: false, + }, + { + title: "Host route with port, match", + route: new(Route).Host("aaa.bbb.ccc:1234"), + request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc:1234", + path: "", + shouldMatch: true, + }, + { + title: "Host route with port, wrong port in request URL", + route: new(Route).Host("aaa.bbb.ccc:1234"), + request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"), + vars: map[string]string{}, + host: "aaa.bbb.ccc:1234", + path: "", + shouldMatch: false, + }, + { + title: "Host route, match with host in request header", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: true, + }, + { + title: "Host route, wrong host in request header", + route: new(Route).Host("aaa.bbb.ccc"), + request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"), + vars: map[string]string{}, + host: "aaa.bbb.ccc", + path: "", + shouldMatch: false, + }, + // BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true}, + { + title: "Host route with port, wrong host in request header", + route: new(Route).Host("aaa.bbb.ccc:1234"), + request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"), + vars: map[string]string{}, + host: "aaa.bbb.ccc:1234", + path: "", + shouldMatch: false, + }, + { + title: "Host route with pattern, match", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with pattern, additional capturing group, match", + route: new(Route).Host("aaa.{v1:[a-z]{2}(b|c)}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v1:[a-z]{2}(b|c)}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with pattern, wrong host in request URL", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: false, + }, + { + title: "Host route with multiple patterns, match", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: true, + }, + { + title: "Host route with multiple patterns, wrong host in request URL", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: false, + }, + { + title: "Host route with hyphenated name and pattern, match", + route: new(Route).Host("aaa.{v-1:[a-z]{3}}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v-1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v-1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with hyphenated name and pattern, additional capturing group, match", + route: new(Route).Host("aaa.{v-1:[a-z]{2}(b|c)}.ccc"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v-1": "bbb"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `aaa.{v-1:[a-z]{2}(b|c)}.ccc`, + shouldMatch: true, + }, + { + title: "Host route with multiple hyphenated names and patterns, match", + route: new(Route).Host("{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v-1": "aaa", "v-2": "bbb", "v-3": "ccc"}, + host: "aaa.bbb.ccc", + path: "", + host_template: `{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}`, + shouldMatch: true, + }, + { + title: "Path route with single pattern with pipe, match", + route: new(Route).Path("/{category:a|b/c}"), + request: newRequest("GET", "http://localhost/a"), + vars: map[string]string{"category": "a"}, + host: "", + path: "/a", + path_template: `/{category:a|b/c}`, + shouldMatch: true, + }, + { + title: "Path route with single pattern with pipe, match", + route: new(Route).Path("/{category:a|b/c}"), + request: newRequest("GET", "http://localhost/b/c"), + vars: map[string]string{"category": "b/c"}, + host: "", + path: "/b/c", + path_template: `/{category:a|b/c}`, + shouldMatch: true, + }, + { + title: "Path route with multiple patterns with pipe, match", + route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"), + request: newRequest("GET", "http://localhost/a/product_name/1"), + vars: map[string]string{"category": "a", "product": "product_name", "id": "1"}, + host: "", + path: "/a/product_name/1", + path_template: `/{category:a|b/c}/{product}/{id:[0-9]+}`, + shouldMatch: true, + }, + { + title: "Path route with multiple patterns with pipe, match", + route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"), + request: newRequest("GET", "http://localhost/b/c/product_name/1"), + vars: map[string]string{"category": "b/c", "product": "product_name", "id": "1"}, + host: "", + path: "/b/c/product_name/1", + path_template: `/{category:a|b/c}/{product}/{id:[0-9]+}`, + shouldMatch: true, + }, + } + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestPath(t *testing.T) { + tests := []routeTest{ + { + title: "Path route, match", + route: new(Route).Path("/111/222/333"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{}, + host: "", + path: "/111/222/333", + shouldMatch: true, + }, + { + title: "Path route, match with trailing slash in request and path", + route: new(Route).Path("/111/"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111/", + shouldMatch: true, + }, + { + title: "Path route, do not match with trailing slash in path", + route: new(Route).Path("/111/"), + request: newRequest("GET", "http://localhost/111"), + vars: map[string]string{}, + host: "", + path: "/111", + path_template: `/111/`, + shouldMatch: false, + }, + { + title: "Path route, do not match with trailing slash in request", + route: new(Route).Path("/111"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111/", + path_template: `/111`, + shouldMatch: false, + }, + { + title: "Path route, wrong path in request in request URL", + route: new(Route).Path("/111/222/333"), + request: newRequest("GET", "http://localhost/1/2/3"), + vars: map[string]string{}, + host: "", + path: "/111/222/333", + shouldMatch: false, + }, + { + title: "Path route with pattern, match", + route: new(Route).Path("/111/{v1:[0-9]{3}}/333"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222/333", + path_template: `/111/{v1:[0-9]{3}}/333`, + shouldMatch: true, + }, + { + title: "Path route with pattern, URL in request does not match", + route: new(Route).Path("/111/{v1:[0-9]{3}}/333"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222/333", + path_template: `/111/{v1:[0-9]{3}}/333`, + shouldMatch: false, + }, + { + title: "Path route with multiple patterns, match", + route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"}, + host: "", + path: "/111/222/333", + path_template: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "Path route with multiple patterns, URL in request does not match", + route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"}, + host: "", + path: "/111/222/333", + path_template: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}`, + shouldMatch: false, + }, + { + title: "Path route with multiple patterns with pipe, match", + route: new(Route).Path("/{category:a|(b/c)}/{product}/{id:[0-9]+}"), + request: newRequest("GET", "http://localhost/a/product_name/1"), + vars: map[string]string{"category": "a", "product": "product_name", "id": "1"}, + host: "", + path: "/a/product_name/1", + path_template: `/{category:a|(b/c)}/{product}/{id:[0-9]+}`, + shouldMatch: true, + }, + { + title: "Path route with hyphenated name and pattern, match", + route: new(Route).Path("/111/{v-1:[0-9]{3}}/333"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v-1": "222"}, + host: "", + path: "/111/222/333", + path_template: `/111/{v-1:[0-9]{3}}/333`, + shouldMatch: true, + }, + { + title: "Path route with multiple hyphenated names and patterns, match", + route: new(Route).Path("/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v-1": "111", "v-2": "222", "v-3": "333"}, + host: "", + path: "/111/222/333", + path_template: `/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "Path route with multiple hyphenated names and patterns with pipe, match", + route: new(Route).Path("/{product-category:a|(b/c)}/{product-name}/{product-id:[0-9]+}"), + request: newRequest("GET", "http://localhost/a/product_name/1"), + vars: map[string]string{"product-category": "a", "product-name": "product_name", "product-id": "1"}, + host: "", + path: "/a/product_name/1", + path_template: `/{product-category:a|(b/c)}/{product-name}/{product-id:[0-9]+}`, + shouldMatch: true, + }, + { + title: "Path route with multiple hyphenated names and patterns with pipe and case insensitive, match", + route: new(Route).Path("/{type:(?i:daily|mini|variety)}-{date:\\d{4,4}-\\d{2,2}-\\d{2,2}}"), + request: newRequest("GET", "http://localhost/daily-2016-01-01"), + vars: map[string]string{"type": "daily", "date": "2016-01-01"}, + host: "", + path: "/daily-2016-01-01", + path_template: `/{type:(?i:daily|mini|variety)}-{date:\d{4,4}-\d{2,2}-\d{2,2}}`, + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestPathPrefix(t *testing.T) { + tests := []routeTest{ + { + title: "PathPrefix route, match", + route: new(Route).PathPrefix("/111"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: true, + }, + { + title: "PathPrefix route, match substring", + route: new(Route).PathPrefix("/1"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{}, + host: "", + path: "/1", + shouldMatch: true, + }, + { + title: "PathPrefix route, URL prefix in request does not match", + route: new(Route).PathPrefix("/111"), + request: newRequest("GET", "http://localhost/1/2/3"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: false, + }, + { + title: "PathPrefix route with pattern, match", + route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222", + path_template: `/111/{v1:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "PathPrefix route with pattern, URL prefix in request does not match", + route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "222"}, + host: "", + path: "/111/222", + path_template: `/111/{v1:[0-9]{3}}`, + shouldMatch: false, + }, + { + title: "PathPrefix route with multiple patterns, match", + route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/222/333"), + vars: map[string]string{"v1": "111", "v2": "222"}, + host: "", + path: "/111/222", + path_template: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}`, + shouldMatch: true, + }, + { + title: "PathPrefix route with multiple patterns, URL prefix in request does not match", + route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"), + request: newRequest("GET", "http://localhost/111/aaa/333"), + vars: map[string]string{"v1": "111", "v2": "222"}, + host: "", + path: "/111/222", + path_template: `/{v1:[0-9]{3}}/{v2:[0-9]{3}}`, + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestHostPath(t *testing.T) { + tests := []routeTest{ + { + title: "Host and Path route, match", + route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{}, + host: "", + path: "", + path_template: `/111/222/333`, + host_template: `aaa.bbb.ccc`, + shouldMatch: true, + }, + { + title: "Host and Path route, wrong host in request URL", + route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{}, + host: "", + path: "", + path_template: `/111/222/333`, + host_template: `aaa.bbb.ccc`, + shouldMatch: false, + }, + { + title: "Host and Path route with pattern, match", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb", "v2": "222"}, + host: "aaa.bbb.ccc", + path: "/111/222/333", + path_template: `/111/{v2:[0-9]{3}}/333`, + host_template: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: true, + }, + { + title: "Host and Path route with pattern, URL in request does not match", + route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "bbb", "v2": "222"}, + host: "aaa.bbb.ccc", + path: "/111/222/333", + path_template: `/111/{v2:[0-9]{3}}/333`, + host_template: `aaa.{v1:[a-z]{3}}.ccc`, + shouldMatch: false, + }, + { + title: "Host and Path route with multiple patterns, match", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"), + request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"}, + host: "aaa.bbb.ccc", + path: "/111/222/333", + path_template: `/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}`, + host_template: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: true, + }, + { + title: "Host and Path route with multiple patterns, URL in request does not match", + route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"), + request: newRequest("GET", "http://aaa.222.ccc/111/222/333"), + vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"}, + host: "aaa.bbb.ccc", + path: "/111/222/333", + path_template: `/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}`, + host_template: `{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}`, + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestHeaders(t *testing.T) { + // newRequestHeaders creates a new request with a method, url, and headers + newRequestHeaders := func(method, url string, headers map[string]string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + for k, v := range headers { + req.Header.Add(k, v) + } + return req + } + + tests := []routeTest{ + { + title: "Headers route, match", + route: new(Route).Headers("foo", "bar", "baz", "ding"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Headers route, bad header values", + route: new(Route).Headers("foo", "bar", "baz", "ding"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Headers route, regex header values to match", + route: new(Route).Headers("foo", "ba[zr]"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Headers route, regex header values to match", + route: new(Route).HeadersRegexp("foo", "ba[zr]"), + request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "baz"}), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } + +} + +func TestMethods(t *testing.T) { + tests := []routeTest{ + { + title: "Methods route, match GET", + route: new(Route).Methods("GET", "POST"), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Methods route, match POST", + route: new(Route).Methods("GET", "POST"), + request: newRequest("POST", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Methods route, bad method", + route: new(Route).Methods("GET", "POST"), + request: newRequest("PUT", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestQueries(t *testing.T) { + tests := []routeTest{ + { + title: "Queries route, match", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route, match with a query string", + route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"), + vars: map[string]string{}, + host: "", + path: "", + path_template: `/api`, + host_template: `www.example.com`, + shouldMatch: true, + }, + { + title: "Queries route, match with a query string out of order", + route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"), + vars: map[string]string{}, + host: "", + path: "", + path_template: `/api`, + host_template: `www.example.com`, + shouldMatch: true, + }, + { + title: "Queries route, bad query", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?foo=bar&baz=dong"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with pattern, match", + route: new(Route).Queries("foo", "{v1}"), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{"v1": "bar"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with multiple patterns, match", + route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern, match", + route: new(Route).Queries("foo", "{v1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=10"), + vars: map[string]string{"v1": "10"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=a"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with regexp pattern with quantifier, match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=1"), + vars: map[string]string{"v1": "1"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, additional variable in query string, match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?bar=2&foo=1"), + vars: map[string]string{"v1": "1"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=12"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with regexp pattern with quantifier, additional capturing group", + route: new(Route).Queries("foo", "{v1:[0-9]{1}(a|b)}"), + request: newRequest("GET", "http://localhost?foo=1a"), + vars: map[string]string{"v1": "1a"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match", + route: new(Route).Queries("foo", "{v1:[0-9]{1}}"), + request: newRequest("GET", "http://localhost?foo=12"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with hyphenated name, match", + route: new(Route).Queries("foo", "{v-1}"), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{"v-1": "bar"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with multiple hyphenated names, match", + route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"), + request: newRequest("GET", "http://localhost?foo=bar&baz=ding"), + vars: map[string]string{"v-1": "bar", "v-2": "ding"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with hyphenate name and pattern, match", + route: new(Route).Queries("foo", "{v-1:[0-9]+}"), + request: newRequest("GET", "http://localhost?foo=10"), + vars: map[string]string{"v-1": "10"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group", + route: new(Route).Queries("foo", "{v-1:[0-9]{1}(a|b)}"), + request: newRequest("GET", "http://localhost?foo=1a"), + vars: map[string]string{"v-1": "1a"}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with empty value, should match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost?foo=bar"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with empty value and no parameter in request, should not match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with empty value and empty parameter in request, should match", + route: new(Route).Queries("foo", ""), + request: newRequest("GET", "http://localhost?foo="), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route with overlapping value, should not match", + route: new(Route).Queries("foo", "bar"), + request: newRequest("GET", "http://localhost?foo=barfoo"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with no parameter in request, should not match", + route: new(Route).Queries("foo", "{bar}"), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + { + title: "Queries route with empty parameter in request, should match", + route: new(Route).Queries("foo", "{bar}"), + request: newRequest("GET", "http://localhost?foo="), + vars: map[string]string{"foo": ""}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Queries route, bad submatch", + route: new(Route).Queries("foo", "bar", "baz", "ding"), + request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestSchemes(t *testing.T) { + tests := []routeTest{ + // Schemes + { + title: "Schemes route, match https", + route: new(Route).Schemes("https", "ftp"), + request: newRequest("GET", "https://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Schemes route, match ftp", + route: new(Route).Schemes("https", "ftp"), + request: newRequest("GET", "ftp://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "Schemes route, bad scheme", + route: new(Route).Schemes("https", "ftp"), + request: newRequest("GET", "http://localhost"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + } + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestMatcherFunc(t *testing.T) { + m := func(r *http.Request, m *RouteMatch) bool { + if r.URL.Host == "aaa.bbb.ccc" { + return true + } + return false + } + + tests := []routeTest{ + { + title: "MatchFunc route, match", + route: new(Route).MatcherFunc(m), + request: newRequest("GET", "http://aaa.bbb.ccc"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: true, + }, + { + title: "MatchFunc route, non-match", + route: new(Route).MatcherFunc(m), + request: newRequest("GET", "http://aaa.222.ccc"), + vars: map[string]string{}, + host: "", + path: "", + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestBuildVarsFunc(t *testing.T) { + tests := []routeTest{ + { + title: "BuildVarsFunc set on route", + route: new(Route).Path(`/111/{v1:\d}{v2:.*}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v1"] = "3" + vars["v2"] = "a" + return vars + }), + request: newRequest("GET", "http://localhost/111/2"), + path: "/111/3a", + path_template: `/111/{v1:\d}{v2:.*}`, + shouldMatch: true, + }, + { + title: "BuildVarsFunc set on route and parent route", + route: new(Route).PathPrefix(`/{v1:\d}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v1"] = "2" + return vars + }).Subrouter().Path(`/{v2:\w}`).BuildVarsFunc(func(vars map[string]string) map[string]string { + vars["v2"] = "b" + return vars + }), + request: newRequest("GET", "http://localhost/1/a"), + path: "/2/b", + path_template: `/{v1:\d}/{v2:\w}`, + shouldMatch: true, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestSubRouter(t *testing.T) { + subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter() + subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter() + + tests := []routeTest{ + { + route: subrouter1.Path("/{v2:[a-z]+}"), + request: newRequest("GET", "http://aaa.google.com/bbb"), + vars: map[string]string{"v1": "aaa", "v2": "bbb"}, + host: "aaa.google.com", + path: "/bbb", + path_template: `/{v2:[a-z]+}`, + host_template: `{v1:[a-z]+}.google.com`, + shouldMatch: true, + }, + { + route: subrouter1.Path("/{v2:[a-z]+}"), + request: newRequest("GET", "http://111.google.com/111"), + vars: map[string]string{"v1": "aaa", "v2": "bbb"}, + host: "aaa.google.com", + path: "/bbb", + path_template: `/{v2:[a-z]+}`, + host_template: `{v1:[a-z]+}.google.com`, + shouldMatch: false, + }, + { + route: subrouter2.Path("/baz/{v2}"), + request: newRequest("GET", "http://localhost/foo/bar/baz/ding"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "/foo/bar/baz/ding", + path_template: `/foo/{v1}/baz/{v2}`, + shouldMatch: true, + }, + { + route: subrouter2.Path("/baz/{v2}"), + request: newRequest("GET", "http://localhost/foo/bar"), + vars: map[string]string{"v1": "bar", "v2": "ding"}, + host: "", + path: "/foo/bar/baz/ding", + path_template: `/foo/{v1}/baz/{v2}`, + shouldMatch: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestNamedRoutes(t *testing.T) { + r1 := NewRouter() + r1.NewRoute().Name("a") + r1.NewRoute().Name("b") + r1.NewRoute().Name("c") + + r2 := r1.NewRoute().Subrouter() + r2.NewRoute().Name("d") + r2.NewRoute().Name("e") + r2.NewRoute().Name("f") + + r3 := r2.NewRoute().Subrouter() + r3.NewRoute().Name("g") + r3.NewRoute().Name("h") + r3.NewRoute().Name("i") + + if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 { + t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes) + } else if r1.Get("i") == nil { + t.Errorf("Subroute name not registered") + } +} + +func TestStrictSlash(t *testing.T) { + r := NewRouter() + r.StrictSlash(true) + + tests := []routeTest{ + { + title: "Redirect path without slash", + route: r.NewRoute().Path("/111/"), + request: newRequest("GET", "http://localhost/111"), + vars: map[string]string{}, + host: "", + path: "/111/", + shouldMatch: true, + shouldRedirect: true, + }, + { + title: "Do not redirect path with slash", + route: r.NewRoute().Path("/111/"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111/", + shouldMatch: true, + shouldRedirect: false, + }, + { + title: "Redirect path with slash", + route: r.NewRoute().Path("/111"), + request: newRequest("GET", "http://localhost/111/"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: true, + shouldRedirect: true, + }, + { + title: "Do not redirect path without slash", + route: r.NewRoute().Path("/111"), + request: newRequest("GET", "http://localhost/111"), + vars: map[string]string{}, + host: "", + path: "/111", + shouldMatch: true, + shouldRedirect: false, + }, + { + title: "Propagate StrictSlash to subrouters", + route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"), + request: newRequest("GET", "http://localhost/static/images"), + vars: map[string]string{}, + host: "", + path: "/static/images/", + shouldMatch: true, + shouldRedirect: true, + }, + { + title: "Ignore StrictSlash for path prefix", + route: r.NewRoute().PathPrefix("/static/"), + request: newRequest("GET", "http://localhost/static/logo.png"), + vars: map[string]string{}, + host: "", + path: "/static/", + shouldMatch: true, + shouldRedirect: false, + }, + } + + for _, test := range tests { + testRoute(t, test) + testTemplate(t, test) + } +} + +func TestWalkSingleDepth(t *testing.T) { + r0 := NewRouter() + r1 := NewRouter() + r2 := NewRouter() + + r0.Path("/g") + r0.Path("/o") + r0.Path("/d").Handler(r1) + r0.Path("/r").Handler(r2) + r0.Path("/a") + + r1.Path("/z") + r1.Path("/i") + r1.Path("/l") + r1.Path("/l") + + r2.Path("/i") + r2.Path("/l") + r2.Path("/l") + + paths := []string{"g", "o", "r", "i", "l", "l", "a"} + depths := []int{0, 0, 0, 1, 1, 1, 0} + i := 0 + err := r0.Walk(func(route *Route, router *Router, ancestors []*Route) error { + matcher := route.matchers[0].(*routeRegexp) + if matcher.template == "/d" { + return SkipRouter + } + if len(ancestors) != depths[i] { + t.Errorf(`Expected depth of %d at i = %d; got "%d"`, depths[i], i, len(ancestors)) + } + if matcher.template != "/"+paths[i] { + t.Errorf(`Expected "/%s" at i = %d; got "%s"`, paths[i], i, matcher.template) + } + i++ + return nil + }) + if err != nil { + panic(err) + } + if i != len(paths) { + t.Errorf("Expected %d routes, found %d", len(paths), i) + } +} + +func TestWalkNested(t *testing.T) { + router := NewRouter() + + g := router.Path("/g").Subrouter() + o := g.PathPrefix("/o").Subrouter() + r := o.PathPrefix("/r").Subrouter() + i := r.PathPrefix("/i").Subrouter() + l1 := i.PathPrefix("/l").Subrouter() + l2 := l1.PathPrefix("/l").Subrouter() + l2.Path("/a") + + paths := []string{"/g", "/g/o", "/g/o/r", "/g/o/r/i", "/g/o/r/i/l", "/g/o/r/i/l/l", "/g/o/r/i/l/l/a"} + idx := 0 + err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { + path := paths[idx] + tpl := route.regexp.path.template + if tpl != path { + t.Errorf(`Expected %s got %s`, path, tpl) + } + idx++ + return nil + }) + if err != nil { + panic(err) + } + if idx != len(paths) { + t.Errorf("Expected %d routes, found %d", len(paths), idx) + } +} + +func TestSubrouterErrorHandling(t *testing.T) { + superRouterCalled := false + subRouterCalled := false + + router := NewRouter() + router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + superRouterCalled = true + }) + subRouter := router.PathPrefix("/bign8").Subrouter() + subRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + subRouterCalled = true + }) + + req, _ := http.NewRequest("GET", "http://localhost/bign8/was/here", nil) + router.ServeHTTP(NewRecorder(), req) + + if superRouterCalled { + t.Error("Super router 404 handler called when sub-router 404 handler is available.") + } + if !subRouterCalled { + t.Error("Sub-router 404 handler was not called.") + } +} + +// ---------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------- + +func getRouteTemplate(route *Route) string { + host, err := route.GetHostTemplate() + if err != nil { + host = "none" + } + path, err := route.GetPathTemplate() + if err != nil { + path = "none" + } + return fmt.Sprintf("Host: %v, Path: %v", host, path) +} + +func testRoute(t *testing.T, test routeTest) { + request := test.request + route := test.route + vars := test.vars + shouldMatch := test.shouldMatch + host := test.host + path := test.path + url := test.host + test.path + shouldRedirect := test.shouldRedirect + + var match RouteMatch + ok := route.Match(request, &match) + if ok != shouldMatch { + msg := "Should match" + if !shouldMatch { + msg = "Should not match" + } + t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars) + return + } + if shouldMatch { + if test.vars != nil && !stringMapEqual(test.vars, match.Vars) { + t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars) + return + } + if host != "" { + u, _ := test.route.URLHost(mapToPairs(match.Vars)...) + if host != u.Host { + t.Errorf("(%v) URLHost not equal: expected %v, got %v -- %v", test.title, host, u.Host, getRouteTemplate(route)) + return + } + } + if path != "" { + u, _ := route.URLPath(mapToPairs(match.Vars)...) + if path != u.Path { + t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, path, u.Path, getRouteTemplate(route)) + return + } + } + if url != "" { + u, _ := route.URL(mapToPairs(match.Vars)...) + if url != u.Host+u.Path { + t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, url, u.Host+u.Path, getRouteTemplate(route)) + return + } + } + if shouldRedirect && match.Handler == nil { + t.Errorf("(%v) Did not redirect", test.title) + return + } + if !shouldRedirect && match.Handler != nil { + t.Errorf("(%v) Unexpected redirect", test.title) + return + } + } +} + +func testTemplate(t *testing.T, test routeTest) { + route := test.route + path_template := test.path_template + if len(path_template) == 0 { + path_template = test.path + } + host_template := test.host_template + if len(host_template) == 0 { + host_template = test.host + } + + path_tmpl, path_err := route.GetPathTemplate() + if path_err == nil && path_tmpl != path_template { + t.Errorf("(%v) GetPathTemplate not equal: expected %v, got %v", test.title, path_template, path_tmpl) + } + + host_tmpl, host_err := route.GetHostTemplate() + if host_err == nil && host_tmpl != host_template { + t.Errorf("(%v) GetHostTemplate not equal: expected %v, got %v", test.title, host_template, host_tmpl) + } +} + +// Tests that the context is cleared or not cleared properly depending on +// the configuration of the router +func TestKeepContext(t *testing.T) { + func1 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/", func1).Name("func1") + + req, _ := http.NewRequest("GET", "http://localhost/", nil) + context.Set(req, "t", 1) + + res := new(http.ResponseWriter) + r.ServeHTTP(*res, req) + + if _, ok := context.GetOk(req, "t"); ok { + t.Error("Context should have been cleared at end of request") + } + + r.KeepContext = true + + req, _ = http.NewRequest("GET", "http://localhost/", nil) + context.Set(req, "t", 1) + + r.ServeHTTP(*res, req) + if _, ok := context.GetOk(req, "t"); !ok { + t.Error("Context should NOT have been cleared at end of request") + } + +} + +type TestA301ResponseWriter struct { + hh http.Header + status int +} + +func (ho TestA301ResponseWriter) Header() http.Header { + return http.Header(ho.hh) +} + +func (ho TestA301ResponseWriter) Write(b []byte) (int, error) { + return 0, nil +} + +func (ho TestA301ResponseWriter) WriteHeader(code int) { + ho.status = code +} + +func Test301Redirect(t *testing.T) { + m := make(http.Header) + + func1 := func(w http.ResponseWriter, r *http.Request) {} + func2 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/api/", func2).Name("func2") + r.HandleFunc("/", func1).Name("func1") + + req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil) + + res := TestA301ResponseWriter{ + hh: m, + status: 0, + } + r.ServeHTTP(&res, req) + + if "http://localhost/api/?abc=def" != res.hh["Location"][0] { + t.Errorf("Should have complete URL with query string") + } +} + +func TestSkipClean(t *testing.T) { + func1 := func(w http.ResponseWriter, r *http.Request) {} + func2 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.SkipClean(true) + r.HandleFunc("/api/", func2).Name("func2") + r.HandleFunc("/", func1).Name("func1") + + req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil) + res := NewRecorder() + r.ServeHTTP(res, req) + + if len(res.HeaderMap["Location"]) != 0 { + t.Errorf("Shouldn't redirect since skip clean is disabled") + } +} + +// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW +func TestSubrouterHeader(t *testing.T) { + expected := "func1 response" + func1 := func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, expected) + } + func2 := func(http.ResponseWriter, *http.Request) {} + + r := NewRouter() + s := r.Headers("SomeSpecialHeader", "").Subrouter() + s.HandleFunc("/", func1).Name("func1") + r.HandleFunc("/", func2).Name("func2") + + req, _ := http.NewRequest("GET", "http://localhost/", nil) + req.Header.Add("SomeSpecialHeader", "foo") + match := new(RouteMatch) + matched := r.Match(req, match) + if !matched { + t.Errorf("Should match request") + } + if match.Route.GetName() != "func1" { + t.Errorf("Expecting func1 handler, got %s", match.Route.GetName()) + } + resp := NewRecorder() + match.Handler.ServeHTTP(resp, req) + if resp.Body.String() != expected { + t.Errorf("Expecting %q", expected) + } +} + +// mapToPairs converts a string map to a slice of string pairs +func mapToPairs(m map[string]string) []string { + var i int + p := make([]string, len(m)*2) + for k, v := range m { + p[i] = k + p[i+1] = v + i += 2 + } + return p +} + +// stringMapEqual checks the equality of two string maps +func stringMapEqual(m1, m2 map[string]string) bool { + nil1 := m1 == nil + nil2 := m2 == nil + if nil1 != nil2 || len(m1) != len(m2) { + return false + } + for k, v := range m1 { + if v != m2[k] { + return false + } + } + return true +} + +// newRequest is a helper function to create a new request with a method and url +func newRequest(method, url string) *http.Request { + req, err := http.NewRequest(method, url, nil) + if err != nil { + panic(err) + } + return req +} diff --git a/vendor/github.com/gorilla/mux/old_test.go b/vendor/github.com/gorilla/mux/old_test.go new file mode 100644 index 000000000..c385a2519 --- /dev/null +++ b/vendor/github.com/gorilla/mux/old_test.go @@ -0,0 +1,710 @@ +// Old tests ported to Go1. This is a mess. Want to drop it one day. + +// Copyright 2011 Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import ( + "bytes" + "net/http" + "testing" +) + +// ---------------------------------------------------------------------------- +// ResponseRecorder +// ---------------------------------------------------------------------------- +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// ResponseRecorder is an implementation of http.ResponseWriter that +// records its mutations for later inspection in tests. +type ResponseRecorder struct { + Code int // the HTTP response code from WriteHeader + HeaderMap http.Header // the HTTP response headers + Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to + Flushed bool +} + +// NewRecorder returns an initialized ResponseRecorder. +func NewRecorder() *ResponseRecorder { + return &ResponseRecorder{ + HeaderMap: make(http.Header), + Body: new(bytes.Buffer), + } +} + +// Header returns the response headers. +func (rw *ResponseRecorder) Header() http.Header { + return rw.HeaderMap +} + +// Write always succeeds and writes to rw.Body, if not nil. +func (rw *ResponseRecorder) Write(buf []byte) (int, error) { + if rw.Body != nil { + rw.Body.Write(buf) + } + if rw.Code == 0 { + rw.Code = http.StatusOK + } + return len(buf), nil +} + +// WriteHeader sets rw.Code. +func (rw *ResponseRecorder) WriteHeader(code int) { + rw.Code = code +} + +// Flush sets rw.Flushed to true. +func (rw *ResponseRecorder) Flush() { + rw.Flushed = true +} + +// ---------------------------------------------------------------------------- + +func TestRouteMatchers(t *testing.T) { + var scheme, host, path, query, method string + var headers map[string]string + var resultVars map[bool]map[string]string + + router := NewRouter() + router.NewRoute().Host("{var1}.google.com"). + Path("/{var2:[a-z]+}/{var3:[0-9]+}"). + Queries("foo", "bar"). + Methods("GET"). + Schemes("https"). + Headers("x-requested-with", "XMLHttpRequest") + router.NewRoute().Host("www.{var4}.com"). + PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}"). + Queries("baz", "ding"). + Methods("POST"). + Schemes("http"). + Headers("Content-Type", "application/json") + + reset := func() { + // Everything match. + scheme = "https" + host = "www.google.com" + path = "/product/42" + query = "?foo=bar" + method = "GET" + headers = map[string]string{"X-Requested-With": "XMLHttpRequest"} + resultVars = map[bool]map[string]string{ + true: {"var1": "www", "var2": "product", "var3": "42"}, + false: {}, + } + } + + reset2 := func() { + // Everything match. + scheme = "http" + host = "www.google.com" + path = "/foo/product/42/path/that/is/ignored" + query = "?baz=ding" + method = "POST" + headers = map[string]string{"Content-Type": "application/json"} + resultVars = map[bool]map[string]string{ + true: {"var4": "google", "var5": "product", "var6": "42"}, + false: {}, + } + } + + match := func(shouldMatch bool) { + url := scheme + "://" + host + path + query + request, _ := http.NewRequest(method, url, nil) + for key, value := range headers { + request.Header.Add(key, value) + } + + var routeMatch RouteMatch + matched := router.Match(request, &routeMatch) + if matched != shouldMatch { + // Need better messages. :) + if matched { + t.Errorf("Should match.") + } else { + t.Errorf("Should not match.") + } + } + + if matched { + currentRoute := routeMatch.Route + if currentRoute == nil { + t.Errorf("Expected a current route.") + } + vars := routeMatch.Vars + expectedVars := resultVars[shouldMatch] + if len(vars) != len(expectedVars) { + t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars) + } + for name, value := range vars { + if expectedVars[name] != value { + t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars) + } + } + } + } + + // 1st route -------------------------------------------------------------- + + // Everything match. + reset() + match(true) + + // Scheme doesn't match. + reset() + scheme = "http" + match(false) + + // Host doesn't match. + reset() + host = "www.mygoogle.com" + match(false) + + // Path doesn't match. + reset() + path = "/product/notdigits" + match(false) + + // Query doesn't match. + reset() + query = "?foo=baz" + match(false) + + // Method doesn't match. + reset() + method = "POST" + match(false) + + // Header doesn't match. + reset() + headers = map[string]string{} + match(false) + + // Everything match, again. + reset() + match(true) + + // 2nd route -------------------------------------------------------------- + + // Everything match. + reset2() + match(true) + + // Scheme doesn't match. + reset2() + scheme = "https" + match(false) + + // Host doesn't match. + reset2() + host = "sub.google.com" + match(false) + + // Path doesn't match. + reset2() + path = "/bar/product/42" + match(false) + + // Query doesn't match. + reset2() + query = "?foo=baz" + match(false) + + // Method doesn't match. + reset2() + method = "GET" + match(false) + + // Header doesn't match. + reset2() + headers = map[string]string{} + match(false) + + // Everything match, again. + reset2() + match(true) +} + +type headerMatcherTest struct { + matcher headerMatcher + headers map[string]string + result bool +} + +var headerMatcherTests = []headerMatcherTest{ + { + matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}), + headers: map[string]string{"X-Requested-With": "XMLHttpRequest"}, + result: true, + }, + { + matcher: headerMatcher(map[string]string{"x-requested-with": ""}), + headers: map[string]string{"X-Requested-With": "anything"}, + result: true, + }, + { + matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}), + headers: map[string]string{}, + result: false, + }, +} + +type hostMatcherTest struct { + matcher *Route + url string + vars map[string]string + result bool +} + +var hostMatcherTests = []hostMatcherTest{ + { + matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"), + url: "http://abc.def.ghi/", + vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"}, + result: true, + }, + { + matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"), + url: "http://a.b.c/", + vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"}, + result: false, + }, +} + +type methodMatcherTest struct { + matcher methodMatcher + method string + result bool +} + +var methodMatcherTests = []methodMatcherTest{ + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "GET", + result: true, + }, + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "POST", + result: true, + }, + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "PUT", + result: true, + }, + { + matcher: methodMatcher([]string{"GET", "POST", "PUT"}), + method: "DELETE", + result: false, + }, +} + +type pathMatcherTest struct { + matcher *Route + url string + vars map[string]string + result bool +} + +var pathMatcherTests = []pathMatcherTest{ + { + matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"), + url: "http://localhost:8080/123/456/789", + vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"}, + result: true, + }, + { + matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"), + url: "http://localhost:8080/1/2/3", + vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"}, + result: false, + }, +} + +type schemeMatcherTest struct { + matcher schemeMatcher + url string + result bool +} + +var schemeMatcherTests = []schemeMatcherTest{ + { + matcher: schemeMatcher([]string{"http", "https"}), + url: "http://localhost:8080/", + result: true, + }, + { + matcher: schemeMatcher([]string{"http", "https"}), + url: "https://localhost:8080/", + result: true, + }, + { + matcher: schemeMatcher([]string{"https"}), + url: "http://localhost:8080/", + result: false, + }, + { + matcher: schemeMatcher([]string{"http"}), + url: "https://localhost:8080/", + result: false, + }, +} + +type urlBuildingTest struct { + route *Route + vars []string + url string +} + +var urlBuildingTests = []urlBuildingTest{ + { + route: new(Route).Host("foo.domain.com"), + vars: []string{}, + url: "http://foo.domain.com", + }, + { + route: new(Route).Host("{subdomain}.domain.com"), + vars: []string{"subdomain", "bar"}, + url: "http://bar.domain.com", + }, + { + route: new(Route).Host("foo.domain.com").Path("/articles"), + vars: []string{}, + url: "http://foo.domain.com/articles", + }, + { + route: new(Route).Path("/articles"), + vars: []string{}, + url: "/articles", + }, + { + route: new(Route).Path("/articles/{category}/{id:[0-9]+}"), + vars: []string{"category", "technology", "id", "42"}, + url: "/articles/technology/42", + }, + { + route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"), + vars: []string{"subdomain", "foo", "category", "technology", "id", "42"}, + url: "http://foo.domain.com/articles/technology/42", + }, +} + +func TestHeaderMatcher(t *testing.T) { + for _, v := range headerMatcherTests { + request, _ := http.NewRequest("GET", "http://localhost:8080/", nil) + for key, value := range v.headers { + request.Header.Add(key, value) + } + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, request.Header) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, request.Header) + } + } + } +} + +func TestHostMatcher(t *testing.T) { + for _, v := range hostMatcherTests { + request, _ := http.NewRequest("GET", v.url, nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + vars := routeMatch.Vars + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.url) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.url) + } + } + if result { + if len(vars) != len(v.vars) { + t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars)) + } + for name, value := range vars { + if v.vars[name] != value { + t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value) + } + } + } else { + if len(vars) != 0 { + t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars)) + } + } + } +} + +func TestMethodMatcher(t *testing.T) { + for _, v := range methodMatcherTests { + request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.method) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.method) + } + } + } +} + +func TestPathMatcher(t *testing.T) { + for _, v := range pathMatcherTests { + request, _ := http.NewRequest("GET", v.url, nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + vars := routeMatch.Vars + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.url) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.url) + } + } + if result { + if len(vars) != len(v.vars) { + t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars)) + } + for name, value := range vars { + if v.vars[name] != value { + t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value) + } + } + } else { + if len(vars) != 0 { + t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars)) + } + } + } +} + +func TestSchemeMatcher(t *testing.T) { + for _, v := range schemeMatcherTests { + request, _ := http.NewRequest("GET", v.url, nil) + var routeMatch RouteMatch + result := v.matcher.Match(request, &routeMatch) + if result != v.result { + if v.result { + t.Errorf("%#v: should match %v.", v.matcher, v.url) + } else { + t.Errorf("%#v: should not match %v.", v.matcher, v.url) + } + } + } +} + +func TestUrlBuilding(t *testing.T) { + + for _, v := range urlBuildingTests { + u, _ := v.route.URL(v.vars...) + url := u.String() + if url != v.url { + t.Errorf("expected %v, got %v", v.url, url) + /* + reversePath := "" + reverseHost := "" + if v.route.pathTemplate != nil { + reversePath = v.route.pathTemplate.Reverse + } + if v.route.hostTemplate != nil { + reverseHost = v.route.hostTemplate.Reverse + } + + t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost) + */ + } + } + + ArticleHandler := func(w http.ResponseWriter, r *http.Request) { + } + + router := NewRouter() + router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article") + + url, _ := router.Get("article").URL("category", "technology", "id", "42") + expected := "/articles/technology/42" + if url.String() != expected { + t.Errorf("Expected %v, got %v", expected, url.String()) + } +} + +func TestMatchedRouteName(t *testing.T) { + routeName := "stock" + router := NewRouter() + route := router.NewRoute().Path("/products/").Name(routeName) + + url := "http://www.example.com/products/" + request, _ := http.NewRequest("GET", url, nil) + var rv RouteMatch + ok := router.Match(request, &rv) + + if !ok || rv.Route != route { + t.Errorf("Expected same route, got %+v.", rv.Route) + } + + retName := rv.Route.GetName() + if retName != routeName { + t.Errorf("Expected %q, got %q.", routeName, retName) + } +} + +func TestSubRouting(t *testing.T) { + // Example from docs. + router := NewRouter() + subrouter := router.NewRoute().Host("www.example.com").Subrouter() + route := subrouter.NewRoute().Path("/products/").Name("products") + + url := "http://www.example.com/products/" + request, _ := http.NewRequest("GET", url, nil) + var rv RouteMatch + ok := router.Match(request, &rv) + + if !ok || rv.Route != route { + t.Errorf("Expected same route, got %+v.", rv.Route) + } + + u, _ := router.Get("products").URL() + builtURL := u.String() + // Yay, subroute aware of the domain when building! + if builtURL != url { + t.Errorf("Expected %q, got %q.", url, builtURL) + } +} + +func TestVariableNames(t *testing.T) { + route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}") + if route.err == nil { + t.Errorf("Expected error for duplicated variable names") + } +} + +func TestRedirectSlash(t *testing.T) { + var route *Route + var routeMatch RouteMatch + r := NewRouter() + + r.StrictSlash(false) + route = r.NewRoute() + if route.strictSlash != false { + t.Errorf("Expected false redirectSlash.") + } + + r.StrictSlash(true) + route = r.NewRoute() + if route.strictSlash != true { + t.Errorf("Expected true redirectSlash.") + } + + route = new(Route) + route.strictSlash = true + route.Path("/{arg1}/{arg2:[0-9]+}/") + request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil) + routeMatch = RouteMatch{} + _ = route.Match(request, &routeMatch) + vars := routeMatch.Vars + if vars["arg1"] != "foo" { + t.Errorf("Expected foo.") + } + if vars["arg2"] != "123" { + t.Errorf("Expected 123.") + } + rsp := NewRecorder() + routeMatch.Handler.ServeHTTP(rsp, request) + if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" { + t.Errorf("Expected redirect header.") + } + + route = new(Route) + route.strictSlash = true + route.Path("/{arg1}/{arg2:[0-9]+}") + request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil) + routeMatch = RouteMatch{} + _ = route.Match(request, &routeMatch) + vars = routeMatch.Vars + if vars["arg1"] != "foo" { + t.Errorf("Expected foo.") + } + if vars["arg2"] != "123" { + t.Errorf("Expected 123.") + } + rsp = NewRecorder() + routeMatch.Handler.ServeHTTP(rsp, request) + if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" { + t.Errorf("Expected redirect header.") + } +} + +// Test for the new regexp library, still not available in stable Go. +func TestNewRegexp(t *testing.T) { + var p *routeRegexp + var matches []string + + tests := map[string]map[string][]string{ + "/{foo:a{2}}": { + "/a": nil, + "/aa": {"aa"}, + "/aaa": nil, + "/aaaa": nil, + }, + "/{foo:a{2,}}": { + "/a": nil, + "/aa": {"aa"}, + "/aaa": {"aaa"}, + "/aaaa": {"aaaa"}, + }, + "/{foo:a{2,3}}": { + "/a": nil, + "/aa": {"aa"}, + "/aaa": {"aaa"}, + "/aaaa": nil, + }, + "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": { + "/a": nil, + "/ab": nil, + "/abc": nil, + "/abcd": nil, + "/abc/ab": {"abc", "ab"}, + "/abc/abc": nil, + "/abcd/ab": nil, + }, + `/{foo:\w{3,}}/{bar:\d{2,}}`: { + "/a": nil, + "/ab": nil, + "/abc": nil, + "/abc/1": nil, + "/abc/12": {"abc", "12"}, + "/abcd/12": {"abcd", "12"}, + "/abcd/123": {"abcd", "123"}, + }, + } + + for pattern, paths := range tests { + p, _ = newRouteRegexp(pattern, false, false, false, false) + for path, result := range paths { + matches = p.regexp.FindStringSubmatch(path) + if result == nil { + if matches != nil { + t.Errorf("%v should not match %v.", pattern, path) + } + } else { + if len(matches) != len(result)+1 { + t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches)) + } else { + for k, v := range result { + if matches[k+1] != v { + t.Errorf("Expected %v, got %v.", v, matches[k+1]) + } + } + } + } + } + } +} diff --git a/vendor/github.com/gorilla/websocket/bench_test.go b/vendor/github.com/gorilla/websocket/bench_test.go new file mode 100644 index 000000000..f66fc36bc --- /dev/null +++ b/vendor/github.com/gorilla/websocket/bench_test.go @@ -0,0 +1,19 @@ +// Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "testing" +) + +func BenchmarkMaskBytes(b *testing.B) { + var key [4]byte + data := make([]byte, 1024) + pos := 0 + for i := 0; i < b.N; i++ { + pos = maskBytes(key, pos, data) + } + b.SetBytes(int64(len(data))) +} diff --git a/vendor/github.com/gorilla/websocket/client_server_test.go b/vendor/github.com/gorilla/websocket/client_server_test.go new file mode 100644 index 000000000..3f7345dde --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_server_test.go @@ -0,0 +1,451 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "crypto/tls" + "crypto/x509" + "encoding/base64" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "strings" + "testing" + "time" +) + +var cstUpgrader = Upgrader{ + Subprotocols: []string{"p0", "p1"}, + ReadBufferSize: 1024, + WriteBufferSize: 1024, + Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) { + http.Error(w, reason.Error(), status) + }, +} + +var cstDialer = Dialer{ + Subprotocols: []string{"p1", "p2"}, + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +type cstHandler struct{ *testing.T } + +type cstServer struct { + *httptest.Server + URL string +} + +const ( + cstPath = "/a/b" + cstRawQuery = "x=y" + cstRequestURI = cstPath + "?" + cstRawQuery +) + +func newServer(t *testing.T) *cstServer { + var s cstServer + s.Server = httptest.NewServer(cstHandler{t}) + s.Server.URL += cstRequestURI + s.URL = makeWsProto(s.Server.URL) + return &s +} + +func newTLSServer(t *testing.T) *cstServer { + var s cstServer + s.Server = httptest.NewTLSServer(cstHandler{t}) + s.Server.URL += cstRequestURI + s.URL = makeWsProto(s.Server.URL) + return &s +} + +func (t cstHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != cstPath { + t.Logf("path=%v, want %v", r.URL.Path, cstPath) + http.Error(w, "bad path", 400) + return + } + if r.URL.RawQuery != cstRawQuery { + t.Logf("query=%v, want %v", r.URL.RawQuery, cstRawQuery) + http.Error(w, "bad path", 400) + return + } + subprotos := Subprotocols(r) + if !reflect.DeepEqual(subprotos, cstDialer.Subprotocols) { + t.Logf("subprotols=%v, want %v", subprotos, cstDialer.Subprotocols) + http.Error(w, "bad protocol", 400) + return + } + ws, err := cstUpgrader.Upgrade(w, r, http.Header{"Set-Cookie": {"sessionID=1234"}}) + if err != nil { + t.Logf("Upgrade: %v", err) + return + } + defer ws.Close() + + if ws.Subprotocol() != "p1" { + t.Logf("Subprotocol() = %s, want p1", ws.Subprotocol()) + ws.Close() + return + } + op, rd, err := ws.NextReader() + if err != nil { + t.Logf("NextReader: %v", err) + return + } + wr, err := ws.NextWriter(op) + if err != nil { + t.Logf("NextWriter: %v", err) + return + } + if _, err = io.Copy(wr, rd); err != nil { + t.Logf("NextWriter: %v", err) + return + } + if err := wr.Close(); err != nil { + t.Logf("Close: %v", err) + return + } +} + +func makeWsProto(s string) string { + return "ws" + strings.TrimPrefix(s, "http") +} + +func sendRecv(t *testing.T, ws *Conn) { + const message = "Hello World!" + if err := ws.SetWriteDeadline(time.Now().Add(time.Second)); err != nil { + t.Fatalf("SetWriteDeadline: %v", err) + } + if err := ws.WriteMessage(TextMessage, []byte(message)); err != nil { + t.Fatalf("WriteMessage: %v", err) + } + if err := ws.SetReadDeadline(time.Now().Add(time.Second)); err != nil { + t.Fatalf("SetReadDeadline: %v", err) + } + _, p, err := ws.ReadMessage() + if err != nil { + t.Fatalf("ReadMessage: %v", err) + } + if string(p) != message { + t.Fatalf("message=%s, want %s", p, message) + } +} + +func TestProxyDial(t *testing.T) { + + s := newServer(t) + defer s.Close() + + surl, _ := url.Parse(s.URL) + + cstDialer.Proxy = http.ProxyURL(surl) + + connect := false + origHandler := s.Server.Config.Handler + + // Capture the request Host header. + s.Server.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + if r.Method == "CONNECT" { + connect = true + w.WriteHeader(200) + return + } + + if !connect { + t.Log("connect not recieved") + http.Error(w, "connect not recieved", 405) + return + } + origHandler.ServeHTTP(w, r) + }) + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) + + cstDialer.Proxy = http.ProxyFromEnvironment +} + +func TestProxyAuthorizationDial(t *testing.T) { + s := newServer(t) + defer s.Close() + + surl, _ := url.Parse(s.URL) + surl.User = url.UserPassword("username", "password") + cstDialer.Proxy = http.ProxyURL(surl) + + connect := false + origHandler := s.Server.Config.Handler + + // Capture the request Host header. + s.Server.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + proxyAuth := r.Header.Get("Proxy-Authorization") + expectedProxyAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte("username:password")) + if r.Method == "CONNECT" && proxyAuth == expectedProxyAuth { + connect = true + w.WriteHeader(200) + return + } + + if !connect { + t.Log("connect with proxy authorization not recieved") + http.Error(w, "connect with proxy authorization not recieved", 405) + return + } + origHandler.ServeHTTP(w, r) + }) + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) + + cstDialer.Proxy = http.ProxyFromEnvironment +} + +func TestDial(t *testing.T) { + s := newServer(t) + defer s.Close() + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) +} + +func TestDialTLS(t *testing.T) { + s := newTLSServer(t) + defer s.Close() + + certs := x509.NewCertPool() + for _, c := range s.TLS.Certificates { + roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1]) + if err != nil { + t.Fatalf("error parsing server's root cert: %v", err) + } + for _, root := range roots { + certs.AddCert(root) + } + } + + u, _ := url.Parse(s.URL) + d := cstDialer + d.NetDial = func(network, addr string) (net.Conn, error) { return net.Dial(network, u.Host) } + d.TLSClientConfig = &tls.Config{RootCAs: certs} + ws, _, err := d.Dial("wss://example.com"+cstRequestURI, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) +} + +func xTestDialTLSBadCert(t *testing.T) { + // This test is deactivated because of noisy logging from the net/http package. + s := newTLSServer(t) + defer s.Close() + + ws, _, err := cstDialer.Dial(s.URL, nil) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } +} + +func xTestDialTLSNoVerify(t *testing.T) { + s := newTLSServer(t) + defer s.Close() + + d := cstDialer + d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + ws, _, err := d.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) +} + +func TestDialTimeout(t *testing.T) { + s := newServer(t) + defer s.Close() + + d := cstDialer + d.HandshakeTimeout = -1 + ws, _, err := d.Dial(s.URL, nil) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } +} + +func TestDialBadScheme(t *testing.T) { + s := newServer(t) + defer s.Close() + + ws, _, err := cstDialer.Dial(s.Server.URL, nil) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } +} + +func TestDialBadOrigin(t *testing.T) { + s := newServer(t) + defer s.Close() + + ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {"bad"}}) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } + if resp == nil { + t.Fatalf("resp=nil, err=%v", err) + } + if resp.StatusCode != http.StatusForbidden { + t.Fatalf("status=%d, want %d", resp.StatusCode, http.StatusForbidden) + } +} + +func TestDialBadHeader(t *testing.T) { + s := newServer(t) + defer s.Close() + + for _, k := range []string{"Upgrade", + "Connection", + "Sec-Websocket-Key", + "Sec-Websocket-Version", + "Sec-Websocket-Protocol"} { + h := http.Header{} + h.Set(k, "bad") + ws, _, err := cstDialer.Dial(s.URL, http.Header{"Origin": {"bad"}}) + if err == nil { + ws.Close() + t.Errorf("Dial with header %s returned nil", k) + } + } +} + +func TestBadMethod(t *testing.T) { + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ws, err := cstUpgrader.Upgrade(w, r, nil) + if err == nil { + t.Errorf("handshake succeeded, expect fail") + ws.Close() + } + })) + defer s.Close() + + resp, err := http.PostForm(s.URL, url.Values{}) + if err != nil { + t.Fatalf("PostForm returned error %v", err) + } + resp.Body.Close() + if resp.StatusCode != http.StatusMethodNotAllowed { + t.Errorf("Status = %d, want %d", resp.StatusCode, http.StatusMethodNotAllowed) + } +} + +func TestHandshake(t *testing.T) { + s := newServer(t) + defer s.Close() + + ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {s.URL}}) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + + var sessionID string + for _, c := range resp.Cookies() { + if c.Name == "sessionID" { + sessionID = c.Value + } + } + if sessionID != "1234" { + t.Error("Set-Cookie not received from the server.") + } + + if ws.Subprotocol() != "p1" { + t.Errorf("ws.Subprotocol() = %s, want p1", ws.Subprotocol()) + } + sendRecv(t, ws) +} + +func TestRespOnBadHandshake(t *testing.T) { + const expectedStatus = http.StatusGone + const expectedBody = "This is the response body." + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(expectedStatus) + io.WriteString(w, expectedBody) + })) + defer s.Close() + + ws, resp, err := cstDialer.Dial(makeWsProto(s.URL), nil) + if err == nil { + ws.Close() + t.Fatalf("Dial: nil") + } + + if resp == nil { + t.Fatalf("resp=nil, err=%v", err) + } + + if resp.StatusCode != expectedStatus { + t.Errorf("resp.StatusCode=%d, want %d", resp.StatusCode, expectedStatus) + } + + p, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("ReadFull(resp.Body) returned error %v", err) + } + + if string(p) != expectedBody { + t.Errorf("resp.Body=%s, want %s", p, expectedBody) + } +} + +// TestHostHeader confirms that the host header provided in the call to Dial is +// sent to the server. +func TestHostHeader(t *testing.T) { + s := newServer(t) + defer s.Close() + + specifiedHost := make(chan string, 1) + origHandler := s.Server.Config.Handler + + // Capture the request Host header. + s.Server.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + specifiedHost <- r.Host + origHandler.ServeHTTP(w, r) + }) + + ws, _, err := cstDialer.Dial(s.URL, http.Header{"Host": {"testhost"}}) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + + if gotHost := <-specifiedHost; gotHost != "testhost" { + t.Fatalf("gotHost = %q, want \"testhost\"", gotHost) + } + + sendRecv(t, ws) +} diff --git a/vendor/github.com/gorilla/websocket/client_test.go b/vendor/github.com/gorilla/websocket/client_test.go new file mode 100644 index 000000000..7d2b0844f --- /dev/null +++ b/vendor/github.com/gorilla/websocket/client_test.go @@ -0,0 +1,72 @@ +// Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "net/url" + "reflect" + "testing" +) + +var parseURLTests = []struct { + s string + u *url.URL + rui string +}{ + {"ws://example.com/", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"}, + {"ws://example.com", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}, "/"}, + {"ws://example.com:7777/", &url.URL{Scheme: "ws", Host: "example.com:7777", Opaque: "/"}, "/"}, + {"wss://example.com/", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/"}, "/"}, + {"wss://example.com/a/b", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b"}, "/a/b"}, + {"ss://example.com/a/b", nil, ""}, + {"ws://webmaster@example.com/", nil, ""}, + {"wss://example.com/a/b?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b", RawQuery: "x=y"}, "/a/b?x=y"}, + {"wss://example.com?x=y", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/", RawQuery: "x=y"}, "/?x=y"}, +} + +func TestParseURL(t *testing.T) { + for _, tt := range parseURLTests { + u, err := parseURL(tt.s) + if tt.u != nil && err != nil { + t.Errorf("parseURL(%q) returned error %v", tt.s, err) + continue + } + if tt.u == nil { + if err == nil { + t.Errorf("parseURL(%q) did not return error", tt.s) + } + continue + } + if !reflect.DeepEqual(u, tt.u) { + t.Errorf("parseURL(%q) = %v, want %v", tt.s, u, tt.u) + continue + } + if u.RequestURI() != tt.rui { + t.Errorf("parseURL(%q).RequestURI() = %v, want %v", tt.s, u.RequestURI(), tt.rui) + } + } +} + +var hostPortNoPortTests = []struct { + u *url.URL + hostPort, hostNoPort string +}{ + {&url.URL{Scheme: "ws", Host: "example.com"}, "example.com:80", "example.com"}, + {&url.URL{Scheme: "wss", Host: "example.com"}, "example.com:443", "example.com"}, + {&url.URL{Scheme: "ws", Host: "example.com:7777"}, "example.com:7777", "example.com"}, + {&url.URL{Scheme: "wss", Host: "example.com:7777"}, "example.com:7777", "example.com"}, +} + +func TestHostPortNoPort(t *testing.T) { + for _, tt := range hostPortNoPortTests { + hostPort, hostNoPort := hostPortNoPort(tt.u) + if hostPort != tt.hostPort { + t.Errorf("hostPortNoPort(%v) returned hostPort %q, want %q", tt.u, hostPort, tt.hostPort) + } + if hostNoPort != tt.hostNoPort { + t.Errorf("hostPortNoPort(%v) returned hostNoPort %q, want %q", tt.u, hostNoPort, tt.hostNoPort) + } + } +} diff --git a/vendor/github.com/gorilla/websocket/conn_test.go b/vendor/github.com/gorilla/websocket/conn_test.go new file mode 100644 index 000000000..0243c1154 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/conn_test.go @@ -0,0 +1,402 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "reflect" + "testing" + "testing/iotest" + "time" +) + +var _ net.Error = errWriteTimeout + +type fakeNetConn struct { + io.Reader + io.Writer +} + +func (c fakeNetConn) Close() error { return nil } +func (c fakeNetConn) LocalAddr() net.Addr { return nil } +func (c fakeNetConn) RemoteAddr() net.Addr { return nil } +func (c fakeNetConn) SetDeadline(t time.Time) error { return nil } +func (c fakeNetConn) SetReadDeadline(t time.Time) error { return nil } +func (c fakeNetConn) SetWriteDeadline(t time.Time) error { return nil } + +func TestFraming(t *testing.T) { + frameSizes := []int{0, 1, 2, 124, 125, 126, 127, 128, 129, 65534, 65535, 65536, 65537} + var readChunkers = []struct { + name string + f func(io.Reader) io.Reader + }{ + {"half", iotest.HalfReader}, + {"one", iotest.OneByteReader}, + {"asis", func(r io.Reader) io.Reader { return r }}, + } + + writeBuf := make([]byte, 65537) + for i := range writeBuf { + writeBuf[i] = byte(i) + } + + for _, isServer := range []bool{true, false} { + for _, chunker := range readChunkers { + + var connBuf bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024) + rc := newConn(fakeNetConn{Reader: chunker.f(&connBuf), Writer: nil}, !isServer, 1024, 1024) + + for _, n := range frameSizes { + for _, iocopy := range []bool{true, false} { + name := fmt.Sprintf("s:%v, r:%s, n:%d c:%v", isServer, chunker.name, n, iocopy) + + w, err := wc.NextWriter(TextMessage) + if err != nil { + t.Errorf("%s: wc.NextWriter() returned %v", name, err) + continue + } + var nn int + if iocopy { + var n64 int64 + n64, err = io.Copy(w, bytes.NewReader(writeBuf[:n])) + nn = int(n64) + } else { + nn, err = w.Write(writeBuf[:n]) + } + if err != nil || nn != n { + t.Errorf("%s: w.Write(writeBuf[:n]) returned %d, %v", name, nn, err) + continue + } + err = w.Close() + if err != nil { + t.Errorf("%s: w.Close() returned %v", name, err) + continue + } + + opCode, r, err := rc.NextReader() + if err != nil || opCode != TextMessage { + t.Errorf("%s: NextReader() returned %d, r, %v", name, opCode, err) + continue + } + rbuf, err := ioutil.ReadAll(r) + if err != nil { + t.Errorf("%s: ReadFull() returned rbuf, %v", name, err) + continue + } + + if len(rbuf) != n { + t.Errorf("%s: len(rbuf) is %d, want %d", name, len(rbuf), n) + continue + } + + for i, b := range rbuf { + if byte(i) != b { + t.Errorf("%s: bad byte at offset %d", name, i) + break + } + } + } + } + } + } +} + +func TestControl(t *testing.T) { + const message = "this is a ping/pong messsage" + for _, isServer := range []bool{true, false} { + for _, isWriteControl := range []bool{true, false} { + name := fmt.Sprintf("s:%v, wc:%v", isServer, isWriteControl) + var connBuf bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024) + rc := newConn(fakeNetConn{Reader: &connBuf, Writer: nil}, !isServer, 1024, 1024) + if isWriteControl { + wc.WriteControl(PongMessage, []byte(message), time.Now().Add(time.Second)) + } else { + w, err := wc.NextWriter(PongMessage) + if err != nil { + t.Errorf("%s: wc.NextWriter() returned %v", name, err) + continue + } + if _, err := w.Write([]byte(message)); err != nil { + t.Errorf("%s: w.Write() returned %v", name, err) + continue + } + if err := w.Close(); err != nil { + t.Errorf("%s: w.Close() returned %v", name, err) + continue + } + var actualMessage string + rc.SetPongHandler(func(s string) error { actualMessage = s; return nil }) + rc.NextReader() + if actualMessage != message { + t.Errorf("%s: pong=%q, want %q", name, actualMessage, message) + continue + } + } + } + } +} + +func TestCloseBeforeFinalFrame(t *testing.T) { + const bufSize = 512 + + expectedErr := &CloseError{Code: CloseNormalClosure, Text: "hello"} + + var b1, b2 bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize) + rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) + + w, _ := wc.NextWriter(BinaryMessage) + w.Write(make([]byte, bufSize+bufSize/2)) + wc.WriteControl(CloseMessage, FormatCloseMessage(expectedErr.Code, expectedErr.Text), time.Now().Add(10*time.Second)) + w.Close() + + op, r, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("NextReader() returned %d, %v", op, err) + } + _, err = io.Copy(ioutil.Discard, r) + if !reflect.DeepEqual(err, expectedErr) { + t.Fatalf("io.Copy() returned %v, want %v", err, expectedErr) + } + _, _, err = rc.NextReader() + if !reflect.DeepEqual(err, expectedErr) { + t.Fatalf("NextReader() returned %v, want %v", err, expectedErr) + } +} + +func TestEOFWithinFrame(t *testing.T) { + const bufSize = 64 + + for n := 0; ; n++ { + var b bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b}, false, 1024, 1024) + rc := newConn(fakeNetConn{Reader: &b, Writer: nil}, true, 1024, 1024) + + w, _ := wc.NextWriter(BinaryMessage) + w.Write(make([]byte, bufSize)) + w.Close() + + if n >= b.Len() { + break + } + b.Truncate(n) + + op, r, err := rc.NextReader() + if err == errUnexpectedEOF { + continue + } + if op != BinaryMessage || err != nil { + t.Fatalf("%d: NextReader() returned %d, %v", n, op, err) + } + _, err = io.Copy(ioutil.Discard, r) + if err != errUnexpectedEOF { + t.Fatalf("%d: io.Copy() returned %v, want %v", n, err, errUnexpectedEOF) + } + _, _, err = rc.NextReader() + if err != errUnexpectedEOF { + t.Fatalf("%d: NextReader() returned %v, want %v", n, err, errUnexpectedEOF) + } + } +} + +func TestEOFBeforeFinalFrame(t *testing.T) { + const bufSize = 512 + + var b1, b2 bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize) + rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) + + w, _ := wc.NextWriter(BinaryMessage) + w.Write(make([]byte, bufSize+bufSize/2)) + + op, r, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("NextReader() returned %d, %v", op, err) + } + _, err = io.Copy(ioutil.Discard, r) + if err != errUnexpectedEOF { + t.Fatalf("io.Copy() returned %v, want %v", err, errUnexpectedEOF) + } + _, _, err = rc.NextReader() + if err != errUnexpectedEOF { + t.Fatalf("NextReader() returned %v, want %v", err, errUnexpectedEOF) + } +} + +func TestReadLimit(t *testing.T) { + + const readLimit = 512 + message := make([]byte, readLimit+1) + + var b1, b2 bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, readLimit-2) + rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) + rc.SetReadLimit(readLimit) + + // Send message at the limit with interleaved pong. + w, _ := wc.NextWriter(BinaryMessage) + w.Write(message[:readLimit-1]) + wc.WriteControl(PongMessage, []byte("this is a pong"), time.Now().Add(10*time.Second)) + w.Write(message[:1]) + w.Close() + + // Send message larger than the limit. + wc.WriteMessage(BinaryMessage, message[:readLimit+1]) + + op, _, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("1: NextReader() returned %d, %v", op, err) + } + op, r, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("2: NextReader() returned %d, %v", op, err) + } + _, err = io.Copy(ioutil.Discard, r) + if err != ErrReadLimit { + t.Fatalf("io.Copy() returned %v", err) + } +} + +func TestUnderlyingConn(t *testing.T) { + var b1, b2 bytes.Buffer + fc := fakeNetConn{Reader: &b1, Writer: &b2} + c := newConn(fc, true, 1024, 1024) + ul := c.UnderlyingConn() + if ul != fc { + t.Fatalf("Underlying conn is not what it should be.") + } +} + +func TestBufioReadBytes(t *testing.T) { + + // Test calling bufio.ReadBytes for value longer than read buffer size. + + m := make([]byte, 512) + m[len(m)-1] = '\n' + + var b1, b2 bytes.Buffer + wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, len(m)+64, len(m)+64) + rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, len(m)-64, len(m)-64) + + w, _ := wc.NextWriter(BinaryMessage) + w.Write(m) + w.Close() + + op, r, err := rc.NextReader() + if op != BinaryMessage || err != nil { + t.Fatalf("NextReader() returned %d, %v", op, err) + } + + br := bufio.NewReader(r) + p, err := br.ReadBytes('\n') + if err != nil { + t.Fatalf("ReadBytes() returned %v", err) + } + if len(p) != len(m) { + t.Fatalf("read returnd %d bytes, want %d bytes", len(p), len(m)) + } +} + +var closeErrorTests = []struct { + err error + codes []int + ok bool +}{ + {&CloseError{Code: CloseNormalClosure}, []int{CloseNormalClosure}, true}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived}, false}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived, CloseNormalClosure}, true}, + {errors.New("hello"), []int{CloseNormalClosure}, false}, +} + +func TestCloseError(t *testing.T) { + for _, tt := range closeErrorTests { + ok := IsCloseError(tt.err, tt.codes...) + if ok != tt.ok { + t.Errorf("IsCloseError(%#v, %#v) returned %v, want %v", tt.err, tt.codes, ok, tt.ok) + } + } +} + +var unexpectedCloseErrorTests = []struct { + err error + codes []int + ok bool +}{ + {&CloseError{Code: CloseNormalClosure}, []int{CloseNormalClosure}, false}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived}, true}, + {&CloseError{Code: CloseNormalClosure}, []int{CloseNoStatusReceived, CloseNormalClosure}, false}, + {errors.New("hello"), []int{CloseNormalClosure}, false}, +} + +func TestUnexpectedCloseErrors(t *testing.T) { + for _, tt := range unexpectedCloseErrorTests { + ok := IsUnexpectedCloseError(tt.err, tt.codes...) + if ok != tt.ok { + t.Errorf("IsUnexpectedCloseError(%#v, %#v) returned %v, want %v", tt.err, tt.codes, ok, tt.ok) + } + } +} + +type blockingWriter struct { + c1, c2 chan struct{} +} + +func (w blockingWriter) Write(p []byte) (int, error) { + // Allow main to continue + close(w.c1) + // Wait for panic in main + <-w.c2 + return len(p), nil +} + +func TestConcurrentWritePanic(t *testing.T) { + w := blockingWriter{make(chan struct{}), make(chan struct{})} + c := newConn(fakeNetConn{Reader: nil, Writer: w}, false, 1024, 1024) + go func() { + c.WriteMessage(TextMessage, []byte{}) + }() + + // wait for goroutine to block in write. + <-w.c1 + + defer func() { + close(w.c2) + if v := recover(); v != nil { + return + } + }() + + c.WriteMessage(TextMessage, []byte{}) + t.Fatal("should not get here") +} + +type failingReader struct{} + +func (r failingReader) Read(p []byte) (int, error) { + return 0, io.EOF +} + +func TestFailedConnectionReadPanic(t *testing.T) { + c := newConn(fakeNetConn{Reader: failingReader{}, Writer: nil}, false, 1024, 1024) + + defer func() { + if v := recover(); v != nil { + return + } + }() + + for i := 0; i < 20000; i++ { + c.ReadMessage() + } + t.Fatal("should not get here") +} diff --git a/vendor/github.com/gorilla/websocket/example_test.go b/vendor/github.com/gorilla/websocket/example_test.go new file mode 100644 index 000000000..96449eac7 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/example_test.go @@ -0,0 +1,46 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket_test + +import ( + "log" + "net/http" + "testing" + + "github.com/gorilla/websocket" +) + +var ( + c *websocket.Conn + req *http.Request +) + +// The websocket.IsUnexpectedCloseError function is useful for identifying +// application and protocol errors. +// +// This server application works with a client application running in the +// browser. The client application does not explicitly close the websocket. The +// only expected close message from the client has the code +// websocket.CloseGoingAway. All other other close messages are likely the +// result of an application or protocol error and are logged to aid debugging. +func ExampleIsUnexpectedCloseError() { + + for { + messageType, p, err := c.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { + log.Printf("error: %v, user-agent: %v", err, req.Header.Get("User-Agent")) + } + return + } + processMesage(messageType, p) + } +} + +func processMesage(mt int, p []byte) {} + +// TestX prevents godoc from showing this entire file in the example. Remove +// this function when a second example is added. +func TestX(t *testing.T) {} diff --git a/vendor/github.com/gorilla/websocket/examples/autobahn/README.md b/vendor/github.com/gorilla/websocket/examples/autobahn/README.md new file mode 100644 index 000000000..075ac1530 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/autobahn/README.md @@ -0,0 +1,13 @@ +# Test Server + +This package contains a server for the [Autobahn WebSockets Test Suite](http://autobahn.ws/testsuite). + +To test the server, run + + go run server.go + +and start the client test driver + + wstest -m fuzzingclient -s fuzzingclient.json + +When the client completes, it writes a report to reports/clients/index.html. diff --git a/vendor/github.com/gorilla/websocket/examples/autobahn/fuzzingclient.json b/vendor/github.com/gorilla/websocket/examples/autobahn/fuzzingclient.json new file mode 100644 index 000000000..27d5a5b14 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/autobahn/fuzzingclient.json @@ -0,0 +1,14 @@ + +{ + "options": {"failByDrop": false}, + "outdir": "./reports/clients", + "servers": [ + {"agent": "ReadAllWriteMessage", "url": "ws://localhost:9000/m", "options": {"version": 18}}, + {"agent": "ReadAllWrite", "url": "ws://localhost:9000/r", "options": {"version": 18}}, + {"agent": "CopyFull", "url": "ws://localhost:9000/f", "options": {"version": 18}}, + {"agent": "CopyWriterOnly", "url": "ws://localhost:9000/c", "options": {"version": 18}} + ], + "cases": ["*"], + "exclude-cases": [], + "exclude-agent-cases": {} +} diff --git a/vendor/github.com/gorilla/websocket/examples/autobahn/server.go b/vendor/github.com/gorilla/websocket/examples/autobahn/server.go new file mode 100644 index 000000000..d96ac84db --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/autobahn/server.go @@ -0,0 +1,246 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Command server is a test server for the Autobahn WebSockets Test Suite. +package main + +import ( + "errors" + "flag" + "github.com/gorilla/websocket" + "io" + "log" + "net/http" + "time" + "unicode/utf8" +) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 4096, + WriteBufferSize: 4096, + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +// echoCopy echoes messages from the client using io.Copy. +func echoCopy(w http.ResponseWriter, r *http.Request, writerOnly bool) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("Upgrade:", err) + return + } + defer conn.Close() + for { + mt, r, err := conn.NextReader() + if err != nil { + if err != io.EOF { + log.Println("NextReader:", err) + } + return + } + if mt == websocket.TextMessage { + r = &validator{r: r} + } + w, err := conn.NextWriter(mt) + if err != nil { + log.Println("NextWriter:", err) + return + } + if mt == websocket.TextMessage { + r = &validator{r: r} + } + if writerOnly { + _, err = io.Copy(struct{ io.Writer }{w}, r) + } else { + _, err = io.Copy(w, r) + } + if err != nil { + if err == errInvalidUTF8 { + conn.WriteControl(websocket.CloseMessage, + websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""), + time.Time{}) + } + log.Println("Copy:", err) + return + } + err = w.Close() + if err != nil { + log.Println("Close:", err) + return + } + } +} + +func echoCopyWriterOnly(w http.ResponseWriter, r *http.Request) { + echoCopy(w, r, true) +} + +func echoCopyFull(w http.ResponseWriter, r *http.Request) { + echoCopy(w, r, false) +} + +// echoReadAll echoes messages from the client by reading the entire message +// with ioutil.ReadAll. +func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("Upgrade:", err) + return + } + defer conn.Close() + for { + mt, b, err := conn.ReadMessage() + if err != nil { + if err != io.EOF { + log.Println("NextReader:", err) + } + return + } + if mt == websocket.TextMessage { + if !utf8.Valid(b) { + conn.WriteControl(websocket.CloseMessage, + websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""), + time.Time{}) + log.Println("ReadAll: invalid utf8") + } + } + if writeMessage { + err = conn.WriteMessage(mt, b) + if err != nil { + log.Println("WriteMessage:", err) + } + } else { + w, err := conn.NextWriter(mt) + if err != nil { + log.Println("NextWriter:", err) + return + } + if _, err := w.Write(b); err != nil { + log.Println("Writer:", err) + return + } + if err := w.Close(); err != nil { + log.Println("Close:", err) + return + } + } + } +} + +func echoReadAllWriter(w http.ResponseWriter, r *http.Request) { + echoReadAll(w, r, false) +} + +func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) { + echoReadAll(w, r, true) +} + +func serveHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found.", 404) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + io.WriteString(w, "Echo Server") +} + +var addr = flag.String("addr", ":9000", "http service address") + +func main() { + flag.Parse() + http.HandleFunc("/", serveHome) + http.HandleFunc("/c", echoCopyWriterOnly) + http.HandleFunc("/f", echoCopyFull) + http.HandleFunc("/r", echoReadAllWriter) + http.HandleFunc("/m", echoReadAllWriteMessage) + err := http.ListenAndServe(*addr, nil) + if err != nil { + log.Fatal("ListenAndServe: ", err) + } +} + +type validator struct { + state int + x rune + r io.Reader +} + +var errInvalidUTF8 = errors.New("invalid utf8") + +func (r *validator) Read(p []byte) (int, error) { + n, err := r.r.Read(p) + state := r.state + x := r.x + for _, b := range p[:n] { + state, x = decode(state, x, b) + if state == utf8Reject { + break + } + } + r.state = state + r.x = x + if state == utf8Reject || (err == io.EOF && state != utf8Accept) { + return n, errInvalidUTF8 + } + return n, err +} + +// UTF-8 decoder from http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ +// +// Copyright (c) 2008-2009 Bjoern Hoehrmann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +var utf8d = [...]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df + 0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef + 0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8 +} + +const ( + utf8Accept = 0 + utf8Reject = 1 +) + +func decode(state int, x rune, b byte) (int, rune) { + t := utf8d[b] + if state != utf8Accept { + x = rune(b&0x3f) | (x << 6) + } else { + x = rune((0xff >> t) & b) + } + state = int(utf8d[256+state*16+int(t)]) + return state, x +} diff --git a/vendor/github.com/gorilla/websocket/examples/chat/README.md b/vendor/github.com/gorilla/websocket/examples/chat/README.md new file mode 100644 index 000000000..5df3cf1a3 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/README.md @@ -0,0 +1,20 @@ +# Chat Example + +This application shows how to use use the +[websocket](https://github.com/gorilla/websocket) package and +[jQuery](http://jquery.com) to implement a simple web chat application. + +## Running the example + +The example requires a working Go development environment. The [Getting +Started](http://golang.org/doc/install) page describes how to install the +development environment. + +Once you have Go up and running, you can download, build and run the example +using the following commands. + + $ go get github.com/gorilla/websocket + $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/chat` + $ go run *.go + +To use the chat example, open http://localhost:8080/ in your browser. diff --git a/vendor/github.com/gorilla/websocket/examples/chat/conn.go b/vendor/github.com/gorilla/websocket/examples/chat/conn.go new file mode 100644 index 000000000..40fd38c2c --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/conn.go @@ -0,0 +1,105 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "github.com/gorilla/websocket" + "log" + "net/http" + "time" +) + +const ( + // Time allowed to write a message to the peer. + writeWait = 10 * time.Second + + // Time allowed to read the next pong message from the peer. + pongWait = 60 * time.Second + + // Send pings to peer with this period. Must be less than pongWait. + pingPeriod = (pongWait * 9) / 10 + + // Maximum message size allowed from peer. + maxMessageSize = 512 +) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +// connection is an middleman between the websocket connection and the hub. +type connection struct { + // The websocket connection. + ws *websocket.Conn + + // Buffered channel of outbound messages. + send chan []byte +} + +// readPump pumps messages from the websocket connection to the hub. +func (c *connection) readPump() { + defer func() { + h.unregister <- c + c.ws.Close() + }() + c.ws.SetReadLimit(maxMessageSize) + c.ws.SetReadDeadline(time.Now().Add(pongWait)) + c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, message, err := c.ws.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) { + log.Printf("error: %v", err) + } + break + } + h.broadcast <- message + } +} + +// write writes a message with the given message type and payload. +func (c *connection) write(mt int, payload []byte) error { + c.ws.SetWriteDeadline(time.Now().Add(writeWait)) + return c.ws.WriteMessage(mt, payload) +} + +// writePump pumps messages from the hub to the websocket connection. +func (c *connection) writePump() { + ticker := time.NewTicker(pingPeriod) + defer func() { + ticker.Stop() + c.ws.Close() + }() + for { + select { + case message, ok := <-c.send: + if !ok { + c.write(websocket.CloseMessage, []byte{}) + return + } + if err := c.write(websocket.TextMessage, message); err != nil { + return + } + case <-ticker.C: + if err := c.write(websocket.PingMessage, []byte{}); err != nil { + return + } + } + } +} + +// serveWs handles websocket requests from the peer. +func serveWs(w http.ResponseWriter, r *http.Request) { + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println(err) + return + } + c := &connection{send: make(chan []byte, 256), ws: ws} + h.register <- c + go c.writePump() + c.readPump() +} diff --git a/vendor/github.com/gorilla/websocket/examples/chat/home.html b/vendor/github.com/gorilla/websocket/examples/chat/home.html new file mode 100644 index 000000000..29599225c --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/home.html @@ -0,0 +1,92 @@ + + + +Chat Example + + + + + +
+
+ + +
+ + diff --git a/vendor/github.com/gorilla/websocket/examples/chat/hub.go b/vendor/github.com/gorilla/websocket/examples/chat/hub.go new file mode 100644 index 000000000..449ba753d --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/hub.go @@ -0,0 +1,51 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// hub maintains the set of active connections and broadcasts messages to the +// connections. +type hub struct { + // Registered connections. + connections map[*connection]bool + + // Inbound messages from the connections. + broadcast chan []byte + + // Register requests from the connections. + register chan *connection + + // Unregister requests from connections. + unregister chan *connection +} + +var h = hub{ + broadcast: make(chan []byte), + register: make(chan *connection), + unregister: make(chan *connection), + connections: make(map[*connection]bool), +} + +func (h *hub) run() { + for { + select { + case c := <-h.register: + h.connections[c] = true + case c := <-h.unregister: + if _, ok := h.connections[c]; ok { + delete(h.connections, c) + close(c.send) + } + case m := <-h.broadcast: + for c := range h.connections { + select { + case c.send <- m: + default: + close(c.send) + delete(h.connections, c) + } + } + } + } +} diff --git a/vendor/github.com/gorilla/websocket/examples/chat/main.go b/vendor/github.com/gorilla/websocket/examples/chat/main.go new file mode 100644 index 000000000..3c4448d72 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/chat/main.go @@ -0,0 +1,39 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "log" + "net/http" + "text/template" +) + +var addr = flag.String("addr", ":8080", "http service address") +var homeTempl = template.Must(template.ParseFiles("home.html")) + +func serveHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found", 404) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + homeTempl.Execute(w, r.Host) +} + +func main() { + flag.Parse() + go h.run() + http.HandleFunc("/", serveHome) + http.HandleFunc("/ws", serveWs) + err := http.ListenAndServe(*addr, nil) + if err != nil { + log.Fatal("ListenAndServe: ", err) + } +} diff --git a/vendor/github.com/gorilla/websocket/examples/command/README.md b/vendor/github.com/gorilla/websocket/examples/command/README.md new file mode 100644 index 000000000..c30d3979a --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/command/README.md @@ -0,0 +1,19 @@ +# Command example + +This example connects a websocket connection to stdin and stdout of a command. +Received messages are written to stdin followed by a `\n`. Each line read from +from standard out is sent as a message to the client. + + $ go get github.com/gorilla/websocket + $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/command` + $ go run main.go + # Open http://localhost:8080/ . + +Try the following commands. + + # Echo sent messages to the output area. + $ go run main.go cat + + # Run a shell.Try sending "ls" and "cat main.go". + $ go run main.go sh + diff --git a/vendor/github.com/gorilla/websocket/examples/command/home.html b/vendor/github.com/gorilla/websocket/examples/command/home.html new file mode 100644 index 000000000..72fd02b2a --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/command/home.html @@ -0,0 +1,96 @@ + + + +Command Example + + + + + +
+
+ + +
+ + diff --git a/vendor/github.com/gorilla/websocket/examples/command/main.go b/vendor/github.com/gorilla/websocket/examples/command/main.go new file mode 100644 index 000000000..f3f022edb --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/command/main.go @@ -0,0 +1,188 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bufio" + "flag" + "io" + "log" + "net/http" + "os" + "os/exec" + "text/template" + "time" + + "github.com/gorilla/websocket" +) + +var ( + addr = flag.String("addr", "127.0.0.1:8080", "http service address") + cmdPath string + homeTempl = template.Must(template.ParseFiles("home.html")) +) + +const ( + // Time allowed to write a message to the peer. + writeWait = 10 * time.Second + + // Maximum message size allowed from peer. + maxMessageSize = 8192 + + // Time allowed to read the next pong message from the peer. + pongWait = 60 * time.Second + + // Send pings to peer with this period. Must be less than pongWait. + pingPeriod = (pongWait * 9) / 10 +) + +func pumpStdin(ws *websocket.Conn, w io.Writer) { + defer ws.Close() + ws.SetReadLimit(maxMessageSize) + ws.SetReadDeadline(time.Now().Add(pongWait)) + ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, message, err := ws.ReadMessage() + if err != nil { + break + } + message = append(message, '\n') + if _, err := w.Write(message); err != nil { + break + } + } +} + +func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) { + defer func() { + ws.Close() + close(done) + }() + s := bufio.NewScanner(r) + for s.Scan() { + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { + break + } + } + if s.Err() != nil { + log.Println("scan:", s.Err()) + } +} + +func ping(ws *websocket.Conn, done chan struct{}) { + ticker := time.NewTicker(pingPeriod) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil { + log.Println("ping:", err) + } + case <-done: + return + } + } +} + +func internalError(ws *websocket.Conn, msg string, err error) { + log.Println(msg, err) + ws.WriteMessage(websocket.TextMessage, []byte("Internal server error.")) +} + +var upgrader = websocket.Upgrader{} + +func serveWs(w http.ResponseWriter, r *http.Request) { + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("upgrade:", err) + return + } + + defer ws.Close() + + outr, outw, err := os.Pipe() + if err != nil { + internalError(ws, "stdout:", err) + return + } + defer outr.Close() + defer outw.Close() + + inr, inw, err := os.Pipe() + if err != nil { + internalError(ws, "stdin:", err) + return + } + defer inr.Close() + defer inw.Close() + + proc, err := os.StartProcess(cmdPath, flag.Args(), &os.ProcAttr{ + Files: []*os.File{inr, outw, outw}, + }) + if err != nil { + internalError(ws, "start:", err) + return + } + + inr.Close() + outw.Close() + + stdoutDone := make(chan struct{}) + go pumpStdout(ws, outr, stdoutDone) + go ping(ws, stdoutDone) + + pumpStdin(ws, inw) + + // Some commands will exit when stdin is closed. + inw.Close() + + // Other commands need a bonk on the head. + if err := proc.Signal(os.Interrupt); err != nil { + log.Println("inter:", err) + } + + select { + case <-stdoutDone: + case <-time.After(time.Second): + // A bigger bonk on the head. + if err := proc.Signal(os.Kill); err != nil { + log.Println("term:", err) + } + <-stdoutDone + } + + if _, err := proc.Wait(); err != nil { + log.Println("wait:", err) + } +} + +func serveHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found", 404) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + homeTempl.Execute(w, r.Host) +} + +func main() { + flag.Parse() + if len(flag.Args()) < 1 { + log.Fatal("must specify at least one argument") + } + var err error + cmdPath, err = exec.LookPath(flag.Args()[0]) + if err != nil { + log.Fatal(err) + } + http.HandleFunc("/", serveHome) + http.HandleFunc("/ws", serveWs) + log.Fatal(http.ListenAndServe(*addr, nil)) +} diff --git a/vendor/github.com/gorilla/websocket/examples/echo/README.md b/vendor/github.com/gorilla/websocket/examples/echo/README.md new file mode 100644 index 000000000..6ad79ed76 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/echo/README.md @@ -0,0 +1,17 @@ +# Client and server example + +This example shows a simple client and server. + +The server echoes messages sent to it. The client sends a message every second +and prints all messages received. + +To run the example, start the server: + + $ go run server.go + +Next, start the client: + + $ go run client.go + +The server includes a simple web client. To use the client, open +http://127.0.0.1:8080 in the browser and follow the instructions on the page. diff --git a/vendor/github.com/gorilla/websocket/examples/echo/client.go b/vendor/github.com/gorilla/websocket/examples/echo/client.go new file mode 100644 index 000000000..6578094e7 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/echo/client.go @@ -0,0 +1,81 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "flag" + "log" + "net/url" + "os" + "os/signal" + "time" + + "github.com/gorilla/websocket" +) + +var addr = flag.String("addr", "localhost:8080", "http service address") + +func main() { + flag.Parse() + log.SetFlags(0) + + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt) + + u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"} + log.Printf("connecting to %s", u.String()) + + c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + if err != nil { + log.Fatal("dial:", err) + } + defer c.Close() + + done := make(chan struct{}) + + go func() { + defer c.Close() + defer close(done) + for { + _, message, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + return + } + log.Printf("recv: %s", message) + } + }() + + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + for { + select { + case t := <-ticker.C: + err := c.WriteMessage(websocket.TextMessage, []byte(t.String())) + if err != nil { + log.Println("write:", err) + return + } + case <-interrupt: + log.Println("interrupt") + // To cleanly close a connection, a client should send a close + // frame and wait for the server to close the connection. + err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + if err != nil { + log.Println("write close:", err) + return + } + select { + case <-done: + case <-time.After(time.Second): + } + c.Close() + return + } + } +} diff --git a/vendor/github.com/gorilla/websocket/examples/echo/server.go b/vendor/github.com/gorilla/websocket/examples/echo/server.go new file mode 100644 index 000000000..a685b0974 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/echo/server.go @@ -0,0 +1,132 @@ +// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "flag" + "html/template" + "log" + "net/http" + + "github.com/gorilla/websocket" +) + +var addr = flag.String("addr", "localhost:8080", "http service address") + +var upgrader = websocket.Upgrader{} // use default options + +func echo(w http.ResponseWriter, r *http.Request) { + c, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Print("upgrade:", err) + return + } + defer c.Close() + for { + mt, message, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + log.Printf("recv: %s", message) + err = c.WriteMessage(mt, message) + if err != nil { + log.Println("write:", err) + break + } + } +} + +func home(w http.ResponseWriter, r *http.Request) { + homeTemplate.Execute(w, "ws://"+r.Host+"/echo") +} + +func main() { + flag.Parse() + log.SetFlags(0) + http.HandleFunc("/echo", echo) + http.HandleFunc("/", home) + log.Fatal(http.ListenAndServe(*addr, nil)) +} + +var homeTemplate = template.Must(template.New("").Parse(` + + + + + + + +
+

Click "Open" to create a connection to the server, +"Send" to send a message to the server and "Close" to close the connection. +You can change the message and send multiple times. +

+

+ + +

+ +

+
+
+
+ + +`)) diff --git a/vendor/github.com/gorilla/websocket/examples/filewatch/README.md b/vendor/github.com/gorilla/websocket/examples/filewatch/README.md new file mode 100644 index 000000000..ca4931f3b --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/filewatch/README.md @@ -0,0 +1,9 @@ +# File Watch example. + +This example sends a file to the browser client for display whenever the file is modified. + + $ go get github.com/gorilla/websocket + $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/filewatch` + $ go run main.go + # Open http://localhost:8080/ . + # Modify the file to see it update in the browser. diff --git a/vendor/github.com/gorilla/websocket/examples/filewatch/main.go b/vendor/github.com/gorilla/websocket/examples/filewatch/main.go new file mode 100644 index 000000000..2ac2b324f --- /dev/null +++ b/vendor/github.com/gorilla/websocket/examples/filewatch/main.go @@ -0,0 +1,193 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "io/ioutil" + "log" + "net/http" + "os" + "strconv" + "text/template" + "time" + + "github.com/gorilla/websocket" +) + +const ( + // Time allowed to write the file to the client. + writeWait = 10 * time.Second + + // Time allowed to read the next pong message from the client. + pongWait = 60 * time.Second + + // Send pings to client with this period. Must be less than pongWait. + pingPeriod = (pongWait * 9) / 10 + + // Poll file for changes with this period. + filePeriod = 10 * time.Second +) + +var ( + addr = flag.String("addr", ":8080", "http service address") + homeTempl = template.Must(template.New("").Parse(homeHTML)) + filename string + upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + } +) + +func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) { + fi, err := os.Stat(filename) + if err != nil { + return nil, lastMod, err + } + if !fi.ModTime().After(lastMod) { + return nil, lastMod, nil + } + p, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fi.ModTime(), err + } + return p, fi.ModTime(), nil +} + +func reader(ws *websocket.Conn) { + defer ws.Close() + ws.SetReadLimit(512) + ws.SetReadDeadline(time.Now().Add(pongWait)) + ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, _, err := ws.ReadMessage() + if err != nil { + break + } + } +} + +func writer(ws *websocket.Conn, lastMod time.Time) { + lastError := "" + pingTicker := time.NewTicker(pingPeriod) + fileTicker := time.NewTicker(filePeriod) + defer func() { + pingTicker.Stop() + fileTicker.Stop() + ws.Close() + }() + for { + select { + case <-fileTicker.C: + var p []byte + var err error + + p, lastMod, err = readFileIfModified(lastMod) + + if err != nil { + if s := err.Error(); s != lastError { + lastError = s + p = []byte(lastError) + } + } else { + lastError = "" + } + + if p != nil { + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.TextMessage, p); err != nil { + return + } + } + case <-pingTicker.C: + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { + return + } + } + } +} + +func serveWs(w http.ResponseWriter, r *http.Request) { + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + if _, ok := err.(websocket.HandshakeError); !ok { + log.Println(err) + } + return + } + + var lastMod time.Time + if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err == nil { + lastMod = time.Unix(0, n) + } + + go writer(ws, lastMod) + reader(ws) +} + +func serveHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found", 404) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", 405) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + p, lastMod, err := readFileIfModified(time.Time{}) + if err != nil { + p = []byte(err.Error()) + lastMod = time.Unix(0, 0) + } + var v = struct { + Host string + Data string + LastMod string + }{ + r.Host, + string(p), + strconv.FormatInt(lastMod.UnixNano(), 16), + } + homeTempl.Execute(w, &v) +} + +func main() { + flag.Parse() + if flag.NArg() != 1 { + log.Fatal("filename not specified") + } + filename = flag.Args()[0] + http.HandleFunc("/", serveHome) + http.HandleFunc("/ws", serveWs) + if err := http.ListenAndServe(*addr, nil); err != nil { + log.Fatal(err) + } +} + +const homeHTML = ` + + + WebSocket Example + + +
{{.Data}}
+ + + +` diff --git a/vendor/github.com/gorilla/websocket/json_test.go b/vendor/github.com/gorilla/websocket/json_test.go new file mode 100644 index 000000000..61100e481 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/json_test.go @@ -0,0 +1,119 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "encoding/json" + "io" + "reflect" + "testing" +) + +func TestJSON(t *testing.T) { + var buf bytes.Buffer + c := fakeNetConn{&buf, &buf} + wc := newConn(c, true, 1024, 1024) + rc := newConn(c, false, 1024, 1024) + + var actual, expect struct { + A int + B string + } + expect.A = 1 + expect.B = "hello" + + if err := wc.WriteJSON(&expect); err != nil { + t.Fatal("write", err) + } + + if err := rc.ReadJSON(&actual); err != nil { + t.Fatal("read", err) + } + + if !reflect.DeepEqual(&actual, &expect) { + t.Fatal("equal", actual, expect) + } +} + +func TestPartialJSONRead(t *testing.T) { + var buf bytes.Buffer + c := fakeNetConn{&buf, &buf} + wc := newConn(c, true, 1024, 1024) + rc := newConn(c, false, 1024, 1024) + + var v struct { + A int + B string + } + v.A = 1 + v.B = "hello" + + messageCount := 0 + + // Partial JSON values. + + data, err := json.Marshal(v) + if err != nil { + t.Fatal(err) + } + for i := len(data) - 1; i >= 0; i-- { + if err := wc.WriteMessage(TextMessage, data[:i]); err != nil { + t.Fatal(err) + } + messageCount++ + } + + // Whitespace. + + if err := wc.WriteMessage(TextMessage, []byte(" ")); err != nil { + t.Fatal(err) + } + messageCount++ + + // Close. + + if err := wc.WriteMessage(CloseMessage, FormatCloseMessage(CloseNormalClosure, "")); err != nil { + t.Fatal(err) + } + + for i := 0; i < messageCount; i++ { + err := rc.ReadJSON(&v) + if err != io.ErrUnexpectedEOF { + t.Error("read", i, err) + } + } + + err = rc.ReadJSON(&v) + if _, ok := err.(*CloseError); !ok { + t.Error("final", err) + } +} + +func TestDeprecatedJSON(t *testing.T) { + var buf bytes.Buffer + c := fakeNetConn{&buf, &buf} + wc := newConn(c, true, 1024, 1024) + rc := newConn(c, false, 1024, 1024) + + var actual, expect struct { + A int + B string + } + expect.A = 1 + expect.B = "hello" + + if err := WriteJSON(wc, &expect); err != nil { + t.Fatal("write", err) + } + + if err := ReadJSON(rc, &actual); err != nil { + t.Fatal("read", err) + } + + if !reflect.DeepEqual(&actual, &expect) { + t.Fatal("equal", actual, expect) + } +} diff --git a/vendor/github.com/gorilla/websocket/server_test.go b/vendor/github.com/gorilla/websocket/server_test.go new file mode 100644 index 000000000..0a28141d6 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/server_test.go @@ -0,0 +1,51 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "net/http" + "reflect" + "testing" +) + +var subprotocolTests = []struct { + h string + protocols []string +}{ + {"", nil}, + {"foo", []string{"foo"}}, + {"foo,bar", []string{"foo", "bar"}}, + {"foo, bar", []string{"foo", "bar"}}, + {" foo, bar", []string{"foo", "bar"}}, + {" foo, bar ", []string{"foo", "bar"}}, +} + +func TestSubprotocols(t *testing.T) { + for _, st := range subprotocolTests { + r := http.Request{Header: http.Header{"Sec-Websocket-Protocol": {st.h}}} + protocols := Subprotocols(&r) + if !reflect.DeepEqual(st.protocols, protocols) { + t.Errorf("SubProtocols(%q) returned %#v, want %#v", st.h, protocols, st.protocols) + } + } +} + +var isWebSocketUpgradeTests = []struct { + ok bool + h http.Header +}{ + {false, http.Header{"Upgrade": {"websocket"}}}, + {false, http.Header{"Connection": {"upgrade"}}}, + {true, http.Header{"Connection": {"upgRade"}, "Upgrade": {"WebSocket"}}}, +} + +func TestIsWebSocketUpgrade(t *testing.T) { + for _, tt := range isWebSocketUpgradeTests { + ok := IsWebSocketUpgrade(&http.Request{Header: tt.h}) + if tt.ok != ok { + t.Errorf("IsWebSocketUpgrade(%v) returned %v, want %v", tt.h, ok, tt.ok) + } + } +} diff --git a/vendor/github.com/gorilla/websocket/util_test.go b/vendor/github.com/gorilla/websocket/util_test.go new file mode 100644 index 000000000..91f70ceb0 --- /dev/null +++ b/vendor/github.com/gorilla/websocket/util_test.go @@ -0,0 +1,34 @@ +// Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "net/http" + "testing" +) + +var tokenListContainsValueTests = []struct { + value string + ok bool +}{ + {"WebSocket", true}, + {"WEBSOCKET", true}, + {"websocket", true}, + {"websockets", false}, + {"x websocket", false}, + {"websocket x", false}, + {"other,websocket,more", true}, + {"other, websocket, more", true}, +} + +func TestTokenListContainsValue(t *testing.T) { + for _, tt := range tokenListContainsValueTests { + h := http.Header{"Upgrade": {tt.value}} + ok := tokenListContainsValue(h, "Upgrade", "websocket") + if ok != tt.ok { + t.Errorf("tokenListContainsValue(h, n, %q) = %v, want %v", tt.value, ok, tt.ok) + } + } +} diff --git a/vendor/github.com/lib/pq/.travis.yml b/vendor/github.com/lib/pq/.travis.yml index 567c7c666..d63afcb9f 100644 --- a/vendor/github.com/lib/pq/.travis.yml +++ b/vendor/github.com/lib/pq/.travis.yml @@ -37,6 +37,7 @@ before_install: - sudo cat /etc/postgresql/$PGVERSION/main/postgresql.conf - sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key - sudo /etc/init.d/postgresql restart + - go get golang.org/x/tools/cmd/goimports env: global: @@ -59,7 +60,7 @@ env: - PGVERSION=9.0 PQTEST_BINARY_PARAMETERS=no script: - - go test -v ./... + - result=$(goimports -d -e $(find -name \*.go)); test -z "$result" || (echo "$result" && false) && go vet ./... && go test -v ./... before_script: - psql -c 'create database pqgotest' -U postgres diff --git a/vendor/github.com/lib/pq/README.md b/vendor/github.com/lib/pq/README.md index b4e3f45cb..148451e80 100644 --- a/vendor/github.com/lib/pq/README.md +++ b/vendor/github.com/lib/pq/README.md @@ -20,11 +20,11 @@ variables. Example: - PGHOST=/var/run/postgresql go test github.com/lib/pq + PGHOST=/run/postgresql go test github.com/lib/pq Optionally, a benchmark suite can be run as part of the tests: - PGHOST=/var/run/postgresql go test -bench . + PGHOST=/run/postgresql go test -bench . ## Features diff --git a/vendor/github.com/lib/pq/bench_test.go b/vendor/github.com/lib/pq/bench_test.go new file mode 100644 index 000000000..e71f41d06 --- /dev/null +++ b/vendor/github.com/lib/pq/bench_test.go @@ -0,0 +1,435 @@ +// +build go1.1 + +package pq + +import ( + "bufio" + "bytes" + "database/sql" + "database/sql/driver" + "io" + "math/rand" + "net" + "runtime" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/lib/pq/oid" +) + +var ( + selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'" + selectSeriesQuery = "SELECT generate_series(1, 100)" +) + +func BenchmarkSelectString(b *testing.B) { + var result string + benchQuery(b, selectStringQuery, &result) +} + +func BenchmarkSelectSeries(b *testing.B) { + var result int + benchQuery(b, selectSeriesQuery, &result) +} + +func benchQuery(b *testing.B, query string, result interface{}) { + b.StopTimer() + db := openTestConn(b) + defer db.Close() + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchQueryLoop(b, db, query, result) + } +} + +func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) { + rows, err := db.Query(query) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + for rows.Next() { + err = rows.Scan(result) + if err != nil { + b.Fatal("failed to scan", err) + } + } +} + +// reading from circularConn yields content[:prefixLen] once, followed by +// content[prefixLen:] over and over again. It never returns EOF. +type circularConn struct { + content string + prefixLen int + pos int + net.Conn // for all other net.Conn methods that will never be called +} + +func (r *circularConn) Read(b []byte) (n int, err error) { + n = copy(b, r.content[r.pos:]) + r.pos += n + if r.pos >= len(r.content) { + r.pos = r.prefixLen + } + return +} + +func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil } + +func (r *circularConn) Close() error { return nil } + +func fakeConn(content string, prefixLen int) *conn { + c := &circularConn{content: content, prefixLen: prefixLen} + return &conn{buf: bufio.NewReader(c), c: c} +} + +// This benchmark is meant to be the same as BenchmarkSelectString, but takes +// out some of the factors this package can't control. The numbers are less noisy, +// but also the costs of network communication aren't accurately represented. +func BenchmarkMockSelectString(b *testing.B) { + b.StopTimer() + // taken from a recorded run of BenchmarkSelectString + // See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html + const response = "1\x00\x00\x00\x04" + + "t\x00\x00\x00\x06\x00\x00" + + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + + "Z\x00\x00\x00\x05I" + + "2\x00\x00\x00\x04" + + "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + + "C\x00\x00\x00\rSELECT 1\x00" + + "Z\x00\x00\x00\x05I" + + "3\x00\x00\x00\x04" + + "Z\x00\x00\x00\x05I" + c := fakeConn(response, 0) + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchMockQuery(b, c, selectStringQuery) + } +} + +var seriesRowData = func() string { + var buf bytes.Buffer + for i := 1; i <= 100; i++ { + digits := byte(2) + if i >= 100 { + digits = 3 + } else if i < 10 { + digits = 1 + } + buf.WriteString("D\x00\x00\x00") + buf.WriteByte(10 + digits) + buf.WriteString("\x00\x01\x00\x00\x00") + buf.WriteByte(digits) + buf.WriteString(strconv.Itoa(i)) + } + return buf.String() +}() + +func BenchmarkMockSelectSeries(b *testing.B) { + b.StopTimer() + var response = "1\x00\x00\x00\x04" + + "t\x00\x00\x00\x06\x00\x00" + + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + + "Z\x00\x00\x00\x05I" + + "2\x00\x00\x00\x04" + + seriesRowData + + "C\x00\x00\x00\x0fSELECT 100\x00" + + "Z\x00\x00\x00\x05I" + + "3\x00\x00\x00\x04" + + "Z\x00\x00\x00\x05I" + c := fakeConn(response, 0) + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchMockQuery(b, c, selectSeriesQuery) + } +} + +func benchMockQuery(b *testing.B, c *conn, query string) { + stmt, err := c.Prepare(query) + if err != nil { + b.Fatal(err) + } + defer stmt.Close() + rows, err := stmt.Query(nil) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + var dest [1]driver.Value + for { + if err := rows.Next(dest[:]); err != nil { + if err == io.EOF { + break + } + b.Fatal(err) + } + } +} + +func BenchmarkPreparedSelectString(b *testing.B) { + var result string + benchPreparedQuery(b, selectStringQuery, &result) +} + +func BenchmarkPreparedSelectSeries(b *testing.B) { + var result int + benchPreparedQuery(b, selectSeriesQuery, &result) +} + +func benchPreparedQuery(b *testing.B, query string, result interface{}) { + b.StopTimer() + db := openTestConn(b) + defer db.Close() + stmt, err := db.Prepare(query) + if err != nil { + b.Fatal(err) + } + defer stmt.Close() + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchPreparedQueryLoop(b, db, stmt, result) + } +} + +func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) { + rows, err := stmt.Query() + if err != nil { + b.Fatal(err) + } + if !rows.Next() { + rows.Close() + b.Fatal("no rows") + } + defer rows.Close() + for rows.Next() { + err = rows.Scan(&result) + if err != nil { + b.Fatal("failed to scan") + } + } +} + +// See the comment for BenchmarkMockSelectString. +func BenchmarkMockPreparedSelectString(b *testing.B) { + b.StopTimer() + const parseResponse = "1\x00\x00\x00\x04" + + "t\x00\x00\x00\x06\x00\x00" + + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + + "Z\x00\x00\x00\x05I" + const responses = parseResponse + + "2\x00\x00\x00\x04" + + "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + + "C\x00\x00\x00\rSELECT 1\x00" + + "Z\x00\x00\x00\x05I" + c := fakeConn(responses, len(parseResponse)) + + stmt, err := c.Prepare(selectStringQuery) + if err != nil { + b.Fatal(err) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchPreparedMockQuery(b, c, stmt) + } +} + +func BenchmarkMockPreparedSelectSeries(b *testing.B) { + b.StopTimer() + const parseResponse = "1\x00\x00\x00\x04" + + "t\x00\x00\x00\x06\x00\x00" + + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + + "Z\x00\x00\x00\x05I" + var responses = parseResponse + + "2\x00\x00\x00\x04" + + seriesRowData + + "C\x00\x00\x00\x0fSELECT 100\x00" + + "Z\x00\x00\x00\x05I" + c := fakeConn(responses, len(parseResponse)) + + stmt, err := c.Prepare(selectSeriesQuery) + if err != nil { + b.Fatal(err) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + benchPreparedMockQuery(b, c, stmt) + } +} + +func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) { + rows, err := stmt.Query(nil) + if err != nil { + b.Fatal(err) + } + defer rows.Close() + var dest [1]driver.Value + for { + if err := rows.Next(dest[:]); err != nil { + if err == io.EOF { + break + } + b.Fatal(err) + } + } +} + +func BenchmarkEncodeInt64(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{}, int64(1234), oid.T_int8) + } +} + +func BenchmarkEncodeFloat64(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{}, 3.14159, oid.T_float8) + } +} + +var testByteString = []byte("abcdefghijklmnopqrstuvwxyz") + +func BenchmarkEncodeByteaHex(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{serverVersion: 90000}, testByteString, oid.T_bytea) + } +} +func BenchmarkEncodeByteaEscape(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{serverVersion: 84000}, testByteString, oid.T_bytea) + } +} + +func BenchmarkEncodeBool(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{}, true, oid.T_bool) + } +} + +var testTimestamptz = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.Local) + +func BenchmarkEncodeTimestamptz(b *testing.B) { + for i := 0; i < b.N; i++ { + encode(¶meterStatus{}, testTimestamptz, oid.T_timestamptz) + } +} + +var testIntBytes = []byte("1234") + +func BenchmarkDecodeInt64(b *testing.B) { + for i := 0; i < b.N; i++ { + decode(¶meterStatus{}, testIntBytes, oid.T_int8, formatText) + } +} + +var testFloatBytes = []byte("3.14159") + +func BenchmarkDecodeFloat64(b *testing.B) { + for i := 0; i < b.N; i++ { + decode(¶meterStatus{}, testFloatBytes, oid.T_float8, formatText) + } +} + +var testBoolBytes = []byte{'t'} + +func BenchmarkDecodeBool(b *testing.B) { + for i := 0; i < b.N; i++ { + decode(¶meterStatus{}, testBoolBytes, oid.T_bool, formatText) + } +} + +func TestDecodeBool(t *testing.T) { + db := openTestConn(t) + rows, err := db.Query("select true") + if err != nil { + t.Fatal(err) + } + rows.Close() +} + +var testTimestamptzBytes = []byte("2013-09-17 22:15:32.360754-07") + +func BenchmarkDecodeTimestamptz(b *testing.B) { + for i := 0; i < b.N; i++ { + decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText) + } +} + +func BenchmarkDecodeTimestamptzMultiThread(b *testing.B) { + oldProcs := runtime.GOMAXPROCS(0) + defer runtime.GOMAXPROCS(oldProcs) + runtime.GOMAXPROCS(runtime.NumCPU()) + globalLocationCache = newLocationCache() + + f := func(wg *sync.WaitGroup, loops int) { + defer wg.Done() + for i := 0; i < loops; i++ { + decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText) + } + } + + wg := &sync.WaitGroup{} + b.ResetTimer() + for j := 0; j < 10; j++ { + wg.Add(1) + go f(wg, b.N/10) + } + wg.Wait() +} + +func BenchmarkLocationCache(b *testing.B) { + globalLocationCache = newLocationCache() + for i := 0; i < b.N; i++ { + globalLocationCache.getLocation(rand.Intn(10000)) + } +} + +func BenchmarkLocationCacheMultiThread(b *testing.B) { + oldProcs := runtime.GOMAXPROCS(0) + defer runtime.GOMAXPROCS(oldProcs) + runtime.GOMAXPROCS(runtime.NumCPU()) + globalLocationCache = newLocationCache() + + f := func(wg *sync.WaitGroup, loops int) { + defer wg.Done() + for i := 0; i < loops; i++ { + globalLocationCache.getLocation(rand.Intn(10000)) + } + } + + wg := &sync.WaitGroup{} + b.ResetTimer() + for j := 0; j < 10; j++ { + wg.Add(1) + go f(wg, b.N/10) + } + wg.Wait() +} + +// Stress test the performance of parsing results from the wire. +func BenchmarkResultParsing(b *testing.B) { + b.StopTimer() + + db := openTestConn(b) + defer db.Close() + _, err := db.Exec("BEGIN") + if err != nil { + b.Fatal(err) + } + + b.StartTimer() + for i := 0; i < b.N; i++ { + res, err := db.Query("SELECT generate_series(1, 50000)") + if err != nil { + b.Fatal(err) + } + res.Close() + } +} diff --git a/vendor/github.com/lib/pq/certs/README b/vendor/github.com/lib/pq/certs/README new file mode 100644 index 000000000..24ab7b256 --- /dev/null +++ b/vendor/github.com/lib/pq/certs/README @@ -0,0 +1,3 @@ +This directory contains certificates and private keys for testing some +SSL-related functionality in Travis. Do NOT use these certificates for +anything other than testing. diff --git a/vendor/github.com/lib/pq/certs/postgresql.crt b/vendor/github.com/lib/pq/certs/postgresql.crt new file mode 100644 index 000000000..6e6b4284a --- /dev/null +++ b/vendor/github.com/lib/pq/certs/postgresql.crt @@ -0,0 +1,69 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pq CA + Validity + Not Before: Oct 11 15:10:11 2014 GMT + Not After : Oct 8 15:10:11 2024 GMT + Subject: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pqgosslcert + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:e3:8c:06:9a:70:54:51:d1:34:34:83:39:cd:a2: + 59:0f:05:ed:8d:d8:0e:34:d0:92:f4:09:4d:ee:8c: + 78:55:49:24:f8:3c:e0:34:58:02:b2:e7:94:58:c1: + e8:e5:bb:d1:af:f6:54:c1:40:b1:90:70:79:0d:35: + 54:9c:8f:16:e9:c2:f0:92:e6:64:49:38:c1:76:f8: + 47:66:c4:5b:4a:b6:a9:43:ce:c8:be:6c:4d:2b:94: + 97:3c:55:bc:d1:d0:6e:b7:53:ae:89:5c:4b:6b:86: + 40:be:c1:ae:1e:64:ce:9c:ae:87:0a:69:e5:c8:21: + 12:be:ae:1d:f6:45:df:16:a7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 9B:25:31:63:A2:D8:06:FF:CB:E3:E9:96:FF:0D:BA:DC:12:7D:04:CF + X509v3 Authority Key Identifier: + keyid:52:93:ED:1E:76:0A:9F:65:4F:DE:19:66:C1:D5:22:40:35:CB:A0:72 + + X509v3 Basic Constraints: + CA:FALSE + X509v3 Key Usage: + Digital Signature, Non Repudiation, Key Encipherment + Signature Algorithm: sha256WithRSAEncryption + 3e:f5:f8:0b:4e:11:bd:00:86:1f:ce:dc:97:02:98:91:11:f5: + 65:f6:f2:8a:b2:3e:47:92:05:69:28:c9:e9:b4:f7:cf:93:d1: + 2d:81:5d:00:3c:23:be:da:70:ea:59:e1:2c:d3:25:49:ae:a6: + 95:54:c1:10:df:23:e3:fe:d6:e4:76:c7:6b:73:ad:1b:34:7c: + e2:56:cc:c0:37:ae:c5:7a:11:20:6c:3d:05:0e:99:cd:22:6c: + cf:59:a1:da:28:d4:65:ba:7d:2f:2b:3d:69:6d:a6:c1:ae:57: + bf:56:64:13:79:f8:48:46:65:eb:81:67:28:0b:7b:de:47:10: + b3:80:3c:31:d1:58:94:01:51:4a:c7:c8:1a:01:a8:af:c4:cd: + bb:84:a5:d9:8b:b4:b9:a1:64:3e:95:d9:90:1d:d5:3f:67:cc: + 3b:ba:f5:b4:d1:33:77:ee:c2:d2:3e:7e:c5:66:6e:b7:35:4c: + 60:57:b0:b8:be:36:c8:f3:d3:95:8c:28:4a:c9:f7:27:a4:0d: + e5:96:99:eb:f5:c8:bd:f3:84:6d:ef:02:f9:8a:36:7d:6b:5f: + 36:68:37:41:d9:74:ae:c6:78:2e:44:86:a1:ad:43:ca:fb:b5: + 3e:ba:10:23:09:02:ac:62:d1:d0:83:c8:95:b9:e3:5e:30:ff: + 5b:2b:38:fa +-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIBAjANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEP +MA0GA1UECBMGTmV2YWRhMRIwEAYDVQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdp +dGh1Yi5jb20vbGliL3BxMQ4wDAYDVQQDEwVwcSBDQTAeFw0xNDEwMTExNTEwMTFa +Fw0yNDEwMDgxNTEwMTFaMGQxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGEx +EjAQBgNVBAcTCUxhcyBWZWdhczEaMBgGA1UEChMRZ2l0aHViLmNvbS9saWIvcHEx +FDASBgNVBAMTC3BxZ29zc2xjZXJ0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQDjjAaacFRR0TQ0gznNolkPBe2N2A400JL0CU3ujHhVSST4POA0WAKy55RYwejl +u9Gv9lTBQLGQcHkNNVScjxbpwvCS5mRJOMF2+EdmxFtKtqlDzsi+bE0rlJc8VbzR +0G63U66JXEtrhkC+wa4eZM6crocKaeXIIRK+rh32Rd8WpwIDAQABo1owWDAdBgNV +HQ4EFgQUmyUxY6LYBv/L4+mW/w263BJ9BM8wHwYDVR0jBBgwFoAUUpPtHnYKn2VP +3hlmwdUiQDXLoHIwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL +BQADggEBAD71+AtOEb0Ahh/O3JcCmJER9WX28oqyPkeSBWkoyem098+T0S2BXQA8 +I77acOpZ4SzTJUmuppVUwRDfI+P+1uR2x2tzrRs0fOJWzMA3rsV6ESBsPQUOmc0i +bM9Zodoo1GW6fS8rPWltpsGuV79WZBN5+EhGZeuBZygLe95HELOAPDHRWJQBUUrH +yBoBqK/EzbuEpdmLtLmhZD6V2ZAd1T9nzDu69bTRM3fuwtI+fsVmbrc1TGBXsLi+ +Nsjz05WMKErJ9yekDeWWmev1yL3zhG3vAvmKNn1rXzZoN0HZdK7GeC5EhqGtQ8r7 +tT66ECMJAqxi0dCDyJW5414w/1srOPo= +-----END CERTIFICATE----- diff --git a/vendor/github.com/lib/pq/certs/postgresql.key b/vendor/github.com/lib/pq/certs/postgresql.key new file mode 100644 index 000000000..eb8b20be9 --- /dev/null +++ b/vendor/github.com/lib/pq/certs/postgresql.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDjjAaacFRR0TQ0gznNolkPBe2N2A400JL0CU3ujHhVSST4POA0 +WAKy55RYwejlu9Gv9lTBQLGQcHkNNVScjxbpwvCS5mRJOMF2+EdmxFtKtqlDzsi+ +bE0rlJc8VbzR0G63U66JXEtrhkC+wa4eZM6crocKaeXIIRK+rh32Rd8WpwIDAQAB +AoGAM5dM6/kp9P700i8qjOgRPym96Zoh5nGfz/rIE5z/r36NBkdvIg8OVZfR96nH +b0b9TOMR5lsPp0sI9yivTWvX6qyvLJRWy2vvx17hXK9NxXUNTAm0PYZUTvCtcPeX +RnJpzQKNZQPkFzF0uXBc4CtPK2Vz0+FGvAelrhYAxnw1dIkCQQD+9qaW5QhXjsjb +Nl85CmXgxPmGROcgLQCO+omfrjf9UXrituU9Dz6auym5lDGEdMFnkzfr+wpasEy9 +mf5ZZOhDAkEA5HjXfVGaCtpydOt6hDon/uZsyssCK2lQ7NSuE3vP+sUsYMzIpEoy +t3VWXqKbo+g9KNDTP4WEliqp1aiSIylzzQJANPeqzihQnlgEdD4MdD4rwhFJwVIp +Le8Lcais1KaN7StzOwxB/XhgSibd2TbnPpw+3bSg5n5lvUdo+e62/31OHwJAU1jS +I+F09KikQIr28u3UUWT2IzTT4cpVv1AHAQyV3sG3YsjSGT0IK20eyP9BEBZU2WL0 +7aNjrvR5aHxKc5FXsQJABsFtyGpgI5X4xufkJZVZ+Mklz2n7iXa+XPatMAHFxAtb +EEMt60rngwMjXAzBSC6OYuYogRRAY3UCacNC5VhLYQ== +-----END RSA PRIVATE KEY----- diff --git a/vendor/github.com/lib/pq/certs/root.crt b/vendor/github.com/lib/pq/certs/root.crt new file mode 100644 index 000000000..aecf8f621 --- /dev/null +++ b/vendor/github.com/lib/pq/certs/root.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIJANmheROCdW1NMA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNV +BAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGExEjAQBgNVBAcTCUxhcyBWZWdhczEaMBgG +A1UEChMRZ2l0aHViLmNvbS9saWIvcHExDjAMBgNVBAMTBXBxIENBMB4XDTE0MTAx +MTE1MDQyOVoXDTI0MTAwODE1MDQyOVowXjELMAkGA1UEBhMCVVMxDzANBgNVBAgT +Bk5ldmFkYTESMBAGA1UEBxMJTGFzIFZlZ2FzMRowGAYDVQQKExFnaXRodWIuY29t +L2xpYi9wcTEOMAwGA1UEAxMFcHEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCV4PxP7ShzWBzUCThcKk3qZtOLtHmszQVtbqhvgTpm1kTRtKBdVMu0 +pLAHQ3JgJCnAYgH0iZxVGoMP16T3irdgsdC48+nNTFM2T0cCdkfDURGIhSFN47cb +Pgy306BcDUD2q7ucW33+dlFSRuGVewocoh4BWM/vMtMvvWzdi4Ag/L/jhb+5wZxZ +sWymsadOVSDePEMKOvlCa3EdVwVFV40TVyDb+iWBUivDAYsS2a3KajuJrO6MbZiE +Sp2RCIkZS2zFmzWxVRi9ZhzIZhh7EVF9JAaNC3T52jhGUdlRq3YpBTMnd89iOh74 +6jWXG7wSuPj3haFzyNhmJ0ZUh+2Ynoh1AgMBAAGjgcMwgcAwHQYDVR0OBBYEFFKT +7R52Cp9lT94ZZsHVIkA1y6ByMIGQBgNVHSMEgYgwgYWAFFKT7R52Cp9lT94ZZsHV +IkA1y6ByoWKkYDBeMQswCQYDVQQGEwJVUzEPMA0GA1UECBMGTmV2YWRhMRIwEAYD +VQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdpdGh1Yi5jb20vbGliL3BxMQ4wDAYD +VQQDEwVwcSBDQYIJANmheROCdW1NMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF +BQADggEBAAEhCLWkqJNMI8b4gkbmj5fqQ/4+oO83bZ3w2Oqf6eZ8I8BC4f2NOyE6 +tRUlq5+aU7eqC1cOAvGjO+YHN/bF/DFpwLlzvUSXt+JP/pYcUjL7v+pIvwqec9hD +ndvM4iIbkD/H/OYQ3L+N3W+G1x7AcFIX+bGCb3PzYVQAjxreV6//wgKBosMGFbZo +HPxT9RPMun61SViF04H5TNs0derVn1+5eiiYENeAhJzQNyZoOOUuX1X/Inx9bEPh +C5vFBtSMgIytPgieRJVWAiMLYsfpIAStrHztRAbBs2DU01LmMgRvHdxgFEKinC/d +UHZZQDP+6pT+zADrGhQGXe4eThaO6f0= +-----END CERTIFICATE----- diff --git a/vendor/github.com/lib/pq/certs/server.crt b/vendor/github.com/lib/pq/certs/server.crt new file mode 100644 index 000000000..ddc995a6d --- /dev/null +++ b/vendor/github.com/lib/pq/certs/server.crt @@ -0,0 +1,81 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pq CA + Validity + Not Before: Oct 11 15:05:15 2014 GMT + Not After : Oct 8 15:05:15 2024 GMT + Subject: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=postgres + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:d7:8a:4c:85:fb:17:a5:3c:8f:e0:72:11:29:ce: + 3f:b0:1f:3f:7d:c6:ee:7f:a7:fc:02:2b:35:47:08: + a6:3d:90:df:5c:56:14:94:00:c7:6d:d1:d2:e2:61: + 95:77:b8:e3:a6:66:31:f9:1f:21:7d:62:e1:27:da: + 94:37:61:4a:ea:63:53:a0:61:b8:9c:bb:a5:e2:e7: + b7:a6:d8:0f:05:04:c7:29:e2:ea:49:2b:7f:de:15: + 00:a6:18:70:50:c7:0c:de:9a:f9:5a:96:b0:e1:94: + 06:c6:6d:4a:21:3b:b4:0f:a5:6d:92:86:34:b2:4e: + d7:0e:a7:19:c0:77:0b:7b:87:c8:92:de:42:ff:86: + d2:b7:9a:a4:d4:15:23:ca:ad:a5:69:21:b8:ce:7e: + 66:cb:85:5d:b9:ed:8b:2d:09:8d:94:e4:04:1e:72: + ec:ef:d0:76:90:15:5a:a4:f7:91:4b:e9:ce:4e:9d: + 5d:9a:70:17:9c:d8:e9:73:83:ea:3d:61:99:a6:cd: + ac:91:40:5a:88:77:e5:4e:2a:8e:3d:13:f3:f9:38: + 6f:81:6b:8a:95:ca:0e:07:ab:6f:da:b4:8c:d9:ff: + aa:78:03:aa:c7:c2:cf:6f:64:92:d3:d8:83:d5:af: + f1:23:18:a7:2e:7b:17:0b:e7:7d:f1:fa:a8:41:a3: + 04:57 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + EE:F0:B3:46:DC:C7:09:EB:0E:B6:2F:E5:FE:62:60:45:44:9F:59:CC + X509v3 Authority Key Identifier: + keyid:52:93:ED:1E:76:0A:9F:65:4F:DE:19:66:C1:D5:22:40:35:CB:A0:72 + + X509v3 Basic Constraints: + CA:FALSE + X509v3 Key Usage: + Digital Signature, Non Repudiation, Key Encipherment + Signature Algorithm: sha256WithRSAEncryption + 7e:5a:6e:be:bf:d2:6c:c1:d6:fa:b6:fb:3f:06:53:36:08:87: + 9d:95:b1:39:af:9e:f6:47:38:17:39:da:25:7c:f2:ad:0c:e3: + ab:74:19:ca:fb:8c:a0:50:c0:1d:19:8a:9c:21:ed:0f:3a:d1: + 96:54:2e:10:09:4f:b8:70:f7:2b:99:43:d2:c6:15:bc:3f:24: + 7d:28:39:32:3f:8d:a4:4f:40:75:7f:3e:0d:1c:d1:69:f2:4e: + 98:83:47:97:d2:25:ac:c9:36:86:2f:04:a6:c4:86:c7:c4:00: + 5f:7f:b9:ad:fc:bf:e9:f5:78:d7:82:1a:51:0d:fc:ab:9e:92: + 1d:5f:0c:18:d1:82:e0:14:c9:ce:91:89:71:ff:49:49:ff:35: + bf:7b:44:78:42:c1:d0:66:65:bb:28:2e:60:ca:9b:20:12:a9: + 90:61:b1:96:ec:15:46:c9:37:f7:07:90:8a:89:45:2a:3f:37: + ec:dc:e3:e5:8f:c3:3a:57:80:a5:54:60:0c:e1:b2:26:99:2b: + 40:7e:36:d1:9a:70:02:ec:63:f4:3b:72:ae:81:fb:30:20:6d: + cb:48:46:c6:b5:8f:39:b1:84:05:25:55:8d:f5:62:f6:1b:46: + 2e:da:a3:4c:26:12:44:d7:56:b6:b8:a9:ca:d3:ab:71:45:7c: + 9f:48:6d:1e +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIBATANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEP +MA0GA1UECBMGTmV2YWRhMRIwEAYDVQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdp +dGh1Yi5jb20vbGliL3BxMQ4wDAYDVQQDEwVwcSBDQTAeFw0xNDEwMTExNTA1MTVa +Fw0yNDEwMDgxNTA1MTVaMGExCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGEx +EjAQBgNVBAcTCUxhcyBWZWdhczEaMBgGA1UEChMRZ2l0aHViLmNvbS9saWIvcHEx +ETAPBgNVBAMTCHBvc3RncmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA14pMhfsXpTyP4HIRKc4/sB8/fcbuf6f8Ais1RwimPZDfXFYUlADHbdHS4mGV +d7jjpmYx+R8hfWLhJ9qUN2FK6mNToGG4nLul4ue3ptgPBQTHKeLqSSt/3hUAphhw +UMcM3pr5Wpaw4ZQGxm1KITu0D6VtkoY0sk7XDqcZwHcLe4fIkt5C/4bSt5qk1BUj +yq2laSG4zn5my4Vdue2LLQmNlOQEHnLs79B2kBVapPeRS+nOTp1dmnAXnNjpc4Pq +PWGZps2skUBaiHflTiqOPRPz+ThvgWuKlcoOB6tv2rSM2f+qeAOqx8LPb2SS09iD +1a/xIxinLnsXC+d98fqoQaMEVwIDAQABo1owWDAdBgNVHQ4EFgQU7vCzRtzHCesO +ti/l/mJgRUSfWcwwHwYDVR0jBBgwFoAUUpPtHnYKn2VP3hlmwdUiQDXLoHIwCQYD +VR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQELBQADggEBAH5abr6/0mzB +1vq2+z8GUzYIh52VsTmvnvZHOBc52iV88q0M46t0Gcr7jKBQwB0Zipwh7Q860ZZU +LhAJT7hw9yuZQ9LGFbw/JH0oOTI/jaRPQHV/Pg0c0WnyTpiDR5fSJazJNoYvBKbE +hsfEAF9/ua38v+n1eNeCGlEN/Kuekh1fDBjRguAUyc6RiXH/SUn/Nb97RHhCwdBm +ZbsoLmDKmyASqZBhsZbsFUbJN/cHkIqJRSo/N+zc4+WPwzpXgKVUYAzhsiaZK0B+ +NtGacALsY/Q7cq6B+zAgbctIRsa1jzmxhAUlVY31YvYbRi7ao0wmEkTXVra4qcrT +q3FFfJ9IbR4= +-----END CERTIFICATE----- diff --git a/vendor/github.com/lib/pq/certs/server.key b/vendor/github.com/lib/pq/certs/server.key new file mode 100644 index 000000000..bd7b019b6 --- /dev/null +++ b/vendor/github.com/lib/pq/certs/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA14pMhfsXpTyP4HIRKc4/sB8/fcbuf6f8Ais1RwimPZDfXFYU +lADHbdHS4mGVd7jjpmYx+R8hfWLhJ9qUN2FK6mNToGG4nLul4ue3ptgPBQTHKeLq +SSt/3hUAphhwUMcM3pr5Wpaw4ZQGxm1KITu0D6VtkoY0sk7XDqcZwHcLe4fIkt5C +/4bSt5qk1BUjyq2laSG4zn5my4Vdue2LLQmNlOQEHnLs79B2kBVapPeRS+nOTp1d +mnAXnNjpc4PqPWGZps2skUBaiHflTiqOPRPz+ThvgWuKlcoOB6tv2rSM2f+qeAOq +x8LPb2SS09iD1a/xIxinLnsXC+d98fqoQaMEVwIDAQABAoIBAF3ZoihUhJ82F4+r +Gz4QyDpv4L1reT2sb1aiabhcU8ZK5nbWJG+tRyjSS/i2dNaEcttpdCj9HR/zhgZM +bm0OuAgG58rVwgS80CZUruq++Qs+YVojq8/gWPTiQD4SNhV2Fmx3HkwLgUk3oxuT +SsvdqzGE3okGVrutCIcgy126eA147VPMoej1Bb3fO6npqK0pFPhZfAc0YoqJuM+k +obRm5pAnGUipyLCFXjA9HYPKwYZw2RtfdA3CiImHeanSdqS+ctrC9y8BV40Th7gZ +haXdKUNdjmIxV695QQ1mkGqpKLZFqhzKioGQ2/Ly2d1iaKN9fZltTusu8unepWJ2 +tlT9qMECgYEA9uHaF1t2CqE+AJvWTihHhPIIuLxoOQXYea1qvxfcH/UMtaLKzCNm +lQ5pqCGsPvp+10f36yttO1ZehIvlVNXuJsjt0zJmPtIolNuJY76yeussfQ9jHheB +5uPEzCFlHzxYbBUyqgWaF6W74okRGzEGJXjYSP0yHPPdU4ep2q3bGiUCgYEA34Af +wBSuQSK7uLxArWHvQhyuvi43ZGXls6oRGl+Ysj54s8BP6XGkq9hEJ6G4yxgyV+BR +DUOs5X8/TLT8POuIMYvKTQthQyCk0eLv2FLdESDuuKx0kBVY3s8lK3/z5HhrdOiN +VMNZU+xDKgKc3hN9ypkk8vcZe6EtH7Y14e0rVcsCgYBTgxi8F/M5K0wG9rAqphNz +VFBA9XKn/2M33cKjO5X5tXIEKzpAjaUQvNxexG04rJGljzG8+mar0M6ONahw5yD1 +O7i/XWgazgpuOEkkVYiYbd8RutfDgR4vFVMn3hAP3eDnRtBplRWH9Ec3HTiNIys6 +F8PKBOQjyRZQQC7jyzW3hQKBgACe5HeuFwXLSOYsb6mLmhR+6+VPT4wR1F95W27N +USk9jyxAnngxfpmTkiziABdgS9N+pfr5cyN4BP77ia/Jn6kzkC5Cl9SN5KdIkA3z +vPVtN/x/ThuQU5zaymmig1ThGLtMYggYOslG4LDfLPxY5YKIhle+Y+259twdr2yf +Mf2dAoGAaGv3tWMgnIdGRk6EQL/yb9PKHo7ShN+tKNlGaK7WwzBdKs+Fe8jkgcr7 +pz4Ne887CmxejdISzOCcdT+Zm9Bx6I/uZwWOtDvWpIgIxVX9a9URj/+D1MxTE/y4 +d6H+c89yDY62I2+drMpdjCd3EtCaTlxpTbRS+s1eAHMH7aEkcCE= +-----END RSA PRIVATE KEY----- diff --git a/vendor/github.com/lib/pq/conn_test.go b/vendor/github.com/lib/pq/conn_test.go new file mode 100644 index 000000000..274147c27 --- /dev/null +++ b/vendor/github.com/lib/pq/conn_test.go @@ -0,0 +1,1434 @@ +package pq + +import ( + "database/sql" + "database/sql/driver" + "fmt" + "io" + "os" + "reflect" + "strings" + "testing" + "time" +) + +type Fatalistic interface { + Fatal(args ...interface{}) +} + +func forceBinaryParameters() bool { + bp := os.Getenv("PQTEST_BINARY_PARAMETERS") + if bp == "yes" { + return true + } else if bp == "" || bp == "no" { + return false + } else { + panic("unexpected value for PQTEST_BINARY_PARAMETERS") + } +} + +func openTestConnConninfo(conninfo string) (*sql.DB, error) { + defaultTo := func(envvar string, value string) { + if os.Getenv(envvar) == "" { + os.Setenv(envvar, value) + } + } + defaultTo("PGDATABASE", "pqgotest") + defaultTo("PGSSLMODE", "disable") + defaultTo("PGCONNECT_TIMEOUT", "20") + + if forceBinaryParameters() && + !strings.HasPrefix(conninfo, "postgres://") && + !strings.HasPrefix(conninfo, "postgresql://") { + conninfo = conninfo + " binary_parameters=yes" + } + + return sql.Open("postgres", conninfo) +} + +func openTestConn(t Fatalistic) *sql.DB { + conn, err := openTestConnConninfo("") + if err != nil { + t.Fatal(err) + } + + return conn +} + +func getServerVersion(t *testing.T, db *sql.DB) int { + var version int + err := db.QueryRow("SHOW server_version_num").Scan(&version) + if err != nil { + t.Fatal(err) + } + return version +} + +func TestReconnect(t *testing.T) { + db1 := openTestConn(t) + defer db1.Close() + tx, err := db1.Begin() + if err != nil { + t.Fatal(err) + } + var pid1 int + err = tx.QueryRow("SELECT pg_backend_pid()").Scan(&pid1) + if err != nil { + t.Fatal(err) + } + db2 := openTestConn(t) + defer db2.Close() + _, err = db2.Exec("SELECT pg_terminate_backend($1)", pid1) + if err != nil { + t.Fatal(err) + } + // The rollback will probably "fail" because we just killed + // its connection above + _ = tx.Rollback() + + const expected int = 42 + var result int + err = db1.QueryRow(fmt.Sprintf("SELECT %d", expected)).Scan(&result) + if err != nil { + t.Fatal(err) + } + if result != expected { + t.Errorf("got %v; expected %v", result, expected) + } +} + +func TestCommitInFailedTransaction(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + rows, err := txn.Query("SELECT error") + if err == nil { + rows.Close() + t.Fatal("expected failure") + } + err = txn.Commit() + if err != ErrInFailedTransaction { + t.Fatalf("expected ErrInFailedTransaction; got %#v", err) + } +} + +func TestOpenURL(t *testing.T) { + testURL := func(url string) { + db, err := openTestConnConninfo(url) + if err != nil { + t.Fatal(err) + } + defer db.Close() + // database/sql might not call our Open at all unless we do something with + // the connection + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + txn.Rollback() + } + testURL("postgres://") + testURL("postgresql://") +} + +const pgpass_file = "/tmp/pqgotest_pgpass" + +func TestPgpass(t *testing.T) { + testAssert := func(conninfo string, expected string, reason string) { + conn, err := openTestConnConninfo(conninfo) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + txn, err := conn.Begin() + if err != nil { + if expected != "fail" { + t.Fatalf(reason, err) + } + return + } + rows, err := txn.Query("SELECT USER") + if err != nil { + txn.Rollback() + rows.Close() + if expected != "fail" { + t.Fatalf(reason, err) + } + } else { + if expected != "ok" { + t.Fatalf(reason, err) + } + } + txn.Rollback() + } + testAssert("", "ok", "missing .pgpass, unexpected error %#v") + os.Setenv("PGPASSFILE", pgpass_file) + testAssert("host=/tmp", "fail", ", unexpected error %#v") + os.Remove(pgpass_file) + pgpass, err := os.OpenFile(pgpass_file, os.O_RDWR|os.O_CREATE, 0644) + if err != nil { + t.Fatalf("Unexpected error writing pgpass file %#v", err) + } + _, err = pgpass.WriteString(`# comment +server:5432:some_db:some_user:pass_A +*:5432:some_db:some_user:pass_B +localhost:*:*:*:pass_C +*:*:*:*:pass_fallback +`) + if err != nil { + t.Fatalf("Unexpected error writing pgpass file %#v", err) + } + pgpass.Close() + + assertPassword := func(extra values, expected string) { + o := &values{"host": "localhost", "sslmode": "disable", "connect_timeout": "20", "user": "majid", "port": "5432", "extra_float_digits": "2", "dbname": "pqgotest", "client_encoding": "UTF8", "datestyle": "ISO, MDY"} + for k, v := range extra { + (*o)[k] = v + } + (&conn{}).handlePgpass(*o) + if o.Get("password") != expected { + t.Fatalf("For %v expected %s got %s", extra, expected, o.Get("password")) + } + } + // wrong permissions for the pgpass file means it should be ignored + assertPassword(values{"host": "example.com", "user": "foo"}, "") + // fix the permissions and check if it has taken effect + os.Chmod(pgpass_file, 0600) + assertPassword(values{"host": "server", "dbname": "some_db", "user": "some_user"}, "pass_A") + assertPassword(values{"host": "example.com", "user": "foo"}, "pass_fallback") + assertPassword(values{"host": "example.com", "dbname": "some_db", "user": "some_user"}, "pass_B") + // localhost also matches the default "" and UNIX sockets + assertPassword(values{"host": "", "user": "some_user"}, "pass_C") + assertPassword(values{"host": "/tmp", "user": "some_user"}, "pass_C") + // cleanup + os.Remove(pgpass_file) + os.Setenv("PGPASSFILE", "") +} + +func TestExec(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("CREATE TEMP TABLE temp (a int)") + if err != nil { + t.Fatal(err) + } + + r, err := db.Exec("INSERT INTO temp VALUES (1)") + if err != nil { + t.Fatal(err) + } + + if n, _ := r.RowsAffected(); n != 1 { + t.Fatalf("expected 1 row affected, not %d", n) + } + + r, err = db.Exec("INSERT INTO temp VALUES ($1), ($2), ($3)", 1, 2, 3) + if err != nil { + t.Fatal(err) + } + + if n, _ := r.RowsAffected(); n != 3 { + t.Fatalf("expected 3 rows affected, not %d", n) + } + + // SELECT doesn't send the number of returned rows in the command tag + // before 9.0 + if getServerVersion(t, db) >= 90000 { + r, err = db.Exec("SELECT g FROM generate_series(1, 2) g") + if err != nil { + t.Fatal(err) + } + if n, _ := r.RowsAffected(); n != 2 { + t.Fatalf("expected 2 rows affected, not %d", n) + } + + r, err = db.Exec("SELECT g FROM generate_series(1, $1) g", 3) + if err != nil { + t.Fatal(err) + } + if n, _ := r.RowsAffected(); n != 3 { + t.Fatalf("expected 3 rows affected, not %d", n) + } + } +} + +func TestStatment(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + st, err := db.Prepare("SELECT 1") + if err != nil { + t.Fatal(err) + } + + st1, err := db.Prepare("SELECT 2") + if err != nil { + t.Fatal(err) + } + + r, err := st.Query() + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if !r.Next() { + t.Fatal("expected row") + } + + var i int + err = r.Scan(&i) + if err != nil { + t.Fatal(err) + } + + if i != 1 { + t.Fatalf("expected 1, got %d", i) + } + + // st1 + + r1, err := st1.Query() + if err != nil { + t.Fatal(err) + } + defer r1.Close() + + if !r1.Next() { + if r.Err() != nil { + t.Fatal(r1.Err()) + } + t.Fatal("expected row") + } + + err = r1.Scan(&i) + if err != nil { + t.Fatal(err) + } + + if i != 2 { + t.Fatalf("expected 2, got %d", i) + } +} + +func TestRowsCloseBeforeDone(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + r, err := db.Query("SELECT 1") + if err != nil { + t.Fatal(err) + } + + err = r.Close() + if err != nil { + t.Fatal(err) + } + + if r.Next() { + t.Fatal("unexpected row") + } + + if r.Err() != nil { + t.Fatal(r.Err()) + } +} + +func TestParameterCountMismatch(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + var notused int + err := db.QueryRow("SELECT false", 1).Scan(¬used) + if err == nil { + t.Fatal("expected err") + } + // make sure we clean up correctly + err = db.QueryRow("SELECT 1").Scan(¬used) + if err != nil { + t.Fatal(err) + } + + err = db.QueryRow("SELECT $1").Scan(¬used) + if err == nil { + t.Fatal("expected err") + } + // make sure we clean up correctly + err = db.QueryRow("SELECT 1").Scan(¬used) + if err != nil { + t.Fatal(err) + } +} + +// Test that EmptyQueryResponses are handled correctly. +func TestEmptyQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("") + if err != nil { + t.Fatal(err) + } + rows, err := db.Query("") + if err != nil { + t.Fatal(err) + } + cols, err := rows.Columns() + if err != nil { + t.Fatal(err) + } + if len(cols) != 0 { + t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) + } + if rows.Next() { + t.Fatal("unexpected row") + } + if rows.Err() != nil { + t.Fatal(rows.Err()) + } + + stmt, err := db.Prepare("") + if err != nil { + t.Fatal(err) + } + _, err = stmt.Exec() + if err != nil { + t.Fatal(err) + } + rows, err = stmt.Query() + if err != nil { + t.Fatal(err) + } + cols, err = rows.Columns() + if err != nil { + t.Fatal(err) + } + if len(cols) != 0 { + t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) + } + if rows.Next() { + t.Fatal("unexpected row") + } + if rows.Err() != nil { + t.Fatal(rows.Err()) + } +} + +// Test that rows.Columns() is correct even if there are no result rows. +func TestEmptyResultSetColumns(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + rows, err := db.Query("SELECT 1 AS a, text 'bar' AS bar WHERE FALSE") + if err != nil { + t.Fatal(err) + } + cols, err := rows.Columns() + if err != nil { + t.Fatal(err) + } + if len(cols) != 2 { + t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) + } + if rows.Next() { + t.Fatal("unexpected row") + } + if rows.Err() != nil { + t.Fatal(rows.Err()) + } + if cols[0] != "a" || cols[1] != "bar" { + t.Fatalf("unexpected Columns result %v", cols) + } + + stmt, err := db.Prepare("SELECT $1::int AS a, text 'bar' AS bar WHERE FALSE") + if err != nil { + t.Fatal(err) + } + rows, err = stmt.Query(1) + if err != nil { + t.Fatal(err) + } + cols, err = rows.Columns() + if err != nil { + t.Fatal(err) + } + if len(cols) != 2 { + t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) + } + if rows.Next() { + t.Fatal("unexpected row") + } + if rows.Err() != nil { + t.Fatal(rows.Err()) + } + if cols[0] != "a" || cols[1] != "bar" { + t.Fatalf("unexpected Columns result %v", cols) + } + +} + +func TestEncodeDecode(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + q := ` + SELECT + E'\\000\\001\\002'::bytea, + 'foobar'::text, + NULL::integer, + '2000-1-1 01:02:03.04-7'::timestamptz, + 0::boolean, + 123, + -321, + 3.14::float8 + WHERE + E'\\000\\001\\002'::bytea = $1 + AND 'foobar'::text = $2 + AND $3::integer is NULL + ` + // AND '2000-1-1 12:00:00.000000-7'::timestamp = $3 + + exp1 := []byte{0, 1, 2} + exp2 := "foobar" + + r, err := db.Query(q, exp1, exp2, nil) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if !r.Next() { + if r.Err() != nil { + t.Fatal(r.Err()) + } + t.Fatal("expected row") + } + + var got1 []byte + var got2 string + var got3 = sql.NullInt64{Valid: true} + var got4 time.Time + var got5, got6, got7, got8 interface{} + + err = r.Scan(&got1, &got2, &got3, &got4, &got5, &got6, &got7, &got8) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(exp1, got1) { + t.Errorf("expected %q byte: %q", exp1, got1) + } + + if !reflect.DeepEqual(exp2, got2) { + t.Errorf("expected %q byte: %q", exp2, got2) + } + + if got3.Valid { + t.Fatal("expected invalid") + } + + if got4.Year() != 2000 { + t.Fatal("wrong year") + } + + if got5 != false { + t.Fatalf("expected false, got %q", got5) + } + + if got6 != int64(123) { + t.Fatalf("expected 123, got %d", got6) + } + + if got7 != int64(-321) { + t.Fatalf("expected -321, got %d", got7) + } + + if got8 != float64(3.14) { + t.Fatalf("expected 3.14, got %f", got8) + } +} + +func TestNoData(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + st, err := db.Prepare("SELECT 1 WHERE true = false") + if err != nil { + t.Fatal(err) + } + defer st.Close() + + r, err := st.Query() + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if r.Next() { + if r.Err() != nil { + t.Fatal(r.Err()) + } + t.Fatal("unexpected row") + } + + _, err = db.Query("SELECT * FROM nonexistenttable WHERE age=$1", 20) + if err == nil { + t.Fatal("Should have raised an error on non existent table") + } + + _, err = db.Query("SELECT * FROM nonexistenttable") + if err == nil { + t.Fatal("Should have raised an error on non existent table") + } +} + +func TestErrorDuringStartup(t *testing.T) { + // Don't use the normal connection setup, this is intended to + // blow up in the startup packet from a non-existent user. + db, err := openTestConnConninfo("user=thisuserreallydoesntexist") + if err != nil { + t.Fatal(err) + } + defer db.Close() + + _, err = db.Begin() + if err == nil { + t.Fatal("expected error") + } + + e, ok := err.(*Error) + if !ok { + t.Fatalf("expected Error, got %#v", err) + } else if e.Code.Name() != "invalid_authorization_specification" && e.Code.Name() != "invalid_password" { + t.Fatalf("expected invalid_authorization_specification or invalid_password, got %s (%+v)", e.Code.Name(), err) + } +} + +func TestBadConn(t *testing.T) { + var err error + + cn := conn{} + func() { + defer cn.errRecover(&err) + panic(io.EOF) + }() + if err != driver.ErrBadConn { + t.Fatalf("expected driver.ErrBadConn, got: %#v", err) + } + if !cn.bad { + t.Fatalf("expected cn.bad") + } + + cn = conn{} + func() { + defer cn.errRecover(&err) + e := &Error{Severity: Efatal} + panic(e) + }() + if err != driver.ErrBadConn { + t.Fatalf("expected driver.ErrBadConn, got: %#v", err) + } + if !cn.bad { + t.Fatalf("expected cn.bad") + } +} + +func TestErrorOnExec(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)") + if err != nil { + t.Fatal(err) + } + + _, err = txn.Exec("INSERT INTO foo VALUES (0), (0)") + if err == nil { + t.Fatal("Should have raised error") + } + + e, ok := err.(*Error) + if !ok { + t.Fatalf("expected Error, got %#v", err) + } else if e.Code.Name() != "unique_violation" { + t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err) + } +} + +func TestErrorOnQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)") + if err != nil { + t.Fatal(err) + } + + _, err = txn.Query("INSERT INTO foo VALUES (0), (0)") + if err == nil { + t.Fatal("Should have raised error") + } + + e, ok := err.(*Error) + if !ok { + t.Fatalf("expected Error, got %#v", err) + } else if e.Code.Name() != "unique_violation" { + t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err) + } +} + +func TestErrorOnQueryRowSimpleQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)") + if err != nil { + t.Fatal(err) + } + + var v int + err = txn.QueryRow("INSERT INTO foo VALUES (0), (0)").Scan(&v) + if err == nil { + t.Fatal("Should have raised error") + } + + e, ok := err.(*Error) + if !ok { + t.Fatalf("expected Error, got %#v", err) + } else if e.Code.Name() != "unique_violation" { + t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err) + } +} + +// Test the QueryRow bug workarounds in stmt.exec() and simpleQuery() +func TestQueryRowBugWorkaround(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + // stmt.exec() + _, err := db.Exec("CREATE TEMP TABLE notnulltemp (a varchar(10) not null)") + if err != nil { + t.Fatal(err) + } + + var a string + err = db.QueryRow("INSERT INTO notnulltemp(a) values($1) RETURNING a", nil).Scan(&a) + if err == sql.ErrNoRows { + t.Fatalf("expected constraint violation error; got: %v", err) + } + pge, ok := err.(*Error) + if !ok { + t.Fatalf("expected *Error; got: %#v", err) + } + if pge.Code.Name() != "not_null_violation" { + t.Fatalf("expected not_null_violation; got: %s (%+v)", pge.Code.Name(), err) + } + + // Test workaround in simpleQuery() + tx, err := db.Begin() + if err != nil { + t.Fatalf("unexpected error %s in Begin", err) + } + defer tx.Rollback() + + _, err = tx.Exec("SET LOCAL check_function_bodies TO FALSE") + if err != nil { + t.Fatalf("could not disable check_function_bodies: %s", err) + } + _, err = tx.Exec(` +CREATE OR REPLACE FUNCTION bad_function() +RETURNS integer +-- hack to prevent the function from being inlined +SET check_function_bodies TO TRUE +AS $$ + SELECT text 'bad' +$$ LANGUAGE sql`) + if err != nil { + t.Fatalf("could not create function: %s", err) + } + + err = tx.QueryRow("SELECT * FROM bad_function()").Scan(&a) + if err == nil { + t.Fatalf("expected error") + } + pge, ok = err.(*Error) + if !ok { + t.Fatalf("expected *Error; got: %#v", err) + } + if pge.Code.Name() != "invalid_function_definition" { + t.Fatalf("expected invalid_function_definition; got: %s (%+v)", pge.Code.Name(), err) + } + + err = tx.Rollback() + if err != nil { + t.Fatalf("unexpected error %s in Rollback", err) + } + + // Also test that simpleQuery()'s workaround works when the query fails + // after a row has been received. + rows, err := db.Query(` +select + (select generate_series(1, ss.i)) +from (select gs.i + from generate_series(1, 2) gs(i) + order by gs.i limit 2) ss`) + if err != nil { + t.Fatalf("query failed: %s", err) + } + if !rows.Next() { + t.Fatalf("expected at least one result row; got %s", rows.Err()) + } + var i int + err = rows.Scan(&i) + if err != nil { + t.Fatalf("rows.Scan() failed: %s", err) + } + if i != 1 { + t.Fatalf("unexpected value for i: %d", i) + } + if rows.Next() { + t.Fatalf("unexpected row") + } + pge, ok = rows.Err().(*Error) + if !ok { + t.Fatalf("expected *Error; got: %#v", err) + } + if pge.Code.Name() != "cardinality_violation" { + t.Fatalf("expected cardinality_violation; got: %s (%+v)", pge.Code.Name(), rows.Err()) + } +} + +func TestSimpleQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + r, err := db.Query("select 1") + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if !r.Next() { + t.Fatal("expected row") + } +} + +func TestBindError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("create temp table test (i integer)") + if err != nil { + t.Fatal(err) + } + + _, err = db.Query("select * from test where i=$1", "hhh") + if err == nil { + t.Fatal("expected an error") + } + + // Should not get error here + r, err := db.Query("select * from test where i=$1", 1) + if err != nil { + t.Fatal(err) + } + defer r.Close() +} + +func TestParseErrorInExtendedQuery(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + rows, err := db.Query("PARSE_ERROR $1", 1) + if err == nil { + t.Fatal("expected error") + } + + rows, err = db.Query("SELECT 1") + if err != nil { + t.Fatal(err) + } + rows.Close() +} + +// TestReturning tests that an INSERT query using the RETURNING clause returns a row. +func TestReturning(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("CREATE TEMP TABLE distributors (did integer default 0, dname text)") + if err != nil { + t.Fatal(err) + } + + rows, err := db.Query("INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets') " + + "RETURNING did;") + if err != nil { + t.Fatal(err) + } + if !rows.Next() { + t.Fatal("no rows") + } + var did int + err = rows.Scan(&did) + if err != nil { + t.Fatal(err) + } + if did != 0 { + t.Fatalf("bad value for did: got %d, want %d", did, 0) + } + + if rows.Next() { + t.Fatal("unexpected next row") + } + err = rows.Err() + if err != nil { + t.Fatal(err) + } +} + +func TestIssue186(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + // Exec() a query which returns results + _, err := db.Exec("VALUES (1), (2), (3)") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("VALUES ($1), ($2), ($3)", 1, 2, 3) + if err != nil { + t.Fatal(err) + } + + // Query() a query which doesn't return any results + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + rows, err := txn.Query("CREATE TEMP TABLE foo(f1 int)") + if err != nil { + t.Fatal(err) + } + if err = rows.Close(); err != nil { + t.Fatal(err) + } + + // small trick to get NoData from a parameterized query + _, err = txn.Exec("CREATE RULE nodata AS ON INSERT TO foo DO INSTEAD NOTHING") + if err != nil { + t.Fatal(err) + } + rows, err = txn.Query("INSERT INTO foo VALUES ($1)", 1) + if err != nil { + t.Fatal(err) + } + if err = rows.Close(); err != nil { + t.Fatal(err) + } +} + +func TestIssue196(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + row := db.QueryRow("SELECT float4 '0.10000122' = $1, float8 '35.03554004971999' = $2", + float32(0.10000122), float64(35.03554004971999)) + + var float4match, float8match bool + err := row.Scan(&float4match, &float8match) + if err != nil { + t.Fatal(err) + } + if !float4match { + t.Errorf("Expected float4 fidelity to be maintained; got no match") + } + if !float8match { + t.Errorf("Expected float8 fidelity to be maintained; got no match") + } +} + +// Test that any CommandComplete messages sent before the query results are +// ignored. +func TestIssue282(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + var search_path string + err := db.QueryRow(` + SET LOCAL search_path TO pg_catalog; + SET LOCAL search_path TO pg_catalog; + SHOW search_path`).Scan(&search_path) + if err != nil { + t.Fatal(err) + } + if search_path != "pg_catalog" { + t.Fatalf("unexpected search_path %s", search_path) + } +} + +func TestReadFloatPrecision(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + row := db.QueryRow("SELECT float4 '0.10000122', float8 '35.03554004971999'") + var float4val float32 + var float8val float64 + err := row.Scan(&float4val, &float8val) + if err != nil { + t.Fatal(err) + } + if float4val != float32(0.10000122) { + t.Errorf("Expected float4 fidelity to be maintained; got no match") + } + if float8val != float64(35.03554004971999) { + t.Errorf("Expected float8 fidelity to be maintained; got no match") + } +} + +func TestXactMultiStmt(t *testing.T) { + // minified test case based on bug reports from + // pico303@gmail.com and rangelspam@gmail.com + t.Skip("Skipping failing test") + db := openTestConn(t) + defer db.Close() + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer tx.Commit() + + rows, err := tx.Query("select 1") + if err != nil { + t.Fatal(err) + } + + if rows.Next() { + var val int32 + if err = rows.Scan(&val); err != nil { + t.Fatal(err) + } + } else { + t.Fatal("Expected at least one row in first query in xact") + } + + rows2, err := tx.Query("select 2") + if err != nil { + t.Fatal(err) + } + + if rows2.Next() { + var val2 int32 + if err := rows2.Scan(&val2); err != nil { + t.Fatal(err) + } + } else { + t.Fatal("Expected at least one row in second query in xact") + } + + if err = rows.Err(); err != nil { + t.Fatal(err) + } + + if err = rows2.Err(); err != nil { + t.Fatal(err) + } + + if err = tx.Commit(); err != nil { + t.Fatal(err) + } +} + +var envParseTests = []struct { + Expected map[string]string + Env []string +}{ + { + Env: []string{"PGDATABASE=hello", "PGUSER=goodbye"}, + Expected: map[string]string{"dbname": "hello", "user": "goodbye"}, + }, + { + Env: []string{"PGDATESTYLE=ISO, MDY"}, + Expected: map[string]string{"datestyle": "ISO, MDY"}, + }, + { + Env: []string{"PGCONNECT_TIMEOUT=30"}, + Expected: map[string]string{"connect_timeout": "30"}, + }, +} + +func TestParseEnviron(t *testing.T) { + for i, tt := range envParseTests { + results := parseEnviron(tt.Env) + if !reflect.DeepEqual(tt.Expected, results) { + t.Errorf("%d: Expected: %#v Got: %#v", i, tt.Expected, results) + } + } +} + +func TestParseComplete(t *testing.T) { + tpc := func(commandTag string, command string, affectedRows int64, shouldFail bool) { + defer func() { + if p := recover(); p != nil { + if !shouldFail { + t.Error(p) + } + } + }() + cn := &conn{} + res, c := cn.parseComplete(commandTag) + if c != command { + t.Errorf("Expected %v, got %v", command, c) + } + n, err := res.RowsAffected() + if err != nil { + t.Fatal(err) + } + if n != affectedRows { + t.Errorf("Expected %d, got %d", affectedRows, n) + } + } + + tpc("ALTER TABLE", "ALTER TABLE", 0, false) + tpc("INSERT 0 1", "INSERT", 1, false) + tpc("UPDATE 100", "UPDATE", 100, false) + tpc("SELECT 100", "SELECT", 100, false) + tpc("FETCH 100", "FETCH", 100, false) + // allow COPY (and others) without row count + tpc("COPY", "COPY", 0, false) + // don't fail on command tags we don't recognize + tpc("UNKNOWNCOMMANDTAG", "UNKNOWNCOMMANDTAG", 0, false) + + // failure cases + tpc("INSERT 1", "", 0, true) // missing oid + tpc("UPDATE 0 1", "", 0, true) // too many numbers + tpc("SELECT foo", "", 0, true) // invalid row count +} + +func TestExecerInterface(t *testing.T) { + // Gin up a straw man private struct just for the type check + cn := &conn{c: nil} + var cni interface{} = cn + + _, ok := cni.(driver.Execer) + if !ok { + t.Fatal("Driver doesn't implement Execer") + } +} + +func TestNullAfterNonNull(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + r, err := db.Query("SELECT 9::integer UNION SELECT NULL::integer") + if err != nil { + t.Fatal(err) + } + + var n sql.NullInt64 + + if !r.Next() { + if r.Err() != nil { + t.Fatal(err) + } + t.Fatal("expected row") + } + + if err := r.Scan(&n); err != nil { + t.Fatal(err) + } + + if n.Int64 != 9 { + t.Fatalf("expected 2, not %d", n.Int64) + } + + if !r.Next() { + if r.Err() != nil { + t.Fatal(err) + } + t.Fatal("expected row") + } + + if err := r.Scan(&n); err != nil { + t.Fatal(err) + } + + if n.Valid { + t.Fatal("expected n to be invalid") + } + + if n.Int64 != 0 { + t.Fatalf("expected n to 2, not %d", n.Int64) + } +} + +func Test64BitErrorChecking(t *testing.T) { + defer func() { + if err := recover(); err != nil { + t.Fatal("panic due to 0xFFFFFFFF != -1 " + + "when int is 64 bits") + } + }() + + db := openTestConn(t) + defer db.Close() + + r, err := db.Query(`SELECT * +FROM (VALUES (0::integer, NULL::text), (1, 'test string')) AS t;`) + + if err != nil { + t.Fatal(err) + } + + defer r.Close() + + for r.Next() { + } +} + +func TestCommit(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Exec("CREATE TEMP TABLE temp (a int)") + if err != nil { + t.Fatal(err) + } + sqlInsert := "INSERT INTO temp VALUES (1)" + sqlSelect := "SELECT * FROM temp" + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + _, err = tx.Exec(sqlInsert) + if err != nil { + t.Fatal(err) + } + err = tx.Commit() + if err != nil { + t.Fatal(err) + } + var i int + err = db.QueryRow(sqlSelect).Scan(&i) + if err != nil { + t.Fatal(err) + } + if i != 1 { + t.Fatalf("expected 1, got %d", i) + } +} + +func TestErrorClass(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Query("SELECT int 'notint'") + if err == nil { + t.Fatal("expected error") + } + pge, ok := err.(*Error) + if !ok { + t.Fatalf("expected *pq.Error, got %#+v", err) + } + if pge.Code.Class() != "22" { + t.Fatalf("expected class 28, got %v", pge.Code.Class()) + } + if pge.Code.Class().Name() != "data_exception" { + t.Fatalf("expected data_exception, got %v", pge.Code.Class().Name()) + } +} + +func TestParseOpts(t *testing.T) { + tests := []struct { + in string + expected values + valid bool + }{ + {"dbname=hello user=goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, + {"dbname=hello user=goodbye ", values{"dbname": "hello", "user": "goodbye"}, true}, + {"dbname = hello user=goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, + {"dbname=hello user =goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, + {"dbname=hello user= goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, + {"host=localhost password='correct horse battery staple'", values{"host": "localhost", "password": "correct horse battery staple"}, true}, + {"dbname=データベース password=パスワード", values{"dbname": "データベース", "password": "パスワード"}, true}, + {"dbname=hello user=''", values{"dbname": "hello", "user": ""}, true}, + {"user='' dbname=hello", values{"dbname": "hello", "user": ""}, true}, + // The last option value is an empty string if there's no non-whitespace after its = + {"dbname=hello user= ", values{"dbname": "hello", "user": ""}, true}, + + // The parser ignores spaces after = and interprets the next set of non-whitespace characters as the value. + {"user= password=foo", values{"user": "password=foo"}, true}, + + // Backslash escapes next char + {`user=a\ \'\\b`, values{"user": `a '\b`}, true}, + {`user='a \'b'`, values{"user": `a 'b`}, true}, + + // Incomplete escape + {`user=x\`, values{}, false}, + + // No '=' after the key + {"postgre://marko@internet", values{}, false}, + {"dbname user=goodbye", values{}, false}, + {"user=foo blah", values{}, false}, + {"user=foo blah ", values{}, false}, + + // Unterminated quoted value + {"dbname=hello user='unterminated", values{}, false}, + } + + for _, test := range tests { + o := make(values) + err := parseOpts(test.in, o) + + switch { + case err != nil && test.valid: + t.Errorf("%q got unexpected error: %s", test.in, err) + case err == nil && test.valid && !reflect.DeepEqual(test.expected, o): + t.Errorf("%q got: %#v want: %#v", test.in, o, test.expected) + case err == nil && !test.valid: + t.Errorf("%q expected an error", test.in) + } + } +} + +func TestRuntimeParameters(t *testing.T) { + type RuntimeTestResult int + const ( + ResultUnknown RuntimeTestResult = iota + ResultSuccess + ResultError // other error + ) + + tests := []struct { + conninfo string + param string + expected string + expectedOutcome RuntimeTestResult + }{ + // invalid parameter + {"DOESNOTEXIST=foo", "", "", ResultError}, + // we can only work with a specific value for these two + {"client_encoding=SQL_ASCII", "", "", ResultError}, + {"datestyle='ISO, YDM'", "", "", ResultError}, + // "options" should work exactly as it does in libpq + {"options='-c search_path=pqgotest'", "search_path", "pqgotest", ResultSuccess}, + // pq should override client_encoding in this case + {"options='-c client_encoding=SQL_ASCII'", "client_encoding", "UTF8", ResultSuccess}, + // allow client_encoding to be set explicitly + {"client_encoding=UTF8", "client_encoding", "UTF8", ResultSuccess}, + // test a runtime parameter not supported by libpq + {"work_mem='139kB'", "work_mem", "139kB", ResultSuccess}, + // test fallback_application_name + {"application_name=foo fallback_application_name=bar", "application_name", "foo", ResultSuccess}, + {"application_name='' fallback_application_name=bar", "application_name", "", ResultSuccess}, + {"fallback_application_name=bar", "application_name", "bar", ResultSuccess}, + } + + for _, test := range tests { + db, err := openTestConnConninfo(test.conninfo) + if err != nil { + t.Fatal(err) + } + + // application_name didn't exist before 9.0 + if test.param == "application_name" && getServerVersion(t, db) < 90000 { + db.Close() + continue + } + + tryGetParameterValue := func() (value string, outcome RuntimeTestResult) { + defer db.Close() + row := db.QueryRow("SELECT current_setting($1)", test.param) + err = row.Scan(&value) + if err != nil { + return "", ResultError + } + return value, ResultSuccess + } + + value, outcome := tryGetParameterValue() + if outcome != test.expectedOutcome && outcome == ResultError { + t.Fatalf("%v: unexpected error: %v", test.conninfo, err) + } + if outcome != test.expectedOutcome { + t.Fatalf("unexpected outcome %v (was expecting %v) for conninfo \"%s\"", + outcome, test.expectedOutcome, test.conninfo) + } + if value != test.expected { + t.Fatalf("bad value for %s: got %s, want %s with conninfo \"%s\"", + test.param, value, test.expected, test.conninfo) + } + } +} + +func TestIsUTF8(t *testing.T) { + var cases = []struct { + name string + want bool + }{ + {"unicode", true}, + {"utf-8", true}, + {"utf_8", true}, + {"UTF-8", true}, + {"UTF8", true}, + {"utf8", true}, + {"u n ic_ode", true}, + {"ut_f%8", true}, + {"ubf8", false}, + {"punycode", false}, + } + + for _, test := range cases { + if g := isUTF8(test.name); g != test.want { + t.Errorf("isUTF8(%q) = %v want %v", test.name, g, test.want) + } + } +} + +func TestQuoteIdentifier(t *testing.T) { + var cases = []struct { + input string + want string + }{ + {`foo`, `"foo"`}, + {`foo bar baz`, `"foo bar baz"`}, + {`foo"bar`, `"foo""bar"`}, + {"foo\x00bar", `"foo"`}, + {"\x00foo", `""`}, + } + + for _, test := range cases { + got := QuoteIdentifier(test.input) + if got != test.want { + t.Errorf("QuoteIdentifier(%q) = %v want %v", test.input, got, test.want) + } + } +} diff --git a/vendor/github.com/lib/pq/copy_test.go b/vendor/github.com/lib/pq/copy_test.go new file mode 100644 index 000000000..86745b38f --- /dev/null +++ b/vendor/github.com/lib/pq/copy_test.go @@ -0,0 +1,465 @@ +package pq + +import ( + "bytes" + "database/sql" + "database/sql/driver" + "strings" + "testing" +) + +func TestCopyInStmt(t *testing.T) { + var stmt string + stmt = CopyIn("table name") + if stmt != `COPY "table name" () FROM STDIN` { + t.Fatal(stmt) + } + + stmt = CopyIn("table name", "column 1", "column 2") + if stmt != `COPY "table name" ("column 1", "column 2") FROM STDIN` { + t.Fatal(stmt) + } + + stmt = CopyIn(`table " name """`, `co"lumn""`) + if stmt != `COPY "table "" name """"""" ("co""lumn""""") FROM STDIN` { + t.Fatal(stmt) + } +} + +func TestCopyInSchemaStmt(t *testing.T) { + var stmt string + stmt = CopyInSchema("schema name", "table name") + if stmt != `COPY "schema name"."table name" () FROM STDIN` { + t.Fatal(stmt) + } + + stmt = CopyInSchema("schema name", "table name", "column 1", "column 2") + if stmt != `COPY "schema name"."table name" ("column 1", "column 2") FROM STDIN` { + t.Fatal(stmt) + } + + stmt = CopyInSchema(`schema " name """`, `table " name """`, `co"lumn""`) + if stmt != `COPY "schema "" name """"""".`+ + `"table "" name """"""" ("co""lumn""""") FROM STDIN` { + t.Fatal(stmt) + } +} + +func TestCopyInMultipleValues(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)") + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "a", "b")) + if err != nil { + t.Fatal(err) + } + + longString := strings.Repeat("#", 500) + + for i := 0; i < 500; i++ { + _, err = stmt.Exec(int64(i), longString) + if err != nil { + t.Fatal(err) + } + } + + _, err = stmt.Exec() + if err != nil { + t.Fatal(err) + } + + err = stmt.Close() + if err != nil { + t.Fatal(err) + } + + var num int + err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num) + if err != nil { + t.Fatal(err) + } + + if num != 500 { + t.Fatalf("expected 500 items, not %d", num) + } +} + +func TestCopyInRaiseStmtTrigger(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + if getServerVersion(t, db) < 90000 { + var exists int + err := db.QueryRow("SELECT 1 FROM pg_language WHERE lanname = 'plpgsql'").Scan(&exists) + if err == sql.ErrNoRows { + t.Skip("language PL/PgSQL does not exist; skipping TestCopyInRaiseStmtTrigger") + } else if err != nil { + t.Fatal(err) + } + } + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)") + if err != nil { + t.Fatal(err) + } + + _, err = txn.Exec(` + CREATE OR REPLACE FUNCTION pg_temp.temptest() + RETURNS trigger AS + $BODY$ begin + raise notice 'Hello world'; + return new; + end $BODY$ + LANGUAGE plpgsql`) + if err != nil { + t.Fatal(err) + } + + _, err = txn.Exec(` + CREATE TRIGGER temptest_trigger + BEFORE INSERT + ON temp + FOR EACH ROW + EXECUTE PROCEDURE pg_temp.temptest()`) + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "a", "b")) + if err != nil { + t.Fatal(err) + } + + longString := strings.Repeat("#", 500) + + _, err = stmt.Exec(int64(1), longString) + if err != nil { + t.Fatal(err) + } + + _, err = stmt.Exec() + if err != nil { + t.Fatal(err) + } + + err = stmt.Close() + if err != nil { + t.Fatal(err) + } + + var num int + err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num) + if err != nil { + t.Fatal(err) + } + + if num != 1 { + t.Fatalf("expected 1 items, not %d", num) + } +} + +func TestCopyInTypes(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER, text VARCHAR, blob BYTEA, nothing VARCHAR)") + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "num", "text", "blob", "nothing")) + if err != nil { + t.Fatal(err) + } + + _, err = stmt.Exec(int64(1234567890), "Héllö\n ☃!\r\t\\", []byte{0, 255, 9, 10, 13}, nil) + if err != nil { + t.Fatal(err) + } + + _, err = stmt.Exec() + if err != nil { + t.Fatal(err) + } + + err = stmt.Close() + if err != nil { + t.Fatal(err) + } + + var num int + var text string + var blob []byte + var nothing sql.NullString + + err = txn.QueryRow("SELECT * FROM temp").Scan(&num, &text, &blob, ¬hing) + if err != nil { + t.Fatal(err) + } + + if num != 1234567890 { + t.Fatal("unexpected result", num) + } + if text != "Héllö\n ☃!\r\t\\" { + t.Fatal("unexpected result", text) + } + if bytes.Compare(blob, []byte{0, 255, 9, 10, 13}) != 0 { + t.Fatal("unexpected result", blob) + } + if nothing.Valid { + t.Fatal("unexpected result", nothing.String) + } +} + +func TestCopyInWrongType(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)") + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "num")) + if err != nil { + t.Fatal(err) + } + defer stmt.Close() + + _, err = stmt.Exec("Héllö\n ☃!\r\t\\") + if err != nil { + t.Fatal(err) + } + + _, err = stmt.Exec() + if err == nil { + t.Fatal("expected error") + } + if pge := err.(*Error); pge.Code.Name() != "invalid_text_representation" { + t.Fatalf("expected 'invalid input syntax for integer' error, got %s (%+v)", pge.Code.Name(), pge) + } +} + +func TestCopyOutsideOfTxnError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + _, err := db.Prepare(CopyIn("temp", "num")) + if err == nil { + t.Fatal("COPY outside of transaction did not return an error") + } + if err != errCopyNotSupportedOutsideTxn { + t.Fatalf("expected %s, got %s", err, err.Error()) + } +} + +func TestCopyInBinaryError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)") + if err != nil { + t.Fatal(err) + } + _, err = txn.Prepare("COPY temp (num) FROM STDIN WITH binary") + if err != errBinaryCopyNotSupported { + t.Fatalf("expected %s, got %+v", errBinaryCopyNotSupported, err) + } + // check that the protocol is in a valid state + err = txn.Rollback() + if err != nil { + t.Fatal(err) + } +} + +func TestCopyFromError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)") + if err != nil { + t.Fatal(err) + } + _, err = txn.Prepare("COPY temp (num) TO STDOUT") + if err != errCopyToNotSupported { + t.Fatalf("expected %s, got %+v", errCopyToNotSupported, err) + } + // check that the protocol is in a valid state + err = txn.Rollback() + if err != nil { + t.Fatal(err) + } +} + +func TestCopySyntaxError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Prepare("COPY ") + if err == nil { + t.Fatal("expected error") + } + if pge := err.(*Error); pge.Code.Name() != "syntax_error" { + t.Fatalf("expected syntax error, got %s (%+v)", pge.Code.Name(), pge) + } + // check that the protocol is in a valid state + err = txn.Rollback() + if err != nil { + t.Fatal(err) + } +} + +// Tests for connection errors in copyin.resploop() +func TestCopyRespLoopConnectionError(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + var pid int + err = txn.QueryRow("SELECT pg_backend_pid()").Scan(&pid) + if err != nil { + t.Fatal(err) + } + + _, err = txn.Exec("CREATE TEMP TABLE temp (a int)") + if err != nil { + t.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "a")) + if err != nil { + t.Fatal(err) + } + defer stmt.Close() + + _, err = db.Exec("SELECT pg_terminate_backend($1)", pid) + if err != nil { + t.Fatal(err) + } + + if getServerVersion(t, db) < 90500 { + // We have to try and send something over, since postgres before + // version 9.5 won't process SIGTERMs while it's waiting for + // CopyData/CopyEnd messages; see tcop/postgres.c. + _, err = stmt.Exec(1) + if err != nil { + t.Fatal(err) + } + } + _, err = stmt.Exec() + if err == nil { + t.Fatalf("expected error") + } + pge, ok := err.(*Error) + if !ok { + if err == driver.ErrBadConn { + // likely an EPIPE + } else { + t.Fatalf("expected *pq.Error or driver.ErrBadConn, got %+#v", err) + } + } else if pge.Code.Name() != "admin_shutdown" { + t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name()) + } + + _ = stmt.Close() +} + +func BenchmarkCopyIn(b *testing.B) { + db := openTestConn(b) + defer db.Close() + + txn, err := db.Begin() + if err != nil { + b.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)") + if err != nil { + b.Fatal(err) + } + + stmt, err := txn.Prepare(CopyIn("temp", "a", "b")) + if err != nil { + b.Fatal(err) + } + + for i := 0; i < b.N; i++ { + _, err = stmt.Exec(int64(i), "hello world!") + if err != nil { + b.Fatal(err) + } + } + + _, err = stmt.Exec() + if err != nil { + b.Fatal(err) + } + + err = stmt.Close() + if err != nil { + b.Fatal(err) + } + + var num int + err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num) + if err != nil { + b.Fatal(err) + } + + if num != b.N { + b.Fatalf("expected %d items, not %d", b.N, num) + } +} diff --git a/vendor/github.com/lib/pq/encode.go b/vendor/github.com/lib/pq/encode.go index e52d39b26..6681bd3e7 100644 --- a/vendor/github.com/lib/pq/encode.go +++ b/vendor/github.com/lib/pq/encode.go @@ -23,7 +23,6 @@ func binaryEncode(parameterStatus *parameterStatus, x interface{}) []byte { default: return encode(parameterStatus, x, oid.T_unknown) } - panic("not reached") } func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) []byte { @@ -76,7 +75,7 @@ func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) inter return int64(int16(binary.BigEndian.Uint16(s))) default: - errorf("don't know how to decode binary parameter of type %u", uint32(typ)) + errorf("don't know how to decode binary parameter of type %d", uint32(typ)) } panic("not reached") diff --git a/vendor/github.com/lib/pq/encode_test.go b/vendor/github.com/lib/pq/encode_test.go new file mode 100644 index 000000000..984abbc94 --- /dev/null +++ b/vendor/github.com/lib/pq/encode_test.go @@ -0,0 +1,727 @@ +package pq + +import ( + "bytes" + "database/sql" + "fmt" + "strings" + "testing" + "time" + + "github.com/lib/pq/oid" +) + +func TestScanTimestamp(t *testing.T) { + var nt NullTime + tn := time.Now() + nt.Scan(tn) + if !nt.Valid { + t.Errorf("Expected Valid=false") + } + if nt.Time != tn { + t.Errorf("Time value mismatch") + } +} + +func TestScanNilTimestamp(t *testing.T) { + var nt NullTime + nt.Scan(nil) + if nt.Valid { + t.Errorf("Expected Valid=false") + } +} + +var timeTests = []struct { + str string + timeval time.Time +}{ + {"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))}, + {"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.000001", time.Date(2001, time.February, 3, 4, 5, 6, 1000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.00001", time.Date(2001, time.February, 3, 4, 5, 6, 10000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.0001", time.Date(2001, time.February, 3, 4, 5, 6, 100000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.001", time.Date(2001, time.February, 3, 4, 5, 6, 1000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.01", time.Date(2001, time.February, 3, 4, 5, 6, 10000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.1", time.Date(2001, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.12", time.Date(2001, time.February, 3, 4, 5, 6, 120000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.123", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.1234", time.Date(2001, time.February, 3, 4, 5, 6, 123400000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.12345", time.Date(2001, time.February, 3, 4, 5, 6, 123450000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.123456", time.Date(2001, time.February, 3, 4, 5, 6, 123456000, time.FixedZone("", 0))}, + {"2001-02-03 04:05:06.123-07", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, + time.FixedZone("", -7*60*60))}, + {"2001-02-03 04:05:06-07", time.Date(2001, time.February, 3, 4, 5, 6, 0, + time.FixedZone("", -7*60*60))}, + {"2001-02-03 04:05:06-07:42", time.Date(2001, time.February, 3, 4, 5, 6, 0, + time.FixedZone("", -(7*60*60+42*60)))}, + {"2001-02-03 04:05:06-07:30:09", time.Date(2001, time.February, 3, 4, 5, 6, 0, + time.FixedZone("", -(7*60*60+30*60+9)))}, + {"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0, + time.FixedZone("", 7*60*60))}, + {"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))}, + {"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, + time.FixedZone("", -7*60*60))}, + {"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"0001-02-03 04:05:06.123 BC", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)}, + {"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"0002-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)}, + {"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, + {"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, + {"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, +} + +// Test that parsing the string results in the expected value. +func TestParseTs(t *testing.T) { + for i, tt := range timeTests { + val, err := ParseTimestamp(nil, tt.str) + if err != nil { + t.Errorf("%d: got error: %v", i, err) + } else if val.String() != tt.timeval.String() { + t.Errorf("%d: expected to parse %q into %q; got %q", + i, tt.str, tt.timeval, val) + } + } +} + +var timeErrorTests = []string{ + "2001", + "2001-2-03", + "2001-02-3", + "2001-02-03 ", + "2001-02-03 04", + "2001-02-03 04:", + "2001-02-03 04:05", + "2001-02-03 04:05:", + "2001-02-03 04:05:6", + "2001-02-03 04:05:06.123 B", +} + +// Test that parsing the string results in an error. +func TestParseTsErrors(t *testing.T) { + for i, tt := range timeErrorTests { + _, err := ParseTimestamp(nil, tt) + if err == nil { + t.Errorf("%d: expected an error from parsing: %v", i, tt) + } + } +} + +// Now test that sending the value into the database and parsing it back +// returns the same time.Time value. +func TestEncodeAndParseTs(t *testing.T) { + db, err := openTestConnConninfo("timezone='Etc/UTC'") + if err != nil { + t.Fatal(err) + } + defer db.Close() + + for i, tt := range timeTests { + var dbstr string + err = db.QueryRow("SELECT ($1::timestamptz)::text", tt.timeval).Scan(&dbstr) + if err != nil { + t.Errorf("%d: could not send value %q to the database: %s", i, tt.timeval, err) + continue + } + + val, err := ParseTimestamp(nil, dbstr) + if err != nil { + t.Errorf("%d: could not parse value %q: %s", i, dbstr, err) + continue + } + val = val.In(tt.timeval.Location()) + if val.String() != tt.timeval.String() { + t.Errorf("%d: expected to parse %q into %q; got %q", i, dbstr, tt.timeval, val) + } + } +} + +var formatTimeTests = []struct { + time time.Time + expected string +}{ + {time.Time{}, "0001-01-01T00:00:00Z"}, + {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03T04:05:06.123456789Z"}, + {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03T04:05:06.123456789+02:00"}, + {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03T04:05:06.123456789-06:00"}, + {time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03T04:05:06-07:30:09"}, + + {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z"}, + {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00"}, + {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00"}, + + {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z BC"}, + {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00 BC"}, + {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00 BC"}, + + {time.Date(1, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09"}, + {time.Date(0, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09 BC"}, +} + +func TestFormatTs(t *testing.T) { + for i, tt := range formatTimeTests { + val := string(formatTs(tt.time)) + if val != tt.expected { + t.Errorf("%d: incorrect time format %q, want %q", i, val, tt.expected) + } + } +} + +func TestTimestampWithTimeZone(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + tx, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer tx.Rollback() + + // try several different locations, all included in Go's zoneinfo.zip + for _, locName := range []string{ + "UTC", + "America/Chicago", + "America/New_York", + "Australia/Darwin", + "Australia/Perth", + } { + loc, err := time.LoadLocation(locName) + if err != nil { + t.Logf("Could not load time zone %s - skipping", locName) + continue + } + + // Postgres timestamps have a resolution of 1 microsecond, so don't + // use the full range of the Nanosecond argument + refTime := time.Date(2012, 11, 6, 10, 23, 42, 123456000, loc) + + for _, pgTimeZone := range []string{"US/Eastern", "Australia/Darwin"} { + // Switch Postgres's timezone to test different output timestamp formats + _, err = tx.Exec(fmt.Sprintf("set time zone '%s'", pgTimeZone)) + if err != nil { + t.Fatal(err) + } + + var gotTime time.Time + row := tx.QueryRow("select $1::timestamp with time zone", refTime) + err = row.Scan(&gotTime) + if err != nil { + t.Fatal(err) + } + + if !refTime.Equal(gotTime) { + t.Errorf("timestamps not equal: %s != %s", refTime, gotTime) + } + + // check that the time zone is set correctly based on TimeZone + pgLoc, err := time.LoadLocation(pgTimeZone) + if err != nil { + t.Logf("Could not load time zone %s - skipping", pgLoc) + continue + } + translated := refTime.In(pgLoc) + if translated.String() != gotTime.String() { + t.Errorf("timestamps not equal: %s != %s", translated, gotTime) + } + } + } +} + +func TestTimestampWithOutTimezone(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + test := func(ts, pgts string) { + r, err := db.Query("SELECT $1::timestamp", pgts) + if err != nil { + t.Fatalf("Could not run query: %v", err) + } + + n := r.Next() + + if n != true { + t.Fatal("Expected at least one row") + } + + var result time.Time + err = r.Scan(&result) + if err != nil { + t.Fatalf("Did not expect error scanning row: %v", err) + } + + expected, err := time.Parse(time.RFC3339, ts) + if err != nil { + t.Fatalf("Could not parse test time literal: %v", err) + } + + if !result.Equal(expected) { + t.Fatalf("Expected time to match %v: got mismatch %v", + expected, result) + } + + n = r.Next() + if n != false { + t.Fatal("Expected only one row") + } + } + + test("2000-01-01T00:00:00Z", "2000-01-01T00:00:00") + + // Test higher precision time + test("2013-01-04T20:14:58.80033Z", "2013-01-04 20:14:58.80033") +} + +func TestInfinityTimestamp(t *testing.T) { + db := openTestConn(t) + defer db.Close() + var err error + var resultT time.Time + + expectedErrorStrPrefix := `sql: Scan error on column index 0: unsupported` + type testCases []struct { + Query string + Param string + ExpectedErrStrPrefix string + ExpectedVal interface{} + } + tc := testCases{ + {"SELECT $1::timestamp", "-infinity", expectedErrorStrPrefix, "-infinity"}, + {"SELECT $1::timestamptz", "-infinity", expectedErrorStrPrefix, "-infinity"}, + {"SELECT $1::timestamp", "infinity", expectedErrorStrPrefix, "infinity"}, + {"SELECT $1::timestamptz", "infinity", expectedErrorStrPrefix, "infinity"}, + } + // try to assert []byte to time.Time + for _, q := range tc { + err = db.QueryRow(q.Query, q.Param).Scan(&resultT) + if !strings.HasPrefix(err.Error(), q.ExpectedErrStrPrefix) { + t.Errorf("Scanning -/+infinity, expected error to have prefix %q, got %q", q.ExpectedErrStrPrefix, err) + } + } + // yield []byte + for _, q := range tc { + var resultI interface{} + err = db.QueryRow(q.Query, q.Param).Scan(&resultI) + if err != nil { + t.Errorf("Scanning -/+infinity, expected no error, got %q", err) + } + result, ok := resultI.([]byte) + if !ok { + t.Errorf("Scanning -/+infinity, expected []byte, got %#v", resultI) + } + if string(result) != q.ExpectedVal { + t.Errorf("Scanning -/+infinity, expected %q, got %q", q.ExpectedVal, result) + } + } + + y1500 := time.Date(1500, time.January, 1, 0, 0, 0, 0, time.UTC) + y2500 := time.Date(2500, time.January, 1, 0, 0, 0, 0, time.UTC) + EnableInfinityTs(y1500, y2500) + + err = db.QueryRow("SELECT $1::timestamp", "infinity").Scan(&resultT) + if err != nil { + t.Errorf("Scanning infinity, expected no error, got %q", err) + } + if !resultT.Equal(y2500) { + t.Errorf("Scanning infinity, expected %q, got %q", y2500, resultT) + } + + err = db.QueryRow("SELECT $1::timestamptz", "infinity").Scan(&resultT) + if err != nil { + t.Errorf("Scanning infinity, expected no error, got %q", err) + } + if !resultT.Equal(y2500) { + t.Errorf("Scanning Infinity, expected time %q, got %q", y2500, resultT.String()) + } + + err = db.QueryRow("SELECT $1::timestamp", "-infinity").Scan(&resultT) + if err != nil { + t.Errorf("Scanning -infinity, expected no error, got %q", err) + } + if !resultT.Equal(y1500) { + t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String()) + } + + err = db.QueryRow("SELECT $1::timestamptz", "-infinity").Scan(&resultT) + if err != nil { + t.Errorf("Scanning -infinity, expected no error, got %q", err) + } + if !resultT.Equal(y1500) { + t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String()) + } + + y_1500 := time.Date(-1500, time.January, 1, 0, 0, 0, 0, time.UTC) + y11500 := time.Date(11500, time.January, 1, 0, 0, 0, 0, time.UTC) + var s string + err = db.QueryRow("SELECT $1::timestamp::text", y_1500).Scan(&s) + if err != nil { + t.Errorf("Encoding -infinity, expected no error, got %q", err) + } + if s != "-infinity" { + t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s) + } + err = db.QueryRow("SELECT $1::timestamptz::text", y_1500).Scan(&s) + if err != nil { + t.Errorf("Encoding -infinity, expected no error, got %q", err) + } + if s != "-infinity" { + t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s) + } + + err = db.QueryRow("SELECT $1::timestamp::text", y11500).Scan(&s) + if err != nil { + t.Errorf("Encoding infinity, expected no error, got %q", err) + } + if s != "infinity" { + t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s) + } + err = db.QueryRow("SELECT $1::timestamptz::text", y11500).Scan(&s) + if err != nil { + t.Errorf("Encoding infinity, expected no error, got %q", err) + } + if s != "infinity" { + t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s) + } + + disableInfinityTs() + + var panicErrorString string + func() { + defer func() { + panicErrorString, _ = recover().(string) + }() + EnableInfinityTs(y2500, y1500) + }() + if panicErrorString != infinityTsNegativeMustBeSmaller { + t.Errorf("Expected error, %q, got %q", infinityTsNegativeMustBeSmaller, panicErrorString) + } +} + +func TestStringWithNul(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + hello0world := string("hello\x00world") + _, err := db.Query("SELECT $1::text", &hello0world) + if err == nil { + t.Fatal("Postgres accepts a string with nul in it; " + + "injection attacks may be plausible") + } +} + +func TestByteSliceToText(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + b := []byte("hello world") + row := db.QueryRow("SELECT $1::text", b) + + var result []byte + err := row.Scan(&result) + if err != nil { + t.Fatal(err) + } + + if string(result) != string(b) { + t.Fatalf("expected %v but got %v", b, result) + } +} + +func TestStringToBytea(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + b := "hello world" + row := db.QueryRow("SELECT $1::bytea", b) + + var result []byte + err := row.Scan(&result) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(result, []byte(b)) { + t.Fatalf("expected %v but got %v", b, result) + } +} + +func TestTextByteSliceToUUID(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + b := []byte("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") + row := db.QueryRow("SELECT $1::uuid", b) + + var result string + err := row.Scan(&result) + if forceBinaryParameters() { + pqErr := err.(*Error) + if pqErr == nil { + t.Errorf("Expected to get error") + } else if pqErr.Code != "22P03" { + t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code) + } + } else { + if err != nil { + t.Fatal(err) + } + + if result != string(b) { + t.Fatalf("expected %v but got %v", b, result) + } + } +} + +func TestBinaryByteSlicetoUUID(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + b := []byte{'\xa0', '\xee', '\xbc', '\x99', + '\x9c', '\x0b', + '\x4e', '\xf8', + '\xbb', '\x00', '\x6b', + '\xb9', '\xbd', '\x38', '\x0a', '\x11'} + row := db.QueryRow("SELECT $1::uuid", b) + + var result string + err := row.Scan(&result) + if forceBinaryParameters() { + if err != nil { + t.Fatal(err) + } + + if result != string("a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11") { + t.Fatalf("expected %v but got %v", b, result) + } + } else { + pqErr := err.(*Error) + if pqErr == nil { + t.Errorf("Expected to get error") + } else if pqErr.Code != "22021" { + t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code) + } + } +} + +func TestStringToUUID(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + s := "a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11" + row := db.QueryRow("SELECT $1::uuid", s) + + var result string + err := row.Scan(&result) + if err != nil { + t.Fatal(err) + } + + if result != s { + t.Fatalf("expected %v but got %v", s, result) + } +} + +func TestTextByteSliceToInt(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + expected := 12345678 + b := []byte(fmt.Sprintf("%d", expected)) + row := db.QueryRow("SELECT $1::int", b) + + var result int + err := row.Scan(&result) + if forceBinaryParameters() { + pqErr := err.(*Error) + if pqErr == nil { + t.Errorf("Expected to get error") + } else if pqErr.Code != "22P03" { + t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code) + } + } else { + if err != nil { + t.Fatal(err) + } + if result != expected { + t.Fatalf("expected %v but got %v", expected, result) + } + } +} + +func TestBinaryByteSliceToInt(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + expected := 12345678 + b := []byte{'\x00', '\xbc', '\x61', '\x4e'} + row := db.QueryRow("SELECT $1::int", b) + + var result int + err := row.Scan(&result) + if forceBinaryParameters() { + if err != nil { + t.Fatal(err) + } + if result != expected { + t.Fatalf("expected %v but got %v", expected, result) + } + } else { + pqErr := err.(*Error) + if pqErr == nil { + t.Errorf("Expected to get error") + } else if pqErr.Code != "22021" { + t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code) + } + } +} + +func TestByteaOutputFormatEncoding(t *testing.T) { + input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123") + want := []byte("\\x5c78000102fffe6162636465666730313233") + got := encode(¶meterStatus{serverVersion: 90000}, input, oid.T_bytea) + if !bytes.Equal(want, got) { + t.Errorf("invalid hex bytea output, got %v but expected %v", got, want) + } + + want = []byte("\\\\x\\000\\001\\002\\377\\376abcdefg0123") + got = encode(¶meterStatus{serverVersion: 84000}, input, oid.T_bytea) + if !bytes.Equal(want, got) { + t.Errorf("invalid escape bytea output, got %v but expected %v", got, want) + } +} + +func TestByteaOutputFormats(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + if getServerVersion(t, db) < 90000 { + // skip + return + } + + testByteaOutputFormat := func(f string, usePrepared bool) { + expectedData := []byte("\x5c\x78\x00\xff\x61\x62\x63\x01\x08") + sqlQuery := "SELECT decode('5c7800ff6162630108', 'hex')" + + var data []byte + + // use a txn to avoid relying on getting the same connection + txn, err := db.Begin() + if err != nil { + t.Fatal(err) + } + defer txn.Rollback() + + _, err = txn.Exec("SET LOCAL bytea_output TO " + f) + if err != nil { + t.Fatal(err) + } + var rows *sql.Rows + var stmt *sql.Stmt + if usePrepared { + stmt, err = txn.Prepare(sqlQuery) + if err != nil { + t.Fatal(err) + } + rows, err = stmt.Query() + } else { + // use Query; QueryRow would hide the actual error + rows, err = txn.Query(sqlQuery) + } + if err != nil { + t.Fatal(err) + } + if !rows.Next() { + if rows.Err() != nil { + t.Fatal(rows.Err()) + } + t.Fatal("shouldn't happen") + } + err = rows.Scan(&data) + if err != nil { + t.Fatal(err) + } + err = rows.Close() + if err != nil { + t.Fatal(err) + } + if stmt != nil { + err = stmt.Close() + if err != nil { + t.Fatal(err) + } + } + if !bytes.Equal(data, expectedData) { + t.Errorf("unexpected bytea value %v for format %s; expected %v", data, f, expectedData) + } + } + + testByteaOutputFormat("hex", false) + testByteaOutputFormat("escape", false) + testByteaOutputFormat("hex", true) + testByteaOutputFormat("escape", true) +} + +func TestAppendEncodedText(t *testing.T) { + var buf []byte + + buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, int64(10)) + buf = append(buf, '\t') + buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, 42.0000000001) + buf = append(buf, '\t') + buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, "hello\tworld") + buf = append(buf, '\t') + buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, []byte{0, 128, 255}) + + if string(buf) != "10\t42.0000000001\thello\\tworld\t\\\\x0080ff" { + t.Fatal(string(buf)) + } +} + +func TestAppendEscapedText(t *testing.T) { + if esc := appendEscapedText(nil, "hallo\tescape"); string(esc) != "hallo\\tescape" { + t.Fatal(string(esc)) + } + if esc := appendEscapedText(nil, "hallo\\tescape\n"); string(esc) != "hallo\\\\tescape\\n" { + t.Fatal(string(esc)) + } + if esc := appendEscapedText(nil, "\n\r\t\f"); string(esc) != "\\n\\r\\t\f" { + t.Fatal(string(esc)) + } +} + +func TestAppendEscapedTextExistingBuffer(t *testing.T) { + var buf []byte + buf = []byte("123\t") + if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" { + t.Fatal(string(esc)) + } + buf = []byte("123\t") + if esc := appendEscapedText(buf, "hallo\\tescape\n"); string(esc) != "123\thallo\\\\tescape\\n" { + t.Fatal(string(esc)) + } + buf = []byte("123\t") + if esc := appendEscapedText(buf, "\n\r\t\f"); string(esc) != "123\t\\n\\r\\t\f" { + t.Fatal(string(esc)) + } +} + +func BenchmarkAppendEscapedText(b *testing.B) { + longString := "" + for i := 0; i < 100; i++ { + longString += "123456789\n" + } + for i := 0; i < b.N; i++ { + appendEscapedText(nil, longString) + } +} + +func BenchmarkAppendEscapedTextNoEscape(b *testing.B) { + longString := "" + for i := 0; i < 100; i++ { + longString += "1234567890" + } + for i := 0; i < b.N; i++ { + appendEscapedText(nil, longString) + } +} diff --git a/vendor/github.com/lib/pq/hstore/hstore.go b/vendor/github.com/lib/pq/hstore/hstore.go new file mode 100644 index 000000000..72d5abf51 --- /dev/null +++ b/vendor/github.com/lib/pq/hstore/hstore.go @@ -0,0 +1,118 @@ +package hstore + +import ( + "database/sql" + "database/sql/driver" + "strings" +) + +// A wrapper for transferring Hstore values back and forth easily. +type Hstore struct { + Map map[string]sql.NullString +} + +// escapes and quotes hstore keys/values +// s should be a sql.NullString or string +func hQuote(s interface{}) string { + var str string + switch v := s.(type) { + case sql.NullString: + if !v.Valid { + return "NULL" + } + str = v.String + case string: + str = v + default: + panic("not a string or sql.NullString") + } + + str = strings.Replace(str, "\\", "\\\\", -1) + return `"` + strings.Replace(str, "\"", "\\\"", -1) + `"` +} + +// Scan implements the Scanner interface. +// +// Note h.Map is reallocated before the scan to clear existing values. If the +// hstore column's database value is NULL, then h.Map is set to nil instead. +func (h *Hstore) Scan(value interface{}) error { + if value == nil { + h.Map = nil + return nil + } + h.Map = make(map[string]sql.NullString) + var b byte + pair := [][]byte{{}, {}} + pi := 0 + inQuote := false + didQuote := false + sawSlash := false + bindex := 0 + for bindex, b = range value.([]byte) { + if sawSlash { + pair[pi] = append(pair[pi], b) + sawSlash = false + continue + } + + switch b { + case '\\': + sawSlash = true + continue + case '"': + inQuote = !inQuote + if !didQuote { + didQuote = true + } + continue + default: + if !inQuote { + switch b { + case ' ', '\t', '\n', '\r': + continue + case '=': + continue + case '>': + pi = 1 + didQuote = false + continue + case ',': + s := string(pair[1]) + if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { + h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} + } else { + h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} + } + pair[0] = []byte{} + pair[1] = []byte{} + pi = 0 + continue + } + } + } + pair[pi] = append(pair[pi], b) + } + if bindex > 0 { + s := string(pair[1]) + if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { + h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} + } else { + h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} + } + } + return nil +} + +// Value implements the driver Valuer interface. Note if h.Map is nil, the +// database column value will be set to NULL. +func (h Hstore) Value() (driver.Value, error) { + if h.Map == nil { + return nil, nil + } + parts := []string{} + for key, val := range h.Map { + thispart := hQuote(key) + "=>" + hQuote(val) + parts = append(parts, thispart) + } + return []byte(strings.Join(parts, ",")), nil +} diff --git a/vendor/github.com/lib/pq/hstore/hstore_test.go b/vendor/github.com/lib/pq/hstore/hstore_test.go new file mode 100644 index 000000000..1c9f2bd49 --- /dev/null +++ b/vendor/github.com/lib/pq/hstore/hstore_test.go @@ -0,0 +1,148 @@ +package hstore + +import ( + "database/sql" + "os" + "testing" + + _ "github.com/lib/pq" +) + +type Fatalistic interface { + Fatal(args ...interface{}) +} + +func openTestConn(t Fatalistic) *sql.DB { + datname := os.Getenv("PGDATABASE") + sslmode := os.Getenv("PGSSLMODE") + + if datname == "" { + os.Setenv("PGDATABASE", "pqgotest") + } + + if sslmode == "" { + os.Setenv("PGSSLMODE", "disable") + } + + conn, err := sql.Open("postgres", "") + if err != nil { + t.Fatal(err) + } + + return conn +} + +func TestHstore(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + // quitely create hstore if it doesn't exist + _, err := db.Exec("CREATE EXTENSION IF NOT EXISTS hstore") + if err != nil { + t.Skipf("Skipping hstore tests - hstore extension create failed: %s", err.Error()) + } + + hs := Hstore{} + + // test for null-valued hstores + err = db.QueryRow("SELECT NULL::hstore").Scan(&hs) + if err != nil { + t.Fatal(err) + } + if hs.Map != nil { + t.Fatalf("expected null map") + } + + err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs) + if err != nil { + t.Fatalf("re-query null map failed: %s", err.Error()) + } + if hs.Map != nil { + t.Fatalf("expected null map") + } + + // test for empty hstores + err = db.QueryRow("SELECT ''::hstore").Scan(&hs) + if err != nil { + t.Fatal(err) + } + if hs.Map == nil { + t.Fatalf("expected empty map, got null map") + } + if len(hs.Map) != 0 { + t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map)) + } + + err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs) + if err != nil { + t.Fatalf("re-query empty map failed: %s", err.Error()) + } + if hs.Map == nil { + t.Fatalf("expected empty map, got null map") + } + if len(hs.Map) != 0 { + t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map)) + } + + // a few example maps to test out + hsOnePair := Hstore{ + Map: map[string]sql.NullString{ + "key1": {String: "value1", Valid: true}, + }, + } + + hsThreePairs := Hstore{ + Map: map[string]sql.NullString{ + "key1": {String: "value1", Valid: true}, + "key2": {String: "value2", Valid: true}, + "key3": {String: "value3", Valid: true}, + }, + } + + hsSmorgasbord := Hstore{ + Map: map[string]sql.NullString{ + "nullstring": {String: "NULL", Valid: true}, + "actuallynull": {String: "", Valid: false}, + "NULL": {String: "NULL string key", Valid: true}, + "withbracket": {String: "value>42", Valid: true}, + "withequal": {String: "value=42", Valid: true}, + `"withquotes1"`: {String: `this "should" be fine`, Valid: true}, + `"withquotes"2"`: {String: `this "should\" also be fine`, Valid: true}, + "embedded1": {String: "value1=>x1", Valid: true}, + "embedded2": {String: `"value2"=>x2`, Valid: true}, + "withnewlines": {String: "\n\nvalue\t=>2", Valid: true}, + "<>": {String: `this, "should,\" also, => be fine`, Valid: true}, + }, + } + + // test encoding in query params, then decoding during Scan + testBidirectional := func(h Hstore) { + err = db.QueryRow("SELECT $1::hstore", h).Scan(&hs) + if err != nil { + t.Fatalf("re-query %d-pair map failed: %s", len(h.Map), err.Error()) + } + if hs.Map == nil { + t.Fatalf("expected %d-pair map, got null map", len(h.Map)) + } + if len(hs.Map) != len(h.Map) { + t.Fatalf("expected %d-pair map, got len(map)=%d", len(h.Map), len(hs.Map)) + } + + for key, val := range hs.Map { + otherval, found := h.Map[key] + if !found { + t.Fatalf(" key '%v' not found in %d-pair map", key, len(h.Map)) + } + if otherval.Valid != val.Valid { + t.Fatalf(" value %v <> %v in %d-pair map", otherval, val, len(h.Map)) + } + if otherval.String != val.String { + t.Fatalf(" value '%v' <> '%v' in %d-pair map", otherval.String, val.String, len(h.Map)) + } + } + } + + testBidirectional(hsOnePair) + testBidirectional(hsThreePairs) + testBidirectional(hsSmorgasbord) +} diff --git a/vendor/github.com/lib/pq/listen_example/doc.go b/vendor/github.com/lib/pq/listen_example/doc.go new file mode 100644 index 000000000..5bc99f5c1 --- /dev/null +++ b/vendor/github.com/lib/pq/listen_example/doc.go @@ -0,0 +1,102 @@ +/* + +Below you will find a self-contained Go program which uses the LISTEN / NOTIFY +mechanism to avoid polling the database while waiting for more work to arrive. + + // + // You can see the program in action by defining a function similar to + // the following: + // + // CREATE OR REPLACE FUNCTION public.get_work() + // RETURNS bigint + // LANGUAGE sql + // AS $$ + // SELECT CASE WHEN random() >= 0.2 THEN int8 '1' END + // $$ + // ; + + package main + + import ( + "database/sql" + "fmt" + "time" + + "github.com/lib/pq" + ) + + func doWork(db *sql.DB, work int64) { + // work here + } + + func getWork(db *sql.DB) { + for { + // get work from the database here + var work sql.NullInt64 + err := db.QueryRow("SELECT get_work()").Scan(&work) + if err != nil { + fmt.Println("call to get_work() failed: ", err) + time.Sleep(10 * time.Second) + continue + } + if !work.Valid { + // no more work to do + fmt.Println("ran out of work") + return + } + + fmt.Println("starting work on ", work.Int64) + go doWork(db, work.Int64) + } + } + + func waitForNotification(l *pq.Listener) { + for { + select { + case <-l.Notify: + fmt.Println("received notification, new work available") + return + case <-time.After(90 * time.Second): + go func() { + l.Ping() + }() + // Check if there's more work available, just in case it takes + // a while for the Listener to notice connection loss and + // reconnect. + fmt.Println("received no work for 90 seconds, checking for new work") + return + } + } + } + + func main() { + var conninfo string = "" + + db, err := sql.Open("postgres", conninfo) + if err != nil { + panic(err) + } + + reportProblem := func(ev pq.ListenerEventType, err error) { + if err != nil { + fmt.Println(err.Error()) + } + } + + listener := pq.NewListener(conninfo, 10 * time.Second, time.Minute, reportProblem) + err = listener.Listen("getwork") + if err != nil { + panic(err) + } + + fmt.Println("entering main loop") + for { + // process all available work before waiting for notifications + getWork(db) + waitForNotification(listener) + } + } + + +*/ +package listen_example diff --git a/vendor/github.com/lib/pq/notify.go b/vendor/github.com/lib/pq/notify.go index 8cad57815..09f94244b 100644 --- a/vendor/github.com/lib/pq/notify.go +++ b/vendor/github.com/lib/pq/notify.go @@ -62,14 +62,18 @@ type ListenerConn struct { // Creates a new ListenerConn. Use NewListener instead. func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) { - cn, err := Open(name) + return newDialListenerConn(defaultDialer{}, name, notificationChan) +} + +func newDialListenerConn(d Dialer, name string, c chan<- *Notification) (*ListenerConn, error) { + cn, err := DialOpen(d, name) if err != nil { return nil, err } l := &ListenerConn{ cn: cn.(*conn), - notificationChan: notificationChan, + notificationChan: c, connState: connStateIdle, replyChan: make(chan message, 2), } @@ -391,6 +395,7 @@ type Listener struct { name string minReconnectInterval time.Duration maxReconnectInterval time.Duration + dialer Dialer eventCallback EventCallbackType lock sync.Mutex @@ -421,10 +426,21 @@ func NewListener(name string, minReconnectInterval time.Duration, maxReconnectInterval time.Duration, eventCallback EventCallbackType) *Listener { + return NewDialListener(defaultDialer{}, name, minReconnectInterval, maxReconnectInterval, eventCallback) +} + +// NewDialListener is like NewListener but it takes a Dialer. +func NewDialListener(d Dialer, + name string, + minReconnectInterval time.Duration, + maxReconnectInterval time.Duration, + eventCallback EventCallbackType) *Listener { + l := &Listener{ name: name, minReconnectInterval: minReconnectInterval, maxReconnectInterval: maxReconnectInterval, + dialer: d, eventCallback: eventCallback, channels: make(map[string]struct{}), @@ -660,7 +676,7 @@ func (l *Listener) closed() bool { func (l *Listener) connect() error { notificationChan := make(chan *Notification, 32) - cn, err := NewListenerConn(l.name, notificationChan) + cn, err := newDialListenerConn(l.dialer, l.name, notificationChan) if err != nil { return err } diff --git a/vendor/github.com/lib/pq/notify_test.go b/vendor/github.com/lib/pq/notify_test.go new file mode 100644 index 000000000..fe8941a4e --- /dev/null +++ b/vendor/github.com/lib/pq/notify_test.go @@ -0,0 +1,574 @@ +package pq + +import ( + "errors" + "fmt" + "io" + "os" + "runtime" + "sync" + "sync/atomic" + "testing" + "time" +) + +var errNilNotification = errors.New("nil notification") + +func expectNotification(t *testing.T, ch <-chan *Notification, relname string, extra string) error { + select { + case n := <-ch: + if n == nil { + return errNilNotification + } + if n.Channel != relname || n.Extra != extra { + return fmt.Errorf("unexpected notification %v", n) + } + return nil + case <-time.After(1500 * time.Millisecond): + return fmt.Errorf("timeout") + } +} + +func expectNoNotification(t *testing.T, ch <-chan *Notification) error { + select { + case n := <-ch: + return fmt.Errorf("unexpected notification %v", n) + case <-time.After(100 * time.Millisecond): + return nil + } +} + +func expectEvent(t *testing.T, eventch <-chan ListenerEventType, et ListenerEventType) error { + select { + case e := <-eventch: + if e != et { + return fmt.Errorf("unexpected event %v", e) + } + return nil + case <-time.After(1500 * time.Millisecond): + panic("expectEvent timeout") + } +} + +func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error { + select { + case e := <-eventch: + return fmt.Errorf("unexpected event %v", e) + case <-time.After(100 * time.Millisecond): + return nil + } +} + +func newTestListenerConn(t *testing.T) (*ListenerConn, <-chan *Notification) { + datname := os.Getenv("PGDATABASE") + sslmode := os.Getenv("PGSSLMODE") + + if datname == "" { + os.Setenv("PGDATABASE", "pqgotest") + } + + if sslmode == "" { + os.Setenv("PGSSLMODE", "disable") + } + + notificationChan := make(chan *Notification) + l, err := NewListenerConn("", notificationChan) + if err != nil { + t.Fatal(err) + } + + return l, notificationChan +} + +func TestNewListenerConn(t *testing.T) { + l, _ := newTestListenerConn(t) + + defer l.Close() +} + +func TestConnListen(t *testing.T) { + l, channel := newTestListenerConn(t) + + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + ok, err := l.Listen("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, channel, "notify_test", "") + if err != nil { + t.Fatal(err) + } +} + +func TestConnUnlisten(t *testing.T) { + l, channel := newTestListenerConn(t) + + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + ok, err := l.Listen("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + + err = expectNotification(t, channel, "notify_test", "") + if err != nil { + t.Fatal(err) + } + + ok, err = l.Unlisten("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + if err != nil { + t.Fatal(err) + } + + err = expectNoNotification(t, channel) + if err != nil { + t.Fatal(err) + } +} + +func TestConnUnlistenAll(t *testing.T) { + l, channel := newTestListenerConn(t) + + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + ok, err := l.Listen("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + + err = expectNotification(t, channel, "notify_test", "") + if err != nil { + t.Fatal(err) + } + + ok, err = l.UnlistenAll() + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test") + if err != nil { + t.Fatal(err) + } + + err = expectNoNotification(t, channel) + if err != nil { + t.Fatal(err) + } +} + +func TestConnClose(t *testing.T) { + l, _ := newTestListenerConn(t) + defer l.Close() + + err := l.Close() + if err != nil { + t.Fatal(err) + } + err = l.Close() + if err != errListenerConnClosed { + t.Fatalf("expected errListenerConnClosed; got %v", err) + } +} + +func TestConnPing(t *testing.T) { + l, _ := newTestListenerConn(t) + defer l.Close() + err := l.Ping() + if err != nil { + t.Fatal(err) + } + err = l.Close() + if err != nil { + t.Fatal(err) + } + err = l.Ping() + if err != errListenerConnClosed { + t.Fatalf("expected errListenerConnClosed; got %v", err) + } +} + +// Test for deadlock where a query fails while another one is queued +func TestConnExecDeadlock(t *testing.T) { + l, _ := newTestListenerConn(t) + defer l.Close() + + var wg sync.WaitGroup + wg.Add(2) + + go func() { + l.ExecSimpleQuery("SELECT pg_sleep(60)") + wg.Done() + }() + runtime.Gosched() + go func() { + l.ExecSimpleQuery("SELECT 1") + wg.Done() + }() + // give the two goroutines some time to get into position + runtime.Gosched() + // calls Close on the net.Conn; equivalent to a network failure + l.Close() + + var done int32 = 0 + go func() { + time.Sleep(10 * time.Second) + if atomic.LoadInt32(&done) != 1 { + panic("timed out") + } + }() + wg.Wait() + atomic.StoreInt32(&done, 1) +} + +// Test for ListenerConn being closed while a slow query is executing +func TestListenerConnCloseWhileQueryIsExecuting(t *testing.T) { + l, _ := newTestListenerConn(t) + defer l.Close() + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + sent, err := l.ExecSimpleQuery("SELECT pg_sleep(60)") + if sent { + panic("expected sent=false") + } + // could be any of a number of errors + if err == nil { + panic("expected error") + } + wg.Done() + }() + // give the above goroutine some time to get into position + runtime.Gosched() + err := l.Close() + if err != nil { + t.Fatal(err) + } + var done int32 = 0 + go func() { + time.Sleep(10 * time.Second) + if atomic.LoadInt32(&done) != 1 { + panic("timed out") + } + }() + wg.Wait() + atomic.StoreInt32(&done, 1) +} + +func TestNotifyExtra(t *testing.T) { + db := openTestConn(t) + defer db.Close() + + if getServerVersion(t, db) < 90000 { + t.Skip("skipping NOTIFY payload test since the server does not appear to support it") + } + + l, channel := newTestListenerConn(t) + defer l.Close() + + ok, err := l.Listen("notify_test") + if !ok || err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_test, 'something'") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, channel, "notify_test", "something") + if err != nil { + t.Fatal(err) + } +} + +// create a new test listener and also set the timeouts +func newTestListenerTimeout(t *testing.T, min time.Duration, max time.Duration) (*Listener, <-chan ListenerEventType) { + datname := os.Getenv("PGDATABASE") + sslmode := os.Getenv("PGSSLMODE") + + if datname == "" { + os.Setenv("PGDATABASE", "pqgotest") + } + + if sslmode == "" { + os.Setenv("PGSSLMODE", "disable") + } + + eventch := make(chan ListenerEventType, 16) + l := NewListener("", min, max, func(t ListenerEventType, err error) { eventch <- t }) + err := expectEvent(t, eventch, ListenerEventConnected) + if err != nil { + t.Fatal(err) + } + return l, eventch +} + +func newTestListener(t *testing.T) (*Listener, <-chan ListenerEventType) { + return newTestListenerTimeout(t, time.Hour, time.Hour) +} + +func TestListenerListen(t *testing.T) { + l, _ := newTestListener(t) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } +} + +func TestListenerUnlisten(t *testing.T) { + l, _ := newTestListener(t) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = l.Unlisten("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNoNotification(t, l.Notify) + if err != nil { + t.Fatal(err) + } +} + +func TestListenerUnlistenAll(t *testing.T) { + l, _ := newTestListener(t) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = l.UnlistenAll() + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNoNotification(t, l.Notify) + if err != nil { + t.Fatal(err) + } +} + +func TestListenerFailedQuery(t *testing.T) { + l, eventch := newTestListener(t) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } + + // shouldn't cause a disconnect + ok, err := l.cn.ExecSimpleQuery("SELECT error") + if !ok { + t.Fatalf("could not send query to server: %v", err) + } + _, ok = err.(PGError) + if !ok { + t.Fatalf("unexpected error %v", err) + } + err = expectNoEvent(t, eventch) + if err != nil { + t.Fatal(err) + } + + // should still work + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } +} + +func TestListenerReconnect(t *testing.T) { + l, eventch := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour) + defer l.Close() + + db := openTestConn(t) + defer db.Close() + + err := l.Listen("notify_listen_test") + if err != nil { + t.Fatal(err) + } + + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } + + // kill the connection and make sure it comes back up + ok, err := l.cn.ExecSimpleQuery("SELECT pg_terminate_backend(pg_backend_pid())") + if ok { + t.Fatalf("could not kill the connection: %v", err) + } + if err != io.EOF { + t.Fatalf("unexpected error %v", err) + } + err = expectEvent(t, eventch, ListenerEventDisconnected) + if err != nil { + t.Fatal(err) + } + err = expectEvent(t, eventch, ListenerEventReconnected) + if err != nil { + t.Fatal(err) + } + + // should still work + _, err = db.Exec("NOTIFY notify_listen_test") + if err != nil { + t.Fatal(err) + } + + // should get nil after Reconnected + err = expectNotification(t, l.Notify, "", "") + if err != errNilNotification { + t.Fatal(err) + } + + err = expectNotification(t, l.Notify, "notify_listen_test", "") + if err != nil { + t.Fatal(err) + } +} + +func TestListenerClose(t *testing.T) { + l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour) + defer l.Close() + + err := l.Close() + if err != nil { + t.Fatal(err) + } + err = l.Close() + if err != errListenerClosed { + t.Fatalf("expected errListenerClosed; got %v", err) + } +} + +func TestListenerPing(t *testing.T) { + l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour) + defer l.Close() + + err := l.Ping() + if err != nil { + t.Fatal(err) + } + + err = l.Close() + if err != nil { + t.Fatal(err) + } + + err = l.Ping() + if err != errListenerClosed { + t.Fatalf("expected errListenerClosed; got %v", err) + } +} diff --git a/vendor/github.com/lib/pq/ssl_test.go b/vendor/github.com/lib/pq/ssl_test.go new file mode 100644 index 000000000..932b336f5 --- /dev/null +++ b/vendor/github.com/lib/pq/ssl_test.go @@ -0,0 +1,226 @@ +package pq + +// This file contains SSL tests + +import ( + _ "crypto/sha256" + "crypto/x509" + "database/sql" + "fmt" + "os" + "path/filepath" + "testing" +) + +func maybeSkipSSLTests(t *testing.T) { + // Require some special variables for testing certificates + if os.Getenv("PQSSLCERTTEST_PATH") == "" { + t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests") + } + + value := os.Getenv("PQGOSSLTESTS") + if value == "" || value == "0" { + t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests") + } else if value != "1" { + t.Fatalf("unexpected value %q for PQGOSSLTESTS", value) + } +} + +func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) { + db, err := openTestConnConninfo(conninfo) + if err != nil { + // should never fail + t.Fatal(err) + } + // Do something with the connection to see whether it's working or not. + tx, err := db.Begin() + if err == nil { + return db, tx.Rollback() + } + _ = db.Close() + return nil, err +} + +func checkSSLSetup(t *testing.T, conninfo string) { + db, err := openSSLConn(t, conninfo) + if err == nil { + db.Close() + t.Fatalf("expected error with conninfo=%q", conninfo) + } +} + +// Connect over SSL and run a simple query to test the basics +func TestSSLConnection(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + db, err := openSSLConn(t, "sslmode=require user=pqgossltest") + if err != nil { + t.Fatal(err) + } + rows, err := db.Query("SELECT 1") + if err != nil { + t.Fatal(err) + } + rows.Close() +} + +// Test sslmode=verify-full +func TestSSLVerifyFull(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + // Not OK according to the system CA + _, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest") + if err == nil { + t.Fatal("expected error") + } + _, ok := err.(x509.UnknownAuthorityError) + if !ok { + t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err) + } + + rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt") + rootCert := "sslrootcert=" + rootCertPath + " " + // No match on Common Name + _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest") + if err == nil { + t.Fatal("expected error") + } + _, ok = err.(x509.HostnameError) + if !ok { + t.Fatalf("expected x509.HostnameError, got %#+v", err) + } + // OK + _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest") + if err != nil { + t.Fatal(err) + } +} + +// Test sslmode=verify-ca +func TestSSLVerifyCA(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + // Not OK according to the system CA + _, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest") + if err == nil { + t.Fatal("expected error") + } + _, ok := err.(x509.UnknownAuthorityError) + if !ok { + t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err) + } + + rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt") + rootCert := "sslrootcert=" + rootCertPath + " " + // No match on Common Name, but that's OK + _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest") + if err != nil { + t.Fatal(err) + } + // Everything OK + _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest") + if err != nil { + t.Fatal(err) + } +} + +func getCertConninfo(t *testing.T, source string) string { + var sslkey string + var sslcert string + + certpath := os.Getenv("PQSSLCERTTEST_PATH") + + switch source { + case "missingkey": + sslkey = "/tmp/filedoesnotexist" + sslcert = filepath.Join(certpath, "postgresql.crt") + case "missingcert": + sslkey = filepath.Join(certpath, "postgresql.key") + sslcert = "/tmp/filedoesnotexist" + case "certtwice": + sslkey = filepath.Join(certpath, "postgresql.crt") + sslcert = filepath.Join(certpath, "postgresql.crt") + case "valid": + sslkey = filepath.Join(certpath, "postgresql.key") + sslcert = filepath.Join(certpath, "postgresql.crt") + default: + t.Fatalf("invalid source %q", source) + } + return fmt.Sprintf("sslmode=require user=pqgosslcert sslkey=%s sslcert=%s", sslkey, sslcert) +} + +// Authenticate over SSL using client certificates +func TestSSLClientCertificates(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + // Should also fail without a valid certificate + db, err := openSSLConn(t, "sslmode=require user=pqgosslcert") + if err == nil { + db.Close() + t.Fatal("expected error") + } + pge, ok := err.(*Error) + if !ok { + t.Fatal("expected pq.Error") + } + if pge.Code.Name() != "invalid_authorization_specification" { + t.Fatalf("unexpected error code %q", pge.Code.Name()) + } + + // Should work + db, err = openSSLConn(t, getCertConninfo(t, "valid")) + if err != nil { + t.Fatal(err) + } + rows, err := db.Query("SELECT 1") + if err != nil { + t.Fatal(err) + } + rows.Close() +} + +// Test errors with ssl certificates +func TestSSLClientCertificatesMissingFiles(t *testing.T) { + maybeSkipSSLTests(t) + // Environment sanity check: should fail without SSL + checkSSLSetup(t, "sslmode=disable user=pqgossltest") + + // Key missing, should fail + _, err := openSSLConn(t, getCertConninfo(t, "missingkey")) + if err == nil { + t.Fatal("expected error") + } + // should be a PathError + _, ok := err.(*os.PathError) + if !ok { + t.Fatalf("expected PathError, got %#+v", err) + } + + // Cert missing, should fail + _, err = openSSLConn(t, getCertConninfo(t, "missingcert")) + if err == nil { + t.Fatal("expected error") + } + // should be a PathError + _, ok = err.(*os.PathError) + if !ok { + t.Fatalf("expected PathError, got %#+v", err) + } + + // Key has wrong permissions, should fail + _, err = openSSLConn(t, getCertConninfo(t, "certtwice")) + if err == nil { + t.Fatal("expected error") + } + if err != ErrSSLKeyHasWorldPermissions { + t.Fatalf("expected ErrSSLKeyHasWorldPermissions, got %#+v", err) + } +} diff --git a/vendor/github.com/lib/pq/url_test.go b/vendor/github.com/lib/pq/url_test.go new file mode 100644 index 000000000..4ff0ce034 --- /dev/null +++ b/vendor/github.com/lib/pq/url_test.go @@ -0,0 +1,66 @@ +package pq + +import ( + "testing" +) + +func TestSimpleParseURL(t *testing.T) { + expected := "host=hostname.remote" + str, err := ParseURL("postgres://hostname.remote") + if err != nil { + t.Fatal(err) + } + + if str != expected { + t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected) + } +} + +func TestIPv6LoopbackParseURL(t *testing.T) { + expected := "host=::1 port=1234" + str, err := ParseURL("postgres://[::1]:1234") + if err != nil { + t.Fatal(err) + } + + if str != expected { + t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected) + } +} + +func TestFullParseURL(t *testing.T) { + expected := `dbname=database host=hostname.remote password=top\ secret port=1234 user=username` + str, err := ParseURL("postgres://username:top%20secret@hostname.remote:1234/database") + if err != nil { + t.Fatal(err) + } + + if str != expected { + t.Fatalf("unexpected result from ParseURL:\n+ %s\n- %s", str, expected) + } +} + +func TestInvalidProtocolParseURL(t *testing.T) { + _, err := ParseURL("http://hostname.remote") + switch err { + case nil: + t.Fatal("Expected an error from parsing invalid protocol") + default: + msg := "invalid connection protocol: http" + if err.Error() != msg { + t.Fatalf("Unexpected error message:\n+ %s\n- %s", + err.Error(), msg) + } + } +} + +func TestMinimalURL(t *testing.T) { + cs, err := ParseURL("postgres://") + if err != nil { + t.Fatal(err) + } + + if cs != "" { + t.Fatalf("expected blank connection string, got: %q", cs) + } +} diff --git a/vendor/github.com/mattermost/rsc/.gitignore b/vendor/github.com/mattermost/rsc/.gitignore new file mode 100644 index 000000000..00268614f --- /dev/null +++ b/vendor/github.com/mattermost/rsc/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/vendor/github.com/mattermost/rsc/README.md b/vendor/github.com/mattermost/rsc/README.md new file mode 100644 index 000000000..d110adfb2 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/README.md @@ -0,0 +1,4 @@ +rsc +=== + +fork of Russ Cox's code.google.com/p/rsc \ No newline at end of file diff --git a/vendor/github.com/mattermost/rsc/app/app.go b/vendor/github.com/mattermost/rsc/app/app.go new file mode 100644 index 000000000..d6db65dd4 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/app/app.go @@ -0,0 +1,39 @@ +package app + +import ( + "fmt" + "net/http" + + "appengine" + "appengine/memcache" + + _ "github.com/mattermost/rsc/appfs/server" + _ "github.com/mattermost/rsc/blog/post" +) + +func init() { + http.HandleFunc("/admin/", Admin) +} + +func Admin(w http.ResponseWriter, req *http.Request) { + c := appengine.NewContext(req) + switch req.FormValue("op") { + default: + fmt.Fprintf(w, "unknown op %s\n", req.FormValue("op")) + case "memcache-get": + key := req.FormValue("key") + item, err := memcache.Get(c, key) + if err != nil { + fmt.Fprintf(w, "ERROR: %s\n", err) + return + } + w.Write(item.Value) + case "memcache-delete": + key := req.FormValue("key") + if err := memcache.Delete(c, key); err != nil { + fmt.Fprintf(w, "ERROR: %s\n", err) + return + } + fmt.Fprintf(w, "deleted %s\n", key) + } +} diff --git a/vendor/github.com/mattermost/rsc/app/app.yaml b/vendor/github.com/mattermost/rsc/app/app.yaml new file mode 100644 index 000000000..d45119345 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/app/app.yaml @@ -0,0 +1,23 @@ +# mkapp +# ~/pub/go_appengine/dev_appserver.py --high_replication tmp +# ~/pub/go_appengine/appcfg.py update tmp + +application: rsc-swtch-app +runtime: go +version: test +api_version: go1 + +handlers: +- url: /\.appfs.* + script: _go_app + secure: always +- url: /draft(/.*)? + script: _go_app + login: required +- url: /admin(/.*)? + script: _go_app + login: admin + +# MUST BE LAST +- url: /.* + script: _go_app diff --git a/vendor/github.com/mattermost/rsc/app/index.yaml b/vendor/github.com/mattermost/rsc/app/index.yaml new file mode 100644 index 000000000..c94524657 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/app/index.yaml @@ -0,0 +1,16 @@ +indexes: + +# AUTOGENERATED + +# This index.yaml is automatically updated whenever the dev_appserver +# detects that a new type of query is run. If you want to manage the +# index.yaml file manually, remove the above marker line (the line +# saying "# AUTOGENERATED"). If you want to manage some indexes +# manually, move them above the marker line. The index.yaml file is +# automatically uploaded to the admin console when you next deploy +# your application using appcfg.py. + +- kind: FileInfo + ancestor: yes + properties: + - name: Path diff --git a/vendor/github.com/mattermost/rsc/appfs/appfile/main.go b/vendor/github.com/mattermost/rsc/appfs/appfile/main.go new file mode 100644 index 000000000..6d77df9aa --- /dev/null +++ b/vendor/github.com/mattermost/rsc/appfs/appfile/main.go @@ -0,0 +1,156 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// appfile is a command-line interface to an appfs file system. +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "os" + + "github.com/mattermost/rsc/appfs/client" + "github.com/mattermost/rsc/keychain" +) + +var c client.Client + +func init() { + flag.StringVar(&c.Host, "h", "localhost:8080", "app serving host") + flag.StringVar(&c.User, "u", "", "user name") + flag.StringVar(&c.Password, "p", "", "password") +} + +func usage() { + fmt.Fprintf(os.Stderr, "usage: appfile [-h host] cmd args...\n") + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintf(os.Stderr, "Commands are:\n") + for _, c := range cmd { + fmt.Fprintf(os.Stderr, "\t%s\n", c.name) + } + os.Exit(2) +} + +func main() { + flag.Usage = usage + flag.Parse() + args := flag.Args() + if len(args) == 0 { + usage() + } + + if c.Password == "" { + var err error + c.User, c.Password, err = keychain.UserPasswd(c.Host, "") + if err != nil { + fmt.Fprintf(os.Stderr, "unable to obtain user and password: %s\n", err) + os.Exit(2) + } + } + + name, args := args[0], args[1:] + for _, c := range cmd { + if name == c.name { + switch c.arg { + case 0, 1: + if len(args) != c.arg { + if c.arg == 0 { + fmt.Fprintf(os.Stderr, "%s takes no arguments\n", name) + os.Exit(2) + } + fmt.Fprintf(os.Stderr, "%s requires 1 argument\n", name) + os.Exit(2) + } + case 2: + if len(args) == 0 { + fmt.Fprintf(os.Stderr, "%s requires at least 1 argument\n", name) + os.Exit(2) + } + } + c.fn(args) + return + } + } + fmt.Fprintf(os.Stderr, "unknown command %s\n", name) + os.Exit(2) +} + +var cmd = []struct { + name string + fn func([]string) + arg int +}{ + {"mkdir", mkdir, 2}, + {"write", write, 1}, + {"read", read, 2}, + {"mkfs", mkfs, 0}, + {"stat", stat, 2}, +} + +func mkdir(args []string) { + for _, name := range args { + if err := c.Create(name, true); err != nil { + log.Printf("mkdir %s: %v", name, err) + } + } +} + +func write(args []string) { + name := args[0] + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + log.Printf("reading stdin: %v", err) + return + } + c.Create(name, false) + if err := c.Write(name, data); err != nil { + log.Printf("write %s: %v", name, err) + } +} + +func read(args []string) { + for _, name := range args { + fi, err := c.Stat(name) + if err != nil { + log.Printf("stat %s: %v", name, err) + continue + } + if fi.IsDir { + dirs, err := c.ReadDir(name) + if err != nil { + log.Printf("read %s: %v", name, err) + continue + } + for _, fi := range dirs { + fmt.Printf("%+v\n", *fi) + } + } else { + data, err := c.Read(name) + if err != nil { + log.Printf("read %s: %v", name, err) + continue + } + os.Stdout.Write(data) + } + } +} + +func mkfs([]string) { + if err := c.Mkfs(); err != nil { + log.Printf("mkfs: %v", err) + } +} + +func stat(args []string) { + for _, name := range args { + fi, err := c.Stat(name) + if err != nil { + log.Printf("stat %s: %v", name, err) + continue + } + fmt.Printf("%+v\n", *fi) + } +} diff --git a/vendor/github.com/mattermost/rsc/appfs/appmount/main.go b/vendor/github.com/mattermost/rsc/appfs/appmount/main.go new file mode 100644 index 000000000..2c9f867d3 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/appfs/appmount/main.go @@ -0,0 +1,287 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// appmount mounts an appfs file system. +package main + +import ( + "bytes" + "encoding/gob" + "flag" + "fmt" + "log" + "os" + "os/exec" + "path" + "strings" + "syscall" + "time" + "sync" + "runtime" + + "github.com/mattermost/rsc/appfs/client" + "github.com/mattermost/rsc/appfs/proto" + "github.com/mattermost/rsc/fuse" + "github.com/mattermost/rsc/keychain" +) + +var usageMessage = `usage: appmount [-h host] [-u user] [-p password] /mnt + +Appmount mounts the appfs file system on the named mount point. + +The default host is localhost:8080. +` + +// Shared between master and slave. +var z struct { + Client client.Client + Debug *bool + Mtpt string +} + +var fc *fuse.Conn +var cl = &z.Client + +func init() { + flag.StringVar(&cl.Host, "h", "localhost:8080", "app serving host") + flag.StringVar(&cl.User, "u", "", "user name") + flag.StringVar(&cl.Password, "p", "", "password") + z.Debug = flag.Bool("debug", false, "") +} + +func usage() { + fmt.Fprint(os.Stderr, usageMessage) + os.Exit(2) +} + +func main() { + log.SetFlags(0) + + if len(os.Args) == 2 && os.Args[1] == "MOUNTSLAVE" { + mountslave() + return + } + + flag.Usage = usage + flag.Parse() + args := flag.Args() + if len(args) == 0 { + usage() + } + z.Mtpt = args[0] + + if cl.Password == "" { + var err error + cl.User, cl.Password, err = keychain.UserPasswd(cl.Host, "") + if err != nil { + fmt.Fprintf(os.Stderr, "unable to obtain user and password: %s\n", err) + os.Exit(2) + } + } + + if _, err := cl.Stat("/"); err != nil { + log.Fatal(err) + } + + // Run in child so that we can exit once child is running. + r, w, err := os.Pipe() + if err != nil { + log.Fatal(err) + } + + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + enc.Encode(&z) + + cmd := exec.Command(os.Args[0], "MOUNTSLAVE") + cmd.Stdin = &buf + cmd.Stdout = w + cmd.Stderr = os.Stderr + if err := cmd.Start(); err != nil { + log.Fatalf("mount process: %v", err) + } + w.Close() + + ok := make([]byte, 10) + n, _ := r.Read(ok) + if n != 2 || string(ok[0:2]) != "OK" { + os.Exit(1) + } + + fmt.Fprintf(os.Stderr, "mounted on %s\n", z.Mtpt) +} + +func mountslave() { + stdout, _ := syscall.Dup(1) + syscall.Dup2(2, 1) + + r := gob.NewDecoder(os.Stdin) + if err := r.Decode(&z); err != nil { + log.Fatalf("gob decode: %v", err) + } + + fc, err := fuse.Mount(z.Mtpt) + if err != nil { + log.Fatal(err) + } + defer exec.Command("umount", z.Mtpt).Run() + + if *z.Debug { + fuse.Debugf = log.Printf + } + + syscall.Write(stdout, []byte("OK")) + syscall.Close(stdout) + fc.Serve(FS{}) +} + +type FS struct{} + +func (FS) Root() (fuse.Node, fuse.Error) { + return file("/") +} + +type File struct { + Name string + FileInfo *proto.FileInfo + Data []byte +} + +type statEntry struct { + fi *proto.FileInfo + err error + t time.Time +} + +var statCache struct { + mu sync.Mutex + m map[string] statEntry +} + +func stat(name string) (*proto.FileInfo, error) { + if runtime.GOOS == "darwin" && strings.Contains(name, "/._") { + // Mac resource forks + return nil, fmt.Errorf("file not found") + } + statCache.mu.Lock() + e, ok := statCache.m[name] + statCache.mu.Unlock() + if ok && time.Since(e.t) < 2*time.Minute { + return e.fi, e.err + } + fi, err := cl.Stat(name) + saveStat(name, fi, err) + return fi, err +} + +func saveStat(name string, fi *proto.FileInfo, err error) { + if *z.Debug { +if fi != nil { + fmt.Fprintf(os.Stderr, "savestat %s %+v\n", name, *fi) +} else { + fmt.Fprintf(os.Stderr, "savestat %s %v\n", name, err) +} + } + statCache.mu.Lock() + if statCache.m == nil { + statCache.m = make(map[string]statEntry) + } + statCache.m[name] = statEntry{fi, err, time.Now()} + statCache.mu.Unlock() +} + +func delStat(name string) { + statCache.mu.Lock() + if statCache.m != nil { + delete(statCache.m, name) + } + statCache.mu.Unlock() +} + +func file(name string) (fuse.Node, fuse.Error) { + fi, err := stat(name) + if err != nil { + if strings.Contains(err.Error(), "no such entity") { + return nil, fuse.ENOENT + } + if *z.Debug { + log.Printf("stat %s: %v", name, err) + } + return nil, fuse.EIO + } + return &File{name, fi, nil}, nil +} + +func (f *File) Attr() (attr fuse.Attr) { + fi := f.FileInfo + attr.Mode = 0666 + if fi.IsDir { + attr.Mode |= 0111 | os.ModeDir + } + attr.Mtime = fi.ModTime + attr.Size = uint64(fi.Size) + return +} + +func (f *File) Lookup(name string, intr fuse.Intr) (fuse.Node, fuse.Error) { + return file(path.Join(f.Name, name)) +} + +func (f *File) ReadAll(intr fuse.Intr) ([]byte, fuse.Error) { + data, err := cl.Read(f.Name) + if err != nil { + log.Printf("read %s: %v", f.Name, err) + return nil, fuse.EIO + } + return data, nil +} + +func (f *File) ReadDir(intr fuse.Intr) ([]fuse.Dirent, fuse.Error) { + fis, err := cl.ReadDir(f.Name) + if err != nil { + log.Printf("read %s: %v", f.Name, err) + return nil, fuse.EIO + } + var dirs []fuse.Dirent + for _, fi := range fis { + saveStat(path.Join(f.Name, fi.Name), fi, nil) + dirs = append(dirs, fuse.Dirent{Name: fi.Name}) + } + return dirs, nil +} + +func (f *File) WriteAll(data []byte, intr fuse.Intr) fuse.Error { + defer delStat(f.Name) + if err := cl.Write(f.Name[1:], data); err != nil { + log.Printf("write %s: %v", f.Name, err) + return fuse.EIO + } + return nil +} + +func (f *File) Mkdir(req *fuse.MkdirRequest, intr fuse.Intr) (fuse.Node, fuse.Error) { + defer delStat(f.Name) + p := path.Join(f.Name, req.Name) + if err := cl.Create(p[1:], true); err != nil { + log.Printf("mkdir %s: %v", p, err) + return nil, fuse.EIO + } + delStat(p) + return file(p) +} + +func (f *File) Create(req *fuse.CreateRequest, resp *fuse.CreateResponse, intr fuse.Intr) (fuse.Node, fuse.Handle, fuse.Error) { + defer delStat(f.Name) + p := path.Join(f.Name, req.Name) + if err := cl.Create(p[1:], false); err != nil { + log.Printf("create %s: %v", p, err) + return nil, nil, fuse.EIO + } + delStat(p) + n, err := file(p) + if err != nil { + return nil, nil, err + } + return n, n, nil +} diff --git a/vendor/github.com/mattermost/rsc/appfs/client/client.go b/vendor/github.com/mattermost/rsc/appfs/client/client.go new file mode 100644 index 000000000..a1deb291d --- /dev/null +++ b/vendor/github.com/mattermost/rsc/appfs/client/client.go @@ -0,0 +1,150 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package client implements a basic appfs client. +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/mattermost/rsc/appfs/proto" +) + +type Client struct { + Host string + User string + Password string +} + +func (c *Client) url(op, path string) string { + scheme := "https" + if strings.HasPrefix(c.Host, "localhost:") { + scheme = "http" + } + if strings.HasSuffix(op, "/") && strings.HasPrefix(path, "/") { + path = path[1:] + } + return scheme + "://"+ c.User + ":" + c.Password + "@" + c.Host + op + path +} + +func (c *Client) do(u string) error { + _, err := c.get(u) + return err +} + +func (c *Client) get(u string) ([]byte, error) { + tries := 0 + for { + r, err := http.Get(u) + if err != nil { + return nil, err + } + defer r.Body.Close() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + if r.StatusCode != 200 { + if r.StatusCode == 500 { + if tries++; tries < 3 { + fmt.Printf("%s %s; sleeping\n", r.Status, data) + time.Sleep(5*time.Second) + continue + } + } + return nil, fmt.Errorf("%s %s", r.Status, data) + } + return data, nil + } + panic("unreachable") +} + +func (c *Client) post(u string, data []byte) ([]byte, error) { + tries := 0 + for { + r, err := http.Post(u, proto.PostContentType, bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + defer r.Body.Close() + rdata, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + if r.StatusCode != 200 { + if r.StatusCode == 500 { + if tries++; tries < 3 { + fmt.Printf("%s %s; sleeping\n", r.Status, rdata) + time.Sleep(5*time.Second) + continue + } + } + return nil, fmt.Errorf("%s %s", r.Status, rdata) + } + return rdata, nil + } + panic("unreachable") +} + +func (c *Client) Create(path string, isdir bool) error { + u := c.url(proto.CreateURL, path) + if isdir { + u += "?dir=1" + } + return c.do(u) +} + +func (c *Client) Read(path string) ([]byte, error) { + return c.get(c.url(proto.ReadURL, path)) +} + +func (c *Client) Write(path string, data []byte) error { + u := c.url(proto.WriteURL, path) + _, err := c.post(u, data) + return err +} + +func (c *Client) Mkfs() error { + return c.do(c.url(proto.MkfsURL, "")) +} + +func (c *Client) Stat(path string) (*proto.FileInfo, error) { + data, err := c.get(c.url(proto.StatURL, path)) + if err != nil { + return nil, err + } + var fi proto.FileInfo + if err := json.Unmarshal(data, &fi); err != nil { + return nil, err + } + return &fi, nil +} + +func (c *Client) ReadDir(path string) ([]*proto.FileInfo, error) { + data, err := c.Read(path) + if err != nil { + return nil, err + } + dec := json.NewDecoder(bytes.NewBuffer(data)) + var out []*proto.FileInfo + for { + var fi proto.FileInfo + err := dec.Decode(&fi) + if err == io.EOF { + break + } + if err != nil { + return out, err + } + out = append(out, &fi) + } + return out, nil +} diff --git a/vendor/github.com/mattermost/rsc/appfs/fs/fs.go b/vendor/github.com/mattermost/rsc/appfs/fs/fs.go new file mode 100644 index 000000000..ac6657393 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/appfs/fs/fs.go @@ -0,0 +1,273 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fs is an indirection layer, allowing code to use a +// file system without knowing whether it is the host file system +// (running without App Engine) or the datastore-based app +// file system (running on App Engine). +// +// When compiled locally, fs refers to files in the local file system, +// and the cache saves nothing. +// +// When compiled for App Engine, fs uses the appfs file system +// and the memcache-based cache. +package fs + +import ( + "bytes" + "encoding/gob" + "fmt" + "io" + "log" + "net/http" + "os" + "time" + + "github.com/mattermost/rsc/appfs/proto" +) + +type AppEngine interface { + NewContext(req *http.Request) interface{} + CacheRead(ctxt interface{}, name, path string) (key interface{}, data []byte, found bool) + CacheWrite(ctxt, key interface{}, data []byte) + Read(ctxt interface{}, path string) ([]byte, *proto.FileInfo, error) + Write(ctxt interface{}, path string, data []byte) error + Remove(ctxt interface{}, path string) error + Mkdir(ctxt interface{}, path string) error + ReadDir(ctxt interface{}, path string) ([]proto.FileInfo, error) + Criticalf(ctxt interface{}, format string, args ...interface{}) + User(ctxt interface{}) string +} + +var ae AppEngine + +func Register(impl AppEngine) { + ae = impl +} + +// Root is the root of the local file system. It has no effect on App Engine. +var Root = "." + +// A Context is an opaque context that is needed to perform file system +// operations. Each context is associated with a single HTTP request. +type Context struct { + context + ae interface{} +} + +// NewContext returns a context associated with the given HTTP request. +func NewContext(req *http.Request) *Context { + if ae != nil { + ctxt := ae.NewContext(req) + return &Context{ae: ctxt} + } + return newContext(req) +} + +// A CacheKey is an opaque cache key that can be used to store new entries +// in the cache. To ensure that the cache remains consistent with the underlying +// file system, the correct procedure is: +// +// 1. Use CacheRead (or CacheLoad) to attempt to load the entry. If it succeeds, use it. +// If not, continue, saving the CacheKey. +// +// 2. Read from the file system and construct the entry that would have +// been in the cache. In order to be consistent, all the file system reads +// should only refer to parts of the file system in the tree rooted at the path +// passed to CacheRead. +// +// 3. Save the entry using CacheWrite (or CacheStore), using the key that was +// created by the CacheRead (or CacheLoad) executed before reading from the +// file system. +// +type CacheKey struct { + cacheKey + ae interface{} +} + +// CacheRead reads from cache the entry with the given name and path. +// The path specifies the scope of information stored in the cache entry. +// An entry is invalidated by a write to any location in the file tree rooted at path. +// The name is an uninterpreted identifier to distinguish the cache entry +// from other entries using the same path. +// +// If it finds a cache entry, CacheRead returns the data and found=true. +// If it does not find a cache entry, CacheRead returns data=nil and found=false. +// Either way, CacheRead returns an appropriate cache key for storing to the +// cache entry using CacheWrite. +func (c *Context) CacheRead(name, path string) (ckey CacheKey, data []byte, found bool) { + if ae != nil { + key, data, found := ae.CacheRead(c.ae, name, path) + return CacheKey{ae: key}, data, found + } + return c.cacheRead(ckey, path) +} + +// CacheLoad uses CacheRead to load gob-encoded data and decodes it into value. +func (c *Context) CacheLoad(name, path string, value interface{}) (ckey CacheKey, found bool) { + ckey, data, found := c.CacheRead(name, path) + if found { + if err := gob.NewDecoder(bytes.NewBuffer(data)).Decode(value); err != nil { + c.Criticalf("gob Decode: %v", err) + found = false + } + } + return +} + +// CacheWrite writes an entry to the cache with the given key, path, and data. +// The cache entry will be invalidated the next time the file tree rooted at path is +// modified in anyway. +func (c *Context) CacheWrite(ckey CacheKey, data []byte) { + if ae != nil { + ae.CacheWrite(c.ae, ckey.ae, data) + return + } + c.cacheWrite(ckey, data) +} + +// CacheStore uses CacheWrite to save the gob-encoded form of value. +func (c *Context) CacheStore(ckey CacheKey, value interface{}) { + var buf bytes.Buffer + if err := gob.NewEncoder(&buf).Encode(value); err != nil { + c.Criticalf("gob Encode: %v", err) + return + } + c.CacheWrite(ckey, buf.Bytes()) +} + +// Read returns the data associated with the file named by path. +// It is a copy and can be modified without affecting the file. +func (c *Context) Read(path string) ([]byte, *proto.FileInfo, error) { + if ae != nil { + return ae.Read(c.ae, path) + } + return c.read(path) +} + +// Write replaces the data associated with the file named by path. +func (c *Context) Write(path string, data []byte) error { + if ae != nil { + return ae.Write(c.ae, path, data) + } + return c.write(path, data) +} + +// Remove removes the file named by path. +func (c *Context) Remove(path string) error { + if ae != nil { + return ae.Remove(c.ae, path) + } + return c.remove(path) +} + +// Mkdir creates a directory with the given path. +// If the path already exists and is a directory, Mkdir returns no error. +func (c *Context) Mkdir(path string) error { + if ae != nil { + return ae.Mkdir(c.ae, path) + } + return c.mkdir(path) +} + +// ReadDir returns the contents of the directory named by the path. +func (c *Context) ReadDir(path string) ([]proto.FileInfo, error) { + if ae != nil { + return ae.ReadDir(c.ae, path) + } + return c.readdir(path) +} + +// ServeFile serves the named file as the response to the HTTP request. +func (c *Context) ServeFile(w http.ResponseWriter, req *http.Request, name string) { + root := &httpFS{c, name} + http.FileServer(root).ServeHTTP(w, req) +} + +// Criticalf logs the message at critical priority. +func (c *Context) Criticalf(format string, args ...interface{}) { + if ae != nil { + ae.Criticalf(c.ae, format, args...) + } + log.Printf(format, args...) +} + +// User returns the name of the user running the request. +func (c *Context) User() string { + if ae != nil { + return ae.User(c.ae) + } + return os.Getenv("USER") +} + +type httpFS struct { + c *Context + name string +} + +type httpFile struct { + data []byte + fi *proto.FileInfo + off int +} + +func (h *httpFS) Open(_ string) (http.File, error) { + data, fi, err := h.c.Read(h.name) + if err != nil { + return nil, err + } + return &httpFile{data, fi, 0}, nil +} + +func (f *httpFile) Close() error { + return nil +} + +type fileInfo struct { + p *proto.FileInfo +} + +func (f *fileInfo) IsDir() bool { return f.p.IsDir } +func (f *fileInfo) Name() string { return f.p.Name } +func (f *fileInfo) ModTime() time.Time { return f.p.ModTime } +func (f *fileInfo) Size() int64 { return f.p.Size } +func (f *fileInfo) Sys() interface{} { return f.p } +func (f *fileInfo) Mode() os.FileMode { + if f.p.IsDir { + return os.ModeDir | 0777 + } + return 0666 +} + +func (f *httpFile) Stat() (os.FileInfo, error) { + return &fileInfo{f.fi}, nil +} + +func (f *httpFile) Readdir(count int) ([]os.FileInfo, error) { + return nil, fmt.Errorf("no directory") +} + +func (f *httpFile) Read(data []byte) (int, error) { + if f.off >= len(f.data) { + return 0, io.EOF + } + n := copy(data, f.data[f.off:]) + f.off += n + return n, nil +} + +func (f *httpFile) Seek(offset int64, whence int) (int64, error) { + off := int(offset) + if int64(off) != offset { + return 0, fmt.Errorf("invalid offset") + } + switch whence { + case 1: + off += f.off + case 2: + off += len(f.data) + } + f.off = off + return int64(off), nil +} diff --git a/vendor/github.com/mattermost/rsc/appfs/fs/local.go b/vendor/github.com/mattermost/rsc/appfs/fs/local.go new file mode 100644 index 000000000..c78b35b64 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/appfs/fs/local.go @@ -0,0 +1,82 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fs + +import ( + "io/ioutil" + "net/http" + "os" + "path/filepath" + + "github.com/mattermost/rsc/appfs/proto" +) + +type context struct{} + +type cacheKey struct{} + +func newContext(req *http.Request) *Context { + return &Context{} +} + +func (*context) cacheRead(ckey CacheKey, path string) (CacheKey, []byte, bool) { + return ckey, nil, false +} + +func (*context) cacheWrite(ckey CacheKey, data []byte) { +} + +func (*context) read(path string) ([]byte, *proto.FileInfo, error) { + p := filepath.Join(Root, path) + dir, err := os.Stat(p) + if err != nil { + return nil, nil, err + } + fi := &proto.FileInfo{ + Name: dir.Name(), + ModTime: dir.ModTime(), + Size: dir.Size(), + IsDir: dir.IsDir(), + } + data, err := ioutil.ReadFile(p) + return data, fi, err +} + +func (*context) write(path string, data []byte) error { + p := filepath.Join(Root, path) + return ioutil.WriteFile(p, data, 0666) +} + +func (*context) remove(path string) error { + p := filepath.Join(Root, path) + return os.Remove(p) +} + +func (*context) mkdir(path string) error { + p := filepath.Join(Root, path) + fi, err := os.Stat(p) + if err == nil && fi.IsDir() { + return nil + } + return os.Mkdir(p, 0777) +} + +func (*context) readdir(path string) ([]proto.FileInfo, error) { + p := filepath.Join(Root, path) + dirs, err := ioutil.ReadDir(p) + if err != nil { + return nil, err + } + var out []proto.FileInfo + for _, dir := range dirs { + out = append(out, proto.FileInfo{ + Name: dir.Name(), + ModTime: dir.ModTime(), + Size: dir.Size(), + IsDir: dir.IsDir(), + }) + } + return out, nil +} diff --git a/vendor/github.com/mattermost/rsc/appfs/proto/data.go b/vendor/github.com/mattermost/rsc/appfs/proto/data.go new file mode 100644 index 000000000..ac15411a8 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/appfs/proto/data.go @@ -0,0 +1,55 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package proto defines the protocol between appfs client and server. +package proto + +import "time" + +// An Auth appears, JSON-encoded, as the X-Appfs-Auth header line, +// to authenticate a request made to the file server. +// The authentication scheme could be made more sophisticated, but since +// we are already forcing the use of TLS, a plain password is fine for now. +type Auth struct { + Password string +} + +// GET /.appfs/stat/path returns the metadata for a file or directory, +// a JSON-encoded FileInfo. +const StatURL = "/.appfs/stat/" + +// GET /.appfs/read/path returns the content of the file or directory. +// The body of the response is the raw file or directory content. +// The content of a directory is a sequence of JSON-encoded FileInfo. +const ReadURL = "/.appfs/read/" + +// POST to /.appfs/write/path writes new data to a file. +// The X-Appfs-SHA1 header is the SHA1 hash of the data. +// The body of the request is the raw file content. +const WriteURL = "/.appfs/write/" + +// POST to /.appfs/mount initializes the file system if it does not +// yet exist in the datastore. +const MkfsURL = "/.appfs/mkfs" + +// POST to /.appfs/create/path creates a new file or directory. +// The named path must not already exist; its parent must exist. +// The query parameter dir=1 indicates that a directory should be created. +const CreateURL = "/.appfs/create/" + +// POST to /.appfs/remove/path removes the file or directory. +// A directory must be empty to be removed. +const RemoveURL = "/.appfs/remove/" + +// A FileInfo is a directory entry. +type FileInfo struct { + Name string // final path element + ModTime time.Time + Size int64 + IsDir bool +} + +// PostContentType is the Content-Type for POSTed data. +// There is no encoding or framing: it is just raw data bytes. +const PostContentType = "x-appfs/raw" diff --git a/vendor/github.com/mattermost/rsc/appfs/server/app.go b/vendor/github.com/mattermost/rsc/appfs/server/app.go new file mode 100644 index 000000000..9486eac41 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/appfs/server/app.go @@ -0,0 +1,982 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package server implements an appfs server backed by the +// App Engine datastore. +package server + +import ( + "bytes" + "crypto/sha1" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "path" + "strconv" + "strings" + "time" + + "appengine" + "appengine/datastore" + "appengine/memcache" + "appengine/user" + + "github.com/mattermost/rsc/appfs/fs" + "github.com/mattermost/rsc/appfs/proto" +) + +const pwFile = "/.password" +var chatty = false + +func init() { + handle(proto.ReadURL, (*request).read) + handle(proto.WriteURL, (*request).write) + handle(proto.StatURL, (*request).stat) + handle(proto.MkfsURL, (*request).mkfs) + handle(proto.CreateURL, (*request).create) + handle(proto.RemoveURL, (*request).remove) +} + +type request struct { + w http.ResponseWriter + req *http.Request + c appengine.Context + name string + mname string + key *datastore.Key +} + +func auth(r *request) bool { + hdr := r.req.Header.Get("Authorization") + if !strings.HasPrefix(hdr, "Basic ") { + return false + } + data, err := base64.StdEncoding.DecodeString(hdr[6:]) + if err != nil { + return false + } + i := bytes.IndexByte(data, ':') + if i < 0 { + return false + } + user, passwd := string(data[:i]), string(data[i+1:]) + + _, data, err = read(r.c, pwFile) + if err != nil { + r.c.Errorf("reading %s: %v", pwFile, err) + if _, err := mkfs(r.c); err != nil { + r.c.Errorf("creating fs: %v", err) + } + _, data, err = read(r.c, pwFile) + if err != nil { + r.c.Errorf("reading %s again: %v", pwFile, err) + return false + } + } + + lines := strings.Split(string(data), "\n") + for _, line := range lines { + if strings.HasPrefix(line, "#") { + continue + } + f := strings.Fields(line) + if len(f) < 3 { + continue + } + if f[0] == user { + return hash(f[1]+passwd) == f[2] + } + } + return false +} + +func hash(s string) string { + h := sha1.New() + h.Write([]byte(s)) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +func handle(prefix string, f func(*request)) { + http.HandleFunc(prefix, func(w http.ResponseWriter, req *http.Request) { + c := appengine.NewContext(req) + r := &request{ + w: w, + req: req, + c: c, + } + + if strings.HasSuffix(prefix, "/") { + r.name, r.mname, r.key = mangle(c, req.URL.Path[len(prefix)-1:]) + } else { + req.URL.Path = "/" + } + defer func() { + if err := recover(); err != nil { + w.WriteHeader(http.StatusConflict) + fmt.Fprintf(w, "%s\n", err) + } + }() + + if !auth(r) { + w.Header().Set("WWW-Authenticate", "Basic realm=\"appfs\"") + http.Error(w, "Need auth", http.StatusUnauthorized) + return + } + + f(r) + }) +} + +func mangle(c appengine.Context, name string) (string, string, *datastore.Key) { + name = path.Clean("/" + name) + n := strings.Count(name, "/") + if name == "/" { + n = 0 + } + mname := fmt.Sprintf("%d%s", n, name) + root := datastore.NewKey(c, "RootKey", "v2:", 0, nil) + key := datastore.NewKey(c, "FileInfo", mname, 0, root) + return name, mname, key +} + +type FileInfo struct { + Path string // mangled path + Name string + Qid int64 // assigned unique id number + Seq int64 // modification sequence number in file tree + ModTime time.Time + Size int64 + IsDir bool +} + +type FileData struct { + Data []byte +} + +func stat(c appengine.Context, name string) (*FileInfo, error) { + var fi FileInfo + name, _, key := mangle(c, name) + c.Infof("DATASTORE Stat %q", name) + err := datastore.Get(c, key, &fi) + if err != nil { + return nil, err + } + return &fi, nil +} + +func (r *request) saveStat(fi *FileInfo) { + jfi, err := json.Marshal(&fi) + if err != nil { + panic(err) + } + r.w.Header().Set("X-Appfs-Stat", string(jfi)) +} + +func (r *request) tx(f func(c appengine.Context) error) { + err := datastore.RunInTransaction(r.c, f, &datastore.TransactionOptions{XG: true}) + if err != nil { + panic(err) + } +} + +func (r *request) stat() { + var fi *FileInfo + r.tx(func(c appengine.Context) error { + fi1, err := stat(c, r.name) + if err != nil { + return err + } + fi = fi1 + return nil + }) + + jfi, err := json.Marshal(&fi) + if err != nil { + panic(err) + } + r.w.Write(jfi) +} + +func read(c appengine.Context, name string) (fi *FileInfo, data []byte, err error) { + name, _, _ = mangle(c, name) + fi1, err := stat(c, name) + if err != nil { + return nil, nil, err + } + if fi1.IsDir { + dt, err := readdir(c, name) + if err != nil { + return nil, nil, err + } + fi = fi1 + data = dt + return fi, data, nil + } + + root := datastore.NewKey(c, "RootKey", "v2:", 0, nil) + dkey := datastore.NewKey(c, "FileData", "", fi1.Qid, root) + var fd FileData + c.Infof("DATASTORE Read %q", name) + if err := datastore.Get(c, dkey, &fd); err != nil { + return nil, nil, err + } + fi = fi1 + data = fd.Data + return fi, data, nil +} + +func (r *request) read() { + var ( + fi *FileInfo + data []byte + ) + r.tx(func(c appengine.Context) error { + var err error + fi, data, err = read(r.c, r.name) + return err + }) + r.saveStat(fi) + r.w.Write(data) +} + +func readdir(c appengine.Context, name string) ([]byte, error) { + name, _, _ = mangle(c, name) + var buf bytes.Buffer + + n := strings.Count(name, "/") + if name == "/" { + name = "" + n = 0 + } + root := datastore.NewKey(c, "RootKey", "v2:", 0, nil) + first := fmt.Sprintf("%d%s/", n+1, name) + limit := fmt.Sprintf("%d%s0", n+1, name) + c.Infof("DATASTORE ReadDir %q", name) + q := datastore.NewQuery("FileInfo"). + Filter("Path >=", first). + Filter("Path <", limit). + Ancestor(root) + enc := json.NewEncoder(&buf) + it := q.Run(c) + var fi FileInfo + var pfi proto.FileInfo + for { + fi = FileInfo{} + _, err := it.Next(&fi) + if err != nil { + if err == datastore.Done { + break + } + return nil, err + } + pfi = proto.FileInfo{ + Name: fi.Name, + ModTime: fi.ModTime, + Size: fi.Size, + IsDir: fi.IsDir, + } + if err := enc.Encode(&pfi); err != nil { + return nil, err + } + } + + return buf.Bytes(), nil +} + +func readdirRaw(c appengine.Context, name string) ([]proto.FileInfo, error) { + name, _, _ = mangle(c, name) + n := strings.Count(name, "/") + if name == "/" { + name = "" + n = 0 + } + root := datastore.NewKey(c, "RootKey", "v2:", 0, nil) + first := fmt.Sprintf("%d%s/", n+1, name) + limit := fmt.Sprintf("%d%s0", n+1, name) + c.Infof("DATASTORE ReadDir %q", name) + q := datastore.NewQuery("FileInfo"). + Filter("Path >=", first). + Filter("Path <", limit). + Ancestor(root) + it := q.Run(c) + var fi FileInfo + var pfi proto.FileInfo + var out []proto.FileInfo + for { + fi = FileInfo{} + _, err := it.Next(&fi) + if err != nil { + if err == datastore.Done { + break + } + return nil, err + } + pfi = proto.FileInfo{ + Name: fi.Name, + ModTime: fi.ModTime, + Size: fi.Size, + IsDir: fi.IsDir, + } + out = append(out, pfi) + } +println("READDIR", name, len(out)) + return out, nil +} + + +var initPasswd = `# Password file +# This file controls access to the server. +# The format is lines of space-separated fields: +# user salt pwhash +# The pwhash is the SHA1 of the salt string concatenated with the password. + +# user=dummy password=dummy (replace with your own entries) +dummy 12345 faa863c7d3d41893f80165c704b714d5e31bdd3b +` + +func (r *request) mkfs() { + var fi *FileInfo + r.tx(func(c appengine.Context) error { + var err error + fi, err = mkfs(c) + return err + }) + r.saveStat(fi) +} + +func mkfs(c appengine.Context) (fi *FileInfo, err error) { + fi1, err := stat(c, "/") + if err == nil { + return fi1, nil + } + + // Root needs to be created. + // Probably root key does too. + root := datastore.NewKey(c, "RootKey", "v2:", 0, nil) + _, err = datastore.Put(c, root, &struct{}{}) + if err != nil { + return nil, fmt.Errorf("mkfs put root: %s", err) + } + + // Entry for /. + _, mpath, key := mangle(c, "/") + fi3 := FileInfo{ + Path: mpath, + Name: "/", + Seq: 2, // 2, not 1, because we're going to write password file with #2 + Qid: 1, + ModTime: time.Now(), + Size: 0, + IsDir: true, + } + _, err = datastore.Put(c, key, &fi3) + if err != nil { + return nil, fmt.Errorf("mkfs put /: %s", err) + } + + /* + * Would like to use this code but App Engine apparently + * does not let Get observe the effect of a Put in the same + * transaction. What planet does that make sense on? + * Instead, we have to execute just the datastore writes that this + * sequence would. + * + _, err = create(c, pwFile, false) + if err != nil { + return nil, fmt.Errorf("mkfs create .password: %s", err) + } + _, err = write(c, pwFile, []byte(initPasswd)) + if err != nil { + return nil, fmt.Errorf("mkfs write .password: %s", err) + } + * + */ + + { + name, mname, key := mangle(c, pwFile) + + // Create data object. + dataKey := int64(2) + root := datastore.NewKey(c, "RootKey", "v2:", 0, nil) + dkey := datastore.NewKey(c, "FileData", "", dataKey, root) + _, err := datastore.Put(c, dkey, &FileData{[]byte(initPasswd)}) + if err != nil { + return nil, err + } + + // Create new directory entry. + _, elem := path.Split(name) + fi1 = &FileInfo{ + Path: mname, + Name: elem, + Qid: 2, + Seq: 2, + ModTime: time.Now(), + Size: int64(len(initPasswd)), + IsDir: false, + } + if _, err := datastore.Put(c, key, fi1); err != nil { + return nil, err + } + } + + return &fi3, nil +} + +func (r *request) write() { + data, err := ioutil.ReadAll(r.req.Body) + if err != nil { + panic(err) + } + + var fi *FileInfo + var seq int64 + r.tx(func(c appengine.Context) error { + var err error + fi, seq, err = write(r.c, r.name, data) + return err + }) + updateCacheTime(r.c, seq) + r.saveStat(fi) +} + +func write(c appengine.Context, name string, data []byte) (*FileInfo, int64, error) { + name, _, key := mangle(c, name) + + // Check that file exists and is not a directory. + fi1, err := stat(c, name) + if err != nil { + return nil, 0, err + } + if fi1.IsDir { + return nil, 0, fmt.Errorf("cannot write to directory") + } + + // Fetch and increment root sequence number. + rfi, err := stat(c, "/") + if err != nil { + return nil, 0, err + } + rfi.Seq++ + + // Write data. + root := datastore.NewKey(c, "RootKey", "v2:", 0, nil) + dkey := datastore.NewKey(c, "FileData", "", fi1.Qid, root) + fd := &FileData{data} + if _, err := datastore.Put(c, dkey, fd); err != nil { + return nil, 0, err + } + + // Update directory entry. + fi1.Seq = rfi.Seq + fi1.Size = int64(len(data)) + fi1.ModTime = time.Now() + if _, err := datastore.Put(c, key, fi1); err != nil { + return nil, 0, err + } + + // Update sequence numbers all the way to the root. + if err := updateSeq(c, name, rfi.Seq, 1); err != nil { + return nil, 0, err + } + + return fi1, rfi.Seq, nil +} + +func updateSeq(c appengine.Context, name string, seq int64, skip int) error { + p := path.Clean(name) + for i := 0; ; i++ { + if i >= skip { + _, _, key := mangle(c, p) + var fi FileInfo + if err := datastore.Get(c, key, &fi); err != nil { + return err + } + fi.Seq = seq + if _, err := datastore.Put(c, key, &fi); err != nil { + return err + } + } + if p == "/" { + break + } + p, _ = path.Split(p) + p = path.Clean(p) + } + return nil +} + +func (r *request) remove() { + panic("remove not implemented") +} + +func (r *request) create() { + var fi *FileInfo + var seq int64 + isDir := r.req.FormValue("dir") == "1" + r.tx(func(c appengine.Context) error { + var err error + fi, seq, err = create(r.c, r.name, isDir, nil) + return err + }) + updateCacheTime(r.c, seq) + r.saveStat(fi) +} + +func create(c appengine.Context, name string, isDir bool, data []byte) (*FileInfo, int64, error) { + name, mname, key := mangle(c, name) + + // File must not exist. + fi1, err := stat(c, name) + if err == nil { + return nil, 0, fmt.Errorf("file already exists") + } + if err != datastore.ErrNoSuchEntity { + return nil, 0, err + } + + // Parent must exist and be a directory. + p, _ := path.Split(name) + fi2, err := stat(c, p) + if err != nil { + if err == datastore.ErrNoSuchEntity { + return nil, 0, fmt.Errorf("parent directory %q does not exist", p) + } + return nil, 0, err + } + if !fi2.IsDir { + return nil, 0, fmt.Errorf("parent %q is not a directory", p) + } + + // Fetch and increment root sequence number. + rfi, err := stat(c, "/") + if err != nil { + return nil, 0, err + } + rfi.Seq++ + + var dataKey int64 + // Create data object. + if !isDir { + dataKey = rfi.Seq + root := datastore.NewKey(c, "RootKey", "v2:", 0, nil) + dkey := datastore.NewKey(c, "FileData", "", dataKey, root) + _, err := datastore.Put(c, dkey, &FileData{data}) + if err != nil { + return nil, 0, err + } + } + + // Create new directory entry. + _, elem := path.Split(name) + fi1 = &FileInfo{ + Path: mname, + Name: elem, + Qid: rfi.Seq, + Seq: rfi.Seq, + ModTime: time.Now(), + Size: int64(len(data)), + IsDir: isDir, + } + if _, err := datastore.Put(c, key, fi1); err != nil { + return nil, 0, err + } + + // Update sequence numbers all the way to root, + // but skip entry we just wrote. + if err := updateSeq(c, name, rfi.Seq, 1); err != nil { + return nil, 0, err + } + + return fi1, rfi.Seq, nil +} + +// Implementation of fs.AppEngine. + +func init() { + fs.Register(ae{}) +} + +type ae struct{} + +func tx(c interface{}, f func(c appengine.Context) error) error { + return datastore.RunInTransaction(c.(appengine.Context), f, &datastore.TransactionOptions{XG: true}) +} + +func (ae) NewContext(req *http.Request) interface{} { + return appengine.NewContext(req) +} + +func (ae) User(ctxt interface{}) string { + c := ctxt.(appengine.Context) + u := user.Current(c) + if u == nil { + return "?" + } + return u.String() +} + +type cacheKey struct { + t int64 + name string +} + +func (ae) CacheRead(ctxt interface{}, name, path string) (key interface{}, data []byte, found bool) { + c := ctxt.(appengine.Context) + t, data, _, err := cacheRead(c, "cache", name, path) + return &cacheKey{t, name}, data, err == nil +} + +func (ae) CacheWrite(ctxt, key interface{}, data []byte) { + c := ctxt.(appengine.Context) + k := key.(*cacheKey) + cacheWrite(c, k.t, "cache", k.name, data) +} + +func (ae ae) Read(ctxt interface{}, name string) (data []byte, pfi *proto.FileInfo, err error) { + c := ctxt.(appengine.Context) + name = path.Clean("/"+name) + if chatty { + c.Infof("AE Read %s", name) + } + _, data, pfi, err = cacheRead(c, "data", name, name) + if err != nil { + err = fmt.Errorf("Read %q: %v", name, err) + } + return +} + +func (ae) Write(ctxt interface{}, path string, data []byte) error { + var seq int64 + err := tx(ctxt, func(c appengine.Context) error { + _, err := stat(c, path) + if err != nil { + _, seq, err = create(c, path, false, data) + } else { + _, seq, err = write(c, path, data) + } + return err + }) + if seq != 0 { + updateCacheTime(ctxt.(appengine.Context), seq) + } + if err != nil { + err = fmt.Errorf("Write %q: %v", path, err) + } + return err +} + +func (ae) Remove(ctxt interface{}, path string) error { + return fmt.Errorf("remove not implemented") +} + +func (ae) Mkdir(ctxt interface{}, path string) error { + var seq int64 + err := tx(ctxt, func(c appengine.Context) error { + var err error + _, seq, err = create(c, path, true, nil) + return err + }) + if seq != 0 { + updateCacheTime(ctxt.(appengine.Context), seq) + } + if err != nil { + err = fmt.Errorf("Mkdir %q: %v", path, err) + } + return err +} + +func (ae) Criticalf(ctxt interface{}, format string, args ...interface{}) { + ctxt.(appengine.Context).Criticalf(format, args...) +} + +type readDirCacheEntry struct { + Dir []proto.FileInfo + Error string +} + +func (ae) ReadDir(ctxt interface{}, name string) (dir []proto.FileInfo, err error) { + c := ctxt.(appengine.Context) + name = path.Clean("/"+name) + t, data, _, err := cacheRead(c, "dir", name, name) + if err == nil { + var e readDirCacheEntry + if err := json.Unmarshal(data, &e); err == nil { + if chatty { + c.Infof("cached ReadDir %q", name) + } + if e.Error != "" { + return nil, errors.New(e.Error) + } + return e.Dir, nil + } + c.Criticalf("unmarshal cached dir %q: %v", name) + } + err = tx(ctxt, func(c appengine.Context) error { + var err error + dir, err = readdirRaw(c, name) + return err + }) + var e readDirCacheEntry + e.Dir = dir + if err != nil { + err = fmt.Errorf("ReadDir %q: %v", name, err) + e.Error = err.Error() + } + if data, err := json.Marshal(&e); err != nil { + c.Criticalf("json marshal cached dir: %v", err) + } else { + c.Criticalf("caching dir %q@%d %d bytes", name, t, len(data)) + cacheWrite(c, t, "dir", name, data) + } + return +} + +// Caching of file system data. +// +// The cache stores entries under keys of the form time,space,name, +// where time is the time at which the entry is valid for, space is a name +// space identifier, and name is an arbitrary name. +// +// A key of the form t,mtime,path maps to an integer value giving the +// modification time of the named path at root time t. +// The special key 0,mtime,/ is an integer giving the current time at the root. +// +// A key of the form t,data,path maps to the content of path at time t. +// +// Thus, a read from path should first obtain the root time, +// then obtain the modification time for the path at that root time +// then obtain the data for that path. +// t1 = get(0,mtime,/) +// t2 = get(t1,mtime,path) +// data = get(t2,data,path) +// +// The API allows clients to cache their own data too, with expiry tied to +// the modification time of a particular path (file or directory). To look +// up one of those, we use: +// t1 = get(0,mtime,/) +// t2 = get(t1,mtime,path) +// data = get(t2,clientdata,name) +// +// To store data in the cache, the t1, t2 should be determined before reading +// from datastore. Then the data should be saved under t2. This ensures +// that if a datastore update happens after the read but before the cache write, +// we'll be writing to an entry that will no longer be used (t2). + +const rootMemcacheKey = "0,mtime,/" + +func updateCacheTime(c appengine.Context, seq int64) { + const key = rootMemcacheKey + bseq := []byte(strconv.FormatInt(seq, 10)) + for tries := 0; tries < 10; tries++ { + item, err := memcache.Get(c, key) + if err != nil { + c.Infof("memcache.Get %q: %v", key, err) + err = memcache.Add(c, &memcache.Item{Key: key, Value: bseq}) + if err == nil { + c.Infof("memcache.Add %q %q ok", key, bseq) + return + } + c.Infof("memcache.Add %q %q: %v", key, bseq, err) + } + v, err := strconv.ParseInt(string(item.Value), 10, 64) + if err != nil { + c.Criticalf("memcache.Get %q = %q (%v)", key, item.Value, err) + return + } + if v >= seq { + return + } + item.Value = bseq + err = memcache.CompareAndSwap(c, item) + if err == nil { + c.Infof("memcache.CAS %q %d->%d ok", key, v, seq) + return + } + c.Infof("memcache.CAS %q %d->%d: %v", key, v, seq, err) + } + c.Criticalf("repeatedly failed to update root key") +} + +func cacheTime(c appengine.Context) (t int64, err error) { + const key = rootMemcacheKey + item, err := memcache.Get(c, key) + if err == nil { + v, err := strconv.ParseInt(string(item.Value), 10, 64) + if err == nil { + if chatty { + c.Infof("cacheTime %q = %v", key, v) + } + return v, nil + } + c.Criticalf("memcache.Get %q = %q (%v) - deleting", key, item.Value, err) + memcache.Delete(c, key) + } + fi, err := stat(c, "/") + if err != nil { + c.Criticalf("stat /: %v", err) + return 0, err + } + updateCacheTime(c, fi.Seq) + return fi.Seq, nil +} + +func cachePathTime(c appengine.Context, path string) (t int64, err error) { + t, err = cacheTime(c) + if err != nil { + return 0, err + } + + key := fmt.Sprintf("%d,mtime,%s", t, path) + item, err := memcache.Get(c, key) + if err == nil { + v, err := strconv.ParseInt(string(item.Value), 10, 64) + if err == nil { + if chatty { + c.Infof("cachePathTime %q = %v", key, v) + } + return v, nil + } + c.Criticalf("memcache.Get %q = %q (%v) - deleting", key, item.Value, err) + memcache.Delete(c, key) + } + + var seq int64 + if fi, err := stat(c, path); err == nil { + seq = fi.Seq + } + + c.Infof("cachePathTime save %q = %v", key, seq) + item = &memcache.Item{Key: key, Value: []byte(strconv.FormatInt(seq, 10))} + if err := memcache.Set(c, item); err != nil { + c.Criticalf("memcache.Set %q %q: %v", key, item.Value, err) + } + return seq, nil +} + +type statCacheEntry struct { + FileInfo *proto.FileInfo + Error string +} + +func cacheRead(c appengine.Context, kind, name, path string) (mtime int64, data []byte, pfi *proto.FileInfo, err error) { + for tries := 0; tries < 10; tries++ { + t, err := cachePathTime(c, path) + if err != nil { + return 0, nil, nil, err + } + + key := fmt.Sprintf("%d,%s,%s", t, kind, name) + item, err := memcache.Get(c, key) + var data []byte + if item != nil { + data = item.Value + } + if err != nil { + c.Infof("memcache miss %q %v", key, err) + } else if chatty { + c.Infof("memcache hit %q (%d bytes)", key, len(data)) + } + if kind != "data" { + // Not a file; whatever memcache says is all we have. + return t, data, nil, err + } + + // Load stat from cache (includes negative entry). + statkey := fmt.Sprintf("%d,stat,%s", t, name) + var st statCacheEntry + _, err = memcache.JSON.Get(c, statkey, &st) + if err == nil { + if st.Error != "" { + if chatty { + c.Infof("memcache hit stat error %q %q", statkey, st.Error) + } + err = errors.New(st.Error) + } else { + if chatty { + c.Infof("memcache hit stat %q", statkey) + } + } + if err != nil || data != nil { + return t, data, st.FileInfo, err + } + } + + // Need stat, or maybe stat+data. + var fi *FileInfo + if data != nil { + c.Infof("stat %q", name) + fi, err = stat(c, name) + if err == nil && fi.Seq != t { + c.Criticalf("loaded %s but found stat %d", key, fi.Seq) + continue + } + } else { + c.Infof("read %q", name) + fi, data, err = read(c, name) + if err == nil && fi.Seq != t { + c.Infof("loaded %s but found read %d", key, fi.Seq) + t = fi.Seq + key = fmt.Sprintf("%d,data,%s", t, name) + statkey = fmt.Sprintf("%d,stat,%s", t, name) + } + + // Save data to memcache. + if err == nil { + if true || chatty { + c.Infof("save data in memcache %q", key) + } + item := &memcache.Item{Key: key, Value: data} + if err := memcache.Set(c, item); err != nil { + c.Criticalf("failed to cache %s: %v", key, err) + } + } + } + + // Cache stat, including error. + st = statCacheEntry{} + if fi != nil { + st.FileInfo = &proto.FileInfo{ + Name: fi.Name, + ModTime: fi.ModTime, + Size: fi.Size, + IsDir: fi.IsDir, + } + } + if err != nil { + st.Error = err.Error() + // If this is a deadline exceeded, do not cache. + if strings.Contains(st.Error, "Canceled") || strings.Contains(st.Error, "Deadline") { + return t, data, st.FileInfo, err + } + } + if chatty { + c.Infof("save stat in memcache %q", statkey) + } + if err := memcache.JSON.Set(c, &memcache.Item{Key: statkey, Object: &st}); err != nil { + c.Criticalf("failed to cache %s: %v", statkey, err) + } + + // Done! + return t, data, st.FileInfo, err + } + + c.Criticalf("failed repeatedly in cacheRead") + return 0, nil, nil, errors.New("cacheRead loop failed") +} + +func cacheWrite(c appengine.Context, t int64, kind, name string, data []byte) error { + mkey := fmt.Sprintf("%d,%s,%s", t, kind, name) + if true || chatty { + c.Infof("cacheWrite %s %d bytes", mkey, len(data)) + } + err := memcache.Set(c, &memcache.Item{Key: mkey, Value: data}) + if err != nil { + c.Criticalf("cacheWrite memcache.Set %q: %v", mkey, err) + } + return err +} diff --git a/vendor/github.com/mattermost/rsc/arq/arq.go b/vendor/github.com/mattermost/rsc/arq/arq.go new file mode 100644 index 000000000..85a5138e9 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/arq/arq.go @@ -0,0 +1,663 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package arq implements read-only access to Arq backups stored on S3. +// Arq is a Mac backup tool (http://www.haystacksoftware.com/arq/) +// but the package can read the backups regardless of operating system. +package arq + +import ( + "bytes" + "compress/gzip" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/mattermost/rsc/plist" + "launchpad.net/goamz/aws" + "launchpad.net/goamz/s3" +) + +// A Conn represents a connection to an S3 server holding Arq backups. +type Conn struct { + b *s3.Bucket + cache string + altCache string +} + +// cachedir returns the canonical directory in which to cache data. +func cachedir() string { + if runtime.GOOS == "darwin" { + return filepath.Join(os.Getenv("HOME"), "Library/Caches/arq-cache") + } + return filepath.Join(os.Getenv("HOME"), ".cache/arq-cache") +} + +// Dial establishes a connection to an S3 server holding Arq backups. +func Dial(auth aws.Auth) (*Conn, error) { + buck := fmt.Sprintf("%s-com-haystacksoftware-arq", strings.ToLower(auth.AccessKey)) + b := s3.New(auth, aws.USEast).Bucket(buck) + c := &Conn{ + b: b, + cache: filepath.Join(cachedir(), buck), + } + if runtime.GOOS == "darwin" { + c.altCache = filepath.Join(os.Getenv("HOME"), "Library/Arq/Cache.noindex/"+buck) + } + + // Check that the bucket works by listing computers (relatively cheap). + if _, err := c.list("", "/", 10); err != nil { + return nil, err + } + + // Create S3 lookaside cache directory. + + return c, nil +} + +func (c *Conn) list(prefix, delim string, max int) (*s3.ListResp, error) { + resp, err := c.b.List(prefix, delim, "", max) + if err != nil { + return nil, err + } + ret := resp + for max == 0 && resp.IsTruncated { + last := resp.Contents[len(resp.Contents)-1].Key + resp, err = c.b.List(prefix, delim, last, max) + if err != nil { + return ret, err + } + ret.Contents = append(ret.Contents, resp.Contents...) + ret.CommonPrefixes = append(ret.CommonPrefixes, resp.CommonPrefixes...) + } + return ret, nil +} + +func (c *Conn) altCachePath(name string) string { + if c.altCache == "" || !strings.Contains(name, "/packsets/") { + return "" + } + i := strings.Index(name, "-trees/") + if i < 0 { + i = strings.Index(name, "-blobs/") + if i < 0 { + return "" + } + } + i += len("-trees/") + 2 + if i >= len(name) { + return "" + } + return filepath.Join(c.altCache, name[:i]+"/"+name[i:]) +} + +func (c *Conn) cget(name string) (data []byte, err error) { + cache := filepath.Join(c.cache, name) + f, err := os.Open(cache) + if err == nil { + defer f.Close() + return ioutil.ReadAll(f) + } + if altCache := c.altCachePath(name); altCache != "" { + f, err := os.Open(altCache) + if err == nil { + defer f.Close() + return ioutil.ReadAll(f) + } + } + + data, err = c.bget(name) + if err != nil { + return nil, err + } + + dir, _ := filepath.Split(cache) + os.MkdirAll(dir, 0700) + ioutil.WriteFile(cache, data, 0600) + return data, nil +} + +func (c *Conn) bget(name string) (data []byte, err error) { + for i := 0; ; { + data, err = c.b.Get(name) + if err == nil { + break + } + if i++; i >= 5 { + return nil, err + } + log.Print(err) + } + return data, nil +} + +func (c *Conn) DeleteCache() { + os.RemoveAll(c.cache) +} + +// Computers returns a list of the computers with backups available on the S3 server. +func (c *Conn) Computers() ([]*Computer, error) { + // Each backup is a top-level directory with a computerinfo file in it. + list, err := c.list("", "/", 0) + if err != nil { + return nil, err + } + var out []*Computer + for _, p := range list.CommonPrefixes { + data, err := c.bget(p + "computerinfo") + if err != nil { + continue + } + var info computerInfo + if err := plist.Unmarshal(data, &info); err != nil { + return nil, err + } + + comp := &Computer{ + Name: info.ComputerName, + User: info.UserName, + UUID: p[:len(p)-1], + conn: c, + index: map[score]ientry{}, + } + + salt, err := c.cget(p + "salt") + if err != nil { + return nil, err + } + comp.crypto.salt = salt + + out = append(out, comp) + } + return out, nil +} + +// A Computer represents a computer with backups (Folders). +type Computer struct { + Name string // name of computer + User string // name of user + UUID string + conn *Conn + crypto cryptoState + index map[score]ientry +} + +// Folders returns a list of the folders that have been backed up on the computer. +func (c *Computer) Folders() ([]*Folder, error) { + // Each folder is a file under computer/buckets/. + list, err := c.conn.list(c.UUID+"/buckets/", "", 0) + if err != nil { + return nil, err + } + var out []*Folder + for _, obj := range list.Contents { + data, err := c.conn.bget(obj.Key) + if err != nil { + return nil, err + } + var info folderInfo + if err := plist.Unmarshal(data, &info); err != nil { + return nil, err + } + out = append(out, &Folder{ + Path: info.LocalPath, + uuid: info.BucketUUID, + comp: c, + conn: c.conn, + }) + } + return out, nil +} + +// Unlock records the password to use when decrypting +// backups from this computer. It must be called before calling Trees +// in any folder obtained for this computer. +func (c *Computer) Unlock(pw string) { + c.crypto.unlock(pw) +} + +func (c *Computer) scget(sc score) ([]byte, error) { + if c.crypto.c == nil { + return nil, fmt.Errorf("computer not yet unlocked") + } + + var data []byte + var err error + ie, ok := c.index[sc] + if ok { + data, err = c.conn.cget(ie.File) + if err != nil { + return nil, err + } + + //fmt.Printf("offset size %d %d\n", ie.Offset, ie.Size) + if len(data) < int(ie.Offset+ie.Size) { + return nil, fmt.Errorf("short pack block") + } + + data = data[ie.Offset:] + if ie.Size < 1+8+1+8+8 { + return nil, fmt.Errorf("short pack block") + } + + bo := binary.BigEndian + + if data[0] != 1 { + return nil, fmt.Errorf("missing mime type") + } + n := bo.Uint64(data[1:]) + if 1+8+n > uint64(len(data)) { + return nil, fmt.Errorf("malformed mime type") + } + mimeType := data[1+8 : 1+8+n] + data = data[1+8+n:] + + n = bo.Uint64(data[1:]) + if 1+8+n > uint64(len(data)) { + return nil, fmt.Errorf("malformed name") + } + name := data[1+8 : 1+8+n] + data = data[1+8+n:] + + _, _ = mimeType, name + // fmt.Printf("%s %s\n", mimeType, name) + + n = bo.Uint64(data[0:]) + if int64(n) != ie.Size { + return nil, fmt.Errorf("unexpected data length %d %d", n, ie.Size) + } + if 8+n > uint64(len(data)) { + return nil, fmt.Errorf("short data %d %d", 8+n, len(data)) + } + + data = data[8 : 8+n] + } else { + data, err = c.conn.cget(c.UUID + "/objects/" + sc.String()) + if err != nil { + log.Fatal(err) + } + } + + data = c.crypto.decrypt(data) + return data, nil +} + +// A Folder represents a backed-up tree on a computer. +type Folder struct { + Path string // root of tree of last backup + uuid string + comp *Computer + conn *Conn +} + +// Load loads xxx +func (f *Folder) Load() error { + if err := f.comp.loadPack(f.uuid, "-trees"); err != nil { + return err + } + if err := f.comp.loadPack(f.uuid, "-blobs"); err != nil { + return err + } + return nil +} + +func (c *Computer) loadPack(fold, suf string) error { + list, err := c.conn.list(c.UUID+"/packsets/"+fold+suf+"/", "", 0) + if err != nil { + return err + } + + for _, obj := range list.Contents { + if !strings.HasSuffix(obj.Key, ".index") { + continue + } + data, err := c.conn.cget(obj.Key) + if err != nil { + return err + } + // fmt.Printf("pack %s\n", obj.Key) + c.saveIndex(obj.Key[:len(obj.Key)-len(".index")]+".pack", data) + } + return nil +} + +func (c *Computer) saveIndex(file string, data []byte) error { + const ( + headerSize = 4 + 4 + 4*256 + entrySize = 8 + 8 + 20 + 4 + trailerSize = 20 + ) + bo := binary.BigEndian + if len(data) < headerSize+trailerSize { + return fmt.Errorf("short index") + } + i := len(data) - trailerSize + sum1 := sha(data[:i]) + sum2 := binaryScore(data[i:]) + if !sum1.Equal(sum2) { + return fmt.Errorf("invalid sha index") + } + + obj := data[headerSize : len(data)-trailerSize] + n := len(obj) / entrySize + if n*entrySize != len(obj) { + return fmt.Errorf("misaligned index %d %d", n*entrySize, len(obj)) + } + nn := bo.Uint32(data[headerSize-4:]) + if int(nn) != n { + return fmt.Errorf("inconsistent index %d %d\n", nn, n) + } + + for i := 0; i < n; i++ { + e := obj[i*entrySize:] + var ie ientry + ie.File = file + ie.Offset = int64(bo.Uint64(e[0:])) + ie.Size = int64(bo.Uint64(e[8:])) + ie.Score = binaryScore(e[16:]) + c.index[ie.Score] = ie + } + return nil +} + +// Trees returns a list of the individual backup snapshots for the folder. +// Note that different trees from the same Folder might have different Paths +// if the folder was "relocated" using the Arq interface. +func (f *Folder) Trees() ([]*Tree, error) { + data, err := f.conn.bget(f.comp.UUID + "/bucketdata/" + f.uuid + "/refs/heads/master") + if err != nil { + return nil, err + } + sc := hexScore(string(data)) + if err != nil { + return nil, err + } + + var out []*Tree + for { + data, err = f.comp.scget(sc) + if err != nil { + return nil, err + } + + var com commit + if err := unpack(data, &com); err != nil { + return nil, err + } + + var info folderInfo + if err := plist.Unmarshal(com.BucketXML, &info); err != nil { + return nil, err + } + + t := &Tree{ + Time: com.CreateTime, + Path: info.LocalPath, + Score: com.Tree.Score, + + commit: com, + comp: f.comp, + folder: f, + info: info, + } + out = append(out, t) + + if len(com.ParentCommits) == 0 { + break + } + + sc = com.ParentCommits[0].Score + } + + for i, n := 0, len(out)-1; i < n-i; i++ { + out[i], out[n-i] = out[n-i], out[i] + } + return out, nil +} + +func (f *Folder) Trees2() ([]*Tree, error) { + list, err := f.conn.list(f.comp.UUID+"/bucketdata/"+f.uuid+"/refs/logs/master/", "", 0) + if err != nil { + return nil, err + } + + var out []*Tree + for _, obj := range list.Contents { + data, err := f.conn.cget(obj.Key) + if err != nil { + return nil, err + } + var l reflog + if err := plist.Unmarshal(data, &l); err != nil { + return nil, err + } + + sc := hexScore(l.NewHeadSHA1) + if err != nil { + return nil, err + } + + data, err = f.comp.scget(sc) + if err != nil { + return nil, err + } + + var com commit + if err := unpack(data, &com); err != nil { + return nil, err + } + + var info folderInfo + if err := plist.Unmarshal(com.BucketXML, &info); err != nil { + return nil, err + } + + t := &Tree{ + Time: com.CreateTime, + Path: info.LocalPath, + Score: com.Tree.Score, + + commit: com, + comp: f.comp, + folder: f, + info: info, + } + out = append(out, t) + } + return out, nil +} + +// A Tree represents a single backed-up file tree snapshot. +type Tree struct { + Time time.Time // time back-up completed + Path string // root of backed-up tree + Score [20]byte + + comp *Computer + folder *Folder + commit commit + info folderInfo + + raw tree + haveRaw bool +} + +// Root returns the File for the tree's root directory. +func (t *Tree) Root() (*File, error) { + if !t.haveRaw { + data, err := t.comp.scget(t.Score) + if err != nil { + return nil, err + } + if err := unpack(data, &t.raw); err != nil { + return nil, err + } + t.haveRaw = true + } + + dir := &File{ + t: t, + dir: &t.raw, + n: &nameNode{"/", node{IsTree: true}}, + } + return dir, nil +} + +// A File represents a file or directory in a tree. +type File struct { + t *Tree + n *nameNode + dir *tree + byName map[string]*nameNode +} + +func (f *File) loadDir() error { + if f.dir == nil { + data, err := f.t.comp.scget(f.n.Node.Blob[0].Score) + if err != nil { + return err + } + var dir tree + if err := unpack(data, &dir); err != nil { + return err + } + f.dir = &dir + } + return nil +} + +func (f *File) Lookup(name string) (*File, error) { + if !f.n.Node.IsTree { + return nil, fmt.Errorf("lookup in non-directory") + } + if f.byName == nil { + if err := f.loadDir(); err != nil { + return nil, err + } + f.byName = map[string]*nameNode{} + for _, n := range f.dir.Nodes { + f.byName[n.Name] = n + } + } + n := f.byName[name] + if n == nil { + return nil, fmt.Errorf("no entry %q", name) + } + return &File{t: f.t, n: n}, nil +} + +func (f *File) Stat() *Dirent { + if f.n.Node.IsTree { + if err := f.loadDir(); err == nil { + return &Dirent{ + Name: f.n.Name, + ModTime: f.dir.Mtime.Time(), + Mode: fileMode(f.dir.Mode), + Size: 0, + } + } + } + return &Dirent{ + Name: f.n.Name, + ModTime: f.n.Node.Mtime.Time(), + Mode: fileMode(f.n.Node.Mode), + Size: int64(f.n.Node.UncompressedSize), + } +} + +type Dirent struct { + Name string + ModTime time.Time + Mode os.FileMode + Size int64 +} + +func (f *File) ReadDir() ([]Dirent, error) { + if !f.n.Node.IsTree { + return nil, fmt.Errorf("ReadDir in non-directory") + } + if err := f.loadDir(); err != nil { + return nil, err + } + var out []Dirent + for _, n := range f.dir.Nodes { + out = append(out, Dirent{ + Name: n.Name, + ModTime: n.Node.Mtime.Time(), + Mode: fileMode(n.Node.Mode), + }) + } + return out, nil +} + +func (f *File) Open() (io.ReadCloser, error) { + return &fileReader{t: f.t, blob: f.n.Node.Blob, n: &f.n.Node}, nil +} + +type fileReader struct { + t *Tree + n *node + blob []sscore + cur io.Reader + close []io.Closer +} + +func (f *fileReader) Read(b []byte) (int, error) { + for { + if f.cur != nil { + n, err := f.cur.Read(b) + if n > 0 || err != nil && err != io.EOF { + return n, err + } + for _, cl := range f.close { + cl.Close() + } + f.close = f.close[:0] + f.cur = nil + } + + if len(f.blob) == 0 { + break + } + + // TODO: Get a direct reader, not a []byte. + data, err := f.t.comp.scget(f.blob[0].Score) + if err != nil { + return 0, err + } + rc := ioutil.NopCloser(bytes.NewBuffer(data)) + + if f.n.CompressData { + gz, err := gzip.NewReader(rc) + if err != nil { + rc.Close() + return 0, err + } + f.close = append(f.close, gz) + f.cur = gz + } else { + f.cur = rc + } + f.close = append(f.close, rc) + f.blob = f.blob[1:] + } + + return 0, io.EOF +} + +func (f *fileReader) Close() error { + for _, cl := range f.close { + cl.Close() + } + f.close = f.close[:0] + f.cur = nil + return nil +} diff --git a/vendor/github.com/mattermost/rsc/arq/arqfs/main.go b/vendor/github.com/mattermost/rsc/arq/arqfs/main.go new file mode 100644 index 000000000..9e9001133 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/arq/arqfs/main.go @@ -0,0 +1,247 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Arqfs implements a file system interface to a collection of Arq backups. + + usage: arqfs [-m mtpt] + +Arqfs mounts the Arq backups on the file system directory mtpt, +(default /mnt/arq). The directory must exist and be writable by +the current user. + +Arq + +Arq is an Amazon S3-based backup system for OS X and sold by +Haystack Software (http://www.haystacksoftware.com/arq/). +This software reads backups written by Arq. +It is not affiliated with or connected to Haystack Software. + +Passwords + +Arqfs reads necessary passwords from the OS X keychain. +It expects at least two entries: + +The keychain entry for s3.amazonaws.com should list the Amazon S3 access ID +as user name and the S3 secret key as password. + +Each backup being accessed must have its own keychain entry for +host arq.swtch.com, listing the backup UUID as user name and the encryption +password as the password. + +Arqfs will not prompt for passwords or create these entries itself: they must +be created using the Keychain Access application. + +FUSE + +Arqfs creates a virtual file system using the FUSE file system layer. +On OS X, it requires OSXFUSE (http://osxfuse.github.com/). + +Cache + +Reading the Arq backups efficiently requires caching directory tree information +on local disk instead of reading the same data from S3 repeatedly. Arqfs caches +data downloaded from S3 in $HOME/Library/Caches/arq-cache/. +If an Arq installation is present on the same machine, arqfs will look in +its cache ($HOME/Library/Arq/Cache.noindex) first, but arqfs will not +write to Arq's cache directory. + +Bugs + +Arqfs only runs on OS X for now, because both FUSE and the keychain access +packages have not been ported to other systems yet. + +Both Arqfs and the FUSE package on which it is based have seen only light +use. There are likely to be bugs. Mail rsc@swtch.com with reports. + +*/ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "syscall" + + "github.com/mattermost/rsc/arq" + "github.com/mattermost/rsc/fuse" + "github.com/mattermost/rsc/keychain" + "launchpad.net/goamz/aws" +) + +var mtpt = flag.String("m", "/mnt/arq", "") + +func main() { + log.SetFlags(0) + + if len(os.Args) == 3 && os.Args[1] == "MOUNTSLAVE" { + *mtpt = os.Args[2] + mountslave() + return + } + + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "usage: arqfs [-m /mnt/arq]\n") + os.Exit(2) + } + flag.Parse() + if len(flag.Args()) != 0 { + flag.Usage() + } + + // Run in child so that we can exit once child is running. + r, w, err := os.Pipe() + if err != nil { + log.Fatal(err) + } + + cmd := exec.Command(os.Args[0], "MOUNTSLAVE", *mtpt) + cmd.Stdout = w + cmd.Stderr = os.Stderr + if err := cmd.Start(); err != nil { + log.Fatalf("mount process: %v", err) + } + w.Close() + + buf := make([]byte, 10) + n, _ := r.Read(buf) + if n != 2 || string(buf[0:2]) != "OK" { + os.Exit(1) + } + + fmt.Fprintf(os.Stderr, "mounted on %s\n", *mtpt) +} + +func mountslave() { + stdout, _ := syscall.Dup(1) + syscall.Dup2(2, 1) + + access, secret, err := keychain.UserPasswd("s3.amazonaws.com", "") + if err != nil { + log.Fatal(err) + } + auth := aws.Auth{AccessKey: access, SecretKey: secret} + + conn, err := arq.Dial(auth) + if err != nil { + log.Fatal(err) + } + + comps, err := conn.Computers() + if err != nil { + log.Fatal(err) + } + + fs := &fuse.Tree{} + for _, c := range comps { + fmt.Fprintf(os.Stderr, "scanning %s...\n", c.Name) + + // TODO: Better password protocol. + _, pw, err := keychain.UserPasswd("arq.swtch.com", c.UUID) + if err != nil { + log.Fatal(err) + } + c.Unlock(pw) + + folders, err := c.Folders() + if err != nil { + log.Fatal(err) + } + + lastDate := "" + n := 0 + for _, f := range folders { + if err := f.Load(); err != nil { + log.Fatal(err) + } + trees, err := f.Trees() + if err != nil { + log.Fatal(err) + } + for _, t := range trees { + y, m, d := t.Time.Date() + date := fmt.Sprintf("%04d/%02d%02d", y, m, d) + suffix := "" + if date == lastDate { + n++ + suffix = fmt.Sprintf(".%d", n) + } else { + n = 0 + } + lastDate = date + f, err := t.Root() + if err != nil { + log.Print(err) + } + // TODO: Pass times to fs.Add. + // fmt.Fprintf(os.Stderr, "%v %s %x\n", t.Time, c.Name+"/"+date+suffix+"/"+t.Path, t.Score) + fs.Add(c.Name+"/"+date+suffix+"/"+t.Path, &fuseNode{f}) + } + } + } + + fmt.Fprintf(os.Stderr, "mounting...\n") + + c, err := fuse.Mount(*mtpt) + if err != nil { + log.Fatal(err) + } + defer exec.Command("umount", *mtpt).Run() + + syscall.Write(stdout, []byte("OK")) + syscall.Close(stdout) + c.Serve(fs) +} + +type fuseNode struct { + arq *arq.File +} + +func (f *fuseNode) Attr() fuse.Attr { + de := f.arq.Stat() + return fuse.Attr{ + Mode: de.Mode, + Mtime: de.ModTime, + Size: uint64(de.Size), + } +} + +func (f *fuseNode) Lookup(name string, intr fuse.Intr) (fuse.Node, fuse.Error) { + ff, err := f.arq.Lookup(name) + if err != nil { + return nil, fuse.ENOENT + } + return &fuseNode{ff}, nil +} + +func (f *fuseNode) ReadDir(intr fuse.Intr) ([]fuse.Dirent, fuse.Error) { + adir, err := f.arq.ReadDir() + if err != nil { + return nil, fuse.EIO + } + var dir []fuse.Dirent + for _, ade := range adir { + dir = append(dir, fuse.Dirent{ + Name: ade.Name, + }) + } + return dir, nil +} + +// TODO: Implement Read+Release, not ReadAll, to avoid giant buffer. +func (f *fuseNode) ReadAll(intr fuse.Intr) ([]byte, fuse.Error) { + rc, err := f.arq.Open() + if err != nil { + return nil, fuse.EIO + } + defer rc.Close() + data, err := ioutil.ReadAll(rc) + if err != nil { + return data, fuse.EIO + } + return data, nil +} diff --git a/vendor/github.com/mattermost/rsc/arq/crypto.go b/vendor/github.com/mattermost/rsc/arq/crypto.go new file mode 100644 index 000000000..e567ec36d --- /dev/null +++ b/vendor/github.com/mattermost/rsc/arq/crypto.go @@ -0,0 +1,93 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package arq + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/sha1" + "hash" + "log" + + "bitbucket.org/taruti/pbkdf2.go" // TODO: Pull in copy +) + +type cryptoState struct { + c cipher.Block + iv []byte + salt []byte +} + +func (c *cryptoState) unlock(pw string) { + const ( + iter = 1000 + keyLen = 48 + aesKeyLen = 32 + aesIVLen = 16 + ) + key1 := pbkdf2.Pbkdf2([]byte(pw), c.salt, iter, sha1.New, keyLen) + var key2 []byte + key2, c.iv = bytesToKey(sha1.New, c.salt, key1, iter, aesKeyLen, aesIVLen) + c.c, _ = aes.NewCipher(key2) +} + +func (c *cryptoState) decrypt(data []byte) []byte { + dec := cipher.NewCBCDecrypter(c.c, c.iv) + if len(data)%aes.BlockSize != 0 { + log.Fatal("bad block") + } + dec.CryptBlocks(data, data) + // fmt.Printf("% x\n", data) + // fmt.Printf("%s\n", data) + + // unpad + { + n := len(data) + p := int(data[n-1]) + if p == 0 || p > aes.BlockSize { + log.Fatal("impossible padding") + } + for i := 0; i < p; i++ { + if data[n-1-i] != byte(p) { + log.Fatal("bad padding") + } + } + data = data[:n-p] + } + return data +} + +func sha(data []byte) score { + h := sha1.New() + h.Write(data) + var sc score + copy(sc[:], h.Sum(nil)) + return sc +} + +func bytesToKey(hf func() hash.Hash, salt, data []byte, iter int, keySize, ivSize int) (key, iv []byte) { + h := hf() + var d, dcat []byte + sum := make([]byte, 0, h.Size()) + for len(dcat) < keySize+ivSize { + // D_i = HASH^count(D_(i-1) || data || salt) + h.Reset() + h.Write(d) + h.Write(data) + h.Write(salt) + sum = h.Sum(sum[:0]) + + for j := 1; j < iter; j++ { + h.Reset() + h.Write(sum) + sum = h.Sum(sum[:0]) + } + + d = append(d[:0], sum...) + dcat = append(dcat, d...) + } + + return dcat[:keySize], dcat[keySize : keySize+ivSize] +} diff --git a/vendor/github.com/mattermost/rsc/arq/data.go b/vendor/github.com/mattermost/rsc/arq/data.go new file mode 100644 index 000000000..74eaba450 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/arq/data.go @@ -0,0 +1,240 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// On-cloud data structures + +package arq + +import ( + "fmt" + "os" + "time" +) + +// plist data structures + +type computerInfo struct { + UserName string `plist:"userName"` + ComputerName string `plist:"computerName"` +} + +type folderInfo struct { + BucketUUID string + BucketName string + ComputerUUID string + LocalPath string + LocalMountPoint string + // don't care about IgnoredRelativePaths or Excludes +} + +type reflog struct { + OldHeadSHA1 string `plist:"oldHeadSHA1"` + NewHeadSHA1 string `plist:"newHeadSHA1"` +} + +// binary data structures + +type score [20]byte + +type sscore struct { + Score score `arq:"HexScore"` + StretchKey bool // v4+ +} + +type tag string + +type commit struct { + Tag tag `arq:"CommitV005"` + Author string + Comment string + ParentCommits []sscore + Tree sscore + Location string + MergeCommonAncestor sscore + CreateTime time.Time + Failed []failed // v3+ + BucketXML []byte // v5+ +} + +type tree struct { + Tag tag `arq:"TreeV015"` + CompressXattr bool + CompressACL bool + Xattr sscore + XattrSize uint64 + ACL sscore + Uid int32 + Gid int32 + Mode int32 + Mtime unixTime + Flags int64 + FinderFlags int32 + XFinderFlags int32 + StDev int32 + StIno int32 + StNlink uint32 + StRdev int32 + Ctime unixTime + StBlocks int64 + StBlksize uint32 + AggrSize uint64 + Crtime unixTime + Nodes []*nameNode `arq:"count32"` +} + +type nameNode struct { + Name string + Node node +} + +type node struct { + IsTree bool + CompressData bool + CompressXattr bool + CompressACL bool + Blob []sscore `arq:"count32"` + UncompressedSize uint64 + Thumbnail sscore + Preview sscore + Xattr sscore + XattrSize uint64 + ACL sscore + Uid int32 + Gid int32 + Mode int32 + Mtime unixTime + Flags int64 + FinderFlags int32 + XFinderFlags int32 + FinderFileType string + FinderFileCreator string + IsExtHidden bool + StDev int32 + StIno int32 + StNlink uint32 + StRdev int32 + Ctime unixTime + CreateTime unixTime + StBlocks int64 + StBlksize uint32 +} + +func fileMode(m int32) os.FileMode { + const ( + // Darwin file mode. + S_IFBLK = 0x6000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFIFO = 0x1000 + S_IFLNK = 0xa000 + S_IFMT = 0xf000 + S_IFREG = 0x8000 + S_IFSOCK = 0xc000 + S_IFWHT = 0xe000 + S_ISGID = 0x400 + S_ISTXT = 0x200 + S_ISUID = 0x800 + S_ISVTX = 0x200 + ) + mode := os.FileMode(m & 0777) + switch m & S_IFMT { + case S_IFBLK, S_IFWHT: + 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 +} + +type unixTime struct { + Sec int64 + Nsec int64 +} + +func (t *unixTime) Time() time.Time { + return time.Unix(t.Sec, t.Nsec) +} + +type failed struct { + Path string + Error string +} + +type ientry struct { + File string + Offset int64 + Size int64 + Score score +} + +func (s score) Equal(t score) bool { + for i := range s { + if s[i] != t[i] { + return false + } + } + return true +} + +func (s score) String() string { + return fmt.Sprintf("%x", s[:]) +} + +func binaryScore(b []byte) score { + if len(b) < 20 { + panic("BinaryScore: not enough data") + } + var sc score + copy(sc[:], b) + return sc +} + +func hexScore(b string) score { + if len(b) < 40 { + panic("HexScore: not enough data") + } + var sc score + for i := 0; i < 40; i++ { + ch := b[i] + if '0' <= ch && ch <= '9' { + ch -= '0' + } else if 'a' <= ch && ch <= 'f' { + ch -= 'a' - 10 + } else { + panic("HexScore: invalid lower hex digit") + } + if i%2 == 0 { + ch <<= 4 + } + sc[i/2] |= ch + } + return sc +} + +func (ss sscore) String() string { + str := ss.Score.String() + if ss.StretchKey { + str += "Y" + } + return str +} diff --git a/vendor/github.com/mattermost/rsc/arq/hist/hist.go b/vendor/github.com/mattermost/rsc/arq/hist/hist.go new file mode 100644 index 000000000..02fb5fbf0 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/arq/hist/hist.go @@ -0,0 +1,160 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Hist shows the history of a given file, using Arq backups. + + usage: hist [-d] [-h host] [-m mtpt] [-s yyyy/mmdd] file ... + +The -d flag causes it to show diffs between successive versions. + +By default, hist assumes backups are mounted at mtpt/host, where +mtpt defaults to /mnt/arq and host is the first element of the local host name. +Hist starts the file list with the present copy of the file. + +The -h and -s flags override these assumptions. + +*/ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" +) + +var usageString = `usage: hist [-d] [-h host] [-m mtpt] [-s yyyy/mmdd] file ... + +Hist lists the known versions of the given file. +The -d flag causes it to show diffs between successive versions. + +By default, hist assumes backups are mounted at mtpt/host, where +mtpt defaults to /mnt/arq and host is the first element of the local host name. +Hist starts the file list with the present copy of the file. + +The -h and -s flags override these assumptions. +` + +var ( + diff = flag.Bool("d", false, "diff") + host = flag.String("h", defaultHost(), "host name") + mtpt = flag.String("m", "/mnt/arq", "mount point") + vers = flag.String("s", "", "version") +) + +func defaultHost() string { + name, _ := os.Hostname() + if name == "" { + name = "gnot" + } + if i := strings.Index(name, "."); i >= 0 { + name = name[:i] + } + return name +} + +func main() { + flag.Usage = func() { + fmt.Fprint(os.Stderr, usageString) + os.Exit(2) + } + + flag.Parse() + args := flag.Args() + if len(args) == 0 { + flag.Usage() + } + + dates := loadDates() + for _, file := range args { + list(dates, file) + } +} + +var ( + yyyy = regexp.MustCompile(`^\d{4}$`) + mmdd = regexp.MustCompile(`^\d{4}(\.\d+)?$`) +) + +func loadDates() []string { + var all []string + ydir, err := ioutil.ReadDir(filepath.Join(*mtpt, *host)) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(3) + } + for _, y := range ydir { + if !y.IsDir() || !yyyy.MatchString(y.Name()) { + continue + } + ddir, err := ioutil.ReadDir(filepath.Join(*mtpt, *host, y.Name())) + if err != nil { + continue + } + for _, d := range ddir { + if !d.IsDir() || !mmdd.MatchString(d.Name()) { + continue + } + date := y.Name() + "/" + d.Name() + if *vers > date { + continue + } + all = append(all, filepath.Join(*mtpt, *host, date)) + } + } + return all +} + +const timeFormat = "Jan 02 15:04:05 MST 2006" + +func list(dates []string, file string) { + var ( + last os.FileInfo + lastPath string + ) + + fi, err := os.Stat(file) + if err != nil { + fmt.Fprintf(os.Stderr, "hist: warning: %s: %v\n", file, err) + } else { + fmt.Printf("%s %s %d\n", fi.ModTime().Format(timeFormat), file, fi.Size()) + last = fi + lastPath = file + } + + file, err = filepath.Abs(file) + if err != nil { + fmt.Fprintf(os.Stderr, "hist: abs: %v\n", err) + return + } + + for i := len(dates)-1; i >= 0; i-- { + p := filepath.Join(dates[i], file) + fi, err := os.Stat(p) + if err != nil { + continue + } + if last != nil && fi.ModTime() == last.ModTime() && fi.Size() == last.Size() { + continue + } + if *diff { + cmd := exec.Command("diff", lastPath, p) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Start(); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + } + cmd.Wait() + } + fmt.Printf("%s %s %d\n", fi.ModTime().Format(timeFormat), p, fi.Size()) + last = fi + lastPath = p + } +} + diff --git a/vendor/github.com/mattermost/rsc/arq/unpack.go b/vendor/github.com/mattermost/rsc/arq/unpack.go new file mode 100644 index 000000000..ec4296a7c --- /dev/null +++ b/vendor/github.com/mattermost/rsc/arq/unpack.go @@ -0,0 +1,227 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parsing of Arq's binary data structures. + +package arq + +import ( + "bytes" + "encoding/binary" + "fmt" + "reflect" + "time" +) + +var errMalformed = fmt.Errorf("malformed data") +var tagType = reflect.TypeOf(tag("")) +var timeType = reflect.TypeOf(time.Time{}) +var scoreType = reflect.TypeOf(score{}) + +func unpack(data []byte, v interface{}) error { + data, err := unpackValue(data, reflect.ValueOf(v).Elem(), "") + if err != nil { + return err + } + if len(data) != 0 { + if len(data) > 100 { + return fmt.Errorf("more data than expected: %x...", data[:100]) + } + return fmt.Errorf("more data than expected: %x", data) + } + return nil +} + +func unpackValue(data []byte, v reflect.Value, tag string) ([]byte, error) { + //println("unpackvalue", v.Type().String(), len(data)) + switch v.Kind() { + case reflect.String: + if v.Type() == tagType { + if tag == "" { + panic("arqfs: missing reflect tag on Tag field") + } + if len(data) < len(tag) || !bytes.Equal(data[:len(tag)], []byte(tag)) { + return nil, errMalformed + } + data = data[len(tag):] + return data, nil + } + if len(data) < 1 { + return nil, errMalformed + } + if data[0] == 0 { + data = data[1:] + v.SetString("") + return data, nil + } + if data[0] != 1 || len(data) < 1+8 { + return nil, errMalformed + } + n := binary.BigEndian.Uint64(data[1:]) + data = data[1+8:] + if n >= uint64(len(data)) { + return nil, errMalformed + } + str := data[:n] + data = data[n:] + v.SetString(string(str)) + return data, nil + + case reflect.Uint32: + if len(data) < 4 { + return nil, errMalformed + } + v.SetUint(uint64(binary.BigEndian.Uint32(data))) + data = data[4:] + return data, nil + + case reflect.Int32: + if len(data) < 4 { + return nil, errMalformed + } + v.SetInt(int64(binary.BigEndian.Uint32(data))) + data = data[4:] + return data, nil + + case reflect.Uint8: + if len(data) < 1 { + return nil, errMalformed + } + v.SetUint(uint64(data[0])) + data = data[1:] + return data, nil + + case reflect.Uint64: + if len(data) < 8 { + return nil, errMalformed + } + v.SetUint(binary.BigEndian.Uint64(data)) + data = data[8:] + return data, nil + + case reflect.Int64: + if len(data) < 8 { + return nil, errMalformed + } + v.SetInt(int64(binary.BigEndian.Uint64(data))) + data = data[8:] + return data, nil + + case reflect.Ptr: + v.Set(reflect.New(v.Type().Elem())) + return unpackValue(data, v.Elem(), tag) + + case reflect.Slice: + var n int + if tag == "count32" { + n32 := binary.BigEndian.Uint32(data) + n = int(n32) + if uint32(n) != n32 { + return nil, errMalformed + } + data = data[4:] + } else { + if len(data) < 8 { + return nil, errMalformed + } + n64 := binary.BigEndian.Uint64(data) + n = int(n64) + if uint64(n) != n64 { + return nil, errMalformed + } + data = data[8:] + } + v.Set(v.Slice(0, 0)) + if v.Type().Elem().Kind() == reflect.Uint8 { + // Fast case for []byte + if len(data) < n { + return nil, errMalformed + } + v.Set(reflect.AppendSlice(v, reflect.ValueOf(data[:n]))) + return data[n:], nil + } + for i := 0; i < n; i++ { + elem := reflect.New(v.Type().Elem()).Elem() + var err error + data, err = unpackValue(data, elem, "") + if err != nil { + return nil, err + } + v.Set(reflect.Append(v, elem)) + } + return data, nil + + case reflect.Array: + if v.Type() == scoreType && tag == "HexScore" { + var s string + data, err := unpackValue(data, reflect.ValueOf(&s).Elem(), "") + if err != nil { + return nil, err + } + if len(s) != 0 { + v.Set(reflect.ValueOf(hexScore(s))) + } + return data, nil + } + n := v.Len() + if v.Type().Elem().Kind() == reflect.Uint8 { + // Fast case for [n]byte + if len(data) < n { + return nil, errMalformed + } + reflect.Copy(v, reflect.ValueOf(data)) + data = data[n:] + return data, nil + } + for i := 0; i < n; i++ { + var err error + data, err = unpackValue(data, v.Index(i), "") + if err != nil { + return nil, err + } + } + return data, nil + + case reflect.Bool: + if len(data) < 1 || data[0] > 1 { + if len(data) >= 1 { + println("badbool", data[0]) + } + return nil, errMalformed + } + v.SetBool(data[0] == 1) + data = data[1:] + return data, nil + + case reflect.Struct: + if v.Type() == timeType { + if len(data) < 1 || data[0] > 1 { + return nil, errMalformed + } + if data[0] == 0 { + v.Set(reflect.ValueOf(time.Time{})) + return data[1:], nil + } + data = data[1:] + if len(data) < 8 { + return nil, errMalformed + } + ms := binary.BigEndian.Uint64(data) + v.Set(reflect.ValueOf(time.Unix(int64(ms/1e3), int64(ms%1e3)*1e6))) + return data[8:], nil + } + for i := 0; i < v.NumField(); i++ { + f := v.Type().Field(i) + fv := v.Field(i) + var err error + data, err = unpackValue(data, fv, f.Tag.Get("arq")) + if err != nil { + return nil, err + } + } + return data, nil + } + + panic("arqfs: unexpected type in unpackValue: " + v.Type().String()) +} diff --git a/vendor/github.com/mattermost/rsc/blog/atom/atom.go b/vendor/github.com/mattermost/rsc/blog/atom/atom.go new file mode 100644 index 000000000..4038e4fcd --- /dev/null +++ b/vendor/github.com/mattermost/rsc/blog/atom/atom.go @@ -0,0 +1,58 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Adapted from encoding/xml/read_test.go. + +// Package atom defines XML data structures for an Atom feed. +package atom + +import ( + "encoding/xml" + "time" +) + +type Feed struct { + XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"` + Title string `xml:"title"` + ID string `xml:"id"` + Link []Link `xml:"link"` + Updated TimeStr `xml:"updated"` + Author *Person `xml:"author"` + Entry []*Entry `xml:"entry"` +} + +type Entry struct { + Title string `xml:"title"` + ID string `xml:"id"` + Link []Link `xml:"link"` + Published TimeStr `xml:"published"` + Updated TimeStr `xml:"updated"` + Author *Person `xml:"author"` + Summary *Text `xml:"summary"` + Content *Text `xml:"content"` +} + +type Link struct { + Rel string `xml:"rel,attr"` + Href string `xml:"href,attr"` +} + +type Person struct { + Name string `xml:"name"` + URI string `xml:"uri"` + Email string `xml:"email"` + InnerXML string `xml:",innerxml"` +} + +type Text struct { + Type string `xml:"type,attr"` + Body string `xml:",chardata"` +} + +type TimeStr string + +func Time(t time.Time) TimeStr { + return TimeStr(t.Format("2006-01-02T15:04:05-07:00")) +} + diff --git a/vendor/github.com/mattermost/rsc/blog/main.go b/vendor/github.com/mattermost/rsc/blog/main.go new file mode 100644 index 000000000..182dc88f1 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/blog/main.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. + +package main + +import ( + "github.com/mattermost/rsc/devweb/slave" + + _ "github.com/mattermost/rsc/blog/post" +) + +func main() { + slave.Main() +} diff --git a/vendor/github.com/mattermost/rsc/blog/post/post.go b/vendor/github.com/mattermost/rsc/blog/post/post.go new file mode 100644 index 000000000..beae651f8 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/blog/post/post.go @@ -0,0 +1,534 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package post + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "fmt" + "html/template" + "net/http" + "os" + "path" + "runtime/debug" + "sort" + "strings" + "time" + + "github.com/mattermost/rsc/appfs/fs" + "github.com/mattermost/rsc/appfs/proto" + "github.com/mattermost/rsc/blog/atom" +) + +func init() { + fs.Root = os.Getenv("HOME") + "/app/" + http.HandleFunc("/", serve) + http.Handle("/feeds/posts/default", http.RedirectHandler("/feed.atom", http.StatusFound)) +} + +var funcMap = template.FuncMap{ + "now": time.Now, + "date": timeFormat, +} + +func timeFormat(fmt string, t time.Time) string { + return t.Format(fmt) +} + +type blogTime struct { + time.Time +} + +var timeFormats = []string{ + time.RFC3339, + "Monday, January 2, 2006", + "January 2, 2006 15:00 -0700", +} + +func (t *blogTime) UnmarshalJSON(data []byte) (err error) { + str := string(data) + for _, f := range timeFormats { + tt, err := time.Parse(`"`+f+`"`, str) + if err == nil { + t.Time = tt + return nil + } + } + return fmt.Errorf("did not recognize time: %s", str) +} + +type PostData struct { + FileModTime time.Time + FileSize int64 + + Title string + Date blogTime + Name string + OldURL string + Summary string + Favorite bool + + Reader []string + + PlusAuthor string // Google+ ID of author + PlusPage string // Google+ Post ID for comment post + PlusAPIKey string // Google+ API key + PlusURL string + HostURL string // host URL + Comments bool + + article string +} + +func (d *PostData) canRead(user string) bool { + for _, r := range d.Reader { + if r == user { + return true + } + } + return false +} + +func (d *PostData) IsDraft() bool { + return d.Date.IsZero() || d.Date.After(time.Now()) +} + +// To find PlusPage value: +// https://www.googleapis.com/plus/v1/people/116810148281701144465/activities/public?key=AIzaSyB_JO6hyAJAL659z0Dmu0RUVVvTx02ZPMM +// + +const owner = "rsc@swtch.com" +const plusRsc = "116810148281701144465" +const plusKey = "AIzaSyB_JO6hyAJAL659z0Dmu0RUVVvTx02ZPMM" +const feedID = "tag:research.swtch.com,2012:research.swtch.com" + +var replacer = strings.NewReplacer( + "⁰", "0", + "¹", "1", + "²", "2", + "³", "3", + "⁴", "4", + "⁵", "5", + "⁶", "6", + "⁷", "7", + "⁸", "8", + "⁹", "9", + "ⁿ", "n", + "₀", "0", + "₁", "1", + "₂", "2", + "₃", "3", + "₄", "4", + "₅", "5", + "₆", "6", + "₇", "7", + "₈", "8", + "₉", "9", + "``", "“", + "''", "”", +) + +func serve(w http.ResponseWriter, req *http.Request) { + ctxt := fs.NewContext(req) + + defer func() { + if err := recover(); err != nil { + var buf bytes.Buffer + fmt.Fprintf(&buf, "panic: %s\n\n", err) + buf.Write(debug.Stack()) + ctxt.Criticalf("%s", buf.String()) + + http.Error(w, buf.String(), 500) + } + }() + + p := path.Clean("/" + req.URL.Path) +/* + if strings.Contains(req.Host, "appspot.com") { + http.Redirect(w, req, "http://research.swtch.com" + p, http.StatusFound) + } +*/ + if p != req.URL.Path { + http.Redirect(w, req, p, http.StatusFound) + return + } + + if p == "/feed.atom" { + atomfeed(w, req) + return + } + + if strings.HasPrefix(p, "/20") && strings.Contains(p[1:], "/") { + // Assume this is an old-style URL. + oldRedirect(ctxt, w, req, p) + } + + user := ctxt.User() + isOwner := ctxt.User() == owner || len(os.Args) >= 2 && os.Args[1] == "LISTEN_STDIN" + if p == "" || p == "/" || p == "/draft" { + if p == "/draft" && user == "?" { + ctxt.Criticalf("/draft loaded by %s", user) + notfound(ctxt, w, req) + return + } + toc(w, req, p == "/draft", isOwner, user) + return + } + + draft := false + if strings.HasPrefix(p, "/draft/") { + if user == "?" { + ctxt.Criticalf("/draft loaded by %s", user) + notfound(ctxt, w, req) + return + } + draft = true + p = p[len("/draft"):] + } + + if strings.Contains(p[1:], "/") { + notfound(ctxt, w, req) + return + } + + if strings.Contains(p, ".") { + // Let Google's front end servers cache static + // content for a short amount of time. + httpCache(w, 5*time.Minute) + ctxt.ServeFile(w, req, "blog/static/"+p) + return + } + + // Use just 'blog' as the cache path so that if we change + // templates, all the cached HTML gets invalidated. + var data []byte + pp := "bloghtml:"+p + if draft && !isOwner { + pp += ",user="+user + } + if key, ok := ctxt.CacheLoad(pp, "blog", &data); !ok { + meta, article, err := loadPost(ctxt, p, req) + if err != nil || meta.IsDraft() != draft || (draft && !isOwner && !meta.canRead(user)) { + ctxt.Criticalf("no %s for %s", p, user) + notfound(ctxt, w, req) + return + } + t := mainTemplate(ctxt) + template.Must(t.New("article").Parse(article)) + + var buf bytes.Buffer + meta.Comments = true + if err := t.Execute(&buf, meta); err != nil { + panic(err) + } + data = buf.Bytes() + ctxt.CacheStore(key, data) + } + w.Write(data) +} + +func notfound(ctxt *fs.Context, w http.ResponseWriter, req *http.Request) { + var buf bytes.Buffer + var data struct { + HostURL string + } + data.HostURL = hostURL(req) + t := mainTemplate(ctxt) + if err := t.Lookup("404").Execute(&buf, &data); err != nil { + panic(err) + } + w.WriteHeader(404) + w.Write(buf.Bytes()) +} + +func mainTemplate(c *fs.Context) *template.Template { + t := template.New("main") + t.Funcs(funcMap) + + main, _, err := c.Read("blog/main.html") + if err != nil { + panic(err) + } + style, _, _ := c.Read("blog/style.html") + main = append(main, style...) + _, err = t.Parse(string(main)) + if err != nil { + panic(err) + } + return t +} + +func loadPost(c *fs.Context, name string, req *http.Request) (meta *PostData, article string, err error) { + meta = &PostData{ + Name: name, + Title: "TITLE HERE", + PlusAuthor: plusRsc, + PlusAPIKey: plusKey, + HostURL: hostURL(req), + } + + art, fi, err := c.Read("blog/post/" + name) + if err != nil { + return nil, "", err + } + if bytes.HasPrefix(art, []byte("{\n")) { + i := bytes.Index(art, []byte("\n}\n")) + if i < 0 { + panic("cannot find end of json metadata") + } + hdr, rest := art[:i+3], art[i+3:] + if err := json.Unmarshal(hdr, meta); err != nil { + panic(fmt.Sprintf("loading %s: %s", name, err)) + } + art = rest + } + meta.FileModTime = fi.ModTime + meta.FileSize = fi.Size + + return meta, replacer.Replace(string(art)), nil +} + +type byTime []*PostData + +func (x byTime) Len() int { return len(x) } +func (x byTime) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byTime) Less(i, j int) bool { return x[i].Date.Time.After(x[j].Date.Time) } + +type TocData struct { + Draft bool + HostURL string + Posts []*PostData +} + +func toc(w http.ResponseWriter, req *http.Request, draft bool, isOwner bool, user string) { + c := fs.NewContext(req) + + var data []byte + keystr := fmt.Sprintf("blog:toc:%v", draft) + if req.FormValue("readdir") != "" { + keystr += ",readdir=" + req.FormValue("readdir") + } + if draft { + keystr += ",user="+user + } + + if key, ok := c.CacheLoad(keystr, "blog", &data); !ok { + c := fs.NewContext(req) + dir, err := c.ReadDir("blog/post") + if err != nil { + panic(err) + } + + if req.FormValue("readdir") == "1" { + fmt.Fprintf(w, "%d dir entries\n", len(dir)) + return + } + + postCache := map[string]*PostData{} + if data, _, err := c.Read("blogcache"); err == nil { + if err := json.Unmarshal(data, &postCache); err != nil { + c.Criticalf("unmarshal blogcache: %v", err) + } + } + + ch := make(chan *PostData, len(dir)) + const par = 20 + var limit = make(chan bool, par) + for i := 0; i < par; i++ { + limit <- true + } + for _, d := range dir { + if meta := postCache[d.Name]; meta != nil && meta.FileModTime.Equal(d.ModTime) && meta.FileSize == d.Size { + ch <- meta + continue + } + + <-limit + go func(d proto.FileInfo) { + defer func() { limit <- true }() + meta, _, err := loadPost(c, d.Name, req) + if err != nil { + // Should not happen: we just listed the directory. + c.Criticalf("loadPost %s: %v", d.Name, err) + return + } + ch <- meta + }(d) + } + for i := 0; i < par; i++ { + <-limit + } + close(ch) + postCache = map[string]*PostData{} + var all []*PostData + for meta := range ch { + postCache[meta.Name] = meta + if meta.IsDraft() == draft && (!draft || isOwner || meta.canRead(user)) { + all = append(all, meta) + } + } + sort.Sort(byTime(all)) + + if data, err := json.Marshal(postCache); err != nil { + c.Criticalf("marshal blogcache: %v", err) + } else if err := c.Write("blogcache", data); err != nil { + c.Criticalf("write blogcache: %v", err) + } + + var buf bytes.Buffer + t := mainTemplate(c) + if err := t.Lookup("toc").Execute(&buf, &TocData{draft, hostURL(req), all}); err != nil { + panic(err) + } + data = buf.Bytes() + c.CacheStore(key, data) + } + w.Write(data) +} + +func oldRedirect(ctxt *fs.Context, w http.ResponseWriter, req *http.Request, p string) { + m := map[string]string{} + if key, ok := ctxt.CacheLoad("blog:oldRedirectMap", "blog/post", &m); !ok { + dir, err := ctxt.ReadDir("blog/post") + if err != nil { + panic(err) + } + + for _, d := range dir { + meta, _, err := loadPost(ctxt, d.Name, req) + if err != nil { + // Should not happen: we just listed the directory. + panic(err) + } + m[meta.OldURL] = "/" + d.Name + } + + ctxt.CacheStore(key, m) + } + + if url, ok := m[p]; ok { + http.Redirect(w, req, url, http.StatusFound) + return + } + + notfound(ctxt, w, req) +} + +func hostURL(req *http.Request) string { + if strings.HasPrefix(req.Host, "localhost") { + return "http://localhost:8080" + } + return "http://research.swtch.com" +} + +func atomfeed(w http.ResponseWriter, req *http.Request) { + c := fs.NewContext(req) + + c.Criticalf("Header: %v", req.Header) + + var data []byte + if key, ok := c.CacheLoad("blog:atomfeed", "blog/post", &data); !ok { + dir, err := c.ReadDir("blog/post") + if err != nil { + panic(err) + } + + var all []*PostData + for _, d := range dir { + meta, article, err := loadPost(c, d.Name, req) + if err != nil { + // Should not happen: we just loaded the directory. + panic(err) + } + if meta.IsDraft() { + continue + } + meta.article = article + all = append(all, meta) + } + sort.Sort(byTime(all)) + + show := all + if len(show) > 10 { + show = show[:10] + for _, meta := range all[10:] { + if meta.Favorite { + show = append(show, meta) + } + } + } + + feed := &atom.Feed{ + Title: "research!rsc", + ID: feedID, + Updated: atom.Time(show[0].Date.Time), + Author: &atom.Person{ + Name: "Russ Cox", + URI: "https://plus.google.com/" + plusRsc, + Email: "rsc@swtch.com", + }, + Link: []atom.Link{ + {Rel: "self", Href: hostURL(req) + "/feed.atom"}, + }, + } + + for _, meta := range show { + t := template.New("main") + t.Funcs(funcMap) + main, _, err := c.Read("blog/atom.html") + if err != nil { + panic(err) + } + _, err = t.Parse(string(main)) + if err != nil { + panic(err) + } + template.Must(t.New("article").Parse(meta.article)) + var buf bytes.Buffer + if err := t.Execute(&buf, meta); err != nil { + panic(err) + } + + e := &atom.Entry{ + Title: meta.Title, + ID: feed.ID + "/" + meta.Name, + Link: []atom.Link{ + {Rel: "alternate", Href: meta.HostURL + "/" + meta.Name}, + }, + Published: atom.Time(meta.Date.Time), + Updated: atom.Time(meta.Date.Time), + Summary: &atom.Text{ + Type: "text", + Body: meta.Summary, + }, + Content: &atom.Text{ + Type: "html", + Body: buf.String(), + }, + } + + feed.Entry = append(feed.Entry, e) + } + + data, err = xml.Marshal(&feed) + if err != nil { + panic(err) + } + + c.CacheStore(key, data) + } + + // Feed readers like to hammer us; let Google cache the + // response to reduce the traffic we have to serve. + httpCache(w, 15*time.Minute) + + w.Header().Set("Content-Type", "application/atom+xml") + w.Write(data) +} + +func httpCache(w http.ResponseWriter, dt time.Duration) { + w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(dt.Seconds()))) +} diff --git a/vendor/github.com/mattermost/rsc/blog/post/qr.go b/vendor/github.com/mattermost/rsc/blog/post/qr.go new file mode 100644 index 000000000..a1a03fdbf --- /dev/null +++ b/vendor/github.com/mattermost/rsc/blog/post/qr.go @@ -0,0 +1,34 @@ +package post + +import ( + "fmt" + "net/http" + "runtime/debug" + + qrweb "github.com/mattermost/rsc/qr/web" +) + +func carp(f http.HandlerFunc) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + defer func() { + if err := recover(); err != nil { + w.Header().Set("Content-Type", "text/plain") + fmt.Fprintf(w, "
\npanic: %s\n\n%s\n", err, debug.Stack())
+			}
+		}()
+		f.ServeHTTP(w, req)
+	})
+}
+
+func init() {
+	//	http.Handle("/qr/bits", carp(qrweb.Bits))
+	http.Handle("/qr/frame", carp(qrweb.Frame))
+	http.Handle("/qr/frames", carp(qrweb.Frames))
+	http.Handle("/qr/mask", carp(qrweb.Mask))
+	http.Handle("/qr/masks", carp(qrweb.Masks))
+	http.Handle("/qr/arrow", carp(qrweb.Arrow))
+	http.Handle("/qr/draw", carp(qrweb.Draw))
+	http.Handle("/qr/bitstable", carp(qrweb.BitsTable))
+	http.Handle("/qr/encode", carp(qrweb.Encode))
+	http.Handle("/qr/show/", carp(qrweb.Show))
+}
diff --git a/vendor/github.com/mattermost/rsc/cmd/crypt/crypt.go b/vendor/github.com/mattermost/rsc/cmd/crypt/crypt.go
new file mode 100644
index 000000000..4a2150540
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/cmd/crypt/crypt.go
@@ -0,0 +1,79 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Crypt is a simple password-based encryption program,
+// demonstrating how to use github.com/mattermost/rsc/crypt.
+//
+// Encrypt input to output using password:
+//	crypt password output
+//
+// Decrypt input to output using password:
+//	crypt -d password output
+//
+// Yes, the password is a command-line argument. This is a demo of the
+// github.com/mattermost/rsc/crypt package. It's not intended for real use.
+//
+package main
+
+import (
+	"encoding/base64"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"github.com/mattermost/rsc/crypt"
+)
+
+func main() {
+	args := os.Args[1:]
+	encrypt := true
+	if len(args) >= 1 && args[0] == "-d" {
+		encrypt = false
+		args = args[1:]
+	}
+	if len(args) != 1 || strings.HasPrefix(args[0], "-") {
+		fmt.Fprintf(os.Stderr, "usage: crypt [-d] password < input > output\n")
+		os.Exit(2)
+	}
+	password := args[0]
+
+	data, err := ioutil.ReadAll(os.Stdin)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "reading stdin: %v\n", err)
+		os.Exit(1)
+	}
+	if encrypt {
+		pkt, err := crypt.Encrypt(password, data)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "%v\n", err)
+			os.Exit(1)
+		}
+		str := base64.StdEncoding.EncodeToString(pkt)
+		for len(str) > 60 {
+			fmt.Printf("%s\n", str[:60])
+			str = str[60:]
+		}
+		fmt.Printf("%s\n", str)
+	} else {
+		pkt, err := base64.StdEncoding.DecodeString(strings.Map(noSpace, string(data)))
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "decoding input: %v\n", err)
+			os.Exit(1)
+		}
+		dec, err := crypt.Decrypt(password, pkt)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "%v\n", err)
+			os.Exit(1)
+		}
+		os.Stdout.Write(dec)
+	}
+}
+
+func noSpace(r rune) rune {
+	if r == ' ' || r == '\t' || r == '\n' {
+		return -1
+	}
+	return r
+}
diff --git a/vendor/github.com/mattermost/rsc/cmd/issue/issue.go b/vendor/github.com/mattermost/rsc/cmd/issue/issue.go
new file mode 100644
index 000000000..651a65d96
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/cmd/issue/issue.go
@@ -0,0 +1,185 @@
+package main
+
+import (
+	"encoding/xml"
+	"flag"
+	"fmt"
+	"html"
+	"log"
+	"net/http"
+	"net/url"
+	"os"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, `usage: issue [-p project] query
+
+If query is a single number, prints the full history for the issue.
+Otherwise, prints a table of matching results.
+The special query 'go1' is shorthand for 'Priority-Go1'.
+`)
+	os.Exit(2)
+}
+
+type Feed struct {
+	Entry Entries `xml:"entry"`
+}
+
+type Entry struct {
+	ID        string    `xml:"id"`
+	Title     string    `xml:"title"`
+	Published time.Time `xml:"published"`
+	Content   string    `xml:"content"`
+	Updates   []Update  `xml:"updates"`
+	Author struct {
+		Name string `xml:"name"`
+	} `xml:"author"`
+	Owner string `xml:"owner"`
+	Status string `xml:"status"`
+	Label []string `xml:"label"`
+}
+
+type Update struct {
+	Summary string `xml:"summary"`
+	Owner   string `xml:"ownerUpdate"`
+	Label   string `xml:"label"`
+	Status  string `xml:"status"`
+}
+
+type Entries []Entry
+
+func (e Entries) Len() int           { return len(e) }
+func (e Entries) Swap(i, j int)      { e[i], e[j] = e[j], e[i] }
+func (e Entries) Less(i, j int) bool { return e[i].Title < e[j].Title }
+
+var project = flag.String("p", "go", "code.google.com project identifier")
+var v = flag.Bool("v", false, "verbose")
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+	if flag.NArg() != 1 {
+		usage()
+	}
+
+	full := false
+	q := flag.Arg(0)
+	n, _ := strconv.Atoi(q)
+	if n != 0 {
+		q = "id:" + q
+		full = true
+	}
+	if q == "go1" {
+		q = "label:Priority-Go1"
+	}
+
+	log.SetFlags(0)
+
+	query := url.Values{
+		"q":           {q},
+		"max-results": {"400"},
+	}
+	if !full {
+		query["can"] = []string{"open"}
+	}
+	u := "https://code.google.com/feeds/issues/p/" + *project + "/issues/full?" + query.Encode()
+	if *v {
+		log.Print(u)
+	}
+	r, err := http.Get(u)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	var feed Feed
+	if err := xml.NewDecoder(r.Body).Decode(&feed); err != nil {
+		log.Fatal(err)
+	}
+	r.Body.Close()
+
+	sort.Sort(feed.Entry)
+	for _, e := range feed.Entry {
+		id := e.ID
+		if i := strings.Index(id, "id="); i >= 0 {
+			id = id[:i+len("id=")]
+		}
+		fmt.Printf("%s\t%s\n", id, e.Title)
+		if full {
+			fmt.Printf("Reported by %s (%s)\n", e.Author.Name, e.Published.Format("2006-01-02 15:04:05"))
+			if e.Owner != "" {
+				fmt.Printf("\tOwner: %s\n", e.Owner)
+			}
+			if e.Status != "" {
+				fmt.Printf("\tStatus: %s\n", e.Status)
+			}
+			for _, l := range e.Label {
+				fmt.Printf("\tLabel: %s\n", l)
+			}
+			if e.Content != "" {
+				fmt.Printf("\n\t%s\n", wrap(html.UnescapeString(e.Content), "\t"))
+			}
+			u := "https://code.google.com/feeds/issues/p/" + *project + "/issues/" + id + "/comments/full"
+			if *v {
+				log.Print(u)
+			}
+			r, err := http.Get(u)
+			if err != nil {
+				log.Fatal(err)
+			}
+
+			var feed Feed
+			if err := xml.NewDecoder(r.Body).Decode(&feed); err != nil {
+				log.Fatal(err)
+			}
+			r.Body.Close()
+
+			for _, e := range feed.Entry {
+				fmt.Printf("\n%s (%s)\n", e.Title, e.Published.Format("2006-01-02 15:04:05"))
+				for _, up := range e.Updates {
+					if up.Summary != "" {
+						fmt.Printf("\tSummary: %s\n", up.Summary)
+					}
+					if up.Owner != "" {
+						fmt.Printf("\tOwner: %s\n", up.Owner)
+					}
+					if up.Status != "" {
+						fmt.Printf("\tStatus: %s\n", up.Status)
+					}
+					if up.Label != "" {
+						fmt.Printf("\tLabel: %s\n", up.Label)
+					}
+				}
+				if e.Content != "" {
+					fmt.Printf("\n\t%s\n", wrap(html.UnescapeString(e.Content), "\t"))
+				}
+			}
+		}
+	}
+}
+
+func wrap(t string, prefix string) string {
+	out := ""
+	t = strings.Replace(t, "\r\n", "\n", -1)
+	lines := strings.Split(t, "\n")
+	for i, line := range lines {
+		if i > 0 {
+			out += "\n" + prefix
+		}
+		s := line
+		for len(s) > 70 {
+			i := strings.LastIndex(s[:70], " ")
+			if i < 0 {
+				i = 69
+			}
+			i++
+			out += s[:i] + "\n" + prefix
+			s = s[i:]
+		}
+		out += s
+	}
+	return out
+}
diff --git a/vendor/github.com/mattermost/rsc/cmd/jfmt/main.go b/vendor/github.com/mattermost/rsc/cmd/jfmt/main.go
new file mode 100644
index 000000000..31c5eddfc
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/cmd/jfmt/main.go
@@ -0,0 +1,37 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// jfmt reads JSON from standard input, formats it, and writes it to standard output.
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+)
+
+func main() {
+	log.SetFlags(0)
+
+	if len(os.Args) > 1 {
+		fmt.Fprintf(os.Stderr, "usage: json < input > output\n")
+		os.Exit(2)
+	}
+	
+	// TODO: Can do on the fly.
+	
+	data, err := ioutil.ReadAll(os.Stdin)
+	if err != nil {
+		log.Fatal(err)
+	}
+	
+	var buf bytes.Buffer
+	json.Indent(&buf, data, "", "    ")
+	buf.WriteByte('\n')
+
+	os.Stdout.Write(buf.Bytes())
+}
diff --git a/vendor/github.com/mattermost/rsc/crypt/crypt.go b/vendor/github.com/mattermost/rsc/crypt/crypt.go
new file mode 100644
index 000000000..c65129be4
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/crypt/crypt.go
@@ -0,0 +1,150 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package crypt provides simple, password-based encryption and decryption of data blobs.
+package crypt
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/hmac"
+	"crypto/rand"
+	"crypto/sha1"
+	"fmt"
+	"io"
+
+	"code.google.com/p/go.crypto/pbkdf2"
+)
+
+// This program manipulates encrypted, signed packets with the following format:
+//	1 byte version
+//	8 byte salt
+//	4 byte key hash
+//	aes.BlockSize-byte IV
+//	aes.BlockSize-byte encryption (maybe longer)
+//	sha1.Size-byte HMAC signature
+
+const version = 0
+
+// deriveKey returns the AES key, HMAC-SHA1 key, and key hash for
+// the given password, salt combination.
+func deriveKey(password string, salt []byte) (aesKey, hmacKey, keyHash []byte) {
+	const keySize = 16
+	key := pbkdf2.Key([]byte(password), salt, 4096, 2*keySize, sha1.New)
+	aesKey = key[:keySize]
+	hmacKey = key[keySize:]
+	h := sha1.New()
+	h.Write(key)
+	keyHash = h.Sum(nil)[:4]
+	return
+}
+
+// Encrypt encrypts the plaintext into an encrypted packet
+// using the given password. The password is required for
+// decryption.
+func Encrypt(password string, plaintext []byte) (encrypted []byte, err error) {
+	// Derive key material from password and salt.
+	salt := make([]byte, 8)
+	_, err = io.ReadFull(rand.Reader, salt)
+	if err != nil {
+		return nil, err
+	}
+	aesKey, hmacKey, keyHash := deriveKey(password, salt)
+
+	// Pad.
+	n := aes.BlockSize - len(plaintext)%aes.BlockSize
+	dec := make([]byte, len(plaintext)+n)
+	copy(dec, plaintext)
+	for i := len(plaintext); i < len(dec); i++ {
+		dec[i] = byte(n)
+	}
+
+	// Encrypt.
+	iv := make([]byte, aes.BlockSize)
+	_, err = io.ReadFull(rand.Reader, iv)
+	if err != nil {
+		return nil, err
+	}
+	aesBlock, err := aes.NewCipher(aesKey)
+	if err != nil {
+		// Cannot happen - key is right size.
+		panic("aes: " + err.Error())
+	}
+	m := cipher.NewCBCEncrypter(aesBlock, iv)
+	enc := make([]byte, len(dec))
+	m.CryptBlocks(enc, dec)
+
+	// Construct packet.
+	var pkt []byte
+	pkt = append(pkt, version)
+	pkt = append(pkt, salt...)
+	pkt = append(pkt, keyHash...)
+	pkt = append(pkt, iv...)
+	pkt = append(pkt, enc...)
+
+	// Sign.
+	h := hmac.New(sha1.New, hmacKey)
+	h.Write(pkt)
+	pkt = append(pkt, h.Sum(nil)...)
+
+	return pkt, nil
+}
+
+// Decrypt decrypts the encrypted packet using the given password.
+// It returns the decrypted data.
+func Decrypt(password string, encrypted []byte) (plaintext []byte, err error) {
+	// Pull apart packet.
+	pkt := encrypted
+	if len(pkt) < 1+8+4+2*aes.BlockSize+sha1.Size {
+		return nil, fmt.Errorf("encrypted packet too short")
+	}
+	vers, pkt := pkt[:1], pkt[1:]
+	salt, pkt := pkt[:8], pkt[8:]
+	hash, pkt := pkt[:4], pkt[4:]
+	iv, pkt := pkt[:aes.BlockSize], pkt[aes.BlockSize:]
+	enc, sig := pkt[:len(pkt)-sha1.Size], pkt[len(pkt)-sha1.Size:]
+
+	if vers[0] != version || len(enc)%aes.BlockSize != 0 {
+		return nil, fmt.Errorf("malformed encrypted packet")
+	}
+
+	// Derive key and check against hash.
+	aesKey, hmacKey, keyHash := deriveKey(password, salt)
+	if !bytes.Equal(hash, keyHash) {
+		return nil, fmt.Errorf("incorrect password - %x vs %x", hash, keyHash)
+	}
+
+	// Verify signature.
+	h := hmac.New(sha1.New, hmacKey)
+	h.Write(encrypted[:len(encrypted)-len(sig)])
+	if !bytes.Equal(sig, h.Sum(nil)) {
+		return nil, fmt.Errorf("cannot authenticate encrypted packet")
+	}
+
+	// Decrypt.
+	aesBlock, err := aes.NewCipher(aesKey)
+	if err != nil {
+		// Cannot happen - key is right size.
+		panic("aes: " + err.Error())
+	}
+	m := cipher.NewCBCDecrypter(aesBlock, iv)
+	dec := make([]byte, len(enc))
+	m.CryptBlocks(dec, enc)
+
+	// Unpad.
+	pad := dec[len(dec)-1]
+	if pad <= 0 || pad > aes.BlockSize {
+		return nil, fmt.Errorf("malformed packet padding")
+	}
+	for _, b := range dec[len(dec)-int(pad):] {
+		if b != pad {
+			return nil, fmt.Errorf("malformed packet padding")
+		}
+	}
+	dec = dec[:len(dec)-int(pad)]
+
+	// Success!
+	return dec, nil
+}
diff --git a/vendor/github.com/mattermost/rsc/devweb/main.go b/vendor/github.com/mattermost/rsc/devweb/main.go
new file mode 100644
index 000000000..7a09f032b
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/devweb/main.go
@@ -0,0 +1,317 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Devweb is a simple environment for developing a web server.
+// It runs its own web server on the given address and proxies
+// all requests to the http server program named by importpath.
+// It takes care of recompiling and restarting the program as needed.
+//
+// The server program should be a trivial main program like:
+//
+//	package main
+//	
+//	import (
+//		"github.com/mattermost/rsc/devweb/slave"
+//	
+//		_ "this/package"
+//		_ "that/package"
+//	)
+//	
+//	func main() {
+//		slave.Main()
+//	}
+//
+// The import _ lines import packages that register HTTP handlers,
+// like in an App Engine program.
+//
+// As you make changes to this/package or that/package (or their
+// dependencies), devweb recompiles and relaunches the servers as
+// needed to serve requests.
+//
+package main
+
+// BUG(rsc): Devweb should probably 
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io"
+	"log"
+	"net"
+	"net/http"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"sync"
+	"time"
+)
+
+func usage() {
+	fmt.Fprint(os.Stderr, `usage: devweb [-addr :8000] importpath
+
+Devweb runs a web server on the given address and proxies all requests
+to the http server program named by importpath.  It takes care of
+recompiling and restarting the program as needed.
+
+The http server program must itself have a -addr argument that
+says which TCP port to listen on.
+`,
+	)
+}
+
+var addr = flag.String("addr", ":8000", "web service address")
+var rootPackage string
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+	args := flag.Args()
+	if len(args) != 1 {
+		usage()
+	}
+	rootPackage = args[0]
+
+	log.Fatal(http.ListenAndServe(*addr, http.HandlerFunc(relay)))
+}
+
+func relay(w http.ResponseWriter, req *http.Request) {
+	defer func() {
+		if err := recover(); err != nil {
+			http.Error(w, fmt.Sprint(err), 200)
+		}
+	}()
+
+	c, proxy, err := buildProxy()
+	if err != nil {
+		panic(err)
+	}
+	defer c.Close()
+	_ = proxy
+
+	outreq := new(http.Request)
+	*outreq = *req // includes shallow copies of maps, but okay
+
+	outreq.Proto = "HTTP/1.1"
+	outreq.ProtoMajor = 1
+	outreq.ProtoMinor = 1
+	outreq.Close = false
+
+	// Remove the connection header to the backend.  We want a
+	// persistent connection, regardless of what the client sent
+	// to us.  This is modifying the same underlying map from req
+	// (shallow copied above) so we only copy it if necessary.
+	if outreq.Header.Get("Connection") != "" {
+		outreq.Header = make(http.Header)
+		copyHeader(outreq.Header, req.Header)
+		outreq.Header.Del("Connection")
+	}
+
+	outreq.Write(c)
+
+	br := bufio.NewReader(c)
+	resp, err := http.ReadResponse(br, outreq)
+	if err != nil {
+		panic(err)
+	}
+
+	copyHeader(w.Header(), resp.Header)
+	w.WriteHeader(resp.StatusCode)
+
+	if resp.Body != nil {
+		io.Copy(w, resp.Body)
+	}
+}
+
+func copyHeader(dst, src http.Header) {
+	for k, vv := range src {
+		for _, v := range vv {
+			dst.Add(k, v)
+		}
+	}
+}
+
+type cmdProxy struct {
+	cmd  *exec.Cmd
+	addr string
+}
+
+func (p *cmdProxy) kill() {
+	if p == nil {
+		return
+	}
+	p.cmd.Process.Kill()
+	p.cmd.Wait()
+}
+
+var proxyInfo struct {
+	sync.Mutex
+	build  time.Time
+	check  time.Time
+	active *cmdProxy
+	err    error
+}
+
+func buildProxy() (c net.Conn, proxy *cmdProxy, err error) {
+	p := &proxyInfo
+
+	t := time.Now()
+	p.Lock()
+	defer p.Unlock()
+	if t.Before(p.check) {
+		// We waited for the lock while someone else dialed.
+		// If we can connect, done.
+		if p.active != nil {
+			if c, err := net.DialTimeout("tcp", p.active.addr, 5*time.Second); err == nil {
+				return c, p.active, nil
+			}
+		}
+	}
+
+	defer func() {
+		p.err = err
+		p.check = time.Now()
+	}()
+
+	pkgs, err := loadPackage(rootPackage)
+	if err != nil {
+		return nil, nil, fmt.Errorf("load %s: %s", rootPackage, err)
+	}
+
+	deps := pkgs[0].Deps
+	if len(deps) > 0 && deps[0] == "C" {
+		deps = deps[1:]
+	}
+	pkgs1, err := loadPackage(deps...)
+	if err != nil {
+		return nil, nil, fmt.Errorf("load %v: %s", deps, err)
+	}
+	pkgs = append(pkgs, pkgs1...)
+
+	var latest time.Time
+
+	for _, pkg := range pkgs {
+		var files []string
+		files = append(files, pkg.GoFiles...)
+		files = append(files, pkg.CFiles...)
+		files = append(files, pkg.HFiles...)
+		files = append(files, pkg.SFiles...)
+		files = append(files, pkg.CgoFiles...)
+
+		for _, file := range files {
+			if fi, err := os.Stat(filepath.Join(pkg.Dir, file)); err == nil && fi.ModTime().After(latest) {
+				latest = fi.ModTime()
+			}
+		}
+	}
+
+	if latest.After(p.build) {
+		p.active.kill()
+		p.active = nil
+
+		out, err := exec.Command("go", "build", "-o", "prox.exe", rootPackage).CombinedOutput()
+		if len(out) > 0 {
+			return nil, nil, fmt.Errorf("%s", out)
+		}
+		if err != nil {
+			return nil, nil, err
+		}
+
+		p.build = latest
+	}
+
+	// If we can connect, done.
+	if p.active != nil {
+		if c, err := net.DialTimeout("tcp", p.active.addr, 5*time.Second); err == nil {
+			return c, p.active, nil
+		}
+	}
+
+	// Otherwise, start a new server.
+	p.active.kill()
+	p.active = nil
+
+	l, err := net.Listen("tcp", "localhost:0")
+	if err != nil {
+		return nil, nil, err
+	}
+	addr := l.Addr().String()
+
+	cmd := exec.Command("prox.exe", "LISTEN_STDIN")
+	cmd.Stdin, err = l.(*net.TCPListener).File()
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err != nil {
+		l.Close()
+		return nil, nil, err
+	}
+	err = cmd.Start()
+	l.Close()
+
+	if err != nil {
+		return nil, nil, err
+	}
+
+	c, err = net.DialTimeout("tcp", addr, 5*time.Second)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	p.active = &cmdProxy{cmd, addr}
+	return c, p.active, nil
+}
+
+type Pkg struct {
+	ImportPath string
+	Dir        string
+	GoFiles    []string
+	CFiles     []string
+	HFiles     []string
+	SFiles     []string
+	CgoFiles   []string
+	Deps       []string
+}
+
+func loadPackage(name ...string) ([]*Pkg, error) {
+	args := []string{"list", "-json"}
+	args = append(args, name...)
+	var stderr bytes.Buffer
+	cmd := exec.Command("go", args...)
+	r, err := cmd.StdoutPipe()
+	if err != nil {
+		return nil, err
+	}
+	cmd.Stderr = &stderr
+	if err := cmd.Start(); err != nil {
+		return nil, err
+	}
+
+	dec := json.NewDecoder(r)
+	var pkgs []*Pkg
+	for {
+		p := new(Pkg)
+		if err := dec.Decode(p); err != nil {
+			if err == io.EOF {
+				break
+			}
+			cmd.Process.Kill()
+			return nil, err
+		}
+		pkgs = append(pkgs, p)
+	}
+
+	err = cmd.Wait()
+	if b := stderr.Bytes(); len(b) > 0 {
+		return nil, fmt.Errorf("%s", b)
+	}
+	if err != nil {
+		return nil, err
+	}
+	if len(pkgs) != len(name) {
+		return nil, fmt.Errorf("found fewer packages than expected")
+	}
+	return pkgs, nil
+}
diff --git a/vendor/github.com/mattermost/rsc/devweb/slave/slave.go b/vendor/github.com/mattermost/rsc/devweb/slave/slave.go
new file mode 100644
index 000000000..a6983c23c
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/devweb/slave/slave.go
@@ -0,0 +1,26 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package slave
+
+import (
+	"fmt"
+	"log"
+	"net"
+	"net/http"
+	"os"
+)
+
+func Main() {
+	if len(os.Args) != 2 || os.Args[1] != "LISTEN_STDIN" {
+		fmt.Fprintf(os.Stderr, "devweb slave must be invoked by devweb\n")
+		os.Exit(2)
+	}
+	l, err := net.FileListener(os.Stdin)
+	if err != nil {
+		log.Fatal(err)
+	}
+	os.Stdin.Close()
+	log.Fatal(http.Serve(l, nil))
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/debug.go b/vendor/github.com/mattermost/rsc/fuse/debug.go
new file mode 100644
index 000000000..2dc5d4312
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/debug.go
@@ -0,0 +1,20 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// FUSE service loop, for servers that wish to use it.
+
+package fuse
+
+import (
+	"runtime"
+)
+
+func stack() string {
+	buf := make([]byte, 1024)
+	return string(buf[:runtime.Stack(buf, false)])
+}
+
+var Debugf = nop
+
+func nop(string, ...interface{}) {}
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse.go b/vendor/github.com/mattermost/rsc/fuse/fuse.go
new file mode 100644
index 000000000..2ad10c93e
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse.go
@@ -0,0 +1,1650 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c,
+// which carries this notice:
+//
+// The files in this directory are subject to the following license.
+// 
+// The author of this software is Russ Cox.
+// 
+//         Copyright (c) 2006 Russ Cox
+// 
+// Permission to use, copy, modify, and distribute this software for any
+// purpose without fee is hereby granted, provided that this entire notice
+// is included in all copies of any software which is or includes a copy
+// or modification of this software and in all copies of the supporting
+// documentation for such software.
+// 
+// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+// WARRANTY.  IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY
+// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
+// FITNESS FOR ANY PARTICULAR PURPOSE.
+
+// Package fuse enables writing FUSE file systems on FreeBSD, Linux, and OS X.
+//
+// On OS X, it requires OSXFUSE (http://osxfuse.github.com/).
+//
+// There are two approaches to writing a FUSE file system.  The first is to speak
+// the low-level message protocol, reading from a Conn using ReadRequest and
+// writing using the various Respond methods.  This approach is closest to
+// the actual interaction with the kernel and can be the simplest one in contexts
+// such as protocol translators.
+//
+// Servers of synthesized file systems tend to share common bookkeeping
+// abstracted away by the second approach, which is to call the Conn's
+// Serve method to serve the FUSE protocol using
+// an implementation of the service methods in the interfaces
+// FS (file system), Node (file or directory), and Handle (opened file or directory).
+// There are a daunting number of such methods that can be written,
+// but few are required.
+// The specific methods are described in the documentation for those interfaces.
+//
+// The hellofs subdirectory contains a simple illustration of the ServeFS approach.
+//
+// Service Methods
+//
+// The required and optional methods for the FS, Node, and Handle interfaces
+// have the general form
+//
+//	Op(req *OpRequest, resp *OpResponse, intr Intr) Error
+//
+// where Op is the name of a FUSE operation.  Op reads request parameters
+// from req and writes results to resp.  An operation whose only result is
+// the error result omits the resp parameter.  Multiple goroutines may call
+// service methods simultaneously; the methods being called are responsible
+// for appropriate synchronization.
+//
+// Interrupted Operations
+//
+// In some file systems, some operations
+// may take an undetermined amount of time.  For example, a Read waiting for
+// a network message or a matching Write might wait indefinitely.  If the request
+// is cancelled and no longer needed, the package will close intr, a chan struct{}.
+// Blocking operations should select on a receive from intr and attempt to
+// abort the operation early if the receive succeeds (meaning the channel is closed).
+// To indicate that the operation failed because it was aborted, return fuse.EINTR.
+//
+// If an operation does not block for an indefinite amount of time, the intr parameter
+// can be ignored.
+//
+// Authentication
+//
+// All requests types embed a Header, meaning that the method can inspect
+// req.Pid, req.Uid, and req.Gid as necessary to implement permission checking.
+// Alternately, XXX.
+//
+// Mount Options
+//
+// XXX
+// 
+package fuse
+
+// BUG(rsc): The mount code for FreeBSD has not been written yet.
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"runtime"
+	"sync"
+	"syscall"
+	"time"
+	"unsafe"
+)
+
+// A Conn represents a connection to a mounted FUSE file system.
+type Conn struct {
+	fd  int
+	buf []byte
+	wio sync.Mutex
+
+	serveConn
+}
+
+// Mount mounts a new FUSE connection on the named directory
+// and returns a connection for reading and writing FUSE messages.
+func Mount(dir string) (*Conn, error) {
+	// TODO(rsc): mount options (...string?)
+	fd, errstr := mount(dir)
+	if errstr != "" {
+		return nil, errors.New(errstr)
+	}
+
+	return &Conn{fd: fd}, nil
+}
+
+// A Request represents a single FUSE request received from the kernel.
+// Use a type switch to determine the specific kind.
+// A request of unrecognized type will have concrete type *Header.
+type Request interface {
+	// Hdr returns the Header associated with this request.
+	Hdr() *Header
+
+	// RespondError responds to the request with the given error.
+	RespondError(Error)
+
+	String() string
+
+	// handle returns the HandleID for the request, or 0.
+	handle() HandleID
+}
+
+// A RequestID identifies an active FUSE request.
+type RequestID uint64
+
+// A NodeID is a number identifying a directory or file.
+// It must be unique among IDs returned in LookupResponses
+// that have not yet been forgotten by ForgetRequests.
+type NodeID uint64
+
+// A HandleID is a number identifying an open directory or file.
+// It only needs to be unique while the directory or file is open.
+type HandleID uint64
+
+// The RootID identifies the root directory of a FUSE file system.
+const RootID NodeID = rootID
+
+// A Header describes the basic information sent in every request.
+type Header struct {
+	Conn *Conn     // connection this request was received on
+	ID   RequestID // unique ID for request
+	Node NodeID    // file or directory the request is about
+	Uid  uint32    // user ID of process making request
+	Gid  uint32    // group ID of process making request
+	Pid  uint32    // process ID of process making request
+}
+
+func (h *Header) String() string {
+	return fmt.Sprintf("ID=%#x Node=%#x Uid=%d Gid=%d Pid=%d", h.ID, h.Node, h.Uid, h.Gid, h.Pid)
+}
+
+func (h *Header) Hdr() *Header {
+	return h
+}
+
+func (h *Header) handle() HandleID {
+	return 0
+}
+
+// An Error is a FUSE error.
+type Error interface {
+	errno() int32
+}
+
+const (
+	// ENOSYS indicates that the call is not supported.
+	ENOSYS = Errno(syscall.ENOSYS)
+
+	// ESTALE is used by Serve to respond to violations of the FUSE protocol.
+	ESTALE = Errno(syscall.ESTALE)
+
+	ENOENT = Errno(syscall.ENOENT)
+	EIO    = Errno(syscall.EIO)
+	EPERM  = Errno(syscall.EPERM)
+)
+
+type errno int
+
+func (e errno) errno() int32 {
+	return int32(e)
+}
+
+// Errno implements Error using a syscall.Errno.
+type Errno syscall.Errno
+
+func (e Errno) errno() int32 {
+	return int32(e)
+}
+
+func (e Errno) String() string {
+	return syscall.Errno(e).Error()
+}
+
+func (h *Header) RespondError(err Error) {
+	// FUSE uses negative errors!
+	// TODO: File bug report against OSXFUSE: positive error causes kernel panic.
+	out := &outHeader{Error: -err.errno(), Unique: uint64(h.ID)}
+	h.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+var maxWrite = syscall.Getpagesize()
+var bufSize = 4096 + maxWrite
+
+// a message represents the bytes of a single FUSE message
+type message struct {
+	conn *Conn
+	buf  []byte    // all bytes
+	hdr  *inHeader // header
+	off  int       // offset for reading additional fields
+}
+
+func newMessage(c *Conn) *message {
+	m := &message{conn: c, buf: make([]byte, bufSize)}
+	m.hdr = (*inHeader)(unsafe.Pointer(&m.buf[0]))
+	return m
+}
+
+func (m *message) len() uintptr {
+	return uintptr(len(m.buf) - m.off)
+}
+
+func (m *message) data() unsafe.Pointer {
+	var p unsafe.Pointer
+	if m.off < len(m.buf) {
+		p = unsafe.Pointer(&m.buf[m.off])
+	}
+	return p
+}
+
+func (m *message) bytes() []byte {
+	return m.buf[m.off:]
+}
+
+func (m *message) Header() Header {
+	h := m.hdr
+	return Header{Conn: m.conn, ID: RequestID(h.Unique), Node: NodeID(h.Nodeid), Uid: h.Uid, Gid: h.Gid, Pid: h.Pid}
+}
+
+// fileMode returns a Go os.FileMode from a Unix mode.
+func fileMode(unixMode uint32) os.FileMode {
+	mode := os.FileMode(unixMode & 0777)
+	switch unixMode & syscall.S_IFMT {
+	case syscall.S_IFREG:
+		// nothing
+	case syscall.S_IFDIR:
+		mode |= os.ModeDir
+	case syscall.S_IFCHR:
+		mode |= os.ModeCharDevice | os.ModeDevice
+	case syscall.S_IFBLK:
+		mode |= os.ModeDevice
+	case syscall.S_IFIFO:
+		mode |= os.ModeNamedPipe
+	case syscall.S_IFLNK:
+		mode |= os.ModeSymlink
+	case syscall.S_IFSOCK:
+		mode |= os.ModeSocket
+	default:
+		// no idea
+		mode |= os.ModeDevice
+	}
+	if unixMode&syscall.S_ISUID != 0 {
+		mode |= os.ModeSetuid
+	}
+	if unixMode&syscall.S_ISGID != 0 {
+		mode |= os.ModeSetgid
+	}
+	return mode
+}
+
+func (c *Conn) ReadRequest() (Request, error) {
+	// TODO: Some kind of buffer reuse.
+	m := newMessage(c)
+	n, err := syscall.Read(c.fd, m.buf)
+	if err != nil && err != syscall.ENODEV {
+		return nil, err
+	}
+	if n <= 0 {
+		return nil, io.EOF
+	}
+	m.buf = m.buf[:n]
+
+	if n < inHeaderSize {
+		return nil, errors.New("fuse: message too short")
+	}
+
+	// FreeBSD FUSE sends a short length in the header
+	// for FUSE_INIT even though the actual read length is correct.
+	if n == inHeaderSize+initInSize && m.hdr.Opcode == opInit && m.hdr.Len < uint32(n) {
+		m.hdr.Len = uint32(n)
+	}
+
+	// OSXFUSE sometimes sends the wrong m.hdr.Len in a FUSE_WRITE message.
+	if m.hdr.Len < uint32(n) && m.hdr.Len >= uint32(unsafe.Sizeof(writeIn{})) && m.hdr.Opcode == opWrite {
+		m.hdr.Len = uint32(n)
+	}
+
+	if m.hdr.Len != uint32(n) {
+		return nil, fmt.Errorf("fuse: read %d opcode %d but expected %d", n, m.hdr.Opcode, m.hdr.Len)
+	}
+
+	m.off = inHeaderSize
+
+	// Convert to data structures.
+	// Do not trust kernel to hand us well-formed data.
+	var req Request
+	switch m.hdr.Opcode {
+	default:
+		println("No opcode", m.hdr.Opcode)
+		goto unrecognized
+
+	case opLookup:
+		buf := m.bytes()
+		n := len(buf)
+		if n == 0 || buf[n-1] != '\x00' {
+			goto corrupt
+		}
+		req = &LookupRequest{
+			Header: m.Header(),
+			Name:   string(buf[:n-1]),
+		}
+
+	case opForget:
+		in := (*forgetIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		req = &ForgetRequest{
+			Header: m.Header(),
+			N:      in.Nlookup,
+		}
+
+	case opGetattr:
+		req = &GetattrRequest{
+			Header: m.Header(),
+		}
+
+	case opSetattr:
+		in := (*setattrIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		req = &SetattrRequest{
+			Header:   m.Header(),
+			Valid:    SetattrValid(in.Valid),
+			Handle:   HandleID(in.Fh),
+			Size:     in.Size,
+			Atime:    time.Unix(int64(in.Atime), int64(in.AtimeNsec)),
+			Mtime:    time.Unix(int64(in.Mtime), int64(in.MtimeNsec)),
+			Mode:     fileMode(in.Mode),
+			Uid:      in.Uid,
+			Gid:      in.Gid,
+			Bkuptime: in.BkupTime(),
+			Chgtime:  in.Chgtime(),
+			Flags:    in.Flags(),
+		}
+
+	case opReadlink:
+		if len(m.bytes()) > 0 {
+			goto corrupt
+		}
+		req = &ReadlinkRequest{
+			Header: m.Header(),
+		}
+
+	case opSymlink:
+		// m.bytes() is "newName\0target\0"
+		names := m.bytes()
+		if len(names) == 0 || names[len(names)-1] != 0 {
+			goto corrupt
+		}
+		i := bytes.IndexByte(names, '\x00')
+		if i < 0 {
+			goto corrupt
+		}
+		newName, target := names[0:i], names[i+1:len(names)-1]
+		req = &SymlinkRequest{
+			Header:  m.Header(),
+			NewName: string(newName),
+			Target:  string(target),
+		}
+
+	case opLink:
+		in := (*linkIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		newName := m.bytes()[unsafe.Sizeof(*in):]
+		if len(newName) < 2 || newName[len(newName)-1] != 0 {
+			goto corrupt
+		}
+		newName = newName[:len(newName)-1]
+		req = &LinkRequest{
+			Header:  m.Header(),
+			OldNode: NodeID(in.Oldnodeid),
+			NewName: string(newName),
+		}
+
+	case opMknod:
+		in := (*mknodIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		name := m.bytes()[unsafe.Sizeof(*in):]
+		if len(name) < 2 || name[len(name)-1] != '\x00' {
+			goto corrupt
+		}
+		name = name[:len(name)-1]
+		req = &MknodRequest{
+			Header: m.Header(),
+			Mode:   fileMode(in.Mode),
+			Rdev:   in.Rdev,
+			Name:   string(name),
+		}
+
+	case opMkdir:
+		in := (*mkdirIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		name := m.bytes()[unsafe.Sizeof(*in):]
+		i := bytes.IndexByte(name, '\x00')
+		if i < 0 {
+			goto corrupt
+		}
+		req = &MkdirRequest{
+			Header: m.Header(),
+			Name:   string(name[:i]),
+			Mode:   fileMode(in.Mode) | os.ModeDir,
+		}
+
+	case opUnlink, opRmdir:
+		buf := m.bytes()
+		n := len(buf)
+		if n == 0 || buf[n-1] != '\x00' {
+			goto corrupt
+		}
+		req = &RemoveRequest{
+			Header: m.Header(),
+			Name:   string(buf[:n-1]),
+			Dir:    m.hdr.Opcode == opRmdir,
+		}
+
+	case opRename:
+		in := (*renameIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		newDirNodeID := NodeID(in.Newdir)
+		oldNew := m.bytes()[unsafe.Sizeof(*in):]
+		// oldNew should be "old\x00new\x00"
+		if len(oldNew) < 4 {
+			goto corrupt
+		}
+		if oldNew[len(oldNew)-1] != '\x00' {
+			goto corrupt
+		}
+		i := bytes.IndexByte(oldNew, '\x00')
+		if i < 0 {
+			goto corrupt
+		}
+		oldName, newName := string(oldNew[:i]), string(oldNew[i+1:len(oldNew)-1])
+		// log.Printf("RENAME: newDirNode = %d; old = %q, new = %q", newDirNodeID, oldName, newName)
+		req = &RenameRequest{
+			Header:  m.Header(),
+			NewDir:  newDirNodeID,
+			OldName: oldName,
+			NewName: newName,
+		}
+
+	case opOpendir, opOpen:
+		in := (*openIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		req = &OpenRequest{
+			Header: m.Header(),
+			Dir:    m.hdr.Opcode == opOpendir,
+			Flags:  in.Flags,
+			Mode:   fileMode(in.Mode),
+		}
+
+	case opRead, opReaddir:
+		in := (*readIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		req = &ReadRequest{
+			Header: m.Header(),
+			Dir:    m.hdr.Opcode == opReaddir,
+			Handle: HandleID(in.Fh),
+			Offset: int64(in.Offset),
+			Size:   int(in.Size),
+		}
+
+	case opWrite:
+		in := (*writeIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		r := &WriteRequest{
+			Header: m.Header(),
+			Handle: HandleID(in.Fh),
+			Offset: int64(in.Offset),
+			Flags:  WriteFlags(in.WriteFlags),
+		}
+		buf := m.bytes()[unsafe.Sizeof(*in):]
+		if uint32(len(buf)) < in.Size {
+			goto corrupt
+		}
+		r.Data = buf
+		req = r
+
+	case opStatfs:
+		req = &StatfsRequest{
+			Header: m.Header(),
+		}
+
+	case opRelease, opReleasedir:
+		in := (*releaseIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		req = &ReleaseRequest{
+			Header:       m.Header(),
+			Dir:          m.hdr.Opcode == opReleasedir,
+			Handle:       HandleID(in.Fh),
+			Flags:        in.Flags,
+			ReleaseFlags: ReleaseFlags(in.ReleaseFlags),
+			LockOwner:    in.LockOwner,
+		}
+
+	case opFsync:
+		in := (*fsyncIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		req = &FsyncRequest{
+			Header: m.Header(),
+			Handle: HandleID(in.Fh),
+			Flags:  in.FsyncFlags,
+		}
+
+	case opSetxattr:
+		var size uint32
+		var r *SetxattrRequest
+		if runtime.GOOS == "darwin" {
+			in := (*setxattrInOSX)(m.data())
+			if m.len() < unsafe.Sizeof(*in) {
+				goto corrupt
+			}
+			r = &SetxattrRequest{
+				Flags:    in.Flags,
+				Position: in.Position,
+			}
+			size = in.Size
+			m.off += int(unsafe.Sizeof(*in))
+		} else {
+			in := (*setxattrIn)(m.data())
+			if m.len() < unsafe.Sizeof(*in) {
+				goto corrupt
+			}
+			r = &SetxattrRequest{}
+			size = in.Size
+			m.off += int(unsafe.Sizeof(*in))
+		}
+		r.Header = m.Header()
+		name := m.bytes()
+		i := bytes.IndexByte(name, '\x00')
+		if i < 0 {
+			goto corrupt
+		}
+		r.Name = string(name[:i])
+		r.Xattr = name[i+1:]
+		if uint32(len(r.Xattr)) < size {
+			goto corrupt
+		}
+		r.Xattr = r.Xattr[:size]
+		req = r
+
+	case opGetxattr:
+		if runtime.GOOS == "darwin" {
+			in := (*getxattrInOSX)(m.data())
+			if m.len() < unsafe.Sizeof(*in) {
+				goto corrupt
+			}
+			req = &GetxattrRequest{
+				Header:   m.Header(),
+				Size:     in.Size,
+				Position: in.Position,
+			}
+		} else {
+			in := (*getxattrIn)(m.data())
+			if m.len() < unsafe.Sizeof(*in) {
+				goto corrupt
+			}
+			req = &GetxattrRequest{
+				Header: m.Header(),
+				Size:   in.Size,
+			}
+		}
+
+	case opListxattr:
+		if runtime.GOOS == "darwin" {
+			in := (*getxattrInOSX)(m.data())
+			if m.len() < unsafe.Sizeof(*in) {
+				goto corrupt
+			}
+			req = &ListxattrRequest{
+				Header:   m.Header(),
+				Size:     in.Size,
+				Position: in.Position,
+			}
+		} else {
+			in := (*getxattrIn)(m.data())
+			if m.len() < unsafe.Sizeof(*in) {
+				goto corrupt
+			}
+			req = &ListxattrRequest{
+				Header: m.Header(),
+				Size:   in.Size,
+			}
+		}
+
+	case opRemovexattr:
+		buf := m.bytes()
+		n := len(buf)
+		if n == 0 || buf[n-1] != '\x00' {
+			goto corrupt
+		}
+		req = &RemovexattrRequest{
+			Header: m.Header(),
+			Name:   string(buf[:n-1]),
+		}
+
+	case opFlush:
+		in := (*flushIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		req = &FlushRequest{
+			Header:    m.Header(),
+			Handle:    HandleID(in.Fh),
+			Flags:     in.FlushFlags,
+			LockOwner: in.LockOwner,
+		}
+
+	case opInit:
+		in := (*initIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		req = &InitRequest{
+			Header:       m.Header(),
+			Major:        in.Major,
+			Minor:        in.Minor,
+			MaxReadahead: in.MaxReadahead,
+			Flags:        InitFlags(in.Flags),
+		}
+
+	case opFsyncdir:
+		panic("opFsyncdir")
+	case opGetlk:
+		panic("opGetlk")
+	case opSetlk:
+		panic("opSetlk")
+	case opSetlkw:
+		panic("opSetlkw")
+
+	case opAccess:
+		in := (*accessIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		req = &AccessRequest{
+			Header: m.Header(),
+			Mask:   in.Mask,
+		}
+
+	case opCreate:
+		in := (*openIn)(m.data())
+		if m.len() < unsafe.Sizeof(*in) {
+			goto corrupt
+		}
+		name := m.bytes()[unsafe.Sizeof(*in):]
+		i := bytes.IndexByte(name, '\x00')
+		if i < 0 {
+			goto corrupt
+		}
+		req = &CreateRequest{
+			Header: m.Header(),
+			Flags:  in.Flags,
+			Mode:   fileMode(in.Mode),
+			Name:   string(name[:i]),
+		}
+
+	case opInterrupt:
+		panic("opInterrupt")
+	case opBmap:
+		panic("opBmap")
+
+	case opDestroy:
+		req = &DestroyRequest{
+			Header: m.Header(),
+		}
+
+	// OS X
+	case opSetvolname:
+		panic("opSetvolname")
+	case opGetxtimes:
+		panic("opGetxtimes")
+	case opExchange:
+		panic("opExchange")
+	}
+
+	return req, nil
+
+corrupt:
+	println("malformed message")
+	return nil, fmt.Errorf("fuse: malformed message")
+
+unrecognized:
+	// Unrecognized message.
+	// Assume higher-level code will send a "no idea what you mean" error.
+	h := m.Header()
+	return &h, nil
+}
+
+func (c *Conn) respond(out *outHeader, n uintptr) {
+	c.wio.Lock()
+	defer c.wio.Unlock()
+	out.Len = uint32(n)
+	msg := (*[1 << 30]byte)(unsafe.Pointer(out))[:n]
+	nn, err := syscall.Write(c.fd, msg)
+	if nn != len(msg) || err != nil {
+		log.Printf("RESPOND WRITE: %d %v", nn, err)
+		log.Printf("with stack: %s", stack())
+	}
+}
+
+func (c *Conn) respondData(out *outHeader, n uintptr, data []byte) {
+	c.wio.Lock()
+	defer c.wio.Unlock()
+	// TODO: use writev
+	out.Len = uint32(n + uintptr(len(data)))
+	msg := make([]byte, out.Len)
+	copy(msg, (*[1 << 30]byte)(unsafe.Pointer(out))[:n])
+	copy(msg[n:], data)
+	syscall.Write(c.fd, msg)
+}
+
+// An InitRequest is the first request sent on a FUSE file system.
+type InitRequest struct {
+	Header
+	Major        uint32
+	Minor        uint32
+	MaxReadahead uint32
+	Flags        InitFlags
+}
+
+func (r *InitRequest) String() string {
+	return fmt.Sprintf("Init [%s] %d.%d ra=%d fl=%v", &r.Header, r.Major, r.Minor, r.MaxReadahead, r.Flags)
+}
+
+// An InitResponse is the response to an InitRequest.
+type InitResponse struct {
+	MaxReadahead uint32
+	Flags        InitFlags
+	MaxWrite     uint32
+}
+
+func (r *InitResponse) String() string {
+	return fmt.Sprintf("Init %+v", *r)
+}
+
+// Respond replies to the request with the given response.
+func (r *InitRequest) Respond(resp *InitResponse) {
+	out := &initOut{
+		outHeader:    outHeader{Unique: uint64(r.ID)},
+		Major:        kernelVersion,
+		Minor:        kernelMinorVersion,
+		MaxReadahead: resp.MaxReadahead,
+		Flags:        uint32(resp.Flags),
+		MaxWrite:     resp.MaxWrite,
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A StatfsRequest requests information about the mounted file system.
+type StatfsRequest struct {
+	Header
+}
+
+func (r *StatfsRequest) String() string {
+	return fmt.Sprintf("Statfs [%s]\n", &r.Header)
+}
+
+// Respond replies to the request with the given response.
+func (r *StatfsRequest) Respond(resp *StatfsResponse) {
+	out := &statfsOut{
+		outHeader: outHeader{Unique: uint64(r.ID)},
+		St: kstatfs{
+			Blocks:  resp.Blocks,
+			Bfree:   resp.Bfree,
+			Bavail:  resp.Bavail,
+			Files:   resp.Files,
+			Bsize:   resp.Bsize,
+			Namelen: resp.Namelen,
+			Frsize:  resp.Frsize,
+		},
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A StatfsResponse is the response to a StatfsRequest.
+type StatfsResponse struct {
+	Blocks  uint64 // Total data blocks in file system.
+	Bfree   uint64 // Free blocks in file system.
+	Bavail  uint64 // Free blocks in file system if you're not root.
+	Files   uint64 // Total files in file system.
+	Ffree   uint64 // Free files in file system.
+	Bsize   uint32 // Block size
+	Namelen uint32 // Maximum file name length?
+	Frsize  uint32 // ?
+}
+
+func (r *StatfsResponse) String() string {
+	return fmt.Sprintf("Statfs %+v", *r)
+}
+
+// An AccessRequest asks whether the file can be accessed
+// for the purpose specified by the mask.
+type AccessRequest struct {
+	Header
+	Mask uint32
+}
+
+func (r *AccessRequest) String() string {
+	return fmt.Sprintf("Access [%s] mask=%#x", &r.Header, r.Mask)
+}
+
+// Respond replies to the request indicating that access is allowed.
+// To deny access, use RespondError.
+func (r *AccessRequest) Respond() {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// An Attr is the metadata for a single file or directory.
+type Attr struct {
+	Inode  uint64      // inode number
+	Size   uint64      // size in bytes
+	Blocks uint64      // size in blocks
+	Atime  time.Time   // time of last access
+	Mtime  time.Time   // time of last modification
+	Ctime  time.Time   // time of last inode change
+	Crtime time.Time   // time of creation (OS X only)
+	Mode   os.FileMode // file mode
+	Nlink  uint32      // number of links
+	Uid    uint32      // owner uid
+	Gid    uint32      // group gid
+	Rdev   uint32      // device numbers
+	Flags  uint32      // chflags(2) flags (OS X only)
+}
+
+func unix(t time.Time) (sec uint64, nsec uint32) {
+	nano := t.UnixNano()
+	sec = uint64(nano / 1e9)
+	nsec = uint32(nano % 1e9)
+	return
+}
+
+func (a *Attr) attr() (out attr) {
+	out.Ino = a.Inode
+	out.Size = a.Size
+	out.Blocks = a.Blocks
+	out.Atime, out.AtimeNsec = unix(a.Atime)
+	out.Mtime, out.MtimeNsec = unix(a.Mtime)
+	out.Ctime, out.CtimeNsec = unix(a.Ctime)
+	out.SetCrtime(unix(a.Crtime))
+	out.Mode = uint32(a.Mode) & 0777
+	switch {
+	default:
+		out.Mode |= syscall.S_IFREG
+	case a.Mode&os.ModeDir != 0:
+		out.Mode |= syscall.S_IFDIR
+	case a.Mode&os.ModeDevice != 0:
+		if a.Mode&os.ModeCharDevice != 0 {
+			out.Mode |= syscall.S_IFCHR
+		} else {
+			out.Mode |= syscall.S_IFBLK
+		}
+	case a.Mode&os.ModeNamedPipe != 0:
+		out.Mode |= syscall.S_IFIFO
+	case a.Mode&os.ModeSymlink != 0:
+		out.Mode |= syscall.S_IFLNK
+	case a.Mode&os.ModeSocket != 0:
+		out.Mode |= syscall.S_IFSOCK
+	}
+	if a.Mode&os.ModeSetuid != 0 {
+		out.Mode |= syscall.S_ISUID
+	}
+	if a.Mode&os.ModeSetgid != 0 {
+		out.Mode |= syscall.S_ISGID
+	}
+	out.Nlink = a.Nlink
+	if out.Nlink < 1 {
+		out.Nlink = 1
+	}
+	out.Uid = a.Uid
+	out.Gid = a.Gid
+	out.Rdev = a.Rdev
+	out.SetFlags(a.Flags)
+
+	return
+}
+
+// A GetattrRequest asks for the metadata for the file denoted by r.Node.
+type GetattrRequest struct {
+	Header
+}
+
+func (r *GetattrRequest) String() string {
+	return fmt.Sprintf("Getattr [%s]", &r.Header)
+}
+
+// Respond replies to the request with the given response.
+func (r *GetattrRequest) Respond(resp *GetattrResponse) {
+	out := &attrOut{
+		outHeader:     outHeader{Unique: uint64(r.ID)},
+		AttrValid:     uint64(resp.AttrValid / time.Second),
+		AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+		Attr:          resp.Attr.attr(),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A GetattrResponse is the response to a GetattrRequest.
+type GetattrResponse struct {
+	AttrValid time.Duration // how long Attr can be cached
+	Attr      Attr          // file attributes
+}
+
+func (r *GetattrResponse) String() string {
+	return fmt.Sprintf("Getattr %+v", *r)
+}
+
+// A GetxattrRequest asks for the extended attributes associated with r.Node.
+type GetxattrRequest struct {
+	Header
+	Size     uint32 // maximum size to return
+	Position uint32 // offset within extended attributes
+}
+
+func (r *GetxattrRequest) String() string {
+	return fmt.Sprintf("Getxattr [%s] %d @%d", &r.Header, r.Size, r.Position)
+}
+
+// Respond replies to the request with the given response.
+func (r *GetxattrRequest) Respond(resp *GetxattrResponse) {
+	out := &getxattrOut{
+		outHeader: outHeader{Unique: uint64(r.ID)},
+		Size:      uint32(len(resp.Xattr)),
+	}
+	r.Conn.respondData(&out.outHeader, unsafe.Sizeof(*out), resp.Xattr)
+}
+
+// A GetxattrResponse is the response to a GetxattrRequest.
+type GetxattrResponse struct {
+	Xattr []byte
+}
+
+func (r *GetxattrResponse) String() string {
+	return fmt.Sprintf("Getxattr %x", r.Xattr)
+}
+
+// A ListxattrRequest asks to list the extended attributes associated with r.Node.
+type ListxattrRequest struct {
+	Header
+	Size     uint32 // maximum size to return
+	Position uint32 // offset within attribute list
+}
+
+func (r *ListxattrRequest) String() string {
+	return fmt.Sprintf("Listxattr [%s] %d @%d", &r.Header, r.Size, r.Position)
+}
+
+// Respond replies to the request with the given response.
+func (r *ListxattrRequest) Respond(resp *ListxattrResponse) {
+	out := &getxattrOut{
+		outHeader: outHeader{Unique: uint64(r.ID)},
+		Size:      uint32(len(resp.Xattr)),
+	}
+	r.Conn.respondData(&out.outHeader, unsafe.Sizeof(*out), resp.Xattr)
+}
+
+// A ListxattrResponse is the response to a ListxattrRequest.
+type ListxattrResponse struct {
+	Xattr []byte
+}
+
+func (r *ListxattrResponse) String() string {
+	return fmt.Sprintf("Listxattr %x", r.Xattr)
+}
+
+// A RemovexattrRequest asks to remove an extended attribute associated with r.Node.
+type RemovexattrRequest struct {
+	Header
+	Name string // name of extended attribute
+}
+
+func (r *RemovexattrRequest) String() string {
+	return fmt.Sprintf("Removexattr [%s] %q", &r.Header, r.Name)
+}
+
+// Respond replies to the request, indicating that the attribute was removed.
+func (r *RemovexattrRequest) Respond() {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A SetxattrRequest asks to set an extended attribute associated with a file.
+type SetxattrRequest struct {
+	Header
+	Flags    uint32
+	Position uint32 // OS X only
+	Name     string
+	Xattr    []byte
+}
+
+func (r *SetxattrRequest) String() string {
+	return fmt.Sprintf("Setxattr [%s] %q %x fl=%v @%#x", &r.Header, r.Name, r.Xattr, r.Flags, r.Position)
+}
+
+// Respond replies to the request, indicating that the extended attribute was set.
+func (r *SetxattrRequest) Respond() {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A LookupRequest asks to look up the given name in the directory named by r.Node.
+type LookupRequest struct {
+	Header
+	Name string
+}
+
+func (r *LookupRequest) String() string {
+	return fmt.Sprintf("Lookup [%s] %q", &r.Header, r.Name)
+}
+
+// Respond replies to the request with the given response.
+func (r *LookupRequest) Respond(resp *LookupResponse) {
+	out := &entryOut{
+		outHeader:      outHeader{Unique: uint64(r.ID)},
+		Nodeid:         uint64(resp.Node),
+		Generation:     resp.Generation,
+		EntryValid:     uint64(resp.EntryValid / time.Second),
+		EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+		AttrValid:      uint64(resp.AttrValid / time.Second),
+		AttrValidNsec:  uint32(resp.AttrValid % time.Second / time.Nanosecond),
+		Attr:           resp.Attr.attr(),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A LookupResponse is the response to a LookupRequest.
+type LookupResponse struct {
+	Node       NodeID
+	Generation uint64
+	EntryValid time.Duration
+	AttrValid  time.Duration
+	Attr       Attr
+}
+
+func (r *LookupResponse) String() string {
+	return fmt.Sprintf("Lookup %+v", *r)
+}
+
+// An OpenRequest asks to open a file or directory
+type OpenRequest struct {
+	Header
+	Dir   bool // is this Opendir?
+	Flags uint32
+	Mode  os.FileMode
+}
+
+func (r *OpenRequest) String() string {
+	return fmt.Sprintf("Open [%s] dir=%v fl=%v mode=%v", &r.Header, r.Dir, r.Flags, r.Mode)
+}
+
+// Respond replies to the request with the given response.
+func (r *OpenRequest) Respond(resp *OpenResponse) {
+	out := &openOut{
+		outHeader: outHeader{Unique: uint64(r.ID)},
+		Fh:        uint64(resp.Handle),
+		OpenFlags: uint32(resp.Flags),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A OpenResponse is the response to a OpenRequest.
+type OpenResponse struct {
+	Handle HandleID
+	Flags  OpenFlags
+}
+
+func (r *OpenResponse) String() string {
+	return fmt.Sprintf("Open %+v", *r)
+}
+
+// A CreateRequest asks to create and open a file (not a directory).
+type CreateRequest struct {
+	Header
+	Name  string
+	Flags uint32
+	Mode  os.FileMode
+}
+
+func (r *CreateRequest) String() string {
+	return fmt.Sprintf("Create [%s] %q fl=%v mode=%v", &r.Header, r.Name, r.Flags, r.Mode)
+}
+
+// Respond replies to the request with the given response.
+func (r *CreateRequest) Respond(resp *CreateResponse) {
+	out := &createOut{
+		outHeader: outHeader{Unique: uint64(r.ID)},
+
+		Nodeid:         uint64(resp.Node),
+		Generation:     resp.Generation,
+		EntryValid:     uint64(resp.EntryValid / time.Second),
+		EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+		AttrValid:      uint64(resp.AttrValid / time.Second),
+		AttrValidNsec:  uint32(resp.AttrValid % time.Second / time.Nanosecond),
+		Attr:           resp.Attr.attr(),
+
+		Fh:        uint64(resp.Handle),
+		OpenFlags: uint32(resp.Flags),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A CreateResponse is the response to a CreateRequest.
+// It describes the created node and opened handle.
+type CreateResponse struct {
+	LookupResponse
+	OpenResponse
+}
+
+func (r *CreateResponse) String() string {
+	return fmt.Sprintf("Create %+v", *r)
+}
+
+// A MkdirRequest asks to create (but not open) a directory.
+type MkdirRequest struct {
+	Header
+	Name string
+	Mode os.FileMode
+}
+
+func (r *MkdirRequest) String() string {
+	return fmt.Sprintf("Mkdir [%s] %q mode=%v", &r.Header, r.Name, r.Mode)
+}
+
+// Respond replies to the request with the given response.
+func (r *MkdirRequest) Respond(resp *MkdirResponse) {
+	out := &entryOut{
+		outHeader:      outHeader{Unique: uint64(r.ID)},
+		Nodeid:         uint64(resp.Node),
+		Generation:     resp.Generation,
+		EntryValid:     uint64(resp.EntryValid / time.Second),
+		EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+		AttrValid:      uint64(resp.AttrValid / time.Second),
+		AttrValidNsec:  uint32(resp.AttrValid % time.Second / time.Nanosecond),
+		Attr:           resp.Attr.attr(),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A MkdirResponse is the response to a MkdirRequest.
+type MkdirResponse struct {
+	LookupResponse
+}
+
+func (r *MkdirResponse) String() string {
+	return fmt.Sprintf("Mkdir %+v", *r)
+}
+
+// A ReadRequest asks to read from an open file.
+type ReadRequest struct {
+	Header
+	Dir    bool // is this Readdir?
+	Handle HandleID
+	Offset int64
+	Size   int
+}
+
+func (r *ReadRequest) handle() HandleID {
+	return r.Handle
+}
+
+func (r *ReadRequest) String() string {
+	return fmt.Sprintf("Read [%s] %#x %d @%#x dir=%v", &r.Header, r.Handle, r.Size, r.Offset, r.Dir)
+}
+
+// Respond replies to the request with the given response.
+func (r *ReadRequest) Respond(resp *ReadResponse) {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respondData(out, unsafe.Sizeof(*out), resp.Data)
+}
+
+// A ReadResponse is the response to a ReadRequest.
+type ReadResponse struct {
+	Data []byte
+}
+
+func (r *ReadResponse) String() string {
+	return fmt.Sprintf("Read %x", r.Data)
+}
+
+// A ReleaseRequest asks to release (close) an open file handle.
+type ReleaseRequest struct {
+	Header
+	Dir          bool // is this Releasedir?
+	Handle       HandleID
+	Flags        uint32 // flags from OpenRequest
+	ReleaseFlags ReleaseFlags
+	LockOwner    uint32
+}
+
+func (r *ReleaseRequest) handle() HandleID {
+	return r.Handle
+}
+
+func (r *ReleaseRequest) String() string {
+	return fmt.Sprintf("Release [%s] %#x fl=%v rfl=%v owner=%#x", &r.Header, r.Handle, r.Flags, r.ReleaseFlags, r.LockOwner)
+}
+
+// Respond replies to the request, indicating that the handle has been released.
+func (r *ReleaseRequest) Respond() {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A DestroyRequest is sent by the kernel when unmounting the file system.
+// No more requests will be received after this one, but it should still be
+// responded to.
+type DestroyRequest struct {
+	Header
+}
+
+func (r *DestroyRequest) String() string {
+	return fmt.Sprintf("Destroy [%s]", &r.Header)
+}
+
+// Respond replies to the request.
+func (r *DestroyRequest) Respond() {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A ForgetRequest is sent by the kernel when forgetting about r.Node
+// as returned by r.N lookup requests.
+type ForgetRequest struct {
+	Header
+	N uint64
+}
+
+func (r *ForgetRequest) String() string {
+	return fmt.Sprintf("Forget [%s] %d", &r.Header, r.N)
+}
+
+// Respond replies to the request, indicating that the forgetfulness has been recorded.
+func (r *ForgetRequest) Respond() {
+	// Don't reply to forget messages.
+}
+
+// A Dirent represents a single directory entry.
+type Dirent struct {
+	Inode uint64 // inode this entry names
+	Type  uint32 // ?
+	Name  string // name of entry
+}
+
+// AppendDirent appends the encoded form of a directory entry to data
+// and returns the resulting slice.
+func AppendDirent(data []byte, dir Dirent) []byte {
+	de := dirent{
+		Ino:     dir.Inode,
+		Namelen: uint32(len(dir.Name)),
+		Type:    dir.Type,
+	}
+	de.Off = uint64(len(data) + direntSize + (len(dir.Name)+7)&^7)
+	data = append(data, (*[direntSize]byte)(unsafe.Pointer(&de))[:]...)
+	data = append(data, dir.Name...)
+	n := direntSize + uintptr(len(dir.Name))
+	if n%8 != 0 {
+		var pad [8]byte
+		data = append(data, pad[:8-n%8]...)
+	}
+	return data
+}
+
+// A WriteRequest asks to write to an open file.
+type WriteRequest struct {
+	Header
+	Handle HandleID
+	Offset int64
+	Data   []byte
+	Flags  WriteFlags
+}
+
+func (r *WriteRequest) String() string {
+	return fmt.Sprintf("Write [%s] %#x %d @%d fl=%v", &r.Header, r.Handle, len(r.Data), r.Offset, r.Flags)
+}
+
+// Respond replies to the request with the given response.
+func (r *WriteRequest) Respond(resp *WriteResponse) {
+	out := &writeOut{
+		outHeader: outHeader{Unique: uint64(r.ID)},
+		Size:      uint32(resp.Size),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+func (r *WriteRequest) handle() HandleID {
+	return r.Handle
+}
+
+// A WriteResponse replies to a write indicating how many bytes were written.
+type WriteResponse struct {
+	Size int
+}
+
+func (r *WriteResponse) String() string {
+	return fmt.Sprintf("Write %+v", *r)
+}
+
+// A SetattrRequest asks to change one or more attributes associated with a file,
+// as indicated by Valid.
+type SetattrRequest struct {
+	Header
+	Valid  SetattrValid
+	Handle HandleID
+	Size   uint64
+	Atime  time.Time
+	Mtime  time.Time
+	Mode   os.FileMode
+	Uid    uint32
+	Gid    uint32
+
+	// OS X only
+	Bkuptime time.Time
+	Chgtime  time.Time
+	Crtime   time.Time
+	Flags    uint32 // see chflags(2)
+}
+
+func (r *SetattrRequest) String() string {
+	var buf bytes.Buffer
+	fmt.Fprintf(&buf, "Setattr [%s]", &r.Header)
+	if r.Valid.Mode() {
+		fmt.Fprintf(&buf, " mode=%v", r.Mode)
+	}
+	if r.Valid.Uid() {
+		fmt.Fprintf(&buf, " uid=%d", r.Uid)
+	}
+	if r.Valid.Gid() {
+		fmt.Fprintf(&buf, " gid=%d", r.Gid)
+	}
+	if r.Valid.Size() {
+		fmt.Fprintf(&buf, " size=%d", r.Size)
+	}
+	if r.Valid.Atime() {
+		fmt.Fprintf(&buf, " atime=%v", r.Atime)
+	}
+	if r.Valid.Mtime() {
+		fmt.Fprintf(&buf, " mtime=%v", r.Mtime)
+	}
+	if r.Valid.Handle() {
+		fmt.Fprintf(&buf, " handle=%#x", r.Handle)
+	} else {
+		fmt.Fprintf(&buf, " handle=INVALID-%#x", r.Handle)
+	}
+	if r.Valid.Crtime() {
+		fmt.Fprintf(&buf, " crtime=%v", r.Crtime)
+	}
+	if r.Valid.Chgtime() {
+		fmt.Fprintf(&buf, " chgtime=%v", r.Chgtime)
+	}
+	if r.Valid.Bkuptime() {
+		fmt.Fprintf(&buf, " bkuptime=%v", r.Bkuptime)
+	}
+	if r.Valid.Flags() {
+		fmt.Fprintf(&buf, " flags=%#x", r.Flags)
+	}
+	return buf.String()
+}
+
+func (r *SetattrRequest) handle() HandleID {
+	if r.Valid.Handle() {
+		return r.Handle
+	}
+	return 0
+}
+
+// Respond replies to the request with the given response,
+// giving the updated attributes.
+func (r *SetattrRequest) Respond(resp *SetattrResponse) {
+	out := &attrOut{
+		outHeader:     outHeader{Unique: uint64(r.ID)},
+		AttrValid:     uint64(resp.AttrValid / time.Second),
+		AttrValidNsec: uint32(resp.AttrValid % time.Second / time.Nanosecond),
+		Attr:          resp.Attr.attr(),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A SetattrResponse is the response to a SetattrRequest.
+type SetattrResponse struct {
+	AttrValid time.Duration // how long Attr can be cached
+	Attr      Attr          // file attributes
+}
+
+func (r *SetattrResponse) String() string {
+	return fmt.Sprintf("Setattr %+v", *r)
+}
+
+// A FlushRequest asks for the current state of an open file to be flushed
+// to storage, as when a file descriptor is being closed.  A single opened Handle
+// may receive multiple FlushRequests over its lifetime.
+type FlushRequest struct {
+	Header
+	Handle    HandleID
+	Flags     uint32
+	LockOwner uint64
+}
+
+func (r *FlushRequest) String() string {
+	return fmt.Sprintf("Flush [%s] %#x fl=%#x lk=%#x", &r.Header, r.Handle, r.Flags, r.LockOwner)
+}
+
+func (r *FlushRequest) handle() HandleID {
+	return r.Handle
+}
+
+// Respond replies to the request, indicating that the flush succeeded.
+func (r *FlushRequest) Respond() {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A RemoveRequest asks to remove a file or directory.
+type RemoveRequest struct {
+	Header
+	Name string // name of extended attribute
+	Dir  bool   // is this rmdir?
+}
+
+func (r *RemoveRequest) String() string {
+	return fmt.Sprintf("Remove [%s] %q dir=%v", &r.Header, r.Name, r.Dir)
+}
+
+// Respond replies to the request, indicating that the file was removed.
+func (r *RemoveRequest) Respond() {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+// A SymlinkRequest is a request to create a symlink making NewName point to Target.
+type SymlinkRequest struct {
+	Header
+	NewName, Target string
+}
+
+func (r *SymlinkRequest) String() string {
+	return fmt.Sprintf("Symlink [%s] from %q to target %q", &r.Header, r.NewName, r.Target)
+}
+
+func (r *SymlinkRequest) handle() HandleID {
+	return 0
+}
+
+// Respond replies to the request, indicating that the symlink was created.
+func (r *SymlinkRequest) Respond(resp *SymlinkResponse) {
+	out := &entryOut{
+		outHeader:      outHeader{Unique: uint64(r.ID)},
+		Nodeid:         uint64(resp.Node),
+		Generation:     resp.Generation,
+		EntryValid:     uint64(resp.EntryValid / time.Second),
+		EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+		AttrValid:      uint64(resp.AttrValid / time.Second),
+		AttrValidNsec:  uint32(resp.AttrValid % time.Second / time.Nanosecond),
+		Attr:           resp.Attr.attr(),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A SymlinkResponse is the response to a SymlinkRequest.
+type SymlinkResponse struct {
+	LookupResponse
+}
+
+// A ReadlinkRequest is a request to read a symlink's target.
+type ReadlinkRequest struct {
+	Header
+}
+
+func (r *ReadlinkRequest) String() string {
+	return fmt.Sprintf("Readlink [%s]", &r.Header)
+}
+
+func (r *ReadlinkRequest) handle() HandleID {
+	return 0
+}
+
+func (r *ReadlinkRequest) Respond(target string) {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respondData(out, unsafe.Sizeof(*out), []byte(target))
+}
+
+// A LinkRequest is a request to create a hard link.
+type LinkRequest struct {
+	Header
+	OldNode NodeID
+	NewName string
+}
+
+func (r *LinkRequest) Respond(resp *LookupResponse) {
+	out := &entryOut{
+		outHeader:      outHeader{Unique: uint64(r.ID)},
+		Nodeid:         uint64(resp.Node),
+		Generation:     resp.Generation,
+		EntryValid:     uint64(resp.EntryValid / time.Second),
+		EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+		AttrValid:      uint64(resp.AttrValid / time.Second),
+		AttrValidNsec:  uint32(resp.AttrValid % time.Second / time.Nanosecond),
+		Attr:           resp.Attr.attr(),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A RenameRequest is a request to rename a file.
+type RenameRequest struct {
+	Header
+	NewDir           NodeID
+	OldName, NewName string
+}
+
+func (r *RenameRequest) handle() HandleID {
+	return 0
+}
+
+func (r *RenameRequest) String() string {
+	return fmt.Sprintf("Rename [%s] from %q to dirnode %d %q", &r.Header, r.OldName, r.NewDir, r.NewName)
+}
+
+func (r *RenameRequest) Respond() {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+type MknodRequest struct {
+	Header
+	Name string
+	Mode os.FileMode
+	Rdev uint32
+}
+
+func (r *MknodRequest) String() string {
+	return fmt.Sprintf("Mknod [%s] Name %q mode %v rdev %d", &r.Header, r.Name, r.Mode, r.Rdev)
+}
+
+func (r *MknodRequest) Respond(resp *LookupResponse) {
+	out := &entryOut{
+		outHeader:      outHeader{Unique: uint64(r.ID)},
+		Nodeid:         uint64(resp.Node),
+		Generation:     resp.Generation,
+		EntryValid:     uint64(resp.EntryValid / time.Second),
+		EntryValidNsec: uint32(resp.EntryValid % time.Second / time.Nanosecond),
+		AttrValid:      uint64(resp.AttrValid / time.Second),
+		AttrValidNsec:  uint32(resp.AttrValid % time.Second / time.Nanosecond),
+		Attr:           resp.Attr.attr(),
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+type FsyncRequest struct {
+	Header
+	Handle HandleID
+	Flags  uint32
+}
+
+func (r *FsyncRequest) String() string {
+	return fmt.Sprintf("Fsync [%s] Handle %v Flags %v", &r.Header, r.Handle, r.Flags)
+}
+
+func (r *FsyncRequest) Respond() {
+	out := &outHeader{Unique: uint64(r.ID)}
+	r.Conn.respond(out, unsafe.Sizeof(*out))
+}
+
+/*{
+
+// A XXXRequest xxx.
+type XXXRequest struct {
+	Header
+	xxx
+}
+
+func (r *XXXRequest) String() string {
+	return fmt.Sprintf("XXX [%s] xxx", &r.Header)
+}
+
+func (r *XXXRequest) handle() HandleID {
+	return r.Handle
+}
+
+// Respond replies to the request with the given response.
+func (r *XXXRequest) Respond(resp *XXXResponse) {
+	out := &xxxOut{
+		outHeader: outHeader{Unique: uint64(r.ID)},
+		xxx,
+	}
+	r.Conn.respond(&out.outHeader, unsafe.Sizeof(*out))
+}
+
+// A XXXResponse is the response to a XXXRequest.
+type XXXResponse struct {
+	xxx
+}
+
+func (r *XXXResponse) String() string {
+	return fmt.Sprintf("XXX %+v", *r)
+}
+
+ }
+*/
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_kernel.go b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel.go
new file mode 100644
index 000000000..a57360e63
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel.go
@@ -0,0 +1,539 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Derived from FUSE's fuse_kernel.h
+/*
+   This file defines the kernel interface of FUSE
+   Copyright (C) 2001-2007  Miklos Szeredi 
+
+
+   This -- and only this -- header file may also be distributed under
+   the terms of the BSD Licence as follows:
+
+   Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+   SUCH DAMAGE.
+*/
+
+package fuse
+
+import (
+	"fmt"
+	"unsafe"
+)
+
+// Version is the FUSE version implemented by the package.
+const Version = "7.8"
+
+const (
+	kernelVersion      = 7
+	kernelMinorVersion = 8
+	rootID             = 1
+)
+
+type kstatfs struct {
+	Blocks  uint64
+	Bfree   uint64
+	Bavail  uint64
+	Files   uint64
+	Ffree   uint64
+	Bsize   uint32
+	Namelen uint32
+	Frsize  uint32
+	Padding uint32
+	Spare   [6]uint32
+}
+
+type fileLock struct {
+	Start uint64
+	End   uint64
+	Type  uint32
+	Pid   uint32
+}
+
+// The SetattrValid are bit flags describing which fields in the SetattrRequest
+// are included in the change.
+type SetattrValid uint32
+
+const (
+	SetattrMode   SetattrValid = 1 << 0
+	SetattrUid    SetattrValid = 1 << 1
+	SetattrGid    SetattrValid = 1 << 2
+	SetattrSize   SetattrValid = 1 << 3
+	SetattrAtime  SetattrValid = 1 << 4
+	SetattrMtime  SetattrValid = 1 << 5
+	SetattrHandle SetattrValid = 1 << 6 // TODO: What does this mean?
+
+	// Linux only(?)
+	SetattrAtimeNow  SetattrValid = 1 << 7
+	SetattrMtimeNow  SetattrValid = 1 << 8
+	SetattrLockOwner SetattrValid = 1 << 9 // http://www.mail-archive.com/git-commits-head@vger.kernel.org/msg27852.html
+
+	// OS X only
+	SetattrCrtime   SetattrValid = 1 << 28
+	SetattrChgtime  SetattrValid = 1 << 29
+	SetattrBkuptime SetattrValid = 1 << 30
+	SetattrFlags    SetattrValid = 1 << 31
+)
+
+func (fl SetattrValid) Mode() bool     { return fl&SetattrMode != 0 }
+func (fl SetattrValid) Uid() bool      { return fl&SetattrUid != 0 }
+func (fl SetattrValid) Gid() bool      { return fl&SetattrGid != 0 }
+func (fl SetattrValid) Size() bool     { return fl&SetattrSize != 0 }
+func (fl SetattrValid) Atime() bool    { return fl&SetattrAtime != 0 }
+func (fl SetattrValid) Mtime() bool    { return fl&SetattrMtime != 0 }
+func (fl SetattrValid) Handle() bool   { return fl&SetattrHandle != 0 }
+func (fl SetattrValid) Crtime() bool   { return fl&SetattrCrtime != 0 }
+func (fl SetattrValid) Chgtime() bool  { return fl&SetattrChgtime != 0 }
+func (fl SetattrValid) Bkuptime() bool { return fl&SetattrBkuptime != 0 }
+func (fl SetattrValid) Flags() bool    { return fl&SetattrFlags != 0 }
+
+func (fl SetattrValid) String() string {
+	return flagString(uint32(fl), setattrValidNames)
+}
+
+var setattrValidNames = []flagName{
+	{uint32(SetattrMode), "SetattrMode"},
+	{uint32(SetattrUid), "SetattrUid"},
+	{uint32(SetattrGid), "SetattrGid"},
+	{uint32(SetattrSize), "SetattrSize"},
+	{uint32(SetattrAtime), "SetattrAtime"},
+	{uint32(SetattrMtime), "SetattrMtime"},
+	{uint32(SetattrHandle), "SetattrHandle"},
+	{uint32(SetattrCrtime), "SetattrCrtime"},
+	{uint32(SetattrChgtime), "SetattrChgtime"},
+	{uint32(SetattrBkuptime), "SetattrBkuptime"},
+	{uint32(SetattrFlags), "SetattrFlags"},
+}
+
+// The OpenFlags are returned in the OpenResponse.
+type OpenFlags uint32
+
+const (
+	OpenDirectIO    OpenFlags = 1 << 0 // bypass page cache for this open file
+	OpenKeepCache   OpenFlags = 1 << 1 // don't invalidate the data cache on open
+	OpenNonSeekable OpenFlags = 1 << 2 // (Linux?)
+
+	OpenPurgeAttr OpenFlags = 1 << 30 // OS X
+	OpenPurgeUBC  OpenFlags = 1 << 31 // OS X
+)
+
+func (fl OpenFlags) String() string {
+	return flagString(uint32(fl), openFlagNames)
+}
+
+var openFlagNames = []flagName{
+	{uint32(OpenDirectIO), "OpenDirectIO"},
+	{uint32(OpenKeepCache), "OpenKeepCache"},
+	{uint32(OpenPurgeAttr), "OpenPurgeAttr"},
+	{uint32(OpenPurgeUBC), "OpenPurgeUBC"},
+}
+
+// The InitFlags are used in the Init exchange.
+type InitFlags uint32
+
+const (
+	InitAsyncRead  InitFlags = 1 << 0
+	InitPosixLocks InitFlags = 1 << 1
+
+	InitCaseSensitive InitFlags = 1 << 29 // OS X only
+	InitVolRename     InitFlags = 1 << 30 // OS X only
+	InitXtimes        InitFlags = 1 << 31 // OS X only
+)
+
+type flagName struct {
+	bit  uint32
+	name string
+}
+
+var initFlagNames = []flagName{
+	{uint32(InitAsyncRead), "InitAsyncRead"},
+	{uint32(InitPosixLocks), "InitPosixLocks"},
+	{uint32(InitCaseSensitive), "InitCaseSensitive"},
+	{uint32(InitVolRename), "InitVolRename"},
+	{uint32(InitXtimes), "InitXtimes"},
+}
+
+func (fl InitFlags) String() string {
+	return flagString(uint32(fl), initFlagNames)
+}
+
+func flagString(f uint32, names []flagName) string {
+	var s string
+
+	if f == 0 {
+		return "0"
+	}
+
+	for _, n := range names {
+		if f&n.bit != 0 {
+			s += "+" + n.name
+			f &^= n.bit
+		}
+	}
+	if f != 0 {
+		s += fmt.Sprintf("%+#x", f)
+	}
+	return s[1:]
+}
+
+// The ReleaseFlags are used in the Release exchange.
+type ReleaseFlags uint32
+
+const (
+	ReleaseFlush ReleaseFlags = 1 << 0
+)
+
+func (fl ReleaseFlags) String() string {
+	return flagString(uint32(fl), releaseFlagNames)
+}
+
+var releaseFlagNames = []flagName{
+	{uint32(ReleaseFlush), "ReleaseFlush"},
+}
+
+// Opcodes
+const (
+	opLookup      = 1
+	opForget      = 2 // no reply
+	opGetattr     = 3
+	opSetattr     = 4
+	opReadlink    = 5
+	opSymlink     = 6
+	opMknod       = 8
+	opMkdir       = 9
+	opUnlink      = 10
+	opRmdir       = 11
+	opRename      = 12
+	opLink        = 13
+	opOpen        = 14
+	opRead        = 15
+	opWrite       = 16
+	opStatfs      = 17
+	opRelease     = 18
+	opFsync       = 20
+	opSetxattr    = 21
+	opGetxattr    = 22
+	opListxattr   = 23
+	opRemovexattr = 24
+	opFlush       = 25
+	opInit        = 26
+	opOpendir     = 27
+	opReaddir     = 28
+	opReleasedir  = 29
+	opFsyncdir    = 30
+	opGetlk       = 31
+	opSetlk       = 32
+	opSetlkw      = 33
+	opAccess      = 34
+	opCreate      = 35
+	opInterrupt   = 36
+	opBmap        = 37
+	opDestroy     = 38
+	opIoctl       = 39 // Linux?
+	opPoll        = 40 // Linux?
+
+	// OS X
+	opSetvolname = 61
+	opGetxtimes  = 62
+	opExchange   = 63
+)
+
+// The read buffer is required to be at least 8k but may be much larger
+const minReadBuffer = 8192
+
+type entryOut struct {
+	outHeader
+	Nodeid         uint64 // Inode ID
+	Generation     uint64 // Inode generation
+	EntryValid     uint64 // Cache timeout for the name
+	AttrValid      uint64 // Cache timeout for the attributes
+	EntryValidNsec uint32
+	AttrValidNsec  uint32
+	Attr           attr
+}
+
+type forgetIn struct {
+	Nlookup uint64
+}
+
+type attrOut struct {
+	outHeader
+	AttrValid     uint64 // Cache timeout for the attributes
+	AttrValidNsec uint32
+	Dummy         uint32
+	Attr          attr
+}
+
+// OS X
+type getxtimesOut struct {
+	outHeader
+	Bkuptime     uint64
+	Crtime       uint64
+	BkuptimeNsec uint32
+	CrtimeNsec   uint32
+}
+
+type mknodIn struct {
+	Mode uint32
+	Rdev uint32
+	// "filename\x00" follows.
+}
+
+type mkdirIn struct {
+	Mode    uint32
+	Padding uint32
+	// filename follows
+}
+
+type renameIn struct {
+	Newdir uint64
+	// "oldname\x00newname\x00" follows
+}
+
+// OS X 
+type exchangeIn struct {
+	Olddir  uint64
+	Newdir  uint64
+	Options uint64
+}
+
+type linkIn struct {
+	Oldnodeid uint64
+}
+
+type setattrInCommon struct {
+	Valid     uint32
+	Padding   uint32
+	Fh        uint64
+	Size      uint64
+	LockOwner uint64 // unused on OS X?
+	Atime     uint64
+	Mtime     uint64
+	Unused2   uint64
+	AtimeNsec uint32
+	MtimeNsec uint32
+	Unused3   uint32
+	Mode      uint32
+	Unused4   uint32
+	Uid       uint32
+	Gid       uint32
+	Unused5   uint32
+}
+
+type openIn struct {
+	Flags uint32
+	Mode  uint32
+}
+
+type openOut struct {
+	outHeader
+	Fh        uint64
+	OpenFlags uint32
+	Padding   uint32
+}
+
+type createOut struct {
+	outHeader
+
+	Nodeid         uint64 // Inode ID
+	Generation     uint64 // Inode generation
+	EntryValid     uint64 // Cache timeout for the name
+	AttrValid      uint64 // Cache timeout for the attributes
+	EntryValidNsec uint32
+	AttrValidNsec  uint32
+	Attr           attr
+
+	Fh        uint64
+	OpenFlags uint32
+	Padding   uint32
+}
+
+type releaseIn struct {
+	Fh           uint64
+	Flags        uint32
+	ReleaseFlags uint32
+	LockOwner    uint32
+}
+
+type flushIn struct {
+	Fh         uint64
+	FlushFlags uint32
+	Padding    uint32
+	LockOwner  uint64
+}
+
+type readIn struct {
+	Fh      uint64
+	Offset  uint64
+	Size    uint32
+	Padding uint32
+}
+
+type writeIn struct {
+	Fh         uint64
+	Offset     uint64
+	Size       uint32
+	WriteFlags uint32
+}
+
+type writeOut struct {
+	outHeader
+	Size    uint32
+	Padding uint32
+}
+
+// The WriteFlags are returned in the WriteResponse.
+type WriteFlags uint32
+
+func (fl WriteFlags) String() string {
+	return flagString(uint32(fl), writeFlagNames)
+}
+
+var writeFlagNames = []flagName{}
+
+const compatStatfsSize = 48
+
+type statfsOut struct {
+	outHeader
+	St kstatfs
+}
+
+type fsyncIn struct {
+	Fh         uint64
+	FsyncFlags uint32
+	Padding    uint32
+}
+
+type setxattrIn struct {
+	Size  uint32
+	Flags uint32
+}
+
+type setxattrInOSX struct {
+	Size  uint32
+	Flags uint32
+
+	// OS X only
+	Position uint32
+	Padding  uint32
+}
+
+type getxattrIn struct {
+	Size    uint32
+	Padding uint32
+}
+
+type getxattrInOSX struct {
+	Size    uint32
+	Padding uint32
+
+	// OS X only
+	Position uint32
+	Padding2 uint32
+}
+
+type getxattrOut struct {
+	outHeader
+	Size    uint32
+	Padding uint32
+}
+
+type lkIn struct {
+	Fh    uint64
+	Owner uint64
+	Lk    fileLock
+}
+
+type lkOut struct {
+	outHeader
+	Lk fileLock
+}
+
+type accessIn struct {
+	Mask    uint32
+	Padding uint32
+}
+
+type initIn struct {
+	Major        uint32
+	Minor        uint32
+	MaxReadahead uint32
+	Flags        uint32
+}
+
+const initInSize = int(unsafe.Sizeof(initIn{}))
+
+type initOut struct {
+	outHeader
+	Major        uint32
+	Minor        uint32
+	MaxReadahead uint32
+	Flags        uint32
+	Unused       uint32
+	MaxWrite     uint32
+}
+
+type interruptIn struct {
+	Unique uint64
+}
+
+type bmapIn struct {
+	Block     uint64
+	BlockSize uint32
+	Padding   uint32
+}
+
+type bmapOut struct {
+	outHeader
+	Block uint64
+}
+
+type inHeader struct {
+	Len     uint32
+	Opcode  uint32
+	Unique  uint64
+	Nodeid  uint64
+	Uid     uint32
+	Gid     uint32
+	Pid     uint32
+	Padding uint32
+}
+
+const inHeaderSize = int(unsafe.Sizeof(inHeader{}))
+
+type outHeader struct {
+	Len    uint32
+	Error  int32
+	Unique uint64
+}
+
+type dirent struct {
+	Ino     uint64
+	Off     uint64
+	Namelen uint32
+	Type    uint32
+	Name    [0]byte
+}
+
+const direntSize = 8 + 8 + 4 + 4
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_darwin.go b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_darwin.go
new file mode 100644
index 000000000..f7bc37766
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_darwin.go
@@ -0,0 +1,58 @@
+package fuse
+
+import (
+	"time"
+)
+
+type attr struct {
+	Ino        uint64
+	Size       uint64
+	Blocks     uint64
+	Atime      uint64
+	Mtime      uint64
+	Ctime      uint64
+	Crtime_    uint64 // OS X only
+	AtimeNsec  uint32
+	MtimeNsec  uint32
+	CtimeNsec  uint32
+	CrtimeNsec uint32 // OS X only
+	Mode       uint32
+	Nlink      uint32
+	Uid        uint32
+	Gid        uint32
+	Rdev       uint32
+	Flags_     uint32 // OS X only; see chflags(2)
+}
+
+func (a *attr) SetCrtime(s uint64, ns uint32) {
+	a.Crtime_, a.CrtimeNsec = s, ns
+}
+
+func (a *attr) SetFlags(f uint32) {
+	a.Flags_ = f
+}
+
+type setattrIn struct {
+	setattrInCommon
+
+	// OS X only
+	Bkuptime_    uint64
+	Chgtime_     uint64
+	Crtime       uint64
+	BkuptimeNsec uint32
+	ChgtimeNsec  uint32
+	CrtimeNsec   uint32
+	Flags_       uint32 // see chflags(2)
+}
+
+func (in *setattrIn) BkupTime() time.Time {
+	return time.Unix(int64(in.Bkuptime_), int64(in.BkuptimeNsec))
+}
+
+func (in *setattrIn) Chgtime() time.Time {
+	return time.Unix(int64(in.Chgtime_), int64(in.ChgtimeNsec))
+}
+
+func (in *setattrIn) Flags() uint32 {
+	return in.Flags_
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_linux.go b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_linux.go
new file mode 100644
index 000000000..914bc3063
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_linux.go
@@ -0,0 +1,50 @@
+package fuse
+
+import "time"
+
+type attr struct {
+	Ino       uint64
+	Size      uint64
+	Blocks    uint64
+	Atime     uint64
+	Mtime     uint64
+	Ctime     uint64
+	AtimeNsec uint32
+	MtimeNsec uint32
+	CtimeNsec uint32
+	Mode      uint32
+	Nlink     uint32
+	Uid       uint32
+	Gid       uint32
+	Rdev      uint32
+	//	Blksize   uint32  // Only in protocol 7.9
+	//	padding_  uint32  // Only in protocol 7.9
+}
+
+func (a *attr) Crtime() time.Time {
+	return time.Time{}
+}
+
+func (a *attr) SetCrtime(s uint64, ns uint32) {
+	// Ignored on Linux.
+}
+
+func (a *attr) SetFlags(f uint32) {
+	// Ignored on Linux.
+}
+
+type setattrIn struct {
+	setattrInCommon
+}
+
+func (in *setattrIn) BkupTime() time.Time {
+	return time.Time{}
+}
+
+func (in *setattrIn) Chgtime() time.Time {
+	return time.Time{}
+}
+
+func (in *setattrIn) Flags() uint32 {
+	return 0
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_std.go b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_std.go
new file mode 100644
index 000000000..074cfd322
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_kernel_std.go
@@ -0,0 +1 @@
+package fuse
diff --git a/vendor/github.com/mattermost/rsc/fuse/fuse_test.go b/vendor/github.com/mattermost/rsc/fuse/fuse_test.go
new file mode 100644
index 000000000..61533b8c5
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/fuse_test.go
@@ -0,0 +1,594 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuse
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"os/exec"
+	"runtime"
+	"syscall"
+	"testing"
+	"time"
+)
+
+var fuseRun = flag.String("fuserun", "", "which fuse test to run. runs all if empty.")
+
+// umount tries its best to unmount dir.
+func umount(dir string) {
+	err := exec.Command("umount", dir).Run()
+	if err != nil && runtime.GOOS == "linux" {
+		exec.Command("/bin/fusermount", "-u", dir).Run()
+	}
+}
+
+func TestFuse(t *testing.T) {
+	Debugf = log.Printf
+	dir, err := ioutil.TempDir("", "fusetest")
+	if err != nil {
+		t.Fatal(err)
+	}
+	os.MkdirAll(dir, 0777)
+
+	c, err := Mount(dir)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer umount(dir)
+
+	go func() {
+		err := c.Serve(testFS{})
+		if err != nil {
+			fmt.Printf("SERVE ERROR: %v\n", err)
+		}
+	}()
+
+	waitForMount(t, dir)
+
+	for _, tt := range fuseTests {
+		if *fuseRun == "" || *fuseRun == tt.name {
+			t.Logf("running %T", tt.node)
+			tt.node.test(dir+"/"+tt.name, t)
+		}
+	}
+}
+
+func waitForMount(t *testing.T, dir string) {
+	// Filename to wait for in dir:
+	probeEntry := *fuseRun
+	if probeEntry == "" {
+		probeEntry = fuseTests[0].name
+	}
+	for tries := 0; tries < 100; tries++ {
+		_, err := os.Stat(dir + "/" + probeEntry)
+		if err == nil {
+			return
+		}
+		time.Sleep(10 * time.Millisecond)
+	}
+	t.Fatalf("mount did not work")
+}
+
+var fuseTests = []struct {
+	name string
+	node interface {
+		Node
+		test(string, *testing.T)
+	}
+}{
+	{"readAll", readAll{}},
+	{"readAll1", &readAll1{}},
+	{"write", &write{}},
+	{"writeAll", &writeAll{}},
+	{"writeAll2", &writeAll2{}},
+	{"release", &release{}},
+	{"mkdir1", &mkdir1{}},
+	{"create1", &create1{}},
+	{"create2", &create2{}},
+	{"symlink1", &symlink1{}},
+	{"link1", &link1{}},
+	{"rename1", &rename1{}},
+	{"mknod1", &mknod1{}},
+}
+
+// TO TEST:
+//	Statfs
+//	Lookup(*LookupRequest, *LookupResponse)
+//	Getattr(*GetattrRequest, *GetattrResponse)
+//	Attr with explicit inode
+//	Setattr(*SetattrRequest, *SetattrResponse)
+//	Access(*AccessRequest)
+//	Open(*OpenRequest, *OpenResponse)
+//	Getxattr, Setxattr, Listxattr, Removexattr
+//	Write(*WriteRequest, *WriteResponse)
+//	Flush(*FlushRequest, *FlushResponse)
+
+// Test Read calling ReadAll.
+
+type readAll struct{ file }
+
+const hi = "hello, world"
+
+func (readAll) ReadAll(intr Intr) ([]byte, Error) {
+	return []byte(hi), nil
+}
+
+func (readAll) test(path string, t *testing.T) {
+	data, err := ioutil.ReadFile(path)
+	if err != nil {
+		t.Errorf("readAll: %v", err)
+		return
+	}
+	if string(data) != hi {
+		t.Errorf("readAll = %q, want %q", data, hi)
+	}
+}
+
+// Test Read.
+
+type readAll1 struct{ file }
+
+func (readAll1) Read(req *ReadRequest, resp *ReadResponse, intr Intr) Error {
+	HandleRead(req, resp, []byte(hi))
+	return nil
+}
+
+func (readAll1) test(path string, t *testing.T) {
+	readAll{}.test(path, t)
+}
+
+// Test Write calling basic Write, with an fsync thrown in too.
+
+type write struct {
+	file
+	data     []byte
+	gotfsync bool
+}
+
+func (w *write) Write(req *WriteRequest, resp *WriteResponse, intr Intr) Error {
+	w.data = append(w.data, req.Data...)
+	resp.Size = len(req.Data)
+	return nil
+}
+
+func (w *write) Fsync(r *FsyncRequest, intr Intr) Error {
+	w.gotfsync = true
+	return nil
+}
+
+func (w *write) test(path string, t *testing.T) {
+	log.Printf("pre-write Create")
+	f, err := os.Create(path)
+	if err != nil {
+		t.Fatalf("Create: %v", err)
+	}
+	log.Printf("pre-write Write")
+	n, err := f.Write([]byte(hi))
+	if err != nil {
+		t.Fatalf("Write: %v", err)
+	}
+	if n != len(hi) {
+		t.Fatalf("short write; n=%d; hi=%d", n, len(hi))
+	}
+
+	err = syscall.Fsync(int(f.Fd()))
+	if err != nil {
+		t.Fatalf("Fsync = %v", err)
+	}
+	if !w.gotfsync {
+		t.Errorf("never received expected fsync call")
+	}
+
+	log.Printf("pre-write Close")
+	err = f.Close()
+	if err != nil {
+		t.Fatalf("Close: %v", err)
+	}
+	log.Printf("post-write Close")
+	if string(w.data) != hi {
+		t.Errorf("writeAll = %q, want %q", w.data, hi)
+	}
+}
+
+// Test Write calling WriteAll.
+
+type writeAll struct {
+	file
+	data     []byte
+	gotfsync bool
+}
+
+func (w *writeAll) Fsync(r *FsyncRequest, intr Intr) Error {
+	w.gotfsync = true
+	return nil
+}
+
+func (w *writeAll) WriteAll(data []byte, intr Intr) Error {
+	w.data = data
+	return nil
+}
+
+func (w *writeAll) test(path string, t *testing.T) {
+	err := ioutil.WriteFile(path, []byte(hi), 0666)
+	if err != nil {
+		t.Fatalf("WriteFile: %v", err)
+		return
+	}
+	if string(w.data) != hi {
+		t.Errorf("writeAll = %q, want %q", w.data, hi)
+	}
+}
+
+// Test Write calling Setattr+Write+Flush.
+
+type writeAll2 struct {
+	file
+	data    []byte
+	setattr bool
+	flush   bool
+}
+
+func (w *writeAll2) Setattr(req *SetattrRequest, resp *SetattrResponse, intr Intr) Error {
+	w.setattr = true
+	return nil
+}
+
+func (w *writeAll2) Flush(req *FlushRequest, intr Intr) Error {
+	w.flush = true
+	return nil
+}
+
+func (w *writeAll2) Write(req *WriteRequest, resp *WriteResponse, intr Intr) Error {
+	w.data = append(w.data, req.Data...)
+	resp.Size = len(req.Data)
+	return nil
+}
+
+func (w *writeAll2) test(path string, t *testing.T) {
+	err := ioutil.WriteFile(path, []byte(hi), 0666)
+	if err != nil {
+		t.Errorf("WriteFile: %v", err)
+		return
+	}
+	if !w.setattr || string(w.data) != hi || !w.flush {
+		t.Errorf("writeAll = %v, %q, %v, want %v, %q, %v", w.setattr, string(w.data), w.flush, true, hi, true)
+	}
+}
+
+// Test Mkdir.
+
+type mkdir1 struct {
+	dir
+	name string
+}
+
+func (f *mkdir1) Mkdir(req *MkdirRequest, intr Intr) (Node, Error) {
+	f.name = req.Name
+	return &mkdir1{}, nil
+}
+
+func (f *mkdir1) test(path string, t *testing.T) {
+	f.name = ""
+	err := os.Mkdir(path+"/foo", 0777)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if f.name != "foo" {
+		t.Error(err)
+		return
+	}
+}
+
+// Test Create (and fsync)
+
+type create1 struct {
+	dir
+	name string
+	f    *writeAll
+}
+
+func (f *create1) Create(req *CreateRequest, resp *CreateResponse, intr Intr) (Node, Handle, Error) {
+	f.name = req.Name
+	f.f = &writeAll{}
+	return f.f, f.f, nil
+}
+
+func (f *create1) test(path string, t *testing.T) {
+	f.name = ""
+	ff, err := os.Create(path + "/foo")
+	if err != nil {
+		t.Errorf("create1 WriteFile: %v", err)
+		return
+	}
+
+	err = syscall.Fsync(int(ff.Fd()))
+	if err != nil {
+		t.Fatalf("Fsync = %v", err)
+	}
+
+	if !f.f.gotfsync {
+		t.Errorf("never received expected fsync call")
+	}
+
+	ff.Close()
+	if f.name != "foo" {
+		t.Errorf("create1 name=%q want foo", f.name)
+	}
+}
+
+// Test Create + WriteAll + Remove
+
+type create2 struct {
+	dir
+	name      string
+	f         *writeAll
+	fooExists bool
+}
+
+func (f *create2) Create(req *CreateRequest, resp *CreateResponse, intr Intr) (Node, Handle, Error) {
+	f.name = req.Name
+	f.f = &writeAll{}
+	return f.f, f.f, nil
+}
+
+func (f *create2) Lookup(name string, intr Intr) (Node, Error) {
+	if f.fooExists && name == "foo" {
+		return file{}, nil
+	}
+	return nil, ENOENT
+}
+
+func (f *create2) Remove(r *RemoveRequest, intr Intr) Error {
+	if f.fooExists && r.Name == "foo" && !r.Dir {
+		f.fooExists = false
+		return nil
+	}
+	return ENOENT
+}
+
+func (f *create2) test(path string, t *testing.T) {
+	f.name = ""
+	err := ioutil.WriteFile(path+"/foo", []byte(hi), 0666)
+	if err != nil {
+		t.Fatalf("create2 WriteFile: %v", err)
+	}
+	if string(f.f.data) != hi {
+		t.Fatalf("create2 writeAll = %q, want %q", f.f.data, hi)
+	}
+
+	f.fooExists = true
+	log.Printf("pre-Remove")
+	err = os.Remove(path + "/foo")
+	if err != nil {
+		t.Fatalf("Remove: %v", err)
+	}
+	err = os.Remove(path + "/foo")
+	if err == nil {
+		t.Fatalf("second Remove = nil; want some error")
+	}
+}
+
+// Test symlink + readlink
+
+type symlink1 struct {
+	dir
+	newName, target string
+}
+
+func (f *symlink1) Symlink(req *SymlinkRequest, intr Intr) (Node, Error) {
+	f.newName = req.NewName
+	f.target = req.Target
+	return symlink{target: req.Target}, nil
+}
+
+func (f *symlink1) test(path string, t *testing.T) {
+	const target = "/some-target"
+
+	err := os.Symlink(target, path+"/symlink.file")
+	if err != nil {
+		t.Errorf("os.Symlink: %v", err)
+		return
+	}
+
+	if f.newName != "symlink.file" {
+		t.Errorf("symlink newName = %q; want %q", f.newName, "symlink.file")
+	}
+	if f.target != target {
+		t.Errorf("symlink target = %q; want %q", f.target, target)
+	}
+
+	gotName, err := os.Readlink(path + "/symlink.file")
+	if err != nil {
+		t.Errorf("os.Readlink: %v", err)
+		return
+	}
+	if gotName != target {
+		t.Errorf("os.Readlink = %q; want %q", gotName, target)
+	}
+}
+
+// Test link
+
+type link1 struct {
+	dir
+	newName string
+}
+
+func (f *link1) Lookup(name string, intr Intr) (Node, Error) {
+	if name == "old" {
+		return file{}, nil
+	}
+	return nil, ENOENT
+}
+
+func (f *link1) Link(r *LinkRequest, old Node, intr Intr) (Node, Error) {
+	f.newName = r.NewName
+	return file{}, nil
+}
+
+func (f *link1) test(path string, t *testing.T) {
+	err := os.Link(path+"/old", path+"/new")
+	if err != nil {
+		t.Fatalf("Link: %v", err)
+	}
+	if f.newName != "new" {
+		t.Fatalf("saw Link for newName %q; want %q", f.newName, "new")
+	}
+}
+
+// Test Rename
+
+type rename1 struct {
+	dir
+	renames int
+}
+
+func (f *rename1) Lookup(name string, intr Intr) (Node, Error) {
+	if name == "old" {
+		return file{}, nil
+	}
+	return nil, ENOENT
+}
+
+func (f *rename1) Rename(r *RenameRequest, newDir Node, intr Intr) Error {
+	if r.OldName == "old" && r.NewName == "new" && newDir == f {
+		f.renames++
+		return nil
+	}
+	return EIO
+}
+
+func (f *rename1) test(path string, t *testing.T) {
+	err := os.Rename(path+"/old", path+"/new")
+	if err != nil {
+		t.Fatalf("Rename: %v", err)
+	}
+	if f.renames != 1 {
+		t.Fatalf("expected rename didn't happen")
+	}
+	err = os.Rename(path+"/old2", path+"/new2")
+	if err == nil {
+		t.Fatal("expected error on second Rename; got nil")
+	}
+}
+
+// Test Release.
+
+type release struct {
+	file
+	did bool
+}
+
+func (r *release) Release(*ReleaseRequest, Intr) Error {
+	r.did = true
+	return nil
+}
+
+func (r *release) test(path string, t *testing.T) {
+	r.did = false
+	f, err := os.Open(path)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	f.Close()
+	time.Sleep(1 * time.Second)
+	if !r.did {
+		t.Error("Close did not Release")
+	}
+}
+
+// Test mknod
+
+type mknod1 struct {
+	dir
+	gotr *MknodRequest
+}
+
+func (f *mknod1) Mknod(r *MknodRequest, intr Intr) (Node, Error) {
+	f.gotr = r
+	return fifo{}, nil
+}
+
+func (f *mknod1) test(path string, t *testing.T) {
+	if os.Getuid() != 0 {
+		t.Logf("skipping unless root")
+		return
+	}
+	defer syscall.Umask(syscall.Umask(0))
+	err := syscall.Mknod(path+"/node", syscall.S_IFIFO|0666, 123)
+	if err != nil {
+		t.Fatalf("Mknod: %v", err)
+	}
+	if f.gotr == nil {
+		t.Fatalf("no recorded MknodRequest")
+	}
+	if g, e := f.gotr.Name, "node"; g != e {
+		t.Errorf("got Name = %q; want %q", g, e)
+	}
+	if g, e := f.gotr.Rdev, uint32(123); g != e {
+		if runtime.GOOS == "linux" {
+			// Linux fuse doesn't echo back the rdev if the node
+			// isn't a device (we're using a FIFO here, as that
+			// bit is portable.)
+		} else {
+			t.Errorf("got Rdev = %v; want %v", g, e)
+		}
+	}
+	if g, e := f.gotr.Mode, os.FileMode(os.ModeNamedPipe|0666); g != e {
+		t.Errorf("got Mode = %v; want %v", g, e)
+	}
+	t.Logf("Got request: %#v", f.gotr)
+}
+
+type file struct{}
+type dir struct{}
+type fifo struct{}
+type symlink struct {
+	target string
+}
+
+func (f file) Attr() Attr    { return Attr{Mode: 0666} }
+func (f dir) Attr() Attr     { return Attr{Mode: os.ModeDir | 0777} }
+func (f fifo) Attr() Attr    { return Attr{Mode: os.ModeNamedPipe | 0666} }
+func (f symlink) Attr() Attr { return Attr{Mode: os.ModeSymlink | 0666} }
+
+func (f symlink) Readlink(*ReadlinkRequest, Intr) (string, Error) {
+	return f.target, nil
+}
+
+type testFS struct{}
+
+func (testFS) Root() (Node, Error) {
+	return testFS{}, nil
+}
+
+func (testFS) Attr() Attr {
+	return Attr{Mode: os.ModeDir | 0555}
+}
+
+func (testFS) Lookup(name string, intr Intr) (Node, Error) {
+	for _, tt := range fuseTests {
+		if tt.name == name {
+			return tt.node, nil
+		}
+	}
+	return nil, ENOENT
+}
+
+func (testFS) ReadDir(intr Intr) ([]Dirent, Error) {
+	var dirs []Dirent
+	for _, tt := range fuseTests {
+		if *fuseRun == "" || *fuseRun == tt.name {
+			log.Printf("Readdir; adding %q", tt.name)
+			dirs = append(dirs, Dirent{Name: tt.name})
+		}
+	}
+	return dirs, nil
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/hellofs/hello.go b/vendor/github.com/mattermost/rsc/fuse/hellofs/hello.go
new file mode 100644
index 000000000..d915473f1
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/hellofs/hello.go
@@ -0,0 +1,62 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Hellofs implements a simple "hello world" file system.
+package main
+
+import (
+	"log"
+	"os"
+
+	"github.com/mattermost/rsc/fuse"
+)
+
+func main() {
+	c, err := fuse.Mount("/mnt/hellofs")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	c.Serve(FS{})
+}
+
+// FS implements the hello world file system.
+type FS struct{}
+
+func (FS) Root() (fuse.Node, fuse.Error) {
+	return Dir{}, nil
+}
+
+// Dir implements both Node and Handle for the root directory.
+type Dir struct{}
+
+func (Dir) Attr() fuse.Attr {
+	return fuse.Attr{Mode: os.ModeDir | 0555}
+}
+
+func (Dir) Lookup(name string, intr fuse.Intr) (fuse.Node, fuse.Error) {
+	if name == "hello" {
+		return File{}, nil
+	}
+	return nil, fuse.ENOENT
+}
+
+var dirDirs = []fuse.Dirent{
+	{Inode: 2, Name: "hello", Type: 0},
+}
+
+func (Dir) ReadDir(intr fuse.Intr) ([]fuse.Dirent, fuse.Error) {
+	return dirDirs, nil
+}
+
+// File implements both Node and Handle for the hello file.
+type File struct{}
+
+func (File) Attr() fuse.Attr {
+	return fuse.Attr{Mode: 0444}
+}
+
+func (File) ReadAll(intr fuse.Intr) ([]byte, fuse.Error) {
+	return []byte("hello, world\n"), nil
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/mount_darwin.go b/vendor/github.com/mattermost/rsc/fuse/mount_darwin.go
new file mode 100644
index 000000000..5e2caaa76
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/mount_darwin.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.
+
+// TODO: Rewrite using package syscall not cgo
+
+package fuse
+
+/*
+
+// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c,
+// which carries this notice:
+//
+// The files in this directory are subject to the following license.
+// 
+// The author of this software is Russ Cox.
+// 
+//         Copyright (c) 2006 Russ Cox
+// 
+// Permission to use, copy, modify, and distribute this software for any
+// purpose without fee is hereby granted, provided that this entire notice
+// is included in all copies of any software which is or includes a copy
+// or modification of this software and in all copies of the supporting
+// documentation for such software.
+// 
+// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+// WARRANTY.  IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY
+// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
+// FITNESS FOR ANY PARTICULAR PURPOSE.
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define nil ((void*)0)
+
+static int
+mountfuse(char *mtpt, char **err)
+{
+	int i, pid, fd, r;
+	char buf[200];
+	struct vfsconf vfs;
+	char *f;
+
+	if(getvfsbyname("fusefs", &vfs) < 0){
+		if(access(f="/Library/Filesystems/osxfusefs.fs"
+			"/Support/load_osxfusefs", 0) < 0){
+		         *err = strdup("cannot find load_fusefs");
+		   	return -1;
+		}
+		if((r=system(f)) < 0){
+			snprintf(buf, sizeof buf, "%s: %s", f, strerror(errno));
+			*err = strdup(buf);
+			return -1;
+		}
+		if(r != 0){
+			snprintf(buf, sizeof buf, "load_fusefs failed: exit %d", r);
+			*err = strdup(buf);
+			return -1;
+		}
+		if(getvfsbyname("osxfusefs", &vfs) < 0){
+			snprintf(buf, sizeof buf, "getvfsbyname osxfusefs: %s", strerror(errno));
+			*err = strdup(buf);
+			return -1;
+		}
+	}
+
+	// Look for available FUSE device.
+	for(i=0;; i++){
+		snprintf(buf, sizeof buf, "/dev/osxfuse%d", i);
+		if(access(buf, 0) < 0){
+			*err = strdup("no available fuse devices");
+			return -1;
+		}
+		if((fd = open(buf, O_RDWR)) >= 0)
+			break;
+	}
+
+	pid = fork();
+	if(pid < 0)
+		return -1;
+	if(pid == 0){
+		snprintf(buf, sizeof buf, "%d", fd);
+		setenv("MOUNT_FUSEFS_CALL_BY_LIB", "", 1);
+		// Different versions of MacFUSE put the
+		// mount_fusefs binary in different places.
+		// Try all.
+		// Leopard location
+		setenv("MOUNT_FUSEFS_DAEMON_PATH",
+			   "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", 1);
+		execl("/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs",
+			  "mount_osxfusefs",
+			  "-o", "iosize=4096", buf, mtpt, nil);
+		fprintf(stderr, "exec mount_osxfusefs: %s\n", strerror(errno));
+		_exit(1);
+	}
+	return fd;
+}
+
+*/
+import "C"
+
+import "unsafe"
+
+func mount(dir string) (int, string) {
+	errp := (**C.char)(C.malloc(16))
+	*errp = nil
+	defer C.free(unsafe.Pointer(errp))
+	cdir := C.CString(dir)
+	defer C.free(unsafe.Pointer(cdir))
+	fd := C.mountfuse(cdir, errp)
+	var err string
+	if *errp != nil {
+		err = C.GoString(*errp)
+	}
+	return int(fd), err
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/mount_linux.go b/vendor/github.com/mattermost/rsc/fuse/mount_linux.go
new file mode 100644
index 000000000..e5bc58b8a
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/mount_linux.go
@@ -0,0 +1,67 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuse
+
+import (
+	"fmt"
+	"net"
+	"os"
+	"os/exec"
+	"syscall"
+)
+
+func mount(dir string) (fusefd int, errmsg string) {
+	fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0)
+	if err != nil {
+		return -1, fmt.Sprintf("socketpair error: %v", err)
+	}
+	defer syscall.Close(fds[0])
+	defer syscall.Close(fds[1])
+
+	cmd := exec.Command("/bin/fusermount", "--", dir)
+	cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3")
+
+	writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes")
+	defer writeFile.Close()
+	cmd.ExtraFiles = []*os.File{writeFile}
+
+	out, err := cmd.CombinedOutput()
+	if len(out) > 0 || err != nil {
+		return -1, fmt.Sprintf("fusermount: %q, %v", out, err)
+	}
+
+	readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads")
+	defer readFile.Close()
+	c, err := net.FileConn(readFile)
+	if err != nil {
+		return -1, fmt.Sprintf("FileConn from fusermount socket: %v", err)
+	}
+	defer c.Close()
+
+	uc, ok := c.(*net.UnixConn)
+	if !ok {
+		return -1, fmt.Sprintf("unexpected FileConn type; expected UnixConn, got %T", c)
+	}
+
+	buf := make([]byte, 32) // expect 1 byte
+	oob := make([]byte, 32) // expect 24 bytes
+	_, oobn, _, _, err := uc.ReadMsgUnix(buf, oob)
+	scms, err := syscall.ParseSocketControlMessage(oob[:oobn])
+	if err != nil {
+		return -1, fmt.Sprintf("ParseSocketControlMessage: %v", err)
+	}
+	if len(scms) != 1 {
+		return -1, fmt.Sprintf("expected 1 SocketControlMessage; got scms = %#v", scms)
+	}
+	scm := scms[0]
+	gotFds, err := syscall.ParseUnixRights(&scm)
+	if err != nil {
+		return -1, fmt.Sprintf("syscall.ParseUnixRights: %v", err)
+	}
+	if len(gotFds) != 1 {
+		return -1, fmt.Sprintf("wanted 1 fd; got %#v", gotFds)
+	}
+	return gotFds[0], ""
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/serve.go b/vendor/github.com/mattermost/rsc/fuse/serve.go
new file mode 100644
index 000000000..fa4f27e3f
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/serve.go
@@ -0,0 +1,1022 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// FUSE service loop, for servers that wish to use it.
+
+package fuse
+
+import (
+	"fmt"
+	"hash/fnv"
+	"io"
+	"log"
+	"os"
+	"path"
+	"sync"
+	"syscall"
+	"time"
+)
+
+// TODO: FINISH DOCS
+
+// An Intr is a channel that signals that a request has been interrupted.
+// Being able to receive from the channel means the request has been
+// interrupted.
+type Intr chan struct{}
+
+func (Intr) String() string { return "fuse.Intr" }
+
+// An FS is the interface required of a file system.
+//
+//	Root() (Node, Error)
+//
+// Root is called to obtain the Node for the file system root.
+//
+// Optional Methods
+//
+// An FS implementation may implement
+// additional methods to handle the corresponding FUSE requests:
+//
+//	Init(req *InitRequest, resp *InitResponse) Error
+//
+// Init is called to initialize the FUSE connection.
+// It can inspect the request and adjust the response as desired.
+// The default response sets MaxReadahead to 0 and MaxWrite to 4096.
+// Init must return promptly.
+//
+//	Statfs(resp *StatfsResponse, intr Intr) Error
+//
+// Statfs is called to obtain file system metadata.  It should write that data to resp.
+//
+//	Rename(req *RenameRequest, intr Intr) Error
+//
+// XXXX this is not implemented like this. Instead, Rename is a method
+// on the source dierctory node, and takes a newDir Node parameter. Fix it like this?
+// Rename is called to rename the file req.OldName in the directory req.OldDir to
+// become the file req.NewName in the directory req.NewDir.
+//
+type FS interface {
+	Root() (Node, Error)
+}
+
+// A Node is the interface required of a file or directory.
+// See the documentation for type FS for general information
+// pertaining to all methods.
+//
+//	Getattr(resp *GetattrResponse, intr Intr) fuse.Error
+//
+// Getattr obtains the standard metadata for the receiver.
+// It should store that metadata in resp.
+//
+//	Open(xxx, intr Intr) (Handle, fuse.Error)
+//
+// Open opens the receiver.
+// XXX note about access.  XXX OpenFlags.
+// XXX note that the Node may be a file or directory.
+//
+// Optional Methods
+//
+// An Node implementation may implement additional methods
+// to handle the corresponding FUSE requests.
+//
+// These optional requests can be called for both file and directory nodes:
+//
+//	Access
+//
+// Access checks whether the calling context has permission for
+// the given operations on the receiver.  If so, Access should return nil.  If not, Access should
+// return EPERM.  Note that this call affects the result of the access(2) system call
+// but not the open(2) system call.  If Access is not implemented, the Node behaves
+// as if it always returns nil (permission granted), relying on checks in Open instead.
+//
+//	Getxattr
+//
+// Getxattr obtains an extended attribute for the receiver.
+// XXX
+//
+//	Listxattr
+// 
+// Listxattr lists the extended attributes recorded for the receiver.
+//
+//	Removexattr
+//
+// Removexattr removes an extended attribute from the receiver.
+//
+//	Setattr
+//
+// Setattr sets the standard metadata for the receiver.
+//
+//	Setxattr
+//
+// Setxattr sets an extended attribute for the receiver.
+//
+// Optional Directory Methods
+//
+// These optional requests will be called only for directory nodes:
+//
+//	Create(xxx)
+//
+// Create creates 
+//
+//	Link(xxx)
+//
+// Link XXX
+//
+//	Lookup(name string, intr Intr) (Node, Error)
+//
+// Lookup looks up a specific entry in the receiver,
+// which must be a directory.  Lookup should return a Node
+// corresponding to the entry.  If the name does not exist in
+// the directory, Lookup should return nil, err.
+//
+// Lookup need not to handle the names "." and "..".
+//
+//	Mkdir
+//
+// Mkdir creates XXX
+//
+//	Mknod XXX
+//
+// XXX
+//
+//	Remove
+//
+// Remove removes the entry with the given name from
+// the receiver, which must be a directory.  The entry to be removed
+// may correspond to a file (unlink) or to a directory (rmdir).
+//
+//	Symlink
+//
+// Symlink creates a new symbolic link in the receiver, which must be a directory.
+// The entry 
+//
+// Optional Symlink Methods
+//
+// This optional request will be called only for symbolic link nodes:
+//
+//	Readlink
+//
+// Readlink reads a symbolic link.
+type Node interface {
+	Attr() Attr
+}
+
+var startTime = time.Now()
+
+func nodeAttr(inode uint64, n Node) (attr Attr) {
+	attr = n.Attr()
+	if attr.Nlink == 0 {
+		attr.Nlink = 1
+	}
+	if attr.Atime.IsZero() {
+		attr.Atime = startTime
+	}
+	if attr.Mtime.IsZero() {
+		attr.Mtime = startTime
+	}
+	if attr.Ctime.IsZero() {
+		attr.Ctime = startTime
+	}
+	if attr.Crtime.IsZero() {
+		attr.Crtime = startTime
+	}
+	if attr.Inode == 0 {
+		attr.Inode = inode
+	}
+	return
+}
+
+// A Handle is the interface required of an opened file or directory.
+// See the documentation for type FS for general information
+// pertaining to all methods.
+//
+//	Flush
+//
+// Flush is called each time the file or directory is closed.  Because there can be
+// multiple file descriptors referring to a single opened file, Flush can be called
+// multiple times.
+//
+// Optional Methods
+//
+// A Handle implementation may implement additional methods to handle
+// the corresponding FUSE requests.  The most common to implement are
+// Read, ReadDir, and Write.
+//
+//	Fsync
+//
+//	Getlk
+//
+//	Read
+//
+//	Readdir
+//
+//	Release
+//
+//	Setlk
+//
+//	Setlkw
+//
+//	Write
+//
+type Handle interface {
+}
+
+// Serve serves the FUSE connection by making calls to the methods
+// of fs and the Nodes and Handles it makes available.  It returns only
+// when the connection has been closed or an unexpected error occurs.
+func (c *Conn) Serve(fs FS) error {
+	if c.req != nil {
+		panic("fuse: Serve called twice")
+	}
+	c.req = map[RequestID]*serveRequest{}
+
+	root, err := fs.Root()
+	if err != nil {
+		return fmt.Errorf("cannot obtain root node: %v", syscall.Errno(err.(Errno)).Error())
+	}
+	c.node = append(c.node, nil, &serveNode{name: "/", node: root})
+	c.handle = append(c.handle, nil)
+
+	for {
+		req, err := c.ReadRequest()
+		if err != nil {
+			if err == io.EOF {
+				break
+			}
+			return err
+		}
+
+		go c.serve(fs, req)
+	}
+	return nil
+}
+
+type serveConn struct {
+	meta        sync.Mutex
+	req         map[RequestID]*serveRequest
+	node        []*serveNode
+	handle      []*serveHandle
+	freeNode    []NodeID
+	freeHandle  []HandleID
+	nodeGen     uint64
+	nodeHandles []map[HandleID]bool // open handles for a node; slice index is NodeID
+}
+
+type serveRequest struct {
+	Request Request
+	Intr    Intr
+}
+
+type serveNode struct {
+	name  string
+	node  Node
+	inode uint64
+	isDir bool
+}
+
+func (sn *serveNode) attr() (attr Attr) {
+	attr = nodeAttr(sn.inode, sn.node)
+	if attr.Inode == 0 {
+		sn.inode = hash(sn.name)
+		attr.Inode = sn.inode
+	}
+	sn.isDir = attr.Mode&os.ModeDir != 0
+	return
+}
+
+func hash(s string) uint64 {
+	f := fnv.New64()
+	f.Write([]byte(s))
+	return f.Sum64()
+}
+
+type serveHandle struct {
+	handle    Handle
+	readData  []byte
+	trunc     bool
+	writeData []byte
+	nodeID    NodeID
+}
+
+func (c *Conn) saveNode(name string, node Node) (id NodeID, gen uint64, sn *serveNode) {
+	sn = &serveNode{name: name, node: node}
+	c.meta.Lock()
+	if n := len(c.freeNode); n > 0 {
+		id = c.freeNode[n-1]
+		c.freeNode = c.freeNode[:n-1]
+		c.node[id] = sn
+		c.nodeGen++
+	} else {
+		id = NodeID(len(c.node))
+		c.node = append(c.node, sn)
+	}
+	gen = c.nodeGen
+	c.meta.Unlock()
+	return
+}
+
+func (c *Conn) saveHandle(handle Handle, nodeID NodeID) (id HandleID, shandle *serveHandle) {
+	c.meta.Lock()
+	shandle = &serveHandle{handle: handle, nodeID: nodeID}
+	if n := len(c.freeHandle); n > 0 {
+		id = c.freeHandle[n-1]
+		c.freeHandle = c.freeHandle[:n-1]
+		c.handle[id] = shandle
+	} else {
+		id = HandleID(len(c.handle))
+		c.handle = append(c.handle, shandle)
+	}
+
+	// Update mapping from node ID -> set of open Handle IDs.
+	for len(c.nodeHandles) <= int(nodeID) {
+		c.nodeHandles = append(c.nodeHandles, nil)
+	}
+	if c.nodeHandles[nodeID] == nil {
+		c.nodeHandles[nodeID] = make(map[HandleID]bool)
+	}
+	c.nodeHandles[nodeID][id] = true
+
+	c.meta.Unlock()
+	return
+}
+
+func (c *Conn) dropNode(id NodeID) {
+	c.meta.Lock()
+	c.node[id] = nil
+	if len(c.nodeHandles) > int(id) {
+		c.nodeHandles[id] = nil
+	}
+	c.freeNode = append(c.freeNode, id)
+	c.meta.Unlock()
+}
+
+func (c *Conn) dropHandle(id HandleID) {
+	c.meta.Lock()
+	h := c.handle[id]
+	delete(c.nodeHandles[h.nodeID], id)
+	c.handle[id] = nil
+	c.freeHandle = append(c.freeHandle, id)
+	c.meta.Unlock()
+}
+
+func (c *Conn) serve(fs FS, r Request) {
+	intr := make(Intr)
+	req := &serveRequest{Request: r, Intr: intr}
+
+	Debugf("<- %s", req)
+	var node Node
+	var handle Handle
+	var snode *serveNode
+	var shandle *serveHandle
+	c.meta.Lock()
+	hdr := r.Hdr()
+	if id := hdr.Node; id != 0 {
+		if id < NodeID(len(c.node)) {
+			snode = c.node[uint(id)]
+		}
+		if snode == nil {
+			c.meta.Unlock()
+			println("missing node", id, len(c.node), snode)
+			Debugf("-> %#x %v", hdr.ID, ESTALE)
+			r.RespondError(ESTALE)
+			return
+		}
+		node = snode.node
+	}
+	if id := r.handle(); id != 0 {
+		if id < HandleID(len(c.handle)) {
+			shandle = c.handle[uint(id)]
+		}
+		if shandle == nil {
+			println("missing handle", id, len(c.handle), shandle)
+			c.meta.Unlock()
+			Debugf("-> %#x %v", hdr.ID, ESTALE)
+			r.RespondError(ESTALE)
+			return
+		}
+		handle = shandle.handle
+	}
+	intr = make(chan struct{})
+	if c.req[hdr.ID] != nil {
+		// This happens with OSXFUSE.  Assume it's okay and
+		// that we'll never see an interrupt for this one.
+		// Otherwise everything wedges.  TODO: Report to OSXFUSE?
+		intr = nil
+	} else {
+		c.req[hdr.ID] = req
+	}
+	c.meta.Unlock()
+
+	// Call this before responding.
+	// After responding is too late: we might get another request
+	// with the same ID and be very confused.
+	done := func(resp interface{}) {
+		Debugf("-> %#x %v", hdr.ID, resp)
+		c.meta.Lock()
+		c.req[hdr.ID] = nil
+		c.meta.Unlock()
+	}
+
+	switch r := r.(type) {
+	default:
+		// Note: To FUSE, ENOSYS means "this server never implements this request."
+		// It would be inappropriate to return ENOSYS for other operations in this
+		// switch that might only be unavailable in some contexts, not all.
+		done(ENOSYS)
+		r.RespondError(ENOSYS)
+
+	// FS operations.
+	case *InitRequest:
+		s := &InitResponse{
+			MaxWrite: 4096,
+		}
+		if fs, ok := fs.(interface {
+			Init(*InitRequest, *InitResponse, Intr) Error
+		}); ok {
+			if err := fs.Init(r, s, intr); err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+		}
+		done(s)
+		r.Respond(s)
+
+	case *StatfsRequest:
+		s := &StatfsResponse{}
+		if fs, ok := fs.(interface {
+			Statfs(*StatfsRequest, *StatfsResponse, Intr) Error
+		}); ok {
+			if err := fs.Statfs(r, s, intr); err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+		}
+		done(s)
+		r.Respond(s)
+
+	// Node operations.
+	case *GetattrRequest:
+		s := &GetattrResponse{}
+		if n, ok := node.(interface {
+			Getattr(*GetattrRequest, *GetattrResponse, Intr) Error
+		}); ok {
+			if err := n.Getattr(r, s, intr); err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+		} else {
+			s.AttrValid = 1 * time.Minute
+			s.Attr = snode.attr()
+		}
+		done(s)
+		r.Respond(s)
+
+	case *SetattrRequest:
+		s := &SetattrResponse{}
+
+		// Special-case truncation, if no other bits are set
+		// and the open Handles all have a WriteAll method.
+		if r.Valid&SetattrSize != 0 && r.Size == 0 {
+			type writeAll interface {
+				WriteAll([]byte, Intr) Error
+			}
+			switch r.Valid {
+			case SetattrLockOwner | SetattrSize, SetattrSize:
+				// Seen on Linux. Handle isn't set.
+				c.meta.Lock()
+				for hid := range c.nodeHandles[hdr.Node] {
+					shandle := c.handle[hid]
+					if _, ok := shandle.handle.(writeAll); ok {
+						shandle.trunc = true
+					}
+				}
+				c.meta.Unlock()
+			case SetattrHandle | SetattrSize:
+				// Seen on OS X; the Handle is provided.
+				if _, ok := handle.(writeAll); ok {
+					shandle.trunc = true
+				}
+			}
+		}
+
+		log.Printf("setattr %v", r)
+		if n, ok := node.(interface {
+			Setattr(*SetattrRequest, *SetattrResponse, Intr) Error
+		}); ok {
+			if err := n.Setattr(r, s, intr); err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+			done(s)
+			r.Respond(s)
+			break
+		}
+
+		if s.AttrValid == 0 {
+			s.AttrValid = 1 * time.Minute
+		}
+		s.Attr = snode.attr()
+		done(s)
+		r.Respond(s)
+
+	case *SymlinkRequest:
+		s := &SymlinkResponse{}
+		n, ok := node.(interface {
+			Symlink(*SymlinkRequest, Intr) (Node, Error)
+		})
+		if !ok {
+			done(EIO) // XXX or EPERM like Mkdir?
+			r.RespondError(EIO)
+			break
+		}
+		n2, err := n.Symlink(r, intr)
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		c.saveLookup(&s.LookupResponse, snode, r.NewName, n2)
+		done(s)
+		r.Respond(s)
+
+	case *ReadlinkRequest:
+		n, ok := node.(interface {
+			Readlink(*ReadlinkRequest, Intr) (string, Error)
+		})
+		if !ok {
+			done(EIO) /// XXX or EPERM?
+			r.RespondError(EIO)
+			break
+		}
+		target, err := n.Readlink(r, intr)
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		done(target)
+		r.Respond(target)
+
+	case *LinkRequest:
+		n, ok := node.(interface {
+			Link(r *LinkRequest, old Node, intr Intr) (Node, Error)
+		})
+		if !ok {
+			log.Printf("Node %T doesn't implement fuse Link", node)
+			done(EIO) /// XXX or EPERM?
+			r.RespondError(EIO)
+			break
+		}
+		c.meta.Lock()
+		var oldNode *serveNode
+		if int(r.OldNode) < len(c.node) {
+			oldNode = c.node[r.OldNode]
+		}
+		c.meta.Unlock()
+		if oldNode == nil {
+			log.Printf("In LinkRequest, node %d not found", r.OldNode)
+			done(EIO)
+			r.RespondError(EIO)
+			break
+		}
+		n2, err := n.Link(r, oldNode.node, intr)
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		s := &LookupResponse{}
+		c.saveLookup(s, snode, r.NewName, n2)
+		done(s)
+		r.Respond(s)
+
+	case *RemoveRequest:
+		n, ok := node.(interface {
+			Remove(*RemoveRequest, Intr) Error
+		})
+		if !ok {
+			done(EIO) /// XXX or EPERM?
+			r.RespondError(EIO)
+			break
+		}
+		err := n.Remove(r, intr)
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		done(nil)
+		r.Respond()
+
+	case *AccessRequest:
+		if n, ok := node.(interface {
+			Access(*AccessRequest, Intr) Error
+		}); ok {
+			if err := n.Access(r, intr); err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+		}
+		done(r)
+		r.Respond()
+
+	case *LookupRequest:
+		var n2 Node
+		var err Error
+		s := &LookupResponse{}
+		if n, ok := node.(interface {
+			Lookup(string, Intr) (Node, Error)
+		}); ok {
+			n2, err = n.Lookup(r.Name, intr)
+		} else if n, ok := node.(interface {
+			Lookup(*LookupRequest, *LookupResponse, Intr) (Node, Error)
+		}); ok {
+			n2, err = n.Lookup(r, s, intr)
+		} else {
+			done(ENOENT)
+			r.RespondError(ENOENT)
+			break
+		}
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		c.saveLookup(s, snode, r.Name, n2)
+		done(s)
+		r.Respond(s)
+
+	case *MkdirRequest:
+		s := &MkdirResponse{}
+		n, ok := node.(interface {
+			Mkdir(*MkdirRequest, Intr) (Node, Error)
+		})
+		if !ok {
+			done(EPERM)
+			r.RespondError(EPERM)
+			break
+		}
+		n2, err := n.Mkdir(r, intr)
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		c.saveLookup(&s.LookupResponse, snode, r.Name, n2)
+		done(s)
+		r.Respond(s)
+
+	case *OpenRequest:
+		s := &OpenResponse{Flags: OpenDirectIO}
+		var h2 Handle
+		if n, ok := node.(interface {
+			Open(*OpenRequest, *OpenResponse, Intr) (Handle, Error)
+		}); ok {
+			hh, err := n.Open(r, s, intr)
+			if err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+			h2 = hh
+		} else {
+			h2 = node
+		}
+		s.Handle, _ = c.saveHandle(h2, hdr.Node)
+		done(s)
+		r.Respond(s)
+
+	case *CreateRequest:
+		n, ok := node.(interface {
+			Create(*CreateRequest, *CreateResponse, Intr) (Node, Handle, Error)
+		})
+		if !ok {
+			// If we send back ENOSYS, FUSE will try mknod+open.
+			done(EPERM)
+			r.RespondError(EPERM)
+			break
+		}
+		s := &CreateResponse{OpenResponse: OpenResponse{Flags: OpenDirectIO}}
+		n2, h2, err := n.Create(r, s, intr)
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		c.saveLookup(&s.LookupResponse, snode, r.Name, n2)
+		h, shandle := c.saveHandle(h2, hdr.Node)
+		s.Handle = h
+		shandle.trunc = true
+		done(s)
+		r.Respond(s)
+
+	case *GetxattrRequest, *SetxattrRequest, *ListxattrRequest, *RemovexattrRequest:
+		// TODO: Use n.
+		done(ENOSYS)
+		r.RespondError(ENOSYS)
+
+	case *ForgetRequest:
+		n, ok := node.(interface {
+			Forget()
+		})
+		if ok {
+			n.Forget()
+		}
+		c.dropNode(hdr.Node)
+		done(r)
+		r.Respond()
+
+	// Handle operations.
+	case *ReadRequest:
+		s := &ReadResponse{Data: make([]byte, 0, r.Size)}
+		if snode.isDir {
+			if h, ok := handle.(interface {
+				ReadDir(Intr) ([]Dirent, Error)
+			}); ok {
+				if shandle.readData == nil {
+					attr := snode.attr()
+					dirs, err := h.ReadDir(intr)
+					if err != nil {
+						done(err)
+						r.RespondError(err)
+						break
+					}
+					var data []byte
+					data = AppendDirent(data, Dirent{Inode: attr.Inode, Name: "."})
+					data = AppendDirent(data, Dirent{Inode: attr.Inode, Name: ".."})
+					for _, dir := range dirs {
+						if dir.Inode == 0 {
+							dir.Inode = hash(path.Join(snode.name, dir.Name))
+						}
+						data = AppendDirent(data, dir)
+					}
+					shandle.readData = data
+				}
+				HandleRead(r, s, shandle.readData)
+				done(s)
+				r.Respond(s)
+				break
+			}
+		} else {
+			if h, ok := handle.(interface {
+				ReadAll(Intr) ([]byte, Error)
+			}); ok {
+				if shandle.readData == nil {
+					data, err := h.ReadAll(intr)
+					if err != nil {
+						done(err)
+						r.RespondError(err)
+						break
+					}
+					if data == nil {
+						data = []byte{}
+					}
+					shandle.readData = data
+				}
+				HandleRead(r, s, shandle.readData)
+				done(s)
+				r.Respond(s)
+				break
+			}
+		}
+		h, ok := handle.(interface {
+			Read(*ReadRequest, *ReadResponse, Intr) Error
+		})
+		if !ok {
+			fmt.Printf("NO READ FOR %T\n", handle)
+			done(EIO)
+			r.RespondError(EIO)
+			break
+		}
+		if err := h.Read(r, s, intr); err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		done(s)
+		r.Respond(s)
+
+	case *WriteRequest:
+		s := &WriteResponse{}
+		if shandle.trunc && r.Offset == int64(len(shandle.writeData)) {
+			shandle.writeData = append(shandle.writeData, r.Data...)
+			s.Size = len(r.Data)
+			done(s)
+			r.Respond(s)
+			break
+		}
+		if h, ok := handle.(interface {
+			Write(*WriteRequest, *WriteResponse, Intr) Error
+		}); ok {
+			if err := h.Write(r, s, intr); err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+			done(s)
+			r.Respond(s)
+			break
+		}
+		println("NO WRITE")
+		done(EIO)
+		r.RespondError(EIO)
+
+	case *FlushRequest:
+		if shandle.trunc {
+			h := handle.(interface {
+				WriteAll([]byte, Intr) Error
+			})
+			if err := h.WriteAll(shandle.writeData, intr); err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+			shandle.writeData = nil
+			shandle.trunc = false
+		}
+		if h, ok := handle.(interface {
+			Flush(*FlushRequest, Intr) Error
+		}); ok {
+			if err := h.Flush(r, intr); err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+		}
+		done(nil)
+		r.Respond()
+
+	case *ReleaseRequest:
+		// No matter what, release the handle.
+		c.dropHandle(r.handle())
+		if h, ok := handle.(interface {
+			Release(*ReleaseRequest, Intr) Error
+		}); ok {
+			if err := h.Release(r, intr); err != nil {
+				done(err)
+				r.RespondError(err)
+				break
+			}
+		}
+		done(nil)
+		r.Respond()
+
+	case *DestroyRequest:
+		fs, ok := fs.(interface {
+			Destroy()
+		})
+		if ok {
+			fs.Destroy()
+		}
+		done(nil)
+		r.Respond()
+
+	case *RenameRequest:
+		c.meta.Lock()
+		var newDirNode *serveNode
+		if int(r.NewDir) < len(c.node) {
+			newDirNode = c.node[r.NewDir]
+		}
+		c.meta.Unlock()
+		if newDirNode == nil {
+			println("RENAME NEW DIR NODE NOT FOUND")
+			done(EIO)
+			r.RespondError(EIO)
+			break
+		}
+		n, ok := node.(interface {
+			Rename(r *RenameRequest, newDir Node, intr Intr) Error
+		})
+		if !ok {
+			log.Printf("Node %T missing Rename method", node)
+			done(EIO) // XXX or EPERM like Mkdir?
+			r.RespondError(EIO)
+			break
+		}
+		err := n.Rename(r, newDirNode.node, intr)
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		done(nil)
+		r.Respond()
+
+	case *MknodRequest:
+		n, ok := node.(interface {
+			Mknod(r *MknodRequest, intr Intr) (Node, Error)
+		})
+		if !ok {
+			log.Printf("Node %T missing Mknod method", node)
+			done(EIO)
+			r.RespondError(EIO)
+			break
+		}
+		n2, err := n.Mknod(r, intr)
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		s := &LookupResponse{}
+		c.saveLookup(s, snode, r.Name, n2)
+		done(s)
+		r.Respond(s)
+
+	case *FsyncRequest:
+		n, ok := node.(interface {
+			Fsync(r *FsyncRequest, intr Intr) Error
+		})
+		if !ok {
+			log.Printf("Node %T missing Fsync method", node)
+			done(EIO)
+			r.RespondError(EIO)
+			break
+		}
+		err := n.Fsync(r, intr)
+		if err != nil {
+			done(err)
+			r.RespondError(err)
+			break
+		}
+		done(nil)
+		r.Respond()
+
+		/*	case *FsyncdirRequest:
+				done(ENOSYS)
+				r.RespondError(ENOSYS)
+
+			case *GetlkRequest, *SetlkRequest, *SetlkwRequest:
+				done(ENOSYS)
+				r.RespondError(ENOSYS)
+
+			// One of a kind.
+			case *InterruptRequest:
+				c.meta.Lock()
+				ireq := c.req[r.OldID]
+				if ireq != nil && ireq.Intr != nil {
+					close(ireq.Intr)
+					ireq.Intr = nil
+				}
+				c.meta.Unlock()
+				done(nil)
+				r.Respond()
+
+			case *BmapRequest:
+				done(ENOSYS)
+				r.RespondError(ENOSYS)
+
+			case *SetvolnameRequest, *GetxtimesRequest, *ExchangeRequest:
+				done(ENOSYS)
+				r.RespondError(ENOSYS)
+		*/
+	}
+}
+
+func (c *Conn) saveLookup(s *LookupResponse, snode *serveNode, elem string, n2 Node) {
+	name := path.Join(snode.name, elem)
+	var sn *serveNode
+	s.Node, s.Generation, sn = c.saveNode(name, n2)
+	if s.EntryValid == 0 {
+		s.EntryValid = 1 * time.Minute
+	}
+	if s.AttrValid == 0 {
+		s.AttrValid = 1 * time.Minute
+	}
+	s.Attr = sn.attr()
+}
+
+// HandleRead handles a read request assuming that data is the entire file content.
+// It adjusts the amount returned in resp according to req.Offset and req.Size.
+func HandleRead(req *ReadRequest, resp *ReadResponse, data []byte) {
+	if req.Offset >= int64(len(data)) {
+		data = nil
+	} else {
+		data = data[req.Offset:]
+	}
+	if len(data) > req.Size {
+		data = data[:req.Size]
+	}
+	n := copy(resp.Data[:req.Size], data)
+	resp.Data = resp.Data[:n]
+}
+
+// DataHandle returns a read-only Handle that satisfies reads
+// using the given data.
+func DataHandle(data []byte) Handle {
+	return &dataHandle{data}
+}
+
+type dataHandle struct {
+	data []byte
+}
+
+func (d *dataHandle) Read(intr Intr) ([]byte, Error) {
+	return d.data, nil
+}
diff --git a/vendor/github.com/mattermost/rsc/fuse/tree.go b/vendor/github.com/mattermost/rsc/fuse/tree.go
new file mode 100644
index 000000000..fec0a748f
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/fuse/tree.go
@@ -0,0 +1,93 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// FUSE directory tree, for servers that wish to use it with the service loop.
+
+package fuse
+
+import (
+	"os"
+	pathpkg "path"
+	"strings"
+)
+
+// A Tree implements a basic directory tree for FUSE.
+type Tree struct {
+	tree
+}
+
+func (t *Tree) Root() (Node, Error) {
+	return &t.tree, nil
+}
+
+// Add adds the path to the tree, resolving to the given node.
+// If path or a prefix of path has already been added to the tree,
+// Add panics.
+func (t *Tree) Add(path string, node Node) {
+	path = pathpkg.Clean("/" + path)[1:]
+	elems := strings.Split(path, "/")
+	dir := Node(&t.tree)
+	for i, elem := range elems {
+		dt, ok := dir.(*tree)
+		if !ok {
+			panic("fuse: Tree.Add for " + strings.Join(elems[:i], "/") + " and " + path)
+		}
+		n := dt.lookup(elem)
+		if n != nil {
+			if i+1 == len(elems) {
+				panic("fuse: Tree.Add for " + path + " conflicts with " + elem)
+			}
+			dir = n
+		} else {
+			if i+1 == len(elems) {
+				dt.add(elem, node)
+			} else {
+				dir = &tree{}
+				dt.add(elem, dir)
+			}
+		}
+	}
+}
+
+type treeDir struct {
+	name string
+	node Node
+}
+
+type tree struct {
+	dir []treeDir
+}
+
+func (t *tree) lookup(name string) Node {
+	for _, d := range t.dir {
+		if d.name == name {
+			return d.node
+		}
+	}
+	return nil
+}
+
+func (t *tree) add(name string, n Node) {
+	t.dir = append(t.dir, treeDir{name, n})
+}
+
+func (t *tree) Attr() Attr {
+	return Attr{Mode: os.ModeDir | 0555}
+}
+
+func (t *tree) Lookup(name string, intr Intr) (Node, Error) {
+	n := t.lookup(name)
+	if n != nil {
+		return n, nil
+	}
+	return nil, ENOENT
+}
+
+func (t *tree) ReadDir(intr Intr) ([]Dirent, Error) {
+	var out []Dirent
+	for _, d := range t.dir {
+		out = append(out, Dirent{Name: d.name})
+	}
+	return out, nil
+}
diff --git a/vendor/github.com/mattermost/rsc/gf256/blog_test.go b/vendor/github.com/mattermost/rsc/gf256/blog_test.go
new file mode 100644
index 000000000..12cc7deb0
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/gf256/blog_test.go
@@ -0,0 +1,85 @@
+// Copyright 2012 The Go 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 a straightforward implementation of
+// Reed-Solomon encoding, along with a benchmark.
+// It goes with http://research.swtch.com/field.
+//
+// For an optimized implementation, see gf256.go.
+
+package gf256
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+)
+
+// BlogECC writes to check the error correcting code bytes
+// for data using the given Reed-Solomon parameters.
+func BlogECC(rs *RSEncoder, m []byte, check []byte) {
+	if len(check) < rs.c {
+		panic("gf256: invalid check byte length")
+	}
+	if rs.c == 0 {
+		return
+	}
+
+	// The check bytes are the remainder after dividing
+	// data padded with c zeros by the generator polynomial.  
+
+	// p = data padded with c zeros.
+	var p []byte
+	n := len(m) + rs.c
+	if len(rs.p) >= n {
+		p = rs.p
+	} else {
+		p = make([]byte, n)
+	}
+	copy(p, m)
+	for i := len(m); i < len(p); i++ {
+		p[i] = 0
+	}
+
+	gen := rs.gen
+
+	// Divide p by gen, leaving the remainder in p[len(data):].
+	// p[0] is the most significant term in p, and
+	// gen[0] is the most significant term in the generator.
+	for i := 0; i < len(m); i++ {
+		k := f.Mul(p[i], f.Inv(gen[0])) // k = pi / g0
+		// p -= k·g
+		for j, g := range gen {
+			p[i+j] = f.Add(p[i+j], f.Mul(k, g))
+		}
+	}
+
+	copy(check, p[len(m):])
+	rs.p = p
+}
+
+func BenchmarkBlogECC(b *testing.B) {
+	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
+	check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f}
+	out := make([]byte, len(check))
+	rs := NewRSEncoder(f, len(check))
+	for i := 0; i < b.N; i++ {
+		BlogECC(rs, data, out)
+	}
+	b.SetBytes(int64(len(data)))
+	if !bytes.Equal(out, check) {
+		fmt.Printf("have %#v want %#v\n", out, check)
+	}
+}
+
+func TestBlogECC(t *testing.T) {
+	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
+	check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
+	out := make([]byte, len(check))
+	rs := NewRSEncoder(f, len(check))
+	BlogECC(rs, data, out)
+	if !bytes.Equal(out, check) {
+		t.Errorf("have %x want %x", out, check)
+	}
+}
diff --git a/vendor/github.com/mattermost/rsc/gf256/gf256_test.go b/vendor/github.com/mattermost/rsc/gf256/gf256_test.go
new file mode 100644
index 000000000..f77fa7d67
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/gf256/gf256_test.go
@@ -0,0 +1,194 @@
+// 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 gf256
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+)
+
+var f = NewField(0x11d, 2) // x^8 + x^4 + x^3 + x^2 + 1
+
+func TestBasic(t *testing.T) {
+	if f.Exp(0) != 1 || f.Exp(1) != 2 || f.Exp(255) != 1 {
+		panic("bad Exp")
+	}
+}
+
+func TestECC(t *testing.T) {
+	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
+	check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
+	out := make([]byte, len(check))
+	rs := NewRSEncoder(f, len(check))
+	rs.ECC(data, out)
+	if !bytes.Equal(out, check) {
+		t.Errorf("have %x want %x", out, check)
+	}
+}
+
+func TestLinear(t *testing.T) {
+	d1 := []byte{0x00, 0x00}
+	c1 := []byte{0x00, 0x00}
+	out := make([]byte, len(c1))
+	rs := NewRSEncoder(f, len(c1))
+	if rs.ECC(d1, out); !bytes.Equal(out, c1) {
+		t.Errorf("ECBytes(%x, %d) = %x, want 0", d1, len(c1), out)
+	}
+	d2 := []byte{0x00, 0x01}
+	c2 := make([]byte, 2)
+	rs.ECC(d2, c2)
+	d3 := []byte{0x00, 0x02}
+	c3 := make([]byte, 2)
+	rs.ECC(d3, c3)
+	cx := make([]byte, 2)
+	for i := range cx {
+		cx[i] = c2[i] ^ c3[i]
+	}
+	d4 := []byte{0x00, 0x03}
+	c4 := make([]byte, 2)
+	rs.ECC(d4, c4)
+	if !bytes.Equal(cx, c4) {
+		t.Errorf("ECBytes(%x, 2) = %x\nECBytes(%x, 2) = %x\nxor = %x\nECBytes(%x, 2) = %x",
+			d2, c2, d3, c3, cx, d4, c4)
+	}
+}
+
+func TestGaussJordan(t *testing.T) {
+	rs := NewRSEncoder(f, 2)
+	m := make([][]byte, 16)
+	for i := range m {
+		m[i] = make([]byte, 4)
+		m[i][i/8] = 1 << uint(i%8)
+		rs.ECC(m[i][:2], m[i][2:])
+	}
+	if false {
+		fmt.Printf("---\n")
+		for _, row := range m {
+			fmt.Printf("%x\n", row)
+		}
+	}
+	b := []uint{0, 1, 2, 3, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27}
+	for i := 0; i < 16; i++ {
+		bi := b[i]
+		if m[i][bi/8]&(1<<(7-bi%8)) == 0 {
+			for j := i + 1; ; j++ {
+				if j >= len(m) {
+					t.Errorf("lost track for %d", bi)
+					break
+				}
+				if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
+					m[i], m[j] = m[j], m[i]
+					break
+				}
+			}
+		}
+		for j := i + 1; j < len(m); j++ {
+			if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
+				for k := range m[j] {
+					m[j][k] ^= m[i][k]
+				}
+			}
+		}
+	}
+	if false {
+		fmt.Printf("---\n")
+		for _, row := range m {
+			fmt.Printf("%x\n", row)
+		}
+	}
+	for i := 15; i >= 0; i-- {
+		bi := b[i]
+		for j := i - 1; j >= 0; j-- {
+			if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
+				for k := range m[j] {
+					m[j][k] ^= m[i][k]
+				}
+			}
+		}
+	}
+	if false {
+		fmt.Printf("---\n")
+		for _, row := range m {
+			fmt.Printf("%x", row)
+			out := make([]byte, 2)
+			if rs.ECC(row[:2], out); !bytes.Equal(out, row[2:]) {
+				fmt.Printf(" - want %x", out)
+			}
+			fmt.Printf("\n")
+		}
+	}
+}
+
+func BenchmarkECC(b *testing.B) {
+	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
+	check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f}
+	out := make([]byte, len(check))
+	rs := NewRSEncoder(f, len(check))
+	for i := 0; i < b.N; i++ {
+		rs.ECC(data, out)
+	}
+	b.SetBytes(int64(len(data)))
+	if !bytes.Equal(out, check) {
+		fmt.Printf("have %#v want %#v\n", out, check)
+	}
+}
+
+func TestGen(t *testing.T) {
+	for i := 0; i < 256; i++ {
+		_, lg := f.gen(i)
+		if lg[0] != 0 {
+			t.Errorf("#%d: %x", i, lg)
+		}
+	}
+}
+
+func TestReducible(t *testing.T) {
+	var count = []int{1, 2, 3, 6, 9, 18, 30, 56, 99, 186} // oeis.org/A1037
+	for i, want := range count {
+		n := 0
+		for p := 1 << uint(i+2); p < 1< 0 {
+		select {
+		case w := <-acmeChan:
+			if w == nil {
+				// Sync with reader.
+				continue
+			}
+			if w.err != nil {
+				if active[w.name] == nil {
+					continue
+				}
+				log.Fatal(w.err)
+			}
+			if *acmeDebug {
+				fmt.Fprintf(os.Stderr, "%s %c%c %d,%d %q\n", w.name, w.C1, w.C2, w.Q0, w.Q1, w.Text)
+			}
+			if w.C1 == 'M' || w.C1 == 'K' {
+				lastActivity = time.Now()
+				if status != xmpp.Available {
+					setStatus(xmpp.Available)
+				}
+			}
+			if (w.C2 == 'x' || w.C2 == 'X') && string(w.Text) == "Del" {
+				// TODO: Hangup connection for w.typ == "acct"?
+				delete(active, w.name)
+				w.Del(true)
+				continue Loop
+			}
+
+			switch w.typ {
+			case "main":
+				switch w.C2 {
+				case 'L': // Button 3 in body: load chat window for contact.
+					w.expand()
+					fallthrough
+				case 'l': // Button 3 in tag
+					arg := string(w.Text)
+					showContact(arg)
+					continue Loop
+				}
+			case "chat":
+				if w.C1 == 'F' && w.C2 == 'I' {
+					continue Loop
+				}
+				if w.C1 != 'M' && w.C1 != 'K' {
+					break
+				}
+				if w.blinky {
+					w.blinky = false
+					w.Fprintf("ctl", "dirty\n")
+				}
+				switch w.C2 {
+				case 'X', 'x':
+					if string(w.Text) == "Ack" {
+						w.Fprintf("ctl", "clean\n")
+					}
+				case 'I':
+					w.sendMsg()
+					continue Loop
+				}
+			}
+			w.WriteEvent(w.Event)
+
+		case msg := <-msgChan:
+			w := msg.w
+			if msg.err != nil {
+				w.Fprintf("body", "ERROR: %s\n", msg.err)
+				continue Loop
+			}
+			you := msg.Remote
+			if i := strings.Index(you, "/"); i >= 0 {
+				you = you[:i]
+			}
+			switch msg.Type {
+			case "chat":
+				w := showContact(you)
+				text := strings.TrimSpace(msg.Text)
+				if text == "" {
+					// Probably a composing notification.
+					continue
+				}
+				w.message("> %s\n", text)
+				w.blinky = true
+				w.dirty = true
+
+			case "presence":
+				pr := msg.Presence
+				pr, new := savePresence(pr, you)
+				if !new {
+					continue
+				}
+				w := lookContact(you)
+				if w != nil {
+					w.status(pr)
+				}
+				mainStatus(pr, you)
+			}
+
+		case t := <-tick:
+			switch status {
+			case xmpp.Available:
+				if t.Sub(lastActivity) > awayTime {
+					setStatus(xmpp.Away)
+				}
+			case xmpp.Away:
+				if t.Sub(lastActivity) > extendedAwayTime {
+					setStatus(xmpp.ExtendedAway)
+				}
+			}
+			for _, w := range active {
+				if w.blinky {
+					w.dirty = !w.dirty
+					if w.dirty {
+						w.Fprintf("ctl", "dirty\n")
+					} else {
+						w.Fprintf("ctl", "clean\n")
+					}
+				}
+			}
+		}
+	}
+}
+
+func setStatus(st xmpp.Status) {
+	status = st
+	client.Status(status, statusMsg)
+	mainWin.statusTag(status, statusMsg)
+}
+
+func savePresence(pr *xmpp.Presence, you string) (pr1 *xmpp.Presence, new bool) {
+	old := cachedPresence(you)
+
+	pr.StatusMsg = strings.TrimSpace(pr.StatusMsg)
+	c := statusCache[you]
+	for i, p := range c {
+		if p.Remote == pr.Remote {
+			c[i] = pr
+			c[0], c[i] = c[i], c[0]
+			goto Best
+		}
+	}
+	c = append(c, pr)
+	c[0], c[len(c)-1] = c[len(c)-1], c[0]
+	statusCache[you] = c
+
+Best:
+	best := cachedPresence(you)
+	return best, old == nil || old.Status != best.Status || old.StatusMsg != best.StatusMsg
+}
+
+func cachedPresence(you string) *xmpp.Presence {
+	c := statusCache[you]
+	if len(c) == 0 {
+		return nil
+	}
+	best := c[0]
+	for _, p := range c {
+		if p.Status > best.Status {
+			best = p
+		}
+	}
+	return best
+}
+
+func short(st xmpp.Status) string {
+	switch st {
+	case xmpp.Unavailable:
+		return "?"
+	case xmpp.ExtendedAway:
+		return "x"
+	case xmpp.Away:
+		return "-"
+	case xmpp.Available:
+		return "+"
+	case xmpp.DoNotDisturb:
+		return "!"
+	}
+	return st.String()
+}
+
+func long(st xmpp.Status) string {
+	switch st {
+	case xmpp.Unavailable:
+		return "unavailable"
+	case xmpp.ExtendedAway:
+		return "offline"
+	case xmpp.Away:
+		return "away"
+	case xmpp.Available:
+		return "available"
+	case xmpp.DoNotDisturb:
+		return "busy"
+	}
+	return st.String()
+}
+
+func (w *Window) time() string {
+	/*
+		Auto-date chat windows:
+
+		 	Show date and time on first message.
+		 	Show time if minute is different from last message.
+		 	Show date if day is different from last message.
+
+		 	Oct 10 12:01 > hi
+		 	12:03 hello there
+		 	12:05 > what's up?
+
+			12:10 [Away]
+	*/
+	now := time.Now()
+	m1, d1, y1 := w.lastTime.Date()
+	m2, d2, y2 := now.Date()
+	w.lastTime = now
+
+	if m1 != m2 || d1 != d2 || y1 != y2 {
+		return now.Format("Jan 2 15:04 ")
+	}
+	return now.Format("15:04 ")
+}
+
+func (w *Window) status(pr *xmpp.Presence) {
+	msg := ""
+	if pr.StatusMsg != "" {
+		msg = ": " + pr.StatusMsg
+	}
+	w.message("[%s%s]\n", long(pr.Status), msg)
+
+	w.statusTag(pr.Status, pr.StatusMsg)
+}
+
+func (w *Window) statusTag(status xmpp.Status, statusMsg string) {
+	data, err := w.ReadAll("tag")
+	if err != nil {
+		log.Printf("read tag: %v", err)
+		return
+	}
+	//log.Printf("tag1: %s\n", data)
+	i := bytes.IndexByte(data, '|')
+	if i >= 0 {
+		data = data[i+1:]
+	} else {
+		data = nil
+	}
+	//log.Printf("tag2: %s\n", data)
+	j := bytes.IndexByte(data, '|')
+	if j >= 0 {
+		data = data[j+1:]
+	}
+	//log.Printf("tag3: %s\n", data)
+
+	msg := ""
+	if statusMsg != "" {
+		msg = " " + statusMsg
+	}
+	w.Ctl("cleartag\n")
+	w.Write("tag", []byte(" "+short(status)+msg+" |"+string(data)))
+}
+
+func mainStatus(pr *xmpp.Presence, you string) {
+	w := mainWin
+	if err := w.Addr("#0/^(.[ \t]+)?" + regexp.QuoteMeta(you) + "([ \t]*|$)/"); err != nil {
+		return
+	}
+	q0, q1, err := w.ReadAddr()
+	if err != nil {
+		log.Printf("ReadAddr: %s\n", err)
+		return
+	}
+	if err := w.Addr("#%d/"+regexp.QuoteMeta(you)+"/", q0); err != nil {
+		log.Printf("Addr2: %s\n", err)
+	}
+	q2, q3, err := w.ReadAddr()
+	if err != nil {
+		log.Printf("ReadAddr2: %s\n", err)
+		return
+	}
+
+	space := " "
+	if q1 > q3 || pr.StatusMsg == "" { // already have or don't need space
+		space = ""
+	}
+	if err := w.Addr("#%d/.*/", q1); err != nil {
+		log.Printf("Addr3: %s\n", err)
+	}
+	w.Fprintf("data", "%s%s", space, pr.StatusMsg)
+
+	space = ""
+	if q0 == q2 {
+		w.Addr("#%d,#%d", q0, q0)
+		space = " "
+	} else {
+		w.Addr("#%d,#%d", q0, q0+1)
+	}
+	w.Fprintf("data", "%s%s", short(pr.Status), space)
+}
+
+func (w *Window) expand() {
+	// Use selection if any.
+	w.Fprintf("ctl", "addr=dot\n")
+	q0, q1, err := w.ReadAddr()
+	if err == nil && q0 <= w.Q0 && w.Q0 <= q1 {
+		goto Read
+	}
+	if err = w.Addr("#%d-/[a-zA-Z0-9_@.\\-]*/,#%d+/[a-zA-Z0-9_@.\\-]*/", w.Q0, w.Q1); err != nil {
+		log.Printf("expand: %v", err)
+		return
+	}
+	q0, q1, err = w.ReadAddr()
+	if err != nil {
+		log.Printf("expand: %v", err)
+		return
+	}
+
+Read:
+	data, err := w.ReadAll("xdata")
+	if err != nil {
+		log.Printf("read: %v", err)
+		return
+	}
+	w.Text = data
+	w.Q0 = q0
+	w.Q1 = q1
+	return
+}
+
+// Invariant: in chat windows, the acme addr corresponds to the
+// empty string just before the input being typed.  Text before addr
+// is the chat history (usually ending in a blank line).
+
+func (w *Window) message(format string, args ...interface{}) {
+	if *acmeDebug {
+		q0, q1, _ := w.ReadAddr()
+		log.Printf("message; addr=%d,%d", q0, q1)
+	}
+	if err := w.Addr(".-/\\n?\\n?/"); err != nil && *acmeDebug {
+		log.Printf("set addr: %s", err)
+	}
+	q0, _, _ := w.ReadAddr()
+	nl := ""
+	if q0 > 0 {
+		nl = "\n"
+	}
+	if *acmeDebug {
+		q0, q1, _ := w.ReadAddr()
+		log.Printf("inserting; addr=%d,%d", q0, q1)
+	}
+	w.Fprintf("data", nl+w.time()+format+"\n", args...)
+	if *acmeDebug {
+		q0, q1, _ := w.ReadAddr()
+		log.Printf("wrote; addr=%d,%d", q0, q1)
+	}
+}
+
+func (w *Window) sendMsg() {
+	if *acmeDebug {
+		q0, q1, _ := w.ReadAddr()
+		log.Printf("sendMsg; addr=%d,%d", q0, q1)
+	}
+	if err := w.Addr(`.,./(.|\n)*\n/`); err != nil {
+		if *acmeDebug {
+			q0, q1, _ := w.ReadAddr()
+			log.Printf("no text (%s); addr=%d,%d", err, q0, q1)
+		}
+		return
+	}
+	q0, q1, _ := w.ReadAddr()
+	if *acmeDebug {
+		log.Printf("found msg; addr=%d,%d", q0, q1)
+	}
+	line, _ := w.ReadAll("xdata")
+	trim := string(bytes.TrimSpace(line))
+	if len(trim) > 0 {
+		err := client.Send(xmpp.Chat{Remote: w.remote, Type: "chat", Text: trim})
+
+		// Select blank line before input (if any) and input.
+		w.Addr("#%d-/\\n?\\n?/,#%d", q0, q1)
+		if *acmeDebug {
+			q0, q1, _ := w.ReadAddr()
+			log.Printf("selected text; addr=%d,%d", q0, q1)
+		}
+		q0, _, _ := w.ReadAddr()
+
+		// Overwrite with \nmsg\n\n.
+		// Leaves addr after final \n, which is where we want it.
+		nl := ""
+		if q0 > 0 {
+			nl = "\n"
+		}
+		errstr := ""
+		if err != nil {
+			errstr = fmt.Sprintf("\n%s", errstr)
+		}
+		w.Fprintf("data", "%s%s%s%s\n\n", nl, w.time(), trim, errstr)
+		if *acmeDebug {
+			q0, q1, _ := w.ReadAddr()
+			log.Printf("wrote; addr=%d,%d", q0, q1)
+		}
+		w.Fprintf("ctl", "clean\n")
+	}
+}
+
+func (w *Window) readAcme() {
+	for {
+		e, err := w.ReadEvent()
+		if err != nil {
+			w.err = err
+			acmeChan <- w
+			break
+		}
+		//fmt.Printf("%c%c %d,%d %d,%d %#x %#q %#q %#q\n", e.C1, e.C2, e.Q0, e.Q1, e.OrigQ0, e.OrigQ1, e.Flag, e.Text, e.Arg, e.Loc)
+		w.Event = e
+		acmeChan <- w
+		acmeChan <- nil
+	}
+}
+
+func (w *Window) readChat() {
+	for {
+		msg, err := client.Recv()
+		if err != nil {
+			msgChan <- &Msg{w: w, err: err}
+			break
+		}
+		//fmt.Printf("%s\n", *msg)
+		msgChan <- &Msg{w: w, Chat: &msg}
+	}
+}
+
+func lookContact(you string) *Window {
+	return active["Chat/"+acct.Nick+"/"+you]
+}
+
+func showContact(you string) *Window {
+	w := lookContact(you)
+	if w != nil {
+		w.Ctl("show\n")
+		return w
+	}
+
+	ww, err := acme.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	name := "Chat/" + acct.Nick + "/" + you
+	ww.Name(name)
+	w = &Window{Win: ww, typ: "chat", name: name, remote: you}
+	w.Fprintf("body", "\n")
+	w.Addr("#1")
+	w.OpenEvent()
+	w.Fprintf("ctl", "cleartag\n")
+	w.Fprintf("tag", " Ack")
+	if p := cachedPresence(you); p != nil {
+		w.status(p)
+	}
+	active[name] = w
+	go w.readAcme()
+	return w
+}
+
+func randid() string {
+	return fmt.Sprint(time.Now())
+}
diff --git a/vendor/github.com/mattermost/rsc/google/chat.go b/vendor/github.com/mattermost/rsc/google/chat.go
new file mode 100644
index 000000000..8e9ae1c50
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/google/chat.go
@@ -0,0 +1,39 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package google
+
+import "github.com/mattermost/rsc/xmpp"
+
+type ChatID struct {
+	ID        string
+	Email     string
+	Status    xmpp.Status
+	StatusMsg string
+}
+
+type ChatSend struct {
+	ID  *ChatID
+	Msg xmpp.Chat
+}
+
+func (g *Client) ChatRecv(cid *ChatID) (*xmpp.Chat, error) {
+	var msg xmpp.Chat
+	if err := g.client.Call("goog.ChatRecv", cid, &msg); err != nil {
+		return nil, err
+	}
+	return &msg, nil
+}
+
+func (g *Client) ChatStatus(cid *ChatID) error {
+	return g.client.Call("goog.ChatRecv", cid, &Empty{})
+}
+
+func (g *Client) ChatSend(cid *ChatID, msg *xmpp.Chat) error {
+	return g.client.Call("goog.ChatSend", &ChatSend{cid, *msg}, &Empty{})
+}
+
+func (g *Client) ChatRoster(cid *ChatID) error {
+	return g.client.Call("goog.ChatRoster", cid, &Empty{})
+}
diff --git a/vendor/github.com/mattermost/rsc/google/gmail/gmail.go b/vendor/github.com/mattermost/rsc/google/gmail/gmail.go
new file mode 100644
index 000000000..1d4072ef7
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/google/gmail/gmail.go
@@ -0,0 +1,1241 @@
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"flag"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"os/exec"
+	"os/signal"
+	"regexp"
+	"sort"
+	"strings"
+	"time"
+
+	"github.com/mattermost/rsc/google"
+	"github.com/mattermost/rsc/imap"
+)
+
+var cmdtab = []struct {
+	Name string
+	Args int
+	F    func(*Cmd, *imap.MsgPart) *imap.MsgPart
+	TF   func(*Cmd, []*imap.Msg) *imap.MsgPart
+	Help string
+}{
+	{"+", 0, pluscmd, tpluscmd, "+        print the next message"},
+	{"a", 1, rcmd, nil, "a        reply to sender and recipients"},
+	{"b", 0, bcmd, nil, "b        print the next 10 headers"},
+	{"d", 0, dcmd, tdcmd, "d        mark for deletion"},
+	{"f", 1, fcmd, tfcmd, "f        forward message"},
+	{"h", 0, hcmd, nil, "h        print elided message summary (,h for all)"},
+	{"help", 0, nil, nil, "help     print this info"},
+	{"i", 0, icmd, nil, "i        incorporate new mail"},
+	{"m", 0, mcmd, tmcmd, "m        mute and delete thread (gmail only)"},
+	{"mime", 0, mimecmd, nil, "mime     print message's MIME structure "},
+	{"p", 0, pcmd, nil, "p        print the processed message"},
+	//	{ "p+",	0,	pcmd, nil,	"p        print the processed message, showing all quoted text" },
+	{"P", 0, Pcmd, nil, "P        print the raw message"},
+	{`"`, 0, quotecmd, nil, `"        print a quoted version of msg`},
+	{"q", 0, qcmd, nil, "q        exit and remove all deleted mail"},
+	{"r", 1, rcmd, nil, "r [addr] reply to sender plus any addrs specified"},
+	{"s", 1, scmd, tscmd, "s name   copy message to named mailbox (label for gmail)"},
+	{"u", 0, ucmd, nil, "u        remove deletion mark"},
+	//	{ "w",	1,	wcmd, nil,	"w file   store message contents as file" },
+	{"W", 0, Wcmd, nil, "W	open in web browser"},
+	{"x", 0, xcmd, nil, "x        exit without flushing deleted messages"},
+	{"y", 0, ycmd, nil, "y        synchronize with mail box"},
+	{"=", 1, eqcmd, nil, "=        print current message number"},
+	{"|", 1, pipecmd, nil, "|cmd     pipe message body to a command"},
+	//	{ "||",	1,	rpipecmd, nil, "||cmd     pipe raw message to a command" },
+	{"!", 1, bangcmd, nil, "!cmd     run a command"},
+}
+
+func init() {
+	// Have to insert helpcmd by hand because it refers to cmdtab,
+	// so it would cause an init loop above.
+	for i := range cmdtab {
+		if cmdtab[i].Name == "help" {
+			cmdtab[i].F = helpcmd
+		}
+	}
+}
+
+type Cmd struct {
+	Name    string
+	Args    []string
+	Line    string // Args[0:] original text
+	ArgLine string // Args[1:] original text
+	F       func(*Cmd, *imap.MsgPart) *imap.MsgPart
+	TF      func(*Cmd, []*imap.Msg) *imap.MsgPart
+	Delete  bool
+	Thread  bool
+	Targ    *imap.MsgPart
+	Targs   []*imap.Msg
+	A1, A2  int
+}
+
+var (
+	bin  = bufio.NewReader(os.Stdin)
+	bout = bufio.NewWriter(os.Stdout)
+
+	acctName = flag.String("a", "", "account to use")
+
+	dot *imap.MsgPart // Selected messages
+
+	inbox       *imap.Box
+	msgs        []*imap.Msg
+	msgNum      = make(map[*imap.Msg]int)
+	deleted     = make(map[*imap.Msg]bool)
+	isGmail     = false
+	acct        google.Account
+	threaded    bool
+	interrupted bool
+
+	maxfrom int
+	subjlen int
+)
+
+func nextMsg(m *imap.Msg) *imap.Msg {
+	i := msgNum[m]
+	i++
+	if i >= len(msgs) {
+		return nil
+	}
+	return msgs[i]
+}
+
+func main() {
+	flag.BoolVar(&imap.Debug, "imapdebug", false, "imap debugging trace")
+	flag.Parse()
+
+	acct = google.Acct(*acctName)
+
+	if args := flag.Args(); len(args) > 0 {
+		for i := range args {
+			args[i] = "-to=" + args[i]
+		}
+		cmd := exec.Command("gmailsend", append([]string{"-a", acct.Email, "-i"}, args...)...)
+		cmd.Stdin = os.Stdin
+		cmd.Stdout = os.Stdout
+		cmd.Stderr = os.Stderr
+		if err := cmd.Run(); err != nil {
+			fmt.Fprintf(os.Stderr, "!%s\n", err)
+			os.Exit(1)
+		}
+		return
+	}
+
+	c, err := imap.NewClient(imap.TLS, "imap.gmail.com", acct.Email, acct.Password, "")
+	if err != nil {
+		log.Fatal(err)
+	}
+	isGmail = c.IsGmail()
+	threaded = isGmail
+
+	inbox = c.Inbox()
+	if err := inbox.Check(); err != nil {
+		log.Fatal(err)
+	}
+
+	msgs = inbox.Msgs()
+	maxfrom = 12
+	for i, m := range msgs {
+		msgNum[m] = i
+		if n := len(from(m.Hdr)); n > maxfrom {
+			maxfrom = n
+		}
+	}
+	if maxfrom > 20 {
+		maxfrom = 20
+	}
+	subjlen = 80 - maxfrom
+
+	rethread()
+
+	go func() {
+		for sig := range signal.Incoming {
+			if sig == os.SIGINT {
+				fmt.Fprintf(os.Stderr, "!interrupt\n")
+				interrupted = true
+				continue
+			}
+			if sig == os.SIGCHLD || sig == os.SIGWINCH {
+				continue
+			}
+			fmt.Fprintf(os.Stderr, "!%s\n", sig)
+		}
+	}()
+
+	for {
+		if dot != nil {
+			fmt.Fprintf(bout, "%d", msgNum[dot.Msg]+1)
+			if dot != &dot.Msg.Root {
+				fmt.Fprintf(bout, ".%s", dot.ID)
+			}
+		}
+		fmt.Fprintf(bout, ": ")
+		bout.Flush()
+
+		line, err := bin.ReadString('\n')
+		if err != nil {
+			break
+		}
+
+		cmd, err := parsecmd(line)
+		if err != nil {
+			fmt.Fprintf(bout, "!%s\n", err)
+			continue
+		}
+
+		if cmd.Targ != nil || cmd.Targs == nil && cmd.A2 == 0 {
+			x := cmd.F(cmd, cmd.Targ)
+			if x != nil {
+				dot = x
+			}
+		} else {
+			targs := cmd.Targs
+			if targs == nil {
+				delta := +1
+				if cmd.A1 > cmd.A2 {
+					delta = -1
+				}
+				for i := cmd.A1; i <= cmd.A2; i += delta {
+					if i < 1 || i > len(msgs) {
+						continue
+					}
+					targs = append(targs, msgs[i-1])
+				}
+			}
+			if cmd.Thread {
+				if !isGmail {
+					fmt.Fprintf(bout, "!need gmail for threaded command\n")
+					continue
+				}
+				byThread := make(map[uint64][]*imap.Msg)
+				for _, m := range msgs {
+					t := m.GmailThread
+					byThread[t] = append(byThread[t], m)
+				}
+				for _, m := range targs {
+					t := m.GmailThread
+					if byThread[t] != nil {
+						if cmd.TF != nil {
+							if x := cmd.TF(cmd, byThread[t]); x != nil {
+								dot = x
+							}
+						} else {
+							for _, mm := range byThread[t] {
+								x := cmd.F(cmd, &mm.Root)
+								if x != nil {
+									dot = x
+								}
+							}
+						}
+					}
+					delete(byThread, t)
+				}
+				continue
+			}
+			for _, m := range targs {
+				if cmd.Delete {
+					dcmd(cmd, &m.Root)
+					if cmd.Name == "p" {
+						// dp is a special case: it advances to the next message before the p.
+						next := nextMsg(m)
+						if next == nil {
+							fmt.Fprintf(bout, "!address\n")
+							dot = &m.Root
+							break
+						}
+						m = next
+					}
+				}
+				x := cmd.F(cmd, &m.Root)
+				if x != nil {
+					dot = x
+				}
+				// TODO: Break loop on interrupt.
+			}
+		}
+	}
+	qcmd(nil, nil)
+}
+
+func parsecmd(line string) (cmd *Cmd, err error) {
+	cmd = &Cmd{}
+	line = strings.TrimSpace(line)
+	if line == "" {
+		// Empty command is a special case: advance and print.
+		cmd.F = pcmd
+		if dot == nil {
+			cmd.A1 = 1
+			cmd.A2 = 1
+		} else {
+			n := msgNum[dot.Msg] + 2
+			if n > len(msgs) {
+				return nil, fmt.Errorf("out of messages")
+			}
+			cmd.A1 = n
+			cmd.A2 = n
+		}
+		return cmd, nil
+	}
+
+	// Global search?
+	if line[0] == 'g' {
+		line = line[1:]
+		if line == "" || line[0] != '/' {
+			// No search string means all messages.
+			cmd.A1 = 1
+			cmd.A2 = len(msgs)
+		} else if line[0] == '/' {
+			re, rest, err := parsere(line)
+			if err != nil {
+				return nil, err
+			}
+			line = rest
+			// Find all messages matching this search string.
+			var targ []*imap.Msg
+			for _, m := range msgs {
+				if re.MatchString(header(m)) {
+					targ = append(targ, m)
+				}
+			}
+			if len(targ) == 0 {
+				return nil, fmt.Errorf("no matches")
+			}
+			cmd.Targs = targ
+		}
+	} else {
+		// Parse an address.
+		a1, targ, rest, err := parseaddr(line, 1)
+		if err != nil {
+			return nil, err
+		}
+		if targ != nil {
+			cmd.Targ = targ
+			line = rest
+		} else {
+			if a1 < 1 || a1 > len(msgs) {
+				return nil, fmt.Errorf("message number %d out of range", a1)
+			}
+			cmd.A1 = a1
+			cmd.A2 = a1
+			a2 := a1
+			if rest != "" && rest[0] == ',' {
+				// This is an address range.
+				a2, targ, rest, err = parseaddr(rest[1:], len(msgs))
+				if err != nil {
+					return nil, err
+				}
+				if a2 < 1 || a2 > len(msgs) {
+					return nil, fmt.Errorf("message number %d out of range", a2)
+				}
+				cmd.A2 = a2
+			} else if rest == line {
+				// There was no address.
+				if dot == nil {
+					cmd.A1 = 1
+					cmd.A2 = 0
+				} else {
+					if dot != nil {
+						if dot == &dot.Msg.Root {
+							// If dot is a plain msg, use a range so that dp works.
+							cmd.A1 = msgNum[dot.Msg] + 1
+							cmd.A2 = cmd.A1
+						} else {
+							cmd.Targ = dot
+						}
+					}
+				}
+			}
+			line = rest
+		}
+	}
+
+	cmd.Line = strings.TrimSpace(line)
+
+	// Insert space after ! or | for tokenization.
+	switch {
+	case strings.HasPrefix(cmd.Line, "||"):
+		cmd.Line = cmd.Line[:2] + " " + cmd.Line[2:]
+	case strings.HasPrefix(cmd.Line, "!"), strings.HasPrefix(cmd.Line, "|"):
+		cmd.Line = cmd.Line[:1] + " " + cmd.Line[1:]
+	}
+
+	av := strings.Fields(cmd.Line)
+	cmd.Args = av
+	if len(av) == 0 || av[0] == "" {
+		// Default is to print.
+		cmd.F = pcmd
+		return cmd, nil
+	}
+
+	name := av[0]
+	cmd.ArgLine = strings.TrimSpace(cmd.Line[len(av[0]):])
+
+	// Hack to allow t prefix on all commands.
+	if len(name) >= 2 && name[0] == 't' {
+		cmd.Thread = true
+		name = name[1:]
+	}
+
+	// Hack to allow d prefix on all commands.
+	if len(name) >= 2 && name[0] == 'd' {
+		cmd.Delete = true
+		name = name[1:]
+	}
+	cmd.Name = name
+
+	// Search command table.
+	for _, ct := range cmdtab {
+		if ct.Name == name {
+			if ct.Args == 0 && len(av) > 1 {
+				return nil, fmt.Errorf("%s doesn't take an argument", name)
+			}
+			cmd.F = ct.F
+			cmd.TF = ct.TF
+			if name == "m" {
+				// mute applies to all thread no matter what
+				cmd.Thread = true
+			}
+			return cmd, nil
+		}
+	}
+	return nil, fmt.Errorf("unknown command %s", name)
+}
+
+func parseaddr(addr string, deflt int) (n int, targ *imap.MsgPart, rest string, err error) {
+	dot := dot
+	n = deflt
+	for {
+		old := addr
+		n, targ, rest, err = parseaddr1(addr, n, dot)
+		if targ != nil || rest == old || err != nil {
+			break
+		}
+		if n < 1 || n > len(msgs) {
+			return 0, nil, "", fmt.Errorf("message number %d out of range", n)
+		}
+		dot = &msgs[n-1].Root
+		addr = rest
+	}
+	return
+}
+
+func parseaddr1(addr string, deflt int, dot *imap.MsgPart) (n int, targ *imap.MsgPart, rest string, err error) {
+	base := 0
+	if dot != nil {
+		base = msgNum[dot.Msg] + 1
+	}
+	if addr == "" {
+		return deflt, nil, addr, nil
+	}
+	var i int
+	sign := 0
+	switch c := addr[0]; c {
+	case '+':
+		sign = +1
+		addr = addr[1:]
+	case '-':
+		sign = -1
+		addr = addr[1:]
+	case '.':
+		if base == 0 {
+			return 0, nil, "", fmt.Errorf("no message selected")
+		}
+		n = base
+		i = 1
+		goto HaveNumber
+	case '$':
+		if len(msgs) == 0 {
+			return 0, nil, "", fmt.Errorf("no messages")
+		}
+		n = len(msgs)
+		i = 1
+		goto HaveNumber
+	case '/', '?':
+		var re *regexp.Regexp
+		re, addr, err = parsere(addr)
+		if err != nil {
+			return
+		}
+		var delta int
+		if c == '/' {
+			delta = +1
+		} else {
+			delta = -1
+		}
+		for j := base + delta; 1 <= j && j <= len(msgs); j += delta {
+			if re.MatchString(header(msgs[j-1])) {
+				n = j
+				i = 0 // already cut addr
+				goto HaveNumber
+			}
+		}
+		err = fmt.Errorf("search")
+		return
+		// TODO case '%'
+	}
+	for i = 0; i < len(addr) && '0' <= addr[i] && addr[i] <= '9'; i++ {
+		n = 10*n + int(addr[i]) - '0'
+	}
+	if sign != 0 {
+		if n == 0 {
+			n = 1
+		}
+		n = base + n*sign
+		goto HaveNumber
+	}
+	if i == 0 {
+		return deflt, nil, addr, nil
+	}
+HaveNumber:
+	rest = addr[i:]
+	if i < len(addr) && addr[i] == '.' {
+		if n < 1 || n > len(msgs) {
+			err = fmt.Errorf("message number %d out of range", n)
+			return
+		}
+		targ = &msgs[n-1].Root
+		for i < len(addr) && addr[i] == '.' {
+			i++
+			var j int
+			n = 0
+			for j = i; j < len(addr) && '0' <= addr[j] && addr[j] <= '9'; j++ {
+				n = 10*n + int(addr[j]) - '0'
+			}
+			if j == i {
+				err = fmt.Errorf("malformed message number %s", addr[:j])
+				return
+			}
+			if n < 1 || n > len(targ.Child) {
+				err = fmt.Errorf("message number %s out of range", addr[:j])
+				return
+			}
+			targ = targ.Child[n-1]
+			i = j
+		}
+		n = 0
+		rest = addr[i:]
+		return
+	}
+	return
+}
+
+func parsere(addr string) (re *regexp.Regexp, rest string, err error) {
+	prog, rest, err := parseprog(addr)
+	if err != nil {
+		return
+	}
+	re, err = regexp.Compile(prog)
+	return
+}
+
+var lastProg string
+
+func parseprog(addr string) (prog string, rest string, err error) {
+	if len(addr) == 1 {
+		if lastProg != "" {
+			return lastProg, "", nil
+		}
+		err = fmt.Errorf("no search")
+		return
+	}
+	i := strings.Index(addr[1:], addr[:1])
+	if i < 0 {
+		prog = addr[1:]
+		rest = ""
+	} else {
+		i += 1 // adjust for slice in IndexByte arg
+		prog, rest = addr[1:i], addr[i+1:]
+	}
+	lastProg = prog
+	return
+}
+
+func bcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	var m *imap.Msg
+	if dot == nil {
+		if len(msgs) == 0 {
+			return nil
+		}
+		m = msgs[0]
+	} else {
+		m = dot.Msg
+	}
+	for i := 0; i < 10; i++ {
+		hcmd(c, &m.Root)
+		next := nextMsg(m)
+		if next == nil {
+			break
+		}
+		m = next
+	}
+	return &m.Root
+}
+
+func dcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		fmt.Fprintf(bout, "!address\n")
+		return nil
+	}
+	deleted[dot.Msg] = true
+	return &dot.Msg.Root
+}
+
+func tdcmd(c *Cmd, msgs []*imap.Msg) *imap.MsgPart {
+	if len(msgs) == 0 {
+		fmt.Fprintf(bout, "!address\n")
+		return nil
+	}
+	for _, m := range msgs {
+		deleted[m] = true
+	}
+	return &msgs[len(msgs)-1].Root
+}
+
+func ucmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		fmt.Fprintf(bout, "!address\n")
+		return nil
+	}
+	delete(deleted, dot.Msg)
+	return &dot.Msg.Root
+}
+
+func eqcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		fmt.Fprintf(bout, "0")
+	} else {
+		fmt.Fprintf(bout, "%d", msgNum[dot.Msg]+1)
+		if dot != &dot.Msg.Root {
+			fmt.Fprintf(bout, ".%s", dot.ID)
+		}
+	}
+	fmt.Fprintf(bout, "\n")
+	return nil
+}
+
+func from(h *imap.MsgHdr) string {
+	if len(h.From) < 1 {
+		return "?"
+	}
+	if name := h.From[0].Name; name != "" {
+		return name
+	}
+	return h.From[0].Email
+}
+
+func header(m *imap.Msg) string {
+	var t string
+	if time.Now().Sub(m.Date) > 365*24*time.Hour {
+		t = m.Date.Format("01/02 15:04")
+	} else {
+		t = m.Date.Format("01/02 2006 ")
+	}
+	ch := ' '
+	if len(m.Root.Child) > 1 || len(m.Root.Child) == 1 && len(m.Root.Child[0].Child) > 0 {
+		ch = 'H'
+	}
+	del := ' '
+	if deleted[m] {
+		del = 'd'
+	}
+	return fmt.Sprintf("%-3d %c%c %s %-*.*s %.*s",
+		msgNum[m]+1, ch, del, t,
+		maxfrom, maxfrom, from(m.Hdr),
+		subjlen, m.Hdr.Subject)
+}
+
+func hcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot != nil {
+		fmt.Fprintf(bout, "%s\n", header(dot.Msg))
+	}
+	return nil
+}
+
+func helpcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	fmt.Fprint(bout, "Commands are of the form []  [args]\n")
+	fmt.Fprint(bout, " :=  | ','| 'g'\n")
+	fmt.Fprint(bout, " := '.' | '$' | '^' |  |  | '+' | '-'\n")
+	fmt.Fprint(bout, " := '/''/' | '?''?'\n")
+	fmt.Fprint(bout, " :=\n")
+	for _, ct := range cmdtab {
+		fmt.Fprintf(bout, "%s\n", ct.Help)
+	}
+	return dot
+}
+
+func mimecmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot != nil {
+		mimeH(fmt.Sprint(msgNum[dot.Msg]+1), dot)
+	}
+	return nil
+}
+
+func mimeH(id string, p *imap.MsgPart) {
+	if p.ID != "" {
+		id = id + "." + p.ID
+	}
+	fmt.Fprintf(bout, "%s %s %s %#q %d\n", id, p.Type, p.Encoding+"/"+p.Charset, p.Name, p.Bytes)
+	for _, child := range p.Child {
+		mimeH(id, child)
+	}
+}
+
+func icmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	sync(false)
+	return nil
+}
+
+func ycmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	sync(true)
+	return nil
+}
+
+func tpluscmd(c *Cmd, msgs []*imap.Msg) *imap.MsgPart {
+	if len(msgs) == 0 {
+		return nil
+	}
+	m := nextMsg(msgs[len(msgs)-1])
+	if m == nil {
+		fmt.Fprintf(bout, "!no more messages\n")
+		return nil
+	}
+	return pcmd(c, &m.Root)
+}
+
+func pluscmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		return nil
+	}
+	m := nextMsg(dot.Msg)
+	if m == nil {
+		fmt.Fprintf(bout, "!no more messages\n")
+		return nil
+	}
+	return pcmd(c, &m.Root)
+}
+
+func addrlist(x []imap.Addr) string {
+	var b bytes.Buffer
+	for i, a := range x {
+		if i > 0 {
+			b.WriteString(", ")
+		}
+		b.WriteString(a.String())
+	}
+	return b.String()
+}
+
+func wpcmd(w io.Writer, c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		return nil
+	}
+	if dot == &dot.Msg.Root {
+		h := dot.Msg.Hdr
+		if len(h.From) > 0 {
+			fmt.Fprintf(w, "From: %s\n", addrlist(h.From))
+		}
+		fmt.Fprintf(w, "Date: %s\n", dot.Msg.Date)
+		if len(h.From) > 0 {
+			fmt.Fprintf(w, "To: %s\n", addrlist(h.To))
+		}
+		if len(h.CC) > 0 {
+			fmt.Fprintf(w, "CC: %s\n", addrlist(h.CC))
+		}
+		if len(h.BCC) > 0 {
+			fmt.Fprintf(w, "BCC: %s\n", addrlist(h.BCC))
+		}
+		if len(h.Subject) > 0 {
+			fmt.Fprintf(w, "Subject: %s\n", h.Subject)
+		}
+		fmt.Fprintf(w, "\n")
+	}
+	printMIME(w, dot, true)
+	fmt.Fprintf(w, "\n")
+	return dot
+}
+
+func pcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	defer bout.Flush()
+	return wpcmd(bout, c, dot)
+}
+
+func pipecmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	args := c.Args[1:]
+	if len(args) == 0 {
+		fmt.Fprintf(bout, "!no command\n")
+		return dot
+	}
+	bout.Flush()
+	cmd := exec.Command(args[0], args[1:]...)
+	w, err := cmd.StdinPipe()
+	if err != nil {
+		fmt.Fprintf(bout, "!%s\n", err)
+		return dot
+	}
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Start(); err != nil {
+		fmt.Fprintf(bout, "!%s\n", err)
+		return dot
+	}
+	wpcmd(w, c, dot)
+	w.Close()
+	if err := cmd.Wait(); err != nil {
+		fmt.Fprintf(bout, "!%s\n", err)
+	}
+	return dot
+}
+
+func bangcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	args := c.Args[1:]
+	if len(args) == 0 {
+		fmt.Fprintf(bout, "!no command\n")
+		return dot
+	}
+	bout.Flush()
+	cmd := exec.Command(args[0], args[1:]...)
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	if err := cmd.Run(); err != nil {
+		fmt.Fprintf(bout, "!%s\n", err)
+	}
+	return nil
+}
+
+func unixfrom(h *imap.MsgHdr) string {
+	if len(h.From) == 0 {
+		return ""
+	}
+	return h.From[0].Email
+}
+
+func unixtime(m *imap.Msg) string {
+	return dot.Msg.Date.Format("Mon Jan _2 15:04:05 MST 2006")
+}
+
+func Pcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		return nil
+	}
+	if dot == &dot.Msg.Root {
+		fmt.Fprintf(bout, "From %s %s\n",
+			unixfrom(dot.Msg.Hdr),
+			unixtime(dot.Msg))
+	}
+	bout.Write(dot.Raw())
+	return dot
+}
+
+func printMIME(w io.Writer, p *imap.MsgPart, top bool) {
+	switch {
+	case top && strings.HasPrefix(p.Type, "text/"):
+		text := p.ShortText()
+		if p.Type == "text/html" {
+			cmd := exec.Command("htmlfmt")
+			cmd.Stdin = bytes.NewBuffer(text)
+			if w == bout {
+				bout.Flush()
+				cmd.Stdout = os.Stdout
+			} else {
+				cmd.Stdout = w
+			}
+			if err := cmd.Run(); err != nil {
+				fmt.Fprintf(w, "%d.%s !%s\n", msgNum[p.Msg]+1, p.ID, err)
+			}
+			return
+		}
+		w.Write(text)
+	case p.Type == "text/plain":
+		if top {
+			panic("printMIME loop")
+		}
+		printMIME(w, p, true)
+	case p.Type == "multipart/alternative":
+		for _, pp := range p.Child {
+			if pp.Type == "text/plain" {
+				printMIME(w, pp, false)
+				return
+			}
+		}
+		if len(p.Child) > 0 {
+			printMIME(w, p.Child[0], false)
+		}
+	case strings.HasPrefix(p.Type, "multipart/"):
+		for _, pp := range p.Child {
+			printMIME(w, pp, false)
+		}
+	default:
+		fmt.Fprintf(w, "%d.%s !%s %s %s\n", msgNum[p.Msg]+1, p.ID, p.Type, p.Desc, p.Name)
+	}
+}
+
+func qcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	flushDeleted()
+	xcmd(c, dot)
+	panic("not reached")
+}
+
+type quoter struct {
+	bol bool
+	w   io.Writer
+}
+
+func (q *quoter) Write(b []byte) (n int, err error) {
+	n = len(b)
+	err = nil
+	for len(b) > 0 {
+		if q.bol {
+			q.w.Write([]byte("> "))
+			q.bol = false
+		}
+		i := bytes.IndexByte(b, '\n')
+		if i < 0 {
+			i = len(b)
+		} else {
+			q.bol = true
+			i++
+		}
+		q.w.Write(b[:i])
+		b = b[i:]
+	}
+	return
+}
+
+func quotecmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		return nil
+	}
+	m := dot.Msg
+	if len(m.Hdr.From) != 0 {
+		a := m.Hdr.From[0]
+		name := a.Name
+		if name == "" {
+			name = a.Email
+		}
+		date := m.Date.Format("Jan 2, 2006 at 15:04")
+		fmt.Fprintf(bout, "On %s, %s wrote:\n", date, name)
+	}
+	printMIME("er{true, bout}, dot, true)
+	return dot
+}
+
+func addre(s string) string {
+	if len(s) < 4 || !strings.EqualFold(s[:4], "re: ") {
+		return "Re: " + s
+	}
+	return s
+}
+
+func rcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil || dot.Msg.Hdr == nil {
+		fmt.Fprintf(bout, "!nothing to reply to\n")
+		return nil
+	}
+
+	h := dot.Msg.Hdr
+	replyTo := h.ReplyTo
+	have := make(map[string]bool)
+	if len(replyTo) == 0 {
+		replyTo = h.From
+	}
+	if c.Name[0] == 'a' {
+		for _, a := range replyTo {
+			have[a.Email] = true
+		}
+		for _, a := range append(append(append([]imap.Addr(nil), h.From...), h.To...), h.CC...) {
+			if !have[a.Email] {
+				have[a.Email] = true
+				replyTo = append(replyTo, a)
+			}
+		}
+	}
+	if len(replyTo) == 0 {
+		fmt.Fprintf(bout, "!no one to reply to\n")
+		return dot
+	}
+
+	args := []string{"-a", acct.Email, "-s", addre(h.Subject), "-in-reply-to", h.MessageID}
+	fmt.Fprintf(bout, "replying to:")
+	for _, a := range replyTo {
+		fmt.Fprintf(bout, " %s", a.Email)
+		args = append(args, "-to", a.String())
+	}
+	for _, arg := range c.Args[1:] {
+		fmt.Fprintf(bout, " %s", arg)
+		args = append(args, "-to", arg)
+	}
+	fmt.Fprintf(bout, "\n")
+	bout.Flush()
+	cmd := exec.Command("gmailsend", args...)
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	err := cmd.Run()
+	if err != nil {
+		fmt.Fprintf(bout, "!%s\n", err)
+	}
+	return dot
+}
+
+func fcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		fmt.Fprintf(bout, "!nothing to forward\n")
+		return nil
+	}
+
+	return fwd(c, dot, nil)
+}
+
+func tfcmd(c *Cmd, msgs []*imap.Msg) *imap.MsgPart {
+	if len(msgs) == 0 {
+		fmt.Fprintf(bout, "!nothing to forward\n")
+		return nil
+	}
+
+	return fwd(c, &msgs[len(msgs)-1].Root, msgs)
+}
+
+func fwd(c *Cmd, dot *imap.MsgPart, msgs []*imap.Msg) *imap.MsgPart {
+	addrs := c.Args[1:]
+	if len(addrs) == 0 {
+		fmt.Fprintf(bout, "!f command requires address to forward to\n")
+		return dot
+	}
+
+	h := dot.Msg.Hdr
+	args := []string{"-a", acct.Email, "-s", "Fwd: " + h.Subject, "-append", "/dev/fd/3"}
+	fmt.Fprintf(bout, "forwarding to:")
+	for _, arg := range addrs {
+		fmt.Fprintf(bout, " %s", arg)
+		args = append(args, "-to", arg)
+	}
+	fmt.Fprintf(bout, "\n")
+	bout.Flush()
+
+	cmd := exec.Command("gmailsend", args...)
+	r, w, err := os.Pipe()
+	if err != nil {
+		fmt.Fprintf(bout, "!%s\n", err)
+		return dot
+	}
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	cmd.ExtraFiles = []*os.File{r}
+	if err := cmd.Start(); err != nil {
+		r.Close()
+		fmt.Fprintf(bout, "!%s\n", err)
+		return dot
+	}
+	r.Close()
+	what := "message"
+	if len(msgs) > 1 {
+		what = "conversation"
+	}
+	fmt.Fprintf(w, "\n\n--- Forwarded %s ---\n", what)
+	if msgs == nil {
+		wpcmd(w, c, dot)
+	} else {
+		for _, m := range msgs {
+			wpcmd(w, c, &m.Root)
+			fmt.Fprintf(w, "\n\n")
+		}
+	}
+	w.Close()
+	if err := cmd.Wait(); err != nil {
+		fmt.Fprintf(bout, "!%s\n", err)
+	}
+	return dot
+}
+
+func rethread() {
+	if !threaded {
+		sort.Sort(byUIDRev(msgs))
+	} else {
+		byThread := make(map[uint64][]*imap.Msg)
+		for _, m := range msgs {
+			t := m.GmailThread
+			byThread[t] = append(byThread[t], m)
+		}
+
+		var threadList [][]*imap.Msg
+		for _, t := range byThread {
+			sort.Sort(byUID(t))
+			threadList = append(threadList, t)
+		}
+		sort.Sort(byUIDList(threadList))
+
+		msgs = msgs[:0]
+		for _, t := range threadList {
+			msgs = append(msgs, t...)
+		}
+	}
+	for i, m := range msgs {
+		msgNum[m] = i
+	}
+}
+
+type byUID []*imap.Msg
+
+func (l byUID) Less(i, j int) bool { return l[i].UID < l[j].UID }
+func (l byUID) Len() int           { return len(l) }
+func (l byUID) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }
+
+type byUIDRev []*imap.Msg
+
+func (l byUIDRev) Less(i, j int) bool { return l[i].UID > l[j].UID }
+func (l byUIDRev) Len() int           { return len(l) }
+func (l byUIDRev) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }
+
+type byUIDList [][]*imap.Msg
+
+func (l byUIDList) Less(i, j int) bool { return l[i][len(l[i])-1].UID > l[j][len(l[j])-1].UID }
+func (l byUIDList) Len() int           { return len(l) }
+func (l byUIDList) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }
+
+func subj(m *imap.Msg) string {
+	s := m.Hdr.Subject
+	for strings.HasPrefix(s, "Re: ") || strings.HasPrefix(s, "RE: ") {
+		s = s[4:]
+	}
+	return s
+}
+
+func mcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	c.ArgLine = "Muted"
+	scmd(c, dot)
+	return dcmd(c, dot)
+}
+
+func tmcmd(c *Cmd, msgs []*imap.Msg) *imap.MsgPart {
+	c.ArgLine = "Muted"
+	tscmd(c, msgs)
+	return tdcmd(c, msgs)
+}
+
+func scmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		return nil
+	}
+	return tscmd(c, []*imap.Msg{dot.Msg})
+}
+
+func tscmd(c *Cmd, msgs []*imap.Msg) *imap.MsgPart {
+	if len(msgs) == 0 {
+		return nil
+	}
+	arg := c.ArgLine
+	dot := &msgs[len(msgs)-1].Root
+	if arg == "" {
+		fmt.Fprintf(bout, "!s needs mailbox (label) name as argument\n")
+		return dot
+	}
+	if strings.EqualFold(arg, "Muted") {
+		if err := dot.Msg.Box.Mute(msgs); err != nil {
+			fmt.Fprintf(bout, "!mute: %s\n", err)
+		}
+	} else {
+		dst := dot.Msg.Box.Client.Box(arg)
+		if dst == nil {
+			fmt.Fprintf(bout, "!unknown mailbox %#q", arg)
+			return dot
+		}
+		if err := dst.Copy(msgs); err != nil {
+			fmt.Fprintf(bout, "!s %#q: %s\n", arg, err)
+		}
+	}
+	return dot
+}
+
+func Wcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	if dot == nil {
+		return nil
+	}
+	if !isGmail {
+		fmt.Fprintf(bout, "!cmd W requires gmail\n")
+		return dot
+	}
+	url := fmt.Sprintf("https://mail.google.com/mail/b/%s/?shva=1#inbox/%x", acct.Email, dot.Msg.GmailThread)
+	if err := exec.Command("open", url).Run(); err != nil {
+		fmt.Fprintf(bout, "!%s\n", err)
+	}
+	return dot
+}
+
+func xcmd(c *Cmd, dot *imap.MsgPart) *imap.MsgPart {
+	// TODO: remove saved attachments?
+	os.Exit(0)
+	panic("not reached")
+}
+
+func flushDeleted() {
+	var toDelete []*imap.Msg
+	for m := range deleted {
+		toDelete = append(toDelete, m)
+	}
+	if len(toDelete) == 0 {
+		return
+	}
+	fmt.Fprintf(os.Stderr, "!deleting %d\n", len(toDelete))
+	err := inbox.Delete(toDelete)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "!deleting: %s\n", err)
+	}
+}
+
+func loadNew() {
+	if err := inbox.Check(); err != nil {
+		fmt.Fprintf(os.Stderr, "!inbox: %s\n", err)
+	}
+
+	old := make(map[*imap.Msg]bool)
+	for _, m := range msgs {
+		old[m] = true
+	}
+
+	nnew := 0
+	new := inbox.Msgs()
+	for _, m := range new {
+		if old[m] {
+			delete(old, m)
+		} else {
+			msgs = append(msgs, m)
+			nnew++
+		}
+	}
+	if nnew > 0 {
+		fmt.Fprintf(os.Stderr, "!%d new messages\n", nnew)
+	}
+	for m := range old {
+		// Deleted
+		m.Flags |= imap.FlagDeleted
+		delete(deleted, m)
+	}
+}
+
+func sync(delete bool) {
+	if delete {
+		flushDeleted()
+	}
+	loadNew()
+	if delete {
+		w := 0
+		for _, m := range msgs {
+			if !m.Deleted() {
+				msgs[w] = m
+				w++
+			}
+		}
+		msgs = msgs[:w]
+	}
+	rethread()
+}
diff --git a/vendor/github.com/mattermost/rsc/google/gmailsend/send.go b/vendor/github.com/mattermost/rsc/google/gmailsend/send.go
new file mode 100644
index 000000000..ace6eb12b
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/google/gmailsend/send.go
@@ -0,0 +1,370 @@
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/base64"
+	"flag"
+	"fmt"
+	"io"
+	"net/smtp"
+	"os"
+	"regexp"
+	"strings"
+
+	"github.com/mattermost/rsc/google"
+)
+
+func enc(s string) string {
+	// TODO =? .. ?=
+	return s
+}
+
+type Addr struct {
+	Name  string
+	Email string
+}
+
+func (a Addr) enc() string {
+	if a.Name == "" {
+		return "<" + a.Email + ">"
+	}
+	if a.Email == "" {
+		return enc(a.Name) + ":;"
+	}
+	return enc(a.Name) + " <" + a.Email + ">"
+}
+
+type Addrs []Addr
+
+func (a *Addrs) String() string {
+	return "[addrlist]"
+}
+
+func (a Addrs) has(s string) bool {
+	for _, aa := range a {
+		if aa.Email == s {
+			return true
+		}
+	}
+	return false
+}
+
+func (a *Addrs) Set(s string) bool {
+	s = strings.TrimSpace(s)
+	if strings.HasSuffix(s, ">") {
+		j := strings.LastIndex(s, "<")
+		if j >= 0 {
+			*a = append(*a, Addr{strings.TrimSpace(s[:j]), s[j+1 : len(s)-1]})
+			return true
+		}
+	}
+
+	if strings.Contains(s, " ") {
+		fmt.Fprintf(os.Stderr, "invalid address: %s", s)
+		os.Exit(2)
+	}
+	*a = append(*a, Addr{"", s})
+	return true
+}
+
+func (a *Addrs) parseLine(s string) {
+	for _, f := range strings.Split(s, ",") {
+		f = strings.TrimSpace(f)
+		if f != "" {
+			a.Set(f)
+		}
+	}
+}
+
+func (a Addrs) fixDomain() {
+	i := strings.Index(acct.Email, "@")
+	if i < 0 {
+		return
+	}
+	dom := acct.Email[i:]
+	for i := range a {
+		if a[i].Email != "" && !strings.Contains(a[i].Email, "@") {
+			a[i].Email += dom
+		}
+	}
+}
+
+var from, to, cc, bcc, replyTo Addrs
+var inReplyTo, subject string
+var appendFile = flag.String("append", "", "file to append to end of body")
+
+var acct google.Account
+var acctName = flag.String("a", "", "account to use")
+var inputHeader = flag.Bool("i", false, "read additional header lines from stdin")
+
+func holdmode() {
+	if os.Getenv("TERM") == "9term" {
+		// forgive me
+		os.Stdout.WriteString("\x1B];*9term-hold+\x07")
+	}
+}
+
+func match(line, prefix string, arg *string) bool {
+	if len(line) < len(prefix) || !strings.EqualFold(line[:len(prefix)], prefix) {
+		return false
+	}
+	*arg = strings.TrimSpace(line[len(prefix):])
+	return true
+}
+
+func main() {
+	flag.StringVar(&inReplyTo, "in-reply-to", "", "In-Reply-To")
+	flag.StringVar(&subject, "s", "", "Subject")
+	flag.Var(&from, "from", "From (can repeat)")
+	flag.Var(&to, "to", "To (can repeat)")
+	flag.Var(&cc, "cc", "CC (can repeat)")
+	flag.Var(&bcc, "bcc", "BCC (can repeat)")
+	flag.Var(&replyTo, "replyTo", "Reply-To (can repeat)")
+
+	flag.Parse()
+	if flag.NArg() != 0 && !*inputHeader {
+		flag.Usage()
+	}
+
+	var body bytes.Buffer
+	input := bufio.NewReader(os.Stdin)
+	if *inputHeader {
+		holdmode()
+	Loop:
+		for {
+			s, err := input.ReadString('\n')
+			if err != nil {
+				if err == io.EOF {
+					break Loop
+				}
+				fmt.Fprintf(os.Stderr, "reading stdin: %s\n", err)
+				os.Exit(2)
+			}
+			var arg string
+			switch {
+			default:
+				if ok, _ := regexp.MatchString(`^\S+:`, s); ok {
+					fmt.Fprintf(os.Stderr, "unknown header line: %s", s)
+					os.Exit(2)
+				}
+				body.WriteString(s)
+				break Loop
+			case match(s, "from:", &arg):
+				from.parseLine(arg)
+			case match(s, "to:", &arg):
+				to.parseLine(arg)
+			case match(s, "cc:", &arg):
+				cc.parseLine(arg)
+			case match(s, "bcc:", &arg):
+				bcc.parseLine(arg)
+			case match(s, "reply-to:", &arg):
+				replyTo.parseLine(arg)
+			case match(s, "subject:", &arg):
+				subject = arg
+			case match(s, "in-reply-to:", &arg):
+				inReplyTo = arg
+			}
+		}
+	}
+
+	acct = google.Acct(*acctName)
+	from.fixDomain()
+	to.fixDomain()
+	cc.fixDomain()
+	bcc.fixDomain()
+	replyTo.fixDomain()
+
+	smtpTo := append(append(to, cc...), bcc...)
+
+	if len(from) == 0 {
+		// TODO: Much better
+		name := ""
+		email := acct.Email
+		if email == "rsc@swtch.com" || email == "rsc@google.com" {
+			name = "Russ Cox"
+		}
+		if email == "rsc@google.com" && (smtpTo.has("go@googlecode.com") || smtpTo.has("golang-dev@googlegroups.com") || smtpTo.has("golang-nuts@googlegroups.com")) {
+			from = append(from, Addr{name, "rsc@golang.org"})
+		} else {
+			from = append(from, Addr{name, email})
+		}
+	}
+
+	if len(from) > 1 {
+		fmt.Fprintf(os.Stderr, "missing -from\n")
+		os.Exit(2)
+	}
+
+	if len(to)+len(cc)+len(bcc) == 0 {
+		fmt.Fprintf(os.Stderr, "missing destinations\n")
+		os.Exit(2)
+	}
+
+	if !*inputHeader {
+		holdmode()
+	}
+	_, err := io.Copy(&body, input)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "reading stdin: %s\n", err)
+		os.Exit(2)
+	}
+
+	if *appendFile != "" {
+		f, err := os.Open(*appendFile)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "append: %s\n", err)
+			os.Exit(2)
+		}
+		_, err = io.Copy(&body, f)
+		f.Close()
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "append: %s\n", err)
+			os.Exit(2)
+		}
+	}
+
+	var msg bytes.Buffer
+	fmt.Fprintf(&msg, "MIME-Version: 1.0\n")
+	if len(from) > 0 {
+		fmt.Fprintf(&msg, "From: ")
+		for i, a := range from {
+			if i > 0 {
+				fmt.Fprintf(&msg, ", ")
+			}
+			fmt.Fprintf(&msg, "%s", a.enc())
+		}
+		fmt.Fprintf(&msg, "\n")
+	}
+	if len(to) > 0 {
+		fmt.Fprintf(&msg, "To: ")
+		for i, a := range to {
+			if i > 0 {
+				fmt.Fprintf(&msg, ", ")
+			}
+			fmt.Fprintf(&msg, "%s", a.enc())
+		}
+		fmt.Fprintf(&msg, "\n")
+	}
+	if len(cc) > 0 {
+		fmt.Fprintf(&msg, "CC: ")
+		for i, a := range cc {
+			if i > 0 {
+				fmt.Fprintf(&msg, ", ")
+			}
+			fmt.Fprintf(&msg, "%s", a.enc())
+		}
+		fmt.Fprintf(&msg, "\n")
+	}
+	if len(replyTo) > 0 {
+		fmt.Fprintf(&msg, "Reply-To: ")
+		for i, a := range replyTo {
+			if i > 0 {
+				fmt.Fprintf(&msg, ", ")
+			}
+			fmt.Fprintf(&msg, "%s", a.enc())
+		}
+		fmt.Fprintf(&msg, "\n")
+	}
+	if inReplyTo != "" {
+		fmt.Fprintf(&msg, "In-Reply-To: %s\n", inReplyTo)
+	}
+	if subject != "" {
+		fmt.Fprintf(&msg, "Subject: %s\n", enc(subject))
+	}
+	fmt.Fprintf(&msg, "Date: xxx\n")
+	fmt.Fprintf(&msg, "Content-Type: text/plain; charset=\"utf-8\"\n")
+	fmt.Fprintf(&msg, "Content-Transfer-Encoding: base64\n")
+	fmt.Fprintf(&msg, "\n")
+	enc64 := base64.StdEncoding.EncodeToString(body.Bytes())
+	for len(enc64) > 72 {
+		fmt.Fprintf(&msg, "%s\n", enc64[:72])
+		enc64 = enc64[72:]
+	}
+	fmt.Fprintf(&msg, "%s\n\n", enc64)
+
+	auth := smtp.PlainAuth(
+		"",
+		acct.Email,
+		acct.Password,
+		"smtp.gmail.com",
+	)
+	var smtpToEmail []string
+	for _, a := range smtpTo {
+		if a.Email != "" {
+			smtpToEmail = append(smtpToEmail, a.Email)
+		}
+	}
+
+	if err := sendMail("smtp.gmail.com:587", auth, from[0].Email, smtpToEmail, msg.Bytes()); err != nil {
+		fmt.Fprintf(os.Stderr, "sending mail: %s\n", err)
+		os.Exit(2)
+	}
+}
+
+/*
+                                                                                                                                                                                                                                                    MIME-Version: 1.0
+Subject: commit/plan9port: rsc: 9term: hold mode back door
+From: Bitbucket 
+To: plan9port-dev@googlegroups.com
+Date: Tue, 11 Oct 2011 13:34:30 -0000
+Message-ID: <20111011133430.31146.55070@bitbucket13.managed.contegix.com>
+Reply-To: commits-noreply@bitbucket.org
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: quoted-printable
+
+1 new changeset in plan9port:
+
+http://bitbucket.org/rsc/plan9port/changeset/8735d7708a1b/
+changeset:   8735d7708a1b
+user:        rsc
+date:        2011-10-11 15:34:25
+summary:     9term: hold mode back door
+
+R=3Drsc
+http://codereview.appspot.com/5248056
+affected #:  2 files (-1 bytes)
+
+Repository URL: https://bitbucket.org/rsc/plan9port/
+
+--
+
+This is a commit notification from bitbucket.org. You are receiving
+this because you have the service enabled, addressing the recipient of
+this email.
+
+*/
+
+func sendMail(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
+	c, err := smtp.Dial(addr)
+	if err != nil {
+		return err
+	}
+	if err = c.StartTLS(nil); err != nil {
+		return err
+	}
+	if err = c.Auth(a); err != nil {
+		return err
+	}
+	if err = c.Mail(from); err != nil {
+		return err
+	}
+	for _, addr := range to {
+		if err = c.Rcpt(addr); err != nil {
+			return err
+		}
+	}
+	w, err := c.Data()
+	if err != nil {
+		return err
+	}
+	_, err = w.Write(msg)
+	if err != nil {
+		return err
+	}
+	err = w.Close()
+	if err != nil {
+		return err
+	}
+	return c.Quit()
+}
diff --git a/vendor/github.com/mattermost/rsc/google/googleserver/chat.go b/vendor/github.com/mattermost/rsc/google/googleserver/chat.go
new file mode 100644
index 000000000..8b064dc92
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/google/googleserver/chat.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.
+
+// TODO: Add ChatHangup.
+// TODO: Auto-hangup chats that are gone.
+
+package main
+
+import (
+	"fmt"
+
+	"github.com/mattermost/rsc/google"
+	"github.com/mattermost/rsc/xmpp"
+)
+
+type chatClient struct {
+	email string
+	id    string
+	xmpp  *xmpp.Client
+}
+
+var chatClients = map[string]*chatClient{}
+
+func (*Server) chatClient(cid *google.ChatID) (*chatClient, error) {
+	id := cid.ID
+	cc := chatClients[cid.ID]
+	if cc == nil {
+		a := google.Cfg.AccountByEmail(cid.Email)
+		if a == nil {
+			return nil, fmt.Errorf("unknown account %s", cid.Email)
+		}
+		// New client.
+		cli, err := xmpp.NewClient("talk.google.com:443", a.Email, a.Password)
+		if err != nil {
+			return nil, err
+		}
+		cc = &chatClient{email: a.Email, id: id, xmpp: cli}
+		cc.xmpp.Status(cid.Status, cid.StatusMsg)
+		chatClients[id] = cc
+	}
+	return cc, nil
+}
+
+func (srv *Server) ChatRecv(cid *google.ChatID, msg *xmpp.Chat) error {
+	cc, err := srv.chatClient(cid)
+	if err != nil {
+		return err
+	}
+	chat, err := cc.xmpp.Recv()
+	if err != nil {
+		return err
+	}
+	*msg = chat
+	return nil
+}
+
+func (srv *Server) ChatStatus(cid *google.ChatID, _ *Empty) error {
+	cc, err := srv.chatClient(cid)
+	if err != nil {
+		return err
+	}
+	return cc.xmpp.Status(cid.Status, cid.StatusMsg)
+}
+
+func (srv *Server) ChatSend(arg *google.ChatSend, _ *Empty) error {
+	cc, err := srv.chatClient(arg.ID)
+	if err != nil {
+		return err
+	}
+	return cc.xmpp.Send(arg.Msg)
+}
+
+func (srv *Server) ChatRoster(cid *google.ChatID, _ *Empty) error {
+	cc, err := srv.chatClient(cid)
+	if err != nil {
+		return err
+	}
+	return cc.xmpp.Roster()
+}
diff --git a/vendor/github.com/mattermost/rsc/google/googleserver/main.go b/vendor/github.com/mattermost/rsc/google/googleserver/main.go
new file mode 100644
index 000000000..2cd022446
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/google/googleserver/main.go
@@ -0,0 +1,139 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	//	"flag"
+	"bufio"
+	"fmt"
+	"log"
+	"net"
+	"net/rpc"
+	"os"
+	"strings"
+	"syscall"
+
+	"github.com/mattermost/rsc/google"
+	"github.com/mattermost/rsc/xmpp"
+)
+
+func main() {
+	google.ReadConfig()
+	switch os.Args[1] {
+	case "add":
+		google.Cfg.Account = append(google.Cfg.Account, &google.Account{Email: os.Args[2], Password: os.Args[3]})
+		google.WriteConfig()
+	case "serve":
+		serve()
+	case "accounts":
+		c, err := google.Dial()
+		if err != nil {
+			log.Fatal(err)
+		}
+		out, err := c.Accounts()
+		if err != nil {
+			log.Fatal(err)
+		}
+		for _, email := range out {
+			fmt.Printf("%s\n", email)
+		}
+	case "ping":
+		c, err := google.Dial()
+		if err != nil {
+			log.Fatal(err)
+		}
+		if err := c.Ping(); err != nil {
+			log.Fatal(err)
+		}
+	case "chat":
+		c, err := google.Dial()
+		if err != nil {
+			log.Fatal(err)
+		}
+		cid := &google.ChatID{ID: "1", Email: os.Args[2], Status: xmpp.Available, StatusMsg: ""}
+		go chatRecv(c, cid)
+		c.ChatRoster(cid)
+		b := bufio.NewReader(os.Stdin)
+		for {
+			line, err := b.ReadString('\n')
+			if err != nil {
+				log.Fatal(err)
+			}
+			line = line[:len(line)-1]
+			i := strings.Index(line, ": ")
+			if i < 0 {
+				log.Printf(": , please")
+				continue
+			}
+			who, msg := line[:i], line[i+2:]
+			if err := c.ChatSend(cid, &xmpp.Chat{Remote: who, Type: "chat", Text: msg}); err != nil {
+				log.Fatal(err)
+			}
+		}
+	}
+}
+
+func chatRecv(c *google.Client, cid *google.ChatID) {
+	for {
+		msg, err := c.ChatRecv(cid)
+		if err != nil {
+			log.Fatal(err)
+		}
+		switch msg.Type {
+		case "roster":
+			for _, contact := range msg.Roster {
+				fmt.Printf("%v\n", contact)
+			}
+		case "presence":
+			fmt.Printf("%v\n", msg.Presence)
+		case "chat":
+			fmt.Printf("%s: %s\n", msg.Remote, msg.Text)
+		default:
+			fmt.Printf("<%s>\n", msg.Type)
+		}
+	}
+}
+
+func listen() net.Listener {
+	socket := google.Dir() + "/socket"
+	os.Remove(socket)
+	l, err := net.Listen("unix", socket)
+	if err != nil {
+		log.Fatal(err)
+	}
+	return l
+}
+
+func serve() {
+	f, err := os.OpenFile(google.Dir()+"/log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.SetOutput(f)
+	syscall.Dup2(f.Fd(), 2)
+	os.Stdout = f
+	os.Stderr = f
+	l := listen()
+	rpc.RegisterName("goog", &Server{})
+	rpc.Accept(l)
+	log.Fatal("rpc.Accept finished: server exiting")
+}
+
+type Server struct{}
+
+type Empty google.Empty
+
+func (*Server) Ping(*Empty, *Empty) error {
+	return nil
+}
+
+func (*Server) Accounts(_ *Empty, out *[]string) error {
+	var email []string
+	for _, a := range google.Cfg.Account {
+		email = append(email, a.Email)
+	}
+	*out = email
+	return nil
+}
diff --git a/vendor/github.com/mattermost/rsc/google/main.go b/vendor/github.com/mattermost/rsc/google/main.go
new file mode 100644
index 000000000..b11a6eefb
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/google/main.go
@@ -0,0 +1,181 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TODO: Something about redialing.
+
+package google
+
+import (
+	//	"flag"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net"
+	"net/rpc"
+	"os"
+	"os/exec"
+	"syscall"
+	"time"
+)
+
+func Dir() string {
+	dir := os.Getenv("HOME") + "/.goog"
+	st, err := os.Stat(dir)
+	if err != nil {
+		if err := os.Mkdir(dir, 0700); err != nil {
+			log.Fatal(err)
+		}
+		st, err = os.Stat(dir)
+		if err != nil {
+			log.Fatal(err)
+		}
+	}
+	if !st.IsDir() {
+		log.Fatalf("%s exists but is not a directory", dir)
+	}
+	if st.Mode()&0077 != 0 {
+		log.Fatalf("%s exists but allows group or other permissions: %#o", dir, st.Mode()&0777)
+	}
+	return dir
+}
+
+func Dial() (*Client, error) {
+	socket := Dir() + "/socket"
+	c, err := net.Dial("unix", socket)
+	if err == nil {
+		return &Client{rpc.NewClient(c)}, nil
+	}
+	log.Print("starting server")
+	os.Remove(socket)
+	runServer()
+	for i := 0; i < 50; i++ {
+		c, err = net.Dial("unix", socket)
+		if err == nil {
+			return &Client{rpc.NewClient(c)}, nil
+		}
+		time.Sleep(200e6)
+		if i == 0 {
+			log.Print("waiting for server...")
+		}
+	}
+	return nil, err
+}
+
+type Client struct {
+	client *rpc.Client
+}
+
+type Empty struct{}
+
+func (g *Client) Ping() error {
+	return g.client.Call("goog.Ping", &Empty{}, &Empty{})
+}
+
+func (g *Client) Accounts() ([]string, error) {
+	var out []string
+	if err := g.client.Call("goog.Accounts", &Empty{}, &out); err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func runServer() {
+	cmd := exec.Command("googleserver", "serve")
+	cmd.SysProcAttr = &syscall.SysProcAttr{}
+	if err := cmd.Start(); err != nil {
+		log.Fatal(err)
+	}
+}
+
+type Config struct {
+	Account []*Account
+}
+
+type Account struct {
+	Email    string
+	Password string
+	Nick     string
+}
+
+func (cfg *Config) AccountByEmail(email string) *Account {
+	for _, a := range cfg.Account {
+		if a.Email == email {
+			return a
+		}
+	}
+	return nil
+}
+
+var Cfg Config
+
+func ReadConfig() {
+	file := Dir() + "/config"
+	st, err := os.Stat(file)
+	if err != nil {
+		return
+	}
+	if st.Mode()&0077 != 0 {
+		log.Fatalf("%s exists but allows group or other permissions: %#o", file, st.Mode()&0777)
+	}
+	data, err := ioutil.ReadFile(file)
+	if err != nil {
+		log.Fatal(err)
+	}
+	Cfg = Config{}
+	if err := json.Unmarshal(data, &Cfg); err != nil {
+		log.Fatal(err)
+	}
+}
+
+func WriteConfig() {
+	file := Dir() + "/config"
+	st, err := os.Stat(file)
+	if err != nil {
+		if err := ioutil.WriteFile(file, nil, 0600); err != nil {
+			log.Fatal(err)
+		}
+		st, err = os.Stat(file)
+		if err != nil {
+			log.Fatal(err)
+		}
+	}
+	if st.Mode()&0077 != 0 {
+		log.Fatalf("%s exists but allows group or other permissions: %#o", file, st.Mode()&0777)
+	}
+	data, err := json.MarshalIndent(&Cfg, "", "\t")
+	if err != nil {
+		log.Fatal(err)
+	}
+	if err := ioutil.WriteFile(file, data, 0600); err != nil {
+		log.Fatal(err)
+	}
+	st, err = os.Stat(file)
+	if err != nil {
+		log.Fatal(err)
+	}
+	if st.Mode()&0077 != 0 {
+		log.Fatalf("%s allows group or other permissions after writing: %#o", file, st.Mode()&0777)
+	}
+}
+
+func Acct(name string) Account {
+	ReadConfig()
+	if name == "" {
+		if len(Cfg.Account) == 0 {
+			fmt.Fprintf(os.Stderr, "no accounts configured\n")
+			os.Exit(2)
+		}
+		return *Cfg.Account[0]
+	}
+
+	for _, a := range Cfg.Account {
+		if a.Email == name || a.Nick == name {
+			return *a
+		}
+	}
+	fmt.Fprintf(os.Stderr, "cannot find account %#q", name)
+	os.Exit(2)
+	panic("not reached")
+}
diff --git a/vendor/github.com/mattermost/rsc/gtfs/gtfs.pb.go b/vendor/github.com/mattermost/rsc/gtfs/gtfs.pb.go
new file mode 100644
index 000000000..7a91a74d5
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/gtfs/gtfs.pb.go
@@ -0,0 +1,440 @@
+// Code generated by protoc-gen-go from "crashme/gtfs.proto"
+// DO NOT EDIT!
+
+package gtfs
+
+import proto "code.google.com/p/goprotobuf/proto"
+import "math"
+
+// Reference proto, math & os imports to suppress error if they are not otherwise used.
+var _ = proto.GetString
+var _ = math.Inf
+var _ error
+
+type FeedHeader_Incrementality int32
+
+const (
+	FeedHeader_FULL_DATASET FeedHeader_Incrementality = 0
+	FeedHeader_DIFFERENTIAL FeedHeader_Incrementality = 1
+)
+
+var FeedHeader_Incrementality_name = map[int32]string{
+	0: "FULL_DATASET",
+	1: "DIFFERENTIAL",
+}
+var FeedHeader_Incrementality_value = map[string]int32{
+	"FULL_DATASET": 0,
+	"DIFFERENTIAL": 1,
+}
+
+func NewFeedHeader_Incrementality(x FeedHeader_Incrementality) *FeedHeader_Incrementality {
+	e := FeedHeader_Incrementality(x)
+	return &e
+}
+func (x FeedHeader_Incrementality) String() string {
+	return proto.EnumName(FeedHeader_Incrementality_name, int32(x))
+}
+
+type TripUpdate_StopTimeUpdate_ScheduleRelationship int32
+
+const (
+	TripUpdate_StopTimeUpdate_SCHEDULED TripUpdate_StopTimeUpdate_ScheduleRelationship = 0
+	TripUpdate_StopTimeUpdate_SKIPPED   TripUpdate_StopTimeUpdate_ScheduleRelationship = 1
+	TripUpdate_StopTimeUpdate_NO_DATA   TripUpdate_StopTimeUpdate_ScheduleRelationship = 2
+)
+
+var TripUpdate_StopTimeUpdate_ScheduleRelationship_name = map[int32]string{
+	0: "SCHEDULED",
+	1: "SKIPPED",
+	2: "NO_DATA",
+}
+var TripUpdate_StopTimeUpdate_ScheduleRelationship_value = map[string]int32{
+	"SCHEDULED": 0,
+	"SKIPPED":   1,
+	"NO_DATA":   2,
+}
+
+func NewTripUpdate_StopTimeUpdate_ScheduleRelationship(x TripUpdate_StopTimeUpdate_ScheduleRelationship) *TripUpdate_StopTimeUpdate_ScheduleRelationship {
+	e := TripUpdate_StopTimeUpdate_ScheduleRelationship(x)
+	return &e
+}
+func (x TripUpdate_StopTimeUpdate_ScheduleRelationship) String() string {
+	return proto.EnumName(TripUpdate_StopTimeUpdate_ScheduleRelationship_name, int32(x))
+}
+
+type VehiclePosition_VehicleStopStatus int32
+
+const (
+	VehiclePosition_INCOMING_AT   VehiclePosition_VehicleStopStatus = 0
+	VehiclePosition_STOPPED_AT    VehiclePosition_VehicleStopStatus = 1
+	VehiclePosition_IN_TRANSIT_TO VehiclePosition_VehicleStopStatus = 2
+)
+
+var VehiclePosition_VehicleStopStatus_name = map[int32]string{
+	0: "INCOMING_AT",
+	1: "STOPPED_AT",
+	2: "IN_TRANSIT_TO",
+}
+var VehiclePosition_VehicleStopStatus_value = map[string]int32{
+	"INCOMING_AT":   0,
+	"STOPPED_AT":    1,
+	"IN_TRANSIT_TO": 2,
+}
+
+func NewVehiclePosition_VehicleStopStatus(x VehiclePosition_VehicleStopStatus) *VehiclePosition_VehicleStopStatus {
+	e := VehiclePosition_VehicleStopStatus(x)
+	return &e
+}
+func (x VehiclePosition_VehicleStopStatus) String() string {
+	return proto.EnumName(VehiclePosition_VehicleStopStatus_name, int32(x))
+}
+
+type VehiclePosition_CongestionLevel int32
+
+const (
+	VehiclePosition_UNKNOWN_CONGESTION_LEVEL VehiclePosition_CongestionLevel = 0
+	VehiclePosition_RUNNING_SMOOTHLY         VehiclePosition_CongestionLevel = 1
+	VehiclePosition_STOP_AND_GO              VehiclePosition_CongestionLevel = 2
+	VehiclePosition_CONGESTION               VehiclePosition_CongestionLevel = 3
+	VehiclePosition_SEVERE_CONGESTION        VehiclePosition_CongestionLevel = 4
+)
+
+var VehiclePosition_CongestionLevel_name = map[int32]string{
+	0: "UNKNOWN_CONGESTION_LEVEL",
+	1: "RUNNING_SMOOTHLY",
+	2: "STOP_AND_GO",
+	3: "CONGESTION",
+	4: "SEVERE_CONGESTION",
+}
+var VehiclePosition_CongestionLevel_value = map[string]int32{
+	"UNKNOWN_CONGESTION_LEVEL": 0,
+	"RUNNING_SMOOTHLY":         1,
+	"STOP_AND_GO":              2,
+	"CONGESTION":               3,
+	"SEVERE_CONGESTION":        4,
+}
+
+func NewVehiclePosition_CongestionLevel(x VehiclePosition_CongestionLevel) *VehiclePosition_CongestionLevel {
+	e := VehiclePosition_CongestionLevel(x)
+	return &e
+}
+func (x VehiclePosition_CongestionLevel) String() string {
+	return proto.EnumName(VehiclePosition_CongestionLevel_name, int32(x))
+}
+
+type Alert_Cause int32
+
+const (
+	Alert_UNKNOWN_CAUSE     Alert_Cause = 1
+	Alert_OTHER_CAUSE       Alert_Cause = 2
+	Alert_TECHNICAL_PROBLEM Alert_Cause = 3
+	Alert_STRIKE            Alert_Cause = 4
+	Alert_DEMONSTRATION     Alert_Cause = 5
+	Alert_ACCIDENT          Alert_Cause = 6
+	Alert_HOLIDAY           Alert_Cause = 7
+	Alert_WEATHER           Alert_Cause = 8
+	Alert_MAINTENANCE       Alert_Cause = 9
+	Alert_CONSTRUCTION      Alert_Cause = 10
+	Alert_POLICE_ACTIVITY   Alert_Cause = 11
+	Alert_MEDICAL_EMERGENCY Alert_Cause = 12
+)
+
+var Alert_Cause_name = map[int32]string{
+	1:  "UNKNOWN_CAUSE",
+	2:  "OTHER_CAUSE",
+	3:  "TECHNICAL_PROBLEM",
+	4:  "STRIKE",
+	5:  "DEMONSTRATION",
+	6:  "ACCIDENT",
+	7:  "HOLIDAY",
+	8:  "WEATHER",
+	9:  "MAINTENANCE",
+	10: "CONSTRUCTION",
+	11: "POLICE_ACTIVITY",
+	12: "MEDICAL_EMERGENCY",
+}
+var Alert_Cause_value = map[string]int32{
+	"UNKNOWN_CAUSE":     1,
+	"OTHER_CAUSE":       2,
+	"TECHNICAL_PROBLEM": 3,
+	"STRIKE":            4,
+	"DEMONSTRATION":     5,
+	"ACCIDENT":          6,
+	"HOLIDAY":           7,
+	"WEATHER":           8,
+	"MAINTENANCE":       9,
+	"CONSTRUCTION":      10,
+	"POLICE_ACTIVITY":   11,
+	"MEDICAL_EMERGENCY": 12,
+}
+
+func NewAlert_Cause(x Alert_Cause) *Alert_Cause {
+	e := Alert_Cause(x)
+	return &e
+}
+func (x Alert_Cause) String() string {
+	return proto.EnumName(Alert_Cause_name, int32(x))
+}
+
+type Alert_Effect int32
+
+const (
+	Alert_NO_SERVICE         Alert_Effect = 1
+	Alert_REDUCED_SERVICE    Alert_Effect = 2
+	Alert_SIGNIFICANT_DELAYS Alert_Effect = 3
+	Alert_DETOUR             Alert_Effect = 4
+	Alert_ADDITIONAL_SERVICE Alert_Effect = 5
+	Alert_MODIFIED_SERVICE   Alert_Effect = 6
+	Alert_OTHER_EFFECT       Alert_Effect = 7
+	Alert_UNKNOWN_EFFECT     Alert_Effect = 8
+	Alert_STOP_MOVED         Alert_Effect = 9
+)
+
+var Alert_Effect_name = map[int32]string{
+	1: "NO_SERVICE",
+	2: "REDUCED_SERVICE",
+	3: "SIGNIFICANT_DELAYS",
+	4: "DETOUR",
+	5: "ADDITIONAL_SERVICE",
+	6: "MODIFIED_SERVICE",
+	7: "OTHER_EFFECT",
+	8: "UNKNOWN_EFFECT",
+	9: "STOP_MOVED",
+}
+var Alert_Effect_value = map[string]int32{
+	"NO_SERVICE":         1,
+	"REDUCED_SERVICE":    2,
+	"SIGNIFICANT_DELAYS": 3,
+	"DETOUR":             4,
+	"ADDITIONAL_SERVICE": 5,
+	"MODIFIED_SERVICE":   6,
+	"OTHER_EFFECT":       7,
+	"UNKNOWN_EFFECT":     8,
+	"STOP_MOVED":         9,
+}
+
+func NewAlert_Effect(x Alert_Effect) *Alert_Effect {
+	e := Alert_Effect(x)
+	return &e
+}
+func (x Alert_Effect) String() string {
+	return proto.EnumName(Alert_Effect_name, int32(x))
+}
+
+type TripDescriptor_ScheduleRelationship int32
+
+const (
+	TripDescriptor_SCHEDULED   TripDescriptor_ScheduleRelationship = 0
+	TripDescriptor_ADDED       TripDescriptor_ScheduleRelationship = 1
+	TripDescriptor_UNSCHEDULED TripDescriptor_ScheduleRelationship = 2
+	TripDescriptor_CANCELED    TripDescriptor_ScheduleRelationship = 3
+	TripDescriptor_REPLACEMENT TripDescriptor_ScheduleRelationship = 5
+)
+
+var TripDescriptor_ScheduleRelationship_name = map[int32]string{
+	0: "SCHEDULED",
+	1: "ADDED",
+	2: "UNSCHEDULED",
+	3: "CANCELED",
+	5: "REPLACEMENT",
+}
+var TripDescriptor_ScheduleRelationship_value = map[string]int32{
+	"SCHEDULED":   0,
+	"ADDED":       1,
+	"UNSCHEDULED": 2,
+	"CANCELED":    3,
+	"REPLACEMENT": 5,
+}
+
+func NewTripDescriptor_ScheduleRelationship(x TripDescriptor_ScheduleRelationship) *TripDescriptor_ScheduleRelationship {
+	e := TripDescriptor_ScheduleRelationship(x)
+	return &e
+}
+func (x TripDescriptor_ScheduleRelationship) String() string {
+	return proto.EnumName(TripDescriptor_ScheduleRelationship_name, int32(x))
+}
+
+type FeedMessage struct {
+	Header           *FeedHeader   `protobuf:"bytes,1,req,name=header" json:"header"`
+	Entity           []*FeedEntity `protobuf:"bytes,2,rep,name=entity" json:"entity"`
+	XXX_unrecognized []byte
+}
+
+func (this *FeedMessage) Reset()         { *this = FeedMessage{} }
+func (this *FeedMessage) String() string { return proto.CompactTextString(this) }
+
+type FeedHeader struct {
+	GtfsRealtimeVersion *string                    `protobuf:"bytes,1,req,name=gtfs_realtime_version" json:"gtfs_realtime_version"`
+	Incrementality      *FeedHeader_Incrementality `protobuf:"varint,2,opt,name=incrementality,enum=transit_realtime.FeedHeader_Incrementality,def=0" json:"incrementality"`
+	Timestamp           *uint64                    `protobuf:"varint,3,opt,name=timestamp" json:"timestamp"`
+	XXX_unrecognized    []byte
+}
+
+func (this *FeedHeader) Reset()         { *this = FeedHeader{} }
+func (this *FeedHeader) String() string { return proto.CompactTextString(this) }
+
+const Default_FeedHeader_Incrementality FeedHeader_Incrementality = FeedHeader_FULL_DATASET
+
+type FeedEntity struct {
+	Id               *string          `protobuf:"bytes,1,req,name=id" json:"id"`
+	IsDeleted        *bool            `protobuf:"varint,2,opt,name=is_deleted,def=0" json:"is_deleted"`
+	TripUpdate       *TripUpdate      `protobuf:"bytes,3,opt,name=trip_update" json:"trip_update"`
+	Vehicle          *VehiclePosition `protobuf:"bytes,4,opt,name=vehicle" json:"vehicle"`
+	Alert            *Alert           `protobuf:"bytes,5,opt,name=alert" json:"alert"`
+	XXX_unrecognized []byte
+}
+
+func (this *FeedEntity) Reset()         { *this = FeedEntity{} }
+func (this *FeedEntity) String() string { return proto.CompactTextString(this) }
+
+const Default_FeedEntity_IsDeleted bool = false
+
+type TripUpdate struct {
+	Trip             *TripDescriptor              `protobuf:"bytes,1,req,name=trip" json:"trip"`
+	Vehicle          *VehicleDescriptor           `protobuf:"bytes,3,opt,name=vehicle" json:"vehicle"`
+	StopTimeUpdate   []*TripUpdate_StopTimeUpdate `protobuf:"bytes,2,rep,name=stop_time_update" json:"stop_time_update"`
+	XXX_unrecognized []byte
+}
+
+func (this *TripUpdate) Reset()         { *this = TripUpdate{} }
+func (this *TripUpdate) String() string { return proto.CompactTextString(this) }
+
+type TripUpdate_StopTimeEvent struct {
+	Delay            *int32 `protobuf:"varint,1,opt,name=delay" json:"delay"`
+	Time             *int64 `protobuf:"varint,2,opt,name=time" json:"time"`
+	Uncertainty      *int32 `protobuf:"varint,3,opt,name=uncertainty" json:"uncertainty"`
+	XXX_unrecognized []byte
+}
+
+func (this *TripUpdate_StopTimeEvent) Reset()         { *this = TripUpdate_StopTimeEvent{} }
+func (this *TripUpdate_StopTimeEvent) String() string { return proto.CompactTextString(this) }
+
+type TripUpdate_StopTimeUpdate struct {
+	StopSequence         *uint32                                         `protobuf:"varint,1,opt,name=stop_sequence" json:"stop_sequence"`
+	StopId               *string                                         `protobuf:"bytes,4,opt,name=stop_id" json:"stop_id"`
+	Arrival              *TripUpdate_StopTimeEvent                       `protobuf:"bytes,2,opt,name=arrival" json:"arrival"`
+	Departure            *TripUpdate_StopTimeEvent                       `protobuf:"bytes,3,opt,name=departure" json:"departure"`
+	ScheduleRelationship *TripUpdate_StopTimeUpdate_ScheduleRelationship `protobuf:"varint,5,opt,name=schedule_relationship,enum=transit_realtime.TripUpdate_StopTimeUpdate_ScheduleRelationship,def=0" json:"schedule_relationship"`
+	XXX_unrecognized     []byte
+}
+
+func (this *TripUpdate_StopTimeUpdate) Reset()         { *this = TripUpdate_StopTimeUpdate{} }
+func (this *TripUpdate_StopTimeUpdate) String() string { return proto.CompactTextString(this) }
+
+const Default_TripUpdate_StopTimeUpdate_ScheduleRelationship TripUpdate_StopTimeUpdate_ScheduleRelationship = TripUpdate_StopTimeUpdate_SCHEDULED
+
+type VehiclePosition struct {
+	Trip                *TripDescriptor                    `protobuf:"bytes,1,opt,name=trip" json:"trip"`
+	Vehicle             *VehicleDescriptor                 `protobuf:"bytes,8,opt,name=vehicle" json:"vehicle"`
+	Position            *Position                          `protobuf:"bytes,2,opt,name=position" json:"position"`
+	CurrentStopSequence *uint32                            `protobuf:"varint,3,opt,name=current_stop_sequence" json:"current_stop_sequence"`
+	StopId              *string                            `protobuf:"bytes,7,opt,name=stop_id" json:"stop_id"`
+	CurrentStatus       *VehiclePosition_VehicleStopStatus `protobuf:"varint,4,opt,name=current_status,enum=transit_realtime.VehiclePosition_VehicleStopStatus,def=2" json:"current_status"`
+	Timestamp           *uint64                            `protobuf:"varint,5,opt,name=timestamp" json:"timestamp"`
+	CongestionLevel     *VehiclePosition_CongestionLevel   `protobuf:"varint,6,opt,name=congestion_level,enum=transit_realtime.VehiclePosition_CongestionLevel" json:"congestion_level"`
+	XXX_unrecognized    []byte
+}
+
+func (this *VehiclePosition) Reset()         { *this = VehiclePosition{} }
+func (this *VehiclePosition) String() string { return proto.CompactTextString(this) }
+
+const Default_VehiclePosition_CurrentStatus VehiclePosition_VehicleStopStatus = VehiclePosition_IN_TRANSIT_TO
+
+type Alert struct {
+	ActivePeriod     []*TimeRange      `protobuf:"bytes,1,rep,name=active_period" json:"active_period"`
+	InformedEntity   []*EntitySelector `protobuf:"bytes,5,rep,name=informed_entity" json:"informed_entity"`
+	Cause            *Alert_Cause      `protobuf:"varint,6,opt,name=cause,enum=transit_realtime.Alert_Cause,def=1" json:"cause"`
+	Effect           *Alert_Effect     `protobuf:"varint,7,opt,name=effect,enum=transit_realtime.Alert_Effect,def=8" json:"effect"`
+	Url              *TranslatedString `protobuf:"bytes,8,opt,name=url" json:"url"`
+	HeaderText       *TranslatedString `protobuf:"bytes,10,opt,name=header_text" json:"header_text"`
+	DescriptionText  *TranslatedString `protobuf:"bytes,11,opt,name=description_text" json:"description_text"`
+	XXX_unrecognized []byte
+}
+
+func (this *Alert) Reset()         { *this = Alert{} }
+func (this *Alert) String() string { return proto.CompactTextString(this) }
+
+const Default_Alert_Cause Alert_Cause = Alert_UNKNOWN_CAUSE
+const Default_Alert_Effect Alert_Effect = Alert_UNKNOWN_EFFECT
+
+type TimeRange struct {
+	Start            *uint64 `protobuf:"varint,1,opt,name=start" json:"start"`
+	End              *uint64 `protobuf:"varint,2,opt,name=end" json:"end"`
+	XXX_unrecognized []byte
+}
+
+func (this *TimeRange) Reset()         { *this = TimeRange{} }
+func (this *TimeRange) String() string { return proto.CompactTextString(this) }
+
+type Position struct {
+	Latitude         *float32 `protobuf:"fixed32,1,req,name=latitude" json:"latitude"`
+	Longitude        *float32 `protobuf:"fixed32,2,req,name=longitude" json:"longitude"`
+	Bearing          *float32 `protobuf:"fixed32,3,opt,name=bearing" json:"bearing"`
+	Odometer         *float64 `protobuf:"fixed64,4,opt,name=odometer" json:"odometer"`
+	Speed            *float32 `protobuf:"fixed32,5,opt,name=speed" json:"speed"`
+	XXX_unrecognized []byte
+}
+
+func (this *Position) Reset()         { *this = Position{} }
+func (this *Position) String() string { return proto.CompactTextString(this) }
+
+type TripDescriptor struct {
+	TripId               *string                              `protobuf:"bytes,1,opt,name=trip_id" json:"trip_id"`
+	RouteId              *string                              `protobuf:"bytes,5,opt,name=route_id" json:"route_id"`
+	StartTime            *string                              `protobuf:"bytes,2,opt,name=start_time" json:"start_time"`
+	StartDate            *string                              `protobuf:"bytes,3,opt,name=start_date" json:"start_date"`
+	ScheduleRelationship *TripDescriptor_ScheduleRelationship `protobuf:"varint,4,opt,name=schedule_relationship,enum=transit_realtime.TripDescriptor_ScheduleRelationship" json:"schedule_relationship"`
+	XXX_unrecognized     []byte
+}
+
+func (this *TripDescriptor) Reset()         { *this = TripDescriptor{} }
+func (this *TripDescriptor) String() string { return proto.CompactTextString(this) }
+
+type VehicleDescriptor struct {
+	Id               *string `protobuf:"bytes,1,opt,name=id" json:"id"`
+	Label            *string `protobuf:"bytes,2,opt,name=label" json:"label"`
+	LicensePlate     *string `protobuf:"bytes,3,opt,name=license_plate" json:"license_plate"`
+	XXX_unrecognized []byte
+}
+
+func (this *VehicleDescriptor) Reset()         { *this = VehicleDescriptor{} }
+func (this *VehicleDescriptor) String() string { return proto.CompactTextString(this) }
+
+type EntitySelector struct {
+	AgencyId         *string         `protobuf:"bytes,1,opt,name=agency_id" json:"agency_id"`
+	RouteId          *string         `protobuf:"bytes,2,opt,name=route_id" json:"route_id"`
+	RouteType        *int32          `protobuf:"varint,3,opt,name=route_type" json:"route_type"`
+	Trip             *TripDescriptor `protobuf:"bytes,4,opt,name=trip" json:"trip"`
+	StopId           *string         `protobuf:"bytes,5,opt,name=stop_id" json:"stop_id"`
+	XXX_unrecognized []byte
+}
+
+func (this *EntitySelector) Reset()         { *this = EntitySelector{} }
+func (this *EntitySelector) String() string { return proto.CompactTextString(this) }
+
+type TranslatedString struct {
+	Translation      []*TranslatedString_Translation `protobuf:"bytes,1,rep,name=translation" json:"translation"`
+	XXX_unrecognized []byte
+}
+
+func (this *TranslatedString) Reset()         { *this = TranslatedString{} }
+func (this *TranslatedString) String() string { return proto.CompactTextString(this) }
+
+type TranslatedString_Translation struct {
+	Text             *string `protobuf:"bytes,1,req,name=text" json:"text"`
+	Language         *string `protobuf:"bytes,2,opt,name=language" json:"language"`
+	XXX_unrecognized []byte
+}
+
+func (this *TranslatedString_Translation) Reset()         { *this = TranslatedString_Translation{} }
+func (this *TranslatedString_Translation) String() string { return proto.CompactTextString(this) }
+
+func init() {
+	proto.RegisterEnum("transit_realtime.FeedHeader_Incrementality", FeedHeader_Incrementality_name, FeedHeader_Incrementality_value)
+	proto.RegisterEnum("transit_realtime.TripUpdate_StopTimeUpdate_ScheduleRelationship", TripUpdate_StopTimeUpdate_ScheduleRelationship_name, TripUpdate_StopTimeUpdate_ScheduleRelationship_value)
+	proto.RegisterEnum("transit_realtime.VehiclePosition_VehicleStopStatus", VehiclePosition_VehicleStopStatus_name, VehiclePosition_VehicleStopStatus_value)
+	proto.RegisterEnum("transit_realtime.VehiclePosition_CongestionLevel", VehiclePosition_CongestionLevel_name, VehiclePosition_CongestionLevel_value)
+	proto.RegisterEnum("transit_realtime.Alert_Cause", Alert_Cause_name, Alert_Cause_value)
+	proto.RegisterEnum("transit_realtime.Alert_Effect", Alert_Effect_name, Alert_Effect_value)
+	proto.RegisterEnum("transit_realtime.TripDescriptor_ScheduleRelationship", TripDescriptor_ScheduleRelationship_name, TripDescriptor_ScheduleRelationship_value)
+}
diff --git a/vendor/github.com/mattermost/rsc/imap/Makefile b/vendor/github.com/mattermost/rsc/imap/Makefile
new file mode 100644
index 000000000..8dd3d2be4
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/imap/Makefile
@@ -0,0 +1,15 @@
+include $(GOROOT)/src/Make.inc
+
+# TARG=code.google.com/p/rsc/imap
+
+TARG=rsc.googlecode.com/hg/imap
+GOFILES=\
+	decode.go\
+	imap.go\
+	mail.go\
+	sx.go\
+	tcs.go\
+
+GCIMPORTS=-I$(GOPATH)/pkg/$(GOOS)_$(GOARCH)
+
+include $(GOROOT)/src/Make.pkg
diff --git a/vendor/github.com/mattermost/rsc/imap/decode.go b/vendor/github.com/mattermost/rsc/imap/decode.go
new file mode 100644
index 000000000..ebeb1d7d7
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/imap/decode.go
@@ -0,0 +1,227 @@
+package imap
+
+import (
+	"bytes"
+	"encoding/base64"
+	"strings"
+	"unicode"
+)
+
+func decode2047chunk(s string) (conv []byte, rest string, ok bool) {
+	// s is =?...
+	// and should be =?charset?e?text?=
+	j := strings.Index(s[2:], "?")
+	if j < 0 {
+		return
+	}
+	j += 2
+	if j+2 >= len(s) || s[j+2] != '?' {
+		return
+	}
+	k := strings.Index(s[j+3:], "?=")
+	if k < 0 {
+		return
+	}
+	k += j + 3
+
+	charset, enc, text, rest := s[2:j], s[j+1], s[j+3:k], s[k+2:]
+	var encoding string
+	switch enc {
+	default:
+		return
+	case 'q', 'Q':
+		encoding = "quoted-printable"
+	case 'b', 'B':
+		encoding = "base64"
+	}
+
+	dat := decodeText([]byte(text), encoding, charset, true)
+	if dat == nil {
+		return
+	}
+	return dat, rest, true
+}
+
+func decodeQP(dat []byte, underscore bool) []byte {
+	out := make([]byte, len(dat))
+	w := 0
+	for i := 0; i < len(dat); i++ {
+		c := dat[i]
+		if underscore && c == '_' {
+			out[w] = ' '
+			w++
+			continue
+		}
+		if c == '\r' {
+			continue
+		}
+		if c == '=' {
+			if i+1 < len(dat) && dat[i+1] == '\n' {
+				i++
+				continue
+			}
+			if i+2 < len(dat) && dat[i+1] == '\r' && dat[i+2] == '\n' {
+				i += 2
+				continue
+			}
+			if i+2 < len(dat) {
+				v := unhex(dat[i+1])<<4 | unhex(dat[i+2])
+				if v >= 0 {
+					out[w] = byte(v)
+					w++
+					i += 2
+					continue
+				}
+			}
+		}
+		out[w] = c
+		w++
+	}
+	return out[:w]
+}
+
+func nocrnl(dat []byte) []byte {
+	w := 0
+	for _, c := range dat {
+		if c != '\r' && c != '\n' {
+			dat[w] = c
+			w++
+		}
+	}
+	return dat[:w]
+}
+
+func decode64(dat []byte) []byte {
+	out := make([]byte, len(dat))
+	copy(out, dat)
+	out = nocrnl(out)
+	n, err := base64.StdEncoding.Decode(out, out)
+	if err != nil {
+		return nil
+	}
+	return out[:n]
+}
+
+func decodeText(dat []byte, encoding, charset string, underscore bool) []byte {
+	odat := dat
+	switch strlwr(encoding) {
+	case "quoted-printable":
+		dat = decodeQP(dat, underscore)
+	case "base64":
+		dat = decode64(dat)
+	}
+	if dat == nil {
+		return nil
+	}
+	if bytes.IndexByte(dat, '\r') >= 0 {
+		if &odat[0] == &dat[0] {
+			dat = append([]byte(nil), dat...)
+		}
+		dat = nocr(dat)
+	}
+
+	charset = strlwr(charset)
+	if charset == "utf-8" || charset == "us-ascii" {
+		return dat
+	}
+	if charset == "iso-8859-1" {
+		// Avoid allocation for iso-8859-1 that is really just ascii.
+		for _, c := range dat {
+			if c >= 0x80 {
+				goto NeedConv
+			}
+		}
+		return dat
+	NeedConv:
+	}
+
+	// TODO: big5, iso-2022-jp
+
+	tab := convtab[charset]
+	if tab == nil {
+		return dat
+	}
+	var b bytes.Buffer
+	for _, c := range dat {
+		if tab[c] < 0 {
+			b.WriteRune(unicode.ReplacementChar)
+		} else {
+			b.WriteRune(tab[c])
+		}
+	}
+	return b.Bytes()
+}
+
+var convtab = map[string]*[256]rune{
+	"iso-8859-1":   &tab_iso8859_1,
+	"iso-8859-2":   &tab_iso8859_2,
+	"iso-8859-3":   &tab_iso8859_3,
+	"iso-8859-4":   &tab_iso8859_4,
+	"iso-8859-5":   &tab_iso8859_5,
+	"iso-8859-6":   &tab_iso8859_6,
+	"iso-8859-7":   &tab_iso8859_7,
+	"iso-8859-8":   &tab_iso8859_8,
+	"iso-8859-9":   &tab_iso8859_9,
+	"iso-8859-10":  &tab_iso8859_10,
+	"iso-8859-15":  &tab_iso8859_15,
+	"koi8-r":       &tab_koi8,
+	"windows-1250": &tab_cp1250,
+	"windows-1251": &tab_cp1251,
+	"windows-1252": &tab_cp1252,
+	"windows-1253": &tab_cp1253,
+	"windows-1254": &tab_cp1254,
+	"windows-1255": &tab_cp1255,
+	"windows-1256": &tab_cp1256,
+	"windows-1257": &tab_cp1257,
+	"windows-1258": &tab_cp1258,
+}
+
+func unrfc2047(s string) string {
+	if !strings.Contains(s, "=?") {
+		return s
+	}
+	var buf bytes.Buffer
+	for {
+		// =?charset?e?text?=
+		i := strings.Index(s, "=?")
+		if i < 0 {
+			break
+		}
+		conv, rest, ok := decode2047chunk(s[i:])
+		if !ok {
+			buf.WriteString(s[:i+2])
+			s = s[i+2:]
+			continue
+		}
+		buf.WriteString(s[:i])
+		buf.Write(conv)
+		s = rest
+	}
+	buf.WriteString(s)
+	return buf.String()
+}
+
+func lwr(c rune) rune {
+	if 'A' <= c && c <= 'Z' {
+		return c + 'a' - 'A'
+	}
+	return c
+}
+
+func strlwr(s string) string {
+	return strings.Map(lwr, s)
+}
+
+func unhex(c byte) int {
+	switch {
+	case '0' <= c && c <= '9':
+		return int(c) - '0'
+	case 'a' <= c && c <= 'f':
+		return int(c) - 'a' + 10
+	case 'A' <= c && c <= 'F':
+		return int(c) - 'A' + 10
+	}
+	return -1
+}
+
+// TODO: Will need modified UTF-7 eventually.
diff --git a/vendor/github.com/mattermost/rsc/imap/decode_test.go b/vendor/github.com/mattermost/rsc/imap/decode_test.go
new file mode 100644
index 000000000..84c31f63a
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/imap/decode_test.go
@@ -0,0 +1,26 @@
+package imap
+
+import "testing"
+
+var unrfc2047Tests = []struct {
+	in, out string
+}{
+	{"hello world", "hello world"},
+	{"hello =?iso-8859-1?q?this is some text?=", "hello this is some text"},
+	{"=?US-ASCII?Q?Keith_Moore?=", "Keith Moore"},
+	{"=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?=", "Keld Jørn Simonsen"},
+	{"=?ISO-8859-1?Q?Andr=E9?= Pirard", "André Pirard"},
+	{"=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=", "If you can read this yo"},
+	{"=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=", "u understand the example."},
+	{"=?ISO-8859-1?Q?Olle_J=E4rnefors?=", "Olle Järnefors"},
+	//	{"=?iso-2022-jp?B?GyRCTTVKISRKP006SiRyS34kPyQ3JEZKcz03JCIkahsoQg==?=", ""},
+	{"=?UTF-8?B?Ik5pbHMgTy4gU2Vsw6VzZGFsIg==?=", `"Nils O. Selåsdal"`},
+}
+
+func TestUnrfc2047(t *testing.T) {
+	for _, tt := range unrfc2047Tests {
+		if out := unrfc2047(tt.in); out != tt.out {
+			t.Errorf("unrfc2047(%#q) = %#q, want %#q", tt.in, out, tt.out)
+		}
+	}
+}
diff --git a/vendor/github.com/mattermost/rsc/imap/imap.go b/vendor/github.com/mattermost/rsc/imap/imap.go
new file mode 100644
index 000000000..6555984d2
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/imap/imap.go
@@ -0,0 +1,1110 @@
+package imap
+
+import (
+	"bufio"
+	"bytes"
+	"crypto/md5"
+	"crypto/tls"
+	"fmt"
+	"io"
+	"log"
+	"net"
+	"os"
+	"os/exec"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+var Debug = false
+
+const tag = "#"
+
+// A Mode specifies the IMAP connection mode.
+type Mode int
+
+const (
+	Unencrypted Mode = iota // unencrypted TCP connection
+	StartTLS                // use IMAP STARTTLS command - unimplemented!
+	TLS                     // direct TLS connection
+	Command                 // exec shell command (server name)
+)
+
+type lock struct {
+	locked bool
+	mu     sync.Mutex
+}
+
+func (l *lock) lock() {
+	l.mu.Lock()
+	l.locked = true
+}
+
+func (l *lock) unlock() {
+	l.mustBeLocked()
+	l.locked = false
+	l.mu.Unlock()
+}
+
+func (l *lock) mustBeLocked() {
+	if !l.locked {
+		panic("not locked")
+	}
+}
+
+type Client struct {
+	server string
+	user   string
+	passwd string
+	mode   Mode
+	root   string
+
+	io            lock
+	rw            io.ReadWriteCloser // i/o to server
+	b             *bufio.Reader      // buffered rw
+	autoReconnect bool               // reconnect on failure
+	connected     bool               // rw is active
+
+	data       lock
+	capability map[string]bool
+	flags      Flags
+	boxByName  map[string]*Box // all known boxes
+	allBox     []*Box          // all known boxes (do we need this?)
+	rootBox    *Box            // root of box tree
+	inbox      *Box            // inbox (special, not in tree)
+	box        *Box            // selected (current) box
+	nextBox    *Box            // next box to select (do we need this?)
+}
+
+func NewClient(mode Mode, server, user, passwd string, root string) (*Client, error) {
+	c := &Client{
+		server:    server,
+		user:      user,
+		passwd:    passwd,
+		mode:      mode,
+		root:      root,
+		boxByName: map[string]*Box{},
+	}
+	c.io.lock()
+	if err := c.reconnect(); err != nil {
+		return nil, err
+	}
+	c.autoReconnect = true
+	c.io.unlock()
+
+	return c, nil
+}
+
+func (c *Client) Close() error {
+	c.io.lock()
+	c.autoReconnect = false
+	c.connected = false
+	if c.rw != nil {
+		c.rw.Close()
+		c.rw = nil
+	}
+	c.io.unlock()
+	return nil
+}
+
+func (c *Client) reconnect() error {
+	c.io.mustBeLocked()
+	c.autoReconnect = false
+	if c.rw != nil {
+		c.rw.Close()
+		c.rw = nil
+	}
+
+	if Debug {
+		log.Printf("dial %s...", c.server)
+	}
+	rw, err := dial(c.server, c.mode)
+	if err != nil {
+		return err
+	}
+
+	c.rw = rw
+	c.connected = true
+	c.capability = nil
+	c.box = nil
+	if Debug {
+		c.b = bufio.NewReader(&tee{rw, os.Stderr})
+	} else {
+		c.b = bufio.NewReader(rw)
+	}
+	x, err := c.rdsx()
+	if x == nil {
+		err = fmt.Errorf("no greeting from %s: %v", c.server, err)
+		goto Error
+	}
+	if len(x.sx) < 2 || !x.sx[0].isAtom("*") || !x.sx[1].isAtom("PREAUTH") {
+		if !x.ok() {
+			err = fmt.Errorf("bad greeting - %s", x)
+			goto Error
+		}
+		if err = c.login(); err != nil {
+			goto Error
+		}
+	}
+	if c.capability == nil {
+		if err = c.cmd(nil, "CAPABILITY"); err != nil {
+			goto Error
+		}
+		if c.capability == nil {
+			err = fmt.Errorf("CAPABILITY command did not return capability list")
+			goto Error
+		}
+	}
+	if err := c.getBoxes(); err != nil {
+		goto Error
+	}
+	c.autoReconnect = true
+	return nil
+
+Error:
+	if c.rw != nil {
+		c.rw.Close()
+		c.rw = nil
+	}
+	c.autoReconnect = true
+	c.connected = false
+	return err
+}
+
+var testDial func(string, Mode) (io.ReadWriteCloser, error)
+
+func dial(server string, mode Mode) (io.ReadWriteCloser, error) {
+	if testDial != nil {
+		return testDial(server, mode)
+	}
+	switch mode {
+	default:
+		// also case Unencrypted
+		return net.Dial("tcp", server+":143")
+	case StartTLS:
+		return nil, fmt.Errorf("StartTLS not supported")
+	case TLS:
+		return tls.Dial("tcp", server+":993", nil)
+	case Command:
+		cmd := exec.Command("sh", "-c", server)
+		cmd.Stderr = os.Stderr
+		r, err := cmd.StdoutPipe()
+		if err != nil {
+			return nil, err
+		}
+		w, err := cmd.StdinPipe()
+		if err != nil {
+			r.Close()
+			return nil, err
+		}
+		if err := cmd.Start(); err != nil {
+			r.Close()
+			w.Close()
+			return nil, err
+		}
+		return &pipe2{r, w}, nil
+	}
+	panic("not reached")
+}
+
+type pipe2 struct {
+	io.ReadCloser
+	io.WriteCloser
+}
+
+func (p *pipe2) Close() error {
+	p.ReadCloser.Close()
+	p.WriteCloser.Close()
+	return nil
+}
+
+type tee struct {
+	r io.Reader
+	w io.Writer
+}
+
+func (t tee) Read(p []byte) (n int, err error) {
+	n, err = t.r.Read(p)
+	if n > 0 {
+		t.w.Write(p[0:n])
+	}
+	return
+}
+
+func (c *Client) rdsx() (*sx, error) {
+	c.io.mustBeLocked()
+	return rdsx(c.b)
+}
+
+type sxError struct {
+	x *sx
+}
+
+func (e *sxError) Error() string { return e.x.String() }
+
+func (c *Client) cmd(b *Box, format string, args ...interface{}) error {
+	x, err := c.cmdsx(b, format, args...)
+	if err != nil {
+		return err
+	}
+	if !x.ok() {
+		return &sxError{x}
+	}
+	return nil
+}
+
+// cmdsx0 runs a single command and return the sx.  Does not redial.
+func (c *Client) cmdsx0(format string, args ...interface{}) (*sx, error) {
+	c.io.mustBeLocked()
+	if c.rw == nil || !c.connected {
+		return nil, fmt.Errorf("not connected")
+	}
+
+	cmd := fmt.Sprintf(format, args...)
+	if Debug {
+		fmt.Fprintf(os.Stderr, ">>> %s %s\n", tag, cmd)
+	}
+	if _, err := fmt.Fprintf(c.rw, "%s %s\r\n", tag, cmd); err != nil {
+		c.connected = false
+		return nil, err
+	}
+	return c.waitsx()
+}
+
+// cmdsx runs a command on box b.  It does redial.
+func (c *Client) cmdsx(b *Box, format string, args ...interface{}) (*sx, error) {
+	c.io.mustBeLocked()
+	c.nextBox = b
+
+Trying:
+	for tries := 0; ; tries++ {
+		if c.rw == nil || !c.connected {
+			if !c.autoReconnect {
+				return nil, fmt.Errorf("not connected")
+			}
+			if err := c.reconnect(); err != nil {
+				return nil, err
+			}
+			if b != nil && c.nextBox == nil {
+				// box disappeared on reconnect
+				return nil, fmt.Errorf("box is gone")
+			}
+		}
+
+		if b != nil && b != c.box {
+			if c.box != nil {
+				// TODO c.box.init = false
+			}
+			c.box = b
+			if _, err := c.cmdsx0("SELECT %s", iquote(b.Name)); err != nil {
+				c.box = nil
+				if tries++; tries == 1 && (c.rw == nil || !c.connected) {
+					continue Trying
+				}
+				return nil, err
+			}
+		}
+
+		x, err := c.cmdsx0(format, args...)
+		if err != nil {
+			if tries++; tries == 1 && (c.rw == nil || !c.connected) {
+				continue Trying
+			}
+			return nil, err
+		}
+		return x, nil
+	}
+	panic("not reached")
+}
+
+func (c *Client) waitsx() (*sx, error) {
+	c.io.mustBeLocked()
+	for {
+		x, err := c.rdsx()
+		if err != nil {
+			c.connected = false
+			return nil, err
+		}
+		if len(x.sx) >= 1 && x.sx[0].kind == sxAtom {
+			if x.sx[0].isAtom(tag) {
+				return x, nil
+			}
+			if x.sx[0].isAtom("*") {
+				c.unexpected(x)
+			}
+		}
+		if x.kind == sxList && len(x.sx) == 0 {
+			c.connected = false
+			return nil, fmt.Errorf("empty response")
+		}
+	}
+	panic("not reached")
+}
+
+func iquote(s string) string {
+	if s == "" {
+		return `""`
+	}
+
+	for i := 0; i < len(s); i++ {
+		if s[i] >= 0x80 || s[i] <= ' ' || s[i] == '\\' || s[i] == '"' {
+			goto Quote
+		}
+	}
+	return s
+
+Quote:
+	var b bytes.Buffer
+	b.WriteByte('"')
+	for i := 0; i < len(s); i++ {
+		if s[i] == '\\' || s[i] == '"' {
+			b.WriteByte('\\')
+		}
+		b.WriteByte(s[i])
+	}
+	b.WriteByte('"')
+	return b.String()
+}
+
+func (c *Client) login() error {
+	c.io.mustBeLocked()
+	x, err := c.cmdsx(nil, "LOGIN %s %s", iquote(c.user), iquote(c.passwd))
+	if err != nil {
+		return err
+	}
+	if !x.ok() {
+		return fmt.Errorf("login rejected: %s", x)
+	}
+	return nil
+}
+
+func (c *Client) getBoxes() error {
+	c.io.mustBeLocked()
+	for _, b := range c.allBox {
+		b.dead = true
+		//	b.exists = 0
+		//	b.maxSeen = 0
+	}
+	list := "LIST"
+	if c.capability["XLIST"] { // Gmail extension
+		list = "XLIST"
+	}
+	if err := c.cmd(nil, "%s %s *", list, iquote(c.root)); err != nil {
+		return err
+	}
+	if err := c.cmd(nil, "%s %s INBOX", list, iquote(c.root)); err != nil {
+		return err
+	}
+	if c.nextBox != nil && c.nextBox.dead {
+		c.nextBox = nil
+	}
+	for _, b := range c.allBox {
+		if b.dead {
+			delete(c.boxByName, b.Name)
+		}
+		b.firstNum = 0
+	}
+	c.allBox = boxTrim(c.allBox)
+	for _, b := range c.allBox {
+		b.child = boxTrim(b.child)
+	}
+	return nil
+}
+
+func boxTrim(list []*Box) []*Box {
+	w := 0
+	for _, b := range list {
+		if !b.dead {
+			list[w] = b
+			w++
+		}
+	}
+	return list[:w]
+}
+
+const maxFetch = 1000
+
+func (c *Client) setAutoReconnect(b bool) {
+	c.autoReconnect = b
+}
+
+func (c *Client) check(b *Box) error {
+	c.io.mustBeLocked()
+	if b.dead {
+		return fmt.Errorf("box is gone")
+	}
+
+	b.load = true
+
+	// Update exists count.
+	if err := c.cmd(b, "NOOP"); err != nil {
+		return err
+	}
+
+	// Have to get through this in one session.
+	// Caller can call again if we get disconnected
+	// and return an error.
+	c.autoReconnect = false
+	defer c.setAutoReconnect(true)
+
+	// First load after reconnect: figure out what changed.
+	if b.firstNum == 0 && len(b.msgByUID) > 0 {
+		var lo, hi uint32 = 1<<32 - 1, 0
+		for _, m := range b.msgByUID {
+			m.dead = true
+			uid := uint32(m.UID)
+			if lo > uid {
+				lo = uid
+			}
+			if hi < uid {
+				hi = uid
+			}
+			m.num = 0
+		}
+		if err := c.cmd(b, "UID FETCH %d:%d FLAGS", lo, hi); err != nil {
+			return err
+		}
+		for _, m := range b.msgByUID {
+			if m.dead {
+				delete(b.msgByUID, m.UID)
+			}
+		}
+	}
+
+	// First-ever load.
+	if b.firstNum == 0 {
+		if b.exists <= maxFetch {
+			b.firstNum = 1
+		} else {
+			b.firstNum = b.exists - maxFetch + 1
+		}
+		n := b.exists - b.firstNum + 1
+		b.msgByNum = make([]*Msg, n)
+		return c.fetchBox(b, b.firstNum, 0)
+	}
+
+	if b.exists <= b.maxSeen {
+		return nil
+	}
+	return c.fetchBox(b, b.maxSeen, 0)
+}
+
+func (c *Client) fetchBox(b *Box, lo int, hi int) error {
+	c.io.mustBeLocked()
+	if b != c.box {
+		if err := c.cmd(b, "NOOP"); err != nil {
+			return err
+		}
+	}
+	extra := ""
+	if c.IsGmail() {
+		extra = " X-GM-MSGID X-GM-THRID X-GM-LABELS"
+	}
+	slo := fmt.Sprint(lo)
+	shi := "*"
+	if hi > 0 {
+		shi = fmt.Sprint(hi)
+	}
+	return c.cmd(b, "FETCH %s:%s (FLAGS UID INTERNALDATE RFC822.SIZE ENVELOPE BODY%s)", slo, shi, extra)
+}
+
+func (c *Client) IsGmail() bool {
+	return c.capability["X-GM-EXT-1"]
+}
+
+// Table-driven IMAP "unexpected response" parser.
+// All the interesting data is in the unexpected responses.
+
+var unextab = []struct {
+	num  int
+	name string
+	fmt  string
+	fn   func(*Client, *sx)
+}{
+	{0, "BYE", "", xbye},
+	{0, "CAPABILITY", "", xcapability},
+	{0, "FLAGS", "AAL", xflags},
+	{0, "LIST", "AALSS", xlist},
+	{0, "XLIST", "AALSS", xlist},
+	{0, "OK", "", xok},
+	//	{0, "SEARCH", "AAN*", xsearch},
+	{1, "EXISTS", "ANA", xexists},
+	{1, "EXPUNGE", "ANA", xexpunge},
+	{1, "FETCH", "ANAL", xfetch},
+	//	{1, "RECENT", "ANA", xrecent},  // why do we care?
+}
+
+func (c *Client) unexpected(x *sx) {
+	c.io.mustBeLocked()
+	var num int
+	var name string
+
+	if len(x.sx) >= 3 && x.sx[1].kind == sxNumber && x.sx[2].kind == sxAtom {
+		num = 1
+		name = string(x.sx[2].data)
+	} else if len(x.sx) >= 2 && x.sx[1].kind == sxAtom {
+		num = 0
+		name = string(x.sx[1].data)
+	} else {
+		return
+	}
+
+	c.data.lock()
+	for _, t := range unextab {
+		if t.num == num && strings.EqualFold(t.name, name) {
+			if t.fmt != "" && !x.match(t.fmt) {
+				log.Printf("malformd %s: %s", name, x)
+				continue
+			}
+			t.fn(c, x)
+		}
+	}
+	c.data.unlock()
+}
+
+func xbye(c *Client, x *sx) {
+	c.io.mustBeLocked()
+	c.rw.Close()
+	c.rw = nil
+	c.connected = false
+}
+
+func xflags(c *Client, x *sx) {
+	c.data.mustBeLocked()
+	// This response contains in x.sx[2] the list of flags
+	// that can be validly attached to messages in c.box.
+	if b := c.box; b != nil {
+		c.flags = x.sx[2].parseFlags()
+	}
+}
+
+func xcapability(c *Client, x *sx) {
+	c.data.mustBeLocked()
+	c.capability = make(map[string]bool)
+	for _, xx := range x.sx[2:] {
+		if xx.kind == sxAtom {
+			c.capability[string(xx.data)] = true
+		}
+	}
+}
+
+func xlist(c *Client, x *sx) {
+	c.data.mustBeLocked()
+	s := string(x.sx[4].data)
+	t := string(x.sx[3].data)
+
+	// INBOX is the special name for the main mailbox.
+	// All the other mailbox names have the root prefix removed, if applicable.
+	inbox := strings.EqualFold(s, "inbox")
+	if inbox {
+		s = "inbox"
+	}
+
+	b := c.newBox(s, t, inbox)
+	if b == nil {
+		return
+	}
+	if inbox {
+		c.inbox = b
+	}
+	if s == c.root {
+		c.rootBox = b
+	}
+	b.dead = false
+	b.flags = x.sx[2].parseFlags()
+}
+
+func xexists(c *Client, x *sx) {
+	c.data.mustBeLocked()
+	if b := c.box; b != nil {
+		b.exists = int(x.sx[1].number)
+		if b.exists < b.maxSeen {
+			b.maxSeen = b.exists
+		}
+	}
+}
+
+func xexpunge(c *Client, x *sx) {
+	c.data.mustBeLocked()
+	if b := c.box; b != nil {
+		n := int(x.sx[1].number)
+		bynum := b.msgByNum
+		if bynum != nil {
+			if n < b.firstNum {
+				b.firstNum--
+			} else if n < b.firstNum+len(bynum) {
+				copy(bynum[n-b.firstNum:], bynum[n-b.firstNum+1:])
+				b.msgByNum = bynum[:len(bynum)-1]
+			} else {
+				log.Printf("expunge unexpected message %d %d %d", b.firstNum, b.exists, b.firstNum+len(bynum))
+			}
+		}
+		if n <= b.exists {
+			b.exists--
+		}
+	}
+}
+
+// Table-driven OK info parser.
+
+var oktab = []struct {
+	name string
+	kind sxKind
+	fn   func(*Client, *Box, *sx)
+}{
+	{"UIDVALIDITY", sxNumber, xokuidvalidity},
+	{"PERMANENTFLAGS", sxList, xokpermflags},
+	{"UNSEEN", sxNumber, xokunseen},
+	{"READ-WRITE", 0, xokreadwrite},
+	{"READ-ONLY", 0, xokreadonly},
+}
+
+func xok(c *Client, x *sx) {
+	c.data.mustBeLocked()
+	b := c.box
+	if b == nil {
+		return
+	}
+	if len(x.sx) >= 4 && x.sx[2].kind == sxAtom && x.sx[2].data[0] == '[' {
+		var arg *sx
+		if x.sx[3].kind == sxAtom && x.sx[3].data[0] == ']' {
+			arg = nil
+		} else if x.sx[4].kind == sxAtom && x.sx[4].data[0] == ']' {
+			arg = x.sx[3]
+		} else {
+			log.Printf("cannot parse OK: %s", x)
+			return
+		}
+		x.sx[2].data = x.sx[2].data[1:]
+		for _, t := range oktab {
+			if x.sx[2].isAtom(t.name) {
+				if t.kind != 0 && (arg == nil || arg.kind != t.kind) {
+					log.Printf("malformed %s: %s", t.name, arg)
+					continue
+				}
+				t.fn(c, b, arg)
+			}
+		}
+	}
+}
+
+func xokuidvalidity(c *Client, b *Box, x *sx) {
+	c.data.mustBeLocked()
+	n := uint32(x.number)
+	if b.validity != n {
+		if b.msgByUID != nil {
+			log.Printf("imap: UID validity reset for %s", b.Name)
+		}
+		b.validity = n
+		b.maxSeen = 0
+		b.firstNum = 0
+		b.msgByNum = nil
+		b.msgByUID = nil
+	}
+}
+
+func xokpermflags(c *Client, b *Box, x *sx) {
+	c.data.mustBeLocked()
+	b.permFlags = x.parseFlags()
+}
+
+func xokunseen(c *Client, b *Box, x *sx) {
+	c.data.mustBeLocked()
+	b.unseen = int(x.number)
+}
+
+func xokreadwrite(c *Client, b *Box, x *sx) {
+	c.data.mustBeLocked()
+	b.readOnly = false
+}
+
+func xokreadonly(c *Client, b *Box, x *sx) {
+	c.data.mustBeLocked()
+	b.readOnly = true
+}
+
+// Table-driven FETCH message info parser.
+
+var msgtab = []struct {
+	name string
+	fn   func(*Msg, *sx, *sx)
+}{
+	{"FLAGS", xmsgflags},
+	{"INTERNALDATE", xmsgdate},
+	{"RFC822.SIZE", xmsgrfc822size},
+	{"ENVELOPE", xmsgenvelope},
+	{"X-GM-MSGID", xmsggmmsgid},
+	{"X-GM-THRID", xmsggmthrid},
+	{"BODY", xmsgbody},
+	{"BODY[", xmsgbodydata},
+}
+
+func xfetch(c *Client, x *sx) {
+	c.data.mustBeLocked()
+	if c.box == nil {
+		log.Printf("FETCH but no open box: %s", x)
+		return
+	}
+
+	// * 152 FETCH (UID 185 FLAGS() ...)
+	n := x.sx[1].number
+	xx := x.sx[3]
+	if len(xx.sx)%2 != 0 {
+		log.Printf("malformed FETCH: %s", x)
+		return
+	}
+	var uid uint64
+	for i := 0; i < len(xx.sx); i += 2 {
+		if xx.sx[i].isAtom("UID") {
+			if xx.sx[i+1].kind == sxNumber {
+				uid = uint64(xx.sx[i+1].number) | uint64(c.box.validity)<<32
+				goto HaveUID
+			}
+		}
+	}
+	// This happens; too bad.
+	// log.Printf("FETCH without UID: %s", x)
+	return
+
+HaveUID:
+	if m := c.box.msgByUID[uid]; m != nil && m.dead {
+		// FETCH during box garbage collection.
+		m.dead = false
+		m.num = int(n)
+		return
+	}
+	m := c.box.newMsg(uid, int(n))
+	for i := 0; i < len(xx.sx); i += 2 {
+		k, v := xx.sx[i], xx.sx[i+1]
+		for _, t := range msgtab {
+			if k.isAtom(t.name) {
+				t.fn(m, k, v)
+			}
+		}
+	}
+}
+
+func xmsggmmsgid(m *Msg, k, v *sx) {
+	m.GmailID = uint64(v.number)
+}
+
+func xmsggmthrid(m *Msg, k, v *sx) {
+	m.GmailThread = uint64(v.number)
+}
+
+func xmsgflags(m *Msg, k, v *sx) {
+	m.Flags = v.parseFlags()
+}
+
+func xmsgrfc822size(m *Msg, k, v *sx) {
+	m.Bytes = v.number
+}
+
+func xmsgdate(m *Msg, k, v *sx) {
+	m.Date = v.parseDate()
+}
+
+func xmsgenvelope(m *Msg, k, v *sx) {
+	m.Hdr = parseEnvelope(v)
+}
+
+func parseEnvelope(v *sx) *MsgHdr {
+	if v.kind != sxList || !v.match("SSLLLLLLSS") {
+		log.Printf("bad envelope: %s", v)
+		return nil
+	}
+
+	hdr := &MsgHdr{
+		Date:      v.sx[0].nstring(),
+		Subject:   unrfc2047(v.sx[1].nstring()),
+		From:      parseAddrs(v.sx[2]),
+		Sender:    parseAddrs(v.sx[3]),
+		ReplyTo:   parseAddrs(v.sx[4]),
+		To:        parseAddrs(v.sx[5]),
+		CC:        parseAddrs(v.sx[6]),
+		BCC:       parseAddrs(v.sx[7]),
+		InReplyTo: unrfc2047(v.sx[8].nstring()),
+		MessageID: unrfc2047(v.sx[9].nstring()),
+	}
+
+	h := md5.New()
+	fmt.Fprintf(h, "date: %s\n", hdr.Date)
+	fmt.Fprintf(h, "subject: %s\n", hdr.Subject)
+	fmt.Fprintf(h, "from: %s\n", hdr.From)
+	fmt.Fprintf(h, "sender: %s\n", hdr.Sender)
+	fmt.Fprintf(h, "replyto: %s\n", hdr.ReplyTo)
+	fmt.Fprintf(h, "to: %s\n", hdr.To)
+	fmt.Fprintf(h, "cc: %s\n", hdr.CC)
+	fmt.Fprintf(h, "bcc: %s\n", hdr.BCC)
+	fmt.Fprintf(h, "inreplyto: %s\n", hdr.InReplyTo)
+	fmt.Fprintf(h, "messageid: %s\n", hdr.MessageID)
+	hdr.Digest = fmt.Sprintf("%x", h.Sum(nil))
+
+	return hdr
+}
+
+func parseAddrs(x *sx) []Addr {
+	var addr []Addr
+	for _, xx := range x.sx {
+		if !xx.match("SSSS") {
+			log.Printf("bad address: %s", x)
+			continue
+		}
+		name := unrfc2047(xx.sx[0].nstring())
+		// sx[1] is route
+		local := unrfc2047(xx.sx[2].nstring())
+		host := unrfc2047(xx.sx[3].nstring())
+		if local == "" || host == "" {
+			// rfc822 group syntax
+			addr = append(addr, Addr{name, ""})
+			continue
+		}
+		addr = append(addr, Addr{name, local + "@" + host})
+	}
+	return addr
+}
+
+func xmsgbody(m *Msg, k, v *sx) {
+	if v.isNil() {
+		return
+	}
+	if v.kind != sxList {
+		log.Printf("bad body: %s", v)
+	}
+
+	// To follow the structure exactly we should be doing this
+	// to m.NewPart(m.Part[0]) with type message/rfc822,
+	// but the extra layer is redundant - what else would be in
+	// a mailbox?
+	parseStructure(&m.Root, v)
+	n := m.num
+	if m.Box.maxSeen < n {
+		m.Box.maxSeen = n
+	}
+}
+
+func parseStructure(p *MsgPart, x *sx) {
+	if x.isNil() {
+		return
+	}
+	if x.kind != sxList {
+		log.Printf("bad structure: %s", x)
+		return
+	}
+	if x.sx[0].isList() {
+		// multipart
+		var i int
+		for i = 0; i < len(x.sx) && x.sx[i].isList(); i++ {
+			parseStructure(p.newPart(), x.sx[i])
+		}
+		if i != len(x.sx)-1 || !x.sx[i].isString() {
+			log.Printf("bad multipart structure: %s", x)
+			p.Type = "multipart/mixed"
+			return
+		}
+		s := strlwr(x.sx[i].nstring())
+		p.Type = "multipart/" + s
+		return
+	}
+
+	// single type
+	if len(x.sx) < 2 || !x.sx[0].isString() {
+		log.Printf("bad type structure: %s", x)
+		return
+	}
+	s := strlwr(x.sx[0].nstring())
+	t := strlwr(x.sx[1].nstring())
+	p.Type = s + "/" + t
+	if len(x.sx) < 7 || !x.sx[2].isList() || !x.sx[3].isString() || !x.sx[4].isString() || !x.sx[5].isString() || !x.sx[6].isNumber() {
+		log.Printf("bad part structure: %s", x)
+		return
+	}
+	parseParams(p, x.sx[2])
+	p.ContentID = x.sx[3].nstring()
+	p.Desc = x.sx[4].nstring()
+	p.Encoding = x.sx[5].nstring()
+	p.Bytes = x.sx[6].number
+	if p.Type == "message/rfc822" {
+		if len(x.sx) < 10 || !x.sx[7].isList() || !x.sx[8].isList() || !x.sx[9].isNumber() {
+			log.Printf("bad rfc822 structure: %s", x)
+			return
+		}
+		p.Hdr = parseEnvelope(x.sx[7])
+		parseStructure(p.newPart(), x.sx[8])
+		p.Lines = x.sx[9].number
+	}
+	if s == "text" {
+		if len(x.sx) < 8 || !x.sx[7].isNumber() {
+			log.Printf("bad text structure: %s", x)
+			return
+		}
+		p.Lines = x.sx[7].number
+	}
+}
+
+func parseParams(p *MsgPart, x *sx) {
+	if x.isNil() {
+		return
+	}
+	if len(x.sx)%2 != 0 {
+		log.Printf("bad message params: %s", x)
+		return
+	}
+
+	for i := 0; i < len(x.sx); i += 2 {
+		k, v := x.sx[i].nstring(), x.sx[i+1].nstring()
+		k = strlwr(k)
+		switch strlwr(k) {
+		case "charset":
+			p.Charset = strlwr(v)
+		case "name":
+			p.Name = v
+		}
+	}
+}
+
+func (c *Client) fetch(p *MsgPart, what string) {
+	c.io.mustBeLocked()
+	id := p.ID
+	if what != "" {
+		if id != "" {
+			id += "."
+		}
+		id += what
+	}
+	c.cmd(p.Msg.Box, "UID FETCH %d BODY[%s]", p.Msg.UID&(1<<32-1), id)
+}
+
+func xmsgbodydata(m *Msg, k, v *sx) {
+	// k.data is []byte("BODY[...")
+	name := string(k.data[5:])
+	if i := strings.Index(name, "]"); i >= 0 {
+		name = name[:i]
+	}
+
+	p := &m.Root
+	for name != "" && '1' <= name[0] && name[0] <= '9' {
+		var num int
+		num, name = parseNum(name)
+		if num == 0 {
+			log.Printf("unexpected body name: %s", k.data)
+			return
+		}
+		num--
+		if num >= len(p.Child) {
+			log.Printf("invalid body name: %s", k.data)
+			return
+		}
+		p = p.Child[num]
+	}
+
+	switch strlwr(name) {
+	case "":
+		p.raw = v.nbytes()
+	case "mime":
+		p.mimeHeader = nocr(v.nbytes())
+	case "header":
+		p.rawHeader = nocr(v.nbytes())
+	case "text":
+		p.rawBody = nocr(v.nbytes())
+	}
+}
+
+func parseNum(name string) (int, string) {
+	rest := ""
+	i := strings.Index(name, ".")
+	if i >= 0 {
+		name, rest = name[:i], name[i+1:]
+	}
+	n, _ := strconv.Atoi(name)
+	return n, rest
+}
+
+func nocr(b []byte) []byte {
+	w := 0
+	for _, c := range b {
+		if c != '\r' {
+			b[w] = c
+			w++
+		}
+	}
+	return b[:w]
+}
+
+type uidList []*Msg
+
+func (l uidList) String() string {
+	var b bytes.Buffer
+	for i, m := range l {
+		if i > 0 {
+			b.WriteByte(',')
+		}
+		fmt.Fprintf(&b, "%d", m.UID&(1<<32-1))
+	}
+	return b.String()
+}
+
+func (c *Client) deleteList(msgs []*Msg) error {
+	if len(msgs) == 0 {
+		return nil
+	}
+	c.io.mustBeLocked()
+
+	b := msgs[0].Box
+	for _, m := range msgs {
+		if m.Box != b {
+			return fmt.Errorf("messages span boxes: %q and %q", b.Name, m.Box.Name)
+		}
+		if uint32(m.UID>>32) != b.validity {
+			return fmt.Errorf("stale message")
+		}
+	}
+
+	err := c.cmd(b, "UID STORE %s +FLAGS (\\Deleted)", uidList(msgs))
+	if err == nil && c.box == b {
+		err = c.cmd(b, "EXPUNGE")
+	}
+	return err
+}
+
+func (c *Client) copyList(dst, src *Box, msgs []*Msg) error {
+	if len(msgs) == 0 {
+		return nil
+	}
+	c.io.mustBeLocked()
+
+	for _, m := range msgs {
+		if m.Box != src {
+			return fmt.Errorf("messages span boxes: %q and %q", src.Name, m.Box.Name)
+		}
+		if uint32(m.UID>>32) != src.validity {
+			return fmt.Errorf("stale message")
+		}
+	}
+
+	var name string
+	if dst == c.inbox {
+		name = "INBOX"
+	} else {
+		name = iquote(dst.Name)
+	}
+	return c.cmd(src, "UID COPY %s %s", uidList(msgs), name)
+}
+
+func (c *Client) muteList(src *Box, msgs []*Msg) error {
+	if len(msgs) == 0 {
+		return nil
+	}
+	c.io.mustBeLocked()
+
+	for _, m := range msgs {
+		if m.Box != src {
+			return fmt.Errorf("messages span boxes: %q and %q", src.Name, m.Box.Name)
+		}
+		if uint32(m.UID>>32) != src.validity {
+			return fmt.Errorf("stale message")
+		}
+	}
+
+	return c.cmd(src, "UID STORE %s +X-GM-LABELS (\\Muted)", uidList(msgs))
+}
diff --git a/vendor/github.com/mattermost/rsc/imap/imap_test.go b/vendor/github.com/mattermost/rsc/imap/imap_test.go
new file mode 100644
index 000000000..75737fcc8
--- /dev/null
+++ b/vendor/github.com/mattermost/rsc/imap/imap_test.go
@@ -0,0 +1,433 @@
+package imap
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/mattermost/rsc/google"
+)
+
+// NOTE: web address is https://mail.google.com/mail/b/rsc@swtch.com/?shva=1#inbox/132e5fd3a6a3c17b
+// where the last is the hex for the thread id.
+// have to have the #inbox part right too.  #label/Hello+World/...
+// or #all as a fallback
+
+// TODO: ID command support (RFC 2971)
+
+const mock = true
+
+var user = "rsc@swtch.com"
+var pw, _ = ioutil.ReadFile("/Users/rsc/.swtchpass")
+
+func TestImap(t *testing.T) {
+	var user, pw string
+	if mock {
+		testDial = fakeDial
+		user = "gre@host.com"
+		pw = "password"
+	} else {
+		acct := google.Acct("rsc@swtch.com")
+		user = acct.Email
+		pw = acct.Password
+	}
+	c, err := NewClient(TLS, "imap.gmail.com", user, pw, "")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	inbox := c.Inbox()
+	msgs := inbox.Msgs()
+
+	for _, m := range msgs {
+		if m.UID == 611764547<<32|57046 {
+			//			c.io.lock()
+			//			c.cmd(c.boxByName[`[Gmail]/All Mail`], `UID SEARCH X-GM-RAW "label:russcox@gmail.com in:inbox in:unread -in:muted"`)
+			//			c.cmd(c.inbox, `UID SEARCH X-GM-RAW "label:russcox@gmail.com in:inbox in:unread -in:muted"`)
+			//			c.cmd(c.boxByName[`To Read`], `UID SEARCH X-GM-RAW "label:russcox@gmail.com in:inbox in:unread -in:muted"`)
+			//			c.cmd(c.boxByName[`[Gmail]/All Mail`], `UID SEARCH X-GM-RAW "label:russcox@gmail.com in:inbox in:unread -in:muted"`)
+			//			c.fetch(m.Root.Child[0], "")
+			//			c.io.unlock()
+			fmt.Println("--")
+			fmt.Println("From:", m.Hdr.From)
+			fmt.Println("To:", m.Hdr.To)
+			fmt.Println("Subject:", m.Hdr.Subject)
+			fmt.Println("M-Date:", time.Unix(m.Date, 0))
+			fmt.Println("Date:", m.Hdr.Date)
+			fmt.Println()
+			fmt.Println(string(m.Root.Child[0].Text()))
+			fmt.Println("--")
+		}
+	}
+	c.Close()
+}
+
+func fakeDial(server string, mode Mode) (io.ReadWriteCloser, error) {
+	r1, w1 := io.Pipe()
+	r2, w2 := io.Pipe()
+	go fakeServer(&pipe2{r1, w2})
+	return &pipe2{r2, w1}, nil
+}
+
+func fakeServer(rw io.ReadWriteCloser) {
+	b := bufio.NewReader(rw)
+	rw.Write([]byte(fakeReply[""]))
+	for {
+		line, err := b.ReadString('\n')
+		if err != nil {
+			break
+		}
+		reply := fakeReply[strings.TrimSpace(line)]
+		if reply == "" {
+			rw.Write([]byte("* BYE\r\n"))
+			break
+		}
+		rw.Write([]byte(reply))
+	}
+	rw.Close()
+}
+
+var fakeReply = map[string]string{
+	``: "* OK Gimap ready for requests from 71.232.17.63 k7if4537693qcx.66\r\n",
+	`# LOGIN gre@host.com password`: "* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS COMPRESS=DEFLATE\r\n" +
+		"# OK gre@host.com Grace Emlin authenticated (Success)\r\n",
+	`# XLIST "" INBOX`: `* XLIST (\HasNoChildren \Inbox) "/" "Inbox"` + "\r\n" +
+		"# OK Success\r\n",
+	`# XLIST "" *`: `* XLIST (\HasNoChildren \Inbox) "/" "Inbox"` + "\r\n" +
+		`* XLIST (\HasNoChildren) "/" "Someday"` + "\r\n" +
+		`* XLIST (\HasNoChildren) "/" "To Read"` + "\r\n" +
+		`* XLIST (\HasNoChildren) "/" "Waiting"` + "\r\n" +
+		`* XLIST (\Noselect \HasChildren) "/" "[Gmail]"` + "\r\n" +
+		`* XLIST (\HasNoChildren \AllMail) "/" "[Gmail]/All Mail"` + "\r\n" +
+		`* XLIST (\HasNoChildren \Drafts) "/" "[Gmail]/Drafts"` + "\r\n" +
+		`* XLIST (\HasNoChildren \Important) "/" "[Gmail]/Important"` + "\r\n" +
+		`* XLIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"` + "\r\n" +
+		`* XLIST (\HasNoChildren \Spam) "/" "[Gmail]/Spam"` + "\r\n" +
+		`* XLIST (\HasNoChildren \Starred) "/" "[Gmail]/Starred"` + "\r\n" +
+		`* XLIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"` + "\r\n" +
+		`* XLIST (\HasNoChildren) "/" "russcox@gmail.com"` + "\r\n" +
+		"# OK Success\r\n",
+	`# LIST "" INBOX`: `* LIST (\HasNoChildren) "/" "INBOX"` + "\r\n" +
+		"# OK Success\r\n",
+	`# LIST "" *`: `* LIST (\HasNoChildren) "/" "INBOX"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "Someday"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "To Read"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "Waiting"` + "\r\n" +
+		`* LIST (\Noselect \HasChildren) "/" "[Gmail]"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "[Gmail]/All Mail"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "[Gmail]/Drafts"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "[Gmail]/Important"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "[Gmail]/Sent Mail"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "[Gmail]/Spam"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "[Gmail]/Starred"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "[Gmail]/Trash"` + "\r\n" +
+		`* LIST (\HasNoChildren) "/" "russcox@gmail.com"` + "\r\n" +
+		"# OK Success\r\n",
+	`# SELECT inbox`: `* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)` + "\r\n" +
+		`* OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen \*)] Flags permitted.` + "\r\n" +
+		`* OK [UIDVALIDITY 611764547] UIDs valid.` + "\r\n" +
+		`* 9 EXISTS` + "\r\n" +
+		`* 0 RECENT` + "\r\n" +
+		`* OK [UIDNEXT 57027] Predicted next UID.` + "\r\n" +
+		"# OK [READ-WRITE] inbox selected. (Success)\r\n",
+	`# UID FETCH 1:* (FLAGS)`: `* 1 FETCH (UID 46074 FLAGS (\Seen))` + "\r\n" +
+		`* 2 FETCH (UID 49094 FLAGS (\Seen))` + "\r\n" +
+		`* 3 FETCH (UID 49317 FLAGS (\Seen))` + "\r\n" +
+		`* 4 FETCH (UID 49424 FLAGS (\Flagged \Seen))` + "\r\n" +
+		`* 5 FETCH (UID 49595 FLAGS (\Seen))` + "\r\n" +
+		`* 6 FETCH (UID 49810 FLAGS (\Seen))` + "\r\n" +
+		`* 7 FETCH (UID 50579 FLAGS (\Seen))` + "\r\n" +
+		`* 8 FETCH (UID 50597 FLAGS (\Seen))` + "\r\n" +
+		`* 9 FETCH (UID 50598 FLAGS (\Seen))` + "\r\n" +
+		"# OK Success\r\n",
+	`# FETCH 1:* (UID FLAGS)`: `* 1 FETCH (UID 46074 FLAGS (\Seen))` + "\r\n" +
+		`* 2 FETCH (UID 49094 FLAGS (\Seen))` + "\r\n" +
+		`* 3 FETCH (UID 49317 FLAGS (\Seen))` + "\r\n" +
+		`* 4 FETCH (UID 49424 FLAGS (\Flagged \Seen))` + "\r\n" +
+		`* 5 FETCH (UID 49595 FLAGS (\Seen))` + "\r\n" +
+		`* 6 FETCH (UID 49810 FLAGS (\Seen))` + "\r\n" +
+		`* 7 FETCH (UID 50579 FLAGS (\Seen))` + "\r\n" +
+		`* 8 FETCH (UID 50597 FLAGS (\Seen))` + "\r\n" +
+		`* 9 FETCH (UID 50598 FLAGS (\Seen))` + "\r\n" +
+		"# OK Success\r\n",
+	`# NOOP`: "# OK Success\r\n",
+	`# UID FETCH 1:* (FLAGS X-GM-MSGID X-GM-THRID)`: `* 1 FETCH (X-GM-THRID 1371690017835349492 X-GM-MSGID 1371690017835349492 UID 46074 FLAGS (\Seen))` + "\r\n" +
+		`* 2 FETCH (X-GM-THRID 1370053443095117076 X-GM-MSGID 1374032778063810116 UID 49094 FLAGS (\Seen))` + "\r\n" +
+		`* 3 FETCH (X-GM-THRID 1370053443095117076 X-GM-MSGID 1374171123044094435 UID 49317 FLAGS (\Seen))` + "\r\n" +
+		`* 4 FETCH (X-GM-THRID 1374260005724669308 X-GM-MSGID 1374260005724669308 UID 49424 FLAGS (\Flagged \Seen))` + "\r\n" +
+		`* 5 FETCH (X-GM-THRID 1374399840419707240 X-GM-MSGID 1374399840419707240 UID 49595 FLAGS (\Seen))` + "\r\n" +
+		`* 6 FETCH (X-GM-THRID 1374564698687599195 X-GM-MSGID 1374564698687599195 UID 49810 FLAGS (\Seen))` + "\r\n" +
+		`* 7 FETCH (X-GM-THRID 1353701773219222407 X-GM-MSGID 1375207927094695931 UID 50579 FLAGS (\Seen))` + "\r\n" +
+		`* 8 FETCH (X-GM-THRID 1375017086705541883 X-GM-MSGID 1375220323861690146 UID 50597 FLAGS (\Seen))` + "\r\n" +
+		`* 9 FETCH (X-GM-THRID 1353701773219222407 X-GM-MSGID 1375220551142026521 UID 50598 FLAGS (\Seen))` + "\r\n" +
+		"# OK Success\r\n",
+	`# UID FETCH 1:* (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE X-GM-MSGID X-GM-THRID)`: `* 1 FETCH (X-GM-THRID 1371690017835349492 X-GM-MSGID 1371690017835349492 UID 46074 RFC822.SIZE 5700 INTERNALDATE "15-Jun-2011 13:45:39 +0000" FLAGS (\Seen) ENVELOPE ("Wed, 15 Jun 2011 13:45:35 +0000" "[re2-dev] Issue 40 in re2: Please make RE2::Rewrite public" ((NIL NIL "re2" "googlecode.com")) ((NIL NIL "re2-dev" "googlegroups.com")) ((NIL NIL "codesite-noreply" "google.com")) ((NIL NIL "re2-dev" "googlegroups.com")) NIL NIL NIL "<0-13244084390050003171-8842966241254494762-re2=googlecode.com@googlecode.com>"))` + "\r\n" +
+		`* 2 FETCH (X-GM-THRID 1370053443095117076 X-GM-MSGID 1374032778063810116 UID 49094 RFC822.SIZE 3558 INTERNALDATE "11-Jul-2011 10:22:49 +0000" FLAGS (\Seen) ENVELOPE ("Mon, 11 Jul 2011 12:22:46 +0200" "Re: [re2-dev] Re: Issue 39 in re2: Eiffel wrapper for RE2" (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Russ Cox" NIL "rsc" "swtch.com")) NIL NIL "" "<4E1ACEF6.4060609@gmail.com>"))` + "\r\n" +
+		`* 3 FETCH (X-GM-THRID 1370053443095117076 X-GM-MSGID 1374171123044094435 UID 49317 RFC822.SIZE 3323 INTERNALDATE "12-Jul-2011 23:01:46 +0000" FLAGS (\Seen) ENVELOPE ("Wed, 13 Jul 2011 01:01:41 +0200" "Re: [re2-dev] Re: Issue 39 in re2: Eiffel wrapper for RE2" (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Russ Cox" NIL "rsc" "swtch.com")) NIL NIL "" "<4E1CD255.6060807@gmail.com>"))` + "\r\n" +
+		`* 4 FETCH (X-GM-THRID 1374260005724669308 X-GM-MSGID 1374260005724669308 UID 49424 RFC822.SIZE 2681 INTERNALDATE "13-Jul-2011 22:34:31 +0000" FLAGS (\Flagged \Seen) ENVELOPE ("Wed, 13 Jul 2011 16:33:43 -0600" "Minor correction for venti(8) user manual for running plan9port on Linux" (("Xing" NIL "xinglin" "cs.utah.edu")) (("Xing" NIL "xinglin" "cs.utah.edu")) (("Xing" NIL "xinglin" "cs.utah.edu")) ((NIL NIL "rsc" "swtch.com")) (("Xing Lin" NIL "xinglin" "cs.utah.edu") ("Raghuveer Pullakandam" NIL "rgv" "cs.utah.edu") ("Robert Ricci" NIL "ricci" "cs.utah.edu") ("Eric Eide" NIL "eeide" "cs.utah.edu")) NIL NIL "<1310596423.3866.11.camel@xing-utah-cs>"))` + "\r\n" +
+		`* 5 FETCH (X-GM-THRID 1374399840419707240 X-GM-MSGID 1374399840419707240 UID 49595 RFC822.SIZE 6496 INTERNALDATE "15-Jul-2011 11:37:07 +0000" FLAGS (\Seen) ENVELOPE ("Fri, 15 Jul 2011 13:36:54 +0200" "[re2-dev] MSVC not exporting VariadicFunction2<.. FullMatchN>::operator()(..) but VariadicFunction2<.. PartialMatchN>::operator()(..)" (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) ((NIL NIL "re2-dev" "googlegroups.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) ((NIL NIL "re2-dev" "googlegroups.com")) NIL NIL NIL "<4E202656.7010408@gmail.com>"))` + "\r\n" +
+		`* 6 FETCH (X-GM-THRID 1374564698687599195 X-GM-MSGID 1374564698687599195 UID 49810 RFC822.SIZE 5485 INTERNALDATE "17-Jul-2011 07:17:29 +0000" FLAGS (\Seen) ENVELOPE ("Sun, 17 Jul 2011 00:17:28 -0700" "Acme IRC client patch" (("Ethan Burns" NIL "burns.ethan" "gmail.com")) (("Ethan Burns" NIL "burns.ethan" "gmail.com")) (("Ethan Burns" NIL "burns.ethan" "gmail.com")) ((NIL NIL "rsc" "swtch.com")) NIL NIL NIL ""))` + "\r\n" +
+		`* 7 FETCH (X-GM-THRID 1353701773219222407 X-GM-MSGID 1375207927094695931 UID 50579 RFC822.SIZE 4049 INTERNALDATE "24-Jul-2011 09:41:19 +0000" FLAGS (\Seen) ENVELOPE ("Sun, 24 Jul 2011 02:41:14 -0700 (PDT)" "Re: [re2-dev] Re: MSVC build" ((NIL NIL "talgil" "gmail.com")) ((NIL NIL "re2-dev" "googlegroups.com")) ((NIL NIL "re2-dev" "googlegroups.com")) ((NIL NIL "re2-dev" "googlegroups.com")) (("ioannis" NIL "ioannis.e" "gmail.com")) NIL "" "<24718992.6777.1311500475040.JavaMail.geo-discussion-forums@yqyy3>"))` + "\r\n" +
+		`* 8 FETCH (X-GM-THRID 1375017086705541883 X-GM-MSGID 1375220323861690146 UID 50597 RFC822.SIZE 3070 INTERNALDATE "24-Jul-2011 12:58:22 +0000" FLAGS (\Seen) ENVELOPE ("Sun, 24 Jul 2011 14:58:15 +0200" "Re: [re2-dev] Rearranging platform dependant features" (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Russ Cox" NIL "rsc" "swtch.com")) NIL NIL "" "<4E2C16E7.3060500@gmail.com>"))` + "\r\n" +
+		`* 9 FETCH (X-GM-THRID 1353701773219222407 X-GM-MSGID 1375220551142026521 UID 50598 RFC822.SIZE 5744 INTERNALDATE "24-Jul-2011 13:01:59 +0000" FLAGS (\Seen) ENVELOPE ("Sun, 24 Jul 2011 15:01:49 +0200" "Re: [re2-dev] Re: MSVC build" (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) ((NIL NIL "re2-dev" "googlegroups.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) ((NIL NIL "re2-dev" "googlegroups.com")) NIL NIL "<24718992.6777.1311500475040.JavaMail.geo-discussion-forums@yqyy3>" "<4E2C17BD.6000702@gmail.com>"))` + "\r\n" +
+		"# OK Success\r\n",
+	`# UID FETCH 57047:* (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY X-GM-MSGID X-GM-THRID X-GM-LABELS)`: `* 9 FETCH (X-GM-THRID 1382192619814696847 X-GM-MSGID 1382192619814696847 X-GM-LABELS ("\\Important" russcox@gmail.com) UID 57046 RFC822.SIZE 4170 INTERNALDATE "09-Oct-2011 12:00:02 +0000" FLAGS () ENVELOPE ("Sun, 09 Oct 2011 12:00:02 +0000" "You have no events scheduled today." (("Google Calendar" NIL "calendar-notification" "google.com")) (("Google Calendar" NIL "calendar-notification" "google.com")) (("Russ Cox" NIL "russcox" "gmail.com")) (("Russ Cox" NIL "russcox" "gmail.com")) NIL NIL NIL "") BODY (("TEXT" "PLAIN" ("CHARSET" "ISO-8859-1" "DELSP" "yes" "FORMAT" "flowed") NIL NIL "7BIT" 465 11)("TEXT" "HTML" ("CHARSET" "ISO-8859-1") NIL NIL "QUOTED-PRINTABLE" 914 12) "ALTERNATIVE"))` + "\r\n" +
+		"# OK Success\r\n",
+	`# UID FETCH 1:* (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY X-GM-MSGID X-GM-THRID X-GM-LABELS)`: `* 1 FETCH (X-GM-THRID 1371690017835349492 X-GM-MSGID 1371690017835349492 X-GM-LABELS () UID 46074 RFC822.SIZE 5700 INTERNALDATE "15-Jun-2011 13:45:39 +0000" FLAGS (\Seen) ENVELOPE ("Wed, 15 Jun 2011 13:45:35 +0000" "[re2-dev] Issue 40 in re2: Please make RE2::Rewrite public" ((NIL NIL "re2" "googlecode.com")) ((NIL NIL "re2-dev" "googlegroups.com")) ((NIL NIL "codesite-noreply" "google.com")) ((NIL NIL "re2-dev" "googlegroups.com")) NIL NIL NIL "<0-13244084390050003171-8842966241254494762-re2=googlecode.com@googlecode.com>") BODY ("TEXT" "PLAIN" ("CHARSET" "ISO-8859-1" "DELSP" "yes" "FORMAT" "flowed") NIL NIL "7BIT" 389 11))` + "\r\n" +
+		`* 2 FETCH (X-GM-THRID 1370053443095117076 X-GM-MSGID 1374032778063810116 X-GM-LABELS ("\\Important") UID 49094 RFC822.SIZE 3558 INTERNALDATE "11-Jul-2011 10:22:49 +0000" FLAGS (\Seen) ENVELOPE ("Mon, 11 Jul 2011 12:22:46 +0200" "Re: [re2-dev] Re: Issue 39 in re2: Eiffel wrapper for RE2" (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Russ Cox" NIL "rsc" "swtch.com")) NIL NIL "" "<4E1ACEF6.4060609@gmail.com>") BODY ("TEXT" "PLAIN" ("CHARSET" "UTF-8" "FORMAT" "flowed") NIL NIL "7BIT" 766 24))` + "\r\n" +
+		`* 3 FETCH (X-GM-THRID 1370053443095117076 X-GM-MSGID 1374171123044094435 X-GM-LABELS ("\\Important") UID 49317 RFC822.SIZE 3323 INTERNALDATE "12-Jul-2011 23:01:46 +0000" FLAGS (\Seen) ENVELOPE ("Wed, 13 Jul 2011 01:01:41 +0200" "Re: [re2-dev] Re: Issue 39 in re2: Eiffel wrapper for RE2" (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Russ Cox" NIL "rsc" "swtch.com")) NIL NIL "" "<4E1CD255.6060807@gmail.com>") BODY ("TEXT" "PLAIN" ("CHARSET" "UTF-8" "FORMAT" "flowed") NIL NIL "7BIT" 435 12))` + "\r\n" +
+		`* 4 FETCH (X-GM-THRID 1374260005724669308 X-GM-MSGID 1374260005724669308 X-GM-LABELS ("\\Important" "\\Starred") UID 49424 RFC822.SIZE 2681 INTERNALDATE "13-Jul-2011 22:34:31 +0000" FLAGS (\Flagged \Seen) ENVELOPE ("Wed, 13 Jul 2011 16:33:43 -0600" "Minor correction for venti(8) user manual for running plan9port on Linux" (("Xing" NIL "xinglin" "cs.utah.edu")) (("Xing" NIL "xinglin" "cs.utah.edu")) (("Xing" NIL "xinglin" "cs.utah.edu")) ((NIL NIL "rsc" "swtch.com")) (("Xing Lin" NIL "xinglin" "cs.utah.edu") ("Raghuveer Pullakandam" NIL "rgv" "cs.utah.edu") ("Robert Ricci" NIL "ricci" "cs.utah.edu") ("Eric Eide" NIL "eeide" "cs.utah.edu")) NIL NIL "<1310596423.3866.11.camel@xing-utah-cs>") BODY ("TEXT" "PLAIN" ("CHARSET" "UTF-8") NIL NIL "8BIT" 789 25))` + "\r\n" +
+		`* 5 FETCH (X-GM-THRID 1374399840419707240 X-GM-MSGID 1374399840419707240 X-GM-LABELS ("\\Important") UID 49595 RFC822.SIZE 6496 INTERNALDATE "15-Jul-2011 11:37:07 +0000" FLAGS (\Seen) ENVELOPE ("Fri, 15 Jul 2011 13:36:54 +0200" "[re2-dev] MSVC not exporting VariadicFunction2<.. FullMatchN>::operator()(..) but VariadicFunction2<.. PartialMatchN>::operator()(..)" (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) ((NIL NIL "re2-dev" "googlegroups.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) ((NIL NIL "re2-dev" "googlegroups.com")) NIL NIL NIL "<4E202656.7010408@gmail.com>") BODY ("TEXT" "PLAIN" ("CHARSET" "ISO-8859-1" "FORMAT" "flowed") NIL NIL "7BIT" 1660 34))` + "\r\n" +
+		`* 6 FETCH (X-GM-THRID 1374564698687599195 X-GM-MSGID 1374564698687599195 X-GM-LABELS ("\\Important") UID 49810 RFC822.SIZE 5485 INTERNALDATE "17-Jul-2011 07:17:29 +0000" FLAGS (\Seen) ENVELOPE ("Sun, 17 Jul 2011 00:17:28 -0700" "Acme IRC client patch" (("Ethan Burns" NIL "burns.ethan" "gmail.com")) (("Ethan Burns" NIL "burns.ethan" "gmail.com")) (("Ethan Burns" NIL "burns.ethan" "gmail.com")) ((NIL NIL "rsc" "swtch.com")) NIL NIL NIL "") BODY (("TEXT" "PLAIN" ("CHARSET" "ISO-8859-1") NIL NIL "7BIT" 443 13)("TEXT" "X-PATCH" ("CHARSET" "US-ASCII" "NAME" "emote.patch") NIL NIL "BASE64" 2774 35) "MIXED"))` + "\r\n" +
+		`* 7 FETCH (X-GM-THRID 1353701773219222407 X-GM-MSGID 1375207927094695931 X-GM-LABELS ("\\Important") UID 50579 RFC822.SIZE 4049 INTERNALDATE "24-Jul-2011 09:41:19 +0000" FLAGS (\Seen) ENVELOPE ("Sun, 24 Jul 2011 02:41:14 -0700 (PDT)" "Re: [re2-dev] Re: MSVC build" ((NIL NIL "talgil" "gmail.com")) ((NIL NIL "re2-dev" "googlegroups.com")) ((NIL NIL "re2-dev" "googlegroups.com")) ((NIL NIL "re2-dev" "googlegroups.com")) (("ioannis" NIL "ioannis.e" "gmail.com")) NIL "" "<24718992.6777.1311500475040.JavaMail.geo-discussion-forums@yqyy3>") BODY (("TEXT" "PLAIN" ("CHARSET" "UTF-8") NIL NIL "7BIT" 133 8)("TEXT" "HTML" ("CHARSET" "UTF-8") NIL NIL "7BIT" 211 0) "ALTERNATIVE"))` + "\r\n" +
+		`* 8 FETCH (X-GM-THRID 1375017086705541883 X-GM-MSGID 1375220323861690146 X-GM-LABELS ("\\Important") UID 50597 RFC822.SIZE 3070 INTERNALDATE "24-Jul-2011 12:58:22 +0000" FLAGS (\Seen) ENVELOPE ("Sun, 24 Jul 2011 14:58:15 +0200" "Re: [re2-dev] Rearranging platform dependant features" (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Pontus Carlsson" NIL "pontusjoncarlsson" "gmail.com")) (("Russ Cox" NIL "rsc" "swtch.com")) NIL NIL "" "<4E2C16E7.3060500@gmail.com>") BODY ("TEXT" "PLAIN" ("CHARSET" "UTF-8" "FORMAT" "flowed") NIL NIL "7BIT" 450 10))` + "\r\n" +
+		`* 9 FETCH (X-GM-THRID 1382192619814696847 X-GM-MSGID 1382192619814696847 X-GM-LABELS ("\\Important" russcox@gmail.com) UID 57046 RFC822.SIZE 4170 INTERNALDATE "09-Oct-2011 12:00:02 +0000" FLAGS () ENVELOPE ("Sun, 09 Oct 2011 12:00:02 +0000" "You have no events scheduled today." (("Google Calendar" NIL "calendar-notification" "google.com")) (("Google Calendar" NIL "calendar-notification" "google.com")) (("Russ Cox" NIL "russcox" "gmail.com")) (("Russ Cox" NIL "russcox" "gmail.com")) NIL NIL NIL "") BODY (("TEXT" "PLAIN" ("CHARSET" "ISO-8859-1" "DELSP" "yes" "FORMAT" "flowed") NIL NIL "7BIT" 465 11)("TEXT" "HTML" ("CHARSET" "ISO-8859-1") NIL NIL "QUOTED-PRINTABLE" 914 12) "ALTERNATIVE"))` + "\r\n" +
+		"# OK Success\r\n",
+	`# UID FETCH 57046 BODY[1]`: `* 9 FETCH (UID 57046 BODY[1] {465}` + "\r\n" +
+		`russcox@gmail.com, you have no events scheduled today Sun Oct 9, 2011.` + "\r\n" +
+		`` + "\r\n" +
+		`View your calendar at https://www.google.com/calendar/` + "\r\n" +
+		`` + "\r\n" +
+		`You are receiving this email at the account russcox@gmail.com because you  ` + "\r\n" +
+		`are subscribed to receive daily agendas for the following calendars: Russ  ` + "\r\n" +
+		`Cox.` + "\r\n" +
+		`` + "\r\n" +
+		`To change which calendars you receive daily agendas for, please log in to  ` + "\r\n" +
+		`https://www.google.com/calendar/ and change your notification settings for  ` + "\r\n" +
+		`each calendar.` + "\r\n" +
+		`)` + "\r\n" +
+		"# OK Success\r\n",
+	`# UID FETCH 57046 BODY[1.TEXT]`: `* 9 FETCH (UID 57046 BODY[1.TEXT] NIL)` + "\r\n" +
+		"# OK Success\r\n",
+	`# UID FETCH 57046 BODY[1.HEADER]`: `* 9 FETCH (UID 57046 BODY[1.HEADER] NIL)` + "\r\n" +
+		"# OK Success\r\n",
+	`# UID FETCH 57046 BODY[1.MIME]`: `* 146 FETCH (UID 57046 BODY[1.MIME] {74}` + "\r\n" +
+		`Content-Type: text/plain; charset=ISO-8859-1; format=flowed; delsp=yes` + "\r\n" +
+		`` + "\r\n" +
+		`)` + "\r\n" +
+		"# OK Success\r\n",
+	`# UID FETCH 57046 BODY[2]`: `* 146 FETCH (UID 57046 BODY[2] {914}` + "\r\n" +
+		`
3D"Google` + "\r\n" + + `

russcox@gmail.com, you have no events s=` + "\r\n" + + `cheduled today Sun Oct 9, 2011

` + "\r\n" + + `

You are=` + "\r\n" + + ` receiving this email at the account russcox@gmail.com because you are subs=` + "\r\n" + + `cribed to receive daily agendas for the following calendars: Russ Cox.

` + "\r\n" + + `

To chan=` + "\r\n" + + `ge which calendars you receive daily agendas for, please log in to https://=` + "\r\n" + + `www.google.com/calendar/ and change your notification settings for each cal=` + "\r\n" + + `endar.

)` + "\r\n" + + "# OK Success\r\n", + `# UID FETCH 57046 BODY[2.TEXT]`: `* 9 FETCH (UID 57046 BODY[2.TEXT] NIL)` + "\r\n" + + "# OK Success\r\n", + `# UID FETCH 57046 BODY[2.HEADER]`: `* 9 FETCH (UID 57046 BODY[2.HEADER] NIL)` + "\r\n" + + "# OK Success\r\n", + `# UID FETCH 57046 BODY[2.MIME]`: `* 146 FETCH (UID 57046 BODY[2.MIME] {92}` + "\r\n" + + `Content-Type: text/html; charset=ISO-8859-1` + "\r\n" + + `Content-Transfer-Encoding: quoted-printable` + "\r\n" + + `` + "\r\n" + + `)` + "\r\n" + + "# OK Success\r\n", + `# UID FETCH 57046 BODY[]`: `* 146 FETCH (UID 57046 BODY[] {4170}` + "\r\n" + + `Delivered-To: rsc@swtch.com` + "\r\n" + + `Received: by 10.216.54.148 with SMTP id i20cs32329wec;` + "\r\n" + + ` Sun, 9 Oct 2011 05:00:30 -0700 (PDT)` + "\r\n" + + `Received: by 10.227.11.2 with SMTP id r2mr4751812wbr.43.1318161630585;` + "\r\n" + + ` Sun, 09 Oct 2011 05:00:30 -0700 (PDT)` + "\r\n" + + `DomainKey-Status: good` + "\r\n" + + `Received-SPF: softfail (google.com: best guess record for domain of transitioning 3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com does not designate as permitted sender)` + "\r\n" + + `Received: by 10.241.227.90 with POP3 id 26mf2646912wyj.48;` + "\r\n" + + ` Sun, 09 Oct 2011 05:00:29 -0700 (PDT)` + "\r\n" + + `X-Gmail-Fetch-Info: russcox@gmail.com 1 smtp.gmail.com 995 russcox` + "\r\n" + + `Delivered-To: russcox@gmail.com` + "\r\n" + + `Received: by 10.142.76.10 with SMTP id y10cs75487wfa;` + "\r\n" + + ` Sun, 9 Oct 2011 05:00:08 -0700 (PDT)` + "\r\n" + + `Return-Path: <3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com>` + "\r\n" + + `Received-SPF: pass (google.com: domain of 3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com designates 10.52.73.100 as permitted sender) client-ip=10.52.73.100;` + "\r\n" + + `Authentication-Results: mr.google.com; spf=pass (google.com: domain of 3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com designates 10.52.73.100 as permitted sender) smtp.mail=3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com; dkim=pass header.i=3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com` + "\r\n" + + `Received: from mr.google.com ([10.52.73.100])` + "\r\n" + + ` by 10.52.73.100 with SMTP id k4mr8053242vdv.5.1318161606360 (num_hops = 1);` + "\r\n" + + ` Sun, 09 Oct 2011 05:00:06 -0700 (PDT)` + "\r\n" + + `DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;` + "\r\n" + + ` d=google.com; s=beta;` + "\r\n" + + ` h=mime-version:reply-to:auto-submitted:message-id:date:subject:from` + "\r\n" + + ` :to:content-type;` + "\r\n" + + ` bh=SGjz0F4q+eFVkoC4yzLKQKvlxTKiUsYbO/KPI+3KOE8=;` + "\r\n" + + ` b=LRBkWBW7ZZ4UJYa7b92zfHa0ZM1K1d0wP/jbgmDw2OZTWtgDICZb30dzhFUfNVdxeN` + "\r\n" + + ` kdMFbRhTLP5NpSXWhbDw==` + "\r\n" + + `MIME-Version: 1.0` + "\r\n" + + `Received: by 10.52.73.100 with SMTP id k4mr5244039vdv.5.1318161602706; Sun, 09` + "\r\n" + + ` Oct 2011 05:00:02 -0700 (PDT)` + "\r\n" + + `Reply-To: Russ Cox ` + "\r\n" + + `Auto-Submitted: auto-generated` + "\r\n" + + `Message-ID: ` + "\r\n" + + `Date: Sun, 09 Oct 2011 12:00:02 +0000` + "\r\n" + + `Subject: You have no events scheduled today.` + "\r\n" + + `From: Google Calendar ` + "\r\n" + + `To: Russ Cox ` + "\r\n" + + `Content-Type: multipart/alternative; boundary=bcaec501c5be15fc6504aedc6af3` + "\r\n" + + `` + "\r\n" + + `--bcaec501c5be15fc6504aedc6af3` + "\r\n" + + `Content-Type: text/plain; charset=ISO-8859-1; format=flowed; delsp=yes` + "\r\n" + + `` + "\r\n" + + `russcox@gmail.com, you have no events scheduled today Sun Oct 9, 2011.` + "\r\n" + + `` + "\r\n" + + `View your calendar at https://www.google.com/calendar/` + "\r\n" + + `` + "\r\n" + + `You are receiving this email at the account russcox@gmail.com because you ` + "\r\n" + + `are subscribed to receive daily agendas for the following calendars: Russ ` + "\r\n" + + `Cox.` + "\r\n" + + `` + "\r\n" + + `To change which calendars you receive daily agendas for, please log in to ` + "\r\n" + + `https://www.google.com/calendar/ and change your notification settings for ` + "\r\n" + + `each calendar.` + "\r\n" + + `` + "\r\n" + + `--bcaec501c5be15fc6504aedc6af3` + "\r\n" + + `Content-Type: text/html; charset=ISO-8859-1` + "\r\n" + + `Content-Transfer-Encoding: quoted-printable` + "\r\n" + + `` + "\r\n" + + `
3D"Google` + "\r\n" + + `

russcox@gmail.com, you have no events s=` + "\r\n" + + `cheduled today Sun Oct 9, 2011

` + "\r\n" + + `

You are=` + "\r\n" + + ` receiving this email at the account russcox@gmail.com because you are subs=` + "\r\n" + + `cribed to receive daily agendas for the following calendars: Russ Cox.

` + "\r\n" + + `

To chan=` + "\r\n" + + `ge which calendars you receive daily agendas for, please log in to https://=` + "\r\n" + + `www.google.com/calendar/ and change your notification settings for each cal=` + "\r\n" + + `endar.

` + "\r\n" + + `--bcaec501c5be15fc6504aedc6af3--` + "\r\n" + + `)` + "\r\n" + + "# OK Success\r\n", + `# UID FETCH 57046 BODY[TEXT]`: `* 146 FETCH (UID 57046 BODY[TEXT] {1647}` + "\r\n" + + `--bcaec501c5be15fc6504aedc6af3` + "\r\n" + + `Content-Type: text/plain; charset=ISO-8859-1; format=flowed; delsp=yes` + "\r\n" + + `` + "\r\n" + + `russcox@gmail.com, you have no events scheduled today Sun Oct 9, 2011.` + "\r\n" + + `` + "\r\n" + + `View your calendar at https://www.google.com/calendar/` + "\r\n" + + `` + "\r\n" + + `You are receiving this email at the account russcox@gmail.com because you ` + "\r\n" + + `are subscribed to receive daily agendas for the following calendars: Russ ` + "\r\n" + + `Cox.` + "\r\n" + + `` + "\r\n" + + `To change which calendars you receive daily agendas for, please log in to ` + "\r\n" + + `https://www.google.com/calendar/ and change your notification settings for ` + "\r\n" + + `each calendar.` + "\r\n" + + `` + "\r\n" + + `--bcaec501c5be15fc6504aedc6af3` + "\r\n" + + `Content-Type: text/html; charset=ISO-8859-1` + "\r\n" + + `Content-Transfer-Encoding: quoted-printable` + "\r\n" + + `` + "\r\n" + + `
3D"Google` + "\r\n" + + `

russcox@gmail.com, you have no events s=` + "\r\n" + + `cheduled today Sun Oct 9, 2011

` + "\r\n" + + `

You are=` + "\r\n" + + ` receiving this email at the account russcox@gmail.com because you are subs=` + "\r\n" + + `cribed to receive daily agendas for the following calendars: Russ Cox.

` + "\r\n" + + `

To chan=` + "\r\n" + + `ge which calendars you receive daily agendas for, please log in to https://=` + "\r\n" + + `www.google.com/calendar/ and change your notification settings for each cal=` + "\r\n" + + `endar.

` + "\r\n" + + `--bcaec501c5be15fc6504aedc6af3--` + "\r\n" + + `)` + "\r\n" + + "# OK Success\r\n", + `# UID FETCH 57046 BODY[HEADER]`: `* 146 FETCH (UID 57046 BODY[HEADER] {2453}` + "\r\n" + + `Delivered-To: rsc@swtch.com` + "\r\n" + + `Received: by 10.216.54.148 with SMTP id i20cs32329wec; Sun, 9 Oct 2011` + "\r\n" + + ` 05:00:30 -0700 (PDT)` + "\r\n" + + `Received: by 10.227.11.2 with SMTP id r2mr4751812wbr.43.1318161630585; Sun, 09` + "\r\n" + + ` Oct 2011 05:00:30 -0700 (PDT)` + "\r\n" + + `DomainKey-Status: good` + "\r\n" + + `Received-SPF: softfail (google.com: best guess record for domain of` + "\r\n" + + ` transitioning` + "\r\n" + + ` 3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com` + "\r\n" + + ` does not designate as permitted sender)` + "\r\n" + + `Received: by 10.241.227.90 with POP3 id 26mf2646912wyj.48; Sun, 09 Oct 2011` + "\r\n" + + ` 05:00:29 -0700 (PDT)` + "\r\n" + + `X-Gmail-Fetch-Info: russcox@gmail.com 1 smtp.gmail.com 995 russcox` + "\r\n" + + `Delivered-To: russcox@gmail.com` + "\r\n" + + `Received: by 10.142.76.10 with SMTP id y10cs75487wfa; Sun, 9 Oct 2011 05:00:08` + "\r\n" + + ` -0700 (PDT)` + "\r\n" + + `Return-Path: <3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com>` + "\r\n" + + `Received-SPF: pass (google.com: domain of` + "\r\n" + + ` 3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com` + "\r\n" + + ` designates 10.52.73.100 as permitted sender) client-ip=10.52.73.100;` + "\r\n" + + `Authentication-Results: mr.google.com; spf=pass (google.com: domain of` + "\r\n" + + ` 3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com` + "\r\n" + + ` designates 10.52.73.100 as permitted sender)` + "\r\n" + + ` smtp.mail=3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com;` + "\r\n" + + ` dkim=pass` + "\r\n" + + ` header.i=3woyRTgcJB5sMPNN7JSBH5DG.7JHMPNN7JSBH5DG.7JH@calendar-server.bounces.google.com` + "\r\n" + + `Received: from mr.google.com ([10.52.73.100]) by 10.52.73.100 with SMTP id` + "\r\n" + + ` k4mr8053242vdv.5.1318161606360 (num_hops = 1); Sun, 09 Oct 2011 05:00:06` + "\r\n" + + ` -0700 (PDT)` + "\r\n" + + `DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=beta;` + "\r\n" + + ` h=mime-version:reply-to:auto-submitted:message-id:date:subject:from` + "\r\n" + + ` :to:content-type; bh=SGjz0F4q+eFVkoC4yzLKQKvlxTKiUsYbO/KPI+3KOE8=;` + "\r\n" + + ` b=LRBkWBW7ZZ4UJYa7b92zfHa0ZM1K1d0wP/jbgmDw2OZTWtgDICZb30dzhFUfNVdxeN` + "\r\n" + + ` kdMFbRhTLP5NpSXWhbDw==` + "\r\n" + + `MIME-Version: 1.0` + "\r\n" + + `Received: by 10.52.73.100 with SMTP id k4mr5244039vdv.5.1318161602706; Sun, 09` + "\r\n" + + ` Oct 2011 05:00:02 -0700 (PDT)` + "\r\n" + + `Reply-To: Russ Cox ` + "\r\n" + + `Auto-Submitted: auto-generated` + "\r\n" + + `Message-ID: ` + "\r\n" + + `Date: Sun, 09 Oct 2011 12:00:02 +0000` + "\r\n" + + `Subject: You have no events scheduled today.` + "\r\n" + + `From: Google Calendar ` + "\r\n" + + `To: Russ Cox ` + "\r\n" + + `Content-Type: multipart/alternative; boundary=bcaec501c5be15fc6504aedc6af3` + "\r\n" + + `` + "\r\n" + + `)` + "\r\n" + + "# OK Success\r\n", + `# UID FETCH 57046 BODY[MIME]`: "# BAD Could not parse command\r\n", +} + +/* + mail sending + +package main + +import ( + "log" + "io/ioutil" + "smtp" + "time" +) +var pw, _ = ioutil.ReadFile("/Users/rsc/.swtchpass") +var msg = `From: "Russ Cox" +To: "Russ Cox" +Subject: test from Go + +This is a message sent from Go +` + +BUG: Does not *REQUIRE* auth. Should. + +func main() { + auth := smtp.PlainAuth( + "", + "rsc@swtch.com", + string(pw), + "smtp.gmail.com", + ) + if err := smtp.SendMail("smtp.gmail.com:587", auth, "rsc@swtch.com", []string{"rsc@google.com"}, []byte(msg+time.LocalTime().String())); err != nil { + log.Fatal(err) + } + println("SENT") +} +*/ diff --git a/vendor/github.com/mattermost/rsc/imap/mail.go b/vendor/github.com/mattermost/rsc/imap/mail.go new file mode 100644 index 000000000..365540f82 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/imap/mail.go @@ -0,0 +1,468 @@ +package imap + +import ( + "bytes" + "fmt" + "log" + "regexp" + "sort" + "strings" + "time" +) + +type Flags uint32 + +const ( + FlagJunk Flags = 1 << iota + FlagNonJunk + FlagReplied + FlagFlagged + FlagDeleted + FlagDraft + FlagRecent + FlagSeen + FlagNoInferiors + FlagNoSelect + FlagMarked + FlagUnMarked + FlagHasChildren + FlagHasNoChildren + FlagInbox // Gmail extension + FlagAllMail // Gmail extension + FlagDrafts // Gmail extension + FlagSent // Gmail extension + FlagSpam // Gmail extension + FlagStarred // Gmail extension + FlagTrash // Gmail extension + FlagImportant // Gmail extension +) + +var flagNames = []string{ + "Junk", + "NonJunk", + "\\Answered", + "\\Flagged", + "\\Deleted", + "\\Draft", + "\\Recent", + "\\Seen", + "\\NoInferiors", + "\\NoSelect", + "\\Marked", + "\\UnMarked", + "\\HasChildren", + "\\HasNoChildren", + "\\Inbox", + "\\AllMail", + "\\Drafts", + "\\Sent", + "\\Spam", + "\\Starred", + "\\Trash", + "\\Important", +} + +// A Box represents an IMAP mailbox. +type Box struct { + Name string // name of mailbox + Elem string // last element in name + Client *Client + + parent *Box // parent in hierarchy + child []*Box // child boxes + dead bool // box no longer exists + inbox bool // box is inbox + flags Flags // allowed flags + permFlags Flags // client-modifiable permanent flags + readOnly bool // box is read-only + + exists int // number of messages in box (according to server) + maxSeen int // maximum message number seen (for polling) + unseen int // number of first unseen message + validity uint32 // UID validity base number + load bool // if false, don't track full set of messages + firstNum int // 0 means box not loaded + msgByNum []*Msg + msgByUID map[uint64]*Msg +} + +func (c *Client) Boxes() []*Box { + c.data.lock() + defer c.data.unlock() + + box := make([]*Box, len(c.allBox)) + copy(box, c.allBox) + return box +} + +func (c *Client) Box(name string) *Box { + c.data.lock() + defer c.data.unlock() + + return c.boxByName[name] +} + +func (c *Client) Inbox() *Box { + c.data.lock() + defer c.data.unlock() + + return c.inbox +} + +func (c *Client) newBox(name, sep string, inbox bool) *Box { + c.data.mustBeLocked() + if b := c.boxByName[name]; b != nil { + return b + } + + b := &Box{ + Name: name, + Elem: name, + Client: c, + inbox: inbox, + } + if !inbox { + b.parent = c.rootBox + } + if !inbox && sep != "" && name != c.root { + if i := strings.LastIndex(name, sep); i >= 0 { + b.Elem = name[i+len(sep):] + b.parent = c.newBox(name[:i], sep, false) + } + } + c.allBox = append(c.allBox, b) + c.boxByName[name] = b + if b.parent != nil { + b.parent.child = append(b.parent.child, b) + } + return b +} + +// A Msg represents an IMAP message. +type Msg struct { + Box *Box // box containing message + Date time.Time // date + Flags Flags // message flags + Bytes int64 // size in bytes + Lines int64 // number of lines + Hdr *MsgHdr // MIME header + Root MsgPart // top-level message part + GmailID uint64 // Gmail message id + GmailThread uint64 // Gmail thread id + UID uint64 // unique id for this message + + deleted bool + dead bool + num int // message number in box (changes) +} + +// TODO: Return os.Error too + +type byUID []*Msg + +func (x byUID) Len() int { return len(x) } +func (x byUID) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byUID) Less(i, j int) bool { return x[i].UID < x[j].UID } + +func (b *Box) Msgs() []*Msg { + b.Client.data.lock() + defer b.Client.data.unlock() + + msgs := make([]*Msg, len(b.msgByUID)) + n := 0 + for _, m := range b.msgByUID { + msgs[n] = m + n++ + } + sort.Sort(byUID(msgs)) + return msgs +} + +func (b *Box) newMsg(uid uint64, id int) *Msg { + b.Client.data.mustBeLocked() + if m := b.msgByUID[uid]; m != nil { + return m + } + if b.msgByUID == nil { + b.msgByUID = map[uint64]*Msg{} + } + m := &Msg{ + UID: uid, + Box: b, + num: id, + } + m.Root.Msg = m + if b.load { + if b.firstNum == 0 { + b.firstNum = id + } + if id < b.firstNum { + log.Printf("warning: unexpected id %d < %d", id, b.firstNum) + byNum := make([]*Msg, len(b.msgByNum)+b.firstNum-id) + copy(byNum[b.firstNum-id:], b.msgByNum) + b.msgByNum = byNum + b.firstNum = id + } + if id-b.firstNum < len(b.msgByNum) { + b.msgByNum[id-b.firstNum] = m + } else { + if id-b.firstNum > len(b.msgByNum) { + log.Printf("warning: unexpected id %d > %d", id, b.firstNum+len(b.msgByNum)) + byNum := make([]*Msg, id-b.firstNum) + copy(byNum, b.msgByNum) + b.msgByNum = byNum + } + b.msgByNum = append(b.msgByNum, m) + } + } + b.msgByUID[uid] = m + return m +} + +func (b *Box) Delete(msgs []*Msg) error { + for _, m := range msgs { + if m.Box != b { + return fmt.Errorf("messages not from this box") + } + } + b.Client.io.lock() + defer b.Client.io.unlock() + err := b.Client.deleteList(msgs) + if err == nil { + b.Client.data.lock() + defer b.Client.data.unlock() + for _, m := range msgs { + if m.Flags&FlagDeleted != 0 { + delete(b.msgByUID, m.UID) + } + } + } + return err +} + +func (b *Box) Copy(msgs []*Msg) error { + if len(msgs) == 0 { + return nil + } + src := msgs[0].Box + for _, m := range msgs { + if m.Box != src { + return fmt.Errorf("messages span boxes: %q and %q", src.Name, m.Box.Name) + } + } + b.Client.io.lock() + defer b.Client.io.unlock() + return b.Client.copyList(b, src, msgs) +} + +func (b *Box) Mute(msgs []*Msg) error { + if len(msgs) == 0 { + return nil + } + for _, m := range msgs { + if m.Box != b { + return fmt.Errorf("messages not from this box") + } + } + b.Client.io.lock() + defer b.Client.io.unlock() + return b.Client.muteList(b, msgs) +} + +func (b *Box) Check() error { + b.Client.io.lock() + defer b.Client.io.unlock() + + return b.Client.check(b) +} + +func (m *Msg) Deleted() bool { + // Racy but okay. Can add a lock later if it matters. + return m.Flags&FlagDeleted != 0 +} + +// A Hdr represents a message header. +type MsgHdr struct { + Date string + Subject string + From []Addr + Sender []Addr + ReplyTo []Addr + To []Addr + CC []Addr + BCC []Addr + InReplyTo string + MessageID string + Digest string +} + +// An Addr represents a single, named email address. +// If Name is empty, only the email address is known. +// If Email is empty, the Addr represents an unspecified (but named) group. +type Addr struct { + Name string + Email string +} + +func (a Addr) String() string { + if a.Email == "" { + return a.Name + } + if a.Name == "" { + return a.Email + } + return a.Name + " <" + a.Email + ">" +} + +// A MsgPart represents a single part of a MIME-encoded message. +type MsgPart struct { + Msg *Msg // containing message + Type string + ContentID string + Desc string + Encoding string + Bytes int64 + Lines int64 + Charset string + Name string + Hdr *MsgHdr + ID string + Child []*MsgPart + + raw []byte // raw message + rawHeader []byte // raw RFC-2822 header, for message/rfc822 + rawBody []byte // raw RFC-2822 body, for message/rfc822 + mimeHeader []byte // mime header, for attachments +} + +func (p *MsgPart) newPart() *MsgPart { + p.Msg.Box.Client.data.mustBeLocked() + dot := "." + if p.ID == "" { // no dot at root + dot = "" + } + pp := &MsgPart{ + Msg: p.Msg, + ID: fmt.Sprint(p.ID, dot, 1+len(p.Child)), + } + p.Child = append(p.Child, pp) + return pp +} + +func (p *MsgPart) Text() []byte { + c := p.Msg.Box.Client + var raw []byte + c.data.lock() + if p == &p.Msg.Root { + raw = p.rawBody + c.data.unlock() + if raw == nil { + c.io.lock() + if raw = p.rawBody; raw == nil { + c.fetch(p, "TEXT") + raw = p.rawBody + } + c.io.unlock() + } + } else { + raw = p.raw + c.data.unlock() + if raw == nil { + c.io.lock() + if raw = p.raw; raw == nil { + c.fetch(p, "") + raw = p.raw + } + c.io.unlock() + } + } + return decodeText(raw, p.Encoding, p.Charset, false) +} + +func (p *MsgPart) Raw() []byte { + c := p.Msg.Box.Client + var raw []byte + c.data.lock() + raw = p.rawBody + c.data.unlock() + if raw == nil { + c.io.lock() + if raw = p.rawBody; raw == nil { + c.fetch(p, "") + raw = p.rawBody + } + c.io.unlock() + } + return raw +} + +var sigDash = []byte("\n--\n") +var quote = []byte("\n> ") +var nl = []byte("\n") + +var onwrote = regexp.MustCompile(`\A\s*On .* wrote:\s*\z`) + +func (p *MsgPart) ShortText() []byte { + t := p.Text() + + return shortText(t) +} + +func shortText(t []byte) []byte { + if t == nil { + return nil + } + + // Cut signature. + i := bytes.LastIndex(t, sigDash) + j := bytes.LastIndex(t, quote) + if i > j && bytes.Count(t[i+1:], nl) <= 10 { + t = t[:i+1] + } + + // Cut trailing quoted text. + for { + rest, last := lastLine(t) + trim := bytes.TrimSpace(last) + if len(rest) < len(t) && (len(trim) == 0 || trim[0] == '>') { + t = rest + continue + } + break + } + + // Cut 'On foo.*wrote:' line. + rest, last := lastLine(t) + if onwrote.Match(last) { + t = rest + } + + // Cut trailing blank lines. + for { + rest, last := lastLine(t) + trim := bytes.TrimSpace(last) + if len(rest) < len(t) && len(trim) == 0 { + t = rest + continue + } + break + } + + // Cut signature again. + i = bytes.LastIndex(t, sigDash) + j = bytes.LastIndex(t, quote) + if i > j && bytes.Count(t[i+1:], nl) <= 10 { + t = t[:i+1] + } + + return t +} + +func lastLine(t []byte) (rest, last []byte) { + n := len(t) + if n > 0 && t[n-1] == '\n' { + n-- + } + j := bytes.LastIndex(t[:n], nl) + return t[:j+1], t[j+1:] +} diff --git a/vendor/github.com/mattermost/rsc/imap/mail_test.go b/vendor/github.com/mattermost/rsc/imap/mail_test.go new file mode 100644 index 000000000..3c1aec860 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/imap/mail_test.go @@ -0,0 +1,335 @@ +package imap + +import "testing" + +var shortTextTests = []struct { + in, out string +}{ + { + in: `From: Brad Fitzpatrick +Date: Tue Oct 18 18:23:11 EDT 2011 +To: r@golang.org, golang-dev@googlegroups.com, reply@codereview.appspotmail.com +Subject: Re: [golang-dev] code review 5307043: rpc: don't panic on write error. (issue 5307043) + +Here's a test: + +bradfitz@gopher:~/go/src/pkg/rpc$ hg diff +diff -r b7f9a5e9b87f src/pkg/rpc/server_test.go +--- a/src/pkg/rpc/server_test.go Tue Oct 18 17:01:42 2011 -0500 ++++ b/src/pkg/rpc/server_test.go Tue Oct 18 15:22:19 2011 -0700 +@@ -467,6 +467,27 @@ + fmt.Printf("mallocs per HTTP rpc round trip: %d\n", +countMallocs(dialHTTP, t)) + } + ++type writeCrasher struct{} ++ ++func (writeCrasher) Close() os.Error { ++ return nil ++} ++ ++func (writeCrasher) Read(p []byte) (int, os.Error) { ++ return 0, os.EOF ++} ++ ++func (writeCrasher) Write(p []byte) (int, os.Error) { ++ return 0, os.NewError("fake write failure") ++} ++ ++func TestClientWriteError(t *testing.T) { ++ c := NewClient(writeCrasher{}) ++ res := false ++ c.Call("foo", 1, &res) ++} ++ + func benchmarkEndToEnd(dial func() (*Client, os.Error), b *testing.B) { + b.StopTimer() + once.Do(startServer) + + +On Tue, Oct 18, 2011 at 3:12 PM, wrote: + +> Reviewers: golang-dev_googlegroups.com, +> +> Message: +> Hello golang-dev@googlegroups.com, +> +> I'd like you to review this change to +> https://go.googlecode.com/hg/ +> +> +> Description: +> rpc: don't panic on write error. +> The mechanism to record the error in the call is already in place. +> Fixes issue 2382. +> +> Please review this at http://codereview.appspot.com/**5307043/ +> +> Affected files: +> M src/pkg/rpc/client.go +> +> +> Index: src/pkg/rpc/client.go +> ==============================**==============================**======= +> --- a/src/pkg/rpc/client.go +> +++ b/src/pkg/rpc/client.go +> @@ -85,7 +85,8 @@ +> client.request.Seq = c.seq +> client.request.ServiceMethod = c.ServiceMethod +> if err := client.codec.WriteRequest(&**client.request, c.Args); err +> != nil { +> - panic("rpc: client encode error: " + err.String()) +> + c.Error = err +> + c.done() +> } +> } +> +> @@ -251,10 +252,10 @@ +> // the same Call object. If done is nil, Go will allocate a new channel. +> // If non-nil, done must be buffered or Go will deliberately crash. +> func (client *Client) Go(serviceMethod string, args interface{}, reply +> interface{}, done chan *Call) *Call { +> - c := new(Call) +> - c.ServiceMethod = serviceMethod +> - c.Args = args +> - c.Reply = reply +> + call := new(Call) +> + call.ServiceMethod = serviceMethod +> + call.Args = args +> + call.Reply = reply +> if done == nil { +> done = make(chan *Call, 10) // buffered. +> } else { +> @@ -266,14 +267,14 @@ +> log.Panic("rpc: done channel is unbuffered") +> } +> } +> - c.Done = done +> + call.Done = done +> if client.shutdown { +> - c.Error = ErrShutdown +> - c.done() +> - return c +> + call.Error = ErrShutdown +> + call.done() +> + return call +> } +> - client.send(c) +> - return c +> + client.send(call) +> + return call +> } +> +> // Call invokes the named function, waits for it to complete, and returns +> its error status. +> +> +> + +`, + out: `From: Brad Fitzpatrick +Date: Tue Oct 18 18:23:11 EDT 2011 +To: r@golang.org, golang-dev@googlegroups.com, reply@codereview.appspotmail.com +Subject: Re: [golang-dev] code review 5307043: rpc: don't panic on write error. (issue 5307043) + +Here's a test: + +bradfitz@gopher:~/go/src/pkg/rpc$ hg diff +diff -r b7f9a5e9b87f src/pkg/rpc/server_test.go +--- a/src/pkg/rpc/server_test.go Tue Oct 18 17:01:42 2011 -0500 ++++ b/src/pkg/rpc/server_test.go Tue Oct 18 15:22:19 2011 -0700 +@@ -467,6 +467,27 @@ + fmt.Printf("mallocs per HTTP rpc round trip: %d\n", +countMallocs(dialHTTP, t)) + } + ++type writeCrasher struct{} ++ ++func (writeCrasher) Close() os.Error { ++ return nil ++} ++ ++func (writeCrasher) Read(p []byte) (int, os.Error) { ++ return 0, os.EOF ++} ++ ++func (writeCrasher) Write(p []byte) (int, os.Error) { ++ return 0, os.NewError("fake write failure") ++} ++ ++func TestClientWriteError(t *testing.T) { ++ c := NewClient(writeCrasher{}) ++ res := false ++ c.Call("foo", 1, &res) ++} ++ + func benchmarkEndToEnd(dial func() (*Client, os.Error), b *testing.B) { + b.StopTimer() + once.Do(startServer) +`, + }, + { + in: `From: David Symonds +Date: Tue Oct 18 18:17:52 EDT 2011 +To: reply@codereview.appspotmail.com, r@golang.org, golang-dev@googlegroups.com +Subject: Re: [golang-dev] code review 5307043: rpc: don't panic on write error. (issue 5307043) + +LGTM +On Oct 19, 2011 9:12 AM, wrote: + +> Reviewers: golang-dev_googlegroups.com, +> +> Message: +> Hello golang-dev@googlegroups.com, +> +> I'd like you to review this change to +> https://go.googlecode.com/hg/ +> +> +> Description: +> rpc: don't panic on write error. +> The mechanism to record the error in the call is already in place. +> Fixes issue 2382. +> +> Please review this at http://codereview.appspot.com/**5307043/ +> +> Affected files: +> M src/pkg/rpc/client.go +> +> +> Index: src/pkg/rpc/client.go +> ==============================**==============================**======= +> --- a/src/pkg/rpc/client.go +> +++ b/src/pkg/rpc/client.go +> @@ -85,7 +85,8 @@ +> client.request.Seq = c.seq +> client.request.ServiceMethod = c.ServiceMethod +> if err := client.codec.WriteRequest(&**client.request, c.Args); err +> != nil { +> - panic("rpc: client encode error: " + err.String()) +> + c.Error = err +> + c.done() +> } +> } +> +> @@ -251,10 +252,10 @@ +> // the same Call object. If done is nil, Go will allocate a new channel. +> // If non-nil, done must be buffered or Go will deliberately crash. +> func (client *Client) Go(serviceMethod string, args interface{}, reply +> interface{}, done chan *Call) *Call { +> - c := new(Call) +> - c.ServiceMethod = serviceMethod +> - c.Args = args +> - c.Reply = reply +> + call := new(Call) +> + call.ServiceMethod = serviceMethod +> + call.Args = args +> + call.Reply = reply +> if done == nil { +> done = make(chan *Call, 10) // buffered. +> } else { +> @@ -266,14 +267,14 @@ +> log.Panic("rpc: done channel is unbuffered") +> } +> } +> - c.Done = done +> + call.Done = done +> if client.shutdown { +> - c.Error = ErrShutdown +> - c.done() +> - return c +> + call.Error = ErrShutdown +> + call.done() +> + return call +> } +> - client.send(c) +> - return c +> + client.send(call) +> + return call +> } +> +> // Call invokes the named function, waits for it to complete, and returns +> its error status. +> +> +> + +`, + out: `From: David Symonds +Date: Tue Oct 18 18:17:52 EDT 2011 +To: reply@codereview.appspotmail.com, r@golang.org, golang-dev@googlegroups.com +Subject: Re: [golang-dev] code review 5307043: rpc: don't panic on write error. (issue 5307043) + +LGTM +`, + }, + { + in: `From: Brad Fitzpatrick +Date: Tue Oct 18 23:26:07 EDT 2011 +To: rsc@golang.org, golang-dev@googlegroups.com, reply@codereview.appspotmail.com +Subject: Re: [golang-dev] code review 5297044: gotest: use $GCFLAGS like make does (issue 5297044) + +LGTM + +On Tue, Oct 18, 2011 at 7:52 PM, wrote: + +> Reviewers: golang-dev_googlegroups.com, +> +> Message: +> Hello golang-dev@googlegroups.com, +> +> I'd like you to review this change to +> https://go.googlecode.com/hg/ +> +> +> Description: +> gotest: use $GCFLAGS like make does +> +> Please review this at http://codereview.appspot.com/**5297044/ +> +> Affected files: +> M src/cmd/gotest/gotest.go +> +> +> Index: src/cmd/gotest/gotest.go +> ==============================**==============================**======= +> --- a/src/cmd/gotest/gotest.go +> +++ b/src/cmd/gotest/gotest.go +> @@ -153,8 +153,12 @@ +> if gc == "" { +> gc = O + "g" +> } +> - XGC = []string{gc, "-I", "_test", "-o", "_xtest_." + O} +> - GC = []string{gc, "-I", "_test", "_testmain.go"} +> + var gcflags []string +> + if gf := strings.TrimSpace(os.Getenv("**GCFLAGS")); gf != "" { +> + gcflags = strings.Fields(gf) +> + } +> + XGC = append([]string{gc, "-I", "_test", "-o", "_xtest_." + O}, +> gcflags...) +> + GC = append(append([]string{gc, "-I", "_test"}, gcflags...), +> "_testmain.go") +> gl := os.Getenv("GL") +> if gl == "" { +> gl = O + "l" +> +> +> +`, + out: `From: Brad Fitzpatrick +Date: Tue Oct 18 23:26:07 EDT 2011 +To: rsc@golang.org, golang-dev@googlegroups.com, reply@codereview.appspotmail.com +Subject: Re: [golang-dev] code review 5297044: gotest: use $GCFLAGS like make does (issue 5297044) + +LGTM +`, + }, +} + +func TestShortText(t *testing.T) { + for i, tt := range shortTextTests { + if out := string(shortText([]byte(tt.in))); out != tt.out { + t.Errorf("#%d: = %q, want %q\n", i, out, tt.out) + } + } +} diff --git a/vendor/github.com/mattermost/rsc/imap/rfc2045.txt b/vendor/github.com/mattermost/rsc/imap/rfc2045.txt new file mode 100644 index 000000000..9f286b1a9 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/imap/rfc2045.txt @@ -0,0 +1,1739 @@ + + + + + + +Network Working Group N. Freed +Request for Comments: 2045 Innosoft +Obsoletes: 1521, 1522, 1590 N. Borenstein +Category: Standards Track First Virtual + November 1996 + + + Multipurpose Internet Mail Extensions + (MIME) Part One: + Format of Internet Message Bodies + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Abstract + + STD 11, RFC 822, defines a message representation protocol specifying + considerable detail about US-ASCII message headers, and leaves the + message content, or message body, as flat US-ASCII text. This set of + documents, collectively called the Multipurpose Internet Mail + Extensions, or MIME, redefines the format of messages to allow for + + (1) textual message bodies in character sets other than + US-ASCII, + + (2) an extensible set of different formats for non-textual + message bodies, + + (3) multi-part message bodies, and + + (4) textual header information in character sets other than + US-ASCII. + + These documents are based on earlier work documented in RFC 934, STD + 11, and RFC 1049, but extends and revises them. Because RFC 822 said + so little about message bodies, these documents are largely + orthogonal to (rather than a revision of) RFC 822. + + This initial document specifies the various headers used to describe + the structure of MIME messages. The second document, RFC 2046, + defines the general structure of the MIME media typing system and + defines an initial set of media types. The third document, RFC 2047, + describes extensions to RFC 822 to allow non-US-ASCII text data in + + + +Freed & Borenstein Standards Track [Page 1] + +RFC 2045 Internet Message Bodies November 1996 + + + Internet mail header fields. The fourth document, RFC 2048, specifies + various IANA registration procedures for MIME-related facilities. The + fifth and final document, RFC 2049, describes MIME conformance + criteria as well as providing some illustrative examples of MIME + message formats, acknowledgements, and the bibliography. + + These documents are revisions of RFCs 1521, 1522, and 1590, which + themselves were revisions of RFCs 1341 and 1342. An appendix in RFC + 2049 describes differences and changes from previous versions. + +Table of Contents + + 1. Introduction ......................................... 3 + 2. Definitions, Conventions, and Generic BNF Grammar .... 5 + 2.1 CRLF ................................................ 5 + 2.2 Character Set ....................................... 6 + 2.3 Message ............................................. 6 + 2.4 Entity .............................................. 6 + 2.5 Body Part ........................................... 7 + 2.6 Body ................................................ 7 + 2.7 7bit Data ........................................... 7 + 2.8 8bit Data ........................................... 7 + 2.9 Binary Data ......................................... 7 + 2.10 Lines .............................................. 7 + 3. MIME Header Fields ................................... 8 + 4. MIME-Version Header Field ............................ 8 + 5. Content-Type Header Field ............................ 10 + 5.1 Syntax of the Content-Type Header Field ............. 12 + 5.2 Content-Type Defaults ............................... 14 + 6. Content-Transfer-Encoding Header Field ............... 14 + 6.1 Content-Transfer-Encoding Syntax .................... 14 + 6.2 Content-Transfer-Encodings Semantics ................ 15 + 6.3 New Content-Transfer-Encodings ...................... 16 + 6.4 Interpretation and Use .............................. 16 + 6.5 Translating Encodings ............................... 18 + 6.6 Canonical Encoding Model ............................ 19 + 6.7 Quoted-Printable Content-Transfer-Encoding .......... 19 + 6.8 Base64 Content-Transfer-Encoding .................... 24 + 7. Content-ID Header Field .............................. 26 + 8. Content-Description Header Field ..................... 27 + 9. Additional MIME Header Fields ........................ 27 + 10. Summary ............................................. 27 + 11. Security Considerations ............................. 27 + 12. Authors' Addresses .................................. 28 + A. Collected Grammar .................................... 29 + + + + + + +Freed & Borenstein Standards Track [Page 2] + +RFC 2045 Internet Message Bodies November 1996 + + +1. Introduction + + Since its publication in 1982, RFC 822 has defined the standard + format of textual mail messages on the Internet. Its success has + been such that the RFC 822 format has been adopted, wholly or + partially, well beyond the confines of the Internet and the Internet + SMTP transport defined by RFC 821. As the format has seen wider use, + a number of limitations have proven increasingly restrictive for the + user community. + + RFC 822 was intended to specify a format for text messages. As such, + non-text messages, such as multimedia messages that might include + audio or images, are simply not mentioned. Even in the case of text, + however, RFC 822 is inadequate for the needs of mail users whose + languages require the use of character sets richer than US-ASCII. + Since RFC 822 does not specify mechanisms for mail containing audio, + video, Asian language text, or even text in most European languages, + additional specifications are needed. + + One of the notable limitations of RFC 821/822 based mail systems is + the fact that they limit the contents of electronic mail messages to + relatively short lines (e.g. 1000 characters or less [RFC-821]) of + 7bit US-ASCII. This forces users to convert any non-textual data + that they may wish to send into seven-bit bytes representable as + printable US-ASCII characters before invoking a local mail UA (User + Agent, a program with which human users send and receive mail). + Examples of such encodings currently used in the Internet include + pure hexadecimal, uuencode, the 3-in-4 base 64 scheme specified in + RFC 1421, the Andrew Toolkit Representation [ATK], and many others. + + The limitations of RFC 822 mail become even more apparent as gateways + are designed to allow for the exchange of mail messages between RFC + 822 hosts and X.400 hosts. X.400 [X400] specifies mechanisms for the + inclusion of non-textual material within electronic mail messages. + The current standards for the mapping of X.400 messages to RFC 822 + messages specify either that X.400 non-textual material must be + converted to (not encoded in) IA5Text format, or that they must be + discarded, notifying the RFC 822 user that discarding has occurred. + This is clearly undesirable, as information that a user may wish to + receive is lost. Even though a user agent may not have the + capability of dealing with the non-textual material, the user might + have some mechanism external to the UA that can extract useful + information from the material. Moreover, it does not allow for the + fact that the message may eventually be gatewayed back into an X.400 + message handling system (i.e., the X.400 message is "tunneled" + through Internet mail), where the non-textual information would + definitely become useful again. + + + + +Freed & Borenstein Standards Track [Page 3] + +RFC 2045 Internet Message Bodies November 1996 + + + This document describes several mechanisms that combine to solve most + of these problems without introducing any serious incompatibilities + with the existing world of RFC 822 mail. In particular, it + describes: + + (1) A MIME-Version header field, which uses a version + number to declare a message to be conformant with MIME + and allows mail processing agents to distinguish + between such messages and those generated by older or + non-conformant software, which are presumed to lack + such a field. + + (2) A Content-Type header field, generalized from RFC 1049, + which can be used to specify the media type and subtype + of data in the body of a message and to fully specify + the native representation (canonical form) of such + data. + + (3) A Content-Transfer-Encoding header field, which can be + used to specify both the encoding transformation that + was applied to the body and the domain of the result. + Encoding transformations other than the identity + transformation are usually applied to data in order to + allow it to pass through mail transport mechanisms + which may have data or character set limitations. + + (4) Two additional header fields that can be used to + further describe the data in a body, the Content-ID and + Content-Description header fields. + + All of the header fields defined in this document are subject to the + general syntactic rules for header fields specified in RFC 822. In + particular, all of these header fields except for Content-Disposition + can include RFC 822 comments, which have no semantic content and + should be ignored during MIME processing. + + Finally, to specify and promote interoperability, RFC 2049 provides a + basic applicability statement for a subset of the above mechanisms + that defines a minimal level of "conformance" with this document. + + HISTORICAL NOTE: Several of the mechanisms described in this set of + documents may seem somewhat strange or even baroque at first reading. + It is important to note that compatibility with existing standards + AND robustness across existing practice were two of the highest + priorities of the working group that developed this set of documents. + In particular, compatibility was always favored over elegance. + + + + + +Freed & Borenstein Standards Track [Page 4] + +RFC 2045 Internet Message Bodies November 1996 + + + Please refer to the current edition of the "Internet Official + Protocol Standards" for the standardization state and status of this + protocol. RFC 822 and STD 3, RFC 1123 also provide essential + background for MIME since no conforming implementation of MIME can + violate them. In addition, several other informational RFC documents + will be of interest to the MIME implementor, in particular RFC 1344, + RFC 1345, and RFC 1524. + +2. Definitions, Conventions, and Generic BNF Grammar + + Although the mechanisms specified in this set of documents are all + described in prose, most are also described formally in the augmented + BNF notation of RFC 822. Implementors will need to be familiar with + this notation in order to understand this set of documents, and are + referred to RFC 822 for a complete explanation of the augmented BNF + notation. + + Some of the augmented BNF in this set of documents makes named + references to syntax rules defined in RFC 822. A complete formal + grammar, then, is obtained by combining the collected grammar + appendices in each document in this set with the BNF of RFC 822 plus + the modifications to RFC 822 defined in RFC 1123 (which specifically + changes the syntax for `return', `date' and `mailbox'). + + All numeric and octet values are given in decimal notation in this + set of documents. All media type values, subtype values, and + parameter names as defined are case-insensitive. However, parameter + values are case-sensitive unless otherwise specified for the specific + parameter. + + FORMATTING NOTE: Notes, such at this one, provide additional + nonessential information which may be skipped by the reader without + missing anything essential. The primary purpose of these non- + essential notes is to convey information about the rationale of this + set of documents, or to place these documents in the proper + historical or evolutionary context. Such information may in + particular be skipped by those who are focused entirely on building a + conformant implementation, but may be of use to those who wish to + understand why certain design choices were made. + +2.1. CRLF + + The term CRLF, in this set of documents, refers to the sequence of + octets corresponding to the two US-ASCII characters CR (decimal value + 13) and LF (decimal value 10) which, taken together, in this order, + denote a line break in RFC 822 mail. + + + + + +Freed & Borenstein Standards Track [Page 5] + +RFC 2045 Internet Message Bodies November 1996 + + +2.2. Character Set + + The term "character set" is used in MIME to refer to a method of + converting a sequence of octets into a sequence of characters. Note + that unconditional and unambiguous conversion in the other direction + is not required, in that not all characters may be representable by a + given character set and a character set may provide more than one + sequence of octets to represent a particular sequence of characters. + + This definition is intended to allow various kinds of character + encodings, from simple single-table mappings such as US-ASCII to + complex table switching methods such as those that use ISO 2022's + techniques, to be used as character sets. However, the definition + associated with a MIME character set name must fully specify the + mapping to be performed. In particular, use of external profiling + information to determine the exact mapping is not permitted. + + NOTE: The term "character set" was originally to describe such + straightforward schemes as US-ASCII and ISO-8859-1 which have a + simple one-to-one mapping from single octets to single characters. + Multi-octet coded character sets and switching techniques make the + situation more complex. For example, some communities use the term + "character encoding" for what MIME calls a "character set", while + using the phrase "coded character set" to denote an abstract mapping + from integers (not octets) to characters. + +2.3. Message + + The term "message", when not further qualified, means either a + (complete or "top-level") RFC 822 message being transferred on a + network, or a message encapsulated in a body of type "message/rfc822" + or "message/partial". + +2.4. Entity + + The term "entity", refers specifically to the MIME-defined header + fields and contents of either a message or one of the parts in the + body of a multipart entity. The specification of such entities is + the essence of MIME. Since the contents of an entity are often + called the "body", it makes sense to speak about the body of an + entity. Any sort of field may be present in the header of an entity, + but only those fields whose names begin with "content-" actually have + any MIME-related meaning. Note that this does NOT imply thay they + have no meaning at all -- an entity that is also a message has non- + MIME header fields whose meanings are defined by RFC 822. + + + + + + +Freed & Borenstein Standards Track [Page 6] + +RFC 2045 Internet Message Bodies November 1996 + + +2.5. Body Part + + The term "body part" refers to an entity inside of a multipart + entity. + +2.6. Body + + The term "body", when not further qualified, means the body of an + entity, that is, the body of either a message or of a body part. + + NOTE: The previous four definitions are clearly circular. This is + unavoidable, since the overall structure of a MIME message is indeed + recursive. + +2.7. 7bit Data + + "7bit data" refers to data that is all represented as relatively + short lines with 998 octets or less between CRLF line separation + sequences [RFC-821]. No octets with decimal values greater than 127 + are allowed and neither are NULs (octets with decimal value 0). CR + (decimal value 13) and LF (decimal value 10) octets only occur as + part of CRLF line separation sequences. + +2.8. 8bit Data + + "8bit data" refers to data that is all represented as relatively + short lines with 998 octets or less between CRLF line separation + sequences [RFC-821]), but octets with decimal values greater than 127 + may be used. As with "7bit data" CR and LF octets only occur as part + of CRLF line separation sequences and no NULs are allowed. + +2.9. Binary Data + + "Binary data" refers to data where any sequence of octets whatsoever + is allowed. + +2.10. Lines + + "Lines" are defined as sequences of octets separated by a CRLF + sequences. This is consistent with both RFC 821 and RFC 822. + "Lines" only refers to a unit of data in a message, which may or may + not correspond to something that is actually displayed by a user + agent. + + + + + + + + +Freed & Borenstein Standards Track [Page 7] + +RFC 2045 Internet Message Bodies November 1996 + + +3. MIME Header Fields + + MIME defines a number of new RFC 822 header fields that are used to + describe the content of a MIME entity. These header fields occur in + at least two contexts: + + (1) As part of a regular RFC 822 message header. + + (2) In a MIME body part header within a multipart + construct. + + The formal definition of these header fields is as follows: + + entity-headers := [ content CRLF ] + [ encoding CRLF ] + [ id CRLF ] + [ description CRLF ] + *( MIME-extension-field CRLF ) + + MIME-message-headers := entity-headers + fields + version CRLF + ; The ordering of the header + ; fields implied by this BNF + ; definition should be ignored. + + MIME-part-headers := entity-headers + [ fields ] + ; Any field not beginning with + ; "content-" can have no defined + ; meaning and may be ignored. + ; The ordering of the header + ; fields implied by this BNF + ; definition should be ignored. + + The syntax of the various specific MIME header fields will be + described in the following sections. + +4. MIME-Version Header Field + + Since RFC 822 was published in 1982, there has really been only one + format standard for Internet messages, and there has been little + perceived need to declare the format standard in use. This document + is an independent specification that complements RFC 822. Although + the extensions in this document have been defined in such a way as to + be compatible with RFC 822, there are still circumstances in which it + might be desirable for a mail-processing agent to know whether a + message was composed with the new standard in mind. + + + +Freed & Borenstein Standards Track [Page 8] + +RFC 2045 Internet Message Bodies November 1996 + + + Therefore, this document defines a new header field, "MIME-Version", + which is to be used to declare the version of the Internet message + body format standard in use. + + Messages composed in accordance with this document MUST include such + a header field, with the following verbatim text: + + MIME-Version: 1.0 + + The presence of this header field is an assertion that the message + has been composed in compliance with this document. + + Since it is possible that a future document might extend the message + format standard again, a formal BNF is given for the content of the + MIME-Version field: + + version := "MIME-Version" ":" 1*DIGIT "." 1*DIGIT + + Thus, future format specifiers, which might replace or extend "1.0", + are constrained to be two integer fields, separated by a period. If + a message is received with a MIME-version value other than "1.0", it + cannot be assumed to conform with this document. + + Note that the MIME-Version header field is required at the top level + of a message. It is not required for each body part of a multipart + entity. It is required for the embedded headers of a body of type + "message/rfc822" or "message/partial" if and only if the embedded + message is itself claimed to be MIME-conformant. + + It is not possible to fully specify how a mail reader that conforms + with MIME as defined in this document should treat a message that + might arrive in the future with some value of MIME-Version other than + "1.0". + + It is also worth noting that version control for specific media types + is not accomplished using the MIME-Version mechanism. In particular, + some formats (such as application/postscript) have version numbering + conventions that are internal to the media format. Where such + conventions exist, MIME does nothing to supersede them. Where no + such conventions exist, a MIME media type might use a "version" + parameter in the content-type field if necessary. + + + + + + + + + + +Freed & Borenstein Standards Track [Page 9] + +RFC 2045 Internet Message Bodies November 1996 + + + NOTE TO IMPLEMENTORS: When checking MIME-Version values any RFC 822 + comment strings that are present must be ignored. In particular, the + following four MIME-Version fields are equivalent: + + MIME-Version: 1.0 + + MIME-Version: 1.0 (produced by MetaSend Vx.x) + + MIME-Version: (produced by MetaSend Vx.x) 1.0 + + MIME-Version: 1.(produced by MetaSend Vx.x)0 + + In the absence of a MIME-Version field, a receiving mail user agent + (whether conforming to MIME requirements or not) may optionally + choose to interpret the body of the message according to local + conventions. Many such conventions are currently in use and it + should be noted that in practice non-MIME messages can contain just + about anything. + + It is impossible to be certain that a non-MIME mail message is + actually plain text in the US-ASCII character set since it might well + be a message that, using some set of nonstandard local conventions + that predate MIME, includes text in another character set or non- + textual data presented in a manner that cannot be automatically + recognized (e.g., a uuencoded compressed UNIX tar file). + +5. Content-Type Header Field + + The purpose of the Content-Type field is to describe the data + contained in the body fully enough that the receiving user agent can + pick an appropriate agent or mechanism to present the data to the + user, or otherwise deal with the data in an appropriate manner. The + value in this field is called a media type. + + HISTORICAL NOTE: The Content-Type header field was first defined in + RFC 1049. RFC 1049 used a simpler and less powerful syntax, but one + that is largely compatible with the mechanism given here. + + The Content-Type header field specifies the nature of the data in the + body of an entity by giving media type and subtype identifiers, and + by providing auxiliary information that may be required for certain + media types. After the media type and subtype names, the remainder + of the header field is simply a set of parameters, specified in an + attribute=value notation. The ordering of parameters is not + significant. + + + + + + +Freed & Borenstein Standards Track [Page 10] + +RFC 2045 Internet Message Bodies November 1996 + + + In general, the top-level media type is used to declare the general + type of data, while the subtype specifies a specific format for that + type of data. Thus, a media type of "image/xyz" is enough to tell a + user agent that the data is an image, even if the user agent has no + knowledge of the specific image format "xyz". Such information can + be used, for example, to decide whether or not to show a user the raw + data from an unrecognized subtype -- such an action might be + reasonable for unrecognized subtypes of text, but not for + unrecognized subtypes of image or audio. For this reason, registered + subtypes of text, image, audio, and video should not contain embedded + information that is really of a different type. Such compound + formats should be represented using the "multipart" or "application" + types. + + Parameters are modifiers of the media subtype, and as such do not + fundamentally affect the nature of the content. The set of + meaningful parameters depends on the media type and subtype. Most + parameters are associated with a single specific subtype. However, a + given top-level media type may define parameters which are applicable + to any subtype of that type. Parameters may be required by their + defining content type or subtype or they may be optional. MIME + implementations must ignore any parameters whose names they do not + recognize. + + For example, the "charset" parameter is applicable to any subtype of + "text", while the "boundary" parameter is required for any subtype of + the "multipart" media type. + + There are NO globally-meaningful parameters that apply to all media + types. Truly global mechanisms are best addressed, in the MIME + model, by the definition of additional Content-* header fields. + + An initial set of seven top-level media types is defined in RFC 2046. + Five of these are discrete types whose content is essentially opaque + as far as MIME processing is concerned. The remaining two are + composite types whose contents require additional handling by MIME + processors. + + This set of top-level media types is intended to be substantially + complete. It is expected that additions to the larger set of + supported types can generally be accomplished by the creation of new + subtypes of these initial types. In the future, more top-level types + may be defined only by a standards-track extension to this standard. + If another top-level type is to be used for any reason, it must be + given a name starting with "X-" to indicate its non-standard status + and to avoid a potential conflict with a future official name. + + + + + +Freed & Borenstein Standards Track [Page 11] + +RFC 2045 Internet Message Bodies November 1996 + + +5.1. Syntax of the Content-Type Header Field + + In the Augmented BNF notation of RFC 822, a Content-Type header field + value is defined as follows: + + content := "Content-Type" ":" type "/" subtype + *(";" parameter) + ; Matching of media type and subtype + ; is ALWAYS case-insensitive. + + type := discrete-type / composite-type + + discrete-type := "text" / "image" / "audio" / "video" / + "application" / extension-token + + composite-type := "message" / "multipart" / extension-token + + extension-token := ietf-token / x-token + + ietf-token := + + x-token := + + subtype := extension-token / iana-token + + iana-token := + + parameter := attribute "=" value + + attribute := token + ; Matching of attributes + ; is ALWAYS case-insensitive. + + value := token / quoted-string + + token := 1* + + tspecials := "(" / ")" / "<" / ">" / "@" / + "," / ";" / ":" / "\" / <"> + "/" / "[" / "]" / "?" / "=" + ; Must be in quoted-string, + ; to use within parameter values + + + +Freed & Borenstein Standards Track [Page 12] + +RFC 2045 Internet Message Bodies November 1996 + + + Note that the definition of "tspecials" is the same as the RFC 822 + definition of "specials" with the addition of the three characters + "/", "?", and "=", and the removal of ".". + + Note also that a subtype specification is MANDATORY -- it may not be + omitted from a Content-Type header field. As such, there are no + default subtypes. + + The type, subtype, and parameter names are not case sensitive. For + example, TEXT, Text, and TeXt are all equivalent top-level media + types. Parameter values are normally case sensitive, but sometimes + are interpreted in a case-insensitive fashion, depending on the + intended use. (For example, multipart boundaries are case-sensitive, + but the "access-type" parameter for message/External-body is not + case-sensitive.) + + Note that the value of a quoted string parameter does not include the + quotes. That is, the quotation marks in a quoted-string are not a + part of the value of the parameter, but are merely used to delimit + that parameter value. In addition, comments are allowed in + accordance with RFC 822 rules for structured header fields. Thus the + following two forms + + Content-type: text/plain; charset=us-ascii (Plain text) + + Content-type: text/plain; charset="us-ascii" + + are completely equivalent. + + Beyond this syntax, the only syntactic constraint on the definition + of subtype names is the desire that their uses must not conflict. + That is, it would be undesirable to have two different communities + using "Content-Type: application/foobar" to mean two different + things. The process of defining new media subtypes, then, is not + intended to be a mechanism for imposing restrictions, but simply a + mechanism for publicizing their definition and usage. There are, + therefore, two acceptable mechanisms for defining new media subtypes: + + (1) Private values (starting with "X-") may be defined + bilaterally between two cooperating agents without + outside registration or standardization. Such values + cannot be registered or standardized. + + (2) New standard values should be registered with IANA as + described in RFC 2048. + + The second document in this set, RFC 2046, defines the initial set of + media types for MIME. + + + +Freed & Borenstein Standards Track [Page 13] + +RFC 2045 Internet Message Bodies November 1996 + + +5.2. Content-Type Defaults + + Default RFC 822 messages without a MIME Content-Type header are taken + by this protocol to be plain text in the US-ASCII character set, + which can be explicitly specified as: + + Content-type: text/plain; charset=us-ascii + + This default is assumed if no Content-Type header field is specified. + It is also recommend that this default be assumed when a + syntactically invalid Content-Type header field is encountered. In + the presence of a MIME-Version header field and the absence of any + Content-Type header field, a receiving User Agent can also assume + that plain US-ASCII text was the sender's intent. Plain US-ASCII + text may still be assumed in the absence of a MIME-Version or the + presence of an syntactically invalid Content-Type header field, but + the sender's intent might have been otherwise. + +6. Content-Transfer-Encoding Header Field + + Many media types which could be usefully transported via email are + represented, in their "natural" format, as 8bit character or binary + data. Such data cannot be transmitted over some transfer protocols. + For example, RFC 821 (SMTP) restricts mail messages to 7bit US-ASCII + data with lines no longer than 1000 characters including any trailing + CRLF line separator. + + It is necessary, therefore, to define a standard mechanism for + encoding such data into a 7bit short line format. Proper labelling + of unencoded material in less restrictive formats for direct use over + less restrictive transports is also desireable. This document + specifies that such encodings will be indicated by a new "Content- + Transfer-Encoding" header field. This field has not been defined by + any previous standard. + +6.1. Content-Transfer-Encoding Syntax + + The Content-Transfer-Encoding field's value is a single token + specifying the type of encoding, as enumerated below. Formally: + + encoding := "Content-Transfer-Encoding" ":" mechanism + + mechanism := "7bit" / "8bit" / "binary" / + "quoted-printable" / "base64" / + ietf-token / x-token + + These values are not case sensitive -- Base64 and BASE64 and bAsE64 + are all equivalent. An encoding type of 7BIT requires that the body + + + +Freed & Borenstein Standards Track [Page 14] + +RFC 2045 Internet Message Bodies November 1996 + + + is already in a 7bit mail-ready representation. This is the default + value -- that is, "Content-Transfer-Encoding: 7BIT" is assumed if the + Content-Transfer-Encoding header field is not present. + +6.2. Content-Transfer-Encodings Semantics + + This single Content-Transfer-Encoding token actually provides two + pieces of information. It specifies what sort of encoding + transformation the body was subjected to and hence what decoding + operation must be used to restore it to its original form, and it + specifies what the domain of the result is. + + The transformation part of any Content-Transfer-Encodings specifies, + either explicitly or implicitly, a single, well-defined decoding + algorithm, which for any sequence of encoded octets either transforms + it to the original sequence of octets which was encoded, or shows + that it is illegal as an encoded sequence. Content-Transfer- + Encodings transformations never depend on any additional external + profile information for proper operation. Note that while decoders + must produce a single, well-defined output for a valid encoding no + such restrictions exist for encoders: Encoding a given sequence of + octets to different, equivalent encoded sequences is perfectly legal. + + Three transformations are currently defined: identity, the "quoted- + printable" encoding, and the "base64" encoding. The domains are + "binary", "8bit" and "7bit". + + The Content-Transfer-Encoding values "7bit", "8bit", and "binary" all + mean that the identity (i.e. NO) encoding transformation has been + performed. As such, they serve simply as indicators of the domain of + the body data, and provide useful information about the sort of + encoding that might be needed for transmission in a given transport + system. The terms "7bit data", "8bit data", and "binary data" are + all defined in Section 2. + + The quoted-printable and base64 encodings transform their input from + an arbitrary domain into material in the "7bit" range, thus making it + safe to carry over restricted transports. The specific definition of + the transformations are given below. + + The proper Content-Transfer-Encoding label must always be used. + Labelling unencoded data containing 8bit characters as "7bit" is not + allowed, nor is labelling unencoded non-line-oriented data as + anything other than "binary" allowed. + + Unlike media subtypes, a proliferation of Content-Transfer-Encoding + values is both undesirable and unnecessary. However, establishing + only a single transformation into the "7bit" domain does not seem + + + +Freed & Borenstein Standards Track [Page 15] + +RFC 2045 Internet Message Bodies November 1996 + + + possible. There is a tradeoff between the desire for a compact and + efficient encoding of largely- binary data and the desire for a + somewhat readable encoding of data that is mostly, but not entirely, + 7bit. For this reason, at least two encoding mechanisms are + necessary: a more or less readable encoding (quoted-printable) and a + "dense" or "uniform" encoding (base64). + + Mail transport for unencoded 8bit data is defined in RFC 1652. As of + the initial publication of this document, there are no standardized + Internet mail transports for which it is legitimate to include + unencoded binary data in mail bodies. Thus there are no + circumstances in which the "binary" Content-Transfer-Encoding is + actually valid in Internet mail. However, in the event that binary + mail transport becomes a reality in Internet mail, or when MIME is + used in conjunction with any other binary-capable mail transport + mechanism, binary bodies must be labelled as such using this + mechanism. + + NOTE: The five values defined for the Content-Transfer-Encoding field + imply nothing about the media type other than the algorithm by which + it was encoded or the transport system requirements if unencoded. + +6.3. New Content-Transfer-Encodings + + Implementors may, if necessary, define private Content-Transfer- + Encoding values, but must use an x-token, which is a name prefixed by + "X-", to indicate its non-standard status, e.g., "Content-Transfer- + Encoding: x-my-new-encoding". Additional standardized Content- + Transfer-Encoding values must be specified by a standards-track RFC. + The requirements such specifications must meet are given in RFC 2048. + As such, all content-transfer-encoding namespace except that + beginning with "X-" is explicitly reserved to the IETF for future + use. + + Unlike media types and subtypes, the creation of new Content- + Transfer-Encoding values is STRONGLY discouraged, as it seems likely + to hinder interoperability with little potential benefit + +6.4. Interpretation and Use + + If a Content-Transfer-Encoding header field appears as part of a + message header, it applies to the entire body of that message. If a + Content-Transfer-Encoding header field appears as part of an entity's + headers, it applies only to the body of that entity. If an entity is + of type "multipart" the Content-Transfer-Encoding is not permitted to + have any value other than "7bit", "8bit" or "binary". Even more + severe restrictions apply to some subtypes of the "message" type. + + + + +Freed & Borenstein Standards Track [Page 16] + +RFC 2045 Internet Message Bodies November 1996 + + + It should be noted that most media types are defined in terms of + octets rather than bits, so that the mechanisms described here are + mechanisms for encoding arbitrary octet streams, not bit streams. If + a bit stream is to be encoded via one of these mechanisms, it must + first be converted to an 8bit byte stream using the network standard + bit order ("big-endian"), in which the earlier bits in a stream + become the higher-order bits in a 8bit byte. A bit stream not ending + at an 8bit boundary must be padded with zeroes. RFC 2046 provides a + mechanism for noting the addition of such padding in the case of the + application/octet-stream media type, which has a "padding" parameter. + + The encoding mechanisms defined here explicitly encode all data in + US-ASCII. Thus, for example, suppose an entity has header fields + such as: + + Content-Type: text/plain; charset=ISO-8859-1 + Content-transfer-encoding: base64 + + This must be interpreted to mean that the body is a base64 US-ASCII + encoding of data that was originally in ISO-8859-1, and will be in + that character set again after decoding. + + Certain Content-Transfer-Encoding values may only be used on certain + media types. In particular, it is EXPRESSLY FORBIDDEN to use any + encodings other than "7bit", "8bit", or "binary" with any composite + media type, i.e. one that recursively includes other Content-Type + fields. Currently the only composite media types are "multipart" and + "message". All encodings that are desired for bodies of type + multipart or message must be done at the innermost level, by encoding + the actual body that needs to be encoded. + + It should also be noted that, by definition, if a composite entity + has a transfer-encoding value such as "7bit", but one of the enclosed + entities has a less restrictive value such as "8bit", then either the + outer "7bit" labelling is in error, because 8bit data are included, + or the inner "8bit" labelling placed an unnecessarily high demand on + the transport system because the actual included data were actually + 7bit-safe. + + NOTE ON ENCODING RESTRICTIONS: Though the prohibition against using + content-transfer-encodings on composite body data may seem overly + restrictive, it is necessary to prevent nested encodings, in which + data are passed through an encoding algorithm multiple times, and + must be decoded multiple times in order to be properly viewed. + Nested encodings add considerable complexity to user agents: Aside + from the obvious efficiency problems with such multiple encodings, + they can obscure the basic structure of a message. In particular, + they can imply that several decoding operations are necessary simply + + + +Freed & Borenstein Standards Track [Page 17] + +RFC 2045 Internet Message Bodies November 1996 + + + to find out what types of bodies a message contains. Banning nested + encodings may complicate the job of certain mail gateways, but this + seems less of a problem than the effect of nested encodings on user + agents. + + Any entity with an unrecognized Content-Transfer-Encoding must be + treated as if it has a Content-Type of "application/octet-stream", + regardless of what the Content-Type header field actually says. + + NOTE ON THE RELATIONSHIP BETWEEN CONTENT-TYPE AND CONTENT-TRANSFER- + ENCODING: It may seem that the Content-Transfer-Encoding could be + inferred from the characteristics of the media that is to be encoded, + or, at the very least, that certain Content-Transfer-Encodings could + be mandated for use with specific media types. There are several + reasons why this is not the case. First, given the varying types of + transports used for mail, some encodings may be appropriate for some + combinations of media types and transports but not for others. (For + example, in an 8bit transport, no encoding would be required for text + in certain character sets, while such encodings are clearly required + for 7bit SMTP.) + + Second, certain media types may require different types of transfer + encoding under different circumstances. For example, many PostScript + bodies might consist entirely of short lines of 7bit data and hence + require no encoding at all. Other PostScript bodies (especially + those using Level 2 PostScript's binary encoding mechanism) may only + be reasonably represented using a binary transport encoding. + Finally, since the Content-Type field is intended to be an open-ended + specification mechanism, strict specification of an association + between media types and encodings effectively couples the + specification of an application protocol with a specific lower-level + transport. This is not desirable since the developers of a media + type should not have to be aware of all the transports in use and + what their limitations are. + +6.5. Translating Encodings + + The quoted-printable and base64 encodings are designed so that + conversion between them is possible. The only issue that arises in + such a conversion is the handling of hard line breaks in quoted- + printable encoding output. When converting from quoted-printable to + base64 a hard line break in the quoted-printable form represents a + CRLF sequence in the canonical form of the data. It must therefore be + converted to a corresponding encoded CRLF in the base64 form of the + data. Similarly, a CRLF sequence in the canonical form of the data + obtained after base64 decoding must be converted to a quoted- + printable hard line break, but ONLY when converting text data. + + + + +Freed & Borenstein Standards Track [Page 18] + +RFC 2045 Internet Message Bodies November 1996 + + +6.6. Canonical Encoding Model + + There was some confusion, in the previous versions of this RFC, + regarding the model for when email data was to be converted to + canonical form and encoded, and in particular how this process would + affect the treatment of CRLFs, given that the representation of + newlines varies greatly from system to system, and the relationship + between content-transfer-encodings and character sets. A canonical + model for encoding is presented in RFC 2049 for this reason. + +6.7. Quoted-Printable Content-Transfer-Encoding + + The Quoted-Printable encoding is intended to represent data that + largely consists of octets that correspond to printable characters in + the US-ASCII character set. It encodes the data in such a way that + the resulting octets are unlikely to be modified by mail transport. + If the data being encoded are mostly US-ASCII text, the encoded form + of the data remains largely recognizable by humans. A body which is + entirely US-ASCII may also be encoded in Quoted-Printable to ensure + the integrity of the data should the message pass through a + character-translating, and/or line-wrapping gateway. + + In this encoding, octets are to be represented as determined by the + following rules: + + (1) (General 8bit representation) Any octet, except a CR or + LF that is part of a CRLF line break of the canonical + (standard) form of the data being encoded, may be + represented by an "=" followed by a two digit + hexadecimal representation of the octet's value. The + digits of the hexadecimal alphabet, for this purpose, + are "0123456789ABCDEF". Uppercase letters must be + used; lowercase letters are not allowed. Thus, for + example, the decimal value 12 (US-ASCII form feed) can + be represented by "=0C", and the decimal value 61 (US- + ASCII EQUAL SIGN) can be represented by "=3D". This + rule must be followed except when the following rules + allow an alternative encoding. + + (2) (Literal representation) Octets with decimal values of + 33 through 60 inclusive, and 62 through 126, inclusive, + MAY be represented as the US-ASCII characters which + correspond to those octets (EXCLAMATION POINT through + LESS THAN, and GREATER THAN through TILDE, + respectively). + + (3) (White Space) Octets with values of 9 and 32 MAY be + represented as US-ASCII TAB (HT) and SPACE characters, + + + +Freed & Borenstein Standards Track [Page 19] + +RFC 2045 Internet Message Bodies November 1996 + + + respectively, but MUST NOT be so represented at the end + of an encoded line. Any TAB (HT) or SPACE characters + on an encoded line MUST thus be followed on that line + by a printable character. In particular, an "=" at the + end of an encoded line, indicating a soft line break + (see rule #5) may follow one or more TAB (HT) or SPACE + characters. It follows that an octet with decimal + value 9 or 32 appearing at the end of an encoded line + must be represented according to Rule #1. This rule is + necessary because some MTAs (Message Transport Agents, + programs which transport messages from one user to + another, or perform a portion of such transfers) are + known to pad lines of text with SPACEs, and others are + known to remove "white space" characters from the end + of a line. Therefore, when decoding a Quoted-Printable + body, any trailing white space on a line must be + deleted, as it will necessarily have been added by + intermediate transport agents. + + (4) (Line Breaks) A line break in a text body, represented + as a CRLF sequence in the text canonical form, must be + represented by a (RFC 822) line break, which is also a + CRLF sequence, in the Quoted-Printable encoding. Since + the canonical representation of media types other than + text do not generally include the representation of + line breaks as CRLF sequences, no hard line breaks + (i.e. line breaks that are intended to be meaningful + and to be displayed to the user) can occur in the + quoted-printable encoding of such types. Sequences + like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely + appear in non-text data represented in quoted- + printable, of course. + + Note that many implementations may elect to encode the + local representation of various content types directly + rather than converting to canonical form first, + encoding, and then converting back to local + representation. In particular, this may apply to plain + text material on systems that use newline conventions + other than a CRLF terminator sequence. Such an + implementation optimization is permissible, but only + when the combined canonicalization-encoding step is + equivalent to performing the three steps separately. + + (5) (Soft Line Breaks) The Quoted-Printable encoding + REQUIRES that encoded lines be no more than 76 + characters long. If longer lines are to be encoded + with the Quoted-Printable encoding, "soft" line breaks + + + +Freed & Borenstein Standards Track [Page 20] + +RFC 2045 Internet Message Bodies November 1996 + + + must be used. An equal sign as the last character on a + encoded line indicates such a non-significant ("soft") + line break in the encoded text. + + Thus if the "raw" form of the line is a single unencoded line that + says: + + Now's the time for all folk to come to the aid of their country. + + This can be represented, in the Quoted-Printable encoding, as: + + Now's the time = + for all folk to come= + to the aid of their country. + + This provides a mechanism with which long lines are encoded in such a + way as to be restored by the user agent. The 76 character limit does + not count the trailing CRLF, but counts all other characters, + including any equal signs. + + Since the hyphen character ("-") may be represented as itself in the + Quoted-Printable encoding, care must be taken, when encapsulating a + quoted-printable encoded body inside one or more multipart entities, + to ensure that the boundary delimiter does not appear anywhere in the + encoded body. (A good strategy is to choose a boundary that includes + a character sequence such as "=_" which can never appear in a + quoted-printable body. See the definition of multipart messages in + RFC 2046.) + + NOTE: The quoted-printable encoding represents something of a + compromise between readability and reliability in transport. Bodies + encoded with the quoted-printable encoding will work reliably over + most mail gateways, but may not work perfectly over a few gateways, + notably those involving translation into EBCDIC. A higher level of + confidence is offered by the base64 Content-Transfer-Encoding. A way + to get reasonably reliable transport through EBCDIC gateways is to + also quote the US-ASCII characters + + !"#$@[\]^`{|}~ + + according to rule #1. + + Because quoted-printable data is generally assumed to be line- + oriented, it is to be expected that the representation of the breaks + between the lines of quoted-printable data may be altered in + transport, in the same manner that plain text mail has always been + altered in Internet mail when passing between systems with differing + newline conventions. If such alterations are likely to constitute a + + + +Freed & Borenstein Standards Track [Page 21] + +RFC 2045 Internet Message Bodies November 1996 + + + corruption of the data, it is probably more sensible to use the + base64 encoding rather than the quoted-printable encoding. + + NOTE: Several kinds of substrings cannot be generated according to + the encoding rules for the quoted-printable content-transfer- + encoding, and hence are formally illegal if they appear in the output + of a quoted-printable encoder. This note enumerates these cases and + suggests ways to handle such illegal substrings if any are + encountered in quoted-printable data that is to be decoded. + + (1) An "=" followed by two hexadecimal digits, one or both + of which are lowercase letters in "abcdef", is formally + illegal. A robust implementation might choose to + recognize them as the corresponding uppercase letters. + + (2) An "=" followed by a character that is neither a + hexadecimal digit (including "abcdef") nor the CR + character of a CRLF pair is illegal. This case can be + the result of US-ASCII text having been included in a + quoted-printable part of a message without itself + having been subjected to quoted-printable encoding. A + reasonable approach by a robust implementation might be + to include the "=" character and the following + character in the decoded data without any + transformation and, if possible, indicate to the user + that proper decoding was not possible at this point in + the data. + + (3) An "=" cannot be the ultimate or penultimate character + in an encoded object. This could be handled as in case + (2) above. + + (4) Control characters other than TAB, or CR and LF as + parts of CRLF pairs, must not appear. The same is true + for octets with decimal values greater than 126. If + found in incoming quoted-printable data by a decoder, a + robust implementation might exclude them from the + decoded data and warn the user that illegal characters + were discovered. + + (5) Encoded lines must not be longer than 76 characters, + not counting the trailing CRLF. If longer lines are + found in incoming, encoded data, a robust + implementation might nevertheless decode the lines, and + might report the erroneous encoding to the user. + + + + + + +Freed & Borenstein Standards Track [Page 22] + +RFC 2045 Internet Message Bodies November 1996 + + + WARNING TO IMPLEMENTORS: If binary data is encoded in quoted- + printable, care must be taken to encode CR and LF characters as "=0D" + and "=0A", respectively. In particular, a CRLF sequence in binary + data should be encoded as "=0D=0A". Otherwise, if CRLF were + represented as a hard line break, it might be incorrectly decoded on + platforms with different line break conventions. + + For formalists, the syntax of quoted-printable data is described by + the following grammar: + + quoted-printable := qp-line *(CRLF qp-line) + + qp-line := *(qp-segment transport-padding CRLF) + qp-part transport-padding + + qp-part := qp-section + ; Maximum length of 76 characters + + qp-segment := qp-section *(SPACE / TAB) "=" + ; Maximum length of 76 characters + + qp-section := [*(ptext / SPACE / TAB) ptext] + + ptext := hex-octet / safe-char + + safe-char := + ; Characters not listed as "mail-safe" in + ; RFC 2049 are also not recommended. + + hex-octet := "=" 2(DIGIT / "A" / "B" / "C" / "D" / "E" / "F") + ; Octet must be used for characters > 127, =, + ; SPACEs or TABs at the ends of lines, and is + ; recommended for any character not listed in + ; RFC 2049 as "mail-safe". + + transport-padding := *LWSP-char + ; Composers MUST NOT generate + ; non-zero length transport + ; padding, but receivers MUST + ; be able to handle padding + ; added by message transports. + + IMPORTANT: The addition of LWSP between the elements shown in this + BNF is NOT allowed since this BNF does not specify a structured + header field. + + + + + +Freed & Borenstein Standards Track [Page 23] + +RFC 2045 Internet Message Bodies November 1996 + + +6.8. Base64 Content-Transfer-Encoding + + The Base64 Content-Transfer-Encoding is designed to represent + arbitrary sequences of octets in a form that need not be humanly + readable. The encoding and decoding algorithms are simple, but the + encoded data are consistently only about 33 percent larger than the + unencoded data. This encoding is virtually identical to the one used + in Privacy Enhanced Mail (PEM) applications, as defined in RFC 1421. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + NOTE: This subset has the important property that it is represented + identically in all versions of ISO 646, including US-ASCII, and all + characters in the subset are also represented identically in all + versions of EBCDIC. Other popular encodings, such as the encoding + used by the uuencode utility, Macintosh binhex 4.0 [RFC-1741], and + the base85 encoding specified as part of Level 2 PostScript, do not + share these properties, and thus do not fulfill the portability + requirements a binary transport encoding for mail must meet. + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + When encoding a bit stream via the base64 encoding, the bit stream + must be presumed to be ordered with the most-significant-bit first. + That is, the first bit in the stream will be the high-order bit in + the first 8bit byte, and the eighth bit will be the low-order bit in + the first 8bit byte, and so on. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. These characters, identified in Table 1, below, are + selected so as to be universally representable, and the set excludes + characters with particular significance to SMTP (e.g., ".", CR, LF) + and to the multipart boundary delimiters defined in RFC 2046 (e.g., + "-"). + + + + + + + + + + + +Freed & Borenstein Standards Track [Page 24] + +RFC 2045 Internet Message Bodies November 1996 + + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + The encoded output stream must be represented in lines of no more + than 76 characters each. All line breaks or other characters not + found in Table 1 must be ignored by decoding software. In base64 + data, characters other than those in Table 1, line breaks, and other + white space probably indicate a transmission error, about which a + warning message or even a message rejection might be appropriate + under some circumstances. + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a body. When fewer than 24 input bits + are available in an input group, zero bits are added (on the right) + to form an integral number of 6-bit groups. Padding at the end of + the data is performed using the "=" character. Since all base64 + input is an integral number of octets, only the following cases can + arise: (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded output will be + an integral multiple of 4 characters with no "=" padding, (2) the + final quantum of encoding input is exactly 8 bits; here, the final + unit of encoded output will be two characters followed by two "=" + padding characters, or (3) the final quantum of encoding input is + exactly 16 bits; here, the final unit of encoded output will be three + characters followed by one "=" padding character. + + Because it is used only for padding at the end of the data, the + occurrence of any "=" characters may be taken as evidence that the + end of the data has been reached (without truncation in transit). No + + + +Freed & Borenstein Standards Track [Page 25] + +RFC 2045 Internet Message Bodies November 1996 + + + such assurance is possible, however, when the number of octets + transmitted was a multiple of three and no "=" characters are + present. + + Any characters outside of the base64 alphabet are to be ignored in + base64-encoded data. + + Care must be taken to use the proper octets for line breaks if base64 + encoding is applied directly to text material that has not been + converted to canonical form. In particular, text line breaks must be + converted into CRLF sequences prior to base64 encoding. The + important thing to note is that this may be done directly by the + encoder rather than in a prior canonicalization step in some + implementations. + + NOTE: There is no need to worry about quoting potential boundary + delimiters within base64-encoded bodies within multipart entities + because no hyphen characters are used in the base64 encoding. + +7. Content-ID Header Field + + In constructing a high-level user agent, it may be desirable to allow + one body to make reference to another. Accordingly, bodies may be + labelled using the "Content-ID" header field, which is syntactically + identical to the "Message-ID" header field: + + id := "Content-ID" ":" msg-id + + Like the Message-ID values, Content-ID values must be generated to be + world-unique. + + The Content-ID value may be used for uniquely identifying MIME + entities in several contexts, particularly for caching data + referenced by the message/external-body mechanism. Although the + Content-ID header is generally optional, its use is MANDATORY in + implementations which generate data of the optional MIME media type + "message/external-body". That is, each message/external-body entity + must have a Content-ID field to permit caching of such data. + + It is also worth noting that the Content-ID value has special + semantics in the case of the multipart/alternative media type. This + is explained in the section of RFC 2046 dealing with + multipart/alternative. + + + + + + + + +Freed & Borenstein Standards Track [Page 26] + +RFC 2045 Internet Message Bodies November 1996 + + +8. Content-Description Header Field + + The ability to associate some descriptive information with a given + body is often desirable. For example, it may be useful to mark an + "image" body as "a picture of the Space Shuttle Endeavor." Such text + may be placed in the Content-Description header field. This header + field is always optional. + + description := "Content-Description" ":" *text + + The description is presumed to be given in the US-ASCII character + set, although the mechanism specified in RFC 2047 may be used for + non-US-ASCII Content-Description values. + +9. Additional MIME Header Fields + + Future documents may elect to define additional MIME header fields + for various purposes. Any new header field that further describes + the content of a message should begin with the string "Content-" to + allow such fields which appear in a message header to be + distinguished from ordinary RFC 822 message header fields. + + MIME-extension-field := + +10. Summary + + Using the MIME-Version, Content-Type, and Content-Transfer-Encoding + header fields, it is possible to include, in a standardized way, + arbitrary types of data with RFC 822 conformant mail messages. No + restrictions imposed by either RFC 821 or RFC 822 are violated, and + care has been taken to avoid problems caused by additional + restrictions imposed by the characteristics of some Internet mail + transport mechanisms (see RFC 2049). + + The next document in this set, RFC 2046, specifies the initial set of + media types that can be labelled and transported using these headers. + +11. Security Considerations + + Security issues are discussed in the second document in this set, RFC + 2046. + + + + + + + + +Freed & Borenstein Standards Track [Page 27] + +RFC 2045 Internet Message Bodies November 1996 + + +12. Authors' Addresses + + For more information, the authors of this document are best contacted + via Internet mail: + + Ned Freed + Innosoft International, Inc. + 1050 East Garvey Avenue South + West Covina, CA 91790 + USA + + Phone: +1 818 919 3600 + Fax: +1 818 919 3614 + EMail: ned@innosoft.com + + + Nathaniel S. Borenstein + First Virtual Holdings + 25 Washington Avenue + Morristown, NJ 07960 + USA + + Phone: +1 201 540 8967 + Fax: +1 201 993 3032 + EMail: nsb@nsb.fv.com + + + MIME is a result of the work of the Internet Engineering Task Force + Working Group on RFC 822 Extensions. The chairman of that group, + Greg Vaudreuil, may be reached at: + + Gregory M. Vaudreuil + Octel Network Services + 17080 Dallas Parkway + Dallas, TX 75248-1905 + USA + + EMail: Greg.Vaudreuil@Octel.Com + + + + + + + + + + + + + +Freed & Borenstein Standards Track [Page 28] + +RFC 2045 Internet Message Bodies November 1996 + + +Appendix A -- Collected Grammar + + This appendix contains the complete BNF grammar for all the syntax + specified by this document. + + By itself, however, this grammar is incomplete. It refers by name to + several syntax rules that are defined by RFC 822. Rather than + reproduce those definitions here, and risk unintentional differences + between the two, this document simply refers the reader to RFC 822 + for the remaining definitions. Wherever a term is undefined, it + refers to the RFC 822 definition. + + attribute := token + ; Matching of attributes + ; is ALWAYS case-insensitive. + + composite-type := "message" / "multipart" / extension-token + + content := "Content-Type" ":" type "/" subtype + *(";" parameter) + ; Matching of media type and subtype + ; is ALWAYS case-insensitive. + + description := "Content-Description" ":" *text + + discrete-type := "text" / "image" / "audio" / "video" / + "application" / extension-token + + encoding := "Content-Transfer-Encoding" ":" mechanism + + entity-headers := [ content CRLF ] + [ encoding CRLF ] + [ id CRLF ] + [ description CRLF ] + *( MIME-extension-field CRLF ) + + extension-token := ietf-token / x-token + + hex-octet := "=" 2(DIGIT / "A" / "B" / "C" / "D" / "E" / "F") + ; Octet must be used for characters > 127, =, + ; SPACEs or TABs at the ends of lines, and is + ; recommended for any character not listed in + ; RFC 2049 as "mail-safe". + + iana-token := + + + + +Freed & Borenstein Standards Track [Page 29] + +RFC 2045 Internet Message Bodies November 1996 + + + ietf-token := + + id := "Content-ID" ":" msg-id + + mechanism := "7bit" / "8bit" / "binary" / + "quoted-printable" / "base64" / + ietf-token / x-token + + MIME-extension-field := + + MIME-message-headers := entity-headers + fields + version CRLF + ; The ordering of the header + ; fields implied by this BNF + ; definition should be ignored. + + MIME-part-headers := entity-headers + [fields] + ; Any field not beginning with + ; "content-" can have no defined + ; meaning and may be ignored. + ; The ordering of the header + ; fields implied by this BNF + ; definition should be ignored. + + parameter := attribute "=" value + + ptext := hex-octet / safe-char + + qp-line := *(qp-segment transport-padding CRLF) + qp-part transport-padding + + qp-part := qp-section + ; Maximum length of 76 characters + + qp-section := [*(ptext / SPACE / TAB) ptext] + + qp-segment := qp-section *(SPACE / TAB) "=" + ; Maximum length of 76 characters + + quoted-printable := qp-line *(CRLF qp-line) + + + + + +Freed & Borenstein Standards Track [Page 30] + +RFC 2045 Internet Message Bodies November 1996 + + + safe-char := + ; Characters not listed as "mail-safe" in + ; RFC 2049 are also not recommended. + + subtype := extension-token / iana-token + + token := 1* + + transport-padding := *LWSP-char + ; Composers MUST NOT generate + ; non-zero length transport + ; padding, but receivers MUST + ; be able to handle padding + ; added by message transports. + + tspecials := "(" / ")" / "<" / ">" / "@" / + "," / ";" / ":" / "\" / <"> + "/" / "[" / "]" / "?" / "=" + ; Must be in quoted-string, + ; to use within parameter values + + type := discrete-type / composite-type + + value := token / quoted-string + + version := "MIME-Version" ":" 1*DIGIT "." 1*DIGIT + + x-token := + + + + + + + + + + + + + + + + + + + + +Freed & Borenstein Standards Track [Page 31] + diff --git a/vendor/github.com/mattermost/rsc/imap/rfc2971.txt b/vendor/github.com/mattermost/rsc/imap/rfc2971.txt new file mode 100644 index 000000000..9e7264dcc --- /dev/null +++ b/vendor/github.com/mattermost/rsc/imap/rfc2971.txt @@ -0,0 +1,451 @@ + + + + + + +Network Working Group T. Showalter +Request for Comments: 2971 Mirapoint, Inc. +Category: Standards Track October 2000 + + + IMAP4 ID extension + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2000). All Rights Reserved. + +Abstract + + The ID extension to the Internet Message Access Protocol - Version + 4rev1 (IMAP4rev1) protocol allows the server and client to exchange + identification information on their implementation in order to make + bug reports and usage statistics more complete. + +1. Introduction + + The IMAP4rev1 protocol described in [IMAP4rev1] provides a method for + accessing remote mail stores, but it provides no facility to + advertise what program a client or server uses to provide service. + This makes it difficult for implementors to get complete bug reports + from users, as it is frequently difficult to know what client or + server is in use. + + Additionally, some sites may wish to assemble usage statistics based + on what clients are used, but in an an environment where users are + permitted to obtain and maintain their own clients this is difficult + to accomplish. + + The ID command provides a facility to advertise information on what + programs are being used along with contact information (should bugs + ever occur). + + + + + + + + +Showalter Standards Track [Page 1] + +RFC 2971 IMAP4 ID extension October 2000 + + +2. Conventions Used in this Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [KEYWORDS]. + + The conventions used in this document are the same as specified in + [IMAP4rev1]. In examples, "C:" and "S:" indicate lines sent by the + client and server respectively. Line breaks have been inserted for + readability. + +3. Specification + + The sole purpose of the ID extension is to enable clients and servers + to exchange information on their implementations for the purposes of + statistical analysis and problem determination. + + This information is be submitted to a server by any client wishing to + provide information for statistical purposes, provided the server + advertises its willingness to take the information with the atom "ID" + included in the list of capabilities returned by the CAPABILITY + command. + + Implementations MUST NOT make operational changes based on the data + sent as part of the ID command or response. The ID command is for + human consumption only, and is not to be used in improving the + performance of clients or servers. + + This includes, but is not limited to, the following: + + Servers MUST NOT attempt to work around client bugs by using + information from the ID command. Clients MUST NOT attempt to work + around server bugs based on the ID response. + + Servers MUST NOT provide features to a client or otherwise + optimize for a particular client by using information from the ID + command. Clients MUST NOT provide features to a server or + otherwise optimize for a particular server based on the ID + response. + + Servers MUST NOT deny access to or refuse service for a client + based on information from the ID command. Clients MUST NOT refuse + to operate or limit their operation with a server based on the ID + response. + + + + + + + +Showalter Standards Track [Page 2] + +RFC 2971 IMAP4 ID extension October 2000 + + + Rationale: It is imperative that this extension not supplant IMAP's + CAPABILITY mechanism with a ad-hoc approach where implementations + guess each other's features based on who they claim to be. + + Implementations MUST NOT send false information in an ID command. + + Implementations MAY send less information than they have available or + no information at all. Such behavior may be useful to preserve user + privacy. See Security Considerations, section 7. + +3.1. ID Command + + Arguments: client parameter list or NIL + + Responses: OPTIONAL untagged response: ID + + Result: OK identification information accepted + BAD command unknown or arguments invalid + + Implementation identification information is sent by the client with + the ID command. + + This command is valid in any state. + + The information sent is in the form of a list of field/value pairs. + Fields are permitted to be any IMAP4 string, and values are permitted + to be any IMAP4 string or NIL. A value of NIL indicates that the + client can not or will not specify this information. The client may + also send NIL instead of the list, indicating that it wants to send + no information, but would still accept a server response. + + The available fields are defined in section 3.3. + + Example: C: a023 ID ("name" "sodr" "version" "19.34" "vendor" + "Pink Floyd Music Limited") + S: * ID NIL + S: a023 OK ID completed + +3.2. ID Response + + Contents: server parameter list + + In response to an ID command issued by the client, the server replies + with a tagged response containing information on its implementation. + The format is the same as the client list. + + + + + + +Showalter Standards Track [Page 3] + +RFC 2971 IMAP4 ID extension October 2000 + + + Example: C: a042 ID NIL + S: * ID ("name" "Cyrus" "version" "1.5" "os" "sunos" + "os-version" "5.5" "support-url" + "mailto:cyrus-bugs+@andrew.cmu.edu") + S: a042 OK ID command completed + + A server MUST send a tagged ID response to an ID command. However, a + server MAY send NIL in place of the list. + +3.3. Defined Field Values + + Any string may be sent as a field, but the following are defined to + describe certain values that might be sent. Implementations are free + to send none, any, or all of these. Strings are not case-sensitive. + Field strings MUST NOT be longer than 30 octets. Value strings MUST + NOT be longer than 1024 octets. Implementations MUST NOT send more + than 30 field-value pairs. + + name Name of the program + version Version number of the program + os Name of the operating system + os-version Version of the operating system + vendor Vendor of the client/server + support-url URL to contact for support + address Postal address of contact/vendor + date Date program was released, specified as a date-time + in IMAP4rev1 + command Command used to start the program + arguments Arguments supplied on the command line, if any + if any + environment Description of environment, i.e., UNIX environment + variables or Windows registry settings + + Implementations MUST NOT use contact information to submit automatic + bug reports. Implementations may include information from an ID + response in a report automatically prepared, but are prohibited from + sending the report without user authorization. + + It is preferable to find the name and version of the underlying + operating system at runtime in cases where this is possible. + + Information sent via an ID response may violate user privacy. See + Security Considerations, section 7. + + Implementations MUST NOT send the same field name more than once. + + + + + + +Showalter Standards Track [Page 4] + +RFC 2971 IMAP4 ID extension October 2000 + + +4. Formal Syntax + + This syntax is intended to augment the grammar specified in + [IMAP4rev1] in order to provide for the ID command. This + specification uses the augmented Backus-Naur Form (BNF) notation as + used in [IMAP4rev1]. + + command_any ::= "CAPABILITY" / "LOGOUT" / "NOOP" / x_command / id + ;; adds id command to command_any in [IMAP4rev1] + + id ::= "ID" SPACE id_params_list + + id_response ::= "ID" SPACE id_params_list + + id_params_list ::= "(" #(string SPACE nstring) ")" / nil + ;; list of field value pairs + + response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / + mailbox_data / message_data / capability_data / id_response) + +5. Use of the ID extension with Firewalls and Other Intermediaries + + There exist proxies, firewalls, and other intermediary systems that + can intercept an IMAP session and make changes to the data exchanged + in the session. Such intermediaries are not anticipated by the IMAP4 + protocol design and are not within the scope of the IMAP4 standard. + However, in order for the ID command to be useful in the presence of + such intermediaries, those intermediaries need to take special note + of the ID command and response. In particular, if an intermediary + changes any part of the IMAP session it must also change the ID + command to advertise its presence. + + A firewall MAY act to block transmission of specific information + fields in the ID command and response that it believes reveal + information that could expose a security vulnerability. However, a + firewall SHOULD NOT disable the extension, when present, entirely, + and SHOULD NOT unconditionally remove either the client or server + list. + + Finally, it should be noted that a firewall, when handling a + CAPABILITY response, MUST NOT allow the names of extensions to be + returned to the client that the firewall has no knowledge of. + + + + + + + + + +Showalter Standards Track [Page 5] + +RFC 2971 IMAP4 ID extension October 2000 + + +6. References + + [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", RFC 2119, March 1997. + + [IMAP4rev1] Crispin, M., "Internet Message Access Protocol - Version + 4rev1", RFC 2060, October 1996. + + [RFC-822] Crocker, D., "Standard for the Format of ARPA Internet + Text Messages", STD 11, RFC 822, August 1982. + +7. Security Considerations + + This extension has the danger of violating the privacy of users if + misused. Clients and servers should notify users that they implement + and enable the ID command. + + It is highly desirable that implementations provide a method of + disabling ID support, perhaps by not sending ID at all, or by sending + NIL as the argument to the ID command or response. + + Implementors must exercise extreme care in adding fields sent as part + of an ID command or response. Some fields, including a processor ID + number, Ethernet address, or other unique (or mostly unique) + identifier allow tracking of users in ways that violate user privacy + expectations. + + Having implementation information of a given client or server may + make it easier for an attacker to gain unauthorized access due to + security holes. + + Since this command includes arbitrary data and does not require the + user to authenticate, server implementations are cautioned to guard + against an attacker sending arbitrary garbage data in order to fill + up the ID log. In particular, if a server naively logs each ID + command to disk without inspecting it, an attacker can simply fire up + thousands of connections and send a few kilobytes of random data. + Servers have to guard against this. Methods include truncating + abnormally large responses; collating responses by storing only a + single copy, then keeping a counter of the number of times that + response has been seen; keeping only particularly interesting parts + of responses; and only logging responses of users who actually log + in. + + Security is affected by firewalls which modify the IMAP protocol + stream; see section 5, Use of the ID Extension with Firewalls and + Other Intermediaries, for more information. + + + + +Showalter Standards Track [Page 6] + +RFC 2971 IMAP4 ID extension October 2000 + + +8. Author's Address + + Tim Showalter + Mirapoint, Inc. + 909 Hermosa Ct. + Sunnyvale, CA 94095 + + EMail: tjs@mirapoint.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Showalter Standards Track [Page 7] + +RFC 2971 IMAP4 ID extension October 2000 + + +9. Full Copyright Statement + + Copyright (C) The Internet Society (2000). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. + + This document and the information contained herein is provided on an + "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + +Showalter Standards Track [Page 8] + diff --git a/vendor/github.com/mattermost/rsc/imap/rfc3501.txt b/vendor/github.com/mattermost/rsc/imap/rfc3501.txt new file mode 100644 index 000000000..5ab48e17c --- /dev/null +++ b/vendor/github.com/mattermost/rsc/imap/rfc3501.txt @@ -0,0 +1,6051 @@ + + + + + + +Network Working Group M. Crispin +Request for Comments: 3501 University of Washington +Obsoletes: 2060 March 2003 +Category: Standards Track + + + INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1 + +Status of this Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (C) The Internet Society (2003). All Rights Reserved. + +Abstract + + The Internet Message Access Protocol, Version 4rev1 (IMAP4rev1) + allows a client to access and manipulate electronic mail messages on + a server. IMAP4rev1 permits manipulation of mailboxes (remote + message folders) in a way that is functionally equivalent to local + folders. IMAP4rev1 also provides the capability for an offline + client to resynchronize with the server. + + IMAP4rev1 includes operations for creating, deleting, and renaming + mailboxes, checking for new messages, permanently removing messages, + setting and clearing flags, RFC 2822 and RFC 2045 parsing, searching, + and selective fetching of message attributes, texts, and portions + thereof. Messages in IMAP4rev1 are accessed by the use of numbers. + These numbers are either message sequence numbers or unique + identifiers. + + IMAP4rev1 supports a single server. A mechanism for accessing + configuration information to support multiple IMAP4rev1 servers is + discussed in RFC 2244. + + IMAP4rev1 does not specify a means of posting mail; this function is + handled by a mail transfer protocol such as RFC 2821. + + + + + + + + +Crispin Standards Track [Page 1] + +RFC 3501 IMAPv4 March 2003 + + +Table of Contents + + IMAP4rev1 Protocol Specification ................................ 4 + 1. How to Read This Document ............................... 4 + 1.1. Organization of This Document ........................... 4 + 1.2. Conventions Used in This Document ....................... 4 + 1.3. Special Notes to Implementors ........................... 5 + 2. Protocol Overview ....................................... 6 + 2.1. Link Level .............................................. 6 + 2.2. Commands and Responses .................................. 6 + 2.2.1. Client Protocol Sender and Server Protocol Receiver ..... 6 + 2.2.2. Server Protocol Sender and Client Protocol Receiver ..... 7 + 2.3. Message Attributes ...................................... 8 + 2.3.1. Message Numbers ......................................... 8 + 2.3.1.1. Unique Identifier (UID) Message Attribute ....... 8 + 2.3.1.2. Message Sequence Number Message Attribute ....... 10 + 2.3.2. Flags Message Attribute ................................. 11 + 2.3.3. Internal Date Message Attribute ......................... 12 + 2.3.4. [RFC-2822] Size Message Attribute ....................... 12 + 2.3.5. Envelope Structure Message Attribute .................... 12 + 2.3.6. Body Structure Message Attribute ........................ 12 + 2.4. Message Texts ........................................... 13 + 3. State and Flow Diagram .................................. 13 + 3.1. Not Authenticated State ................................. 13 + 3.2. Authenticated State ..................................... 13 + 3.3. Selected State .......................................... 13 + 3.4. Logout State ............................................ 14 + 4. Data Formats ............................................ 16 + 4.1. Atom .................................................... 16 + 4.2. Number .................................................. 16 + 4.3. String .................................................. 16 + 4.3.1. 8-bit and Binary Strings ................................ 17 + 4.4. Parenthesized List ...................................... 17 + 4.5. NIL ..................................................... 17 + 5. Operational Considerations .............................. 18 + 5.1. Mailbox Naming .......................................... 18 + 5.1.1. Mailbox Hierarchy Naming ................................ 19 + 5.1.2. Mailbox Namespace Naming Convention ..................... 19 + 5.1.3. Mailbox International Naming Convention ................. 19 + 5.2. Mailbox Size and Message Status Updates ................. 21 + 5.3. Response when no Command in Progress .................... 21 + 5.4. Autologout Timer ........................................ 22 + 5.5. Multiple Commands in Progress ........................... 22 + 6. Client Commands ........................................ 23 + 6.1. Client Commands - Any State ............................ 24 + 6.1.1. CAPABILITY Command ..................................... 24 + 6.1.2. NOOP Command ........................................... 25 + 6.1.3. LOGOUT Command ......................................... 26 + + + +Crispin Standards Track [Page 2] + +RFC 3501 IMAPv4 March 2003 + + + 6.2. Client Commands - Not Authenticated State .............. 26 + 6.2.1. STARTTLS Command ....................................... 27 + 6.2.2. AUTHENTICATE Command ................................... 28 + 6.2.3. LOGIN Command .......................................... 30 + 6.3. Client Commands - Authenticated State .................. 31 + 6.3.1. SELECT Command ......................................... 32 + 6.3.2. EXAMINE Command ........................................ 34 + 6.3.3. CREATE Command ......................................... 34 + 6.3.4. DELETE Command ......................................... 35 + 6.3.5. RENAME Command ......................................... 37 + 6.3.6. SUBSCRIBE Command ...................................... 39 + 6.3.7. UNSUBSCRIBE Command .................................... 39 + 6.3.8. LIST Command ........................................... 40 + 6.3.9. LSUB Command ........................................... 43 + 6.3.10. STATUS Command ......................................... 44 + 6.3.11. APPEND Command ......................................... 46 + 6.4. Client Commands - Selected State ....................... 47 + 6.4.1. CHECK Command .......................................... 47 + 6.4.2. CLOSE Command .......................................... 48 + 6.4.3. EXPUNGE Command ........................................ 49 + 6.4.4. SEARCH Command ......................................... 49 + 6.4.5. FETCH Command .......................................... 54 + 6.4.6. STORE Command .......................................... 58 + 6.4.7. COPY Command ........................................... 59 + 6.4.8. UID Command ............................................ 60 + 6.5. Client Commands - Experimental/Expansion ............... 62 + 6.5.1. X Command ........................................ 62 + 7. Server Responses ....................................... 62 + 7.1. Server Responses - Status Responses .................... 63 + 7.1.1. OK Response ............................................ 65 + 7.1.2. NO Response ............................................ 66 + 7.1.3. BAD Response ........................................... 66 + 7.1.4. PREAUTH Response ....................................... 67 + 7.1.5. BYE Response ........................................... 67 + 7.2. Server Responses - Server and Mailbox Status ........... 68 + 7.2.1. CAPABILITY Response .................................... 68 + 7.2.2. LIST Response .......................................... 69 + 7.2.3. LSUB Response .......................................... 70 + 7.2.4 STATUS Response ........................................ 70 + 7.2.5. SEARCH Response ........................................ 71 + 7.2.6. FLAGS Response ......................................... 71 + 7.3. Server Responses - Mailbox Size ........................ 71 + 7.3.1. EXISTS Response ........................................ 71 + 7.3.2. RECENT Response ........................................ 72 + 7.4. Server Responses - Message Status ...................... 72 + 7.4.1. EXPUNGE Response ....................................... 72 + 7.4.2. FETCH Response ......................................... 73 + 7.5. Server Responses - Command Continuation Request ........ 79 + + + +Crispin Standards Track [Page 3] + +RFC 3501 IMAPv4 March 2003 + + + 8. Sample IMAP4rev1 connection ............................ 80 + 9. Formal Syntax .......................................... 81 + 10. Author's Note .......................................... 92 + 11. Security Considerations ................................ 92 + 11.1. STARTTLS Security Considerations ....................... 92 + 11.2. Other Security Considerations .......................... 93 + 12. IANA Considerations .................................... 94 + Appendices ..................................................... 95 + A. References ............................................. 95 + B. Changes from RFC 2060 .................................. 97 + C. Key Word Index ......................................... 103 + Author's Address ............................................... 107 + Full Copyright Statement ....................................... 108 + +IMAP4rev1 Protocol Specification + +1. How to Read This Document + +1.1. Organization of This Document + + This document is written from the point of view of the implementor of + an IMAP4rev1 client or server. Beyond the protocol overview in + section 2, it is not optimized for someone trying to understand the + operation of the protocol. The material in sections 3 through 5 + provides the general context and definitions with which IMAP4rev1 + operates. + + Sections 6, 7, and 9 describe the IMAP commands, responses, and + syntax, respectively. The relationships among these are such that it + is almost impossible to understand any of them separately. In + particular, do not attempt to deduce command syntax from the command + section alone; instead refer to the Formal Syntax section. + +1.2. Conventions Used in This Document + + "Conventions" are basic principles or procedures. Document + conventions are noted in this section. + + In examples, "C:" and "S:" indicate lines sent by the client and + server respectively. + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "MAY", and "OPTIONAL" in this document are to + be interpreted as described in [KEYWORDS]. + + The word "can" (not "may") is used to refer to a possible + circumstance or situation, as opposed to an optional facility of the + protocol. + + + +Crispin Standards Track [Page 4] + +RFC 3501 IMAPv4 March 2003 + + + "User" is used to refer to a human user, whereas "client" refers to + the software being run by the user. + + "Connection" refers to the entire sequence of client/server + interaction from the initial establishment of the network connection + until its termination. + + "Session" refers to the sequence of client/server interaction from + the time that a mailbox is selected (SELECT or EXAMINE command) until + the time that selection ends (SELECT or EXAMINE of another mailbox, + CLOSE command, or connection termination). + + Characters are 7-bit US-ASCII unless otherwise specified. Other + character sets are indicated using a "CHARSET", as described in + [MIME-IMT] and defined in [CHARSET]. CHARSETs have important + additional semantics in addition to defining character set; refer to + these documents for more detail. + + There are several protocol conventions in IMAP. These refer to + aspects of the specification which are not strictly part of the IMAP + protocol, but reflect generally-accepted practice. Implementations + need to be aware of these conventions, and avoid conflicts whether or + not they implement the convention. For example, "&" may not be used + as a hierarchy delimiter since it conflicts with the Mailbox + International Naming Convention, and other uses of "&" in mailbox + names are impacted as well. + +1.3. Special Notes to Implementors + + Implementors of the IMAP protocol are strongly encouraged to read the + IMAP implementation recommendations document [IMAP-IMPLEMENTATION] in + conjunction with this document, to help understand the intricacies of + this protocol and how best to build an interoperable product. + + IMAP4rev1 is designed to be upwards compatible from the [IMAP2] and + unpublished IMAP2bis protocols. IMAP4rev1 is largely compatible with + the IMAP4 protocol described in RFC 1730; the exception being in + certain facilities added in RFC 1730 that proved problematic and were + subsequently removed. In the course of the evolution of IMAP4rev1, + some aspects in the earlier protocols have become obsolete. Obsolete + commands, responses, and data formats which an IMAP4rev1 + implementation can encounter when used with an earlier implementation + are described in [IMAP-OBSOLETE]. + + Other compatibility issues with IMAP2bis, the most common variant of + the earlier protocol, are discussed in [IMAP-COMPAT]. A full + discussion of compatibility issues with rare (and presumed extinct) + + + + +Crispin Standards Track [Page 5] + +RFC 3501 IMAPv4 March 2003 + + + variants of [IMAP2] is in [IMAP-HISTORICAL]; this document is + primarily of historical interest. + + IMAP was originally developed for the older [RFC-822] standard, and + as a consequence several fetch items in IMAP incorporate "RFC822" in + their name. With the exception of RFC822.SIZE, there are more modern + replacements; for example, the modern version of RFC822.HEADER is + BODY.PEEK[HEADER]. In all cases, "RFC822" should be interpreted as a + reference to the updated [RFC-2822] standard. + +2. Protocol Overview + +2.1. Link Level + + The IMAP4rev1 protocol assumes a reliable data stream such as that + provided by TCP. When TCP is used, an IMAP4rev1 server listens on + port 143. + +2.2. Commands and Responses + + An IMAP4rev1 connection consists of the establishment of a + client/server network connection, an initial greeting from the + server, and client/server interactions. These client/server + interactions consist of a client command, server data, and a server + completion result response. + + All interactions transmitted by client and server are in the form of + lines, that is, strings that end with a CRLF. The protocol receiver + of an IMAP4rev1 client or server is either reading a line, or is + reading a sequence of octets with a known count followed by a line. + +2.2.1. Client Protocol Sender and Server Protocol Receiver + + The client command begins an operation. Each client command is + prefixed with an identifier (typically a short alphanumeric string, + e.g., A0001, A0002, etc.) called a "tag". A different tag is + generated by the client for each command. + + Clients MUST follow the syntax outlined in this specification + strictly. It is a syntax error to send a command with missing or + extraneous spaces or arguments. + + There are two cases in which a line from the client does not + represent a complete command. In one case, a command argument is + quoted with an octet count (see the description of literal in String + under Data Formats); in the other case, the command arguments require + server feedback (see the AUTHENTICATE command). In either case, the + + + + +Crispin Standards Track [Page 6] + +RFC 3501 IMAPv4 March 2003 + + + server sends a command continuation request response if it is ready + for the octets (if appropriate) and the remainder of the command. + This response is prefixed with the token "+". + + Note: If instead, the server detected an error in the + command, it sends a BAD completion response with a tag + matching the command (as described below) to reject the + command and prevent the client from sending any more of the + command. + + It is also possible for the server to send a completion + response for some other command (if multiple commands are + in progress), or untagged data. In either case, the + command continuation request is still pending; the client + takes the appropriate action for the response, and reads + another response from the server. In all cases, the client + MUST send a complete command (including receiving all + command continuation request responses and command + continuations for the command) before initiating a new + command. + + The protocol receiver of an IMAP4rev1 server reads a command line + from the client, parses the command and its arguments, and transmits + server data and a server command completion result response. + +2.2.2. Server Protocol Sender and Client Protocol Receiver + + Data transmitted by the server to the client and status responses + that do not indicate command completion are prefixed with the token + "*", and are called untagged responses. + + Server data MAY be sent as a result of a client command, or MAY be + sent unilaterally by the server. There is no syntactic difference + between server data that resulted from a specific command and server + data that were sent unilaterally. + + The server completion result response indicates the success or + failure of the operation. It is tagged with the same tag as the + client command which began the operation. Thus, if more than one + command is in progress, the tag in a server completion response + identifies the command to which the response applies. There are + three possible server completion responses: OK (indicating success), + NO (indicating failure), or BAD (indicating a protocol error such as + unrecognized command or command syntax error). + + Servers SHOULD enforce the syntax outlined in this specification + strictly. Any client command with a protocol syntax error, including + (but not limited to) missing or extraneous spaces or arguments, + + + +Crispin Standards Track [Page 7] + +RFC 3501 IMAPv4 March 2003 + + + SHOULD be rejected, and the client given a BAD server completion + response. + + The protocol receiver of an IMAP4rev1 client reads a response line + from the server. It then takes action on the response based upon the + first token of the response, which can be a tag, a "*", or a "+". + + A client MUST be prepared to accept any server response at all times. + This includes server data that was not requested. Server data SHOULD + be recorded, so that the client can reference its recorded copy + rather than sending a command to the server to request the data. In + the case of certain server data, the data MUST be recorded. + + This topic is discussed in greater detail in the Server Responses + section. + +2.3. Message Attributes + + In addition to message text, each message has several attributes + associated with it. These attributes can be retrieved individually + or in conjunction with other attributes or message texts. + +2.3.1. Message Numbers + + Messages in IMAP4rev1 are accessed by one of two numbers; the unique + identifier or the message sequence number. + + +2.3.1.1. Unique Identifier (UID) Message Attribute + + A 32-bit value assigned to each message, which when used with the + unique identifier validity value (see below) forms a 64-bit value + that MUST NOT refer to any other message in the mailbox or any + subsequent mailbox with the same name forever. Unique identifiers + are assigned in a strictly ascending fashion in the mailbox; as each + message is added to the mailbox it is assigned a higher UID than the + message(s) which were added previously. Unlike message sequence + numbers, unique identifiers are not necessarily contiguous. + + The unique identifier of a message MUST NOT change during the + session, and SHOULD NOT change between sessions. Any change of + unique identifiers between sessions MUST be detectable using the + UIDVALIDITY mechanism discussed below. Persistent unique identifiers + are required for a client to resynchronize its state from a previous + session with the server (e.g., disconnected or offline access + clients); this is discussed further in [IMAP-DISC]. + + + + + +Crispin Standards Track [Page 8] + +RFC 3501 IMAPv4 March 2003 + + + Associated with every mailbox are two values which aid in unique + identifier handling: the next unique identifier value and the unique + identifier validity value. + + The next unique identifier value is the predicted value that will be + assigned to a new message in the mailbox. Unless the unique + identifier validity also changes (see below), the next unique + identifier value MUST have the following two characteristics. First, + the next unique identifier value MUST NOT change unless new messages + are added to the mailbox; and second, the next unique identifier + value MUST change whenever new messages are added to the mailbox, + even if those new messages are subsequently expunged. + + Note: The next unique identifier value is intended to + provide a means for a client to determine whether any + messages have been delivered to the mailbox since the + previous time it checked this value. It is not intended to + provide any guarantee that any message will have this + unique identifier. A client can only assume, at the time + that it obtains the next unique identifier value, that + messages arriving after that time will have a UID greater + than or equal to that value. + + The unique identifier validity value is sent in a UIDVALIDITY + response code in an OK untagged response at mailbox selection time. + If unique identifiers from an earlier session fail to persist in this + session, the unique identifier validity value MUST be greater than + the one used in the earlier session. + + Note: Ideally, unique identifiers SHOULD persist at all + times. Although this specification recognizes that failure + to persist can be unavoidable in certain server + environments, it STRONGLY ENCOURAGES message store + implementation techniques that avoid this problem. For + example: + + 1) Unique identifiers MUST be strictly ascending in the + mailbox at all times. If the physical message store is + re-ordered by a non-IMAP agent, this requires that the + unique identifiers in the mailbox be regenerated, since + the former unique identifiers are no longer strictly + ascending as a result of the re-ordering. + + 2) If the message store has no mechanism to store unique + identifiers, it must regenerate unique identifiers at + each session, and each session must have a unique + UIDVALIDITY value. + + + + +Crispin Standards Track [Page 9] + +RFC 3501 IMAPv4 March 2003 + + + 3) If the mailbox is deleted and a new mailbox with the + same name is created at a later date, the server must + either keep track of unique identifiers from the + previous instance of the mailbox, or it must assign a + new UIDVALIDITY value to the new instance of the + mailbox. A good UIDVALIDITY value to use in this case + is a 32-bit representation of the creation date/time of + the mailbox. It is alright to use a constant such as + 1, but only if it guaranteed that unique identifiers + will never be reused, even in the case of a mailbox + being deleted (or renamed) and a new mailbox by the + same name created at some future time. + + 4) The combination of mailbox name, UIDVALIDITY, and UID + must refer to a single immutable message on that server + forever. In particular, the internal date, [RFC-2822] + size, envelope, body structure, and message texts + (RFC822, RFC822.HEADER, RFC822.TEXT, and all BODY[...] + fetch data items) must never change. This does not + include message numbers, nor does it include attributes + that can be set by a STORE command (e.g., FLAGS). + + +2.3.1.2. Message Sequence Number Message Attribute + + A relative position from 1 to the number of messages in the mailbox. + This position MUST be ordered by ascending unique identifier. As + each new message is added, it is assigned a message sequence number + that is 1 higher than the number of messages in the mailbox before + that new message was added. + + Message sequence numbers can be reassigned during the session. For + example, when a message is permanently removed (expunged) from the + mailbox, the message sequence number for all subsequent messages is + decremented. The number of messages in the mailbox is also + decremented. Similarly, a new message can be assigned a message + sequence number that was once held by some other message prior to an + expunge. + + In addition to accessing messages by relative position in the + mailbox, message sequence numbers can be used in mathematical + calculations. For example, if an untagged "11 EXISTS" is received, + and previously an untagged "8 EXISTS" was received, three new + messages have arrived with message sequence numbers of 9, 10, and 11. + Another example, if message 287 in a 523 message mailbox has UID + 12345, there are exactly 286 messages which have lesser UIDs and 236 + messages which have greater UIDs. + + + + +Crispin Standards Track [Page 10] + +RFC 3501 IMAPv4 March 2003 + + +2.3.2. Flags Message Attribute + + A list of zero or more named tokens associated with the message. A + flag is set by its addition to this list, and is cleared by its + removal. There are two types of flags in IMAP4rev1. A flag of + either type can be permanent or session-only. + + A system flag is a flag name that is pre-defined in this + specification. All system flags begin with "\". Certain system + flags (\Deleted and \Seen) have special semantics described + elsewhere. The currently-defined system flags are: + + \Seen + Message has been read + + \Answered + Message has been answered + + \Flagged + Message is "flagged" for urgent/special attention + + \Deleted + Message is "deleted" for removal by later EXPUNGE + + \Draft + Message has not completed composition (marked as a draft). + + \Recent + Message is "recently" arrived in this mailbox. This session + is the first session to have been notified about this + message; if the session is read-write, subsequent sessions + will not see \Recent set for this message. This flag can not + be altered by the client. + + If it is not possible to determine whether or not this + session is the first session to be notified about a message, + then that message SHOULD be considered recent. + + If multiple connections have the same mailbox selected + simultaneously, it is undefined which of these connections + will see newly-arrived messages with \Recent set and which + will see it without \Recent set. + + A keyword is defined by the server implementation. Keywords do not + begin with "\". Servers MAY permit the client to define new keywords + in the mailbox (see the description of the PERMANENTFLAGS response + code for more information). + + + + +Crispin Standards Track [Page 11] + +RFC 3501 IMAPv4 March 2003 + + + A flag can be permanent or session-only on a per-flag basis. + Permanent flags are those which the client can add or remove from the + message flags permanently; that is, concurrent and subsequent + sessions will see any change in permanent flags. Changes to session + flags are valid only in that session. + + Note: The \Recent system flag is a special case of a + session flag. \Recent can not be used as an argument in a + STORE or APPEND command, and thus can not be changed at + all. + +2.3.3. Internal Date Message Attribute + + The internal date and time of the message on the server. This + is not the date and time in the [RFC-2822] header, but rather a + date and time which reflects when the message was received. In + the case of messages delivered via [SMTP], this SHOULD be the + date and time of final delivery of the message as defined by + [SMTP]. In the case of messages delivered by the IMAP4rev1 COPY + command, this SHOULD be the internal date and time of the source + message. In the case of messages delivered by the IMAP4rev1 + APPEND command, this SHOULD be the date and time as specified in + the APPEND command description. All other cases are + implementation defined. + +2.3.4. [RFC-2822] Size Message Attribute + + The number of octets in the message, as expressed in [RFC-2822] + format. + +2.3.5. Envelope Structure Message Attribute + + A parsed representation of the [RFC-2822] header of the message. + Note that the IMAP Envelope structure is not the same as an + [SMTP] envelope. + +2.3.6. Body Structure Message Attribute + + A parsed representation of the [MIME-IMB] body structure + information of the message. + + + + + + + + + + + +Crispin Standards Track [Page 12] + +RFC 3501 IMAPv4 March 2003 + + +2.4. Message Texts + + In addition to being able to fetch the full [RFC-2822] text of a + message, IMAP4rev1 permits the fetching of portions of the full + message text. Specifically, it is possible to fetch the + [RFC-2822] message header, [RFC-2822] message body, a [MIME-IMB] + body part, or a [MIME-IMB] header. + +3. State and Flow Diagram + + Once the connection between client and server is established, an + IMAP4rev1 connection is in one of four states. The initial + state is identified in the server greeting. Most commands are + only valid in certain states. It is a protocol error for the + client to attempt a command while the connection is in an + inappropriate state, and the server will respond with a BAD or + NO (depending upon server implementation) command completion + result. + +3.1. Not Authenticated State + + In the not authenticated state, the client MUST supply + authentication credentials before most commands will be + permitted. This state is entered when a connection starts + unless the connection has been pre-authenticated. + +3.2. Authenticated State + + In the authenticated state, the client is authenticated and MUST + select a mailbox to access before commands that affect messages + will be permitted. This state is entered when a + pre-authenticated connection starts, when acceptable + authentication credentials have been provided, after an error in + selecting a mailbox, or after a successful CLOSE command. + +3.3. Selected State + + In a selected state, a mailbox has been selected to access. + This state is entered when a mailbox has been successfully + selected. + + + + + + + + + + + +Crispin Standards Track [Page 13] + +RFC 3501 IMAPv4 March 2003 + + +3.4. Logout State + + In the logout state, the connection is being terminated. This + state can be entered as a result of a client request (via the + LOGOUT command) or by unilateral action on the part of either + the client or server. + + If the client requests the logout state, the server MUST send an + untagged BYE response and a tagged OK response to the LOGOUT + command before the server closes the connection; and the client + MUST read the tagged OK response to the LOGOUT command before + the client closes the connection. + + A server MUST NOT unilaterally close the connection without + sending an untagged BYE response that contains the reason for + having done so. A client SHOULD NOT unilaterally close the + connection, and instead SHOULD issue a LOGOUT command. If the + server detects that the client has unilaterally closed the + connection, the server MAY omit the untagged BYE response and + simply close its connection. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Crispin Standards Track [Page 14] + +RFC 3501 IMAPv4 March 2003 + + + +----------------------+ + |connection established| + +----------------------+ + || + \/ + +--------------------------------------+ + | server greeting | + +--------------------------------------+ + || (1) || (2) || (3) + \/ || || + +-----------------+ || || + |Not Authenticated| || || + +-----------------+ || || + || (7) || (4) || || + || \/ \/ || + || +----------------+ || + || | Authenticated |<=++ || + || +----------------+ || || + || || (7) || (5) || (6) || + || || \/ || || + || || +--------+ || || + || || |Selected|==++ || + || || +--------+ || + || || || (7) || + \/ \/ \/ \/ + +--------------------------------------+ + | Logout | + +--------------------------------------+ + || + \/ + +-------------------------------+ + |both sides close the connection| + +-------------------------------+ + + (1) connection without pre-authentication (OK greeting) + (2) pre-authenticated connection (PREAUTH greeting) + (3) rejected connection (BYE greeting) + (4) successful LOGIN or AUTHENTICATE command + (5) successful SELECT or EXAMINE command + (6) CLOSE command, or failed SELECT or EXAMINE command + (7) LOGOUT command, server shutdown, or connection closed + + + + + + + + + + +Crispin Standards Track [Page 15] + +RFC 3501 IMAPv4 March 2003 + + +4. Data Formats + + IMAP4rev1 uses textual commands and responses. Data in + IMAP4rev1 can be in one of several forms: atom, number, string, + parenthesized list, or NIL. Note that a particular data item + may take more than one form; for example, a data item defined as + using "astring" syntax may be either an atom or a string. + +4.1. Atom + + An atom consists of one or more non-special characters. + +4.2. Number + + A number consists of one or more digit characters, and + represents a numeric value. + +4.3. String + + A string is in one of two forms: either literal or quoted + string. The literal form is the general form of string. The + quoted string form is an alternative that avoids the overhead of + processing a literal at the cost of limitations of characters + which may be used. + + A literal is a sequence of zero or more octets (including CR and + LF), prefix-quoted with an octet count in the form of an open + brace ("{"), the number of octets, close brace ("}"), and CRLF. + In the case of literals transmitted from server to client, the + CRLF is immediately followed by the octet data. In the case of + literals transmitted from client to server, the client MUST wait + to receive a command continuation request (described later in + this document) before sending the octet data (and the remainder + of the command). + + A quoted string is a sequence of zero or more 7-bit characters, + excluding CR and LF, with double quote (<">) characters at each + end. + + The empty string is represented as either "" (a quoted string + with zero characters between double quotes) or as {0} followed + by CRLF (a literal with an octet count of 0). + + Note: Even if the octet count is 0, a client transmitting a + literal MUST wait to receive a command continuation request. + + + + + + +Crispin Standards Track [Page 16] + +RFC 3501 IMAPv4 March 2003 + + +4.3.1. 8-bit and Binary Strings + + 8-bit textual and binary mail is supported through the use of a + [MIME-IMB] content transfer encoding. IMAP4rev1 implementations MAY + transmit 8-bit or multi-octet characters in literals, but SHOULD do + so only when the [CHARSET] is identified. + + Although a BINARY body encoding is defined, unencoded binary strings + are not permitted. A "binary string" is any string with NUL + characters. Implementations MUST encode binary data into a textual + form, such as BASE64, before transmitting the data. A string with an + excessive amount of CTL characters MAY also be considered to be + binary. + +4.4. Parenthesized List + + Data structures are represented as a "parenthesized list"; a sequence + of data items, delimited by space, and bounded at each end by + parentheses. A parenthesized list can contain other parenthesized + lists, using multiple levels of parentheses to indicate nesting. + + The empty list is represented as () -- a parenthesized list with no + members. + +4.5. NIL + + The special form "NIL" represents the non-existence of a particular + data item that is represented as a string or parenthesized list, as + distinct from the empty string "" or the empty parenthesized list (). + + Note: NIL is never used for any data item which takes the + form of an atom. For example, a mailbox name of "NIL" is a + mailbox named NIL as opposed to a non-existent mailbox + name. This is because mailbox uses "astring" syntax which + is an atom or a string. Conversely, an addr-name of NIL is + a non-existent personal name, because addr-name uses + "nstring" syntax which is NIL or a string, but never an + atom. + + + + + + + + + + + + + +Crispin Standards Track [Page 17] + +RFC 3501 IMAPv4 March 2003 + + +5. Operational Considerations + + The following rules are listed here to ensure that all IMAP4rev1 + implementations interoperate properly. + +5.1. Mailbox Naming + + Mailbox names are 7-bit. Client implementations MUST NOT attempt to + create 8-bit mailbox names, and SHOULD interpret any 8-bit mailbox + names returned by LIST or LSUB as UTF-8. Server implementations + SHOULD prohibit the creation of 8-bit mailbox names, and SHOULD NOT + return 8-bit mailbox names in LIST or LSUB. See section 5.1.3 for + more information on how to represent non-ASCII mailbox names. + + Note: 8-bit mailbox names were undefined in earlier + versions of this protocol. Some sites used a local 8-bit + character set to represent non-ASCII mailbox names. Such + usage is not interoperable, and is now formally deprecated. + + The case-insensitive mailbox name INBOX is a special name reserved to + mean "the primary mailbox for this user on this server". The + interpretation of all other names is implementation-dependent. + + In particular, this specification takes no position on case + sensitivity in non-INBOX mailbox names. Some server implementations + are fully case-sensitive; others preserve case of a newly-created + name but otherwise are case-insensitive; and yet others coerce names + to a particular case. Client implementations MUST interact with any + of these. If a server implementation interprets non-INBOX mailbox + names as case-insensitive, it MUST treat names using the + international naming convention specially as described in section + 5.1.3. + + There are certain client considerations when creating a new mailbox + name: + + 1) Any character which is one of the atom-specials (see the Formal + Syntax) will require that the mailbox name be represented as a + quoted string or literal. + + 2) CTL and other non-graphic characters are difficult to represent + in a user interface and are best avoided. + + 3) Although the list-wildcard characters ("%" and "*") are valid + in a mailbox name, it is difficult to use such mailbox names + with the LIST and LSUB commands due to the conflict with + wildcard interpretation. + + + + +Crispin Standards Track [Page 18] + +RFC 3501 IMAPv4 March 2003 + + + 4) Usually, a character (determined by the server implementation) + is reserved to delimit levels of hierarchy. + + 5) Two characters, "#" and "&", have meanings by convention, and + should be avoided except when used in that convention. + +5.1.1. Mailbox Hierarchy Naming + + If it is desired to export hierarchical mailbox names, mailbox names + MUST be left-to-right hierarchical using a single character to + separate levels of hierarchy. The same hierarchy separator character + is used for all levels of hierarchy within a single name. + +5.1.2. Mailbox Namespace Naming Convention + + By convention, the first hierarchical element of any mailbox name + which begins with "#" identifies the "namespace" of the remainder of + the name. This makes it possible to disambiguate between different + types of mailbox stores, each of which have their own namespaces. + + For example, implementations which offer access to USENET + newsgroups MAY use the "#news" namespace to partition the + USENET newsgroup namespace from that of other mailboxes. + Thus, the comp.mail.misc newsgroup would have a mailbox + name of "#news.comp.mail.misc", and the name + "comp.mail.misc" can refer to a different object (e.g., a + user's private mailbox). + +5.1.3. Mailbox International Naming Convention + + By convention, international mailbox names in IMAP4rev1 are specified + using a modified version of the UTF-7 encoding described in [UTF-7]. + Modified UTF-7 may also be usable in servers that implement an + earlier version of this protocol. + + In modified UTF-7, printable US-ASCII characters, except for "&", + represent themselves; that is, characters with octet values 0x20-0x25 + and 0x27-0x7e. The character "&" (0x26) is represented by the + two-octet sequence "&-". + + All other characters (octet values 0x00-0x1f and 0x7f-0xff) are + represented in modified BASE64, with a further modification from + [UTF-7] that "," is used instead of "/". Modified BASE64 MUST NOT be + used to represent any printing US-ASCII character which can represent + itself. + + + + + + +Crispin Standards Track [Page 19] + +RFC 3501 IMAPv4 March 2003 + + + "&" is used to shift to modified BASE64 and "-" to shift back to + US-ASCII. There is no implicit shift from BASE64 to US-ASCII, and + null shifts ("-&" while in BASE64; note that "&-" while in US-ASCII + means "&") are not permitted. However, all names start in US-ASCII, + and MUST end in US-ASCII; that is, a name that ends with a non-ASCII + ISO-10646 character MUST end with a "-"). + + The purpose of these modifications is to correct the following + problems with UTF-7: + + 1) UTF-7 uses the "+" character for shifting; this conflicts with + the common use of "+" in mailbox names, in particular USENET + newsgroup names. + + 2) UTF-7's encoding is BASE64 which uses the "/" character; this + conflicts with the use of "/" as a popular hierarchy delimiter. + + 3) UTF-7 prohibits the unencoded usage of "\"; this conflicts with + the use of "\" as a popular hierarchy delimiter. + + 4) UTF-7 prohibits the unencoded usage of "~"; this conflicts with + the use of "~" in some servers as a home directory indicator. + + 5) UTF-7 permits multiple alternate forms to represent the same + string; in particular, printable US-ASCII characters can be + represented in encoded form. + + Although modified UTF-7 is a convention, it establishes certain + requirements on server handling of any mailbox name with an + embedded "&" character. In particular, server implementations + MUST preserve the exact form of the modified BASE64 portion of a + modified UTF-7 name and treat that text as case-sensitive, even if + names are otherwise case-insensitive or case-folded. + + Server implementations SHOULD verify that any mailbox name with an + embedded "&" character, used as an argument to CREATE, is: in the + correctly modified UTF-7 syntax, has no superfluous shifts, and + has no encoding in modified BASE64 of any printing US-ASCII + character which can represent itself. However, client + implementations MUST NOT depend upon the server doing this, and + SHOULD NOT attempt to create a mailbox name with an embedded "&" + character unless it complies with the modified UTF-7 syntax. + + Server implementations which export a mail store that does not + follow the modified UTF-7 convention MUST convert to modified + UTF-7 any mailbox name that contains either non-ASCII characters + or the "&" character. + + + + +Crispin Standards Track [Page 20] + +RFC 3501 IMAPv4 March 2003 + + + For example, here is a mailbox name which mixes English, + Chinese, and Japanese text: + ~peter/mail/&U,BTFw-/&ZeVnLIqe- + + For example, the string "&Jjo!" is not a valid mailbox + name because it does not contain a shift to US-ASCII + before the "!". The correct form is "&Jjo-!". The + string "&U,BTFw-&ZeVnLIqe-" is not permitted because it + contains a superfluous shift. The correct form is + "&U,BTF2XlZyyKng-". + +5.2. Mailbox Size and Message Status Updates + + At any time, a server can send data that the client did not request. + Sometimes, such behavior is REQUIRED. For example, agents other than + the server MAY add messages to the mailbox (e.g., new message + delivery), change the flags of the messages in the mailbox (e.g., + simultaneous access to the same mailbox by multiple agents), or even + remove messages from the mailbox. A server MUST send mailbox size + updates automatically if a mailbox size change is observed during the + processing of a command. A server SHOULD send message flag updates + automatically, without requiring the client to request such updates + explicitly. + + Special rules exist for server notification of a client about the + removal of messages to prevent synchronization errors; see the + description of the EXPUNGE response for more detail. In particular, + it is NOT permitted to send an EXISTS response that would reduce the + number of messages in the mailbox; only the EXPUNGE response can do + this. + + Regardless of what implementation decisions a client makes on + remembering data from the server, a client implementation MUST record + mailbox size updates. It MUST NOT assume that any command after the + initial mailbox selection will return the size of the mailbox. + +5.3. Response when no Command in Progress + + Server implementations are permitted to send an untagged response + (except for EXPUNGE) while there is no command in progress. Server + implementations that send such responses MUST deal with flow control + considerations. Specifically, they MUST either (1) verify that the + size of the data does not exceed the underlying transport's available + window size, or (2) use non-blocking writes. + + + + + + + +Crispin Standards Track [Page 21] + +RFC 3501 IMAPv4 March 2003 + + +5.4. Autologout Timer + + If a server has an inactivity autologout timer, the duration of that + timer MUST be at least 30 minutes. The receipt of ANY command from + the client during that interval SHOULD suffice to reset the + autologout timer. + +5.5. Multiple Commands in Progress + + The client MAY send another command without waiting for the + completion result response of a command, subject to ambiguity rules + (see below) and flow control constraints on the underlying data + stream. Similarly, a server MAY begin processing another command + before processing the current command to completion, subject to + ambiguity rules. However, any command continuation request responses + and command continuations MUST be negotiated before any subsequent + command is initiated. + + The exception is if an ambiguity would result because of a command + that would affect the results of other commands. Clients MUST NOT + send multiple commands without waiting if an ambiguity would result. + If the server detects a possible ambiguity, it MUST execute commands + to completion in the order given by the client. + + The most obvious example of ambiguity is when a command would affect + the results of another command, e.g., a FETCH of a message's flags + and a STORE of that same message's flags. + + A non-obvious ambiguity occurs with commands that permit an untagged + EXPUNGE response (commands other than FETCH, STORE, and SEARCH), + since an untagged EXPUNGE response can invalidate sequence numbers in + a subsequent command. This is not a problem for FETCH, STORE, or + SEARCH commands because servers are prohibited from sending EXPUNGE + responses while any of those commands are in progress. Therefore, if + the client sends any command other than FETCH, STORE, or SEARCH, it + MUST wait for the completion result response before sending a command + with message sequence numbers. + + Note: UID FETCH, UID STORE, and UID SEARCH are different + commands from FETCH, STORE, and SEARCH. If the client + sends a UID command, it must wait for a completion result + response before sending a command with message sequence + numbers. + + + + + + + + +Crispin Standards Track [Page 22] + +RFC 3501 IMAPv4 March 2003 + + + For example, the following non-waiting command sequences are invalid: + + FETCH + NOOP + STORE + STORE + COPY + FETCH + COPY + COPY + CHECK + FETCH + + The following are examples of valid non-waiting command sequences: + + FETCH + STORE + SEARCH + CHECK + STORE + COPY + EXPUNGE + + UID SEARCH + UID SEARCH may be valid or invalid as a non-waiting + command sequence, depending upon whether or not the second UID + SEARCH contains message sequence numbers. + +6. Client Commands + + IMAP4rev1 commands are described in this section. Commands are + organized by the state in which the command is permitted. Commands + which are permitted in multiple states are listed in the minimum + permitted state (for example, commands valid in authenticated and + selected state are listed in the authenticated state commands). + + Command arguments, identified by "Arguments:" in the command + descriptions below, are described by function, not by syntax. The + precise syntax of command arguments is described in the Formal Syntax + section. + + Some commands cause specific server responses to be returned; these + are identified by "Responses:" in the command descriptions below. + See the response descriptions in the Responses section for + information on these responses, and the Formal Syntax section for the + precise syntax of these responses. It is possible for server data to + be transmitted as a result of any command. Thus, commands that do + not specifically require server data specify "no specific responses + for this command" instead of "none". + + The "Result:" in the command description refers to the possible + tagged status responses to a command, and any special interpretation + of these status responses. + + The state of a connection is only changed by successful commands + which are documented as changing state. A rejected command (BAD + response) never changes the state of the connection or of the + selected mailbox. A failed command (NO response) generally does not + change the state of the connection or of the selected mailbox; the + exception being the SELECT and EXAMINE commands. + + + +Crispin Standards Track [Page 23] + +RFC 3501 IMAPv4 March 2003 + + +6.1. Client Commands - Any State + + The following commands are valid in any state: CAPABILITY, NOOP, and + LOGOUT. + +6.1.1. CAPABILITY Command + + Arguments: none + + Responses: REQUIRED untagged response: CAPABILITY + + Result: OK - capability completed + BAD - command unknown or arguments invalid + + The CAPABILITY command requests a listing of capabilities that the + server supports. The server MUST send a single untagged + CAPABILITY response with "IMAP4rev1" as one of the listed + capabilities before the (tagged) OK response. + + A capability name which begins with "AUTH=" indicates that the + server supports that particular authentication mechanism. All + such names are, by definition, part of this specification. For + example, the authorization capability for an experimental + "blurdybloop" authenticator would be "AUTH=XBLURDYBLOOP" and not + "XAUTH=BLURDYBLOOP" or "XAUTH=XBLURDYBLOOP". + + Other capability names refer to extensions, revisions, or + amendments to this specification. See the documentation of the + CAPABILITY response for additional information. No capabilities, + beyond the base IMAP4rev1 set defined in this specification, are + enabled without explicit client action to invoke the capability. + + Client and server implementations MUST implement the STARTTLS, + LOGINDISABLED, and AUTH=PLAIN (described in [IMAP-TLS]) + capabilities. See the Security Considerations section for + important information. + + See the section entitled "Client Commands - + Experimental/Expansion" for information about the form of site or + implementation-specific capabilities. + + + + + + + + + + + +Crispin Standards Track [Page 24] + +RFC 3501 IMAPv4 March 2003 + + + Example: C: abcd CAPABILITY + S: * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI + LOGINDISABLED + S: abcd OK CAPABILITY completed + C: efgh STARTTLS + S: efgh OK STARTLS completed + + C: ijkl CAPABILITY + S: * CAPABILITY IMAP4rev1 AUTH=GSSAPI AUTH=PLAIN + S: ijkl OK CAPABILITY completed + + +6.1.2. NOOP Command + + Arguments: none + + Responses: no specific responses for this command (but see below) + + Result: OK - noop completed + BAD - command unknown or arguments invalid + + The NOOP command always succeeds. It does nothing. + + Since any command can return a status update as untagged data, the + NOOP command can be used as a periodic poll for new messages or + message status updates during a period of inactivity (this is the + preferred method to do this). The NOOP command can also be used + to reset any inactivity autologout timer on the server. + + Example: C: a002 NOOP + S: a002 OK NOOP completed + . . . + C: a047 NOOP + S: * 22 EXPUNGE + S: * 23 EXISTS + S: * 3 RECENT + S: * 14 FETCH (FLAGS (\Seen \Deleted)) + S: a047 OK NOOP completed + + + + + + + + + + + + + +Crispin Standards Track [Page 25] + +RFC 3501 IMAPv4 March 2003 + + +6.1.3. LOGOUT Command + + Arguments: none + + Responses: REQUIRED untagged response: BYE + + Result: OK - logout completed + BAD - command unknown or arguments invalid + + The LOGOUT command informs the server that the client is done with + the connection. The server MUST send a BYE untagged response + before the (tagged) OK response, and then close the network + connection. + + Example: C: A023 LOGOUT + S: * BYE IMAP4rev1 Server logging out + S: A023 OK LOGOUT completed + (Server and client then close the connection) + +6.2. Client Commands - Not Authenticated State + + In the not authenticated state, the AUTHENTICATE or LOGIN command + establishes authentication and enters the authenticated state. The + AUTHENTICATE command provides a general mechanism for a variety of + authentication techniques, privacy protection, and integrity + checking; whereas the LOGIN command uses a traditional user name and + plaintext password pair and has no means of establishing privacy + protection or integrity checking. + + The STARTTLS command is an alternate form of establishing session + privacy protection and integrity checking, but does not establish + authentication or enter the authenticated state. + + Server implementations MAY allow access to certain mailboxes without + establishing authentication. This can be done by means of the + ANONYMOUS [SASL] authenticator described in [ANONYMOUS]. An older + convention is a LOGIN command using the userid "anonymous"; in this + case, a password is required although the server may choose to accept + any password. The restrictions placed on anonymous users are + implementation-dependent. + + Once authenticated (including as anonymous), it is not possible to + re-enter not authenticated state. + + + + + + + + +Crispin Standards Track [Page 26] + +RFC 3501 IMAPv4 March 2003 + + + In addition to the universal commands (CAPABILITY, NOOP, and LOGOUT), + the following commands are valid in the not authenticated state: + STARTTLS, AUTHENTICATE and LOGIN. See the Security Considerations + section for important information about these commands. + +6.2.1. STARTTLS Command + + Arguments: none + + Responses: no specific response for this command + + Result: OK - starttls completed, begin TLS negotiation + BAD - command unknown or arguments invalid + + A [TLS] negotiation begins immediately after the CRLF at the end + of the tagged OK response from the server. Once a client issues a + STARTTLS command, it MUST NOT issue further commands until a + server response is seen and the [TLS] negotiation is complete. + + The server remains in the non-authenticated state, even if client + credentials are supplied during the [TLS] negotiation. This does + not preclude an authentication mechanism such as EXTERNAL (defined + in [SASL]) from using client identity determined by the [TLS] + negotiation. + + Once [TLS] has been started, the client MUST discard cached + information about server capabilities and SHOULD re-issue the + CAPABILITY command. This is necessary to protect against man-in- + the-middle attacks which alter the capabilities list prior to + STARTTLS. The server MAY advertise different capabilities after + STARTTLS. + + Example: C: a001 CAPABILITY + S: * CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED + S: a001 OK CAPABILITY completed + C: a002 STARTTLS + S: a002 OK Begin TLS negotiation now + + C: a003 CAPABILITY + S: * CAPABILITY IMAP4rev1 AUTH=PLAIN + S: a003 OK CAPABILITY completed + C: a004 LOGIN joe password + S: a004 OK LOGIN completed + + + + + + + + +Crispin Standards Track [Page 27] + +RFC 3501 IMAPv4 March 2003 + + +6.2.2. AUTHENTICATE Command + + Arguments: authentication mechanism name + + Responses: continuation data can be requested + + Result: OK - authenticate completed, now in authenticated state + NO - authenticate failure: unsupported authentication + mechanism, credentials rejected + BAD - command unknown or arguments invalid, + authentication exchange cancelled + + The AUTHENTICATE command indicates a [SASL] authentication + mechanism to the server. If the server supports the requested + authentication mechanism, it performs an authentication protocol + exchange to authenticate and identify the client. It MAY also + negotiate an OPTIONAL security layer for subsequent protocol + interactions. If the requested authentication mechanism is not + supported, the server SHOULD reject the AUTHENTICATE command by + sending a tagged NO response. + + The AUTHENTICATE command does not support the optional "initial + response" feature of [SASL]. Section 5.1 of [SASL] specifies how + to handle an authentication mechanism which uses an initial + response. + + The service name specified by this protocol's profile of [SASL] is + "imap". + + The authentication protocol exchange consists of a series of + server challenges and client responses that are specific to the + authentication mechanism. A server challenge consists of a + command continuation request response with the "+" token followed + by a BASE64 encoded string. The client response consists of a + single line consisting of a BASE64 encoded string. If the client + wishes to cancel an authentication exchange, it issues a line + consisting of a single "*". If the server receives such a + response, it MUST reject the AUTHENTICATE command by sending a + tagged BAD response. + + If a security layer is negotiated through the [SASL] + authentication exchange, it takes effect immediately following the + CRLF that concludes the authentication exchange for the client, + and the CRLF of the tagged OK response for the server. + + While client and server implementations MUST implement the + AUTHENTICATE command itself, it is not required to implement any + authentication mechanisms other than the PLAIN mechanism described + + + +Crispin Standards Track [Page 28] + +RFC 3501 IMAPv4 March 2003 + + + in [IMAP-TLS]. Also, an authentication mechanism is not required + to support any security layers. + + Note: a server implementation MUST implement a + configuration in which it does NOT permit any plaintext + password mechanisms, unless either the STARTTLS command + has been negotiated or some other mechanism that + protects the session from password snooping has been + provided. Server sites SHOULD NOT use any configuration + which permits a plaintext password mechanism without + such a protection mechanism against password snooping. + Client and server implementations SHOULD implement + additional [SASL] mechanisms that do not use plaintext + passwords, such the GSSAPI mechanism described in [SASL] + and/or the [DIGEST-MD5] mechanism. + + Servers and clients can support multiple authentication + mechanisms. The server SHOULD list its supported authentication + mechanisms in the response to the CAPABILITY command so that the + client knows which authentication mechanisms to use. + + A server MAY include a CAPABILITY response code in the tagged OK + response of a successful AUTHENTICATE command in order to send + capabilities automatically. It is unnecessary for a client to + send a separate CAPABILITY command if it recognizes these + automatic capabilities. This should only be done if a security + layer was not negotiated by the AUTHENTICATE command, because the + tagged OK response as part of an AUTHENTICATE command is not + protected by encryption/integrity checking. [SASL] requires the + client to re-issue a CAPABILITY command in this case. + + If an AUTHENTICATE command fails with a NO response, the client + MAY try another authentication mechanism by issuing another + AUTHENTICATE command. It MAY also attempt to authenticate by + using the LOGIN command (see section 6.2.3 for more detail). In + other words, the client MAY request authentication types in + decreasing order of preference, with the LOGIN command as a last + resort. + + The authorization identity passed from the client to the server + during the authentication exchange is interpreted by the server as + the user name whose privileges the client is requesting. + + + + + + + + + +Crispin Standards Track [Page 29] + +RFC 3501 IMAPv4 March 2003 + + + Example: S: * OK IMAP4rev1 Server + C: A001 AUTHENTICATE GSSAPI + S: + + C: YIIB+wYJKoZIhvcSAQICAQBuggHqMIIB5qADAgEFoQMCAQ6iBw + MFACAAAACjggEmYYIBIjCCAR6gAwIBBaESGxB1Lndhc2hpbmd0 + b24uZWR1oi0wK6ADAgEDoSQwIhsEaW1hcBsac2hpdmFtcy5jYW + Mud2FzaGluZ3Rvbi5lZHWjgdMwgdCgAwIBAaEDAgEDooHDBIHA + cS1GSa5b+fXnPZNmXB9SjL8Ollj2SKyb+3S0iXMljen/jNkpJX + AleKTz6BQPzj8duz8EtoOuNfKgweViyn/9B9bccy1uuAE2HI0y + C/PHXNNU9ZrBziJ8Lm0tTNc98kUpjXnHZhsMcz5Mx2GR6dGknb + I0iaGcRerMUsWOuBmKKKRmVMMdR9T3EZdpqsBd7jZCNMWotjhi + vd5zovQlFqQ2Wjc2+y46vKP/iXxWIuQJuDiisyXF0Y8+5GTpAL + pHDc1/pIGmMIGjoAMCAQGigZsEgZg2on5mSuxoDHEA1w9bcW9n + FdFxDKpdrQhVGVRDIzcCMCTzvUboqb5KjY1NJKJsfjRQiBYBdE + NKfzK+g5DlV8nrw81uOcP8NOQCLR5XkoMHC0Dr/80ziQzbNqhx + O6652Npft0LQwJvenwDI13YxpwOdMXzkWZN/XrEqOWp6GCgXTB + vCyLWLlWnbaUkZdEYbKHBPjd8t/1x5Yg== + S: + YGgGCSqGSIb3EgECAgIAb1kwV6ADAgEFoQMCAQ+iSzBJoAMC + AQGiQgRAtHTEuOP2BXb9sBYFR4SJlDZxmg39IxmRBOhXRKdDA0 + uHTCOT9Bq3OsUTXUlk0CsFLoa8j+gvGDlgHuqzWHPSQg== + C: + S: + YDMGCSqGSIb3EgECAgIBAAD/////6jcyG4GE3KkTzBeBiVHe + ceP2CWY0SR0fAQAgAAQEBAQ= + C: YDMGCSqGSIb3EgECAgIBAAD/////3LQBHXTpFfZgrejpLlLImP + wkhbfa2QteAQAgAG1yYwE= + S: A001 OK GSSAPI authentication successful + + Note: The line breaks within server challenges and client + responses are for editorial clarity and are not in real + authenticators. + + +6.2.3. LOGIN Command + + Arguments: user name + password + + Responses: no specific responses for this command + + Result: OK - login completed, now in authenticated state + NO - login failure: user name or password rejected + BAD - command unknown or arguments invalid + + The LOGIN command identifies the client to the server and carries + the plaintext password authenticating this user. + + + + + + +Crispin Standards Track [Page 30] + +RFC 3501 IMAPv4 March 2003 + + + A server MAY include a CAPABILITY response code in the tagged OK + response to a successful LOGIN command in order to send + capabilities automatically. It is unnecessary for a client to + send a separate CAPABILITY command if it recognizes these + automatic capabilities. + + Example: C: a001 LOGIN SMITH SESAME + S: a001 OK LOGIN completed + + Note: Use of the LOGIN command over an insecure network + (such as the Internet) is a security risk, because anyone + monitoring network traffic can obtain plaintext passwords. + The LOGIN command SHOULD NOT be used except as a last + resort, and it is recommended that client implementations + have a means to disable any automatic use of the LOGIN + command. + + Unless either the STARTTLS command has been negotiated or + some other mechanism that protects the session from + password snooping has been provided, a server + implementation MUST implement a configuration in which it + advertises the LOGINDISABLED capability and does NOT permit + the LOGIN command. Server sites SHOULD NOT use any + configuration which permits the LOGIN command without such + a protection mechanism against password snooping. A client + implementation MUST NOT send a LOGIN command if the + LOGINDISABLED capability is advertised. + +6.3. Client Commands - Authenticated State + + In the authenticated state, commands that manipulate mailboxes as + atomic entities are permitted. Of these commands, the SELECT and + EXAMINE commands will select a mailbox for access and enter the + selected state. + + In addition to the universal commands (CAPABILITY, NOOP, and LOGOUT), + the following commands are valid in the authenticated state: SELECT, + EXAMINE, CREATE, DELETE, RENAME, SUBSCRIBE, UNSUBSCRIBE, LIST, LSUB, + STATUS, and APPEND. + + + + + + + + + + + + +Crispin Standards Track [Page 31] + +RFC 3501 IMAPv4 March 2003 + + +6.3.1. SELECT Command + + Arguments: mailbox name + + Responses: REQUIRED untagged responses: FLAGS, EXISTS, RECENT + REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS, + UIDNEXT, UIDVALIDITY + + Result: OK - select completed, now in selected state + NO - select failure, now in authenticated state: no + such mailbox, can't access mailbox + BAD - command unknown or arguments invalid + + The SELECT command selects a mailbox so that messages in the + mailbox can be accessed. Before returning an OK to the client, + the server MUST send the following untagged data to the client. + Note that earlier versions of this protocol only required the + FLAGS, EXISTS, and RECENT untagged data; consequently, client + implementations SHOULD implement default behavior for missing data + as discussed with the individual item. + + FLAGS Defined flags in the mailbox. See the description + of the FLAGS response for more detail. + + EXISTS The number of messages in the mailbox. See the + description of the EXISTS response for more detail. + + RECENT The number of messages with the \Recent flag set. + See the description of the RECENT response for more + detail. + + OK [UNSEEN ] + The message sequence number of the first unseen + message in the mailbox. If this is missing, the + client can not make any assumptions about the first + unseen message in the mailbox, and needs to issue a + SEARCH command if it wants to find it. + + OK [PERMANENTFLAGS ()] + A list of message flags that the client can change + permanently. If this is missing, the client should + assume that all flags can be changed permanently. + + OK [UIDNEXT ] + The next unique identifier value. Refer to section + 2.3.1.1 for more information. If this is missing, + the client can not make any assumptions about the + next unique identifier value. + + + +Crispin Standards Track [Page 32] + +RFC 3501 IMAPv4 March 2003 + + + OK [UIDVALIDITY ] + The unique identifier validity value. Refer to + section 2.3.1.1 for more information. If this is + missing, the server does not support unique + identifiers. + + Only one mailbox can be selected at a time in a connection; + simultaneous access to multiple mailboxes requires multiple + connections. The SELECT command automatically deselects any + currently selected mailbox before attempting the new selection. + Consequently, if a mailbox is selected and a SELECT command that + fails is attempted, no mailbox is selected. + + If the client is permitted to modify the mailbox, the server + SHOULD prefix the text of the tagged OK response with the + "[READ-WRITE]" response code. + + If the client is not permitted to modify the mailbox but is + permitted read access, the mailbox is selected as read-only, and + the server MUST prefix the text of the tagged OK response to + SELECT with the "[READ-ONLY]" response code. Read-only access + through SELECT differs from the EXAMINE command in that certain + read-only mailboxes MAY permit the change of permanent state on a + per-user (as opposed to global) basis. Netnews messages marked in + a server-based .newsrc file are an example of such per-user + permanent state that can be modified with read-only mailboxes. + + Example: C: A142 SELECT INBOX + S: * 172 EXISTS + S: * 1 RECENT + S: * OK [UNSEEN 12] Message 12 is first unseen + S: * OK [UIDVALIDITY 3857529045] UIDs valid + S: * OK [UIDNEXT 4392] Predicted next UID + S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) + S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited + S: A142 OK [READ-WRITE] SELECT completed + + + + + + + + + + + + + + + +Crispin Standards Track [Page 33] + +RFC 3501 IMAPv4 March 2003 + + +6.3.2. EXAMINE Command + + Arguments: mailbox name + + Responses: REQUIRED untagged responses: FLAGS, EXISTS, RECENT + REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS, + UIDNEXT, UIDVALIDITY + + Result: OK - examine completed, now in selected state + NO - examine failure, now in authenticated state: no + such mailbox, can't access mailbox + BAD - command unknown or arguments invalid + + The EXAMINE command is identical to SELECT and returns the same + output; however, the selected mailbox is identified as read-only. + No changes to the permanent state of the mailbox, including + per-user state, are permitted; in particular, EXAMINE MUST NOT + cause messages to lose the \Recent flag. + + The text of the tagged OK response to the EXAMINE command MUST + begin with the "[READ-ONLY]" response code. + + Example: C: A932 EXAMINE blurdybloop + S: * 17 EXISTS + S: * 2 RECENT + S: * OK [UNSEEN 8] Message 8 is first unseen + S: * OK [UIDVALIDITY 3857529045] UIDs valid + S: * OK [UIDNEXT 4392] Predicted next UID + S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) + S: * OK [PERMANENTFLAGS ()] No permanent flags permitted + S: A932 OK [READ-ONLY] EXAMINE completed + + +6.3.3. CREATE Command + + Arguments: mailbox name + + Responses: no specific responses for this command + + Result: OK - create completed + NO - create failure: can't create mailbox with that name + BAD - command unknown or arguments invalid + + The CREATE command creates a mailbox with the given name. An OK + response is returned only if a new mailbox with that name has been + created. It is an error to attempt to create INBOX or a mailbox + with a name that refers to an extant mailbox. Any error in + creation will return a tagged NO response. + + + +Crispin Standards Track [Page 34] + +RFC 3501 IMAPv4 March 2003 + + + If the mailbox name is suffixed with the server's hierarchy + separator character (as returned from the server by a LIST + command), this is a declaration that the client intends to create + mailbox names under this name in the hierarchy. Server + implementations that do not require this declaration MUST ignore + the declaration. In any case, the name created is without the + trailing hierarchy delimiter. + + If the server's hierarchy separator character appears elsewhere in + the name, the server SHOULD create any superior hierarchical names + that are needed for the CREATE command to be successfully + completed. In other words, an attempt to create "foo/bar/zap" on + a server in which "/" is the hierarchy separator character SHOULD + create foo/ and foo/bar/ if they do not already exist. + + If a new mailbox is created with the same name as a mailbox which + was deleted, its unique identifiers MUST be greater than any + unique identifiers used in the previous incarnation of the mailbox + UNLESS the new incarnation has a different unique identifier + validity value. See the description of the UID command for more + detail. + + Example: C: A003 CREATE owatagusiam/ + S: A003 OK CREATE completed + C: A004 CREATE owatagusiam/blurdybloop + S: A004 OK CREATE completed + + Note: The interpretation of this example depends on whether + "/" was returned as the hierarchy separator from LIST. If + "/" is the hierarchy separator, a new level of hierarchy + named "owatagusiam" with a member called "blurdybloop" is + created. Otherwise, two mailboxes at the same hierarchy + level are created. + + +6.3.4. DELETE Command + + Arguments: mailbox name + + Responses: no specific responses for this command + + Result: OK - delete completed + NO - delete failure: can't delete mailbox with that name + BAD - command unknown or arguments invalid + + + + + + + +Crispin Standards Track [Page 35] + +RFC 3501 IMAPv4 March 2003 + + + The DELETE command permanently removes the mailbox with the given + name. A tagged OK response is returned only if the mailbox has + been deleted. It is an error to attempt to delete INBOX or a + mailbox name that does not exist. + + The DELETE command MUST NOT remove inferior hierarchical names. + For example, if a mailbox "foo" has an inferior "foo.bar" + (assuming "." is the hierarchy delimiter character), removing + "foo" MUST NOT remove "foo.bar". It is an error to attempt to + delete a name that has inferior hierarchical names and also has + the \Noselect mailbox name attribute (see the description of the + LIST response for more details). + + It is permitted to delete a name that has inferior hierarchical + names and does not have the \Noselect mailbox name attribute. In + this case, all messages in that mailbox are removed, and the name + will acquire the \Noselect mailbox name attribute. + + The value of the highest-used unique identifier of the deleted + mailbox MUST be preserved so that a new mailbox created with the + same name will not reuse the identifiers of the former + incarnation, UNLESS the new incarnation has a different unique + identifier validity value. See the description of the UID command + for more detail. + + Examples: C: A682 LIST "" * + S: * LIST () "/" blurdybloop + S: * LIST (\Noselect) "/" foo + S: * LIST () "/" foo/bar + S: A682 OK LIST completed + C: A683 DELETE blurdybloop + S: A683 OK DELETE completed + C: A684 DELETE foo + S: A684 NO Name "foo" has inferior hierarchical names + C: A685 DELETE foo/bar + S: A685 OK DELETE Completed + C: A686 LIST "" * + S: * LIST (\Noselect) "/" foo + S: A686 OK LIST completed + C: A687 DELETE foo + S: A687 OK DELETE Completed + + + + + + + + + + +Crispin Standards Track [Page 36] + +RFC 3501 IMAPv4 March 2003 + + + C: A82 LIST "" * + S: * LIST () "." blurdybloop + S: * LIST () "." foo + S: * LIST () "." foo.bar + S: A82 OK LIST completed + C: A83 DELETE blurdybloop + S: A83 OK DELETE completed + C: A84 DELETE foo + S: A84 OK DELETE Completed + C: A85 LIST "" * + S: * LIST () "." foo.bar + S: A85 OK LIST completed + C: A86 LIST "" % + S: * LIST (\Noselect) "." foo + S: A86 OK LIST completed + + +6.3.5. RENAME Command + + Arguments: existing mailbox name + new mailbox name + + Responses: no specific responses for this command + + Result: OK - rename completed + NO - rename failure: can't rename mailbox with that name, + can't rename to mailbox with that name + BAD - command unknown or arguments invalid + + The RENAME command changes the name of a mailbox. A tagged OK + response is returned only if the mailbox has been renamed. It is + an error to attempt to rename from a mailbox name that does not + exist or to a mailbox name that already exists. Any error in + renaming will return a tagged NO response. + + If the name has inferior hierarchical names, then the inferior + hierarchical names MUST also be renamed. For example, a rename of + "foo" to "zap" will rename "foo/bar" (assuming "/" is the + hierarchy delimiter character) to "zap/bar". + + If the server's hierarchy separator character appears in the name, + the server SHOULD create any superior hierarchical names that are + needed for the RENAME command to complete successfully. In other + words, an attempt to rename "foo/bar/zap" to baz/rag/zowie on a + server in which "/" is the hierarchy separator character SHOULD + create baz/ and baz/rag/ if they do not already exist. + + + + + +Crispin Standards Track [Page 37] + +RFC 3501 IMAPv4 March 2003 + + + The value of the highest-used unique identifier of the old mailbox + name MUST be preserved so that a new mailbox created with the same + name will not reuse the identifiers of the former incarnation, + UNLESS the new incarnation has a different unique identifier + validity value. See the description of the UID command for more + detail. + + Renaming INBOX is permitted, and has special behavior. It moves + all messages in INBOX to a new mailbox with the given name, + leaving INBOX empty. If the server implementation supports + inferior hierarchical names of INBOX, these are unaffected by a + rename of INBOX. + + Examples: C: A682 LIST "" * + S: * LIST () "/" blurdybloop + S: * LIST (\Noselect) "/" foo + S: * LIST () "/" foo/bar + S: A682 OK LIST completed + C: A683 RENAME blurdybloop sarasoop + S: A683 OK RENAME completed + C: A684 RENAME foo zowie + S: A684 OK RENAME Completed + C: A685 LIST "" * + S: * LIST () "/" sarasoop + S: * LIST (\Noselect) "/" zowie + S: * LIST () "/" zowie/bar + S: A685 OK LIST completed + + C: Z432 LIST "" * + S: * LIST () "." INBOX + S: * LIST () "." INBOX.bar + S: Z432 OK LIST completed + C: Z433 RENAME INBOX old-mail + S: Z433 OK RENAME completed + C: Z434 LIST "" * + S: * LIST () "." INBOX + S: * LIST () "." INBOX.bar + S: * LIST () "." old-mail + S: Z434 OK LIST completed + + + + + + + + + + + + +Crispin Standards Track [Page 38] + +RFC 3501 IMAPv4 March 2003 + + +6.3.6. SUBSCRIBE Command + + Arguments: mailbox + + Responses: no specific responses for this command + + Result: OK - subscribe completed + NO - subscribe failure: can't subscribe to that name + BAD - command unknown or arguments invalid + + The SUBSCRIBE command adds the specified mailbox name to the + server's set of "active" or "subscribed" mailboxes as returned by + the LSUB command. This command returns a tagged OK response only + if the subscription is successful. + + A server MAY validate the mailbox argument to SUBSCRIBE to verify + that it exists. However, it MUST NOT unilaterally remove an + existing mailbox name from the subscription list even if a mailbox + by that name no longer exists. + + Note: This requirement is because a server site can + choose to routinely remove a mailbox with a well-known + name (e.g., "system-alerts") after its contents expire, + with the intention of recreating it when new contents + are appropriate. + + + Example: C: A002 SUBSCRIBE #news.comp.mail.mime + S: A002 OK SUBSCRIBE completed + + +6.3.7. UNSUBSCRIBE Command + + Arguments: mailbox name + + Responses: no specific responses for this command + + Result: OK - unsubscribe completed + NO - unsubscribe failure: can't unsubscribe that name + BAD - command unknown or arguments invalid + + The UNSUBSCRIBE command removes the specified mailbox name from + the server's set of "active" or "subscribed" mailboxes as returned + by the LSUB command. This command returns a tagged OK response + only if the unsubscription is successful. + + Example: C: A002 UNSUBSCRIBE #news.comp.mail.mime + S: A002 OK UNSUBSCRIBE completed + + + +Crispin Standards Track [Page 39] + +RFC 3501 IMAPv4 March 2003 + + +6.3.8. LIST Command + + Arguments: reference name + mailbox name with possible wildcards + + Responses: untagged responses: LIST + + Result: OK - list completed + NO - list failure: can't list that reference or name + BAD - command unknown or arguments invalid + + The LIST command returns a subset of names from the complete set + of all names available to the client. Zero or more untagged LIST + replies are returned, containing the name attributes, hierarchy + delimiter, and name; see the description of the LIST reply for + more detail. + + The LIST command SHOULD return its data quickly, without undue + delay. For example, it SHOULD NOT go to excess trouble to + calculate the \Marked or \Unmarked status or perform other + processing; if each name requires 1 second of processing, then a + list of 1200 names would take 20 minutes! + + An empty ("" string) reference name argument indicates that the + mailbox name is interpreted as by SELECT. The returned mailbox + names MUST match the supplied mailbox name pattern. A non-empty + reference name argument is the name of a mailbox or a level of + mailbox hierarchy, and indicates the context in which the mailbox + name is interpreted. + + An empty ("" string) mailbox name argument is a special request to + return the hierarchy delimiter and the root name of the name given + in the reference. The value returned as the root MAY be the empty + string if the reference is non-rooted or is an empty string. In + all cases, a hierarchy delimiter (or NIL if there is no hierarchy) + is returned. This permits a client to get the hierarchy delimiter + (or find out that the mailbox names are flat) even when no + mailboxes by that name currently exist. + + The reference and mailbox name arguments are interpreted into a + canonical form that represents an unambiguous left-to-right + hierarchy. The returned mailbox names will be in the interpreted + form. + + + + + + + + +Crispin Standards Track [Page 40] + +RFC 3501 IMAPv4 March 2003 + + + Note: The interpretation of the reference argument is + implementation-defined. It depends upon whether the + server implementation has a concept of the "current + working directory" and leading "break out characters", + which override the current working directory. + + For example, on a server which exports a UNIX or NT + filesystem, the reference argument contains the current + working directory, and the mailbox name argument would + contain the name as interpreted in the current working + directory. + + If a server implementation has no concept of break out + characters, the canonical form is normally the reference + name appended with the mailbox name. Note that if the + server implements the namespace convention (section + 5.1.2), "#" is a break out character and must be treated + as such. + + If the reference argument is not a level of mailbox + hierarchy (that is, it is a \NoInferiors name), and/or + the reference argument does not end with the hierarchy + delimiter, it is implementation-dependent how this is + interpreted. For example, a reference of "foo/bar" and + mailbox name of "rag/baz" could be interpreted as + "foo/bar/rag/baz", "foo/barrag/baz", or "foo/rag/baz". + A client SHOULD NOT use such a reference argument except + at the explicit request of the user. A hierarchical + browser MUST NOT make any assumptions about server + interpretation of the reference unless the reference is + a level of mailbox hierarchy AND ends with the hierarchy + delimiter. + + Any part of the reference argument that is included in the + interpreted form SHOULD prefix the interpreted form. It SHOULD + also be in the same form as the reference name argument. This + rule permits the client to determine if the returned mailbox name + is in the context of the reference argument, or if something about + the mailbox argument overrode the reference argument. Without + this rule, the client would have to have knowledge of the server's + naming semantics including what characters are "breakouts" that + override a naming context. + + + + + + + + + +Crispin Standards Track [Page 41] + +RFC 3501 IMAPv4 March 2003 + + + For example, here are some examples of how references + and mailbox names might be interpreted on a UNIX-based + server: + + Reference Mailbox Name Interpretation + ------------ ------------ -------------- + ~smith/Mail/ foo.* ~smith/Mail/foo.* + archive/ % archive/% + #news. comp.mail.* #news.comp.mail.* + ~smith/Mail/ /usr/doc/foo /usr/doc/foo + archive/ ~fred/Mail/* ~fred/Mail/* + + The first three examples demonstrate interpretations in + the context of the reference argument. Note that + "~smith/Mail" SHOULD NOT be transformed into something + like "/u2/users/smith/Mail", or it would be impossible + for the client to determine that the interpretation was + in the context of the reference. + + The character "*" is a wildcard, and matches zero or more + characters at this position. The character "%" is similar to "*", + but it does not match a hierarchy delimiter. If the "%" wildcard + is the last character of a mailbox name argument, matching levels + of hierarchy are also returned. If these levels of hierarchy are + not also selectable mailboxes, they are returned with the + \Noselect mailbox name attribute (see the description of the LIST + response for more details). + + Server implementations are permitted to "hide" otherwise + accessible mailboxes from the wildcard characters, by preventing + certain characters or names from matching a wildcard in certain + situations. For example, a UNIX-based server might restrict the + interpretation of "*" so that an initial "/" character does not + match. + + The special name INBOX is included in the output from LIST, if + INBOX is supported by this server for this user and if the + uppercase string "INBOX" matches the interpreted reference and + mailbox name arguments with wildcards as described above. The + criteria for omitting INBOX is whether SELECT INBOX will return + failure; it is not relevant whether the user's real INBOX resides + on this or some other server. + + + + + + + + + +Crispin Standards Track [Page 42] + +RFC 3501 IMAPv4 March 2003 + + + Example: C: A101 LIST "" "" + S: * LIST (\Noselect) "/" "" + S: A101 OK LIST Completed + C: A102 LIST #news.comp.mail.misc "" + S: * LIST (\Noselect) "." #news. + S: A102 OK LIST Completed + C: A103 LIST /usr/staff/jones "" + S: * LIST (\Noselect) "/" / + S: A103 OK LIST Completed + C: A202 LIST ~/Mail/ % + S: * LIST (\Noselect) "/" ~/Mail/foo + S: * LIST () "/" ~/Mail/meetings + S: A202 OK LIST completed + + +6.3.9. LSUB Command + + Arguments: reference name + mailbox name with possible wildcards + + Responses: untagged responses: LSUB + + Result: OK - lsub completed + NO - lsub failure: can't list that reference or name + BAD - command unknown or arguments invalid + + The LSUB command returns a subset of names from the set of names + that the user has declared as being "active" or "subscribed". + Zero or more untagged LSUB replies are returned. The arguments to + LSUB are in the same form as those for LIST. + + The returned untagged LSUB response MAY contain different mailbox + flags from a LIST untagged response. If this should happen, the + flags in the untagged LIST are considered more authoritative. + + A special situation occurs when using LSUB with the % wildcard. + Consider what happens if "foo/bar" (with a hierarchy delimiter of + "/") is subscribed but "foo" is not. A "%" wildcard to LSUB must + return foo, not foo/bar, in the LSUB response, and it MUST be + flagged with the \Noselect attribute. + + The server MUST NOT unilaterally remove an existing mailbox name + from the subscription list even if a mailbox by that name no + longer exists. + + + + + + + +Crispin Standards Track [Page 43] + +RFC 3501 IMAPv4 March 2003 + + + Example: C: A002 LSUB "#news." "comp.mail.*" + S: * LSUB () "." #news.comp.mail.mime + S: * LSUB () "." #news.comp.mail.misc + S: A002 OK LSUB completed + C: A003 LSUB "#news." "comp.%" + S: * LSUB (\NoSelect) "." #news.comp.mail + S: A003 OK LSUB completed + + +6.3.10. STATUS Command + + Arguments: mailbox name + status data item names + + Responses: untagged responses: STATUS + + Result: OK - status completed + NO - status failure: no status for that name + BAD - command unknown or arguments invalid + + The STATUS command requests the status of the indicated mailbox. + It does not change the currently selected mailbox, nor does it + affect the state of any messages in the queried mailbox (in + particular, STATUS MUST NOT cause messages to lose the \Recent + flag). + + The STATUS command provides an alternative to opening a second + IMAP4rev1 connection and doing an EXAMINE command on a mailbox to + query that mailbox's status without deselecting the current + mailbox in the first IMAP4rev1 connection. + + Unlike the LIST command, the STATUS command is not guaranteed to + be fast in its response. Under certain circumstances, it can be + quite slow. In some implementations, the server is obliged to + open the mailbox read-only internally to obtain certain status + information. Also unlike the LIST command, the STATUS command + does not accept wildcards. + + Note: The STATUS command is intended to access the + status of mailboxes other than the currently selected + mailbox. Because the STATUS command can cause the + mailbox to be opened internally, and because this + information is available by other means on the selected + mailbox, the STATUS command SHOULD NOT be used on the + currently selected mailbox. + + + + + + +Crispin Standards Track [Page 44] + +RFC 3501 IMAPv4 March 2003 + + + The STATUS command MUST NOT be used as a "check for new + messages in the selected mailbox" operation (refer to + sections 7, 7.3.1, and 7.3.2 for more information about + the proper method for new message checking). + + Because the STATUS command is not guaranteed to be fast + in its results, clients SHOULD NOT expect to be able to + issue many consecutive STATUS commands and obtain + reasonable performance. + + The currently defined status data items that can be requested are: + + MESSAGES + The number of messages in the mailbox. + + RECENT + The number of messages with the \Recent flag set. + + UIDNEXT + The next unique identifier value of the mailbox. Refer to + section 2.3.1.1 for more information. + + UIDVALIDITY + The unique identifier validity value of the mailbox. Refer to + section 2.3.1.1 for more information. + + UNSEEN + The number of messages which do not have the \Seen flag set. + + + Example: C: A042 STATUS blurdybloop (UIDNEXT MESSAGES) + S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292) + S: A042 OK STATUS completed + + + + + + + + + + + + + + + + + + +Crispin Standards Track [Page 45] + +RFC 3501 IMAPv4 March 2003 + + +6.3.11. APPEND Command + + Arguments: mailbox name + OPTIONAL flag parenthesized list + OPTIONAL date/time string + message literal + + Responses: no specific responses for this command + + Result: OK - append completed + NO - append error: can't append to that mailbox, error + in flags or date/time or message text + BAD - command unknown or arguments invalid + + The APPEND command appends the literal argument as a new message + to the end of the specified destination mailbox. This argument + SHOULD be in the format of an [RFC-2822] message. 8-bit + characters are permitted in the message. A server implementation + that is unable to preserve 8-bit data properly MUST be able to + reversibly convert 8-bit APPEND data to 7-bit using a [MIME-IMB] + content transfer encoding. + + Note: There MAY be exceptions, e.g., draft messages, in + which required [RFC-2822] header lines are omitted in + the message literal argument to APPEND. The full + implications of doing so MUST be understood and + carefully weighed. + + If a flag parenthesized list is specified, the flags SHOULD be set + in the resulting message; otherwise, the flag list of the + resulting message is set to empty by default. In either case, the + Recent flag is also set. + + If a date-time is specified, the internal date SHOULD be set in + the resulting message; otherwise, the internal date of the + resulting message is set to the current date and time by default. + + If the append is unsuccessful for any reason, the mailbox MUST be + restored to its state before the APPEND attempt; no partial + appending is permitted. + + If the destination mailbox does not exist, a server MUST return an + error, and MUST NOT automatically create the mailbox. Unless it + is certain that the destination mailbox can not be created, the + server MUST send the response code "[TRYCREATE]" as the prefix of + the text of the tagged NO response. This gives a hint to the + client that it can attempt a CREATE command and retry the APPEND + if the CREATE is successful. + + + +Crispin Standards Track [Page 46] + +RFC 3501 IMAPv4 March 2003 + + + If the mailbox is currently selected, the normal new message + actions SHOULD occur. Specifically, the server SHOULD notify the + client immediately via an untagged EXISTS response. If the server + does not do so, the client MAY issue a NOOP command (or failing + that, a CHECK command) after one or more APPEND commands. + + Example: C: A003 APPEND saved-messages (\Seen) {310} + S: + Ready for literal data + C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) + C: From: Fred Foobar + C: Subject: afternoon meeting + C: To: mooch@owatagu.siam.edu + C: Message-Id: + C: MIME-Version: 1.0 + C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII + C: + C: Hello Joe, do you think we can meet at 3:30 tomorrow? + C: + S: A003 OK APPEND completed + + Note: The APPEND command is not used for message delivery, + because it does not provide a mechanism to transfer [SMTP] + envelope information. + +6.4. Client Commands - Selected State + + In the selected state, commands that manipulate messages in a mailbox + are permitted. + + In addition to the universal commands (CAPABILITY, NOOP, and LOGOUT), + and the authenticated state commands (SELECT, EXAMINE, CREATE, + DELETE, RENAME, SUBSCRIBE, UNSUBSCRIBE, LIST, LSUB, STATUS, and + APPEND), the following commands are valid in the selected state: + CHECK, CLOSE, EXPUNGE, SEARCH, FETCH, STORE, COPY, and UID. + +6.4.1. CHECK Command + + Arguments: none + + Responses: no specific responses for this command + + Result: OK - check completed + BAD - command unknown or arguments invalid + + The CHECK command requests a checkpoint of the currently selected + mailbox. A checkpoint refers to any implementation-dependent + housekeeping associated with the mailbox (e.g., resolving the + server's in-memory state of the mailbox with the state on its + + + +Crispin Standards Track [Page 47] + +RFC 3501 IMAPv4 March 2003 + + + disk) that is not normally executed as part of each command. A + checkpoint MAY take a non-instantaneous amount of real time to + complete. If a server implementation has no such housekeeping + considerations, CHECK is equivalent to NOOP. + + There is no guarantee that an EXISTS untagged response will happen + as a result of CHECK. NOOP, not CHECK, SHOULD be used for new + message polling. + + Example: C: FXXZ CHECK + S: FXXZ OK CHECK Completed + + +6.4.2. CLOSE Command + + Arguments: none + + Responses: no specific responses for this command + + Result: OK - close completed, now in authenticated state + BAD - command unknown or arguments invalid + + The CLOSE command permanently removes all messages that have the + \Deleted flag set from the currently selected mailbox, and returns + to the authenticated state from the selected state. No untagged + EXPUNGE responses are sent. + + No messages are removed, and no error is given, if the mailbox is + selected by an EXAMINE command or is otherwise selected read-only. + + Even if a mailbox is selected, a SELECT, EXAMINE, or LOGOUT + command MAY be issued without previously issuing a CLOSE command. + The SELECT, EXAMINE, and LOGOUT commands implicitly close the + currently selected mailbox without doing an expunge. However, + when many messages are deleted, a CLOSE-LOGOUT or CLOSE-SELECT + sequence is considerably faster than an EXPUNGE-LOGOUT or + EXPUNGE-SELECT because no untagged EXPUNGE responses (which the + client would probably ignore) are sent. + + Example: C: A341 CLOSE + S: A341 OK CLOSE completed + + + + + + + + + + +Crispin Standards Track [Page 48] + +RFC 3501 IMAPv4 March 2003 + + +6.4.3. EXPUNGE Command + + Arguments: none + + Responses: untagged responses: EXPUNGE + + Result: OK - expunge completed + NO - expunge failure: can't expunge (e.g., permission + denied) + BAD - command unknown or arguments invalid + + The EXPUNGE command permanently removes all messages that have the + \Deleted flag set from the currently selected mailbox. Before + returning an OK to the client, an untagged EXPUNGE response is + sent for each message that is removed. + + Example: C: A202 EXPUNGE + S: * 3 EXPUNGE + S: * 3 EXPUNGE + S: * 5 EXPUNGE + S: * 8 EXPUNGE + S: A202 OK EXPUNGE completed + + Note: In this example, messages 3, 4, 7, and 11 had the + \Deleted flag set. See the description of the EXPUNGE + response for further explanation. + + +6.4.4. SEARCH Command + + Arguments: OPTIONAL [CHARSET] specification + searching criteria (one or more) + + Responses: REQUIRED untagged response: SEARCH + + Result: OK - search completed + NO - search error: can't search that [CHARSET] or + criteria + BAD - command unknown or arguments invalid + + The SEARCH command searches the mailbox for messages that match + the given searching criteria. Searching criteria consist of one + or more search keys. The untagged SEARCH response from the server + contains a listing of message sequence numbers corresponding to + those messages that match the searching criteria. + + + + + + +Crispin Standards Track [Page 49] + +RFC 3501 IMAPv4 March 2003 + + + When multiple keys are specified, the result is the intersection + (AND function) of all the messages that match those keys. For + example, the criteria DELETED FROM "SMITH" SINCE 1-Feb-1994 refers + to all deleted messages from Smith that were placed in the mailbox + since February 1, 1994. A search key can also be a parenthesized + list of one or more search keys (e.g., for use with the OR and NOT + keys). + + Server implementations MAY exclude [MIME-IMB] body parts with + terminal content media types other than TEXT and MESSAGE from + consideration in SEARCH matching. + + The OPTIONAL [CHARSET] specification consists of the word + "CHARSET" followed by a registered [CHARSET]. It indicates the + [CHARSET] of the strings that appear in the search criteria. + [MIME-IMB] content transfer encodings, and [MIME-HDRS] strings in + [RFC-2822]/[MIME-IMB] headers, MUST be decoded before comparing + text in a [CHARSET] other than US-ASCII. US-ASCII MUST be + supported; other [CHARSET]s MAY be supported. + + If the server does not support the specified [CHARSET], it MUST + return a tagged NO response (not a BAD). This response SHOULD + contain the BADCHARSET response code, which MAY list the + [CHARSET]s supported by the server. + + In all search keys that use strings, a message matches the key if + the string is a substring of the field. The matching is + case-insensitive. + + The defined search keys are as follows. Refer to the Formal + Syntax section for the precise syntactic definitions of the + arguments. + + + Messages with message sequence numbers corresponding to the + specified message sequence number set. + + ALL + All messages in the mailbox; the default initial key for + ANDing. + + ANSWERED + Messages with the \Answered flag set. + + + + + + + + +Crispin Standards Track [Page 50] + +RFC 3501 IMAPv4 March 2003 + + + BCC + Messages that contain the specified string in the envelope + structure's BCC field. + + BEFORE + Messages whose internal date (disregarding time and timezone) + is earlier than the specified date. + + BODY + Messages that contain the specified string in the body of the + message. + + CC + Messages that contain the specified string in the envelope + structure's CC field. + + DELETED + Messages with the \Deleted flag set. + + DRAFT + Messages with the \Draft flag set. + + FLAGGED + Messages with the \Flagged flag set. + + FROM + Messages that contain the specified string in the envelope + structure's FROM field. + + HEADER + Messages that have a header with the specified field-name (as + defined in [RFC-2822]) and that contains the specified string + in the text of the header (what comes after the colon). If the + string to search is zero-length, this matches all messages that + have a header line with the specified field-name regardless of + the contents. + + KEYWORD + Messages with the specified keyword flag set. + + LARGER + Messages with an [RFC-2822] size larger than the specified + number of octets. + + NEW + Messages that have the \Recent flag set but not the \Seen flag. + This is functionally equivalent to "(RECENT UNSEEN)". + + + + +Crispin Standards Track [Page 51] + +RFC 3501 IMAPv4 March 2003 + + + NOT + Messages that do not match the specified search key. + + OLD + Messages that do not have the \Recent flag set. This is + functionally equivalent to "NOT RECENT" (as opposed to "NOT + NEW"). + + ON + Messages whose internal date (disregarding time and timezone) + is within the specified date. + + OR + Messages that match either search key. + + RECENT + Messages that have the \Recent flag set. + + SEEN + Messages that have the \Seen flag set. + + SENTBEFORE + Messages whose [RFC-2822] Date: header (disregarding time and + timezone) is earlier than the specified date. + + SENTON + Messages whose [RFC-2822] Date: header (disregarding time and + timezone) is within the specified date. + + SENTSINCE + Messages whose [RFC-2822] Date: header (disregarding time and + timezone) is within or later than the specified date. + + SINCE + Messages whose internal date (disregarding time and timezone) + is within or later than the specified date. + + SMALLER + Messages with an [RFC-2822] size smaller than the specified + number of octets. + + + + + + + + + + + +Crispin Standards Track [Page 52] + +RFC 3501 IMAPv4 March 2003 + + + SUBJECT + Messages that contain the specified string in the envelope + structure's SUBJECT field. + + TEXT + Messages that contain the specified string in the header or + body of the message. + + TO + Messages that contain the specified string in the envelope + structure's TO field. + + UID + Messages with unique identifiers corresponding to the specified + unique identifier set. Sequence set ranges are permitted. + + UNANSWERED + Messages that do not have the \Answered flag set. + + UNDELETED + Messages that do not have the \Deleted flag set. + + UNDRAFT + Messages that do not have the \Draft flag set. + + UNFLAGGED + Messages that do not have the \Flagged flag set. + + UNKEYWORD + Messages that do not have the specified keyword flag set. + + UNSEEN + Messages that do not have the \Seen flag set. + + + + + + + + + + + + + + + + + + +Crispin Standards Track [Page 53] + +RFC 3501 IMAPv4 March 2003 + + + Example: C: A282 SEARCH FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith" + S: * SEARCH 2 84 882 + S: A282 OK SEARCH completed + C: A283 SEARCH TEXT "string not in mailbox" + S: * SEARCH + S: A283 OK SEARCH completed + C: A284 SEARCH CHARSET UTF-8 TEXT {6} + C: XXXXXX + S: * SEARCH 43 + S: A284 OK SEARCH completed + + Note: Since this document is restricted to 7-bit ASCII + text, it is not possible to show actual UTF-8 data. The + "XXXXXX" is a placeholder for what would be 6 octets of + 8-bit data in an actual transaction. + + +6.4.5. FETCH Command + + Arguments: sequence set + message data item names or macro + + Responses: untagged responses: FETCH + + Result: OK - fetch completed + NO - fetch error: can't fetch that data + BAD - command unknown or arguments invalid + + The FETCH command retrieves data associated with a message in the + mailbox. The data items to be fetched can be either a single atom + or a parenthesized list. + + Most data items, identified in the formal syntax under the + msg-att-static rule, are static and MUST NOT change for any + particular message. Other data items, identified in the formal + syntax under the msg-att-dynamic rule, MAY change, either as a + result of a STORE command or due to external events. + + For example, if a client receives an ENVELOPE for a + message when it already knows the envelope, it can + safely ignore the newly transmitted envelope. + + There are three macros which specify commonly-used sets of data + items, and can be used instead of data items. A macro must be + used by itself, and not in conjunction with other macros or data + items. + + + + + +Crispin Standards Track [Page 54] + +RFC 3501 IMAPv4 March 2003 + + + ALL + Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE) + + FAST + Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE) + + FULL + Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE + BODY) + + The currently defined data items that can be fetched are: + + BODY + Non-extensible form of BODYSTRUCTURE. + + BODY[
]<> + The text of a particular body section. The section + specification is a set of zero or more part specifiers + delimited by periods. A part specifier is either a part number + or one of the following: HEADER, HEADER.FIELDS, + HEADER.FIELDS.NOT, MIME, and TEXT. An empty section + specification refers to the entire message, including the + header. + + Every message has at least one part number. Non-[MIME-IMB] + messages, and non-multipart [MIME-IMB] messages with no + encapsulated message, only have a part 1. + + Multipart messages are assigned consecutive part numbers, as + they occur in the message. If a particular part is of type + message or multipart, its parts MUST be indicated by a period + followed by the part number within that nested multipart part. + + A part of type MESSAGE/RFC822 also has nested part numbers, + referring to parts of the MESSAGE part's body. + + The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and TEXT part + specifiers can be the sole part specifier or can be prefixed by + one or more numeric part specifiers, provided that the numeric + part specifier refers to a part of type MESSAGE/RFC822. The + MIME part specifier MUST be prefixed by one or more numeric + part specifiers. + + The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part + specifiers refer to the [RFC-2822] header of the message or of + an encapsulated [MIME-IMT] MESSAGE/RFC822 message. + HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of + field-name (as defined in [RFC-2822]) names, and return a + + + +Crispin Standards Track [Page 55] + +RFC 3501 IMAPv4 March 2003 + + + subset of the header. The subset returned by HEADER.FIELDS + contains only those header fields with a field-name that + matches one of the names in the list; similarly, the subset + returned by HEADER.FIELDS.NOT contains only the header fields + with a non-matching field-name. The field-matching is + case-insensitive but otherwise exact. Subsetting does not + exclude the [RFC-2822] delimiting blank line between the header + and the body; the blank line is included in all header fetches, + except in the case of a message which has no body and no blank + line. + + The MIME part specifier refers to the [MIME-IMB] header for + this part. + + The TEXT part specifier refers to the text body of the message, + omitting the [RFC-2822] header. + + Here is an example of a complex message with some of its + part specifiers: + + HEADER ([RFC-2822] header of the message) + TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED + 1 TEXT/PLAIN + 2 APPLICATION/OCTET-STREAM + 3 MESSAGE/RFC822 + 3.HEADER ([RFC-2822] header of the message) + 3.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED + 3.1 TEXT/PLAIN + 3.2 APPLICATION/OCTET-STREAM + 4 MULTIPART/MIXED + 4.1 IMAGE/GIF + 4.1.MIME ([MIME-IMB] header for the IMAGE/GIF) + 4.2 MESSAGE/RFC822 + 4.2.HEADER ([RFC-2822] header of the message) + 4.2.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED + 4.2.1 TEXT/PLAIN + 4.2.2 MULTIPART/ALTERNATIVE + 4.2.2.1 TEXT/PLAIN + 4.2.2.2 TEXT/RICHTEXT + + + It is possible to fetch a substring of the designated text. + This is done by appending an open angle bracket ("<"), the + octet position of the first desired octet, a period, the + maximum number of octets desired, and a close angle bracket + (">") to the part specifier. If the starting octet is beyond + the end of the text, an empty string is returned. + + + + +Crispin Standards Track [Page 56] + +RFC 3501 IMAPv4 March 2003 + + + Any partial fetch that attempts to read beyond the end of the + text is truncated as appropriate. A partial fetch that starts + at octet 0 is returned as a partial fetch, even if this + truncation happened. + + Note: This means that BODY[]<0.2048> of a 1500-octet message + will return BODY[]<0> with a literal of size 1500, not + BODY[]. + + Note: A substring fetch of a HEADER.FIELDS or + HEADER.FIELDS.NOT part specifier is calculated after + subsetting the header. + + The \Seen flag is implicitly set; if this causes the flags to + change, they SHOULD be included as part of the FETCH responses. + + BODY.PEEK[
]<> + An alternate form of BODY[
] that does not implicitly + set the \Seen flag. + + BODYSTRUCTURE + The [MIME-IMB] body structure of the message. This is computed + by the server by parsing the [MIME-IMB] header fields in the + [RFC-2822] header and [MIME-IMB] headers. + + ENVELOPE + The envelope structure of the message. This is computed by the + server by parsing the [RFC-2822] header into the component + parts, defaulting various fields as necessary. + + FLAGS + The flags that are set for this message. + + INTERNALDATE + The internal date of the message. + + RFC822 + Functionally equivalent to BODY[], differing in the syntax of + the resulting untagged FETCH data (RFC822 is returned). + + RFC822.HEADER + Functionally equivalent to BODY.PEEK[HEADER], differing in the + syntax of the resulting untagged FETCH data (RFC822.HEADER is + returned). + + RFC822.SIZE + The [RFC-2822] size of the message. + + + + +Crispin Standards Track [Page 57] + +RFC 3501 IMAPv4 March 2003 + + + RFC822.TEXT + Functionally equivalent to BODY[TEXT], differing in the syntax + of the resulting untagged FETCH data (RFC822.TEXT is returned). + + UID + The unique identifier for the message. + + + Example: C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) + S: * 2 FETCH .... + S: * 3 FETCH .... + S: * 4 FETCH .... + S: A654 OK FETCH completed + + +6.4.6. STORE Command + + Arguments: sequence set + message data item name + value for message data item + + Responses: untagged responses: FETCH + + Result: OK - store completed + NO - store error: can't store that data + BAD - command unknown or arguments invalid + + The STORE command alters data associated with a message in the + mailbox. Normally, STORE will return the updated value of the + data with an untagged FETCH response. A suffix of ".SILENT" in + the data item name prevents the untagged FETCH, and the server + SHOULD assume that the client has determined the updated value + itself or does not care about the updated value. + + Note: Regardless of whether or not the ".SILENT" suffix + was used, the server SHOULD send an untagged FETCH + response if a change to a message's flags from an + external source is observed. The intent is that the + status of the flags is determinate without a race + condition. + + + + + + + + + + + +Crispin Standards Track [Page 58] + +RFC 3501 IMAPv4 March 2003 + + + The currently defined data items that can be stored are: + + FLAGS + Replace the flags for the message (other than \Recent) with the + argument. The new value of the flags is returned as if a FETCH + of those flags was done. + + FLAGS.SILENT + Equivalent to FLAGS, but without returning a new value. + + +FLAGS + Add the argument to the flags for the message. The new value + of the flags is returned as if a FETCH of those flags was done. + + +FLAGS.SILENT + Equivalent to +FLAGS, but without returning a new value. + + -FLAGS + Remove the argument from the flags for the message. The new + value of the flags is returned as if a FETCH of those flags was + done. + + -FLAGS.SILENT + Equivalent to -FLAGS, but without returning a new value. + + + Example: C: A003 STORE 2:4 +FLAGS (\Deleted) + S: * 2 FETCH (FLAGS (\Deleted \Seen)) + S: * 3 FETCH (FLAGS (\Deleted)) + S: * 4 FETCH (FLAGS (\Deleted \Flagged \Seen)) + S: A003 OK STORE completed + + +6.4.7. COPY Command + + Arguments: sequence set + mailbox name + + Responses: no specific responses for this command + + Result: OK - copy completed + NO - copy error: can't copy those messages or to that + name + BAD - command unknown or arguments invalid + + + + + + + +Crispin Standards Track [Page 59] + +RFC 3501 IMAPv4 March 2003 + + + The COPY command copies the specified message(s) to the end of the + specified destination mailbox. The flags and internal date of the + message(s) SHOULD be preserved, and the Recent flag SHOULD be set, + in the copy. + + If the destination mailbox does not exist, a server SHOULD return + an error. It SHOULD NOT automatically create the mailbox. Unless + it is certain that the destination mailbox can not be created, the + server MUST send the response code "[TRYCREATE]" as the prefix of + the text of the tagged NO response. This gives a hint to the + client that it can attempt a CREATE command and retry the COPY if + the CREATE is successful. + + If the COPY command is unsuccessful for any reason, server + implementations MUST restore the destination mailbox to its state + before the COPY attempt. + + Example: C: A003 COPY 2:4 MEETING + S: A003 OK COPY completed + + +6.4.8. UID Command + + Arguments: command name + command arguments + + Responses: untagged responses: FETCH, SEARCH + + Result: OK - UID command completed + NO - UID command error + BAD - command unknown or arguments invalid + + The UID command has two forms. In the first form, it takes as its + arguments a COPY, FETCH, or STORE command with arguments + appropriate for the associated command. However, the numbers in + the sequence set argument are unique identifiers instead of + message sequence numbers. Sequence set ranges are permitted, but + there is no guarantee that unique identifiers will be contiguous. + + A non-existent unique identifier is ignored without any error + message generated. Thus, it is possible for a UID FETCH command + to return an OK without any data or a UID COPY or UID STORE to + return an OK without performing any operations. + + In the second form, the UID command takes a SEARCH command with + SEARCH command arguments. The interpretation of the arguments is + the same as with SEARCH; however, the numbers returned in a SEARCH + response for a UID SEARCH command are unique identifiers instead + + + +Crispin Standards Track [Page 60] + +RFC 3501 IMAPv4 March 2003 + + + of message sequence numbers. For example, the command UID SEARCH + 1:100 UID 443:557 returns the unique identifiers corresponding to + the intersection of two sequence sets, the message sequence number + range 1:100 and the UID range 443:557. + + Note: in the above example, the UID range 443:557 + appears. The same comment about a non-existent unique + identifier being ignored without any error message also + applies here. Hence, even if neither UID 443 or 557 + exist, this range is valid and would include an existing + UID 495. + + Also note that a UID range of 559:* always includes the + UID of the last message in the mailbox, even if 559 is + higher than any assigned UID value. This is because the + contents of a range are independent of the order of the + range endpoints. Thus, any UID range with * as one of + the endpoints indicates at least one message (the + message with the highest numbered UID), unless the + mailbox is empty. + + The number after the "*" in an untagged FETCH response is always a + message sequence number, not a unique identifier, even for a UID + command response. However, server implementations MUST implicitly + include the UID message data item as part of any FETCH response + caused by a UID command, regardless of whether a UID was specified + as a message data item to the FETCH. + + + Note: The rule about including the UID message data item as part + of a FETCH response primarily applies to the UID FETCH and UID + STORE commands, including a UID FETCH command that does not + include UID as a message data item. Although it is unlikely that + the other UID commands will cause an untagged FETCH, this rule + applies to these commands as well. + + Example: C: A999 UID FETCH 4827313:4828442 FLAGS + S: * 23 FETCH (FLAGS (\Seen) UID 4827313) + S: * 24 FETCH (FLAGS (\Seen) UID 4827943) + S: * 25 FETCH (FLAGS (\Seen) UID 4828442) + S: A999 OK UID FETCH completed + + + + + + + + + + +Crispin Standards Track [Page 61] + +RFC 3501 IMAPv4 March 2003 + + +6.5. Client Commands - Experimental/Expansion + + +6.5.1. X Command + + Arguments: implementation defined + + Responses: implementation defined + + Result: OK - command completed + NO - failure + BAD - command unknown or arguments invalid + + Any command prefixed with an X is an experimental command. + Commands which are not part of this specification, a standard or + standards-track revision of this specification, or an + IESG-approved experimental protocol, MUST use the X prefix. + + Any added untagged responses issued by an experimental command + MUST also be prefixed with an X. Server implementations MUST NOT + send any such untagged responses, unless the client requested it + by issuing the associated experimental command. + + Example: C: a441 CAPABILITY + S: * CAPABILITY IMAP4rev1 XPIG-LATIN + S: a441 OK CAPABILITY completed + C: A442 XPIG-LATIN + S: * XPIG-LATIN ow-nay eaking-spay ig-pay atin-lay + S: A442 OK XPIG-LATIN ompleted-cay + +7. Server Responses + + Server responses are in three forms: status responses, server data, + and command continuation request. The information contained in a + server response, identified by "Contents:" in the response + descriptions below, is described by function, not by syntax. The + precise syntax of server responses is described in the Formal Syntax + section. + + The client MUST be prepared to accept any response at all times. + + Status responses can be tagged or untagged. Tagged status responses + indicate the completion result (OK, NO, or BAD status) of a client + command, and have a tag matching the command. + + Some status responses, and all server data, are untagged. An + untagged response is indicated by the token "*" instead of a tag. + Untagged status responses indicate server greeting, or server status + + + +Crispin Standards Track [Page 62] + +RFC 3501 IMAPv4 March 2003 + + + that does not indicate the completion of a command (for example, an + impending system shutdown alert). For historical reasons, untagged + server data responses are also called "unsolicited data", although + strictly speaking, only unilateral server data is truly + "unsolicited". + + Certain server data MUST be recorded by the client when it is + received; this is noted in the description of that data. Such data + conveys critical information which affects the interpretation of all + subsequent commands and responses (e.g., updates reflecting the + creation or destruction of messages). + + Other server data SHOULD be recorded for later reference; if the + client does not need to record the data, or if recording the data has + no obvious purpose (e.g., a SEARCH response when no SEARCH command is + in progress), the data SHOULD be ignored. + + An example of unilateral untagged server data occurs when the IMAP + connection is in the selected state. In the selected state, the + server checks the mailbox for new messages as part of command + execution. Normally, this is part of the execution of every command; + hence, a NOOP command suffices to check for new messages. If new + messages are found, the server sends untagged EXISTS and RECENT + responses reflecting the new size of the mailbox. Server + implementations that offer multiple simultaneous access to the same + mailbox SHOULD also send appropriate unilateral untagged FETCH and + EXPUNGE responses if another agent changes the state of any message + flags or expunges any messages. + + Command continuation request responses use the token "+" instead of a + tag. These responses are sent by the server to indicate acceptance + of an incomplete client command and readiness for the remainder of + the command. + +7.1. Server Responses - Status Responses + + Status responses are OK, NO, BAD, PREAUTH and BYE. OK, NO, and BAD + can be tagged or untagged. PREAUTH and BYE are always untagged. + + Status responses MAY include an OPTIONAL "response code". A response + code consists of data inside square brackets in the form of an atom, + possibly followed by a space and arguments. The response code + contains additional information or status codes for client software + beyond the OK/NO/BAD condition, and are defined when there is a + specific action that a client can take based upon the additional + information. + + + + + +Crispin Standards Track [Page 63] + +RFC 3501 IMAPv4 March 2003 + + + The currently defined response codes are: + + ALERT + + The human-readable text contains a special alert that MUST be + presented to the user in a fashion that calls the user's + attention to the message. + + BADCHARSET + + Optionally followed by a parenthesized list of charsets. A + SEARCH failed because the given charset is not supported by + this implementation. If the optional list of charsets is + given, this lists the charsets that are supported by this + implementation. + + CAPABILITY + + Followed by a list of capabilities. This can appear in the + initial OK or PREAUTH response to transmit an initial + capabilities list. This makes it unnecessary for a client to + send a separate CAPABILITY command if it recognizes this + response. + + PARSE + + The human-readable text represents an error in parsing the + [RFC-2822] header or [MIME-IMB] headers of a message in the + mailbox. + + PERMANENTFLAGS + + Followed by a parenthesized list of flags, indicates which of + the known flags the client can change permanently. Any flags + that are in the FLAGS untagged response, but not the + PERMANENTFLAGS list, can not be set permanently. If the client + attempts to STORE a flag that is not in the PERMANENTFLAGS + list, the server will either ignore the change or store the + state change for the remainder of the current session only. + The PERMANENTFLAGS list can also include the special flag \*, + which indicates that it is possible to create new keywords by + attempting to store those flags in the mailbox. + + + + + + + + + +Crispin Standards Track [Page 64] + +RFC 3501 IMAPv4 March 2003 + + + READ-ONLY + + The mailbox is selected read-only, or its access while selected + has changed from read-write to read-only. + + READ-WRITE + + The mailbox is selected read-write, or its access while + selected has changed from read-only to read-write. + + TRYCREATE + + An APPEND or COPY attempt is failing because the target mailbox + does not exist (as opposed to some other reason). This is a + hint to the client that the operation can succeed if the + mailbox is first created by the CREATE command. + + UIDNEXT + + Followed by a decimal number, indicates the next unique + identifier value. Refer to section 2.3.1.1 for more + information. + + UIDVALIDITY + + Followed by a decimal number, indicates the unique identifier + validity value. Refer to section 2.3.1.1 for more information. + + UNSEEN + + Followed by a decimal number, indicates the number of the first + message without the \Seen flag set. + + Additional response codes defined by particular client or server + implementations SHOULD be prefixed with an "X" until they are + added to a revision of this protocol. Client implementations + SHOULD ignore response codes that they do not recognize. + +7.1.1. OK Response + + Contents: OPTIONAL response code + human-readable text + + The OK response indicates an information message from the server. + When tagged, it indicates successful completion of the associated + command. The human-readable text MAY be presented to the user as + an information message. The untagged form indicates an + + + + +Crispin Standards Track [Page 65] + +RFC 3501 IMAPv4 March 2003 + + + information-only message; the nature of the information MAY be + indicated by a response code. + + The untagged form is also used as one of three possible greetings + at connection startup. It indicates that the connection is not + yet authenticated and that a LOGIN command is needed. + + Example: S: * OK IMAP4rev1 server ready + C: A001 LOGIN fred blurdybloop + S: * OK [ALERT] System shutdown in 10 minutes + S: A001 OK LOGIN Completed + + +7.1.2. NO Response + + Contents: OPTIONAL response code + human-readable text + + The NO response indicates an operational error message from the + server. When tagged, it indicates unsuccessful completion of the + associated command. The untagged form indicates a warning; the + command can still complete successfully. The human-readable text + describes the condition. + + Example: C: A222 COPY 1:2 owatagusiam + S: * NO Disk is 98% full, please delete unnecessary data + S: A222 OK COPY completed + C: A223 COPY 3:200 blurdybloop + S: * NO Disk is 98% full, please delete unnecessary data + S: * NO Disk is 99% full, please delete unnecessary data + S: A223 NO COPY failed: disk is full + + +7.1.3. BAD Response + + Contents: OPTIONAL response code + human-readable text + + The BAD response indicates an error message from the server. When + tagged, it reports a protocol-level error in the client's command; + the tag indicates the command that caused the error. The untagged + form indicates a protocol-level error for which the associated + command can not be determined; it can also indicate an internal + server failure. The human-readable text describes the condition. + + + + + + + +Crispin Standards Track [Page 66] + +RFC 3501 IMAPv4 March 2003 + + + Example: C: ...very long command line... + S: * BAD Command line too long + C: ...empty line... + S: * BAD Empty command line + C: A443 EXPUNGE + S: * BAD Disk crash, attempting salvage to a new disk! + S: * OK Salvage successful, no data lost + S: A443 OK Expunge completed + + +7.1.4. PREAUTH Response + + Contents: OPTIONAL response code + human-readable text + + The PREAUTH response is always untagged, and is one of three + possible greetings at connection startup. It indicates that the + connection has already been authenticated by external means; thus + no LOGIN command is needed. + + Example: S: * PREAUTH IMAP4rev1 server logged in as Smith + + +7.1.5. BYE Response + + Contents: OPTIONAL response code + human-readable text + + The BYE response is always untagged, and indicates that the server + is about to close the connection. The human-readable text MAY be + displayed to the user in a status report by the client. The BYE + response is sent under one of four conditions: + + 1) as part of a normal logout sequence. The server will close + the connection after sending the tagged OK response to the + LOGOUT command. + + 2) as a panic shutdown announcement. The server closes the + connection immediately. + + 3) as an announcement of an inactivity autologout. The server + closes the connection immediately. + + 4) as one of three possible greetings at connection startup, + indicating that the server is not willing to accept a + connection from this client. The server closes the + connection immediately. + + + + +Crispin Standards Track [Page 67] + +RFC 3501 IMAPv4 March 2003 + + + The difference between a BYE that occurs as part of a normal + LOGOUT sequence (the first case) and a BYE that occurs because of + a failure (the other three cases) is that the connection closes + immediately in the failure case. In all cases the client SHOULD + continue to read response data from the server until the + connection is closed; this will ensure that any pending untagged + or completion responses are read and processed. + + Example: S: * BYE Autologout; idle for too long + +7.2. Server Responses - Server and Mailbox Status + + These responses are always untagged. This is how server and mailbox + status data are transmitted from the server to the client. Many of + these responses typically result from a command with the same name. + +7.2.1. CAPABILITY Response + + Contents: capability listing + + The CAPABILITY response occurs as a result of a CAPABILITY + command. The capability listing contains a space-separated + listing of capability names that the server supports. The + capability listing MUST include the atom "IMAP4rev1". + + In addition, client and server implementations MUST implement the + STARTTLS, LOGINDISABLED, and AUTH=PLAIN (described in [IMAP-TLS]) + capabilities. See the Security Considerations section for + important information. + + A capability name which begins with "AUTH=" indicates that the + server supports that particular authentication mechanism. + + The LOGINDISABLED capability indicates that the LOGIN command is + disabled, and that the server will respond with a tagged NO + response to any attempt to use the LOGIN command even if the user + name and password are valid. An IMAP client MUST NOT issue the + LOGIN command if the server advertises the LOGINDISABLED + capability. + + Other capability names indicate that the server supports an + extension, revision, or amendment to the IMAP4rev1 protocol. + Server responses MUST conform to this document until the client + issues a command that uses the associated capability. + + Capability names MUST either begin with "X" or be standard or + standards-track IMAP4rev1 extensions, revisions, or amendments + registered with IANA. A server MUST NOT offer unregistered or + + + +Crispin Standards Track [Page 68] + +RFC 3501 IMAPv4 March 2003 + + + non-standard capability names, unless such names are prefixed with + an "X". + + Client implementations SHOULD NOT require any capability name + other than "IMAP4rev1", and MUST ignore any unknown capability + names. + + A server MAY send capabilities automatically, by using the + CAPABILITY response code in the initial PREAUTH or OK responses, + and by sending an updated CAPABILITY response code in the tagged + OK response as part of a successful authentication. It is + unnecessary for a client to send a separate CAPABILITY command if + it recognizes these automatic capabilities. + + Example: S: * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI XPIG-LATIN + + +7.2.2. LIST Response + + Contents: name attributes + hierarchy delimiter + name + + The LIST response occurs as a result of a LIST command. It + returns a single name that matches the LIST specification. There + can be multiple LIST responses for a single LIST command. + + Four name attributes are defined: + + \Noinferiors + It is not possible for any child levels of hierarchy to exist + under this name; no child levels exist now and none can be + created in the future. + + \Noselect + It is not possible to use this name as a selectable mailbox. + + \Marked + The mailbox has been marked "interesting" by the server; the + mailbox probably contains messages that have been added since + the last time the mailbox was selected. + + \Unmarked + The mailbox does not contain any additional messages since the + last time the mailbox was selected. + + + + + + +Crispin Standards Track [Page 69] + +RFC 3501 IMAPv4 March 2003 + + + If it is not feasible for the server to determine whether or not + the mailbox is "interesting", or if the name is a \Noselect name, + the server SHOULD NOT send either \Marked or \Unmarked. + + The hierarchy delimiter is a character used to delimit levels of + hierarchy in a mailbox name. A client can use it to create child + mailboxes, and to search higher or lower levels of naming + hierarchy. All children of a top-level hierarchy node MUST use + the same separator character. A NIL hierarchy delimiter means + that no hierarchy exists; the name is a "flat" name. + + The name represents an unambiguous left-to-right hierarchy, and + MUST be valid for use as a reference in LIST and LSUB commands. + Unless \Noselect is indicated, the name MUST also be valid as an + argument for commands, such as SELECT, that accept mailbox names. + + Example: S: * LIST (\Noselect) "/" ~/Mail/foo + + +7.2.3. LSUB Response + + Contents: name attributes + hierarchy delimiter + name + + The LSUB response occurs as a result of an LSUB command. It + returns a single name that matches the LSUB specification. There + can be multiple LSUB responses for a single LSUB command. The + data is identical in format to the LIST response. + + Example: S: * LSUB () "." #news.comp.mail.misc + + +7.2.4 STATUS Response + + Contents: name + status parenthesized list + + The STATUS response occurs as a result of an STATUS command. It + returns the mailbox name that matches the STATUS specification and + the requested mailbox status information. + + Example: S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292) + + + + + + + + +Crispin Standards Track [Page 70] + +RFC 3501 IMAPv4 March 2003 + + +7.2.5. SEARCH Response + + Contents: zero or more numbers + + The SEARCH response occurs as a result of a SEARCH or UID SEARCH + command. The number(s) refer to those messages that match the + search criteria. For SEARCH, these are message sequence numbers; + for UID SEARCH, these are unique identifiers. Each number is + delimited by a space. + + Example: S: * SEARCH 2 3 6 + + +7.2.6. FLAGS Response + + Contents: flag parenthesized list + + The FLAGS response occurs as a result of a SELECT or EXAMINE + command. The flag parenthesized list identifies the flags (at a + minimum, the system-defined flags) that are applicable for this + mailbox. Flags other than the system flags can also exist, + depending on server implementation. + + The update from the FLAGS response MUST be recorded by the client. + + Example: S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) + + +7.3. Server Responses - Mailbox Size + + These responses are always untagged. This is how changes in the size + of the mailbox are transmitted from the server to the client. + Immediately following the "*" token is a number that represents a + message count. + +7.3.1. EXISTS Response + + Contents: none + + The EXISTS response reports the number of messages in the mailbox. + This response occurs as a result of a SELECT or EXAMINE command, + and if the size of the mailbox changes (e.g., new messages). + + The update from the EXISTS response MUST be recorded by the + client. + + Example: S: * 23 EXISTS + + + + +Crispin Standards Track [Page 71] + +RFC 3501 IMAPv4 March 2003 + + +7.3.2. RECENT Response + + Contents: none + + The RECENT response reports the number of messages with the + \Recent flag set. This response occurs as a result of a SELECT or + EXAMINE command, and if the size of the mailbox changes (e.g., new + messages). + + Note: It is not guaranteed that the message sequence + numbers of recent messages will be a contiguous range of + the highest n messages in the mailbox (where n is the + value reported by the RECENT response). Examples of + situations in which this is not the case are: multiple + clients having the same mailbox open (the first session + to be notified will see it as recent, others will + probably see it as non-recent), and when the mailbox is + re-ordered by a non-IMAP agent. + + The only reliable way to identify recent messages is to + look at message flags to see which have the \Recent flag + set, or to do a SEARCH RECENT. + + The update from the RECENT response MUST be recorded by the + client. + + Example: S: * 5 RECENT + + +7.4. Server Responses - Message Status + + These responses are always untagged. This is how message data are + transmitted from the server to the client, often as a result of a + command with the same name. Immediately following the "*" token is a + number that represents a message sequence number. + +7.4.1. EXPUNGE Response + + Contents: none + + The EXPUNGE response reports that the specified message sequence + number has been permanently removed from the mailbox. The message + sequence number for each successive message in the mailbox is + immediately decremented by 1, and this decrement is reflected in + message sequence numbers in subsequent responses (including other + untagged EXPUNGE responses). + + + + + +Crispin Standards Track [Page 72] + +RFC 3501 IMAPv4 March 2003 + + + The EXPUNGE response also decrements the number of messages in the + mailbox; it is not necessary to send an EXISTS response with the + new value. + + As a result of the immediate decrement rule, message sequence + numbers that appear in a set of successive EXPUNGE responses + depend upon whether the messages are removed starting from lower + numbers to higher numbers, or from higher numbers to lower + numbers. For example, if the last 5 messages in a 9-message + mailbox are expunged, a "lower to higher" server will send five + untagged EXPUNGE responses for message sequence number 5, whereas + a "higher to lower server" will send successive untagged EXPUNGE + responses for message sequence numbers 9, 8, 7, 6, and 5. + + An EXPUNGE response MUST NOT be sent when no command is in + progress, nor while responding to a FETCH, STORE, or SEARCH + command. This rule is necessary to prevent a loss of + synchronization of message sequence numbers between client and + server. A command is not "in progress" until the complete command + has been received; in particular, a command is not "in progress" + during the negotiation of command continuation. + + Note: UID FETCH, UID STORE, and UID SEARCH are different + commands from FETCH, STORE, and SEARCH. An EXPUNGE + response MAY be sent during a UID command. + + The update from the EXPUNGE response MUST be recorded by the + client. + + Example: S: * 44 EXPUNGE + + +7.4.2. FETCH Response + + Contents: message data + + The FETCH response returns data about a message to the client. + The data are pairs of data item names and their values in + parentheses. This response occurs as the result of a FETCH or + STORE command, as well as by unilateral server decision (e.g., + flag updates). + + The current data items are: + + BODY + A form of BODYSTRUCTURE without extension data. + + + + + +Crispin Standards Track [Page 73] + +RFC 3501 IMAPv4 March 2003 + + + BODY[
]<> + A string expressing the body contents of the specified section. + The string SHOULD be interpreted by the client according to the + content transfer encoding, body type, and subtype. + + If the origin octet is specified, this string is a substring of + the entire body contents, starting at that origin octet. This + means that BODY[]<0> MAY be truncated, but BODY[] is NEVER + truncated. + + Note: The origin octet facility MUST NOT be used by a server + in a FETCH response unless the client specifically requested + it by means of a FETCH of a BODY[
]<> data + item. + + 8-bit textual data is permitted if a [CHARSET] identifier is + part of the body parameter parenthesized list for this section. + Note that headers (part specifiers HEADER or MIME, or the + header portion of a MESSAGE/RFC822 part), MUST be 7-bit; 8-bit + characters are not permitted in headers. Note also that the + [RFC-2822] delimiting blank line between the header and the + body is not affected by header line subsetting; the blank line + is always included as part of header data, except in the case + of a message which has no body and no blank line. + + Non-textual data such as binary data MUST be transfer encoded + into a textual form, such as BASE64, prior to being sent to the + client. To derive the original binary data, the client MUST + decode the transfer encoded string. + + BODYSTRUCTURE + A parenthesized list that describes the [MIME-IMB] body + structure of a message. This is computed by the server by + parsing the [MIME-IMB] header fields, defaulting various fields + as necessary. + + For example, a simple text message of 48 lines and 2279 octets + can have a body structure of: ("TEXT" "PLAIN" ("CHARSET" + "US-ASCII") NIL NIL "7BIT" 2279 48) + + Multiple parts are indicated by parenthesis nesting. Instead + of a body type as the first element of the parenthesized list, + there is a sequence of one or more nested body structures. The + second element of the parenthesized list is the multipart + subtype (mixed, digest, parallel, alternative, etc.). + + + + + + +Crispin Standards Track [Page 74] + +RFC 3501 IMAPv4 March 2003 + + + For example, a two part message consisting of a text and a + BASE64-encoded text attachment can have a body structure of: + (("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 + 23)("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") + "<960723163407.20117h@cac.washington.edu>" "Compiler diff" + "BASE64" 4554 73) "MIXED") + + Extension data follows the multipart subtype. Extension data + is never returned with the BODY fetch, but can be returned with + a BODYSTRUCTURE fetch. Extension data, if present, MUST be in + the defined order. The extension data of a multipart body part + are in the following order: + + body parameter parenthesized list + A parenthesized list of attribute/value pairs [e.g., ("foo" + "bar" "baz" "rag") where "bar" is the value of "foo", and + "rag" is the value of "baz"] as defined in [MIME-IMB]. + + body disposition + A parenthesized list, consisting of a disposition type + string, followed by a parenthesized list of disposition + attribute/value pairs as defined in [DISPOSITION]. + + body language + A string or parenthesized list giving the body language + value as defined in [LANGUAGE-TAGS]. + + body location + A string list giving the body content URI as defined in + [LOCATION]. + + Any following extension data are not yet defined in this + version of the protocol. Such extension data can consist of + zero or more NILs, strings, numbers, or potentially nested + parenthesized lists of such data. Client implementations that + do a BODYSTRUCTURE fetch MUST be prepared to accept such + extension data. Server implementations MUST NOT send such + extension data until it has been defined by a revision of this + protocol. + + The basic fields of a non-multipart body part are in the + following order: + + body type + A string giving the content media type name as defined in + [MIME-IMB]. + + + + + +Crispin Standards Track [Page 75] + +RFC 3501 IMAPv4 March 2003 + + + body subtype + A string giving the content subtype name as defined in + [MIME-IMB]. + + body parameter parenthesized list + A parenthesized list of attribute/value pairs [e.g., ("foo" + "bar" "baz" "rag") where "bar" is the value of "foo" and + "rag" is the value of "baz"] as defined in [MIME-IMB]. + + body id + A string giving the content id as defined in [MIME-IMB]. + + body description + A string giving the content description as defined in + [MIME-IMB]. + + body encoding + A string giving the content transfer encoding as defined in + [MIME-IMB]. + + body size + A number giving the size of the body in octets. Note that + this size is the size in its transfer encoding and not the + resulting size after any decoding. + + A body type of type MESSAGE and subtype RFC822 contains, + immediately after the basic fields, the envelope structure, + body structure, and size in text lines of the encapsulated + message. + + A body type of type TEXT contains, immediately after the basic + fields, the size of the body in text lines. Note that this + size is the size in its content transfer encoding and not the + resulting size after any decoding. + + Extension data follows the basic fields and the type-specific + fields listed above. Extension data is never returned with the + BODY fetch, but can be returned with a BODYSTRUCTURE fetch. + Extension data, if present, MUST be in the defined order. + + The extension data of a non-multipart body part are in the + following order: + + body MD5 + A string giving the body MD5 value as defined in [MD5]. + + + + + + +Crispin Standards Track [Page 76] + +RFC 3501 IMAPv4 March 2003 + + + body disposition + A parenthesized list with the same content and function as + the body disposition for a multipart body part. + + body language + A string or parenthesized list giving the body language + value as defined in [LANGUAGE-TAGS]. + + body location + A string list giving the body content URI as defined in + [LOCATION]. + + Any following extension data are not yet defined in this + version of the protocol, and would be as described above under + multipart extension data. + + ENVELOPE + A parenthesized list that describes the envelope structure of a + message. This is computed by the server by parsing the + [RFC-2822] header into the component parts, defaulting various + fields as necessary. + + The fields of the envelope structure are in the following + order: date, subject, from, sender, reply-to, to, cc, bcc, + in-reply-to, and message-id. The date, subject, in-reply-to, + and message-id fields are strings. The from, sender, reply-to, + to, cc, and bcc fields are parenthesized lists of address + structures. + + An address structure is a parenthesized list that describes an + electronic mail address. The fields of an address structure + are in the following order: personal name, [SMTP] + at-domain-list (source route), mailbox name, and host name. + + [RFC-2822] group syntax is indicated by a special form of + address structure in which the host name field is NIL. If the + mailbox name field is also NIL, this is an end of group marker + (semi-colon in RFC 822 syntax). If the mailbox name field is + non-NIL, this is a start of group marker, and the mailbox name + field holds the group name phrase. + + If the Date, Subject, In-Reply-To, and Message-ID header lines + are absent in the [RFC-2822] header, the corresponding member + of the envelope is NIL; if these header lines are present but + empty the corresponding member of the envelope is the empty + string. + + + + + +Crispin Standards Track [Page 77] + +RFC 3501 IMAPv4 March 2003 + + + Note: some servers may return a NIL envelope member in the + "present but empty" case. Clients SHOULD treat NIL and + empty string as identical. + + Note: [RFC-2822] requires that all messages have a valid + Date header. Therefore, the date member in the envelope can + not be NIL or the empty string. + + Note: [RFC-2822] requires that the In-Reply-To and + Message-ID headers, if present, have non-empty content. + Therefore, the in-reply-to and message-id members in the + envelope can not be the empty string. + + If the From, To, cc, and bcc header lines are absent in the + [RFC-2822] header, or are present but empty, the corresponding + member of the envelope is NIL. + + If the Sender or Reply-To lines are absent in the [RFC-2822] + header, or are present but empty, the server sets the + corresponding member of the envelope to be the same value as + the from member (the client is not expected to know to do + this). + + Note: [RFC-2822] requires that all messages have a valid + From header. Therefore, the from, sender, and reply-to + members in the envelope can not be NIL. + + FLAGS + A parenthesized list of flags that are set for this message. + + INTERNALDATE + A string representing the internal date of the message. + + RFC822 + Equivalent to BODY[]. + + RFC822.HEADER + Equivalent to BODY[HEADER]. Note that this did not result in + \Seen being set, because RFC822.HEADER response data occurs as + a result of a FETCH of RFC822.HEADER. BODY[HEADER] response + data occurs as a result of a FETCH of BODY[HEADER] (which sets + \Seen) or BODY.PEEK[HEADER] (which does not set \Seen). + + RFC822.SIZE + A number expressing the [RFC-2822] size of the message. + + + + + + +Crispin Standards Track [Page 78] + +RFC 3501 IMAPv4 March 2003 + + + RFC822.TEXT + Equivalent to BODY[TEXT]. + + UID + A number expressing the unique identifier of the message. + + + Example: S: * 23 FETCH (FLAGS (\Seen) RFC822.SIZE 44827) + + +7.5. Server Responses - Command Continuation Request + + The command continuation request response is indicated by a "+" token + instead of a tag. This form of response indicates that the server is + ready to accept the continuation of a command from the client. The + remainder of this response is a line of text. + + This response is used in the AUTHENTICATE command to transmit server + data to the client, and request additional client data. This + response is also used if an argument to any command is a literal. + + The client is not permitted to send the octets of the literal unless + the server indicates that it is expected. This permits the server to + process commands and reject errors on a line-by-line basis. The + remainder of the command, including the CRLF that terminates a + command, follows the octets of the literal. If there are any + additional command arguments, the literal octets are followed by a + space and those arguments. + + Example: C: A001 LOGIN {11} + S: + Ready for additional command text + C: FRED FOOBAR {7} + S: + Ready for additional command text + C: fat man + S: A001 OK LOGIN completed + C: A044 BLURDYBLOOP {102856} + S: A044 BAD No such command as "BLURDYBLOOP" + + + + + + + + + + + + + + +Crispin Standards Track [Page 79] + +RFC 3501 IMAPv4 March 2003 + + +8. Sample IMAP4rev1 connection + + The following is a transcript of an IMAP4rev1 connection. A long + line in this sample is broken for editorial clarity. + +S: * OK IMAP4rev1 Service Ready +C: a001 login mrc secret +S: a001 OK LOGIN completed +C: a002 select inbox +S: * 18 EXISTS +S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) +S: * 2 RECENT +S: * OK [UNSEEN 17] Message 17 is the first unseen message +S: * OK [UIDVALIDITY 3857529045] UIDs valid +S: a002 OK [READ-WRITE] SELECT completed +C: a003 fetch 12 full +S: * 12 FETCH (FLAGS (\Seen) INTERNALDATE "17-Jul-1996 02:44:25 -0700" + RFC822.SIZE 4286 ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)" + "IMAP4rev1 WG mtg summary and minutes" + (("Terry Gray" NIL "gray" "cac.washington.edu")) + (("Terry Gray" NIL "gray" "cac.washington.edu")) + (("Terry Gray" NIL "gray" "cac.washington.edu")) + ((NIL NIL "imap" "cac.washington.edu")) + ((NIL NIL "minutes" "CNRI.Reston.VA.US") + ("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL + "") + BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 3028 + 92)) +S: a003 OK FETCH completed +C: a004 fetch 12 body[header] +S: * 12 FETCH (BODY[HEADER] {342} +S: Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT) +S: From: Terry Gray +S: Subject: IMAP4rev1 WG mtg summary and minutes +S: To: imap@cac.washington.edu +S: cc: minutes@CNRI.Reston.VA.US, John Klensin +S: Message-Id: +S: MIME-Version: 1.0 +S: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII +S: +S: ) +S: a004 OK FETCH completed +C: a005 store 12 +flags \deleted +S: * 12 FETCH (FLAGS (\Seen \Deleted)) +S: a005 OK +FLAGS completed +C: a006 logout +S: * BYE IMAP4rev1 server terminating connection +S: a006 OK LOGOUT completed + + + +Crispin Standards Track [Page 80] + +RFC 3501 IMAPv4 March 2003 + + +9. Formal Syntax + + The following syntax specification uses the Augmented Backus-Naur + Form (ABNF) notation as specified in [ABNF]. + + In the case of alternative or optional rules in which a later rule + overlaps an earlier rule, the rule which is listed earlier MUST take + priority. For example, "\Seen" when parsed as a flag is the \Seen + flag name and not a flag-extension, even though "\Seen" can be parsed + as a flag-extension. Some, but not all, instances of this rule are + noted below. + + Note: [ABNF] rules MUST be followed strictly; in + particular: + + (1) Except as noted otherwise, all alphabetic characters + are case-insensitive. The use of upper or lower case + characters to define token strings is for editorial clarity + only. Implementations MUST accept these strings in a + case-insensitive fashion. + + (2) In all cases, SP refers to exactly one space. It is + NOT permitted to substitute TAB, insert additional spaces, + or otherwise treat SP as being equivalent to LWSP. + + (3) The ASCII NUL character, %x00, MUST NOT be used at any + time. + +address = "(" addr-name SP addr-adl SP addr-mailbox SP + addr-host ")" + +addr-adl = nstring + ; Holds route from [RFC-2822] route-addr if + ; non-NIL + +addr-host = nstring + ; NIL indicates [RFC-2822] group syntax. + ; Otherwise, holds [RFC-2822] domain name + +addr-mailbox = nstring + ; NIL indicates end of [RFC-2822] group; if + ; non-NIL and addr-host is NIL, holds + ; [RFC-2822] group name. + ; Otherwise, holds [RFC-2822] local-part + ; after removing [RFC-2822] quoting + + + + + + +Crispin Standards Track [Page 81] + +RFC 3501 IMAPv4 March 2003 + + +addr-name = nstring + ; If non-NIL, holds phrase from [RFC-2822] + ; mailbox after removing [RFC-2822] quoting + +append = "APPEND" SP mailbox [SP flag-list] [SP date-time] SP + literal + +astring = 1*ASTRING-CHAR / string + +ASTRING-CHAR = ATOM-CHAR / resp-specials + +atom = 1*ATOM-CHAR + +ATOM-CHAR = + +atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / + quoted-specials / resp-specials + +authenticate = "AUTHENTICATE" SP auth-type *(CRLF base64) + +auth-type = atom + ; Defined by [SASL] + +base64 = *(4base64-char) [base64-terminal] + +base64-char = ALPHA / DIGIT / "+" / "/" + ; Case-sensitive + +base64-terminal = (2base64-char "==") / (3base64-char "=") + +body = "(" (body-type-1part / body-type-mpart) ")" + +body-extension = nstring / number / + "(" body-extension *(SP body-extension) ")" + ; Future expansion. Client implementations + ; MUST accept body-extension fields. Server + ; implementations MUST NOT generate + ; body-extension fields except as defined by + ; future standard or standards-track + ; revisions of this specification. + +body-ext-1part = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang + [SP body-fld-loc *(SP body-extension)]]] + ; MUST NOT be returned on non-extensible + ; "BODY" fetch + + + + + + +Crispin Standards Track [Page 82] + +RFC 3501 IMAPv4 March 2003 + + +body-ext-mpart = body-fld-param [SP body-fld-dsp [SP body-fld-lang + [SP body-fld-loc *(SP body-extension)]]] + ; MUST NOT be returned on non-extensible + ; "BODY" fetch + +body-fields = body-fld-param SP body-fld-id SP body-fld-desc SP + body-fld-enc SP body-fld-octets + +body-fld-desc = nstring + +body-fld-dsp = "(" string SP body-fld-param ")" / nil + +body-fld-enc = (DQUOTE ("7BIT" / "8BIT" / "BINARY" / "BASE64"/ + "QUOTED-PRINTABLE") DQUOTE) / string + +body-fld-id = nstring + +body-fld-lang = nstring / "(" string *(SP string) ")" + +body-fld-loc = nstring + +body-fld-lines = number + +body-fld-md5 = nstring + +body-fld-octets = number + +body-fld-param = "(" string SP string *(SP string SP string) ")" / nil + +body-type-1part = (body-type-basic / body-type-msg / body-type-text) + [SP body-ext-1part] + +body-type-basic = media-basic SP body-fields + ; MESSAGE subtype MUST NOT be "RFC822" + +body-type-mpart = 1*body SP media-subtype + [SP body-ext-mpart] + +body-type-msg = media-message SP body-fields SP envelope + SP body SP body-fld-lines + +body-type-text = media-text SP body-fields SP body-fld-lines + +capability = ("AUTH=" auth-type) / atom + ; New capabilities MUST begin with "X" or be + ; registered with IANA as standard or + ; standards-track + + + + +Crispin Standards Track [Page 83] + +RFC 3501 IMAPv4 March 2003 + + +capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1" + *(SP capability) + ; Servers MUST implement the STARTTLS, AUTH=PLAIN, + ; and LOGINDISABLED capabilities + ; Servers which offer RFC 1730 compatibility MUST + ; list "IMAP4" as the first capability. + +CHAR8 = %x01-ff + ; any OCTET except NUL, %x00 + +command = tag SP (command-any / command-auth / command-nonauth / + command-select) CRLF + ; Modal based on state + +command-any = "CAPABILITY" / "LOGOUT" / "NOOP" / x-command + ; Valid in all states + +command-auth = append / create / delete / examine / list / lsub / + rename / select / status / subscribe / unsubscribe + ; Valid only in Authenticated or Selected state + +command-nonauth = login / authenticate / "STARTTLS" + ; Valid only when in Not Authenticated state + +command-select = "CHECK" / "CLOSE" / "EXPUNGE" / copy / fetch / store / + uid / search + ; Valid only when in Selected state + +continue-req = "+" SP (resp-text / base64) CRLF + +copy = "COPY" SP sequence-set SP mailbox + +create = "CREATE" SP mailbox + ; Use of INBOX gives a NO error + +date = date-text / DQUOTE date-text DQUOTE + +date-day = 1*2DIGIT + ; Day of month + +date-day-fixed = (SP DIGIT) / 2DIGIT + ; Fixed-format version of date-day + +date-month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / + "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" + +date-text = date-day "-" date-month "-" date-year + + + + +Crispin Standards Track [Page 84] + +RFC 3501 IMAPv4 March 2003 + + +date-year = 4DIGIT + +date-time = DQUOTE date-day-fixed "-" date-month "-" date-year + SP time SP zone DQUOTE + +delete = "DELETE" SP mailbox + ; Use of INBOX gives a NO error + +digit-nz = %x31-39 + ; 1-9 + +envelope = "(" env-date SP env-subject SP env-from SP + env-sender SP env-reply-to SP env-to SP env-cc SP + env-bcc SP env-in-reply-to SP env-message-id ")" + +env-bcc = "(" 1*address ")" / nil + +env-cc = "(" 1*address ")" / nil + +env-date = nstring + +env-from = "(" 1*address ")" / nil + +env-in-reply-to = nstring + +env-message-id = nstring + +env-reply-to = "(" 1*address ")" / nil + +env-sender = "(" 1*address ")" / nil + +env-subject = nstring + +env-to = "(" 1*address ")" / nil + +examine = "EXAMINE" SP mailbox + +fetch = "FETCH" SP sequence-set SP ("ALL" / "FULL" / "FAST" / + fetch-att / "(" fetch-att *(SP fetch-att) ")") + +fetch-att = "ENVELOPE" / "FLAGS" / "INTERNALDATE" / + "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] / + "BODY" ["STRUCTURE"] / "UID" / + "BODY" section ["<" number "." nz-number ">"] / + "BODY.PEEK" section ["<" number "." nz-number ">"] + + + + + + +Crispin Standards Track [Page 85] + +RFC 3501 IMAPv4 March 2003 + + +flag = "\Answered" / "\Flagged" / "\Deleted" / + "\Seen" / "\Draft" / flag-keyword / flag-extension + ; Does not include "\Recent" + +flag-extension = "\" atom + ; Future expansion. Client implementations + ; MUST accept flag-extension flags. Server + ; implementations MUST NOT generate + ; flag-extension flags except as defined by + ; future standard or standards-track + ; revisions of this specification. + +flag-fetch = flag / "\Recent" + +flag-keyword = atom + +flag-list = "(" [flag *(SP flag)] ")" + +flag-perm = flag / "\*" + +greeting = "*" SP (resp-cond-auth / resp-cond-bye) CRLF + +header-fld-name = astring + +header-list = "(" header-fld-name *(SP header-fld-name) ")" + +list = "LIST" SP mailbox SP list-mailbox + +list-mailbox = 1*list-char / string + +list-char = ATOM-CHAR / list-wildcards / resp-specials + +list-wildcards = "%" / "*" + +literal = "{" number "}" CRLF *CHAR8 + ; Number represents the number of CHAR8s + +login = "LOGIN" SP userid SP password + +lsub = "LSUB" SP mailbox SP list-mailbox + + + + + + + + + + + +Crispin Standards Track [Page 86] + +RFC 3501 IMAPv4 March 2003 + + +mailbox = "INBOX" / astring + ; INBOX is case-insensitive. All case variants of + ; INBOX (e.g., "iNbOx") MUST be interpreted as INBOX + ; not as an astring. An astring which consists of + ; the case-insensitive sequence "I" "N" "B" "O" "X" + ; is considered to be INBOX and not an astring. + ; Refer to section 5.1 for further + ; semantic details of mailbox names. + +mailbox-data = "FLAGS" SP flag-list / "LIST" SP mailbox-list / + "LSUB" SP mailbox-list / "SEARCH" *(SP nz-number) / + "STATUS" SP mailbox SP "(" [status-att-list] ")" / + number SP "EXISTS" / number SP "RECENT" + +mailbox-list = "(" [mbx-list-flags] ")" SP + (DQUOTE QUOTED-CHAR DQUOTE / nil) SP mailbox + +mbx-list-flags = *(mbx-list-oflag SP) mbx-list-sflag + *(SP mbx-list-oflag) / + mbx-list-oflag *(SP mbx-list-oflag) + +mbx-list-oflag = "\Noinferiors" / flag-extension + ; Other flags; multiple possible per LIST response + +mbx-list-sflag = "\Noselect" / "\Marked" / "\Unmarked" + ; Selectability flags; only one per LIST response + +media-basic = ((DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" / + "MESSAGE" / "VIDEO") DQUOTE) / string) SP + media-subtype + ; Defined in [MIME-IMT] + +media-message = DQUOTE "MESSAGE" DQUOTE SP DQUOTE "RFC822" DQUOTE + ; Defined in [MIME-IMT] + +media-subtype = string + ; Defined in [MIME-IMT] + +media-text = DQUOTE "TEXT" DQUOTE SP media-subtype + ; Defined in [MIME-IMT] + +message-data = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att)) + +msg-att = "(" (msg-att-dynamic / msg-att-static) + *(SP (msg-att-dynamic / msg-att-static)) ")" + +msg-att-dynamic = "FLAGS" SP "(" [flag-fetch *(SP flag-fetch)] ")" + ; MAY change for a message + + + +Crispin Standards Track [Page 87] + +RFC 3501 IMAPv4 March 2003 + + +msg-att-static = "ENVELOPE" SP envelope / "INTERNALDATE" SP date-time / + "RFC822" [".HEADER" / ".TEXT"] SP nstring / + "RFC822.SIZE" SP number / + "BODY" ["STRUCTURE"] SP body / + "BODY" section ["<" number ">"] SP nstring / + "UID" SP uniqueid + ; MUST NOT change for a message + +nil = "NIL" + +nstring = string / nil + +number = 1*DIGIT + ; Unsigned 32-bit integer + ; (0 <= n < 4,294,967,296) + +nz-number = digit-nz *DIGIT + ; Non-zero unsigned 32-bit integer + ; (0 < n < 4,294,967,296) + +password = astring + +quoted = DQUOTE *QUOTED-CHAR DQUOTE + +QUOTED-CHAR = / + "\" quoted-specials + +quoted-specials = DQUOTE / "\" + +rename = "RENAME" SP mailbox SP mailbox + ; Use of INBOX as a destination gives a NO error + +response = *(continue-req / response-data) response-done + +response-data = "*" SP (resp-cond-state / resp-cond-bye / + mailbox-data / message-data / capability-data) CRLF + +response-done = response-tagged / response-fatal + +response-fatal = "*" SP resp-cond-bye CRLF + ; Server closes connection immediately + +response-tagged = tag SP resp-cond-state CRLF + +resp-cond-auth = ("OK" / "PREAUTH") SP resp-text + ; Authentication condition + + + + + +Crispin Standards Track [Page 88] + +RFC 3501 IMAPv4 March 2003 + + +resp-cond-bye = "BYE" SP resp-text + +resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text + ; Status condition + +resp-specials = "]" + +resp-text = ["[" resp-text-code "]" SP] text + +resp-text-code = "ALERT" / + "BADCHARSET" [SP "(" astring *(SP astring) ")" ] / + capability-data / "PARSE" / + "PERMANENTFLAGS" SP "(" + [flag-perm *(SP flag-perm)] ")" / + "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + "UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number / + "UNSEEN" SP nz-number / + atom [SP 1*] + +search = "SEARCH" [SP "CHARSET" SP astring] 1*(SP search-key) + ; CHARSET argument to MUST be registered with IANA + +search-key = "ALL" / "ANSWERED" / "BCC" SP astring / + "BEFORE" SP date / "BODY" SP astring / + "CC" SP astring / "DELETED" / "FLAGGED" / + "FROM" SP astring / "KEYWORD" SP flag-keyword / + "NEW" / "OLD" / "ON" SP date / "RECENT" / "SEEN" / + "SINCE" SP date / "SUBJECT" SP astring / + "TEXT" SP astring / "TO" SP astring / + "UNANSWERED" / "UNDELETED" / "UNFLAGGED" / + "UNKEYWORD" SP flag-keyword / "UNSEEN" / + ; Above this line were in [IMAP2] + "DRAFT" / "HEADER" SP header-fld-name SP astring / + "LARGER" SP number / "NOT" SP search-key / + "OR" SP search-key SP search-key / + "SENTBEFORE" SP date / "SENTON" SP date / + "SENTSINCE" SP date / "SMALLER" SP number / + "UID" SP sequence-set / "UNDRAFT" / sequence-set / + "(" search-key *(SP search-key) ")" + +section = "[" [section-spec] "]" + +section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list / + "TEXT" + ; top-level or MESSAGE/RFC822 part + +section-part = nz-number *("." nz-number) + ; body part nesting + + + +Crispin Standards Track [Page 89] + +RFC 3501 IMAPv4 March 2003 + + +section-spec = section-msgtext / (section-part ["." section-text]) + +section-text = section-msgtext / "MIME" + ; text other than actual body part (headers, etc.) + +select = "SELECT" SP mailbox + +seq-number = nz-number / "*" + ; message sequence number (COPY, FETCH, STORE + ; commands) or unique identifier (UID COPY, + ; UID FETCH, UID STORE commands). + ; * represents the largest number in use. In + ; the case of message sequence numbers, it is + ; the number of messages in a non-empty mailbox. + ; In the case of unique identifiers, it is the + ; unique identifier of the last message in the + ; mailbox or, if the mailbox is empty, the + ; mailbox's current UIDNEXT value. + ; The server should respond with a tagged BAD + ; response to a command that uses a message + ; sequence number greater than the number of + ; messages in the selected mailbox. This + ; includes "*" if the selected mailbox is empty. + +seq-range = seq-number ":" seq-number + ; two seq-number values and all values between + ; these two regardless of order. + ; Example: 2:4 and 4:2 are equivalent and indicate + ; values 2, 3, and 4. + ; Example: a unique identifier sequence range of + ; 3291:* includes the UID of the last message in + ; the mailbox, even if that value is less than 3291. + +sequence-set = (seq-number / seq-range) *("," sequence-set) + ; set of seq-number values, regardless of order. + ; Servers MAY coalesce overlaps and/or execute the + ; sequence in any order. + ; Example: a message sequence number set of + ; 2,4:7,9,12:* for a mailbox with 15 messages is + ; equivalent to 2,4,5,6,7,9,12,13,14,15 + ; Example: a message sequence number set of *:4,5:7 + ; for a mailbox with 10 messages is equivalent to + ; 10,9,8,7,6,5,4,5,6,7 and MAY be reordered and + ; overlap coalesced to be 4,5,6,7,8,9,10. + +status = "STATUS" SP mailbox SP + "(" status-att *(SP status-att) ")" + + + + +Crispin Standards Track [Page 90] + +RFC 3501 IMAPv4 March 2003 + + +status-att = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" / + "UNSEEN" + +status-att-list = status-att SP number *(SP status-att SP number) + +store = "STORE" SP sequence-set SP store-att-flags + +store-att-flags = (["+" / "-"] "FLAGS" [".SILENT"]) SP + (flag-list / (flag *(SP flag))) + +string = quoted / literal + +subscribe = "SUBSCRIBE" SP mailbox + +tag = 1* + +text = 1*TEXT-CHAR + +TEXT-CHAR = + +time = 2DIGIT ":" 2DIGIT ":" 2DIGIT + ; Hours minutes seconds + +uid = "UID" SP (copy / fetch / search / store) + ; Unique identifiers used instead of message + ; sequence numbers + +uniqueid = nz-number + ; Strictly ascending + +unsubscribe = "UNSUBSCRIBE" SP mailbox + +userid = astring + +x-command = "X" atom + +zone = ("+" / "-") 4DIGIT + ; Signed four-digit value of hhmm representing + ; hours and minutes east of Greenwich (that is, + ; the amount that the given time differs from + ; Universal Time). Subtracting the timezone + ; from the given time will give the UT form. + ; The Universal Time zone is "+0000". + + + + + + + + +Crispin Standards Track [Page 91] + +RFC 3501 IMAPv4 March 2003 + + +10. Author's Note + + This document is a revision or rewrite of earlier documents, and + supercedes the protocol specification in those documents: RFC 2060, + RFC 1730, unpublished IMAP2bis.TXT document, RFC 1176, and RFC 1064. + +11. Security Considerations + + IMAP4rev1 protocol transactions, including electronic mail data, are + sent in the clear over the network unless protection from snooping is + negotiated. This can be accomplished either by the use of STARTTLS, + negotiated privacy protection in the AUTHENTICATE command, or some + other protection mechanism. + +11.1. STARTTLS Security Considerations + + The specification of the STARTTLS command and LOGINDISABLED + capability in this document replaces that in [IMAP-TLS]. [IMAP-TLS] + remains normative for the PLAIN [SASL] authenticator. + + IMAP client and server implementations MUST implement the + TLS_RSA_WITH_RC4_128_MD5 [TLS] cipher suite, and SHOULD implement the + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA [TLS] cipher suite. This is + important as it assures that any two compliant implementations can be + configured to interoperate. All other cipher suites are OPTIONAL. + Note that this is a change from section 2.1 of [IMAP-TLS]. + + During the [TLS] negotiation, the client MUST check its understanding + of the server hostname against the server's identity as presented in + the server Certificate message, in order to prevent man-in-the-middle + attacks. If the match fails, the client SHOULD either ask for + explicit user confirmation, or terminate the connection and indicate + that the server's identity is suspect. Matching is performed + according to these rules: + + The client MUST use the server hostname it used to open the + connection as the value to compare against the server name + as expressed in the server certificate. The client MUST + NOT use any form of the server hostname derived from an + insecure remote source (e.g., insecure DNS lookup). CNAME + canonicalization is not done. + + If a subjectAltName extension of type dNSName is present in + the certificate, it SHOULD be used as the source of the + server's identity. + + Matching is case-insensitive. + + + + +Crispin Standards Track [Page 92] + +RFC 3501 IMAPv4 March 2003 + + + A "*" wildcard character MAY be used as the left-most name + component in the certificate. For example, *.example.com + would match a.example.com, foo.example.com, etc. but would + not match example.com. + + If the certificate contains multiple names (e.g., more than + one dNSName field), then a match with any one of the fields + is considered acceptable. + + Both the client and server MUST check the result of the STARTTLS + command and subsequent [TLS] negotiation to see whether acceptable + authentication or privacy was achieved. + +11.2. Other Security Considerations + + A server error message for an AUTHENTICATE command which fails due to + invalid credentials SHOULD NOT detail why the credentials are + invalid. + + Use of the LOGIN command sends passwords in the clear. This can be + avoided by using the AUTHENTICATE command with a [SASL] mechanism + that does not use plaintext passwords, by first negotiating + encryption via STARTTLS or some other protection mechanism. + + A server implementation MUST implement a configuration that, at the + time of authentication, requires: + (1) The STARTTLS command has been negotiated. + OR + (2) Some other mechanism that protects the session from password + snooping has been provided. + OR + (3) The following measures are in place: + (a) The LOGINDISABLED capability is advertised, and [SASL] + mechanisms (such as PLAIN) using plaintext passwords are NOT + advertised in the CAPABILITY list. + AND + (b) The LOGIN command returns an error even if the password is + correct. + AND + (c) The AUTHENTICATE command returns an error with all [SASL] + mechanisms that use plaintext passwords, even if the password + is correct. + + A server error message for a failing LOGIN command SHOULD NOT specify + that the user name, as opposed to the password, is invalid. + + A server SHOULD have mechanisms in place to limit or delay failed + AUTHENTICATE/LOGIN attempts. + + + +Crispin Standards Track [Page 93] + +RFC 3501 IMAPv4 March 2003 + + + Additional security considerations are discussed in the section + discussing the AUTHENTICATE and LOGIN commands. + +12. IANA Considerations + + IMAP4 capabilities are registered by publishing a standards track or + IESG approved experimental RFC. The registry is currently located + at: + + http://www.iana.org/assignments/imap4-capabilities + + As this specification revises the STARTTLS and LOGINDISABLED + extensions previously defined in [IMAP-TLS], the registry will be + updated accordingly. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Crispin Standards Track [Page 94] + +RFC 3501 IMAPv4 March 2003 + + +Appendices + +A. Normative References + + The following documents contain definitions or specifications that + are necessary to understand this document properly: + [ABNF] Crocker, D. and P. Overell, "Augmented BNF for + Syntax Specifications: ABNF", RFC 2234, + November 1997. + + [ANONYMOUS] Newman, C., "Anonymous SASL Mechanism", RFC + 2245, November 1997. + + [CHARSET] Freed, N. and J. Postel, "IANA Character Set + Registration Procedures", RFC 2978, October + 2000. + + [DIGEST-MD5] Leach, P. and C. Newman, "Using Digest + Authentication as a SASL Mechanism", RFC 2831, + May 2000. + + [DISPOSITION] Troost, R., Dorner, S. and K. Moore, + "Communicating Presentation Information in + Internet Messages: The Content-Disposition + Header", RFC 2183, August 1997. + + [IMAP-TLS] Newman, C., "Using TLS with IMAP, POP3 and + ACAP", RFC 2595, June 1999. + + [KEYWORDS] Bradner, S., "Key words for use in RFCs to + Indicate Requirement Levels", BCP 14, RFC 2119, + March 1997. + + [LANGUAGE-TAGS] Alvestrand, H., "Tags for the Identification of + Languages", BCP 47, RFC 3066, January 2001. + + [LOCATION] Palme, J., Hopmann, A. and N. Shelness, "MIME + Encapsulation of Aggregate Documents, such as + HTML (MHTML)", RFC 2557, March 1999. + + [MD5] Myers, J. and M. Rose, "The Content-MD5 Header + Field", RFC 1864, October 1995. + + + + + + + + + +Crispin Standards Track [Page 95] + +RFC 3501 IMAPv4 March 2003 + + + [MIME-HDRS] Moore, K., "MIME (Multipurpose Internet Mail + Extensions) Part Three: Message Header + Extensions for Non-ASCII Text", RFC 2047, + November 1996. + + [MIME-IMB] Freed, N. and N. Borenstein, "MIME + (Multipurpose Internet Mail Extensions) Part + One: Format of Internet Message Bodies", RFC + 2045, November 1996. + + [MIME-IMT] Freed, N. and N. Borenstein, "MIME + (Multipurpose Internet Mail Extensions) Part + Two: Media Types", RFC 2046, November 1996. + + [RFC-2822] Resnick, P., "Internet Message Format", RFC + 2822, April 2001. + + [SASL] Myers, J., "Simple Authentication and Security + Layer (SASL)", RFC 2222, October 1997. + + [TLS] Dierks, T. and C. Allen, "The TLS Protocol + Version 1.0", RFC 2246, January 1999. + + [UTF-7] Goldsmith, D. and M. Davis, "UTF-7: A Mail-Safe + Transformation Format of Unicode", RFC 2152, + May 1997. + + The following documents describe quality-of-implementation issues + that should be carefully considered when implementing this protocol: + + [IMAP-IMPLEMENTATION] Leiba, B., "IMAP Implementation + Recommendations", RFC 2683, September 1999. + + [IMAP-MULTIACCESS] Gahrns, M., "IMAP4 Multi-Accessed Mailbox + Practice", RFC 2180, July 1997. + +A.1 Informative References + + The following documents describe related protocols: + + [IMAP-DISC] Austein, R., "Synchronization Operations for + Disconnected IMAP4 Clients", Work in Progress. + + [IMAP-MODEL] Crispin, M., "Distributed Electronic Mail + Models in IMAP4", RFC 1733, December 1994. + + + + + + +Crispin Standards Track [Page 96] + +RFC 3501 IMAPv4 March 2003 + + + [ACAP] Newman, C. and J. Myers, "ACAP -- Application + Configuration Access Protocol", RFC 2244, + November 1997. + + [SMTP] Klensin, J., "Simple Mail Transfer Protocol", + STD 10, RFC 2821, April 2001. + + The following documents are historical or describe historical aspects + of this protocol: + + [IMAP-COMPAT] Crispin, M., "IMAP4 Compatibility with + IMAP2bis", RFC 2061, December 1996. + + [IMAP-HISTORICAL] Crispin, M., "IMAP4 Compatibility with IMAP2 + and IMAP2bis", RFC 1732, December 1994. + + [IMAP-OBSOLETE] Crispin, M., "Internet Message Access Protocol + - Obsolete Syntax", RFC 2062, December 1996. + + [IMAP2] Crispin, M., "Interactive Mail Access Protocol + - Version 2", RFC 1176, August 1990. + + [RFC-822] Crocker, D., "Standard for the Format of ARPA + Internet Text Messages", STD 11, RFC 822, + August 1982. + + [RFC-821] Postel, J., "Simple Mail Transfer Protocol", + STD 10, RFC 821, August 1982. + +B. Changes from RFC 2060 + + 1) Clarify description of unique identifiers and their semantics. + + 2) Fix the SELECT description to clarify that UIDVALIDITY is required + in the SELECT and EXAMINE responses. + + 3) Added an example of a failing search. + + 4) Correct store-att-flags: "#flag" should be "1#flag". + + 5) Made search and section rules clearer. + + 6) Correct the STORE example. + + 7) Correct "BASE645" misspelling. + + 8) Remove extraneous close parenthesis in example of two-part message + with text and BASE64 attachment. + + + +Crispin Standards Track [Page 97] + +RFC 3501 IMAPv4 March 2003 + + + 9) Remove obsolete "MAILBOX" response from mailbox-data. + + 10) A spurious "<" in the rule for mailbox-data was removed. + + 11) Add CRLF to continue-req. + + 12) Specifically exclude "]" from the atom in resp-text-code. + + 13) Clarify that clients and servers should adhere strictly to the + protocol syntax. + + 14) Emphasize in 5.2 that EXISTS can not be used to shrink a mailbox. + + 15) Add NEWNAME to resp-text-code. + + 16) Clarify that the empty string, not NIL, is used as arguments to + LIST. + + 17) Clarify that NIL can be returned as a hierarchy delimiter for the + empty string mailbox name argument if the mailbox namespace is flat. + + 18) Clarify that addr-mailbox and addr-name have RFC-2822 quoting + removed. + + 19) Update UTF-7 reference. + + 20) Fix example in 6.3.11. + + 21) Clarify that non-existent UIDs are ignored. + + 22) Update DISPOSITION reference. + + 23) Expand state diagram. + + 24) Clarify that partial fetch responses are only returned in + response to a partial fetch command. + + 25) Add UIDNEXT response code. Correct UIDVALIDITY definition + reference. + + 26) Further clarification of "can" vs. "MAY". + + 27) Reference RFC-2119. + + 28) Clarify that superfluous shifts are not permitted in modified + UTF-7. + + 29) Clarify that there are no implicit shifts in modified UTF-7. + + + +Crispin Standards Track [Page 98] + +RFC 3501 IMAPv4 March 2003 + + + 30) Clarify that "INBOX" in a mailbox name is always INBOX, even if + it is given as a string. + + 31) Add missing open parenthesis in media-basic grammar rule. + + 32) Correct attribute syntax in mailbox-data. + + 33) Add UIDNEXT to EXAMINE responses. + + 34) Clarify UNSEEN, PERMANENTFLAGS, UIDVALIDITY, and UIDNEXT + responses in SELECT and EXAMINE. They are required now, but weren't + in older versions. + + 35) Update references with RFC numbers. + + 36) Flush text-mime2. + + 37) Clarify that modified UTF-7 names must be case-sensitive and that + violating the convention should be avoided. + + 38) Correct UID FETCH example. + + 39) Clarify UID FETCH, UID STORE, and UID SEARCH vs. untagged EXPUNGE + responses. + + 40) Clarify the use of the word "convention". + + 41) Clarify that a command is not "in progress" until it has been + fully received (specifically, that a command is not "in progress" + during command continuation negotiation). + + 42) Clarify envelope defaulting. + + 43) Clarify that SP means one and only one space character. + + 44) Forbid silly states in LIST response. + + 45) Clarify that the ENVELOPE, INTERNALDATE, RFC822*, BODY*, and UID + for a message is static. + + 46) Add BADCHARSET response code. + + 47) Update formal syntax to [ABNF] conventions. + + 48) Clarify trailing hierarchy delimiter in CREATE semantics. + + 49) Clarify that the "blank line" is the [RFC-2822] delimiting blank + line. + + + +Crispin Standards Track [Page 99] + +RFC 3501 IMAPv4 March 2003 + + + 50) Clarify that RENAME should also create hierarchy as needed for + the command to complete. + + 51) Fix body-ext-mpart to not require language if disposition + present. + + 52) Clarify the RFC822.HEADER response. + + 53) Correct missing space after charset astring in search. + + 54) Correct missing quote for BADCHARSET in resp-text-code. + + 55) Clarify that ALL, FAST, and FULL preclude any other data items + appearing. + + 56) Clarify semantics of reference argument in LIST. + + 57) Clarify that a null string for SEARCH HEADER X-FOO means any + message with a header line with a field-name of X-FOO regardless of + the text of the header. + + 58) Specifically reserve 8-bit mailbox names for future use as UTF-8. + + 59) It is not an error for the client to store a flag that is not in + the PERMANENTFLAGS list; however, the server will either ignore the + change or make the change in the session only. + + 60) Correct/clarify the text regarding superfluous shifts. + + 61) Correct typographic errors in the "Changes" section. + + 62) Clarify that STATUS must not be used to check for new messages in + the selected mailbox + + 63) Clarify LSUB behavior with "%" wildcard. + + 64) Change AUTHORIZATION to AUTHENTICATE in section 7.5. + + 65) Clarify description of multipart body type. + + 66) Clarify that STORE FLAGS does not affect \Recent. + + 67) Change "west" to "east" in description of timezone. + + 68) Clarify that commands which break command pipelining must wait + for a completion result response. + + 69) Clarify that EXAMINE does not affect \Recent. + + + +Crispin Standards Track [Page 100] + +RFC 3501 IMAPv4 March 2003 + + + 70) Make description of MIME structure consistent. + + 71) Clarify that date searches disregard the time and timezone of the + INTERNALDATE or Date: header. In other words, "ON 13-APR-2000" means + messages with an INTERNALDATE text which starts with "13-APR-2000", + even if timezone differential from the local timezone is sufficient + to move that INTERNALDATE into the previous or next day. + + 72) Clarify that the header fetches don't add a blank line if one + isn't in the [RFC-2822] message. + + 73) Clarify (in discussion of UIDs) that messages are immutable. + + 74) Add an example of CHARSET searching. + + 75) Clarify in SEARCH that keywords are a type of flag. + + 76) Clarify the mandatory nature of the SELECT data responses. + + 77) Add optional CAPABILITY response code in the initial OK or + PREAUTH. + + 78) Add note that server can send an untagged CAPABILITY command as + part of the responses to AUTHENTICATE and LOGIN. + + 79) Remove statement about it being unnecessary to issue a CAPABILITY + command more than once in a connection. That statement is no longer + true. + + 80) Clarify that untagged EXPUNGE decrements the number of messages + in the mailbox. + + 81) Fix definition of "body" (concatenation has tighter binding than + alternation). + + 82) Add a new "Special Notes to Implementors" section with reference + to [IMAP-IMPLEMENTATION]. + + 83) Clarify that an untagged CAPABILITY response to an AUTHENTICATE + command should only be done if a security layer was not negotiated. + + 84) Change the definition of atom to exclude "]". Update astring to + include "]" for compatibility with the past. Remove resp-text-atom. + + 85) Remove NEWNAME. It can't work because mailbox names can be + literals and can include "]". Functionality can be addressed via + referrals. + + + + +Crispin Standards Track [Page 101] + +RFC 3501 IMAPv4 March 2003 + + + 86) Move modified UTF-7 rationale in order to have more logical + paragraph flow. + + 87) Clarify UID uniqueness guarantees with the use of MUST. + + 88) Note that clients should read response data until the connection + is closed instead of immediately closing on a BYE. + + 89) Change RFC-822 references to RFC-2822. + + 90) Clarify that RFC-2822 should be followed instead of RFC-822. + + 91) Change recommendation of optional automatic capabilities in LOGIN + and AUTHENTICATE to use the CAPABILITY response code in the tagged + OK. This is more interoperable than an unsolicited untagged + CAPABILITY response. + + 92) STARTTLS and AUTH=PLAIN are mandatory to implement; add + recommendations for other [SASL] mechanisms. + + 93) Clarify that a "connection" (as opposed to "server" or "command") + is in one of the four states. + + 94) Clarify that a failed or rejected command does not change state. + + 95) Split references between normative and informative. + + 96) Discuss authentication failure issues in security section. + + 97) Clarify that a data item is not necessarily of only one data + type. + + 98) Clarify that sequence ranges are independent of order. + + 99) Change an example to clarify that superfluous shifts in + Modified-UTF7 can not be fixed just by omitting the shift. The + entire string must be recalculated. + + 100) Change Envelope Structure definition since [RFC-2822] uses + "envelope" to refer to the [SMTP] envelope and not the envelope data + that appears in the [RFC-2822] header. + + 101) Expand on RFC822.HEADER response data vs. BODY[HEADER]. + + 102) Clarify Logout state semantics, change ASCII art. + + 103) Security changes to comply with IESG requirements. + + + + +Crispin Standards Track [Page 102] + +RFC 3501 IMAPv4 March 2003 + + + 104) Add definition for body URI. + + 105) Break sequence range definition into three rules, with rewritten + descriptions for each. + + 106) Move STARTTLS and LOGINDISABLED here from [IMAP-TLS]. + + 107) Add IANA Considerations section. + + 108) Clarify valid client assumptions for new message UIDs vs. + UIDNEXT. + + 109) Clarify that changes to permanentflags affect concurrent + sessions as well as subsequent sessions. + + 110) Clarify that authenticated state can be entered by the CLOSE + command. + + 111) Emphasize that SELECT and EXAMINE are the exceptions to the rule + that a failing command does not change state. + + 112) Clarify that newly-appended messages have the Recent flag set. + + 113) Clarify that newly-copied messages SHOULD have the Recent flag + set. + + 114) Clarify that UID commands always return the UID in FETCH + responses. + +C. Key Word Index + + +FLAGS (store command data item) ............... 59 + +FLAGS.SILENT (store command data item) ........ 59 + -FLAGS (store command data item) ............... 59 + -FLAGS.SILENT (store command data item) ........ 59 + ALERT (response code) ...................................... 64 + ALL (fetch item) ........................................... 55 + ALL (search key) ........................................... 50 + ANSWERED (search key) ...................................... 50 + APPEND (command) ........................................... 45 + AUTHENTICATE (command) ..................................... 27 + BAD (response) ............................................. 66 + BADCHARSET (response code) ................................. 64 + BCC (search key) .................................. 51 + BEFORE (search key) ................................. 51 + BODY (fetch item) .......................................... 55 + BODY (fetch result) ........................................ 73 + BODY (search key) ................................. 51 + + + +Crispin Standards Track [Page 103] + +RFC 3501 IMAPv4 March 2003 + + + BODY.PEEK[
]<> (fetch item) ............... 57 + BODYSTRUCTURE (fetch item) ................................. 57 + BODYSTRUCTURE (fetch result) ............................... 74 + BODY[
]<> (fetch result) ............. 74 + BODY[
]<> (fetch item) .................... 55 + BYE (response) ............................................. 67 + Body Structure (message attribute) ......................... 12 + CAPABILITY (command) ....................................... 24 + CAPABILITY (response code) ................................. 64 + CAPABILITY (response) ...................................... 68 + CC (search key) ................................... 51 + CHECK (command) ............................................ 47 + CLOSE (command) ............................................ 48 + COPY (command) ............................................. 59 + CREATE (command) ........................................... 34 + DELETE (command) ........................................... 35 + DELETED (search key) ....................................... 51 + DRAFT (search key) ......................................... 51 + ENVELOPE (fetch item) ...................................... 57 + ENVELOPE (fetch result) .................................... 77 + EXAMINE (command) .......................................... 33 + EXISTS (response) .......................................... 71 + EXPUNGE (command) .......................................... 48 + EXPUNGE (response) ......................................... 72 + Envelope Structure (message attribute) ..................... 12 + FAST (fetch item) .......................................... 55 + FETCH (command) ............................................ 54 + FETCH (response) ........................................... 73 + FLAGGED (search key) ....................................... 51 + FLAGS (fetch item) ......................................... 57 + FLAGS (fetch result) ....................................... 78 + FLAGS (response) ........................................... 71 + FLAGS (store command data item) ................ 59 + FLAGS.SILENT (store command data item) ......... 59 + FROM (search key) ................................. 51 + FULL (fetch item) .......................................... 55 + Flags (message attribute) .................................. 11 + HEADER (part specifier) .................................... 55 + HEADER (search key) .................. 51 + HEADER.FIELDS (part specifier) ............... 55 + HEADER.FIELDS.NOT (part specifier) ........... 55 + INTERNALDATE (fetch item) .................................. 57 + INTERNALDATE (fetch result) ................................ 78 + Internal Date (message attribute) .......................... 12 + KEYWORD (search key) ................................ 51 + Keyword (type of flag) ..................................... 11 + LARGER (search key) .................................... 51 + LIST (command) ............................................. 40 + + + +Crispin Standards Track [Page 104] + +RFC 3501 IMAPv4 March 2003 + + + LIST (response) ............................................ 69 + LOGIN (command) ............................................ 30 + LOGOUT (command) ........................................... 25 + LSUB (command) ............................................. 43 + LSUB (response) ............................................ 70 + MAY (specification requirement term) ....................... 4 + MESSAGES (status item) ..................................... 45 + MIME (part specifier) ...................................... 56 + MUST (specification requirement term) ...................... 4 + MUST NOT (specification requirement term) .................. 4 + Message Sequence Number (message attribute) ................ 10 + NEW (search key) ........................................... 51 + NO (response) .............................................. 66 + NOOP (command) ............................................. 25 + NOT (search key) .............................. 52 + OK (response) .............................................. 65 + OLD (search key) ........................................... 52 + ON (search key) ..................................... 52 + OPTIONAL (specification requirement term) .................. 4 + OR (search key) ................ 52 + PARSE (response code) ...................................... 64 + PERMANENTFLAGS (response code) ............................. 64 + PREAUTH (response) ......................................... 67 + Permanent Flag (class of flag) ............................. 12 + READ-ONLY (response code) .................................. 65 + READ-WRITE (response code) ................................. 65 + RECENT (response) .......................................... 72 + RECENT (search key) ........................................ 52 + RECENT (status item) ....................................... 45 + RENAME (command) ........................................... 37 + REQUIRED (specification requirement term) .................. 4 + RFC822 (fetch item) ........................................ 57 + RFC822 (fetch result) ...................................... 78 + RFC822.HEADER (fetch item) ................................. 57 + RFC822.HEADER (fetch result) ............................... 78 + RFC822.SIZE (fetch item) ................................... 57 + RFC822.SIZE (fetch result) ................................. 78 + RFC822.TEXT (fetch item) ................................... 58 + RFC822.TEXT (fetch result) ................................. 79 + SEARCH (command) ........................................... 49 + SEARCH (response) .......................................... 71 + SEEN (search key) .......................................... 52 + SELECT (command) ........................................... 31 + SENTBEFORE (search key) ............................. 52 + SENTON (search key) ................................. 52 + SENTSINCE (search key) .............................. 52 + SHOULD (specification requirement term) .................... 4 + SHOULD NOT (specification requirement term) ................ 4 + + + +Crispin Standards Track [Page 105] + +RFC 3501 IMAPv4 March 2003 + + + SINCE (search key) .................................. 52 + SMALLER (search key) ................................... 52 + STARTTLS (command) ......................................... 27 + STATUS (command) ........................................... 44 + STATUS (response) .......................................... 70 + STORE (command) ............................................ 58 + SUBJECT (search key) .............................. 53 + SUBSCRIBE (command) ........................................ 38 + Session Flag (class of flag) ............................... 12 + System Flag (type of flag) ................................. 11 + TEXT (part specifier) ...................................... 56 + TEXT (search key) ................................. 53 + TO (search key) ................................... 53 + TRYCREATE (response code) .................................. 65 + UID (command) .............................................. 60 + UID (fetch item) ........................................... 58 + UID (fetch result) ......................................... 79 + UID (search key) ............................ 53 + UIDNEXT (response code) .................................... 65 + UIDNEXT (status item) ...................................... 45 + UIDVALIDITY (response code) ................................ 65 + UIDVALIDITY (status item) .................................. 45 + UNANSWERED (search key) .................................... 53 + UNDELETED (search key) ..................................... 53 + UNDRAFT (search key) ....................................... 53 + UNFLAGGED (search key) ..................................... 53 + UNKEYWORD (search key) .............................. 53 + UNSEEN (response code) ..................................... 65 + UNSEEN (search key) ........................................ 53 + UNSEEN (status item) ....................................... 45 + UNSUBSCRIBE (command) ...................................... 39 + Unique Identifier (UID) (message attribute) ................ 8 + X (command) .......................................... 62 + [RFC-2822] Size (message attribute) ........................ 12 + \Answered (system flag) .................................... 11 + \Deleted (system flag) ..................................... 11 + \Draft (system flag) ....................................... 11 + \Flagged (system flag) ..................................... 11 + \Marked (mailbox name attribute) ........................... 69 + \Noinferiors (mailbox name attribute) ...................... 69 + \Noselect (mailbox name attribute) ......................... 69 + \Recent (system flag) ...................................... 11 + \Seen (system flag) ........................................ 11 + \Unmarked (mailbox name attribute) ......................... 69 + + + + + + + +Crispin Standards Track [Page 106] + +RFC 3501 IMAPv4 March 2003 + + +Author's Address + + Mark R. Crispin + Networks and Distributed Computing + University of Washington + 4545 15th Avenue NE + Seattle, WA 98105-4527 + + Phone: (206) 543-5762 + + EMail: MRC@CAC.Washington.EDU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Crispin Standards Track [Page 107] + +RFC 3501 IMAPv4 March 2003 + + +Full Copyright Statement + + Copyright (C) The Internet Society (2003). All Rights Reserved. + + This document and translations of it may be copied and furnished to + others, and derivative works that comment on or otherwise explain it + or assist in its implementation may be prepared, copied, published + and distributed, in whole or in part, without restriction of any + kind, provided that the above copyright notice and this paragraph are + included on all such copies and derivative works. However, this + document itself may not be modified in any way, such as by removing + the copyright notice or references to the Internet Society or other + Internet organizations, except as needed for the purpose of + developing Internet standards in which case the procedures for + copyrights defined in the Internet Standards process must be + followed, or as required to translate it into languages other than + English. + + The limited permissions granted above are perpetual and will not be + revoked by the Internet Society or its successors or assigns. v This + document and the information contained herein is provided on an "AS + IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK + FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL + NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY + OR FITNESS FOR A PARTICULAR PURPOSE. + +Acknowledgement + + Funding for the RFC Editor function is currently provided by the + Internet Society. + + + + + + + + + + + + + + + + + + + + +Crispin Standards Track [Page 108] + diff --git a/vendor/github.com/mattermost/rsc/imap/sx.go b/vendor/github.com/mattermost/rsc/imap/sx.go new file mode 100644 index 000000000..8b0e361fa --- /dev/null +++ b/vendor/github.com/mattermost/rsc/imap/sx.go @@ -0,0 +1,350 @@ +package imap + +import ( + "bufio" + "bytes" + "fmt" + "io" + "log" + "strings" + "time" +) + +type sxKind int + +const ( + sxNone sxKind = iota + sxAtom + sxString + sxNumber + sxList +) + +type sx struct { + kind sxKind + data []byte + number int64 + sx []*sx +} + +func rdsx(b *bufio.Reader) (*sx, error) { + x := &sx{kind: sxList} + for { + xx, err := rdsx1(b) + if err != nil { + return nil, err + } + if xx == nil { + break + } + x.sx = append(x.sx, xx) + } + return x, nil +} + +func rdsx1(b *bufio.Reader) (*sx, error) { + c, err := b.ReadByte() + if c == ' ' { + c, err = b.ReadByte() + } + if c == '\r' { + c, err = b.ReadByte() + } + if err != nil { + return nil, err + } + if c == '\n' { + return nil, nil + } + if c == ')' { // end of list + b.UnreadByte() + return nil, nil + } + if c == '(' { // parenthesized list + x, err := rdsx(b) + if err != nil { + return nil, err + } + c, err = b.ReadByte() + if err != nil { + return nil, err + } + if c != ')' { + // oops! not good + b.UnreadByte() + } + return x, nil + } + if c == '{' { // length-prefixed string + n := 0 + for { + c, _ = b.ReadByte() + if c < '0' || c > '9' { + break + } + n = n*10 + int(c) - '0' + } + if c != '}' { + // oops! not good + b.UnreadByte() + } + c, err = b.ReadByte() + if c != '\r' { + // oops! not good + } + c, err = b.ReadByte() + if c != '\n' { + // oops! not good + } + data := make([]byte, n) + if _, err := io.ReadFull(b, data); err != nil { + return nil, err + } + return &sx{kind: sxString, data: data}, nil + } + if c == '"' { // quoted string + var data []byte + for { + c, err = b.ReadByte() + if err != nil { + return nil, err + } + if c == '"' { + break + } + if c == '\\' { + c, _ = b.ReadByte() + } + data = append(data, c) + } + return &sx{kind: sxString, data: data}, nil + } + if '0' <= c && c <= '9' { // number + n := int64(c) - '0' + for { + c, err := b.ReadByte() + if err != nil { + return nil, err + } + if c < '0' || c > '9' { + break + } + n = n*10 + int64(c) - '0' + } + b.UnreadByte() + return &sx{kind: sxNumber, number: n}, nil + } + + // atom + nbr := 0 + var data []byte + data = append(data, c) + for { + c, err = b.ReadByte() + if err != nil { + return nil, err + } + if c <= ' ' || c == '(' || c == ')' || c == '{' || c == '}' { + break + } + if c == '[' { + // allow embedded brackets as in BODY[] + if data[0] == '[' { + break + } + nbr++ + } + if c == ']' { + if nbr <= 0 { + break + } + nbr-- + } + data = append(data, c) + } + if c != ' ' { + b.UnreadByte() + } + return &sx{kind: sxAtom, data: data}, nil +} + +func (x *sx) ok() bool { + return len(x.sx) >= 2 && x.sx[1].kind == sxAtom && strings.EqualFold(string(x.sx[1].data), "ok") +} + +func (x *sx) String() string { + var b bytes.Buffer + x.fmt(&b, true) + return b.String() +} + +func (x *sx) fmt(b *bytes.Buffer, paren bool) { + if x == nil { + return + } + switch x.kind { + case sxAtom, sxString: + fmt.Fprintf(b, "%q", x.data) + case sxNumber: + fmt.Fprintf(b, "%d", x.number) + case sxList: + if paren { + b.WriteByte('(') + } + for i, xx := range x.sx { + if i > 0 { + b.WriteByte(' ') + } + xx.fmt(b, paren) + } + if paren { + b.WriteByte(')') + } + default: + b.WriteByte('?') + } +} + +var bytesNIL = []byte("NIL") + +var fmtKind = []sxKind{ + 'L': sxList, + 'S': sxString, + 'N': sxNumber, + 'A': sxAtom, +} + +func (x *sx) match(format string) bool { + done := false + c := format[0] + for i := 0; i < len(x.sx); i++ { + if !done { + if i >= len(format) { + log.Printf("sxmatch: too short") + return false + } + if format[i] == '*' { + done = true + } else { + c = format[i] + } + } + xx := x.sx[i] + if xx.kind == sxAtom && xx.isNil() { + if c == 'L' { + xx.kind = sxList + xx.data = nil + } else if c == 'S' { + xx.kind = sxString + xx.data = nil + } + } + if xx.kind == sxAtom && c == 'S' { + xx.kind = sxString + } + if xx.kind != fmtKind[c] { + log.Printf("sxmatch: %s not %c", xx, c) + return false + } + } + if len(format) > len(x.sx) { + log.Printf("sxmatch: too long") + return false + } + return true +} + +func (x *sx) isAtom(name string) bool { + if x == nil || x.kind != sxAtom { + return false + } + data := x.data + n := len(name) + if n > 0 && name[n-1] == '[' { + i := bytes.IndexByte(data, '[') + if i < 0 { + return false + } + data = data[:i] + name = name[:n-1] + } + for i := 0; i < len(name); i++ { + if i >= len(data) || lwr(rune(data[i])) != lwr(rune(name[i])) { + return false + } + } + return len(name) == len(data) +} + +func (x *sx) isString() bool { + if x.isNil() { + return true + } + if x.kind == sxAtom { + x.kind = sxString + } + return x.kind == sxString +} + +func (x *sx) isNumber() bool { + return x.kind == sxNumber +} + +func (x *sx) isNil() bool { + return x == nil || + x.kind == sxList && len(x.sx) == 0 || + x.kind == sxAtom && bytes.Equal(x.data, bytesNIL) +} + +func (x *sx) isList() bool { + return x.isNil() || x.kind == sxList +} + +func (x *sx) parseFlags() Flags { + if x.kind != sxList { + log.Printf("malformed flags: %s", x) + return 0 + } + + f := Flags(0) +SX: + for _, xx := range x.sx { + if xx.kind != sxAtom { + continue + } + for i, name := range flagNames { + if xx.isAtom(name) { + f |= 1 << uint(i) + continue SX + } + } + if Debug { + log.Printf("unknown flag: %v", xx) + } + } + return f +} + +func (x *sx) parseDate() time.Time { + if x.kind != sxString { + log.Printf("malformed date: %s", x) + return time.Time{} + } + + t, err := time.Parse("02-Jan-2006 15:04:05 -0700", string(x.data)) + if err != nil { + log.Printf("malformed date: %s (%s)", x, err) + } + return t +} + +func (x *sx) nstring() string { + return string(x.nbytes()) +} + +func (x *sx) nbytes() []byte { + if x.isNil() { + return nil + } + return x.data +} diff --git a/vendor/github.com/mattermost/rsc/imap/sx_test.go b/vendor/github.com/mattermost/rsc/imap/sx_test.go new file mode 100644 index 000000000..9644209ca --- /dev/null +++ b/vendor/github.com/mattermost/rsc/imap/sx_test.go @@ -0,0 +1,60 @@ +package imap + +import ( + "bufio" + "reflect" + "strings" + "testing" +) + +var sxTests = []struct { + in string + out *sx +}{ + {"1234", &sx{kind: sxNumber, number: 1234}}, + {"hello", &sx{kind: sxAtom, data: []byte("hello")}}, + {"hello[world]", &sx{kind: sxAtom, data: []byte("hello[world]")}}, + {`"h\\ello"`, &sx{kind: sxString, data: []byte(`h\ello`)}}, + {"{6}\r\nh\\ello", &sx{kind: sxString, data: []byte(`h\ello`)}}, + {`(hello "world" (again) ())`, + &sx{ + kind: sxList, + sx: []*sx{ + &sx{ + kind: sxAtom, + data: []byte("hello"), + }, + &sx{ + kind: sxString, + data: []byte("world"), + }, + &sx{ + kind: sxList, + sx: []*sx{ + &sx{ + kind: sxAtom, + data: []byte("again"), + }, + }, + }, + &sx{ + kind: sxList, + }, + }, + }, + }, +} + +func TestSx(t *testing.T) { + for _, tt := range sxTests { + b := bufio.NewReader(strings.NewReader(tt.in + "\n")) + sx, err := rdsx1(b) + if err != nil { + t.Errorf("parse %s: %v", tt.in, err) + continue + } + if !reflect.DeepEqual(sx, tt.out) { + t.Errorf("rdsx1(%s) = %v, want %v", tt.in, sx, tt.out) + } + } +} diff --git a/vendor/github.com/mattermost/rsc/imap/tcs.go b/vendor/github.com/mattermost/rsc/imap/tcs.go new file mode 100644 index 000000000..6a0939e01 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/imap/tcs.go @@ -0,0 +1,602 @@ +package imap + +// NOTE(rsc): These belong elsewhere but the existing charset +// packages seem too complicated. + +var tab_iso8859_1 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +} + +var tab_iso8859_2 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x00a0, 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7, + 0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b, + 0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7, + 0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, + 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, + 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, + 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, + 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, + 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, + 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, +} + +var tab_iso8859_3 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x00a0, 0x0126, 0x02d8, 0x00a3, 0x00a4, -1, 0x0124, 0x00a7, + 0x00a8, 0x0130, 0x015e, 0x011e, 0x0134, 0x00ad, -1, 0x017b, + 0x00b0, 0x0127, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x0125, 0x00b7, + 0x00b8, 0x0131, 0x015f, 0x011f, 0x0135, 0x00bd, -1, 0x017c, + 0x00c0, 0x00c1, 0x00c2, -1, 0x00c4, 0x010a, 0x0108, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + -1, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x0120, 0x00d6, 0x00d7, + 0x011c, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x016c, 0x015c, 0x00df, + 0x00e0, 0x00e1, 0x00e2, -1, 0x00e4, 0x010b, 0x0109, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, + -1, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x0121, 0x00f6, 0x00f7, + 0x011d, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x016d, 0x015d, 0x02d9, +} + +var tab_iso8859_4 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x00a0, 0x0104, 0x0138, 0x0156, 0x00a4, 0x0128, 0x013b, 0x00a7, + 0x00a8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00ad, 0x017d, 0x00af, + 0x00b0, 0x0105, 0x02db, 0x0157, 0x00b4, 0x0129, 0x013c, 0x02c7, + 0x00b8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014a, 0x017e, 0x014b, + 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x012a, + 0x0110, 0x0145, 0x014c, 0x0136, 0x00d4, 0x00d5, 0x00d6, 0x00d7, + 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x0168, 0x016a, 0x00df, + 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x012b, + 0x0111, 0x0146, 0x014d, 0x0137, 0x00f4, 0x00f5, 0x00f6, 0x00f7, + 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x0169, 0x016b, 0x02d9, +} + +var tab_iso8859_5 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x00a0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, + 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x00ad, 0x040e, 0x040f, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, + 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, + 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x00a7, 0x045e, 0x045f, +} + +var tab_iso8859_6 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x00a0, -1, -1, -1, 0x00a4, -1, -1, -1, + -1, -1, -1, -1, 0x060c, 0x00ad, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 0x061b, -1, -1, -1, 0x061f, + -1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, + 0x0638, 0x0639, 0x063a, -1, -1, -1, -1, -1, + 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, + 0x0648, 0x0649, 0x064a, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, + 0x0650, 0x0651, 0x0652, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +} + +var tab_iso8859_7 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x00a0, 0x2018, 0x2019, 0x00a3, -1, -1, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, -1, 0x00ab, 0x00ac, 0x00ad, -1, 0x2015, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x0385, 0x0386, 0x00b7, + 0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, + 0x03a0, 0x03a1, -1, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, + 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af, + 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, + 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, + 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, + 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, -1, +} + +var tab_iso8859_8 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x00a0, -1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x00d7, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x203e, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x00b9, 0x00f7, 0x00bb, 0x00bc, 0x00bd, 0x00be, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 0x2017, + 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, + 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, + 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, + 0x05e8, 0x05e9, 0x05ea, -1, -1, -1, -1, -1, +} + +var tab_iso8859_9 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + 0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, + 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, + 0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, + 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff, +} + +var tab_iso8859_10 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x00a0, 0x0104, 0x0112, 0x0122, 0x012a, 0x0128, 0x0136, 0x00a7, + 0x013b, 0x0110, 0x0160, 0x0166, 0x017d, 0x00ad, 0x016a, 0x014a, + 0x00b0, 0x0105, 0x0113, 0x0123, 0x012b, 0x0129, 0x0137, 0x00b7, + 0x013c, 0x0110, 0x0161, 0x0167, 0x017e, 0x2014, 0x016b, 0x014b, + 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x00cf, + 0x00d0, 0x0145, 0x014c, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0168, + 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, + 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x00ef, + 0x00f0, 0x0146, 0x014d, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x0169, + 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x0138, +} + +var tab_iso8859_15 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0x20ac, 0xa5, 0x0160, 0xa7, 0x0161, 0xa9, 0xaa, 0xab, 0xac, 0xad, + 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0x017d, 0xb5, 0xb6, 0xb7, 0x017e, 0xb9, 0xba, 0xbb, + 0x0152, 0x0153, 0x0178, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, + 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +} + +var tab_koi8 = [256]rune{ + 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, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, + 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, + 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, + 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, + 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, + 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, + 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, + 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a, +} + +var tab_cp1250 = [256]rune{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, -1, 0x201A, -1, 0x201E, 0x2026, 0x2020, 0x2021, + -1, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, + -1, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + -1, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A, + 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B, + 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C, + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, + 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, + 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, + 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, + 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9, +} +var tab_cp1251 = [256]rune{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, + 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, + 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + -1, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, + 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, + 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, + 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, + 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, +} +var tab_cp1252 = [256]rune{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, -1, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, -1, 0x017D, -1, + -1, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, -1, 0x017E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, +} +var tab_cp1253 = [256]rune{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, -1, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + -1, 0x2030, -1, 0x2039, -1, -1, -1, -1, + -1, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + -1, 0x2122, -1, 0x203A, -1, -1, -1, -1, + 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, -1, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, + 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, -1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, -1, +} +var tab_cp1254 = [256]rune{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, -1, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, -1, -1, -1, + -1, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, -1, -1, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF, +} +var tab_cp1255 = [256]rune{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, -1, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, -1, 0x2039, -1, -1, -1, -1, + -1, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, -1, 0x203A, -1, -1, -1, -1, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AA, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, -1, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, + 0x05F4, -1, -1, -1, -1, -1, -1, -1, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, -1, -1, 0x200E, 0x200F, -1, +} +var tab_cp1256 = [256]rune{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, + 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA, + 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F, + 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, + 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0641, 0x0642, 0x0643, + 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF, + 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, + 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2, +} +var tab_cp1257 = [256]rune{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, -1, 0x201A, -1, 0x201E, 0x2026, 0x2020, 0x2021, + -1, 0x2030, -1, 0x2039, -1, 0x00A8, 0x02C7, 0x00B8, + -1, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + -1, 0x2122, -1, 0x203A, -1, 0x00AF, 0x02DB, -1, + 0x00A0, -1, 0x00A2, 0x00A3, 0x00A4, -1, 0x00A6, 0x00A7, + 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, + 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, + 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, + 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, + 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9, +} +var tab_cp1258 = [256]rune{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + 0x20AC, -1, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, -1, 0x2039, 0x0152, -1, -1, -1, + -1, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, -1, 0x203A, 0x0153, -1, -1, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF, + 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF, + 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF, +} diff --git a/vendor/github.com/mattermost/rsc/keychain/doc.go b/vendor/github.com/mattermost/rsc/keychain/doc.go new file mode 100644 index 000000000..e7e21fd2e --- /dev/null +++ b/vendor/github.com/mattermost/rsc/keychain/doc.go @@ -0,0 +1,28 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package keychain implements access to the passwords and other keys +// stored in the system-provided keychain. +package keychain + +// BUG(rsc): Package keychain is only implemented on OS X. + +import ( + "fmt" +) + +// UserPasswd returns the user name and password for authenticating +// to the named server. If the user argument is non-empty, UserPasswd +// restricts its search to passwords for the named user. +func UserPasswd(server, preferredUser string) (user, passwd string, err error) { + user, passwd, err = userPasswd(server, preferredUser) + if err != nil { + if preferredUser != "" { + err = fmt.Errorf("loading password for %s@%s: %v", preferredUser, server, err) + } else { + err = fmt.Errorf("loading password for %s: %v", server, err) + } + } + return +} diff --git a/vendor/github.com/mattermost/rsc/keychain/mac.go b/vendor/github.com/mattermost/rsc/keychain/mac.go new file mode 100644 index 000000000..523579169 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/keychain/mac.go @@ -0,0 +1,107 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package keychain + +/* +#include +#include +#include + +#cgo LDFLAGS: -framework CoreFoundation -framework Security + +static char* +mac2c(CFStringRef s) +{ + char *p; + int n; + + n = CFStringGetLength(s)*8; + p = malloc(n); + CFStringGetCString(s, p, n, kCFStringEncodingUTF8); + return p; +} + +void +keychain_getpasswd(char *user0, char *server, char **user, char **passwd, char **error) +{ + OSStatus st; + UInt32 len; + void *data; + SecKeychainItemRef it; + CFStringRef str; + + *user = NULL; + *passwd = NULL; + *error = NULL; + + st = SecKeychainFindInternetPassword( + NULL, // default keychain + strlen(server), server, + 0, NULL, // security domain + strlen(user0), user0, // account name + 0, NULL, // path + 0, // port + 0, // protocol type + kSecAuthenticationTypeDefault, + &len, + &data, + &it); + if(st != 0) { + str = SecCopyErrorMessageString(st, NULL); + *error = mac2c(str); + CFRelease(str); + return; + } + *passwd = malloc(len+1); + memmove(*passwd, data, len); + (*passwd)[len] = '\0'; + SecKeychainItemFreeContent(NULL, data); + + SecKeychainAttribute attr = {kSecAccountItemAttr, 0, NULL}; + SecKeychainAttributeList attrl = {1, &attr}; + st = SecKeychainItemCopyContent( + it, + NULL, + &attrl, + 0, NULL); + if(st != 0) { + str = SecCopyErrorMessageString(st, NULL); + *error = mac2c(str); + CFRelease(str); + return; + } + data = attr.data; + len = attr.length; + *user = malloc(len+1); + memmove(*user, data, len); + (*user)[len] = '\0'; + SecKeychainItemFreeContent(&attrl, NULL); +} +*/ +import "C" + +import ( + "errors" + "unsafe" +) + +func userPasswd(server, user string) (user1, passwd string, err error) { + cServer := C.CString(server) + cUser := C.CString(user) + defer C.free(unsafe.Pointer(cServer)) + defer C.free(unsafe.Pointer(cUser)) + + var cPasswd, cError *C.char + C.keychain_getpasswd(cUser, cServer, &cUser, &cPasswd, &cError) + defer C.free(unsafe.Pointer(cUser)) + defer C.free(unsafe.Pointer(cPasswd)) + defer C.free(unsafe.Pointer(cError)) + + if cError != nil { + return "", "", errors.New(C.GoString(cError)) + } + + return C.GoString(cUser), C.GoString(cPasswd), nil +} diff --git a/vendor/github.com/mattermost/rsc/mbta/Passages.pb b/vendor/github.com/mattermost/rsc/mbta/Passages.pb new file mode 100644 index 000000000..5eb9e900e Binary files /dev/null and b/vendor/github.com/mattermost/rsc/mbta/Passages.pb differ diff --git a/vendor/github.com/mattermost/rsc/mbta/Vehicles.pb b/vendor/github.com/mattermost/rsc/mbta/Vehicles.pb new file mode 100644 index 000000000..98d90c800 Binary files /dev/null and b/vendor/github.com/mattermost/rsc/mbta/Vehicles.pb differ diff --git a/vendor/github.com/mattermost/rsc/mbta/mbta.go b/vendor/github.com/mattermost/rsc/mbta/mbta.go new file mode 100644 index 000000000..c8c68e6e2 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/mbta/mbta.go @@ -0,0 +1,28 @@ +package main + +import ( + "io/ioutil" + "log" + "os" + + "code.google.com/p/goprotobuf/proto" + "github.com/mattermost/rsc/gtfs" +) + +func main() { + log.SetFlags(0) + if len(os.Args) != 2 { + log.Fatal("usage: mbta file.pb") + } + pb, err := ioutil.ReadFile(os.Args[1]) + if err != nil { + log.Fatal(err) + } + + var feed gtfs.FeedMessage + if err := proto.Unmarshal(pb, &feed); err != nil { + log.Fatal(err) + } + + proto.MarshalText(os.Stdout, &feed) +} diff --git a/vendor/github.com/mattermost/rsc/mkapp b/vendor/github.com/mattermost/rsc/mkapp new file mode 100755 index 000000000..1a52931f1 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/mkapp @@ -0,0 +1,51 @@ +#!/bin/bash +# Copyright 2012 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# Run this script in your app root directory, the one containing +# the app.yaml file. Then point appcfg.py or dev_appserver.py +# at './tmp' instead of '.'. +# +# It creates a symlink forest in a subdirectory named 'tmp' that +# includes all the files from packages under your app root as +# well as any packages from your $GOPATH that are needed by +# the app packages. This makes deployment to App Engine +# bring in just the packages you need, without manual chasing +# of dependencies. Perhaps some day appcfg.py and dev_appserver.py +# will work this way by default. + +set -e +rm -rf tmp +dirs=$(find . -type d) +mkdir tmp + +for i in * +do + case "$i" in + tmp | *.go) + ;; + *) + ln -s ../$i tmp/$i + esac +done + +# Like App Engine +export GOOS=linux +export GOARCH=amd64 + +mkdir tmp/_go_top +cp $(go list -f '{{range .GoFiles}}{{.}} {{end}}') tmp/_go_top + +dirs=$(go list -e -f '{{if not .Standard}}{{.ImportPath}} {{end}}' $(go list -f '{{range .Deps}}{{.}} {{end}}' $dirs)) +for import in $dirs +do + case "$import" in + appengine | appengine/*) + ;; + *) + mkdir -p tmp/$import + files=$(go list -f '{{range .GoFiles}}{{$.Dir}}/{{.}} {{end}}' $import) + ln -s $files tmp/$import 2>&1 | grep -v 'is a directory' || true # ignore subdirectory warnings + esac +done diff --git a/vendor/github.com/mattermost/rsc/plist/plist.go b/vendor/github.com/mattermost/rsc/plist/plist.go new file mode 100644 index 000000000..1c76bface --- /dev/null +++ b/vendor/github.com/mattermost/rsc/plist/plist.go @@ -0,0 +1,179 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package plist implements parsing of Apple plist files. +package plist + +import ( + "bytes" + "fmt" + "reflect" + "strconv" +) + +func next(data []byte) (skip, tag, rest []byte) { + i := bytes.IndexByte(data, '<') + if i < 0 { + return data, nil, nil + } + j := bytes.IndexByte(data[i:], '>') + if j < 0 { + return data, nil, nil + } + j += i + 1 + return data[:i], data[i:j], data[j:] +} + +func Unmarshal(data []byte, v interface{}) error { + _, tag, data := next(data) + if !bytes.HasPrefix(tag, []byte("")) { + return fmt.Errorf("junk on end of plist") + } + return nil +} + +func unmarshalValue(data []byte, v reflect.Value) (rest []byte, err error) { + _, tag, data := next(data) + if tag == nil { + return nil, fmt.Errorf("unexpected end of data") + } + + if v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + + switch string(tag) { + case "": + t := v.Type() + if v.Kind() != reflect.Struct { + return nil, fmt.Errorf("cannot unmarshal into non-struct %s", v.Type()) + } + Dict: + for { + _, tag, data = next(data) + if len(tag) == 0 { + return nil, fmt.Errorf("eof inside ") + } + if string(tag) == "" { + break + } + if string(tag) != "" { + return nil, fmt.Errorf("unexpected tag %s inside ", tag) + } + var body []byte + body, tag, data = next(data) + if len(tag) == 0 { + return nil, fmt.Errorf("eof inside ") + } + if string(tag) != "" { + return nil, fmt.Errorf("unexpected tag %s inside ", tag) + } + name := string(body) + var i int + for i = 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Name == name || f.Tag.Get("plist") == name { + data, err = unmarshalValue(data, v.Field(i)) + continue Dict + } + } + data, err = skipValue(data) + if err != nil { + return nil, err + } + } + return data, nil + + case "": + t := v.Type() + if v.Kind() != reflect.Slice { + return nil, fmt.Errorf("cannot unmarshal into non-slice %s", v.Type()) + } + for { + _, tag, rest := next(data) + if len(tag) == 0 { + return nil, fmt.Errorf("eof inside ") + } + if string(tag) == "" { + data = rest + break + } + elem := reflect.New(t.Elem()).Elem() + data, err = unmarshalValue(data, elem) + if err != nil { + return nil, err + } + v.Set(reflect.Append(v, elem)) + } + return data, nil + + case "": + if v.Kind() != reflect.String { + return nil, fmt.Errorf("cannot unmarshal into non-string %s", v.Type()) + } + body, etag, data := next(data) + if len(etag) == 0 { + return nil, fmt.Errorf("eof inside ") + } + if string(etag) != "" { + return nil, fmt.Errorf("expected but got %s", etag) + } + v.SetString(string(body)) // TODO: unescape + return data, nil + + case "": + if v.Kind() != reflect.Int { + return nil, fmt.Errorf("cannot unmarshal into non-int %s", v.Type()) + } + body, etag, data := next(data) + if len(etag) == 0 { + return nil, fmt.Errorf("eof inside ") + } + if string(etag) != "" { + return nil, fmt.Errorf("expected but got %s", etag) + } + i, err := strconv.Atoi(string(body)) + if err != nil { + return nil, fmt.Errorf("non-integer in tag: %s", body) + } + v.SetInt(int64(i)) + return data, nil + } + return nil, fmt.Errorf("unexpected tag %s", tag) +} + +func skipValue(data []byte) (rest []byte, err error) { + n := 0 + for { + var tag []byte + _, tag, data = next(data) + if len(tag) == 0 { + return nil, fmt.Errorf("unexpected eof") + } + if tag[1] == '/' { + if n == 0 { + return nil, fmt.Errorf("unexpected closing tag") + } + n-- + if n == 0 { + break + } + } else { + n++ + } + } + return data, nil +} diff --git a/vendor/github.com/mattermost/rsc/plist/plist_test.go b/vendor/github.com/mattermost/rsc/plist/plist_test.go new file mode 100644 index 000000000..42f496c67 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/plist/plist_test.go @@ -0,0 +1,110 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package plist + +import ( + "reflect" + "testing" +) + +var thePlist = ` + + BucketUUID + C218A47D-DAFB-4476-9C67-597E556D7D8A + BucketName + rsc + ComputerUUID + E7859547-BB9C-41C0-871E-858A0526BAE7 + LocalPath + /Users/rsc + LocalMountPoint + /Users + IgnoredRelativePaths + + /.Trash + /go/pkg + /go1/pkg + /Library/Caches + + Excludes + + excludes + + + type + 2 + text + .unison. + + + + + +` + +var plistTests = []struct { + in string + out interface{} +}{ + { + thePlist, + &MyStruct{ + BucketUUID: "C218A47D-DAFB-4476-9C67-597E556D7D8A", + BucketName: "rsc", + ComputerUUID: "E7859547-BB9C-41C0-871E-858A0526BAE7", + LocalPath: "/Users/rsc", + LocalMountPoint: "/Users", + IgnoredRelativePaths: []string{ + "/.Trash", + "/go/pkg", + "/go1/pkg", + "/Library/Caches", + }, + Excludes: Exclude1{ + Excludes: []Exclude2{ + {Type: 2, + Text: ".unison.", + }, + }, + }, + }, + }, + { + thePlist, + &struct{}{}, + }, +} + +type MyStruct struct { + BucketUUID string + BucketName string + ComputerUUID string + LocalPath string + LocalMountPoint string + IgnoredRelativePaths []string + Excludes Exclude1 +} + +type Exclude1 struct { + Excludes []Exclude2 `plist:"excludes"` +} + +type Exclude2 struct { + Type int `plist:"type"` + Text string `plist:"text"` +} + +func TestUnmarshal(t *testing.T) { + for _, tt := range plistTests { + v := reflect.New(reflect.ValueOf(tt.out).Type().Elem()).Interface() + if err := Unmarshal([]byte(tt.in), v); err != nil { + t.Errorf("%s", err) + continue + } + if !reflect.DeepEqual(tt.out, v) { + t.Errorf("unmarshal not equal") + } + } +} diff --git a/vendor/github.com/mattermost/rsc/qr/coding/qr_test.go b/vendor/github.com/mattermost/rsc/qr/coding/qr_test.go new file mode 100644 index 000000000..b8199bb51 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/qr/coding/qr_test.go @@ -0,0 +1,133 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package coding + +import ( + "bytes" + "testing" + + "github.com/mattermost/rsc/gf256" + "github.com/mattermost/rsc/qr/libqrencode" +) + +func test(t *testing.T, v Version, l Level, text ...Encoding) bool { + s := "" + ty := libqrencode.EightBit + switch x := text[0].(type) { + case String: + s = string(x) + case Alpha: + s = string(x) + ty = libqrencode.Alphanumeric + case Num: + s = string(x) + ty = libqrencode.Numeric + } + key, err := libqrencode.Encode(libqrencode.Version(v), libqrencode.Level(l), ty, s) + if err != nil { + t.Errorf("libqrencode.Encode(%v, %v, %d, %#q): %v", v, l, ty, s, err) + return false + } + mask := (^key.Pixel[8][2]&1)<<2 | (key.Pixel[8][3]&1)<<1 | (^key.Pixel[8][4] & 1) + p, err := NewPlan(v, l, Mask(mask)) + if err != nil { + t.Errorf("NewPlan(%v, L, %d): %v", v, err, mask) + return false + } + if len(p.Pixel) != len(key.Pixel) { + t.Errorf("%v: NewPlan uses %dx%d, libqrencode uses %dx%d", v, len(p.Pixel), len(p.Pixel), len(key.Pixel), len(key.Pixel)) + return false + } + c, err := p.Encode(text...) + if err != nil { + t.Errorf("Encode: %v", err) + return false + } + badpix := 0 +Pixel: + for y, prow := range p.Pixel { + for x, pix := range prow { + pix &^= Black + if c.Black(x, y) { + pix |= Black + } + + keypix := key.Pixel[y][x] + want := Pixel(0) + switch { + case keypix&libqrencode.Finder != 0: + want = Position.Pixel() + case keypix&libqrencode.Alignment != 0: + want = Alignment.Pixel() + case keypix&libqrencode.Timing != 0: + want = Timing.Pixel() + case keypix&libqrencode.Format != 0: + want = Format.Pixel() + want |= OffsetPixel(pix.Offset()) // sic + want |= pix & Invert + case keypix&libqrencode.PVersion != 0: + want = PVersion.Pixel() + case keypix&libqrencode.DataECC != 0: + if pix.Role() == Check || pix.Role() == Extra { + want = pix.Role().Pixel() + } else { + want = Data.Pixel() + } + want |= OffsetPixel(pix.Offset()) + want |= pix & Invert + default: + want = Unused.Pixel() + } + if keypix&libqrencode.Black != 0 { + want |= Black + } + if pix != want { + t.Errorf("%v/%v: Pixel[%d][%d] = %v, want %v %#x", v, mask, y, x, pix, want, keypix) + if badpix++; badpix >= 100 { + t.Errorf("stopping after %d bad pixels", badpix) + break Pixel + } + } + } + } + return badpix == 0 +} + +var input = []Encoding{ + String("hello"), + Num("1"), + Num("12"), + Num("123"), + Alpha("AB"), + Alpha("ABC"), +} + +func TestVersion(t *testing.T) { + badvers := 0 +Version: + for v := Version(1); v <= 40; v++ { + for l := L; l <= H; l++ { + for _, in := range input { + if !test(t, v, l, in) { + if badvers++; badvers >= 10 { + t.Errorf("stopping after %d bad versions", badvers) + break Version + } + } + } + } + } +} + +func TestEncode(t *testing.T) { + data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} + check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55} + rs := gf256.NewRSEncoder(Field, len(check)) + out := make([]byte, len(check)) + rs.ECC(data, out) + if !bytes.Equal(out, check) { + t.Errorf("have %x want %x", out, check) + } +} diff --git a/vendor/github.com/mattermost/rsc/qr/libqrencode/Makefile b/vendor/github.com/mattermost/rsc/qr/libqrencode/Makefile new file mode 100644 index 000000000..4c9591462 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/qr/libqrencode/Makefile @@ -0,0 +1,4 @@ +include $(GOROOT)/src/Make.inc +TARG=rsc.googlecode.com/hg/qr/libqrencode +CGOFILES=qrencode.go +include $(GOROOT)/src/Make.pkg diff --git a/vendor/github.com/mattermost/rsc/qr/libqrencode/qrencode.go b/vendor/github.com/mattermost/rsc/qr/libqrencode/qrencode.go new file mode 100644 index 000000000..f4ce3ffb6 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/qr/libqrencode/qrencode.go @@ -0,0 +1,149 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package libqrencode wraps the C libqrencode library. +// The qr package (in this package's parent directory) +// does not use any C wrapping. This code is here only +// for use during that package's tests. +package libqrencode + +/* +#cgo LDFLAGS: -lqrencode +#include +*/ +import "C" + +import ( + "fmt" + "image" + "image/color" + "unsafe" +) + +type Version int + +type Mode int + +const ( + Numeric Mode = C.QR_MODE_NUM + Alphanumeric Mode = C.QR_MODE_AN + EightBit Mode = C.QR_MODE_8 +) + +type Level int + +const ( + L Level = C.QR_ECLEVEL_L + M Level = C.QR_ECLEVEL_M + Q Level = C.QR_ECLEVEL_Q + H Level = C.QR_ECLEVEL_H +) + +type Pixel int + +const ( + Black Pixel = 1 << iota + DataECC + Format + PVersion + Timing + Alignment + Finder + NonData +) + +type Code struct { + Version int + Width int + Pixel [][]Pixel + Scale int +} + +func (*Code) ColorModel() color.Model { + return color.RGBAModel +} + +func (c *Code) Bounds() image.Rectangle { + d := (c.Width + 8) * c.Scale + return image.Rect(0, 0, d, d) +} + +var ( + white color.Color = color.RGBA{0xFF, 0xFF, 0xFF, 0xFF} + black color.Color = color.RGBA{0x00, 0x00, 0x00, 0xFF} + blue color.Color = color.RGBA{0x00, 0x00, 0x80, 0xFF} + red color.Color = color.RGBA{0xFF, 0x40, 0x40, 0xFF} + yellow color.Color = color.RGBA{0xFF, 0xFF, 0x00, 0xFF} + gray color.Color = color.RGBA{0x80, 0x80, 0x80, 0xFF} + green color.Color = color.RGBA{0x22, 0x8B, 0x22, 0xFF} +) + +func (c *Code) At(x, y int) color.Color { + x = x/c.Scale - 4 + y = y/c.Scale - 4 + if 0 <= x && x < c.Width && 0 <= y && y < c.Width { + switch p := c.Pixel[y][x]; { + case p&Black == 0: + // nothing + case p&DataECC != 0: + return black + case p&Format != 0: + return blue + case p&PVersion != 0: + return red + case p&Timing != 0: + return yellow + case p&Alignment != 0: + return gray + case p&Finder != 0: + return green + } + } + return white +} + +type Chunk struct { + Mode Mode + Text string +} + +func Encode(version Version, level Level, mode Mode, text string) (*Code, error) { + return EncodeChunk(version, level, Chunk{mode, text}) +} + +func EncodeChunk(version Version, level Level, chunk ...Chunk) (*Code, error) { + qi, err := C.QRinput_new2(C.int(version), C.QRecLevel(level)) + if qi == nil { + return nil, fmt.Errorf("QRinput_new2: %v", err) + } + defer C.QRinput_free(qi) + for _, ch := range chunk { + data := []byte(ch.Text) + n, err := C.QRinput_append(qi, C.QRencodeMode(ch.Mode), C.int(len(data)), (*C.uchar)(&data[0])) + if n < 0 { + return nil, fmt.Errorf("QRinput_append %q: %v", data, err) + } + } + + qc, err := C.QRcode_encodeInput(qi) + if qc == nil { + return nil, fmt.Errorf("QRinput_encodeInput: %v", err) + } + + c := &Code{ + Version: int(qc.version), + Width: int(qc.width), + Scale: 16, + } + pix := make([]Pixel, c.Width*c.Width) + cdat := (*[1000 * 1000]byte)(unsafe.Pointer(qc.data))[:len(pix)] + for i := range pix { + pix[i] = Pixel(cdat[i]) + } + c.Pixel = make([][]Pixel, c.Width) + for i := range c.Pixel { + c.Pixel[i] = pix[i*c.Width : (i+1)*c.Width] + } + return c, nil +} diff --git a/vendor/github.com/mattermost/rsc/qr/png_test.go b/vendor/github.com/mattermost/rsc/qr/png_test.go new file mode 100644 index 000000000..27a622924 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/qr/png_test.go @@ -0,0 +1,73 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qr + +import ( + "bytes" + "image" + "image/color" + "image/png" + "io/ioutil" + "testing" +) + +func TestPNG(t *testing.T) { + c, err := Encode("hello, world", L) + if err != nil { + t.Fatal(err) + } + pngdat := c.PNG() + if true { + ioutil.WriteFile("x.png", pngdat, 0666) + } + m, err := png.Decode(bytes.NewBuffer(pngdat)) + if err != nil { + t.Fatal(err) + } + gm := m.(*image.Gray) + + scale := c.Scale + siz := c.Size + nbad := 0 + for y := 0; y < scale*(8+siz); y++ { + for x := 0; x < scale*(8+siz); x++ { + v := byte(255) + if c.Black(x/scale-4, y/scale-4) { + v = 0 + } + if gv := gm.At(x, y).(color.Gray).Y; gv != v { + t.Errorf("%d,%d = %d, want %d", x, y, gv, v) + if nbad++; nbad >= 20 { + t.Fatalf("too many bad pixels") + } + } + } + } +} + +func BenchmarkPNG(b *testing.B) { + c, err := Encode("0123456789012345678901234567890123456789", L) + if err != nil { + panic(err) + } + var bytes []byte + for i := 0; i < b.N; i++ { + bytes = c.PNG() + } + b.SetBytes(int64(len(bytes))) +} + +func BenchmarkImagePNG(b *testing.B) { + c, err := Encode("0123456789012345678901234567890123456789", L) + if err != nil { + panic(err) + } + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + png.Encode(&buf, c.Image()) + } + b.SetBytes(int64(buf.Len())) +} diff --git a/vendor/github.com/mattermost/rsc/qr/web/pic.go b/vendor/github.com/mattermost/rsc/qr/web/pic.go new file mode 100644 index 000000000..6baef94d2 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/qr/web/pic.go @@ -0,0 +1,506 @@ +package web + +import ( + "bytes" + "fmt" + "image" + "image/color" + "image/draw" + "image/png" + "net/http" + "strconv" + "strings" + + "code.google.com/p/freetype-go/freetype" + "github.com/mattermost/rsc/appfs/fs" + "github.com/mattermost/rsc/qr" + "github.com/mattermost/rsc/qr/coding" +) + +func makeImage(req *http.Request, caption, font string, pt, size, border, scale int, f func(x, y int) uint32) *image.RGBA { + d := (size + 2*border) * scale + csize := 0 + if caption != "" { + if pt == 0 { + pt = 11 + } + csize = pt * 2 + } + c := image.NewRGBA(image.Rect(0, 0, d, d+csize)) + + // white + u := &image.Uniform{C: color.White} + draw.Draw(c, c.Bounds(), u, image.ZP, draw.Src) + + for y := 0; y < size; y++ { + for x := 0; x < size; x++ { + r := image.Rect((x+border)*scale, (y+border)*scale, (x+border+1)*scale, (y+border+1)*scale) + rgba := f(x, y) + u.C = color.RGBA{byte(rgba >> 24), byte(rgba >> 16), byte(rgba >> 8), byte(rgba)} + draw.Draw(c, r, u, image.ZP, draw.Src) + } + } + + if csize != 0 { + if font == "" { + font = "data/luxisr.ttf" + } + ctxt := fs.NewContext(req) + dat, _, err := ctxt.Read(font) + if err != nil { + panic(err) + } + tfont, err := freetype.ParseFont(dat) + if err != nil { + panic(err) + } + ft := freetype.NewContext() + ft.SetDst(c) + ft.SetDPI(100) + ft.SetFont(tfont) + ft.SetFontSize(float64(pt)) + ft.SetSrc(image.NewUniform(color.Black)) + ft.SetClip(image.Rect(0, 0, 0, 0)) + wid, err := ft.DrawString(caption, freetype.Pt(0, 0)) + if err != nil { + panic(err) + } + p := freetype.Pt(d, d+3*pt/2) + p.X -= wid.X + p.X /= 2 + ft.SetClip(c.Bounds()) + ft.DrawString(caption, p) + } + + return c +} + +func makeFrame(req *http.Request, font string, pt, vers, l, scale, dots int) image.Image { + lev := coding.Level(l) + p, err := coding.NewPlan(coding.Version(vers), lev, 0) + if err != nil { + panic(err) + } + + nd := p.DataBytes / p.Blocks + nc := p.CheckBytes / p.Blocks + extra := p.DataBytes - nd*p.Blocks + + cap := fmt.Sprintf("QR v%d, %s", vers, lev) + if dots > 0 { + cap = fmt.Sprintf("QR v%d order, from bottom right", vers) + } + m := makeImage(req, cap, font, pt, len(p.Pixel), 0, scale, func(x, y int) uint32 { + pix := p.Pixel[y][x] + switch pix.Role() { + case coding.Data: + if dots > 0 { + return 0xffffffff + } + off := int(pix.Offset() / 8) + nd := nd + var i int + for i = 0; i < p.Blocks; i++ { + if i == extra { + nd++ + } + if off < nd { + break + } + off -= nd + } + return blockColors[i%len(blockColors)] + case coding.Check: + if dots > 0 { + return 0xffffffff + } + i := (int(pix.Offset()/8) - p.DataBytes) / nc + return dark(blockColors[i%len(blockColors)]) + } + if pix&coding.Black != 0 { + return 0x000000ff + } + return 0xffffffff + }) + + if dots > 0 { + b := m.Bounds() + for y := 0; y <= len(p.Pixel); y++ { + for x := 0; x < b.Dx(); x++ { + m.SetRGBA(x, y*scale-(y/len(p.Pixel)), color.RGBA{127, 127, 127, 255}) + } + } + for x := 0; x <= len(p.Pixel); x++ { + for y := 0; y < b.Dx(); y++ { + m.SetRGBA(x*scale-(x/len(p.Pixel)), y, color.RGBA{127, 127, 127, 255}) + } + } + order := make([]image.Point, (p.DataBytes+p.CheckBytes)*8+1) + for y, row := range p.Pixel { + for x, pix := range row { + if r := pix.Role(); r != coding.Data && r != coding.Check { + continue + } + // draw.Draw(m, m.Bounds().Add(image.Pt(x*scale, y*scale)), dot, image.ZP, draw.Over) + order[pix.Offset()] = image.Point{x*scale + scale/2, y*scale + scale/2} + } + } + + for mode := 0; mode < 2; mode++ { + for i, p := range order { + q := order[i+1] + if q.X == 0 { + break + } + line(m, p, q, mode) + } + } + } + return m +} + +func line(m *image.RGBA, p, q image.Point, mode int) { + x := 0 + y := 0 + dx := q.X - p.X + dy := q.Y - p.Y + xsign := +1 + ysign := +1 + if dx < 0 { + xsign = -1 + dx = -dx + } + if dy < 0 { + ysign = -1 + dy = -dy + } + pt := func() { + switch mode { + case 0: + for dx := -2; dx <= 2; dx++ { + for dy := -2; dy <= 2; dy++ { + if dy*dx <= -4 || dy*dx >= 4 { + continue + } + m.SetRGBA(p.X+x*xsign+dx, p.Y+y*ysign+dy, color.RGBA{255, 192, 192, 255}) + } + } + + case 1: + m.SetRGBA(p.X+x*xsign, p.Y+y*ysign, color.RGBA{128, 0, 0, 255}) + } + } + if dx > dy { + for x < dx || y < dy { + pt() + x++ + if float64(x)*float64(dy)/float64(dx)-float64(y) > 0.5 { + y++ + } + } + } else { + for x < dx || y < dy { + pt() + y++ + if float64(y)*float64(dx)/float64(dy)-float64(x) > 0.5 { + x++ + } + } + } + pt() +} + +func pngEncode(c image.Image) []byte { + var b bytes.Buffer + png.Encode(&b, c) + return b.Bytes() +} + +// Frame handles a request for a single QR frame. +func Frame(w http.ResponseWriter, req *http.Request) { + arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } + v := arg("v") + scale := arg("scale") + if scale == 0 { + scale = 8 + } + + w.Header().Set("Cache-Control", "public, max-age=3600") + w.Write(pngEncode(makeFrame(req, req.FormValue("font"), arg("pt"), v, arg("l"), scale, arg("dots")))) +} + +// Frames handles a request for multiple QR frames. +func Frames(w http.ResponseWriter, req *http.Request) { + vs := strings.Split(req.FormValue("v"), ",") + + arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } + scale := arg("scale") + if scale == 0 { + scale = 8 + } + font := req.FormValue("font") + pt := arg("pt") + dots := arg("dots") + + var images []image.Image + l := arg("l") + for _, v := range vs { + l := l + if i := strings.Index(v, "."); i >= 0 { + l, _ = strconv.Atoi(v[i+1:]) + v = v[:i] + } + vv, _ := strconv.Atoi(v) + images = append(images, makeFrame(req, font, pt, vv, l, scale, dots)) + } + + b := images[len(images)-1].Bounds() + + dx := arg("dx") + if dx == 0 { + dx = b.Dx() + } + x, y := 0, 0 + xmax := 0 + sep := arg("sep") + if sep == 0 { + sep = 10 + } + var points []image.Point + for i, m := range images { + if x > 0 { + x += sep + } + if x > 0 && x+m.Bounds().Dx() > dx { + y += sep + images[i-1].Bounds().Dy() + x = 0 + } + points = append(points, image.Point{x, y}) + x += m.Bounds().Dx() + if x > xmax { + xmax = x + } + + } + + c := image.NewRGBA(image.Rect(0, 0, xmax, y+b.Dy())) + for i, m := range images { + draw.Draw(c, c.Bounds().Add(points[i]), m, image.ZP, draw.Src) + } + + w.Header().Set("Cache-Control", "public, max-age=3600") + w.Write(pngEncode(c)) +} + +// Mask handles a request for a single QR mask. +func Mask(w http.ResponseWriter, req *http.Request) { + arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } + v := arg("v") + m := arg("m") + scale := arg("scale") + if scale == 0 { + scale = 8 + } + + w.Header().Set("Cache-Control", "public, max-age=3600") + w.Write(pngEncode(makeMask(req, req.FormValue("font"), arg("pt"), v, m, scale))) +} + +// Masks handles a request for multiple QR masks. +func Masks(w http.ResponseWriter, req *http.Request) { + arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } + v := arg("v") + scale := arg("scale") + if scale == 0 { + scale = 8 + } + font := req.FormValue("font") + pt := arg("pt") + var mm []image.Image + for m := 0; m < 8; m++ { + mm = append(mm, makeMask(req, font, pt, v, m, scale)) + } + dx := mm[0].Bounds().Dx() + dy := mm[0].Bounds().Dy() + + sep := arg("sep") + if sep == 0 { + sep = 10 + } + c := image.NewRGBA(image.Rect(0, 0, (dx+sep)*4-sep, (dy+sep)*2-sep)) + for m := 0; m < 8; m++ { + x := (m % 4) * (dx + sep) + y := (m / 4) * (dy + sep) + draw.Draw(c, c.Bounds().Add(image.Pt(x, y)), mm[m], image.ZP, draw.Src) + } + + w.Header().Set("Cache-Control", "public, max-age=3600") + w.Write(pngEncode(c)) +} + +var maskName = []string{ + "(x+y) % 2", + "y % 2", + "x % 3", + "(x+y) % 3", + "(y/2 + x/3) % 2", + "xy%2 + xy%3", + "(xy%2 + xy%3) % 2", + "(xy%3 + (x+y)%2) % 2", +} + +func makeMask(req *http.Request, font string, pt int, vers, mask, scale int) image.Image { + p, err := coding.NewPlan(coding.Version(vers), coding.L, coding.Mask(mask)) + if err != nil { + panic(err) + } + m := makeImage(req, maskName[mask], font, pt, len(p.Pixel), 0, scale, func(x, y int) uint32 { + pix := p.Pixel[y][x] + switch pix.Role() { + case coding.Data, coding.Check: + if pix&coding.Invert != 0 { + return 0x000000ff + } + } + return 0xffffffff + }) + return m +} + +var blockColors = []uint32{ + 0x7777ffff, + 0xffff77ff, + 0xff7777ff, + 0x77ffffff, + 0x1e90ffff, + 0xffffe0ff, + 0x8b6969ff, + 0x77ff77ff, + 0x9b30ffff, + 0x00bfffff, + 0x90e890ff, + 0xfff68fff, + 0xffec8bff, + 0xffa07aff, + 0xffa54fff, + 0xeee8aaff, + 0x98fb98ff, + 0xbfbfbfff, + 0x54ff9fff, + 0xffaeb9ff, + 0xb23aeeff, + 0xbbffffff, + 0x7fffd4ff, + 0xff7a7aff, + 0x00007fff, +} + +func dark(x uint32) uint32 { + r, g, b, a := byte(x>>24), byte(x>>16), byte(x>>8), byte(x) + r = r/2 + r/4 + g = g/2 + g/4 + b = b/2 + b/4 + return uint32(r)<<24 | uint32(g)<<16 | uint32(b)<<8 | uint32(a) +} + +func clamp(x int) byte { + if x < 0 { + return 0 + } + if x > 255 { + return 255 + } + return byte(x) +} + +func max(x, y int) int { + if x > y { + return x + } + return y +} + +// Arrow handles a request for an arrow pointing in a given direction. +func Arrow(w http.ResponseWriter, req *http.Request) { + arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } + dir := arg("dir") + size := arg("size") + if size == 0 { + size = 50 + } + del := size / 10 + + m := image.NewRGBA(image.Rect(0, 0, size, size)) + + if dir == 4 { + draw.Draw(m, m.Bounds(), image.Black, image.ZP, draw.Src) + draw.Draw(m, image.Rect(5, 5, size-5, size-5), image.White, image.ZP, draw.Src) + } + + pt := func(x, y int, c color.RGBA) { + switch dir { + case 0: + m.SetRGBA(x, y, c) + case 1: + m.SetRGBA(y, size-1-x, c) + case 2: + m.SetRGBA(size-1-x, size-1-y, c) + case 3: + m.SetRGBA(size-1-y, x, c) + } + } + + for y := 0; y < size/2; y++ { + for x := 0; x < del && x < y; x++ { + pt(x, y, color.RGBA{0, 0, 0, 255}) + } + for x := del; x < y-del; x++ { + pt(x, y, color.RGBA{128, 128, 255, 255}) + } + for x := max(y-del, 0); x <= y; x++ { + pt(x, y, color.RGBA{0, 0, 0, 255}) + } + } + for y := size / 2; y < size; y++ { + for x := 0; x < del && x < size-1-y; x++ { + pt(x, y, color.RGBA{0, 0, 0, 255}) + } + for x := del; x < size-1-y-del; x++ { + pt(x, y, color.RGBA{128, 128, 192, 255}) + } + for x := max(size-1-y-del, 0); x <= size-1-y; x++ { + pt(x, y, color.RGBA{0, 0, 0, 255}) + } + } + + w.Header().Set("Cache-Control", "public, max-age=3600") + w.Write(pngEncode(m)) +} + +// Encode encodes a string using the given version, level, and mask. +func Encode(w http.ResponseWriter, req *http.Request) { + val := func(s string) int { + v, _ := strconv.Atoi(req.FormValue(s)) + return v + } + + l := coding.Level(val("l")) + v := coding.Version(val("v")) + enc := coding.String(req.FormValue("t")) + m := coding.Mask(val("m")) + + p, err := coding.NewPlan(v, l, m) + if err != nil { + panic(err) + } + cc, err := p.Encode(enc) + if err != nil { + panic(err) + } + + c := &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: 8} + w.Header().Set("Content-Type", "image/png") + w.Header().Set("Cache-Control", "public, max-age=3600") + w.Write(c.PNG()) +} + diff --git a/vendor/github.com/mattermost/rsc/qr/web/play.go b/vendor/github.com/mattermost/rsc/qr/web/play.go new file mode 100644 index 000000000..120f50b81 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/qr/web/play.go @@ -0,0 +1,1118 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +QR data layout + +qr/ + upload/ + id.png + id.fix + flag/ + id + +*/ +// TODO: Random seed taken from GET for caching, repeatability. +// TODO: Flag for abuse button + some kind of dashboard. +// TODO: +1 button on web page? permalink? +// TODO: Flag for abuse button on permalinks too? +// TODO: Make the page prettier. +// TODO: Cache headers. + +package web + +import ( + "bytes" + "crypto/md5" + "encoding/base64" + "encoding/json" + "fmt" + "html/template" + "image" + "image/color" + _ "image/gif" + _ "image/jpeg" + "image/png" + "io" + "math/rand" + "net/http" + "net/url" + "os" + "sort" + "strconv" + "strings" + "time" + + "github.com/mattermost/rsc/appfs/fs" + "github.com/mattermost/rsc/gf256" + "github.com/mattermost/rsc/qr" + "github.com/mattermost/rsc/qr/coding" + "github.com/mattermost/rsc/qr/web/resize" +) + +func runTemplate(c *fs.Context, w http.ResponseWriter, name string, data interface{}) { + t := template.New("main") + + main, _, err := c.Read(name) + if err != nil { + panic(err) + } + style, _, _ := c.Read("style.html") + main = append(main, style...) + _, err = t.Parse(string(main)) + if err != nil { + panic(err) + } + + var buf bytes.Buffer + if err := t.Execute(&buf, &data); err != nil { + panic(err) + } + w.Write(buf.Bytes()) +} + +func isImgName(s string) bool { + if len(s) != 32 { + return false + } + for i := 0; i < len(s); i++ { + if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' { + continue + } + return false + } + return true +} + +func isTagName(s string) bool { + if len(s) != 16 { + return false + } + for i := 0; i < len(s); i++ { + if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' { + continue + } + return false + } + return true +} + +// Draw is the handler for drawing a QR code. +func Draw(w http.ResponseWriter, req *http.Request) { + ctxt := fs.NewContext(req) + + url := req.FormValue("url") + if url == "" { + url = "http://swtch.com/qr" + } + if req.FormValue("upload") == "1" { + upload(w, req, url) + return + } + + t0 := time.Now() + img := req.FormValue("i") + if !isImgName(img) { + img = "pjw" + } + if req.FormValue("show") == "png" { + i := loadSize(ctxt, img, 48) + var buf bytes.Buffer + png.Encode(&buf, i) + w.Write(buf.Bytes()) + return + } + if req.FormValue("flag") == "1" { + flag(w, req, img, ctxt) + return + } + if req.FormValue("x") == "" { + var data = struct { + Name string + URL string + }{ + Name: img, + URL: url, + } + runTemplate(ctxt, w, "qr/main.html", &data) + return + } + + arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x } + targ := makeTarg(ctxt, img, 17+4*arg("v")+arg("z")) + + m := &Image{ + Name: img, + Dx: arg("x"), + Dy: arg("y"), + URL: req.FormValue("u"), + Version: arg("v"), + Mask: arg("m"), + RandControl: arg("r") > 0, + Dither: arg("i") > 0, + OnlyDataBits: arg("d") > 0, + SaveControl: arg("c") > 0, + Scale: arg("scale"), + Target: targ, + Seed: int64(arg("s")), + Rotation: arg("o"), + Size: arg("z"), + } + if m.Version > 8 { + m.Version = 8 + } + + if m.Scale == 0 { + if arg("l") > 1 { + m.Scale = 8 + } else { + m.Scale = 4 + } + } + if m.Version >= 12 && m.Scale >= 4 { + m.Scale /= 2 + } + + if arg("l") == 1 { + data, err := json.Marshal(m) + if err != nil { + panic(err) + } + h := md5.New() + h.Write(data) + tag := fmt.Sprintf("%x", h.Sum(nil))[:16] + if err := ctxt.Write("qrsave/"+tag, data); err != nil { + panic(err) + } + http.Redirect(w, req, "/qr/show/" + tag, http.StatusTemporaryRedirect) + return + } + + if err := m.Encode(req); err != nil { + fmt.Fprintf(w, "%s\n", err) + return + } + + var dat []byte + switch { + case m.SaveControl: + dat = m.Control + default: + dat = m.Code.PNG() + } + + if arg("l") > 0 { + w.Header().Set("Content-Type", "image/png") + w.Write(dat) + return + } + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprint(w, "

") + fmt.Fprintf(w, "
\n", m.Link()) + fmt.Fprintf(w, "
\n") + fmt.Fprintf(w, "
%v
\n", time.Now().Sub(t0)) +} + +func (m *Image) Small() bool { + return 8*(17+4*int(m.Version)) < 512 +} + +func (m *Image) Link() string { + s := fmt.Sprint + b := func(v bool) string { + if v { + return "1" + } + return "0" + } + val := url.Values{ + "i": {m.Name}, + "x": {s(m.Dx)}, + "y": {s(m.Dy)}, + "z": {s(m.Size)}, + "u": {m.URL}, + "v": {s(m.Version)}, + "m": {s(m.Mask)}, + "r": {b(m.RandControl)}, + "t": {b(m.Dither)}, + "d": {b(m.OnlyDataBits)}, + "c": {b(m.SaveControl)}, + "s": {s(m.Seed)}, + } + return "/qr/draw?" + val.Encode() +} + +// Show is the handler for showing a stored QR code. +func Show(w http.ResponseWriter, req *http.Request) { + ctxt := fs.NewContext(req) + tag := req.URL.Path[len("/qr/show/"):] + png := strings.HasSuffix(tag, ".png") + if png { + tag = tag[:len(tag)-len(".png")] + } + if !isTagName(tag) { + fmt.Fprintf(w, "Sorry, QR code not found\n") + return + } + if req.FormValue("flag") == "1" { + flag(w, req, tag, ctxt) + return + } + data, _, err := ctxt.Read("qrsave/" + tag) + if err != nil { + fmt.Fprintf(w, "Sorry, QR code not found.\n") + return + } + + var m Image + if err := json.Unmarshal(data, &m); err != nil { + panic(err) + } + m.Tag = tag + + switch req.FormValue("size") { + case "big": + m.Scale *= 2 + case "small": + m.Scale /= 2 + } + + if png { + if err := m.Encode(req); err != nil { + panic(err) + return + } + w.Header().Set("Cache-Control", "public, max-age=3600") + w.Write(m.Code.PNG()) + return + } + + w.Header().Set("Cache-Control", "public, max-age=300") + runTemplate(ctxt, w, "qr/permalink.html", &m) +} + +func upload(w http.ResponseWriter, req *http.Request, link string) { + // Upload of a new image. + // Copied from Moustachio demo. + f, _, err := req.FormFile("image") + if err != nil { + fmt.Fprintf(w, "You need to select an image to upload.\n") + return + } + defer f.Close() + + i, _, err := image.Decode(f) + if err != nil { + panic(err) + } + + // Convert image to 128x128 gray+alpha. + b := i.Bounds() + const max = 128 + // If it's gigantic, it's more efficient to downsample first + // and then resize; resizing will smooth out the roughness. + var i1 *image.RGBA + if b.Dx() > 4*max || b.Dy() > 4*max { + w, h := 2*max, 2*max + if b.Dx() > b.Dy() { + h = b.Dy() * h / b.Dx() + } else { + w = b.Dx() * w / b.Dy() + } + i1 = resize.Resample(i, b, w, h) + } else { + // "Resample" to same size, just to convert to RGBA. + i1 = resize.Resample(i, b, b.Dx(), b.Dy()) + } + b = i1.Bounds() + + // Encode to PNG. + dx, dy := 128, 128 + if b.Dx() > b.Dy() { + dy = b.Dy() * dx / b.Dx() + } else { + dx = b.Dx() * dy / b.Dy() + } + i128 := resize.ResizeRGBA(i1, i1.Bounds(), dx, dy) + + var buf bytes.Buffer + if err := png.Encode(&buf, i128); err != nil { + panic(err) + } + + h := md5.New() + h.Write(buf.Bytes()) + tag := fmt.Sprintf("%x", h.Sum(nil))[:32] + + ctxt := fs.NewContext(req) + if err := ctxt.Write("qr/upload/"+tag+".png", buf.Bytes()); err != nil { + panic(err) + } + + // Redirect with new image tag. + // Redirect to draw with new image tag. + http.Redirect(w, req, req.URL.Path+"?"+url.Values{"i": {tag}, "url": {link}}.Encode(), 302) +} + +func flag(w http.ResponseWriter, req *http.Request, img string, ctxt *fs.Context) { + if !isImgName(img) && !isTagName(img) { + fmt.Fprintf(w, "Invalid image.\n") + return + } + data, _, _ := ctxt.Read("qr/flag/" + img) + data = append(data, '!') + ctxt.Write("qr/flag/" + img, data) + + fmt.Fprintf(w, "Thank you. The image has been reported.\n") +} + +func loadSize(ctxt *fs.Context, name string, max int) *image.RGBA { + data, _, err := ctxt.Read("qr/upload/" + name + ".png") + if err != nil { + panic(err) + } + i, _, err := image.Decode(bytes.NewBuffer(data)) + if err != nil { + panic(err) + } + b := i.Bounds() + dx, dy := max, max + if b.Dx() > b.Dy() { + dy = b.Dy() * dx / b.Dx() + } else { + dx = b.Dx() * dy / b.Dy() + } + var irgba *image.RGBA + switch i := i.(type) { + case *image.RGBA: + irgba = resize.ResizeRGBA(i, i.Bounds(), dx, dy) + case *image.NRGBA: + irgba = resize.ResizeNRGBA(i, i.Bounds(), dx, dy) + } + return irgba +} + +func makeTarg(ctxt *fs.Context, name string, max int) [][]int { + i := loadSize(ctxt, name, max) + b := i.Bounds() + dx, dy := b.Dx(), b.Dy() + targ := make([][]int, dy) + arr := make([]int, dx*dy) + for y := 0; y < dy; y++ { + targ[y], arr = arr[:dx], arr[dx:] + row := targ[y] + for x := 0; x < dx; x++ { + p := i.Pix[y*i.Stride+4*x:] + r, g, b, a := p[0], p[1], p[2], p[3] + if a == 0 { + row[x] = -1 + } else { + row[x] = int((299*uint32(r) + 587*uint32(g) + 114*uint32(b) + 500) / 1000) + } + } + } + return targ +} + +type Image struct { + Name string + Target [][]int + Dx int + Dy int + URL string + Tag string + Version int + Mask int + Scale int + Rotation int + Size int + + // RandControl says to pick the pixels randomly. + RandControl bool + Seed int64 + + // Dither says to dither instead of using threshold pixel layout. + Dither bool + + // OnlyDataBits says to use only data bits, not check bits. + OnlyDataBits bool + + // Code is the final QR code. + Code *qr.Code + + // Control is a PNG showing the pixels that we controlled. + // Pixels we don't control are grayed out. + SaveControl bool + Control []byte +} + +type Pixinfo struct { + X int + Y int + Pix coding.Pixel + Targ byte + DTarg int + Contrast int + HardZero bool + Block *BitBlock + Bit uint +} + +type Pixorder struct { + Off int + Priority int +} + +type byPriority []Pixorder + +func (x byPriority) Len() int { return len(x) } +func (x byPriority) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byPriority) Less(i, j int) bool { return x[i].Priority > x[j].Priority } + +func (m *Image) target(x, y int) (targ byte, contrast int) { + tx := x + m.Dx + ty := y + m.Dy + if ty < 0 || ty >= len(m.Target) || tx < 0 || tx >= len(m.Target[ty]) { + return 255, -1 + } + + v0 := m.Target[ty][tx] + if v0 < 0 { + return 255, -1 + } + targ = byte(v0) + + n := 0 + sum := 0 + sumsq := 0 + const del = 5 + for dy := -del; dy <= del; dy++ { + for dx := -del; dx <= del; dx++ { + if 0 <= ty+dy && ty+dy < len(m.Target) && 0 <= tx+dx && tx+dx < len(m.Target[ty+dy]) { + v := m.Target[ty+dy][tx+dx] + sum += v + sumsq += v * v + n++ + } + } + } + + avg := sum / n + contrast = sumsq/n - avg*avg + return +} + +func (m *Image) rotate(p *coding.Plan, rot int) { + if rot == 0 { + return + } + + N := len(p.Pixel) + pix := make([][]coding.Pixel, N) + apix := make([]coding.Pixel, N*N) + for i := range pix { + pix[i], apix = apix[:N], apix[N:] + } + + switch rot { + case 0: + // ok + case 1: + for y := 0; y < N; y++ { + for x := 0; x < N; x++ { + pix[y][x] = p.Pixel[x][N-1-y] + } + } + case 2: + for y := 0; y < N; y++ { + for x := 0; x < N; x++ { + pix[y][x] = p.Pixel[N-1-y][N-1-x] + } + } + case 3: + for y := 0; y < N; y++ { + for x := 0; x < N; x++ { + pix[y][x] = p.Pixel[N-1-x][y] + } + } + } + + p.Pixel = pix +} + +func (m *Image) Encode(req *http.Request) error { + p, err := coding.NewPlan(coding.Version(m.Version), coding.L, coding.Mask(m.Mask)) + if err != nil { + return err + } + + m.rotate(p, m.Rotation) + + rand := rand.New(rand.NewSource(m.Seed)) + + // QR parameters. + nd := p.DataBytes / p.Blocks + nc := p.CheckBytes / p.Blocks + extra := p.DataBytes - nd*p.Blocks + rs := gf256.NewRSEncoder(coding.Field, nc) + + // Build information about pixels, indexed by data/check bit number. + pixByOff := make([]Pixinfo, (p.DataBytes+p.CheckBytes)*8) + expect := make([][]bool, len(p.Pixel)) + for y, row := range p.Pixel { + expect[y] = make([]bool, len(row)) + for x, pix := range row { + targ, contrast := m.target(x, y) + if m.RandControl && contrast >= 0 { + contrast = rand.Intn(128) + 64*((x+y)%2) + 64*((x+y)%3%2) + } + expect[y][x] = pix&coding.Black != 0 + if r := pix.Role(); r == coding.Data || r == coding.Check { + pixByOff[pix.Offset()] = Pixinfo{X: x, Y: y, Pix: pix, Targ: targ, Contrast: contrast} + } + } + } + +Again: + // Count fixed initial data bits, prepare template URL. + url := m.URL + "#" + var b coding.Bits + coding.String(url).Encode(&b, p.Version) + coding.Num("").Encode(&b, p.Version) + bbit := b.Bits() + dbit := p.DataBytes*8 - bbit + if dbit < 0 { + return fmt.Errorf("cannot encode URL into available bits") + } + num := make([]byte, dbit/10*3) + for i := range num { + num[i] = '0' + } + b.Pad(dbit) + b.Reset() + coding.String(url).Encode(&b, p.Version) + coding.Num(num).Encode(&b, p.Version) + b.AddCheckBytes(p.Version, p.Level) + data := b.Bytes() + + doff := 0 // data offset + coff := 0 // checksum offset + mbit := bbit + dbit/10*10 + + // Choose pixels. + bitblocks := make([]*BitBlock, p.Blocks) + for blocknum := 0; blocknum < p.Blocks; blocknum++ { + if blocknum == p.Blocks-extra { + nd++ + } + + bdata := data[doff/8 : doff/8+nd] + cdata := data[p.DataBytes+coff/8 : p.DataBytes+coff/8+nc] + bb := newBlock(nd, nc, rs, bdata, cdata) + bitblocks[blocknum] = bb + + // Determine which bits in this block we can try to edit. + lo, hi := 0, nd*8 + if lo < bbit-doff { + lo = bbit - doff + if lo > hi { + lo = hi + } + } + if hi > mbit-doff { + hi = mbit - doff + if hi < lo { + hi = lo + } + } + + // Preserve [0, lo) and [hi, nd*8). + for i := 0; i < lo; i++ { + if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) { + return fmt.Errorf("cannot preserve required bits") + } + } + for i := hi; i < nd*8; i++ { + if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) { + return fmt.Errorf("cannot preserve required bits") + } + } + + // Can edit [lo, hi) and checksum bits to hit target. + // Determine which ones to try first. + order := make([]Pixorder, (hi-lo)+nc*8) + for i := lo; i < hi; i++ { + order[i-lo].Off = doff + i + } + for i := 0; i < nc*8; i++ { + order[hi-lo+i].Off = p.DataBytes*8 + coff + i + } + if m.OnlyDataBits { + order = order[:hi-lo] + } + for i := range order { + po := &order[i] + po.Priority = pixByOff[po.Off].Contrast<<8 | rand.Intn(256) + } + sort.Sort(byPriority(order)) + + const mark = false + for i := range order { + po := &order[i] + pinfo := &pixByOff[po.Off] + bval := pinfo.Targ + if bval < 128 { + bval = 1 + } else { + bval = 0 + } + pix := pinfo.Pix + if pix&coding.Invert != 0 { + bval ^= 1 + } + if pinfo.HardZero { + bval = 0 + } + + var bi int + if pix.Role() == coding.Data { + bi = po.Off - doff + } else { + bi = po.Off - p.DataBytes*8 - coff + nd*8 + } + if bb.canSet(uint(bi), bval) { + pinfo.Block = bb + pinfo.Bit = uint(bi) + if mark { + p.Pixel[pinfo.Y][pinfo.X] = coding.Black + } + } else { + if pinfo.HardZero { + panic("hard zero") + } + if mark { + p.Pixel[pinfo.Y][pinfo.X] = 0 + } + } + } + bb.copyOut() + + const cheat = false + for i := 0; i < nd*8; i++ { + pinfo := &pixByOff[doff+i] + pix := p.Pixel[pinfo.Y][pinfo.X] + if bb.B[i/8]&(1<= 128 { + // want white + pval = 0 + v = 255 + } + + bval := pval // bit value + if pix&coding.Invert != 0 { + bval ^= 1 + } + if pinfo.HardZero && bval != 0 { + bval ^= 1 + pval ^= 1 + v ^= 255 + } + + // Set pixel value as we want it. + pinfo.Block.reset(pinfo.Bit, bval) + + _, _ = x, y + + err := targ - v + if x+1 < len(row) { + addDither(pixByOff, row[x+1], err*7/16) + } + if false && y+1 < len(p.Pixel) { + if x > 0 { + addDither(pixByOff, p.Pixel[y+1][x-1], err*3/16) + } + addDither(pixByOff, p.Pixel[y+1][x], err*5/16) + if x+1 < len(row) { + addDither(pixByOff, p.Pixel[y+1][x+1], err*1/16) + } + } + } + } + + for _, bb := range bitblocks { + bb.copyOut() + } + } + + noops := 0 + // Copy numbers back out. + for i := 0; i < dbit/10; i++ { + // Pull out 10 bits. + v := 0 + for j := 0; j < 10; j++ { + bi := uint(bbit + 10*i + j) + v <<= 1 + v |= int((data[bi/8] >> (7 - bi&7)) & 1) + } + // Turn into 3 digits. + if v >= 1000 { + // Oops - too many 1 bits. + // We know the 512, 256, 128, 64, 32 bits are all set. + // Pick one at random to clear. This will break some + // checksum bits, but so be it. + println("oops", i, v) + pinfo := &pixByOff[bbit+10*i+3] // TODO random + pinfo.Contrast = 1e9 >> 8 + pinfo.HardZero = true + noops++ + } + num[i*3+0] = byte(v/100 + '0') + num[i*3+1] = byte(v/10%10 + '0') + num[i*3+2] = byte(v%10 + '0') + } + if noops > 0 { + goto Again + } + + var b1 coding.Bits + coding.String(url).Encode(&b1, p.Version) + coding.Num(num).Encode(&b1, p.Version) + b1.AddCheckBytes(p.Version, p.Level) + if !bytes.Equal(b.Bytes(), b1.Bytes()) { + fmt.Printf("mismatch\n%d %x\n%d %x\n", len(b.Bytes()), b.Bytes(), len(b1.Bytes()), b1.Bytes()) + panic("byte mismatch") + } + + cc, err := p.Encode(coding.String(url), coding.Num(num)) + if err != nil { + return err + } + + if !m.Dither { + for y, row := range expect { + for x, pix := range row { + if cc.Black(x, y) != pix { + println("mismatch", x, y, p.Pixel[y][x].String()) + } + } + } + } + + m.Code = &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: m.Scale} + + if m.SaveControl { + m.Control = pngEncode(makeImage(req, "", "", 0, cc.Size, 4, m.Scale, func(x, y int) (rgba uint32) { + pix := p.Pixel[y][x] + if pix.Role() == coding.Data || pix.Role() == coding.Check { + pinfo := &pixByOff[pix.Offset()] + if pinfo.Block != nil { + if cc.Black(x, y) { + return 0x000000ff + } + return 0xffffffff + } + } + if cc.Black(x, y) { + return 0x3f3f3fff + } + return 0xbfbfbfff + })) + } + + return nil +} + +func addDither(pixByOff []Pixinfo, pix coding.Pixel, err int) { + if pix.Role() != coding.Data && pix.Role() != coding.Check { + return + } + pinfo := &pixByOff[pix.Offset()] + println("add", pinfo.X, pinfo.Y, pinfo.DTarg, err) + pinfo.DTarg += err +} + +func readTarget(name string) ([][]int, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + m, err := png.Decode(f) + if err != nil { + return nil, fmt.Errorf("decode %s: %v", name, err) + } + rect := m.Bounds() + target := make([][]int, rect.Dy()) + for i := range target { + target[i] = make([]int, rect.Dx()) + } + for y, row := range target { + for x := range row { + a := int(color.RGBAModel.Convert(m.At(x, y)).(color.RGBA).A) + t := int(color.GrayModel.Convert(m.At(x, y)).(color.Gray).Y) + if a == 0 { + t = -1 + } + row[x] = t + } + } + return target, nil +} + +type BitBlock struct { + DataBytes int + CheckBytes int + B []byte + M [][]byte + Tmp []byte + RS *gf256.RSEncoder + bdata []byte + cdata []byte +} + +func newBlock(nd, nc int, rs *gf256.RSEncoder, dat, cdata []byte) *BitBlock { + b := &BitBlock{ + DataBytes: nd, + CheckBytes: nc, + B: make([]byte, nd+nc), + Tmp: make([]byte, nc), + RS: rs, + bdata: dat, + cdata: cdata, + } + copy(b.B, dat) + rs.ECC(b.B[:nd], b.B[nd:]) + b.check() + if !bytes.Equal(b.Tmp, cdata) { + panic("cdata") + } + + b.M = make([][]byte, nd*8) + for i := range b.M { + row := make([]byte, nd+nc) + b.M[i] = row + for j := range row { + row[j] = 0 + } + row[i/8] = 1 << (7 - uint(i%8)) + rs.ECC(row[:nd], row[nd:]) + } + return b +} + +func (b *BitBlock) check() { + b.RS.ECC(b.B[:b.DataBytes], b.Tmp) + if !bytes.Equal(b.B[b.DataBytes:], b.Tmp) { + fmt.Printf("ecc mismatch\n%x\n%x\n", b.B[b.DataBytes:], b.Tmp) + panic("mismatch") + } +} + +func (b *BitBlock) reset(bi uint, bval byte) { + if (b.B[bi/8]>>(7-bi&7))&1 == bval { + // already has desired bit + return + } + // rows that have already been set + m := b.M[len(b.M):cap(b.M)] + for _, row := range m { + if row[bi/8]&(1<<(7-bi&7)) != 0 { + // Found it. + for j, v := range row { + b.B[j] ^= v + } + return + } + } + panic("reset of unset bit") +} + +func (b *BitBlock) canSet(bi uint, bval byte) bool { + found := false + m := b.M + for j, row := range m { + if row[bi/8]&(1<<(7-bi&7)) == 0 { + continue + } + if !found { + found = true + if j != 0 { + m[0], m[j] = m[j], m[0] + } + continue + } + for k := range row { + row[k] ^= m[0][k] + } + } + if !found { + return false + } + + targ := m[0] + + // Subtract from saved-away rows too. + for _, row := range m[len(m):cap(m)] { + if row[bi/8]&(1<<(7-bi&7)) == 0 { + continue + } + for k := range row { + row[k] ^= targ[k] + } + } + + // Found a row with bit #bi == 1 and cut that bit from all the others. + // Apply to data and remove from m. + if (b.B[bi/8]>>(7-bi&7))&1 != bval { + for j, v := range targ { + b.B[j] ^= v + } + } + b.check() + n := len(m) - 1 + m[0], m[n] = m[n], m[0] + b.M = m[:n] + + for _, row := range b.M { + if row[bi/8]&(1<<(7-bi&7)) != 0 { + panic("did not reduce") + } + } + + return true +} + +func (b *BitBlock) copyOut() { + b.check() + copy(b.bdata, b.B[:b.DataBytes]) + copy(b.cdata, b.B[b.DataBytes:]) +} + +func showtable(w http.ResponseWriter, b *BitBlock, gray func(int) bool) { + nd := b.DataBytes + nc := b.CheckBytes + + fmt.Fprintf(w, "\n") + line := func() { + fmt.Fprintf(w, "\n") + for i := 0; i < (nd+nc)*8; i++ { + fmt.Fprintf(w, "> uint(7-i&7) & 1 + if gray(i) { + fmt.Fprintf(w, " class='gray'") + } + fmt.Fprintf(w, ">") + if v == 1 { + fmt.Fprintf(w, "1") + } + } + line() + } + + m := b.M[len(b.M):cap(b.M)] + for i := len(m) - 1; i >= 0; i-- { + dorow(m[i]) + } + m = b.M + for _, row := range b.M { + dorow(row) + } + + fmt.Fprintf(w, "
\n", (nd+nc)*8) + } + line() + dorow := func(row []byte) { + fmt.Fprintf(w, "
\n") +} + +func BitsTable(w http.ResponseWriter, req *http.Request) { + nd := 2 + nc := 2 + fmt.Fprintf(w, ` + + `) + rs := gf256.NewRSEncoder(coding.Field, nc) + dat := make([]byte, nd+nc) + b := newBlock(nd, nc, rs, dat[:nd], dat[nd:]) + for i := 0; i < nd*8; i++ { + b.canSet(uint(i), 0) + } + showtable(w, b, func(i int) bool { return i < nd*8 }) + + b = newBlock(nd, nc, rs, dat[:nd], dat[nd:]) + for j := 0; j < (nd+nc)*8; j += 2 { + b.canSet(uint(j), 0) + } + showtable(w, b, func(i int) bool { return i%2 == 0 }) + +} diff --git a/vendor/github.com/mattermost/rsc/qr/web/resize/resize.go b/vendor/github.com/mattermost/rsc/qr/web/resize/resize.go new file mode 100644 index 000000000..02c8b0040 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/qr/web/resize/resize.go @@ -0,0 +1,152 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package resize + +import ( + "image" + "image/color" +) + +// average convert the sums to averages and returns the result. +func average(sum []uint64, w, h int, n uint64) *image.RGBA { + ret := image.NewRGBA(image.Rect(0, 0, w, h)) + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + index := 4 * (y*w + x) + pix := ret.Pix[y*ret.Stride+x*4:] + pix[0] = uint8(sum[index+0] / n) + pix[1] = uint8(sum[index+1] / n) + pix[2] = uint8(sum[index+2] / n) + pix[3] = uint8(sum[index+3] / n) + } + } + return ret +} + +// ResizeRGBA returns a scaled copy of the RGBA image slice r of m. +// The returned image has width w and height h. +func ResizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) *image.RGBA { + ww, hh := uint64(w), uint64(h) + dx, dy := uint64(r.Dx()), uint64(r.Dy()) + // See comment in Resize. + n, sum := dx*dy, make([]uint64, 4*w*h) + for y := r.Min.Y; y < r.Max.Y; y++ { + pix := m.Pix[(y-r.Min.Y)*m.Stride:] + for x := r.Min.X; x < r.Max.X; x++ { + // Get the source pixel. + p := pix[(x-r.Min.X)*4:] + r64 := uint64(p[0]) + g64 := uint64(p[1]) + b64 := uint64(p[2]) + a64 := uint64(p[3]) + // Spread the source pixel over 1 or more destination rows. + py := uint64(y) * hh + for remy := hh; remy > 0; { + qy := dy - (py % dy) + if qy > remy { + qy = remy + } + // Spread the source pixel over 1 or more destination columns. + px := uint64(x) * ww + index := 4 * ((py/dy)*ww + (px / dx)) + for remx := ww; remx > 0; { + qx := dx - (px % dx) + if qx > remx { + qx = remx + } + qxy := qx * qy + sum[index+0] += r64 * qxy + sum[index+1] += g64 * qxy + sum[index+2] += b64 * qxy + sum[index+3] += a64 * qxy + index += 4 + px += qx + remx -= qx + } + py += qy + remy -= qy + } + } + } + return average(sum, w, h, n) +} + +// ResizeNRGBA returns a scaled copy of the RGBA image slice r of m. +// The returned image has width w and height h. +func ResizeNRGBA(m *image.NRGBA, r image.Rectangle, w, h int) *image.RGBA { + ww, hh := uint64(w), uint64(h) + dx, dy := uint64(r.Dx()), uint64(r.Dy()) + // See comment in Resize. + n, sum := dx*dy, make([]uint64, 4*w*h) + for y := r.Min.Y; y < r.Max.Y; y++ { + pix := m.Pix[(y-r.Min.Y)*m.Stride:] + for x := r.Min.X; x < r.Max.X; x++ { + // Get the source pixel. + p := pix[(x-r.Min.X)*4:] + r64 := uint64(p[0]) + g64 := uint64(p[1]) + b64 := uint64(p[2]) + a64 := uint64(p[3]) + r64 = (r64 * a64) / 255 + g64 = (g64 * a64) / 255 + b64 = (b64 * a64) / 255 + // Spread the source pixel over 1 or more destination rows. + py := uint64(y) * hh + for remy := hh; remy > 0; { + qy := dy - (py % dy) + if qy > remy { + qy = remy + } + // Spread the source pixel over 1 or more destination columns. + px := uint64(x) * ww + index := 4 * ((py/dy)*ww + (px / dx)) + for remx := ww; remx > 0; { + qx := dx - (px % dx) + if qx > remx { + qx = remx + } + qxy := qx * qy + sum[index+0] += r64 * qxy + sum[index+1] += g64 * qxy + sum[index+2] += b64 * qxy + sum[index+3] += a64 * qxy + index += 4 + px += qx + remx -= qx + } + py += qy + remy -= qy + } + } + } + return average(sum, w, h, n) +} + +// Resample returns a resampled copy of the image slice r of m. +// The returned image has width w and height h. +func Resample(m image.Image, r image.Rectangle, w, h int) *image.RGBA { + if w < 0 || h < 0 { + return nil + } + if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 { + return image.NewRGBA(image.Rect(0, 0, w, h)) + } + curw, curh := r.Dx(), r.Dy() + img := image.NewRGBA(image.Rect(0, 0, w, h)) + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + // Get a source pixel. + subx := x * curw / w + suby := y * curh / h + r32, g32, b32, a32 := m.At(subx, suby).RGBA() + r := uint8(r32 >> 8) + g := uint8(g32 >> 8) + b := uint8(b32 >> 8) + a := uint8(a32 >> 8) + img.SetRGBA(x, y, color.RGBA{r, g, b, a}) + } + } + return img +} diff --git a/vendor/github.com/mattermost/rsc/regexp/regmerge/copy.go b/vendor/github.com/mattermost/rsc/regexp/regmerge/copy.go new file mode 100644 index 000000000..4e723260b --- /dev/null +++ b/vendor/github.com/mattermost/rsc/regexp/regmerge/copy.go @@ -0,0 +1,225 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copied from code.google.com/p/codesearch/regexp/copy.go. + +// Copied from Go's regexp/syntax. +// Formatters edited to handle instByteRange. + +package main + +import ( + "bytes" + "fmt" + "regexp/syntax" + "sort" + "strconv" + "unicode" +) + +// cleanClass sorts the ranges (pairs of elements of r), +// merges them, and eliminates duplicates. +func cleanClass(rp *[]rune) []rune { + + // Sort by lo increasing, hi decreasing to break ties. + sort.Sort(ranges{rp}) + + r := *rp + if len(r) < 2 { + return r + } + + // Merge abutting, overlapping. + w := 2 // write index + for i := 2; i < len(r); i += 2 { + lo, hi := r[i], r[i+1] + if lo <= r[w-1]+1 { + // merge with previous range + if hi > r[w-1] { + r[w-1] = hi + } + continue + } + // new disjoint range + r[w] = lo + r[w+1] = hi + w += 2 + } + + return r[:w] +} + +// appendRange returns the result of appending the range lo-hi to the class r. +func appendRange(r []rune, lo, hi rune) []rune { + // Expand last range or next to last range if it overlaps or abuts. + // Checking two ranges helps when appending case-folded + // alphabets, so that one range can be expanding A-Z and the + // other expanding a-z. + n := len(r) + for i := 2; i <= 4; i += 2 { // twice, using i=2, i=4 + if n >= i { + rlo, rhi := r[n-i], r[n-i+1] + if lo <= rhi+1 && rlo <= hi+1 { + if lo < rlo { + r[n-i] = lo + } + if hi > rhi { + r[n-i+1] = hi + } + return r + } + } + } + + return append(r, lo, hi) +} + +const ( + // minimum and maximum runes involved in folding. + // checked during test. + minFold = 0x0041 + maxFold = 0x1044f +) + +// appendFoldedRange returns the result of appending the range lo-hi +// and its case folding-equivalent runes to the class r. +func appendFoldedRange(r []rune, lo, hi rune) []rune { + // Optimizations. + if lo <= minFold && hi >= maxFold { + // Range is full: folding can't add more. + return appendRange(r, lo, hi) + } + if hi < minFold || lo > maxFold { + // Range is outside folding possibilities. + return appendRange(r, lo, hi) + } + if lo < minFold { + // [lo, minFold-1] needs no folding. + r = appendRange(r, lo, minFold-1) + lo = minFold + } + if hi > maxFold { + // [maxFold+1, hi] needs no folding. + r = appendRange(r, maxFold+1, hi) + hi = maxFold + } + + // Brute force. Depend on appendRange to coalesce ranges on the fly. + for c := lo; c <= hi; c++ { + r = appendRange(r, c, c) + f := unicode.SimpleFold(c) + for f != c { + r = appendRange(r, f, f) + f = unicode.SimpleFold(f) + } + } + return r +} + +// ranges implements sort.Interface on a []rune. +// The choice of receiver type definition is strange +// but avoids an allocation since we already have +// a *[]rune. +type ranges struct { + p *[]rune +} + +func (ra ranges) Less(i, j int) bool { + p := *ra.p + i *= 2 + j *= 2 + return p[i] < p[j] || p[i] == p[j] && p[i+1] > p[j+1] +} + +func (ra ranges) Len() int { + return len(*ra.p) / 2 +} + +func (ra ranges) Swap(i, j int) { + p := *ra.p + i *= 2 + j *= 2 + p[i], p[i+1], p[j], p[j+1] = p[j], p[j+1], p[i], p[i+1] +} + +func progString(p *syntax.Prog) string { + var b bytes.Buffer + dumpProg(&b, p) + return b.String() +} + +func instString(i *syntax.Inst) string { + var b bytes.Buffer + dumpInst(&b, i) + return b.String() +} + +func bw(b *bytes.Buffer, args ...string) { + for _, s := range args { + b.WriteString(s) + } +} + +func dumpProg(b *bytes.Buffer, p *syntax.Prog) { + for j := range p.Inst { + i := &p.Inst[j] + pc := strconv.Itoa(j) + if len(pc) < 3 { + b.WriteString(" "[len(pc):]) + } + if j == p.Start { + pc += "*" + } + bw(b, pc, "\t") + dumpInst(b, i) + bw(b, "\n") + } +} + +func u32(i uint32) string { + return strconv.FormatUint(uint64(i), 10) +} + +func dumpInst(b *bytes.Buffer, i *syntax.Inst) { + switch i.Op { + case syntax.InstAlt: + bw(b, "alt -> ", u32(i.Out), ", ", u32(i.Arg)) + case syntax.InstAltMatch: + bw(b, "altmatch -> ", u32(i.Out), ", ", u32(i.Arg)) + case syntax.InstCapture: + bw(b, "cap ", u32(i.Arg), " -> ", u32(i.Out)) + case syntax.InstEmptyWidth: + bw(b, "empty ", u32(i.Arg), " -> ", u32(i.Out)) + case syntax.InstMatch: + bw(b, "match") + case syntax.InstFail: + bw(b, "fail") + case syntax.InstNop: + bw(b, "nop -> ", u32(i.Out)) + case instByteRange: + fmt.Fprintf(b, "byte %02x-%02x", (i.Arg>>8)&0xFF, i.Arg&0xFF) + if i.Arg&argFold != 0 { + bw(b, "/i") + } + bw(b, " -> ", u32(i.Out)) + + // Should not happen + case syntax.InstRune: + if i.Rune == nil { + // shouldn't happen + bw(b, "rune ") + } + bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune))) + if syntax.Flags(i.Arg)&syntax.FoldCase != 0 { + bw(b, "/i") + } + bw(b, " -> ", u32(i.Out)) + case syntax.InstRune1: + bw(b, "rune1 ", strconv.QuoteToASCII(string(i.Rune)), " -> ", u32(i.Out)) + case syntax.InstRuneAny: + bw(b, "any -> ", u32(i.Out)) + case syntax.InstRuneAnyNotNL: + bw(b, "anynotnl -> ", u32(i.Out)) + } +} diff --git a/vendor/github.com/mattermost/rsc/regexp/regmerge/main.go b/vendor/github.com/mattermost/rsc/regexp/regmerge/main.go new file mode 100644 index 000000000..672337a04 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/regexp/regmerge/main.go @@ -0,0 +1,96 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "fmt" + "log" + "os" + "regexp/syntax" + "runtime/pprof" +) + +var maxState = flag.Int("m", 1e5, "maximum number of states to explore") +var cpuprof = flag.String("cpuprofile", "", "cpu profile file") + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "usage: regmerge [-m maxstate] regexp [regexp2 regexp3....]\n") + os.Exit(2) + } + flag.Parse() + + if len(flag.Args()) < 1 { + flag.Usage() + } + + os.Exit(run(flag.Args())) +} + +func run(args []string) int { + if *cpuprof != "" { + f, err := os.Create(*cpuprof) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + m, err := compile(flag.Args()...) + if err != nil { + log.Fatal(err) + } + n := 100 + for ;; n *= 2 { + if n >= *maxState { + if n >= 2* *maxState { + fmt.Printf("reached state limit\n") + return 1 + } + n = *maxState + } + log.Printf("try %d states...\n", n) + s, err := m.findMatch(n) + if err == nil { + fmt.Printf("%q\n", s) + return 0 + } + if err != ErrMemory { + fmt.Printf("failed: %s\n", err) + return 3 + } + } + panic("unreachable") +} + +func compile(exprs ...string) (*matcher, error) { + var progs []*syntax.Prog + for _, expr := range exprs { + re, err := syntax.Parse(expr, syntax.Perl) + if err != nil { + return nil, err + } + sre := re.Simplify() + prog, err := syntax.Compile(sre) + if err != nil { + return nil, err + } + if err := toByteProg(prog); err != nil { + return nil, err + } + progs = append(progs, prog) + } + m := &matcher{} + if err := m.init(joinProgs(progs), len(progs)); err != nil { + return nil, err + } + return m, nil +} + +func bug() { + panic("regmerge: internal error") +} diff --git a/vendor/github.com/mattermost/rsc/regexp/regmerge/match.go b/vendor/github.com/mattermost/rsc/regexp/regmerge/match.go new file mode 100644 index 000000000..6a4b1f967 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/regexp/regmerge/match.go @@ -0,0 +1,406 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copied from code.google.com/p/codesearch/regexp/copy.go +// and adapted for the problem of finding a matching string, not +// testing whether a particular string matches. + +package main + +import ( + "encoding/binary" + "errors" + "fmt" + "hash/fnv" + "hash" + "log" + "regexp/syntax" +) + +// A matcher holds the state for running regular expression search. +type matcher struct { + buf []byte + prog *syntax.Prog // compiled program + dstate map[uint32]*dstate // dstate cache + start *dstate // start state + startLine *dstate // start state for beginning of line + z1, z2, z3 nstate // three temporary nstates + ids []int + numState int + maxState int + numByte int + numMatch int + undo [256]byte + all *dstate + allTail **dstate + h hash.Hash32 +} + +// An nstate corresponds to an NFA state. +type nstate struct { + q Set // queue of program instructions + flag flags // flags (TODO) + needFlag syntax.EmptyOp +} + +// The flags record state about a position between bytes in the text. +type flags uint32 + +const ( + flagBOL flags = 1 << iota // beginning of line + flagEOL // end of line + flagBOT // beginning of text + flagEOT // end of text + flagWord // last byte was word byte +) + +// A dstate corresponds to a DFA state. +type dstate struct { + enc string // encoded nstate + nextAll *dstate + nextHash *dstate + prev *dstate + prevByte int + done bool +} + +func (z *nstate) String() string { + return fmt.Sprintf("%v/%#x+%#x", z.q.Dense(), z.flag, z.needFlag) +} + +// enc encodes z as a string. +func (m *matcher) enc(z *nstate) []byte { + buf := m.buf[:0] + buf = append(buf, byte(z.needFlag), byte(z.flag)) + ids := m.ids[:0] + for _, id := range z.q.Dense() { + ids = append(ids, int(id)) + } + sortInts(ids) + last := ^uint32(0) + for _, id := range ids { + x := uint32(id)-last + last = uint32(id) + for x >= 0x80 { + buf = append(buf, byte(x)|0x80) + x >>= 7 + } + buf = append(buf, byte(x)) + } + m.buf = buf + return buf +} + +// dec decodes the encoding s into z. +func (m *matcher) dec(z *nstate, s string) { + b := append(m.buf[:0], s...) + m.buf = b + z.needFlag = syntax.EmptyOp(b[0]) + b = b[1:] + i, n := binary.Uvarint(b) + if n <= 0 { + bug() + } + b = b[n:] + z.flag = flags(i) + z.q.Reset() + last := ^uint32(0) + for len(b) > 0 { + i, n = binary.Uvarint(b) + if n <= 0 { + bug() + } + b = b[n:] + last += uint32(i) + z.q.Add(last, 0, 0) + } +} + +// init initializes the matcher. +func (m *matcher) init(prog *syntax.Prog, n int) error { + m.prog = prog + m.dstate = make(map[uint32]*dstate) + m.numMatch = n + m.maxState = 10 + m.allTail = &m.all + m.numByte = 256 + for i := range m.undo { + m.undo[i] = byte(i) + } + m.h = fnv.New32() + + m.z1.q.Init(uint32(len(prog.Inst))) + m.z2.q.Init(uint32(len(prog.Inst))) + m.z3.q.Init(uint32(len(prog.Inst))) + m.ids = make([]int, 0, len(prog.Inst)) + + m.addq(&m.z1.q, uint32(prog.Start), syntax.EmptyBeginLine|syntax.EmptyBeginText) + m.z1.flag = flagBOL | flagBOT + m.start = m.cache(&m.z1, nil, 0) + + m.z1.q.Reset() + m.addq(&m.z1.q, uint32(prog.Start), syntax.EmptyBeginLine) + m.z1.flag = flagBOL + m.startLine = m.cache(&m.z1, nil, 0) + + m.crunchProg() + + return nil +} + +// stepEmpty steps runq to nextq expanding according to flag. +func (m *matcher) stepEmpty(runq, nextq *Set, flag syntax.EmptyOp) { + nextq.Reset() + for _, id := range runq.Dense() { + m.addq(nextq, id, flag) + } +} + +// stepByte steps runq to nextq consuming c and then expanding according to flag. +// It returns true if a match ends immediately before c. +// c is either an input byte or endText. +func (m *matcher) stepByte(runq, nextq *Set, c int, flag syntax.EmptyOp) (match bool) { + nextq.Reset() + m.addq(nextq, uint32(m.prog.Start), flag) + + nmatch := 0 + for _, id := range runq.Dense() { + i := &m.prog.Inst[id] + switch i.Op { + default: + continue + case syntax.InstMatch: + nmatch++ + continue + case instByteRange: + if c == endText { + break + } + lo := int((i.Arg >> 8) & 0xFF) + hi := int(i.Arg & 0xFF) + if i.Arg&argFold != 0 && 'a' <= c && c <= 'z' { + c += 'A' - 'a' + } + if lo <= c && c <= hi { + m.addq(nextq, i.Out, flag) + } + } + } + return nmatch == m.numMatch +} + +// addq adds id to the queue, expanding according to flag. +func (m *matcher) addq(q *Set, id uint32, flag syntax.EmptyOp) { + if q.Has(id, 0) { + return + } + q.MustAdd(id) + i := &m.prog.Inst[id] + switch i.Op { + case syntax.InstCapture, syntax.InstNop: + m.addq(q, i.Out, flag) + case syntax.InstAlt, syntax.InstAltMatch: + m.addq(q, i.Out, flag) + m.addq(q, i.Arg, flag) + case syntax.InstEmptyWidth: + if syntax.EmptyOp(i.Arg)&^flag == 0 { + m.addq(q, i.Out, flag) + } + } +} + +const endText = -1 + +// computeNext computes the next DFA state if we're in d reading c (an input byte or endText). +func (m *matcher) computeNext(this, next *nstate, d *dstate, c int) bool { + // compute flags in effect before c + flag := syntax.EmptyOp(0) + if this.flag&flagBOL != 0 { + flag |= syntax.EmptyBeginLine + } + if this.flag&flagBOT != 0 { + flag |= syntax.EmptyBeginText + } + if this.flag&flagWord != 0 { + if !isWordByte(c) { + flag |= syntax.EmptyWordBoundary + } else { + flag |= syntax.EmptyNoWordBoundary + } + } else { + if isWordByte(c) { + flag |= syntax.EmptyWordBoundary + } else { + flag |= syntax.EmptyNoWordBoundary + } + } + if c == '\n' { + flag |= syntax.EmptyEndLine + } + if c == endText { + flag |= syntax.EmptyEndLine | syntax.EmptyEndText + } + + if flag &= this.needFlag; flag != 0 { + // re-expand queue using new flags. + // TODO: only do this when it matters + // (something is gating on word boundaries). + m.stepEmpty(&this.q, &next.q, flag) + this, next = next, &m.z3 + } + + // now compute flags after c. + flag = 0 + next.flag = 0 + if c == '\n' { + flag |= syntax.EmptyBeginLine + next.flag |= flagBOL + } + if isWordByte(c) { + next.flag |= flagWord + } + + // re-add start, process rune + expand according to flags. + if m.stepByte(&this.q, &next.q, c, flag) { + return true + } + next.needFlag = m.queueFlag(&next.q) + if next.needFlag&syntax.EmptyBeginLine == 0 { + next.flag &^= flagBOL + } + if next.needFlag&(syntax.EmptyWordBoundary|syntax.EmptyNoWordBoundary) == 0{ + next.flag &^= flagWord + } + + m.cache(next, d, c) + return false +} + +func (m *matcher) queueFlag(runq *Set) syntax.EmptyOp { + var e uint32 + for _, id := range runq.Dense() { + i := &m.prog.Inst[id] + if i.Op == syntax.InstEmptyWidth { + e |= i.Arg + } + } + return syntax.EmptyOp(e) +} + +func (m *matcher) hash(enc []byte) uint32 { + m.h.Reset() + m.h.Write(enc) + return m.h.Sum32() +} + +func (m *matcher) find(h uint32, enc []byte) *dstate { +Search: + for d := m.dstate[h]; d!=nil; d=d.nextHash { + s := d.enc + if len(s) != len(enc) { + continue Search + } + for i, b := range enc { + if s[i] != b { + continue Search + } + } + return d + } + return nil +} + +func (m *matcher) cache(z *nstate, prev *dstate, prevByte int) *dstate { + enc := m.enc(z) + h := m.hash(enc) + d := m.find(h, enc) + if d != nil { + return d + } + + if m.numState >= m.maxState { + panic(ErrMemory) + } + m.numState++ + d = &dstate{ + enc: string(enc), + prev: prev, + prevByte: prevByte, + nextHash: m.dstate[h], + } + m.dstate[h] = d + *m.allTail = d + m.allTail = &d.nextAll + + return d +} + +// isWordByte reports whether the byte c is a word character: ASCII only. +// This is used to implement \b and \B. This is not right for Unicode, but: +// - it's hard to get right in a byte-at-a-time matching world +// (the DFA has only one-byte lookahead) +// - this crude approximation is the same one PCRE uses +func isWordByte(c int) bool { + return 'A' <= c && c <= 'Z' || + 'a' <= c && c <= 'z' || + '0' <= c && c <= '9' || + c == '_' +} + +var ErrNoMatch = errors.New("no matching strings") +var ErrMemory = errors.New("exhausted memory") + +func (m *matcher) findMatch(maxState int) (s string, err error) { + defer func() { + switch r := recover().(type) { + case nil: + return + case error: + err = r + return + default: + panic(r) + } + }() + + m.maxState = maxState + numState := 0 + var d *dstate + var c int + for d = m.all; d != nil; d = d.nextAll { + numState++ + if d.done { + continue + } + this, next := &m.z1, &m.z2 + m.dec(this, d.enc) + if m.computeNext(this, next, d, endText) { + c = endText + goto Found + } + for _, cb := range m.undo[:m.numByte] { + if m.computeNext(this, next, d, int(cb)) { + c = int(cb) + goto Found + } + } + d.done = true + } + log.Printf("searched %d states; queued %d states", numState, m.numState) + return "", ErrNoMatch + +Found: + var buf []byte + if c >= 0 { + buf = append(buf, byte(c)) + } + for d1 := d; d1.prev != nil; d1= d1.prev { + buf = append(buf, byte(d1.prevByte)) + } + for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 { + buf[i], buf[j] = buf[j], buf[i] + } + log.Printf("searched %d states; queued %d states", numState, m.numState) + return string(buf), nil +} diff --git a/vendor/github.com/mattermost/rsc/regexp/regmerge/merge.go b/vendor/github.com/mattermost/rsc/regexp/regmerge/merge.go new file mode 100644 index 000000000..15f51cfa7 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/regexp/regmerge/merge.go @@ -0,0 +1,103 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code to merge (join) multiple regexp progs into a single prog. +// New code; not copied from anywhere. + +package main + +import "regexp/syntax" + +func joinProgs(progs []*syntax.Prog) *syntax.Prog { + all := &syntax.Prog{} + for i, p := range progs { + n := len(all.Inst) + all.Inst = append(all.Inst, p.Inst...) + match := shiftInst(all.Inst[n:], n) + if match < 0 { + // no match instruction; give up + all.Inst = []syntax.Inst{{Op: syntax.InstFail}} + all.Start = 0 + return all + } + match += n + m := len(all.Inst) + all.Inst = append(all.Inst, + syntax.Inst{Op: syntax.InstAlt, Out: uint32(p.Start+n), Arg: uint32(m+1)}, + syntax.Inst{Op: instByteRange, Arg: 0x00FF, Out: uint32(m)}, + syntax.Inst{Op: instByteRange, Arg: 0x00FF, Out: uint32(match)}, + syntax.Inst{Op: syntax.InstMatch}, + ) + all.Inst[match] = syntax.Inst{Op: syntax.InstAlt, Out: uint32(m+2), Arg: uint32(m+3)} + + if i == 0 { + all.Start = m + } else { + old := all.Start + all.Start = len(all.Inst) + all.Inst = append(all.Inst, syntax.Inst{Op: syntax.InstAlt, Out: uint32(old), Arg: uint32(m)}) + } + } + + return all +} + +func shiftInst(inst []syntax.Inst, n int) int { + match := -1 + for i := range inst { + ip := &inst[i] + ip.Out += uint32(n) + if ip.Op == syntax.InstMatch { + if match >= 0 { + panic("double match") + } + match = i + } + if ip.Op == syntax.InstAlt || ip.Op == syntax.InstAltMatch { + ip.Arg += uint32(n) + } + } + return match +} + +func (m *matcher) crunchProg() { + var rewrite [256]byte + + for i := range m.prog.Inst { + ip := &m.prog.Inst[i] + switch ip.Op { + case instByteRange: + lo, hi := byte(ip.Arg>>8), byte(ip.Arg) + rewrite[lo] = 1 + if hi < 255 { + rewrite[hi+1] = 1 + } + case syntax.InstEmptyWidth: + switch op := syntax.EmptyOp(ip.Arg); { + case op&(syntax.EmptyBeginLine|syntax.EmptyEndLine) != 0: + rewrite['\n'] = 1 + rewrite['\n'+1] = 1 + case op&(syntax.EmptyWordBoundary|syntax.EmptyNoWordBoundary) != 0: + rewrite['A'] = 1 + rewrite['Z'+1] = 1 + rewrite['a'] = 1 + rewrite['z'+1] = 1 + rewrite['0'] = 1 + rewrite['9'+1] = 1 + rewrite['_'] = 1 + rewrite['_'+1] = 1 + } + } + } + + rewrite[0] = 0 + for i := 1; i < 256; i++ { + rewrite[i] += rewrite[i-1] + } + m.numByte = int(rewrite[255]) + 1 + + for i := 255; i >= 0; i-- { + m.undo[rewrite[i]] = byte(i) + } +} diff --git a/vendor/github.com/mattermost/rsc/regexp/regmerge/sort.go b/vendor/github.com/mattermost/rsc/regexp/regmerge/sort.go new file mode 100644 index 000000000..e40f87714 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/regexp/regmerge/sort.go @@ -0,0 +1,199 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copy of go/src/pkg/sort/sort.go, specialized for []int +// and to remove some array indexing. + +package main + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// Insertion sort +func insertionSort(data []int, a, b int) { + for i := a + 1; i < b; i++ { + for j := i; j > a && data[j] < data[j-1]; j-- { + data[j], data[j-1] = data[j-1], data[j] + } + } +} + +// siftDown implements the heap property on data[lo, hi). +// first is an offset into the array where the root of the heap lies. +func siftDown(data []int, lo, hi, first int) { + root := lo + for { + child := 2*root + 1 + if child >= hi { + break + } + if child+1 < hi && data[first+child] < data[first+child+1] { + child++ + } + if !(data[first+root] < data[first+child]) { + return + } + data[first+root], data[first+child] = data[first+child], data[first+root] + root = child + } +} + +func heapSort(data []int, a, b int) { + first := a + lo := 0 + hi := b - a + + // Build heap with greatest element at top. + for i := (hi - 1) / 2; i >= 0; i-- { + siftDown(data, i, hi, first) + } + + // Pop elements, largest first, into end of data. + for i := hi - 1; i >= 0; i-- { + data[first], data[first+i] = data[first+i], data[first] + siftDown(data, lo, i, first) + } +} + +// Quicksort, following Bentley and McIlroy, +// ``Engineering a Sort Function,'' SP&E November 1993. + +// medianOfThree moves the median of the three values data[a], data[b], data[c] into data[a]. +func medianOfThree(data []int, a, b, c int) { + m0 := b + m1 := a + m2 := c + // bubble sort on 3 elements + if data[m1] < data[m0] { + data[m1], data[m0] = data[m0], data[m1] + } + if data[m2] < data[m1] { + data[m2], data[m1] = data[m1], data[m2] + } + if data[m1] < data[m0] { + data[m1], data[m0] = data[m0], data[m1] + } + // now data[m0] <= data[m1] <= data[m2] +} + +func swapRange(data []int, a, b, n int) { + for i := 0; i < n; i++ { + data[a+i], data[b+i] = data[b+i], data[a+i] + } +} + +func doPivot(data []int, lo, hi int) (midlo, midhi int) { + m := lo + (hi-lo)/2 // Written like this to avoid integer overflow. + if hi-lo > 40 { + // Tukey's ``Ninther,'' median of three medians of three. + s := (hi - lo) / 8 + medianOfThree(data, lo, lo+s, lo+2*s) + medianOfThree(data, m, m-s, m+s) + medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) + } + medianOfThree(data, lo, m, hi-1) + + // Invariants are: + // data[lo] = pivot (set up by ChoosePivot) + // data[lo <= i < a] = pivot + // data[a <= i < b] < pivot + // data[b <= i < c] is unexamined + // data[c <= i < d] > pivot + // data[d <= i < hi] = pivot + // + // Once b meets c, can swap the "= pivot" sections + // into the middle of the slice. + pivot := lo + a, b, c, d := lo+1, lo+1, hi, hi + dpivot := data[pivot] + db, dc1 := data[b], data[c-1] + for b < c { + if db < dpivot { // data[b] < pivot + b++ + if b < c { + db = data[b] + } + continue + } + if !(dpivot < db) { // data[b] = pivot + data[a], data[b] = db, data[a] + a++ + b++ + if b < c { + db = data[b] + } + continue + } + if dpivot < dc1 { // data[c-1] > pivot + c-- + if c > 0 { + dc1 = data[c-1] + } + continue + } + if !(dc1 < dpivot) { // data[c-1] = pivot + data[c-1], data[d-1] = data[d-1], dc1 + c-- + d-- + if c > 0 { + dc1 = data[c-1] + } + continue + } + // data[b] > pivot; data[c-1] < pivot + data[b], data[c-1] = dc1, db + b++ + c-- + if b < c { + db = data[b] + dc1 = data[c-1] + } + } + + n := min(b-a, a-lo) + swapRange(data, lo, b-n, n) + + n = min(hi-d, d-c) + swapRange(data, c, hi-n, n) + + return lo + b - a, hi - (d - c) +} + +func quickSort(data []int, a, b, maxDepth int) { + for b-a > 7 { + if maxDepth == 0 { + heapSort(data, a, b) + return + } + maxDepth-- + mlo, mhi := doPivot(data, a, b) + // Avoiding recursion on the larger subproblem guarantees + // a stack depth of at most lg(b-a). + if mlo-a < b-mhi { + quickSort(data, a, mlo, maxDepth) + a = mhi // i.e., quickSort(data, mhi, b) + } else { + quickSort(data, mhi, b, maxDepth) + b = mlo // i.e., quickSort(data, a, mlo) + } + } + if b-a > 1 { + insertionSort(data, a, b) + } +} + +func sortInts(data []int) { + // Switch to heapsort if depth of 2*ceil(lg(n)) is reached. + n := len(data) + maxDepth := 0 + for 1<= utf8.RuneSelf { + return + } + if fold && !asciiFold(r) { + return + } + return byte(r), byte(r), fold, true + } + if len(i.Rune) == 2 && i.Rune[1] < utf8.RuneSelf { + if fold { + for r := i.Rune[0]; r <= i.Rune[1]; r++ { + if asciiFold(r) { + return + } + } + } + return byte(i.Rune[0]), byte(i.Rune[1]), fold, true + } + if len(i.Rune) == 4 && i.Rune[0] == i.Rune[1] && i.Rune[2] == i.Rune[3] && unicode.SimpleFold(i.Rune[0]) == i.Rune[2] && unicode.SimpleFold(i.Rune[2]) == i.Rune[0] { + return byte(i.Rune[0]), byte(i.Rune[0]), true, true + } + + return +} + +func asciiFold(r rune) bool { + if r >= utf8.RuneSelf { + return false + } + r1 := unicode.SimpleFold(r) + if r1 >= utf8.RuneSelf { + return false + } + if r1 == r { + return true + } + return unicode.SimpleFold(r1) == r +} + +func maxRune(n int) rune { + b := 0 + if n == 1 { + b = 7 + } else { + b = 8 - (n + 1) + 6*(n-1) + } + return 1< 0xbf { + // Not a continuation byte, no need to cache. + return b.uncachedSuffix(lo, hi, fold, next) + } + + key := cacheKey{lo, hi, fold, next} + if pc, ok := b.cache[key]; ok { + return pc + } + + pc := b.uncachedSuffix(lo, hi, fold, next) + b.cache[key] = pc + return pc +} + +func (b *runeBuilder) addBranch(pc uint32) { + // Add pc to the branch at the beginning. + i := &b.p.Inst[b.begin] + switch i.Op { + case syntax.InstFail: + i.Op = syntax.InstNop + i.Out = pc + return + case syntax.InstNop: + i.Op = syntax.InstAlt + i.Arg = pc + return + case syntax.InstAlt: + apc := uint32(len(b.p.Inst)) + b.p.Inst = append(b.p.Inst, syntax.Inst{Op: instAlt, Out: i.Arg, Arg: pc}) + i = &b.p.Inst[b.begin] + i.Arg = apc + b.begin = apc + } +} + +func (b *runeBuilder) addRange(lo, hi rune, fold bool) { + if lo > hi { + return + } + + // TODO: Pick off 80-10FFFF for special handling? + if lo == 0x80 && hi == 0x10FFFF { + } + + // Split range into same-length sized ranges. + for i := 1; i < utf8.UTFMax; i++ { + max := maxRune(i) + if lo <= max && max < hi { + b.addRange(lo, max, fold) + b.addRange(max+1, hi, fold) + return + } + } + + // ASCII range is special. + if hi < utf8.RuneSelf { + b.addBranch(b.suffix(byte(lo), byte(hi), fold, 0)) + return + } + + // Split range into sections that agree on leading bytes. + for i := 1; i < utf8.UTFMax; i++ { + m := rune(1)<= 0; i-- { + pc = b.suffix(ulo[i], uhi[i], false, pc) + } + b.addBranch(pc) +} diff --git a/vendor/github.com/mattermost/rsc/rosetta/graph/Makefile b/vendor/github.com/mattermost/rsc/rosetta/graph/Makefile new file mode 100644 index 000000000..1cb49f788 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/rosetta/graph/Makefile @@ -0,0 +1,7 @@ +include $(GOROOT)/src/Make.inc + +TARG=rsc.googlecode.com/hg/rosetta/graph +GOFILES=\ + graph.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/vendor/github.com/mattermost/rsc/rosetta/graph/graph.go b/vendor/github.com/mattermost/rsc/rosetta/graph/graph.go new file mode 100644 index 000000000..359617d41 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/rosetta/graph/graph.go @@ -0,0 +1,133 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Simple demonstration of a graph interface and +// Dijkstra's algorithm built on top of that interface, +// without using inheritance. + +package graph + +import "container/heap" + +// A Graph is the interface implemented by graphs that +// this package can run algorithms on. +type Graph interface { + // NumVertex returns the number of vertices in the graph. + NumVertex() int + + // VertexID returns a vertex ID, 0 <= ID < NumVertex(), for v. + VertexID(v Vertex) int + + // Neighbors returns a slice of vertices that are adjacent + // to v in the graph. + Neighbors(v Vertex) []Vertex +} + +type Vertex interface { + String() string +} + +// ShortestPath uses Dijkstra's algorithm to find the shortest +// path from start to end in g. It returns the path as a slice of +// vertices, with start first and end last. If there is no path, +// ShortestPath returns nil. +func ShortestPath(g Graph, start, end Vertex) []Vertex { + d := newDijkstra(g) + + d.visit(start, 1, nil) + for !d.empty() { + p := d.next() + if g.VertexID(p.v) == g.VertexID(end) { + break + } + for _, v := range g.Neighbors(p.v) { + d.visit(v, p.depth+1, p) + } + } + + p := d.pos(end) + if p.depth == 0 { + // unvisited - no path + return nil + } + path := make([]Vertex, p.depth) + for ; p != nil; p = p.parent { + path[p.depth-1] = p.v + } + return path +} + +// A dpos is a position in the Dijkstra traversal. +type dpos struct { + depth int + heapIndex int + v Vertex + parent *dpos +} + +// A dijkstra is the Dijkstra traversal's work state. +// It contains the heap queue and per-vertex information. +type dijkstra struct { + g Graph + q []*dpos + byID []dpos +} + +func newDijkstra(g Graph) *dijkstra { + d := &dijkstra{g: g} + d.byID = make([]dpos, g.NumVertex()) + return d +} + +func (d *dijkstra) pos(v Vertex) *dpos { + p := &d.byID[d.g.VertexID(v)] + p.v = v // in case this is the first time we've seen it + return p +} + +func (d *dijkstra) visit(v Vertex, depth int, parent *dpos) { + p := d.pos(v) + if p.depth == 0 { + p.parent = parent + p.depth = depth + heap.Push(d, p) + } +} + +func (d *dijkstra) empty() bool { + return len(d.q) == 0 +} + +func (d *dijkstra) next() *dpos { + return heap.Pop(d).(*dpos) +} + +// Implementation of heap.Interface +func (d *dijkstra) Len() int { + return len(d.q) +} + +func (d *dijkstra) Less(i, j int) bool { + return d.q[i].depth < d.q[j].depth +} + +func (d *dijkstra) Swap(i, j int) { + d.q[i], d.q[j] = d.q[j], d.q[i] + d.q[i].heapIndex = i + d.q[j].heapIndex = j +} + +func (d *dijkstra) Push(x interface{}) { + p := x.(*dpos) + p.heapIndex = len(d.q) + d.q = append(d.q, p) +} + +func (d *dijkstra) Pop() interface{} { + n := len(d.q) + x := d.q[n-1] + d.q = d.q[:n-1] + x.heapIndex = -1 + return x +} diff --git a/vendor/github.com/mattermost/rsc/rosetta/maze/Makefile b/vendor/github.com/mattermost/rsc/rosetta/maze/Makefile new file mode 100644 index 000000000..8a83abd05 --- /dev/null +++ b/vendor/github.com/mattermost/rsc/rosetta/maze/Makefile @@ -0,0 +1,8 @@ +include $(GOROOT)/src/Make.inc + +TARG=maze +GOFILES=\ + maze.go\ + +include $(GOROOT)/src/Make.cmd + diff --git a/vendor/github.com/mattermost/rsc/rosetta/maze/maze.go b/vendor/github.com/mattermost/rsc/rosetta/maze/maze.go new file mode 100644 index 000000000..3cd15d04f --- /dev/null +++ b/vendor/github.com/mattermost/rsc/rosetta/maze/maze.go @@ -0,0 +1,191 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Rosetta Code-inspired maze generator and solver. +// Demonstrates use of interfaces to separate algorithm +// implementations (graph.ShortestPath, heap.*) from data. +// (In contrast, multiple inheritance approaches require +// you to store their data in your data structures as part +// of the inheritance.) + +package main + +import ( + "bytes" + "fmt" + "math/rand" + "time" + + "github.com/mattermost/rsc/rosetta/graph" +) + +type Maze struct { + w, h int + grid [][]walls +} + +type Dir uint + +const ( + North Dir = iota + East + West + South +) + +type walls uint8 + +const allWalls walls = 1<