<commit_before>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package cache

import (
	"math"
	"testing"
	"time"
)

// Tests against a generic Cache interface.
// They should pass for all implementations.
type cacheFactory func(*testing.T, time.Duration) Cache

// Test typical cache interactions
func typicalGetSet(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)

	value := "foo"
	if err = cache.Set("value", value, DefaultExpiryTime); err != nil {
		t.Errorf("Error setting a value: %s", err)
	}

	value = ""
	err = cache.Get("value", &value)
	if err != nil {
		t.Errorf("Error getting a value: %s", err)
	}
	if value != "foo" {
		t.Errorf("Expected to get foo back, got %s", value)
	}
}

// Test the increment-decrement cases
func incrDecr(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)

	// Normal increment / decrement operation.
	if err = cache.Set("int", 10, ForEverNeverExpiry); err != nil {
		t.Errorf("Error setting int: %s", err)
	}
	newValue, err := cache.Increment("int", 50)
	if err != nil {
		t.Errorf("Error incrementing int: %s", err)
	}
	if newValue != 60 {
		t.Errorf("Expected 60, was %d", newValue)
	}

	if newValue, err = cache.Decrement("int", 50); err != nil {
		t.Errorf("Error decrementing: %s", err)
	}
	if newValue != 10 {
		t.Errorf("Expected 10, was %d", newValue)
	}

	// Increment wraparound
	newValue, err = cache.Increment("int", math.MaxUint64-5)
	if err != nil {
		t.Errorf("Error wrapping around: %s", err)
	}
	if newValue != 4 {
		t.Errorf("Expected wraparound 4, got %d", newValue)
	}

	// Decrement capped at 0
	newValue, err = cache.Decrement("int", 25)
	if err != nil {
		t.Errorf("Error decrementing below 0: %s", err)
	}
	if newValue != 0 {
		t.Errorf("Expected capped at 0, got %d", newValue)
	}
}

func expiration(t *testing.T, newCache cacheFactory) {
	// memcached does not support expiration times less than 1 second.
	var err error
	cache := newCache(t, time.Second)
	// Test Set w/ DefaultExpiryTime
	value := 10
	if err = cache.Set("int", value, DefaultExpiryTime); err != nil {
		t.Errorf("Set failed: %s", err)
	}
	time.Sleep(2 * time.Second)
	if err = cache.Get("int", &value); err != ErrCacheMiss {
		t.Errorf("Expected CacheMiss, but got: %s", err)
	}

	// Test Set w/ short time
	if err = cache.Set("int", value, time.Second); err != nil {
		t.Errorf("Set failed: %s", err)
	}
	time.Sleep(2 * time.Second)
	if err = cache.Get("int", &value); err != ErrCacheMiss {
		t.Errorf("Expected CacheMiss, but got: %s", err)
	}

	// Test Set w/ longer time.
	if err = cache.Set("int", value, time.Hour); err != nil {
		t.Errorf("Set failed: %s", err)
	}
	time.Sleep(2 * time.Second)
	if err = cache.Get("int", &value); err != nil {
		t.Errorf("Expected to get the value, but got: %s", err)
	}

	// Test Set w/ forever.
	if err = cache.Set("int", value, ForEverNeverExpiry); err != nil {
		t.Errorf("Set failed: %s", err)
	}
	time.Sleep(2 * time.Second)
	if err = cache.Get("int", &value); err != nil {
		t.Errorf("Expected to get the value, but got: %s", err)
	}
}

func emptyCache(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)

	err = cache.Get("notexist", 0)
	if err == nil {
		t.Errorf("Error expected for non-existent key")
	}
	if err != ErrCacheMiss {
		t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
	}

	err = cache.Delete("notexist")
	if err != ErrCacheMiss {
		t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
	}

	_, err = cache.Increment("notexist", 1)
	if err != ErrCacheMiss {
		t.Errorf("Expected cache miss incrementing non-existent key: %s", err)
	}

	_, err = cache.Decrement("notexist", 1)
	if err != ErrCacheMiss {
		t.Errorf("Expected cache miss decrementing non-existent key: %s", err)
	}
}

func testReplace(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)

	// Replace in an empty cache.
	if err = cache.Replace("notexist", 1, ForEverNeverExpiry); err != ErrNotStored {
		t.Errorf("Replace in empty cache: expected ErrNotStored, got: %s", err)
	}

	// Set a value of 1, and replace it with 2
	if err = cache.Set("int", 1, time.Second); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}

	if err = cache.Replace("int", 2, time.Second); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}
	var i int
	if err = cache.Get("int", &i); err != nil {
		t.Errorf("Unexpected error getting a replaced item: %s", err)
	}
	if i != 2 {
		t.Errorf("Expected 2, got %d", i)
	}

	// Wait for it to expire and replace with 3 (unsuccessfully).
	time.Sleep(2 * time.Second)
	if err = cache.Replace("int", 3, time.Second); err != ErrNotStored {
		t.Errorf("Expected ErrNotStored, got: %s", err)
	}
	if err = cache.Get("int", &i); err != ErrCacheMiss {
		t.Errorf("Expected cache miss, got: %s", err)
	}
}

func testAdd(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)
	// Add to an empty cache.
	if err = cache.Add("int", 1, time.Second*3); err != nil {
		t.Errorf("Unexpected error adding to empty cache: %s", err)
	}

	// Try to add again. (fail)
	if err = cache.Add("int", 2, time.Second*3); err != nil {
		if err != ErrNotStored {
			t.Errorf("Expected ErrNotStored adding dupe to cache: %s", err)
		}
	}

	// Wait for it to expire, and add again.
	time.Sleep(8 * time.Second)
	if err = cache.Add("int", 3, time.Second*5); err != nil {
		t.Errorf("Unexpected error adding to cache: %s", err)
	}

	// Get and verify the value.
	var i int
	if err = cache.Get("int", &i); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}
	if i != 3 {
		t.Errorf("Expected 3, got: %d", i)
	}
}

func testGetMulti(t *testing.T, newCache cacheFactory) {
	cache := newCache(t, time.Hour)

	m := map[string]interface{}{
		"str": "foo",
		"num": 42,
		"foo": struct{ Bar string }{"baz"},
	}

	var keys []string
	for key, value := range m {
		keys = append(keys, key)
		if err := cache.Set(key, value, time.Second*10); err != nil {
			t.Errorf("Error setting a value: %s", err)
		}
	}

	g, err := cache.GetMulti(keys...)
	if err != nil {
		t.Errorf("Error in get-multi: %s", err)
	}

	var str string
	if err = g.Get("str", &str); err != nil || str != "foo" {
		t.Errorf("Error getting str: %s / %s", err, str)
	}

	var num int
	if err = g.Get("num", &num); err != nil || num != 42 {
		t.Errorf("Error getting num: %s / %v", err, num)
	}

	var foo struct{ Bar string }
	if err = g.Get("foo", &foo); err != nil || foo.Bar != "baz" {
		t.Errorf("Error getting foo: %s / %v", err, foo)
	}
}
<commit_msg>change expiretime for fix `cache_test.go:240: Error getting str: revel/cache: key not found / `<commit_after>// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package cache

import (
	"math"
	"testing"
	"time"
)

// Tests against a generic Cache interface.
// They should pass for all implementations.
type cacheFactory func(*testing.T, time.Duration) Cache

// Test typical cache interactions
func typicalGetSet(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)

	value := "foo"
	if err = cache.Set("value", value, DefaultExpiryTime); err != nil {
		t.Errorf("Error setting a value: %s", err)
	}

	value = ""
	err = cache.Get("value", &value)
	if err != nil {
		t.Errorf("Error getting a value: %s", err)
	}
	if value != "foo" {
		t.Errorf("Expected to get foo back, got %s", value)
	}
}

// Test the increment-decrement cases
func incrDecr(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)

	// Normal increment / decrement operation.
	if err = cache.Set("int", 10, ForEverNeverExpiry); err != nil {
		t.Errorf("Error setting int: %s", err)
	}
	newValue, err := cache.Increment("int", 50)
	if err != nil {
		t.Errorf("Error incrementing int: %s", err)
	}
	if newValue != 60 {
		t.Errorf("Expected 60, was %d", newValue)
	}

	if newValue, err = cache.Decrement("int", 50); err != nil {
		t.Errorf("Error decrementing: %s", err)
	}
	if newValue != 10 {
		t.Errorf("Expected 10, was %d", newValue)
	}

	// Increment wraparound
	newValue, err = cache.Increment("int", math.MaxUint64-5)
	if err != nil {
		t.Errorf("Error wrapping around: %s", err)
	}
	if newValue != 4 {
		t.Errorf("Expected wraparound 4, got %d", newValue)
	}

	// Decrement capped at 0
	newValue, err = cache.Decrement("int", 25)
	if err != nil {
		t.Errorf("Error decrementing below 0: %s", err)
	}
	if newValue != 0 {
		t.Errorf("Expected capped at 0, got %d", newValue)
	}
}

func expiration(t *testing.T, newCache cacheFactory) {
	// memcached does not support expiration times less than 1 second.
	var err error
	cache := newCache(t, time.Second)
	// Test Set w/ DefaultExpiryTime
	value := 10
	if err = cache.Set("int", value, DefaultExpiryTime); err != nil {
		t.Errorf("Set failed: %s", err)
	}
	time.Sleep(2 * time.Second)
	if err = cache.Get("int", &value); err != ErrCacheMiss {
		t.Errorf("Expected CacheMiss, but got: %s", err)
	}

	// Test Set w/ short time
	if err = cache.Set("int", value, time.Second); err != nil {
		t.Errorf("Set failed: %s", err)
	}
	time.Sleep(2 * time.Second)
	if err = cache.Get("int", &value); err != ErrCacheMiss {
		t.Errorf("Expected CacheMiss, but got: %s", err)
	}

	// Test Set w/ longer time.
	if err = cache.Set("int", value, time.Hour); err != nil {
		t.Errorf("Set failed: %s", err)
	}
	time.Sleep(2 * time.Second)
	if err = cache.Get("int", &value); err != nil {
		t.Errorf("Expected to get the value, but got: %s", err)
	}

	// Test Set w/ forever.
	if err = cache.Set("int", value, ForEverNeverExpiry); err != nil {
		t.Errorf("Set failed: %s", err)
	}
	time.Sleep(2 * time.Second)
	if err = cache.Get("int", &value); err != nil {
		t.Errorf("Expected to get the value, but got: %s", err)
	}
}

func emptyCache(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)

	err = cache.Get("notexist", 0)
	if err == nil {
		t.Errorf("Error expected for non-existent key")
	}
	if err != ErrCacheMiss {
		t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
	}

	err = cache.Delete("notexist")
	if err != ErrCacheMiss {
		t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
	}

	_, err = cache.Increment("notexist", 1)
	if err != ErrCacheMiss {
		t.Errorf("Expected cache miss incrementing non-existent key: %s", err)
	}

	_, err = cache.Decrement("notexist", 1)
	if err != ErrCacheMiss {
		t.Errorf("Expected cache miss decrementing non-existent key: %s", err)
	}
}

func testReplace(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)

	// Replace in an empty cache.
	if err = cache.Replace("notexist", 1, ForEverNeverExpiry); err != ErrNotStored {
		t.Errorf("Replace in empty cache: expected ErrNotStored, got: %s", err)
	}

	// Set a value of 1, and replace it with 2
	if err = cache.Set("int", 1, time.Second); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}

	if err = cache.Replace("int", 2, time.Second); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}
	var i int
	if err = cache.Get("int", &i); err != nil {
		t.Errorf("Unexpected error getting a replaced item: %s", err)
	}
	if i != 2 {
		t.Errorf("Expected 2, got %d", i)
	}

	// Wait for it to expire and replace with 3 (unsuccessfully).
	time.Sleep(2 * time.Second)
	if err = cache.Replace("int", 3, time.Second); err != ErrNotStored {
		t.Errorf("Expected ErrNotStored, got: %s", err)
	}
	if err = cache.Get("int", &i); err != ErrCacheMiss {
		t.Errorf("Expected cache miss, got: %s", err)
	}
}

func testAdd(t *testing.T, newCache cacheFactory) {
	var err error
	cache := newCache(t, time.Hour)
	// Add to an empty cache.
	if err = cache.Add("int", 1, time.Second*3); err != nil {
		t.Errorf("Unexpected error adding to empty cache: %s", err)
	}

	// Try to add again. (fail)
	if err = cache.Add("int", 2, time.Second*3); err != nil {
		if err != ErrNotStored {
			t.Errorf("Expected ErrNotStored adding dupe to cache: %s", err)
		}
	}

	// Wait for it to expire, and add again.
	time.Sleep(8 * time.Second)
	if err = cache.Add("int", 3, time.Second*5); err != nil {
		t.Errorf("Unexpected error adding to cache: %s", err)
	}

	// Get and verify the value.
	var i int
	if err = cache.Get("int", &i); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}
	if i != 3 {
		t.Errorf("Expected 3, got: %d", i)
	}
}

