<commit_before>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strings_test

import (
	"fmt"
	"strings"
	"unicode"
)

func ExampleFields() {
	fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
	// Output: Fields are: ["foo" "bar" "baz"]
}

func ExampleFieldsFunc() {
	f := func(c rune) bool {
		return !unicode.IsLetter(c) && !unicode.IsNumber(c)
	}
	fmt.Printf("Fields are: %q", strings.FieldsFunc("  foo1;bar2,baz3...", f))
	// Output: Fields are: ["foo1" "bar2" "baz3"]
}

func ExampleCompare() {
	fmt.Println(strings.Compare("a", "b"))
	fmt.Println(strings.Compare("a", "a"))
	fmt.Println(strings.Compare("b", "a"))
	// Output:
	// -1
	// 0
	// 1
}

func ExampleContains() {
	fmt.Println(strings.Contains("seafood", "foo"))
	fmt.Println(strings.Contains("seafood", "bar"))
	fmt.Println(strings.Contains("seafood", ""))
	fmt.Println(strings.Contains("", ""))
	// Output:
	// true
	// false
	// true
	// true
}

func ExampleContainsAny() {
	fmt.Println(strings.ContainsAny("team", "i"))
	fmt.Println(strings.ContainsAny("failure", "u & i"))
	fmt.Println(strings.ContainsAny("foo", ""))
	fmt.Println(strings.ContainsAny("", ""))
	// Output:
	// false
	// true
	// false
	// false
}

func ExampleContainsRune() {
	// Finds whether a string contains a particular Unicode code point.
	// The code point for the lowercase letter "a", for example, is 97.
	fmt.Println(strings.ContainsRune("aardvark", 97))
	fmt.Println(strings.ContainsRune("timeout", 97))
	// Output:
	// true
	// false
}

func ExampleCount() {
	fmt.Println(strings.Count("cheese", "e"))
	fmt.Println(strings.Count("five", "")) // before & after each rune
	// Output:
	// 3
	// 5
}

func ExampleEqualFold() {
	fmt.Println(strings.EqualFold("Go", "go"))
	// Output: true
}

func ExampleHasPrefix() {
	fmt.Println(strings.HasPrefix("Gopher", "Go"))
	fmt.Println(strings.HasPrefix("Gopher", "C"))
	fmt.Println(strings.HasPrefix("Gopher", ""))
	// Output:
	// true
	// false
	// true
}

func ExampleHasSuffix() {
	fmt.Println(strings.HasSuffix("Amigo", "go"))
	fmt.Println(strings.HasSuffix("Amigo", "O"))
	fmt.Println(strings.HasSuffix("Amigo", "Ami"))
	fmt.Println(strings.HasSuffix("Amigo", ""))
	// Output:
	// true
	// false
	// false
	// true
}

func ExampleIndex() {
	fmt.Println(strings.Index("chicken", "ken"))
	fmt.Println(strings.Index("chicken", "dmr"))
	// Output:
	// 4
	// -1
}

func ExampleIndexFunc() {
	f := func(c rune) bool {
		return unicode.Is(unicode.Han, c)
	}
	fmt.Println(strings.IndexFunc("Hello, 世界", f))
	fmt.Println(strings.IndexFunc("Hello, world", f))
	// Output:
	// 7
	// -1
}

func ExampleIndexAny() {
	fmt.Println(strings.IndexAny("chicken", "aeiouy"))
	fmt.Println(strings.IndexAny("crwth", "aeiouy"))
	// Output:
	// 2
	// -1
}

func ExampleIndexByte() {
	fmt.Println(strings.IndexByte("golang", 'g'))
	fmt.Println(strings.IndexByte("gophers", 'h'))
	fmt.Println(strings.IndexByte("golang", 'x'))
	// Output:
	// 0
	// 3
	// -1
}
func ExampleIndexRune() {
	fmt.Println(strings.IndexRune("chicken", 'k'))
	fmt.Println(strings.IndexRune("chicken", 'd'))
	// Output:
	// 4
	// -1
}

func ExampleLastIndex() {
	fmt.Println(strings.Index("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "rodent"))
	// Output:
	// 0
	// 3
	// -1
}

func ExampleLastIndexAny() {
	fmt.Println(strings.LastIndexAny("go gopher", "go"))
	fmt.Println(strings.LastIndexAny("go gopher", "rodent"))
	fmt.Println(strings.LastIndexAny("go gopher", "fail"))
	// Output:
	// 4
	// 8
	// -1
}

func ExampleLastIndexByte() {
	fmt.Println(strings.LastIndexByte("Hello, world", 'l'))
	fmt.Println(strings.LastIndexByte("Hello, world", 'o'))
	fmt.Println(strings.LastIndexByte("Hello, world", 'x'))
	// Output:
	// 10
	// 8
	// -1
}

func ExampleLastIndexFunc() {
	fmt.Println(strings.LastIndexFunc("go 123", unicode.IsNumber))
	fmt.Println(strings.LastIndexFunc("123 go", unicode.IsNumber))
	fmt.Println(strings.LastIndexFunc("go", unicode.IsNumber))
	// Output:
	// 5
	// 2
	// -1
}

func ExampleJoin() {
	s := []string{"foo", "bar", "baz"}
	fmt.Println(strings.Join(s, ", "))
	// Output: foo, bar, baz
}

func ExampleRepeat() {
	fmt.Println("ba" + strings.Repeat("na", 2))
	// Output: banana
}

func ExampleReplace() {
	fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
	fmt.Println(strings.ReplaceAll("oink oink oink", "oink", "moo"))
	// Output:
	// oinky oinky oink
	// moo moo moo
}

func ExampleSplit() {
	fmt.Printf("%q\n", strings.Split("a,b,c", ","))
	fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
	fmt.Printf("%q\n", strings.Split(" xyz ", ""))
	fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
	// Output:
	// ["a" "b" "c"]
	// ["" "man " "plan " "canal panama"]
	// [" " "x" "y" "z" " "]
	// [""]
}

func ExampleSplitN() {
	fmt.Printf("%q\n", strings.SplitN("a,b,c", ",", 2))
	z := strings.SplitN("a,b,c", ",", 0)
	fmt.Printf("%q (nil = %v)\n", z, z == nil)
	// Output:
	// ["a" "b,c"]
	// [] (nil = true)
}

func ExampleSplitAfter() {
	fmt.Printf("%q\n", strings.SplitAfter("a,b,c", ","))
	// Output: ["a," "b," "c"]
}

func ExampleSplitAfterN() {
	fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2))
	// Output: ["a," "b,c"]
}

func ExampleTitle() {
	fmt.Println(strings.Title("her royal highness"))
	// Output: Her Royal Highness
}

func ExampleToTitle() {
	fmt.Println(strings.ToTitle("loud noises"))
	fmt.Println(strings.ToTitle("хлеб"))
	// Output:
	// LOUD NOISES
	// ХЛЕБ
}

func ExampleToTitleSpecial() {
	fmt.Println(strings.ToTitleSpecial(unicode.TurkishCase, "dünyanın ilk borsa yapısı Aizonai kabul edilir"))
	// Output:
	// DÜNYANIN İLK BORSA YAPISI AİZONAİ KABUL EDİLİR
}

func ExampleMap() {
	rot13 := func(r rune) rune {
		switch {
		case r >= 'A' && r <= 'Z':
			return 'A' + (r-'A'+13)%26
		case r >= 'a' && r <= 'z':
			return 'a' + (r-'a'+13)%26
		}
		return r
	}
	fmt.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher..."))
	// Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
}

func ExampleNewReplacer() {
	r := strings.NewReplacer("<", "<", ">", ">")
	fmt.Println(r.Replace("This is HTML!"))
	// Output: This is <b>HTML</b>!
}

func ExampleToUpper() {
	fmt.Println(strings.ToUpper("Gopher"))
	// Output: GOPHER
}

func ExampleToUpperSpecial() {
	fmt.Println(strings.ToUpperSpecial(unicode.TurkishCase, "örnek iş"))
	// Output: ÖRNEK İŞ
}

func ExampleToLower() {
	fmt.Println(strings.ToLower("Gopher"))
	// Output: gopher
}

func ExampleToLowerSpecial() {
	fmt.Println(strings.ToLowerSpecial(unicode.TurkishCase, "Önnek İş"))
	// Output: önnek iş
}

func ExampleTrim() {
	fmt.Print(strings.Trim("¡¡¡Hello, Gophers!!!", "!¡"))
	// Output: Hello, Gophers
}

func ExampleTrimSpace() {
	fmt.Println(strings.TrimSpace(" \t\n Hello, Gophers \n\t\r\n"))
	// Output: Hello, Gophers
}

func ExampleTrimPrefix() {
	var s = "¡¡¡Hello, Gophers!!!"
	s = strings.TrimPrefix(s, "¡¡¡Hello, ")
	s = strings.TrimPrefix(s, "¡¡¡Howdy, ")
	fmt.Print(s)
	// Output: Gophers!!!
}

func ExampleTrimSuffix() {
	var s = "¡¡¡Hello, Gophers!!!"
	s = strings.TrimSuffix(s, ", Gophers!!!")
	s = strings.TrimSuffix(s, ", Marmots!!!")
	fmt.Print(s)
	// Output: ¡¡¡Hello
}

func ExampleTrimFunc() {
	fmt.Print(strings.TrimFunc("¡¡¡Hello, Gophers!!!", func(r rune) bool {
		return !unicode.IsLetter(r) && !unicode.IsNumber(r)
	}))
	// Output: Hello, Gophers
}

func ExampleTrimLeft() {
	fmt.Print(strings.TrimLeft("¡¡¡Hello, Gophers!!!", "!¡"))
	// Output: Hello, Gophers!!!
}

func ExampleTrimLeftFunc() {
	fmt.Print(strings.TrimLeftFunc("¡¡¡Hello, Gophers!!!", func(r rune) bool {
		return !unicode.IsLetter(r) && !unicode.IsNumber(r)
	}))
	// Output: Hello, Gophers!!!
}

func ExampleTrimRight() {
	fmt.Print(strings.TrimRight("¡¡¡Hello, Gophers!!!", "!¡"))
	// Output: ¡¡¡Hello, Gophers
}

func ExampleTrimRightFunc() {
	fmt.Print(strings.TrimRightFunc("¡¡¡Hello, Gophers!!!", func(r rune) bool {
		return !unicode.IsLetter(r) && !unicode.IsNumber(r)
	}))
	// Output: ¡¡¡Hello, Gophers
}

func ExampleBuilder() {
	var b strings.Builder
	for i := 3; i >= 1; i-- {
		fmt.Fprintf(&b, "%d...", i)
	}
	b.WriteString("ignition")
	fmt.Println(b.String())

	// Output: 3...2...1...ignition
}
<commit_msg>strings: revert accidental example change from CL 153840<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strings_test

import (
	"fmt"
	"strings"
	"unicode"
)

func ExampleFields() {
	fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
	// Output: Fields are: ["foo" "bar" "baz"]
}

func ExampleFieldsFunc() {
	f := func(c rune) bool {
		return !unicode.IsLetter(c) && !unicode.IsNumber(c)
	}
	fmt.Printf("Fields are: %q", strings.FieldsFunc("  foo1;bar2,baz3...", f))
	// Output: Fields are: ["foo1" "bar2" "baz3"]
}

func ExampleCompare() {
	fmt.Println(strings.Compare("a", "b"))
	fmt.Println(strings.Compare("a", "a"))
	fmt.Println(strings.Compare("b", "a"))
	// Output:
	// -1
	// 0
	// 1
}

func ExampleContains() {
	fmt.Println(strings.Contains("seafood", "foo"))
	fmt.Println(strings.Contains("seafood", "bar"))
	fmt.Println(strings.Contains("seafood", ""))
	fmt.Println(strings.Contains("", ""))
	// Output:
	// true
	// false
	// true
	// true
}

func ExampleContainsAny() {
	fmt.Println(strings.ContainsAny("team", "i"))
	fmt.Println(strings.ContainsAny("failure", "u & i"))
	fmt.Println(strings.ContainsAny("foo", ""))
	fmt.Println(strings.ContainsAny("", ""))
	// Output:
	// false
	// true
	// false
	// false
}

func ExampleContainsRune() {
	// Finds whether a string contains a particular Unicode code point.
	// The code point for the lowercase letter "a", for example, is 97.
	fmt.Println(strings.ContainsRune("aardvark", 97))
	fmt.Println(strings.ContainsRune("timeout", 97))
	// Output:
	// true
	// false
}

func ExampleCount() {
	fmt.Println(strings.Count("cheese", "e"))
	fmt.Println(strings.Count("five", "")) // before & after each rune
	// Output:
	// 3
	// 5
}

func ExampleEqualFold() {
	fmt.Println(strings.EqualFold("Go", "go"))
	// Output: true
}

func ExampleHasPrefix() {
	fmt.Println(strings.HasPrefix("Gopher", "Go"))
	fmt.Println(strings.HasPrefix("Gopher", "C"))
	fmt.Println(strings.HasPrefix("Gopher", ""))
	// Output:
	// true
	// false
	// true
}

func ExampleHasSuffix() {
	fmt.Println(strings.HasSuffix("Amigo", "go"))
	fmt.Println(strings.HasSuffix("Amigo", "O"))
	fmt.Println(strings.HasSuffix("Amigo", "Ami"))
	fmt.Println(strings.HasSuffix("Amigo", ""))
	// Output:
	// true
	// false
	// false
	// true
}

func ExampleIndex() {
	fmt.Println(strings.Index("chicken", "ken"))
	fmt.Println(strings.Index("chicken", "dmr"))
	// Output:
	// 4
	// -1
}

func ExampleIndexFunc() {
	f := func(c rune) bool {
		return unicode.Is(unicode.Han, c)
	}
	fmt.Println(strings.IndexFunc("Hello, 世界", f))
	fmt.Println(strings.IndexFunc("Hello, world", f))
	// Output:
	// 7
	// -1
}

func ExampleIndexAny() {
	fmt.Println(strings.IndexAny("chicken", "aeiouy"))
	fmt.Println(strings.IndexAny("crwth", "aeiouy"))
	// Output:
	// 2
	// -1
}

