소스 검색

Initial commit

Sergey Alexandrovich 7 년 전
커밋
0c006ce61e
75개의 변경된 파일17091개의 추가작업 그리고 0개의 파일을 삭제
  1. 2 0
      .gitignore
  2. 21 0
      LICENSE
  3. 22 0
      Makefile
  4. 12 0
      _vendor/src/github.com/h2non/bimg/.editorconfig
  5. 8 0
      _vendor/src/github.com/h2non/bimg/.gitignore
  6. 98 0
      _vendor/src/github.com/h2non/bimg/.travis.yml
  7. 85 0
      _vendor/src/github.com/h2non/bimg/History.md
  8. 24 0
      _vendor/src/github.com/h2non/bimg/LICENSE
  9. 347 0
      _vendor/src/github.com/h2non/bimg/README.md
  10. 15 0
      _vendor/src/github.com/h2non/bimg/file.go
  11. 38 0
      _vendor/src/github.com/h2non/bimg/file_test.go
  12. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/corrupt.jpg
  13. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/northern_cardinal_bird.jpg
  14. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/test.gif
  15. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/test.jp2
  16. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/test.jpg
  17. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/test.pdf
  18. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/test.png
  19. 346 0
      _vendor/src/github.com/h2non/bimg/fixtures/test.svg
  20. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/test.webp
  21. 0 0
      _vendor/src/github.com/h2non/bimg/fixtures/test_gif.jpg
  22. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/test_icc_prophoto.jpg
  23. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/test_issue.jpg
  24. 0 0
      _vendor/src/github.com/h2non/bimg/fixtures/test_pdf.jpg
  25. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/test_square.jpg
  26. 0 0
      _vendor/src/github.com/h2non/bimg/fixtures/test_svg.jpg
  27. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/transparent.png
  28. BIN
      _vendor/src/github.com/h2non/bimg/fixtures/vertical.jpg
  29. 223 0
      _vendor/src/github.com/h2non/bimg/image.go
  30. 496 0
      _vendor/src/github.com/h2non/bimg/image_test.go
  31. 77 0
      _vendor/src/github.com/h2non/bimg/metadata.go
  32. 124 0
      _vendor/src/github.com/h2non/bimg/metadata_test.go
  33. 218 0
      _vendor/src/github.com/h2non/bimg/options.go
  34. 302 0
      _vendor/src/github.com/h2non/bimg/preinstall.sh
  35. 561 0
      _vendor/src/github.com/h2non/bimg/resize.go
  36. 644 0
      _vendor/src/github.com/h2non/bimg/resize_test.go
  37. 172 0
      _vendor/src/github.com/h2non/bimg/type.go
  38. 128 0
      _vendor/src/github.com/h2non/bimg/type_test.go
  39. 4 0
      _vendor/src/github.com/h2non/bimg/version.go
  40. 632 0
      _vendor/src/github.com/h2non/bimg/vips.go
  41. 532 0
      _vendor/src/github.com/h2non/bimg/vips.h
  42. 163 0
      _vendor/src/github.com/h2non/bimg/vips_test.go
  43. 21 0
      _vendor/src/github.com/tj/go-debug/History.md
  44. 8 0
      _vendor/src/github.com/tj/go-debug/Makefile
  45. 75 0
      _vendor/src/github.com/tj/go-debug/Readme.md
  46. 128 0
      _vendor/src/github.com/tj/go-debug/debug.go
  47. 152 0
      _vendor/src/github.com/tj/go-debug/debug_test.go
  48. 25 0
      _vendor/src/github.com/tj/go-debug/example/multiple.go
  49. 16 0
      _vendor/src/github.com/tj/go-debug/example/single.go
  50. 9 0
      _vendor/src/gopkg.in/yaml.v2/.travis.yml
  51. 13 0
      _vendor/src/gopkg.in/yaml.v2/LICENSE
  52. 31 0
      _vendor/src/gopkg.in/yaml.v2/LICENSE.libyaml
  53. 131 0
      _vendor/src/gopkg.in/yaml.v2/README.md
  54. 742 0
      _vendor/src/gopkg.in/yaml.v2/apic.go
  55. 682 0
      _vendor/src/gopkg.in/yaml.v2/decode.go
  56. 998 0
      _vendor/src/gopkg.in/yaml.v2/decode_test.go
  57. 1684 0
      _vendor/src/gopkg.in/yaml.v2/emitterc.go
  58. 306 0
      _vendor/src/gopkg.in/yaml.v2/encode.go
  59. 501 0
      _vendor/src/gopkg.in/yaml.v2/encode_test.go
  60. 1095 0
      _vendor/src/gopkg.in/yaml.v2/parserc.go
  61. 394 0
      _vendor/src/gopkg.in/yaml.v2/readerc.go
  62. 208 0
      _vendor/src/gopkg.in/yaml.v2/resolve.go
  63. 2710 0
      _vendor/src/gopkg.in/yaml.v2/scannerc.go
  64. 104 0
      _vendor/src/gopkg.in/yaml.v2/sorter.go
  65. 12 0
      _vendor/src/gopkg.in/yaml.v2/suite_test.go
  66. 89 0
      _vendor/src/gopkg.in/yaml.v2/writerc.go
  67. 346 0
      _vendor/src/gopkg.in/yaml.v2/yaml.go
  68. 716 0
      _vendor/src/gopkg.in/yaml.v2/yamlh.go
  69. 173 0
      _vendor/src/gopkg.in/yaml.v2/yamlprivateh.go
  70. 93 0
      config.go
  71. 12 0
      config.yml.example
  72. 26 0
      crypt.go
  73. 59 0
      download.go
  74. 147 0
      main.go
  75. 91 0
      process.go

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+bin/
+config.yml

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Sergey Alexandrovich
+
+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.

+ 22 - 0
Makefile

@@ -0,0 +1,22 @@
+current_dir := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+vendor      := $(current_dir)/_vendor
+goenv       := GOPATH="$(vendor):$(GOPATH)"
+
+all: clean vendorize build
+
+clean:
+	rm -rf bin/
+
+vendorize:
+	cd $(current_dir)
+	GOPATH=$(vendor) go get -d
+	find $(vendor) -name ".git" -type d | xargs rm -rf
+
+clean-vendor:
+	rm -rf $(vendor)
+
+hard-vendorize: clean-vendor vendorize
+
+build:
+	cd $(current_dir)
+	$(goenv) go build -v -ldflags '-w -s' -o bin/server

+ 12 - 0
_vendor/src/github.com/h2non/bimg/.editorconfig

@@ -0,0 +1,12 @@
+root = true
+
+[*]
+indent_style = tabs
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false

+ 8 - 0
_vendor/src/github.com/h2non/bimg/.gitignore

@@ -0,0 +1,8 @@
+/bimg
+/bundle
+bin
+/*.jpg
+/*.png
+/*.webp
+/fixtures/*_out.*
+/.idea/

+ 98 - 0
_vendor/src/github.com/h2non/bimg/.travis.yml

@@ -0,0 +1,98 @@
+language: go
+
+dist: trusty
+sudo: false
+
+go:
+  - 1.6
+  - 1.7
+  - 1.8
+  - tip
+
+env:
+  - LIBVIPS=7.42.3
+  - LIBVIPS=8.2.3
+  - LIBVIPS=8.3.3
+  - LIBVIPS=8.4.5
+  - LIBVIPS=8.5.5
+  - LIBVIPS=master
+
+matrix:
+  allow_failures:
+    - env: LIBVIPS=7.42.3
+    - env: LIBVIPS=8.2.3
+    - env: LIBVIPS=8.3.3
+
+cache:
+  apt:
+  directories:
+    - $HOME/libvips
+
+addons:
+  apt:
+    packages:
+      - gobject-introspection
+      - gtk-doc-tools
+      - libcfitsio3-dev
+      - libfftw3-dev
+      - libgif-dev
+      - libgs-dev
+      - libgsf-1-dev
+      - libmatio-dev
+      - libopenslide-dev
+      - liborc-0.4-dev
+      - libpango1.0-dev
+      - libpoppler-glib-dev
+      - libwebp-dev
+
+# VIPS 8.3.3 requires Poppler 0.30 which is not released on Trusty.
+before_install:
+  - >
+    test "$LIBVIPS" != "master" -a "$LIBVIPS" \< "8.5" \
+        && wget http://www.vips.ecs.soton.ac.uk/supported/${LIBVIPS%.*}/vips-${LIBVIPS}.tar.gz -O vips.tgz \
+        || echo ":-)"
+  - >
+    test "$LIBVIPS" != "master" -a "$LIBVIPS" \> "8.5" \
+        && wget https://github.com/jcupitt/libvips/releases/download/v${LIBVIPS}/vips-${LIBVIPS}.tar.gz -O vips.tgz \
+        || echo ":-)"
+  - >
+    test $LIBVIPS == "master"\
+        && wget https://github.com/jcupitt/libvips/archive/${LIBVIPS}.tar.gz -O vips.tgz \
+        || echo ":-)"
+  - mkdir libvips
+  - tar xf vips.tgz -C libvips --strip-components 1
+  - cd libvips
+  - test -f autogen.sh && ./autogen.sh || ./bootstrap.sh
+  - >
+    CXXFLAGS=-D_GLIBCXX_USE_CXX11_ABI=0
+    ./configure
+    --disable-debug
+    --disable-dependency-tracking
+    --disable-introspection
+    --disable-static
+    --enable-gtk-doc-html=no
+    --enable-gtk-doc=no
+    --enable-pyvips8=no
+    --without-orc
+    --without-python
+    --prefix=$HOME/libvips
+    $1
+  - make
+  - make install
+  - cd ..
+  - export PATH=$PATH:$HOME/libvips/bin
+  - export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$HOME/libvips/lib/pkgconfig
+  - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/libvips/lib
+  - vips --vips-version
+
+before_script:
+  - go get -u github.com/golang/lint/golint
+
+script:
+  - diff -u <(echo -n) <(gofmt -s -d ./)
+  - diff -u <(echo -n) <(go vet ./)
+  - diff -u <(echo -n) <(golint ./)
+  - go test -v -race -covermode=atomic -coverprofile=coverage.out
+
+after_success:
+  - goveralls -coverprofile=coverage.out -service=travis-ci

+ 85 - 0
_vendor/src/github.com/h2non/bimg/History.md

@@ -0,0 +1,85 @@
+
+## v1.0.9 / 2017-05-25
+
+  * Merge pull request #156 from Dynom/SmartCropToGravity
+  * Adding a test, verifying both ways of enabling SmartCrop work
+  * Merge pull request #149 from waldophotos/master
+  * Replacing SmartCrop with a Gravity option
+  * refactor(docs): v8.4
+  * Change for older LIBVIPS versions. `vips_bandjoin_const1` is added in libvips 8.2.
+  * Second try, watermarking memory issue fix
+
+## v1.0.8 / 2017-05-18
+
+  * Merge pull request #145 from greut/smartcrop
+  * Merge pull request #155 from greut/libvips8.5.5
+  * Update libvips to 8.5.5.
+  * Adding basic smartcrop support.
+  * Merge pull request #153 from abracadaber/master
+  * Added Linux Mint 17.3+ distro names
+  * feat(docs): add new maintainer notice (thanks to @kirillDanshin)
+  * Merge pull request #152 from greut/libvips85
+  * Download latest version of libvips from github.
+  * Merge pull request #147 from h2non/revert-143-master
+  * Revert "Fix for memory issue when watermarking images"
+  * Merge pull request #146 from greut/minor-major
+  * Merge pull request #143 from waldophotos/master
+  * Merge pull request #144 from greut/go18
+  * Fix tests where minor/major were mixed up
+  * Enabled go 1.8 builds.
+  * Fix the unref of images, when image isn't transparent
+  * Fix for memory issue when watermarking images
+  * feat(docs): add maintainers sections
+  * Merge pull request #132 from jaume-pinyol/WATERMARK_SUPPORT
+  * Add support for image watermarks
+  * Merge pull request #131 from greut/versions
+  * Running tests on more specific versions.
+  * refactor(preinstall.sh): remove deprecation notice
+  * Update preinstall.sh
+  * fix(requirements): required libvips 7.42
+  * fix(History): typo
+  * chore(History): add breaking change note
+
+## v1.0.7 / 13-01-2017
+
+- fix(#128): crop image calculation for missing width or height axis.
+- feat: add TIFF save output format (**note**: this introduces a minor interface breaking change in `bimg.IsImageTypeSupportedByVips` auxiliary function).
+
+## v1.0.6 / 12-11-2016
+
+- feat(#118): handle 16-bit PNGs.
+- feat(#119): adds JPEG2000 file for the type tests.
+- feat(#121): test bimg against multiple libvips versions.
+
+## v1.0.5 / 01-10-2016
+
+- feat(#92): support Extend param with optional background.
+- fix(#106): allow image area extraction without explicit x/y axis.
+- feat(api): add Extend type with `libvips` enum alias.
+
+## v1.0.4 / 29-09-2016
+
+- fix(#111): safe check of magick image type support.
+
+## v1.0.3 / 28-09-2016
+
+- fix(#95): better image type inference and support check.
+- fix(background): pass proper background RGB color for PNG image conversion.
+- feat(types): validate supported image types by current `libvips` compilation.
+- feat(types): consistent SVG image checking.
+- feat(api): add public functions `VipsIsTypeSupported()`, `IsImageTypeSupportedByVips()` and `IsSVGImage()`.
+
+## v1.0.2 / 27-09-2016
+
+- feat(#95): support GIF, SVG and PDF formats.
+- fix(#108): auto-width and height calculations now round instead of floor.
+
+## v1.0.1 / 22-06-2016
+
+- fix(#90): Do not not dereference the original image a second time.
+
+## v1.0.0 / 21-04-2016
+
+- refactor(api): breaking changes: normalize public members to follow Go naming idioms.
+- feat(version): bump to major version. API contract won't be compromised in `v1`.
+- feat(docs): add missing inline godoc documentation.

+ 24 - 0
_vendor/src/github.com/h2non/bimg/LICENSE

@@ -0,0 +1,24 @@
+The MIT License
+
+Copyright (c) Tomas Aparicio and contributors
+
+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.

+ 347 - 0
_vendor/src/github.com/h2non/bimg/README.md

@@ -0,0 +1,347 @@
+# bimg [![Build Status](https://travis-ci.org/h2non/bimg.svg)](https://travis-ci.org/h2non/bimg) [![GoDoc](https://godoc.org/github.com/h2non/bimg?status.svg)](https://godoc.org/github.com/h2non/bimg) [![Go Report Card](http://goreportcard.com/badge/h2non/bimg)](http://goreportcard.com/report/h2non/bimg) [![Coverage Status](https://coveralls.io/repos/github/h2non/bimg/badge.svg?branch=master)](https://coveralls.io/github/h2non/bimg?branch=master) ![License](https://img.shields.io/badge/license-MIT-blue.svg)
+
+Small [Go](http://golang.org) package for fast high-level image processing using [libvips](https://github.com/jcupitt/libvips) via C bindings, providing a simple, elegant and fluent [programmatic API](#examples).
+
+bimg was designed to be a small and efficient library supporting a common set of [image operations](#supported-image-operations) such as crop, resize, rotate, zoom or watermark. It can read JPEG, PNG, WEBP natively, and optionally TIFF, PDF, GIF and SVG formats if `libvips@8.3+` is compiled with proper library bindings.
+
+bimg is able to output images as JPEG, PNG and WEBP formats, including transparent conversion across them.
+
+bimg uses internally libvips, a powerful library written in C for image processing which requires a [low memory footprint](http://www.vips.ecs.soton.ac.uk/index.php?title=Speed_and_Memory_Use)
+and it's typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings or Go native `image` package, and in some cases it's even 8x faster processing JPEG images.
+
+If you're looking for an HTTP based image processing solution, see [imaginary](https://github.com/h2non/imaginary).
+
+bimg was heavily inspired in [sharp](https://github.com/lovell/sharp), its homologous package built for [node.js](http://nodejs.org). bimg is used in production environments processing thousands of images per day.
+
+**v1 notice**: `bimg` introduces some minor breaking changes in `v1` release.
+If you're using `gopkg.in`, you can still rely in the `v0` without worrying about API breaking changes.
+
+`bimg` is currently maintained by [Kirill Danshin](https://github.com/kirillDanshin).
+
+## Contents
+
+- [Supported image operations](#supported-image-operations)
+- [Prerequisites](#prerequisites)
+- [Installation](#installation)
+- [Performance](#performance)
+- [Benchmark](#benchmark)
+- [Examples](#examples)
+- [Debugging](#debugging)
+- [API](#api)
+- [Authors](#authors)
+- [Credits](#credits)
+
+## Supported image operations
+
+- Resize
+- Enlarge
+- Crop (including smart crop support)
+- Rotate (with auto-rotate based on EXIF orientation)
+- Flip (with auto-flip based on EXIF metadata)
+- Flop
+- Zoom
+- Thumbnail
+- Extract area
+- Watermark (using text or image)
+- Gaussian blur effect
+- Custom output color space (RGB, grayscale...)
+- Format conversion (with additional quality/compression settings)
+- EXIF metadata (size, alpha channel, profile, orientation...)
+
+## Prerequisites
+
+- [libvips](https://github.com/jcupitt/libvips) 7.42+ or 8+ (8.4+ recommended)
+- C compatible compiler such as gcc 4.6+ or clang 3.0+
+- Go 1.3+
+
+**Note**: `libvips` v8.3+ is required for GIF, PDF and SVG support.
+
+## Installation
+
+```bash
+go get -u gopkg.in/h2non/bimg.v1
+```
+
+### libvips
+
+Run the following script as `sudo` (supports OSX, Debian/Ubuntu, Redhat, Fedora, Amazon Linux):
+```bash
+curl -s https://raw.githubusercontent.com/h2non/bimg/master/preinstall.sh | sudo bash -
+```
+
+If you wanna take the advantage of [OpenSlide](http://openslide.org/), simply add `--with-openslide` to enable it:
+```bash
+curl -s https://raw.githubusercontent.com/h2non/bimg/master/preinstall.sh | sudo bash -s --with-openslide
+```
+
+The [install script](https://github.com/h2non/bimg/blob/master/preinstall.sh) requires `curl` and `pkg-config`.
+
+## Performance
+
+libvips is probably the faster open source solution for image processing.
+Here you can see some performance test comparisons for multiple scenarios:
+
+- [libvips speed and memory usage](http://www.vips.ecs.soton.ac.uk/index.php?title=Speed_and_Memory_Use)
+
+## Benchmark
+
+Tested using Go 1.5.1 and libvips-7.42.3 in OSX i7 2.7Ghz
+```
+BenchmarkRotateJpeg-8     	      20	  64686945 ns/op
+BenchmarkResizeLargeJpeg-8	      20	  63390416 ns/op
+BenchmarkResizePng-8      	     100	  18147294 ns/op
+BenchmarkResizeWebP-8     	     100	  20836741 ns/op
+BenchmarkConvertToJpeg-8  	     100	  12831812 ns/op
+BenchmarkConvertToPng-8   	      10	 128901422 ns/op
+BenchmarkConvertToWebp-8  	      10	 204027990 ns/op
+BenchmarkCropJpeg-8       	      30	  59068572 ns/op
+BenchmarkCropPng-8        	      10	 117303259 ns/op
+BenchmarkCropWebP-8       	      10	 107060659 ns/op
+BenchmarkExtractJpeg-8    	      50	  30708919 ns/op
+BenchmarkExtractPng-8     	    3000	    595546 ns/op
+BenchmarkExtractWebp-8    	    3000	    386379 ns/op
+BenchmarkZoomJpeg-8       	      10	 160005424 ns/op
+BenchmarkZoomPng-8        	      30	  44561047 ns/op
+BenchmarkZoomWebp-8       	      10	 126732678 ns/op
+BenchmarkWatermarkJpeg-8  	      20	  79006133 ns/op
+BenchmarkWatermarPng-8    	     200	   8197291 ns/op
+BenchmarkWatermarWebp-8   	      30	  49360369 ns/op
+```
+
+## Examples
+
+```go
+import (
+  "fmt"
+  "os"
+  "gopkg.in/h2non/bimg.v1"
+)
+```
+
+#### Resize
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Resize(800, 600)
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+size, err := bimg.NewImage(newImage).Size()
+if size.Width == 400 && size.Height == 300 {
+  fmt.Println("The image size is valid")
+}
+
+bimg.Write("new.jpg", newImage)
+```
+
+#### Rotate
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Rotate(90)
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+bimg.Write("new.jpg", newImage)
+```
+
+#### Convert
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Convert(bimg.PNG)
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+if bimg.NewImage(newImage).Type() == "png" {
+  fmt.Fprintln(os.Stderr, "The image was converted into png")
+}
+```
+
+#### Force resize
+
+Force resize operation without perserving the aspect ratio:
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).ForceResize(1000, 500)
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+size := bimg.Size(newImage)
+if size.Width != 1000 || size.Height != 500 {
+  fmt.Fprintln(os.Stderr, "Incorrect image size")
+}
+```
+
+#### Custom colour space (black & white)
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Colourspace(bimg.INTERPRETATION_B_W)
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+colourSpace, _ := bimg.ImageInterpretation(newImage)
+if colourSpace != bimg.INTERPRETATION_B_W {
+  fmt.Fprintln(os.Stderr, "Invalid colour space")
+}
+```
+
+#### Custom options
+
+See [Options](https://godoc.org/github.com/h2non/bimg#Options) struct to discover all the available fields
+
+```go
+options := bimg.Options{
+  Width:        800,
+  Height:       600,
+  Crop:         true,
+  Quality:      95,
+  Rotate:       180,
+  Interlace:    true,
+}
+
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Process(options)
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+bimg.Write("new.jpg", newImage)
+```
+
+#### Watermark
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+watermark := bimg.Watermark{
+  Text:       "Chuck Norris (c) 2315",
+  Opacity:    0.25,
+  Width:      200,
+  DPI:        100,
+  Margin:     150,
+  Font:       "sans bold 12",
+  Background: bimg.Color{255, 255, 255},
+}
+
+newImage, err := bimg.NewImage(buffer).Watermark(watermark)
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+bimg.Write("new.jpg", newImage)
+```
+
+#### Fluent interface
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+image := bimg.NewImage(buffer)
+
+// first crop image
+_, err := image.CropByWidth(300)
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+// then flip it
+newImage, err := image.Flip()
+if err != nil {
+  fmt.Fprintln(os.Stderr, err)
+}
+
+// save the cropped and flipped image
+bimg.Write("new.jpg", newImage)
+```
+
+## Debugging
+
+Run the process passing the `DEBUG` environment variable
+```
+DEBUG=bimg ./app
+```
+
+Enable libvips traces (note that a lot of data will be written in stdout):
+```
+VIPS_TRACE=1 ./app
+```
+
+You can also dump a core on failure, as [John Cuppit](https://github.com/jcupitt) said:
+```c
+g_log_set_always_fatal(
+                G_LOG_FLAG_RECURSION |
+                G_LOG_FLAG_FATAL |
+                G_LOG_LEVEL_ERROR |
+                G_LOG_LEVEL_CRITICAL |
+                G_LOG_LEVEL_WARNING );
+```
+
+Or set the G_DEBUG environment variable:
+```
+export G_DEBUG=fatal-warnings,fatal-criticals
+```
+
+## API
+
+See [godoc reference](https://godoc.org/github.com/h2non/bimg) for detailed API documentation.
+
+## Authors
+
+- [Tomás Aparicio](https://github.com/h2non) - Original author and architect.
+- [Kirill Danshin](https://github.com/kirillDanshin) - Maintainer since April 2017.
+
+## Credits
+
+People who recurrently contributed to improve `bimg` in some way.
+
+- [John Cupitt](https://github.com/jcupitt)
+- [Yoan Blanc](https://github.com/greut)
+- [Christophe Eblé](https://github.com/chreble)
+- [Brant Fitzsimmons](https://github.com/bfitzsimmons)
+- [Thomas Meson](https://github.com/zllak)
+
+Thank you!
+
+## License
+
+MIT - Tomas Aparicio
+
+[![views](https://sourcegraph.com/api/repos/github.com/h2non/bimg/.counters/views.svg)](https://sourcegraph.com/github.com/h2non/bimg)

+ 15 - 0
_vendor/src/github.com/h2non/bimg/file.go

@@ -0,0 +1,15 @@
+package bimg
+
+import "io/ioutil"
+
+// Read reads all the content of the given file path
+// and returns it as byte buffer.
+func Read(path string) ([]byte, error) {
+	return ioutil.ReadFile(path)
+}
+
+// Write writes the given byte buffer into disk
+// to the given file path.
+func Write(path string, buf []byte) error {
+	return ioutil.WriteFile(path, buf, 0644)
+}

+ 38 - 0
_vendor/src/github.com/h2non/bimg/file_test.go

@@ -0,0 +1,38 @@
+package bimg
+
+import (
+	"testing"
+)
+
+func TestRead(t *testing.T) {
+	buf, err := Read("fixtures/test.jpg")
+
+	if err != nil {
+		t.Errorf("Cannot read the image: %#v", err)
+	}
+
+	if len(buf) == 0 {
+		t.Fatal("Empty buffer")
+	}
+
+	if DetermineImageType(buf) != JPEG {
+		t.Fatal("Image is not jpeg")
+	}
+}
+
+func TestWrite(t *testing.T) {
+	buf, err := Read("fixtures/test.jpg")
+
+	if err != nil {
+		t.Errorf("Cannot read the image: %#v", err)
+	}
+
+	if len(buf) == 0 {
+		t.Fatal("Empty buffer")
+	}
+
+	err = Write("fixtures/test_write_out.jpg", buf)
+	if err != nil {
+		t.Fatalf("Cannot write the file: %#v", err)
+	}
+}

BIN
_vendor/src/github.com/h2non/bimg/fixtures/corrupt.jpg


BIN
_vendor/src/github.com/h2non/bimg/fixtures/northern_cardinal_bird.jpg


BIN
_vendor/src/github.com/h2non/bimg/fixtures/test.gif


BIN
_vendor/src/github.com/h2non/bimg/fixtures/test.jp2


BIN
_vendor/src/github.com/h2non/bimg/fixtures/test.jpg


BIN
_vendor/src/github.com/h2non/bimg/fixtures/test.pdf


BIN
_vendor/src/github.com/h2non/bimg/fixtures/test.png


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 346 - 0
_vendor/src/github.com/h2non/bimg/fixtures/test.svg


BIN
_vendor/src/github.com/h2non/bimg/fixtures/test.webp


+ 0 - 0
_vendor/src/github.com/h2non/bimg/fixtures/test_gif.jpg


BIN
_vendor/src/github.com/h2non/bimg/fixtures/test_icc_prophoto.jpg


BIN
_vendor/src/github.com/h2non/bimg/fixtures/test_issue.jpg


+ 0 - 0
_vendor/src/github.com/h2non/bimg/fixtures/test_pdf.jpg


BIN
_vendor/src/github.com/h2non/bimg/fixtures/test_square.jpg


+ 0 - 0
_vendor/src/github.com/h2non/bimg/fixtures/test_svg.jpg


BIN
_vendor/src/github.com/h2non/bimg/fixtures/transparent.png


BIN
_vendor/src/github.com/h2non/bimg/fixtures/vertical.jpg


+ 223 - 0
_vendor/src/github.com/h2non/bimg/image.go

@@ -0,0 +1,223 @@
+package bimg
+
+// Image provides a simple method DSL to transform a given image as byte buffer.
+type Image struct {
+	buffer []byte
+}
+
+// NewImage creates a new Image struct with method DSL.
+func NewImage(buf []byte) *Image {
+	return &Image{buf}
+}
+
+// Resize resizes the image to fixed width and height.
+func (i *Image) Resize(width, height int) ([]byte, error) {
+	options := Options{
+		Width:  width,
+		Height: height,
+		Embed:  true,
+	}
+	return i.Process(options)
+}
+
+// ForceResize resizes with custom size (aspect ratio won't be maintained).
+func (i *Image) ForceResize(width, height int) ([]byte, error) {
+	options := Options{
+		Width:  width,
+		Height: height,
+		Force:  true,
+	}
+	return i.Process(options)
+}
+
+// ResizeAndCrop resizes the image to fixed width and height with additional crop transformation.
+func (i *Image) ResizeAndCrop(width, height int) ([]byte, error) {
+	options := Options{
+		Width:  width,
+		Height: height,
+		Embed:  true,
+		Crop:   true,
+	}
+	return i.Process(options)
+}
+
+// SmartCrop produces a thumbnail aiming at focus on the interesting part.
+func (i *Image) SmartCrop(width, height int) ([]byte, error) {
+	options := Options{
+		Width:   width,
+		Height:  height,
+		Crop:    true,
+		Gravity: GravitySmart,
+	}
+	return i.Process(options)
+}
+
+// Extract area from the by X/Y axis in the current image.
+func (i *Image) Extract(top, left, width, height int) ([]byte, error) {
+	options := Options{
+		Top:        top,
+		Left:       left,
+		AreaWidth:  width,
+		AreaHeight: height,
+	}
+
+	if top == 0 && left == 0 {
+		options.Top = -1
+	}
+
+	return i.Process(options)
+}
+
+// Enlarge enlarges the image by width and height. Aspect ratio is maintained.
+func (i *Image) Enlarge(width, height int) ([]byte, error) {
+	options := Options{
+		Width:   width,
+		Height:  height,
+		Enlarge: true,
+	}
+	return i.Process(options)
+}
+
+// EnlargeAndCrop enlarges the image by width and height with additional crop transformation.
+func (i *Image) EnlargeAndCrop(width, height int) ([]byte, error) {
+	options := Options{
+		Width:   width,
+		Height:  height,
+		Enlarge: true,
+		Crop:    true,
+	}
+	return i.Process(options)
+}
+
+// Crop crops the image to the exact size specified.
+func (i *Image) Crop(width, height int, gravity Gravity) ([]byte, error) {
+	options := Options{
+		Width:   width,
+		Height:  height,
+		Gravity: gravity,
+		Crop:    true,
+	}
+	return i.Process(options)
+}
+
+// CropByWidth crops an image by width only param (auto height).
+func (i *Image) CropByWidth(width int) ([]byte, error) {
+	options := Options{
+		Width: width,
+		Crop:  true,
+	}
+	return i.Process(options)
+}
+
+// CropByHeight crops an image by height (auto width).
+func (i *Image) CropByHeight(height int) ([]byte, error) {
+	options := Options{
+		Height: height,
+		Crop:   true,
+	}
+	return i.Process(options)
+}
+
+// Thumbnail creates a thumbnail of the image by the a given width by aspect ratio 4:4.
+func (i *Image) Thumbnail(pixels int) ([]byte, error) {
+	options := Options{
+		Width:   pixels,
+		Height:  pixels,
+		Crop:    true,
+		Quality: 95,
+	}
+	return i.Process(options)
+}
+
+// Watermark adds text as watermark on the given image.
+func (i *Image) Watermark(w Watermark) ([]byte, error) {
+	options := Options{Watermark: w}
+	return i.Process(options)
+}
+
+// WatermarkImage adds image as watermark on the given image.
+func (i *Image) WatermarkImage(w WatermarkImage) ([]byte, error) {
+	options := Options{WatermarkImage: w}
+	return i.Process(options)
+}
+
+// Zoom zooms the image by the given factor.
+// You should probably call Extract() before.
+func (i *Image) Zoom(factor int) ([]byte, error) {
+	options := Options{Zoom: factor}
+	return i.Process(options)
+}
+
+// Rotate rotates the image by given angle degrees (0, 90, 180 or 270).
+func (i *Image) Rotate(a Angle) ([]byte, error) {
+	options := Options{Rotate: a}
+	return i.Process(options)
+}
+
+// Flip flips the image about the vertical Y axis.
+func (i *Image) Flip() ([]byte, error) {
+	options := Options{Flip: true}
+	return i.Process(options)
+}
+
+// Flop flops the image about the horizontal X axis.
+func (i *Image) Flop() ([]byte, error) {
+	options := Options{Flop: true}
+	return i.Process(options)
+}
+
+// Convert converts image to another format.
+func (i *Image) Convert(t ImageType) ([]byte, error) {
+	options := Options{Type: t}
+	return i.Process(options)
+}
+
+// Colourspace performs a color space conversion bsaed on the given interpretation.
+func (i *Image) Colourspace(c Interpretation) ([]byte, error) {
+	options := Options{Interpretation: c}
+	return i.Process(options)
+}
+
+// Process processes the image based on the given transformation options,
+// talking with libvips bindings accordingly and returning the resultant
+// image buffer.
+func (i *Image) Process(o Options) ([]byte, error) {
+	image, err := Resize(i.buffer, o)
+	if err != nil {
+		return nil, err
+	}
+	i.buffer = image
+	return image, nil
+}
+
+// Metadata returns the image metadata (size, alpha channel, profile, EXIF rotation).
+func (i *Image) Metadata() (ImageMetadata, error) {
+	return Metadata(i.buffer)
+}
+
+// Interpretation gets the image interpretation type.
+// See: http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsImage.html#VipsInterpretation
+func (i *Image) Interpretation() (Interpretation, error) {
+	return ImageInterpretation(i.buffer)
+}
+
+// ColourspaceIsSupported checks if the current image
+// color space is supported.
+func (i *Image) ColourspaceIsSupported() (bool, error) {
+	return ColourspaceIsSupported(i.buffer)
+}
+
+// Type returns the image type format (jpeg, png, webp, tiff).
+func (i *Image) Type() string {
+	return DetermineImageTypeName(i.buffer)
+}
+
+// Size returns the image size as form of width and height pixels.
+func (i *Image) Size() (ImageSize, error) {
+	return Size(i.buffer)
+}
+
+// Image returns the current resultant image image buffer.
+func (i *Image) Image() []byte {
+	return i.buffer
+}

+ 496 - 0
_vendor/src/github.com/h2non/bimg/image_test.go

@@ -0,0 +1,496 @@
+package bimg
+
+import (
+	"fmt"
+	"path"
+	"testing"
+)
+
+func TestImageResize(t *testing.T) {
+	buf, err := initImage("test.jpg").Resize(300, 240)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	err = assertSize(buf, 300, 240)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_resize_out.jpg", buf)
+}
+
+func TestImageGifResize(t *testing.T) {
+	_, err := initImage("test.gif").Resize(300, 240)
+	if err == nil {
+		t.Errorf("GIF shouldn't be saved within VIPS")
+	}
+}
+
+func TestImagePdfResize(t *testing.T) {
+	_, err := initImage("test.pdf").Resize(300, 240)
+	if err == nil {
+		t.Errorf("PDF cannot be saved within VIPS")
+	}
+}
+
+func TestImageSvgResize(t *testing.T) {
+	_, err := initImage("test.svg").Resize(300, 240)
+	if err == nil {
+		t.Errorf("SVG cannot be saved within VIPS")
+	}
+}
+
+func TestImageGifToJpeg(t *testing.T) {
+	if VipsMajorVersion >= 8 && VipsMinorVersion > 2 {
+		i := initImage("test.gif")
+		options := Options{
+			Type: JPEG,
+		}
+		buf, err := i.Process(options)
+		if err != nil {
+			t.Errorf("Cannot process the image: %#v", err)
+		}
+
+		Write("fixtures/test_gif.jpg", buf)
+	}
+}
+
+func TestImagePdfToJpeg(t *testing.T) {
+	if VipsMajorVersion >= 8 && VipsMinorVersion > 2 {
+		i := initImage("test.pdf")
+		options := Options{
+			Type: JPEG,
+		}
+		buf, err := i.Process(options)
+		if err != nil {
+			t.Errorf("Cannot process the image: %#v", err)
+		}
+
+		Write("fixtures/test_pdf.jpg", buf)
+	}
+}
+
+func TestImageSvgToJpeg(t *testing.T) {
+	if VipsMajorVersion >= 8 && VipsMinorVersion > 2 {
+		i := initImage("test.svg")
+		options := Options{
+			Type: JPEG,
+		}
+		buf, err := i.Process(options)
+		if err != nil {
+			t.Errorf("Cannot process the image: %#v", err)
+		}
+
+		Write("fixtures/test_svg.jpg", buf)
+	}
+}
+
+func TestImageResizeAndCrop(t *testing.T) {
+	buf, err := initImage("test.jpg").ResizeAndCrop(300, 200)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	err = assertSize(buf, 300, 200)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_resize_crop_out.jpg", buf)
+}
+
+func TestImageExtract(t *testing.T) {
+	buf, err := initImage("test.jpg").Extract(100, 100, 300, 200)
+	if err != nil {
+		t.Errorf("Cannot process the image: %s", err)
+	}
+
+	err = assertSize(buf, 300, 200)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_extract_out.jpg", buf)
+}
+
+func TestImageExtractZero(t *testing.T) {
+	buf, err := initImage("test.jpg").Extract(0, 0, 300, 200)
+	if err != nil {
+		t.Errorf("Cannot process the image: %s", err)
+	}
+
+	err = assertSize(buf, 300, 200)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_extract_zero_out.jpg", buf)
+}
+
+func TestImageEnlarge(t *testing.T) {
+	buf, err := initImage("test.png").Enlarge(500, 375)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	err = assertSize(buf, 500, 375)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_enlarge_out.jpg", buf)
+}
+
+func TestImageEnlargeAndCrop(t *testing.T) {
+	buf, err := initImage("test.png").EnlargeAndCrop(800, 480)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	err = assertSize(buf, 800, 480)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_enlarge_crop_out.jpg", buf)
+}
+
+func TestImageCrop(t *testing.T) {
+	buf, err := initImage("test.jpg").Crop(800, 600, GravityNorth)
+	if err != nil {
+		t.Errorf("Cannot process the image: %s", err)
+	}
+
+	err = assertSize(buf, 800, 600)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_crop_out.jpg", buf)
+}
+
+func TestImageCropByWidth(t *testing.T) {
+	buf, err := initImage("test.jpg").CropByWidth(600)
+	if err != nil {
+		t.Errorf("Cannot process the image: %s", err)
+	}
+
+	err = assertSize(buf, 600, 1050)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_crop_width_out.jpg", buf)
+}
+
+func TestImageCropByHeight(t *testing.T) {
+	buf, err := initImage("test.jpg").CropByHeight(300)
+	if err != nil {
+		t.Errorf("Cannot process the image: %s", err)
+	}
+
+	err = assertSize(buf, 1680, 300)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_crop_height_out.jpg", buf)
+}
+
+func TestImageThumbnail(t *testing.T) {
+	buf, err := initImage("test.jpg").Thumbnail(100)
+	if err != nil {
+		t.Errorf("Cannot process the image: %s", err)
+	}
+
+	err = assertSize(buf, 100, 100)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_thumbnail_out.jpg", buf)
+}
+
+func TestImageWatermark(t *testing.T) {
+	image := initImage("test.jpg")
+	_, err := image.Crop(800, 600, GravityNorth)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	buf, err := image.Watermark(Watermark{
+		Text:       "Copy me if you can",
+		Opacity:    0.5,
+		Width:      200,
+		DPI:        100,
+		Background: Color{255, 255, 255},
+	})
+	if err != nil {
+		t.Error(err)
+	}
+
+	err = assertSize(buf, 800, 600)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if DetermineImageType(buf) != JPEG {
+		t.Fatal("Image is not jpeg")
+	}
+
+	Write("fixtures/test_watermark_text_out.jpg", buf)
+}
+
+func TestImageWatermarkWithImage(t *testing.T) {
+	image := initImage("test.jpg")
+	watermark, _ := imageBuf("transparent.png")
+
+	_, err := image.Crop(800, 600, GravityNorth)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	buf, err := image.WatermarkImage(WatermarkImage{Left: 100, Top: 100, Buf: watermark})
+
+	if err != nil {
+		t.Error(err)
+	}
+
+	err = assertSize(buf, 800, 600)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if DetermineImageType(buf) != JPEG {
+		t.Fatal("Image is not jpeg")
+	}
+
+	Write("fixtures/test_watermark_image_out.jpg", buf)
+}
+
+func TestImageWatermarkNoReplicate(t *testing.T) {
+	image := initImage("test.jpg")
+	_, err := image.Crop(800, 600, GravityNorth)
+	if err != nil {
+		t.Errorf("Cannot process the image: %s", err)
+	}
+
+	buf, err := image.Watermark(Watermark{
+		Text:        "Copy me if you can",
+		Opacity:     0.5,
+		Width:       200,
+		DPI:         100,
+		NoReplicate: true,
+		Background:  Color{255, 255, 255},
+	})
+	if err != nil {
+		t.Error(err)
+	}
+
+	err = assertSize(buf, 800, 600)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if DetermineImageType(buf) != JPEG {
+		t.Fatal("Image is not jpeg")
+	}
+
+	Write("fixtures/test_watermark_replicate_out.jpg", buf)
+}
+
+func TestImageZoom(t *testing.T) {
+	image := initImage("test.jpg")
+
+	_, err := image.Extract(100, 100, 400, 300)
+	if err != nil {
+		t.Errorf("Cannot extract the image: %s", err)
+	}
+
+	buf, err := image.Zoom(1)
+	if err != nil {
+		t.Errorf("Cannot process the image: %s", err)
+	}
+
+	err = assertSize(buf, 800, 600)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_zoom_out.jpg", buf)
+}
+
+func TestImageFlip(t *testing.T) {
+	buf, err := initImage("test.jpg").Flip()
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+	Write("fixtures/test_flip_out.jpg", buf)
+}
+
+func TestImageFlop(t *testing.T) {
+	buf, err := initImage("test.jpg").Flop()
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+	Write("fixtures/test_flop_out.jpg", buf)
+}
+
+func TestImageRotate(t *testing.T) {
+	buf, err := initImage("test_flip_out.jpg").Rotate(90)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+	Write("fixtures/test_image_rotate_out.jpg", buf)
+}
+
+func TestImageConvert(t *testing.T) {
+	buf, err := initImage("test.jpg").Convert(PNG)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+	Write("fixtures/test_image_convert_out.png", buf)
+}
+
+func TestTransparentImageConvert(t *testing.T) {
+	image := initImage("transparent.png")
+	options := Options{
+		Type:       JPEG,
+		Background: Color{255, 255, 255},
+	}
+	buf, err := image.Process(options)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+	Write("fixtures/test_transparent_image_convert_out.jpg", buf)
+}
+
+func TestImageMetadata(t *testing.T) {
+	data, err := initImage("test.png").Metadata()
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+	if data.Alpha != true {
+		t.Fatal("Invalid alpha channel")
+	}
+	if data.Size.Width != 400 {
+		t.Fatal("Invalid width size")
+	}
+	if data.Type != "png" {
+		t.Fatal("Invalid image type")
+	}
+}
+
+func TestInterpretation(t *testing.T) {
+	interpretation, err := initImage("test.jpg").Interpretation()
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+	if interpretation != InterpretationSRGB {
+		t.Errorf("Invalid interpretation: %d", interpretation)
+	}
+}
+
+func TestImageColourspace(t *testing.T) {
+	tests := []struct {
+		file           string
+		interpretation Interpretation
+	}{
+		{"test.jpg", InterpretationSRGB},
+		{"test.jpg", InterpretationBW},
+	}
+
+	for _, test := range tests {
+		buf, err := initImage(test.file).Colourspace(test.interpretation)
+		if err != nil {
+			t.Errorf("Cannot process the image: %#v", err)
+		}
+
+		interpretation, err := ImageInterpretation(buf)
+		if interpretation != test.interpretation {
+			t.Errorf("Invalid colourspace")
+		}
+	}
+}
+
+func TestImageColourspaceIsSupported(t *testing.T) {
+	supported, err := initImage("test.jpg").ColourspaceIsSupported()
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+	if supported != true {
+		t.Errorf("Non-supported colourspace")
+	}
+}
+
+func TestFluentInterface(t *testing.T) {
+	image := initImage("test.jpg")
+	_, err := image.CropByWidth(300)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	_, err = image.Flip()
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	_, err = image.Convert(PNG)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	data, _ := image.Metadata()
+	if data.Alpha != false {
+		t.Fatal("Invalid alpha channel")
+	}
+	if data.Size.Width != 300 {
+		t.Fatal("Invalid width size")
+	}
+	if data.Type != "png" {
+		t.Fatal("Invalid image type")
+	}
+
+	Write("fixtures/test_image_fluent_out.png", image.Image())
+}
+
+func TestImageSmartCrop(t *testing.T) {
+
+	if !(VipsMajorVersion >= 8 && VipsMinorVersion > 4) {
+		t.Skipf("Skipping this test, libvips doesn't meet version requirement %s > 8.4", VipsVersion)
+	}
+
+	i := initImage("northern_cardinal_bird.jpg")
+	buf, err := i.SmartCrop(300, 300)
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+
+	err = assertSize(buf, 300, 300)
+	if err != nil {
+		t.Error(err)
+	}
+
+	Write("fixtures/test_smart_crop.jpg", buf)
+}
+
+func initImage(file string) *Image {
+	buf, _ := imageBuf(file)
+	return NewImage(buf)
+}
+
+func imageBuf(file string) ([]byte, error) {
+	return Read(path.Join("fixtures", file))
+}
+
+func assertSize(buf []byte, width, height int) error {
+	size, err := NewImage(buf).Size()
+	if err != nil {
+		return err
+	}
+	if size.Width != width || size.Height != height {
+		return fmt.Errorf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+	return nil
+}

+ 77 - 0
_vendor/src/github.com/h2non/bimg/metadata.go

@@ -0,0 +1,77 @@
+package bimg
+
+/*
+#cgo pkg-config: vips
+#include "vips/vips.h"
+*/
+import "C"
+
+// ImageSize represents the image width and height values
+type ImageSize struct {
+	Width  int
+	Height int
+}
+
+// ImageMetadata represents the basic metadata fields
+type ImageMetadata struct {
+	Orientation int
+	Channels    int
+	Alpha       bool
+	Profile     bool
+	Type        string
+	Space       string
+	Colourspace string
+	Size        ImageSize
+}
+
+// Size returns the image size by width and height pixels.
+func Size(buf []byte) (ImageSize, error) {
+	metadata, err := Metadata(buf)
+	if err != nil {
+		return ImageSize{}, err
+	}
+
+	return ImageSize{
+		Width:  int(metadata.Size.Width),
+		Height: int(metadata.Size.Height),
+	}, nil
+}
+
+// ColourspaceIsSupported checks if the image colourspace is supported by libvips.
+func ColourspaceIsSupported(buf []byte) (bool, error) {
+	return vipsColourspaceIsSupportedBuffer(buf)
+}
+
+// ImageInterpretation returns the image interpretation type.
+// See: http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsImage.html#VipsInterpretation
+func ImageInterpretation(buf []byte) (Interpretation, error) {
+	return vipsInterpretationBuffer(buf)
+}
+
+// Metadata returns the image metadata (size, type, alpha channel, profile, EXIF orientation...).
+func Metadata(buf []byte) (ImageMetadata, error) {
+	defer C.vips_thread_shutdown()
+
+	image, imageType, err := vipsRead(buf)
+	if err != nil {
+		return ImageMetadata{}, err
+	}
+	defer C.g_object_unref(C.gpointer(image))
+
+	size := ImageSize{
+		Width:  int(image.Xsize),
+		Height: int(image.Ysize),
+	}
+
+	metadata := ImageMetadata{
+		Size:        size,
+		Channels:    int(image.Bands),
+		Orientation: vipsExifOrientation(image),
+		Alpha:       vipsHasAlpha(image),
+		Profile:     vipsHasProfile(image),
+		Space:       vipsSpace(image),
+		Type:        ImageTypeName(imageType),
+	}
+
+	return metadata, nil
+}

+ 124 - 0
_vendor/src/github.com/h2non/bimg/metadata_test.go

@@ -0,0 +1,124 @@
+package bimg
+
+import (
+	"io/ioutil"
+	"os"
+	"path"
+	"testing"
+)
+
+func TestSize(t *testing.T) {
+	files := []struct {
+		name   string
+		width  int
+		height int
+	}{
+		{"test.jpg", 1680, 1050},
+		{"test.png", 400, 300},
+		{"test.webp", 550, 368},
+	}
+	for _, file := range files {
+		size, err := Size(readFile(file.name))
+		if err != nil {
+			t.Fatalf("Cannot read the image: %#v", err)
+		}
+
+		if size.Width != file.width || size.Height != file.height {
+			t.Fatalf("Unexpected image size: %dx%d", size.Width, size.Height)
+		}
+	}
+}
+
+func TestMetadata(t *testing.T) {
+	files := []struct {
+		name        string
+		format      string
+		orientation int
+		alpha       bool
+		profile     bool
+		space       string
+	}{
+		{"test.jpg", "jpeg", 0, false, false, "srgb"},
+		{"test_icc_prophoto.jpg", "jpeg", 0, false, true, "srgb"},
+		{"test.png", "png", 0, true, false, "srgb"},
+		{"test.webp", "webp", 0, false, false, "srgb"},
+	}
+
+	for _, file := range files {
+		metadata, err := Metadata(readFile(file.name))
+		if err != nil {
+			t.Fatalf("Cannot read the image: %s -> %s", file.name, err)
+		}
+
+		if metadata.Type != file.format {
+			t.Fatalf("Unexpected image format: %s", file.format)
+		}
+		if metadata.Orientation != file.orientation {
+			t.Fatalf("Unexpected image orientation: %d != %d", metadata.Orientation, file.orientation)
+		}
+		if metadata.Alpha != file.alpha {
+			t.Fatalf("Unexpected image alpha: %t != %t", metadata.Alpha, file.alpha)
+		}
+		if metadata.Profile != file.profile {
+			t.Fatalf("Unexpected image profile: %t != %t", metadata.Profile, file.profile)
+		}
+		if metadata.Space != file.space {
+			t.Fatalf("Unexpected image profile: %t != %t", metadata.Profile, file.profile)
+		}
+	}
+}
+
+func TestImageInterpretation(t *testing.T) {
+	files := []struct {
+		name           string
+		interpretation Interpretation
+	}{
+		{"test.jpg", InterpretationSRGB},
+		{"test.png", InterpretationSRGB},
+		{"test.webp", InterpretationSRGB},
+	}
+
+	for _, file := range files {
+		interpretation, err := ImageInterpretation(readFile(file.name))
+		if err != nil {
+			t.Fatalf("Cannot read the image: %s -> %s", file.name, err)
+		}
+		if interpretation != file.interpretation {
+			t.Fatalf("Unexpected image interpretation")
+		}
+	}
+}
+
+func TestColourspaceIsSupported(t *testing.T) {
+	files := []struct {
+		name string
+	}{
+		{"test.jpg"},
+		{"test.png"},
+		{"test.webp"},
+	}
+
+	for _, file := range files {
+		supported, err := ColourspaceIsSupported(readFile(file.name))
+		if err != nil {
+			t.Fatalf("Cannot read the image: %s -> %s", file.name, err)
+		}
+		if supported != true {
+			t.Fatalf("Unsupported image colourspace")
+		}
+	}
+
+	supported, err := initImage("test.jpg").ColourspaceIsSupported()
+	if err != nil {
+		t.Errorf("Cannot process the image: %#v", err)
+	}
+	if supported != true {
+		t.Errorf("Non-supported colourspace")
+	}
+}
+
+func readFile(file string) []byte {
+	data, _ := os.Open(path.Join("fixtures", file))
+	buf, _ := ioutil.ReadAll(data)
+	return buf
+}

+ 218 - 0
_vendor/src/github.com/h2non/bimg/options.go

@@ -0,0 +1,218 @@
+package bimg
+
+/*
+#cgo pkg-config: vips
+#include "vips/vips.h"
+*/
+import "C"
+
+const (
+	// Quality defines the default JPEG quality to be used.
+	Quality = 80
+	// MaxSize defines the maximum pixels width or height supported.
+	MaxSize = 16383
+)
+
+// Gravity represents the image gravity value.
+type Gravity int
+
+const (
+	// GravityCentre represents the centre value used for image gravity orientation.
+	GravityCentre Gravity = iota
+	// GravityNorth represents the north value used for image gravity orientation.
+	GravityNorth
+	// GravityEast represents the east value used for image gravity orientation.
+	GravityEast
+	// GravitySouth represents the south value used for image gravity orientation.
+	GravitySouth
+	// GravityWest represents the west value used for image gravity orientation.
+	GravityWest
+	// GravitySmart enables libvips Smart Crop algorithm for image gravity orientation.
+	GravitySmart
+)
+
+// Interpolator represents the image interpolation value.
+type Interpolator int
+
+const (
+	// Bicubic interpolation value.
+	Bicubic Interpolator = iota
+	// Bilinear interpolation value.
+	Bilinear
+	// Nohalo interpolation value.
+	Nohalo
+)
+
+var interpolations = map[Interpolator]string{
+	Bicubic:  "bicubic",
+	Bilinear: "bilinear",
+	Nohalo:   "nohalo",
+}
+
+func (i Interpolator) String() string {
+	return interpolations[i]
+}
+
+// Angle represents the image rotation angle value.
+type Angle int
+
+const (
+	// D0 represents the rotation angle 0 degrees.
+	D0 Angle = 0
+	// D45 represents the rotation angle 90 degrees.
+	D45 Angle = 45
+	// D90 represents the rotation angle 90 degrees.
+	D90 Angle = 90
+	// D135 represents the rotation angle 90 degrees.
+	D135 Angle = 135
+	// D180 represents the rotation angle 180 degrees.
+	D180 Angle = 180
+	// D235 represents the rotation angle 235 degrees.
+	D235 Angle = 235
+	// D270 represents the rotation angle 270 degrees.
+	D270 Angle = 270
+	// D315 represents the rotation angle 180 degrees.
+	D315 Angle = 315
+)
+
+// Direction represents the image direction value.
+type Direction int
+
+const (
+	// Horizontal represents the orizontal image direction value.
+	Horizontal Direction = C.VIPS_DIRECTION_HORIZONTAL
+	// Vertical represents the vertical image direction value.
+	Vertical Direction = C.VIPS_DIRECTION_VERTICAL
+)
+
+// Interpretation represents the image interpretation type.
+// See: http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsImage.html#VipsInterpretation
+type Interpretation int
+
+const (
+	// InterpretationError points to the libvips interpretation error type.
+	InterpretationError Interpretation = C.VIPS_INTERPRETATION_ERROR
+	// InterpretationMultiband points to its libvips interpretation equivalent type.
+	InterpretationMultiband Interpretation = C.VIPS_INTERPRETATION_MULTIBAND
+	// InterpretationBW points to its libvips interpretation equivalent type.
+	InterpretationBW Interpretation = C.VIPS_INTERPRETATION_B_W
+	// InterpretationCMYK points to its libvips interpretation equivalent type.
+	InterpretationCMYK Interpretation = C.VIPS_INTERPRETATION_CMYK
+	// InterpretationRGB points to its libvips interpretation equivalent type.
+	InterpretationRGB Interpretation = C.VIPS_INTERPRETATION_RGB
+	// InterpretationSRGB points to its libvips interpretation equivalent type.
+	InterpretationSRGB Interpretation = C.VIPS_INTERPRETATION_sRGB
+	// InterpretationRGB16 points to its libvips interpretation equivalent type.
+	InterpretationRGB16 Interpretation = C.VIPS_INTERPRETATION_RGB16
+	// InterpretationGREY16 points to its libvips interpretation equivalent type.
+	InterpretationGREY16 Interpretation = C.VIPS_INTERPRETATION_GREY16
+	// InterpretationScRGB points to its libvips interpretation equivalent type.
+	InterpretationScRGB Interpretation = C.VIPS_INTERPRETATION_scRGB
+	// InterpretationLAB points to its libvips interpretation equivalent type.
+	InterpretationLAB Interpretation = C.VIPS_INTERPRETATION_LAB
+	// InterpretationXYZ points to its libvips interpretation equivalent type.
+	InterpretationXYZ Interpretation = C.VIPS_INTERPRETATION_XYZ
+)
+
+// Extend represents the image extend mode, used when the edges
+// of an image are extended, you can specify how you want the extension done.
+// See: http://www.vips.ecs.soton.ac.uk/supported/8.4/doc/html/libvips/libvips-conversion.html#VIPS-EXTEND-BACKGROUND:CAPS
+type Extend int
+
+const (
+	// ExtendBlack extend with black (all 0) pixels mode.
+	ExtendBlack Extend = C.VIPS_EXTEND_BLACK
+	// ExtendCopy copy the image edges.
+	ExtendCopy Extend = C.VIPS_EXTEND_COPY
+	// ExtendRepeat repeat the whole image.
+	ExtendRepeat Extend = C.VIPS_EXTEND_REPEAT
+	// ExtendMirror mirror the whole image.
+	ExtendMirror Extend = C.VIPS_EXTEND_MIRROR
+	// ExtendWhite extend with white (all bits set) pixels.
+	ExtendWhite Extend = C.VIPS_EXTEND_WHITE
+	// ExtendBackground with colour from the background property.
+	ExtendBackground Extend = C.VIPS_EXTEND_BACKGROUND
+	// ExtendLast extend with last pixel.
+	ExtendLast Extend = C.VIPS_EXTEND_LAST
+)
+
+// WatermarkFont defines the default watermark font to be used.
+var WatermarkFont = "sans 10"
+
+// Color represents a traditional RGB color scheme.
+type Color struct {
+	R, G, B uint8
+}
+
+// ColorBlack is a shortcut to black RGB color representation.
+var ColorBlack = Color{0, 0, 0}
+
+// Watermark represents the text-based watermark supported options.
+type Watermark struct {
+	Width       int
+	DPI         int
+	Margin      int
+	Opacity     float32
+	NoReplicate bool
+	Text        string
+	Font        string
+	Background  Color
+}
+
+// WatermarkImage represents the image-based watermark supported options.
+type WatermarkImage struct {
+	Left    int
+	Top     int
+	Buf     []byte
+	Opacity float32
+}
+
+// GaussianBlur represents the gaussian image transformation values.
+type GaussianBlur struct {
+	Sigma   float64
+	MinAmpl float64
+}
+
+// Sharpen represents the image sharp transformation options.
+type Sharpen struct {
+	Radius int
+	X1     float64
+	Y2     float64
+	Y3     float64
+	M1     float64
+	M2     float64
+}
+
+// Options represents the supported image transformation options.
+type Options struct {
+	Height         int
+	Width          int
+	AreaHeight     int
+	AreaWidth      int
+	Top            int
+	Left           int
+	Quality        int
+	Compression    int
+	Zoom           int
+	Crop           bool
+	SmartCrop      bool // Deprecated
+	Enlarge        bool
+	Embed          bool
+	Flip           bool
+	Flop           bool
+	Force          bool
+	NoAutoRotate   bool
+	NoProfile      bool
+	Interlace      bool
+	Extend         Extend
+	Rotate         Angle
+	Background     Color
+	Gravity        Gravity
+	Watermark      Watermark
+	WatermarkImage WatermarkImage
+	Type           ImageType
+	Interpolator   Interpolator
+	Interpretation Interpretation
+	GaussianBlur   GaussianBlur
+	Sharpen        Sharpen
+}

+ 302 - 0
_vendor/src/github.com/h2non/bimg/preinstall.sh

@@ -0,0 +1,302 @@
+#!/bin/bash
+
+vips_version_minimum=8.4.2
+vips_version_latest_major_minor=8.4
+vips_version_latest_patch=2
+
+openslide_version_minimum=3.4.0
+openslide_version_latest_major_minor=3.4
+openslide_version_latest_patch=1
+
+install_libvips_from_source() {
+  echo "Compiling libvips $vips_version_latest_major_minor.$vips_version_latest_patch from source"
+  curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major_minor/vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
+  tar zvxf vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
+  cd vips-$vips_version_latest_major_minor.$vips_version_latest_patch
+  CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" ./configure --disable-debug --disable-docs --disable-static --disable-introspection --disable-dependency-tracking --enable-cxx=yes --without-python --without-orc --without-fftw $1
+  make
+  make install
+  cd ..
+  rm -rf vips-$vips_version_latest_major_minor.$vips_version_latest_patch
+  rm vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
+  ldconfig
+  echo "Installed libvips $(PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig pkg-config --modversion vips)"
+}
+
+install_libopenslide_from_source() {
+  echo "Compiling openslide $openslide_version_latest_major_minor.$openslide_version_latest_patch from source"
+  curl -O -L https://github.com/openslide/openslide/releases/download/v$openslide_version_latest_major_minor.$openslide_version_latest_patch/openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
+  tar xzvf openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
+  cd openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch
+  PKG_CONFIG_PATH=$pkg_config_path ./configure $1
+  make
+  make install
+  cd ..
+  rm -rf openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch
+  rm openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
+  ldconfig
+  echo "Installed libopenslide $openslide_version_latest_major_minor.$openslide_version_latest_patch"
+}
+
+sorry() {
+  echo "Sorry, I don't yet know how to install lib$1 on $2"
+  exit 1
+}
+
+pkg_config_path="$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig"
+
+check_if_library_exists() {
+  PKG_CONFIG_PATH=$pkg_config_path pkg-config --exists $1
+  if [ $? -eq 0 ]; then
+    version_found=$(PKG_CONFIG_PATH=$pkg_config_path pkg-config --modversion $1)
+    PKG_CONFIG_PATH=$pkg_config_path pkg-config --atleast-version=$2 $1
+    if [ $? -eq 0 ]; then
+      # Found suitable version of libvips
+      echo "Found lib$1 $version_found"
+      return 1
+    fi
+    echo "Found lib$1 $version_found but require $2"
+  else
+    echo "Could not find lib$1 using a PKG_CONFIG_PATH of '$pkg_config_path'"
+  fi
+  return 0
+}
+
+enable_openslide=0
+# Is libvips already installed, and is it at least the minimum required version?
+if [ $# -eq 1 ]; then
+  if [ "$1" = "--with-openslide" ]; then
+    echo "Installing vips with openslide support"
+    enable_openslide=1
+  else
+    echo "Sorry, $1 is not supported. Did you mean --with-openslide?"
+    exit 1
+  fi
+fi
+
+if ! type pkg-config >/dev/null; then
+  sorry "vips" "a system without pkg-config"
+fi
+
+openslide_exists=0
+if [ $enable_openslide -eq 1 ]; then
+  check_if_library_exists "openslide" "$openslide_version_minimum"
+  openslide_exists=$?
+fi
+
+check_if_library_exists "vips" "$vips_version_minimum"
+vips_exists=$?
+if [ $vips_exists -eq 1 ] && [ $enable_openslide -eq 1 ]; then
+  if [ $openslide_exists -eq 1 ]; then
+    # Check if vips compiled with openslide support
+    vips_with_openslide=`vips list classes | grep -i opensli`
+    if [ -z $vips_with_openslide ]; then
+      echo "Vips compiled without openslide support."
+    else
+      exit 0
+    fi
+  fi
+elif [ $vips_exists -eq 1 ] && [ $enable_openslide -eq 0 ]; then
+  exit 0
+fi
+
+# Verify root/sudo access
+if [ "$(id -u)" -ne "0" ]; then
+  echo "Sorry, I need root/sudo access to continue"
+  exit 1
+fi
+
+# Deprecation warning
+if [ "$(arch)" == "x86_64" ]; then
+  echo "This script is no longer required on most 64-bit Linux systems when using sharp v0.12.0+"
+fi
+
+# OS-specific installations of libopenslide follows
+# Either openslide does not exist, or vips is installed without openslide support
+if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_exists -eq 0 ]; then
+  if [ -f /etc/debian_version ]; then
+    # Debian Linux
+    DISTRO=$(lsb_release -c -s)
+    echo "Detected Debian Linux '$DISTRO'"
+    case "$DISTRO" in
+      jessie|vivid|wily|xenial)
+        # Debian 8, Ubuntu 15
+        echo "Installing libopenslide via apt-get"
+        apt-get install -y libopenslide-dev
+        ;;
+      trusty|utopic|qiana|rebecca|rafaela|freya|rosa|sarah|serena)
+        # Ubuntu 14, Mint 17+
+        echo "Installing libopenslide dependencies via apt-get"
+        apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev
+        install_libopenslide_from_source
+        ;;
+      precise|wheezy|maya)
+        # Debian 7, Ubuntu 12.04, Mint 13
+        echo "Installing libopenslide dependencies via apt-get"
+        apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev
+        install_libopenslide_from_source
+        ;;
+      *)
+        # Unsupported Debian-based OS
+        sorry "openslide" "Debian-based $DISTRO"
+        ;;
+    esac
+  elif [ -f /etc/redhat-release ]; then
+    # Red Hat Linux
+    RELEASE=$(cat /etc/redhat-release)
+    echo "Detected Red Hat Linux '$RELEASE'"
+    case $RELEASE in
+      "Red Hat Enterprise Linux release 7."*|"CentOS Linux release 7."*|"Scientific Linux release 7."*)
+        # RHEL/CentOS 7
+        echo "Installing libopenslide dependencies via yum"
+        yum groupinstall -y "Development Tools"
+        yum install -y tar curl libpng-devel libjpeg-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel gdk-pixbuf2-devel sqlite-devel cairo-devel glib2-devel
+        install_libopenslide_from_source "--prefix=/usr"
+        ;;
+      "Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
+        # RHEL/CentOS 6
+        echo "Installing libopenslide dependencies via yum"
+        yum groupinstall -y "Development Tools"
+        yum install -y tar curl libpng-devel libjpeg-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel gdk-pixbuf2-devel sqlite-devel cairo-devel glib2-devel
+        install_libopenslide_from_source "--prefix=/usr"
+        ;;
+      "Fedora release 21 "*|"Fedora release 22 "*)
+        # Fedora 21, 22
+        echo "Installing libopenslide via yum"
+        yum install -y openslide-devel
+        ;;
+      *)
+        # Unsupported RHEL-based OS
+        sorry "openslide" "$RELEASE"
+        ;;
+    esac
+  elif [ -f /etc/os-release ]; then
+    RELEASE=$(cat /etc/os-release | grep VERSION)
+    echo "Detected OpenSuse Linux '$RELEASE'"
+    case $RELEASE in
+      *"13.2"*)
+      echo "Installing libopenslide via zypper"
+      zypper --gpg-auto-import-keys install -y libopenslide-devel
+      ;;
+    esac
+  elif [ -f /etc/SuSE-brand ]; then
+    RELEASE=$(cat /etc/SuSE-brand | grep VERSION)
+    echo "Detected OpenSuse Linux '$RELEASE'"
+    case $RELEASE in
+      *"13.1")
+      echo "Installing libopenslide dependencies via zypper"
+      zypper --gpg-auto-import-keys install -y --type pattern devel_basis
+      zypper --gpg-auto-import-keys install -y tar curl libpng16-devel libjpeg-turbo libjpeg8-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel libgdk_pixbuf-2_0-0 sqlite3-devel cairo-devel glib2-devel
+      install_libopenslide_from_source
+      ;;
+    esac
+  else
+    # Unsupported OS
+    sorry "openslide" "$(uname -a)"
+  fi
+fi
+
+# OS-specific installations of libvips follows
+
+if [ -f /etc/debian_version ]; then
+  # Debian Linux
+  DISTRO=$(lsb_release -c -s)
+  echo "Detected Debian Linux '$DISTRO'"
+  case "$DISTRO" in
+    jessie|trusty|utopic|vivid|wily|xenial|qiana|rebecca|rafaela|freya|rosa|sarah|serena)
+      # Debian 8, Ubuntu 14.04+, Mint 17+
+      echo "Installing libvips dependencies via apt-get"
+      apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
+      install_libvips_from_source
+      ;;
+    precise|wheezy|maya)
+      # Debian 7, Ubuntu 12.04, Mint 13
+      echo "Installing libvips dependencies via apt-get"
+      add-apt-repository -y ppa:lyrasis/precise-backports
+      apt-get update
+      apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff4-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
+      install_libvips_from_source
+      ;;
+    *)
+      # Unsupported Debian-based OS
+      sorry "vips" "Debian-based $DISTRO"
+      ;;
+  esac
+elif [ -f /etc/redhat-release ]; then
+  # Red Hat Linux
+  RELEASE=$(cat /etc/redhat-release)
+  echo "Detected Red Hat Linux '$RELEASE'"
+  case $RELEASE in
+    "Red Hat Enterprise Linux release 7."*|"CentOS Linux release 7."*|"Scientific Linux release 7."*)
+      # RHEL/CentOS 7
+      echo "Installing libvips dependencies via yum"
+      yum groupinstall -y "Development Tools"
+      yum install -y tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
+      install_libvips_from_source "--prefix=/usr"
+      ;;
+    "Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
+      # RHEL/CentOS 6
+      echo "Installing libvips dependencies via yum"
+      yum groupinstall -y "Development Tools"
+      yum install -y tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms-devel ImageMagick-devel
+      yum install -y http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm
+      yum install -y --enablerepo=nux-dextop gobject-introspection-devel
+      yum install -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
+      yum install -y --enablerepo=remi libwebp-devel
+      install_libvips_from_source "--prefix=/usr"
+      ;;
+    "Fedora"*)
+      # Fedora 21, 22, 23
+      echo "Installing libvips dependencies via yum"
+      yum groupinstall -y "Development Tools"
+      yum install -y gcc-c++ gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
+      install_libvips_from_source "--prefix=/usr"
+      ;;
+    *)
+      # Unsupported RHEL-based OS
+      sorry "vips" "$RELEASE"
+      ;;
+  esac
+elif [ -f /etc/system-release ]; then
+  # Probably Amazon Linux
+  RELEASE=$(cat /etc/system-release)
+  case $RELEASE in
+    "Amazon Linux AMI release 2015.03"|"Amazon Linux AMI release 2015.09")
+      # Amazon Linux
+      echo "Detected '$RELEASE'"
+      echo "Installing libvips dependencies via yum"
+      yum groupinstall -y "Development Tools"
+      yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
+      install_libvips_from_source "--prefix=/usr"
+      ;;
+    *)
+      # Unsupported Amazon Linux version
+      sorry "vips" "$RELEASE"
+      ;;
+  esac
+elif [ -f /etc/os-release ]; then
+  RELEASE=$(cat /etc/os-release | grep VERSION)
+  echo "Detected OpenSuse Linux '$RELEASE'"
+  case $RELEASE in
+    *"13.2"*)
+    echo "Installing libvips dependencies via zypper"
+    zypper --gpg-auto-import-keys install -y --type pattern devel_basis
+    zypper --gpg-auto-import-keys install -y tar curl gtk-doc libxml2-devel libjpeg-turbo libjpeg8-devel libpng16-devel libtiff-devel libexif-devel liblcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
+    install_libvips_from_source
+    ;;
+  esac
+elif [ -f /etc/SuSE-brand ]; then
+  RELEASE=$(cat /etc/SuSE-brand | grep VERSION)
+  echo "Detected OpenSuse Linux '$RELEASE'"
+  case $RELEASE in
+    *"13.1")
+    echo "Installing libvips dependencies via zypper"
+    zypper --gpg-auto-import-keys install -y --type pattern devel_basis
+    zypper --gpg-auto-import-keys install -y tar curl gtk-doc libxml2-devel libjpeg-turbo libjpeg8-devel libpng16-devel libtiff-devel libexif-devel liblcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
+    install_libvips_from_source
+    ;;
+  esac
+else
+  # Unsupported OS
+  sorry "vips" "$(uname -a)"
+fi

+ 561 - 0
_vendor/src/github.com/h2non/bimg/resize.go

@@ -0,0 +1,561 @@
+package bimg
+
+/*
+#cgo pkg-config: vips
+#include "vips/vips.h"
+*/
+import "C"
+
+import (
+	"errors"
+	"math"
+)
+
+// Resize is used to transform a given image as byte buffer
+// with the passed options.
+func Resize(buf []byte, o Options) ([]byte, error) {
+	defer C.vips_thread_shutdown()
+
+	image, imageType, err := loadImage(buf)
+
+	if err != nil {
+		return nil, err
+	}
+
+	// Clone and define default options
+	o = applyDefaults(o, imageType)
+
+	if !IsTypeSupported(o.Type) {
+		return nil, errors.New("Unsupported image output type")
+	}
+
+	debug("Options: %#v", o)
+
+	// Auto rotate image based on EXIF orientation header
+	image, rotated, err := rotateAndFlipImage(image, o)
+	if err != nil {
+		return nil, err
+	}
+
+	// If JPEG image, retrieve the buffer
+	if rotated && imageType == JPEG && !o.NoAutoRotate {
+		buf, err = getImageBuffer(image)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	inWidth := int(image.Xsize)
+	inHeight := int(image.Ysize)
+
+	// Infer the required operation based on the in/out image sizes for a coherent transformation
+	normalizeOperation(&o, inWidth, inHeight)
+
+	// image calculations
+	factor := imageCalculations(&o, inWidth, inHeight)
+	shrink := calculateShrink(factor, o.Interpolator)
+	residual := calculateResidual(factor, shrink)
+
+	// Do not enlarge the output if the input width or height
+	// are already less than the required dimensions
+	if !o.Enlarge && !o.Force {
+		if inWidth < o.Width && inHeight < o.Height {
+			factor = 1.0
+			shrink = 1
+			residual = 0
+			o.Width = inWidth
+			o.Height = inHeight
+		}
+	}
+
+	// Try to use libjpeg shrink-on-load
+	if imageType == JPEG && shrink >= 2 {
+		tmpImage, factor, err := shrinkJpegImage(buf, image, factor, shrink)
+		if err != nil {
+			return nil, err
+		}
+
+		image = tmpImage
+		factor = math.Max(factor, 1.0)
+		shrink = int(math.Floor(factor))
+		residual = float64(shrink) / factor
+	}
+
+	// Zoom image, if necessary
+	image, err = zoomImage(image, o.Zoom)
+	if err != nil {
+		return nil, err
+	}
+
+	// Transform image, if necessary
+	if shouldTransformImage(o, inWidth, inHeight) {
+		image, err = transformImage(image, o, shrink, residual)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// Apply effects, if necessary
+	if shouldApplyEffects(o) {
+		image, err = applyEffects(image, o)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// Add watermark, if necessary
+	image, err = watermarkImageWithText(image, o.Watermark)
+	if err != nil {
+		return nil, err
+	}
+
+	// Add watermark, if necessary
+	image, err = watermarkImageWithAnotherImage(image, o.WatermarkImage)
+	if err != nil {
+		return nil, err
+	}
+
+	// Flatten image on a background, if necessary
+	image, err = imageFlatten(image, imageType, o)
+	if err != nil {
+		return nil, err
+	}
+
+	return saveImage(image, o)
+}
+
+func loadImage(buf []byte) (*C.VipsImage, ImageType, error) {
+	if len(buf) == 0 {
+		return nil, JPEG, errors.New("Image buffer is empty")
+	}
+
+	image, imageType, err := vipsRead(buf)
+	if err != nil {
+		return nil, JPEG, err
+	}
+
+	return image, imageType, nil
+}
+
+func applyDefaults(o Options, imageType ImageType) Options {
+	if o.Quality == 0 {
+		o.Quality = Quality
+	}
+	if o.Compression == 0 {
+		o.Compression = 6
+	}
+	if o.Type == 0 {
+		o.Type = imageType
+	}
+	if o.Interpretation == 0 {
+		o.Interpretation = InterpretationSRGB
+	}
+	return o
+}
+
+func saveImage(image *C.VipsImage, o Options) ([]byte, error) {
+	saveOptions := vipsSaveOptions{
+		Quality:        o.Quality,
+		Type:           o.Type,
+		Compression:    o.Compression,
+		Interlace:      o.Interlace,
+		NoProfile:      o.NoProfile,
+		Interpretation: o.Interpretation,
+	}
+	// Finally get the resultant buffer
+	return vipsSave(image, saveOptions)
+}
+
+func normalizeOperation(o *Options, inWidth, inHeight int) {
+	if !o.Force && !o.Crop && !o.Embed && !o.Enlarge && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) {
+		o.Force = true
+	}
+}
+
+func shouldTransformImage(o Options, inWidth, inHeight int) bool {
+	return o.Force || (o.Width > 0 && o.Width != inWidth) ||
+		(o.Height > 0 && o.Height != inHeight) || o.AreaWidth > 0 || o.AreaHeight > 0
+}
+
+func shouldApplyEffects(o Options) bool {
+	return o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 || o.Sharpen.Radius > 0 && o.Sharpen.Y2 > 0 || o.Sharpen.Y3 > 0
+}
+
+func transformImage(image *C.VipsImage, o Options, shrink int, residual float64) (*C.VipsImage, error) {
+	var err error
+	// Use vips_shrink with the integral reduction
+	if shrink > 1 {
+		image, residual, err = shrinkImage(image, o, residual, shrink)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	residualx, residualy := residual, residual
+	if o.Force {
+		residualx = float64(o.Width) / float64(image.Xsize)
+		residualy = float64(o.Height) / float64(image.Ysize)
+	}
+
+	if o.Force || residual != 0 {
+		image, err = vipsAffine(image, residualx, residualy, o.Interpolator)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	if o.Force {
+		o.Crop = false
+		o.Embed = false
+	}
+
+	image, err = extractOrEmbedImage(image, o)
+	if err != nil {
+		return nil, err
+	}
+
+	debug("Transform: shrink=%v, residual=%v, interpolator=%v",
+		shrink, residual, o.Interpolator.String())
+
+	return image, nil
+}
+
+func applyEffects(image *C.VipsImage, o Options) (*C.VipsImage, error) {
+	var err error
+
+	if o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 {
+		image, err = vipsGaussianBlur(image, o.GaussianBlur)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	if o.Sharpen.Radius > 0 && o.Sharpen.Y2 > 0 || o.Sharpen.Y3 > 0 {
+		image, err = vipsSharpen(image, o.Sharpen)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	debug("Effects: gaussSigma=%v, gaussMinAmpl=%v, sharpenRadius=%v",
+		o.GaussianBlur.Sigma, o.GaussianBlur.MinAmpl, o.Sharpen.Radius)
+
+	return image, nil
+}
+
+func extractOrEmbedImage(image *C.VipsImage, o Options) (*C.VipsImage, error) {
+	var err error
+	inWidth := int(image.Xsize)
+	inHeight := int(image.Ysize)
+
+	switch {
+	case o.Gravity == GravitySmart, o.SmartCrop:
+		image, err = vipsSmartCrop(image, o.Width, o.Height)
+		break
+	case o.Crop:
+		width := int(math.Min(float64(inWidth), float64(o.Width)))
+		height := int(math.Min(float64(inHeight), float64(o.Height)))
+		left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity)
+		left, top = int(math.Max(float64(left), 0)), int(math.Max(float64(top), 0))
+		image, err = vipsExtract(image, left, top, width, height)
+		break
+	case o.Embed:
+		left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2
+		image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend, o.Background)
+		break
+
+	case o.Top != 0 || o.Left != 0 || o.AreaWidth != 0 || o.AreaHeight != 0:
+		if o.AreaWidth == 0 {
+			o.AreaHeight = o.Width
+		}
+		if o.AreaHeight == 0 {
+			o.AreaHeight = o.Height
+		}
+		if o.AreaWidth == 0 || o.AreaHeight == 0 {
+			return nil, errors.New("Extract area width/height params are required")
+		}
+		image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight)
+		break
+	}
+
+	return image, err
+}
+
+func rotateAndFlipImage(image *C.VipsImage, o Options) (*C.VipsImage, bool, error) {
+	var err error
+	var rotated bool
+	var direction Direction = -1
+
+	if o.NoAutoRotate == false {
+		rotation, flip := calculateRotationAndFlip(image, o.Rotate)
+		if flip {
+			o.Flip = flip
+		}
+		if rotation > 0 && o.Rotate == 0 {
+			o.Rotate = rotation
+		}
+	}
+
+	if o.Rotate > 0 {
+		rotated = true
+		image, err = vipsRotate(image, getAngle(o.Rotate))
+	}
+
+	if o.Flip {
+		direction = Horizontal
+	} else if o.Flop {
+		direction = Vertical
+	}
+
+	if direction != -1 {
+		rotated = true
+		image, err = vipsFlip(image, direction)
+	}
+
+	return image, rotated, err
+}
+
+func watermarkImageWithText(image *C.VipsImage, w Watermark) (*C.VipsImage, error) {
+	if w.Text == "" {
+		return image, nil
+	}
+
+	// Defaults
+	if w.Font == "" {
+		w.Font = WatermarkFont
+	}
+	if w.Width == 0 {
+		w.Width = int(math.Floor(float64(image.Xsize / 6)))
+	}
+	if w.DPI == 0 {
+		w.DPI = 150
+	}
+	if w.Margin == 0 {
+		w.Margin = w.Width
+	}
+	if w.Opacity == 0 {
+		w.Opacity = 0.25
+	} else if w.Opacity > 1 {
+		w.Opacity = 1
+	}
+
+	image, err := vipsWatermark(image, w)
+	if err != nil {
+		return nil, err
+	}
+
+	return image, nil
+}
+
+func watermarkImageWithAnotherImage(image *C.VipsImage, w WatermarkImage) (*C.VipsImage, error) {
+
+	if len(w.Buf) == 0 {
+		return image, nil
+	}
+
+	if w.Opacity == 0.0 {
+		w.Opacity = 1.0
+	}
+
+	image, err := vipsDrawWatermark(image, w)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return image, nil
+}
+
+func imageFlatten(image *C.VipsImage, imageType ImageType, o Options) (*C.VipsImage, error) {
+	// Only PNG images are supported for now
+	if imageType != PNG || o.Background == ColorBlack {
+		return image, nil
+	}
+	return vipsFlattenBackground(image, o.Background)
+}
+
+func zoomImage(image *C.VipsImage, zoom int) (*C.VipsImage, error) {
+	if zoom == 0 {
+		return image, nil
+	}
+	return vipsZoom(image, zoom+1)
+}
+
+func shrinkImage(image *C.VipsImage, o Options, residual float64, shrink int) (*C.VipsImage, float64, error) {
+	// Use vips_shrink with the integral reduction
+	image, err := vipsShrink(image, shrink)
+	if err != nil {
+		return nil, 0, err
+	}
+
+	// Recalculate residual float based on dimensions of required vs shrunk images
+	residualx := float64(o.Width) / float64(image.Xsize)
+	residualy := float64(o.Height) / float64(image.Ysize)
+
+	if o.Crop {
+		residual = math.Max(residualx, residualy)
+	} else {
+		residual = math.Min(residualx, residualy)
+	}
+
+	return image, residual, nil
+}
+
+func shrinkJpegImage(buf []byte, input *C.VipsImage, factor float64, shrink int) (*C.VipsImage, float64, error) {
+	var image *C.VipsImage
+	var err error
+	shrinkOnLoad := 1
+
+	// Recalculate integral shrink and double residual
+	switch {
+	case shrink >= 8:
+		factor = factor / 8
+		shrinkOnLoad = 8
+	case shrink >= 4:
+		factor = factor / 4
+		shrinkOnLoad = 4
+	case shrink >= 2:
+		factor = factor / 2
+		shrinkOnLoad = 2
+	}
+
+	// Reload input using shrink-on-load
+	if shrinkOnLoad > 1 {
+		image, err = vipsShrinkJpeg(buf, input, shrinkOnLoad)
+	}
+
+	return image, factor, err
+}
+
+func imageCalculations(o *Options, inWidth, inHeight int) float64 {
+	factor := 1.0
+	xfactor := float64(inWidth) / float64(o.Width)
+	yfactor := float64(inHeight) / float64(o.Height)
+
+	switch {
+	// Fixed width and height
+	case o.Width > 0 && o.Height > 0:
+		if o.Crop {
+			factor = math.Min(xfactor, yfactor)
+		} else {
+			factor = math.Max(xfactor, yfactor)
+		}
+	// Fixed width, auto height
+	case o.Width > 0:
+		if o.Crop {
+			o.Height = inHeight
+		} else {
+			factor = xfactor
+			o.Height = roundFloat(float64(inHeight) / factor)
+		}
+	// Fixed height, auto width
+	case o.Height > 0:
+		if o.Crop {
+			o.Width = inWidth
+		} else {
+			factor = yfactor
+			o.Width = roundFloat(float64(inWidth) / factor)
+		}
+	// Identity transform
+	default:
+		o.Width = inWidth
+		o.Height = inHeight
+		break
+	}
+
+	return factor
+}
+
+func roundFloat(f float64) int {
+	if f < 0 {
+		return int(math.Ceil(f - 0.5))
+	}
+	return int(math.Floor(f + 0.5))
+}
+
+func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
+	left, top := 0, 0
+
+	switch gravity {
+	case GravityNorth:
+		left = (inWidth - outWidth + 1) / 2
+	case GravityEast:
+		left = inWidth - outWidth
+		top = (inHeight - outHeight + 1) / 2
+	case GravitySouth:
+		left = (inWidth - outWidth + 1) / 2
+		top = inHeight - outHeight
+	case GravityWest:
+		top = (inHeight - outHeight + 1) / 2
+	default:
+		left = (inWidth - outWidth + 1) / 2
+		top = (inHeight - outHeight + 1) / 2
+	}
+
+	return left, top
+}
+
+func calculateRotationAndFlip(image *C.VipsImage, angle Angle) (Angle, bool) {
+	rotate := D0
+	flip := false
+
+	if angle > 0 {
+		return rotate, flip
+	}
+
+	switch vipsExifOrientation(image) {
+	case 6:
+		rotate = D90
+		break
+	case 3:
+		rotate = D180
+		break
+	case 8:
+		rotate = D270
+		break
+	case 2:
+		flip = true
+		break // flip 1
+	case 7:
+		flip = true
+		rotate = D90
+		break // flip 6
+	case 4:
+		flip = true
+		rotate = D180
+		break // flip 3
+	case 5:
+		flip = true
+		rotate = D270
+		break // flip 8
+	}
+
+	return rotate, flip
+}
+
+func calculateShrink(factor float64, i Interpolator) int {
+	var shrink float64
+
+	// Calculate integral box shrink
+	windowSize := vipsWindowSize(i.String())
+	if factor >= 2 && windowSize > 3 {
+		// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
+		shrink = float64(math.Floor(factor * 3.0 / windowSize))
+	} else {
+		shrink = math.Floor(factor)
+	}
+
+	return int(math.Max(shrink, 1))
+}
+
+func calculateResidual(factor float64, shrink int) float64 {
+	return float64(shrink) / factor
+}
+
+func getAngle(angle Angle) Angle {
+	divisor := angle % 90
+	if divisor != 0 {
+		angle = angle - divisor
+	}
+	return Angle(math.Min(float64(angle), 270))
+}

+ 644 - 0
_vendor/src/github.com/h2non/bimg/resize_test.go

@@ -0,0 +1,644 @@
+package bimg
+
+import (
+	"bytes"
+	"crypto/md5"
+	"image"
+	"image/jpeg"
+	"io/ioutil"
+	"os"
+	"path"
+	"strconv"
+	"testing"
+)
+
+func TestResize(t *testing.T) {
+	options := Options{Width: 800, Height: 600}
+	buf, _ := Read("fixtures/test.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	if DetermineImageType(newImg) != JPEG {
+		t.Fatal("Image is not jpeg")
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != options.Height || size.Width != options.Width {
+		t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_out.jpg", newImg)
+}
+
+func TestResizeVerticalImage(t *testing.T) {
+	tests := []struct {
+		format  ImageType
+		options Options
+	}{
+		{JPEG, Options{Width: 800, Height: 600}},
+		{JPEG, Options{Width: 1000, Height: 1000}},
+		{JPEG, Options{Width: 1000, Height: 1500}},
+		{JPEG, Options{Width: 1000}},
+		{JPEG, Options{Height: 1500}},
+		{JPEG, Options{Width: 100, Height: 50}},
+		{JPEG, Options{Width: 2000, Height: 2000}},
+		{JPEG, Options{Width: 500, Height: 1000}},
+		{JPEG, Options{Width: 500}},
+		{JPEG, Options{Height: 500}},
+		{JPEG, Options{Crop: true, Width: 500, Height: 1000}},
+		{JPEG, Options{Crop: true, Enlarge: true, Width: 2000, Height: 1400}},
+		{JPEG, Options{Enlarge: true, Force: true, Width: 2000, Height: 2000}},
+		{JPEG, Options{Force: true, Width: 2000, Height: 2000}},
+	}
+
+	buf, _ := Read("fixtures/vertical.jpg")
+	for _, test := range tests {
+		image, err := Resize(buf, test.options)
+		if err != nil {
+			t.Errorf("Resize(imgData, %#v) error: %#v", test.options, err)
+		}
+
+		if DetermineImageType(image) != test.format {
+			t.Fatalf("Image format is invalid. Expected: %#v", test.format)
+		}
+
+		size, _ := Size(image)
+		if test.options.Height > 0 && size.Height != test.options.Height {
+			t.Fatalf("Invalid height: %d", size.Height)
+		}
+		if test.options.Width > 0 && size.Width != test.options.Width {
+			t.Fatalf("Invalid width: %d", size.Width)
+		}
+
+		Write("fixtures/test_vertical_"+strconv.Itoa(test.options.Width)+"x"+strconv.Itoa(test.options.Height)+"_out.jpg", image)
+	}
+}
+
+func TestResizeCustomSizes(t *testing.T) {
+	tests := []struct {
+		format  ImageType
+		options Options
+	}{
+		{JPEG, Options{Width: 800, Height: 600}},
+		{JPEG, Options{Width: 1000, Height: 1000}},
+		{JPEG, Options{Width: 100, Height: 50}},
+		{JPEG, Options{Width: 2000, Height: 2000}},
+		{JPEG, Options{Width: 500, Height: 1000}},
+		{JPEG, Options{Width: 500}},
+		{JPEG, Options{Height: 500}},
+		{JPEG, Options{Crop: true, Width: 500, Height: 1000}},
+		{JPEG, Options{Crop: true, Enlarge: true, Width: 2000, Height: 1400}},
+		{JPEG, Options{Enlarge: true, Force: true, Width: 2000, Height: 2000}},
+		{JPEG, Options{Force: true, Width: 2000, Height: 2000}},
+	}
+
+	buf, _ := Read("fixtures/test.jpg")
+	for _, test := range tests {
+		image, err := Resize(buf, test.options)
+		if err != nil {
+			t.Errorf("Resize(imgData, %#v) error: %#v", test.options, err)
+		}
+
+		if DetermineImageType(image) != test.format {
+			t.Fatalf("Image format is invalid. Expected: %#v", test.format)
+		}
+
+		size, _ := Size(image)
+		if test.options.Height > 0 && size.Height != test.options.Height {
+			t.Fatalf("Invalid height: %d", size.Height)
+		}
+		if test.options.Width > 0 && size.Width != test.options.Width {
+			t.Fatalf("Invalid width: %d", size.Width)
+		}
+	}
+}
+
+func TestResizePrecision(t *testing.T) {
+	// see https://github.com/h2non/bimg/issues/99
+	img := image.NewGray16(image.Rect(0, 0, 1920, 1080))
+	input := &bytes.Buffer{}
+	jpeg.Encode(input, img, nil)
+
+	opts := Options{Width: 300}
+	newImg, err := Resize(input.Bytes(), opts)
+	if err != nil {
+		t.Fatalf("Resize(imgData, %#v) error: %#v", opts, err)
+	}
+
+	size, _ := Size(newImg)
+	if size.Width != opts.Width {
+		t.Fatalf("Invalid width: %d", size.Width)
+	}
+}
+
+func TestRotate(t *testing.T) {
+	options := Options{Width: 800, Height: 600, Rotate: 270, Crop: true}
+	buf, _ := Read("fixtures/test.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	if DetermineImageType(newImg) != JPEG {
+		t.Error("Image is not jpeg")
+	}
+
+	size, _ := Size(newImg)
+	if size.Width != options.Width || size.Height != options.Height {
+		t.Errorf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_rotate_out.jpg", newImg)
+}
+
+func TestInvalidRotateDegrees(t *testing.T) {
+	options := Options{Width: 800, Height: 600, Rotate: 111, Crop: true}
+	buf, _ := Read("fixtures/test.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	if DetermineImageType(newImg) != JPEG {
+		t.Errorf("Image is not jpeg")
+	}
+
+	size, _ := Size(newImg)
+	if size.Width != options.Width || size.Height != options.Height {
+		t.Errorf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_rotate_invalid_out.jpg", newImg)
+}
+
+func TestCorruptedImage(t *testing.T) {
+	options := Options{Width: 800, Height: 600}
+	buf, _ := Read("fixtures/corrupt.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	if DetermineImageType(newImg) != JPEG {
+		t.Fatal("Image is not jpeg")
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != options.Height || size.Width != options.Width {
+		t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_corrupt_out.jpg", newImg)
+}
+
+func TestNoColorProfile(t *testing.T) {
+	options := Options{Width: 800, Height: 600, NoProfile: true}
+	buf, _ := Read("fixtures/test.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	metadata, err := Metadata(newImg)
+	if metadata.Profile == true {
+		t.Fatal("Invalid profile data")
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != options.Height || size.Width != options.Width {
+		t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+}
+
+func TestEmbedExtendColor(t *testing.T) {
+	options := Options{Width: 400, Height: 600, Crop: false, Embed: true, Extend: ExtendWhite, Background: Color{255, 20, 10}}
+	buf, _ := Read("fixtures/test_issue.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != options.Height || size.Width != options.Width {
+		t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_extend_white_out.jpg", newImg)
+}
+
+func TestEmbedExtendWithCustomColor(t *testing.T) {
+	options := Options{Width: 400, Height: 600, Crop: false, Embed: true, Extend: 5, Background: Color{255, 20, 10}}
+	buf, _ := Read("fixtures/test_issue.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != options.Height || size.Width != options.Width {
+		t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_extend_background_out.jpg", newImg)
+}
+
+func TestGaussianBlur(t *testing.T) {
+	options := Options{Width: 800, Height: 600, GaussianBlur: GaussianBlur{Sigma: 5}}
+	buf, _ := Read("fixtures/test.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != options.Height || size.Width != options.Width {
+		t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_gaussian_out.jpg", newImg)
+}
+
+func TestSharpen(t *testing.T) {
+	options := Options{Width: 800, Height: 600, Sharpen: Sharpen{Radius: 1, X1: 1.5, Y2: 20, Y3: 50, M1: 1, M2: 2}}
+	buf, _ := Read("fixtures/test.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != options.Height || size.Width != options.Width {
+		t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_sharpen_out.jpg", newImg)
+}
+
+func TestExtractWithDefaultAxis(t *testing.T) {
+	options := Options{AreaWidth: 200, AreaHeight: 200}
+	buf, _ := Read("fixtures/test.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != options.AreaHeight || size.Width != options.AreaWidth {
+		t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_extract_defaults_out.jpg", newImg)
+}
+
+func TestExtractCustomAxis(t *testing.T) {
+	options := Options{Top: 100, Left: 100, AreaWidth: 200, AreaHeight: 200}
+	buf, _ := Read("fixtures/test.jpg")
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != options.AreaHeight || size.Width != options.AreaWidth {
+		t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+	}
+
+	Write("fixtures/test_extract_custom_axis_out.jpg", newImg)
+}
+
+func TestConvert(t *testing.T) {
+	width, height := 300, 240
+	formats := [3]ImageType{PNG, WEBP, JPEG}
+
+	files := []string{
+		"test.jpg",
+		"test.png",
+		"test.webp",
+	}
+
+	for _, file := range files {
+		img, err := os.Open("fixtures/" + file)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		buf, err := ioutil.ReadAll(img)
+		if err != nil {
+			t.Fatal(err)
+		}
+		img.Close()
+
+		for _, format := range formats {
+			options := Options{Width: width, Height: height, Crop: true, Type: format}
+
+			newImg, err := Resize(buf, options)
+			if err != nil {
+				t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+			}
+
+			if DetermineImageType(newImg) != format {
+				t.Fatal("Image is not png")
+			}
+
+			size, _ := Size(newImg)
+			if size.Height != height || size.Width != width {
+				t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+			}
+		}
+	}
+}
+
+func TestResizePngWithTransparency(t *testing.T) {
+	width, height := 300, 240
+
+	options := Options{Width: width, Height: height, Crop: true}
+	img, err := os.Open("fixtures/transparent.png")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer img.Close()
+
+	buf, err := ioutil.ReadAll(img)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	newImg, err := Resize(buf, options)
+	if err != nil {
+		t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+	}
+
+	if DetermineImageType(newImg) != PNG {
+		t.Fatal("Image is not png")
+	}
+
+	size, _ := Size(newImg)
+	if size.Height != height || size.Width != width {
+		t.Fatal("Invalid image size")
+	}
+
+	Write("fixtures/transparent_out.png", newImg)
+}
+
+func TestIfBothSmartCropOptionsAreIdentical(t *testing.T) {
+	if !(VipsMajorVersion >= 8 && VipsMinorVersion > 4) {
+		t.Skipf("Skipping this test, libvips doesn't meet version requirement %s > 8.4", VipsVersion)
+	}
+
+	benchmarkOptions := Options{Width: 100, Height: 100, Crop: true}
+	smartCropOptions := Options{Width: 100, Height: 100, Crop: true, SmartCrop: true}
+	gravityOptions := Options{Width: 100, Height: 100, Crop: true, Gravity: GravitySmart}
+
+	testImg, err := os.Open("fixtures/northern_cardinal_bird.jpg")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer testImg.Close()
+
+	testImgByte, err := ioutil.ReadAll(testImg)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	scImg, err := Resize(testImgByte, smartCropOptions)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	gImg, err := Resize(testImgByte, gravityOptions)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	benchmarkImg, err := Resize(testImgByte, benchmarkOptions)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	sch, gh, bh := md5.Sum(scImg), md5.Sum(gImg), md5.Sum(benchmarkImg)
+	if gh == bh || sch == bh {
+		t.Error("Expected both options produce a different result from a standard crop.")
+	}
+
+	if sch != gh {
+		t.Errorf("Expected both options to result in the same output, %x != %x", sch, gh)
+	}
+}
+
+func runBenchmarkResize(file string, o Options, b *testing.B) {
+	buf, _ := Read(path.Join("fixtures", file))
+
+	for n := 0; n < b.N; n++ {
+		Resize(buf, o)
+	}
+}
+
+func BenchmarkRotateJpeg(b *testing.B) {
+	options := Options{Rotate: 180}
+	runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkResizeLargeJpeg(b *testing.B) {
+	options := Options{
+		Width:  800,
+		Height: 600,
+	}
+	runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkResizePng(b *testing.B) {
+	options := Options{
+		Width:  200,
+		Height: 200,
+	}
+	runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkResizeWebP(b *testing.B) {
+	options := Options{
+		Width:  200,
+		Height: 200,
+	}
+	runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkConvertToJpeg(b *testing.B) {
+	options := Options{Type: JPEG}
+	runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkConvertToPng(b *testing.B) {
+	options := Options{Type: PNG}
+	runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkConvertToWebp(b *testing.B) {
+	options := Options{Type: WEBP}
+	runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkCropJpeg(b *testing.B) {
+	options := Options{
+		Width:  800,
+		Height: 600,
+	}
+	runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkCropPng(b *testing.B) {
+	options := Options{
+		Width:  800,
+		Height: 600,
+	}
+	runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkCropWebP(b *testing.B) {
+	options := Options{
+		Width:  800,
+		Height: 600,
+	}
+	runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkExtractJpeg(b *testing.B) {
+	options := Options{
+		Top:        100,
+		Left:       50,
+		AreaWidth:  600,
+		AreaHeight: 480,
+	}
+	runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkExtractPng(b *testing.B) {
+	options := Options{
+		Top:        100,
+		Left:       50,
+		AreaWidth:  600,
+		AreaHeight: 480,
+	}
+	runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkExtractWebp(b *testing.B) {
+	options := Options{
+		Top:        100,
+		Left:       50,
+		AreaWidth:  600,
+		AreaHeight: 480,
+	}
+	runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkZoomJpeg(b *testing.B) {
+	options := Options{Zoom: 1}
+	runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkZoomPng(b *testing.B) {
+	options := Options{Zoom: 1}
+	runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkZoomWebp(b *testing.B) {
+	options := Options{Zoom: 1}
+	runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkWatermarkJpeg(b *testing.B) {
+	options := Options{
+		Watermark: Watermark{
+			Text:       "Chuck Norris (c) 2315",
+			Opacity:    0.25,
+			Width:      200,
+			DPI:        100,
+			Margin:     150,
+			Font:       "sans bold 12",
+			Background: Color{255, 255, 255},
+		},
+	}
+	runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkWatermarPng(b *testing.B) {
+	options := Options{
+		Watermark: Watermark{
+			Text:       "Chuck Norris (c) 2315",
+			Opacity:    0.25,
+			Width:      200,
+			DPI:        100,
+			Margin:     150,
+			Font:       "sans bold 12",
+			Background: Color{255, 255, 255},
+		},
+	}
+	runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkWatermarWebp(b *testing.B) {
+	options := Options{
+		Watermark: Watermark{
+			Text:       "Chuck Norris (c) 2315",
+			Opacity:    0.25,
+			Width:      200,
+			DPI:        100,
+			Margin:     150,
+			Font:       "sans bold 12",
+			Background: Color{255, 255, 255},
+		},
+	}
+	runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkWatermarkImageJpeg(b *testing.B) {
+	watermark := readFile("transparent.png")
+	options := Options{
+		WatermarkImage: WatermarkImage{
+			Buf:     watermark,
+			Opacity: 0.25,
+			Left:    100,
+			Top:     100,
+		},
+	}
+	runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkWatermarImagePng(b *testing.B) {
+	watermark := readFile("transparent.png")
+	options := Options{
+		WatermarkImage: WatermarkImage{
+			Buf:     watermark,
+			Opacity: 0.25,
+			Left:    100,
+			Top:     100,
+		},
+	}
+	runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkWatermarImageWebp(b *testing.B) {
+	watermark := readFile("transparent.png")
+	options := Options{
+		WatermarkImage: WatermarkImage{
+			Buf:     watermark,
+			Opacity: 0.25,
+			Left:    100,
+			Top:     100,
+		},
+	}
+	runBenchmarkResize("test.webp", options, b)
+}

+ 172 - 0
_vendor/src/github.com/h2non/bimg/type.go

@@ -0,0 +1,172 @@
+package bimg
+
+import (
+	"regexp"
+	"sync"
+	"unicode/utf8"
+)
+
+const (
+	// UNKNOWN represents an unknow image type value.
+	UNKNOWN ImageType = iota
+	// JPEG represents the JPEG image type.
+	JPEG
+	// WEBP represents the WEBP image type.
+	WEBP
+	// PNG represents the PNG image type.
+	PNG
+	// TIFF represents the TIFF image type.
+	TIFF
+	// GIF represents the GIF image type.
+	GIF
+	// PDF represents the PDF type.
+	PDF
+	// SVG represents the SVG image type.
+	SVG
+	// MAGICK represents the libmagick compatible genetic image type.
+	MAGICK
+)
+
+// ImageType represents an image type value.
+type ImageType int
+
+var (
+	htmlCommentRegex = regexp.MustCompile("(?i)<!--([\\s\\S]*?)-->")
+	svgRegex         = regexp.MustCompile(`(?i)^\s*(?:<\?xml[^>]*>\s*)?(?:<!doctype svg[^>]*>\s*)?<svg[^>]*>[^*]*<\/svg>\s*$`)
+)
+
+// ImageTypes stores as pairs of image types supported and its alias names.
+var ImageTypes = map[ImageType]string{
+	JPEG:   "jpeg",
+	PNG:    "png",
+	WEBP:   "webp",
+	TIFF:   "tiff",
+	GIF:    "gif",
+	PDF:    "pdf",
+	SVG:    "svg",
+	MAGICK: "magick",
+}
+
+// imageMutex is used to provide thread-safe synchronization
+// for SupportedImageTypes map.
+var imageMutex = &sync.RWMutex{}
+
+// SupportedImageType represents whether a type can be loaded and/or saved by
+// the current libvips compilation.
+type SupportedImageType struct {
+	Load bool
+	Save bool
+}
+
+// SupportedImageTypes stores the optional image type supported
+// by the current libvips compilation.
+// Note: lazy evaluation as demand is required due
+// to bootstrap runtime limitation with C/libvips world.
+var SupportedImageTypes = map[ImageType]SupportedImageType{}
+
+// discoverSupportedImageTypes is used to fill SupportedImageTypes map.
+func discoverSupportedImageTypes() {
+	imageMutex.Lock()
+	for imageType := range ImageTypes {
+		SupportedImageTypes[imageType] = SupportedImageType{
+			Load: VipsIsTypeSupported(imageType),
+			Save: VipsIsTypeSupportedSave(imageType),
+		}
+	}
+	imageMutex.Unlock()
+}
+
+// isBinary checks if the given buffer is a binary file.
+func isBinary(buf []byte) bool {
+	if len(buf) < 24 {
+		return false
+	}
+	for i := 0; i < 24; i++ {
+		charCode, _ := utf8.DecodeRuneInString(string(buf[i]))
+		if charCode == 65533 || charCode <= 8 {
+			return true
+		}
+	}
+	return false
+}
+
+// IsSVGImage returns true if the given buffer is a valid SVG image.
+func IsSVGImage(buf []byte) bool {
+	return !isBinary(buf) && svgRegex.Match(htmlCommentRegex.ReplaceAll(buf, []byte{}))
+}
+
+// DetermineImageType determines the image type format (jpeg, png, webp or tiff)
+func DetermineImageType(buf []byte) ImageType {
+	return vipsImageType(buf)
+}
+
+// DetermineImageTypeName determines the image type format by name (jpeg, png, webp or tiff)
+func DetermineImageTypeName(buf []byte) string {
+	return ImageTypeName(vipsImageType(buf))
+}
+
+// IsImageTypeSupportedByVips returns true if the given image type
+// is supported by current libvips compilation.
+func IsImageTypeSupportedByVips(t ImageType) SupportedImageType {
+	imageMutex.RLock()
+
+	// Discover supported image types and cache the result
+	itShouldDiscover := len(SupportedImageTypes) == 0
+	if itShouldDiscover {
+		imageMutex.RUnlock()
+		discoverSupportedImageTypes()
+	}
+
+	// Check if image type is actually supported
+	supported, ok := SupportedImageTypes[t]
+	if !itShouldDiscover {
+		imageMutex.RUnlock()
+	}
+
+	if ok {
+		return supported
+	}
+	return SupportedImageType{Load: false, Save: false}
+}
+
+// IsTypeSupported checks if a given image type is supported
+func IsTypeSupported(t ImageType) bool {
+	_, ok := ImageTypes[t]
+	return ok && IsImageTypeSupportedByVips(t).Load
+}
+
+// IsTypeNameSupported checks if a given image type name is supported
+func IsTypeNameSupported(t string) bool {
+	for imageType, name := range ImageTypes {
+		if name == t {
+			return IsImageTypeSupportedByVips(imageType).Load
+		}
+	}
+	return false
+}
+
+// IsTypeSupportedSave checks if a given image type is support for saving
+func IsTypeSupportedSave(t ImageType) bool {
+	_, ok := ImageTypes[t]
+	return ok && IsImageTypeSupportedByVips(t).Save
+}
+
+// IsTypeNameSupportedSave checks if a given image type name is supported for
+// saving
+func IsTypeNameSupportedSave(t string) bool {
+	for imageType, name := range ImageTypes {
+		if name == t {
+			return IsImageTypeSupportedByVips(imageType).Save
+		}
+	}
+	return false
+}
+
+// ImageTypeName is used to get the human friendly name of an image format.
+func ImageTypeName(t ImageType) string {
+	imageType := ImageTypes[t]
+	if imageType == "" {
+		return "unknown"
+	}
+	return imageType
+}

+ 128 - 0
_vendor/src/github.com/h2non/bimg/type_test.go

@@ -0,0 +1,128 @@
+package bimg
+
+import (
+	"io/ioutil"
+	"os"
+	"path"
+	"testing"
+)
+
+func TestDeterminateImageType(t *testing.T) {
+	files := []struct {
+		name     string
+		expected ImageType
+	}{
+		{"test.jpg", JPEG},
+		{"test.png", PNG},
+		{"test.webp", WEBP},
+		{"test.gif", GIF},
+		{"test.pdf", PDF},
+		{"test.svg", SVG},
+		{"test.jp2", MAGICK},
+	}
+
+	for _, file := range files {
+		img, _ := os.Open(path.Join("fixtures", file.name))
+		buf, _ := ioutil.ReadAll(img)
+		defer img.Close()
+
+		if DetermineImageType(buf) != file.expected {
+			t.Fatal("Image type is not valid")
+		}
+	}
+}
+
+func TestDeterminateImageTypeName(t *testing.T) {
+	files := []struct {
+		name     string
+		expected string
+	}{
+		{"test.jpg", "jpeg"},
+		{"test.png", "png"},
+		{"test.webp", "webp"},
+		{"test.gif", "gif"},
+		{"test.pdf", "pdf"},
+		{"test.svg", "svg"},
+		{"test.jp2", "magick"},
+	}
+
+	for _, file := range files {
+		img, _ := os.Open(path.Join("fixtures", file.name))
+		buf, _ := ioutil.ReadAll(img)
+		defer img.Close()
+
+		if DetermineImageTypeName(buf) != file.expected {
+			t.Fatal("Image type is not valid")
+		}
+	}
+}
+
+func TestIsTypeSupported(t *testing.T) {
+	types := []struct {
+		name ImageType
+	}{
+		{JPEG}, {PNG}, {WEBP}, {GIF}, {PDF},
+	}
+
+	for _, n := range types {
+		if IsTypeSupported(n.name) == false {
+			t.Fatalf("Image type %#v is not valid", ImageTypes[n.name])
+		}
+	}
+}
+
+func TestIsTypeNameSupported(t *testing.T) {
+	types := []struct {
+		name     string
+		expected bool
+	}{
+		{"jpeg", true},
+		{"png", true},
+		{"webp", true},
+		{"gif", true},
+		{"pdf", true},
+	}
+
+	for _, n := range types {
+		if IsTypeNameSupported(n.name) != n.expected {
+			t.Fatalf("Image type %#v is not valid", n.name)
+		}
+	}
+}
+
+func TestIsTypeSupportedSave(t *testing.T) {
+	types := []struct {
+		name ImageType
+	}{
+		{JPEG}, {PNG}, {WEBP},
+	}
+	if VipsVersion >= "8.5.0" {
+		types = append(types, struct{ name ImageType }{TIFF})
+	}
+
+	for _, n := range types {
+		if IsTypeSupportedSave(n.name) == false {
+			t.Fatalf("Image type %#v is not valid", ImageTypes[n.name])
+		}
+	}
+}
+
+func TestIsTypeNameSupportedSave(t *testing.T) {
+	types := []struct {
+		name     string
+		expected bool
+	}{
+		{"jpeg", true},
+		{"png", true},
+		{"webp", true},
+		{"gif", false},
+		{"pdf", false},
+		{"tiff", VipsVersion >= "8.5.0"},
+	}
+
+	for _, n := range types {
+		if IsTypeNameSupportedSave(n.name) != n.expected {
+			t.Fatalf("Image type %#v is not valid", n.name)
+		}
+	}
+}

+ 4 - 0
_vendor/src/github.com/h2non/bimg/version.go

@@ -0,0 +1,4 @@
+package bimg
+
+// Version represents the current package semantic version.
+const Version = "1.0.9"

+ 632 - 0
_vendor/src/github.com/h2non/bimg/vips.go

@@ -0,0 +1,632 @@
+package bimg
+
+/*
+#cgo pkg-config: vips
+#include "vips.h"
+*/
+import "C"
+
+import (
+	"errors"
+	"fmt"
+	"math"
+	"os"
+	"runtime"
+	"strings"
+	"sync"
+	"unsafe"
+
+	d "github.com/tj/go-debug"
+)
+
+// debug is internally used to
+var debug = d.Debug("bimg")
+
+// VipsVersion exposes the current libvips semantic version
+const VipsVersion = string(C.VIPS_VERSION)
+
+// VipsMajorVersion exposes the current libvips major version number
+const VipsMajorVersion = int(C.VIPS_MAJOR_VERSION)
+
+// VipsMinorVersion exposes the current libvips minor version number
+const VipsMinorVersion = int(C.VIPS_MINOR_VERSION)
+
+const (
+	maxCacheMem  = 100 * 1024 * 1024
+	maxCacheSize = 500
+)
+
+var (
+	m           sync.Mutex
+	initialized bool
+)
+
+// VipsMemoryInfo represents the memory stats provided by libvips.
+type VipsMemoryInfo struct {
+	Memory          int64
+	MemoryHighwater int64
+	Allocations     int64
+}
+
+// vipsSaveOptions represents the internal option used to talk with libvips.
+type vipsSaveOptions struct {
+	Quality        int
+	Compression    int
+	Type           ImageType
+	Interlace      bool
+	NoProfile      bool
+	Interpretation Interpretation
+}
+
+type vipsWatermarkOptions struct {
+	Width       C.int
+	DPI         C.int
+	Margin      C.int
+	NoReplicate C.int
+	Opacity     C.float
+	Background  [3]C.double
+}
+
+type vipsWatermarkImageOptions struct {
+	Left    C.int
+	Top     C.int
+	Opacity C.float
+}
+
+type vipsWatermarkTextOptions struct {
+	Text *C.char
+	Font *C.char
+}
+
+func init() {
+	Initialize()
+}
+
+// Initialize is used to explicitly start libvips in thread-safe way.
+// Only call this function if you have previously turned off libvips.
+func Initialize() {
+	if C.VIPS_MAJOR_VERSION <= 7 && C.VIPS_MINOR_VERSION < 40 {
+		panic("unsupported libvips version!")
+	}
+
+	m.Lock()
+	runtime.LockOSThread()
+	defer m.Unlock()
+	defer runtime.UnlockOSThread()
+
+	err := C.vips_init(C.CString("bimg"))
+	if err != 0 {
+		panic("unable to start vips!")
+	}
+
+	// Set libvips cache params
+	C.vips_cache_set_max_mem(maxCacheMem)
+	C.vips_cache_set_max(maxCacheSize)
+
+	// Define a custom thread concurrency limit in libvips (this may generate thread-unsafe issues)
+	// See: https://github.com/jcupitt/libvips/issues/261#issuecomment-92850414
+	if os.Getenv("VIPS_CONCURRENCY") == "" {
+		C.vips_concurrency_set(1)
+	}
+
+	// Enable libvips cache tracing
+	if os.Getenv("VIPS_TRACE") != "" {
+		C.vips_enable_cache_set_trace()
+	}
+
+	initialized = true
+}
+
+// Shutdown is used to shutdown libvips in a thread-safe way.
+// You can call this to drop caches as well.
+// If libvips was already initialized, the function is no-op
+func Shutdown() {
+	m.Lock()
+	defer m.Unlock()
+
+	if initialized {
+		C.vips_shutdown()
+		initialized = false
+	}
+}
+
+// VipsDebugInfo outputs to stdout libvips collected data. Useful for debugging.
+func VipsDebugInfo() {
+	C.im__print_all()
+}
+
+// VipsMemory gets memory info stats from libvips (cache size, memory allocs...)
+func VipsMemory() VipsMemoryInfo {
+	return VipsMemoryInfo{
+		Memory:          int64(C.vips_tracked_get_mem()),
+		MemoryHighwater: int64(C.vips_tracked_get_mem_highwater()),
+		Allocations:     int64(C.vips_tracked_get_allocs()),
+	}
+}
+
+// VipsIsTypeSupported returns true if the given image type
+// is supported by the current libvips compilation.
+func VipsIsTypeSupported(t ImageType) bool {
+	if t == JPEG {
+		return int(C.vips_type_find_bridge(C.JPEG)) != 0
+	}
+	if t == WEBP {
+		return int(C.vips_type_find_bridge(C.WEBP)) != 0
+	}
+	if t == PNG {
+		return int(C.vips_type_find_bridge(C.PNG)) != 0
+	}
+	if t == GIF {
+		return int(C.vips_type_find_bridge(C.GIF)) != 0
+	}
+	if t == PDF {
+		return int(C.vips_type_find_bridge(C.PDF)) != 0
+	}
+	if t == SVG {
+		return int(C.vips_type_find_bridge(C.SVG)) != 0
+	}
+	if t == TIFF {
+		return int(C.vips_type_find_bridge(C.TIFF)) != 0
+	}
+	if t == MAGICK {
+		return int(C.vips_type_find_bridge(C.MAGICK)) != 0
+	}
+	return false
+}
+
+// VipsIsTypeSupportedSave returns true if the given image type
+// is supported by the current libvips compilation for the
+// save operation.
+func VipsIsTypeSupportedSave(t ImageType) bool {
+	if t == JPEG {
+		return int(C.vips_type_find_save_bridge(C.JPEG)) != 0
+	}
+	if t == WEBP {
+		return int(C.vips_type_find_save_bridge(C.WEBP)) != 0
+	}
+	if t == PNG {
+		return int(C.vips_type_find_save_bridge(C.PNG)) != 0
+	}
+	if t == TIFF {
+		return int(C.vips_type_find_save_bridge(C.TIFF)) != 0
+	}
+	return false
+}
+
+func vipsExifOrientation(image *C.VipsImage) int {
+	return int(C.vips_exif_orientation(image))
+}
+
+func vipsHasAlpha(image *C.VipsImage) bool {
+	return int(C.has_alpha_channel(image)) > 0
+}
+
+func vipsHasProfile(image *C.VipsImage) bool {
+	return int(C.has_profile_embed(image)) > 0
+}
+
+func vipsWindowSize(name string) float64 {
+	cname := C.CString(name)
+	defer C.free(unsafe.Pointer(cname))
+	return float64(C.interpolator_window_size(cname))
+}
+
+func vipsSpace(image *C.VipsImage) string {
+	return C.GoString(C.vips_enum_nick_bridge(image))
+}
+
+func vipsRotate(image *C.VipsImage, angle Angle) (*C.VipsImage, error) {
+	var out *C.VipsImage
+	defer C.g_object_unref(C.gpointer(image))
+
+	err := C.vips_rotate(image, &out, C.int(angle))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return out, nil
+}
+
+func vipsFlip(image *C.VipsImage, direction Direction) (*C.VipsImage, error) {
+	var out *C.VipsImage
+	defer C.g_object_unref(C.gpointer(image))
+
+	err := C.vips_flip_bridge(image, &out, C.int(direction))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return out, nil
+}
+
+func vipsZoom(image *C.VipsImage, zoom int) (*C.VipsImage, error) {
+	var out *C.VipsImage
+	defer C.g_object_unref(C.gpointer(image))
+
+	err := C.vips_zoom_bridge(image, &out, C.int(zoom), C.int(zoom))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return out, nil
+}
+
+func vipsWatermark(image *C.VipsImage, w Watermark) (*C.VipsImage, error) {
+	var out *C.VipsImage
+
+	// Defaults
+	noReplicate := 0
+	if w.NoReplicate {
+		noReplicate = 1
+	}
+
+	text := C.CString(w.Text)
+	font := C.CString(w.Font)
+	background := [3]C.double{C.double(w.Background.R), C.double(w.Background.G), C.double(w.Background.B)}
+
+	textOpts := vipsWatermarkTextOptions{text, font}
+	opts := vipsWatermarkOptions{C.int(w.Width), C.int(w.DPI), C.int(w.Margin), C.int(noReplicate), C.float(w.Opacity), background}
+
+	defer C.free(unsafe.Pointer(text))
+	defer C.free(unsafe.Pointer(font))
+
+	err := C.vips_watermark(image, &out, (*C.WatermarkTextOptions)(unsafe.Pointer(&textOpts)), (*C.WatermarkOptions)(unsafe.Pointer(&opts)))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return out, nil
+}
+
+func vipsRead(buf []byte) (*C.VipsImage, ImageType, error) {
+	var image *C.VipsImage
+	imageType := vipsImageType(buf)
+
+	if imageType == UNKNOWN {
+		return nil, UNKNOWN, errors.New("Unsupported image format")
+	}
+
+	length := C.size_t(len(buf))
+	imageBuf := unsafe.Pointer(&buf[0])
+
+	err := C.vips_init_image(imageBuf, length, C.int(imageType), &image)
+	if err != 0 {
+		return nil, UNKNOWN, catchVipsError()
+	}
+
+	return image, imageType, nil
+}
+
+func vipsColourspaceIsSupportedBuffer(buf []byte) (bool, error) {
+	image, _, err := vipsRead(buf)
+	if err != nil {
+		return false, err
+	}
+	C.g_object_unref(C.gpointer(image))
+	return vipsColourspaceIsSupported(image), nil
+}
+
+func vipsColourspaceIsSupported(image *C.VipsImage) bool {
+	return int(C.vips_colourspace_issupported_bridge(image)) == 1
+}
+
+func vipsInterpretationBuffer(buf []byte) (Interpretation, error) {
+	image, _, err := vipsRead(buf)
+	if err != nil {
+		return InterpretationError, err
+	}
+	C.g_object_unref(C.gpointer(image))
+	return vipsInterpretation(image), nil
+}
+
+func vipsInterpretation(image *C.VipsImage) Interpretation {
+	return Interpretation(C.vips_image_guess_interpretation_bridge(image))
+}
+
+func vipsFlattenBackground(image *C.VipsImage, background Color) (*C.VipsImage, error) {
+	var outImage *C.VipsImage
+
+	backgroundC := [3]C.double{
+		C.double(background.R),
+		C.double(background.G),
+		C.double(background.B),
+	}
+
+	if vipsHasAlpha(image) {
+		err := C.vips_flatten_background_brigde(image, &outImage,
+			backgroundC[0], backgroundC[1], backgroundC[2])
+		if int(err) != 0 {
+			return nil, catchVipsError()
+		}
+		C.g_object_unref(C.gpointer(image))
+		image = outImage
+	}
+
+	return image, nil
+}
+
+func vipsPreSave(image *C.VipsImage, o *vipsSaveOptions) (*C.VipsImage, error) {
+	// Remove ICC profile metadata
+	if o.NoProfile {
+		C.remove_profile(image)
+	}
+
+	// Use a default interpretation and cast it to C type
+	if o.Interpretation == 0 {
+		o.Interpretation = InterpretationSRGB
+	}
+	interpretation := C.VipsInterpretation(o.Interpretation)
+
+	// Apply the proper colour space
+	var outImage *C.VipsImage
+	if vipsColourspaceIsSupported(image) {
+		err := C.vips_colourspace_bridge(image, &outImage, interpretation)
+		if int(err) != 0 {
+			return nil, catchVipsError()
+		}
+		image = outImage
+	}
+
+	return image, nil
+}
+
+func vipsSave(image *C.VipsImage, o vipsSaveOptions) ([]byte, error) {
+	defer C.g_object_unref(C.gpointer(image))
+
+	tmpImage, err := vipsPreSave(image, &o)
+	if err != nil {
+		return nil, err
+	}
+
+	// When an image has an unsupported color space, vipsPreSave
+	// returns the pointer of the image passed to it unmodified.
+	// When this occurs, we must take care to not dereference the
+	// original image a second time; we may otherwise erroneously
+	// free the object twice.
+	if tmpImage != image {
+		defer C.g_object_unref(C.gpointer(tmpImage))
+	}
+
+	length := C.size_t(0)
+	saveErr := C.int(0)
+	interlace := C.int(boolToInt(o.Interlace))
+	quality := C.int(o.Quality)
+
+	if o.Type != 0 && !IsTypeSupportedSave(o.Type) {
+		return nil, fmt.Errorf("VIPS cannot save to %#v", ImageTypes[o.Type])
+	}
+	var ptr unsafe.Pointer
+	switch o.Type {
+	case WEBP:
+		saveErr = C.vips_webpsave_bridge(tmpImage, &ptr, &length, 1, quality)
+	case PNG:
+		saveErr = C.vips_pngsave_bridge(tmpImage, &ptr, &length, 1, C.int(o.Compression), quality, interlace)
+	case TIFF:
+		saveErr = C.vips_tiffsave_bridge(tmpImage, &ptr, &length)
+	default:
+		saveErr = C.vips_jpegsave_bridge(tmpImage, &ptr, &length, 1, quality, interlace)
+	}
+
+	if int(saveErr) != 0 {
+		return nil, catchVipsError()
+	}
+
+	buf := C.GoBytes(ptr, C.int(length))
+
+	// Clean up
+	C.g_free(C.gpointer(ptr))
+	C.vips_error_clear()
+
+	return buf, nil
+}
+
+func getImageBuffer(image *C.VipsImage) ([]byte, error) {
+	var ptr unsafe.Pointer
+
+	length := C.size_t(0)
+	interlace := C.int(0)
+	quality := C.int(100)
+
+	err := C.int(0)
+	err = C.vips_jpegsave_bridge(image, &ptr, &length, 1, quality, interlace)
+	if int(err) != 0 {
+		return nil, catchVipsError()
+	}
+
+	defer C.g_free(C.gpointer(ptr))
+	defer C.vips_error_clear()
+
+	return C.GoBytes(ptr, C.int(length)), nil
+}
+
+func vipsExtract(image *C.VipsImage, left, top, width, height int) (*C.VipsImage, error) {
+	var buf *C.VipsImage
+	defer C.g_object_unref(C.gpointer(image))
+
+	if width > MaxSize || height > MaxSize {
+		return nil, errors.New("Maximum image size exceeded")
+	}
+
+	top, left = max(top), max(left)
+	err := C.vips_extract_area_bridge(image, &buf, C.int(left), C.int(top), C.int(width), C.int(height))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return buf, nil
+}
+
+func vipsSmartCrop(image *C.VipsImage, width, height int) (*C.VipsImage, error) {
+	var buf *C.VipsImage
+	defer C.g_object_unref(C.gpointer(image))
+
+	if width > MaxSize || height > MaxSize {
+		return nil, errors.New("Maximum image size exceeded")
+	}
+
+	err := C.vips_smartcrop_bridge(image, &buf, C.int(width), C.int(height))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return buf, nil
+}
+
+func vipsShrinkJpeg(buf []byte, input *C.VipsImage, shrink int) (*C.VipsImage, error) {
+	var image *C.VipsImage
+	var ptr = unsafe.Pointer(&buf[0])
+	defer C.g_object_unref(C.gpointer(input))
+
+	err := C.vips_jpegload_buffer_shrink(ptr, C.size_t(len(buf)), &image, C.int(shrink))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return image, nil
+}
+
+func vipsShrink(input *C.VipsImage, shrink int) (*C.VipsImage, error) {
+	var image *C.VipsImage
+	defer C.g_object_unref(C.gpointer(input))
+
+	err := C.vips_shrink_bridge(input, &image, C.double(float64(shrink)), C.double(float64(shrink)))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return image, nil
+}
+
+func vipsEmbed(input *C.VipsImage, left, top, width, height int, extend Extend, background Color) (*C.VipsImage, error) {
+	var image *C.VipsImage
+
+	// Max extend value, see: http://www.vips.ecs.soton.ac.uk/supported/8.4/doc/html/libvips/libvips-conversion.html#VipsExtend
+	if extend > 5 {
+		extend = ExtendBackground
+	}
+
+	defer C.g_object_unref(C.gpointer(input))
+	err := C.vips_embed_bridge(input, &image, C.int(left), C.int(top), C.int(width),
+		C.int(height), C.int(extend), C.double(background.R), C.double(background.G), C.double(background.B))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return image, nil
+}
+
+func vipsAffine(input *C.VipsImage, residualx, residualy float64, i Interpolator) (*C.VipsImage, error) {
+	var image *C.VipsImage
+	cstring := C.CString(i.String())
+	interpolator := C.vips_interpolate_new(cstring)
+
+	defer C.free(unsafe.Pointer(cstring))
+	defer C.g_object_unref(C.gpointer(input))
+	defer C.g_object_unref(C.gpointer(interpolator))
+
+	err := C.vips_affine_interpolator(input, &image, C.double(residualx), 0, 0, C.double(residualy), interpolator)
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return image, nil
+}
+
+func vipsImageType(buf []byte) ImageType {
+	if len(buf) == 0 {
+		return UNKNOWN
+	}
+	if buf[0] == 0x89 && buf[1] == 0x50 && buf[2] == 0x4E && buf[3] == 0x47 {
+		return PNG
+	}
+	if buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF {
+		return JPEG
+	}
+	if IsTypeSupported(WEBP) && buf[8] == 0x57 && buf[9] == 0x45 && buf[10] == 0x42 && buf[11] == 0x50 {
+		return WEBP
+	}
+	if IsTypeSupported(TIFF) &&
+		((buf[0] == 0x49 && buf[1] == 0x49 && buf[2] == 0x2A && buf[3] == 0x0) ||
+			(buf[0] == 0x4D && buf[1] == 0x4D && buf[2] == 0x0 && buf[3] == 0x2A)) {
+		return TIFF
+	}
+	if IsTypeSupported(GIF) && buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 {
+		return GIF
+	}
+	if IsTypeSupported(PDF) && buf[0] == 0x25 && buf[1] == 0x50 && buf[2] == 0x44 && buf[3] == 0x46 {
+		return PDF
+	}
+	if IsTypeSupported(SVG) && IsSVGImage(buf) {
+		return SVG
+	}
+	if IsTypeSupported(MAGICK) && strings.HasSuffix(readImageType(buf), "MagickBuffer") {
+		return MAGICK
+	}
+	return UNKNOWN
+}
+
+func readImageType(buf []byte) string {
+	length := C.size_t(len(buf))
+	imageBuf := unsafe.Pointer(&buf[0])
+	load := C.vips_foreign_find_load_buffer(imageBuf, length)
+	return C.GoString(load)
+}
+
+func catchVipsError() error {
+	s := C.GoString(C.vips_error_buffer())
+	C.vips_error_clear()
+	C.vips_thread_shutdown()
+	return errors.New(s)
+}
+
+func boolToInt(b bool) int {
+	if b {
+		return 1
+	}
+	return 0
+}
+
+func vipsGaussianBlur(image *C.VipsImage, o GaussianBlur) (*C.VipsImage, error) {
+	var out *C.VipsImage
+	defer C.g_object_unref(C.gpointer(image))
+
+	err := C.vips_gaussblur_bridge(image, &out, C.double(o.Sigma), C.double(o.MinAmpl))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+	return out, nil
+}
+
+func vipsSharpen(image *C.VipsImage, o Sharpen) (*C.VipsImage, error) {
+	var out *C.VipsImage
+	defer C.g_object_unref(C.gpointer(image))
+
+	err := C.vips_sharpen_bridge(image, &out, C.int(o.Radius), C.double(o.X1), C.double(o.Y2), C.double(o.Y3), C.double(o.M1), C.double(o.M2))
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+	return out, nil
+}
+
+func max(x int) int {
+	return int(math.Max(float64(x), 0))
+}
+
+func vipsDrawWatermark(image *C.VipsImage, o WatermarkImage) (*C.VipsImage, error) {
+	var out *C.VipsImage
+
+	watermark, _, e := vipsRead(o.Buf)
+	if e != nil {
+		return nil, e
+	}
+
+	opts := vipsWatermarkImageOptions{C.int(o.Left), C.int(o.Top), C.float(o.Opacity)}
+
+	err := C.vips_watermark_image(image, watermark, &out, (*C.WatermarkImageOptions)(unsafe.Pointer(&opts)))
+
+	if err != 0 {
+		return nil, catchVipsError()
+	}
+
+	return out, nil
+}

+ 532 - 0
_vendor/src/github.com/h2non/bimg/vips.h

@@ -0,0 +1,532 @@
+#include <stdlib.h>
+#include <string.h>
+#include <vips/vips.h>
+#include <vips/foreign.h>
+#include <vips/vips7compat.h>
+
+/**
+ * Starting libvips 7.41, VIPS_ANGLE_x has been renamed to VIPS_ANGLE_Dx
+ * "to help python". So we provide the macro to correctly build for versions
+ * before 7.41.x.
+ * https://github.com/jcupitt/libvips/blob/master/ChangeLog#L128
+ */
+
+#if (VIPS_MAJOR_VERSION == 7 && VIPS_MINOR_VERSION < 41)
+#define VIPS_ANGLE_D0 VIPS_ANGLE_0
+#define VIPS_ANGLE_D90 VIPS_ANGLE_90
+#define VIPS_ANGLE_D180 VIPS_ANGLE_180
+#define VIPS_ANGLE_D270 VIPS_ANGLE_270
+#endif
+
+#define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation"
+
+enum types {
+	UNKNOWN = 0,
+	JPEG,
+	WEBP,
+	PNG,
+	TIFF,
+	GIF,
+	PDF,
+	SVG,
+	MAGICK
+};
+
+typedef struct {
+	const char *Text;
+	const char *Font;
+} WatermarkTextOptions;
+
+typedef struct {
+	int    Width;
+	int    DPI;
+	int    Margin;
+	int    NoReplicate;
+	float  Opacity;
+	double Background[3];
+} WatermarkOptions;
+
+typedef struct {
+	int    Left;
+	int    Top;
+	float    Opacity;
+} WatermarkImageOptions;
+
+static unsigned long
+has_profile_embed(VipsImage *image) {
+	return vips_image_get_typeof(image, VIPS_META_ICC_NAME);
+}
+
+static void
+remove_profile(VipsImage *image) {
+	vips_image_remove(image, VIPS_META_ICC_NAME);
+}
+
+static gboolean
+with_interlace(int interlace) {
+	return interlace > 0 ? TRUE : FALSE;
+}
+
+static int
+has_alpha_channel(VipsImage *image) {
+	return (
+		(image->Bands == 2 && image->Type == VIPS_INTERPRETATION_B_W) ||
+		(image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) ||
+		(image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK)
+	) ? 1 : 0;
+}
+
+/**
+ * This method is here to handle the weird initialization of the vips lib.
+ * libvips use a macro VIPS_INIT() that call vips__init() in version < 7.41,
+ * or calls vips_init() in version >= 7.41.
+ *
+ * Anyway, it's not possible to build bimg on Debian Jessie with libvips 7.40.x,
+ * as vips_init() is a macro to VIPS_INIT(), which is also a macro, hence, cgo
+ * is unable to determine the return type of vips_init(), making the build impossible.
+ * In order to correctly build bimg, for version < 7.41, we should undef vips_init and
+ * creates a vips_init() method that calls VIPS_INIT().
+ */
+
+#if (VIPS_MAJOR_VERSION == 7 && VIPS_MINOR_VERSION < 41)
+#undef vips_init
+int
+vips_init(const char *argv0)
+{
+	return VIPS_INIT(argv0);
+}
+#endif
+
+void
+vips_enable_cache_set_trace() {
+	vips_cache_set_trace(TRUE);
+}
+
+int
+vips_affine_interpolator(VipsImage *in, VipsImage **out, double a, double b, double c, double d, VipsInterpolate *interpolator) {
+	return vips_affine(in, out, a, b, c, d, "interpolate", interpolator, NULL);
+}
+
+int
+vips_jpegload_buffer_shrink(void *buf, size_t len, VipsImage **out, int shrink) {
+	return vips_jpegload_buffer(buf, len, out, "shrink", shrink, NULL);
+}
+
+int
+vips_flip_bridge(VipsImage *in, VipsImage **out, int direction) {
+	return vips_flip(in, out, direction, NULL);
+}
+
+int
+vips_shrink_bridge(VipsImage *in, VipsImage **out, double xshrink, double yshrink) {
+	return vips_shrink(in, out, xshrink, yshrink, NULL);
+}
+
+int
+vips_type_find_bridge(int t) {
+	if (t == GIF) {
+		return vips_type_find("VipsOperation", "gifload");
+	}
+	if (t == PDF) {
+		return vips_type_find("VipsOperation", "pdfload");
+	}
+	if (t == TIFF) {
+		return vips_type_find("VipsOperation", "tiffload");
+	}
+	if (t == SVG) {
+		return vips_type_find("VipsOperation", "svgload");
+	}
+	if (t == WEBP) {
+		return vips_type_find("VipsOperation", "webpload");
+	}
+	if (t == PNG) {
+		return vips_type_find("VipsOperation", "pngload");
+	}
+	if (t == JPEG) {
+		return vips_type_find("VipsOperation", "jpegload");
+	}
+	if (t == MAGICK) {
+		return vips_type_find("VipsOperation", "magickload");
+	}
+	return 0;
+}
+
+int
+vips_type_find_save_bridge(int t) {
+	if (t == TIFF) {
+		return vips_type_find("VipsOperation", "tiffsave_buffer");
+	}
+	if (t == WEBP) {
+		return vips_type_find("VipsOperation", "webpsave_buffer");
+	}
+	if (t == PNG) {
+		return vips_type_find("VipsOperation", "pngsave_buffer");
+	}
+	if (t == JPEG) {
+		return vips_type_find("VipsOperation", "jpegsave_buffer");
+	}
+	return 0;
+}
+
+int
+vips_rotate(VipsImage *in, VipsImage **out, int angle) {
+	int rotate = VIPS_ANGLE_D0;
+
+	angle %= 360;
+
+	if (angle == 45) {
+		rotate = VIPS_ANGLE45_D45;
+	} else if (angle == 90) {
+		rotate = VIPS_ANGLE_D90;
+	} else if (angle == 135) {
+		rotate = VIPS_ANGLE45_D135;
+	} else if (angle == 180) {
+		rotate = VIPS_ANGLE_D180;
+	} else if (angle == 225) {
+		rotate = VIPS_ANGLE45_D225;
+	} else if (angle == 270) {
+		rotate = VIPS_ANGLE_D270;
+	} else if (angle == 315) {
+		rotate = VIPS_ANGLE45_D315;
+	} else {
+		angle = 0;
+	}
+
+	if (angle > 0 && angle % 90 != 0) {
+		return vips_rot45(in, out, "angle", rotate, NULL);
+	} else {
+		return vips_rot(in, out, rotate, NULL);
+	}
+}
+
+int
+vips_exif_orientation(VipsImage *image) {
+	int orientation = 0;
+	const char *exif;
+	if (
+		vips_image_get_typeof(image, EXIF_IFD0_ORIENTATION) != 0 &&
+		!vips_image_get_string(image, EXIF_IFD0_ORIENTATION, &exif)
+	) {
+		orientation = atoi(&exif[0]);
+	}
+	return orientation;
+}
+
+int
+interpolator_window_size(char const *name) {
+	VipsInterpolate *interpolator = vips_interpolate_new(name);
+	int window_size = vips_interpolate_get_window_size(interpolator);
+	g_object_unref(interpolator);
+	return window_size;
+}
+
+const char *
+vips_enum_nick_bridge(VipsImage *image) {
+	return vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
+}
+
+int
+vips_zoom_bridge(VipsImage *in, VipsImage **out, int xfac, int yfac) {
+	return vips_zoom(in, out, xfac, yfac, NULL);
+}
+
+int
+vips_embed_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height, int extend, double r, double g, double b) {
+	if (extend == VIPS_EXTEND_BACKGROUND) {
+		double background[3] = {r, g, b};
+		VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3);
+		return vips_embed(in, out, left, top, width, height, "extend", extend, "background", vipsBackground, NULL);
+	}
+	return vips_embed(in, out, left, top, width, height, "extend", extend, NULL);
+}
+
+int
+vips_extract_area_bridge(VipsImage *in, VipsImage **out, int left, int top, int width, int height) {
+	return vips_extract_area(in, out, left, top, width, height, NULL);
+}
+
+int
+vips_colourspace_issupported_bridge(VipsImage *in) {
+	return vips_colourspace_issupported(in) ? 1 : 0;
+}
+
+VipsInterpretation
+vips_image_guess_interpretation_bridge(VipsImage *in) {
+	return vips_image_guess_interpretation(in);
+}
+
+int
+vips_colourspace_bridge(VipsImage *in, VipsImage **out, VipsInterpretation space) {
+	return vips_colourspace(in, out, space, NULL);
+}
+
+int
+vips_jpegsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int quality, int interlace) {
+	return vips_jpegsave_buffer(in, buf, len,
+		"strip", strip,
+		"Q", quality,
+		"optimize_coding", TRUE,
+		"interlace", with_interlace(interlace),
+		NULL
+	);
+}
+
+int
+vips_pngsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int compression, int quality, int interlace) {
+#if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42))
+	return vips_pngsave_buffer(in, buf, len,
+		"strip", FALSE,
+		"compression", compression,
+		"interlace", with_interlace(interlace),
+		"filter", VIPS_FOREIGN_PNG_FILTER_NONE,
+		NULL
+	);
+#else
+	return vips_pngsave_buffer(in, buf, len,
+		"strip", FALSE,
+		"compression", compression,
+		"interlace", with_interlace(interlace),
+		NULL
+	);
+#endif
+}
+
+int
+vips_webpsave_bridge(VipsImage *in, void **buf, size_t *len, int strip, int quality) {
+	return vips_webpsave_buffer(in, buf, len,
+		"strip", strip,
+		"Q", quality,
+		NULL
+	);
+}
+
+int
+vips_tiffsave_bridge(VipsImage *in, void **buf, size_t *len) {
+#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 5)
+	return vips_tiffsave_buffer(in, buf, len, NULL);
+#else
+	return 0;
+#endif
+}
+
+int
+vips_is_16bit (VipsInterpretation interpretation) {
+	return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
+}
+
+int
+vips_flatten_background_brigde(VipsImage *in, VipsImage **out, double r, double g, double b) {
+	if (vips_is_16bit(in->Type)) {
+		r = 65535 * r / 255;
+		g = 65535 * g / 255;
+		b = 65535 * b / 255;
+	}
+
+	double background[3] = {r, g, b};
+	VipsArrayDouble *vipsBackground = vips_array_double_new(background, 3);
+
+	return vips_flatten(in, out,
+		"background", vipsBackground,
+		"max_alpha", vips_is_16bit(in->Type) ? 65535.0 : 255.0,
+		NULL
+	);
+}
+
+int
+vips_init_image (void *buf, size_t len, int imageType, VipsImage **out) {
+	int code = 1;
+
+	if (imageType == JPEG) {
+		code = vips_jpegload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL);
+	} else if (imageType == PNG) {
+		code = vips_pngload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL);
+	} else if (imageType == WEBP) {
+		code = vips_webpload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL);
+	} else if (imageType == TIFF) {
+		code = vips_tiffload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL);
+#if (VIPS_MAJOR_VERSION >= 8)
+#if (VIPS_MINOR_VERSION >= 3)
+	} else if (imageType == GIF) {
+		code = vips_gifload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL);
+	} else if (imageType == PDF) {
+		code = vips_pdfload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL);
+	} else if (imageType == SVG) {
+		code = vips_svgload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL);
+#endif
+	} else if (imageType == MAGICK) {
+		code = vips_magickload_buffer(buf, len, out, "access", VIPS_ACCESS_RANDOM, NULL);
+#endif
+	}
+
+	return code;
+}
+
+int
+vips_watermark_replicate (VipsImage *orig, VipsImage *in, VipsImage **out) {
+	VipsImage *cache = vips_image_new();
+
+	if (
+		vips_replicate(in, &cache,
+			1 + orig->Xsize / in->Xsize,
+			1 + orig->Ysize / in->Ysize, NULL) ||
+		vips_crop(cache, out, 0, 0, orig->Xsize, orig->Ysize, NULL)
+	) {
+		g_object_unref(cache);
+		return 1;
+	}
+
+	g_object_unref(cache);
+	return 0;
+}
+
+int
+vips_watermark(VipsImage *in, VipsImage **out, WatermarkTextOptions *to, WatermarkOptions *o) {
+	double ones[3] = { 1, 1, 1 };
+
+	VipsImage *base = vips_image_new();
+	VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 10);
+	t[0] = in;
+
+	// Make the mask.
+	if (
+		vips_text(&t[1], to->Text,
+			"width", o->Width,
+			"dpi", o->DPI,
+			"font", to->Font,
+			NULL) ||
+		vips_linear1(t[1], &t[2], o->Opacity, 0.0, NULL) ||
+		vips_cast(t[2], &t[3], VIPS_FORMAT_UCHAR, NULL) ||
+		vips_embed(t[3], &t[4], 100, 100, t[3]->Xsize + o->Margin, t[3]->Ysize + o->Margin, NULL)
+		) {
+		g_object_unref(base);
+		return 1;
+	}
+
+	// Replicate if necessary
+	if (o->NoReplicate != 1) {
+		VipsImage *cache = vips_image_new();
+		if (vips_watermark_replicate(t[0], t[4], &cache)) {
+			g_object_unref(cache);
+			g_object_unref(base);
+			return 1;
+		}
+		g_object_unref(t[4]);
+		t[4] = cache;
+	}
+
+	// Make the constant image to paint the text with.
+	if (
+		vips_black(&t[5], 1, 1, NULL) ||
+		vips_linear(t[5], &t[6], ones, o->Background, 3, NULL) ||
+		vips_cast(t[6], &t[7], VIPS_FORMAT_UCHAR, NULL) ||
+		vips_copy(t[7], &t[8], "interpretation", t[0]->Type, NULL) ||
+		vips_embed(t[8], &t[9], 0, 0, t[0]->Xsize, t[0]->Ysize, "extend", VIPS_EXTEND_COPY, NULL)
+		) {
+		g_object_unref(base);
+		return 1;
+	}
+
+	// Blend the mask and text and write to output.
+	if (vips_ifthenelse(t[4], t[9], t[0], out, "blend", TRUE, NULL)) {
+		g_object_unref(base);
+		return 1;
+	}
+
+	g_object_unref(base);
+	return 0;
+}
+
+int
+vips_gaussblur_bridge(VipsImage *in, VipsImage **out, double sigma, double min_ampl) {
+#if (VIPS_MAJOR_VERSION == 7 && VIPS_MINOR_VERSION < 41)
+	return vips_gaussblur(in, out, (int) sigma, NULL);
+#else
+	return vips_gaussblur(in, out, sigma, NULL, "min_ampl", min_ampl, NULL);
+#endif
+}
+
+int
+vips_sharpen_bridge(VipsImage *in, VipsImage **out, int radius, double x1, double y2, double y3, double m1, double m2) {
+#if (VIPS_MAJOR_VERSION == 7 && VIPS_MINOR_VERSION < 41)
+	return vips_sharpen(in, out, radius, x1, y2, y3, m1, m2, NULL);
+#else
+	return vips_sharpen(in, out, "radius", radius, "x1", x1, "y2", y2, "y3", y3, "m1", m1, "m2", m2, NULL);
+#endif
+}
+
+int
+vips_add_band(VipsImage *in, VipsImage **out, double c) {
+#if (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 2))
+	return vips_bandjoin_const1(in, out, c, NULL);
+#else
+	VipsImage *base = vips_image_new();
+	if (
+		vips_black(&base, in->Xsize, in->Ysize, NULL) ||
+		vips_linear1(base, &base, 1, c, NULL)) {
+			g_object_unref(base);
+			return 1;
+		}
+	g_object_unref(base);
+	return vips_bandjoin2(in, base, out, c, NULL);
+#endif
+}
+
+int
+vips_watermark_image(VipsImage *in, VipsImage *sub, VipsImage **out, WatermarkImageOptions *o) {
+	VipsImage *base = vips_image_new();
+	VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 10);
+
+  // add in and sub for unreffing and later use
+	t[0] = in;
+	t[1] = sub;
+
+  if (has_alpha_channel(in) == 0) {
+		vips_add_band(in, &t[0], 255.0);
+		// in is no longer in the array and won't be unreffed, so add it at the end
+		t[8] = in;
+	}
+
+	if (has_alpha_channel(sub) == 0) {
+		vips_add_band(sub, &t[1], 255.0);
+		// sub is no longer in the array and won't be unreffed, so add it at the end
+		t[9] = sub;
+	}
+
+	// Place watermark image in the right place and size it to the size of the
+	// image that should be watermarked
+	if (
+		vips_embed(t[1], &t[2], o->Left, o->Top, t[0]->Xsize, t[0]->Ysize, NULL)) {
+			g_object_unref(base);
+		return 1;
+	}
+
+	// Create a mask image based on the alpha band from the watermark image
+	// and place it in the right position
+	if (
+		vips_extract_band(t[1], &t[3], t[1]->Bands - 1, "n", 1, NULL) ||
+		vips_linear1(t[3], &t[4], o->Opacity, 0.0, NULL) ||
+		vips_cast(t[4], &t[5], VIPS_FORMAT_UCHAR, NULL) ||
+		vips_copy(t[5], &t[6], "interpretation", t[0]->Type, NULL) ||
+		vips_embed(t[6], &t[7], o->Left, o->Top, t[0]->Xsize, t[0]->Ysize, NULL))	{
+			g_object_unref(base);
+		return 1;
+	}
+
+	// Blend the mask and watermark image and write to output.
+	if (vips_ifthenelse(t[7], t[2], t[0], out, "blend", TRUE, NULL)) {
+		g_object_unref(base);
+		return 1;
+	}
+
+	g_object_unref(base);
+	return 0;
+}
+
+int
+vips_smartcrop_bridge(VipsImage *in, VipsImage **out, int width, int height) {
+#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 5)
+	return vips_smartcrop(in, out, width, height, NULL);
+#else
+	return 0;
+#endif
+}

+ 163 - 0
_vendor/src/github.com/h2non/bimg/vips_test.go

@@ -0,0 +1,163 @@
+package bimg
+
+import (
+	"io/ioutil"
+	"os"
+	"path"
+	"testing"
+)
+
+func TestVipsRead(t *testing.T) {
+	files := []struct {
+		name     string
+		expected ImageType
+	}{
+		{"test.jpg", JPEG},
+		{"test.png", PNG},
+		{"test.webp", WEBP},
+	}
+
+	for _, file := range files {
+		image, imageType, _ := vipsRead(readImage(file.name))
+		if image == nil {
+			t.Fatal("Empty image")
+		}
+		if imageType != file.expected {
+			t.Fatal("Invalid image type")
+		}
+	}
+}
+
+func TestVipsSave(t *testing.T) {
+	types := [...]ImageType{JPEG, PNG, WEBP}
+
+	for _, typ := range types {
+		image, _, _ := vipsRead(readImage("test.jpg"))
+		options := vipsSaveOptions{Quality: 95, Type: typ}
+
+		buf, err := vipsSave(image, options)
+		if err != nil {
+			t.Fatalf("Cannot save the image as '%v'", ImageTypes[typ])
+		}
+		if len(buf) == 0 {
+			t.Fatalf("Empty saved '%v' image", ImageTypes[typ])
+		}
+	}
+}
+
+func TestVipsSaveTiff(t *testing.T) {
+	if !IsTypeSupportedSave(TIFF) {
+		t.Skipf("Format %#v is not supported", ImageTypes[TIFF])
+	}
+	image, _, _ := vipsRead(readImage("test.jpg"))
+	options := vipsSaveOptions{Quality: 95, Type: TIFF}
+	buf, _ := vipsSave(image, options)
+
+	if len(buf) == 0 {
+		t.Fatalf("Empty saved '%v' image", ImageTypes[TIFF])
+	}
+}
+
+func TestVipsRotate(t *testing.T) {
+	files := []struct {
+		name   string
+		rotate Angle
+	}{
+		{"test.jpg", D90},
+		{"test_square.jpg", D45},
+	}
+
+	for _, file := range files {
+		image, _, _ := vipsRead(readImage(file.name))
+
+		newImg, err := vipsRotate(image, file.rotate)
+		if err != nil {
+			t.Fatal("Cannot rotate the image")
+		}
+
+		buf, _ := vipsSave(newImg, vipsSaveOptions{Quality: 95})
+		if len(buf) == 0 {
+			t.Fatal("Empty image")
+		}
+	}
+}
+
+func TestVipsZoom(t *testing.T) {
+	image, _, _ := vipsRead(readImage("test.jpg"))
+
+	newImg, err := vipsZoom(image, 1)
+	if err != nil {
+		t.Fatal("Cannot save the image")
+	}
+
+	buf, _ := vipsSave(newImg, vipsSaveOptions{Quality: 95})
+	if len(buf) == 0 {
+		t.Fatal("Empty image")
+	}
+}
+
+func TestVipsWatermark(t *testing.T) {
+	image, _, _ := vipsRead(readImage("test.jpg"))
+
+	watermark := Watermark{
+		Text:       "Copy me if you can",
+		Font:       "sans bold 12",
+		Opacity:    0.5,
+		Width:      200,
+		DPI:        100,
+		Margin:     100,
+		Background: Color{255, 255, 255},
+	}
+
+	newImg, err := vipsWatermark(image, watermark)
+	if err != nil {
+		t.Errorf("Cannot add watermark: %s", err)
+	}
+
+	buf, _ := vipsSave(newImg, vipsSaveOptions{Quality: 95})
+	if len(buf) == 0 {
+		t.Fatal("Empty image")
+	}
+}
+
+func TestVipsWatermarkWithImage(t *testing.T) {
+	image, _, _ := vipsRead(readImage("test.jpg"))
+
+	watermark := readImage("transparent.png")
+
+	options := WatermarkImage{Left: 100, Top: 100, Opacity: 1.0, Buf: watermark}
+	newImg, err := vipsDrawWatermark(image, options)
+	if err != nil {
+		t.Errorf("Cannot add watermark: %s", err)
+	}
+
+	buf, _ := vipsSave(newImg, vipsSaveOptions{Quality: 95})
+	if len(buf) == 0 {
+		t.Fatal("Empty image")
+	}
+}
+
+func TestVipsImageType(t *testing.T) {
+	imgType := vipsImageType(readImage("test.jpg"))
+	if imgType != JPEG {
+		t.Fatal("Invalid image type")
+	}
+}
+
+func TestVipsMemory(t *testing.T) {
+	mem := VipsMemory()
+
+	if mem.Memory < 1024 {
+		t.Fatal("Invalid memory")
+	}
+	if mem.Allocations == 0 {
+		t.Fatal("Invalid memory allocations")
+	}
+}
+
+func readImage(file string) []byte {
+	img, _ := os.Open(path.Join("fixtures", file))
+	buf, _ := ioutil.ReadAll(img)
+	defer img.Close()
+	return buf
+}

+ 21 - 0
_vendor/src/github.com/tj/go-debug/History.md

@@ -0,0 +1,21 @@
+
+v2.0.0 / 2014-10-22
+==================
+
+ * remove live toggling feature. Closes #10
+
+1.1.1 / 2014-07-07
+==================
+
+ * fix: dispose socket. Closes #1
+
+1.1.0 / 2014-06-29
+==================
+
+ * add unix domain socket live debugging support
+ * add support for enabling/disabling at runtime
+
+0.1.0 / 2014-05-24
+==================
+
+ * add global and debug relative deltas

+ 8 - 0
_vendor/src/github.com/tj/go-debug/Makefile

@@ -0,0 +1,8 @@
+
+test:
+	@go test
+
+bench:
+	@go test -bench=.
+
+.PHONY: bench test

+ 75 - 0
_vendor/src/github.com/tj/go-debug/Readme.md

@@ -0,0 +1,75 @@
+
+# go-debug
+
+ Conditional debug logging for Go libraries.
+
+ View the [docs](http://godoc.org/github.com/tj/go-debug).
+
+## Installation
+
+```
+$ go get github.com/tj/go-debug
+```
+
+## Example
+
+```go
+package main
+
+import . "github.com/tj/go-debug"
+import "time"
+
+var debug = Debug("single")
+
+func main() {
+  for {
+    debug("sending mail")
+    debug("send email to %s", "tobi@segment.io")
+    debug("send email to %s", "loki@segment.io")
+    debug("send email to %s", "jane@segment.io")
+    time.Sleep(500 * time.Millisecond)
+  }
+}
+```
+
+If you run the program with the `DEBUG=*` environment variable you will see:
+
+```
+15:58:15.115 34us   33us   single - sending mail
+15:58:15.116 3us    3us    single - send email to tobi@segment.io
+15:58:15.116 1us    1us    single - send email to loki@segment.io
+15:58:15.116 1us    1us    single - send email to jane@segment.io
+15:58:15.620 504ms  504ms  single - sending mail
+15:58:15.620 6us    6us    single - send email to tobi@segment.io
+15:58:15.620 4us    4us    single - send email to loki@segment.io
+15:58:15.620 4us    4us    single - send email to jane@segment.io
+15:58:16.123 503ms  503ms  single - sending mail
+15:58:16.123 7us    7us    single - send email to tobi@segment.io
+15:58:16.123 4us    4us    single - send email to loki@segment.io
+15:58:16.123 4us    4us    single - send email to jane@segment.io
+15:58:16.625 501ms  501ms  single - sending mail
+15:58:16.625 4us    4us    single - send email to tobi@segment.io
+15:58:16.625 4us    4us    single - send email to loki@segment.io
+15:58:16.625 5us    5us    single - send email to jane@segment.io
+```
+
+A timestamp and two deltas are displayed. The timestamp consists of hour, minute, second and microseconds. The left-most delta is relative to the previous debug call of any name, followed by a delta specific to that debug function. These may be useful to identify timing issues and potential bottlenecks.
+
+## The DEBUG environment variable
+
+ Executables often support `--verbose` flags for conditional logging, however
+ libraries typically either require altering your code to enable logging,
+ or simply omit logging all together. go-debug allows conditional logging
+ to be enabled via the __DEBUG__ environment variable, where one or more
+ patterns may be specified.
+
+ For example suppose your application has several models and you want
+ to output logs for users only, you might use `DEBUG=models:user`. In contrast
+ if you wanted to see what all database activity was you might use `DEBUG=models:*`,
+ or if you're love being swamped with logs: `DEBUG=*`. You may also specify a list of names delimited by a comma, for example `DEBUG=mongo,redis:*`.
+
+ The name given _should_ be the package name, however you can use whatever you like.
+
+# License
+
+MIT

+ 128 - 0
_vendor/src/github.com/tj/go-debug/debug.go

@@ -0,0 +1,128 @@
+package debug
+
+import (
+	"fmt"
+	"io"
+	"math/rand"
+	"os"
+	"regexp"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+
+var (
+	writer  io.Writer = os.Stderr
+	reg     *regexp.Regexp
+	m       sync.Mutex
+	enabled = false
+)
+
+// Debugger function.
+type DebugFunction func(string, ...interface{})
+
+// Terminal colors used at random.
+var colors []string = []string{
+	"31",
+	"32",
+	"33",
+	"34",
+	"35",
+	"36",
+}
+
+// Initialize with DEBUG environment variable.
+func init() {
+	env := os.Getenv("DEBUG")
+
+	if "" != env {
+		Enable(env)
+	}
+}
+
+// SetWriter replaces the default of os.Stderr with `w`.
+func SetWriter(w io.Writer) {
+	m.Lock()
+	defer m.Unlock()
+	writer = w
+}
+
+// Disable all pattern matching. This function is thread-safe.
+func Disable() {
+	m.Lock()
+	defer m.Unlock()
+	enabled = false
+}
+
+// Enable the given debug `pattern`. Patterns take a glob-like form,
+// for example if you wanted to enable everything, just use "*", or
+// if you had a library named mongodb you could use "mongodb:connection",
+// or "mongodb:*". Multiple matches can be made with a comma, for
+// example "mongo*,redis*".
+//
+// This function is thread-safe.
+func Enable(pattern string) {
+	m.Lock()
+	defer m.Unlock()
+	pattern = regexp.QuoteMeta(pattern)
+	pattern = strings.Replace(pattern, "\\*", ".*?", -1)
+	pattern = strings.Replace(pattern, ",", "|", -1)
+	pattern = "^(" + pattern + ")$"
+	reg = regexp.MustCompile(pattern)
+	enabled = true
+}
+
+// Debug creates a debug function for `name` which you call
+// with printf-style arguments in your application or library.
+func Debug(name string) DebugFunction {
+	prevGlobal := time.Now()
+	color := colors[rand.Intn(len(colors))]
+	prev := time.Now()
+
+	return func(format string, args ...interface{}) {
+		if !enabled {
+			return
+		}
+
+		if !reg.MatchString(name) {
+			return
+		}
+
+		d := deltas(prevGlobal, prev, color)
+		fmt.Fprintf(writer, d+" \033["+color+"m"+name+"\033[0m - "+format+"\n", args...)
+		prevGlobal = time.Now()
+		prev = time.Now()
+	}
+}
+
+// Return formatting for deltas.
+func deltas(prevGlobal, prev time.Time, color string) string {
+	now := time.Now()
+	global := now.Sub(prevGlobal).Nanoseconds()
+	delta := now.Sub(prev).Nanoseconds()
+	ts := now.UTC().Format("15:04:05.000")
+	deltas := fmt.Sprintf("%s %-6s \033["+color+"m%-6s", ts, humanizeNano(global), humanizeNano(delta))
+	return deltas
+}
+
+// Humanize nanoseconds to a string.
+func humanizeNano(n int64) string {
+	var suffix string
+
+	switch {
+	case n > 1e9:
+		n /= 1e9
+		suffix = "s"
+	case n > 1e6:
+		n /= 1e6
+		suffix = "ms"
+	case n > 1e3:
+		n /= 1e3
+		suffix = "us"
+	default:
+		suffix = "ns"
+	}
+
+	return strconv.Itoa(int(n)) + suffix
+}

+ 152 - 0
_vendor/src/github.com/tj/go-debug/debug_test.go

@@ -0,0 +1,152 @@
+package debug
+
+import "testing"
+import "strings"
+import "bytes"
+import "time"
+
+func assertContains(t *testing.T, str, substr string) {
+	if !strings.Contains(str, substr) {
+		t.Fatalf("expected %q to contain %q", str, substr)
+	}
+}
+
+func assertNotContains(t *testing.T, str, substr string) {
+	if strings.Contains(str, substr) {
+		t.Fatalf("expected %q to not contain %q", str, substr)
+	}
+}
+
+func TestDefault(t *testing.T) {
+	var b []byte
+	buf := bytes.NewBuffer(b)
+	SetWriter(buf)
+
+	debug := Debug("foo")
+	debug("something")
+	debug("here")
+	debug("whoop")
+
+	if buf.Len() != 0 {
+		t.Fatalf("buffer should be empty")
+	}
+}
+
+func TestEnable(t *testing.T) {
+	var b []byte
+	buf := bytes.NewBuffer(b)
+	SetWriter(buf)
+
+	Enable("foo")
+
+	debug := Debug("foo")
+	debug("something")
+	debug("here")
+	debug("whoop")
+
+	if buf.Len() == 0 {
+		t.Fatalf("buffer should have output")
+	}
+
+	str := string(buf.Bytes())
+	assertContains(t, str, "something")
+	assertContains(t, str, "here")
+	assertContains(t, str, "whoop")
+}
+
+func TestMultipleOneEnabled(t *testing.T) {
+	var b []byte
+	buf := bytes.NewBuffer(b)
+	SetWriter(buf)
+
+	Enable("foo")
+
+	foo := Debug("foo")
+	foo("foo")
+
+	bar := Debug("bar")
+	bar("bar")
+
+	if buf.Len() == 0 {
+		t.Fatalf("buffer should have output")
+	}
+
+	str := string(buf.Bytes())
+	assertContains(t, str, "foo")
+	assertNotContains(t, str, "bar")
+}
+
+func TestMultipleEnabled(t *testing.T) {
+	var b []byte
+	buf := bytes.NewBuffer(b)
+	SetWriter(buf)
+
+	Enable("foo,bar")
+
+	foo := Debug("foo")
+	foo("foo")
+
+	bar := Debug("bar")
+	bar("bar")
+
+	if buf.Len() == 0 {
+		t.Fatalf("buffer should have output")
+	}
+
+	str := string(buf.Bytes())
+	assertContains(t, str, "foo")
+	assertContains(t, str, "bar")
+}
+
+func TestEnableDisable(t *testing.T) {
+	var b []byte
+	buf := bytes.NewBuffer(b)
+	SetWriter(buf)
+
+	Enable("foo,bar")
+	Disable()
+
+	foo := Debug("foo")
+	foo("foo")
+
+	bar := Debug("bar")
+	bar("bar")
+
+	if buf.Len() != 0 {
+		t.Fatalf("buffer should not have output")
+	}
+}
+
+func ExampleEnable() {
+	Enable("mongo:connection")
+	Enable("mongo:*")
+	Enable("foo,bar,baz")
+	Enable("*")
+}
+
+func ExampleDebug() {
+	var debug = Debug("single")
+
+	for {
+		debug("sending mail")
+		debug("send email to %s", "tobi@segment.io")
+		debug("send email to %s", "loki@segment.io")
+		debug("send email to %s", "jane@segment.io")
+		time.Sleep(500 * time.Millisecond)
+	}
+}
+
+func BenchmarkDisabled(b *testing.B) {
+	debug := Debug("something")
+	for i := 0; i < b.N; i++ {
+		debug("stuff")
+	}
+}
+
+func BenchmarkNonMatch(b *testing.B) {
+	debug := Debug("something")
+	Enable("nonmatch")
+	for i := 0; i < b.N; i++ {
+		debug("stuff")
+	}
+}

+ 25 - 0
_vendor/src/github.com/tj/go-debug/example/multiple.go

@@ -0,0 +1,25 @@
+package main
+
+import . "github.com/visionmedia/go-debug"
+import "time"
+
+var a = Debug("multiple:a")
+var b = Debug("multiple:b")
+var c = Debug("multiple:c")
+
+func work(debug DebugFunction, delay time.Duration) {
+	for {
+		debug("doing stuff")
+		time.Sleep(delay)
+	}
+}
+
+func main() {
+	q := make(chan bool)
+
+	go work(a, 1000*time.Millisecond)
+	go work(b, 250*time.Millisecond)
+	go work(c, 100*time.Millisecond)
+
+	<-q
+}

+ 16 - 0
_vendor/src/github.com/tj/go-debug/example/single.go

@@ -0,0 +1,16 @@
+package main
+
+import . "github.com/visionmedia/go-debug"
+import "time"
+
+var debug = Debug("single")
+
+func main() {
+	for {
+		debug("sending mail")
+		debug("send email to %s", "tobi@segment.io")
+		debug("send email to %s", "loki@segment.io")
+		debug("send email to %s", "jane@segment.io")
+		time.Sleep(500 * time.Millisecond)
+	}
+}

+ 9 - 0
_vendor/src/gopkg.in/yaml.v2/.travis.yml

@@ -0,0 +1,9 @@
+language: go
+
+go:
+    - 1.4
+    - 1.5
+    - 1.6
+    - tip
+
+go_import_path: gopkg.in/yaml.v2

+ 13 - 0
_vendor/src/gopkg.in/yaml.v2/LICENSE

@@ -0,0 +1,13 @@
+Copyright 2011-2016 Canonical Ltd.
+
+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.

+ 31 - 0
_vendor/src/gopkg.in/yaml.v2/LICENSE.libyaml

@@ -0,0 +1,31 @@
+The following files were ported to Go from C files of libyaml, and thus
+are still covered by their original copyright and license:
+
+    apic.go
+    emitterc.go
+    parserc.go
+    readerc.go
+    scannerc.go
+    writerc.go
+    yamlh.go
+    yamlprivateh.go
+
+Copyright (c) 2006 Kirill Simonov
+
+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.

+ 131 - 0
_vendor/src/gopkg.in/yaml.v2/README.md

@@ -0,0 +1,131 @@
+# YAML support for the Go language
+
+Introduction
+------------
+
+The yaml package enables Go programs to comfortably encode and decode YAML
+values. It was developed within [Canonical](https://www.canonical.com) as
+part of the [juju](https://juju.ubuntu.com) project, and is based on a
+pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML)
+C library to parse and generate YAML data quickly and reliably.
+
+Compatibility
+-------------
+
+The yaml package supports most of YAML 1.1 and 1.2, including support for
+anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
+implemented, and base-60 floats from YAML 1.1 are purposefully not
+supported since they're a poor design and are gone in YAML 1.2.
+
+Installation and usage
+----------------------
+
+The import path for the package is *gopkg.in/yaml.v2*.
+
+To install it, run:
+
+    go get gopkg.in/yaml.v2
+
+API documentation
+-----------------
+
+If opened in a browser, the import path itself leads to the API documentation:
+
+  * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2)
+
+API stability
+-------------
+
+The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in).
+
+
+License
+-------
+
+The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details.
+
+
+Example
+-------
+
+```Go
+package main
+
+import (
+        "fmt"
+        "log"
+
+        "gopkg.in/yaml.v2"
+)
+
+var data = `
+a: Easy!
+b:
+  c: 2
+  d: [3, 4]
+`
+
+type T struct {
+        A string
+        B struct {
+                RenamedC int   `yaml:"c"`
+                D        []int `yaml:",flow"`
+        }
+}
+
+func main() {
+        t := T{}
+    
+        err := yaml.Unmarshal([]byte(data), &t)
+        if err != nil {
+                log.Fatalf("error: %v", err)
+        }
+        fmt.Printf("--- t:\n%v\n\n", t)
+    
+        d, err := yaml.Marshal(&t)
+        if err != nil {
+                log.Fatalf("error: %v", err)
+        }
+        fmt.Printf("--- t dump:\n%s\n\n", string(d))
+    
+        m := make(map[interface{}]interface{})
+    
+        err = yaml.Unmarshal([]byte(data), &m)
+        if err != nil {
+                log.Fatalf("error: %v", err)
+        }
+        fmt.Printf("--- m:\n%v\n\n", m)
+    
+        d, err = yaml.Marshal(&m)
+        if err != nil {
+                log.Fatalf("error: %v", err)
+        }
+        fmt.Printf("--- m dump:\n%s\n\n", string(d))
+}
+```
+
+This example will generate the following output:
+
+```
+--- t:
+{Easy! {2 [3 4]}}
+
+--- t dump:
+a: Easy!
+b:
+  c: 2
+  d: [3, 4]
+
+
+--- m:
+map[a:Easy! b:map[c:2 d:[3 4]]]
+
+--- m dump:
+a: Easy!
+b:
+  c: 2
+  d:
+  - 3
+  - 4
+```
+

+ 742 - 0
_vendor/src/gopkg.in/yaml.v2/apic.go

@@ -0,0 +1,742 @@
+package yaml
+
+import (
+	"io"
+	"os"
+)
+
+func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) {
+	//fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens))
+
+	// Check if we can move the queue at the beginning of the buffer.
+	if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) {
+		if parser.tokens_head != len(parser.tokens) {
+			copy(parser.tokens, parser.tokens[parser.tokens_head:])
+		}
+		parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head]
+		parser.tokens_head = 0
+	}
+	parser.tokens = append(parser.tokens, *token)
+	if pos < 0 {
+		return
+	}
+	copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:])
+	parser.tokens[parser.tokens_head+pos] = *token
+}
+
+// Create a new parser object.
+func yaml_parser_initialize(parser *yaml_parser_t) bool {
+	*parser = yaml_parser_t{
+		raw_buffer: make([]byte, 0, input_raw_buffer_size),
+		buffer:     make([]byte, 0, input_buffer_size),
+	}
+	return true
+}
+
+// Destroy a parser object.
+func yaml_parser_delete(parser *yaml_parser_t) {
+	*parser = yaml_parser_t{}
+}
+
+// String read handler.
+func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
+	if parser.input_pos == len(parser.input) {
+		return 0, io.EOF
+	}
+	n = copy(buffer, parser.input[parser.input_pos:])
+	parser.input_pos += n
+	return n, nil
+}
+
+// File read handler.
+func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
+	return parser.input_file.Read(buffer)
+}
+
+// Set a string input.
+func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) {
+	if parser.read_handler != nil {
+		panic("must set the input source only once")
+	}
+	parser.read_handler = yaml_string_read_handler
+	parser.input = input
+	parser.input_pos = 0
+}
+
+// Set a file input.
+func yaml_parser_set_input_file(parser *yaml_parser_t, file *os.File) {
+	if parser.read_handler != nil {
+		panic("must set the input source only once")
+	}
+	parser.read_handler = yaml_file_read_handler
+	parser.input_file = file
+}
+
+// Set the source encoding.
+func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
+	if parser.encoding != yaml_ANY_ENCODING {
+		panic("must set the encoding only once")
+	}
+	parser.encoding = encoding
+}
+
+// Create a new emitter object.
+func yaml_emitter_initialize(emitter *yaml_emitter_t) bool {
+	*emitter = yaml_emitter_t{
+		buffer:     make([]byte, output_buffer_size),
+		raw_buffer: make([]byte, 0, output_raw_buffer_size),
+		states:     make([]yaml_emitter_state_t, 0, initial_stack_size),
+		events:     make([]yaml_event_t, 0, initial_queue_size),
+	}
+	return true
+}
+
+// Destroy an emitter object.
+func yaml_emitter_delete(emitter *yaml_emitter_t) {
+	*emitter = yaml_emitter_t{}
+}
+
+// String write handler.
+func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
+	*emitter.output_buffer = append(*emitter.output_buffer, buffer...)
+	return nil
+}
+
+// File write handler.
+func yaml_file_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
+	_, err := emitter.output_file.Write(buffer)
+	return err
+}
+
+// Set a string output.
+func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) {
+	if emitter.write_handler != nil {
+		panic("must set the output target only once")
+	}
+	emitter.write_handler = yaml_string_write_handler
+	emitter.output_buffer = output_buffer
+}
+
+// Set a file output.
+func yaml_emitter_set_output_file(emitter *yaml_emitter_t, file io.Writer) {
+	if emitter.write_handler != nil {
+		panic("must set the output target only once")
+	}
+	emitter.write_handler = yaml_file_write_handler
+	emitter.output_file = file
+}
+
+// Set the output encoding.
+func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) {
+	if emitter.encoding != yaml_ANY_ENCODING {
+		panic("must set the output encoding only once")
+	}
+	emitter.encoding = encoding
+}
+
+// Set the canonical output style.
+func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
+	emitter.canonical = canonical
+}
+
+//// Set the indentation increment.
+func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
+	if indent < 2 || indent > 9 {
+		indent = 2
+	}
+	emitter.best_indent = indent
+}
+
+// Set the preferred line width.
+func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) {
+	if width < 0 {
+		width = -1
+	}
+	emitter.best_width = width
+}
+
+// Set if unescaped non-ASCII characters are allowed.
+func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) {
+	emitter.unicode = unicode
+}
+
+// Set the preferred line break character.
+func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) {
+	emitter.line_break = line_break
+}
+
+///*
+// * Destroy a token object.
+// */
+//
+//YAML_DECLARE(void)
+//yaml_token_delete(yaml_token_t *token)
+//{
+//    assert(token);  // Non-NULL token object expected.
+//
+//    switch (token.type)
+//    {
+//        case YAML_TAG_DIRECTIVE_TOKEN:
+//            yaml_free(token.data.tag_directive.handle);
+//            yaml_free(token.data.tag_directive.prefix);
+//            break;
+//
+//        case YAML_ALIAS_TOKEN:
+//            yaml_free(token.data.alias.value);
+//            break;
+//
+//        case YAML_ANCHOR_TOKEN:
+//            yaml_free(token.data.anchor.value);
+//            break;
+//
+//        case YAML_TAG_TOKEN:
+//            yaml_free(token.data.tag.handle);
+//            yaml_free(token.data.tag.suffix);
+//            break;
+//
+//        case YAML_SCALAR_TOKEN:
+//            yaml_free(token.data.scalar.value);
+//            break;
+//
+//        default:
+//            break;
+//    }
+//
+//    memset(token, 0, sizeof(yaml_token_t));
+//}
+//
+///*
+// * Check if a string is a valid UTF-8 sequence.
+// *
+// * Check 'reader.c' for more details on UTF-8 encoding.
+// */
+//
+//static int
+//yaml_check_utf8(yaml_char_t *start, size_t length)
+//{
+//    yaml_char_t *end = start+length;
+//    yaml_char_t *pointer = start;
+//
+//    while (pointer < end) {
+//        unsigned char octet;
+//        unsigned int width;
+//        unsigned int value;
+//        size_t k;
+//
+//        octet = pointer[0];
+//        width = (octet & 0x80) == 0x00 ? 1 :
+//                (octet & 0xE0) == 0xC0 ? 2 :
+//                (octet & 0xF0) == 0xE0 ? 3 :
+//                (octet & 0xF8) == 0xF0 ? 4 : 0;
+//        value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+//                (octet & 0xE0) == 0xC0 ? octet & 0x1F :
+//                (octet & 0xF0) == 0xE0 ? octet & 0x0F :
+//                (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+//        if (!width) return 0;
+//        if (pointer+width > end) return 0;
+//        for (k = 1; k < width; k ++) {
+//            octet = pointer[k];
+//            if ((octet & 0xC0) != 0x80) return 0;
+//            value = (value << 6) + (octet & 0x3F);
+//        }
+//        if (!((width == 1) ||
+//            (width == 2 && value >= 0x80) ||
+//            (width == 3 && value >= 0x800) ||
+//            (width == 4 && value >= 0x10000))) return 0;
+//
+//        pointer += width;
+//    }
+//
+//    return 1;
+//}
+//
+
+// Create STREAM-START.
+func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) bool {
+	*event = yaml_event_t{
+		typ:      yaml_STREAM_START_EVENT,
+		encoding: encoding,
+	}
+	return true
+}
+
+// Create STREAM-END.
+func yaml_stream_end_event_initialize(event *yaml_event_t) bool {
+	*event = yaml_event_t{
+		typ: yaml_STREAM_END_EVENT,
+	}
+	return true
+}
+
+// Create DOCUMENT-START.
+func yaml_document_start_event_initialize(event *yaml_event_t, version_directive *yaml_version_directive_t,
+	tag_directives []yaml_tag_directive_t, implicit bool) bool {
+	*event = yaml_event_t{
+		typ:               yaml_DOCUMENT_START_EVENT,
+		version_directive: version_directive,
+		tag_directives:    tag_directives,
+		implicit:          implicit,
+	}
+	return true
+}
+
+// Create DOCUMENT-END.
+func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) bool {
+	*event = yaml_event_t{
+		typ:      yaml_DOCUMENT_END_EVENT,
+		implicit: implicit,
+	}
+	return true
+}
+
+///*
+// * Create ALIAS.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t)
+//{
+//    mark yaml_mark_t = { 0, 0, 0 }
+//    anchor_copy *yaml_char_t = NULL
+//
+//    assert(event) // Non-NULL event object is expected.
+//    assert(anchor) // Non-NULL anchor is expected.
+//
+//    if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0
+//
+//    anchor_copy = yaml_strdup(anchor)
+//    if (!anchor_copy)
+//        return 0
+//
+//    ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark)
+//
+//    return 1
+//}
+
+// Create SCALAR.
+func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {
+	*event = yaml_event_t{
+		typ:             yaml_SCALAR_EVENT,
+		anchor:          anchor,
+		tag:             tag,
+		value:           value,
+		implicit:        plain_implicit,
+		quoted_implicit: quoted_implicit,
+		style:           yaml_style_t(style),
+	}
+	return true
+}
+
+// Create SEQUENCE-START.
+func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool {
+	*event = yaml_event_t{
+		typ:      yaml_SEQUENCE_START_EVENT,
+		anchor:   anchor,
+		tag:      tag,
+		implicit: implicit,
+		style:    yaml_style_t(style),
+	}
+	return true
+}
+
+// Create SEQUENCE-END.
+func yaml_sequence_end_event_initialize(event *yaml_event_t) bool {
+	*event = yaml_event_t{
+		typ: yaml_SEQUENCE_END_EVENT,
+	}
+	return true
+}
+
+// Create MAPPING-START.
+func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) bool {
+	*event = yaml_event_t{
+		typ:      yaml_MAPPING_START_EVENT,
+		anchor:   anchor,
+		tag:      tag,
+		implicit: implicit,
+		style:    yaml_style_t(style),
+	}
+	return true
+}
+
+// Create MAPPING-END.
+func yaml_mapping_end_event_initialize(event *yaml_event_t) bool {
+	*event = yaml_event_t{
+		typ: yaml_MAPPING_END_EVENT,
+	}
+	return true
+}
+
+// Destroy an event object.
+func yaml_event_delete(event *yaml_event_t) {
+	*event = yaml_event_t{}
+}
+
+///*
+// * Create a document object.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_initialize(document *yaml_document_t,
+//        version_directive *yaml_version_directive_t,
+//        tag_directives_start *yaml_tag_directive_t,
+//        tag_directives_end *yaml_tag_directive_t,
+//        start_implicit int, end_implicit int)
+//{
+//    struct {
+//        error yaml_error_type_t
+//    } context
+//    struct {
+//        start *yaml_node_t
+//        end *yaml_node_t
+//        top *yaml_node_t
+//    } nodes = { NULL, NULL, NULL }
+//    version_directive_copy *yaml_version_directive_t = NULL
+//    struct {
+//        start *yaml_tag_directive_t
+//        end *yaml_tag_directive_t
+//        top *yaml_tag_directive_t
+//    } tag_directives_copy = { NULL, NULL, NULL }
+//    value yaml_tag_directive_t = { NULL, NULL }
+//    mark yaml_mark_t = { 0, 0, 0 }
+//
+//    assert(document) // Non-NULL document object is expected.
+//    assert((tag_directives_start && tag_directives_end) ||
+//            (tag_directives_start == tag_directives_end))
+//                            // Valid tag directives are expected.
+//
+//    if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error
+//
+//    if (version_directive) {
+//        version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t))
+//        if (!version_directive_copy) goto error
+//        version_directive_copy.major = version_directive.major
+//        version_directive_copy.minor = version_directive.minor
+//    }
+//
+//    if (tag_directives_start != tag_directives_end) {
+//        tag_directive *yaml_tag_directive_t
+//        if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
+//            goto error
+//        for (tag_directive = tag_directives_start
+//                tag_directive != tag_directives_end; tag_directive ++) {
+//            assert(tag_directive.handle)
+//            assert(tag_directive.prefix)
+//            if (!yaml_check_utf8(tag_directive.handle,
+//                        strlen((char *)tag_directive.handle)))
+//                goto error
+//            if (!yaml_check_utf8(tag_directive.prefix,
+//                        strlen((char *)tag_directive.prefix)))
+//                goto error
+//            value.handle = yaml_strdup(tag_directive.handle)
+//            value.prefix = yaml_strdup(tag_directive.prefix)
+//            if (!value.handle || !value.prefix) goto error
+//            if (!PUSH(&context, tag_directives_copy, value))
+//                goto error
+//            value.handle = NULL
+//            value.prefix = NULL
+//        }
+//    }
+//
+//    DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
+//            tag_directives_copy.start, tag_directives_copy.top,
+//            start_implicit, end_implicit, mark, mark)
+//
+//    return 1
+//
+//error:
+//    STACK_DEL(&context, nodes)
+//    yaml_free(version_directive_copy)
+//    while (!STACK_EMPTY(&context, tag_directives_copy)) {
+//        value yaml_tag_directive_t = POP(&context, tag_directives_copy)
+//        yaml_free(value.handle)
+//        yaml_free(value.prefix)
+//    }
+//    STACK_DEL(&context, tag_directives_copy)
+//    yaml_free(value.handle)
+//    yaml_free(value.prefix)
+//
+//    return 0
+//}
+//
+///*
+// * Destroy a document object.
+// */
+//
+//YAML_DECLARE(void)
+//yaml_document_delete(document *yaml_document_t)
+//{
+//    struct {
+//        error yaml_error_type_t
+//    } context
+//    tag_directive *yaml_tag_directive_t
+//
+//    context.error = YAML_NO_ERROR // Eliminate a compliler warning.
+//
+//    assert(document) // Non-NULL document object is expected.
+//
+//    while (!STACK_EMPTY(&context, document.nodes)) {
+//        node yaml_node_t = POP(&context, document.nodes)
+//        yaml_free(node.tag)
+//        switch (node.type) {
+//            case YAML_SCALAR_NODE:
+//                yaml_free(node.data.scalar.value)
+//                break
+//            case YAML_SEQUENCE_NODE:
+//                STACK_DEL(&context, node.data.sequence.items)
+//                break
+//            case YAML_MAPPING_NODE:
+//                STACK_DEL(&context, node.data.mapping.pairs)
+//                break
+//            default:
+//                assert(0) // Should not happen.
+//        }
+//    }
+//    STACK_DEL(&context, document.nodes)
+//
+//    yaml_free(document.version_directive)
+//    for (tag_directive = document.tag_directives.start
+//            tag_directive != document.tag_directives.end
+//            tag_directive++) {
+//        yaml_free(tag_directive.handle)
+//        yaml_free(tag_directive.prefix)
+//    }
+//    yaml_free(document.tag_directives.start)
+//
+//    memset(document, 0, sizeof(yaml_document_t))
+//}
+//
+///**
+// * Get a document node.
+// */
+//
+//YAML_DECLARE(yaml_node_t *)
+//yaml_document_get_node(document *yaml_document_t, index int)
+//{
+//    assert(document) // Non-NULL document object is expected.
+//
+//    if (index > 0 && document.nodes.start + index <= document.nodes.top) {
+//        return document.nodes.start + index - 1
+//    }
+//    return NULL
+//}
+//
+///**
+// * Get the root object.
+// */
+//
+//YAML_DECLARE(yaml_node_t *)
+//yaml_document_get_root_node(document *yaml_document_t)
+//{
+//    assert(document) // Non-NULL document object is expected.
+//
+//    if (document.nodes.top != document.nodes.start) {
+//        return document.nodes.start
+//    }
+//    return NULL
+//}
+//
+///*
+// * Add a scalar node to a document.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_add_scalar(document *yaml_document_t,
+//        tag *yaml_char_t, value *yaml_char_t, length int,
+//        style yaml_scalar_style_t)
+//{
+//    struct {
+//        error yaml_error_type_t
+//    } context
+//    mark yaml_mark_t = { 0, 0, 0 }
+//    tag_copy *yaml_char_t = NULL
+//    value_copy *yaml_char_t = NULL
+//    node yaml_node_t
+//
+//    assert(document) // Non-NULL document object is expected.
+//    assert(value) // Non-NULL value is expected.
+//
+//    if (!tag) {
+//        tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG
+//    }
+//
+//    if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
+//    tag_copy = yaml_strdup(tag)
+//    if (!tag_copy) goto error
+//
+//    if (length < 0) {
+//        length = strlen((char *)value)
+//    }
+//
+//    if (!yaml_check_utf8(value, length)) goto error
+//    value_copy = yaml_malloc(length+1)
+//    if (!value_copy) goto error
+//    memcpy(value_copy, value, length)
+//    value_copy[length] = '\0'
+//
+//    SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark)
+//    if (!PUSH(&context, document.nodes, node)) goto error
+//
+//    return document.nodes.top - document.nodes.start
+//
+//error:
+//    yaml_free(tag_copy)
+//    yaml_free(value_copy)
+//
+//    return 0
+//}
+//
+///*
+// * Add a sequence node to a document.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_add_sequence(document *yaml_document_t,
+//        tag *yaml_char_t, style yaml_sequence_style_t)
+//{
+//    struct {
+//        error yaml_error_type_t
+//    } context
+//    mark yaml_mark_t = { 0, 0, 0 }
+//    tag_copy *yaml_char_t = NULL
+//    struct {
+//        start *yaml_node_item_t
+//        end *yaml_node_item_t
+//        top *yaml_node_item_t
+//    } items = { NULL, NULL, NULL }
+//    node yaml_node_t
+//
+//    assert(document) // Non-NULL document object is expected.
+//
+//    if (!tag) {
+//        tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG
+//    }
+//
+//    if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
+//    tag_copy = yaml_strdup(tag)
+//    if (!tag_copy) goto error
+//
+//    if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error
+//
+//    SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
+//            style, mark, mark)
+//    if (!PUSH(&context, document.nodes, node)) goto error
+//
+//    return document.nodes.top - document.nodes.start
+//
+//error:
+//    STACK_DEL(&context, items)
+//    yaml_free(tag_copy)
+//
+//    return 0
+//}
+//
+///*
+// * Add a mapping node to a document.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_add_mapping(document *yaml_document_t,
+//        tag *yaml_char_t, style yaml_mapping_style_t)
+//{
+//    struct {
+//        error yaml_error_type_t
+//    } context
+//    mark yaml_mark_t = { 0, 0, 0 }
+//    tag_copy *yaml_char_t = NULL
+//    struct {
+//        start *yaml_node_pair_t
+//        end *yaml_node_pair_t
+//        top *yaml_node_pair_t
+//    } pairs = { NULL, NULL, NULL }
+//    node yaml_node_t
+//
+//    assert(document) // Non-NULL document object is expected.
+//
+//    if (!tag) {
+//        tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG
+//    }
+//
+//    if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
+//    tag_copy = yaml_strdup(tag)
+//    if (!tag_copy) goto error
+//
+//    if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error
+//
+//    MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
+//            style, mark, mark)
+//    if (!PUSH(&context, document.nodes, node)) goto error
+//
+//    return document.nodes.top - document.nodes.start
+//
+//error:
+//    STACK_DEL(&context, pairs)
+//    yaml_free(tag_copy)
+//
+//    return 0
+//}
+//
+///*
+// * Append an item to a sequence node.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_append_sequence_item(document *yaml_document_t,
+//        sequence int, item int)
+//{
+//    struct {
+//        error yaml_error_type_t
+//    } context
+//
+//    assert(document) // Non-NULL document is required.
+//    assert(sequence > 0
+//            && document.nodes.start + sequence <= document.nodes.top)
+//                            // Valid sequence id is required.
+//    assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE)
+//                            // A sequence node is required.
+//    assert(item > 0 && document.nodes.start + item <= document.nodes.top)
+//                            // Valid item id is required.
+//
+//    if (!PUSH(&context,
+//                document.nodes.start[sequence-1].data.sequence.items, item))
+//        return 0
+//
+//    return 1
+//}
+//
+///*
+// * Append a pair of a key and a value to a mapping node.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_append_mapping_pair(document *yaml_document_t,
+//        mapping int, key int, value int)
+//{
+//    struct {
+//        error yaml_error_type_t
+//    } context
+//
+//    pair yaml_node_pair_t
+//
+//    assert(document) // Non-NULL document is required.
+//    assert(mapping > 0
+//            && document.nodes.start + mapping <= document.nodes.top)
+//                            // Valid mapping id is required.
+//    assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE)
+//                            // A mapping node is required.
+//    assert(key > 0 && document.nodes.start + key <= document.nodes.top)
+//                            // Valid key id is required.
+//    assert(value > 0 && document.nodes.start + value <= document.nodes.top)
+//                            // Valid value id is required.
+//
+//    pair.key = key
+//    pair.value = value
+//
+//    if (!PUSH(&context,
+//                document.nodes.start[mapping-1].data.mapping.pairs, pair))
+//        return 0
+//
+//    return 1
+//}
+//
+//

+ 682 - 0
_vendor/src/gopkg.in/yaml.v2/decode.go

@@ -0,0 +1,682 @@
+package yaml
+
+import (
+	"encoding"
+	"encoding/base64"
+	"fmt"
+	"math"
+	"reflect"
+	"strconv"
+	"time"
+)
+
+const (
+	documentNode = 1 << iota
+	mappingNode
+	sequenceNode
+	scalarNode
+	aliasNode
+)
+
+type node struct {
+	kind         int
+	line, column int
+	tag          string
+	value        string
+	implicit     bool
+	children     []*node
+	anchors      map[string]*node
+}
+
+// ----------------------------------------------------------------------------
+// Parser, produces a node tree out of a libyaml event stream.
+
+type parser struct {
+	parser yaml_parser_t
+	event  yaml_event_t
+	doc    *node
+}
+
+func newParser(b []byte) *parser {
+	p := parser{}
+	if !yaml_parser_initialize(&p.parser) {
+		panic("failed to initialize YAML emitter")
+	}
+
+	if len(b) == 0 {
+		b = []byte{'\n'}
+	}
+
+	yaml_parser_set_input_string(&p.parser, b)
+
+	p.skip()
+	if p.event.typ != yaml_STREAM_START_EVENT {
+		panic("expected stream start event, got " + strconv.Itoa(int(p.event.typ)))
+	}
+	p.skip()
+	return &p
+}
+
+func (p *parser) destroy() {
+	if p.event.typ != yaml_NO_EVENT {
+		yaml_event_delete(&p.event)
+	}
+	yaml_parser_delete(&p.parser)
+}
+
+func (p *parser) skip() {
+	if p.event.typ != yaml_NO_EVENT {
+		if p.event.typ == yaml_STREAM_END_EVENT {
+			failf("attempted to go past the end of stream; corrupted value?")
+		}
+		yaml_event_delete(&p.event)
+	}
+	if !yaml_parser_parse(&p.parser, &p.event) {
+		p.fail()
+	}
+}
+
+func (p *parser) fail() {
+	var where string
+	var line int
+	if p.parser.problem_mark.line != 0 {
+		line = p.parser.problem_mark.line
+	} else if p.parser.context_mark.line != 0 {
+		line = p.parser.context_mark.line
+	}
+	if line != 0 {
+		where = "line " + strconv.Itoa(line) + ": "
+	}
+	var msg string
+	if len(p.parser.problem) > 0 {
+		msg = p.parser.problem
+	} else {
+		msg = "unknown problem parsing YAML content"
+	}
+	failf("%s%s", where, msg)
+}
+
+func (p *parser) anchor(n *node, anchor []byte) {
+	if anchor != nil {
+		p.doc.anchors[string(anchor)] = n
+	}
+}
+
+func (p *parser) parse() *node {
+	switch p.event.typ {
+	case yaml_SCALAR_EVENT:
+		return p.scalar()
+	case yaml_ALIAS_EVENT:
+		return p.alias()
+	case yaml_MAPPING_START_EVENT:
+		return p.mapping()
+	case yaml_SEQUENCE_START_EVENT:
+		return p.sequence()
+	case yaml_DOCUMENT_START_EVENT:
+		return p.document()
+	case yaml_STREAM_END_EVENT:
+		// Happens when attempting to decode an empty buffer.
+		return nil
+	default:
+		panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ)))
+	}
+}
+
+func (p *parser) node(kind int) *node {
+	return &node{
+		kind:   kind,
+		line:   p.event.start_mark.line,
+		column: p.event.start_mark.column,
+	}
+}
+
+func (p *parser) document() *node {
+	n := p.node(documentNode)
+	n.anchors = make(map[string]*node)
+	p.doc = n
+	p.skip()
+	n.children = append(n.children, p.parse())
+	if p.event.typ != yaml_DOCUMENT_END_EVENT {
+		panic("expected end of document event but got " + strconv.Itoa(int(p.event.typ)))
+	}
+	p.skip()
+	return n
+}
+
+func (p *parser) alias() *node {
+	n := p.node(aliasNode)
+	n.value = string(p.event.anchor)
+	p.skip()
+	return n
+}
+
+func (p *parser) scalar() *node {
+	n := p.node(scalarNode)
+	n.value = string(p.event.value)
+	n.tag = string(p.event.tag)
+	n.implicit = p.event.implicit
+	p.anchor(n, p.event.anchor)
+	p.skip()
+	return n
+}
+
+func (p *parser) sequence() *node {
+	n := p.node(sequenceNode)
+	p.anchor(n, p.event.anchor)
+	p.skip()
+	for p.event.typ != yaml_SEQUENCE_END_EVENT {
+		n.children = append(n.children, p.parse())
+	}
+	p.skip()
+	return n
+}
+
+func (p *parser) mapping() *node {
+	n := p.node(mappingNode)
+	p.anchor(n, p.event.anchor)
+	p.skip()
+	for p.event.typ != yaml_MAPPING_END_EVENT {
+		n.children = append(n.children, p.parse(), p.parse())
+	}
+	p.skip()
+	return n
+}
+
+// ----------------------------------------------------------------------------
+// Decoder, unmarshals a node into a provided value.
+
+type decoder struct {
+	doc     *node
+	aliases map[string]bool
+	mapType reflect.Type
+	terrors []string
+}
+
+var (
+	mapItemType    = reflect.TypeOf(MapItem{})
+	durationType   = reflect.TypeOf(time.Duration(0))
+	defaultMapType = reflect.TypeOf(map[interface{}]interface{}{})
+	ifaceType      = defaultMapType.Elem()
+)
+
+func newDecoder() *decoder {
+	d := &decoder{mapType: defaultMapType}
+	d.aliases = make(map[string]bool)
+	return d
+}
+
+func (d *decoder) terror(n *node, tag string, out reflect.Value) {
+	if n.tag != "" {
+		tag = n.tag
+	}
+	value := n.value
+	if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG {
+		if len(value) > 10 {
+			value = " `" + value[:7] + "...`"
+		} else {
+			value = " `" + value + "`"
+		}
+	}
+	d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type()))
+}
+
+func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) {
+	terrlen := len(d.terrors)
+	err := u.UnmarshalYAML(func(v interface{}) (err error) {
+		defer handleErr(&err)
+		d.unmarshal(n, reflect.ValueOf(v))
+		if len(d.terrors) > terrlen {
+			issues := d.terrors[terrlen:]
+			d.terrors = d.terrors[:terrlen]
+			return &TypeError{issues}
+		}
+		return nil
+	})
+	if e, ok := err.(*TypeError); ok {
+		d.terrors = append(d.terrors, e.Errors...)
+		return false
+	}
+	if err != nil {
+		fail(err)
+	}
+	return true
+}
+
+// d.prepare initializes and dereferences pointers and calls UnmarshalYAML
+// if a value is found to implement it.
+// It returns the initialized and dereferenced out value, whether
+// unmarshalling was already done by UnmarshalYAML, and if so whether
+// its types unmarshalled appropriately.
+//
+// If n holds a null value, prepare returns before doing anything.
+func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
+	if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "" && n.implicit) {
+		return out, false, false
+	}
+	again := true
+	for again {
+		again = false
+		if out.Kind() == reflect.Ptr {
+			if out.IsNil() {
+				out.Set(reflect.New(out.Type().Elem()))
+			}
+			out = out.Elem()
+			again = true
+		}
+		if out.CanAddr() {
+			if u, ok := out.Addr().Interface().(Unmarshaler); ok {
+				good = d.callUnmarshaler(n, u)
+				return out, true, good
+			}
+		}
+	}
+	return out, false, false
+}
+
+func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
+	switch n.kind {
+	case documentNode:
+		return d.document(n, out)
+	case aliasNode:
+		return d.alias(n, out)
+	}
+	out, unmarshaled, good := d.prepare(n, out)
+	if unmarshaled {
+		return good
+	}
+	switch n.kind {
+	case scalarNode:
+		good = d.scalar(n, out)
+	case mappingNode:
+		good = d.mapping(n, out)
+	case sequenceNode:
+		good = d.sequence(n, out)
+	default:
+		panic("internal error: unknown node kind: " + strconv.Itoa(n.kind))
+	}
+	return good
+}
+
+func (d *decoder) document(n *node, out reflect.Value) (good bool) {
+	if len(n.children) == 1 {
+		d.doc = n
+		d.unmarshal(n.children[0], out)
+		return true
+	}
+	return false
+}
+
+func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
+	an, ok := d.doc.anchors[n.value]
+	if !ok {
+		failf("unknown anchor '%s' referenced", n.value)
+	}
+	if d.aliases[n.value] {
+		failf("anchor '%s' value contains itself", n.value)
+	}
+	d.aliases[n.value] = true
+	good = d.unmarshal(an, out)
+	delete(d.aliases, n.value)
+	return good
+}
+
+var zeroValue reflect.Value
+
+func resetMap(out reflect.Value) {
+	for _, k := range out.MapKeys() {
+		out.SetMapIndex(k, zeroValue)
+	}
+}
+
+func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
+	var tag string
+	var resolved interface{}
+	if n.tag == "" && !n.implicit {
+		tag = yaml_STR_TAG
+		resolved = n.value
+	} else {
+		tag, resolved = resolve(n.tag, n.value)
+		if tag == yaml_BINARY_TAG {
+			data, err := base64.StdEncoding.DecodeString(resolved.(string))
+			if err != nil {
+				failf("!!binary value contains invalid base64 data")
+			}
+			resolved = string(data)
+		}
+	}
+	if resolved == nil {
+		if out.Kind() == reflect.Map && !out.CanAddr() {
+			resetMap(out)
+		} else {
+			out.Set(reflect.Zero(out.Type()))
+		}
+		return true
+	}
+	if s, ok := resolved.(string); ok && out.CanAddr() {
+		if u, ok := out.Addr().Interface().(encoding.TextUnmarshaler); ok {
+			err := u.UnmarshalText([]byte(s))
+			if err != nil {
+				fail(err)
+			}
+			return true
+		}
+	}
+	switch out.Kind() {
+	case reflect.String:
+		if tag == yaml_BINARY_TAG {
+			out.SetString(resolved.(string))
+			good = true
+		} else if resolved != nil {
+			out.SetString(n.value)
+			good = true
+		}
+	case reflect.Interface:
+		if resolved == nil {
+			out.Set(reflect.Zero(out.Type()))
+		} else {
+			out.Set(reflect.ValueOf(resolved))
+		}
+		good = true
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		switch resolved := resolved.(type) {
+		case int:
+			if !out.OverflowInt(int64(resolved)) {
+				out.SetInt(int64(resolved))
+				good = true
+			}
+		case int64:
+			if !out.OverflowInt(resolved) {
+				out.SetInt(resolved)
+				good = true
+			}
+		case uint64:
+			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
+				out.SetInt(int64(resolved))
+				good = true
+			}
+		case float64:
+			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
+				out.SetInt(int64(resolved))
+				good = true
+			}
+		case string:
+			if out.Type() == durationType {
+				d, err := time.ParseDuration(resolved)
+				if err == nil {
+					out.SetInt(int64(d))
+					good = true
+				}
+			}
+		}
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		switch resolved := resolved.(type) {
+		case int:
+			if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
+				out.SetUint(uint64(resolved))
+				good = true
+			}
+		case int64:
+			if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
+				out.SetUint(uint64(resolved))
+				good = true
+			}
+		case uint64:
+			if !out.OverflowUint(uint64(resolved)) {
+				out.SetUint(uint64(resolved))
+				good = true
+			}
+		case float64:
+			if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) {
+				out.SetUint(uint64(resolved))
+				good = true
+			}
+		}
+	case reflect.Bool:
+		switch resolved := resolved.(type) {
+		case bool:
+			out.SetBool(resolved)
+			good = true
+		}
+	case reflect.Float32, reflect.Float64:
+		switch resolved := resolved.(type) {
+		case int:
+			out.SetFloat(float64(resolved))
+			good = true
+		case int64:
+			out.SetFloat(float64(resolved))
+			good = true
+		case uint64:
+			out.SetFloat(float64(resolved))
+			good = true
+		case float64:
+			out.SetFloat(resolved)
+			good = true
+		}
+	case reflect.Ptr:
+		if out.Type().Elem() == reflect.TypeOf(resolved) {
+			// TODO DOes this make sense? When is out a Ptr except when decoding a nil value?
+			elem := reflect.New(out.Type().Elem())
+			elem.Elem().Set(reflect.ValueOf(resolved))
+			out.Set(elem)
+			good = true
+		}
+	}
+	if !good {
+		d.terror(n, tag, out)
+	}
+	return good
+}
+
+func settableValueOf(i interface{}) reflect.Value {
+	v := reflect.ValueOf(i)
+	sv := reflect.New(v.Type()).Elem()
+	sv.Set(v)
+	return sv
+}
+
+func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
+	l := len(n.children)
+
+	var iface reflect.Value
+	switch out.Kind() {
+	case reflect.Slice:
+		out.Set(reflect.MakeSlice(out.Type(), l, l))
+	case reflect.Interface:
+		// No type hints. Will have to use a generic sequence.
+		iface = out
+		out = settableValueOf(make([]interface{}, l))
+	default:
+		d.terror(n, yaml_SEQ_TAG, out)
+		return false
+	}
+	et := out.Type().Elem()
+
+	j := 0
+	for i := 0; i < l; i++ {
+		e := reflect.New(et).Elem()
+		if ok := d.unmarshal(n.children[i], e); ok {
+			out.Index(j).Set(e)
+			j++
+		}
+	}
+	out.Set(out.Slice(0, j))
+	if iface.IsValid() {
+		iface.Set(out)
+	}
+	return true
+}
+
+func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
+	switch out.Kind() {
+	case reflect.Struct:
+		return d.mappingStruct(n, out)
+	case reflect.Slice:
+		return d.mappingSlice(n, out)
+	case reflect.Map:
+		// okay
+	case reflect.Interface:
+		if d.mapType.Kind() == reflect.Map {
+			iface := out
+			out = reflect.MakeMap(d.mapType)
+			iface.Set(out)
+		} else {
+			slicev := reflect.New(d.mapType).Elem()
+			if !d.mappingSlice(n, slicev) {
+				return false
+			}
+			out.Set(slicev)
+			return true
+		}
+	default:
+		d.terror(n, yaml_MAP_TAG, out)
+		return false
+	}
+	outt := out.Type()
+	kt := outt.Key()
+	et := outt.Elem()
+
+	mapType := d.mapType
+	if outt.Key() == ifaceType && outt.Elem() == ifaceType {
+		d.mapType = outt
+	}
+
+	if out.IsNil() {
+		out.Set(reflect.MakeMap(outt))
+	}
+	l := len(n.children)
+	for i := 0; i < l; i += 2 {
+		if isMerge(n.children[i]) {
+			d.merge(n.children[i+1], out)
+			continue
+		}
+		k := reflect.New(kt).Elem()
+		if d.unmarshal(n.children[i], k) {
+			kkind := k.Kind()
+			if kkind == reflect.Interface {
+				kkind = k.Elem().Kind()
+			}
+			if kkind == reflect.Map || kkind == reflect.Slice {
+				failf("invalid map key: %#v", k.Interface())
+			}
+			e := reflect.New(et).Elem()
+			if d.unmarshal(n.children[i+1], e) {
+				out.SetMapIndex(k, e)
+			}
+		}
+	}
+	d.mapType = mapType
+	return true
+}
+
+func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) {
+	outt := out.Type()
+	if outt.Elem() != mapItemType {
+		d.terror(n, yaml_MAP_TAG, out)
+		return false
+	}
+
+	mapType := d.mapType
+	d.mapType = outt
+
+	var slice []MapItem
+	var l = len(n.children)
+	for i := 0; i < l; i += 2 {
+		if isMerge(n.children[i]) {
+			d.merge(n.children[i+1], out)
+			continue
+		}
+		item := MapItem{}
+		k := reflect.ValueOf(&item.Key).Elem()
+		if d.unmarshal(n.children[i], k) {
+			v := reflect.ValueOf(&item.Value).Elem()
+			if d.unmarshal(n.children[i+1], v) {
+				slice = append(slice, item)
+			}
+		}
+	}
+	out.Set(reflect.ValueOf(slice))
+	d.mapType = mapType
+	return true
+}
+
+func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
+	sinfo, err := getStructInfo(out.Type())
+	if err != nil {
+		panic(err)
+	}
+	name := settableValueOf("")
+	l := len(n.children)
+
+	var inlineMap reflect.Value
+	var elemType reflect.Type
+	if sinfo.InlineMap != -1 {
+		inlineMap = out.Field(sinfo.InlineMap)
+		inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
+		elemType = inlineMap.Type().Elem()
+	}
+
+	for i := 0; i < l; i += 2 {
+		ni := n.children[i]
+		if isMerge(ni) {
+			d.merge(n.children[i+1], out)
+			continue
+		}
+		if !d.unmarshal(ni, name) {
+			continue
+		}
+		if info, ok := sinfo.FieldsMap[name.String()]; ok {
+			var field reflect.Value
+			if info.Inline == nil {
+				field = out.Field(info.Num)
+			} else {
+				field = out.FieldByIndex(info.Inline)
+			}
+			d.unmarshal(n.children[i+1], field)
+		} else if sinfo.InlineMap != -1 {
+			if inlineMap.IsNil() {
+				inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
+			}
+			value := reflect.New(elemType).Elem()
+			d.unmarshal(n.children[i+1], value)
+			inlineMap.SetMapIndex(name, value)
+		}
+	}
+	return true
+}
+
+func failWantMap() {
+	failf("map merge requires map or sequence of maps as the value")
+}
+
+func (d *decoder) merge(n *node, out reflect.Value) {
+	switch n.kind {
+	case mappingNode:
+		d.unmarshal(n, out)
+	case aliasNode:
+		an, ok := d.doc.anchors[n.value]
+		if ok && an.kind != mappingNode {
+			failWantMap()
+		}
+		d.unmarshal(n, out)
+	case sequenceNode:
+		// Step backwards as earlier nodes take precedence.
+		for i := len(n.children) - 1; i >= 0; i-- {
+			ni := n.children[i]
+			if ni.kind == aliasNode {
+				an, ok := d.doc.anchors[ni.value]
+				if ok && an.kind != mappingNode {
+					failWantMap()
+				}
+			} else if ni.kind != mappingNode {
+				failWantMap()
+			}
+			d.unmarshal(ni, out)
+		}
+	default:
+		failWantMap()
+	}
+}
+
+func isMerge(n *node) bool {
+	return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG)
+}

+ 998 - 0
_vendor/src/gopkg.in/yaml.v2/decode_test.go

@@ -0,0 +1,998 @@
+package yaml_test
+
+import (
+	"errors"
+	. "gopkg.in/check.v1"
+	"gopkg.in/yaml.v2"
+	"math"
+	"net"
+	"reflect"
+	"strings"
+	"time"
+)
+
+var unmarshalIntTest = 123
+
+var unmarshalTests = []struct {
+	data  string
+	value interface{}
+}{
+	{
+		"",
+		&struct{}{},
+	}, {
+		"{}", &struct{}{},
+	}, {
+		"v: hi",
+		map[string]string{"v": "hi"},
+	}, {
+		"v: hi", map[string]interface{}{"v": "hi"},
+	}, {
+		"v: true",
+		map[string]string{"v": "true"},
+	}, {
+		"v: true",
+		map[string]interface{}{"v": true},
+	}, {
+		"v: 10",
+		map[string]interface{}{"v": 10},
+	}, {
+		"v: 0b10",
+		map[string]interface{}{"v": 2},
+	}, {
+		"v: 0xA",
+		map[string]interface{}{"v": 10},
+	}, {
+		"v: 4294967296",
+		map[string]int64{"v": 4294967296},
+	}, {
+		"v: 0.1",
+		map[string]interface{}{"v": 0.1},
+	}, {
+		"v: .1",
+		map[string]interface{}{"v": 0.1},
+	}, {
+		"v: .Inf",
+		map[string]interface{}{"v": math.Inf(+1)},
+	}, {
+		"v: -.Inf",
+		map[string]interface{}{"v": math.Inf(-1)},
+	}, {
+		"v: -10",
+		map[string]interface{}{"v": -10},
+	}, {
+		"v: -.1",
+		map[string]interface{}{"v": -0.1},
+	},
+
+	// Simple values.
+	{
+		"123",
+		&unmarshalIntTest,
+	},
+
+	// Floats from spec
+	{
+		"canonical: 6.8523e+5",
+		map[string]interface{}{"canonical": 6.8523e+5},
+	}, {
+		"expo: 685.230_15e+03",
+		map[string]interface{}{"expo": 685.23015e+03},
+	}, {
+		"fixed: 685_230.15",
+		map[string]interface{}{"fixed": 685230.15},
+	}, {
+		"neginf: -.inf",
+		map[string]interface{}{"neginf": math.Inf(-1)},
+	}, {
+		"fixed: 685_230.15",
+		map[string]float64{"fixed": 685230.15},
+	},
+	//{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported
+	//{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails.
+
+	// Bools from spec
+	{
+		"canonical: y",
+		map[string]interface{}{"canonical": true},
+	}, {
+		"answer: NO",
+		map[string]interface{}{"answer": false},
+	}, {
+		"logical: True",
+		map[string]interface{}{"logical": true},
+	}, {
+		"option: on",
+		map[string]interface{}{"option": true},
+	}, {
+		"option: on",
+		map[string]bool{"option": true},
+	},
+	// Ints from spec
+	{
+		"canonical: 685230",
+		map[string]interface{}{"canonical": 685230},
+	}, {
+		"decimal: +685_230",
+		map[string]interface{}{"decimal": 685230},
+	}, {
+		"octal: 02472256",
+		map[string]interface{}{"octal": 685230},
+	}, {
+		"hexa: 0x_0A_74_AE",
+		map[string]interface{}{"hexa": 685230},
+	}, {
+		"bin: 0b1010_0111_0100_1010_1110",
+		map[string]interface{}{"bin": 685230},
+	}, {
+		"bin: -0b101010",
+		map[string]interface{}{"bin": -42},
+	}, {
+		"decimal: +685_230",
+		map[string]int{"decimal": 685230},
+	},
+
+	//{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported
+
+	// Nulls from spec
+	{
+		"empty:",
+		map[string]interface{}{"empty": nil},
+	}, {
+		"canonical: ~",
+		map[string]interface{}{"canonical": nil},
+	}, {
+		"english: null",
+		map[string]interface{}{"english": nil},
+	}, {
+		"~: null key",
+		map[interface{}]string{nil: "null key"},
+	}, {
+		"empty:",
+		map[string]*bool{"empty": nil},
+	},
+
+	// Flow sequence
+	{
+		"seq: [A,B]",
+		map[string]interface{}{"seq": []interface{}{"A", "B"}},
+	}, {
+		"seq: [A,B,C,]",
+		map[string][]string{"seq": []string{"A", "B", "C"}},
+	}, {
+		"seq: [A,1,C]",
+		map[string][]string{"seq": []string{"A", "1", "C"}},
+	}, {
+		"seq: [A,1,C]",
+		map[string][]int{"seq": []int{1}},
+	}, {
+		"seq: [A,1,C]",
+		map[string]interface{}{"seq": []interface{}{"A", 1, "C"}},
+	},
+	// Block sequence
+	{
+		"seq:\n - A\n - B",
+		map[string]interface{}{"seq": []interface{}{"A", "B"}},
+	}, {
+		"seq:\n - A\n - B\n - C",
+		map[string][]string{"seq": []string{"A", "B", "C"}},
+	}, {
+		"seq:\n - A\n - 1\n - C",
+		map[string][]string{"seq": []string{"A", "1", "C"}},
+	}, {
+		"seq:\n - A\n - 1\n - C",
+		map[string][]int{"seq": []int{1}},
+	}, {
+		"seq:\n - A\n - 1\n - C",
+		map[string]interface{}{"seq": []interface{}{"A", 1, "C"}},
+	},
+
+	// Literal block scalar
+	{
+		"scalar: | # Comment\n\n literal\n\n \ttext\n\n",
+		map[string]string{"scalar": "\nliteral\n\n\ttext\n"},
+	},
+
+	// Folded block scalar
+	{
+		"scalar: > # Comment\n\n folded\n line\n \n next\n line\n  * one\n  * two\n\n last\n line\n\n",
+		map[string]string{"scalar": "\nfolded line\nnext line\n * one\n * two\n\nlast line\n"},
+	},
+
+	// Map inside interface with no type hints.
+	{
+		"a: {b: c}",
+		map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
+	},
+
+	// Structs and type conversions.
+	{
+		"hello: world",
+		&struct{ Hello string }{"world"},
+	}, {
+		"a: {b: c}",
+		&struct{ A struct{ B string } }{struct{ B string }{"c"}},
+	}, {
+		"a: {b: c}",
+		&struct{ A *struct{ B string } }{&struct{ B string }{"c"}},
+	}, {
+		"a: {b: c}",
+		&struct{ A map[string]string }{map[string]string{"b": "c"}},
+	}, {
+		"a: {b: c}",
+		&struct{ A *map[string]string }{&map[string]string{"b": "c"}},
+	}, {
+		"a:",
+		&struct{ A map[string]string }{},
+	}, {
+		"a: 1",
+		&struct{ A int }{1},
+	}, {
+		"a: 1",
+		&struct{ A float64 }{1},
+	}, {
+		"a: 1.0",
+		&struct{ A int }{1},
+	}, {
+		"a: 1.0",
+		&struct{ A uint }{1},
+	}, {
+		"a: [1, 2]",
+		&struct{ A []int }{[]int{1, 2}},
+	}, {
+		"a: 1",
+		&struct{ B int }{0},
+	}, {
+		"a: 1",
+		&struct {
+			B int "a"
+		}{1},
+	}, {
+		"a: y",
+		&struct{ A bool }{true},
+	},
+
+	// Some cross type conversions
+	{
+		"v: 42",
+		map[string]uint{"v": 42},
+	}, {
+		"v: -42",
+		map[string]uint{},
+	}, {
+		"v: 4294967296",
+		map[string]uint64{"v": 4294967296},
+	}, {
+		"v: -4294967296",
+		map[string]uint64{},
+	},
+
+	// int
+	{
+		"int_max: 2147483647",
+		map[string]int{"int_max": math.MaxInt32},
+	},
+	{
+		"int_min: -2147483648",
+		map[string]int{"int_min": math.MinInt32},
+	},
+	{
+		"int_overflow: 9223372036854775808", // math.MaxInt64 + 1
+		map[string]int{},
+	},
+
+	// int64
+	{
+		"int64_max: 9223372036854775807",
+		map[string]int64{"int64_max": math.MaxInt64},
+	},
+	{
+		"int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111",
+		map[string]int64{"int64_max_base2": math.MaxInt64},
+	},
+	{
+		"int64_min: -9223372036854775808",
+		map[string]int64{"int64_min": math.MinInt64},
+	},
+	{
+		"int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111",
+		map[string]int64{"int64_neg_base2": -math.MaxInt64},
+	},
+	{
+		"int64_overflow: 9223372036854775808", // math.MaxInt64 + 1
+		map[string]int64{},
+	},
+
+	// uint
+	{
+		"uint_min: 0",
+		map[string]uint{"uint_min": 0},
+	},
+	{
+		"uint_max: 4294967295",
+		map[string]uint{"uint_max": math.MaxUint32},
+	},
+	{
+		"uint_underflow: -1",
+		map[string]uint{},
+	},
+
+	// uint64
+	{
+		"uint64_min: 0",
+		map[string]uint{"uint64_min": 0},
+	},
+	{
+		"uint64_max: 18446744073709551615",
+		map[string]uint64{"uint64_max": math.MaxUint64},
+	},
+	{
+		"uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111",
+		map[string]uint64{"uint64_max_base2": math.MaxUint64},
+	},
+	{
+		"uint64_maxint64: 9223372036854775807",
+		map[string]uint64{"uint64_maxint64": math.MaxInt64},
+	},
+	{
+		"uint64_underflow: -1",
+		map[string]uint64{},
+	},
+
+	// float32
+	{
+		"float32_max: 3.40282346638528859811704183484516925440e+38",
+		map[string]float32{"float32_max": math.MaxFloat32},
+	},
+	{
+		"float32_nonzero: 1.401298464324817070923729583289916131280e-45",
+		map[string]float32{"float32_nonzero": math.SmallestNonzeroFloat32},
+	},
+	{
+		"float32_maxuint64: 18446744073709551615",
+		map[string]float32{"float32_maxuint64": float32(math.MaxUint64)},
+	},
+	{
+		"float32_maxuint64+1: 18446744073709551616",
+		map[string]float32{"float32_maxuint64+1": float32(math.MaxUint64 + 1)},
+	},
+
+	// float64
+	{
+		"float64_max: 1.797693134862315708145274237317043567981e+308",
+		map[string]float64{"float64_max": math.MaxFloat64},
+	},
+	{
+		"float64_nonzero: 4.940656458412465441765687928682213723651e-324",
+		map[string]float64{"float64_nonzero": math.SmallestNonzeroFloat64},
+	},
+	{
+		"float64_maxuint64: 18446744073709551615",
+		map[string]float64{"float64_maxuint64": float64(math.MaxUint64)},
+	},
+	{
+		"float64_maxuint64+1: 18446744073709551616",
+		map[string]float64{"float64_maxuint64+1": float64(math.MaxUint64 + 1)},
+	},
+
+	// Overflow cases.
+	{
+		"v: 4294967297",
+		map[string]int32{},
+	}, {
+		"v: 128",
+		map[string]int8{},
+	},
+
+	// Quoted values.
+	{
+		"'1': '\"2\"'",
+		map[interface{}]interface{}{"1": "\"2\""},
+	}, {
+		"v:\n- A\n- 'B\n\n  C'\n",
+		map[string][]string{"v": []string{"A", "B\nC"}},
+	},
+
+	// Explicit tags.
+	{
+		"v: !!float '1.1'",
+		map[string]interface{}{"v": 1.1},
+	}, {
+		"v: !!null ''",
+		map[string]interface{}{"v": nil},
+	}, {
+		"%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'",
+		map[string]interface{}{"v": 1},
+	},
+
+	// Anchors and aliases.
+	{
+		"a: &x 1\nb: &y 2\nc: *x\nd: *y\n",
+		&struct{ A, B, C, D int }{1, 2, 1, 2},
+	}, {
+		"a: &a {c: 1}\nb: *a",
+		&struct {
+			A, B struct {
+				C int
+			}
+		}{struct{ C int }{1}, struct{ C int }{1}},
+	}, {
+		"a: &a [1, 2]\nb: *a",
+		&struct{ B []int }{[]int{1, 2}},
+	}, {
+		"b: *a\na: &a {c: 1}",
+		&struct {
+			A, B struct {
+				C int
+			}
+		}{struct{ C int }{1}, struct{ C int }{1}},
+	},
+
+	// Bug #1133337
+	{
+		"foo: ''",
+		map[string]*string{"foo": new(string)},
+	}, {
+		"foo: null",
+		map[string]string{"foo": ""},
+	}, {
+		"foo: null",
+		map[string]interface{}{"foo": nil},
+	},
+
+	// Ignored field
+	{
+		"a: 1\nb: 2\n",
+		&struct {
+			A int
+			B int "-"
+		}{1, 0},
+	},
+
+	// Bug #1191981
+	{
+		"" +
+			"%YAML 1.1\n" +
+			"--- !!str\n" +
+			`"Generic line break (no glyph)\n\` + "\n" +
+			` Generic line break (glyphed)\n\` + "\n" +
+			` Line separator\u2028\` + "\n" +
+			` Paragraph separator\u2029"` + "\n",
+		"" +
+			"Generic line break (no glyph)\n" +
+			"Generic line break (glyphed)\n" +
+			"Line separator\u2028Paragraph separator\u2029",
+	},
+
+	// Struct inlining
+	{
+		"a: 1\nb: 2\nc: 3\n",
+		&struct {
+			A int
+			C inlineB `yaml:",inline"`
+		}{1, inlineB{2, inlineC{3}}},
+	},
+
+	// Map inlining
+	{
+		"a: 1\nb: 2\nc: 3\n",
+		&struct {
+			A int
+			C map[string]int `yaml:",inline"`
+		}{1, map[string]int{"b": 2, "c": 3}},
+	},
+
+	// bug 1243827
+	{
+		"a: -b_c",
+		map[string]interface{}{"a": "-b_c"},
+	},
+	{
+		"a: +b_c",
+		map[string]interface{}{"a": "+b_c"},
+	},
+	{
+		"a: 50cent_of_dollar",
+		map[string]interface{}{"a": "50cent_of_dollar"},
+	},
+
+	// Duration
+	{
+		"a: 3s",
+		map[string]time.Duration{"a": 3 * time.Second},
+	},
+
+	// Issue #24.
+	{
+		"a: <foo>",
+		map[string]string{"a": "<foo>"},
+	},
+
+	// Base 60 floats are obsolete and unsupported.
+	{
+		"a: 1:1\n",
+		map[string]string{"a": "1:1"},
+	},
+
+	// Binary data.
+	{
+		"a: !!binary gIGC\n",
+		map[string]string{"a": "\x80\x81\x82"},
+	}, {
+		"a: !!binary |\n  " + strings.Repeat("kJCQ", 17) + "kJ\n  CQ\n",
+		map[string]string{"a": strings.Repeat("\x90", 54)},
+	}, {
+		"a: !!binary |\n  " + strings.Repeat("A", 70) + "\n  ==\n",
+		map[string]string{"a": strings.Repeat("\x00", 52)},
+	},
+
+	// Ordered maps.
+	{
+		"{b: 2, a: 1, d: 4, c: 3, sub: {e: 5}}",
+		&yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}},
+	},
+
+	// Issue #39.
+	{
+		"a:\n b:\n  c: d\n",
+		map[string]struct{ B interface{} }{"a": {map[interface{}]interface{}{"c": "d"}}},
+	},
+
+	// Custom map type.
+	{
+		"a: {b: c}",
+		M{"a": M{"b": "c"}},
+	},
+
+	// Support encoding.TextUnmarshaler.
+	{
+		"a: 1.2.3.4\n",
+		map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
+	},
+	{
+		"a: 2015-02-24T18:19:39Z\n",
+		map[string]time.Time{"a": time.Unix(1424801979, 0).In(time.UTC)},
+	},
+
+	// Encode empty lists as zero-length slices.
+	{
+		"a: []",
+		&struct{ A []int }{[]int{}},
+	},
+
+	// UTF-16-LE
+	{
+		"\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n\x00",
+		M{"ñoño": "very yes"},
+	},
+	// UTF-16-LE with surrogate.
+	{
+		"\xff\xfe\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \x00=\xd8\xd4\xdf\n\x00",
+		M{"ñoño": "very yes 🟔"},
+	},
+
+	// UTF-16-BE
+	{
+		"\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00\n",
+		M{"ñoño": "very yes"},
+	},
+	// UTF-16-BE with surrogate.
+	{
+		"\xfe\xff\x00\xf1\x00o\x00\xf1\x00o\x00:\x00 \x00v\x00e\x00r\x00y\x00 \x00y\x00e\x00s\x00 \xd8=\xdf\xd4\x00\n",
+		M{"ñoño": "very yes 🟔"},
+	},
+
+	// YAML Float regex shouldn't match this
+	{
+		"a: 123456e1\n",
+		M{"a": "123456e1"},
+	}, {
+		"a: 123456E1\n",
+		M{"a": "123456E1"},
+	},
+}
+
+type M map[interface{}]interface{}
+
+type inlineB struct {
+	B       int
+	inlineC `yaml:",inline"`
+}
+
+type inlineC struct {
+	C int
+}
+
+func (s *S) TestUnmarshal(c *C) {
+	for _, item := range unmarshalTests {
+		t := reflect.ValueOf(item.value).Type()
+		var value interface{}
+		switch t.Kind() {
+		case reflect.Map:
+			value = reflect.MakeMap(t).Interface()
+		case reflect.String:
+			value = reflect.New(t).Interface()
+		case reflect.Ptr:
+			value = reflect.New(t.Elem()).Interface()
+		default:
+			c.Fatalf("missing case for %s", t)
+		}
+		err := yaml.Unmarshal([]byte(item.data), value)
+		if _, ok := err.(*yaml.TypeError); !ok {
+			c.Assert(err, IsNil)
+		}
+		if t.Kind() == reflect.String {
+			c.Assert(*value.(*string), Equals, item.value)
+		} else {
+			c.Assert(value, DeepEquals, item.value)
+		}
+	}
+}
+
+func (s *S) TestUnmarshalNaN(c *C) {
+	value := map[string]interface{}{}
+	err := yaml.Unmarshal([]byte("notanum: .NaN"), &value)
+	c.Assert(err, IsNil)
+	c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true)
+}
+
+var unmarshalErrorTests = []struct {
+	data, error string
+}{
+	{"v: !!float 'error'", "yaml: cannot decode !!str `error` as a !!float"},
+	{"v: [A,", "yaml: line 1: did not find expected node content"},
+	{"v:\n- [A,", "yaml: line 2: did not find expected node content"},
+	{"a: *b\n", "yaml: unknown anchor 'b' referenced"},
+	{"a: &a\n  b: *a\n", "yaml: anchor 'a' value contains itself"},
+	{"value: -", "yaml: block sequence entries are not allowed in this context"},
+	{"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"},
+	{"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`},
+	{"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`},
+}
+
+func (s *S) TestUnmarshalErrors(c *C) {
+	for _, item := range unmarshalErrorTests {
+		var value interface{}
+		err := yaml.Unmarshal([]byte(item.data), &value)
+		c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value))
+	}
+}
+
+var unmarshalerTests = []struct {
+	data, tag string
+	value     interface{}
+}{
+	{"_: {hi: there}", "!!map", map[interface{}]interface{}{"hi": "there"}},
+	{"_: [1,A]", "!!seq", []interface{}{1, "A"}},
+	{"_: 10", "!!int", 10},
+	{"_: null", "!!null", nil},
+	{`_: BAR!`, "!!str", "BAR!"},
+	{`_: "BAR!"`, "!!str", "BAR!"},
+	{"_: !!foo 'BAR!'", "!!foo", "BAR!"},
+	{`_: ""`, "!!str", ""},
+}
+
+var unmarshalerResult = map[int]error{}
+
+type unmarshalerType struct {
+	value interface{}
+}
+
+func (o *unmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error {
+	if err := unmarshal(&o.value); err != nil {
+		return err
+	}
+	if i, ok := o.value.(int); ok {
+		if result, ok := unmarshalerResult[i]; ok {
+			return result
+		}
+	}
+	return nil
+}
+
+type unmarshalerPointer struct {
+	Field *unmarshalerType "_"
+}
+
+type unmarshalerValue struct {
+	Field unmarshalerType "_"
+}
+
+func (s *S) TestUnmarshalerPointerField(c *C) {
+	for _, item := range unmarshalerTests {
+		obj := &unmarshalerPointer{}
+		err := yaml.Unmarshal([]byte(item.data), obj)
+		c.Assert(err, IsNil)
+		if item.value == nil {
+			c.Assert(obj.Field, IsNil)
+		} else {
+			c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
+			c.Assert(obj.Field.value, DeepEquals, item.value)
+		}
+	}
+}
+
+func (s *S) TestUnmarshalerValueField(c *C) {
+	for _, item := range unmarshalerTests {
+		obj := &unmarshalerValue{}
+		err := yaml.Unmarshal([]byte(item.data), obj)
+		c.Assert(err, IsNil)
+		c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
+		c.Assert(obj.Field.value, DeepEquals, item.value)
+	}
+}
+
+func (s *S) TestUnmarshalerWholeDocument(c *C) {
+	obj := &unmarshalerType{}
+	err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj)
+	c.Assert(err, IsNil)
+	value, ok := obj.value.(map[interface{}]interface{})
+	c.Assert(ok, Equals, true, Commentf("value: %#v", obj.value))
+	c.Assert(value["_"], DeepEquals, unmarshalerTests[0].value)
+}
+
+func (s *S) TestUnmarshalerTypeError(c *C) {
+	unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}}
+	unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}}
+	defer func() {
+		delete(unmarshalerResult, 2)
+		delete(unmarshalerResult, 4)
+	}()
+
+	type T struct {
+		Before int
+		After  int
+		M      map[string]*unmarshalerType
+	}
+	var v T
+	data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}`
+	err := yaml.Unmarshal([]byte(data), &v)
+	c.Assert(err, ErrorMatches, ""+
+		"yaml: unmarshal errors:\n"+
+		"  line 1: cannot unmarshal !!str `A` into int\n"+
+		"  foo\n"+
+		"  bar\n"+
+		"  line 1: cannot unmarshal !!str `B` into int")
+	c.Assert(v.M["abc"], NotNil)
+	c.Assert(v.M["def"], IsNil)
+	c.Assert(v.M["ghi"], NotNil)
+	c.Assert(v.M["jkl"], IsNil)
+
+	c.Assert(v.M["abc"].value, Equals, 1)
+	c.Assert(v.M["ghi"].value, Equals, 3)
+}
+
+type proxyTypeError struct{}
+
+func (v *proxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	var s string
+	var a int32
+	var b int64
+	if err := unmarshal(&s); err != nil {
+		panic(err)
+	}
+	if s == "a" {
+		if err := unmarshal(&b); err == nil {
+			panic("should have failed")
+		}
+		return unmarshal(&a)
+	}
+	if err := unmarshal(&a); err == nil {
+		panic("should have failed")
+	}
+	return unmarshal(&b)
+}
+
+func (s *S) TestUnmarshalerTypeErrorProxying(c *C) {
+	type T struct {
+		Before int
+		After  int
+		M      map[string]*proxyTypeError
+	}
+	var v T
+	data := `{before: A, m: {abc: a, def: b}, after: B}`
+	err := yaml.Unmarshal([]byte(data), &v)
+	c.Assert(err, ErrorMatches, ""+
+		"yaml: unmarshal errors:\n"+
+		"  line 1: cannot unmarshal !!str `A` into int\n"+
+		"  line 1: cannot unmarshal !!str `a` into int32\n"+
+		"  line 1: cannot unmarshal !!str `b` into int64\n"+
+		"  line 1: cannot unmarshal !!str `B` into int")
+}
+
+type failingUnmarshaler struct{}
+
+var failingErr = errors.New("failingErr")
+
+func (ft *failingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	return failingErr
+}
+
+func (s *S) TestUnmarshalerError(c *C) {
+	err := yaml.Unmarshal([]byte("a: b"), &failingUnmarshaler{})
+	c.Assert(err, Equals, failingErr)
+}
+
+type sliceUnmarshaler []int
+
+func (su *sliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	var slice []int
+	err := unmarshal(&slice)
+	if err == nil {
+		*su = slice
+		return nil
+	}
+
+	var intVal int
+	err = unmarshal(&intVal)
+	if err == nil {
+		*su = []int{intVal}
+		return nil
+	}
+
+	return err
+}
+
+func (s *S) TestUnmarshalerRetry(c *C) {
+	var su sliceUnmarshaler
+	err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su)
+	c.Assert(err, IsNil)
+	c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1, 2, 3}))
+
+	err = yaml.Unmarshal([]byte("1"), &su)
+	c.Assert(err, IsNil)
+	c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1}))
+}
+
+// From http://yaml.org/type/merge.html
+var mergeTests = `
+anchors:
+  list:
+    - &CENTER { "x": 1, "y": 2 }
+    - &LEFT   { "x": 0, "y": 2 }
+    - &BIG    { "r": 10 }
+    - &SMALL  { "r": 1 }
+
+# All the following maps are equal:
+
+plain:
+  # Explicit keys
+  "x": 1
+  "y": 2
+  "r": 10
+  label: center/big
+
+mergeOne:
+  # Merge one map
+  << : *CENTER
+  "r": 10
+  label: center/big
+
+mergeMultiple:
+  # Merge multiple maps
+  << : [ *CENTER, *BIG ]
+  label: center/big
+
+override:
+  # Override
+  << : [ *BIG, *LEFT, *SMALL ]
+  "x": 1
+  label: center/big
+
+shortTag:
+  # Explicit short merge tag
+  !!merge "<<" : [ *CENTER, *BIG ]
+  label: center/big
+
+longTag:
+  # Explicit merge long tag
+  !<tag:yaml.org,2002:merge> "<<" : [ *CENTER, *BIG ]
+  label: center/big
+
+inlineMap:
+  # Inlined map 
+  << : {"x": 1, "y": 2, "r": 10}
+  label: center/big
+
+inlineSequenceMap:
+  # Inlined map in sequence
+  << : [ *CENTER, {"r": 10} ]
+  label: center/big
+`
+
+func (s *S) TestMerge(c *C) {
+	var want = map[interface{}]interface{}{
+		"x":     1,
+		"y":     2,
+		"r":     10,
+		"label": "center/big",
+	}
+
+	var m map[interface{}]interface{}
+	err := yaml.Unmarshal([]byte(mergeTests), &m)
+	c.Assert(err, IsNil)
+	for name, test := range m {
+		if name == "anchors" {
+			continue
+		}
+		c.Assert(test, DeepEquals, want, Commentf("test %q failed", name))
+	}
+}
+
+func (s *S) TestMergeStruct(c *C) {
+	type Data struct {
+		X, Y, R int
+		Label   string
+	}
+	want := Data{1, 2, 10, "center/big"}
+
+	var m map[string]Data
+	err := yaml.Unmarshal([]byte(mergeTests), &m)
+	c.Assert(err, IsNil)
+	for name, test := range m {
+		if name == "anchors" {
+			continue
+		}
+		c.Assert(test, Equals, want, Commentf("test %q failed", name))
+	}
+}
+
+var unmarshalNullTests = []func() interface{}{
+	func() interface{} { var v interface{}; v = "v"; return &v },
+	func() interface{} { var s = "s"; return &s },
+	func() interface{} { var s = "s"; sptr := &s; return &sptr },
+	func() interface{} { var i = 1; return &i },
+	func() interface{} { var i = 1; iptr := &i; return &iptr },
+	func() interface{} { m := map[string]int{"s": 1}; return &m },
+	func() interface{} { m := map[string]int{"s": 1}; return m },
+}
+
+func (s *S) TestUnmarshalNull(c *C) {
+	for _, test := range unmarshalNullTests {
+		item := test()
+		zero := reflect.Zero(reflect.TypeOf(item).Elem()).Interface()
+		err := yaml.Unmarshal([]byte("null"), item)
+		c.Assert(err, IsNil)
+		if reflect.TypeOf(item).Kind() == reflect.Map {
+			c.Assert(reflect.ValueOf(item).Interface(), DeepEquals, reflect.MakeMap(reflect.TypeOf(item)).Interface())
+		} else {
+			c.Assert(reflect.ValueOf(item).Elem().Interface(), DeepEquals, zero)
+		}
+	}
+}
+
+func (s *S) TestUnmarshalSliceOnPreset(c *C) {
+	// Issue #48.
+	v := struct{ A []int }{[]int{1}}
+	yaml.Unmarshal([]byte("a: [2]"), &v)
+	c.Assert(v.A, DeepEquals, []int{2})
+}
+
+//var data []byte
+//func init() {
+//	var err error
+//	data, err = ioutil.ReadFile("/tmp/file.yaml")
+//	if err != nil {
+//		panic(err)
+//	}
+//}
+//
+//func (s *S) BenchmarkUnmarshal(c *C) {
+//	var err error
+//	for i := 0; i < c.N; i++ {
+//		var v map[string]interface{}
+//		err = yaml.Unmarshal(data, &v)
+//	}
+//	if err != nil {
+//		panic(err)
+//	}
+//}
+//
+//func (s *S) BenchmarkMarshal(c *C) {
+//	var v map[string]interface{}
+//	yaml.Unmarshal(data, &v)
+//	c.ResetTimer()
+//	for i := 0; i < c.N; i++ {
+//		yaml.Marshal(&v)
+//	}
+//}

+ 1684 - 0
_vendor/src/gopkg.in/yaml.v2/emitterc.go

@@ -0,0 +1,1684 @@
+package yaml
+
+import (
+	"bytes"
+)
+
+// Flush the buffer if needed.
+func flush(emitter *yaml_emitter_t) bool {
+	if emitter.buffer_pos+5 >= len(emitter.buffer) {
+		return yaml_emitter_flush(emitter)
+	}
+	return true
+}
+
+// Put a character to the output buffer.
+func put(emitter *yaml_emitter_t, value byte) bool {
+	if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) {
+		return false
+	}
+	emitter.buffer[emitter.buffer_pos] = value
+	emitter.buffer_pos++
+	emitter.column++
+	return true
+}
+
+// Put a line break to the output buffer.
+func put_break(emitter *yaml_emitter_t) bool {
+	if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) {
+		return false
+	}
+	switch emitter.line_break {
+	case yaml_CR_BREAK:
+		emitter.buffer[emitter.buffer_pos] = '\r'
+		emitter.buffer_pos += 1
+	case yaml_LN_BREAK:
+		emitter.buffer[emitter.buffer_pos] = '\n'
+		emitter.buffer_pos += 1
+	case yaml_CRLN_BREAK:
+		emitter.buffer[emitter.buffer_pos+0] = '\r'
+		emitter.buffer[emitter.buffer_pos+1] = '\n'
+		emitter.buffer_pos += 2
+	default:
+		panic("unknown line break setting")
+	}
+	emitter.column = 0
+	emitter.line++
+	return true
+}
+
+// Copy a character from a string into buffer.
+func write(emitter *yaml_emitter_t, s []byte, i *int) bool {
+	if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) {
+		return false
+	}
+	p := emitter.buffer_pos
+	w := width(s[*i])
+	switch w {
+	case 4:
+		emitter.buffer[p+3] = s[*i+3]
+		fallthrough
+	case 3:
+		emitter.buffer[p+2] = s[*i+2]
+		fallthrough
+	case 2:
+		emitter.buffer[p+1] = s[*i+1]
+		fallthrough
+	case 1:
+		emitter.buffer[p+0] = s[*i+0]
+	default:
+		panic("unknown character width")
+	}
+	emitter.column++
+	emitter.buffer_pos += w
+	*i += w
+	return true
+}
+
+// Write a whole string into buffer.
+func write_all(emitter *yaml_emitter_t, s []byte) bool {
+	for i := 0; i < len(s); {
+		if !write(emitter, s, &i) {
+			return false
+		}
+	}
+	return true
+}
+
+// Copy a line break character from a string into buffer.
+func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool {
+	if s[*i] == '\n' {
+		if !put_break(emitter) {
+			return false
+		}
+		*i++
+	} else {
+		if !write(emitter, s, i) {
+			return false
+		}
+		emitter.column = 0
+		emitter.line++
+	}
+	return true
+}
+
+// Set an emitter error and return false.
+func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool {
+	emitter.error = yaml_EMITTER_ERROR
+	emitter.problem = problem
+	return false
+}
+
+// Emit an event.
+func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	emitter.events = append(emitter.events, *event)
+	for !yaml_emitter_need_more_events(emitter) {
+		event := &emitter.events[emitter.events_head]
+		if !yaml_emitter_analyze_event(emitter, event) {
+			return false
+		}
+		if !yaml_emitter_state_machine(emitter, event) {
+			return false
+		}
+		yaml_event_delete(event)
+		emitter.events_head++
+	}
+	return true
+}
+
+// Check if we need to accumulate more events before emitting.
+//
+// We accumulate extra
+//  - 1 event for DOCUMENT-START
+//  - 2 events for SEQUENCE-START
+//  - 3 events for MAPPING-START
+//
+func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool {
+	if emitter.events_head == len(emitter.events) {
+		return true
+	}
+	var accumulate int
+	switch emitter.events[emitter.events_head].typ {
+	case yaml_DOCUMENT_START_EVENT:
+		accumulate = 1
+		break
+	case yaml_SEQUENCE_START_EVENT:
+		accumulate = 2
+		break
+	case yaml_MAPPING_START_EVENT:
+		accumulate = 3
+		break
+	default:
+		return false
+	}
+	if len(emitter.events)-emitter.events_head > accumulate {
+		return false
+	}
+	var level int
+	for i := emitter.events_head; i < len(emitter.events); i++ {
+		switch emitter.events[i].typ {
+		case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT:
+			level++
+		case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT:
+			level--
+		}
+		if level == 0 {
+			return false
+		}
+	}
+	return true
+}
+
+// Append a directive to the directives stack.
+func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool {
+	for i := 0; i < len(emitter.tag_directives); i++ {
+		if bytes.Equal(value.handle, emitter.tag_directives[i].handle) {
+			if allow_duplicates {
+				return true
+			}
+			return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive")
+		}
+	}
+
+	// [Go] Do we actually need to copy this given garbage collection
+	// and the lack of deallocating destructors?
+	tag_copy := yaml_tag_directive_t{
+		handle: make([]byte, len(value.handle)),
+		prefix: make([]byte, len(value.prefix)),
+	}
+	copy(tag_copy.handle, value.handle)
+	copy(tag_copy.prefix, value.prefix)
+	emitter.tag_directives = append(emitter.tag_directives, tag_copy)
+	return true
+}
+
+// Increase the indentation level.
+func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool {
+	emitter.indents = append(emitter.indents, emitter.indent)
+	if emitter.indent < 0 {
+		if flow {
+			emitter.indent = emitter.best_indent
+		} else {
+			emitter.indent = 0
+		}
+	} else if !indentless {
+		emitter.indent += emitter.best_indent
+	}
+	return true
+}
+
+// State dispatcher.
+func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	switch emitter.state {
+	default:
+	case yaml_EMIT_STREAM_START_STATE:
+		return yaml_emitter_emit_stream_start(emitter, event)
+
+	case yaml_EMIT_FIRST_DOCUMENT_START_STATE:
+		return yaml_emitter_emit_document_start(emitter, event, true)
+
+	case yaml_EMIT_DOCUMENT_START_STATE:
+		return yaml_emitter_emit_document_start(emitter, event, false)
+
+	case yaml_EMIT_DOCUMENT_CONTENT_STATE:
+		return yaml_emitter_emit_document_content(emitter, event)
+
+	case yaml_EMIT_DOCUMENT_END_STATE:
+		return yaml_emitter_emit_document_end(emitter, event)
+
+	case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
+		return yaml_emitter_emit_flow_sequence_item(emitter, event, true)
+
+	case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE:
+		return yaml_emitter_emit_flow_sequence_item(emitter, event, false)
+
+	case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
+		return yaml_emitter_emit_flow_mapping_key(emitter, event, true)
+
+	case yaml_EMIT_FLOW_MAPPING_KEY_STATE:
+		return yaml_emitter_emit_flow_mapping_key(emitter, event, false)
+
+	case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
+		return yaml_emitter_emit_flow_mapping_value(emitter, event, true)
+
+	case yaml_EMIT_FLOW_MAPPING_VALUE_STATE:
+		return yaml_emitter_emit_flow_mapping_value(emitter, event, false)
+
+	case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE:
+		return yaml_emitter_emit_block_sequence_item(emitter, event, true)
+
+	case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE:
+		return yaml_emitter_emit_block_sequence_item(emitter, event, false)
+
+	case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE:
+		return yaml_emitter_emit_block_mapping_key(emitter, event, true)
+
+	case yaml_EMIT_BLOCK_MAPPING_KEY_STATE:
+		return yaml_emitter_emit_block_mapping_key(emitter, event, false)
+
+	case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE:
+		return yaml_emitter_emit_block_mapping_value(emitter, event, true)
+
+	case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE:
+		return yaml_emitter_emit_block_mapping_value(emitter, event, false)
+
+	case yaml_EMIT_END_STATE:
+		return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END")
+	}
+	panic("invalid emitter state")
+}
+
+// Expect STREAM-START.
+func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	if event.typ != yaml_STREAM_START_EVENT {
+		return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START")
+	}
+	if emitter.encoding == yaml_ANY_ENCODING {
+		emitter.encoding = event.encoding
+		if emitter.encoding == yaml_ANY_ENCODING {
+			emitter.encoding = yaml_UTF8_ENCODING
+		}
+	}
+	if emitter.best_indent < 2 || emitter.best_indent > 9 {
+		emitter.best_indent = 2
+	}
+	if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 {
+		emitter.best_width = 80
+	}
+	if emitter.best_width < 0 {
+		emitter.best_width = 1<<31 - 1
+	}
+	if emitter.line_break == yaml_ANY_BREAK {
+		emitter.line_break = yaml_LN_BREAK
+	}
+
+	emitter.indent = -1
+	emitter.line = 0
+	emitter.column = 0
+	emitter.whitespace = true
+	emitter.indention = true
+
+	if emitter.encoding != yaml_UTF8_ENCODING {
+		if !yaml_emitter_write_bom(emitter) {
+			return false
+		}
+	}
+	emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE
+	return true
+}
+
+// Expect DOCUMENT-START or STREAM-END.
+func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
+
+	if event.typ == yaml_DOCUMENT_START_EVENT {
+
+		if event.version_directive != nil {
+			if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) {
+				return false
+			}
+		}
+
+		for i := 0; i < len(event.tag_directives); i++ {
+			tag_directive := &event.tag_directives[i]
+			if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) {
+				return false
+			}
+			if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) {
+				return false
+			}
+		}
+
+		for i := 0; i < len(default_tag_directives); i++ {
+			tag_directive := &default_tag_directives[i]
+			if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) {
+				return false
+			}
+		}
+
+		implicit := event.implicit
+		if !first || emitter.canonical {
+			implicit = false
+		}
+
+		if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) {
+			if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
+				return false
+			}
+			if !yaml_emitter_write_indent(emitter) {
+				return false
+			}
+		}
+
+		if event.version_directive != nil {
+			implicit = false
+			if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) {
+				return false
+			}
+			if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) {
+				return false
+			}
+			if !yaml_emitter_write_indent(emitter) {
+				return false
+			}
+		}
+
+		if len(event.tag_directives) > 0 {
+			implicit = false
+			for i := 0; i < len(event.tag_directives); i++ {
+				tag_directive := &event.tag_directives[i]
+				if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) {
+					return false
+				}
+				if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) {
+					return false
+				}
+				if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) {
+					return false
+				}
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+			}
+		}
+
+		if yaml_emitter_check_empty_document(emitter) {
+			implicit = false
+		}
+		if !implicit {
+			if !yaml_emitter_write_indent(emitter) {
+				return false
+			}
+			if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) {
+				return false
+			}
+			if emitter.canonical {
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+			}
+		}
+
+		emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE
+		return true
+	}
+
+	if event.typ == yaml_STREAM_END_EVENT {
+		if emitter.open_ended {
+			if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
+				return false
+			}
+			if !yaml_emitter_write_indent(emitter) {
+				return false
+			}
+		}
+		if !yaml_emitter_flush(emitter) {
+			return false
+		}
+		emitter.state = yaml_EMIT_END_STATE
+		return true
+	}
+
+	return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END")
+}
+
+// Expect the root node.
+func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE)
+	return yaml_emitter_emit_node(emitter, event, true, false, false, false)
+}
+
+// Expect DOCUMENT-END.
+func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	if event.typ != yaml_DOCUMENT_END_EVENT {
+		return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END")
+	}
+	if !yaml_emitter_write_indent(emitter) {
+		return false
+	}
+	if !event.implicit {
+		// [Go] Allocate the slice elsewhere.
+		if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
+			return false
+		}
+		if !yaml_emitter_write_indent(emitter) {
+			return false
+		}
+	}
+	if !yaml_emitter_flush(emitter) {
+		return false
+	}
+	emitter.state = yaml_EMIT_DOCUMENT_START_STATE
+	emitter.tag_directives = emitter.tag_directives[:0]
+	return true
+}
+
+// Expect a flow item node.
+func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
+	if first {
+		if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) {
+			return false
+		}
+		if !yaml_emitter_increase_indent(emitter, true, false) {
+			return false
+		}
+		emitter.flow_level++
+	}
+
+	if event.typ == yaml_SEQUENCE_END_EVENT {
+		emitter.flow_level--
+		emitter.indent = emitter.indents[len(emitter.indents)-1]
+		emitter.indents = emitter.indents[:len(emitter.indents)-1]
+		if emitter.canonical && !first {
+			if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+				return false
+			}
+			if !yaml_emitter_write_indent(emitter) {
+				return false
+			}
+		}
+		if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) {
+			return false
+		}
+		emitter.state = emitter.states[len(emitter.states)-1]
+		emitter.states = emitter.states[:len(emitter.states)-1]
+
+		return true
+	}
+
+	if !first {
+		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+			return false
+		}
+	}
+
+	if emitter.canonical || emitter.column > emitter.best_width {
+		if !yaml_emitter_write_indent(emitter) {
+			return false
+		}
+	}
+	emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
+	return yaml_emitter_emit_node(emitter, event, false, true, false, false)
+}
+
+// Expect a flow key node.
+func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
+	if first {
+		if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) {
+			return false
+		}
+		if !yaml_emitter_increase_indent(emitter, true, false) {
+			return false
+		}
+		emitter.flow_level++
+	}
+
+	if event.typ == yaml_MAPPING_END_EVENT {
+		emitter.flow_level--
+		emitter.indent = emitter.indents[len(emitter.indents)-1]
+		emitter.indents = emitter.indents[:len(emitter.indents)-1]
+		if emitter.canonical && !first {
+			if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+				return false
+			}
+			if !yaml_emitter_write_indent(emitter) {
+				return false
+			}
+		}
+		if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) {
+			return false
+		}
+		emitter.state = emitter.states[len(emitter.states)-1]
+		emitter.states = emitter.states[:len(emitter.states)-1]
+		return true
+	}
+
+	if !first {
+		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+			return false
+		}
+	}
+	if emitter.canonical || emitter.column > emitter.best_width {
+		if !yaml_emitter_write_indent(emitter) {
+			return false
+		}
+	}
+
+	if !emitter.canonical && yaml_emitter_check_simple_key(emitter) {
+		emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE)
+		return yaml_emitter_emit_node(emitter, event, false, false, true, true)
+	}
+	if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) {
+		return false
+	}
+	emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE)
+	return yaml_emitter_emit_node(emitter, event, false, false, true, false)
+}
+
+// Expect a flow value node.
+func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool {
+	if simple {
+		if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) {
+			return false
+		}
+	} else {
+		if emitter.canonical || emitter.column > emitter.best_width {
+			if !yaml_emitter_write_indent(emitter) {
+				return false
+			}
+		}
+		if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) {
+			return false
+		}
+	}
+	emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
+	return yaml_emitter_emit_node(emitter, event, false, false, true, false)
+}
+
+// Expect a block item node.
+func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
+	if first {
+		if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) {
+			return false
+		}
+	}
+	if event.typ == yaml_SEQUENCE_END_EVENT {
+		emitter.indent = emitter.indents[len(emitter.indents)-1]
+		emitter.indents = emitter.indents[:len(emitter.indents)-1]
+		emitter.state = emitter.states[len(emitter.states)-1]
+		emitter.states = emitter.states[:len(emitter.states)-1]
+		return true
+	}
+	if !yaml_emitter_write_indent(emitter) {
+		return false
+	}
+	if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) {
+		return false
+	}
+	emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE)
+	return yaml_emitter_emit_node(emitter, event, false, true, false, false)
+}
+
+// Expect a block key node.
+func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
+	if first {
+		if !yaml_emitter_increase_indent(emitter, false, false) {
+			return false
+		}
+	}
+	if event.typ == yaml_MAPPING_END_EVENT {
+		emitter.indent = emitter.indents[len(emitter.indents)-1]
+		emitter.indents = emitter.indents[:len(emitter.indents)-1]
+		emitter.state = emitter.states[len(emitter.states)-1]
+		emitter.states = emitter.states[:len(emitter.states)-1]
+		return true
+	}
+	if !yaml_emitter_write_indent(emitter) {
+		return false
+	}
+	if yaml_emitter_check_simple_key(emitter) {
+		emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE)
+		return yaml_emitter_emit_node(emitter, event, false, false, true, true)
+	}
+	if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) {
+		return false
+	}
+	emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE)
+	return yaml_emitter_emit_node(emitter, event, false, false, true, false)
+}
+
+// Expect a block value node.
+func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool {
+	if simple {
+		if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) {
+			return false
+		}
+	} else {
+		if !yaml_emitter_write_indent(emitter) {
+			return false
+		}
+		if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) {
+			return false
+		}
+	}
+	emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE)
+	return yaml_emitter_emit_node(emitter, event, false, false, true, false)
+}
+
+// Expect a node.
+func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t,
+	root bool, sequence bool, mapping bool, simple_key bool) bool {
+
+	emitter.root_context = root
+	emitter.sequence_context = sequence
+	emitter.mapping_context = mapping
+	emitter.simple_key_context = simple_key
+
+	switch event.typ {
+	case yaml_ALIAS_EVENT:
+		return yaml_emitter_emit_alias(emitter, event)
+	case yaml_SCALAR_EVENT:
+		return yaml_emitter_emit_scalar(emitter, event)
+	case yaml_SEQUENCE_START_EVENT:
+		return yaml_emitter_emit_sequence_start(emitter, event)
+	case yaml_MAPPING_START_EVENT:
+		return yaml_emitter_emit_mapping_start(emitter, event)
+	default:
+		return yaml_emitter_set_emitter_error(emitter,
+			"expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS")
+	}
+}
+
+// Expect ALIAS.
+func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	if !yaml_emitter_process_anchor(emitter) {
+		return false
+	}
+	emitter.state = emitter.states[len(emitter.states)-1]
+	emitter.states = emitter.states[:len(emitter.states)-1]
+	return true
+}
+
+// Expect SCALAR.
+func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	if !yaml_emitter_select_scalar_style(emitter, event) {
+		return false
+	}
+	if !yaml_emitter_process_anchor(emitter) {
+		return false
+	}
+	if !yaml_emitter_process_tag(emitter) {
+		return false
+	}
+	if !yaml_emitter_increase_indent(emitter, true, false) {
+		return false
+	}
+	if !yaml_emitter_process_scalar(emitter) {
+		return false
+	}
+	emitter.indent = emitter.indents[len(emitter.indents)-1]
+	emitter.indents = emitter.indents[:len(emitter.indents)-1]
+	emitter.state = emitter.states[len(emitter.states)-1]
+	emitter.states = emitter.states[:len(emitter.states)-1]
+	return true
+}
+
+// Expect SEQUENCE-START.
+func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	if !yaml_emitter_process_anchor(emitter) {
+		return false
+	}
+	if !yaml_emitter_process_tag(emitter) {
+		return false
+	}
+	if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE ||
+		yaml_emitter_check_empty_sequence(emitter) {
+		emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE
+	} else {
+		emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE
+	}
+	return true
+}
+
+// Expect MAPPING-START.
+func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+	if !yaml_emitter_process_anchor(emitter) {
+		return false
+	}
+	if !yaml_emitter_process_tag(emitter) {
+		return false
+	}
+	if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE ||
+		yaml_emitter_check_empty_mapping(emitter) {
+		emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE
+	} else {
+		emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE
+	}
+	return true
+}
+
+// Check if the document content is an empty scalar.
+func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool {
+	return false // [Go] Huh?
+}
+
+// Check if the next events represent an empty sequence.
+func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool {
+	if len(emitter.events)-emitter.events_head < 2 {
+		return false
+	}
+	return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT &&
+		emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT
+}
+
+// Check if the next events represent an empty mapping.
+func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool {
+	if len(emitter.events)-emitter.events_head < 2 {
+		return false
+	}
+	return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT &&
+		emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT
+}
+
+// Check if the next node can be expressed as a simple key.
+func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool {
+	length := 0
+	switch emitter.events[emitter.events_head].typ {
+	case yaml_ALIAS_EVENT:
+		length += len(emitter.anchor_data.anchor)
+	case yaml_SCALAR_EVENT:
+		if emitter.scalar_data.multiline {
+			return false
+		}
+		length += len(emitter.anchor_data.anchor) +
+			len(emitter.tag_data.handle) +
+			len(emitter.tag_data.suffix) +
+			len(emitter.scalar_data.value)
+	case yaml_SEQUENCE_START_EVENT:
+		if !yaml_emitter_check_empty_sequence(emitter) {
+			return false
+		}
+		length += len(emitter.anchor_data.anchor) +
+			len(emitter.tag_data.handle) +
+			len(emitter.tag_data.suffix)
+	case yaml_MAPPING_START_EVENT:
+		if !yaml_emitter_check_empty_mapping(emitter) {
+			return false
+		}
+		length += len(emitter.anchor_data.anchor) +
+			len(emitter.tag_data.handle) +
+			len(emitter.tag_data.suffix)
+	default:
+		return false
+	}
+	return length <= 128
+}
+
+// Determine an acceptable scalar style.
+func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+
+	no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0
+	if no_tag && !event.implicit && !event.quoted_implicit {
+		return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified")
+	}
+
+	style := event.scalar_style()
+	if style == yaml_ANY_SCALAR_STYLE {
+		style = yaml_PLAIN_SCALAR_STYLE
+	}
+	if emitter.canonical {
+		style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+	}
+	if emitter.simple_key_context && emitter.scalar_data.multiline {
+		style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+	}
+
+	if style == yaml_PLAIN_SCALAR_STYLE {
+		if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed ||
+			emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed {
+			style = yaml_SINGLE_QUOTED_SCALAR_STYLE
+		}
+		if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) {
+			style = yaml_SINGLE_QUOTED_SCALAR_STYLE
+		}
+		if no_tag && !event.implicit {
+			style = yaml_SINGLE_QUOTED_SCALAR_STYLE
+		}
+	}
+	if style == yaml_SINGLE_QUOTED_SCALAR_STYLE {
+		if !emitter.scalar_data.single_quoted_allowed {
+			style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+		}
+	}
+	if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE {
+		if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context {
+			style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+		}
+	}
+
+	if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE {
+		emitter.tag_data.handle = []byte{'!'}
+	}
+	emitter.scalar_data.style = style
+	return true
+}
+
+// Write an achor.
+func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool {
+	if emitter.anchor_data.anchor == nil {
+		return true
+	}
+	c := []byte{'&'}
+	if emitter.anchor_data.alias {
+		c[0] = '*'
+	}
+	if !yaml_emitter_write_indicator(emitter, c, true, false, false) {
+		return false
+	}
+	return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor)
+}
+
+// Write a tag.
+func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool {
+	if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 {
+		return true
+	}
+	if len(emitter.tag_data.handle) > 0 {
+		if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) {
+			return false
+		}
+		if len(emitter.tag_data.suffix) > 0 {
+			if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) {
+				return false
+			}
+		}
+	} else {
+		// [Go] Allocate these slices elsewhere.
+		if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) {
+			return false
+		}
+		if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) {
+			return false
+		}
+		if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) {
+			return false
+		}
+	}
+	return true
+}
+
+// Write a scalar.
+func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool {
+	switch emitter.scalar_data.style {
+	case yaml_PLAIN_SCALAR_STYLE:
+		return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context)
+
+	case yaml_SINGLE_QUOTED_SCALAR_STYLE:
+		return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context)
+
+	case yaml_DOUBLE_QUOTED_SCALAR_STYLE:
+		return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context)
+
+	case yaml_LITERAL_SCALAR_STYLE:
+		return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value)
+
+	case yaml_FOLDED_SCALAR_STYLE:
+		return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value)
+	}
+	panic("unknown scalar style")
+}
+
+// Check if a %YAML directive is valid.
+func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool {
+	if version_directive.major != 1 || version_directive.minor != 1 {
+		return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive")
+	}
+	return true
+}
+
+// Check if a %TAG directive is valid.
+func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool {
+	handle := tag_directive.handle
+	prefix := tag_directive.prefix
+	if len(handle) == 0 {
+		return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty")
+	}
+	if handle[0] != '!' {
+		return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'")
+	}
+	if handle[len(handle)-1] != '!' {
+		return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'")
+	}
+	for i := 1; i < len(handle)-1; i += width(handle[i]) {
+		if !is_alpha(handle, i) {
+			return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only")
+		}
+	}
+	if len(prefix) == 0 {
+		return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty")
+	}
+	return true
+}
+
+// Check if an anchor is valid.
+func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool {
+	if len(anchor) == 0 {
+		problem := "anchor value must not be empty"
+		if alias {
+			problem = "alias value must not be empty"
+		}
+		return yaml_emitter_set_emitter_error(emitter, problem)
+	}
+	for i := 0; i < len(anchor); i += width(anchor[i]) {
+		if !is_alpha(anchor, i) {
+			problem := "anchor value must contain alphanumerical characters only"
+			if alias {
+				problem = "alias value must contain alphanumerical characters only"
+			}
+			return yaml_emitter_set_emitter_error(emitter, problem)
+		}
+	}
+	emitter.anchor_data.anchor = anchor
+	emitter.anchor_data.alias = alias
+	return true
+}
+
+// Check if a tag is valid.
+func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool {
+	if len(tag) == 0 {
+		return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty")
+	}
+	for i := 0; i < len(emitter.tag_directives); i++ {
+		tag_directive := &emitter.tag_directives[i]
+		if bytes.HasPrefix(tag, tag_directive.prefix) {
+			emitter.tag_data.handle = tag_directive.handle
+			emitter.tag_data.suffix = tag[len(tag_directive.prefix):]
+			return true
+		}
+	}
+	emitter.tag_data.suffix = tag
+	return true
+}
+
+// Check if a scalar is valid.
+func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
+	var (
+		block_indicators   = false
+		flow_indicators    = false
+		line_breaks        = false
+		special_characters = false
+
+		leading_space  = false
+		leading_break  = false
+		trailing_space = false
+		trailing_break = false
+		break_space    = false
+		space_break    = false
+
+		preceeded_by_whitespace = false
+		followed_by_whitespace  = false
+		previous_space          = false
+		previous_break          = false
+	)
+
+	emitter.scalar_data.value = value
+
+	if len(value) == 0 {
+		emitter.scalar_data.multiline = false
+		emitter.scalar_data.flow_plain_allowed = false
+		emitter.scalar_data.block_plain_allowed = true
+		emitter.scalar_data.single_quoted_allowed = true
+		emitter.scalar_data.block_allowed = false
+		return true
+	}
+
+	if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) {
+		block_indicators = true
+		flow_indicators = true
+	}
+
+	preceeded_by_whitespace = true
+	for i, w := 0, 0; i < len(value); i += w {
+		w = width(value[i])
+		followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w)
+
+		if i == 0 {
+			switch value[i] {
+			case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`':
+				flow_indicators = true
+				block_indicators = true
+			case '?', ':':
+				flow_indicators = true
+				if followed_by_whitespace {
+					block_indicators = true
+				}
+			case '-':
+				if followed_by_whitespace {
+					flow_indicators = true
+					block_indicators = true
+				}
+			}
+		} else {
+			switch value[i] {
+			case ',', '?', '[', ']', '{', '}':
+				flow_indicators = true
+			case ':':
+				flow_indicators = true
+				if followed_by_whitespace {
+					block_indicators = true
+				}
+			case '#':
+				if preceeded_by_whitespace {
+					flow_indicators = true
+					block_indicators = true
+				}
+			}
+		}
+
+		if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode {
+			special_characters = true
+		}
+		if is_space(value, i) {
+			if i == 0 {
+				leading_space = true
+			}
+			if i+width(value[i]) == len(value) {
+				trailing_space = true
+			}
+			if previous_break {
+				break_space = true
+			}
+			previous_space = true
+			previous_break = false
+		} else if is_break(value, i) {
+			line_breaks = true
+			if i == 0 {
+				leading_break = true
+			}
+			if i+width(value[i]) == len(value) {
+				trailing_break = true
+			}
+			if previous_space {
+				space_break = true
+			}
+			previous_space = false
+			previous_break = true
+		} else {
+			previous_space = false
+			previous_break = false
+		}
+
+		// [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition.
+		preceeded_by_whitespace = is_blankz(value, i)
+	}
+
+	emitter.scalar_data.multiline = line_breaks
+	emitter.scalar_data.flow_plain_allowed = true
+	emitter.scalar_data.block_plain_allowed = true
+	emitter.scalar_data.single_quoted_allowed = true
+	emitter.scalar_data.block_allowed = true
+
+	if leading_space || leading_break || trailing_space || trailing_break {
+		emitter.scalar_data.flow_plain_allowed = false
+		emitter.scalar_data.block_plain_allowed = false
+	}
+	if trailing_space {
+		emitter.scalar_data.block_allowed = false
+	}
+	if break_space {
+		emitter.scalar_data.flow_plain_allowed = false
+		emitter.scalar_data.block_plain_allowed = false
+		emitter.scalar_data.single_quoted_allowed = false
+	}
+	if space_break || special_characters {
+		emitter.scalar_data.flow_plain_allowed = false
+		emitter.scalar_data.block_plain_allowed = false
+		emitter.scalar_data.single_quoted_allowed = false
+		emitter.scalar_data.block_allowed = false
+	}
+	if line_breaks {
+		emitter.scalar_data.flow_plain_allowed = false
+		emitter.scalar_data.block_plain_allowed = false
+	}
+	if flow_indicators {
+		emitter.scalar_data.flow_plain_allowed = false
+	}
+	if block_indicators {
+		emitter.scalar_data.block_plain_allowed = false
+	}
+	return true
+}
+
+// Check if the event data is valid.
+func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+
+	emitter.anchor_data.anchor = nil
+	emitter.tag_data.handle = nil
+	emitter.tag_data.suffix = nil
+	emitter.scalar_data.value = nil
+
+	switch event.typ {
+	case yaml_ALIAS_EVENT:
+		if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) {
+			return false
+		}
+
+	case yaml_SCALAR_EVENT:
+		if len(event.anchor) > 0 {
+			if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) {
+				return false
+			}
+		}
+		if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) {
+			if !yaml_emitter_analyze_tag(emitter, event.tag) {
+				return false
+			}
+		}
+		if !yaml_emitter_analyze_scalar(emitter, event.value) {
+			return false
+		}
+
+	case yaml_SEQUENCE_START_EVENT:
+		if len(event.anchor) > 0 {
+			if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) {
+				return false
+			}
+		}
+		if len(event.tag) > 0 && (emitter.canonical || !event.implicit) {
+			if !yaml_emitter_analyze_tag(emitter, event.tag) {
+				return false
+			}
+		}
+
+	case yaml_MAPPING_START_EVENT:
+		if len(event.anchor) > 0 {
+			if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) {
+				return false
+			}
+		}
+		if len(event.tag) > 0 && (emitter.canonical || !event.implicit) {
+			if !yaml_emitter_analyze_tag(emitter, event.tag) {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+// Write the BOM character.
+func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool {
+	if !flush(emitter) {
+		return false
+	}
+	pos := emitter.buffer_pos
+	emitter.buffer[pos+0] = '\xEF'
+	emitter.buffer[pos+1] = '\xBB'
+	emitter.buffer[pos+2] = '\xBF'
+	emitter.buffer_pos += 3
+	return true
+}
+
+func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool {
+	indent := emitter.indent
+	if indent < 0 {
+		indent = 0
+	}
+	if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) {
+		if !put_break(emitter) {
+			return false
+		}
+	}
+	for emitter.column < indent {
+		if !put(emitter, ' ') {
+			return false
+		}
+	}
+	emitter.whitespace = true
+	emitter.indention = true
+	return true
+}
+
+func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool {
+	if need_whitespace && !emitter.whitespace {
+		if !put(emitter, ' ') {
+			return false
+		}
+	}
+	if !write_all(emitter, indicator) {
+		return false
+	}
+	emitter.whitespace = is_whitespace
+	emitter.indention = (emitter.indention && is_indention)
+	emitter.open_ended = false
+	return true
+}
+
+func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool {
+	if !write_all(emitter, value) {
+		return false
+	}
+	emitter.whitespace = false
+	emitter.indention = false
+	return true
+}
+
+func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool {
+	if !emitter.whitespace {
+		if !put(emitter, ' ') {
+			return false
+		}
+	}
+	if !write_all(emitter, value) {
+		return false
+	}
+	emitter.whitespace = false
+	emitter.indention = false
+	return true
+}
+
+func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool {
+	if need_whitespace && !emitter.whitespace {
+		if !put(emitter, ' ') {
+			return false
+		}
+	}
+	for i := 0; i < len(value); {
+		var must_write bool
+		switch value[i] {
+		case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']':
+			must_write = true
+		default:
+			must_write = is_alpha(value, i)
+		}
+		if must_write {
+			if !write(emitter, value, &i) {
+				return false
+			}
+		} else {
+			w := width(value[i])
+			for k := 0; k < w; k++ {
+				octet := value[i]
+				i++
+				if !put(emitter, '%') {
+					return false
+				}
+
+				c := octet >> 4
+				if c < 10 {
+					c += '0'
+				} else {
+					c += 'A' - 10
+				}
+				if !put(emitter, c) {
+					return false
+				}
+
+				c = octet & 0x0f
+				if c < 10 {
+					c += '0'
+				} else {
+					c += 'A' - 10
+				}
+				if !put(emitter, c) {
+					return false
+				}
+			}
+		}
+	}
+	emitter.whitespace = false
+	emitter.indention = false
+	return true
+}
+
+func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
+	if !emitter.whitespace {
+		if !put(emitter, ' ') {
+			return false
+		}
+	}
+
+	spaces := false
+	breaks := false
+	for i := 0; i < len(value); {
+		if is_space(value, i) {
+			if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) {
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+				i += width(value[i])
+			} else {
+				if !write(emitter, value, &i) {
+					return false
+				}
+			}
+			spaces = true
+		} else if is_break(value, i) {
+			if !breaks && value[i] == '\n' {
+				if !put_break(emitter) {
+					return false
+				}
+			}
+			if !write_break(emitter, value, &i) {
+				return false
+			}
+			emitter.indention = true
+			breaks = true
+		} else {
+			if breaks {
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+			}
+			if !write(emitter, value, &i) {
+				return false
+			}
+			emitter.indention = false
+			spaces = false
+			breaks = false
+		}
+	}
+
+	emitter.whitespace = false
+	emitter.indention = false
+	if emitter.root_context {
+		emitter.open_ended = true
+	}
+
+	return true
+}
+
+func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
+
+	if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) {
+		return false
+	}
+
+	spaces := false
+	breaks := false
+	for i := 0; i < len(value); {
+		if is_space(value, i) {
+			if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) {
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+				i += width(value[i])
+			} else {
+				if !write(emitter, value, &i) {
+					return false
+				}
+			}
+			spaces = true
+		} else if is_break(value, i) {
+			if !breaks && value[i] == '\n' {
+				if !put_break(emitter) {
+					return false
+				}
+			}
+			if !write_break(emitter, value, &i) {
+				return false
+			}
+			emitter.indention = true
+			breaks = true
+		} else {
+			if breaks {
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+			}
+			if value[i] == '\'' {
+				if !put(emitter, '\'') {
+					return false
+				}
+			}
+			if !write(emitter, value, &i) {
+				return false
+			}
+			emitter.indention = false
+			spaces = false
+			breaks = false
+		}
+	}
+	if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) {
+		return false
+	}
+	emitter.whitespace = false
+	emitter.indention = false
+	return true
+}
+
+func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
+	spaces := false
+	if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) {
+		return false
+	}
+
+	for i := 0; i < len(value); {
+		if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) ||
+			is_bom(value, i) || is_break(value, i) ||
+			value[i] == '"' || value[i] == '\\' {
+
+			octet := value[i]
+
+			var w int
+			var v rune
+			switch {
+			case octet&0x80 == 0x00:
+				w, v = 1, rune(octet&0x7F)
+			case octet&0xE0 == 0xC0:
+				w, v = 2, rune(octet&0x1F)
+			case octet&0xF0 == 0xE0:
+				w, v = 3, rune(octet&0x0F)
+			case octet&0xF8 == 0xF0:
+				w, v = 4, rune(octet&0x07)
+			}
+			for k := 1; k < w; k++ {
+				octet = value[i+k]
+				v = (v << 6) + (rune(octet) & 0x3F)
+			}
+			i += w
+
+			if !put(emitter, '\\') {
+				return false
+			}
+
+			var ok bool
+			switch v {
+			case 0x00:
+				ok = put(emitter, '0')
+			case 0x07:
+				ok = put(emitter, 'a')
+			case 0x08:
+				ok = put(emitter, 'b')
+			case 0x09:
+				ok = put(emitter, 't')
+			case 0x0A:
+				ok = put(emitter, 'n')
+			case 0x0b:
+				ok = put(emitter, 'v')
+			case 0x0c:
+				ok = put(emitter, 'f')
+			case 0x0d:
+				ok = put(emitter, 'r')
+			case 0x1b:
+				ok = put(emitter, 'e')
+			case 0x22:
+				ok = put(emitter, '"')
+			case 0x5c:
+				ok = put(emitter, '\\')
+			case 0x85:
+				ok = put(emitter, 'N')
+			case 0xA0:
+				ok = put(emitter, '_')
+			case 0x2028:
+				ok = put(emitter, 'L')
+			case 0x2029:
+				ok = put(emitter, 'P')
+			default:
+				if v <= 0xFF {
+					ok = put(emitter, 'x')
+					w = 2
+				} else if v <= 0xFFFF {
+					ok = put(emitter, 'u')
+					w = 4
+				} else {
+					ok = put(emitter, 'U')
+					w = 8
+				}
+				for k := (w - 1) * 4; ok && k >= 0; k -= 4 {
+					digit := byte((v >> uint(k)) & 0x0F)
+					if digit < 10 {
+						ok = put(emitter, digit+'0')
+					} else {
+						ok = put(emitter, digit+'A'-10)
+					}
+				}
+			}
+			if !ok {
+				return false
+			}
+			spaces = false
+		} else if is_space(value, i) {
+			if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 {
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+				if is_space(value, i+1) {
+					if !put(emitter, '\\') {
+						return false
+					}
+				}
+				i += width(value[i])
+			} else if !write(emitter, value, &i) {
+				return false
+			}
+			spaces = true
+		} else {
+			if !write(emitter, value, &i) {
+				return false
+			}
+			spaces = false
+		}
+	}
+	if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) {
+		return false
+	}
+	emitter.whitespace = false
+	emitter.indention = false
+	return true
+}
+
+func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool {
+	if is_space(value, 0) || is_break(value, 0) {
+		indent_hint := []byte{'0' + byte(emitter.best_indent)}
+		if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) {
+			return false
+		}
+	}
+
+	emitter.open_ended = false
+
+	var chomp_hint [1]byte
+	if len(value) == 0 {
+		chomp_hint[0] = '-'
+	} else {
+		i := len(value) - 1
+		for value[i]&0xC0 == 0x80 {
+			i--
+		}
+		if !is_break(value, i) {
+			chomp_hint[0] = '-'
+		} else if i == 0 {
+			chomp_hint[0] = '+'
+			emitter.open_ended = true
+		} else {
+			i--
+			for value[i]&0xC0 == 0x80 {
+				i--
+			}
+			if is_break(value, i) {
+				chomp_hint[0] = '+'
+				emitter.open_ended = true
+			}
+		}
+	}
+	if chomp_hint[0] != 0 {
+		if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) {
+			return false
+		}
+	}
+	return true
+}
+
+func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool {
+	if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) {
+		return false
+	}
+	if !yaml_emitter_write_block_scalar_hints(emitter, value) {
+		return false
+	}
+	if !put_break(emitter) {
+		return false
+	}
+	emitter.indention = true
+	emitter.whitespace = true
+	breaks := true
+	for i := 0; i < len(value); {
+		if is_break(value, i) {
+			if !write_break(emitter, value, &i) {
+				return false
+			}
+			emitter.indention = true
+			breaks = true
+		} else {
+			if breaks {
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+			}
+			if !write(emitter, value, &i) {
+				return false
+			}
+			emitter.indention = false
+			breaks = false
+		}
+	}
+
+	return true
+}
+
+func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool {
+	if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) {
+		return false
+	}
+	if !yaml_emitter_write_block_scalar_hints(emitter, value) {
+		return false
+	}
+
+	if !put_break(emitter) {
+		return false
+	}
+	emitter.indention = true
+	emitter.whitespace = true
+
+	breaks := true
+	leading_spaces := true
+	for i := 0; i < len(value); {
+		if is_break(value, i) {
+			if !breaks && !leading_spaces && value[i] == '\n' {
+				k := 0
+				for is_break(value, k) {
+					k += width(value[k])
+				}
+				if !is_blankz(value, k) {
+					if !put_break(emitter) {
+						return false
+					}
+				}
+			}
+			if !write_break(emitter, value, &i) {
+				return false
+			}
+			emitter.indention = true
+			breaks = true
+		} else {
+			if breaks {
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+				leading_spaces = is_blank(value, i)
+			}
+			if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width {
+				if !yaml_emitter_write_indent(emitter) {
+					return false
+				}
+				i += width(value[i])
+			} else {
+				if !write(emitter, value, &i) {
+					return false
+				}
+			}
+			emitter.indention = false
+			breaks = false
+		}
+	}
+	return true
+}

+ 306 - 0
_vendor/src/gopkg.in/yaml.v2/encode.go

@@ -0,0 +1,306 @@
+package yaml
+
+import (
+	"encoding"
+	"fmt"
+	"reflect"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type encoder struct {
+	emitter yaml_emitter_t
+	event   yaml_event_t
+	out     []byte
+	flow    bool
+}
+
+func newEncoder() (e *encoder) {
+	e = &encoder{}
+	e.must(yaml_emitter_initialize(&e.emitter))
+	yaml_emitter_set_output_string(&e.emitter, &e.out)
+	yaml_emitter_set_unicode(&e.emitter, true)
+	e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING))
+	e.emit()
+	e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true))
+	e.emit()
+	return e
+}
+
+func (e *encoder) finish() {
+	e.must(yaml_document_end_event_initialize(&e.event, true))
+	e.emit()
+	e.emitter.open_ended = false
+	e.must(yaml_stream_end_event_initialize(&e.event))
+	e.emit()
+}
+
+func (e *encoder) destroy() {
+	yaml_emitter_delete(&e.emitter)
+}
+
+func (e *encoder) emit() {
+	// This will internally delete the e.event value.
+	if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT {
+		e.must(false)
+	}
+}
+
+func (e *encoder) must(ok bool) {
+	if !ok {
+		msg := e.emitter.problem
+		if msg == "" {
+			msg = "unknown problem generating YAML content"
+		}
+		failf("%s", msg)
+	}
+}
+
+func (e *encoder) marshal(tag string, in reflect.Value) {
+	if !in.IsValid() {
+		e.nilv()
+		return
+	}
+	iface := in.Interface()
+	if m, ok := iface.(Marshaler); ok {
+		v, err := m.MarshalYAML()
+		if err != nil {
+			fail(err)
+		}
+		if v == nil {
+			e.nilv()
+			return
+		}
+		in = reflect.ValueOf(v)
+	} else if m, ok := iface.(encoding.TextMarshaler); ok {
+		text, err := m.MarshalText()
+		if err != nil {
+			fail(err)
+		}
+		in = reflect.ValueOf(string(text))
+	}
+	switch in.Kind() {
+	case reflect.Interface:
+		if in.IsNil() {
+			e.nilv()
+		} else {
+			e.marshal(tag, in.Elem())
+		}
+	case reflect.Map:
+		e.mapv(tag, in)
+	case reflect.Ptr:
+		if in.IsNil() {
+			e.nilv()
+		} else {
+			e.marshal(tag, in.Elem())
+		}
+	case reflect.Struct:
+		e.structv(tag, in)
+	case reflect.Slice:
+		if in.Type().Elem() == mapItemType {
+			e.itemsv(tag, in)
+		} else {
+			e.slicev(tag, in)
+		}
+	case reflect.String:
+		e.stringv(tag, in)
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		if in.Type() == durationType {
+			e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
+		} else {
+			e.intv(tag, in)
+		}
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		e.uintv(tag, in)
+	case reflect.Float32, reflect.Float64:
+		e.floatv(tag, in)
+	case reflect.Bool:
+		e.boolv(tag, in)
+	default:
+		panic("cannot marshal type: " + in.Type().String())
+	}
+}
+
+func (e *encoder) mapv(tag string, in reflect.Value) {
+	e.mappingv(tag, func() {
+		keys := keyList(in.MapKeys())
+		sort.Sort(keys)
+		for _, k := range keys {
+			e.marshal("", k)
+			e.marshal("", in.MapIndex(k))
+		}
+	})
+}
+
+func (e *encoder) itemsv(tag string, in reflect.Value) {
+	e.mappingv(tag, func() {
+		slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
+		for _, item := range slice {
+			e.marshal("", reflect.ValueOf(item.Key))
+			e.marshal("", reflect.ValueOf(item.Value))
+		}
+	})
+}
+
+func (e *encoder) structv(tag string, in reflect.Value) {
+	sinfo, err := getStructInfo(in.Type())
+	if err != nil {
+		panic(err)
+	}
+	e.mappingv(tag, func() {
+		for _, info := range sinfo.FieldsList {
+			var value reflect.Value
+			if info.Inline == nil {
+				value = in.Field(info.Num)
+			} else {
+				value = in.FieldByIndex(info.Inline)
+			}
+			if info.OmitEmpty && isZero(value) {
+				continue
+			}
+			e.marshal("", reflect.ValueOf(info.Key))
+			e.flow = info.Flow
+			e.marshal("", value)
+		}
+		if sinfo.InlineMap >= 0 {
+			m := in.Field(sinfo.InlineMap)
+			if m.Len() > 0 {
+				e.flow = false
+				keys := keyList(m.MapKeys())
+				sort.Sort(keys)
+				for _, k := range keys {
+					if _, found := sinfo.FieldsMap[k.String()]; found {
+						panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
+					}
+					e.marshal("", k)
+					e.flow = false
+					e.marshal("", m.MapIndex(k))
+				}
+			}
+		}
+	})
+}
+
+func (e *encoder) mappingv(tag string, f func()) {
+	implicit := tag == ""
+	style := yaml_BLOCK_MAPPING_STYLE
+	if e.flow {
+		e.flow = false
+		style = yaml_FLOW_MAPPING_STYLE
+	}
+	e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
+	e.emit()
+	f()
+	e.must(yaml_mapping_end_event_initialize(&e.event))
+	e.emit()
+}
+
+func (e *encoder) slicev(tag string, in reflect.Value) {
+	implicit := tag == ""
+	style := yaml_BLOCK_SEQUENCE_STYLE
+	if e.flow {
+		e.flow = false
+		style = yaml_FLOW_SEQUENCE_STYLE
+	}
+	e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
+	e.emit()
+	n := in.Len()
+	for i := 0; i < n; i++ {
+		e.marshal("", in.Index(i))
+	}
+	e.must(yaml_sequence_end_event_initialize(&e.event))
+	e.emit()
+}
+
+// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
+//
+// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
+// in YAML 1.2 and by this package, but these should be marshalled quoted for
+// the time being for compatibility with other parsers.
+func isBase60Float(s string) (result bool) {
+	// Fast path.
+	if s == "" {
+		return false
+	}
+	c := s[0]
+	if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
+		return false
+	}
+	// Do the full match.
+	return base60float.MatchString(s)
+}
+
+// From http://yaml.org/type/float.html, except the regular expression there
+// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
+var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
+
+func (e *encoder) stringv(tag string, in reflect.Value) {
+	var style yaml_scalar_style_t
+	s := in.String()
+	rtag, rs := resolve("", s)
+	if rtag == yaml_BINARY_TAG {
+		if tag == "" || tag == yaml_STR_TAG {
+			tag = rtag
+			s = rs.(string)
+		} else if tag == yaml_BINARY_TAG {
+			failf("explicitly tagged !!binary data must be base64-encoded")
+		} else {
+			failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
+		}
+	}
+	if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) {
+		style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+	} else if strings.Contains(s, "\n") {
+		style = yaml_LITERAL_SCALAR_STYLE
+	} else {
+		style = yaml_PLAIN_SCALAR_STYLE
+	}
+	e.emitScalar(s, "", tag, style)
+}
+
+func (e *encoder) boolv(tag string, in reflect.Value) {
+	var s string
+	if in.Bool() {
+		s = "true"
+	} else {
+		s = "false"
+	}
+	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
+}
+
+func (e *encoder) intv(tag string, in reflect.Value) {
+	s := strconv.FormatInt(in.Int(), 10)
+	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
+}
+
+func (e *encoder) uintv(tag string, in reflect.Value) {
+	s := strconv.FormatUint(in.Uint(), 10)
+	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
+}
+
+func (e *encoder) floatv(tag string, in reflect.Value) {
+	// FIXME: Handle 64 bits here.
+	s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32)
+	switch s {
+	case "+Inf":
+		s = ".inf"
+	case "-Inf":
+		s = "-.inf"
+	case "NaN":
+		s = ".nan"
+	}
+	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
+}
+
+func (e *encoder) nilv() {
+	e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
+}
+
+func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
+	implicit := tag == ""
+	e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
+	e.emit()
+}

+ 501 - 0
_vendor/src/gopkg.in/yaml.v2/encode_test.go

@@ -0,0 +1,501 @@
+package yaml_test
+
+import (
+	"fmt"
+	"math"
+	"strconv"
+	"strings"
+	"time"
+
+	. "gopkg.in/check.v1"
+	"gopkg.in/yaml.v2"
+	"net"
+	"os"
+)
+
+var marshalIntTest = 123
+
+var marshalTests = []struct {
+	value interface{}
+	data  string
+}{
+	{
+		nil,
+		"null\n",
+	}, {
+		&struct{}{},
+		"{}\n",
+	}, {
+		map[string]string{"v": "hi"},
+		"v: hi\n",
+	}, {
+		map[string]interface{}{"v": "hi"},
+		"v: hi\n",
+	}, {
+		map[string]string{"v": "true"},
+		"v: \"true\"\n",
+	}, {
+		map[string]string{"v": "false"},
+		"v: \"false\"\n",
+	}, {
+		map[string]interface{}{"v": true},
+		"v: true\n",
+	}, {
+		map[string]interface{}{"v": false},
+		"v: false\n",
+	}, {
+		map[string]interface{}{"v": 10},
+		"v: 10\n",
+	}, {
+		map[string]interface{}{"v": -10},
+		"v: -10\n",
+	}, {
+		map[string]uint{"v": 42},
+		"v: 42\n",
+	}, {
+		map[string]interface{}{"v": int64(4294967296)},
+		"v: 4294967296\n",
+	}, {
+		map[string]int64{"v": int64(4294967296)},
+		"v: 4294967296\n",
+	}, {
+		map[string]uint64{"v": 4294967296},
+		"v: 4294967296\n",
+	}, {
+		map[string]interface{}{"v": "10"},
+		"v: \"10\"\n",
+	}, {
+		map[string]interface{}{"v": 0.1},
+		"v: 0.1\n",
+	}, {
+		map[string]interface{}{"v": float64(0.1)},
+		"v: 0.1\n",
+	}, {
+		map[string]interface{}{"v": -0.1},
+		"v: -0.1\n",
+	}, {
+		map[string]interface{}{"v": math.Inf(+1)},
+		"v: .inf\n",
+	}, {
+		map[string]interface{}{"v": math.Inf(-1)},
+		"v: -.inf\n",
+	}, {
+		map[string]interface{}{"v": math.NaN()},
+		"v: .nan\n",
+	}, {
+		map[string]interface{}{"v": nil},
+		"v: null\n",
+	}, {
+		map[string]interface{}{"v": ""},
+		"v: \"\"\n",
+	}, {
+		map[string][]string{"v": []string{"A", "B"}},
+		"v:\n- A\n- B\n",
+	}, {
+		map[string][]string{"v": []string{"A", "B\nC"}},
+		"v:\n- A\n- |-\n  B\n  C\n",
+	}, {
+		map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
+		"v:\n- A\n- 1\n- B:\n  - 2\n  - 3\n",
+	}, {
+		map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
+		"a:\n  b: c\n",
+	}, {
+		map[string]interface{}{"a": "-"},
+		"a: '-'\n",
+	},
+
+	// Simple values.
+	{
+		&marshalIntTest,
+		"123\n",
+	},
+
+	// Structures
+	{
+		&struct{ Hello string }{"world"},
+		"hello: world\n",
+	}, {
+		&struct {
+			A struct {
+				B string
+			}
+		}{struct{ B string }{"c"}},
+		"a:\n  b: c\n",
+	}, {
+		&struct {
+			A *struct {
+				B string
+			}
+		}{&struct{ B string }{"c"}},
+		"a:\n  b: c\n",
+	}, {
+		&struct {
+			A *struct {
+				B string
+			}
+		}{},
+		"a: null\n",
+	}, {
+		&struct{ A int }{1},
+		"a: 1\n",
+	}, {
+		&struct{ A []int }{[]int{1, 2}},
+		"a:\n- 1\n- 2\n",
+	}, {
+		&struct {
+			B int "a"
+		}{1},
+		"a: 1\n",
+	}, {
+		&struct{ A bool }{true},
+		"a: true\n",
+	},
+
+	// Conditional flag
+	{
+		&struct {
+			A int "a,omitempty"
+			B int "b,omitempty"
+		}{1, 0},
+		"a: 1\n",
+	}, {
+		&struct {
+			A int "a,omitempty"
+			B int "b,omitempty"
+		}{0, 0},
+		"{}\n",
+	}, {
+		&struct {
+			A *struct{ X, y int } "a,omitempty,flow"
+		}{&struct{ X, y int }{1, 2}},
+		"a: {x: 1}\n",
+	}, {
+		&struct {
+			A *struct{ X, y int } "a,omitempty,flow"
+		}{nil},
+		"{}\n",
+	}, {
+		&struct {
+			A *struct{ X, y int } "a,omitempty,flow"
+		}{&struct{ X, y int }{}},
+		"a: {x: 0}\n",
+	}, {
+		&struct {
+			A struct{ X, y int } "a,omitempty,flow"
+		}{struct{ X, y int }{1, 2}},
+		"a: {x: 1}\n",
+	}, {
+		&struct {
+			A struct{ X, y int } "a,omitempty,flow"
+		}{struct{ X, y int }{0, 1}},
+		"{}\n",
+	}, {
+		&struct {
+			A float64 "a,omitempty"
+			B float64 "b,omitempty"
+		}{1, 0},
+		"a: 1\n",
+	},
+
+	// Flow flag
+	{
+		&struct {
+			A []int "a,flow"
+		}{[]int{1, 2}},
+		"a: [1, 2]\n",
+	}, {
+		&struct {
+			A map[string]string "a,flow"
+		}{map[string]string{"b": "c", "d": "e"}},
+		"a: {b: c, d: e}\n",
+	}, {
+		&struct {
+			A struct {
+				B, D string
+			} "a,flow"
+		}{struct{ B, D string }{"c", "e"}},
+		"a: {b: c, d: e}\n",
+	},
+
+	// Unexported field
+	{
+		&struct {
+			u int
+			A int
+		}{0, 1},
+		"a: 1\n",
+	},
+
+	// Ignored field
+	{
+		&struct {
+			A int
+			B int "-"
+		}{1, 2},
+		"a: 1\n",
+	},
+
+	// Struct inlining
+	{
+		&struct {
+			A int
+			C inlineB `yaml:",inline"`
+		}{1, inlineB{2, inlineC{3}}},
+		"a: 1\nb: 2\nc: 3\n",
+	},
+
+	// Map inlining
+	{
+		&struct {
+			A int
+			C map[string]int `yaml:",inline"`
+		}{1, map[string]int{"b": 2, "c": 3}},
+		"a: 1\nb: 2\nc: 3\n",
+	},
+
+	// Duration
+	{
+		map[string]time.Duration{"a": 3 * time.Second},
+		"a: 3s\n",
+	},
+
+	// Issue #24: bug in map merging logic.
+	{
+		map[string]string{"a": "<foo>"},
+		"a: <foo>\n",
+	},
+
+	// Issue #34: marshal unsupported base 60 floats quoted for compatibility
+	// with old YAML 1.1 parsers.
+	{
+		map[string]string{"a": "1:1"},
+		"a: \"1:1\"\n",
+	},
+
+	// Binary data.
+	{
+		map[string]string{"a": "\x00"},
+		"a: \"\\0\"\n",
+	}, {
+		map[string]string{"a": "\x80\x81\x82"},
+		"a: !!binary gIGC\n",
+	}, {
+		map[string]string{"a": strings.Repeat("\x90", 54)},
+		"a: !!binary |\n  " + strings.Repeat("kJCQ", 17) + "kJ\n  CQ\n",
+	},
+
+	// Ordered maps.
+	{
+		&yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}},
+		"b: 2\na: 1\nd: 4\nc: 3\nsub:\n  e: 5\n",
+	},
+
+	// Encode unicode as utf-8 rather than in escaped form.
+	{
+		map[string]string{"a": "你好"},
+		"a: 你好\n",
+	},
+
+	// Support encoding.TextMarshaler.
+	{
+		map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
+		"a: 1.2.3.4\n",
+	},
+	{
+		map[string]time.Time{"a": time.Unix(1424801979, 0)},
+		"a: 2015-02-24T18:19:39Z\n",
+	},
+
+	// Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible).
+	{
+		map[string]string{"a": "b: c"},
+		"a: 'b: c'\n",
+	},
+
+	// Containing hash mark ('#') in string should be quoted
+	{
+		map[string]string{"a": "Hello #comment"},
+		"a: 'Hello #comment'\n",
+	},
+	{
+		map[string]string{"a": "你好 #comment"},
+		"a: '你好 #comment'\n",
+	},
+}
+
+func (s *S) TestMarshal(c *C) {
+	defer os.Setenv("TZ", os.Getenv("TZ"))
+	os.Setenv("TZ", "UTC")
+	for _, item := range marshalTests {
+		data, err := yaml.Marshal(item.value)
+		c.Assert(err, IsNil)
+		c.Assert(string(data), Equals, item.data)
+	}
+}
+
+var marshalErrorTests = []struct {
+	value interface{}
+	error string
+	panic string
+}{{
+	value: &struct {
+		B       int
+		inlineB ",inline"
+	}{1, inlineB{2, inlineC{3}}},
+	panic: `Duplicated key 'b' in struct struct \{ B int; .*`,
+}, {
+	value: &struct {
+		A int
+		B map[string]int ",inline"
+	}{1, map[string]int{"a": 2}},
+	panic: `Can't have key "a" in inlined map; conflicts with struct field`,
+}}
+
+func (s *S) TestMarshalErrors(c *C) {
+	for _, item := range marshalErrorTests {
+		if item.panic != "" {
+			c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
+		} else {
+			_, err := yaml.Marshal(item.value)
+			c.Assert(err, ErrorMatches, item.error)
+		}
+	}
+}
+
+func (s *S) TestMarshalTypeCache(c *C) {
+	var data []byte
+	var err error
+	func() {
+		type T struct{ A int }
+		data, err = yaml.Marshal(&T{})
+		c.Assert(err, IsNil)
+	}()
+	func() {
+		type T struct{ B int }
+		data, err = yaml.Marshal(&T{})
+		c.Assert(err, IsNil)
+	}()
+	c.Assert(string(data), Equals, "b: 0\n")
+}
+
+var marshalerTests = []struct {
+	data  string
+	value interface{}
+}{
+	{"_:\n  hi: there\n", map[interface{}]interface{}{"hi": "there"}},
+	{"_:\n- 1\n- A\n", []interface{}{1, "A"}},
+	{"_: 10\n", 10},
+	{"_: null\n", nil},
+	{"_: BAR!\n", "BAR!"},
+}
+
+type marshalerType struct {
+	value interface{}
+}
+
+func (o marshalerType) MarshalText() ([]byte, error) {
+	panic("MarshalText called on type with MarshalYAML")
+}
+
+func (o marshalerType) MarshalYAML() (interface{}, error) {
+	return o.value, nil
+}
+
+type marshalerValue struct {
+	Field marshalerType "_"
+}
+
+func (s *S) TestMarshaler(c *C) {
+	for _, item := range marshalerTests {
+		obj := &marshalerValue{}
+		obj.Field.value = item.value
+		data, err := yaml.Marshal(obj)
+		c.Assert(err, IsNil)
+		c.Assert(string(data), Equals, string(item.data))
+	}
+}
+
+func (s *S) TestMarshalerWholeDocument(c *C) {
+	obj := &marshalerType{}
+	obj.value = map[string]string{"hello": "world!"}
+	data, err := yaml.Marshal(obj)
+	c.Assert(err, IsNil)
+	c.Assert(string(data), Equals, "hello: world!\n")
+}
+
+type failingMarshaler struct{}
+
+func (ft *failingMarshaler) MarshalYAML() (interface{}, error) {
+	return nil, failingErr
+}
+
+func (s *S) TestMarshalerError(c *C) {
+	_, err := yaml.Marshal(&failingMarshaler{})
+	c.Assert(err, Equals, failingErr)
+}
+
+func (s *S) TestSortedOutput(c *C) {
+	order := []interface{}{
+		false,
+		true,
+		1,
+		uint(1),
+		1.0,
+		1.1,
+		1.2,
+		2,
+		uint(2),
+		2.0,
+		2.1,
+		"",
+		".1",
+		".2",
+		".a",
+		"1",
+		"2",
+		"a!10",
+		"a/2",
+		"a/10",
+		"a~10",
+		"ab/1",
+		"b/1",
+		"b/01",
+		"b/2",
+		"b/02",
+		"b/3",
+		"b/03",
+		"b1",
+		"b01",
+		"b3",
+		"c2.10",
+		"c10.2",
+		"d1",
+		"d12",
+		"d12a",
+	}
+	m := make(map[interface{}]int)
+	for _, k := range order {
+		m[k] = 1
+	}
+	data, err := yaml.Marshal(m)
+	c.Assert(err, IsNil)
+	out := "\n" + string(data)
+	last := 0
+	for i, k := range order {
+		repr := fmt.Sprint(k)
+		if s, ok := k.(string); ok {
+			if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
+				repr = `"` + repr + `"`
+			}
+		}
+		index := strings.Index(out, "\n"+repr+":")
+		if index == -1 {
+			c.Fatalf("%#v is not in the output: %#v", k, out)
+		}
+		if index < last {
+			c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
+		}
+		last = index
+	}
+}

+ 1095 - 0
_vendor/src/gopkg.in/yaml.v2/parserc.go

@@ -0,0 +1,1095 @@
+package yaml
+
+import (
+	"bytes"
+)
+
+// The parser implements the following grammar:
+//
+// stream               ::= STREAM-START implicit_document? explicit_document* STREAM-END
+// implicit_document    ::= block_node DOCUMENT-END*
+// explicit_document    ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+// block_node_or_indentless_sequence    ::=
+//                          ALIAS
+//                          | properties (block_content | indentless_block_sequence)?
+//                          | block_content
+//                          | indentless_block_sequence
+// block_node           ::= ALIAS
+//                          | properties block_content?
+//                          | block_content
+// flow_node            ::= ALIAS
+//                          | properties flow_content?
+//                          | flow_content
+// properties           ::= TAG ANCHOR? | ANCHOR TAG?
+// block_content        ::= block_collection | flow_collection | SCALAR
+// flow_content         ::= flow_collection | SCALAR
+// block_collection     ::= block_sequence | block_mapping
+// flow_collection      ::= flow_sequence | flow_mapping
+// block_sequence       ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
+// indentless_sequence  ::= (BLOCK-ENTRY block_node?)+
+// block_mapping        ::= BLOCK-MAPPING_START
+//                          ((KEY block_node_or_indentless_sequence?)?
+//                          (VALUE block_node_or_indentless_sequence?)?)*
+//                          BLOCK-END
+// flow_sequence        ::= FLOW-SEQUENCE-START
+//                          (flow_sequence_entry FLOW-ENTRY)*
+//                          flow_sequence_entry?
+//                          FLOW-SEQUENCE-END
+// flow_sequence_entry  ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+// flow_mapping         ::= FLOW-MAPPING-START
+//                          (flow_mapping_entry FLOW-ENTRY)*
+//                          flow_mapping_entry?
+//                          FLOW-MAPPING-END
+// flow_mapping_entry   ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+
+// Peek the next token in the token queue.
+func peek_token(parser *yaml_parser_t) *yaml_token_t {
+	if parser.token_available || yaml_parser_fetch_more_tokens(parser) {
+		return &parser.tokens[parser.tokens_head]
+	}
+	return nil
+}
+
+// Remove the next token from the queue (must be called after peek_token).
+func skip_token(parser *yaml_parser_t) {
+	parser.token_available = false
+	parser.tokens_parsed++
+	parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN
+	parser.tokens_head++
+}
+
+// Get the next event.
+func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool {
+	// Erase the event object.
+	*event = yaml_event_t{}
+
+	// No events after the end of the stream or error.
+	if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE {
+		return true
+	}
+
+	// Generate the next event.
+	return yaml_parser_state_machine(parser, event)
+}
+
+// Set parser error.
+func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool {
+	parser.error = yaml_PARSER_ERROR
+	parser.problem = problem
+	parser.problem_mark = problem_mark
+	return false
+}
+
+func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool {
+	parser.error = yaml_PARSER_ERROR
+	parser.context = context
+	parser.context_mark = context_mark
+	parser.problem = problem
+	parser.problem_mark = problem_mark
+	return false
+}
+
+// State dispatcher.
+func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool {
+	//trace("yaml_parser_state_machine", "state:", parser.state.String())
+
+	switch parser.state {
+	case yaml_PARSE_STREAM_START_STATE:
+		return yaml_parser_parse_stream_start(parser, event)
+
+	case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE:
+		return yaml_parser_parse_document_start(parser, event, true)
+
+	case yaml_PARSE_DOCUMENT_START_STATE:
+		return yaml_parser_parse_document_start(parser, event, false)
+
+	case yaml_PARSE_DOCUMENT_CONTENT_STATE:
+		return yaml_parser_parse_document_content(parser, event)
+
+	case yaml_PARSE_DOCUMENT_END_STATE:
+		return yaml_parser_parse_document_end(parser, event)
+
+	case yaml_PARSE_BLOCK_NODE_STATE:
+		return yaml_parser_parse_node(parser, event, true, false)
+
+	case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE:
+		return yaml_parser_parse_node(parser, event, true, true)
+
+	case yaml_PARSE_FLOW_NODE_STATE:
+		return yaml_parser_parse_node(parser, event, false, false)
+
+	case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE:
+		return yaml_parser_parse_block_sequence_entry(parser, event, true)
+
+	case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE:
+		return yaml_parser_parse_block_sequence_entry(parser, event, false)
+
+	case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE:
+		return yaml_parser_parse_indentless_sequence_entry(parser, event)
+
+	case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE:
+		return yaml_parser_parse_block_mapping_key(parser, event, true)
+
+	case yaml_PARSE_BLOCK_MAPPING_KEY_STATE:
+		return yaml_parser_parse_block_mapping_key(parser, event, false)
+
+	case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE:
+		return yaml_parser_parse_block_mapping_value(parser, event)
+
+	case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE:
+		return yaml_parser_parse_flow_sequence_entry(parser, event, true)
+
+	case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE:
+		return yaml_parser_parse_flow_sequence_entry(parser, event, false)
+
+	case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE:
+		return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event)
+
+	case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE:
+		return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event)
+
+	case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE:
+		return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event)
+
+	case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE:
+		return yaml_parser_parse_flow_mapping_key(parser, event, true)
+
+	case yaml_PARSE_FLOW_MAPPING_KEY_STATE:
+		return yaml_parser_parse_flow_mapping_key(parser, event, false)
+
+	case yaml_PARSE_FLOW_MAPPING_VALUE_STATE:
+		return yaml_parser_parse_flow_mapping_value(parser, event, false)
+
+	case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE:
+		return yaml_parser_parse_flow_mapping_value(parser, event, true)
+
+	default:
+		panic("invalid parser state")
+	}
+}
+
+// Parse the production:
+// stream   ::= STREAM-START implicit_document? explicit_document* STREAM-END
+//              ************
+func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool {
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+	if token.typ != yaml_STREAM_START_TOKEN {
+		return yaml_parser_set_parser_error(parser, "did not find expected <stream-start>", token.start_mark)
+	}
+	parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE
+	*event = yaml_event_t{
+		typ:        yaml_STREAM_START_EVENT,
+		start_mark: token.start_mark,
+		end_mark:   token.end_mark,
+		encoding:   token.encoding,
+	}
+	skip_token(parser)
+	return true
+}
+
+// Parse the productions:
+// implicit_document    ::= block_node DOCUMENT-END*
+//                          *
+// explicit_document    ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+//                          *************************
+func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool {
+
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+
+	// Parse extra document end indicators.
+	if !implicit {
+		for token.typ == yaml_DOCUMENT_END_TOKEN {
+			skip_token(parser)
+			token = peek_token(parser)
+			if token == nil {
+				return false
+			}
+		}
+	}
+
+	if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN &&
+		token.typ != yaml_TAG_DIRECTIVE_TOKEN &&
+		token.typ != yaml_DOCUMENT_START_TOKEN &&
+		token.typ != yaml_STREAM_END_TOKEN {
+		// Parse an implicit document.
+		if !yaml_parser_process_directives(parser, nil, nil) {
+			return false
+		}
+		parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE)
+		parser.state = yaml_PARSE_BLOCK_NODE_STATE
+
+		*event = yaml_event_t{
+			typ:        yaml_DOCUMENT_START_EVENT,
+			start_mark: token.start_mark,
+			end_mark:   token.end_mark,
+		}
+
+	} else if token.typ != yaml_STREAM_END_TOKEN {
+		// Parse an explicit document.
+		var version_directive *yaml_version_directive_t
+		var tag_directives []yaml_tag_directive_t
+		start_mark := token.start_mark
+		if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) {
+			return false
+		}
+		token = peek_token(parser)
+		if token == nil {
+			return false
+		}
+		if token.typ != yaml_DOCUMENT_START_TOKEN {
+			yaml_parser_set_parser_error(parser,
+				"did not find expected <document start>", token.start_mark)
+			return false
+		}
+		parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE)
+		parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE
+		end_mark := token.end_mark
+
+		*event = yaml_event_t{
+			typ:               yaml_DOCUMENT_START_EVENT,
+			start_mark:        start_mark,
+			end_mark:          end_mark,
+			version_directive: version_directive,
+			tag_directives:    tag_directives,
+			implicit:          false,
+		}
+		skip_token(parser)
+
+	} else {
+		// Parse the stream end.
+		parser.state = yaml_PARSE_END_STATE
+		*event = yaml_event_t{
+			typ:        yaml_STREAM_END_EVENT,
+			start_mark: token.start_mark,
+			end_mark:   token.end_mark,
+		}
+		skip_token(parser)
+	}
+
+	return true
+}
+
+// Parse the productions:
+// explicit_document    ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+//                                                    ***********
+//
+func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool {
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+	if token.typ == yaml_VERSION_DIRECTIVE_TOKEN ||
+		token.typ == yaml_TAG_DIRECTIVE_TOKEN ||
+		token.typ == yaml_DOCUMENT_START_TOKEN ||
+		token.typ == yaml_DOCUMENT_END_TOKEN ||
+		token.typ == yaml_STREAM_END_TOKEN {
+		parser.state = parser.states[len(parser.states)-1]
+		parser.states = parser.states[:len(parser.states)-1]
+		return yaml_parser_process_empty_scalar(parser, event,
+			token.start_mark)
+	}
+	return yaml_parser_parse_node(parser, event, true, false)
+}
+
+// Parse the productions:
+// implicit_document    ::= block_node DOCUMENT-END*
+//                                     *************
+// explicit_document    ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+//
+func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool {
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+
+	start_mark := token.start_mark
+	end_mark := token.start_mark
+
+	implicit := true
+	if token.typ == yaml_DOCUMENT_END_TOKEN {
+		end_mark = token.end_mark
+		skip_token(parser)
+		implicit = false
+	}
+
+	parser.tag_directives = parser.tag_directives[:0]
+
+	parser.state = yaml_PARSE_DOCUMENT_START_STATE
+	*event = yaml_event_t{
+		typ:        yaml_DOCUMENT_END_EVENT,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+		implicit:   implicit,
+	}
+	return true
+}
+
+// Parse the productions:
+// block_node_or_indentless_sequence    ::=
+//                          ALIAS
+//                          *****
+//                          | properties (block_content | indentless_block_sequence)?
+//                            **********  *
+//                          | block_content | indentless_block_sequence
+//                            *
+// block_node           ::= ALIAS
+//                          *****
+//                          | properties block_content?
+//                            ********** *
+//                          | block_content
+//                            *
+// flow_node            ::= ALIAS
+//                          *****
+//                          | properties flow_content?
+//                            ********** *
+//                          | flow_content
+//                            *
+// properties           ::= TAG ANCHOR? | ANCHOR TAG?
+//                          *************************
+// block_content        ::= block_collection | flow_collection | SCALAR
+//                                                               ******
+// flow_content         ::= flow_collection | SCALAR
+//                                            ******
+func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool {
+	//defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)()
+
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+
+	if token.typ == yaml_ALIAS_TOKEN {
+		parser.state = parser.states[len(parser.states)-1]
+		parser.states = parser.states[:len(parser.states)-1]
+		*event = yaml_event_t{
+			typ:        yaml_ALIAS_EVENT,
+			start_mark: token.start_mark,
+			end_mark:   token.end_mark,
+			anchor:     token.value,
+		}
+		skip_token(parser)
+		return true
+	}
+
+	start_mark := token.start_mark
+	end_mark := token.start_mark
+
+	var tag_token bool
+	var tag_handle, tag_suffix, anchor []byte
+	var tag_mark yaml_mark_t
+	if token.typ == yaml_ANCHOR_TOKEN {
+		anchor = token.value
+		start_mark = token.start_mark
+		end_mark = token.end_mark
+		skip_token(parser)
+		token = peek_token(parser)
+		if token == nil {
+			return false
+		}
+		if token.typ == yaml_TAG_TOKEN {
+			tag_token = true
+			tag_handle = token.value
+			tag_suffix = token.suffix
+			tag_mark = token.start_mark
+			end_mark = token.end_mark
+			skip_token(parser)
+			token = peek_token(parser)
+			if token == nil {
+				return false
+			}
+		}
+	} else if token.typ == yaml_TAG_TOKEN {
+		tag_token = true
+		tag_handle = token.value
+		tag_suffix = token.suffix
+		start_mark = token.start_mark
+		tag_mark = token.start_mark
+		end_mark = token.end_mark
+		skip_token(parser)
+		token = peek_token(parser)
+		if token == nil {
+			return false
+		}
+		if token.typ == yaml_ANCHOR_TOKEN {
+			anchor = token.value
+			end_mark = token.end_mark
+			skip_token(parser)
+			token = peek_token(parser)
+			if token == nil {
+				return false
+			}
+		}
+	}
+
+	var tag []byte
+	if tag_token {
+		if len(tag_handle) == 0 {
+			tag = tag_suffix
+			tag_suffix = nil
+		} else {
+			for i := range parser.tag_directives {
+				if bytes.Equal(parser.tag_directives[i].handle, tag_handle) {
+					tag = append([]byte(nil), parser.tag_directives[i].prefix...)
+					tag = append(tag, tag_suffix...)
+					break
+				}
+			}
+			if len(tag) == 0 {
+				yaml_parser_set_parser_error_context(parser,
+					"while parsing a node", start_mark,
+					"found undefined tag handle", tag_mark)
+				return false
+			}
+		}
+	}
+
+	implicit := len(tag) == 0
+	if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN {
+		end_mark = token.end_mark
+		parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE
+		*event = yaml_event_t{
+			typ:        yaml_SEQUENCE_START_EVENT,
+			start_mark: start_mark,
+			end_mark:   end_mark,
+			anchor:     anchor,
+			tag:        tag,
+			implicit:   implicit,
+			style:      yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE),
+		}
+		return true
+	}
+	if token.typ == yaml_SCALAR_TOKEN {
+		var plain_implicit, quoted_implicit bool
+		end_mark = token.end_mark
+		if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') {
+			plain_implicit = true
+		} else if len(tag) == 0 {
+			quoted_implicit = true
+		}
+		parser.state = parser.states[len(parser.states)-1]
+		parser.states = parser.states[:len(parser.states)-1]
+
+		*event = yaml_event_t{
+			typ:             yaml_SCALAR_EVENT,
+			start_mark:      start_mark,
+			end_mark:        end_mark,
+			anchor:          anchor,
+			tag:             tag,
+			value:           token.value,
+			implicit:        plain_implicit,
+			quoted_implicit: quoted_implicit,
+			style:           yaml_style_t(token.style),
+		}
+		skip_token(parser)
+		return true
+	}
+	if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN {
+		// [Go] Some of the events below can be merged as they differ only on style.
+		end_mark = token.end_mark
+		parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE
+		*event = yaml_event_t{
+			typ:        yaml_SEQUENCE_START_EVENT,
+			start_mark: start_mark,
+			end_mark:   end_mark,
+			anchor:     anchor,
+			tag:        tag,
+			implicit:   implicit,
+			style:      yaml_style_t(yaml_FLOW_SEQUENCE_STYLE),
+		}
+		return true
+	}
+	if token.typ == yaml_FLOW_MAPPING_START_TOKEN {
+		end_mark = token.end_mark
+		parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE
+		*event = yaml_event_t{
+			typ:        yaml_MAPPING_START_EVENT,
+			start_mark: start_mark,
+			end_mark:   end_mark,
+			anchor:     anchor,
+			tag:        tag,
+			implicit:   implicit,
+			style:      yaml_style_t(yaml_FLOW_MAPPING_STYLE),
+		}
+		return true
+	}
+	if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN {
+		end_mark = token.end_mark
+		parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE
+		*event = yaml_event_t{
+			typ:        yaml_SEQUENCE_START_EVENT,
+			start_mark: start_mark,
+			end_mark:   end_mark,
+			anchor:     anchor,
+			tag:        tag,
+			implicit:   implicit,
+			style:      yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE),
+		}
+		return true
+	}
+	if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN {
+		end_mark = token.end_mark
+		parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE
+		*event = yaml_event_t{
+			typ:        yaml_MAPPING_START_EVENT,
+			start_mark: start_mark,
+			end_mark:   end_mark,
+			anchor:     anchor,
+			tag:        tag,
+			implicit:   implicit,
+			style:      yaml_style_t(yaml_BLOCK_MAPPING_STYLE),
+		}
+		return true
+	}
+	if len(anchor) > 0 || len(tag) > 0 {
+		parser.state = parser.states[len(parser.states)-1]
+		parser.states = parser.states[:len(parser.states)-1]
+
+		*event = yaml_event_t{
+			typ:             yaml_SCALAR_EVENT,
+			start_mark:      start_mark,
+			end_mark:        end_mark,
+			anchor:          anchor,
+			tag:             tag,
+			implicit:        implicit,
+			quoted_implicit: false,
+			style:           yaml_style_t(yaml_PLAIN_SCALAR_STYLE),
+		}
+		return true
+	}
+
+	context := "while parsing a flow node"
+	if block {
+		context = "while parsing a block node"
+	}
+	yaml_parser_set_parser_error_context(parser, context, start_mark,
+		"did not find expected node content", token.start_mark)
+	return false
+}
+
+// Parse the productions:
+// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
+//                    ********************  *********** *             *********
+//
+func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
+	if first {
+		token := peek_token(parser)
+		parser.marks = append(parser.marks, token.start_mark)
+		skip_token(parser)
+	}
+
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+
+	if token.typ == yaml_BLOCK_ENTRY_TOKEN {
+		mark := token.end_mark
+		skip_token(parser)
+		token = peek_token(parser)
+		if token == nil {
+			return false
+		}
+		if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN {
+			parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE)
+			return yaml_parser_parse_node(parser, event, true, false)
+		} else {
+			parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE
+			return yaml_parser_process_empty_scalar(parser, event, mark)
+		}
+	}
+	if token.typ == yaml_BLOCK_END_TOKEN {
+		parser.state = parser.states[len(parser.states)-1]
+		parser.states = parser.states[:len(parser.states)-1]
+		parser.marks = parser.marks[:len(parser.marks)-1]
+
+		*event = yaml_event_t{
+			typ:        yaml_SEQUENCE_END_EVENT,
+			start_mark: token.start_mark,
+			end_mark:   token.end_mark,
+		}
+
+		skip_token(parser)
+		return true
+	}
+
+	context_mark := parser.marks[len(parser.marks)-1]
+	parser.marks = parser.marks[:len(parser.marks)-1]
+	return yaml_parser_set_parser_error_context(parser,
+		"while parsing a block collection", context_mark,
+		"did not find expected '-' indicator", token.start_mark)
+}
+
+// Parse the productions:
+// indentless_sequence  ::= (BLOCK-ENTRY block_node?)+
+//                           *********** *
+func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool {
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+
+	if token.typ == yaml_BLOCK_ENTRY_TOKEN {
+		mark := token.end_mark
+		skip_token(parser)
+		token = peek_token(parser)
+		if token == nil {
+			return false
+		}
+		if token.typ != yaml_BLOCK_ENTRY_TOKEN &&
+			token.typ != yaml_KEY_TOKEN &&
+			token.typ != yaml_VALUE_TOKEN &&
+			token.typ != yaml_BLOCK_END_TOKEN {
+			parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE)
+			return yaml_parser_parse_node(parser, event, true, false)
+		}
+		parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE
+		return yaml_parser_process_empty_scalar(parser, event, mark)
+	}
+	parser.state = parser.states[len(parser.states)-1]
+	parser.states = parser.states[:len(parser.states)-1]
+
+	*event = yaml_event_t{
+		typ:        yaml_SEQUENCE_END_EVENT,
+		start_mark: token.start_mark,
+		end_mark:   token.start_mark, // [Go] Shouldn't this be token.end_mark?
+	}
+	return true
+}
+
+// Parse the productions:
+// block_mapping        ::= BLOCK-MAPPING_START
+//                          *******************
+//                          ((KEY block_node_or_indentless_sequence?)?
+//                            *** *
+//                          (VALUE block_node_or_indentless_sequence?)?)*
+//
+//                          BLOCK-END
+//                          *********
+//
+func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
+	if first {
+		token := peek_token(parser)
+		parser.marks = append(parser.marks, token.start_mark)
+		skip_token(parser)
+	}
+
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+
+	if token.typ == yaml_KEY_TOKEN {
+		mark := token.end_mark
+		skip_token(parser)
+		token = peek_token(parser)
+		if token == nil {
+			return false
+		}
+		if token.typ != yaml_KEY_TOKEN &&
+			token.typ != yaml_VALUE_TOKEN &&
+			token.typ != yaml_BLOCK_END_TOKEN {
+			parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE)
+			return yaml_parser_parse_node(parser, event, true, true)
+		} else {
+			parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE
+			return yaml_parser_process_empty_scalar(parser, event, mark)
+		}
+	} else if token.typ == yaml_BLOCK_END_TOKEN {
+		parser.state = parser.states[len(parser.states)-1]
+		parser.states = parser.states[:len(parser.states)-1]
+		parser.marks = parser.marks[:len(parser.marks)-1]
+		*event = yaml_event_t{
+			typ:        yaml_MAPPING_END_EVENT,
+			start_mark: token.start_mark,
+			end_mark:   token.end_mark,
+		}
+		skip_token(parser)
+		return true
+	}
+
+	context_mark := parser.marks[len(parser.marks)-1]
+	parser.marks = parser.marks[:len(parser.marks)-1]
+	return yaml_parser_set_parser_error_context(parser,
+		"while parsing a block mapping", context_mark,
+		"did not find expected key", token.start_mark)
+}
+
+// Parse the productions:
+// block_mapping        ::= BLOCK-MAPPING_START
+//
+//                          ((KEY block_node_or_indentless_sequence?)?
+//
+//                          (VALUE block_node_or_indentless_sequence?)?)*
+//                           ***** *
+//                          BLOCK-END
+//
+//
+func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+	if token.typ == yaml_VALUE_TOKEN {
+		mark := token.end_mark
+		skip_token(parser)
+		token = peek_token(parser)
+		if token == nil {
+			return false
+		}
+		if token.typ != yaml_KEY_TOKEN &&
+			token.typ != yaml_VALUE_TOKEN &&
+			token.typ != yaml_BLOCK_END_TOKEN {
+			parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE)
+			return yaml_parser_parse_node(parser, event, true, true)
+		}
+		parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE
+		return yaml_parser_process_empty_scalar(parser, event, mark)
+	}
+	parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE
+	return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+}
+
+// Parse the productions:
+// flow_sequence        ::= FLOW-SEQUENCE-START
+//                          *******************
+//                          (flow_sequence_entry FLOW-ENTRY)*
+//                           *                   **********
+//                          flow_sequence_entry?
+//                          *
+//                          FLOW-SEQUENCE-END
+//                          *****************
+// flow_sequence_entry  ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//                          *
+//
+func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
+	if first {
+		token := peek_token(parser)
+		parser.marks = append(parser.marks, token.start_mark)
+		skip_token(parser)
+	}
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+	if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN {
+		if !first {
+			if token.typ == yaml_FLOW_ENTRY_TOKEN {
+				skip_token(parser)
+				token = peek_token(parser)
+				if token == nil {
+					return false
+				}
+			} else {
+				context_mark := parser.marks[len(parser.marks)-1]
+				parser.marks = parser.marks[:len(parser.marks)-1]
+				return yaml_parser_set_parser_error_context(parser,
+					"while parsing a flow sequence", context_mark,
+					"did not find expected ',' or ']'", token.start_mark)
+			}
+		}
+
+		if token.typ == yaml_KEY_TOKEN {
+			parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE
+			*event = yaml_event_t{
+				typ:        yaml_MAPPING_START_EVENT,
+				start_mark: token.start_mark,
+				end_mark:   token.end_mark,
+				implicit:   true,
+				style:      yaml_style_t(yaml_FLOW_MAPPING_STYLE),
+			}
+			skip_token(parser)
+			return true
+		} else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN {
+			parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE)
+			return yaml_parser_parse_node(parser, event, false, false)
+		}
+	}
+
+	parser.state = parser.states[len(parser.states)-1]
+	parser.states = parser.states[:len(parser.states)-1]
+	parser.marks = parser.marks[:len(parser.marks)-1]
+
+	*event = yaml_event_t{
+		typ:        yaml_SEQUENCE_END_EVENT,
+		start_mark: token.start_mark,
+		end_mark:   token.end_mark,
+	}
+
+	skip_token(parser)
+	return true
+}
+
+//
+// Parse the productions:
+// flow_sequence_entry  ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//                                      *** *
+//
+func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool {
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+	if token.typ != yaml_VALUE_TOKEN &&
+		token.typ != yaml_FLOW_ENTRY_TOKEN &&
+		token.typ != yaml_FLOW_SEQUENCE_END_TOKEN {
+		parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE)
+		return yaml_parser_parse_node(parser, event, false, false)
+	}
+	mark := token.end_mark
+	skip_token(parser)
+	parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE
+	return yaml_parser_process_empty_scalar(parser, event, mark)
+}
+
+// Parse the productions:
+// flow_sequence_entry  ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//                                                      ***** *
+//
+func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+	if token.typ == yaml_VALUE_TOKEN {
+		skip_token(parser)
+		token := peek_token(parser)
+		if token == nil {
+			return false
+		}
+		if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN {
+			parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE)
+			return yaml_parser_parse_node(parser, event, false, false)
+		}
+	}
+	parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE
+	return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+}
+
+// Parse the productions:
+// flow_sequence_entry  ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//                                                                      *
+//
+func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool {
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+	parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE
+	*event = yaml_event_t{
+		typ:        yaml_MAPPING_END_EVENT,
+		start_mark: token.start_mark,
+		end_mark:   token.start_mark, // [Go] Shouldn't this be end_mark?
+	}
+	return true
+}
+
+// Parse the productions:
+// flow_mapping         ::= FLOW-MAPPING-START
+//                          ******************
+//                          (flow_mapping_entry FLOW-ENTRY)*
+//                           *                  **********
+//                          flow_mapping_entry?
+//                          ******************
+//                          FLOW-MAPPING-END
+//                          ****************
+// flow_mapping_entry   ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//                          *           *** *
+//
+func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
+	if first {
+		token := peek_token(parser)
+		parser.marks = append(parser.marks, token.start_mark)
+		skip_token(parser)
+	}
+
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+
+	if token.typ != yaml_FLOW_MAPPING_END_TOKEN {
+		if !first {
+			if token.typ == yaml_FLOW_ENTRY_TOKEN {
+				skip_token(parser)
+				token = peek_token(parser)
+				if token == nil {
+					return false
+				}
+			} else {
+				context_mark := parser.marks[len(parser.marks)-1]
+				parser.marks = parser.marks[:len(parser.marks)-1]
+				return yaml_parser_set_parser_error_context(parser,
+					"while parsing a flow mapping", context_mark,
+					"did not find expected ',' or '}'", token.start_mark)
+			}
+		}
+
+		if token.typ == yaml_KEY_TOKEN {
+			skip_token(parser)
+			token = peek_token(parser)
+			if token == nil {
+				return false
+			}
+			if token.typ != yaml_VALUE_TOKEN &&
+				token.typ != yaml_FLOW_ENTRY_TOKEN &&
+				token.typ != yaml_FLOW_MAPPING_END_TOKEN {
+				parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE)
+				return yaml_parser_parse_node(parser, event, false, false)
+			} else {
+				parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE
+				return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+			}
+		} else if token.typ != yaml_FLOW_MAPPING_END_TOKEN {
+			parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE)
+			return yaml_parser_parse_node(parser, event, false, false)
+		}
+	}
+
+	parser.state = parser.states[len(parser.states)-1]
+	parser.states = parser.states[:len(parser.states)-1]
+	parser.marks = parser.marks[:len(parser.marks)-1]
+	*event = yaml_event_t{
+		typ:        yaml_MAPPING_END_EVENT,
+		start_mark: token.start_mark,
+		end_mark:   token.end_mark,
+	}
+	skip_token(parser)
+	return true
+}
+
+// Parse the productions:
+// flow_mapping_entry   ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//                                   *                  ***** *
+//
+func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool {
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+	if empty {
+		parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE
+		return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+	}
+	if token.typ == yaml_VALUE_TOKEN {
+		skip_token(parser)
+		token = peek_token(parser)
+		if token == nil {
+			return false
+		}
+		if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN {
+			parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE)
+			return yaml_parser_parse_node(parser, event, false, false)
+		}
+	}
+	parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE
+	return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+}
+
+// Generate an empty scalar event.
+func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool {
+	*event = yaml_event_t{
+		typ:        yaml_SCALAR_EVENT,
+		start_mark: mark,
+		end_mark:   mark,
+		value:      nil, // Empty
+		implicit:   true,
+		style:      yaml_style_t(yaml_PLAIN_SCALAR_STYLE),
+	}
+	return true
+}
+
+var default_tag_directives = []yaml_tag_directive_t{
+	{[]byte("!"), []byte("!")},
+	{[]byte("!!"), []byte("tag:yaml.org,2002:")},
+}
+
+// Parse directives.
+func yaml_parser_process_directives(parser *yaml_parser_t,
+	version_directive_ref **yaml_version_directive_t,
+	tag_directives_ref *[]yaml_tag_directive_t) bool {
+
+	var version_directive *yaml_version_directive_t
+	var tag_directives []yaml_tag_directive_t
+
+	token := peek_token(parser)
+	if token == nil {
+		return false
+	}
+
+	for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN {
+		if token.typ == yaml_VERSION_DIRECTIVE_TOKEN {
+			if version_directive != nil {
+				yaml_parser_set_parser_error(parser,
+					"found duplicate %YAML directive", token.start_mark)
+				return false
+			}
+			if token.major != 1 || token.minor != 1 {
+				yaml_parser_set_parser_error(parser,
+					"found incompatible YAML document", token.start_mark)
+				return false
+			}
+			version_directive = &yaml_version_directive_t{
+				major: token.major,
+				minor: token.minor,
+			}
+		} else if token.typ == yaml_TAG_DIRECTIVE_TOKEN {
+			value := yaml_tag_directive_t{
+				handle: token.value,
+				prefix: token.prefix,
+			}
+			if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) {
+				return false
+			}
+			tag_directives = append(tag_directives, value)
+		}
+
+		skip_token(parser)
+		token = peek_token(parser)
+		if token == nil {
+			return false
+		}
+	}
+
+	for i := range default_tag_directives {
+		if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) {
+			return false
+		}
+	}
+
+	if version_directive_ref != nil {
+		*version_directive_ref = version_directive
+	}
+	if tag_directives_ref != nil {
+		*tag_directives_ref = tag_directives
+	}
+	return true
+}
+
+// Append a tag directive to the directives stack.
+func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool {
+	for i := range parser.tag_directives {
+		if bytes.Equal(value.handle, parser.tag_directives[i].handle) {
+			if allow_duplicates {
+				return true
+			}
+			return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark)
+		}
+	}
+
+	// [Go] I suspect the copy is unnecessary. This was likely done
+	// because there was no way to track ownership of the data.
+	value_copy := yaml_tag_directive_t{
+		handle: make([]byte, len(value.handle)),
+		prefix: make([]byte, len(value.prefix)),
+	}
+	copy(value_copy.handle, value.handle)
+	copy(value_copy.prefix, value.prefix)
+	parser.tag_directives = append(parser.tag_directives, value_copy)
+	return true
+}

+ 394 - 0
_vendor/src/gopkg.in/yaml.v2/readerc.go

@@ -0,0 +1,394 @@
+package yaml
+
+import (
+	"io"
+)
+
+// Set the reader error and return 0.
+func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool {
+	parser.error = yaml_READER_ERROR
+	parser.problem = problem
+	parser.problem_offset = offset
+	parser.problem_value = value
+	return false
+}
+
+// Byte order marks.
+const (
+	bom_UTF8    = "\xef\xbb\xbf"
+	bom_UTF16LE = "\xff\xfe"
+	bom_UTF16BE = "\xfe\xff"
+)
+
+// Determine the input stream encoding by checking the BOM symbol. If no BOM is
+// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure.
+func yaml_parser_determine_encoding(parser *yaml_parser_t) bool {
+	// Ensure that we had enough bytes in the raw buffer.
+	for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 {
+		if !yaml_parser_update_raw_buffer(parser) {
+			return false
+		}
+	}
+
+	// Determine the encoding.
+	buf := parser.raw_buffer
+	pos := parser.raw_buffer_pos
+	avail := len(buf) - pos
+	if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] {
+		parser.encoding = yaml_UTF16LE_ENCODING
+		parser.raw_buffer_pos += 2
+		parser.offset += 2
+	} else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] {
+		parser.encoding = yaml_UTF16BE_ENCODING
+		parser.raw_buffer_pos += 2
+		parser.offset += 2
+	} else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] {
+		parser.encoding = yaml_UTF8_ENCODING
+		parser.raw_buffer_pos += 3
+		parser.offset += 3
+	} else {
+		parser.encoding = yaml_UTF8_ENCODING
+	}
+	return true
+}
+
+// Update the raw buffer.
+func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool {
+	size_read := 0
+
+	// Return if the raw buffer is full.
+	if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) {
+		return true
+	}
+
+	// Return on EOF.
+	if parser.eof {
+		return true
+	}
+
+	// Move the remaining bytes in the raw buffer to the beginning.
+	if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) {
+		copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:])
+	}
+	parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos]
+	parser.raw_buffer_pos = 0
+
+	// Call the read handler to fill the buffer.
+	size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)])
+	parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read]
+	if err == io.EOF {
+		parser.eof = true
+	} else if err != nil {
+		return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1)
+	}
+	return true
+}
+
+// Ensure that the buffer contains at least `length` characters.
+// Return true on success, false on failure.
+//
+// The length is supposed to be significantly less that the buffer size.
+func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool {
+	if parser.read_handler == nil {
+		panic("read handler must be set")
+	}
+
+	// If the EOF flag is set and the raw buffer is empty, do nothing.
+	if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) {
+		return true
+	}
+
+	// Return if the buffer contains enough characters.
+	if parser.unread >= length {
+		return true
+	}
+
+	// Determine the input encoding if it is not known yet.
+	if parser.encoding == yaml_ANY_ENCODING {
+		if !yaml_parser_determine_encoding(parser) {
+			return false
+		}
+	}
+
+	// Move the unread characters to the beginning of the buffer.
+	buffer_len := len(parser.buffer)
+	if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len {
+		copy(parser.buffer, parser.buffer[parser.buffer_pos:])
+		buffer_len -= parser.buffer_pos
+		parser.buffer_pos = 0
+	} else if parser.buffer_pos == buffer_len {
+		buffer_len = 0
+		parser.buffer_pos = 0
+	}
+
+	// Open the whole buffer for writing, and cut it before returning.
+	parser.buffer = parser.buffer[:cap(parser.buffer)]
+
+	// Fill the buffer until it has enough characters.
+	first := true
+	for parser.unread < length {
+
+		// Fill the raw buffer if necessary.
+		if !first || parser.raw_buffer_pos == len(parser.raw_buffer) {
+			if !yaml_parser_update_raw_buffer(parser) {
+				parser.buffer = parser.buffer[:buffer_len]
+				return false
+			}
+		}
+		first = false
+
+		// Decode the raw buffer.
+	inner:
+		for parser.raw_buffer_pos != len(parser.raw_buffer) {
+			var value rune
+			var width int
+
+			raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos
+
+			// Decode the next character.
+			switch parser.encoding {
+			case yaml_UTF8_ENCODING:
+				// Decode a UTF-8 character.  Check RFC 3629
+				// (http://www.ietf.org/rfc/rfc3629.txt) for more details.
+				//
+				// The following table (taken from the RFC) is used for
+				// decoding.
+				//
+				//    Char. number range |        UTF-8 octet sequence
+				//      (hexadecimal)    |              (binary)
+				//   --------------------+------------------------------------
+				//   0000 0000-0000 007F | 0xxxxxxx
+				//   0000 0080-0000 07FF | 110xxxxx 10xxxxxx
+				//   0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
+				//   0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+				//
+				// Additionally, the characters in the range 0xD800-0xDFFF
+				// are prohibited as they are reserved for use with UTF-16
+				// surrogate pairs.
+
+				// Determine the length of the UTF-8 sequence.
+				octet := parser.raw_buffer[parser.raw_buffer_pos]
+				switch {
+				case octet&0x80 == 0x00:
+					width = 1
+				case octet&0xE0 == 0xC0:
+					width = 2
+				case octet&0xF0 == 0xE0:
+					width = 3
+				case octet&0xF8 == 0xF0:
+					width = 4
+				default:
+					// The leading octet is invalid.
+					return yaml_parser_set_reader_error(parser,
+						"invalid leading UTF-8 octet",
+						parser.offset, int(octet))
+				}
+
+				// Check if the raw buffer contains an incomplete character.
+				if width > raw_unread {
+					if parser.eof {
+						return yaml_parser_set_reader_error(parser,
+							"incomplete UTF-8 octet sequence",
+							parser.offset, -1)
+					}
+					break inner
+				}
+
+				// Decode the leading octet.
+				switch {
+				case octet&0x80 == 0x00:
+					value = rune(octet & 0x7F)
+				case octet&0xE0 == 0xC0:
+					value = rune(octet & 0x1F)
+				case octet&0xF0 == 0xE0:
+					value = rune(octet & 0x0F)
+				case octet&0xF8 == 0xF0:
+					value = rune(octet & 0x07)
+				default:
+					value = 0
+				}
+
+				// Check and decode the trailing octets.
+				for k := 1; k < width; k++ {
+					octet = parser.raw_buffer[parser.raw_buffer_pos+k]
+
+					// Check if the octet is valid.
+					if (octet & 0xC0) != 0x80 {
+						return yaml_parser_set_reader_error(parser,
+							"invalid trailing UTF-8 octet",
+							parser.offset+k, int(octet))
+					}
+
+					// Decode the octet.
+					value = (value << 6) + rune(octet&0x3F)
+				}
+
+				// Check the length of the sequence against the value.
+				switch {
+				case width == 1:
+				case width == 2 && value >= 0x80:
+				case width == 3 && value >= 0x800:
+				case width == 4 && value >= 0x10000:
+				default:
+					return yaml_parser_set_reader_error(parser,
+						"invalid length of a UTF-8 sequence",
+						parser.offset, -1)
+				}
+
+				// Check the range of the value.
+				if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF {
+					return yaml_parser_set_reader_error(parser,
+						"invalid Unicode character",
+						parser.offset, int(value))
+				}
+
+			case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING:
+				var low, high int
+				if parser.encoding == yaml_UTF16LE_ENCODING {
+					low, high = 0, 1
+				} else {
+					low, high = 1, 0
+				}
+
+				// The UTF-16 encoding is not as simple as one might
+				// naively think.  Check RFC 2781
+				// (http://www.ietf.org/rfc/rfc2781.txt).
+				//
+				// Normally, two subsequent bytes describe a Unicode
+				// character.  However a special technique (called a
+				// surrogate pair) is used for specifying character
+				// values larger than 0xFFFF.
+				//
+				// A surrogate pair consists of two pseudo-characters:
+				//      high surrogate area (0xD800-0xDBFF)
+				//      low surrogate area (0xDC00-0xDFFF)
+				//
+				// The following formulas are used for decoding
+				// and encoding characters using surrogate pairs:
+				//
+				//  U  = U' + 0x10000   (0x01 00 00 <= U <= 0x10 FF FF)
+				//  U' = yyyyyyyyyyxxxxxxxxxx   (0 <= U' <= 0x0F FF FF)
+				//  W1 = 110110yyyyyyyyyy
+				//  W2 = 110111xxxxxxxxxx
+				//
+				// where U is the character value, W1 is the high surrogate
+				// area, W2 is the low surrogate area.
+
+				// Check for incomplete UTF-16 character.
+				if raw_unread < 2 {
+					if parser.eof {
+						return yaml_parser_set_reader_error(parser,
+							"incomplete UTF-16 character",
+							parser.offset, -1)
+					}
+					break inner
+				}
+
+				// Get the character.
+				value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) +
+					(rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8)
+
+				// Check for unexpected low surrogate area.
+				if value&0xFC00 == 0xDC00 {
+					return yaml_parser_set_reader_error(parser,
+						"unexpected low surrogate area",
+						parser.offset, int(value))
+				}
+
+				// Check for a high surrogate area.
+				if value&0xFC00 == 0xD800 {
+					width = 4
+
+					// Check for incomplete surrogate pair.
+					if raw_unread < 4 {
+						if parser.eof {
+							return yaml_parser_set_reader_error(parser,
+								"incomplete UTF-16 surrogate pair",
+								parser.offset, -1)
+						}
+						break inner
+					}
+
+					// Get the next character.
+					value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) +
+						(rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8)
+
+					// Check for a low surrogate area.
+					if value2&0xFC00 != 0xDC00 {
+						return yaml_parser_set_reader_error(parser,
+							"expected low surrogate area",
+							parser.offset+2, int(value2))
+					}
+
+					// Generate the value of the surrogate pair.
+					value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF)
+				} else {
+					width = 2
+				}
+
+			default:
+				panic("impossible")
+			}
+
+			// Check if the character is in the allowed range:
+			//      #x9 | #xA | #xD | [#x20-#x7E]               (8 bit)
+			//      | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD]    (16 bit)
+			//      | [#x10000-#x10FFFF]                        (32 bit)
+			switch {
+			case value == 0x09:
+			case value == 0x0A:
+			case value == 0x0D:
+			case value >= 0x20 && value <= 0x7E:
+			case value == 0x85:
+			case value >= 0xA0 && value <= 0xD7FF:
+			case value >= 0xE000 && value <= 0xFFFD:
+			case value >= 0x10000 && value <= 0x10FFFF:
+			default:
+				return yaml_parser_set_reader_error(parser,
+					"control characters are not allowed",
+					parser.offset, int(value))
+			}
+
+			// Move the raw pointers.
+			parser.raw_buffer_pos += width
+			parser.offset += width
+
+			// Finally put the character into the buffer.
+			if value <= 0x7F {
+				// 0000 0000-0000 007F . 0xxxxxxx
+				parser.buffer[buffer_len+0] = byte(value)
+				buffer_len += 1
+			} else if value <= 0x7FF {
+				// 0000 0080-0000 07FF . 110xxxxx 10xxxxxx
+				parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6))
+				parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F))
+				buffer_len += 2
+			} else if value <= 0xFFFF {
+				// 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx
+				parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12))
+				parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F))
+				parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F))
+				buffer_len += 3
+			} else {
+				// 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+				parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18))
+				parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F))
+				parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F))
+				parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F))
+				buffer_len += 4
+			}
+
+			parser.unread++
+		}
+
+		// On EOF, put NUL into the buffer and return.
+		if parser.eof {
+			parser.buffer[buffer_len] = 0
+			buffer_len++
+			parser.unread++
+			break
+		}
+	}
+	parser.buffer = parser.buffer[:buffer_len]
+	return true
+}

+ 208 - 0
_vendor/src/gopkg.in/yaml.v2/resolve.go

@@ -0,0 +1,208 @@
+package yaml
+
+import (
+	"encoding/base64"
+	"math"
+	"regexp"
+	"strconv"
+	"strings"
+	"unicode/utf8"
+)
+
+type resolveMapItem struct {
+	value interface{}
+	tag   string
+}
+
+var resolveTable = make([]byte, 256)
+var resolveMap = make(map[string]resolveMapItem)
+
+func init() {
+	t := resolveTable
+	t[int('+')] = 'S' // Sign
+	t[int('-')] = 'S'
+	for _, c := range "0123456789" {
+		t[int(c)] = 'D' // Digit
+	}
+	for _, c := range "yYnNtTfFoO~" {
+		t[int(c)] = 'M' // In map
+	}
+	t[int('.')] = '.' // Float (potentially in map)
+
+	var resolveMapList = []struct {
+		v   interface{}
+		tag string
+		l   []string
+	}{
+		{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
+		{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
+		{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
+		{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
+		{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
+		{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
+		{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
+		{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
+		{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
+		{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
+		{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
+		{"<<", yaml_MERGE_TAG, []string{"<<"}},
+	}
+
+	m := resolveMap
+	for _, item := range resolveMapList {
+		for _, s := range item.l {
+			m[s] = resolveMapItem{item.v, item.tag}
+		}
+	}
+}
+
+const longTagPrefix = "tag:yaml.org,2002:"
+
+func shortTag(tag string) string {
+	// TODO This can easily be made faster and produce less garbage.
+	if strings.HasPrefix(tag, longTagPrefix) {
+		return "!!" + tag[len(longTagPrefix):]
+	}
+	return tag
+}
+
+func longTag(tag string) string {
+	if strings.HasPrefix(tag, "!!") {
+		return longTagPrefix + tag[2:]
+	}
+	return tag
+}
+
+func resolvableTag(tag string) bool {
+	switch tag {
+	case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG:
+		return true
+	}
+	return false
+}
+
+var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`)
+
+func resolve(tag string, in string) (rtag string, out interface{}) {
+	if !resolvableTag(tag) {
+		return tag, in
+	}
+
+	defer func() {
+		switch tag {
+		case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
+			return
+		}
+		failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
+	}()
+
+	// Any data is accepted as a !!str or !!binary.
+	// Otherwise, the prefix is enough of a hint about what it might be.
+	hint := byte('N')
+	if in != "" {
+		hint = resolveTable[in[0]]
+	}
+	if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
+		// Handle things we can lookup in a map.
+		if item, ok := resolveMap[in]; ok {
+			return item.tag, item.value
+		}
+
+		// Base 60 floats are a bad idea, were dropped in YAML 1.2, and
+		// are purposefully unsupported here. They're still quoted on
+		// the way out for compatibility with other parser, though.
+
+		switch hint {
+		case 'M':
+			// We've already checked the map above.
+
+		case '.':
+			// Not in the map, so maybe a normal float.
+			floatv, err := strconv.ParseFloat(in, 64)
+			if err == nil {
+				return yaml_FLOAT_TAG, floatv
+			}
+
+		case 'D', 'S':
+			// Int, float, or timestamp.
+			plain := strings.Replace(in, "_", "", -1)
+			intv, err := strconv.ParseInt(plain, 0, 64)
+			if err == nil {
+				if intv == int64(int(intv)) {
+					return yaml_INT_TAG, int(intv)
+				} else {
+					return yaml_INT_TAG, intv
+				}
+			}
+			uintv, err := strconv.ParseUint(plain, 0, 64)
+			if err == nil {
+				return yaml_INT_TAG, uintv
+			}
+			if yamlStyleFloat.MatchString(plain) {
+				floatv, err := strconv.ParseFloat(plain, 64)
+				if err == nil {
+					return yaml_FLOAT_TAG, floatv
+				}
+			}
+			if strings.HasPrefix(plain, "0b") {
+				intv, err := strconv.ParseInt(plain[2:], 2, 64)
+				if err == nil {
+					if intv == int64(int(intv)) {
+						return yaml_INT_TAG, int(intv)
+					} else {
+						return yaml_INT_TAG, intv
+					}
+				}
+				uintv, err := strconv.ParseUint(plain[2:], 2, 64)
+				if err == nil {
+					return yaml_INT_TAG, uintv
+				}
+			} else if strings.HasPrefix(plain, "-0b") {
+				intv, err := strconv.ParseInt(plain[3:], 2, 64)
+				if err == nil {
+					if intv == int64(int(intv)) {
+						return yaml_INT_TAG, -int(intv)
+					} else {
+						return yaml_INT_TAG, -intv
+					}
+				}
+			}
+			// XXX Handle timestamps here.
+
+		default:
+			panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
+		}
+	}
+	if tag == yaml_BINARY_TAG {
+		return yaml_BINARY_TAG, in
+	}
+	if utf8.ValidString(in) {
+		return yaml_STR_TAG, in
+	}
+	return yaml_BINARY_TAG, encodeBase64(in)
+}
+
+// encodeBase64 encodes s as base64 that is broken up into multiple lines
+// as appropriate for the resulting length.
+func encodeBase64(s string) string {
+	const lineLen = 70
+	encLen := base64.StdEncoding.EncodedLen(len(s))
+	lines := encLen/lineLen + 1
+	buf := make([]byte, encLen*2+lines)
+	in := buf[0:encLen]
+	out := buf[encLen:]
+	base64.StdEncoding.Encode(in, []byte(s))
+	k := 0
+	for i := 0; i < len(in); i += lineLen {
+		j := i + lineLen
+		if j > len(in) {
+			j = len(in)
+		}
+		k += copy(out[k:], in[i:j])
+		if lines > 1 {
+			out[k] = '\n'
+			k++
+		}
+	}
+	return string(out[:k])
+}

+ 2710 - 0
_vendor/src/gopkg.in/yaml.v2/scannerc.go

@@ -0,0 +1,2710 @@
+package yaml
+
+import (
+	"bytes"
+	"fmt"
+)
+
+// Introduction
+// ************
+//
+// The following notes assume that you are familiar with the YAML specification
+// (http://yaml.org/spec/1.2/spec.html).  We mostly follow it, although in
+// some cases we are less restrictive that it requires.
+//
+// The process of transforming a YAML stream into a sequence of events is
+// divided on two steps: Scanning and Parsing.
+//
+// The Scanner transforms the input stream into a sequence of tokens, while the
+// parser transform the sequence of tokens produced by the Scanner into a
+// sequence of parsing events.
+//
+// The Scanner is rather clever and complicated. The Parser, on the contrary,
+// is a straightforward implementation of a recursive-descendant parser (or,
+// LL(1) parser, as it is usually called).
+//
+// Actually there are two issues of Scanning that might be called "clever", the
+// rest is quite straightforward.  The issues are "block collection start" and
+// "simple keys".  Both issues are explained below in details.
+//
+// Here the Scanning step is explained and implemented.  We start with the list
+// of all the tokens produced by the Scanner together with short descriptions.
+//
+// Now, tokens:
+//
+//      STREAM-START(encoding)          # The stream start.
+//      STREAM-END                      # The stream end.
+//      VERSION-DIRECTIVE(major,minor)  # The '%YAML' directive.
+//      TAG-DIRECTIVE(handle,prefix)    # The '%TAG' directive.
+//      DOCUMENT-START                  # '---'
+//      DOCUMENT-END                    # '...'
+//      BLOCK-SEQUENCE-START            # Indentation increase denoting a block
+//      BLOCK-MAPPING-START             # sequence or a block mapping.
+//      BLOCK-END                       # Indentation decrease.
+//      FLOW-SEQUENCE-START             # '['
+//      FLOW-SEQUENCE-END               # ']'
+//      BLOCK-SEQUENCE-START            # '{'
+//      BLOCK-SEQUENCE-END              # '}'
+//      BLOCK-ENTRY                     # '-'
+//      FLOW-ENTRY                      # ','
+//      KEY                             # '?' or nothing (simple keys).
+//      VALUE                           # ':'
+//      ALIAS(anchor)                   # '*anchor'
+//      ANCHOR(anchor)                  # '&anchor'
+//      TAG(handle,suffix)              # '!handle!suffix'
+//      SCALAR(value,style)             # A scalar.
+//
+// The following two tokens are "virtual" tokens denoting the beginning and the
+// end of the stream:
+//
+//      STREAM-START(encoding)
+//      STREAM-END
+//
+// We pass the information about the input stream encoding with the
+// STREAM-START token.
+//
+// The next two tokens are responsible for tags:
+//
+//      VERSION-DIRECTIVE(major,minor)
+//      TAG-DIRECTIVE(handle,prefix)
+//
+// Example:
+//
+//      %YAML   1.1
+//      %TAG    !   !foo
+//      %TAG    !yaml!  tag:yaml.org,2002:
+//      ---
+//
+// The correspoding sequence of tokens:
+//
+//      STREAM-START(utf-8)
+//      VERSION-DIRECTIVE(1,1)
+//      TAG-DIRECTIVE("!","!foo")
+//      TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:")
+//      DOCUMENT-START
+//      STREAM-END
+//
+// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole
+// line.
+//
+// The document start and end indicators are represented by:
+//
+//      DOCUMENT-START
+//      DOCUMENT-END
+//
+// Note that if a YAML stream contains an implicit document (without '---'
+// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be
+// produced.
+//
+// In the following examples, we present whole documents together with the
+// produced tokens.
+//
+//      1. An implicit document:
+//
+//          'a scalar'
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          SCALAR("a scalar",single-quoted)
+//          STREAM-END
+//
+//      2. An explicit document:
+//
+//          ---
+//          'a scalar'
+//          ...
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          DOCUMENT-START
+//          SCALAR("a scalar",single-quoted)
+//          DOCUMENT-END
+//          STREAM-END
+//
+//      3. Several documents in a stream:
+//
+//          'a scalar'
+//          ---
+//          'another scalar'
+//          ---
+//          'yet another scalar'
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          SCALAR("a scalar",single-quoted)
+//          DOCUMENT-START
+//          SCALAR("another scalar",single-quoted)
+//          DOCUMENT-START
+//          SCALAR("yet another scalar",single-quoted)
+//          STREAM-END
+//
+// We have already introduced the SCALAR token above.  The following tokens are
+// used to describe aliases, anchors, tag, and scalars:
+//
+//      ALIAS(anchor)
+//      ANCHOR(anchor)
+//      TAG(handle,suffix)
+//      SCALAR(value,style)
+//
+// The following series of examples illustrate the usage of these tokens:
+//
+//      1. A recursive sequence:
+//
+//          &A [ *A ]
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          ANCHOR("A")
+//          FLOW-SEQUENCE-START
+//          ALIAS("A")
+//          FLOW-SEQUENCE-END
+//          STREAM-END
+//
+//      2. A tagged scalar:
+//
+//          !!float "3.14"  # A good approximation.
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          TAG("!!","float")
+//          SCALAR("3.14",double-quoted)
+//          STREAM-END
+//
+//      3. Various scalar styles:
+//
+//          --- # Implicit empty plain scalars do not produce tokens.
+//          --- a plain scalar
+//          --- 'a single-quoted scalar'
+//          --- "a double-quoted scalar"
+//          --- |-
+//            a literal scalar
+//          --- >-
+//            a folded
+//            scalar
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          DOCUMENT-START
+//          DOCUMENT-START
+//          SCALAR("a plain scalar",plain)
+//          DOCUMENT-START
+//          SCALAR("a single-quoted scalar",single-quoted)
+//          DOCUMENT-START
+//          SCALAR("a double-quoted scalar",double-quoted)
+//          DOCUMENT-START
+//          SCALAR("a literal scalar",literal)
+//          DOCUMENT-START
+//          SCALAR("a folded scalar",folded)
+//          STREAM-END
+//
+// Now it's time to review collection-related tokens. We will start with
+// flow collections:
+//
+//      FLOW-SEQUENCE-START
+//      FLOW-SEQUENCE-END
+//      FLOW-MAPPING-START
+//      FLOW-MAPPING-END
+//      FLOW-ENTRY
+//      KEY
+//      VALUE
+//
+// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and
+// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}'
+// correspondingly.  FLOW-ENTRY represent the ',' indicator.  Finally the
+// indicators '?' and ':', which are used for denoting mapping keys and values,
+// are represented by the KEY and VALUE tokens.
+//
+// The following examples show flow collections:
+//
+//      1. A flow sequence:
+//
+//          [item 1, item 2, item 3]
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          FLOW-SEQUENCE-START
+//          SCALAR("item 1",plain)
+//          FLOW-ENTRY
+//          SCALAR("item 2",plain)
+//          FLOW-ENTRY
+//          SCALAR("item 3",plain)
+//          FLOW-SEQUENCE-END
+//          STREAM-END
+//
+//      2. A flow mapping:
+//
+//          {
+//              a simple key: a value,  # Note that the KEY token is produced.
+//              ? a complex key: another value,
+//          }
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          FLOW-MAPPING-START
+//          KEY
+//          SCALAR("a simple key",plain)
+//          VALUE
+//          SCALAR("a value",plain)
+//          FLOW-ENTRY
+//          KEY
+//          SCALAR("a complex key",plain)
+//          VALUE
+//          SCALAR("another value",plain)
+//          FLOW-ENTRY
+//          FLOW-MAPPING-END
+//          STREAM-END
+//
+// A simple key is a key which is not denoted by the '?' indicator.  Note that
+// the Scanner still produce the KEY token whenever it encounters a simple key.
+//
+// For scanning block collections, the following tokens are used (note that we
+// repeat KEY and VALUE here):
+//
+//      BLOCK-SEQUENCE-START
+//      BLOCK-MAPPING-START
+//      BLOCK-END
+//      BLOCK-ENTRY
+//      KEY
+//      VALUE
+//
+// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation
+// increase that precedes a block collection (cf. the INDENT token in Python).
+// The token BLOCK-END denote indentation decrease that ends a block collection
+// (cf. the DEDENT token in Python).  However YAML has some syntax pecularities
+// that makes detections of these tokens more complex.
+//
+// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators
+// '-', '?', and ':' correspondingly.
+//
+// The following examples show how the tokens BLOCK-SEQUENCE-START,
+// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner:
+//
+//      1. Block sequences:
+//
+//          - item 1
+//          - item 2
+//          -
+//            - item 3.1
+//            - item 3.2
+//          -
+//            key 1: value 1
+//            key 2: value 2
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          BLOCK-SEQUENCE-START
+//          BLOCK-ENTRY
+//          SCALAR("item 1",plain)
+//          BLOCK-ENTRY
+//          SCALAR("item 2",plain)
+//          BLOCK-ENTRY
+//          BLOCK-SEQUENCE-START
+//          BLOCK-ENTRY
+//          SCALAR("item 3.1",plain)
+//          BLOCK-ENTRY
+//          SCALAR("item 3.2",plain)
+//          BLOCK-END
+//          BLOCK-ENTRY
+//          BLOCK-MAPPING-START
+//          KEY
+//          SCALAR("key 1",plain)
+//          VALUE
+//          SCALAR("value 1",plain)
+//          KEY
+//          SCALAR("key 2",plain)
+//          VALUE
+//          SCALAR("value 2",plain)
+//          BLOCK-END
+//          BLOCK-END
+//          STREAM-END
+//
+//      2. Block mappings:
+//
+//          a simple key: a value   # The KEY token is produced here.
+//          ? a complex key
+//          : another value
+//          a mapping:
+//            key 1: value 1
+//            key 2: value 2
+//          a sequence:
+//            - item 1
+//            - item 2
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          BLOCK-MAPPING-START
+//          KEY
+//          SCALAR("a simple key",plain)
+//          VALUE
+//          SCALAR("a value",plain)
+//          KEY
+//          SCALAR("a complex key",plain)
+//          VALUE
+//          SCALAR("another value",plain)
+//          KEY
+//          SCALAR("a mapping",plain)
+//          BLOCK-MAPPING-START
+//          KEY
+//          SCALAR("key 1",plain)
+//          VALUE
+//          SCALAR("value 1",plain)
+//          KEY
+//          SCALAR("key 2",plain)
+//          VALUE
+//          SCALAR("value 2",plain)
+//          BLOCK-END
+//          KEY
+//          SCALAR("a sequence",plain)
+//          VALUE
+//          BLOCK-SEQUENCE-START
+//          BLOCK-ENTRY
+//          SCALAR("item 1",plain)
+//          BLOCK-ENTRY
+//          SCALAR("item 2",plain)
+//          BLOCK-END
+//          BLOCK-END
+//          STREAM-END
+//
+// YAML does not always require to start a new block collection from a new
+// line.  If the current line contains only '-', '?', and ':' indicators, a new
+// block collection may start at the current line.  The following examples
+// illustrate this case:
+//
+//      1. Collections in a sequence:
+//
+//          - - item 1
+//            - item 2
+//          - key 1: value 1
+//            key 2: value 2
+//          - ? complex key
+//            : complex value
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          BLOCK-SEQUENCE-START
+//          BLOCK-ENTRY
+//          BLOCK-SEQUENCE-START
+//          BLOCK-ENTRY
+//          SCALAR("item 1",plain)
+//          BLOCK-ENTRY
+//          SCALAR("item 2",plain)
+//          BLOCK-END
+//          BLOCK-ENTRY
+//          BLOCK-MAPPING-START
+//          KEY
+//          SCALAR("key 1",plain)
+//          VALUE
+//          SCALAR("value 1",plain)
+//          KEY
+//          SCALAR("key 2",plain)
+//          VALUE
+//          SCALAR("value 2",plain)
+//          BLOCK-END
+//          BLOCK-ENTRY
+//          BLOCK-MAPPING-START
+//          KEY
+//          SCALAR("complex key")
+//          VALUE
+//          SCALAR("complex value")
+//          BLOCK-END
+//          BLOCK-END
+//          STREAM-END
+//
+//      2. Collections in a mapping:
+//
+//          ? a sequence
+//          : - item 1
+//            - item 2
+//          ? a mapping
+//          : key 1: value 1
+//            key 2: value 2
+//
+//      Tokens:
+//
+//          STREAM-START(utf-8)
+//          BLOCK-MAPPING-START
+//          KEY
+//          SCALAR("a sequence",plain)
+//          VALUE
+//          BLOCK-SEQUENCE-START
+//          BLOCK-ENTRY
+//          SCALAR("item 1",plain)
+//          BLOCK-ENTRY
+//          SCALAR("item 2",plain)
+//          BLOCK-END
+//          KEY
+//          SCALAR("a mapping",plain)
+//          VALUE
+//          BLOCK-MAPPING-START
+//          KEY
+//          SCALAR("key 1",plain)
+//          VALUE
+//          SCALAR("value 1",plain)
+//          KEY
+//          SCALAR("key 2",plain)
+//          VALUE
+//          SCALAR("value 2",plain)
+//          BLOCK-END
+//          BLOCK-END
+//          STREAM-END
+//
+// YAML also permits non-indented sequences if they are included into a block
+// mapping.  In this case, the token BLOCK-SEQUENCE-START is not produced:
+//
+//      key:
+//      - item 1    # BLOCK-SEQUENCE-START is NOT produced here.
+//      - item 2
+//
+// Tokens:
+//
+//      STREAM-START(utf-8)
+//      BLOCK-MAPPING-START
+//      KEY
+//      SCALAR("key",plain)
+//      VALUE
+//      BLOCK-ENTRY
+//      SCALAR("item 1",plain)
+//      BLOCK-ENTRY
+//      SCALAR("item 2",plain)
+//      BLOCK-END
+//
+
+// Ensure that the buffer contains the required number of characters.
+// Return true on success, false on failure (reader error or memory error).
+func cache(parser *yaml_parser_t, length int) bool {
+	// [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B)
+	return parser.unread >= length || yaml_parser_update_buffer(parser, length)
+}
+
+// Advance the buffer pointer.
+func skip(parser *yaml_parser_t) {
+	parser.mark.index++
+	parser.mark.column++
+	parser.unread--
+	parser.buffer_pos += width(parser.buffer[parser.buffer_pos])
+}
+
+func skip_line(parser *yaml_parser_t) {
+	if is_crlf(parser.buffer, parser.buffer_pos) {
+		parser.mark.index += 2
+		parser.mark.column = 0
+		parser.mark.line++
+		parser.unread -= 2
+		parser.buffer_pos += 2
+	} else if is_break(parser.buffer, parser.buffer_pos) {
+		parser.mark.index++
+		parser.mark.column = 0
+		parser.mark.line++
+		parser.unread--
+		parser.buffer_pos += width(parser.buffer[parser.buffer_pos])
+	}
+}
+
+// Copy a character to a string buffer and advance pointers.
+func read(parser *yaml_parser_t, s []byte) []byte {
+	w := width(parser.buffer[parser.buffer_pos])
+	if w == 0 {
+		panic("invalid character sequence")
+	}
+	if len(s) == 0 {
+		s = make([]byte, 0, 32)
+	}
+	if w == 1 && len(s)+w <= cap(s) {
+		s = s[:len(s)+1]
+		s[len(s)-1] = parser.buffer[parser.buffer_pos]
+		parser.buffer_pos++
+	} else {
+		s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...)
+		parser.buffer_pos += w
+	}
+	parser.mark.index++
+	parser.mark.column++
+	parser.unread--
+	return s
+}
+
+// Copy a line break character to a string buffer and advance pointers.
+func read_line(parser *yaml_parser_t, s []byte) []byte {
+	buf := parser.buffer
+	pos := parser.buffer_pos
+	switch {
+	case buf[pos] == '\r' && buf[pos+1] == '\n':
+		// CR LF . LF
+		s = append(s, '\n')
+		parser.buffer_pos += 2
+		parser.mark.index++
+		parser.unread--
+	case buf[pos] == '\r' || buf[pos] == '\n':
+		// CR|LF . LF
+		s = append(s, '\n')
+		parser.buffer_pos += 1
+	case buf[pos] == '\xC2' && buf[pos+1] == '\x85':
+		// NEL . LF
+		s = append(s, '\n')
+		parser.buffer_pos += 2
+	case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'):
+		// LS|PS . LS|PS
+		s = append(s, buf[parser.buffer_pos:pos+3]...)
+		parser.buffer_pos += 3
+	default:
+		return s
+	}
+	parser.mark.index++
+	parser.mark.column = 0
+	parser.mark.line++
+	parser.unread--
+	return s
+}
+
+// Get the next token.
+func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool {
+	// Erase the token object.
+	*token = yaml_token_t{} // [Go] Is this necessary?
+
+	// No tokens after STREAM-END or error.
+	if parser.stream_end_produced || parser.error != yaml_NO_ERROR {
+		return true
+	}
+
+	// Ensure that the tokens queue contains enough tokens.
+	if !parser.token_available {
+		if !yaml_parser_fetch_more_tokens(parser) {
+			return false
+		}
+	}
+
+	// Fetch the next token from the queue.
+	*token = parser.tokens[parser.tokens_head]
+	parser.tokens_head++
+	parser.tokens_parsed++
+	parser.token_available = false
+
+	if token.typ == yaml_STREAM_END_TOKEN {
+		parser.stream_end_produced = true
+	}
+	return true
+}
+
+// Set the scanner error and return false.
+func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool {
+	parser.error = yaml_SCANNER_ERROR
+	parser.context = context
+	parser.context_mark = context_mark
+	parser.problem = problem
+	parser.problem_mark = parser.mark
+	return false
+}
+
+func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool {
+	context := "while parsing a tag"
+	if directive {
+		context = "while parsing a %TAG directive"
+	}
+	return yaml_parser_set_scanner_error(parser, context, context_mark, "did not find URI escaped octet")
+}
+
+func trace(args ...interface{}) func() {
+	pargs := append([]interface{}{"+++"}, args...)
+	fmt.Println(pargs...)
+	pargs = append([]interface{}{"---"}, args...)
+	return func() { fmt.Println(pargs...) }
+}
+
+// Ensure that the tokens queue contains at least one token which can be
+// returned to the Parser.
+func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
+	// While we need more tokens to fetch, do it.
+	for {
+		// Check if we really need to fetch more tokens.
+		need_more_tokens := false
+
+		if parser.tokens_head == len(parser.tokens) {
+			// Queue is empty.
+			need_more_tokens = true
+		} else {
+			// Check if any potential simple key may occupy the head position.
+			if !yaml_parser_stale_simple_keys(parser) {
+				return false
+			}
+
+			for i := range parser.simple_keys {
+				simple_key := &parser.simple_keys[i]
+				if simple_key.possible && simple_key.token_number == parser.tokens_parsed {
+					need_more_tokens = true
+					break
+				}
+			}
+		}
+
+		// We are finished.
+		if !need_more_tokens {
+			break
+		}
+		// Fetch the next token.
+		if !yaml_parser_fetch_next_token(parser) {
+			return false
+		}
+	}
+
+	parser.token_available = true
+	return true
+}
+
+// The dispatcher for token fetchers.
+func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
+	// Ensure that the buffer is initialized.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+
+	// Check if we just started scanning.  Fetch STREAM-START then.
+	if !parser.stream_start_produced {
+		return yaml_parser_fetch_stream_start(parser)
+	}
+
+	// Eat whitespaces and comments until we reach the next token.
+	if !yaml_parser_scan_to_next_token(parser) {
+		return false
+	}
+
+	// Remove obsolete potential simple keys.
+	if !yaml_parser_stale_simple_keys(parser) {
+		return false
+	}
+
+	// Check the indentation level against the current column.
+	if !yaml_parser_unroll_indent(parser, parser.mark.column) {
+		return false
+	}
+
+	// Ensure that the buffer contains at least 4 characters.  4 is the length
+	// of the longest indicators ('--- ' and '... ').
+	if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) {
+		return false
+	}
+
+	// Is it the end of the stream?
+	if is_z(parser.buffer, parser.buffer_pos) {
+		return yaml_parser_fetch_stream_end(parser)
+	}
+
+	// Is it a directive?
+	if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' {
+		return yaml_parser_fetch_directive(parser)
+	}
+
+	buf := parser.buffer
+	pos := parser.buffer_pos
+
+	// Is it the document start indicator?
+	if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) {
+		return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN)
+	}
+
+	// Is it the document end indicator?
+	if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) {
+		return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN)
+	}
+
+	// Is it the flow sequence start indicator?
+	if buf[pos] == '[' {
+		return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN)
+	}
+
+	// Is it the flow mapping start indicator?
+	if parser.buffer[parser.buffer_pos] == '{' {
+		return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN)
+	}
+
+	// Is it the flow sequence end indicator?
+	if parser.buffer[parser.buffer_pos] == ']' {
+		return yaml_parser_fetch_flow_collection_end(parser,
+			yaml_FLOW_SEQUENCE_END_TOKEN)
+	}
+
+	// Is it the flow mapping end indicator?
+	if parser.buffer[parser.buffer_pos] == '}' {
+		return yaml_parser_fetch_flow_collection_end(parser,
+			yaml_FLOW_MAPPING_END_TOKEN)
+	}
+
+	// Is it the flow entry indicator?
+	if parser.buffer[parser.buffer_pos] == ',' {
+		return yaml_parser_fetch_flow_entry(parser)
+	}
+
+	// Is it the block entry indicator?
+	if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) {
+		return yaml_parser_fetch_block_entry(parser)
+	}
+
+	// Is it the key indicator?
+	if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) {
+		return yaml_parser_fetch_key(parser)
+	}
+
+	// Is it the value indicator?
+	if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) {
+		return yaml_parser_fetch_value(parser)
+	}
+
+	// Is it an alias?
+	if parser.buffer[parser.buffer_pos] == '*' {
+		return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN)
+	}
+
+	// Is it an anchor?
+	if parser.buffer[parser.buffer_pos] == '&' {
+		return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN)
+	}
+
+	// Is it a tag?
+	if parser.buffer[parser.buffer_pos] == '!' {
+		return yaml_parser_fetch_tag(parser)
+	}
+
+	// Is it a literal scalar?
+	if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 {
+		return yaml_parser_fetch_block_scalar(parser, true)
+	}
+
+	// Is it a folded scalar?
+	if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 {
+		return yaml_parser_fetch_block_scalar(parser, false)
+	}
+
+	// Is it a single-quoted scalar?
+	if parser.buffer[parser.buffer_pos] == '\'' {
+		return yaml_parser_fetch_flow_scalar(parser, true)
+	}
+
+	// Is it a double-quoted scalar?
+	if parser.buffer[parser.buffer_pos] == '"' {
+		return yaml_parser_fetch_flow_scalar(parser, false)
+	}
+
+	// Is it a plain scalar?
+	//
+	// A plain scalar may start with any non-blank characters except
+	//
+	//      '-', '?', ':', ',', '[', ']', '{', '}',
+	//      '#', '&', '*', '!', '|', '>', '\'', '\"',
+	//      '%', '@', '`'.
+	//
+	// In the block context (and, for the '-' indicator, in the flow context
+	// too), it may also start with the characters
+	//
+	//      '-', '?', ':'
+	//
+	// if it is followed by a non-space character.
+	//
+	// The last rule is more restrictive than the specification requires.
+	// [Go] Make this logic more reasonable.
+	//switch parser.buffer[parser.buffer_pos] {
+	//case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`':
+	//}
+	if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' ||
+		parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' ||
+		parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' ||
+		parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' ||
+		parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' ||
+		parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' ||
+		parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' ||
+		parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' ||
+		parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' ||
+		parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') ||
+		(parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) ||
+		(parser.flow_level == 0 &&
+			(parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') &&
+			!is_blankz(parser.buffer, parser.buffer_pos+1)) {
+		return yaml_parser_fetch_plain_scalar(parser)
+	}
+
+	// If we don't determine the token type so far, it is an error.
+	return yaml_parser_set_scanner_error(parser,
+		"while scanning for the next token", parser.mark,
+		"found character that cannot start any token")
+}
+
+// Check the list of potential simple keys and remove the positions that
+// cannot contain simple keys anymore.
+func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool {
+	// Check for a potential simple key for each flow level.
+	for i := range parser.simple_keys {
+		simple_key := &parser.simple_keys[i]
+
+		// The specification requires that a simple key
+		//
+		//  - is limited to a single line,
+		//  - is shorter than 1024 characters.
+		if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) {
+
+			// Check if the potential simple key to be removed is required.
+			if simple_key.required {
+				return yaml_parser_set_scanner_error(parser,
+					"while scanning a simple key", simple_key.mark,
+					"could not find expected ':'")
+			}
+			simple_key.possible = false
+		}
+	}
+	return true
+}
+
+// Check if a simple key may start at the current position and add it if
+// needed.
+func yaml_parser_save_simple_key(parser *yaml_parser_t) bool {
+	// A simple key is required at the current position if the scanner is in
+	// the block context and the current column coincides with the indentation
+	// level.
+
+	required := parser.flow_level == 0 && parser.indent == parser.mark.column
+
+	// A simple key is required only when it is the first token in the current
+	// line.  Therefore it is always allowed.  But we add a check anyway.
+	if required && !parser.simple_key_allowed {
+		panic("should not happen")
+	}
+
+	//
+	// If the current position may start a simple key, save it.
+	//
+	if parser.simple_key_allowed {
+		simple_key := yaml_simple_key_t{
+			possible:     true,
+			required:     required,
+			token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head),
+		}
+		simple_key.mark = parser.mark
+
+		if !yaml_parser_remove_simple_key(parser) {
+			return false
+		}
+		parser.simple_keys[len(parser.simple_keys)-1] = simple_key
+	}
+	return true
+}
+
+// Remove a potential simple key at the current flow level.
+func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool {
+	i := len(parser.simple_keys) - 1
+	if parser.simple_keys[i].possible {
+		// If the key is required, it is an error.
+		if parser.simple_keys[i].required {
+			return yaml_parser_set_scanner_error(parser,
+				"while scanning a simple key", parser.simple_keys[i].mark,
+				"could not find expected ':'")
+		}
+	}
+	// Remove the key from the stack.
+	parser.simple_keys[i].possible = false
+	return true
+}
+
+// Increase the flow level and resize the simple key list if needed.
+func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool {
+	// Reset the simple key on the next level.
+	parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{})
+
+	// Increase the flow level.
+	parser.flow_level++
+	return true
+}
+
+// Decrease the flow level.
+func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool {
+	if parser.flow_level > 0 {
+		parser.flow_level--
+		parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1]
+	}
+	return true
+}
+
+// Push the current indentation level to the stack and set the new level
+// the current column is greater than the indentation level.  In this case,
+// append or insert the specified token into the token queue.
+func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool {
+	// In the flow context, do nothing.
+	if parser.flow_level > 0 {
+		return true
+	}
+
+	if parser.indent < column {
+		// Push the current indentation level to the stack and set the new
+		// indentation level.
+		parser.indents = append(parser.indents, parser.indent)
+		parser.indent = column
+
+		// Create a token and insert it into the queue.
+		token := yaml_token_t{
+			typ:        typ,
+			start_mark: mark,
+			end_mark:   mark,
+		}
+		if number > -1 {
+			number -= parser.tokens_parsed
+		}
+		yaml_insert_token(parser, number, &token)
+	}
+	return true
+}
+
+// Pop indentation levels from the indents stack until the current level
+// becomes less or equal to the column.  For each indentation level, append
+// the BLOCK-END token.
+func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool {
+	// In the flow context, do nothing.
+	if parser.flow_level > 0 {
+		return true
+	}
+
+	// Loop through the indentation levels in the stack.
+	for parser.indent > column {
+		// Create a token and append it to the queue.
+		token := yaml_token_t{
+			typ:        yaml_BLOCK_END_TOKEN,
+			start_mark: parser.mark,
+			end_mark:   parser.mark,
+		}
+		yaml_insert_token(parser, -1, &token)
+
+		// Pop the indentation level.
+		parser.indent = parser.indents[len(parser.indents)-1]
+		parser.indents = parser.indents[:len(parser.indents)-1]
+	}
+	return true
+}
+
+// Initialize the scanner and produce the STREAM-START token.
+func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool {
+
+	// Set the initial indentation.
+	parser.indent = -1
+
+	// Initialize the simple key stack.
+	parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{})
+
+	// A simple key is allowed at the beginning of the stream.
+	parser.simple_key_allowed = true
+
+	// We have started.
+	parser.stream_start_produced = true
+
+	// Create the STREAM-START token and append it to the queue.
+	token := yaml_token_t{
+		typ:        yaml_STREAM_START_TOKEN,
+		start_mark: parser.mark,
+		end_mark:   parser.mark,
+		encoding:   parser.encoding,
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the STREAM-END token and shut down the scanner.
+func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool {
+
+	// Force new line.
+	if parser.mark.column != 0 {
+		parser.mark.column = 0
+		parser.mark.line++
+	}
+
+	// Reset the indentation level.
+	if !yaml_parser_unroll_indent(parser, -1) {
+		return false
+	}
+
+	// Reset simple keys.
+	if !yaml_parser_remove_simple_key(parser) {
+		return false
+	}
+
+	parser.simple_key_allowed = false
+
+	// Create the STREAM-END token and append it to the queue.
+	token := yaml_token_t{
+		typ:        yaml_STREAM_END_TOKEN,
+		start_mark: parser.mark,
+		end_mark:   parser.mark,
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token.
+func yaml_parser_fetch_directive(parser *yaml_parser_t) bool {
+	// Reset the indentation level.
+	if !yaml_parser_unroll_indent(parser, -1) {
+		return false
+	}
+
+	// Reset simple keys.
+	if !yaml_parser_remove_simple_key(parser) {
+		return false
+	}
+
+	parser.simple_key_allowed = false
+
+	// Create the YAML-DIRECTIVE or TAG-DIRECTIVE token.
+	token := yaml_token_t{}
+	if !yaml_parser_scan_directive(parser, &token) {
+		return false
+	}
+	// Append the token to the queue.
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the DOCUMENT-START or DOCUMENT-END token.
+func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool {
+	// Reset the indentation level.
+	if !yaml_parser_unroll_indent(parser, -1) {
+		return false
+	}
+
+	// Reset simple keys.
+	if !yaml_parser_remove_simple_key(parser) {
+		return false
+	}
+
+	parser.simple_key_allowed = false
+
+	// Consume the token.
+	start_mark := parser.mark
+
+	skip(parser)
+	skip(parser)
+	skip(parser)
+
+	end_mark := parser.mark
+
+	// Create the DOCUMENT-START or DOCUMENT-END token.
+	token := yaml_token_t{
+		typ:        typ,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+	}
+	// Append the token to the queue.
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token.
+func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool {
+	// The indicators '[' and '{' may start a simple key.
+	if !yaml_parser_save_simple_key(parser) {
+		return false
+	}
+
+	// Increase the flow level.
+	if !yaml_parser_increase_flow_level(parser) {
+		return false
+	}
+
+	// A simple key may follow the indicators '[' and '{'.
+	parser.simple_key_allowed = true
+
+	// Consume the token.
+	start_mark := parser.mark
+	skip(parser)
+	end_mark := parser.mark
+
+	// Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token.
+	token := yaml_token_t{
+		typ:        typ,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+	}
+	// Append the token to the queue.
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token.
+func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool {
+	// Reset any potential simple key on the current flow level.
+	if !yaml_parser_remove_simple_key(parser) {
+		return false
+	}
+
+	// Decrease the flow level.
+	if !yaml_parser_decrease_flow_level(parser) {
+		return false
+	}
+
+	// No simple keys after the indicators ']' and '}'.
+	parser.simple_key_allowed = false
+
+	// Consume the token.
+
+	start_mark := parser.mark
+	skip(parser)
+	end_mark := parser.mark
+
+	// Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token.
+	token := yaml_token_t{
+		typ:        typ,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+	}
+	// Append the token to the queue.
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the FLOW-ENTRY token.
+func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool {
+	// Reset any potential simple keys on the current flow level.
+	if !yaml_parser_remove_simple_key(parser) {
+		return false
+	}
+
+	// Simple keys are allowed after ','.
+	parser.simple_key_allowed = true
+
+	// Consume the token.
+	start_mark := parser.mark
+	skip(parser)
+	end_mark := parser.mark
+
+	// Create the FLOW-ENTRY token and append it to the queue.
+	token := yaml_token_t{
+		typ:        yaml_FLOW_ENTRY_TOKEN,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the BLOCK-ENTRY token.
+func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool {
+	// Check if the scanner is in the block context.
+	if parser.flow_level == 0 {
+		// Check if we are allowed to start a new entry.
+		if !parser.simple_key_allowed {
+			return yaml_parser_set_scanner_error(parser, "", parser.mark,
+				"block sequence entries are not allowed in this context")
+		}
+		// Add the BLOCK-SEQUENCE-START token if needed.
+		if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) {
+			return false
+		}
+	} else {
+		// It is an error for the '-' indicator to occur in the flow context,
+		// but we let the Parser detect and report about it because the Parser
+		// is able to point to the context.
+	}
+
+	// Reset any potential simple keys on the current flow level.
+	if !yaml_parser_remove_simple_key(parser) {
+		return false
+	}
+
+	// Simple keys are allowed after '-'.
+	parser.simple_key_allowed = true
+
+	// Consume the token.
+	start_mark := parser.mark
+	skip(parser)
+	end_mark := parser.mark
+
+	// Create the BLOCK-ENTRY token and append it to the queue.
+	token := yaml_token_t{
+		typ:        yaml_BLOCK_ENTRY_TOKEN,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the KEY token.
+func yaml_parser_fetch_key(parser *yaml_parser_t) bool {
+
+	// In the block context, additional checks are required.
+	if parser.flow_level == 0 {
+		// Check if we are allowed to start a new key (not nessesary simple).
+		if !parser.simple_key_allowed {
+			return yaml_parser_set_scanner_error(parser, "", parser.mark,
+				"mapping keys are not allowed in this context")
+		}
+		// Add the BLOCK-MAPPING-START token if needed.
+		if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) {
+			return false
+		}
+	}
+
+	// Reset any potential simple keys on the current flow level.
+	if !yaml_parser_remove_simple_key(parser) {
+		return false
+	}
+
+	// Simple keys are allowed after '?' in the block context.
+	parser.simple_key_allowed = parser.flow_level == 0
+
+	// Consume the token.
+	start_mark := parser.mark
+	skip(parser)
+	end_mark := parser.mark
+
+	// Create the KEY token and append it to the queue.
+	token := yaml_token_t{
+		typ:        yaml_KEY_TOKEN,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the VALUE token.
+func yaml_parser_fetch_value(parser *yaml_parser_t) bool {
+
+	simple_key := &parser.simple_keys[len(parser.simple_keys)-1]
+
+	// Have we found a simple key?
+	if simple_key.possible {
+		// Create the KEY token and insert it into the queue.
+		token := yaml_token_t{
+			typ:        yaml_KEY_TOKEN,
+			start_mark: simple_key.mark,
+			end_mark:   simple_key.mark,
+		}
+		yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token)
+
+		// In the block context, we may need to add the BLOCK-MAPPING-START token.
+		if !yaml_parser_roll_indent(parser, simple_key.mark.column,
+			simple_key.token_number,
+			yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) {
+			return false
+		}
+
+		// Remove the simple key.
+		simple_key.possible = false
+
+		// A simple key cannot follow another simple key.
+		parser.simple_key_allowed = false
+
+	} else {
+		// The ':' indicator follows a complex key.
+
+		// In the block context, extra checks are required.
+		if parser.flow_level == 0 {
+
+			// Check if we are allowed to start a complex value.
+			if !parser.simple_key_allowed {
+				return yaml_parser_set_scanner_error(parser, "", parser.mark,
+					"mapping values are not allowed in this context")
+			}
+
+			// Add the BLOCK-MAPPING-START token if needed.
+			if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) {
+				return false
+			}
+		}
+
+		// Simple keys after ':' are allowed in the block context.
+		parser.simple_key_allowed = parser.flow_level == 0
+	}
+
+	// Consume the token.
+	start_mark := parser.mark
+	skip(parser)
+	end_mark := parser.mark
+
+	// Create the VALUE token and append it to the queue.
+	token := yaml_token_t{
+		typ:        yaml_VALUE_TOKEN,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the ALIAS or ANCHOR token.
+func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool {
+	// An anchor or an alias could be a simple key.
+	if !yaml_parser_save_simple_key(parser) {
+		return false
+	}
+
+	// A simple key cannot follow an anchor or an alias.
+	parser.simple_key_allowed = false
+
+	// Create the ALIAS or ANCHOR token and append it to the queue.
+	var token yaml_token_t
+	if !yaml_parser_scan_anchor(parser, &token, typ) {
+		return false
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the TAG token.
+func yaml_parser_fetch_tag(parser *yaml_parser_t) bool {
+	// A tag could be a simple key.
+	if !yaml_parser_save_simple_key(parser) {
+		return false
+	}
+
+	// A simple key cannot follow a tag.
+	parser.simple_key_allowed = false
+
+	// Create the TAG token and append it to the queue.
+	var token yaml_token_t
+	if !yaml_parser_scan_tag(parser, &token) {
+		return false
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens.
+func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool {
+	// Remove any potential simple keys.
+	if !yaml_parser_remove_simple_key(parser) {
+		return false
+	}
+
+	// A simple key may follow a block scalar.
+	parser.simple_key_allowed = true
+
+	// Create the SCALAR token and append it to the queue.
+	var token yaml_token_t
+	if !yaml_parser_scan_block_scalar(parser, &token, literal) {
+		return false
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens.
+func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool {
+	// A plain scalar could be a simple key.
+	if !yaml_parser_save_simple_key(parser) {
+		return false
+	}
+
+	// A simple key cannot follow a flow scalar.
+	parser.simple_key_allowed = false
+
+	// Create the SCALAR token and append it to the queue.
+	var token yaml_token_t
+	if !yaml_parser_scan_flow_scalar(parser, &token, single) {
+		return false
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Produce the SCALAR(...,plain) token.
+func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool {
+	// A plain scalar could be a simple key.
+	if !yaml_parser_save_simple_key(parser) {
+		return false
+	}
+
+	// A simple key cannot follow a flow scalar.
+	parser.simple_key_allowed = false
+
+	// Create the SCALAR token and append it to the queue.
+	var token yaml_token_t
+	if !yaml_parser_scan_plain_scalar(parser, &token) {
+		return false
+	}
+	yaml_insert_token(parser, -1, &token)
+	return true
+}
+
+// Eat whitespaces and comments until the next token is found.
+func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
+
+	// Until the next token is not found.
+	for {
+		// Allow the BOM mark to start a line.
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+		if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) {
+			skip(parser)
+		}
+
+		// Eat whitespaces.
+		// Tabs are allowed:
+		//  - in the flow context
+		//  - in the block context, but not at the beginning of the line or
+		//  after '-', '?', or ':' (complex value).
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+
+		for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') {
+			skip(parser)
+			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+				return false
+			}
+		}
+
+		// Eat a comment until a line break.
+		if parser.buffer[parser.buffer_pos] == '#' {
+			for !is_breakz(parser.buffer, parser.buffer_pos) {
+				skip(parser)
+				if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+					return false
+				}
+			}
+		}
+
+		// If it is a line break, eat it.
+		if is_break(parser.buffer, parser.buffer_pos) {
+			if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+				return false
+			}
+			skip_line(parser)
+
+			// In the block context, a new line may start a simple key.
+			if parser.flow_level == 0 {
+				parser.simple_key_allowed = true
+			}
+		} else {
+			break // We have found a token.
+		}
+	}
+
+	return true
+}
+
+// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token.
+//
+// Scope:
+//      %YAML    1.1    # a comment \n
+//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+//      %TAG    !yaml!  tag:yaml.org,2002:  \n
+//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+//
+func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool {
+	// Eat '%'.
+	start_mark := parser.mark
+	skip(parser)
+
+	// Scan the directive name.
+	var name []byte
+	if !yaml_parser_scan_directive_name(parser, start_mark, &name) {
+		return false
+	}
+
+	// Is it a YAML directive?
+	if bytes.Equal(name, []byte("YAML")) {
+		// Scan the VERSION directive value.
+		var major, minor int8
+		if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) {
+			return false
+		}
+		end_mark := parser.mark
+
+		// Create a VERSION-DIRECTIVE token.
+		*token = yaml_token_t{
+			typ:        yaml_VERSION_DIRECTIVE_TOKEN,
+			start_mark: start_mark,
+			end_mark:   end_mark,
+			major:      major,
+			minor:      minor,
+		}
+
+		// Is it a TAG directive?
+	} else if bytes.Equal(name, []byte("TAG")) {
+		// Scan the TAG directive value.
+		var handle, prefix []byte
+		if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) {
+			return false
+		}
+		end_mark := parser.mark
+
+		// Create a TAG-DIRECTIVE token.
+		*token = yaml_token_t{
+			typ:        yaml_TAG_DIRECTIVE_TOKEN,
+			start_mark: start_mark,
+			end_mark:   end_mark,
+			value:      handle,
+			prefix:     prefix,
+		}
+
+		// Unknown directive.
+	} else {
+		yaml_parser_set_scanner_error(parser, "while scanning a directive",
+			start_mark, "found unknown directive name")
+		return false
+	}
+
+	// Eat the rest of the line including any comments.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+
+	for is_blank(parser.buffer, parser.buffer_pos) {
+		skip(parser)
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+
+	if parser.buffer[parser.buffer_pos] == '#' {
+		for !is_breakz(parser.buffer, parser.buffer_pos) {
+			skip(parser)
+			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+				return false
+			}
+		}
+	}
+
+	// Check if we are at the end of the line.
+	if !is_breakz(parser.buffer, parser.buffer_pos) {
+		yaml_parser_set_scanner_error(parser, "while scanning a directive",
+			start_mark, "did not find expected comment or line break")
+		return false
+	}
+
+	// Eat a line break.
+	if is_break(parser.buffer, parser.buffer_pos) {
+		if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+			return false
+		}
+		skip_line(parser)
+	}
+
+	return true
+}
+
+// Scan the directive name.
+//
+// Scope:
+//      %YAML   1.1     # a comment \n
+//       ^^^^
+//      %TAG    !yaml!  tag:yaml.org,2002:  \n
+//       ^^^
+//
+func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool {
+	// Consume the directive name.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+
+	var s []byte
+	for is_alpha(parser.buffer, parser.buffer_pos) {
+		s = read(parser, s)
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+
+	// Check if the name is empty.
+	if len(s) == 0 {
+		yaml_parser_set_scanner_error(parser, "while scanning a directive",
+			start_mark, "could not find expected directive name")
+		return false
+	}
+
+	// Check for an blank character after the name.
+	if !is_blankz(parser.buffer, parser.buffer_pos) {
+		yaml_parser_set_scanner_error(parser, "while scanning a directive",
+			start_mark, "found unexpected non-alphabetical character")
+		return false
+	}
+	*name = s
+	return true
+}
+
+// Scan the value of VERSION-DIRECTIVE.
+//
+// Scope:
+//      %YAML   1.1     # a comment \n
+//           ^^^^^^
+func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool {
+	// Eat whitespaces.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+	for is_blank(parser.buffer, parser.buffer_pos) {
+		skip(parser)
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+
+	// Consume the major version number.
+	if !yaml_parser_scan_version_directive_number(parser, start_mark, major) {
+		return false
+	}
+
+	// Eat '.'.
+	if parser.buffer[parser.buffer_pos] != '.' {
+		return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",
+			start_mark, "did not find expected digit or '.' character")
+	}
+
+	skip(parser)
+
+	// Consume the minor version number.
+	if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) {
+		return false
+	}
+	return true
+}
+
+const max_number_length = 2
+
+// Scan the version number of VERSION-DIRECTIVE.
+//
+// Scope:
+//      %YAML   1.1     # a comment \n
+//              ^
+//      %YAML   1.1     # a comment \n
+//                ^
+func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool {
+
+	// Repeat while the next character is digit.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+	var value, length int8
+	for is_digit(parser.buffer, parser.buffer_pos) {
+		// Check if the number is too long.
+		length++
+		if length > max_number_length {
+			return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",
+				start_mark, "found extremely long version number")
+		}
+		value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos))
+		skip(parser)
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+
+	// Check if the number was present.
+	if length == 0 {
+		return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",
+			start_mark, "did not find expected version number")
+	}
+	*number = value
+	return true
+}
+
+// Scan the value of a TAG-DIRECTIVE token.
+//
+// Scope:
+//      %TAG    !yaml!  tag:yaml.org,2002:  \n
+//          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+//
+func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool {
+	var handle_value, prefix_value []byte
+
+	// Eat whitespaces.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+
+	for is_blank(parser.buffer, parser.buffer_pos) {
+		skip(parser)
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+
+	// Scan a handle.
+	if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) {
+		return false
+	}
+
+	// Expect a whitespace.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+	if !is_blank(parser.buffer, parser.buffer_pos) {
+		yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive",
+			start_mark, "did not find expected whitespace")
+		return false
+	}
+
+	// Eat whitespaces.
+	for is_blank(parser.buffer, parser.buffer_pos) {
+		skip(parser)
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+
+	// Scan a prefix.
+	if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) {
+		return false
+	}
+
+	// Expect a whitespace or line break.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+	if !is_blankz(parser.buffer, parser.buffer_pos) {
+		yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive",
+			start_mark, "did not find expected whitespace or line break")
+		return false
+	}
+
+	*handle = handle_value
+	*prefix = prefix_value
+	return true
+}
+
+func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool {
+	var s []byte
+
+	// Eat the indicator character.
+	start_mark := parser.mark
+	skip(parser)
+
+	// Consume the value.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+
+	for is_alpha(parser.buffer, parser.buffer_pos) {
+		s = read(parser, s)
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+
+	end_mark := parser.mark
+
+	/*
+	 * Check if length of the anchor is greater than 0 and it is followed by
+	 * a whitespace character or one of the indicators:
+	 *
+	 *      '?', ':', ',', ']', '}', '%', '@', '`'.
+	 */
+
+	if len(s) == 0 ||
+		!(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' ||
+			parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' ||
+			parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' ||
+			parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' ||
+			parser.buffer[parser.buffer_pos] == '`') {
+		context := "while scanning an alias"
+		if typ == yaml_ANCHOR_TOKEN {
+			context = "while scanning an anchor"
+		}
+		yaml_parser_set_scanner_error(parser, context, start_mark,
+			"did not find expected alphabetic or numeric character")
+		return false
+	}
+
+	// Create a token.
+	*token = yaml_token_t{
+		typ:        typ,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+		value:      s,
+	}
+
+	return true
+}
+
+/*
+ * Scan a TAG token.
+ */
+
+func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool {
+	var handle, suffix []byte
+
+	start_mark := parser.mark
+
+	// Check if the tag is in the canonical form.
+	if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+		return false
+	}
+
+	if parser.buffer[parser.buffer_pos+1] == '<' {
+		// Keep the handle as ''
+
+		// Eat '!<'
+		skip(parser)
+		skip(parser)
+
+		// Consume the tag value.
+		if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) {
+			return false
+		}
+
+		// Check for '>' and eat it.
+		if parser.buffer[parser.buffer_pos] != '>' {
+			yaml_parser_set_scanner_error(parser, "while scanning a tag",
+				start_mark, "did not find the expected '>'")
+			return false
+		}
+
+		skip(parser)
+	} else {
+		// The tag has either the '!suffix' or the '!handle!suffix' form.
+
+		// First, try to scan a handle.
+		if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) {
+			return false
+		}
+
+		// Check if it is, indeed, handle.
+		if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' {
+			// Scan the suffix now.
+			if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) {
+				return false
+			}
+		} else {
+			// It wasn't a handle after all.  Scan the rest of the tag.
+			if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) {
+				return false
+			}
+
+			// Set the handle to '!'.
+			handle = []byte{'!'}
+
+			// A special case: the '!' tag.  Set the handle to '' and the
+			// suffix to '!'.
+			if len(suffix) == 0 {
+				handle, suffix = suffix, handle
+			}
+		}
+	}
+
+	// Check the character which ends the tag.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+	if !is_blankz(parser.buffer, parser.buffer_pos) {
+		yaml_parser_set_scanner_error(parser, "while scanning a tag",
+			start_mark, "did not find expected whitespace or line break")
+		return false
+	}
+
+	end_mark := parser.mark
+
+	// Create a token.
+	*token = yaml_token_t{
+		typ:        yaml_TAG_TOKEN,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+		value:      handle,
+		suffix:     suffix,
+	}
+	return true
+}
+
+// Scan a tag handle.
+func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool {
+	// Check the initial '!' character.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+	if parser.buffer[parser.buffer_pos] != '!' {
+		yaml_parser_set_scanner_tag_error(parser, directive,
+			start_mark, "did not find expected '!'")
+		return false
+	}
+
+	var s []byte
+
+	// Copy the '!' character.
+	s = read(parser, s)
+
+	// Copy all subsequent alphabetical and numerical characters.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+	for is_alpha(parser.buffer, parser.buffer_pos) {
+		s = read(parser, s)
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+
+	// Check if the trailing character is '!' and copy it.
+	if parser.buffer[parser.buffer_pos] == '!' {
+		s = read(parser, s)
+	} else {
+		// It's either the '!' tag or not really a tag handle.  If it's a %TAG
+		// directive, it's an error.  If it's a tag token, it must be a part of URI.
+		if directive && !(s[0] == '!' && s[1] == 0) {
+			yaml_parser_set_scanner_tag_error(parser, directive,
+				start_mark, "did not find expected '!'")
+			return false
+		}
+	}
+
+	*handle = s
+	return true
+}
+
+// Scan a tag.
+func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool {
+	//size_t length = head ? strlen((char *)head) : 0
+	var s []byte
+
+	// Copy the head if needed.
+	//
+	// Note that we don't copy the leading '!' character.
+	if len(head) > 1 {
+		s = append(s, head[1:]...)
+	}
+
+	// Scan the tag.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+
+	// The set of characters that may appear in URI is as follows:
+	//
+	//      '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&',
+	//      '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']',
+	//      '%'.
+	// [Go] Convert this into more reasonable logic.
+	for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' ||
+		parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' ||
+		parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' ||
+		parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' ||
+		parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' ||
+		parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' ||
+		parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' ||
+		parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' ||
+		parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' ||
+		parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' ||
+		parser.buffer[parser.buffer_pos] == '%' {
+		// Check if it is a URI-escape sequence.
+		if parser.buffer[parser.buffer_pos] == '%' {
+			if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) {
+				return false
+			}
+		} else {
+			s = read(parser, s)
+		}
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+
+	// Check if the tag is non-empty.
+	if len(s) == 0 {
+		yaml_parser_set_scanner_tag_error(parser, directive,
+			start_mark, "did not find expected tag URI")
+		return false
+	}
+	*uri = s
+	return true
+}
+
+// Decode an URI-escape sequence corresponding to a single UTF-8 character.
+func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool {
+
+	// Decode the required number of characters.
+	w := 1024
+	for w > 0 {
+		// Check for a URI-escaped octet.
+		if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) {
+			return false
+		}
+
+		if !(parser.buffer[parser.buffer_pos] == '%' &&
+			is_hex(parser.buffer, parser.buffer_pos+1) &&
+			is_hex(parser.buffer, parser.buffer_pos+2)) {
+			return yaml_parser_set_scanner_tag_error(parser, directive,
+				start_mark, "did not find URI escaped octet")
+		}
+
+		// Get the octet.
+		octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2))
+
+		// If it is the leading octet, determine the length of the UTF-8 sequence.
+		if w == 1024 {
+			w = width(octet)
+			if w == 0 {
+				return yaml_parser_set_scanner_tag_error(parser, directive,
+					start_mark, "found an incorrect leading UTF-8 octet")
+			}
+		} else {
+			// Check if the trailing octet is correct.
+			if octet&0xC0 != 0x80 {
+				return yaml_parser_set_scanner_tag_error(parser, directive,
+					start_mark, "found an incorrect trailing UTF-8 octet")
+			}
+		}
+
+		// Copy the octet and move the pointers.
+		*s = append(*s, octet)
+		skip(parser)
+		skip(parser)
+		skip(parser)
+		w--
+	}
+	return true
+}
+
+// Scan a block scalar.
+func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool {
+	// Eat the indicator '|' or '>'.
+	start_mark := parser.mark
+	skip(parser)
+
+	// Scan the additional block scalar indicators.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+
+	// Check for a chomping indicator.
+	var chomping, increment int
+	if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' {
+		// Set the chomping method and eat the indicator.
+		if parser.buffer[parser.buffer_pos] == '+' {
+			chomping = +1
+		} else {
+			chomping = -1
+		}
+		skip(parser)
+
+		// Check for an indentation indicator.
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+		if is_digit(parser.buffer, parser.buffer_pos) {
+			// Check that the indentation is greater than 0.
+			if parser.buffer[parser.buffer_pos] == '0' {
+				yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+					start_mark, "found an indentation indicator equal to 0")
+				return false
+			}
+
+			// Get the indentation level and eat the indicator.
+			increment = as_digit(parser.buffer, parser.buffer_pos)
+			skip(parser)
+		}
+
+	} else if is_digit(parser.buffer, parser.buffer_pos) {
+		// Do the same as above, but in the opposite order.
+
+		if parser.buffer[parser.buffer_pos] == '0' {
+			yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+				start_mark, "found an indentation indicator equal to 0")
+			return false
+		}
+		increment = as_digit(parser.buffer, parser.buffer_pos)
+		skip(parser)
+
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+		if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' {
+			if parser.buffer[parser.buffer_pos] == '+' {
+				chomping = +1
+			} else {
+				chomping = -1
+			}
+			skip(parser)
+		}
+	}
+
+	// Eat whitespaces and comments to the end of the line.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+	for is_blank(parser.buffer, parser.buffer_pos) {
+		skip(parser)
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+	}
+	if parser.buffer[parser.buffer_pos] == '#' {
+		for !is_breakz(parser.buffer, parser.buffer_pos) {
+			skip(parser)
+			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+				return false
+			}
+		}
+	}
+
+	// Check if we are at the end of the line.
+	if !is_breakz(parser.buffer, parser.buffer_pos) {
+		yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+			start_mark, "did not find expected comment or line break")
+		return false
+	}
+
+	// Eat a line break.
+	if is_break(parser.buffer, parser.buffer_pos) {
+		if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+			return false
+		}
+		skip_line(parser)
+	}
+
+	end_mark := parser.mark
+
+	// Set the indentation level if it was specified.
+	var indent int
+	if increment > 0 {
+		if parser.indent >= 0 {
+			indent = parser.indent + increment
+		} else {
+			indent = increment
+		}
+	}
+
+	// Scan the leading line breaks and determine the indentation level if needed.
+	var s, leading_break, trailing_breaks []byte
+	if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) {
+		return false
+	}
+
+	// Scan the block scalar content.
+	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+		return false
+	}
+	var leading_blank, trailing_blank bool
+	for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) {
+		// We are at the beginning of a non-empty line.
+
+		// Is it a trailing whitespace?
+		trailing_blank = is_blank(parser.buffer, parser.buffer_pos)
+
+		// Check if we need to fold the leading line break.
+		if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' {
+			// Do we need to join the lines by space?
+			if len(trailing_breaks) == 0 {
+				s = append(s, ' ')
+			}
+		} else {
+			s = append(s, leading_break...)
+		}
+		leading_break = leading_break[:0]
+
+		// Append the remaining line breaks.
+		s = append(s, trailing_breaks...)
+		trailing_breaks = trailing_breaks[:0]
+
+		// Is it a leading whitespace?
+		leading_blank = is_blank(parser.buffer, parser.buffer_pos)
+
+		// Consume the current line.
+		for !is_breakz(parser.buffer, parser.buffer_pos) {
+			s = read(parser, s)
+			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+				return false
+			}
+		}
+
+		// Consume the line break.
+		if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+			return false
+		}
+
+		leading_break = read_line(parser, leading_break)
+
+		// Eat the following indentation spaces and line breaks.
+		if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) {
+			return false
+		}
+	}
+
+	// Chomp the tail.
+	if chomping != -1 {
+		s = append(s, leading_break...)
+	}
+	if chomping == 1 {
+		s = append(s, trailing_breaks...)
+	}
+
+	// Create a token.
+	*token = yaml_token_t{
+		typ:        yaml_SCALAR_TOKEN,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+		value:      s,
+		style:      yaml_LITERAL_SCALAR_STYLE,
+	}
+	if !literal {
+		token.style = yaml_FOLDED_SCALAR_STYLE
+	}
+	return true
+}
+
+// Scan indentation spaces and line breaks for a block scalar.  Determine the
+// indentation level if needed.
+func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool {
+	*end_mark = parser.mark
+
+	// Eat the indentation spaces and line breaks.
+	max_indent := 0
+	for {
+		// Eat the indentation spaces.
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+		for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) {
+			skip(parser)
+			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+				return false
+			}
+		}
+		if parser.mark.column > max_indent {
+			max_indent = parser.mark.column
+		}
+
+		// Check for a tab character messing the indentation.
+		if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) {
+			return yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+				start_mark, "found a tab character where an indentation space is expected")
+		}
+
+		// Have we found a non-empty line?
+		if !is_break(parser.buffer, parser.buffer_pos) {
+			break
+		}
+
+		// Consume the line break.
+		if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+			return false
+		}
+		// [Go] Should really be returning breaks instead.
+		*breaks = read_line(parser, *breaks)
+		*end_mark = parser.mark
+	}
+
+	// Determine the indentation level if needed.
+	if *indent == 0 {
+		*indent = max_indent
+		if *indent < parser.indent+1 {
+			*indent = parser.indent + 1
+		}
+		if *indent < 1 {
+			*indent = 1
+		}
+	}
+	return true
+}
+
+// Scan a quoted scalar.
+func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool {
+	// Eat the left quote.
+	start_mark := parser.mark
+	skip(parser)
+
+	// Consume the content of the quoted scalar.
+	var s, leading_break, trailing_breaks, whitespaces []byte
+	for {
+		// Check that there are no document indicators at the beginning of the line.
+		if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) {
+			return false
+		}
+
+		if parser.mark.column == 0 &&
+			((parser.buffer[parser.buffer_pos+0] == '-' &&
+				parser.buffer[parser.buffer_pos+1] == '-' &&
+				parser.buffer[parser.buffer_pos+2] == '-') ||
+				(parser.buffer[parser.buffer_pos+0] == '.' &&
+					parser.buffer[parser.buffer_pos+1] == '.' &&
+					parser.buffer[parser.buffer_pos+2] == '.')) &&
+			is_blankz(parser.buffer, parser.buffer_pos+3) {
+			yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar",
+				start_mark, "found unexpected document indicator")
+			return false
+		}
+
+		// Check for EOF.
+		if is_z(parser.buffer, parser.buffer_pos) {
+			yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar",
+				start_mark, "found unexpected end of stream")
+			return false
+		}
+
+		// Consume non-blank characters.
+		leading_blanks := false
+		for !is_blankz(parser.buffer, parser.buffer_pos) {
+			if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' {
+				// Is is an escaped single quote.
+				s = append(s, '\'')
+				skip(parser)
+				skip(parser)
+
+			} else if single && parser.buffer[parser.buffer_pos] == '\'' {
+				// It is a right single quote.
+				break
+			} else if !single && parser.buffer[parser.buffer_pos] == '"' {
+				// It is a right double quote.
+				break
+
+			} else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) {
+				// It is an escaped line break.
+				if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) {
+					return false
+				}
+				skip(parser)
+				skip_line(parser)
+				leading_blanks = true
+				break
+
+			} else if !single && parser.buffer[parser.buffer_pos] == '\\' {
+				// It is an escape sequence.
+				code_length := 0
+
+				// Check the escape character.
+				switch parser.buffer[parser.buffer_pos+1] {
+				case '0':
+					s = append(s, 0)
+				case 'a':
+					s = append(s, '\x07')
+				case 'b':
+					s = append(s, '\x08')
+				case 't', '\t':
+					s = append(s, '\x09')
+				case 'n':
+					s = append(s, '\x0A')
+				case 'v':
+					s = append(s, '\x0B')
+				case 'f':
+					s = append(s, '\x0C')
+				case 'r':
+					s = append(s, '\x0D')
+				case 'e':
+					s = append(s, '\x1B')
+				case ' ':
+					s = append(s, '\x20')
+				case '"':
+					s = append(s, '"')
+				case '\'':
+					s = append(s, '\'')
+				case '\\':
+					s = append(s, '\\')
+				case 'N': // NEL (#x85)
+					s = append(s, '\xC2')
+					s = append(s, '\x85')
+				case '_': // #xA0
+					s = append(s, '\xC2')
+					s = append(s, '\xA0')
+				case 'L': // LS (#x2028)
+					s = append(s, '\xE2')
+					s = append(s, '\x80')
+					s = append(s, '\xA8')
+				case 'P': // PS (#x2029)
+					s = append(s, '\xE2')
+					s = append(s, '\x80')
+					s = append(s, '\xA9')
+				case 'x':
+					code_length = 2
+				case 'u':
+					code_length = 4
+				case 'U':
+					code_length = 8
+				default:
+					yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",
+						start_mark, "found unknown escape character")
+					return false
+				}
+
+				skip(parser)
+				skip(parser)
+
+				// Consume an arbitrary escape code.
+				if code_length > 0 {
+					var value int
+
+					// Scan the character value.
+					if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) {
+						return false
+					}
+					for k := 0; k < code_length; k++ {
+						if !is_hex(parser.buffer, parser.buffer_pos+k) {
+							yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",
+								start_mark, "did not find expected hexdecimal number")
+							return false
+						}
+						value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k)
+					}
+
+					// Check the value and write the character.
+					if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF {
+						yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",
+							start_mark, "found invalid Unicode character escape code")
+						return false
+					}
+					if value <= 0x7F {
+						s = append(s, byte(value))
+					} else if value <= 0x7FF {
+						s = append(s, byte(0xC0+(value>>6)))
+						s = append(s, byte(0x80+(value&0x3F)))
+					} else if value <= 0xFFFF {
+						s = append(s, byte(0xE0+(value>>12)))
+						s = append(s, byte(0x80+((value>>6)&0x3F)))
+						s = append(s, byte(0x80+(value&0x3F)))
+					} else {
+						s = append(s, byte(0xF0+(value>>18)))
+						s = append(s, byte(0x80+((value>>12)&0x3F)))
+						s = append(s, byte(0x80+((value>>6)&0x3F)))
+						s = append(s, byte(0x80+(value&0x3F)))
+					}
+
+					// Advance the pointer.
+					for k := 0; k < code_length; k++ {
+						skip(parser)
+					}
+				}
+			} else {
+				// It is a non-escaped non-blank character.
+				s = read(parser, s)
+			}
+			if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+				return false
+			}
+		}
+
+		// Check if we are at the end of the scalar.
+		if single {
+			if parser.buffer[parser.buffer_pos] == '\'' {
+				break
+			}
+		} else {
+			if parser.buffer[parser.buffer_pos] == '"' {
+				break
+			}
+		}
+
+		// Consume blank characters.
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+
+		for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) {
+			if is_blank(parser.buffer, parser.buffer_pos) {
+				// Consume a space or a tab character.
+				if !leading_blanks {
+					whitespaces = read(parser, whitespaces)
+				} else {
+					skip(parser)
+				}
+			} else {
+				if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+					return false
+				}
+
+				// Check if it is a first line break.
+				if !leading_blanks {
+					whitespaces = whitespaces[:0]
+					leading_break = read_line(parser, leading_break)
+					leading_blanks = true
+				} else {
+					trailing_breaks = read_line(parser, trailing_breaks)
+				}
+			}
+			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+				return false
+			}
+		}
+
+		// Join the whitespaces or fold line breaks.
+		if leading_blanks {
+			// Do we need to fold line breaks?
+			if len(leading_break) > 0 && leading_break[0] == '\n' {
+				if len(trailing_breaks) == 0 {
+					s = append(s, ' ')
+				} else {
+					s = append(s, trailing_breaks...)
+				}
+			} else {
+				s = append(s, leading_break...)
+				s = append(s, trailing_breaks...)
+			}
+			trailing_breaks = trailing_breaks[:0]
+			leading_break = leading_break[:0]
+		} else {
+			s = append(s, whitespaces...)
+			whitespaces = whitespaces[:0]
+		}
+	}
+
+	// Eat the right quote.
+	skip(parser)
+	end_mark := parser.mark
+
+	// Create a token.
+	*token = yaml_token_t{
+		typ:        yaml_SCALAR_TOKEN,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+		value:      s,
+		style:      yaml_SINGLE_QUOTED_SCALAR_STYLE,
+	}
+	if !single {
+		token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+	}
+	return true
+}
+
+// Scan a plain scalar.
+func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool {
+
+	var s, leading_break, trailing_breaks, whitespaces []byte
+	var leading_blanks bool
+	var indent = parser.indent + 1
+
+	start_mark := parser.mark
+	end_mark := parser.mark
+
+	// Consume the content of the plain scalar.
+	for {
+		// Check for a document indicator.
+		if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) {
+			return false
+		}
+		if parser.mark.column == 0 &&
+			((parser.buffer[parser.buffer_pos+0] == '-' &&
+				parser.buffer[parser.buffer_pos+1] == '-' &&
+				parser.buffer[parser.buffer_pos+2] == '-') ||
+				(parser.buffer[parser.buffer_pos+0] == '.' &&
+					parser.buffer[parser.buffer_pos+1] == '.' &&
+					parser.buffer[parser.buffer_pos+2] == '.')) &&
+			is_blankz(parser.buffer, parser.buffer_pos+3) {
+			break
+		}
+
+		// Check for a comment.
+		if parser.buffer[parser.buffer_pos] == '#' {
+			break
+		}
+
+		// Consume non-blank characters.
+		for !is_blankz(parser.buffer, parser.buffer_pos) {
+
+			// Check for 'x:x' in the flow context. TODO: Fix the test "spec-08-13".
+			if parser.flow_level > 0 &&
+				parser.buffer[parser.buffer_pos] == ':' &&
+				!is_blankz(parser.buffer, parser.buffer_pos+1) {
+				yaml_parser_set_scanner_error(parser, "while scanning a plain scalar",
+					start_mark, "found unexpected ':'")
+				return false
+			}
+
+			// Check for indicators that may end a plain scalar.
+			if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) ||
+				(parser.flow_level > 0 &&
+					(parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == ':' ||
+						parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' ||
+						parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' ||
+						parser.buffer[parser.buffer_pos] == '}')) {
+				break
+			}
+
+			// Check if we need to join whitespaces and breaks.
+			if leading_blanks || len(whitespaces) > 0 {
+				if leading_blanks {
+					// Do we need to fold line breaks?
+					if leading_break[0] == '\n' {
+						if len(trailing_breaks) == 0 {
+							s = append(s, ' ')
+						} else {
+							s = append(s, trailing_breaks...)
+						}
+					} else {
+						s = append(s, leading_break...)
+						s = append(s, trailing_breaks...)
+					}
+					trailing_breaks = trailing_breaks[:0]
+					leading_break = leading_break[:0]
+					leading_blanks = false
+				} else {
+					s = append(s, whitespaces...)
+					whitespaces = whitespaces[:0]
+				}
+			}
+
+			// Copy the character.
+			s = read(parser, s)
+
+			end_mark = parser.mark
+			if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+				return false
+			}
+		}
+
+		// Is it the end?
+		if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) {
+			break
+		}
+
+		// Consume blank characters.
+		if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+			return false
+		}
+
+		for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) {
+			if is_blank(parser.buffer, parser.buffer_pos) {
+
+				// Check for tab character that abuse indentation.
+				if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) {
+					yaml_parser_set_scanner_error(parser, "while scanning a plain scalar",
+						start_mark, "found a tab character that violate indentation")
+					return false
+				}
+
+				// Consume a space or a tab character.
+				if !leading_blanks {
+					whitespaces = read(parser, whitespaces)
+				} else {
+					skip(parser)
+				}
+			} else {
+				if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+					return false
+				}
+
+				// Check if it is a first line break.
+				if !leading_blanks {
+					whitespaces = whitespaces[:0]
+					leading_break = read_line(parser, leading_break)
+					leading_blanks = true
+				} else {
+					trailing_breaks = read_line(parser, trailing_breaks)
+				}
+			}
+			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+				return false
+			}
+		}
+
+		// Check indentation level.
+		if parser.flow_level == 0 && parser.mark.column < indent {
+			break
+		}
+	}
+
+	// Create a token.
+	*token = yaml_token_t{
+		typ:        yaml_SCALAR_TOKEN,
+		start_mark: start_mark,
+		end_mark:   end_mark,
+		value:      s,
+		style:      yaml_PLAIN_SCALAR_STYLE,
+	}
+
+	// Note that we change the 'simple_key_allowed' flag.
+	if leading_blanks {
+		parser.simple_key_allowed = true
+	}
+	return true
+}

+ 104 - 0
_vendor/src/gopkg.in/yaml.v2/sorter.go

@@ -0,0 +1,104 @@
+package yaml
+
+import (
+	"reflect"
+	"unicode"
+)
+
+type keyList []reflect.Value
+
+func (l keyList) Len() int      { return len(l) }
+func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+func (l keyList) Less(i, j int) bool {
+	a := l[i]
+	b := l[j]
+	ak := a.Kind()
+	bk := b.Kind()
+	for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
+		a = a.Elem()
+		ak = a.Kind()
+	}
+	for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
+		b = b.Elem()
+		bk = b.Kind()
+	}
+	af, aok := keyFloat(a)
+	bf, bok := keyFloat(b)
+	if aok && bok {
+		if af != bf {
+			return af < bf
+		}
+		if ak != bk {
+			return ak < bk
+		}
+		return numLess(a, b)
+	}
+	if ak != reflect.String || bk != reflect.String {
+		return ak < bk
+	}
+	ar, br := []rune(a.String()), []rune(b.String())
+	for i := 0; i < len(ar) && i < len(br); i++ {
+		if ar[i] == br[i] {
+			continue
+		}
+		al := unicode.IsLetter(ar[i])
+		bl := unicode.IsLetter(br[i])
+		if al && bl {
+			return ar[i] < br[i]
+		}
+		if al || bl {
+			return bl
+		}
+		var ai, bi int
+		var an, bn int64
+		for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
+			an = an*10 + int64(ar[ai]-'0')
+		}
+		for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
+			bn = bn*10 + int64(br[bi]-'0')
+		}
+		if an != bn {
+			return an < bn
+		}
+		if ai != bi {
+			return ai < bi
+		}
+		return ar[i] < br[i]
+	}
+	return len(ar) < len(br)
+}
+
+// keyFloat returns a float value for v if it is a number/bool
+// and whether it is a number/bool or not.
+func keyFloat(v reflect.Value) (f float64, ok bool) {
+	switch v.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return float64(v.Int()), true
+	case reflect.Float32, reflect.Float64:
+		return v.Float(), true
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return float64(v.Uint()), true
+	case reflect.Bool:
+		if v.Bool() {
+			return 1, true
+		}
+		return 0, true
+	}
+	return 0, false
+}
+
+// numLess returns whether a < b.
+// a and b must necessarily have the same kind.
+func numLess(a, b reflect.Value) bool {
+	switch a.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return a.Int() < b.Int()
+	case reflect.Float32, reflect.Float64:
+		return a.Float() < b.Float()
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return a.Uint() < b.Uint()
+	case reflect.Bool:
+		return !a.Bool() && b.Bool()
+	}
+	panic("not a number")
+}

+ 12 - 0
_vendor/src/gopkg.in/yaml.v2/suite_test.go

@@ -0,0 +1,12 @@
+package yaml_test
+
+import (
+	. "gopkg.in/check.v1"
+	"testing"
+)
+
+func Test(t *testing.T) { TestingT(t) }
+
+type S struct{}
+
+var _ = Suite(&S{})

+ 89 - 0
_vendor/src/gopkg.in/yaml.v2/writerc.go

@@ -0,0 +1,89 @@
+package yaml
+
+// Set the writer error and return false.
+func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
+	emitter.error = yaml_WRITER_ERROR
+	emitter.problem = problem
+	return false
+}
+
+// Flush the output buffer.
+func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
+	if emitter.write_handler == nil {
+		panic("write handler not set")
+	}
+
+	// Check if the buffer is empty.
+	if emitter.buffer_pos == 0 {
+		return true
+	}
+
+	// If the output encoding is UTF-8, we don't need to recode the buffer.
+	if emitter.encoding == yaml_UTF8_ENCODING {
+		if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
+			return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
+		}
+		emitter.buffer_pos = 0
+		return true
+	}
+
+	// Recode the buffer into the raw buffer.
+	var low, high int
+	if emitter.encoding == yaml_UTF16LE_ENCODING {
+		low, high = 0, 1
+	} else {
+		high, low = 1, 0
+	}
+
+	pos := 0
+	for pos < emitter.buffer_pos {
+		// See the "reader.c" code for more details on UTF-8 encoding.  Note
+		// that we assume that the buffer contains a valid UTF-8 sequence.
+
+		// Read the next UTF-8 character.
+		octet := emitter.buffer[pos]
+
+		var w int
+		var value rune
+		switch {
+		case octet&0x80 == 0x00:
+			w, value = 1, rune(octet&0x7F)
+		case octet&0xE0 == 0xC0:
+			w, value = 2, rune(octet&0x1F)
+		case octet&0xF0 == 0xE0:
+			w, value = 3, rune(octet&0x0F)
+		case octet&0xF8 == 0xF0:
+			w, value = 4, rune(octet&0x07)
+		}
+		for k := 1; k < w; k++ {
+			octet = emitter.buffer[pos+k]
+			value = (value << 6) + (rune(octet) & 0x3F)
+		}
+		pos += w
+
+		// Write the character.
+		if value < 0x10000 {
+			var b [2]byte
+			b[high] = byte(value >> 8)
+			b[low] = byte(value & 0xFF)
+			emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1])
+		} else {
+			// Write the character using a surrogate pair (check "reader.c").
+			var b [4]byte
+			value -= 0x10000
+			b[high] = byte(0xD8 + (value >> 18))
+			b[low] = byte((value >> 10) & 0xFF)
+			b[high+2] = byte(0xDC + ((value >> 8) & 0xFF))
+			b[low+2] = byte(value & 0xFF)
+			emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3])
+		}
+	}
+
+	// Write the raw buffer.
+	if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil {
+		return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
+	}
+	emitter.buffer_pos = 0
+	emitter.raw_buffer = emitter.raw_buffer[:0]
+	return true
+}

+ 346 - 0
_vendor/src/gopkg.in/yaml.v2/yaml.go

@@ -0,0 +1,346 @@
+// Package yaml implements YAML support for the Go language.
+//
+// Source code and other details for the project are available at GitHub:
+//
+//   https://github.com/go-yaml/yaml
+//
+package yaml
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"strings"
+	"sync"
+)
+
+// MapSlice encodes and decodes as a YAML map.
+// The order of keys is preserved when encoding and decoding.
+type MapSlice []MapItem
+
+// MapItem is an item in a MapSlice.
+type MapItem struct {
+	Key, Value interface{}
+}
+
+// The Unmarshaler interface may be implemented by types to customize their
+// behavior when being unmarshaled from a YAML document. The UnmarshalYAML
+// method receives a function that may be called to unmarshal the original
+// YAML value into a field or variable. It is safe to call the unmarshal
+// function parameter more than once if necessary.
+type Unmarshaler interface {
+	UnmarshalYAML(unmarshal func(interface{}) error) error
+}
+
+// The Marshaler interface may be implemented by types to customize their
+// behavior when being marshaled into a YAML document. The returned value
+// is marshaled in place of the original value implementing Marshaler.
+//
+// If an error is returned by MarshalYAML, the marshaling procedure stops
+// and returns with the provided error.
+type Marshaler interface {
+	MarshalYAML() (interface{}, error)
+}
+
+// Unmarshal decodes the first document found within the in byte slice
+// and assigns decoded values into the out value.
+//
+// Maps and pointers (to a struct, string, int, etc) are accepted as out
+// values. If an internal pointer within a struct is not initialized,
+// the yaml package will initialize it if necessary for unmarshalling
+// the provided data. The out parameter must not be nil.
+//
+// The type of the decoded values should be compatible with the respective
+// values in out. If one or more values cannot be decoded due to a type
+// mismatches, decoding continues partially until the end of the YAML
+// content, and a *yaml.TypeError is returned with details for all
+// missed values.
+//
+// Struct fields are only unmarshalled if they are exported (have an
+// upper case first letter), and are unmarshalled using the field name
+// lowercased as the default key. Custom keys may be defined via the
+// "yaml" name in the field tag: the content preceding the first comma
+// is used as the key, and the following comma-separated options are
+// used to tweak the marshalling process (see Marshal).
+// Conflicting names result in a runtime error.
+//
+// For example:
+//
+//     type T struct {
+//         F int `yaml:"a,omitempty"`
+//         B int
+//     }
+//     var t T
+//     yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
+//
+// See the documentation of Marshal for the format of tags and a list of
+// supported tag options.
+//
+func Unmarshal(in []byte, out interface{}) (err error) {
+	defer handleErr(&err)
+	d := newDecoder()
+	p := newParser(in)
+	defer p.destroy()
+	node := p.parse()
+	if node != nil {
+		v := reflect.ValueOf(out)
+		if v.Kind() == reflect.Ptr && !v.IsNil() {
+			v = v.Elem()
+		}
+		d.unmarshal(node, v)
+	}
+	if len(d.terrors) > 0 {
+		return &TypeError{d.terrors}
+	}
+	return nil
+}
+
+// Marshal serializes the value provided into a YAML document. The structure
+// of the generated document will reflect the structure of the value itself.
+// Maps and pointers (to struct, string, int, etc) are accepted as the in value.
+//
+// Struct fields are only unmarshalled if they are exported (have an upper case
+// first letter), and are unmarshalled using the field name lowercased as the
+// default key. Custom keys may be defined via the "yaml" name in the field
+// tag: the content preceding the first comma is used as the key, and the
+// following comma-separated options are used to tweak the marshalling process.
+// Conflicting names result in a runtime error.
+//
+// The field tag format accepted is:
+//
+//     `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
+//
+// The following flags are currently supported:
+//
+//     omitempty    Only include the field if it's not set to the zero
+//                  value for the type or to empty slices or maps.
+//                  Does not apply to zero valued structs.
+//
+//     flow         Marshal using a flow style (useful for structs,
+//                  sequences and maps).
+//
+//     inline       Inline the field, which must be a struct or a map,
+//                  causing all of its fields or keys to be processed as if
+//                  they were part of the outer struct. For maps, keys must
+//                  not conflict with the yaml keys of other struct fields.
+//
+// In addition, if the key is "-", the field is ignored.
+//
+// For example:
+//
+//     type T struct {
+//         F int "a,omitempty"
+//         B int
+//     }
+//     yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
+//     yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
+//
+func Marshal(in interface{}) (out []byte, err error) {
+	defer handleErr(&err)
+	e := newEncoder()
+	defer e.destroy()
+	e.marshal("", reflect.ValueOf(in))
+	e.finish()
+	out = e.out
+	return
+}
+
+func handleErr(err *error) {
+	if v := recover(); v != nil {
+		if e, ok := v.(yamlError); ok {
+			*err = e.err
+		} else {
+			panic(v)
+		}
+	}
+}
+
+type yamlError struct {
+	err error
+}
+
+func fail(err error) {
+	panic(yamlError{err})
+}
+
+func failf(format string, args ...interface{}) {
+	panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
+}
+
+// A TypeError is returned by Unmarshal when one or more fields in
+// the YAML document cannot be properly decoded into the requested
+// types. When this error is returned, the value is still
+// unmarshaled partially.
+type TypeError struct {
+	Errors []string
+}
+
+func (e *TypeError) Error() string {
+	return fmt.Sprintf("yaml: unmarshal errors:\n  %s", strings.Join(e.Errors, "\n  "))
+}
+
+// --------------------------------------------------------------------------
+// Maintain a mapping of keys to structure field indexes
+
+// The code in this section was copied from mgo/bson.
+
+// structInfo holds details for the serialization of fields of
+// a given struct.
+type structInfo struct {
+	FieldsMap  map[string]fieldInfo
+	FieldsList []fieldInfo
+
+	// InlineMap is the number of the field in the struct that
+	// contains an ,inline map, or -1 if there's none.
+	InlineMap int
+}
+
+type fieldInfo struct {
+	Key       string
+	Num       int
+	OmitEmpty bool
+	Flow      bool
+
+	// Inline holds the field index if the field is part of an inlined struct.
+	Inline []int
+}
+
+var structMap = make(map[reflect.Type]*structInfo)
+var fieldMapMutex sync.RWMutex
+
+func getStructInfo(st reflect.Type) (*structInfo, error) {
+	fieldMapMutex.RLock()
+	sinfo, found := structMap[st]
+	fieldMapMutex.RUnlock()
+	if found {
+		return sinfo, nil
+	}
+
+	n := st.NumField()
+	fieldsMap := make(map[string]fieldInfo)
+	fieldsList := make([]fieldInfo, 0, n)
+	inlineMap := -1
+	for i := 0; i != n; i++ {
+		field := st.Field(i)
+		if field.PkgPath != "" && !field.Anonymous {
+			continue // Private field
+		}
+
+		info := fieldInfo{Num: i}
+
+		tag := field.Tag.Get("yaml")
+		if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
+			tag = string(field.Tag)
+		}
+		if tag == "-" {
+			continue
+		}
+
+		inline := false
+		fields := strings.Split(tag, ",")
+		if len(fields) > 1 {
+			for _, flag := range fields[1:] {
+				switch flag {
+				case "omitempty":
+					info.OmitEmpty = true
+				case "flow":
+					info.Flow = true
+				case "inline":
+					inline = true
+				default:
+					return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
+				}
+			}
+			tag = fields[0]
+		}
+
+		if inline {
+			switch field.Type.Kind() {
+			case reflect.Map:
+				if inlineMap >= 0 {
+					return nil, errors.New("Multiple ,inline maps in struct " + st.String())
+				}
+				if field.Type.Key() != reflect.TypeOf("") {
+					return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
+				}
+				inlineMap = info.Num
+			case reflect.Struct:
+				sinfo, err := getStructInfo(field.Type)
+				if err != nil {
+					return nil, err
+				}
+				for _, finfo := range sinfo.FieldsList {
+					if _, found := fieldsMap[finfo.Key]; found {
+						msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
+						return nil, errors.New(msg)
+					}
+					if finfo.Inline == nil {
+						finfo.Inline = []int{i, finfo.Num}
+					} else {
+						finfo.Inline = append([]int{i}, finfo.Inline...)
+					}
+					fieldsMap[finfo.Key] = finfo
+					fieldsList = append(fieldsList, finfo)
+				}
+			default:
+				//return nil, errors.New("Option ,inline needs a struct value or map field")
+				return nil, errors.New("Option ,inline needs a struct value field")
+			}
+			continue
+		}
+
+		if tag != "" {
+			info.Key = tag
+		} else {
+			info.Key = strings.ToLower(field.Name)
+		}
+
+		if _, found = fieldsMap[info.Key]; found {
+			msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
+			return nil, errors.New(msg)
+		}
+
+		fieldsList = append(fieldsList, info)
+		fieldsMap[info.Key] = info
+	}
+
+	sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
+
+	fieldMapMutex.Lock()
+	structMap[st] = sinfo
+	fieldMapMutex.Unlock()
+	return sinfo, nil
+}
+
+func isZero(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.String:
+		return len(v.String()) == 0
+	case reflect.Interface, reflect.Ptr:
+		return v.IsNil()
+	case reflect.Slice:
+		return v.Len() == 0
+	case reflect.Map:
+		return v.Len() == 0
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return v.Uint() == 0
+	case reflect.Bool:
+		return !v.Bool()
+	case reflect.Struct:
+		vt := v.Type()
+		for i := v.NumField() - 1; i >= 0; i-- {
+			if vt.Field(i).PkgPath != "" {
+				continue // Private field
+			}
+			if !isZero(v.Field(i)) {
+				return false
+			}
+		}
+		return true
+	}
+	return false
+}

+ 716 - 0
_vendor/src/gopkg.in/yaml.v2/yamlh.go

@@ -0,0 +1,716 @@
+package yaml
+
+import (
+	"io"
+)
+
+// The version directive data.
+type yaml_version_directive_t struct {
+	major int8 // The major version number.
+	minor int8 // The minor version number.
+}
+
+// The tag directive data.
+type yaml_tag_directive_t struct {
+	handle []byte // The tag handle.
+	prefix []byte // The tag prefix.
+}
+
+type yaml_encoding_t int
+
+// The stream encoding.
+const (
+	// Let the parser choose the encoding.
+	yaml_ANY_ENCODING yaml_encoding_t = iota
+
+	yaml_UTF8_ENCODING    // The default UTF-8 encoding.
+	yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM.
+	yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM.
+)
+
+type yaml_break_t int
+
+// Line break types.
+const (
+	// Let the parser choose the break type.
+	yaml_ANY_BREAK yaml_break_t = iota
+
+	yaml_CR_BREAK   // Use CR for line breaks (Mac style).
+	yaml_LN_BREAK   // Use LN for line breaks (Unix style).
+	yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style).
+)
+
+type yaml_error_type_t int
+
+// Many bad things could happen with the parser and emitter.
+const (
+	// No error is produced.
+	yaml_NO_ERROR yaml_error_type_t = iota
+
+	yaml_MEMORY_ERROR   // Cannot allocate or reallocate a block of memory.
+	yaml_READER_ERROR   // Cannot read or decode the input stream.
+	yaml_SCANNER_ERROR  // Cannot scan the input stream.
+	yaml_PARSER_ERROR   // Cannot parse the input stream.
+	yaml_COMPOSER_ERROR // Cannot compose a YAML document.
+	yaml_WRITER_ERROR   // Cannot write to the output stream.
+	yaml_EMITTER_ERROR  // Cannot emit a YAML stream.
+)
+
+// The pointer position.
+type yaml_mark_t struct {
+	index  int // The position index.
+	line   int // The position line.
+	column int // The position column.
+}
+
+// Node Styles
+
+type yaml_style_t int8
+
+type yaml_scalar_style_t yaml_style_t
+
+// Scalar styles.
+const (
+	// Let the emitter choose the style.
+	yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota
+
+	yaml_PLAIN_SCALAR_STYLE         // The plain scalar style.
+	yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
+	yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
+	yaml_LITERAL_SCALAR_STYLE       // The literal scalar style.
+	yaml_FOLDED_SCALAR_STYLE        // The folded scalar style.
+)
+
+type yaml_sequence_style_t yaml_style_t
+
+// Sequence styles.
+const (
+	// Let the emitter choose the style.
+	yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota
+
+	yaml_BLOCK_SEQUENCE_STYLE // The block sequence style.
+	yaml_FLOW_SEQUENCE_STYLE  // The flow sequence style.
+)
+
+type yaml_mapping_style_t yaml_style_t
+
+// Mapping styles.
+const (
+	// Let the emitter choose the style.
+	yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota
+
+	yaml_BLOCK_MAPPING_STYLE // The block mapping style.
+	yaml_FLOW_MAPPING_STYLE  // The flow mapping style.
+)
+
+// Tokens
+
+type yaml_token_type_t int
+
+// Token types.
+const (
+	// An empty token.
+	yaml_NO_TOKEN yaml_token_type_t = iota
+
+	yaml_STREAM_START_TOKEN // A STREAM-START token.
+	yaml_STREAM_END_TOKEN   // A STREAM-END token.
+
+	yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token.
+	yaml_TAG_DIRECTIVE_TOKEN     // A TAG-DIRECTIVE token.
+	yaml_DOCUMENT_START_TOKEN    // A DOCUMENT-START token.
+	yaml_DOCUMENT_END_TOKEN      // A DOCUMENT-END token.
+
+	yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token.
+	yaml_BLOCK_MAPPING_START_TOKEN  // A BLOCK-SEQUENCE-END token.
+	yaml_BLOCK_END_TOKEN            // A BLOCK-END token.
+
+	yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token.
+	yaml_FLOW_SEQUENCE_END_TOKEN   // A FLOW-SEQUENCE-END token.
+	yaml_FLOW_MAPPING_START_TOKEN  // A FLOW-MAPPING-START token.
+	yaml_FLOW_MAPPING_END_TOKEN    // A FLOW-MAPPING-END token.
+
+	yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token.
+	yaml_FLOW_ENTRY_TOKEN  // A FLOW-ENTRY token.
+	yaml_KEY_TOKEN         // A KEY token.
+	yaml_VALUE_TOKEN       // A VALUE token.
+
+	yaml_ALIAS_TOKEN  // An ALIAS token.
+	yaml_ANCHOR_TOKEN // An ANCHOR token.
+	yaml_TAG_TOKEN    // A TAG token.
+	yaml_SCALAR_TOKEN // A SCALAR token.
+)
+
+func (tt yaml_token_type_t) String() string {
+	switch tt {
+	case yaml_NO_TOKEN:
+		return "yaml_NO_TOKEN"
+	case yaml_STREAM_START_TOKEN:
+		return "yaml_STREAM_START_TOKEN"
+	case yaml_STREAM_END_TOKEN:
+		return "yaml_STREAM_END_TOKEN"
+	case yaml_VERSION_DIRECTIVE_TOKEN:
+		return "yaml_VERSION_DIRECTIVE_TOKEN"
+	case yaml_TAG_DIRECTIVE_TOKEN:
+		return "yaml_TAG_DIRECTIVE_TOKEN"
+	case yaml_DOCUMENT_START_TOKEN:
+		return "yaml_DOCUMENT_START_TOKEN"
+	case yaml_DOCUMENT_END_TOKEN:
+		return "yaml_DOCUMENT_END_TOKEN"
+	case yaml_BLOCK_SEQUENCE_START_TOKEN:
+		return "yaml_BLOCK_SEQUENCE_START_TOKEN"
+	case yaml_BLOCK_MAPPING_START_TOKEN:
+		return "yaml_BLOCK_MAPPING_START_TOKEN"
+	case yaml_BLOCK_END_TOKEN:
+		return "yaml_BLOCK_END_TOKEN"
+	case yaml_FLOW_SEQUENCE_START_TOKEN:
+		return "yaml_FLOW_SEQUENCE_START_TOKEN"
+	case yaml_FLOW_SEQUENCE_END_TOKEN:
+		return "yaml_FLOW_SEQUENCE_END_TOKEN"
+	case yaml_FLOW_MAPPING_START_TOKEN:
+		return "yaml_FLOW_MAPPING_START_TOKEN"
+	case yaml_FLOW_MAPPING_END_TOKEN:
+		return "yaml_FLOW_MAPPING_END_TOKEN"
+	case yaml_BLOCK_ENTRY_TOKEN:
+		return "yaml_BLOCK_ENTRY_TOKEN"
+	case yaml_FLOW_ENTRY_TOKEN:
+		return "yaml_FLOW_ENTRY_TOKEN"
+	case yaml_KEY_TOKEN:
+		return "yaml_KEY_TOKEN"
+	case yaml_VALUE_TOKEN:
+		return "yaml_VALUE_TOKEN"
+	case yaml_ALIAS_TOKEN:
+		return "yaml_ALIAS_TOKEN"
+	case yaml_ANCHOR_TOKEN:
+		return "yaml_ANCHOR_TOKEN"
+	case yaml_TAG_TOKEN:
+		return "yaml_TAG_TOKEN"
+	case yaml_SCALAR_TOKEN:
+		return "yaml_SCALAR_TOKEN"
+	}
+	return "<unknown token>"
+}
+
+// The token structure.
+type yaml_token_t struct {
+	// The token type.
+	typ yaml_token_type_t
+
+	// The start/end of the token.
+	start_mark, end_mark yaml_mark_t
+
+	// The stream encoding (for yaml_STREAM_START_TOKEN).
+	encoding yaml_encoding_t
+
+	// The alias/anchor/scalar value or tag/tag directive handle
+	// (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN).
+	value []byte
+
+	// The tag suffix (for yaml_TAG_TOKEN).
+	suffix []byte
+
+	// The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN).
+	prefix []byte
+
+	// The scalar style (for yaml_SCALAR_TOKEN).
+	style yaml_scalar_style_t
+
+	// The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN).
+	major, minor int8
+}
+
+// Events
+
+type yaml_event_type_t int8
+
+// Event types.
+const (
+	// An empty event.
+	yaml_NO_EVENT yaml_event_type_t = iota
+
+	yaml_STREAM_START_EVENT   // A STREAM-START event.
+	yaml_STREAM_END_EVENT     // A STREAM-END event.
+	yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event.
+	yaml_DOCUMENT_END_EVENT   // A DOCUMENT-END event.
+	yaml_ALIAS_EVENT          // An ALIAS event.
+	yaml_SCALAR_EVENT         // A SCALAR event.
+	yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event.
+	yaml_SEQUENCE_END_EVENT   // A SEQUENCE-END event.
+	yaml_MAPPING_START_EVENT  // A MAPPING-START event.
+	yaml_MAPPING_END_EVENT    // A MAPPING-END event.
+)
+
+// The event structure.
+type yaml_event_t struct {
+
+	// The event type.
+	typ yaml_event_type_t
+
+	// The start and end of the event.
+	start_mark, end_mark yaml_mark_t
+
+	// The document encoding (for yaml_STREAM_START_EVENT).
+	encoding yaml_encoding_t
+
+	// The version directive (for yaml_DOCUMENT_START_EVENT).
+	version_directive *yaml_version_directive_t
+
+	// The list of tag directives (for yaml_DOCUMENT_START_EVENT).
+	tag_directives []yaml_tag_directive_t
+
+	// The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT).
+	anchor []byte
+
+	// The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
+	tag []byte
+
+	// The scalar value (for yaml_SCALAR_EVENT).
+	value []byte
+
+	// Is the document start/end indicator implicit, or the tag optional?
+	// (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT).
+	implicit bool
+
+	// Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT).
+	quoted_implicit bool
+
+	// The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
+	style yaml_style_t
+}
+
+func (e *yaml_event_t) scalar_style() yaml_scalar_style_t     { return yaml_scalar_style_t(e.style) }
+func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) }
+func (e *yaml_event_t) mapping_style() yaml_mapping_style_t   { return yaml_mapping_style_t(e.style) }
+
+// Nodes
+
+const (
+	yaml_NULL_TAG      = "tag:yaml.org,2002:null"      // The tag !!null with the only possible value: null.
+	yaml_BOOL_TAG      = "tag:yaml.org,2002:bool"      // The tag !!bool with the values: true and false.
+	yaml_STR_TAG       = "tag:yaml.org,2002:str"       // The tag !!str for string values.
+	yaml_INT_TAG       = "tag:yaml.org,2002:int"       // The tag !!int for integer values.
+	yaml_FLOAT_TAG     = "tag:yaml.org,2002:float"     // The tag !!float for float values.
+	yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values.
+
+	yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences.
+	yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping.
+
+	// Not in original libyaml.
+	yaml_BINARY_TAG = "tag:yaml.org,2002:binary"
+	yaml_MERGE_TAG  = "tag:yaml.org,2002:merge"
+
+	yaml_DEFAULT_SCALAR_TAG   = yaml_STR_TAG // The default scalar tag is !!str.
+	yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq.
+	yaml_DEFAULT_MAPPING_TAG  = yaml_MAP_TAG // The default mapping tag is !!map.
+)
+
+type yaml_node_type_t int
+
+// Node types.
+const (
+	// An empty node.
+	yaml_NO_NODE yaml_node_type_t = iota
+
+	yaml_SCALAR_NODE   // A scalar node.
+	yaml_SEQUENCE_NODE // A sequence node.
+	yaml_MAPPING_NODE  // A mapping node.
+)
+
+// An element of a sequence node.
+type yaml_node_item_t int
+
+// An element of a mapping node.
+type yaml_node_pair_t struct {
+	key   int // The key of the element.
+	value int // The value of the element.
+}
+
+// The node structure.
+type yaml_node_t struct {
+	typ yaml_node_type_t // The node type.
+	tag []byte           // The node tag.
+
+	// The node data.
+
+	// The scalar parameters (for yaml_SCALAR_NODE).
+	scalar struct {
+		value  []byte              // The scalar value.
+		length int                 // The length of the scalar value.
+		style  yaml_scalar_style_t // The scalar style.
+	}
+
+	// The sequence parameters (for YAML_SEQUENCE_NODE).
+	sequence struct {
+		items_data []yaml_node_item_t    // The stack of sequence items.
+		style      yaml_sequence_style_t // The sequence style.
+	}
+
+	// The mapping parameters (for yaml_MAPPING_NODE).
+	mapping struct {
+		pairs_data  []yaml_node_pair_t   // The stack of mapping pairs (key, value).
+		pairs_start *yaml_node_pair_t    // The beginning of the stack.
+		pairs_end   *yaml_node_pair_t    // The end of the stack.
+		pairs_top   *yaml_node_pair_t    // The top of the stack.
+		style       yaml_mapping_style_t // The mapping style.
+	}
+
+	start_mark yaml_mark_t // The beginning of the node.
+	end_mark   yaml_mark_t // The end of the node.
+
+}
+
+// The document structure.
+type yaml_document_t struct {
+
+	// The document nodes.
+	nodes []yaml_node_t
+
+	// The version directive.
+	version_directive *yaml_version_directive_t
+
+	// The list of tag directives.
+	tag_directives_data  []yaml_tag_directive_t
+	tag_directives_start int // The beginning of the tag directives list.
+	tag_directives_end   int // The end of the tag directives list.
+
+	start_implicit int // Is the document start indicator implicit?
+	end_implicit   int // Is the document end indicator implicit?
+
+	// The start/end of the document.
+	start_mark, end_mark yaml_mark_t
+}
+
+// The prototype of a read handler.
+//
+// The read handler is called when the parser needs to read more bytes from the
+// source. The handler should write not more than size bytes to the buffer.
+// The number of written bytes should be set to the size_read variable.
+//
+// [in,out]   data        A pointer to an application data specified by
+//                        yaml_parser_set_input().
+// [out]      buffer      The buffer to write the data from the source.
+// [in]       size        The size of the buffer.
+// [out]      size_read   The actual number of bytes read from the source.
+//
+// On success, the handler should return 1.  If the handler failed,
+// the returned value should be 0. On EOF, the handler should set the
+// size_read to 0 and return 1.
+type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error)
+
+// This structure holds information about a potential simple key.
+type yaml_simple_key_t struct {
+	possible     bool        // Is a simple key possible?
+	required     bool        // Is a simple key required?
+	token_number int         // The number of the token.
+	mark         yaml_mark_t // The position mark.
+}
+
+// The states of the parser.
+type yaml_parser_state_t int
+
+const (
+	yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota
+
+	yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE           // Expect the beginning of an implicit document.
+	yaml_PARSE_DOCUMENT_START_STATE                    // Expect DOCUMENT-START.
+	yaml_PARSE_DOCUMENT_CONTENT_STATE                  // Expect the content of a document.
+	yaml_PARSE_DOCUMENT_END_STATE                      // Expect DOCUMENT-END.
+	yaml_PARSE_BLOCK_NODE_STATE                        // Expect a block node.
+	yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence.
+	yaml_PARSE_FLOW_NODE_STATE                         // Expect a flow node.
+	yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE        // Expect the first entry of a block sequence.
+	yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE              // Expect an entry of a block sequence.
+	yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE         // Expect an entry of an indentless sequence.
+	yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE           // Expect the first key of a block mapping.
+	yaml_PARSE_BLOCK_MAPPING_KEY_STATE                 // Expect a block mapping key.
+	yaml_PARSE_BLOCK_MAPPING_VALUE_STATE               // Expect a block mapping value.
+	yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE         // Expect the first entry of a flow sequence.
+	yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE               // Expect an entry of a flow sequence.
+	yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE   // Expect a key of an ordered mapping.
+	yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping.
+	yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE   // Expect the and of an ordered mapping entry.
+	yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE            // Expect the first key of a flow mapping.
+	yaml_PARSE_FLOW_MAPPING_KEY_STATE                  // Expect a key of a flow mapping.
+	yaml_PARSE_FLOW_MAPPING_VALUE_STATE                // Expect a value of a flow mapping.
+	yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE          // Expect an empty value of a flow mapping.
+	yaml_PARSE_END_STATE                               // Expect nothing.
+)
+
+func (ps yaml_parser_state_t) String() string {
+	switch ps {
+	case yaml_PARSE_STREAM_START_STATE:
+		return "yaml_PARSE_STREAM_START_STATE"
+	case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE:
+		return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE"
+	case yaml_PARSE_DOCUMENT_START_STATE:
+		return "yaml_PARSE_DOCUMENT_START_STATE"
+	case yaml_PARSE_DOCUMENT_CONTENT_STATE:
+		return "yaml_PARSE_DOCUMENT_CONTENT_STATE"
+	case yaml_PARSE_DOCUMENT_END_STATE:
+		return "yaml_PARSE_DOCUMENT_END_STATE"
+	case yaml_PARSE_BLOCK_NODE_STATE:
+		return "yaml_PARSE_BLOCK_NODE_STATE"
+	case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE:
+		return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE"
+	case yaml_PARSE_FLOW_NODE_STATE:
+		return "yaml_PARSE_FLOW_NODE_STATE"
+	case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE:
+		return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE"
+	case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE:
+		return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE"
+	case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE:
+		return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE"
+	case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE:
+		return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE"
+	case yaml_PARSE_BLOCK_MAPPING_KEY_STATE:
+		return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE"
+	case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE:
+		return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE"
+	case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE:
+		return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE"
+	case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE:
+		return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE"
+	case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE:
+		return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE"
+	case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE:
+		return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE"
+	case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE:
+		return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE"
+	case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE:
+		return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE"
+	case yaml_PARSE_FLOW_MAPPING_KEY_STATE:
+		return "yaml_PARSE_FLOW_MAPPING_KEY_STATE"
+	case yaml_PARSE_FLOW_MAPPING_VALUE_STATE:
+		return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE"
+	case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE:
+		return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE"
+	case yaml_PARSE_END_STATE:
+		return "yaml_PARSE_END_STATE"
+	}
+	return "<unknown parser state>"
+}
+
+// This structure holds aliases data.
+type yaml_alias_data_t struct {
+	anchor []byte      // The anchor.
+	index  int         // The node id.
+	mark   yaml_mark_t // The anchor mark.
+}
+
+// The parser structure.
+//
+// All members are internal. Manage the structure using the
+// yaml_parser_ family of functions.
+type yaml_parser_t struct {
+
+	// Error handling
+
+	error yaml_error_type_t // Error type.
+
+	problem string // Error description.
+
+	// The byte about which the problem occured.
+	problem_offset int
+	problem_value  int
+	problem_mark   yaml_mark_t
+
+	// The error context.
+	context      string
+	context_mark yaml_mark_t
+
+	// Reader stuff
+
+	read_handler yaml_read_handler_t // Read handler.
+
+	input_file io.Reader // File input data.
+	input      []byte    // String input data.
+	input_pos  int
+
+	eof bool // EOF flag
+
+	buffer     []byte // The working buffer.
+	buffer_pos int    // The current position of the buffer.
+
+	unread int // The number of unread characters in the buffer.
+
+	raw_buffer     []byte // The raw buffer.
+	raw_buffer_pos int    // The current position of the buffer.
+
+	encoding yaml_encoding_t // The input encoding.
+
+	offset int         // The offset of the current position (in bytes).
+	mark   yaml_mark_t // The mark of the current position.
+
+	// Scanner stuff
+
+	stream_start_produced bool // Have we started to scan the input stream?
+	stream_end_produced   bool // Have we reached the end of the input stream?
+
+	flow_level int // The number of unclosed '[' and '{' indicators.
+
+	tokens          []yaml_token_t // The tokens queue.
+	tokens_head     int            // The head of the tokens queue.
+	tokens_parsed   int            // The number of tokens fetched from the queue.
+	token_available bool           // Does the tokens queue contain a token ready for dequeueing.
+
+	indent  int   // The current indentation level.
+	indents []int // The indentation levels stack.
+
+	simple_key_allowed bool                // May a simple key occur at the current position?
+	simple_keys        []yaml_simple_key_t // The stack of simple keys.
+
+	// Parser stuff
+
+	state          yaml_parser_state_t    // The current parser state.
+	states         []yaml_parser_state_t  // The parser states stack.
+	marks          []yaml_mark_t          // The stack of marks.
+	tag_directives []yaml_tag_directive_t // The list of TAG directives.
+
+	// Dumper stuff
+
+	aliases []yaml_alias_data_t // The alias data.
+
+	document *yaml_document_t // The currently parsed document.
+}
+
+// Emitter Definitions
+
+// The prototype of a write handler.
+//
+// The write handler is called when the emitter needs to flush the accumulated
+// characters to the output.  The handler should write @a size bytes of the
+// @a buffer to the output.
+//
+// @param[in,out]   data        A pointer to an application data specified by
+//                              yaml_emitter_set_output().
+// @param[in]       buffer      The buffer with bytes to be written.
+// @param[in]       size        The size of the buffer.
+//
+// @returns On success, the handler should return @c 1.  If the handler failed,
+// the returned value should be @c 0.
+//
+type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
+
+type yaml_emitter_state_t int
+
+// The emitter states.
+const (
+	// Expect STREAM-START.
+	yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota
+
+	yaml_EMIT_FIRST_DOCUMENT_START_STATE       // Expect the first DOCUMENT-START or STREAM-END.
+	yaml_EMIT_DOCUMENT_START_STATE             // Expect DOCUMENT-START or STREAM-END.
+	yaml_EMIT_DOCUMENT_CONTENT_STATE           // Expect the content of a document.
+	yaml_EMIT_DOCUMENT_END_STATE               // Expect DOCUMENT-END.
+	yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE   // Expect the first item of a flow sequence.
+	yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE         // Expect an item of a flow sequence.
+	yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE     // Expect the first key of a flow mapping.
+	yaml_EMIT_FLOW_MAPPING_KEY_STATE           // Expect a key of a flow mapping.
+	yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE  // Expect a value for a simple key of a flow mapping.
+	yaml_EMIT_FLOW_MAPPING_VALUE_STATE         // Expect a value of a flow mapping.
+	yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE  // Expect the first item of a block sequence.
+	yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE        // Expect an item of a block sequence.
+	yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE    // Expect the first key of a block mapping.
+	yaml_EMIT_BLOCK_MAPPING_KEY_STATE          // Expect the key of a block mapping.
+	yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping.
+	yaml_EMIT_BLOCK_MAPPING_VALUE_STATE        // Expect a value of a block mapping.
+	yaml_EMIT_END_STATE                        // Expect nothing.
+)
+
+// The emitter structure.
+//
+// All members are internal.  Manage the structure using the @c yaml_emitter_
+// family of functions.
+type yaml_emitter_t struct {
+
+	// Error handling
+
+	error   yaml_error_type_t // Error type.
+	problem string            // Error description.
+
+	// Writer stuff
+
+	write_handler yaml_write_handler_t // Write handler.
+
+	output_buffer *[]byte   // String output data.
+	output_file   io.Writer // File output data.
+
+	buffer     []byte // The working buffer.
+	buffer_pos int    // The current position of the buffer.
+
+	raw_buffer     []byte // The raw buffer.
+	raw_buffer_pos int    // The current position of the buffer.
+
+	encoding yaml_encoding_t // The stream encoding.
+
+	// Emitter stuff
+
+	canonical   bool         // If the output is in the canonical style?
+	best_indent int          // The number of indentation spaces.
+	best_width  int          // The preferred width of the output lines.
+	unicode     bool         // Allow unescaped non-ASCII characters?
+	line_break  yaml_break_t // The preferred line break.
+
+	state  yaml_emitter_state_t   // The current emitter state.
+	states []yaml_emitter_state_t // The stack of states.
+
+	events      []yaml_event_t // The event queue.
+	events_head int            // The head of the event queue.
+
+	indents []int // The stack of indentation levels.
+
+	tag_directives []yaml_tag_directive_t // The list of tag directives.
+
+	indent int // The current indentation level.
+
+	flow_level int // The current flow level.
+
+	root_context       bool // Is it the document root context?
+	sequence_context   bool // Is it a sequence context?
+	mapping_context    bool // Is it a mapping context?
+	simple_key_context bool // Is it a simple mapping key context?
+
+	line       int  // The current line.
+	column     int  // The current column.
+	whitespace bool // If the last character was a whitespace?
+	indention  bool // If the last character was an indentation character (' ', '-', '?', ':')?
+	open_ended bool // If an explicit document end is required?
+
+	// Anchor analysis.
+	anchor_data struct {
+		anchor []byte // The anchor value.
+		alias  bool   // Is it an alias?
+	}
+
+	// Tag analysis.
+	tag_data struct {
+		handle []byte // The tag handle.
+		suffix []byte // The tag suffix.
+	}
+
+	// Scalar analysis.
+	scalar_data struct {
+		value                 []byte              // The scalar value.
+		multiline             bool                // Does the scalar contain line breaks?
+		flow_plain_allowed    bool                // Can the scalar be expessed in the flow plain style?
+		block_plain_allowed   bool                // Can the scalar be expressed in the block plain style?
+		single_quoted_allowed bool                // Can the scalar be expressed in the single quoted style?
+		block_allowed         bool                // Can the scalar be expressed in the literal or folded styles?
+		style                 yaml_scalar_style_t // The output style.
+	}
+
+	// Dumper stuff
+
+	opened bool // If the stream was already opened?
+	closed bool // If the stream was already closed?
+
+	// The information associated with the document nodes.
+	anchors *struct {
+		references int  // The number of references.
+		anchor     int  // The anchor id.
+		serialized bool // If the node has been emitted?
+	}
+
+	last_anchor_id int // The last assigned anchor id.
+
+	document *yaml_document_t // The currently emitted document.
+}

+ 173 - 0
_vendor/src/gopkg.in/yaml.v2/yamlprivateh.go

@@ -0,0 +1,173 @@
+package yaml
+
+const (
+	// The size of the input raw buffer.
+	input_raw_buffer_size = 512
+
+	// The size of the input buffer.
+	// It should be possible to decode the whole raw buffer.
+	input_buffer_size = input_raw_buffer_size * 3
+
+	// The size of the output buffer.
+	output_buffer_size = 128
+
+	// The size of the output raw buffer.
+	// It should be possible to encode the whole output buffer.
+	output_raw_buffer_size = (output_buffer_size*2 + 2)
+
+	// The size of other stacks and queues.
+	initial_stack_size  = 16
+	initial_queue_size  = 16
+	initial_string_size = 16
+)
+
+// Check if the character at the specified position is an alphabetical
+// character, a digit, '_', or '-'.
+func is_alpha(b []byte, i int) bool {
+	return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-'
+}
+
+// Check if the character at the specified position is a digit.
+func is_digit(b []byte, i int) bool {
+	return b[i] >= '0' && b[i] <= '9'
+}
+
+// Get the value of a digit.
+func as_digit(b []byte, i int) int {
+	return int(b[i]) - '0'
+}
+
+// Check if the character at the specified position is a hex-digit.
+func is_hex(b []byte, i int) bool {
+	return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f'
+}
+
+// Get the value of a hex-digit.
+func as_hex(b []byte, i int) int {
+	bi := b[i]
+	if bi >= 'A' && bi <= 'F' {
+		return int(bi) - 'A' + 10
+	}
+	if bi >= 'a' && bi <= 'f' {
+		return int(bi) - 'a' + 10
+	}
+	return int(bi) - '0'
+}
+
+// Check if the character is ASCII.
+func is_ascii(b []byte, i int) bool {
+	return b[i] <= 0x7F
+}
+
+// Check if the character at the start of the buffer can be printed unescaped.
+func is_printable(b []byte, i int) bool {
+	return ((b[i] == 0x0A) || // . == #x0A
+		(b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E
+		(b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF
+		(b[i] > 0xC2 && b[i] < 0xED) ||
+		(b[i] == 0xED && b[i+1] < 0xA0) ||
+		(b[i] == 0xEE) ||
+		(b[i] == 0xEF && // #xE000 <= . <= #xFFFD
+			!(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF
+			!(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF))))
+}
+
+// Check if the character at the specified position is NUL.
+func is_z(b []byte, i int) bool {
+	return b[i] == 0x00
+}
+
+// Check if the beginning of the buffer is a BOM.
+func is_bom(b []byte, i int) bool {
+	return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
+}
+
+// Check if the character at the specified position is space.
+func is_space(b []byte, i int) bool {
+	return b[i] == ' '
+}
+
+// Check if the character at the specified position is tab.
+func is_tab(b []byte, i int) bool {
+	return b[i] == '\t'
+}
+
+// Check if the character at the specified position is blank (space or tab).
+func is_blank(b []byte, i int) bool {
+	//return is_space(b, i) || is_tab(b, i)
+	return b[i] == ' ' || b[i] == '\t'
+}
+
+// Check if the character at the specified position is a line break.
+func is_break(b []byte, i int) bool {
+	return (b[i] == '\r' || // CR (#xD)
+		b[i] == '\n' || // LF (#xA)
+		b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
+		b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
+		b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029)
+}
+
+func is_crlf(b []byte, i int) bool {
+	return b[i] == '\r' && b[i+1] == '\n'
+}
+
+// Check if the character is a line break or NUL.
+func is_breakz(b []byte, i int) bool {
+	//return is_break(b, i) || is_z(b, i)
+	return (        // is_break:
+	b[i] == '\r' || // CR (#xD)
+		b[i] == '\n' || // LF (#xA)
+		b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
+		b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
+		b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
+		// is_z:
+		b[i] == 0)
+}
+
+// Check if the character is a line break, space, or NUL.
+func is_spacez(b []byte, i int) bool {
+	//return is_space(b, i) || is_breakz(b, i)
+	return ( // is_space:
+	b[i] == ' ' ||
+		// is_breakz:
+		b[i] == '\r' || // CR (#xD)
+		b[i] == '\n' || // LF (#xA)
+		b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
+		b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
+		b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
+		b[i] == 0)
+}
+
+// Check if the character is a line break, space, tab, or NUL.
+func is_blankz(b []byte, i int) bool {
+	//return is_blank(b, i) || is_breakz(b, i)
+	return ( // is_blank:
+	b[i] == ' ' || b[i] == '\t' ||
+		// is_breakz:
+		b[i] == '\r' || // CR (#xD)
+		b[i] == '\n' || // LF (#xA)
+		b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
+		b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
+		b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
+		b[i] == 0)
+}
+
+// Determine the width of the character.
+func width(b byte) int {
+	// Don't replace these by a switch without first
+	// confirming that it is being inlined.
+	if b&0x80 == 0x00 {
+		return 1
+	}
+	if b&0xE0 == 0xC0 {
+		return 2
+	}
+	if b&0xF0 == 0xE0 {
+		return 3
+	}
+	if b&0xF8 == 0xF0 {
+		return 4
+	}
+	return 0
+
+}

+ 93 - 0
config.go

@@ -0,0 +1,93 @@
+package main
+
+import (
+	"encoding/hex"
+	"flag"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+
+	"gopkg.in/yaml.v2"
+)
+
+type config struct {
+	Bind         string
+	ReadTimeout  int `yaml:"read_timeout"`
+	WriteTimeout int `yaml:"write_timeout"`
+
+	Key     string
+	Salt    string
+	KeyBin  []byte
+	SaltBin []byte
+
+	MaxSrcDimension int `yaml:"max_src_dimension"`
+
+	Quality     int
+	Compression int
+}
+
+var conf = config{
+	Bind:            ":8080",
+	MaxSrcDimension: 4096,
+}
+
+func absPathToFile(path string) string {
+	if filepath.IsAbs(path) {
+		return path
+	}
+
+	appPath, _ := filepath.Abs(filepath.Dir(os.Args[0]))
+	return filepath.Join(appPath, path)
+}
+
+func init() {
+	cpath := flag.String(
+		"config", "../config.yml", "path to configuration file",
+	)
+	flag.Parse()
+
+	file, err := os.Open(absPathToFile(*cpath))
+	if err != nil {
+		log.Fatalln(err)
+	}
+	defer file.Close()
+
+	cdata, err := ioutil.ReadAll(file)
+	if err != nil {
+		log.Fatalln(err)
+	}
+
+	err = yaml.Unmarshal(cdata, &conf)
+	if err != nil {
+		log.Fatalln(err)
+	}
+
+	if len(conf.Bind) == 0 {
+		conf.Bind = ":8080"
+	}
+
+	if conf.MaxSrcDimension == 0 {
+		conf.MaxSrcDimension = 4096
+	}
+
+	if conf.KeyBin, err = hex.DecodeString(conf.Key); err != nil {
+		log.Fatalln("Invalid key. Key should be encoded to hex")
+	}
+
+	if conf.SaltBin, err = hex.DecodeString(conf.Salt); err != nil {
+		log.Fatalln("Invalid salt. Salt should be encoded to hex")
+	}
+
+	if conf.MaxSrcDimension == 0 {
+		conf.MaxSrcDimension = 4096
+	}
+
+	if conf.Quality == 0 {
+		conf.Quality = 80
+	}
+
+	if conf.Compression == 0 {
+		conf.Compression = 6
+	}
+}

+ 12 - 0
config.yml.example

@@ -0,0 +1,12 @@
+bind: ":8080"
+read_timeout: 10
+write_timeout: 10
+
+# key and salt are hex-encoded
+key: 943b421c9eb07c830af81030552c86009268de4e532ba2ee2eab8247c6da0881
+salt: 520f986b998545b4785e0defbc4f3c1203f22de2374a3d53cb7a7fe9fea309c5
+
+max_src_dimension: 4096
+
+quality: 80
+compression: 6

+ 26 - 0
crypt.go

@@ -0,0 +1,26 @@
+package main
+
+import (
+	"crypto/hmac"
+	"crypto/sha256"
+	"encoding/base64"
+	"errors"
+)
+
+func validatePath(token, path string) error {
+	messageMAC, err := base64.RawURLEncoding.DecodeString(token)
+	if err != nil {
+		return errors.New("Invalid token encoding")
+	}
+
+	mac := hmac.New(sha256.New, conf.KeyBin)
+	mac.Write(conf.SaltBin)
+	mac.Write([]byte(path))
+	expectedMAC := mac.Sum(nil)
+
+	if !hmac.Equal(messageMAC, expectedMAC) {
+		return errors.New("Invalid token")
+	}
+
+	return nil
+}

+ 59 - 0
download.go

@@ -0,0 +1,59 @@
+package main
+
+import (
+	"bytes"
+	"errors"
+	"image"
+	"net/http"
+
+	_ "image/gif"
+	_ "image/jpeg"
+	_ "image/png"
+)
+
+const chunkSize = 4096
+
+func checkTypeAndDimensions(b []byte) error {
+	imgconf, _, err := image.DecodeConfig(bytes.NewReader(b))
+	if err != nil {
+		return err
+	}
+	if imgconf.Width > conf.MaxSrcDimension || imgconf.Height > conf.MaxSrcDimension {
+		return errors.New("File is too big")
+	}
+	return nil
+}
+
+func readAndCheckImage(res *http.Response) ([]byte, error) {
+	b := make([]byte, chunkSize)
+	n, err := res.Body.Read(b)
+	if err != nil {
+		return nil, err
+	}
+
+	if err = checkTypeAndDimensions(b[:n]); err != nil {
+		return nil, err
+	}
+
+	buf := bytes.NewBuffer(b[:n])
+
+	if res.ContentLength > 0 {
+		buf.Grow(int(res.ContentLength))
+	}
+
+	if _, err = buf.ReadFrom(res.Body); err != nil {
+		return nil, err
+	}
+
+	return buf.Bytes(), nil
+}
+
+func downloadImage(url string) ([]byte, error) {
+	res, err := http.Get(url)
+	if err != nil {
+		return nil, err
+	}
+	defer res.Body.Close()
+
+	return readAndCheckImage(res)
+}

+ 147 - 0
main.go

@@ -0,0 +1,147 @@
+package main
+
+import (
+	"bytes"
+	"encoding/base64"
+	"errors"
+	"fmt"
+	"image"
+	"log"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type httpHandler struct{}
+
+func parsePath(r *http.Request) (string, processingOptions, error) {
+	var po processingOptions
+	var err error
+
+	path := r.URL.Path
+	parts := strings.Split(strings.TrimPrefix(path, "/"), "/")
+
+	if len(parts) < 7 {
+		return "", po, errors.New("Invalid path")
+	}
+
+	token := parts[0]
+
+	if err = validatePath(token, strings.TrimPrefix(path, fmt.Sprintf("/%s", token))); err != nil {
+		return "", po, err
+	}
+
+	po.resize = parts[1]
+
+	if po.width, err = strconv.Atoi(parts[2]); err != nil {
+		return "", po, fmt.Errorf("Invalid width: %s", parts[2])
+	}
+
+	if po.height, err = strconv.Atoi(parts[3]); err != nil {
+		return "", po, fmt.Errorf("Invalid height: %s", parts[3])
+	}
+
+	if g, ok := gravityTypes[parts[4]]; ok {
+		po.gravity = g
+	} else {
+		return "", po, fmt.Errorf("Invalid gravity: %s", parts[4])
+	}
+
+	po.enlarge = parts[5] != "0"
+
+	filenameParts := strings.Split(strings.Join(parts[6:], ""), ".")
+
+	if len(filenameParts) < 2 {
+		po.format = imageTypes["jpg"]
+	} else if f, ok := imageTypes[filenameParts[1]]; ok {
+		po.format = f
+	} else {
+		return "", po, fmt.Errorf("Invalid image format: %s", filenameParts[1])
+	}
+
+	filename, err := base64.RawURLEncoding.DecodeString(filenameParts[0])
+	if err != nil {
+		return "", po, errors.New("Invalid filename encoding")
+	}
+
+	return string(filename), po, nil
+}
+
+func imageContentType(b []byte) string {
+	_, imgtype, _ := image.DecodeConfig(bytes.NewReader(b))
+	return fmt.Sprintf("image/%s", imgtype)
+}
+
+func logResponse(status int, msg string) {
+	var color int
+
+	if status > 500 {
+		color = 31
+	} else if status > 400 {
+		color = 33
+	} else {
+		color = 32
+	}
+
+	log.Printf("|\033[7;%dm %d \033[0m| %s\n", color, status, msg)
+}
+
+func respondWithImage(rw http.ResponseWriter, data []byte, imgURL string, po processingOptions) {
+	logResponse(200, fmt.Sprintf("Processed: %s; %+v", imgURL, po))
+
+	rw.WriteHeader(200)
+	rw.Header().Set("Content-Type", imageContentType(data))
+	rw.Write(data)
+}
+
+func respondWithError(rw http.ResponseWriter, status int, err error, msg string) {
+	logResponse(status, err.Error())
+
+	rw.WriteHeader(status)
+	rw.Write([]byte(msg))
+}
+
+func (h httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+	log.Printf("GET: %s\n", r.URL.RequestURI())
+
+	imgURL, procOpt, err := parsePath(r)
+	if err != nil {
+		respondWithError(rw, 404, err, "Invalid image url")
+		return
+	}
+
+	if _, err = url.ParseRequestURI(imgURL); err != nil {
+		respondWithError(rw, 404, err, "Invalid image url")
+		return
+	}
+
+	b, err := downloadImage(imgURL)
+	if err != nil {
+		respondWithError(rw, 404, err, "Image is unreacable")
+		return
+	}
+
+	b, err = processImage(b, procOpt)
+	if err != nil {
+		respondWithError(rw, 500, err, "Error occured while processing image")
+		return
+	}
+
+	respondWithImage(rw, b, imgURL, procOpt)
+}
+
+func main() {
+	s := &http.Server{
+		Addr:           conf.Bind,
+		Handler:        httpHandler{},
+		ReadTimeout:    time.Duration(conf.ReadTimeout) * time.Second,
+		WriteTimeout:   time.Duration(conf.WriteTimeout) * time.Second,
+		MaxHeaderBytes: 1 << 20,
+	}
+
+	log.Printf("Starting server at %s\n", conf.Bind)
+
+	log.Fatal(s.ListenAndServe())
+}

+ 91 - 0
process.go

@@ -0,0 +1,91 @@
+package main
+
+import (
+	"math"
+
+	"github.com/h2non/bimg"
+)
+
+type processingOptions struct {
+	resize  string
+	width   int
+	height  int
+	gravity bimg.Gravity
+	enlarge bool
+	format  bimg.ImageType
+}
+
+var imageTypes = map[string]bimg.ImageType{
+	"jpeg": bimg.JPEG,
+	"jpg":  bimg.JPEG,
+	"png":  bimg.PNG,
+}
+
+var gravityTypes = map[string]bimg.Gravity{
+	"ce": bimg.GravityCentre,
+	"no": bimg.GravityNorth,
+	"ea": bimg.GravityEast,
+	"so": bimg.GravitySouth,
+	"we": bimg.GravityWest,
+	"sm": bimg.GravitySmart,
+}
+
+func round(f float64) int {
+	return int(f + .5)
+}
+func calcSize(size bimg.ImageSize, po processingOptions) (int, int) {
+	if (po.width == size.Width && po.height == size.Height) || (po.resize != "fill" && po.resize != "fit") {
+		return po.width, po.height
+	}
+
+	fsw, fsh, fow, foh := float64(size.Width), float64(size.Height), float64(po.width), float64(po.height)
+
+	wr := fow / fsw
+	hr := foh / fsh
+
+	var rate float64
+	if po.resize == "fit" {
+		rate = math.Min(wr, hr)
+	} else {
+		rate = math.Max(wr, hr)
+	}
+
+	return round(math.Min(fsw*rate, fow)), round(math.Min(fsh*rate, foh))
+}
+
+func processImage(p []byte, po processingOptions) ([]byte, error) {
+	var err error
+
+	img := bimg.NewImage(p)
+
+	size, err := img.Size()
+	if err != nil {
+		return nil, err
+	}
+
+	// Default options
+	opts := bimg.Options{
+		Interpolator: bimg.Bicubic,
+		Quality:      conf.Quality,
+		Compression:  conf.Compression,
+		Gravity:      po.gravity,
+		Enlarge:      po.enlarge,
+		Type:         po.format,
+	}
+
+	opts.Width, opts.Height = calcSize(size, po)
+
+	switch po.resize {
+	case "fit":
+		opts.Embed = true
+	case "fill":
+		opts.Embed = true
+		opts.Crop = true
+	case "crop":
+		opts.Crop = true
+	default:
+		opts.Force = true
+	}
+
+	return img.Process(opts)
+}

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.