func testGetMulti(t *testing.T, newCache cacheFactory) {
	cache := newCache(t, time.Hour)

	m := map[string]interface{}{
		"str": "foo",
		"num": 42,
		"foo": struct{ Bar string }{"baz"},
	}

	var keys []string
	for key, value := range m {
		keys = append(keys, key)
		if err := cache.Set(key, value, time.Second*30); err != nil {
			t.Errorf("Error setting a value: %s", err)
		}
	}

	g, err := cache.GetMulti(keys...)
	if err != nil {
		t.Errorf("Error in get-multi: %s", err)
	}

	var str string
	if err = g.Get("str", &str); err != nil || str != "foo" {
		t.Errorf("Error getting str: %s / %s", err, str)
	}

	var num int
	if err = g.Get("num", &num); err != nil || num != 42 {
		t.Errorf("Error getting num: %s / %v", err, num)
	}

	var foo struct{ Bar string }
	if err = g.Get("foo", &foo); err != nil || foo.Bar != "baz" {
		t.Errorf("Error getting foo: %s / %v", err, foo)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Fission Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cache

import "testing"
import "log"
import "time"

func checkErr(err error) {
	if err != nil {
		log.Panicf("err: %v", err)
	}
}

func TestCache(t *testing.T) {
	c := MakeCache(100 * time.Millisecond)

	err := c.Set("a", "b")
	checkErr(err)
	err = c.Set("p", "q")
	checkErr(err)

	val, err := c.Get("a")
	checkErr(err)
	if val != "b" {
		log.Panicf("value %v", val)
	}

	cc := c.Copy()
	if len(cc) != 2 {
		log.Panicf("expected 2 items")
	}

	err = c.Delete("a")
	checkErr(err)

	_, err = c.Get("a")
	if err == nil {
		log.Panicf("found deleted element")
	}

	err = c.Set("expires", "42")
	checkErr(err)
	time.Sleep(150 * time.Millisecond)
	_, err = c.Get("expires")
	if err == nil {
		log.Panicf("found expired element")
	}
}
<commit_msg>Fix cache test<commit_after>/*
Copyright 2016 The Fission Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cache

import "testing"
import "log"
import "time"

func checkErr(err error) {
	if err != nil {
		log.Panicf("err: %v", err)
	}
}

func TestCache(t *testing.T) {
	c := MakeCache(100*time.Millisecond, 100*time.Millisecond)

	err, _ := c.Set("a", "b")
	checkErr(err)
	err, _ = c.Set("p", "q")
	checkErr(err)

	val, err := c.Get("a")
	checkErr(err)
	if val != "b" {
		log.Panicf("value %v", val)
	}

	cc := c.Copy()
	if len(cc) != 2 {
		log.Panicf("expected 2 items")
	}

	err = c.Delete("a")
	checkErr(err)

	_, err = c.Get("a")
	if err == nil {
		log.Panicf("found deleted element")
	}

	err, _ = c.Set("expires", "42")
	checkErr(err)
	time.Sleep(150 * time.Millisecond)
	_, err = c.Get("expires")
	if err == nil {
		log.Panicf("found expired element")
	}
}
<|endoftext|>


<commit_before>package command

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"

	"github.com/codegangsta/cli"
	"github.com/coreos/go-etcd/etcd"
)

// NewExecWatchCommand returns the CLI command for "exec-watch".
func NewExecWatchCommand() cli.Command {
	return cli.Command{
		Name:  "exec-watch",
		Usage: "watch a key for changes and exec an executable",
		Flags: []cli.Flag{
			cli.IntFlag{"index", 0, "watch from the given index"},
			cli.IntFlag{"after-index", 0, "watch after the given index"},
		},
		Action: func(c *cli.Context) {
			handle(c, execWatchCommandFunc)
		},
	}
}

// execWatchCommandFunc executes the "exec-watch" command.
func execWatchCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) {
	_ = io.Copy
	_ = exec.Command
	args := c.Args()
	argsLen := len(args)

	if argsLen < 2 {
		return nil, errors.New("Key and command to exec required")
	}
	key := args[argsLen-1]
	afterIndex := c.Int("after-index")
	index := c.Int("index")

	if (index != 0) && (afterIndex != 0) {
		return nil, errors.New("index and after-index cannot be used together")
	} else if (index == 0) && (afterIndex != 0) {
		index = afterIndex + 1
	}

	sigch := make(chan os.Signal, 1)
	signal.Notify(sigch, os.Interrupt)
	stop := make(chan bool)

	go func() {
		<-sigch
		stop <- true
		os.Exit(0)
	}()

	receiver := make(chan *etcd.Response)
	go client.Watch(key, uint64(index), receiver, stop)

	for {
		resp := <-receiver
		if c.GlobalBool("debug") {
			fmt.Fprintln(os.Stderr, <-curlChan)
		}
		cmd := exec.Command(args[0], args[1:argsLen-1]...)
		cmd.Env = environResponse(resp, os.Environ())

		stdout, err := cmd.StdoutPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		stderr, err := cmd.StderrPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		err = cmd.Start()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		go io.Copy(os.Stdout, stdout)
		go io.Copy(os.Stderr, stderr)
		cmd.Wait()
	}

	return nil, nil
}

func environResponse(resp *etcd.Response, env []string) []string {
	env = append(env, "ETCD_MODIFIED_INDEX="+fmt.Sprintf("%d", resp.ModifiedIndex))
	env = append(env, "ETCD_KEY="+resp.Key)
	env = append(env, "ETCD_VALUE="+resp.Value)
	return env
}
<commit_msg>fix(command): make the exec-watch prefix ETCD_WATCH<commit_after>package command

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"

	"github.com/codegangsta/cli"
	"github.com/coreos/go-etcd/etcd"
)

// NewExecWatchCommand returns the CLI command for "exec-watch".
func NewExecWatchCommand() cli.Command {
	return cli.Command{
		Name:  "exec-watch",
		Usage: "watch a key for changes and exec an executable",
		Flags: []cli.Flag{
			cli.IntFlag{"index", 0, "watch from the given index"},
			cli.IntFlag{"after-index", 0, "watch after the given index"},
		},
		Action: func(c *cli.Context) {
			handle(c, execWatchCommandFunc)
		},
	}
}

// execWatchCommandFunc executes the "exec-watch" command.
func execWatchCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error) {
	_ = io.Copy
	_ = exec.Command
	args := c.Args()
	argsLen := len(args)

	if argsLen < 2 {
		return nil, errors.New("Key and command to exec required")
	}
	key := args[argsLen-1]
	afterIndex := c.Int("after-index")
	index := c.Int("index")

	if (index != 0) && (afterIndex != 0) {
		return nil, errors.New("index and after-index cannot be used together")
	} else if (index == 0) && (afterIndex != 0) {
		index = afterIndex + 1
	}

	sigch := make(chan os.Signal, 1)
	signal.Notify(sigch, os.Interrupt)
	stop := make(chan bool)

	go func() {
		<-sigch
		stop <- true
		os.Exit(0)
	}()

	receiver := make(chan *etcd.Response)
	go client.Watch(key, uint64(index), receiver, stop)

	for {
		resp := <-receiver
		if c.GlobalBool("debug") {
			fmt.Fprintln(os.Stderr, <-curlChan)
		}
		cmd := exec.Command(args[0], args[1:argsLen-1]...)
		cmd.Env = environResponse(resp, os.Environ())

		stdout, err := cmd.StdoutPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		stderr, err := cmd.StderrPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		err = cmd.Start()
		if err != nil {
			fmt.Fprintf(os.Stderr, err.Error())
			os.Exit(1)
		}
		go io.Copy(os.Stdout, stdout)
		go io.Copy(os.Stderr, stderr)
		cmd.Wait()
	}

	return nil, nil
}

func environResponse(resp *etcd.Response, env []string) []string {
	env = append(env, "ETCD_WATCH_MODIFIED_INDEX="+fmt.Sprintf("%d", resp.ModifiedIndex))
	env = append(env, "ETCD_WATCH_KEY="+resp.Key)
	env = append(env, "ETCD_WATCH_VALUE="+resp.Value)
	return env
}
<|endoftext|>


<commit_before>// go-qrcode
// Copyright 2014 Tom Harwood

/*
Package qrcode implements a QR Code encoder.

A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be
encoded.

A QR Code contains error recovery information to aid reading damaged or
obscured codes. There are four levels of error recovery: qrcode.{Low, Medium,
High, Highest}. QR Codes with a higher recovery level are more robust to damage,
at the cost of being physically larger.

Two functions cover most use cases:

- Create a PNG image:

	var png []byte
	png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256)

- Create a PNG image and write to a file:

	err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png")

Both examples use the qrcode.Medium error Recovery Level and create a 256x256
pixel, black on white QR Code.

The maximum capacity of a QR Code varies according to the content encoded and
the error recovery level. The maximum capacity is 2,953 bytes, 4,296
alphanumeric characters, 7,089 numeric digits, or a combination of these.

This package implements a subset of QR Code 2005, as defined in ISO/IEC
18004:2006.
*/
package qrcode

import (
	"bytes"
	"errors"
	"image"
	"image/color"
	"image/png"
	"io"
	"io/ioutil"
	"log"
	"os"

	bitset "github.com/skip2/go-qrcode/bitset"
	reedsolomon "github.com/skip2/go-qrcode/reedsolomon"
)

// Encode a QR Code and return a raw PNG image.
//
// size is both the image width and height in pixels. If size is too small then
// a larger image is silently returned.
//
// To serve over HTTP, remember to send a Content-Type: image/png header.
func Encode(content string, level RecoveryLevel, size int) ([]byte, error) {
	var q *QRCode

	q, err := New(content, level)

	if err != nil {
		return nil, err
	}

	return q.PNG(size)
}

// WriteFile encodes, then writes a QR Code to the given filename in PNG format.
//
// size is both the width and height in pixels. If size is too small then a
// larger image is silently written.
func WriteFile(content string, level RecoveryLevel, size int, filename string) error {
	var q *QRCode

	q, err := New(content, level)

	if err != nil {
		return err
	}

	return q.WriteFile(size, filename)
}

// A QRCode represents a valid encoded QRCode.
type QRCode struct {
	// Original content encoded.
	Content string

	// QR Code type.
	Level         RecoveryLevel
	VersionNumber int

	// User settable drawing options.
	ForegroundColor color.Color
	BackgroundColor color.Color

	encoder *dataEncoder
	version qrCodeVersion

	data   *bitset.Bitset
	symbol *symbol
	mask   int
}

// New constructs a QRCode.
//
//	var q *qrcode.QRCode
//	q, err := qrcode.New("my content", qrcode.Medium)
//
// An error occurs if the content is too long.
func New(content string, level RecoveryLevel) (*QRCode, error) {
	encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26,
		dataEncoderType27To40}

	var encoder *dataEncoder
	var encoded *bitset.Bitset
	var chosenVersion *qrCodeVersion
	var err error

	for _, t := range encoders {
		encoder = newDataEncoder(t)
		encoded, err = encoder.encode([]byte(content))

		if err != nil {
			continue
		}

		chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len())

		if chosenVersion != nil {
			break
		}
	}

	if err != nil {
		return nil, err
	} else if chosenVersion == nil {
		return nil, errors.New("content too long to encode")
	}

	q := &QRCode{
		Content: content,

		Level:         level,
		VersionNumber: chosenVersion.version,

		ForegroundColor: color.Black,
		BackgroundColor: color.White,

		encoder: encoder,
		data:    encoded,
		version: *chosenVersion,
	}

	q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len()))

	return q, nil
}

func newWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) {
	var encoder *dataEncoder

	switch {
	case version >= 1 && version <= 9:
		encoder = newDataEncoder(dataEncoderType1To9)
	case version >= 10 && version <= 26:
		encoder = newDataEncoder(dataEncoderType10To26)
	case version >= 27 && version <= 40:
		encoder = newDataEncoder(dataEncoderType27To40)
	default:
		log.Fatalf("Invalid version %d (expected 1-40 inclusive)", version)
	}

	var encoded *bitset.Bitset
	encoded, err := encoder.encode([]byte(content))

	if err != nil {
		return nil, err
	}

	chosenVersion := getQRCodeVersion(level, version)

	if chosenVersion == nil {
		return nil, errors.New("cannot find QR Code version")
	}

	q := &QRCode{
		Content: content,

		Level:         level,
		VersionNumber: chosenVersion.version,

		ForegroundColor: color.Black,
		BackgroundColor: color.White,

		encoder: encoder,
		data:    encoded,
		version: *chosenVersion,
	}

	q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len()))

	return q, nil
}

// Bitmap returns the QR Code as a 2D array of 1-bit pixels.
//
// bitmap[y][x] is true if the pixel at (x, y) is set.
//
// The bitmap includes the required "quiet zone" around the QR Code to aid
// decoding.
func (q *QRCode) Bitmap() [][]bool {
	return q.symbol.bitmap()
}