func ExampleIndexByte() {
	fmt.Println(strings.IndexByte("golang", 'g'))
	fmt.Println(strings.IndexByte("gophers", 'h'))
	fmt.Println(strings.IndexByte("golang", 'x'))
	// Output:
	// 0
	// 3
	// -1
}
func ExampleIndexRune() {
	fmt.Println(strings.IndexRune("chicken", 'k'))
	fmt.Println(strings.IndexRune("chicken", 'd'))
	// Output:
	// 4
	// -1
}

func ExampleLastIndex() {
	fmt.Println(strings.Index("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "go"))
	fmt.Println(strings.LastIndex("go gopher", "rodent"))
	// Output:
	// 0
	// 3
	// -1
}

func ExampleLastIndexAny() {
	fmt.Println(strings.LastIndexAny("go gopher", "go"))
	fmt.Println(strings.LastIndexAny("go gopher", "rodent"))
	fmt.Println(strings.LastIndexAny("go gopher", "fail"))
	// Output:
	// 4
	// 8
	// -1
}

func ExampleLastIndexByte() {
	fmt.Println(strings.LastIndexByte("Hello, world", 'l'))
	fmt.Println(strings.LastIndexByte("Hello, world", 'o'))
	fmt.Println(strings.LastIndexByte("Hello, world", 'x'))
	// Output:
	// 10
	// 8
	// -1
}

func ExampleLastIndexFunc() {
	fmt.Println(strings.LastIndexFunc("go 123", unicode.IsNumber))
	fmt.Println(strings.LastIndexFunc("123 go", unicode.IsNumber))
	fmt.Println(strings.LastIndexFunc("go", unicode.IsNumber))
	// Output:
	// 5
	// 2
	// -1
}

func ExampleJoin() {
	s := []string{"foo", "bar", "baz"}
	fmt.Println(strings.Join(s, ", "))
	// Output: foo, bar, baz
}

func ExampleRepeat() {
	fmt.Println("ba" + strings.Repeat("na", 2))
	// Output: banana
}

func ExampleReplace() {
	fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
	fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
	// Output:
	// oinky oinky oink
	// moo moo moo
}

func ExampleSplit() {
	fmt.Printf("%q\n", strings.Split("a,b,c", ","))
	fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
	fmt.Printf("%q\n", strings.Split(" xyz ", ""))
	fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
	// Output:
	// ["a" "b" "c"]
	// ["" "man " "plan " "canal panama"]
	// [" " "x" "y" "z" " "]
	// [""]
}

func ExampleSplitN() {
	fmt.Printf("%q\n", strings.SplitN("a,b,c", ",", 2))
	z := strings.SplitN("a,b,c", ",", 0)
	fmt.Printf("%q (nil = %v)\n", z, z == nil)
	// Output:
	// ["a" "b,c"]
	// [] (nil = true)
}

func ExampleSplitAfter() {
	fmt.Printf("%q\n", strings.SplitAfter("a,b,c", ","))
	// Output: ["a," "b," "c"]
}

func ExampleSplitAfterN() {
	fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2))
	// Output: ["a," "b,c"]
}

func ExampleTitle() {
	fmt.Println(strings.Title("her royal highness"))
	// Output: Her Royal Highness
}

func ExampleToTitle() {
	fmt.Println(strings.ToTitle("loud noises"))
	fmt.Println(strings.ToTitle("хлеб"))
	// Output:
	// LOUD NOISES
	// ХЛЕБ
}

func ExampleToTitleSpecial() {
	fmt.Println(strings.ToTitleSpecial(unicode.TurkishCase, "dünyanın ilk borsa yapısı Aizonai kabul edilir"))
	// Output:
	// DÜNYANIN İLK BORSA YAPISI AİZONAİ KABUL EDİLİR
}

func ExampleMap() {
	rot13 := func(r rune) rune {
		switch {
		case r >= 'A' && r <= 'Z':
			return 'A' + (r-'A'+13)%26
		case r >= 'a' && r <= 'z':
			return 'a' + (r-'a'+13)%26
		}
		return r
	}
	fmt.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher..."))
	// Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
}

func ExampleNewReplacer() {
	r := strings.NewReplacer("<", "<", ">", ">")
	fmt.Println(r.Replace("This is HTML!"))
	// Output: This is <b>HTML</b>!
}

func ExampleToUpper() {
	fmt.Println(strings.ToUpper("Gopher"))
	// Output: GOPHER
}

func ExampleToUpperSpecial() {
	fmt.Println(strings.ToUpperSpecial(unicode.TurkishCase, "örnek iş"))
	// Output: ÖRNEK İŞ
}

func ExampleToLower() {
	fmt.Println(strings.ToLower("Gopher"))
	// Output: gopher
}

func ExampleToLowerSpecial() {
	fmt.Println(strings.ToLowerSpecial(unicode.TurkishCase, "Önnek İş"))
	// Output: önnek iş
}

func ExampleTrim() {
	fmt.Print(strings.Trim("¡¡¡Hello, Gophers!!!", "!¡"))
	// Output: Hello, Gophers
}

func ExampleTrimSpace() {
	fmt.Println(strings.TrimSpace(" \t\n Hello, Gophers \n\t\r\n"))
	// Output: Hello, Gophers
}

func ExampleTrimPrefix() {
	var s = "¡¡¡Hello, Gophers!!!"
	s = strings.TrimPrefix(s, "¡¡¡Hello, ")
	s = strings.TrimPrefix(s, "¡¡¡Howdy, ")
	fmt.Print(s)
	// Output: Gophers!!!
}

func ExampleTrimSuffix() {
	var s = "¡¡¡Hello, Gophers!!!"
	s = strings.TrimSuffix(s, ", Gophers!!!")
	s = strings.TrimSuffix(s, ", Marmots!!!")
	fmt.Print(s)
	// Output: ¡¡¡Hello
}

func ExampleTrimFunc() {
	fmt.Print(strings.TrimFunc("¡¡¡Hello, Gophers!!!", func(r rune) bool {
		return !unicode.IsLetter(r) && !unicode.IsNumber(r)
	}))
	// Output: Hello, Gophers
}

func ExampleTrimLeft() {
	fmt.Print(strings.TrimLeft("¡¡¡Hello, Gophers!!!", "!¡"))
	// Output: Hello, Gophers!!!
}

func ExampleTrimLeftFunc() {
	fmt.Print(strings.TrimLeftFunc("¡¡¡Hello, Gophers!!!", func(r rune) bool {
		return !unicode.IsLetter(r) && !unicode.IsNumber(r)
	}))
	// Output: Hello, Gophers!!!
}

func ExampleTrimRight() {
	fmt.Print(strings.TrimRight("¡¡¡Hello, Gophers!!!", "!¡"))
	// Output: ¡¡¡Hello, Gophers
}

func ExampleTrimRightFunc() {
	fmt.Print(strings.TrimRightFunc("¡¡¡Hello, Gophers!!!", func(r rune) bool {
		return !unicode.IsLetter(r) && !unicode.IsNumber(r)
	}))
	// Output: ¡¡¡Hello, Gophers
}

func ExampleBuilder() {
	var b strings.Builder
	for i := 3; i >= 1; i-- {
		fmt.Fprintf(&b, "%d...", i)
	}
	b.WriteString("ignition")
	fmt.Println(b.String())

	// Output: 3...2...1...ignition
}
<|endoftext|>


<commit_before>package bll

import (
	"gost/api"
	"gost/filter/apifilter"
	"gost/models"
	"gost/service/transactionservice"
	"net/http"

	"gopkg.in/mgo.v2/bson"
)

func GetTransaction(transactionID bson.ObjectId) api.Response {
	dbTransaction, err := transactionservice.GetTransaction(transactionID)
	if err != nil || dbTransaction == nil {
		return api.NotFound(api.ErrEntityNotFound)
	}

	transaction := &models.Transaction{}
	transaction.Expand(dbTransaction)

	return api.SingleDataResponse(http.StatusOK, transaction)
}

func CreateTransaction(transaction *models.Transaction) api.Response {
	if !apifilter.CheckTransactionIntegrity(transaction) {
		return api.BadRequest(api.ErrEntityIntegrity)
	}

	dbTransaction := transaction.Collapse()
	if dbTransaction == nil {
		return api.InternalServerError(api.ErrEntityProcess)
	}

	err := transactionservice.CreateTransaction(dbTransaction)
	if err != nil {
		return api.InternalServerError(api.ErrEntityProcess)
	}
	transaction.ID = dbTransaction.ID

	return api.SingleDataResponse(http.StatusCreated, transaction)
}
<commit_msg>Added missing godoc to the bll methods<commit_after>package bll

import (
	"gost/api"
	"gost/filter/apifilter"
	"gost/models"
	"gost/service/transactionservice"
	"net/http"

	"gopkg.in/mgo.v2/bson"
)

// GetTransaction retrieves an existing Transaction based on its ID
func GetTransaction(transactionID bson.ObjectId) api.Response {
	dbTransaction, err := transactionservice.GetTransaction(transactionID)
	if err != nil || dbTransaction == nil {
		return api.NotFound(api.ErrEntityNotFound)
	}

	transaction := &models.Transaction{}
	transaction.Expand(dbTransaction)

	return api.SingleDataResponse(http.StatusOK, transaction)
}

// CreateTransaction creates a new Transaction
func CreateTransaction(transaction *models.Transaction) api.Response {
	if !apifilter.CheckTransactionIntegrity(transaction) {
		return api.BadRequest(api.ErrEntityIntegrity)
	}

	dbTransaction := transaction.Collapse()
	if dbTransaction == nil {
		return api.InternalServerError(api.ErrEntityProcess)
	}

	err := transactionservice.CreateTransaction(dbTransaction)
	if err != nil {
		return api.InternalServerError(api.ErrEntityProcess)
	}
	transaction.ID = dbTransaction.ID

	return api.SingleDataResponse(http.StatusCreated, transaction)
}
<|endoftext|>


<commit_before>package daemon

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"strconv"
	"time"

	"github.com/noironetworks/cilium-net/common"
	"github.com/noironetworks/cilium-net/common/types"

	dTypes "github.com/docker/engine-api/types"
	dTypesEvents "github.com/docker/engine-api/types/events"
	k8sAPI "k8s.io/kubernetes/pkg/api"
	k8sDockerLbls "k8s.io/kubernetes/pkg/kubelet/types"
)

const (
	syncRateDocker = time.Duration(30 * time.Second)

	maxRetries = 3
)

// EnableDockerEventListener watches for docker events. Performs the plumbing for the
// containers started or dead.
func (d *Daemon) EnableDockerEventListener() error {
	eo := dTypes.EventsOptions{Since: strconv.FormatInt(time.Now().Unix(), 10)}
	r, err := d.dockerClient.Events(eo)
	if err != nil {
		return err
	}

	d.EnableDockerSync(true)

	log.Debugf("Listening for docker events")
	go d.listenForEvents(r)
	return nil
}

func (d *Daemon) EnableDockerSync(once bool) {
	for {
		cList, err := d.dockerClient.ContainerList(dTypes.ContainerListOptions{All: false})
		if err != nil {
			log.Errorf("Failed to retrieve the container list %s", err)
		}
		for _, cont := range cList {
			go d.createContainer(cont.ID, cont.Labels)
		}

		if once {
			return
		}
		time.Sleep(syncRateDocker)
	}
}

func (d *Daemon) listenForEvents(reader io.ReadCloser) {
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		var e dTypesEvents.Message
		if err := json.Unmarshal(scanner.Bytes(), &e); err != nil {
			log.Errorf("Error while unmarshalling event: %+v", e)
		}
		log.Debugf("Processing an event %+v", e)
		go d.processEvent(e)
	}
	if err := scanner.Err(); err != nil {
		log.Errorf("Error while reading events: %+v", err)
	}
}

func (d *Daemon) processEvent(m dTypesEvents.Message) {
	if m.Type == "container" {
		switch m.Status {
		case "start":
			d.createContainer(m.ID, m.Actor.Attributes)
		case "die":
			d.deleteContainer(m.ID)
		}
	}
}

func getCiliumEndpointID(cont dTypes.ContainerJSON, gwIP net.IP) *uint16 {
	for _, contNetwork := range cont.NetworkSettings.Networks {
		ipv6gw := net.ParseIP(contNetwork.IPv6Gateway)
		if ipv6gw.Equal(gwIP) {
			ip := net.ParseIP(contNetwork.GlobalIPv6Address)
			id := common.EndpointAddr2ID(ip)
			return &id
		}
	}
	return nil
}

func (d *Daemon) fetchK8sLabels(dockerLbls map[string]string) (map[string]string, error) {
	ns := k8sDockerLbls.GetPodNamespace(dockerLbls)
	if ns == "" {
		ns = "default"
	}
	podName := k8sDockerLbls.GetPodName(dockerLbls)
	if podName == "" {
		return nil, nil
	}
	result := &k8sAPI.Pod{}
	log.Debugf("Connecting to kubernetes to retrieve labels for pod %s ns %s", podName, ns)
	if err := d.k8sClient.Get().Namespace(ns).Resource("pods").Name(podName).Do().Into(result); err != nil {
		return nil, err
	}
	k8sLabels := result.GetLabels()
	k8sLabels[common.K8sPodNamespaceLabel] = ns
	return k8sLabels, nil
}

func (d *Daemon) getFilteredLabels(allLabels map[string]string) types.Labels {
	var ciliumLabels, k8sLabels types.Labels
	if podName := k8sDockerLbls.GetPodName(allLabels); podName != "" {
		k8sNormalLabels, err := d.fetchK8sLabels(allLabels)
		if err != nil {
			log.Warningf("Error while getting kubernetes labels: %s", err)
		} else if k8sNormalLabels != nil {
			k8sLabels = types.Map2Labels(k8sNormalLabels, common.K8sLabelSource)
		}
	}

	ciliumLabels = types.Map2Labels(allLabels, common.CiliumLabelSource)

	ciliumLabels.MergeLabels(k8sLabels)

	d.conf.ValidLabelPrefixesMU.RLock()
	defer d.conf.ValidLabelPrefixesMU.RUnlock()
	return d.conf.ValidLabelPrefixes.FilterLabels(ciliumLabels)
}