// Image returns the QR Code as an image.Image.
//
// size is both the width and height in pixels.
func (q *QRCode) Image(size int) image.Image {
	// Minimum pixels (both width and height) required.
	realSize := q.symbol.size

	// Actual pixels available to draw the symbol. Automatically increase the
	// image size if it's not large enough.
	if size < realSize {
		size = realSize
	}

	// Size of each module drawn.
	pixelsPerModule := size / realSize

	// Center the symbol within the image.
	offset := (size - realSize*pixelsPerModule) / 2

	rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}}
	img := image.NewRGBA(rect)

	for i := 0; i < size; i++ {
		for j := 0; j < size; j++ {
			img.Set(i, j, q.BackgroundColor)
		}
	}

	bitmap := q.symbol.bitmap()
	for y, row := range bitmap {
		for x, v := range row {
			if v {
				startX := x*pixelsPerModule + offset
				startY := y*pixelsPerModule + offset
				for i := startX; i < startX+pixelsPerModule; i++ {
					for j := startY; j < startY+pixelsPerModule; j++ {
						img.Set(i, j, q.ForegroundColor)
					}
				}
			}
		}
	}

	return img
}

// PNG returns the QR Code as a PNG image.
//
// size is both the image width and height in pixels. If size is too small then
// a larger image is silently returned.
func (q *QRCode) PNG(size int) ([]byte, error) {
	img := q.Image(size)

	var b bytes.Buffer
	err := png.Encode(&b, img)

	if err != nil {
		return nil, err
	}

	return b.Bytes(), nil
}

// Write writes the QR Code as a PNG image to io.Writer.
//
// size is both the image width and height in pixels. If size is too small then
// a larger image is silently written.
func (q *QRCode) Write(size int, out io.Writer) error {
	var png []byte

	png, err := q.PNG(size)

	if err != nil {
		return err
	}
	_, err = out.Write(png)
	return err
}

// WriteFile writes the QR Code as a PNG image to the specified file.
//
// size is both the image width and height in pixels. If size is too small then
// a larger image is silently written.
func (q *QRCode) WriteFile(size int, filename string) error {
	var png []byte

	png, err := q.PNG(size)

	if err != nil {
		return err
	}

	return ioutil.WriteFile(filename, png, os.FileMode(0644))
}

// encode completes the steps required to encode the QR Code. These include
// adding the terminator bits and padding, splitting the data into blocks and
// applying the error correction, and selecting the best data mask.
func (q *QRCode) encode(numTerminatorBits int) {
	q.addTerminatorBits(numTerminatorBits)
	q.addPadding()

	encoded := q.encodeBlocks()

	const numMasks int = 8
	penalty := 0

	for mask := 0; mask < numMasks; mask++ {
		var s *symbol
		var err error

		s, err = buildRegularSymbol(q.version, mask, encoded)

		if err != nil {
			log.Panic(err.Error())
		}

		numEmptyModules := s.numEmptyModules()
		if numEmptyModules != 0 {
			log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)",
				numEmptyModules, q.VersionNumber)
		}

		p := s.penaltyScore()

		//log.Printf("mask=%d p=%3d p1=%3d p2=%3d p3=%3d p4=%d\n", mask, p, s.penalty1(), s.penalty2(), s.penalty3(), s.penalty4())

		if q.symbol == nil || p < penalty {
			q.symbol = s
			q.mask = mask
			penalty = p
		}
	}
}

// addTerminatorBits adds final terminator bits to the encoded data.
//
// The number of terminator bits required is determined when the QR Code version
// is chosen (which itself depends on the length of the data encoded). The
// terminator bits are thus added after the QR Code version
// is chosen, rather than at the data encoding stage.
func (q *QRCode) addTerminatorBits(numTerminatorBits int) {
	q.data.AppendNumBools(numTerminatorBits, false)
}

// encodeBlocks takes the completed (terminated & padded) encoded data, splits
// the data into blocks (as specified by the QR Code version), applies error
// correction to each block, then interleaves the blocks together.
//
// The QR Code's final data sequence is returned.
func (q *QRCode) encodeBlocks() *bitset.Bitset {
	// Split into blocks.
	type dataBlock struct {
		data          *bitset.Bitset
		ecStartOffset int
	}

	block := make([]dataBlock, q.version.numBlocks())

	start := 0
	end := 0
	blockID := 0

	for _, b := range q.version.block {
		for j := 0; j < b.numBlocks; j++ {
			start = end
			end = start + b.numDataCodewords*8

			// Apply error correction to each block.
			numErrorCodewords := b.numCodewords - b.numDataCodewords
			block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords)
			block[blockID].ecStartOffset = end - start

			blockID++
		}
	}

	// Interleave the blocks.

	result := bitset.New()

	// Combine data blocks.
	working := true
	for i := 0; working; i += 8 {
		working = false

		for j, b := range block {
			if i >= block[j].ecStartOffset {
				continue
			}

			result.Append(b.data.Substr(i, i+8))

			working = true
		}
	}

	// Combine error correction blocks.
	working = true
	for i := 0; working; i += 8 {
		working = false

		for j, b := range block {
			offset := i + block[j].ecStartOffset
			if offset >= block[j].data.Len() {
				continue
			}

			result.Append(b.data.Substr(offset, offset+8))

			working = true
		}
	}

	// Append remainder bits.
	result.AppendNumBools(q.version.numRemainderBits, false)

	return result
}

// max returns the maximum of a and b.
func max(a int, b int) int {
	if a > b {
		return a
	}

	return b
}

// addPadding pads the encoded data upto the full length required.
func (q *QRCode) addPadding() {
	numDataBits := q.version.numDataBits()

	if q.data.Len() == numDataBits {
		return
	}

	// Pad to the nearest codeword boundary.
	q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false)

	// Pad codewords 0b11101100 and 0b00010001.
	padding := [2]*bitset.Bitset{
		bitset.New(true, true, true, false, true, true, false, false),
		bitset.New(false, false, false, true, false, false, false, true),
	}

	// Insert pad codewords alternately.
	i := 0
	for numDataBits-q.data.Len() >= 8 {
		q.data.Append(padding[i])

		i = 1 - i // Alternate between 0 and 1.
	}

	if q.data.Len() != numDataBits {
		log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits)
	}
}
<commit_msg>Crunch down output PNG by a bit<commit_after>// go-qrcode
// Copyright 2014 Tom Harwood

/*
Package qrcode implements a QR Code encoder.

A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be
encoded.

A QR Code contains error recovery information to aid reading damaged or
obscured codes. There are four levels of error recovery: qrcode.{Low, Medium,
High, Highest}. QR Codes with a higher recovery level are more robust to damage,
at the cost of being physically larger.

Two functions cover most use cases:

- Create a PNG image:

	var png []byte
	png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256)

- Create a PNG image and write to a file:

	err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png")

Both examples use the qrcode.Medium error Recovery Level and create a 256x256
pixel, black on white QR Code.

The maximum capacity of a QR Code varies according to the content encoded and
the error recovery level. The maximum capacity is 2,953 bytes, 4,296
alphanumeric characters, 7,089 numeric digits, or a combination of these.

This package implements a subset of QR Code 2005, as defined in ISO/IEC
18004:2006.
*/
package qrcode

import (
	"bytes"
	"errors"
	"image"
	"image/color"
	"image/png"
	"io"
	"io/ioutil"
	"log"
	"os"

	bitset "github.com/skip2/go-qrcode/bitset"
	reedsolomon "github.com/skip2/go-qrcode/reedsolomon"
)

// Encode a QR Code and return a raw PNG image.
//
// size is both the image width and height in pixels. If size is too small then
// a larger image is silently returned.
//
// To serve over HTTP, remember to send a Content-Type: image/png header.
func Encode(content string, level RecoveryLevel, size int) ([]byte, error) {
	var q *QRCode

	q, err := New(content, level)

	if err != nil {
		return nil, err
	}

	return q.PNG(size)
}

// WriteFile encodes, then writes a QR Code to the given filename in PNG format.
//
// size is both the width and height in pixels. If size is too small then a
// larger image is silently written.
func WriteFile(content string, level RecoveryLevel, size int, filename string) error {
	var q *QRCode

	q, err := New(content, level)

	if err != nil {
		return err
	}

	return q.WriteFile(size, filename)
}

// A QRCode represents a valid encoded QRCode.
type QRCode struct {
	// Original content encoded.
	Content string

	// QR Code type.
	Level         RecoveryLevel
	VersionNumber int

	// User settable drawing options.
	ForegroundColor color.Color
	BackgroundColor color.Color

	encoder *dataEncoder
	version qrCodeVersion

	data   *bitset.Bitset
	symbol *symbol
	mask   int
}

// New constructs a QRCode.
//
//	var q *qrcode.QRCode
//	q, err := qrcode.New("my content", qrcode.Medium)
//
// An error occurs if the content is too long.
func New(content string, level RecoveryLevel) (*QRCode, error) {
	encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26,
		dataEncoderType27To40}

	var encoder *dataEncoder
	var encoded *bitset.Bitset
	var chosenVersion *qrCodeVersion
	var err error

	for _, t := range encoders {
		encoder = newDataEncoder(t)
		encoded, err = encoder.encode([]byte(content))

		if err != nil {
			continue
		}

		chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len())

		if chosenVersion != nil {
			break
		}
	}

	if err != nil {
		return nil, err
	} else if chosenVersion == nil {
		return nil, errors.New("content too long to encode")
	}

	q := &QRCode{
		Content: content,

		Level:         level,
		VersionNumber: chosenVersion.version,

		ForegroundColor: color.Black,
		BackgroundColor: color.White,

		encoder: encoder,
		data:    encoded,
		version: *chosenVersion,
	}

	q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len()))

	return q, nil
}

func newWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) {
	var encoder *dataEncoder

	switch {
	case version >= 1 && version <= 9:
		encoder = newDataEncoder(dataEncoderType1To9)
	case version >= 10 && version <= 26:
		encoder = newDataEncoder(dataEncoderType10To26)
	case version >= 27 && version <= 40:
		encoder = newDataEncoder(dataEncoderType27To40)
	default:
		log.Fatalf("Invalid version %d (expected 1-40 inclusive)", version)
	}

	var encoded *bitset.Bitset
	encoded, err := encoder.encode([]byte(content))

	if err != nil {
		return nil, err
	}

	chosenVersion := getQRCodeVersion(level, version)

	if chosenVersion == nil {
		return nil, errors.New("cannot find QR Code version")
	}

	q := &QRCode{
		Content: content,

		Level:         level,
		VersionNumber: chosenVersion.version,

		ForegroundColor: color.Black,
		BackgroundColor: color.White,

		encoder: encoder,
		data:    encoded,
		version: *chosenVersion,
	}

	q.encode(chosenVersion.numTerminatorBitsRequired(encoded.Len()))

	return q, nil
}

// Bitmap returns the QR Code as a 2D array of 1-bit pixels.
//
// bitmap[y][x] is true if the pixel at (x, y) is set.
//
// The bitmap includes the required "quiet zone" around the QR Code to aid
// decoding.
func (q *QRCode) Bitmap() [][]bool {
	return q.symbol.bitmap()
}

// Image returns the QR Code as an image.Image.
//
// size is both the width and height in pixels.
func (q *QRCode) Image(size int) image.Image {
	// Minimum pixels (both width and height) required.
	realSize := q.symbol.size

	// Actual pixels available to draw the symbol. Automatically increase the
	// image size if it's not large enough.
	if size < realSize {
		size = realSize
	}

	// Size of each module drawn.
	pixelsPerModule := size / realSize

	// Center the symbol within the image.
	offset := (size - realSize*pixelsPerModule) / 2

	rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}}

	// Saves a few bytes to have them in this order
	p := color.Palette([]color.Color{q.BackgroundColor, q.ForegroundColor})
	img := image.NewPaletted(rect, p)

	for i := 0; i < size; i++ {
		for j := 0; j < size; j++ {
			img.Set(i, j, q.BackgroundColor)
		}
	}

	bitmap := q.symbol.bitmap()
	for y, row := range bitmap {
		for x, v := range row {
			if v {
				startX := x*pixelsPerModule + offset
				startY := y*pixelsPerModule + offset
				for i := startX; i < startX+pixelsPerModule; i++ {
					for j := startY; j < startY+pixelsPerModule; j++ {
						img.Set(i, j, q.ForegroundColor)
					}
				}
			}
		}
	}

	return img
}

// PNG returns the QR Code as a PNG image.
//
// size is both the image width and height in pixels. If size is too small then
// a larger image is silently returned.
func (q *QRCode) PNG(size int) ([]byte, error) {
	img := q.Image(size)

	encoder := png.Encoder{CompressionLevel: png.BestCompression}

	var b bytes.Buffer
	err := encoder.Encode(&b, img)

	if err != nil {
		return nil, err
	}

	return b.Bytes(), nil
}

// Write writes the QR Code as a PNG image to io.Writer.
//
// size is both the image width and height in pixels. If size is too small then
// a larger image is silently written.
func (q *QRCode) Write(size int, out io.Writer) error {
	var png []byte

	png, err := q.PNG(size)

	if err != nil {
		return err
	}
	_, err = out.Write(png)
	return err
}

// WriteFile writes the QR Code as a PNG image to the specified file.
//
// size is both the image width and height in pixels. If size is too small then
// a larger image is silently written.
func (q *QRCode) WriteFile(size int, filename string) error {
	var png []byte

	png, err := q.PNG(size)

	if err != nil {
		return err
	}

	return ioutil.WriteFile(filename, png, os.FileMode(0644))
}

// encode completes the steps required to encode the QR Code. These include
// adding the terminator bits and padding, splitting the data into blocks and
// applying the error correction, and selecting the best data mask.
func (q *QRCode) encode(numTerminatorBits int) {
	q.addTerminatorBits(numTerminatorBits)
	q.addPadding()

	encoded := q.encodeBlocks()

	const numMasks int = 8
	penalty := 0

	for mask := 0; mask < numMasks; mask++ {
		var s *symbol
		var err error

		s, err = buildRegularSymbol(q.version, mask, encoded)

		if err != nil {
			log.Panic(err.Error())
		}

		numEmptyModules := s.numEmptyModules()
		if numEmptyModules != 0 {
			log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)",
				numEmptyModules, q.VersionNumber)
		}

		p := s.penaltyScore()

		//log.Printf("mask=%d p=%3d p1=%3d p2=%3d p3=%3d p4=%d\n", mask, p, s.penalty1(), s.penalty2(), s.penalty3(), s.penalty4())

		if q.symbol == nil || p < penalty {
			q.symbol = s
			q.mask = mask
			penalty = p
		}
	}
}

// addTerminatorBits adds final terminator bits to the encoded data.
//
// The number of terminator bits required is determined when the QR Code version
// is chosen (which itself depends on the length of the data encoded). The
// terminator bits are thus added after the QR Code version
// is chosen, rather than at the data encoding stage.
func (q *QRCode) addTerminatorBits(numTerminatorBits int) {
	q.data.AppendNumBools(numTerminatorBits, false)
}

// encodeBlocks takes the completed (terminated & padded) encoded data, splits
// the data into blocks (as specified by the QR Code version), applies error
// correction to each block, then interleaves the blocks together.
//
// The QR Code's final data sequence is returned.
func (q *QRCode) encodeBlocks() *bitset.Bitset {
	// Split into blocks.
	type dataBlock struct {
		data          *bitset.Bitset
		ecStartOffset int
	}

	block := make([]dataBlock, q.version.numBlocks())

	start := 0
	end := 0
	blockID := 0

	for _, b := range q.version.block {
		for j := 0; j < b.numBlocks; j++ {
			start = end
			end = start + b.numDataCodewords*8

			// Apply error correction to each block.
			numErrorCodewords := b.numCodewords - b.numDataCodewords
			block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords)
			block[blockID].ecStartOffset = end - start

			blockID++
		}
	}

	// Interleave the blocks.

	result := bitset.New()

	// Combine data blocks.
	working := true
	for i := 0; working; i += 8 {
		working = false

		for j, b := range block {
			if i >= block[j].ecStartOffset {
				continue
			}

			result.Append(b.data.Substr(i, i+8))

			working = true
		}
	}

	// Combine error correction blocks.
	working = true
	for i := 0; working; i += 8 {
		working = false

		for j, b := range block {
			offset := i + block[j].ecStartOffset
			if offset >= block[j].data.Len() {
				continue
			}

			result.Append(b.data.Substr(offset, offset+8))

			working = true
		}
	}

	// Append remainder bits.
	result.AppendNumBools(q.version.numRemainderBits, false)

	return result
}

// max returns the maximum of a and b.
func max(a int, b int) int {
	if a > b {
		return a
	}

	return b
}

// addPadding pads the encoded data upto the full length required.
func (q *QRCode) addPadding() {
	numDataBits := q.version.numDataBits()

	if q.data.Len() == numDataBits {
		return
	}

	// Pad to the nearest codeword boundary.
	q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false)

	// Pad codewords 0b11101100 and 0b00010001.
	padding := [2]*bitset.Bitset{
		bitset.New(true, true, true, false, true, true, false, false),
		bitset.New(false, false, false, true, false, false, false, true),
	}

	// Insert pad codewords alternately.
	i := 0
	for numDataBits-q.data.Len() >= 8 {
		q.data.Append(padding[i])

		i = 1 - i // Alternate between 0 and 1.
	}

	if q.data.Len() != numDataBits {
		log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits)
	}
}
<|endoftext|>


<commit_before>package httpx

import (
	"net/http"

	"github.com/gorilla/mux"
	"golang.org/x/net/context"
)

// Router is an httpx.Handler router.
type Router struct {
	// NotFoundHandler is a Handler that will be called when a route is not
	// found.
	NotFoundHandler Handler

	// This router is ultimately backed by a gorilla mux router.
	mux *mux.Router
}

// NewRouter returns a new Router instance.
func NewRouter() *Router {
	return &Router{
		mux: mux.NewRouter(),
	}
}

// Handle adds a new router that routes requests using the method verb against
// path to the given Handler.
func (r *Router) Handle(method, path string, h Handler) {
	// mux.Handler expects an http.Handler. We wrap the Hander in a handler,
	// which satisfies the http.Handler interface. When this route is
	// eventually used, it's type asserted back to a Handler.
	hh := &handler{h}

	r.mux.Handle(path, hh).Methods(method)
}

// Handler returns a Handler that can be used to serve the request. Most of this
// is pulled from http://goo.gl/tyxad8.
func (r *Router) Handler(req *http.Request) (h Handler, vars map[string]string) {
	var match mux.RouteMatch

	if r.mux.Match(req, &match) {
		h = match.Handler.(Handler)
		vars = match.Vars
		return
	}

	if r.NotFoundHandler == nil {
		h = HandlerFunc(NotFound)
		return
	}

	h = r.NotFoundHandler
	return
}

// ServeHTTP implements the http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.ServeHTTPContext(context.Background(), w, req)
}

// ServeHTTPContext implements the Handler interface.
func (r *Router) ServeHTTPContext(ctx context.Context, w http.ResponseWriter, req *http.Request) error {
	h, vars := r.Handler(req)
	return h.ServeHTTPContext(WithVars(ctx, vars), w, req)
}

// Vars extracts the route vars from a context.Context.
func Vars(ctx context.Context) map[string]string {
	vars, ok := ctx.Value(varsKey).(map[string]string)
	if !ok {
		return map[string]string{}
	}

	return vars
}

// WithVars adds the vars to the context.Context.
func WithVars(ctx context.Context, vars map[string]string) context.Context {
	return context.WithValue(ctx, varsKey, vars)
}

// handler adapts a Handler to an http.Handler.
type handler struct {
	Handler
}

// ServeHTTP implements the http.Handler interface. This method is never
// actually called by this package, it's only used as a means to pass a Handler
// in and out of mux.
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.ServeHTTPContext(context.Background(), w, r)
}

// NotFound is a HandlerFunc that just delegates off to http.NotFound.
func NotFound(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	http.NotFound(w, r)
	return nil
}
<commit_msg>Don't implement http.Handler.<commit_after>package httpx

import (
	"net/http"

	"github.com/gorilla/mux"
	"golang.org/x/net/context"
)

// Router is an httpx.Handler router.
type Router struct {
	// NotFoundHandler is a Handler that will be called when a route is not
	// found.
	NotFoundHandler Handler

	// This router is ultimately backed by a gorilla mux router.
	mux *mux.Router
}

// NewRouter returns a new Router instance.
func NewRouter() *Router {
	return &Router{
		mux: mux.NewRouter(),
	}
}

// Handle adds a new router that routes requests using the method verb against
// path to the given Handler.
func (r *Router) Handle(method, path string, h Handler) {
	// mux.Handler expects an http.Handler. We wrap the Hander in a handler,
	// which satisfies the http.Handler interface. When this route is
	// eventually used, it's type asserted back to a Handler.
	hh := &handler{h}

	r.mux.Handle(path, hh).Methods(method)
}

// Handler returns a Handler that can be used to serve the request. Most of this
// is pulled from http://goo.gl/tyxad8.
func (r *Router) Handler(req *http.Request) (h Handler, vars map[string]string) {
	var match mux.RouteMatch

	if r.mux.Match(req, &match) {
		h = match.Handler.(Handler)
		vars = match.Vars
		return
	}

	if r.NotFoundHandler == nil {
		h = HandlerFunc(NotFound)
		return
	}

	h = r.NotFoundHandler
	return
}

// ServeHTTPContext implements the Handler interface.
func (r *Router) ServeHTTPContext(ctx context.Context, w http.ResponseWriter, req *http.Request) error {
	h, vars := r.Handler(req)
	return h.ServeHTTPContext(WithVars(ctx, vars), w, req)
}

// Vars extracts the route vars from a context.Context.
func Vars(ctx context.Context) map[string]string {
	vars, ok := ctx.Value(varsKey).(map[string]string)
	if !ok {
		return map[string]string{}
	}

	return vars
}

// WithVars adds the vars to the context.Context.
func WithVars(ctx context.Context, vars map[string]string) context.Context {
	return context.WithValue(ctx, varsKey, vars)
}

// handler adapts a Handler to an http.Handler.
type handler struct {
	Handler
}

// ServeHTTP implements the http.Handler interface. This method is never
// actually called by this package, it's only used as a means to pass a Handler
// in and out of mux.
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.ServeHTTPContext(context.Background(), w, r)
}

// NotFound is a HandlerFunc that just delegates off to http.NotFound.
func NotFound(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
	http.NotFound(w, r)
	return nil
}
<|endoftext|>


<commit_before><commit_msg>删除readability.go<commit_after><|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/gdamore/tcell"
	"github.com/rivo/tview"

	"nimona.io/internal/rand"
)

type (
	App struct {
		Windows       Windows
		StatusText    []Status
		Conversations Conversations
		Channels      Channels
		Chat          *chat
	}
	Channels struct {
		InputLines   chan string
		MessageAdded chan *Message
	}
	Windows struct {
		Participants *tview.List
		Input        *tview.InputField
		Chat         *tview.TextView
		App          *tview.Application
	}
	Status struct {
		Text    string
		Created string
	}
	Conversations []*Conversation // helper, used for sorting
	Conversation  struct {
		Hash         string
		Messages     Messages
		Participants Participants
		LastActivity time.Time
	}
	Messages []*Message // helper, used for sorting
	Message  struct {
		Hash             string
		ConversationHash string
		Body             string
		SenderHash       string
		SenderNickname   string
		Created          time.Time
	}
	Participants []*Participant // helper, used for sorting
	Participant  struct {
		Hash     string
		Nickname string
	}
)

func (a Messages) Len() int {
	return len(a)
}

func (a Messages) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a Messages) Less(i, j int) bool {
	return a[i].Created.Before(a[j].Created)
}

func (a Participants) Len() int {
	return len(a)
}

func (a Participants) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a Participants) Less(i, j int) bool {
	return a[i].Nickname < a[j].Nickname
}

func NewApp(conversationHash string) *App {
	app := &App{
		Windows: Windows{
			Participants: tview.NewList(),
			Input:        tview.NewInputField(),
			Chat:         tview.NewTextView(),
			App:          tview.NewApplication(),
		},
		StatusText: []Status{},
		Channels: Channels{
			InputLines:   make(chan string, 100),
			MessageAdded: make(chan *Message, 100),
		},
		Conversations: Conversations{
			&Conversation{
				Hash:     conversationHash,
				Messages: Messages{},
			},
		},
	}

	go func() {
		conv := app.Conversations[0]
		for {
			select {
			case messageAdded := <-app.Channels.MessageAdded:
				duplicate := false
				for _, message := range conv.Messages {
					if message.Hash == messageAdded.Hash {
						duplicate = true
						break
					}
				}
				if duplicate {
					break
				}
				conv.Messages = append(
					conv.Messages,
					messageAdded,
				)
				sort.Sort(conv.Messages)
				// TODO only do this if the message is in the wrong order
				app.Windows.Chat.Clear()
				max := len(conv.Messages)
				if max > 100 {
					max -= 100
				}
				for _, message := range conv.Messages[max:] {
					if message.SenderHash == "system" {
						app.Windows.Chat.Write([]byte(
							fmt.Sprintf(
								"\n[red][%s] %s",
								message.Created.Format("02/01 15:04:05"),
								message.Body,
							),
						))
						continue
					}
					app.Windows.Chat.Write([]byte(
						fmt.Sprintf(
							"\n[lightcyan][%s][gold] <%s>[white] %s",
							message.Created.Format("02/01 15:04:05"),
							message.SenderNickname,
							message.Body,
						),
					))
				}
				app.Windows.Chat.ScrollToEnd()
				app.Windows.App.Draw()
				// deal with users
				if messageAdded.SenderHash == "system" {
					continue
				}
				userExists := false
				for _, u := range conv.Participants {
					if u.Hash == messageAdded.SenderHash {
						userExists = true
						break
					}
				}
				if !userExists {
					conv.Participants = append(
						conv.Participants,
						&Participant{
							Hash:     messageAdded.SenderHash,
							Nickname: messageAdded.SenderNickname,
						},
					)
					sort.Sort(conv.Participants)
					app.Windows.Participants.Clear()
					for _, user := range conv.Participants {
						app.Windows.Participants.AddItem(user.Nickname, "", 0, func() {})
					}
				}
			}
		}
	}()

	return app
}