func (d *Daemon) createContainer(dockerID string, allLabels map[string]string) {
	log.Debugf("Processing container %s", dockerID)

	ciliumLabels := d.getFilteredLabels(allLabels)

	if err := d.refreshContainerLabels(dockerID, ciliumLabels, true); err != nil {
		log.Errorf("%s", err)
	}
}

func (d *Daemon) refreshContainerLabels(dockerID string, labels types.Labels, isProbe bool) error {
	if isNewContainer, container, err := d.updateOperationalLabels(dockerID, labels, isProbe); err != nil {
		return err
	} else {
		return d.updateContainer(container, isNewContainer)
	}
}

func (d *Daemon) updateOperationalLabels(dockerID string, newLabels types.Labels, isProbe bool) (bool, *types.Container, error) {
	dockerCont, err := d.dockerClient.ContainerInspect(dockerID)
	if err != nil {
		return false, nil, fmt.Errorf("Error while inspecting container '%s': %s", dockerID, err)
	}

	isNewContainer := false
	d.containersMU.Lock()

	var (
		cont           types.Container
		epLabelsSHA256 string
	)

	if ciliumContainer, ok := d.containers[dockerID]; !ok {
		isNewContainer = true
		opLabels := types.OpLabels{
			AllLabels:      newLabels.DeepCopy(),
			UserLabels:     types.Labels{},
			ProbeLabels:    newLabels.DeepCopy(),
			EndpointLabels: newLabels.DeepCopy(),
		}
		cont = types.Container{dockerCont, opLabels, 0}
	} else {
		if ciliumContainer.NRetries > maxRetries {
			d.containersMU.Unlock()
			return isNewContainer, nil, nil
		}
		ep, err := d.EndpointGetByDockerID(ciliumContainer.ID)
		if err == nil && ep == nil {
			ciliumContainer.NRetries++
		} else {
			ciliumContainer.NRetries = 0
		}

		newLabelsSHA256, err := newLabels.SHA256Sum()
		if err != nil {
			log.Errorf("Error calculating SHA256Sum of labels %+v: %s", newLabels, err)
		}

		if isProbe {
			probeLabelsSHA256, err := ciliumContainer.OpLabels.ProbeLabels.SHA256Sum()
			if err != nil {
				log.Errorf("Error calculating SHA256Sum of labels %+v: %s", ciliumContainer.OpLabels.ProbeLabels, err)
			}
			if probeLabelsSHA256 != newLabelsSHA256 {
				isNewContainer = true
				epLabelsSHA256, err = ciliumContainer.OpLabels.EndpointLabels.SHA256Sum()
				if err != nil {
					log.Errorf("Error calculating SHA256Sum of labels %+v: %s", ciliumContainer.OpLabels.EndpointLabels, err)
				}
				// probe labels have changed
				// we need to find out which labels were deleted and added
				deletedLabels := ciliumContainer.OpLabels.ProbeLabels.DeepCopy()
				for k, v := range newLabels {
					if ciliumContainer.OpLabels.ProbeLabels[k] == nil {
						tmpLbl1 := *v
						tmpLbl2 := *v
						ciliumContainer.OpLabels.AllLabels[k] = &tmpLbl1
						ciliumContainer.OpLabels.EndpointLabels[k] = &tmpLbl2
					} else {
						delete(deletedLabels, k)
					}
				}

				for k, _ := range deletedLabels {
					delete(ciliumContainer.OpLabels.AllLabels, k)
					delete(ciliumContainer.OpLabels.EndpointLabels, k)
				}
			}
		} else {
			// If it is not probe then all newLabels will be applied

			epLabelsSHA256, err = ciliumContainer.OpLabels.EndpointLabels.SHA256Sum()
			if err != nil {
				log.Errorf("Error calculating SHA256Sum of labels %+v: %s", ciliumContainer.OpLabels.EndpointLabels, err)
			}
			if epLabelsSHA256 != newLabelsSHA256 {
				isNewContainer = true
				ciliumContainer.OpLabels.EndpointLabels = newLabels
			}
		}

		cont = types.Container{dockerCont, ciliumContainer.OpLabels, ciliumContainer.NRetries}
	}

	if isNewContainer {
		if err := d.DeleteLabelsBySHA256(epLabelsSHA256, dockerID); err != nil {
			log.Errorf("Error while deleting old labels (%+v) of container %s: %s", epLabelsSHA256, dockerID, err)
		}
	}

	d.containers[dockerID] = &cont
	contCpy := cont

	d.containersMU.Unlock()

	return isNewContainer, &contCpy, nil
}

func (d *Daemon) updateContainer(container *types.Container, isNewContainer bool) error {
	if container == nil {
		return nil
	}

	dockerID := container.ID

	log.Debugf("Putting labels %+v", container.OpLabels.EndpointLabels)
	secCtxlabels, isNewLabel, err := d.PutLabels(container.OpLabels.EndpointLabels, dockerID)
	if err != nil {
		return fmt.Errorf("Error while getting labels ID: %s", err)
	}

	ciliumID := getCiliumEndpointID(container.ContainerJSON, d.conf.NodeAddress)
	var dockerEPID string
	if container.ContainerJSON.NetworkSettings != nil {
		dockerEPID = container.ContainerJSON.NetworkSettings.EndpointID
	}

	try := 1
	maxTries := 5
	var ep *types.Endpoint
	for try < maxTries {
		if ep = d.setEndpointSecLabel(ciliumID, dockerID, dockerEPID, secCtxlabels); ep != nil {
			break
		}
		if container.IsDockerOrInfracontainer() {
			log.Warningf("Something went wrong, the docker ID '%s' was not locally found. Attempt... %d", dockerID, try)
		}
		time.Sleep(time.Duration(try) * time.Second)
		try++
	}
	if try >= maxTries {
		if container.IsDockerOrInfracontainer() {
			return fmt.Errorf("It was impossible to store the SecLabel %d for docker endpoint ID '%s'", secCtxlabels.ID, dockerID)
		}
		return nil
	}
	if isNewContainer {
		if err = d.createBPFMAPs(ep.ID); err != nil {
			return fmt.Errorf("Unable to create & attach BPF programs for container %s: %s", dockerID, err)
		}
	}

	// Perform the policy map updates after programs have been created
	if isNewLabel || isNewContainer {
		d.triggerPolicyUpdates([]uint32{secCtxlabels.ID})
	}

	log.Infof("Added SecLabelID %d to container %s", secCtxlabels.ID, dockerID)

	return nil
}

func (d *Daemon) deleteContainer(dockerID string) {
	log.Debugf("Processing container %s", dockerID)

	d.containersMU.Lock()
	if container, ok := d.containers[dockerID]; ok {
		ep, err := d.EndpointGetByDockerID(dockerID)
		if err != nil {
			log.Warningf("Error while getting endpoint by docker ID: %s", err)
		}

		sha256sum, err := container.OpLabels.EndpointLabels.SHA256Sum()
		if err != nil {
			log.Errorf("Error while creating SHA256Sum for labels %+v: %s", container.OpLabels.EndpointLabels, err)
		}

		if err := d.DeleteLabelsBySHA256(sha256sum, dockerID); err != nil {
			log.Errorf("Error while deleting labels (SHA256SUM:%s) %+v: %s", sha256sum, container.OpLabels.EndpointLabels, err)
		}

		delete(d.containers, dockerID)

		if ep != nil {
			d.EndpointLeave(ep.ID)
		}
	}
	d.containersMU.Unlock()
}
<commit_msg>seclabel: pruning dangling non-cilium containers<commit_after>package daemon

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"strconv"
	"time"

	"github.com/noironetworks/cilium-net/common"
	"github.com/noironetworks/cilium-net/common/types"

	dTypes "github.com/docker/engine-api/types"
	dTypesEvents "github.com/docker/engine-api/types/events"
	k8sAPI "k8s.io/kubernetes/pkg/api"
	k8sDockerLbls "k8s.io/kubernetes/pkg/kubelet/types"
)

const (
	syncRateDocker = time.Duration(30 * time.Second)

	maxRetries = 3
)

// EnableDockerEventListener watches for docker events. Performs the plumbing for the
// containers started or dead.
func (d *Daemon) EnableDockerEventListener() error {
	eo := dTypes.EventsOptions{Since: strconv.FormatInt(time.Now().Unix(), 10)}
	r, err := d.dockerClient.Events(eo)
	if err != nil {
		return err
	}

	d.EnableDockerSync(true)

	log.Debugf("Listening for docker events")
	go d.listenForEvents(r)
	return nil
}

func (d *Daemon) EnableDockerSync(once bool) {
	for {
		cList, err := d.dockerClient.ContainerList(dTypes.ContainerListOptions{All: false})
		if err != nil {
			log.Errorf("Failed to retrieve the container list %s", err)
		}
		for _, cont := range cList {
			go d.createContainer(cont.ID, cont.Labels)
		}

		if once {
			return
		}
		time.Sleep(syncRateDocker)
	}
}

func (d *Daemon) listenForEvents(reader io.ReadCloser) {
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		var e dTypesEvents.Message
		if err := json.Unmarshal(scanner.Bytes(), &e); err != nil {
			log.Errorf("Error while unmarshalling event: %+v", e)
		}
		log.Debugf("Processing an event %+v", e)
		go d.processEvent(e)
	}
	if err := scanner.Err(); err != nil {
		log.Errorf("Error while reading events: %+v", err)
	}
}

func (d *Daemon) processEvent(m dTypesEvents.Message) {
	if m.Type == "container" {
		switch m.Status {
		case "start":
			d.createContainer(m.ID, m.Actor.Attributes)
		case "die":
			d.deleteContainer(m.ID)
		}
	}
}

func getCiliumEndpointID(cont dTypes.ContainerJSON, gwIP net.IP) *uint16 {
	for _, contNetwork := range cont.NetworkSettings.Networks {
		ipv6gw := net.ParseIP(contNetwork.IPv6Gateway)
		if ipv6gw.Equal(gwIP) {
			ip := net.ParseIP(contNetwork.GlobalIPv6Address)
			id := common.EndpointAddr2ID(ip)
			return &id
		}
	}
	return nil
}

func (d *Daemon) fetchK8sLabels(dockerLbls map[string]string) (map[string]string, error) {
	ns := k8sDockerLbls.GetPodNamespace(dockerLbls)
	if ns == "" {
		ns = "default"
	}
	podName := k8sDockerLbls.GetPodName(dockerLbls)
	if podName == "" {
		return nil, nil
	}
	result := &k8sAPI.Pod{}
	log.Debugf("Connecting to kubernetes to retrieve labels for pod %s ns %s", podName, ns)
	if err := d.k8sClient.Get().Namespace(ns).Resource("pods").Name(podName).Do().Into(result); err != nil {
		return nil, err
	}
	k8sLabels := result.GetLabels()
	k8sLabels[common.K8sPodNamespaceLabel] = ns
	return k8sLabels, nil
}

func (d *Daemon) getFilteredLabels(allLabels map[string]string) types.Labels {
	var ciliumLabels, k8sLabels types.Labels
	if podName := k8sDockerLbls.GetPodName(allLabels); podName != "" {
		k8sNormalLabels, err := d.fetchK8sLabels(allLabels)
		if err != nil {
			log.Warningf("Error while getting kubernetes labels: %s", err)
		} else if k8sNormalLabels != nil {
			k8sLabels = types.Map2Labels(k8sNormalLabels, common.K8sLabelSource)
		}
	}

	ciliumLabels = types.Map2Labels(allLabels, common.CiliumLabelSource)

	ciliumLabels.MergeLabels(k8sLabels)

	d.conf.ValidLabelPrefixesMU.RLock()
	defer d.conf.ValidLabelPrefixesMU.RUnlock()
	return d.conf.ValidLabelPrefixes.FilterLabels(ciliumLabels)
}

func (d *Daemon) createContainer(dockerID string, allLabels map[string]string) {
	log.Debugf("Processing container %s", dockerID)

	ciliumLabels := d.getFilteredLabels(allLabels)

	if err := d.refreshContainerLabels(dockerID, ciliumLabels, true); err != nil {
		log.Errorf("%s", err)
	}
}

func (d *Daemon) refreshContainerLabels(dockerID string, labels types.Labels, isProbe bool) error {
	if isNewContainer, container, err := d.updateOperationalLabels(dockerID, labels, isProbe); err != nil {
		return err
	} else {
		return d.updateContainer(container, isNewContainer)
	}
}