func (app *App) AddSystemText(msg string) {
	app.Channels.MessageAdded <- &Message{
		Hash:       rand.String(16),
		SenderHash: "system",
		Body:       msg,
		Created:    time.Now(),
	}
}

func (app *App) Quit() {
	app.Windows.App.Stop()
	os.Exit(0)
}

func (app *App) Show() {
	log.SetOutput(ioutil.Discard)

	app.Windows.Participants.SetBorder(true).SetTitle(" Participants ")
	app.Windows.Participants.ShowSecondaryText(false)
	app.Windows.Participants.SetSelectedBackgroundColor(tcell.ColorBlack)
	app.Windows.Participants.SetSelectedTextColor(tcell.ColorWhite)
	app.Windows.Participants.SetBorderPadding(0, 0, 1, 1)

	app.Windows.Chat.SetBorder(true)
	app.Windows.Chat.SetTitleAlign(tview.AlignLeft)
	app.Windows.Chat.SetDynamicColors(true)
	app.Windows.Chat.SetScrollable(true)
	app.Windows.Chat.SetWordWrap(true)
	app.Windows.Chat.SetBorderPadding(0, 0, 0, 0)

	app.Windows.Input.SetFieldBackgroundColor(tcell.ColorBlack)
	app.Windows.Input.SetBorder(true)
	app.Windows.Input.SetDoneFunc(func(key tcell.Key) {
		if key == tcell.KeyEnter {
			if app.Windows.Input.GetText() == "" {
				return
			}

			text := app.Windows.Input.GetText()

			if text[0] == '/' {
				words := strings.Fields(text[1:])
				if len(words) == 0 {
					return
				}
				switch words[0] {
				case "quit", "q":
					app.Quit()
					os.Exit(0)
				case "info", "i":
					app.AddSystemText("Info:")
					app.AddSystemText(
						fmt.Sprintf(
							"* public key: %s",
							app.Chat.local.GetPrimaryPeerKey().PublicKey(),
						),
					)
					app.AddSystemText(
						fmt.Sprintf(
							"* addresses: %s",
							app.Chat.local.GetAddresses(),
						),
					)
					app.AddSystemText(
						fmt.Sprintf(
							"* pinned hashes: %v",
							app.Chat.local.GetContentHashes(),
						),
					)
				default:
					app.AddSystemText(fmt.Sprintf("[red]No such command '%s'.", text[1:]))
				}
				app.Windows.Input.SetText("")
				return
			}

			app.Channels.InputLines <- text
			app.Windows.Input.SetText("")
		}
	})
	app.Windows.Input.SetInputCapture(app.Windows.Input.GetInputCapture())

	app.Windows.Chat.SetTitle(" " + app.Conversations[0].Hash + " ")

	flexLists := tview.NewFlex().SetDirection(tview.FlexRow)
	flexChat := tview.NewFlex().SetDirection(tview.FlexRow)

	flex := tview.NewFlex()
	flexLists.AddItem(app.Windows.Participants, 0, 4, false)
	flexChat.AddItem(app.Windows.Chat, 0, 10, false)
	flexChat.AddItem(app.Windows.Input, 3, 2, false)
	flex.AddItem(flexLists, 0, 1, false)
	flex.AddItem(flexChat, 0, 5, false)

	if err := app.Windows.App.SetRoot(flex, true).SetFocus(app.Windows.Input).Run(); err != nil {
		panic(err)
	}
}
<commit_msg>fix(examples/chat): fix max number of messages<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/gdamore/tcell"
	"github.com/rivo/tview"

	"nimona.io/internal/rand"
)

type (
	App struct {
		Windows       Windows
		StatusText    []Status
		Conversations Conversations
		Channels      Channels
		Chat          *chat
	}
	Channels struct {
		InputLines   chan string
		MessageAdded chan *Message
	}
	Windows struct {
		Participants *tview.List
		Input        *tview.InputField
		Chat         *tview.TextView
		App          *tview.Application
	}
	Status struct {
		Text    string
		Created string
	}
	Conversations []*Conversation // helper, used for sorting
	Conversation  struct {
		Hash         string
		Messages     Messages
		Participants Participants
		LastActivity time.Time
	}
	Messages []*Message // helper, used for sorting
	Message  struct {
		Hash             string
		ConversationHash string
		Body             string
		SenderHash       string
		SenderNickname   string
		Created          time.Time
	}
	Participants []*Participant // helper, used for sorting
	Participant  struct {
		Hash     string
		Nickname string
	}
)

func (a Messages) Len() int {
	return len(a)
}

func (a Messages) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a Messages) Less(i, j int) bool {
	return a[i].Created.Before(a[j].Created)
}

func (a Participants) Len() int {
	return len(a)
}

func (a Participants) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a Participants) Less(i, j int) bool {
	return a[i].Nickname < a[j].Nickname
}

func NewApp(conversationHash string) *App {
	app := &App{
		Windows: Windows{
			Participants: tview.NewList(),
			Input:        tview.NewInputField(),
			Chat:         tview.NewTextView(),
			App:          tview.NewApplication(),
		},
		StatusText: []Status{},
		Channels: Channels{
			InputLines:   make(chan string, 100),
			MessageAdded: make(chan *Message, 100),
		},
		Conversations: Conversations{
			&Conversation{
				Hash:     conversationHash,
				Messages: Messages{},
			},
		},
	}

	go func() {
		conv := app.Conversations[0]
		for {
			select {
			case messageAdded := <-app.Channels.MessageAdded:
				duplicate := false
				for _, message := range conv.Messages {
					if message.Hash == messageAdded.Hash {
						duplicate = true
						break
					}
				}
				if duplicate {
					break
				}
				conv.Messages = append(
					conv.Messages,
					messageAdded,
				)
				sort.Sort(conv.Messages)
				// TODO only do this if the message is in the wrong order
				app.Windows.Chat.Clear()
				min := 0
				if len(conv.Messages) > 100 {
					min = len(conv.Messages) - 100
				}
				for _, message := range conv.Messages[min:] {
					if message.SenderHash == "system" {
						app.Windows.Chat.Write([]byte(
							fmt.Sprintf(
								"\n[red][%s] %s",
								message.Created.Format("02/01 15:04:05"),
								message.Body,
							),
						))
						continue
					}
					app.Windows.Chat.Write([]byte(
						fmt.Sprintf(
							"\n[lightcyan][%s][gold] <%s>[white] %s",
							message.Created.Format("02/01 15:04:05"),
							message.SenderNickname,
							message.Body,
						),
					))
				}
				app.Windows.Chat.ScrollToEnd()
				app.Windows.App.Draw()
				// deal with users
				if messageAdded.SenderHash == "system" {
					continue
				}
				userExists := false
				for _, u := range conv.Participants {
					if u.Hash == messageAdded.SenderHash {
						userExists = true
						break
					}
				}
				if !userExists {
					conv.Participants = append(
						conv.Participants,
						&Participant{
							Hash:     messageAdded.SenderHash,
							Nickname: messageAdded.SenderNickname,
						},
					)
					sort.Sort(conv.Participants)
					app.Windows.Participants.Clear()
					for _, user := range conv.Participants {
						app.Windows.Participants.AddItem(user.Nickname, "", 0, func() {})
					}
				}
			}
		}
	}()

	return app
}

func (app *App) AddSystemText(msg string) {
	app.Channels.MessageAdded <- &Message{
		Hash:       rand.String(16),
		SenderHash: "system",
		Body:       msg,
		Created:    time.Now(),
	}
}

func (app *App) Quit() {
	app.Windows.App.Stop()
	os.Exit(0)
}

func (app *App) Show() {
	log.SetOutput(ioutil.Discard)

	app.Windows.Participants.SetBorder(true).SetTitle(" Participants ")
	app.Windows.Participants.ShowSecondaryText(false)
	app.Windows.Participants.SetSelectedBackgroundColor(tcell.ColorBlack)
	app.Windows.Participants.SetSelectedTextColor(tcell.ColorWhite)
	app.Windows.Participants.SetBorderPadding(0, 0, 1, 1)

	app.Windows.Chat.SetBorder(true)
	app.Windows.Chat.SetTitleAlign(tview.AlignLeft)
	app.Windows.Chat.SetDynamicColors(true)
	app.Windows.Chat.SetScrollable(true)
	app.Windows.Chat.SetWordWrap(true)
	app.Windows.Chat.SetBorderPadding(0, 0, 0, 0)

	app.Windows.Input.SetFieldBackgroundColor(tcell.ColorBlack)
	app.Windows.Input.SetBorder(true)
	app.Windows.Input.SetDoneFunc(func(key tcell.Key) {
		if key == tcell.KeyEnter {
			if app.Windows.Input.GetText() == "" {
				return
			}

			text := app.Windows.Input.GetText()

			if text[0] == '/' {
				words := strings.Fields(text[1:])
				if len(words) == 0 {
					return
				}
				switch words[0] {
				case "quit", "q":
					app.Quit()
					os.Exit(0)
				case "info", "i":
					app.AddSystemText("Info:")
					app.AddSystemText(
						fmt.Sprintf(
							"* public key: %s",
							app.Chat.local.GetPrimaryPeerKey().PublicKey(),
						),
					)
					app.AddSystemText(
						fmt.Sprintf(
							"* addresses: %s",
							app.Chat.local.GetAddresses(),
						),
					)
					app.AddSystemText(
						fmt.Sprintf(
							"* pinned hashes: %v",
							app.Chat.local.GetContentHashes(),
						),
					)
				default:
					app.AddSystemText(fmt.Sprintf("[red]No such command '%s'.", text[1:]))
				}
				app.Windows.Input.SetText("")
				return
			}

			app.Channels.InputLines <- text
			app.Windows.Input.SetText("")
		}
	})
	app.Windows.Input.SetInputCapture(app.Windows.Input.GetInputCapture())

	app.Windows.Chat.SetTitle(" " + app.Conversations[0].Hash + " ")

	flexLists := tview.NewFlex().SetDirection(tview.FlexRow)
	flexChat := tview.NewFlex().SetDirection(tview.FlexRow)

	flex := tview.NewFlex()
	flexLists.AddItem(app.Windows.Participants, 0, 4, false)
	flexChat.AddItem(app.Windows.Chat, 0, 10, false)
	flexChat.AddItem(app.Windows.Input, 3, 2, false)
	flex.AddItem(flexLists, 0, 1, false)
	flex.AddItem(flexChat, 0, 5, false)

	if err := app.Windows.App.SetRoot(flex, true).SetFocus(app.Windows.Input).Run(); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package clocky

import (
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"template"
	"xml"

	"appengine"
	"appengine/memcache"
)

func newParser(b []byte) (p *xml.Parser) {
	// NWS serves XML in ISO-8859-1 for no reason; the data is really ASCII.
	p = xml.NewParser(strings.NewReader(string(b)))
	p.CharsetReader = func(charset string, input io.Reader) (io.Reader, os.Error) {
		return input, nil
	}
	return p
}

func Conditions(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "conditions")
	if err != nil {
		c.Errorf("%q", err)
		return
	}

	data := struct {
		Temp_c      string
		WindChill_c string
		Wind_Mph    string
	}{}
	p := newParser(item.Value)
	if err = p.Unmarshal(&data, nil); err != nil {
		c.Errorf("%q", err)
		return
	}

	io.WriteString(w, `<div class=header>`)
	if len(data.Temp_c) != 0 {
		io.WriteString(w, `<span class=larger>`)
		template.HTMLEscape(w, []byte(data.Temp_c))
		io.WriteString(w, `°</span> `)
	}
	if len(data.WindChill_c) != 0 {
		io.WriteString(w, `wind chill `)
		template.HTMLEscape(w, []byte(data.WindChill_c))
		io.WriteString(w, `°`)
	} else if data.Wind_Mph != "" {
		mph, err := strconv.Atof64(data.Wind_Mph)
		if err == nil {
			if mph == 0 {
				io.WriteString(w, "wind calm")
			} else {
				fmt.Fprintf(w, "wind %d km/h", int(mph*1.609344))
			}
		}
	}
	io.WriteString(w, `</div>`)
}

var (
	nbspRegexp   = regexp.MustCompile(` [0-9]+\.`)
	thinspRegexp = regexp.MustCompile(`[0-9] (am|pm|km/h)`)
)

func Forecast(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "forecast")
	if err != nil {
		c.Errorf("%q", err)
		return
	}

	data := struct {
		Data []struct {
			Type       string `xml:"attr"`
			TimeLayout []struct {
				LayoutKey      string `xml:"layout-key"`
				StartValidTime []struct {
					PeriodName []byte `xml:"attr"`
				}
			}
			Parameters struct {
				WordedForecast struct {
					TimeLayout string   `xml:"attr"`
					Text       []string `xml:"name>text"`
				}
			}
		}
	}{}
	p := newParser(item.Value)
	if err = p.Unmarshal(&data, nil); err != nil {
		c.Errorf("%q", err)
		return
	}

	io.WriteString(w, `<div class=smaller style="text-align: left">`)
	for _, d := range data.Data {
		if d.Type != "forecast" {
			continue
		}
		var periods [][]byte
		for _, tl := range d.TimeLayout {
			if tl.LayoutKey != d.Parameters.WordedForecast.TimeLayout {
				continue
			}
			for _, svt := range tl.StartValidTime {
				periods = append(periods, svt.PeriodName)
			}
		}
		texts := d.Parameters.WordedForecast.Text
		if len(texts) != len(periods) {
			c.Errorf("weather: len(texts) = %d, len(periods) = %d",
				len(texts), len(periods))
			continue
		}
		if len(texts) > 3 {
			texts = texts[:3]
		}
		for i, text := range texts {
			io.WriteString(w, `<div style="margin-bottom: 8px"><span class=header>`)
			template.HTMLEscape(w, periods[i])
			io.WriteString(w, `:</span> `)

			spaceSubs := make(map[int]string)
			matches := nbspRegexp.FindAllStringIndex(text, -1)
			for i := 0; i < len(matches[0]); i += 2 {
				spaceSubs[matches[0][i]] = " "
			}
			matches = thinspRegexp.FindAllStringIndex(text, -1)
			for i := 0; i < len(matches[0]); i += 2 {
				spaceSubs[matches[0][i]+1] = `<span style="white-space: nowrap"> </span>`
			}
			for i, ch := range text {
				sub, ok := spaceSubs[i]
				if ok {
					io.WriteString(w, sub)
				} else {
					io.WriteString(w, string(ch))
				}
			}

			io.WriteString(w, `</div>`)
		}
	}
	io.WriteString(w, `</div>`)
}
<commit_msg>Fix up forecast to use more lowercase.<commit_after>package clocky

import (
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"template"
	"xml"

	"appengine"
	"appengine/memcache"
)

func newParser(b []byte) (p *xml.Parser) {
	// NWS serves XML in ISO-8859-1 for no reason; the data is really ASCII.
	p = xml.NewParser(strings.NewReader(string(b)))
	p.CharsetReader = func(charset string, input io.Reader) (io.Reader, os.Error) {
		return input, nil
	}
	return p
}

func Conditions(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "conditions")
	if err != nil {
		c.Errorf("%q", err)
		return
	}

	data := struct {
		Temp_c      string
		WindChill_c string
		Wind_Mph    string
	}{}
	p := newParser(item.Value)
	if err = p.Unmarshal(&data, nil); err != nil {
		c.Errorf("%q", err)
		return
	}

	io.WriteString(w, `<div class=header>`)
	if len(data.Temp_c) != 0 {
		io.WriteString(w, `<span class=larger>`)
		template.HTMLEscape(w, []byte(data.Temp_c))
		io.WriteString(w, `°</span> `)
	}
	if len(data.WindChill_c) != 0 {
		io.WriteString(w, `wind chill `)
		template.HTMLEscape(w, []byte(data.WindChill_c))
		io.WriteString(w, `°`)
	} else if data.Wind_Mph != "" {
		mph, err := strconv.Atof64(data.Wind_Mph)
		if err == nil {
			if mph == 0 {
				io.WriteString(w, "wind calm")
			} else {
				fmt.Fprintf(w, "wind %d km/h", int(mph*1.609344))
			}
		}
	}
	io.WriteString(w, `</div>`)
}

var (
	nbspRegexp   = regexp.MustCompile(` [0-9]+\.`)
	thinspRegexp = regexp.MustCompile(`[0-9] (am|pm|km/h)`)
)

func Forecast(w io.Writer, c appengine.Context) {
	item, err := memcache.Get(c, "forecast")
	if err != nil {
		c.Errorf("%q", err)
		return
	}

	data := struct {
		Data []struct {
			Type       string `xml:"attr"`
			TimeLayout []struct {
				LayoutKey      string `xml:"layout-key"`
				StartValidTime []struct {
					PeriodName string `xml:"attr"`
				}
			}
			Parameters struct {
				WordedForecast struct {
					TimeLayout string   `xml:"attr"`
					Text       []string `xml:"name>text"`
				}
			}
		}
	}{}
	p := newParser(item.Value)
	if err = p.Unmarshal(&data, nil); err != nil {
		c.Errorf("%q", err)
		return
	}

	io.WriteString(w, `<div class=smaller style="text-align: left">`)
	for _, d := range data.Data {
		if d.Type != "forecast" {
			continue
		}
		var periods []string
		for _, tl := range d.TimeLayout {
			if tl.LayoutKey != d.Parameters.WordedForecast.TimeLayout {
				continue
			}
			for _, svt := range tl.StartValidTime {
				pn := svt.PeriodName
				pn = strings.Replace(pn, " Morning", " morning", -1)
				pn = strings.Replace(pn, " Afternoon", " afternoon", -1)
				pn = strings.Replace(pn, " Night", " night", -1)
				periods = append(periods, pn)
			}
		}
		texts := d.Parameters.WordedForecast.Text
		if len(texts) != len(periods) {
			c.Errorf("weather: len(texts) = %d, len(periods) = %d",
				len(texts), len(periods))
			continue
		}
		if len(texts) > 3 {
			texts = texts[:3]
		}
		for i, text := range texts {
			io.WriteString(w, `<div style="margin-bottom: 8px"><span class=header>`)
			template.HTMLEscape(w, []byte(periods[i]))
			io.WriteString(w, `:</span> `)

			spaceSubs := make(map[int]string)
			matches := nbspRegexp.FindAllStringIndex(text, -1)
			for i := 0; i < len(matches[0]); i += 2 {
				spaceSubs[matches[0][i]] = " "
			}
			matches = thinspRegexp.FindAllStringIndex(text, -1)
			for i := 0; i < len(matches[0]); i += 2 {
				spaceSubs[matches[0][i]+1] = `<span style="white-space: nowrap"> </span>`
			}
			for i, ch := range text {
				sub, ok := spaceSubs[i]
				if ok {
					io.WriteString(w, sub)
				} else {
					io.WriteString(w, string(ch))
				}
			}

			io.WriteString(w, `</div>`)
		}
	}
	io.WriteString(w, `</div>`)
}
<|endoftext|>


<commit_before>package lastfm

import (
	"encoding/xml"
)

// AlbumInfo is a type representing the metadata and tracklist for an album.
// When returned as part of search results, the AlbumInfo objects will be incomplete,
// importantly they don't contain the track-listing.
//
// See http://www.last.fm/api/show/album.search
type AlbumInfo struct {
	XMLName     xml.Name `xml:"album"`
	Name        string   `xml:"name"`
	Artist      string   `xml:"artist"`
	ID          string   `xml:"id"`
	MBID        string   `xml:"mbid"`
	Url         string   `xml:"url"`
	ReleaseDate string   `xml:"releaseDate"`
	Images      []struct {
		Url  string `xml:",chardata"`
		Size string `xml:"size,attr"`
	} `xml:"image"`
	Listeners string `xml:"listeners"`
	Playcount string `xml:"playcount"`
	Tracks    []struct {
		Rank int    `xml:"rank,attr"`
		Name string `xml:"name"`
		// Duration is the length of the track in seconds
		Duration int    `xml:"duration"`
		MBID     string `xml:"mbid"`
		Url      string `xml:"url"`
		Artist   struct {
			Name string `xml:"name"`
			MBID string `xml:"mbid"`
			Url  string `xml:"url"`
		} `xml:"artist"`
	} `xml:"tracks>track"`
}

// Affiliation is a type that contains the details of a "buy link", for either a physical or e-commerce retailer.
type Affiliation struct {
	SupplierName string `xml:"supplierName"`
	Price        struct {
		Currency  string `xml:"currency:`
		Amount    string `xml:"amount"`
		Formatted string `xml:"formatted"`
	} `xml:"price"`
	BuyLink      string `xml:"buyLink"`
	SupplierIcon string `xml:"supplierIcon"`
	IsSearch     string `xml:"isSearch"`
}

// BuyLinks contains two lists of Affiliations, differentiating between Physical and Download retailers
type BuyLinks struct {
	Physicals []Affiliation `xml:"physicals>affiliation"`
	Downloads []Affiliation `xml:"downloads>affiliation"`
}

// A Shout is a comment on an album, artist or track
type Shout struct {
	Body   string `xml:"body"`
	Author string `xml:"author"`
	Date   string `xml:"date"`
}

// Tag
type Tag struct {
	Name string `xml:"name"`
	// Count is a weighted count of how often the tag has been applied, with a maximum value of 100
	Count int    `xml:"count"`
	Url   string `xml:"url"`
}

// AlbumSearchResults contains a list of AlbumInfos, along with the totals and paging information for the call
//
// The AlbumInfo values contained won't be complete. Specifically, they don't include track listings.
type AlbumSearchResults struct {
	TotalResults int         `xml:"totalResults"`
	StartIndex   int         `xml:"startIndex"`
	ItemsPerPage int         `xml:"itemsPerPage"`
	Albums       []AlbumInfo `xml:"albummatches>album"`
}
<commit_msg>clarify comment<commit_after>package lastfm

import (
	"encoding/xml"
)

// AlbumInfo is a type representing the metadata and tracklist for an album.
// When returned as part of search results, the AlbumInfo objects will be incomplete,
// importantly they don't contain the track-listing.
//
// See http://www.last.fm/api/show/album.search
type AlbumInfo struct {
	XMLName     xml.Name `xml:"album"`
	Name        string   `xml:"name"`
	Artist      string   `xml:"artist"`
	ID          string   `xml:"id"`
	MBID        string   `xml:"mbid"`
	Url         string   `xml:"url"`
	ReleaseDate string   `xml:"releaseDate"`
	Images      []struct {
		Url  string `xml:",chardata"`
		Size string `xml:"size,attr"`
	} `xml:"image"`
	Listeners string `xml:"listeners"`
	Playcount string `xml:"playcount"`
	Tracks    []struct {
		Rank int    `xml:"rank,attr"`
		Name string `xml:"name"`
		// Duration is the length of the track in seconds
		Duration int    `xml:"duration"`
		MBID     string `xml:"mbid"`
		Url      string `xml:"url"`
		Artist   struct {
			Name string `xml:"name"`
			MBID string `xml:"mbid"`
			Url  string `xml:"url"`
		} `xml:"artist"`
	} `xml:"tracks>track"`
}

// Affiliation is a type that contains the details of a "buy link", for either a physical or e-commerce retailer.
type Affiliation struct {
	SupplierName string `xml:"supplierName"`
	Price        struct {
		Currency  string `xml:"currency:`
		Amount    string `xml:"amount"`
		Formatted string `xml:"formatted"`
	} `xml:"price"`
	BuyLink      string `xml:"buyLink"`
	SupplierIcon string `xml:"supplierIcon"`
	IsSearch     string `xml:"isSearch"`
}

// BuyLinks contains two lists of Affiliations, differentiating between Physical and Download retailers
type BuyLinks struct {
	Physicals []Affiliation `xml:"physicals>affiliation"`
	Downloads []Affiliation `xml:"downloads>affiliation"`
}

// A Shout is a comment on an album, artist or track
type Shout struct {
	Body   string `xml:"body"`
	Author string `xml:"author"`
	Date   string `xml:"date"`
}

// Tag
type Tag struct {
	Name string `xml:"name"`
	// Count is a weighted count of how often the tag has been applied, with a maximum value of 100
	Count int    `xml:"count"`
	Url   string `xml:"url"`
}

// AlbumSearchResults contains a list of AlbumInfos, along with the totals and paging information for the call
//
// The AlbumInfo values contained won't be complete. Specifically, they don't include track listings.
// To get the full version, call GetInfo with the MBID
type AlbumSearchResults struct {
	TotalResults int         `xml:"totalResults"`
	StartIndex   int         `xml:"startIndex"`
	ItemsPerPage int         `xml:"itemsPerPage"`
	Albums       []AlbumInfo `xml:"albummatches>album"`
}
<|endoftext|>


<commit_before>package hashmap

import (
	"math/rand"
	"strconv"
	"testing"
)

// testKey is an implementation of the Key interface for testing.
type testKey uint64

// Hash returns the lower 32 bits. This is intended so that hash collisions can
// be easily constructed.
func (x testKey) Hash() uint32 {
	return uint32(x & 0xffffffff)
}

// Equal returns true if and only if the other value is also a testKey and they
// are equal.
func (x testKey) Equal(other interface{}) bool {
	y, ok := other.(testKey)
	return ok && x == y
}