func (d *Daemon) updateOperationalLabels(dockerID string, newLabels types.Labels, isProbe bool) (bool, *types.Container, error) {
	dockerCont, err := d.dockerClient.ContainerInspect(dockerID)
	if err != nil {
		return false, nil, fmt.Errorf("Error while inspecting container '%s': %s", dockerID, err)
	}

	isNewContainer := false
	d.containersMU.Lock()

	var (
		cont           types.Container
		epLabelsSHA256 string
	)

	if ciliumContainer, ok := d.containers[dockerID]; !ok {
		isNewContainer = true
		opLabels := types.OpLabels{
			AllLabels:      newLabels.DeepCopy(),
			UserLabels:     types.Labels{},
			ProbeLabels:    newLabels.DeepCopy(),
			EndpointLabels: newLabels.DeepCopy(),
		}
		cont = types.Container{dockerCont, opLabels, 0}
	} else {
		if ciliumContainer.NRetries > maxRetries {
			epSHA256Sum, err := ciliumContainer.OpLabels.EndpointLabels.SHA256Sum()
			if err != nil {
				log.Errorf("Error calculating SHA256Sum of labels %+v: %s", ciliumContainer.OpLabels.EndpointLabels, err)
			}
			d.DeleteLabelsBySHA256(epSHA256Sum, ciliumContainer.ID)
			d.containersMU.Unlock()
			return isNewContainer, nil, nil
		}
		ep, err := d.EndpointGetByDockerID(ciliumContainer.ID)
		if err == nil && ep == nil {
			ciliumContainer.NRetries++
		} else {
			ciliumContainer.NRetries = 0
		}

		newLabelsSHA256, err := newLabels.SHA256Sum()
		if err != nil {
			log.Errorf("Error calculating SHA256Sum of labels %+v: %s", newLabels, err)
		}

		if isProbe {
			probeLabelsSHA256, err := ciliumContainer.OpLabels.ProbeLabels.SHA256Sum()
			if err != nil {
				log.Errorf("Error calculating SHA256Sum of labels %+v: %s", ciliumContainer.OpLabels.ProbeLabels, err)
			}
			if probeLabelsSHA256 != newLabelsSHA256 {
				isNewContainer = true
				epLabelsSHA256, err = ciliumContainer.OpLabels.EndpointLabels.SHA256Sum()
				if err != nil {
					log.Errorf("Error calculating SHA256Sum of labels %+v: %s", ciliumContainer.OpLabels.EndpointLabels, err)
				}
				// probe labels have changed
				// we need to find out which labels were deleted and added
				deletedLabels := ciliumContainer.OpLabels.ProbeLabels.DeepCopy()
				for k, v := range newLabels {
					if ciliumContainer.OpLabels.ProbeLabels[k] == nil {
						tmpLbl1 := *v
						tmpLbl2 := *v
						ciliumContainer.OpLabels.AllLabels[k] = &tmpLbl1
						ciliumContainer.OpLabels.EndpointLabels[k] = &tmpLbl2
					} else {
						delete(deletedLabels, k)
					}
				}

				for k, _ := range deletedLabels {
					delete(ciliumContainer.OpLabels.AllLabels, k)
					delete(ciliumContainer.OpLabels.EndpointLabels, k)
				}
			}
		} else {
			// If it is not probe then all newLabels will be applied

			epLabelsSHA256, err = ciliumContainer.OpLabels.EndpointLabels.SHA256Sum()
			if err != nil {
				log.Errorf("Error calculating SHA256Sum of labels %+v: %s", ciliumContainer.OpLabels.EndpointLabels, err)
			}
			if epLabelsSHA256 != newLabelsSHA256 {
				isNewContainer = true
				ciliumContainer.OpLabels.EndpointLabels = newLabels
			}
		}

		cont = types.Container{dockerCont, ciliumContainer.OpLabels, ciliumContainer.NRetries}
	}

	if isNewContainer {
		if err := d.DeleteLabelsBySHA256(epLabelsSHA256, dockerID); err != nil {
			log.Errorf("Error while deleting old labels (%+v) of container %s: %s", epLabelsSHA256, dockerID, err)
		}
	}

	d.containers[dockerID] = &cont
	contCpy := cont

	d.containersMU.Unlock()

	return isNewContainer, &contCpy, nil
}

func (d *Daemon) updateContainer(container *types.Container, isNewContainer bool) error {
	if container == nil {
		return nil
	}

	dockerID := container.ID

	log.Debugf("Putting labels %+v", container.OpLabels.EndpointLabels)
	secCtxlabels, isNewLabel, err := d.PutLabels(container.OpLabels.EndpointLabels, dockerID)
	if err != nil {
		return fmt.Errorf("Error while getting labels ID: %s", err)
	}

	ciliumID := getCiliumEndpointID(container.ContainerJSON, d.conf.NodeAddress)
	var dockerEPID string
	if container.ContainerJSON.NetworkSettings != nil {
		dockerEPID = container.ContainerJSON.NetworkSettings.EndpointID
	}

	try := 1
	maxTries := 5
	var ep *types.Endpoint
	for try < maxTries {
		if ep = d.setEndpointSecLabel(ciliumID, dockerID, dockerEPID, secCtxlabels); ep != nil {
			break
		}
		if container.IsDockerOrInfracontainer() {
			log.Warningf("Something went wrong, the docker ID '%s' was not locally found. Attempt... %d", dockerID, try)
		}
		time.Sleep(time.Duration(try) * time.Second)
		try++
	}
	if try >= maxTries {
		if container.IsDockerOrInfracontainer() {
			return fmt.Errorf("It was impossible to store the SecLabel %d for docker endpoint ID '%s'", secCtxlabels.ID, dockerID)
		}
		return nil
	}
	if isNewContainer {
		if err = d.createBPFMAPs(ep.ID); err != nil {
			return fmt.Errorf("Unable to create & attach BPF programs for container %s: %s", dockerID, err)
		}
	}

	// Perform the policy map updates after programs have been created
	if isNewLabel || isNewContainer {
		d.triggerPolicyUpdates([]uint32{secCtxlabels.ID})
	}

	log.Infof("Added SecLabelID %d to container %s", secCtxlabels.ID, dockerID)

	return nil
}

func (d *Daemon) deleteContainer(dockerID string) {
	log.Debugf("Processing container %s", dockerID)

	d.containersMU.Lock()
	if container, ok := d.containers[dockerID]; ok {
		ep, err := d.EndpointGetByDockerID(dockerID)
		if err != nil {
			log.Warningf("Error while getting endpoint by docker ID: %s", err)
		}

		sha256sum, err := container.OpLabels.EndpointLabels.SHA256Sum()
		if err != nil {
			log.Errorf("Error while creating SHA256Sum for labels %+v: %s", container.OpLabels.EndpointLabels, err)
		}

		if err := d.DeleteLabelsBySHA256(sha256sum, dockerID); err != nil {
			log.Errorf("Error while deleting labels (SHA256SUM:%s) %+v: %s", sha256sum, container.OpLabels.EndpointLabels, err)
		}

		delete(d.containers, dockerID)

		if ep != nil {
			d.EndpointLeave(ep.ID)
		}
	}
	d.containersMU.Unlock()
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 The Protocol Authors.

package protocol

import (
	"errors"
)

var (
	ErrNoError    error
	ErrGeneric    = errors.New("generic error")
	ErrNoSuchFile = errors.New("no such file")
	ErrInvalid    = errors.New("file is invalid")
)

var lookupError = map[ErrorCode]error{
	ErrorCodeNoError:     ErrNoError,
	ErrorCodeGeneric:     ErrGeneric,
	ErrorCodeNoSuchFile:  ErrNoSuchFile,
	ErrorCodeInvalidFile: ErrInvalid,
}

var lookupCode = map[error]ErrorCode{
	ErrNoError:    ErrorCodeNoError,
	ErrGeneric:    ErrorCodeGeneric,
	ErrNoSuchFile: ErrorCodeNoSuchFile,
	ErrInvalid:    ErrorCodeInvalidFile,
}

func codeToError(code ErrorCode) error {
	err, ok := lookupError[code]
	if !ok {
		return ErrGeneric
	}
	return err
}

func errorToCode(err error) ErrorCode {
	code, ok := lookupCode[err]
	if !ok {
		return ErrorCodeGeneric
	}
	return code
}
<commit_msg>lib/protocol: Simplify codeToError, errorToCode<commit_after>// Copyright (C) 2014 The Protocol Authors.

package protocol

import "errors"

var (
	ErrGeneric    = errors.New("generic error")
	ErrNoSuchFile = errors.New("no such file")
	ErrInvalid    = errors.New("file is invalid")
)

func codeToError(code ErrorCode) error {
	switch code {
	case ErrorCodeNoError:
		return nil
	case ErrorCodeNoSuchFile:
		return ErrNoSuchFile
	case ErrorCodeInvalidFile:
		return ErrInvalid
	default:
		return ErrGeneric
	}
}

func errorToCode(err error) ErrorCode {
	switch err {
	case nil:
		return ErrorCodeNoError
	case ErrNoSuchFile:
		return ErrorCodeNoSuchFile
	case ErrInvalid:
		return ErrorCodeInvalidFile
	default:
		return ErrorCodeGeneric
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 The Wuffs 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
//
//    https://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 rac

import (
	"io"
)

const (
	numRBuffersPerWorker = 2
	rBufferSize          = 65536
)

type rBuffer [rBufferSize]byte

// rWork is a unit of work for concurrent reading. The Manager sends dRanges
// for Workers to read. Workers send filled buffers to the concReader.
type rWork struct {
	err error

	// dRange is set by the Manager goroutine, for a Worker's incoming work.
	// That Worker may slice that dRange into smaller pieces of outgoing work.
	// Each outgoing piece has a dRange.Size() of at most rBufferSize.
	dRange Range

	// buffer[i:j] holds bytes decompressed from the underlying RAC file but
	// not yet served onwards to concReader.Read's caller.
	//
	// j should equal dRange.Size().
	//
	// When the buffer is done with (e.g. if i == j, or if we've canceled a
	// read-in-progress), the buffer is returned to its owning Worker
	// goroutine via recyclec.
	//
	// These fields are not used by the Manager goroutine.
	buffer   *rBuffer
	i, j     uint32
	recyclec chan<- *rBuffer
}

func (r *rWork) recycle() {
	if (r.recyclec != nil) && (r.buffer != nil) {
		r.recyclec <- r.buffer
		r.recyclec = nil
		r.buffer = nil
		r.i = 0
		r.j = 0
	}
}

// stopWork is a cancel (non-permanent, keepWorking = true) or close
// (permanent, keepWorking = false) notice to the Manager and Workers. The
// recipient needs to acknowledge the request by receiving from ackc (if
// non-nil).
type stopWork struct {
	ackc        <-chan struct{}
	keepWorking bool
}

// concReader co-ordinates multiple goroutines (1 Manager, multiple Workers)
// serving a Reader.
type concReader struct {
	// Channels between the concReader, the Manager and multiple Workers.
	//
	// The concReader sends roic and                recvs resc.
	// The Manager    recvs roic and sends reqc.
	// Each Worker                   recvs reqc and sends resc.
	roic chan Range // Region of Interest channel.
	reqc chan rWork // Work-Request       channel.
	resc chan rWork // Work-Response      channel.

	// stopc and ackc are used to synchronize the concReader, Manager and
	// Workers, either canceling work-in-progress or closing everything down.
	//
	// Importantly, these are unbuffered channels. Sending and receiving will
	// wait for the other end to synchronize.
	stopc chan stopWork
	ackc  chan struct{}

	// currWork holds the unit of work currently being processed by Read. It
	// will be recycled after Read is done with it, or if a Seek causes us to
	// cancel the work-in-progress.
	currWork rWork

	// completedWorks hold completed units of work that are not the next unit
	// to be sent out via Read. Works may arrive out of order.
	//
	// The map is keyed by an rWork's dRange[0].
	completedWorks map[int64]rWork

	// numWorkers is the number of concurrent Workers.
	numWorkers int

	// seekResolved means that Read does not have to seek to pos.
	//
	// Each Seek call is relatively cheap, only changing the pos field. The
	// bulk of the work happens in the first Read call following a Seek call.
	seekResolved bool

	// seenRead means that we've already seen at least one Read call.
	seenRead bool

	// pos is the current position, in DSpace. It is the base value when Seek
	// is called with io.SeekCurrent.
	pos int64

	// posLimit is an upper limit on pos. pos can go higher than it (e.g.
	// seeking past the end of the file in DSpace), but after doing so, Read
	// will always return (0, io.EOF).
	posLimit int64

	// decompressedSize is the size of the RAC file in DSpace.
	decompressedSize int64
}

func (c *concReader) initialize(racReader *Reader) {
	if racReader.Concurrency <= 1 {
		return
	}
	c.numWorkers = racReader.Concurrency
	if c.numWorkers > 65536 {
		c.numWorkers = 65536
	}

	// Set up other state.
	c.completedWorks = map[int64]rWork{}
	c.posLimit = racReader.chunkReader.decompressedSize
	c.decompressedSize = racReader.chunkReader.decompressedSize

	// Set up the Manager and the Workers.
	c.roic = make(chan Range)
	c.reqc = make(chan rWork, c.numWorkers)
	c.resc = make(chan rWork, c.numWorkers*numRBuffersPerWorker)

	// Set up the channels used in stopAnyWorkInProgress. It is important that
	// these are unbuffered, so that communication is also synchronization.
	c.stopc = make(chan stopWork)
	c.ackc = make(chan struct{})

	for i := 0; i < c.numWorkers; i++ {
		rr := racReader.clone()
		rr.Concurrency = 0
		go runRWorker(c.stopc, c.resc, c.reqc, rr)
	}
	go runRManager(c.stopc, c.roic, c.reqc, &racReader.chunkReader)
}

func (c *concReader) ready() bool {
	return c.stopc != nil
}

func (c *concReader) Close() error {
	if c.stopc != nil {
		c.stopAnyWorkInProgress(false)
		c.stopc = nil
	}
	return nil
}

func (c *concReader) CloseWithoutWaiting() error {
	if c.stopc != nil {
		// Just close the c.stopc channel, which should eventually shut down
		// the Manager and Worker goroutines. Everything else can be garbage
		// collected.
		close(c.stopc)
		c.stopc = nil
	}
	return nil
}

func (c *concReader) seek(offset int64, whence int, limit int64) (int64, error) {
	pos := c.pos
	switch whence {
	case io.SeekStart:
		pos = offset
	case io.SeekCurrent:
		pos += offset
	case io.SeekEnd:
		pos = c.decompressedSize + offset
	default:
		return 0, errSeekToInvalidWhence
	}

	if c.pos != pos {
		if pos < 0 {
			return 0, errSeekToNegativePosition
		}
		c.pos = pos
		c.seekResolved = false
	}

	if limit > c.decompressedSize {
		limit = c.decompressedSize
	}
	c.posLimit = limit

	return pos, nil
}

func (c *concReader) Read(p []byte) (int, error) {
	if c.pos >= c.posLimit {
		return 0, io.EOF
	}

	if !c.seekResolved {
		c.seekResolved = true
		if c.seenRead {
			c.stopAnyWorkInProgress(true)
		}
		c.seenRead = true
		c.roic <- Range{c.pos, c.posLimit}
	}

	for numRead := 0; ; {
		if c.pos >= c.posLimit {
			return numRead, io.EOF
		}
		if len(p) == 0 {
			return numRead, nil
		}

		// Fill p from c.currWork.
		if c.currWork.i < c.currWork.j {
			n := copy(p, c.currWork.buffer[c.currWork.i:c.currWork.j])
			p = p[n:]
			numRead += n
			c.pos += int64(n)
			c.currWork.i += uint32(n)
			err := c.currWork.err

			// Recycle c.currWork if we're done with it.
			if c.currWork.i >= c.currWork.j {
				c.currWork.recycle()
			}

			if err != nil {
				return numRead, err
			}
			continue
		}

		c.currWork = c.nextWork()
	}
}

func (c *concReader) nextWork() rWork {
	for {
		if work, ok := c.completedWorks[c.pos]; ok {
			delete(c.completedWorks, c.pos)
			return work
		}
		work := <-c.resc
		c.completedWorks[work.dRange[0]] = work
	}
}

// stopAnyWorkInProgress winds up any Manager and Worker work-in-progress.
// keepWorking is whether those goroutines should stick around to do future
// work. It should be false for closes and true otherwise.
func (c *concReader) stopAnyWorkInProgress(keepWorking bool) {
	// Synchronize the Manager and Workers on stopc (an unbuffered channel).
	for i, n := 0, 1+c.numWorkers; i < n; i++ {
		c.stopc <- stopWork{c.ackc, keepWorking}
	}

	if keepWorking {
		c.recycleBuffers()
	}

	// Synchronize the Manager and Workers on ackc (an unbuffered channel).
	for i, n := 0, 1+c.numWorkers; i < n; i++ {
		c.ackc <- struct{}{}
	}
}

func (c *concReader) recycleBuffers() {
	c.currWork.recycle()

	for k, work := range c.completedWorks {
		work.recycle()
		delete(c.completedWorks, k)
	}

	// Drain c's buffered channels.
	drainWorkChan(c.reqc)
	drainWorkChan(c.resc)
}

func drainWorkChan(c chan rWork) {
	for {
		select {
		case work := <-c:
			work.recycle()
		default:
			return
		}
	}
}

func runRWorker(stopc <-chan stopWork, resc chan<- rWork, reqc <-chan rWork, racReader *Reader) {
	input, output := reqc, (chan<- rWork)(nil)
	outWork := rWork{}

	// dRange is what part of incoming work remains to be read from the
	// racReader.
	dRange := Range{}

	// Each worker owns up to numRBuffersPerWorker buffers, some of which may
	// be temporarily loaned to the concReader goroutine.
	buffers := [numRBuffersPerWorker]*rBuffer{}
	recyclec := make(chan *rBuffer, numRBuffersPerWorker)
	canAlloc := numRBuffersPerWorker

loop:
	for {
		select {
		case stop := <-stopc:
			if stop.ackc != nil {
				<-stop.ackc
			} else {
				// No need to ack. This is CloseWithoutWaiting.
			}
			if !stop.keepWorking {
				return
			}
			continue loop

		case inWork := <-input:
			input = nil
			if inWork.err == nil {
				dRange = inWork.dRange
				if dRange.Empty() {
					inWork.err = errInternalEmptyDRange
				} else {
					inWork.err = racReader.SeekRange(dRange[0], dRange[1])
				}
				if inWork.err == io.EOF {
					inWork.err = io.ErrUnexpectedEOF
				}
			}
			if inWork.err != nil {
				output, outWork = resc, inWork
				continue loop
			}

		case output <- outWork:
			output, outWork = nil, rWork{}

		case recycledBuffer := <-recyclec:
			for i := range buffers {
				if buffers[i] == nil {
					buffers[i], recycledBuffer = recycledBuffer, nil
					break
				}
			}
			if recycledBuffer != nil {
				panic("unreachable")
			}
		}

		// If there's existing outWork, sending it trumps making new outWork.
		if output != nil {
			continue loop
		}

		// If dRange was completely processsed, get new inWork.
		if dRange.Empty() {
			input = reqc
			continue loop
		}

		// Find a new or recycled buffer.
		buffer := (*rBuffer)(nil)
		{
			b := -1
			if buffers[0] != nil {
				b = 0
			} else if buffers[1] != nil {
				b = 1
			}

			if b >= 0 {
				buffer, buffers[b] = buffers[b], nil
			} else if canAlloc == 0 {
				// Wait until we receive a recycled buffer.
				continue loop
			} else {
				canAlloc--
				buffer = &rBuffer{}
			}
		}

		// Make a new outWork, shrinking dRange to be whatever's left over.
		{
			n, err := racReader.Read(buffer[:])
			if err == io.EOF {
				err = nil
			}
			oldDPos := dRange[0]
			newDPos := dRange[0] + int64(n)
			dRange[0] = newDPos
			output, outWork = resc, rWork{
				err:      err,
				dRange:   Range{oldDPos, newDPos},
				buffer:   buffer,
				i:        0,
				j:        uint32(n),
				recyclec: recyclec,
			}
		}
	}
}

func runRManager(stopc <-chan stopWork, roic <-chan Range, reqc chan<- rWork, chunkReader *ChunkReader) {
	input, output := roic, (chan<- rWork)(nil)
	roi := Range{}
	work := rWork{}

loop:
	for {
		select {
		case stop := <-stopc:
			if stop.ackc != nil {
				<-stop.ackc
			} else {
				// No need to ack. This is CloseWithoutWaiting.
			}
			if !stop.keepWorking {
				return
			}
			continue loop

		case roi = <-input:
			input, output = nil, reqc
			if err := chunkReader.SeekToChunkContaining(roi[0]); err != nil {
				if err == io.EOF {
					err = io.ErrUnexpectedEOF
				}
				work = rWork{err: err}
				continue loop
			}

		case output <- work:
			err := work.err
			work = rWork{}
			if err != nil {
				input, output = roic, nil
				continue loop
			}
		}

		for {
			chunk, err := chunkReader.NextChunk()
			if err == io.EOF {
				input, output = roic, nil
				continue loop
			} else if err != nil {
				work = rWork{err: err}
				continue loop
			}

			if chunk.DRange[0] >= roi[1] {
				input, output = roic, nil
				continue loop
			}
			if dr := chunk.DRange.Intersect(roi); !dr.Empty() {
				work = rWork{dRange: dr}
				continue loop
			}
		}
	}
}
<commit_msg>Tweak how rac.concReader.Read moves to next work<commit_after>// Copyright 2019 The Wuffs 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
//
//    https://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 rac

import (
	"io"
)

const (
	numRBuffersPerWorker = 2
	rBufferSize          = 65536
)

type rBuffer [rBufferSize]byte

// rWork is a unit of work for concurrent reading. The Manager sends dRanges
// for Workers to read. Workers send filled buffers to the concReader.
type rWork struct {
	err error

	// dRange is set by the Manager goroutine, for a Worker's incoming work.
	// That Worker may slice that dRange into smaller pieces of outgoing work.
	// Each outgoing piece has a dRange.Size() of at most rBufferSize.
	dRange Range

	// buffer[i:j] holds bytes decompressed from the underlying RAC file but
	// not yet served onwards to concReader.Read's caller.
	//
	// j should equal dRange.Size().
	//
	// When the buffer is done with (e.g. if i == j, or if we've canceled a
	// read-in-progress), the buffer is returned to its owning Worker
	// goroutine via recyclec.
	//
	// These fields are not used by the Manager goroutine.
	buffer   *rBuffer
	i, j     uint32
	recyclec chan<- *rBuffer
}

func (r *rWork) recycle() {
	if (r.recyclec != nil) && (r.buffer != nil) {
		r.recyclec <- r.buffer
		r.recyclec = nil
		r.buffer = nil
		r.i = 0
		r.j = 0
	}
}

// stopWork is a cancel (non-permanent, keepWorking = true) or close
// (permanent, keepWorking = false) notice to the Manager and Workers. The
// recipient needs to acknowledge the request by receiving from ackc (if
// non-nil).
type stopWork struct {
	ackc        <-chan struct{}
	keepWorking bool
}

// concReader co-ordinates multiple goroutines (1 Manager, multiple Workers)
// serving a Reader.
type concReader struct {
	// Channels between the concReader, the Manager and multiple Workers.
	//
	// The concReader sends roic and                recvs resc.
	// The Manager    recvs roic and sends reqc.
	// Each Worker                   recvs reqc and sends resc.
	roic chan Range // Region of Interest channel.
	reqc chan rWork // Work-Request       channel.
	resc chan rWork // Work-Response      channel.

	// stopc and ackc are used to synchronize the concReader, Manager and
	// Workers, either canceling work-in-progress or closing everything down.
	//
	// Importantly, these are unbuffered channels. Sending and receiving will
	// wait for the other end to synchronize.
	stopc chan stopWork
	ackc  chan struct{}

	// currWork holds the unit of work currently being processed by Read. It
	// will be recycled after Read is done with it, or if a Seek causes us to
	// cancel the work-in-progress.
	currWork rWork

	// completedWorks hold completed units of work that are not the next unit
	// to be sent out via Read. Works may arrive out of order.
	//
	// The map is keyed by an rWork's dRange[0].
	completedWorks map[int64]rWork

	// numWorkers is the number of concurrent Workers.
	numWorkers int

	// seekResolved means that Read does not have to seek to pos.
	//
	// Each Seek call is relatively cheap, only changing the pos field. The
	// bulk of the work happens in the first Read call following a Seek call.
	seekResolved bool

	// seenRead means that we've already seen at least one Read call.
	seenRead bool

	// pos is the current position, in DSpace. It is the base value when Seek
	// is called with io.SeekCurrent.
	pos int64

	// posLimit is an upper limit on pos. pos can go higher than it (e.g.
	// seeking past the end of the file in DSpace), but after doing so, Read
	// will always return (0, io.EOF).
	posLimit int64

	// decompressedSize is the size of the RAC file in DSpace.
	decompressedSize int64
}

func (c *concReader) initialize(racReader *Reader) {
	if racReader.Concurrency <= 1 {
		return
	}
	c.numWorkers = racReader.Concurrency
	if c.numWorkers > 65536 {
		c.numWorkers = 65536
	}

	// Set up other state.
	c.completedWorks = map[int64]rWork{}
	c.posLimit = racReader.chunkReader.decompressedSize
	c.decompressedSize = racReader.chunkReader.decompressedSize

	// Set up the Manager and the Workers.
	c.roic = make(chan Range)
	c.reqc = make(chan rWork, c.numWorkers)
	c.resc = make(chan rWork, c.numWorkers*numRBuffersPerWorker)

	// Set up the channels used in stopAnyWorkInProgress. It is important that
	// these are unbuffered, so that communication is also synchronization.
	c.stopc = make(chan stopWork)
	c.ackc = make(chan struct{})

	for i := 0; i < c.numWorkers; i++ {
		rr := racReader.clone()
		rr.Concurrency = 0
		go runRWorker(c.stopc, c.resc, c.reqc, rr)
	}
	go runRManager(c.stopc, c.roic, c.reqc, &racReader.chunkReader)
}

func (c *concReader) ready() bool {
	return c.stopc != nil
}

func (c *concReader) Close() error {
	if c.stopc != nil {
		c.stopAnyWorkInProgress(false)
		c.stopc = nil
	}
	return nil
}

func (c *concReader) CloseWithoutWaiting() error {
	if c.stopc != nil {
		// Just close the c.stopc channel, which should eventually shut down
		// the Manager and Worker goroutines. Everything else can be garbage
		// collected.
		close(c.stopc)
		c.stopc = nil
	}
	return nil
}

func (c *concReader) seek(offset int64, whence int, limit int64) (int64, error) {
	pos := c.pos
	switch whence {
	case io.SeekStart:
		pos = offset
	case io.SeekCurrent:
		pos += offset
	case io.SeekEnd:
		pos = c.decompressedSize + offset
	default:
		return 0, errSeekToInvalidWhence
	}

	if c.pos != pos {
		if pos < 0 {
			return 0, errSeekToNegativePosition
		}
		c.pos = pos
		c.seekResolved = false
	}

	if limit > c.decompressedSize {
		limit = c.decompressedSize
	}
	c.posLimit = limit

	return pos, nil
}

func (c *concReader) Read(p []byte) (int, error) {
	if c.pos >= c.posLimit {
		return 0, io.EOF
	}

	if !c.seekResolved {
		c.seekResolved = true
		if c.seenRead {
			c.stopAnyWorkInProgress(true)
		}
		c.seenRead = true
		c.roic <- Range{c.pos, c.posLimit}
	}

	for numRead := 0; ; {
		if c.pos >= c.posLimit {
			return numRead, io.EOF
		}
		if len(p) == 0 {
			return numRead, nil
		}
		if c.currWork.i >= c.currWork.j {
			err := c.currWork.err
			c.currWork.recycle()
			if err != nil {
				return numRead, err
			}
			c.currWork = c.nextWork()
		}

		// Fill p from c.currWork.
		n := copy(p, c.currWork.buffer[c.currWork.i:c.currWork.j])
		p = p[n:]
		numRead += n
		c.pos += int64(n)
		c.currWork.i += uint32(n)
	}
}

func (c *concReader) nextWork() rWork {
	for {
		if work, ok := c.completedWorks[c.pos]; ok {
			delete(c.completedWorks, c.pos)
			return work
		}
		work := <-c.resc
		c.completedWorks[work.dRange[0]] = work
	}
}

// stopAnyWorkInProgress winds up any Manager and Worker work-in-progress.
// keepWorking is whether those goroutines should stick around to do future
// work. It should be false for closes and true otherwise.
func (c *concReader) stopAnyWorkInProgress(keepWorking bool) {
	// Synchronize the Manager and Workers on stopc (an unbuffered channel).
	for i, n := 0, 1+c.numWorkers; i < n; i++ {
		c.stopc <- stopWork{c.ackc, keepWorking}
	}

	if keepWorking {
		c.recycleBuffers()
	}

	// Synchronize the Manager and Workers on ackc (an unbuffered channel).
	for i, n := 0, 1+c.numWorkers; i < n; i++ {
		c.ackc <- struct{}{}
	}
}

func (c *concReader) recycleBuffers() {
	c.currWork.recycle()

	for k, work := range c.completedWorks {
		work.recycle()
		delete(c.completedWorks, k)
	}

	// Drain c's buffered channels.
	drainWorkChan(c.reqc)
	drainWorkChan(c.resc)
}

func drainWorkChan(c chan rWork) {
	for {
		select {
		case work := <-c:
			work.recycle()
		default:
			return
		}
	}
}

func runRWorker(stopc <-chan stopWork, resc chan<- rWork, reqc <-chan rWork, racReader *Reader) {
	input, output := reqc, (chan<- rWork)(nil)
	outWork := rWork{}

	// dRange is what part of incoming work remains to be read from the
	// racReader.
	dRange := Range{}

	// Each worker owns up to numRBuffersPerWorker buffers, some of which may
	// be temporarily loaned to the concReader goroutine.
	buffers := [numRBuffersPerWorker]*rBuffer{}
	recyclec := make(chan *rBuffer, numRBuffersPerWorker)
	canAlloc := numRBuffersPerWorker

loop:
	for {
		select {
		case stop := <-stopc:
			if stop.ackc != nil {
				<-stop.ackc
			} else {
				// No need to ack. This is CloseWithoutWaiting.
			}
			if !stop.keepWorking {
				return
			}
			continue loop

		case inWork := <-input:
			input = nil
			if inWork.err == nil {
				dRange = inWork.dRange
				if dRange.Empty() {
					inWork.err = errInternalEmptyDRange
				} else {
					inWork.err = racReader.SeekRange(dRange[0], dRange[1])
				}
				if inWork.err == io.EOF {
					inWork.err = io.ErrUnexpectedEOF
				}
			}
			if inWork.err != nil {
				output, outWork = resc, inWork
				continue loop
			}

		case output <- outWork:
			output, outWork = nil, rWork{}

		case recycledBuffer := <-recyclec:
			for i := range buffers {
				if buffers[i] == nil {
					buffers[i], recycledBuffer = recycledBuffer, nil
					break
				}
			}
			if recycledBuffer != nil {
				panic("unreachable")
			}
		}

		// If there's existing outWork, sending it trumps making new outWork.
		if output != nil {
			continue loop
		}

		// If dRange was completely processsed, get new inWork.
		if dRange.Empty() {
			input = reqc
			continue loop
		}

		// Find a new or recycled buffer.
		buffer := (*rBuffer)(nil)
		{
			b := -1
			if buffers[0] != nil {
				b = 0
			} else if buffers[1] != nil {
				b = 1
			}

			if b >= 0 {
				buffer, buffers[b] = buffers[b], nil
			} else if canAlloc == 0 {
				// Wait until we receive a recycled buffer.
				continue loop
			} else {
				canAlloc--
				buffer = &rBuffer{}
			}
		}

		// Make a new outWork, shrinking dRange to be whatever's left over.
		{
			n, err := racReader.Read(buffer[:])
			if err == io.EOF {
				err = nil
			}
			oldDPos := dRange[0]
			newDPos := dRange[0] + int64(n)
			dRange[0] = newDPos
			output, outWork = resc, rWork{
				err:      err,
				dRange:   Range{oldDPos, newDPos},
				buffer:   buffer,
				i:        0,
				j:        uint32(n),
				recyclec: recyclec,
			}
		}
	}
}

func runRManager(stopc <-chan stopWork, roic <-chan Range, reqc chan<- rWork, chunkReader *ChunkReader) {
	input, output := roic, (chan<- rWork)(nil)
	roi := Range{}
	work := rWork{}

loop:
	for {
		select {
		case stop := <-stopc:
			if stop.ackc != nil {
				<-stop.ackc
			} else {
				// No need to ack. This is CloseWithoutWaiting.
			}
			if !stop.keepWorking {
				return
			}
			continue loop

		case roi = <-input:
			input, output = nil, reqc
			if err := chunkReader.SeekToChunkContaining(roi[0]); err != nil {
				if err == io.EOF {
					err = io.ErrUnexpectedEOF
				}
				work = rWork{err: err}
				continue loop
			}

		case output <- work:
			err := work.err
			work = rWork{}
			if err != nil {
				input, output = roic, nil
				continue loop
			}
		}

		for {
			chunk, err := chunkReader.NextChunk()
			if err == io.EOF {
				input, output = roic, nil
				continue loop
			} else if err != nil {
				work = rWork{err: err}
				continue loop
			}

			if chunk.DRange[0] >= roi[1] {
				input, output = roic, nil
				continue loop
			}
			if dr := chunk.DRange.Intersect(roi); !dr.Empty() {
				work = rWork{dRange: dr}
				continue loop
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gredis_test

import (
	"github.com/gogf/gf/container/gvar"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/guid"
	"testing"
	"time"

	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/util/gconv"

	"github.com/gogf/gf/database/gredis"
	"github.com/gogf/gf/test/gtest"
	redis2 "github.com/gomodule/redigo/redis"
)

var (
	config = gredis.Config{
		Host: "127.0.0.1",
		Port: 6379,
		Db:   1,
	}

	tlsConfig = gredis.Config{
		Host: "127.0.0.1",
		Port: 6379,
		Db:   1,
		TLS: true,
		TLSSkipVerify: true,
	}
)

func Test_NewClose(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		t.AssertNE(redis, nil)
		err := redis.Close()
		t.Assert(err, nil)
	})
}

func Test_Do(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		defer redis.Close()
		_, err := redis.Do("SET", "k", "v")
		t.Assert(err, nil)

		r, err := redis.Do("GET", "k")
		t.Assert(err, nil)
		t.Assert(r, []byte("v"))

		_, err = redis.Do("DEL", "k")
		t.Assert(err, nil)
		r, err = redis.Do("GET", "k")
		t.Assert(err, nil)
		t.Assert(r, nil)
	})
}

func Test_Stats(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		defer redis.Close()
		redis.SetMaxIdle(2)
		redis.SetMaxActive(100)
		redis.SetIdleTimeout(500 * time.Millisecond)
		redis.SetMaxConnLifetime(500 * time.Millisecond)

		array := make([]*gredis.Conn, 0)
		for i := 0; i < 10; i++ {
			array = append(array, redis.Conn())
		}
		stats := redis.Stats()
		t.Assert(stats.ActiveCount, 10)
		t.Assert(stats.IdleCount, 0)
		for i := 0; i < 10; i++ {
			array[i].Close()
		}
		stats = redis.Stats()
		t.Assert(stats.ActiveCount, 2)
		t.Assert(stats.IdleCount, 2)
		//time.Sleep(3000*time.Millisecond)
		//stats  = redis.Stats()
		//fmt.Println(stats)
		//t.Assert(stats.ActiveCount,  0)
		//t.Assert(stats.IdleCount,    0)
	})
}

func Test_Conn(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		defer redis.Close()
		conn := redis.Conn()
		defer conn.Close()

		key := gconv.String(gtime.TimestampNano())
		value := []byte("v")
		r, err := conn.Do("SET", key, value)
		t.Assert(err, nil)

		r, err = conn.Do("GET", key)
		t.Assert(err, nil)
		t.Assert(r, value)

		_, err = conn.Do("DEL", key)
		t.Assert(err, nil)
		r, err = conn.Do("GET", key)
		t.Assert(err, nil)
		t.Assert(r, nil)
	})
}