type anotherTestKey struct{}

func (anotherTestKey) Hash() uint32           { return 0 }
func (anotherTestKey) Equal(interface{}) bool { return false }

const (
	NSequential = 0x1000
	NCollision  = 0x100
	NRandom     = 0x4000
)

type refEntry struct {
	k testKey
	v string
}

var refEntries = []refEntry{
	{0x100000001, "x1"},
	{0x200000001, "y1"},
}

func init() {
	add := func(k testKey, v string) {
		refEntries = append(refEntries, refEntry{k, v})
	}
	for i := 0; i < NSequential; i++ {
		add(testKey(i), hex(uint64(i)))
	}
	for i := 0; i < NCollision; i++ {
		add(testKey(uint64(i+1)<<32), "collision "+hex(uint64(i)))
	}
	for i := 0; i < NRandom; i++ {
		k := rand.Uint64()
		add(testKey(k), "random "+hex(k))
	}
}

func hex(i uint64) string {
	return "0x" + strconv.FormatUint(i, 16)
}

func TestHashMap(t *testing.T) {
	m := Empty
	// Len of Empty should be 0.
	if m.Len() != 0 {
		t.Errorf("m.Len = %d, want %d", m.Len(), 0)
	}
	// Assoc and Len.
	size := 0
	for _, e := range refEntries {
		m = m.Assoc(e.k, e.v)
		size++
		if m.Len() != size {
			t.Errorf("m.Len = %d, want %d", m.Len(), size)
		}
	}
	// Build a reference map.
	ref := make(map[testKey]string, len(refEntries))
	for _, e := range refEntries {
		ref[e.k] = e.v
	}
	// Get.
	testMapContent(t, m, ref)
	in, got := m.Get(anotherTestKey{})
	if in {
		t.Errorf("m.Get <bad key> returns entry %v", got)
	}
	// Without.
	for _, e := range refEntries {
		delete(ref, e.k)
		m = m.Without(e.k)
		if m.Len() != len(ref) {
			t.Errorf("m.Len() = %d after removing, should be %v", m.Len(), len(ref))
		}
		in, _ := m.Get(e.k)
		if in {
			t.Errorf("m.Get(%v) still returns item after removal", e.k)
		}
		// Checking all elements is expensive. Only do this 1% of the time.
		if rand.Float64() < 0.01 {
			testMapContent(t, m, ref)
		}
	}
}

func testMapContent(t *testing.T, m HashMap, ref map[testKey]string) {
	for k, v := range ref {
		in, got := m.Get(k)
		if !in {
			t.Errorf("m.Get 0x%x returns no entry", k)
		}
		if got != v {
			t.Errorf("m.Get(0x%x) = %v, want %v", k, got, v)
		}
	}
}
<commit_msg>Fix compatibility with pre 1.8 Go in tests.<commit_after>package hashmap

import (
	"math/rand"
	"strconv"
	"testing"
	"time"
)

// testKey is an implementation of the Key interface for testing.
type testKey uint64

// Hash returns the lower 32 bits. This is intended so that hash collisions can
// be easily constructed.
func (x testKey) Hash() uint32 {
	return uint32(x & 0xffffffff)
}

// Equal returns true if and only if the other value is also a testKey and they
// are equal.
func (x testKey) Equal(other interface{}) bool {
	y, ok := other.(testKey)
	return ok && x == y
}

type anotherTestKey struct{}

func (anotherTestKey) Hash() uint32           { return 0 }
func (anotherTestKey) Equal(interface{}) bool { return false }

const (
	NSequential = 0x1000
	NCollision  = 0x100
	NRandom     = 0x4000
)

type refEntry struct {
	k testKey
	v string
}

var refEntries = []refEntry{
	{0x100000001, "x1"},
	{0x200000001, "y1"},
}

func init() {
	add := func(k testKey, v string) {
		refEntries = append(refEntries, refEntry{k, v})
	}
	for i := 0; i < NSequential; i++ {
		add(testKey(i), hex(uint64(i)))
	}
	for i := 0; i < NCollision; i++ {
		add(testKey(uint64(i+1)<<32), "collision "+hex(uint64(i)))
	}
	rand.Seed(time.Now().UTC().UnixNano())
	for i := 0; i < NRandom; i++ {
		// Avoid rand.Uint64 for compatibility with pre 1.8 Go
		k := uint64(rand.Int63())>>31 | uint64(rand.Int63())<<32
		add(testKey(k), "random "+hex(k))
	}
}

func hex(i uint64) string {
	return "0x" + strconv.FormatUint(i, 16)
}

func TestHashMap(t *testing.T) {
	m := Empty
	// Len of Empty should be 0.
	if m.Len() != 0 {
		t.Errorf("m.Len = %d, want %d", m.Len(), 0)
	}
	// Assoc and Len.
	size := 0
	for _, e := range refEntries {
		m = m.Assoc(e.k, e.v)
		size++
		if m.Len() != size {
			t.Errorf("m.Len = %d, want %d", m.Len(), size)
		}
	}
	// Build a reference map.
	ref := make(map[testKey]string, len(refEntries))
	for _, e := range refEntries {
		ref[e.k] = e.v
	}
	// Get.
	testMapContent(t, m, ref)
	in, got := m.Get(anotherTestKey{})
	if in {
		t.Errorf("m.Get <bad key> returns entry %v", got)
	}
	// Without.
	for _, e := range refEntries {
		delete(ref, e.k)
		m = m.Without(e.k)
		if m.Len() != len(ref) {
			t.Errorf("m.Len() = %d after removing, should be %v", m.Len(), len(ref))
		}
		in, _ := m.Get(e.k)
		if in {
			t.Errorf("m.Get(%v) still returns item after removal", e.k)
		}
		// Checking all elements is expensive. Only do this 1% of the time.
		if rand.Float64() < 0.01 {
			testMapContent(t, m, ref)
		}
	}
}

func testMapContent(t *testing.T, m HashMap, ref map[testKey]string) {
	for k, v := range ref {
		in, got := m.Get(k)
		if !in {
			t.Errorf("m.Get 0x%x returns no entry", k)
		}
		if got != v {
			t.Errorf("m.Get(0x%x) = %v, want %v", k, got, v)
		}
	}
}
<|endoftext|>


<commit_before>package kafka

import (
	"context"
	"fmt"
	"io"
	"math"
	"sync"
	"time"
)

// Reader provides a high-level API for consuming messages from kafka.
//
// A Reader automatically manages reconnections to a kafka server, and
// blocking methods have context support for asynchronous cancellations.
type Reader struct {
	// immutable fields of the reader
	config ReaderConfig

	// communication channels between the parent reader and its subreaders
	msgs chan readerMessage
	errs chan readerError
	done chan struct{}

	// mutable fields of the reader (synchronized on the mutex)
	mutex   sync.Mutex
	ctx     context.Context
	cancel  context.CancelFunc
	version int64
	offset  int64
	dirty   bool
	closed  bool
}

// ReaderConfig is a configuration object used to create new instances of
// Reader.
type ReaderConfig struct {
	// The list of broker addresses used to connect to the kafka cluster.
	Brokers []string

	// The topic to read messages from.
	Topic string

	// The partition number to read messages from.
	Partition int

	// An dialer used to open connections to the kafka server. This field is
	// optional, if nil, the default dialer is used instead.
	Dialer *Dialer

	// Min and max number of bytes to fetch from kafka in each request.
	MinBytes int
	MaxBytes int

	// Maximum amount of time to wait for new data to come when fetching batches
	// of messages from kafka.
	MaxWait time.Duration
}

// NewReader creates and returns a new Reader configured with config.
func NewReader(config ReaderConfig) *Reader {
	if len(config.Brokers) == 0 {
		panic("cannot create a new kafka reader with an empty list of broker addresses")
	}

	if len(config.Topic) == 0 {
		panic("cannot create a new kafka reader with an empty topic")
	}

	if config.Partition < 0 || config.Partition >= math.MaxInt32 {
		panic(fmt.Sprintf("partition number out of bounds: %d", config.Partition))
	}

	if config.Dialer == nil {
		config.Dialer = DefaultDialer
	}

	if config.MinBytes > config.MaxBytes {
		panic(fmt.Sprintf("minimum batch size greater than the maximum (min = %d, max = %d)", config.MinBytes, config.MaxBytes))
	}

	if config.MinBytes < 0 {
		panic(fmt.Sprintf("invalid negative minimum batch size (min = %d)", config.MinBytes))
	}

	if config.MaxBytes < 0 {
		panic(fmt.Sprintf("invalid negative maximum batch size (max = %d)", config.MaxBytes))
	}

	if config.MaxBytes == 0 {
		config.MaxBytes = 10e6 // 10 MB
	}

	if config.MinBytes == 0 {
		config.MinBytes = config.MaxBytes
	}

	if config.MaxWait == 0 {
		config.MaxWait = 10 * time.Second
	}

	ctx, cancel := context.WithCancel(context.Background())
	return &Reader{
		config: config,
		msgs:   make(chan readerMessage),
		errs:   make(chan readerError),
		done:   make(chan struct{}),
		ctx:    ctx,
		cancel: cancel,
		offset: -1,
		dirty:  true,
	}
}

// Config returns the reader's configuration.
func (r *Reader) Config() ReaderConfig {
	return r.config
}

// Close closes the stream, preventing the program from reading any more
// messages from it.
func (r *Reader) Close() error {
	r.mutex.Lock()
	if !r.closed {
		close(r.done)
	}
	r.closed = true
	r.dirty = false
	r.cancel()
	r.mutex.Unlock()
	return nil
}

// ReadMessage reads and return the next message from the r. The method call
// blocks until a message becomes available, or an error occurs. The program
// may also specify a context to asynchronously cancel the blocking operation.
func (r *Reader) ReadMessage(ctx context.Context) (msg Message, err error) {
	for {
		r.mutex.Lock()

		if r.dirty {
			subctx, cancel := context.WithCancel(context.Background())

			r.cancel() // always cancel the previous reader
			r.cancel = cancel
			r.dirty = false
			r.version++

			go (&reader{
				dialer:    r.config.Dialer,
				brokers:   r.config.Brokers,
				topic:     r.config.Topic,
				partition: r.config.Partition,
				minBytes:  r.config.MinBytes,
				maxBytes:  r.config.MaxBytes,
				maxWait:   r.config.MaxWait,
				version:   r.version,
				msgs:      r.msgs,
				errs:      r.errs,
			}).run(subctx, r.offset)
		}

		rctx, version := r.ctx, r.version
		r.mutex.Unlock()

		select {
		case <-ctx.Done():
			err = ctx.Err()
			return

		case m := <-r.msgs:
			if m.version >= version {
				r.mutex.Lock()
				if version == r.version {
					r.offset = m.message.Offset + 1
				}
				r.mutex.Unlock()
				msg = m.message
				return
			}

		case e := <-r.errs:
			if e.version >= version {
				err = e.error
				return
			}

		case <-r.done:
			err = io.ErrClosedPipe
			return

		case <-rctx.Done():
			// The subreader was canceled, loop back to start a new one.
		}
	}
}

// Offset returns the current offset of the reader.
func (r *Reader) Offset() int64 {
	r.mutex.Lock()
	offset := r.offset
	r.mutex.Unlock()
	return offset
}

// SetOffset changes the offset from which the next batch of messages will be
// read.
//
// The method fails with io.ErrClosedPipe if the reader has already been closed.
func (r *Reader) SetOffset(offset int64) error {
	var err error
	r.mutex.Lock()
	if r.closed {
		err = io.ErrClosedPipe
	} else if offset != r.offset {
		r.offset = offset
		r.dirty = true
		r.cancel() // may wake up a blocked Read call to spawn a new reader
	}
	r.mutex.Unlock()
	return err
}

// A reader reads messages from kafka and produces them on its channels, it's
// used as an way to asynchronously fetch messages while the main program reads
// them using the high level reader API.
type reader struct {
	dialer    *Dialer
	brokers   []string
	topic     string
	partition int
	minBytes  int
	maxBytes  int
	maxWait   time.Duration
	version   int64
	msgs      chan<- readerMessage
	errs      chan<- readerError
}

type readerMessage struct {
	version int64
	message Message
}

type readerError struct {
	version int64
	error   error
}

func (r *reader) run(ctx context.Context, offset int64) {
	// This is the reader's main loop, it only ends if the context is canceled
	// and will keep attempting to reader messages otherwise.
	//
	// Retrying indefinitely has the nice side effect of preventing Read calls
	// on the parent reader to block if connection to the kafka server fails,
	// the reader keeps reporting errors on the error channel which will then
	// be surfaced to the program.
	// If the reader wasn't retrying then the program would block indefinitely
	// on a Read call after reading the first error.
	for attempt := 0; true; attempt++ {
		if attempt != 0 {
			if !sleep(ctx, backoff(attempt, time.Second, time.Minute)) {
				return
			}
		}

		conn, start, err := r.initialize(ctx, offset)
		switch err {
		case nil:
		case OffsetMetadataTooLarge:
			// This would happen if the requested offset is passed the last
			// offset on the partition leader. In that case we're just going
			// to retry later hoping that enough data has been produced.
		default:
			// Wait 4 attempts before reporting the first errors, this helps
			// mitigate situations where the kafka server is temporarily
			// unavailable.
			if attempt >= 3 {
				r.sendError(ctx, err)
			}
			continue
		}

		// Resetting the attempt counter ensures that if a failre occurs after
		// a successful initialization we don't keep increasing the backoff
		// timeout.
		attempt = 0

		// Now we're sure to have an absolute offset number, may anything happen
		// to the connection we know we'll want to restart from this offset.
		offset = start

		errcount := 0
	readLoop:
		for {
			if !sleep(ctx, backoff(errcount, 100*time.Millisecond, 10*time.Second)) {
				conn.Close()
				return
			}

			switch offset, err = r.read(ctx, offset, conn); err {
			case nil:
				errcount = 0
				continue
			case RequestTimedOut:
				// Timeout on the kafka side, this can be safely retried.
			case OffsetOutOfRange:
				// We may be reading past the last offset, will retry later.
			case context.Canceled:
				// Another reader has taken over, we can safely quit.
				conn.Close()
				return
			default:
				if _, ok := err.(Error); ok {
					r.sendError(ctx, err)
				} else {
					conn.Close()
					break readLoop
				}
			}

			errcount++
		}
	}
}

func (r *reader) initialize(ctx context.Context, offset int64) (conn *Conn, start int64, err error) {
	for _, broker := range r.brokers {
		var first int64

		if conn, err = r.dialer.DialLeader(ctx, "tcp", broker, r.topic, r.partition); err != nil {
			continue
		}

		// This deadline controls how long the offset negatiation may take.
		conn.SetDeadline(time.Now().Add(10 * time.Second))

		if first, err = conn.ReadFirstOffset(); err != nil {
			conn.Close()
			break
		}

		// In case the reader was configured with an offset before the first
		// offset, skipping directly to the first offset.
		if offset < first {
			offset = first
		}

		if start, err = conn.Seek(offset, 1); err != nil {
			conn.Close()
			break
		}

		break
	}

	return
}

func (r *reader) read(ctx context.Context, offset int64, conn *Conn) (int64, error) {
	conn.SetReadDeadline(time.Now().Add(r.maxWait))
	batch := conn.ReadBatch(r.minBytes, r.maxBytes)
	conn.SetReadDeadline(time.Now().Add(10 * time.Second))

	var msg Message
	var err error

	for {
		if msg, err = batch.ReadMessage(); err != nil {
			err = batch.Close()
			break
		}

		if err = r.sendMessage(ctx, msg); err != nil {
			batch.Close()
			break
		}

		offset = msg.Offset + 1
	}

	return offset, err
}

func (r *reader) sendMessage(ctx context.Context, msg Message) error {
	select {
	case r.msgs <- readerMessage{version: r.version, message: msg}:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

func (r *reader) sendError(ctx context.Context, err error) error {
	select {
	case r.errs <- readerError{version: r.version, error: err}:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}
<commit_msg>fix typo<commit_after>package kafka

import (
	"context"
	"fmt"
	"io"
	"math"
	"sync"
	"time"
)

// Reader provides a high-level API for consuming messages from kafka.
//
// A Reader automatically manages reconnections to a kafka server, and
// blocking methods have context support for asynchronous cancellations.
type Reader struct {
	// immutable fields of the reader
	config ReaderConfig

	// communication channels between the parent reader and its subreaders
	msgs chan readerMessage
	errs chan readerError
	done chan struct{}

	// mutable fields of the reader (synchronized on the mutex)
	mutex   sync.Mutex
	ctx     context.Context
	cancel  context.CancelFunc
	version int64
	offset  int64
	dirty   bool
	closed  bool
}

// ReaderConfig is a configuration object used to create new instances of
// Reader.
type ReaderConfig struct {
	// The list of broker addresses used to connect to the kafka cluster.
	Brokers []string

	// The topic to read messages from.
	Topic string

	// The partition number to read messages from.
	Partition int

	// An dialer used to open connections to the kafka server. This field is
	// optional, if nil, the default dialer is used instead.
	Dialer *Dialer

	// Min and max number of bytes to fetch from kafka in each request.
	MinBytes int
	MaxBytes int

	// Maximum amount of time to wait for new data to come when fetching batches
	// of messages from kafka.
	MaxWait time.Duration
}

// NewReader creates and returns a new Reader configured with config.
func NewReader(config ReaderConfig) *Reader {
	if len(config.Brokers) == 0 {
		panic("cannot create a new kafka reader with an empty list of broker addresses")
	}

	if len(config.Topic) == 0 {
		panic("cannot create a new kafka reader with an empty topic")
	}

	if config.Partition < 0 || config.Partition >= math.MaxInt32 {
		panic(fmt.Sprintf("partition number out of bounds: %d", config.Partition))
	}

	if config.Dialer == nil {
		config.Dialer = DefaultDialer
	}

	if config.MinBytes > config.MaxBytes {
		panic(fmt.Sprintf("minimum batch size greater than the maximum (min = %d, max = %d)", config.MinBytes, config.MaxBytes))
	}

	if config.MinBytes < 0 {
		panic(fmt.Sprintf("invalid negative minimum batch size (min = %d)", config.MinBytes))
	}

	if config.MaxBytes < 0 {
		panic(fmt.Sprintf("invalid negative maximum batch size (max = %d)", config.MaxBytes))
	}

	if config.MaxBytes == 0 {
		config.MaxBytes = 10e6 // 10 MB
	}

	if config.MinBytes == 0 {
		config.MinBytes = config.MaxBytes
	}

	if config.MaxWait == 0 {
		config.MaxWait = 10 * time.Second
	}

	ctx, cancel := context.WithCancel(context.Background())
	return &Reader{
		config: config,
		msgs:   make(chan readerMessage),
		errs:   make(chan readerError),
		done:   make(chan struct{}),
		ctx:    ctx,
		cancel: cancel,
		offset: -1,
		dirty:  true,
	}
}

// Config returns the reader's configuration.
func (r *Reader) Config() ReaderConfig {
	return r.config
}

// Close closes the stream, preventing the program from reading any more
// messages from it.
func (r *Reader) Close() error {
	r.mutex.Lock()
	if !r.closed {
		close(r.done)
	}
	r.closed = true
	r.dirty = false
	r.cancel()
	r.mutex.Unlock()
	return nil
}

// ReadMessage reads and return the next message from the r. The method call
// blocks until a message becomes available, or an error occurs. The program
// may also specify a context to asynchronously cancel the blocking operation.
func (r *Reader) ReadMessage(ctx context.Context) (msg Message, err error) {
	for {
		r.mutex.Lock()

		if r.dirty {
			subctx, cancel := context.WithCancel(context.Background())

			r.cancel() // always cancel the previous reader
			r.cancel = cancel
			r.dirty = false
			r.version++

			go (&reader{
				dialer:    r.config.Dialer,
				brokers:   r.config.Brokers,
				topic:     r.config.Topic,
				partition: r.config.Partition,
				minBytes:  r.config.MinBytes,
				maxBytes:  r.config.MaxBytes,
				maxWait:   r.config.MaxWait,
				version:   r.version,
				msgs:      r.msgs,
				errs:      r.errs,
			}).run(subctx, r.offset)
		}

		rctx, version := r.ctx, r.version
		r.mutex.Unlock()

		select {
		case <-ctx.Done():
			err = ctx.Err()
			return

		case m := <-r.msgs:
			if m.version >= version {
				r.mutex.Lock()
				if version == r.version {
					r.offset = m.message.Offset + 1
				}
				r.mutex.Unlock()
				msg = m.message
				return
			}

		case e := <-r.errs:
			if e.version >= version {
				err = e.error
				return
			}

		case <-r.done:
			err = io.ErrClosedPipe
			return

		case <-rctx.Done():
			// The subreader was canceled, loop back to start a new one.
		}
	}
}

// Offset returns the current offset of the reader.
func (r *Reader) Offset() int64 {
	r.mutex.Lock()
	offset := r.offset
	r.mutex.Unlock()
	return offset
}

// SetOffset changes the offset from which the next batch of messages will be
// read.
//
// The method fails with io.ErrClosedPipe if the reader has already been closed.
func (r *Reader) SetOffset(offset int64) error {
	var err error
	r.mutex.Lock()
	if r.closed {
		err = io.ErrClosedPipe
	} else if offset != r.offset {
		r.offset = offset
		r.dirty = true
		r.cancel() // may wake up a blocked Read call to spawn a new reader
	}
	r.mutex.Unlock()
	return err
}

// A reader reads messages from kafka and produces them on its channels, it's
// used as an way to asynchronously fetch messages while the main program reads
// them using the high level reader API.
type reader struct {
	dialer    *Dialer
	brokers   []string
	topic     string
	partition int
	minBytes  int
	maxBytes  int
	maxWait   time.Duration
	version   int64
	msgs      chan<- readerMessage
	errs      chan<- readerError
}

type readerMessage struct {
	version int64
	message Message
}

type readerError struct {
	version int64
	error   error
}

func (r *reader) run(ctx context.Context, offset int64) {
	// This is the reader's main loop, it only ends if the context is canceled
	// and will keep attempting to reader messages otherwise.
	//
	// Retrying indefinitely has the nice side effect of preventing Read calls
	// on the parent reader to block if connection to the kafka server fails,
	// the reader keeps reporting errors on the error channel which will then
	// be surfaced to the program.
	// If the reader wasn't retrying then the program would block indefinitely
	// on a Read call after reading the first error.
	for attempt := 0; true; attempt++ {
		if attempt != 0 {
			if !sleep(ctx, backoff(attempt, time.Second, time.Minute)) {
				return
			}
		}

		conn, start, err := r.initialize(ctx, offset)
		switch err {
		case nil:
		case OffsetMetadataTooLarge:
			// This would happen if the requested offset is passed the last
			// offset on the partition leader. In that case we're just going
			// to retry later hoping that enough data has been produced.
		default:
			// Wait 4 attempts before reporting the first errors, this helps
			// mitigate situations where the kafka server is temporarily
			// unavailable.
			if attempt >= 3 {
				r.sendError(ctx, err)
			}
			continue
		}

		// Resetting the attempt counter ensures that if a failre occurs after
		// a successful initialization we don't keep increasing the backoff
		// timeout.
		attempt = 0

		// Now we're sure to have an absolute offset number, may anything happen
		// to the connection we know we'll want to restart from this offset.
		offset = start

		errcount := 0
	readLoop:
		for {
			if !sleep(ctx, backoff(errcount, 100*time.Millisecond, 10*time.Second)) {
				conn.Close()
				return
			}

			switch offset, err = r.read(ctx, offset, conn); err {
			case nil:
				errcount = 0
				continue
			case RequestTimedOut:
				// Timeout on the kafka side, this can be safely retried.
			case OffsetOutOfRange:
				// We may be reading past the last offset, will retry later.
			case context.Canceled:
				// Another reader has taken over, we can safely quit.
				conn.Close()
				return
			default:
				if _, ok := err.(Error); ok {
					r.sendError(ctx, err)
				} else {
					conn.Close()
					break readLoop
				}
			}

			errcount++
		}
	}
}

func (r *reader) initialize(ctx context.Context, offset int64) (conn *Conn, start int64, err error) {
	for _, broker := range r.brokers {
		var first int64

		if conn, err = r.dialer.DialLeader(ctx, "tcp", broker, r.topic, r.partition); err != nil {
			continue
		}

		// This deadline controls how long the offset negotiation may take.
		conn.SetDeadline(time.Now().Add(10 * time.Second))

		if first, err = conn.ReadFirstOffset(); err != nil {
			conn.Close()
			break
		}

		// In case the reader was configured with an offset before the first
		// offset, skipping directly to the first offset.
		if offset < first {
			offset = first
		}

		if start, err = conn.Seek(offset, 1); err != nil {
			conn.Close()
			break
		}

		break
	}

	return
}

func (r *reader) read(ctx context.Context, offset int64, conn *Conn) (int64, error) {
	conn.SetReadDeadline(time.Now().Add(r.maxWait))
	batch := conn.ReadBatch(r.minBytes, r.maxBytes)
	conn.SetReadDeadline(time.Now().Add(10 * time.Second))

	var msg Message
	var err error

	for {
		if msg, err = batch.ReadMessage(); err != nil {
			err = batch.Close()
			break
		}

		if err = r.sendMessage(ctx, msg); err != nil {
			batch.Close()
			break
		}

		offset = msg.Offset + 1
	}

	return offset, err
}

func (r *reader) sendMessage(ctx context.Context, msg Message) error {
	select {
	case r.msgs <- readerMessage{version: r.version, message: msg}:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

func (r *reader) sendError(ctx context.Context, err error) error {
	select {
	case r.errs <- readerError{version: r.version, error: err}:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}
<|endoftext|>