func Test_Instance(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		group := "my-test"
		gredis.SetConfig(config, group)
		defer gredis.RemoveConfig(group)
		redis := gredis.Instance(group)
		defer redis.Close()

		conn := redis.Conn()
		defer conn.Close()

		_, err := conn.Do("SET", "k", "v")
		t.Assert(err, nil)

		r, err := conn.Do("GET", "k")
		t.Assert(err, nil)
		t.Assert(r, []byte("v"))

		_, err = conn.Do("DEL", "k")
		t.Assert(err, nil)
		r, err = conn.Do("GET", "k")
		t.Assert(err, nil)
		t.Assert(r, nil)
	})
}

func Test_Error(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		config1 := gredis.Config{
			Host:           "127.0.0.2",
			Port:           6379,
			Db:             1,
			ConnectTimeout: time.Second,
		}
		redis := gredis.New(config1)
		_, err := redis.Do("info")
		t.AssertNE(err, nil)

		config1 = gredis.Config{
			Host: "127.0.0.1",
			Port: 6379,
			Db:   1,
			Pass: "666666",
		}
		redis = gredis.New(config1)
		_, err = redis.Do("info")
		t.AssertNE(err, nil)

		config1 = gredis.Config{
			Host: "127.0.0.1",
			Port: 6379,
			Db:   100,
		}
		redis = gredis.New(config1)
		_, err = redis.Do("info")
		t.AssertNE(err, nil)

		redis = gredis.Instance("gf")
		t.Assert(redis == nil, true)
		gredis.ClearConfig()

		redis = gredis.New(config)
		defer redis.Close()
		_, err = redis.DoVar("SET", "k", "v")
		t.Assert(err, nil)

		v, err := redis.DoVar("GET", "k")
		t.Assert(err, nil)
		t.Assert(v.String(), "v")

		conn := redis.GetConn()
		_, err = conn.DoVar("SET", "k", "v")
		t.Assert(err, nil)

		//v, err = conn.ReceiveVar()
		//t.Assert(err, nil)
		//t.Assert(v.String(), "v")

		psc := redis2.PubSubConn{Conn: conn}
		psc.Subscribe("gf")
		redis.DoVar("PUBLISH", "gf", "gf test")
		go func() {
			for {
				v, _ := conn.ReceiveVar()
				switch obj := v.Val().(type) {
				case redis2.Message:
					t.Assert(string(obj.Data), "gf test")
				case redis2.Subscription:

				}
			}
		}()

		time.Sleep(time.Second)
	})
}

func Test_Bool(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		defer func() {
			redis.Do("DEL", "key-true")
			redis.Do("DEL", "key-false")
		}()

		_, err := redis.Do("SET", "key-true", true)
		t.Assert(err, nil)

		_, err = redis.Do("SET", "key-false", false)
		t.Assert(err, nil)

		r, err := redis.DoVar("GET", "key-true")
		t.Assert(err, nil)
		t.Assert(r.Bool(), true)

		r, err = redis.DoVar("GET", "key-false")
		t.Assert(err, nil)
		t.Assert(r.Bool(), false)
	})
}

func Test_Int(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		key := guid.S()
		defer redis.Do("DEL", key)

		_, err := redis.Do("SET", key, 1)
		t.Assert(err, nil)

		r, err := redis.DoVar("GET", key)
		t.Assert(err, nil)
		t.Assert(r.Int(), 1)
	})
}

func Test_HSet(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		key := guid.S()
		defer redis.Do("DEL", key)

		_, err := redis.Do("HSET", key, "name", "john")
		t.Assert(err, nil)

		r, err := redis.DoVar("HGETALL", key)
		t.Assert(err, nil)
		t.Assert(r.Strings(), g.ArrayStr{"name", "john"})
	})
}

func Test_HGetAll1(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		var err error
		redis := gredis.New(config)
		key := guid.S()
		defer redis.Do("DEL", key)

		_, err = redis.Do("HSET", key, "id", 100)
		t.Assert(err, nil)
		_, err = redis.Do("HSET", key, "name", "john")
		t.Assert(err, nil)

		r, err := redis.DoVar("HGETALL", key)
		t.Assert(err, nil)
		t.Assert(r.Map(), g.MapStrAny{
			"id":   100,
			"name": "john",
		})
	})
}

func Test_HGetAll2(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		var (
			err   error
			key   = guid.S()
			redis = gredis.New(config)
		)
		defer redis.Do("DEL", key)

		_, err = redis.Do("HSET", key, "id", 100)
		t.Assert(err, nil)
		_, err = redis.Do("HSET", key, "name", "john")
		t.Assert(err, nil)

		result, err := redis.DoVar("HGETALL", key)
		t.Assert(err, nil)

		t.Assert(gconv.Uint(result.MapStrVar()["id"]), 100)
		t.Assert(result.MapStrVar()["id"].Uint(), 100)
	})
}

func Test_Auto_Marshal(t *testing.T) {
	var (
		err   error
		redis = gredis.New(config)
		key   = guid.S()
	)
	defer redis.Do("DEL", key)

	type User struct {
		Id   int
		Name string
	}

	gtest.C(t, func(t *gtest.T) {
		user := &User{
			Id:   10000,
			Name: "john",
		}

		_, err = redis.Do("SET", key, user)
		t.Assert(err, nil)

		r, err := redis.DoVar("GET", key)
		t.Assert(err, nil)
		t.Assert(r.Map(), g.MapStrAny{
			"Id":   user.Id,
			"Name": user.Name,
		})

		var user2 *User
		t.Assert(r.Struct(&user2), nil)
		t.Assert(user2.Id, user.Id)
		t.Assert(user2.Name, user.Name)
	})
}

func Test_Auto_MarshalSlice(t *testing.T) {
	var (
		err   error
		redis = gredis.New(config)
		key   = guid.S()
	)
	defer redis.Do("DEL", key)

	type User struct {
		Id   int
		Name string
	}

	gtest.C(t, func(t *gtest.T) {
		var (
			result *gvar.Var
			key    = "user-slice"
			users1 = []User{
				{
					Id:   1,
					Name: "john1",
				},
				{
					Id:   2,
					Name: "john2",
				},
			}
		)

		_, err = redis.Do("SET", key, users1)
		t.Assert(err, nil)

		result, err = redis.DoVar("GET", key)
		t.Assert(err, nil)

		var users2 []User
		err = result.Structs(&users2)
		t.Assert(err, nil)
		t.Assert(users2, users1)
	})
}

func Test_Conn_TLS(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(tlsConfig)
		defer redis.Close()
		conn := redis.Conn()
		defer conn.Close()

		key := gconv.String(gtime.TimestampNano())
		value := []byte("v")
		r, err := conn.Do("SET", key, value)
		t.Assert(err, nil)

		r, err = conn.Do("GET", key)
		t.Assert(err, nil)
		t.Assert(r, value)

		_, err = conn.Do("DEL", key)
		t.Assert(err, nil)
		r, err = conn.Do("GET", key)
		t.Assert(err, nil)
		t.Assert(r, nil)
	})
}
<commit_msg>remove tls unit test case<commit_after>// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gredis_test

import (
	"github.com/gogf/gf/container/gvar"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/guid"
	"testing"
	"time"

	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/util/gconv"

	"github.com/gogf/gf/database/gredis"
	"github.com/gogf/gf/test/gtest"
	redis2 "github.com/gomodule/redigo/redis"
)

var (
	config = gredis.Config{
		Host: "127.0.0.1",
		Port: 6379,
		Db:   1,
	}

	//demo for tls config
	tlsConfig = gredis.Config{
		Host: "127.0.0.1",
		Port: 6379,
		Db:   1,
		TLS: true,
		TLSSkipVerify: true,
	}
)

func Test_NewClose(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		t.AssertNE(redis, nil)
		err := redis.Close()
		t.Assert(err, nil)
	})
}

func Test_Do(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		defer redis.Close()
		_, err := redis.Do("SET", "k", "v")
		t.Assert(err, nil)

		r, err := redis.Do("GET", "k")
		t.Assert(err, nil)
		t.Assert(r, []byte("v"))

		_, err = redis.Do("DEL", "k")
		t.Assert(err, nil)
		r, err = redis.Do("GET", "k")
		t.Assert(err, nil)
		t.Assert(r, nil)
	})
}

func Test_Stats(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		defer redis.Close()
		redis.SetMaxIdle(2)
		redis.SetMaxActive(100)
		redis.SetIdleTimeout(500 * time.Millisecond)
		redis.SetMaxConnLifetime(500 * time.Millisecond)

		array := make([]*gredis.Conn, 0)
		for i := 0; i < 10; i++ {
			array = append(array, redis.Conn())
		}
		stats := redis.Stats()
		t.Assert(stats.ActiveCount, 10)
		t.Assert(stats.IdleCount, 0)
		for i := 0; i < 10; i++ {
			array[i].Close()
		}
		stats = redis.Stats()
		t.Assert(stats.ActiveCount, 2)
		t.Assert(stats.IdleCount, 2)
		//time.Sleep(3000*time.Millisecond)
		//stats  = redis.Stats()
		//fmt.Println(stats)
		//t.Assert(stats.ActiveCount,  0)
		//t.Assert(stats.IdleCount,    0)
	})
}

func Test_Conn(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		defer redis.Close()
		conn := redis.Conn()
		defer conn.Close()

		key := gconv.String(gtime.TimestampNano())
		value := []byte("v")
		r, err := conn.Do("SET", key, value)
		t.Assert(err, nil)

		r, err = conn.Do("GET", key)
		t.Assert(err, nil)
		t.Assert(r, value)

		_, err = conn.Do("DEL", key)
		t.Assert(err, nil)
		r, err = conn.Do("GET", key)
		t.Assert(err, nil)
		t.Assert(r, nil)
	})
}

func Test_Instance(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		group := "my-test"
		gredis.SetConfig(config, group)
		defer gredis.RemoveConfig(group)
		redis := gredis.Instance(group)
		defer redis.Close()

		conn := redis.Conn()
		defer conn.Close()

		_, err := conn.Do("SET", "k", "v")
		t.Assert(err, nil)

		r, err := conn.Do("GET", "k")
		t.Assert(err, nil)
		t.Assert(r, []byte("v"))

		_, err = conn.Do("DEL", "k")
		t.Assert(err, nil)
		r, err = conn.Do("GET", "k")
		t.Assert(err, nil)
		t.Assert(r, nil)
	})
}

func Test_Error(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		config1 := gredis.Config{
			Host:           "127.0.0.2",
			Port:           6379,
			Db:             1,
			ConnectTimeout: time.Second,
		}
		redis := gredis.New(config1)
		_, err := redis.Do("info")
		t.AssertNE(err, nil)

		config1 = gredis.Config{
			Host: "127.0.0.1",
			Port: 6379,
			Db:   1,
			Pass: "666666",
		}
		redis = gredis.New(config1)
		_, err = redis.Do("info")
		t.AssertNE(err, nil)

		config1 = gredis.Config{
			Host: "127.0.0.1",
			Port: 6379,
			Db:   100,
		}
		redis = gredis.New(config1)
		_, err = redis.Do("info")
		t.AssertNE(err, nil)

		redis = gredis.Instance("gf")
		t.Assert(redis == nil, true)
		gredis.ClearConfig()

		redis = gredis.New(config)
		defer redis.Close()
		_, err = redis.DoVar("SET", "k", "v")
		t.Assert(err, nil)

		v, err := redis.DoVar("GET", "k")
		t.Assert(err, nil)
		t.Assert(v.String(), "v")

		conn := redis.GetConn()
		_, err = conn.DoVar("SET", "k", "v")
		t.Assert(err, nil)

		//v, err = conn.ReceiveVar()
		//t.Assert(err, nil)
		//t.Assert(v.String(), "v")

		psc := redis2.PubSubConn{Conn: conn}
		psc.Subscribe("gf")
		redis.DoVar("PUBLISH", "gf", "gf test")
		go func() {
			for {
				v, _ := conn.ReceiveVar()
				switch obj := v.Val().(type) {
				case redis2.Message:
					t.Assert(string(obj.Data), "gf test")
				case redis2.Subscription:

				}
			}
		}()

		time.Sleep(time.Second)
	})
}

func Test_Bool(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		defer func() {
			redis.Do("DEL", "key-true")
			redis.Do("DEL", "key-false")
		}()

		_, err := redis.Do("SET", "key-true", true)
		t.Assert(err, nil)

		_, err = redis.Do("SET", "key-false", false)
		t.Assert(err, nil)

		r, err := redis.DoVar("GET", "key-true")
		t.Assert(err, nil)
		t.Assert(r.Bool(), true)

		r, err = redis.DoVar("GET", "key-false")
		t.Assert(err, nil)
		t.Assert(r.Bool(), false)
	})
}

func Test_Int(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		key := guid.S()
		defer redis.Do("DEL", key)

		_, err := redis.Do("SET", key, 1)
		t.Assert(err, nil)

		r, err := redis.DoVar("GET", key)
		t.Assert(err, nil)
		t.Assert(r.Int(), 1)
	})
}

func Test_HSet(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		redis := gredis.New(config)
		key := guid.S()
		defer redis.Do("DEL", key)

		_, err := redis.Do("HSET", key, "name", "john")
		t.Assert(err, nil)

		r, err := redis.DoVar("HGETALL", key)
		t.Assert(err, nil)
		t.Assert(r.Strings(), g.ArrayStr{"name", "john"})
	})
}

func Test_HGetAll1(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		var err error
		redis := gredis.New(config)
		key := guid.S()
		defer redis.Do("DEL", key)

		_, err = redis.Do("HSET", key, "id", 100)
		t.Assert(err, nil)
		_, err = redis.Do("HSET", key, "name", "john")
		t.Assert(err, nil)

		r, err := redis.DoVar("HGETALL", key)
		t.Assert(err, nil)
		t.Assert(r.Map(), g.MapStrAny{
			"id":   100,
			"name": "john",
		})
	})
}

func Test_HGetAll2(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		var (
			err   error
			key   = guid.S()
			redis = gredis.New(config)
		)
		defer redis.Do("DEL", key)

		_, err = redis.Do("HSET", key, "id", 100)
		t.Assert(err, nil)
		_, err = redis.Do("HSET", key, "name", "john")
		t.Assert(err, nil)

		result, err := redis.DoVar("HGETALL", key)
		t.Assert(err, nil)

		t.Assert(gconv.Uint(result.MapStrVar()["id"]), 100)
		t.Assert(result.MapStrVar()["id"].Uint(), 100)
	})
}

func Test_Auto_Marshal(t *testing.T) {
	var (
		err   error
		redis = gredis.New(config)
		key   = guid.S()
	)
	defer redis.Do("DEL", key)

	type User struct {
		Id   int
		Name string
	}

	gtest.C(t, func(t *gtest.T) {
		user := &User{
			Id:   10000,
			Name: "john",
		}

		_, err = redis.Do("SET", key, user)
		t.Assert(err, nil)

		r, err := redis.DoVar("GET", key)
		t.Assert(err, nil)
		t.Assert(r.Map(), g.MapStrAny{
			"Id":   user.Id,
			"Name": user.Name,
		})

		var user2 *User
		t.Assert(r.Struct(&user2), nil)
		t.Assert(user2.Id, user.Id)
		t.Assert(user2.Name, user.Name)
	})
}

func Test_Auto_MarshalSlice(t *testing.T) {
	var (
		err   error
		redis = gredis.New(config)
		key   = guid.S()
	)
	defer redis.Do("DEL", key)

	type User struct {
		Id   int
		Name string
	}

	gtest.C(t, func(t *gtest.T) {
		var (
			result *gvar.Var
			key    = "user-slice"
			users1 = []User{
				{
					Id:   1,
					Name: "john1",
				},
				{
					Id:   2,
					Name: "john2",
				},
			}
		)

		_, err = redis.Do("SET", key, users1)
		t.Assert(err, nil)

		result, err = redis.DoVar("GET", key)
		t.Assert(err, nil)

		var users2 []User
		err = result.Structs(&users2)
		t.Assert(err, nil)
		t.Assert(users2, users1)
	})
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Godoc comment extraction and comment -> HTML formatting.

package doc

import (
	"go/ast";
	"io";
	"once";
	"regexp";
	"strings";
	"template";	// for htmlEscape
)

// Comment extraction

var (
	comment_markers *regexp.Regexp;
	trailing_whitespace *regexp.Regexp;
	comment_junk *regexp.Regexp;
)

func makeRex(s string) *regexp.Regexp {
	re, err := regexp.Compile(s);
	if err != nil {
		panic("MakeRegexp ", s, " ", err.String());
	}
	return re;
}

// TODO(rsc): Cannot use var initialization for regexps,
// because Regexp constructor needs threads.
func setupRegexps() {
	comment_markers = makeRex("^/[/*] ?");
	trailing_whitespace = makeRex("[ \t\r]+$");
	comment_junk = makeRex("^[ \t]*(/\\*|\\*/)[ \t]*$");
}

// CommentText returns the text of comment,
// with the comment markers - //, /*, and */ - removed.
func CommentText(comment *ast.CommentGroup) string {
	if comment == nil {
		return "";
	}
	comments := make([]string, len(comment.List));
	for i, c := range comment.List {
		comments[i] = string(c.Text);
	}

	once.Do(setupRegexps);
	lines := make([]string, 0, 20);
	for _, c := range comments {
		// split on newlines
		cl := strings.Split(c, "\n", 0);

		// walk lines, stripping comment markers
		w := 0;
		for _, l := range cl {
			// remove /* and */ lines
			if comment_junk.MatchString(l) {
				continue;
			}

			// strip trailing white space
			m := trailing_whitespace.ExecuteString(l);
			if len(m) > 0 {
				l = l[0 : m[1]];
			}

			// strip leading comment markers
			m = comment_markers.ExecuteString(l);
			if len(m) > 0 {
				l = l[m[1] : len(l)];
			}

			cl[w] = l;
			w++;
		}
		cl = cl[0:w];

		// Add this comment to total list.
		for _, l := range cl {
			n := len(lines);
			if n+1 >= cap(lines) {
				newlines := make([]string, n, 2*cap(lines));
				for k := range newlines {
					newlines[k] = lines[k];
				}
				lines = newlines;
			}
			lines = lines[0 : n+1];
			lines[n] = l;
		}
	}

	// Remove leading blank lines; convert runs of
	// interior blank lines to a single blank line.
	n := 0;
	for _, line := range lines {
		if line != "" || n > 0 && lines[n-1] != "" {
			lines[n] = line;
			n++;
		}
	}
	lines = lines[0 : n];

	// Add final "" entry to get trailing newline from Join.
	// The original loop always leaves room for one more.
	if n > 0 && lines[n-1] != "" {
		lines = lines[0 : n+1];
		lines[n] = "";
	}

	return strings.Join(lines, "\n");
}

// Split bytes into lines.
func split(text []byte) [][]byte {
	// count lines
	n := 0;
	last := 0;
	for i, c := range text {
		if c == '\n' {
			last = i+1;
			n++;
		}
	}
	if last < len(text) {
		n++;
	}

	// split
	out := make([][]byte, n);
	last = 0;
	n = 0;
	for i, c := range text {
		if c == '\n' {
			out[n] = text[last : i+1];
			last = i+1;
			n++;
		}
	}
	if last < len(text) {
		out[n] = text[last : len(text)];
	}

	return out;
}

var (
	ldquo = strings.Bytes("“");
	rdquo = strings.Bytes("”");
)

// Escape comment text for HTML.
// Also, turn `` into “ and '' into ”.
func commentEscape(w io.Writer, s []byte) {
	last := 0;
	for i := 0; i < len(s)-1; i++ {
		if s[i] == s[i+1] && (s[i] == '`' || s[i] == '\'') {
			template.HtmlEscape(w, s[last : i]);
			last = i+2;
			switch s[i] {
			case '`':
				w.Write(ldquo);
			case '\'':
				w.Write(rdquo);
			}
			i++;	// loop will add one more
		}
	}
	template.HtmlEscape(w, s[last : len(s)]);
}

var (
	html_p = strings.Bytes("
\n");
	html_endp = strings.Bytes("
\n");
	html_pre = strings.Bytes("
");
	html_endpre = strings.Bytes("
\n");
)

func indentLen(s []byte) int {
	i := 0;
	for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
		i++;
	}
	return i;
}

func isBlank(s []byte) bool {
	return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
}

func commonPrefix(a, b []byte) []byte {
	i := 0;
	for i < len(a) && i < len(b) && a[i] == b[i] {
		i++;
	}
	return a[0 : i];
}

func unindent(block [][]byte) {
	if len(block) == 0 {
		return;
	}

	// compute maximum common white prefix
	prefix := block[0][0 : indentLen(block[0])];
	for _, line := range block {
		if !isBlank(line) {
			prefix = commonPrefix(prefix, line[0 : indentLen(line)]);
		}
	}
	n := len(prefix);

	// remove
	for i, line := range block {
		if !isBlank(line) {
			block[i] = line[n : len(line)];
		}
	}
}

// Convert comment text to formatted HTML.
// The comment was prepared by DocReader,
// so it is known not to have leading, trailing blank lines
// nor to have trailing spaces at the end of lines.
// The comment markers have already been removed.
//
// Turn each run of multiple \n into

// Turn each run of indented lines into
 without indent.
//
// TODO(rsc): I'd like to pass in an array of variable names []string
// and then italicize those strings when they appear as words.
func ToHtml(w io.Writer, s []byte) {
	inpara := false;

	/* TODO(rsc): 6g cant generate code for these
	close := func() {
		if inpara {
			w.Write(html_endp);
			inpara = false;
		}
	};
	open := func() {
		if !inpara {
			w.Write(html_p);
			inpara = true;
		}
	};
	*/

	lines := split(s);
	unindent(lines);
	for i := 0; i < len(lines);  {
		line := lines[i];
		if isBlank(line) {
			// close paragraph
			if inpara {
				w.Write(html_endp);
				inpara = false;
			}
			i++;
			continue;
		}
		if indentLen(line) > 0 {
			// close paragraph
			if inpara {
				w.Write(html_endp);
				inpara = false;
			}

			// count indented or blank lines
			j := i+1;
			for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
				j++;
			}
			// but not trailing blank lines
			for j > i && isBlank(lines[j-1]) {
				j--;
			}
			block := lines[i : j];
			i = j;

			unindent(block);

			// put those lines in a pre block.
			// they don't get the nice text formatting,
			// just html escaping
			w.Write(html_pre);
			for _, line := range block {
				template.HtmlEscape(w, line);
			}
			w.Write(html_endpre);
			continue;
		}
		// open paragraph
		if !inpara {
			w.Write(html_p);
			inpara = true;
		}
		commentEscape(w, lines[i]);
		i++;
	}
	if inpara {
		w.Write(html_endp);
		inpara = false;
	}
}

<commit_msg>more comment work. got rid of regexps. primary bug fix is that // inside /* */ do not get stripped anymore, so that the text inside<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Godoc comment extraction and comment -> HTML formatting.

package doc

import (
	"go/ast";
	"io";
	"strings";
	"template";	// for htmlEscape
)

// Comment extraction

// CommentText returns the text of comment,
// with the comment markers - //, /*, and */ - removed.
func CommentText(comment *ast.CommentGroup) string {
	if comment == nil {
		return "";
	}
	comments := make([]string, len(comment.List));
	for i, c := range comment.List {
		comments[i] = string(c.Text);
	}

	lines := make([]string, 0, 20);
	for _, c := range comments {
		// Remove comment markers.
		// The parser has given us exactly the comment text.
		switch n := len(c); {
		case n >= 4 && c[0:2] == "/*" && c[n-2:n] == "*/":
			c = c[2:n-2];
		case n >= 2 && c[0:2] == "//":
			c = c[2:n];
			// Remove leading space after //, if there is one.
			if len(c) > 0 && c[0] == ' ' {
				c = c[1:len(c)];
			}
		}

		// Split on newlines.
		cl := strings.Split(c, "\n", 0);

		// Walk lines, stripping trailing white space and adding to list.
		for _, l := range cl {
			// Strip trailing white space
			m := len(l);
			for m > 0 && (l[m-1] == ' ' || l[m-1] == '\n' || l[m-1] == '\t' || l[m-1] == '\r') {
				m--;
			}
			l = l[0 : m];

			// Add to list.
			n := len(lines);
			if n+1 >= cap(lines) {
				newlines := make([]string, n, 2*cap(lines));
				for k := range newlines {
					newlines[k] = lines[k];
				}
				lines = newlines;
			}
			lines = lines[0 : n+1];
			lines[n] = l;
		}
	}

	// Remove leading blank lines; convert runs of
	// interior blank lines to a single blank line.
	n := 0;
	for _, line := range lines {
		if line != "" || n > 0 && lines[n-1] != "" {
			lines[n] = line;
			n++;
		}
	}
	lines = lines[0 : n];

	// Add final "" entry to get trailing newline from Join.
	// The original loop always leaves room for one more.
	if n > 0 && lines[n-1] != "" {
		lines = lines[0 : n+1];
		lines[n] = "";
	}

	return strings.Join(lines, "\n");
}

// Split bytes into lines.
func split(text []byte) [][]byte {
	// count lines
	n := 0;
	last := 0;
	for i, c := range text {
		if c == '\n' {
			last = i+1;
			n++;
		}
	}
	if last < len(text) {
		n++;
	}

	// split
	out := make([][]byte, n);
	last = 0;
	n = 0;
	for i, c := range text {
		if c == '\n' {
			out[n] = text[last : i+1];
			last = i+1;
			n++;
		}
	}
	if last < len(text) {
		out[n] = text[last : len(text)];
	}

	return out;
}

var (
	ldquo = strings.Bytes("“");
	rdquo = strings.Bytes("”");
)

// Escape comment text for HTML.
// Also, turn `` into “ and '' into ”.
func commentEscape(w io.Writer, s []byte) {
	last := 0;
	for i := 0; i < len(s)-1; i++ {
		if s[i] == s[i+1] && (s[i] == '`' || s[i] == '\'') {
			template.HtmlEscape(w, s[last : i]);
			last = i+2;
			switch s[i] {
			case '`':
				w.Write(ldquo);
			case '\'':
				w.Write(rdquo);
			}
			i++;	// loop will add one more
		}
	}
	template.HtmlEscape(w, s[last : len(s)]);
}

var (
	html_p = strings.Bytes("
\n");
	html_endp = strings.Bytes("
\n");
	html_pre = strings.Bytes("
");
	html_endpre = strings.Bytes("
\n");
)

func indentLen(s []byte) int {
	i := 0;
	for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
		i++;
	}
	return i;
}

func isBlank(s []byte) bool {
	return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
}

func commonPrefix(a, b []byte) []byte {
	i := 0;
	for i < len(a) && i < len(b) && a[i] == b[i] {
		i++;
	}
	return a[0 : i];
}

func unindent(block [][]byte) {
	if len(block) == 0 {
		return;
	}

	// compute maximum common white prefix
	prefix := block[0][0 : indentLen(block[0])];
	for _, line := range block {
		if !isBlank(line) {
			prefix = commonPrefix(prefix, line[0 : indentLen(line)]);
		}
	}
	n := len(prefix);

	// remove
	for i, line := range block {
		if !isBlank(line) {
			block[i] = line[n : len(line)];
		}
	}
}

// Convert comment text to formatted HTML.
// The comment was prepared by DocReader,
// so it is known not to have leading, trailing blank lines
// nor to have trailing spaces at the end of lines.
// The comment markers have already been removed.
//
// Turn each run of multiple \n into

// Turn each run of indented lines into
 without indent.
//
// TODO(rsc): I'd like to pass in an array of variable names []string
// and then italicize those strings when they appear as words.
func ToHtml(w io.Writer, s []byte) {
	inpara := false;

	/* TODO(rsc): 6g cant generate code for these
	close := func() {
		if inpara {
			w.Write(html_endp);
			inpara = false;
		}
	};
	open := func() {
		if !inpara {
			w.Write(html_p);
			inpara = true;
		}
	};
	*/

	lines := split(s);
	unindent(lines);
	for i := 0; i < len(lines);  {
		line := lines[i];
		if isBlank(line) {
			// close paragraph
			if inpara {
				w.Write(html_endp);
				inpara = false;
			}
			i++;
			continue;
		}
		if indentLen(line) > 0 {
			// close paragraph
			if inpara {
				w.Write(html_endp);
				inpara = false;
			}

			// count indented or blank lines
			j := i+1;
			for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
				j++;
			}
			// but not trailing blank lines
			for j > i && isBlank(lines[j-1]) {
				j--;
			}
			block := lines[i : j];
			i = j;

			unindent(block);

			// put those lines in a pre block.
			// they don't get the nice text formatting,
			// just html escaping
			w.Write(html_pre);
			for _, line := range block {
				template.HtmlEscape(w, line);
			}
			w.Write(html_endpre);
			continue;
		}
		// open paragraph
		if !inpara {
			w.Write(html_p);
			inpara = true;
		}
		commentEscape(w, lines[i]);
		i++;
	}
	if inpara {
		w.Write(html_endp);
		inpara = false;
	}
}

<|endoftext|>


<commit_before>package cli

import (
	"testing"
	"github.com/stretchr/testify/assert"
	"github.com/stelligent/mu/common"
)

func TestNewEnvironmentsCommand(t *testing.T) {
	assert := assert.New(t)

	ctx := common.NewContext()

	command := newEnvironmentsCommand(ctx)

	assert.NotNil(command)
	assert.Equal("environment", command.Name, "Name should match")
	assert.Equal(1, len(command.Aliases), "Aliases len should match")
	assert.Equal("env", command.Aliases[0], "Aliases should match")
	assert.Equal("options for managing environments", command.Usage, "Usage should match")
	assert.Equal(4, len(command.Subcommands), "Subcommands len should match")
}

func TestNewEnvironmentsUpsertCommand(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	command := newEnvironmentsUpsertCommand(ctx)

	assert.NotNil(command)
	assert.Equal("upsert", command.Name, "Name should match")
	assert.Equal(1, len(command.Aliases), "Aliases len should match")
	assert.Equal("up", command.Aliases[0], "Aliases should match")
	assert.Equal("<environment>", command.ArgsUsage, "ArgsUsage should match")
	assert.NotNil(command.Action)
}

func TestNewEnvironmentsListCommand(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	command := newEnvironmentsListCommand(ctx)

	assert.NotNil(command)
	assert.Equal("list", command.Name, "Name should match")
	assert.Equal(1, len(command.Aliases), "Aliases len should match")
	assert.Equal("ls", command.Aliases[0], "Aliases should match")
	assert.Equal("list environments", command.Usage, "Usage should match")
	assert.NotNil(command.Action)
}
func TestNewEnvironmentsShowCommand(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	command := newEnvironmentsShowCommand(ctx)

	assert.NotNil(command)
	assert.Equal("show", command.Name, "Name should match")
	assert.Equal("<environment>", command.ArgsUsage, "ArgsUsage should match")
	assert.NotNil(command.Action)
}
func TestNewEnvironmentsTerminateCommand(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	command := newEnvironmentsTerminateCommand(ctx)

	assert.NotNil(command)
	assert.Equal("terminate", command.Name, "Name should match")
	assert.Equal(1, len(command.Aliases), "Aliases len should match")
	assert.Equal("term", command.Aliases[0], "Aliases should match")
	assert.Equal("<environment>", command.ArgsUsage, "ArgsUsage should match")
	assert.NotNil(command.Action)
}
<commit_msg>add test coverage to environment commands<commit_after>package cli

import (
	"testing"
	"github.com/stretchr/testify/assert"
	"github.com/stelligent/mu/common"
	"github.com/urfave/cli"
	"io/ioutil"
	"flag"
	"bytes"
	"github.com/aws/aws-sdk-go/service/cloudformation/cloudformationiface"
)

func TestNewEnvironmentsCommand(t *testing.T) {
	assert := assert.New(t)

	ctx := common.NewContext()

	command := newEnvironmentsCommand(ctx)

	assert.NotNil(command)
	assert.Equal("environment", command.Name, "Name should match")
	assert.Equal(1, len(command.Aliases), "Aliases len should match")
	assert.Equal("env", command.Aliases[0], "Aliases should match")
	assert.Equal("options for managing environments", command.Usage, "Usage should match")
	assert.Equal(4, len(command.Subcommands), "Subcommands len should match")

	args := []string { "environment","help" }
	err := runCommand(command, args)
	assert.Nil(err)
}

func TestNewEnvironmentsUpsertCommand(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	command := newEnvironmentsUpsertCommand(ctx)
	ctx.CloudFormation = new(mockedCloudFormation)

	assert.NotNil(command)
	assert.Equal("upsert", command.Name, "Name should match")
	assert.Equal(1, len(command.Aliases), "Aliases len should match")
	assert.Equal("up", command.Aliases[0], "Aliases should match")
	assert.Equal("<environment>", command.ArgsUsage, "ArgsUsage should match")
	assert.NotNil(command.Action)

	args := []string { "upsert" }
	err := runCommand(command, args)
	assert.NotNil(err)
	assert.Equal(1,lastExitCode)

	args = []string { "upsert","fooenv" }
	err = runCommand(command, args)
	assert.NotNil(err)
	assert.Equal(1,lastExitCode)
}

func TestNewEnvironmentsListCommand(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	command := newEnvironmentsListCommand(ctx)

	assert.NotNil(command)
	assert.Equal("list", command.Name, "Name should match")
	assert.Equal(1, len(command.Aliases), "Aliases len should match")
	assert.Equal("ls", command.Aliases[0], "Aliases should match")
	assert.Equal("list environments", command.Usage, "Usage should match")
	assert.NotNil(command.Action)
}
func TestNewEnvironmentsShowCommand(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	command := newEnvironmentsShowCommand(ctx)

	assert.NotNil(command)
	assert.Equal("show", command.Name, "Name should match")
	assert.Equal("<environment>", command.ArgsUsage, "ArgsUsage should match")
	assert.NotNil(command.Action)
}
func TestNewEnvironmentsTerminateCommand(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	command := newEnvironmentsTerminateCommand(ctx)

	assert.NotNil(command)
	assert.Equal("terminate", command.Name, "Name should match")
	assert.Equal(1, len(command.Aliases), "Aliases len should match")
	assert.Equal("term", command.Aliases[0], "Aliases should match")
	assert.Equal("<environment>", command.ArgsUsage, "ArgsUsage should match")
	assert.NotNil(command.Action)
}

func runCommand(command *cli.Command, args []string) error {
	app := cli.NewApp()
	app.Writer = ioutil.Discard
	set := flag.NewFlagSet("test", 0)
	set.Parse(args)
	appContext := cli.NewContext(app, set, nil)
	return command.Run(appContext)
}

var (
	lastExitCode = 0
	fakeOsExiter = func(rc int) {
		lastExitCode = rc
	}
	fakeErrWriter = &bytes.Buffer{}
)

func init() {
	cli.OsExiter = fakeOsExiter
	cli.ErrWriter = fakeErrWriter
}
type mockedCloudFormation struct {
	cloudformationiface.CloudFormationAPI
}
<|endoftext|>