<commit_before>package main

// This file contains about everything related to persons aka users. At the top you will find routes
// and at the bottom you can find CRUD options. Some functions in this file are analogous
// to the ones in posts.go.

import (
	"errors"
	r "github.com/dancannon/gorethink"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	"log"
	"net/http"
)

// Person struct holds all relevant data for representing user accounts on Vertigo.
// A complete Person struct also includes Posts field (type []Post) which includes
// all posts made by the user.
type Person struct {
	Id       string `json:"id" gorethink:",omitempty"`
	Name     string `json:"name" form:"name" binding:"required" gorethink:"name"`
	Password string `form:"password" json:"password,omitempty" gorethink:"-,omitempty"`
	Digest   []byte `json:"digest,omitempty" gorethink:"digest"`
	Email    string `json:"email,omitempty" form:"email" binding:"required" gorethink:"email"`
	Posts    []Post `json:"posts" gorethink:"posts"`
}

// CreateUser is a route which creates a new person struct according to posted parameters.
// Requires session cookie.
// Returns created user struct for API requests and redirects to "/user" on frontend ones.
func CreateUser(req *http.Request, res render.Render, db *r.Session, s sessions.Session, person Person) {
	if !EmailIsUnique(db, person) {
		res.JSON(422, map[string]interface{}{"error": "Email already in use"})
		return
	}
	user, err := person.Insert(db)
	if err != nil {
		log.Println(err)
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	switch root(req) {
	case "api":
		s.Set("user", user.Id)
		res.JSON(200, user)
		return
	case "user":
		s.Set("user", user.Id)
		res.Redirect("/user", 302)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// DeleteUser is a route which deletes a user from database according to session cookie.
// The function calls Login function inside, so it also requires password in POST data.
// Currently unavailable function on both API and frontend side.
func DeleteUser(req *http.Request, res render.Render, db *r.Session, s sessions.Session, person Person) {
	person, err := person.Login(db)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	err = person.Delete(db, s)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	switch root(req) {
	case "api":
		s.Delete("user")
		res.JSON(200, map[string]interface{}{"status": "User successfully deleted"})
		return
	case "user":
		s.Delete("user")
		res.HTML(200, "User successfully deleted", nil)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// ReadUser is a route which fetches user according to parameter "id" on API side and according to retrieved
// session cookie on frontend side.
// Returns user struct with all posts merged to object on API call. Frontend call will render user "home" page, "user/index.tmpl".
func ReadUser(req *http.Request, params martini.Params, res render.Render, s sessions.Session, db *r.Session) {
	var person Person
	switch root(req) {
	case "api":
		person.Id = params["id"]
		user, err := person.Get(db)
		if err != nil {
			res.JSON(500, map[string]interface{}{"error": "Internal server error"})
			return
		}
		res.JSON(200, user)
		return
	case "user":
		user, err := person.Session(db, s)
		if err != nil {
			res.HTML(500, "error", err)
			return
		}
		res.HTML(200, "user/index", user)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// ReadUsers is a route only available on API side, which fetches all users with post data merged.
// Returns complete list of users on success.
func ReadUsers(res render.Render, db *r.Session) {
	var person Person
	users, err := person.GetAll(db)
	if err != nil {
		res.JSON(500, err)
		return
	}
	res.JSON(200, users)
}

// EmailIsUnique returns bool value acoording to whether user email already exists in database with called user struct.
// The function is used to make sure two persons do not register under the same email. This limitation could however be removed,
// as by default primary key for tables used by Vertigo is ID, not email.
func EmailIsUnique(db *r.Session, person Person) bool {
	row, err := r.Table("users").Filter(func(user r.RqlTerm) r.RqlTerm {
		return user.Field("email").Eq(person.Email)
	}).RunRow(db)
	if err != nil || !row.IsNil() {
		return false
	}
	return true
}

// LoginUser is a route which compares plaintext password sent with POST request with
// hash stored in database. On successful request returns session cookie named "user", which contains
// user's ID encrypted, which is the primary key used in database table.
// When called by API it responds with person struct without post data merged.
// On frontend call it redirect the client to "/user" page.
func LoginUser(req *http.Request, s sessions.Session, res render.Render, db *r.Session, person Person) {
	person, err := person.Login(db)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	switch root(req) {
	case "api":
		s.Set("user", person.Id)
		res.JSON(200, person)
		return
	case "user":
		s.Set("user", person.Id)
		res.Redirect("/user", 302)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// LogoutUser is a route which deletes session cookie "user", from the given client.
// On API call responds with HTTP 200 body and on frontend the client is redirected to homepage "/".
func LogoutUser(req *http.Request, s sessions.Session, res render.Render, db *r.Session, person Person) {
	s.Delete("user")
	switch root(req) {
	case "api":
		res.JSON(200, map[string]interface{}{"success": "You've been logged out."})
		return
	case "user":
		res.Redirect("/", 302)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// Login or person.Login is a function which retrieves user according to given .Email field.
// The function then compares the retrieved object's .Digest field with given .Password field.
// If the .Password and .Hash match, the function returns the requested Person struct.
func (person Person) Login(db *r.Session) (Person, error) {
	row, err := r.Table("users").Filter(func(post r.RqlTerm) r.RqlTerm {
		return post.Field("email").Eq(person.Email)
	}).RunRow(db)
	if err != nil || row.IsNil() {
		return person, err
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	if CompareHash(person.Digest, person.Password) {
		return person, nil
	} else {
		return person, errors.New("Wrong username or password.")
	}
}

// Get or person.Get returns Person object according to given .Id
// with post information merged, but without the .Digest and .Email field.
func (person Person) Get(db *r.Session) (Person, error) {
	row, err := r.Table("users").Get(person.Id).Merge(map[string]interface{}{"posts": r.Table("posts").Filter(func(post r.RqlTerm) r.RqlTerm {
		return post.Field("author").Eq(person.Id)
	}).CoerceTo("ARRAY").Without("author")}).Without("digest", "email").RunRow(db)
	if err != nil {
		return person, err
	}
	if row.IsNil() {
		return person, errors.New("Nothing was found.")
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	return person, err
}

// Session or person.Session returns Person object from client session cookie.
// The returned object has post data merged.
func (person Person) Session(db *r.Session, s sessions.Session) (Person, error) {
	data := s.Get("user")
	id, exists := data.(string)
	if exists {
		var person Person
		person.Id = id
		person, err := person.Get(db)
		if err != nil {
			return person, err
		}
		return person, nil
	}
	return person, errors.New("Session could not be retrieved.")
}

// Delete or person.Delete deletes the user with given .Id from the database.
func (person Person) Delete(db *r.Session, s sessions.Session) error {
	person, err := person.Session(db, s)
	if err != nil {
		return err
	}
	_, err = r.Table("users").Get(person.Id).Delete().RunRow(db)
	if err != nil {
		return err
	}
	return nil
}

// Insert or person.Insert inserts a new Person struct into the database.
// The function creates .Digest hash from .Password.
func (person Person) Insert(db *r.Session) (Person, error) {
	person.Digest = GenerateHash(person.Password)
	// We dont want to store plaintext password.
	// Options given in Person struct will omit the field
	// from being written to database at all.
	person.Password = ""
	row, err := r.Table("users").Insert(person).RunRow(db)
	if err != nil {
		return person, err
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	return person, err
}

// GetAll or person.GetAll fetches all persons with post data merged from the database.
func (person Person) GetAll(db *r.Session) ([]Person, error) {
	var persons []Person
	rows, err := r.Table("users").Without("digest", "email").Run(db)
	if err != nil {
		return nil, err
	}
	for rows.Next() {
		err := rows.Scan(&person)
		person, err := person.Get(db)
		if err != nil {
			return nil, err
		}
		persons = append(persons, person)
	}
	return persons, nil
}
<commit_msg>fix logout route<commit_after>package main

// This file contains about everything related to persons aka users. At the top you will find routes
// and at the bottom you can find CRUD options. Some functions in this file are analogous
// to the ones in posts.go.

import (
	"errors"
	r "github.com/dancannon/gorethink"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	"log"
	"net/http"
)

// Person struct holds all relevant data for representing user accounts on Vertigo.
// A complete Person struct also includes Posts field (type []Post) which includes
// all posts made by the user.
type Person struct {
	Id       string `json:"id" gorethink:",omitempty"`
	Name     string `json:"name" form:"name" binding:"required" gorethink:"name"`
	Password string `form:"password" json:"password,omitempty" gorethink:"-,omitempty"`
	Digest   []byte `json:"digest,omitempty" gorethink:"digest"`
	Email    string `json:"email,omitempty" form:"email" binding:"required" gorethink:"email"`
	Posts    []Post `json:"posts" gorethink:"posts"`
}

// CreateUser is a route which creates a new person struct according to posted parameters.
// Requires session cookie.
// Returns created user struct for API requests and redirects to "/user" on frontend ones.
func CreateUser(req *http.Request, res render.Render, db *r.Session, s sessions.Session, person Person) {
	if !EmailIsUnique(db, person) {
		res.JSON(422, map[string]interface{}{"error": "Email already in use"})
		return
	}
	user, err := person.Insert(db)
	if err != nil {
		log.Println(err)
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	switch root(req) {
	case "api":
		s.Set("user", user.Id)
		res.JSON(200, user)
		return
	case "user":
		s.Set("user", user.Id)
		res.Redirect("/user", 302)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// DeleteUser is a route which deletes a user from database according to session cookie.
// The function calls Login function inside, so it also requires password in POST data.
// Currently unavailable function on both API and frontend side.
func DeleteUser(req *http.Request, res render.Render, db *r.Session, s sessions.Session, person Person) {
	person, err := person.Login(db)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	err = person.Delete(db, s)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	switch root(req) {
	case "api":
		s.Delete("user")
		res.JSON(200, map[string]interface{}{"status": "User successfully deleted"})
		return
	case "user":
		s.Delete("user")
		res.HTML(200, "User successfully deleted", nil)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// ReadUser is a route which fetches user according to parameter "id" on API side and according to retrieved
// session cookie on frontend side.
// Returns user struct with all posts merged to object on API call. Frontend call will render user "home" page, "user/index.tmpl".
func ReadUser(req *http.Request, params martini.Params, res render.Render, s sessions.Session, db *r.Session) {
	var person Person
	switch root(req) {
	case "api":
		person.Id = params["id"]
		user, err := person.Get(db)
		if err != nil {
			res.JSON(500, map[string]interface{}{"error": "Internal server error"})
			return
		}
		res.JSON(200, user)
		return
	case "user":
		user, err := person.Session(db, s)
		if err != nil {
			res.HTML(500, "error", err)
			return
		}
		res.HTML(200, "user/index", user)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// ReadUsers is a route only available on API side, which fetches all users with post data merged.
// Returns complete list of users on success.
func ReadUsers(res render.Render, db *r.Session) {
	var person Person
	users, err := person.GetAll(db)
	if err != nil {
		res.JSON(500, err)
		return
	}
	res.JSON(200, users)
}

// EmailIsUnique returns bool value acoording to whether user email already exists in database with called user struct.
// The function is used to make sure two persons do not register under the same email. This limitation could however be removed,
// as by default primary key for tables used by Vertigo is ID, not email.
func EmailIsUnique(db *r.Session, person Person) bool {
	row, err := r.Table("users").Filter(func(user r.RqlTerm) r.RqlTerm {
		return user.Field("email").Eq(person.Email)
	}).RunRow(db)
	if err != nil || !row.IsNil() {
		return false
	}
	return true
}

// LoginUser is a route which compares plaintext password sent with POST request with
// hash stored in database. On successful request returns session cookie named "user", which contains
// user's ID encrypted, which is the primary key used in database table.
// When called by API it responds with person struct without post data merged.
// On frontend call it redirect the client to "/user" page.
func LoginUser(req *http.Request, s sessions.Session, res render.Render, db *r.Session, person Person) {
	person, err := person.Login(db)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	switch root(req) {
	case "api":
		s.Set("user", person.Id)
		res.JSON(200, person)
		return
	case "user":
		s.Set("user", person.Id)
		res.Redirect("/user", 302)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// LogoutUser is a route which deletes session cookie "user", from the given client.
// On API call responds with HTTP 200 body and on frontend the client is redirected to homepage "/".
func LogoutUser(req *http.Request, s sessions.Session, res render.Render) {
	s.Delete("user")
	switch root(req) {
	case "api":
		res.JSON(200, map[string]interface{}{"success": "You've been logged out."})
		return
	case "user":
		res.Redirect("/", 302)
		return
	}
	res.JSON(500, map[string]interface{}{"error": "Internal server error"})
}

// Login or person.Login is a function which retrieves user according to given .Email field.
// The function then compares the retrieved object's .Digest field with given .Password field.
// If the .Password and .Hash match, the function returns the requested Person struct.
func (person Person) Login(db *r.Session) (Person, error) {
	row, err := r.Table("users").Filter(func(post r.RqlTerm) r.RqlTerm {
		return post.Field("email").Eq(person.Email)
	}).RunRow(db)
	if err != nil || row.IsNil() {
		return person, err
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	if CompareHash(person.Digest, person.Password) {
		return person, nil
	} else {
		return person, errors.New("Wrong username or password.")
	}
}

// Get or person.Get returns Person object according to given .Id
// with post information merged, but without the .Digest and .Email field.
func (person Person) Get(db *r.Session) (Person, error) {
	row, err := r.Table("users").Get(person.Id).Merge(map[string]interface{}{"posts": r.Table("posts").Filter(func(post r.RqlTerm) r.RqlTerm {
		return post.Field("author").Eq(person.Id)
	}).CoerceTo("ARRAY").Without("author")}).Without("digest", "email").RunRow(db)
	if err != nil {
		return person, err
	}
	if row.IsNil() {
		return person, errors.New("Nothing was found.")
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	return person, err
}

// Session or person.Session returns Person object from client session cookie.
// The returned object has post data merged.
func (person Person) Session(db *r.Session, s sessions.Session) (Person, error) {
	data := s.Get("user")
	id, exists := data.(string)
	if exists {
		var person Person
		person.Id = id
		person, err := person.Get(db)
		if err != nil {
			return person, err
		}
		return person, nil
	}
	return person, errors.New("Session could not be retrieved.")
}

// Delete or person.Delete deletes the user with given .Id from the database.
func (person Person) Delete(db *r.Session, s sessions.Session) error {
	person, err := person.Session(db, s)
	if err != nil {
		return err
	}
	_, err = r.Table("users").Get(person.Id).Delete().RunRow(db)
	if err != nil {
		return err
	}
	return nil
}

// Insert or person.Insert inserts a new Person struct into the database.
// The function creates .Digest hash from .Password.
func (person Person) Insert(db *r.Session) (Person, error) {
	person.Digest = GenerateHash(person.Password)
	// We dont want to store plaintext password.
	// Options given in Person struct will omit the field
	// from being written to database at all.
	person.Password = ""
	row, err := r.Table("users").Insert(person).RunRow(db)
	if err != nil {
		return person, err
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	return person, err
}

// GetAll or person.GetAll fetches all persons with post data merged from the database.
func (person Person) GetAll(db *r.Session) ([]Person, error) {
	var persons []Person
	rows, err := r.Table("users").Without("digest", "email").Run(db)
	if err != nil {
		return nil, err
	}
	for rows.Next() {
		err := rows.Scan(&person)
		person, err := person.Get(db)
		if err != nil {
			return nil, err
		}
		persons = append(persons, person)
	}
	return persons, nil
}
<|endoftext|>


<commit_before>package bonjour

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/socketplane/bonjour/Godeps/_workspace/src/github.com/socketplane/go-fastping"
)

type response struct {
	addr *net.IPAddr
	rtt  time.Duration
}

const (
	ECHO_REPLY = iota
	NO_REPLY
	ERROR
)

func echo(address string, ip *net.IP) (int, error) {
	p := fastping.NewPinger()
	p.Debug = false
	netProto := "ip4:icmp"
	if strings.Index(address, ":") != -1 {
		netProto = "ip6:ipv6-icmp"
	}
	ra, err := net.ResolveIPAddr(netProto, address)
	if err != nil {
		return ERROR, err
	}

	if ip != nil && ip.To4() != nil {
		p.ListenAddr, _ = net.ResolveIPAddr("ip4", ip.To4().String())
	}

	results := make(map[string]*response)
	results[ra.String()] = nil
	p.AddIPAddr(ra)

	onRecv, onIdle, onErr := make(chan *response), make(chan bool), make(chan int)

	p.OnRecv = func(addr *net.IPAddr, t time.Duration) {
		onRecv <- &response{addr: addr, rtt: t}
	}
	p.OnIdle = func() {
		onIdle <- true
	}

	p.OnErr = func(addr *net.IPAddr, t int) {
		onErr <- t
	}

	p.MaxRTT = time.Second
	go p.Run()

	ret := NO_REPLY
	select {
	case <-onRecv:
		ret = ECHO_REPLY
	case <-onIdle:
		ret = NO_REPLY
	case res := <-onErr:
		errId := fmt.Sprintf("%d", res)
		err = errors.New(errId)
		ret = ERROR
	}
	p.Stop()
	return ret, err
}
<commit_msg>Removing Stop() from echo since we dont use RunLoop()<commit_after>package bonjour

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/socketplane/bonjour/Godeps/_workspace/src/github.com/socketplane/go-fastping"
)

type response struct {
	addr *net.IPAddr
	rtt  time.Duration
}

const (
	ECHO_REPLY = iota
	NO_REPLY
	ERROR
)

func echo(address string, ip *net.IP) (int, error) {
	p := fastping.NewPinger()
	p.Debug = false
	netProto := "ip4:icmp"
	if strings.Index(address, ":") != -1 {
		netProto = "ip6:ipv6-icmp"
	}
	ra, err := net.ResolveIPAddr(netProto, address)
	if err != nil {
		return ERROR, err
	}

	if ip != nil && ip.To4() != nil {
		p.ListenAddr, _ = net.ResolveIPAddr("ip4", ip.To4().String())
	}

	results := make(map[string]*response)
	results[ra.String()] = nil
	p.AddIPAddr(ra)

	onRecv, onIdle, onErr := make(chan *response), make(chan bool), make(chan int)

	p.OnRecv = func(addr *net.IPAddr, t time.Duration) {
		onRecv <- &response{addr: addr, rtt: t}
	}
	p.OnIdle = func() {
		onIdle <- true
	}

	p.OnErr = func(addr *net.IPAddr, t int) {
		onErr <- t
	}

	p.MaxRTT = time.Second
	go p.Run()

	ret := NO_REPLY
	select {
	case <-onRecv:
		ret = ECHO_REPLY
	case <-onIdle:
		ret = NO_REPLY
	case res := <-onErr:
		errId := fmt.Sprintf("%d", res)
		err = errors.New(errId)
		ret = ERROR
	}
	return ret, err
}
<|endoftext|>


<commit_before>// Copyright 2017 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 glog

import (
	"bytes"
	"context"
	"fmt"
	"github.com/gogf/gf/container/gtype"
	"github.com/gogf/gf/internal/intlog"
	"github.com/gogf/gf/os/gfpool"
	"github.com/gogf/gf/os/gmlock"
	"github.com/gogf/gf/os/gtimer"
	"io"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/gogf/gf/debug/gdebug"

	"github.com/gogf/gf/os/gfile"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/text/gregex"
	"github.com/gogf/gf/util/gconv"
)

// Logger is the struct for logging management.
type Logger struct {
	rmu    sync.Mutex      // Mutex for rotation feature.
	ctx    context.Context // Context for logging.
	init   *gtype.Bool     // Initialized.
	parent *Logger         // Parent logger, if it is not empty, it means the logger is used in chaining function.
	config Config          // Logger configuration.
}

const (
	defaultFileFormat = `{Y-m-d}.log`
	defaultFileFlags  = os.O_CREATE | os.O_WRONLY | os.O_APPEND
	defaultFilePerm   = os.FileMode(0666)
	defaultFileExpire = time.Minute
	pathFilterKey     = "/os/glog/glog"
)

const (
	F_ASYNC      = 1 << iota // Print logging content asynchronously。
	F_FILE_LONG              // Print full file name and line number: /a/b/c/d.go:23.
	F_FILE_SHORT             // Print final file name element and line number: d.go:23. overrides F_FILE_LONG.
	F_TIME_DATE              // Print the date in the local time zone: 2009-01-23.
	F_TIME_TIME              // Print the time in the local time zone: 01:23:23.
	F_TIME_MILLI             // Print the time with milliseconds in the local time zone: 01:23:23.675.
	F_CALLER_FN              // Print Caller function name and package: main.main
	F_TIME_STD   = F_TIME_DATE | F_TIME_MILLI
)

// New creates and returns a custom logger.
func New() *Logger {
	logger := &Logger{
		init:   gtype.NewBool(),
		config: DefaultConfig(),
	}
	return logger
}

// NewWithWriter creates and returns a custom logger with io.Writer.
func NewWithWriter(writer io.Writer) *Logger {
	l := New()
	l.SetWriter(writer)
	return l
}

// Clone returns a new logger, which is the clone the current logger.
// It's commonly used for chaining operations.
func (l *Logger) Clone() *Logger {
	logger := New()
	logger.ctx = l.ctx
	logger.config = l.config
	logger.parent = l
	return logger
}

// getFilePath returns the logging file path.
// The logging file name must have extension name of "log".
func (l *Logger) getFilePath(now time.Time) string {
	// Content containing "{}" in the file name is formatted using gtime.
	file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.config.File, func(s string) string {
		return gtime.New(now).Format(strings.Trim(s, "{}"))
	})
	file = gfile.Join(l.config.Path, file)
	return file
}

// print prints <s> to defined writer, logging file or passed <std>.
func (l *Logger) print(std io.Writer, lead string, values ...interface{}) {
	// Lazy initialize for rotation feature.
	// It uses atomic reading operation to enhance the performance checking.
	// It here uses CAP for performance and concurrent safety.
	p := l
	if p.parent != nil {
		p = p.parent
	}
	if !p.init.Val() && p.init.Cas(false, true) {
		// It just initializes once for each logger.
		if p.config.RotateSize > 0 || p.config.RotateExpire > 0 {
			gtimer.AddOnce(p.config.RotateCheckInterval, p.rotateChecksTimely)
			intlog.Printf("logger rotation initialized: every %s", p.config.RotateCheckInterval.String())
		}
	}

	var (
		now    = time.Now()
		buffer = bytes.NewBuffer(nil)
	)
	if l.config.HeaderPrint {
		// Time.
		timeFormat := ""
		if l.config.Flags&F_TIME_DATE > 0 {
			timeFormat += "2006-01-02 "
		}
		if l.config.Flags&F_TIME_TIME > 0 {
			timeFormat += "15:04:05 "
		}
		if l.config.Flags&F_TIME_MILLI > 0 {
			timeFormat += "15:04:05.000 "
		}
		if len(timeFormat) > 0 {
			buffer.WriteString(now.Format(timeFormat))
		}
		// Lead string.
		if len(lead) > 0 {
			buffer.WriteString(lead)
			if len(values) > 0 {
				buffer.WriteByte(' ')
			}
		}
		// Caller path and Fn name.
		if l.config.Flags&(F_FILE_LONG|F_FILE_SHORT|F_CALLER_FN) > 0 {
			callerPath := ""
			callerFnName, path, line := gdebug.CallerWithFilter(pathFilterKey, l.config.StSkip)
			if l.config.Flags&F_CALLER_FN > 0 {
				buffer.WriteString(fmt.Sprintf(`[%s] `, callerFnName))
			}
			if l.config.Flags&F_FILE_LONG > 0 {
				callerPath = fmt.Sprintf(`%s:%d: `, path, line)
			}
			if l.config.Flags&F_FILE_SHORT > 0 {
				callerPath = fmt.Sprintf(`%s:%d: `, gfile.Basename(path), line)
			}
			buffer.WriteString(callerPath)

		}
		// Prefix.
		if len(l.config.Prefix) > 0 {
			buffer.WriteString(l.config.Prefix + " ")
		}
	}
	// Convert value to string.
	var (
		tempStr  = ""
		valueStr = ""
	)
	// Context values.
	if l.ctx != nil && len(l.config.CtxKeys) > 0 {
		ctxStr := ""
		for _, key := range l.config.CtxKeys {
			if v := l.ctx.Value(key); v != nil {
				if ctxStr != "" {
					ctxStr += ", "
				}
				ctxStr += fmt.Sprintf("%s: %+v", key, v)
			}
		}
		if ctxStr != "" {
			buffer.WriteString(fmt.Sprintf("{%s} ", ctxStr))
		}
	}
	for _, v := range values {
		if err, ok := v.(error); ok {
			tempStr = fmt.Sprintf("%+v", err)
		} else {
			tempStr = gconv.String(v)
		}
		if len(valueStr) > 0 {
			if valueStr[len(valueStr)-1] == '\n' {
				// Remove one blank line(\n\n).
				if tempStr[0] == '\n' {
					valueStr += tempStr[1:]
				} else {
					valueStr += tempStr
				}
			} else {
				valueStr += " " + tempStr
			}
		} else {
			valueStr = tempStr
		}
	}
	buffer.WriteString(valueStr + "\n")
	if l.config.Flags&F_ASYNC > 0 {
		err := asyncPool.Add(func() {
			l.printToWriter(now, std, buffer)
		})
		if err != nil {
			intlog.Error(err)
		}
	} else {
		l.printToWriter(now, std, buffer)
	}
}

// printToWriter writes buffer to writer.
func (l *Logger) printToWriter(now time.Time, std io.Writer, buffer *bytes.Buffer) {
	if l.config.Writer == nil {
		// Output content to disk file.
		if l.config.Path != "" {
			l.printToFile(now, buffer)
		}
		// Allow output to stdout?
		if l.config.StdoutPrint {
			if _, err := std.Write(buffer.Bytes()); err != nil {
				intlog.Error(err)
			}
		}
	} else {
		if _, err := l.config.Writer.Write(buffer.Bytes()); err != nil {
			// panic(err)
			intlog.Error(err)
		}
	}
}

// printToFile outputs logging content to disk file.
func (l *Logger) printToFile(now time.Time, buffer *bytes.Buffer) {
	var (
		logFilePath   = l.getFilePath(now)
		memoryLockKey = "glog.file.lock:" + logFilePath
	)
	gmlock.Lock(memoryLockKey)
	defer gmlock.Unlock(memoryLockKey)

	// Rotation file size checks.
	if l.config.RotateSize > 0 {
		if gfile.Size(logFilePath) > l.config.RotateSize {
			l.rotateFileBySize(now)
		}
	}
	// Logging content outputting to disk file.
	if file := l.getFilePointer(logFilePath); file == nil {
		intlog.Errorf(`got nil file pointer for: %s`, logFilePath)
	} else {
		if _, err := file.Write(buffer.Bytes()); err != nil {
			intlog.Error(err)
		}
		if err := file.Close(); err != nil {
			intlog.Error(err)
		}
	}
}

// getFilePointer retrieves and returns a file pointer from file pool.
func (l *Logger) getFilePointer(path string) *gfpool.File {
	file, err := gfpool.Open(
		path,
		defaultFileFlags,
		defaultFilePerm,
		defaultFileExpire,
	)
	if err != nil {
		// panic(err)
		intlog.Error(err)
	}
	return file
}

// printStd prints content <s> without stack.
func (l *Logger) printStd(lead string, value ...interface{}) {
	l.print(os.Stdout, lead, value...)
}

// printStd prints content <s> with stack check.
func (l *Logger) printErr(lead string, value ...interface{}) {
	if l.config.StStatus == 1 {
		if s := l.GetStack(); s != "" {
			value = append(value, "\nStack:\n"+s)
		}
	}
	// In matter of sequence, do not use stderr here, but use the same stdout.
	l.print(os.Stdout, lead, value...)
}

// format formats <values> using fmt.Sprintf.
func (l *Logger) format(format string, value ...interface{}) string {
	return fmt.Sprintf(format, value...)
}

// PrintStack prints the caller stack,
// the optional parameter <skip> specify the skipped stack offset from the end point.
func (l *Logger) PrintStack(skip ...int) {
	if s := l.GetStack(skip...); s != "" {
		l.Println("Stack:\n" + s)
	} else {
		l.Println()
	}
}

// GetStack returns the caller stack content,
// the optional parameter <skip> specify the skipped stack offset from the end point.
func (l *Logger) GetStack(skip ...int) string {
	stackSkip := l.config.StSkip
	if len(skip) > 0 {
		stackSkip += skip[0]
	}
	filters := []string{pathFilterKey}
	if l.config.StFilter != "" {
		filters = append(filters, l.config.StFilter)
	}
	return gdebug.StackWithFilters(filters, stackSkip)
}
<commit_msg>remove automatic stack content printing for error that has stack infomation<commit_after>// Copyright 2017 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 glog

import (
	"bytes"
	"context"
	"fmt"
	"github.com/gogf/gf/container/gtype"
	"github.com/gogf/gf/internal/intlog"
	"github.com/gogf/gf/os/gfpool"
	"github.com/gogf/gf/os/gmlock"
	"github.com/gogf/gf/os/gtimer"
	"io"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/gogf/gf/debug/gdebug"

	"github.com/gogf/gf/os/gfile"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/text/gregex"
	"github.com/gogf/gf/util/gconv"
)

// Logger is the struct for logging management.
type Logger struct {
	rmu    sync.Mutex      // Mutex for rotation feature.
	ctx    context.Context // Context for logging.
	init   *gtype.Bool     // Initialized.
	parent *Logger         // Parent logger, if it is not empty, it means the logger is used in chaining function.
	config Config          // Logger configuration.
}

const (
	defaultFileFormat = `{Y-m-d}.log`
	defaultFileFlags  = os.O_CREATE | os.O_WRONLY | os.O_APPEND
	defaultFilePerm   = os.FileMode(0666)
	defaultFileExpire = time.Minute
	pathFilterKey     = "/os/glog/glog"
)

const (
	F_ASYNC      = 1 << iota // Print logging content asynchronously。
	F_FILE_LONG              // Print full file name and line number: /a/b/c/d.go:23.
	F_FILE_SHORT             // Print final file name element and line number: d.go:23. overrides F_FILE_LONG.
	F_TIME_DATE              // Print the date in the local time zone: 2009-01-23.
	F_TIME_TIME              // Print the time in the local time zone: 01:23:23.
	F_TIME_MILLI             // Print the time with milliseconds in the local time zone: 01:23:23.675.
	F_CALLER_FN              // Print Caller function name and package: main.main
	F_TIME_STD   = F_TIME_DATE | F_TIME_MILLI
)

// New creates and returns a custom logger.
func New() *Logger {
	logger := &Logger{
		init:   gtype.NewBool(),
		config: DefaultConfig(),
	}
	return logger
}

// NewWithWriter creates and returns a custom logger with io.Writer.
func NewWithWriter(writer io.Writer) *Logger {
	l := New()
	l.SetWriter(writer)
	return l
}

// Clone returns a new logger, which is the clone the current logger.
// It's commonly used for chaining operations.
func (l *Logger) Clone() *Logger {
	logger := New()
	logger.ctx = l.ctx
	logger.config = l.config
	logger.parent = l
	return logger
}

// getFilePath returns the logging file path.
// The logging file name must have extension name of "log".
func (l *Logger) getFilePath(now time.Time) string {
	// Content containing "{}" in the file name is formatted using gtime.
	file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.config.File, func(s string) string {
		return gtime.New(now).Format(strings.Trim(s, "{}"))
	})
	file = gfile.Join(l.config.Path, file)
	return file
}

// print prints <s> to defined writer, logging file or passed <std>.
func (l *Logger) print(std io.Writer, lead string, values ...interface{}) {
	// Lazy initialize for rotation feature.
	// It uses atomic reading operation to enhance the performance checking.
	// It here uses CAP for performance and concurrent safety.
	p := l
	if p.parent != nil {
		p = p.parent
	}
	if !p.init.Val() && p.init.Cas(false, true) {
		// It just initializes once for each logger.
		if p.config.RotateSize > 0 || p.config.RotateExpire > 0 {
			gtimer.AddOnce(p.config.RotateCheckInterval, p.rotateChecksTimely)
			intlog.Printf("logger rotation initialized: every %s", p.config.RotateCheckInterval.String())
		}
	}

	var (
		now    = time.Now()
		buffer = bytes.NewBuffer(nil)
	)
	if l.config.HeaderPrint {
		// Time.
		timeFormat := ""
		if l.config.Flags&F_TIME_DATE > 0 {
			timeFormat += "2006-01-02 "
		}
		if l.config.Flags&F_TIME_TIME > 0 {
			timeFormat += "15:04:05 "
		}
		if l.config.Flags&F_TIME_MILLI > 0 {
			timeFormat += "15:04:05.000 "
		}
		if len(timeFormat) > 0 {
			buffer.WriteString(now.Format(timeFormat))
		}
		// Lead string.
		if len(lead) > 0 {
			buffer.WriteString(lead)
			if len(values) > 0 {
				buffer.WriteByte(' ')
			}
		}
		// Caller path and Fn name.
		if l.config.Flags&(F_FILE_LONG|F_FILE_SHORT|F_CALLER_FN) > 0 {
			callerPath := ""
			callerFnName, path, line := gdebug.CallerWithFilter(pathFilterKey, l.config.StSkip)
			if l.config.Flags&F_CALLER_FN > 0 {
				buffer.WriteString(fmt.Sprintf(`[%s] `, callerFnName))
			}
			if l.config.Flags&F_FILE_LONG > 0 {
				callerPath = fmt.Sprintf(`%s:%d: `, path, line)
			}
			if l.config.Flags&F_FILE_SHORT > 0 {
				callerPath = fmt.Sprintf(`%s:%d: `, gfile.Basename(path), line)
			}
			buffer.WriteString(callerPath)

		}
		// Prefix.
		if len(l.config.Prefix) > 0 {
			buffer.WriteString(l.config.Prefix + " ")
		}
	}
	// Convert value to string.
	var (
		tempStr  = ""
		valueStr = ""
	)
	// Context values.
	if l.ctx != nil && len(l.config.CtxKeys) > 0 {
		ctxStr := ""
		for _, key := range l.config.CtxKeys {
			if v := l.ctx.Value(key); v != nil {
				if ctxStr != "" {
					ctxStr += ", "
				}
				ctxStr += fmt.Sprintf("%s: %+v", key, v)
			}
		}
		if ctxStr != "" {
			buffer.WriteString(fmt.Sprintf("{%s} ", ctxStr))
		}
	}
	for _, v := range values {
		tempStr = gconv.String(v)
		if len(valueStr) > 0 {
			if valueStr[len(valueStr)-1] == '\n' {
				// Remove one blank line(\n\n).
				if tempStr[0] == '\n' {
					valueStr += tempStr[1:]
				} else {
					valueStr += tempStr
				}
			} else {
				valueStr += " " + tempStr
			}
		} else {
			valueStr = tempStr
		}
	}
	buffer.WriteString(valueStr + "\n")
	if l.config.Flags&F_ASYNC > 0 {
		err := asyncPool.Add(func() {
			l.printToWriter(now, std, buffer)
		})
		if err != nil {
			intlog.Error(err)
		}
	} else {
		l.printToWriter(now, std, buffer)
	}
}

// printToWriter writes buffer to writer.
func (l *Logger) printToWriter(now time.Time, std io.Writer, buffer *bytes.Buffer) {
	if l.config.Writer == nil {
		// Output content to disk file.
		if l.config.Path != "" {
			l.printToFile(now, buffer)
		}
		// Allow output to stdout?
		if l.config.StdoutPrint {
			if _, err := std.Write(buffer.Bytes()); err != nil {
				intlog.Error(err)
			}
		}
	} else {
		if _, err := l.config.Writer.Write(buffer.Bytes()); err != nil {
			// panic(err)
			intlog.Error(err)
		}
	}
}

// printToFile outputs logging content to disk file.
func (l *Logger) printToFile(now time.Time, buffer *bytes.Buffer) {
	var (
		logFilePath   = l.getFilePath(now)
		memoryLockKey = "glog.file.lock:" + logFilePath
	)
	gmlock.Lock(memoryLockKey)
	defer gmlock.Unlock(memoryLockKey)

	// Rotation file size checks.
	if l.config.RotateSize > 0 {
		if gfile.Size(logFilePath) > l.config.RotateSize {
			l.rotateFileBySize(now)
		}
	}
	// Logging content outputting to disk file.
	if file := l.getFilePointer(logFilePath); file == nil {
		intlog.Errorf(`got nil file pointer for: %s`, logFilePath)
	} else {
		if _, err := file.Write(buffer.Bytes()); err != nil {
			intlog.Error(err)
		}
		if err := file.Close(); err != nil {
			intlog.Error(err)
		}
	}
}

// getFilePointer retrieves and returns a file pointer from file pool.
func (l *Logger) getFilePointer(path string) *gfpool.File {
	file, err := gfpool.Open(
		path,
		defaultFileFlags,
		defaultFilePerm,
		defaultFileExpire,
	)
	if err != nil {
		// panic(err)
		intlog.Error(err)
	}
	return file
}

// printStd prints content <s> without stack.
func (l *Logger) printStd(lead string, value ...interface{}) {
	l.print(os.Stdout, lead, value...)
}

// printStd prints content <s> with stack check.
func (l *Logger) printErr(lead string, value ...interface{}) {
	if l.config.StStatus == 1 {
		if s := l.GetStack(); s != "" {
			value = append(value, "\nStack:\n"+s)
		}
	}
	// In matter of sequence, do not use stderr here, but use the same stdout.
	l.print(os.Stdout, lead, value...)
}

// format formats <values> using fmt.Sprintf.
func (l *Logger) format(format string, value ...interface{}) string {
	return fmt.Sprintf(format, value...)
}

// PrintStack prints the caller stack,
// the optional parameter <skip> specify the skipped stack offset from the end point.
func (l *Logger) PrintStack(skip ...int) {
	if s := l.GetStack(skip...); s != "" {
		l.Println("Stack:\n" + s)
	} else {
		l.Println()
	}
}

// GetStack returns the caller stack content,
// the optional parameter <skip> specify the skipped stack offset from the end point.
func (l *Logger) GetStack(skip ...int) string {
	stackSkip := l.config.StSkip
	if len(skip) > 0 {
		stackSkip += skip[0]
	}
	filters := []string{pathFilterKey}
	if l.config.StFilter != "" {
		filters = append(filters, l.config.StFilter)
	}
	return gdebug.StackWithFilters(filters, stackSkip)
}
<|endoftext|>


<commit_before>//
// Copyright © 2011-2013 Guy M. Allard
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package stompngo

import (
	"bufio"
	"crypto/rand"
	"crypto/sha1"
	"fmt"
	"io"
	"strings"
)

/*
	Encode a string per STOMP 1.1+ specifications.
*/
func encode(s string) string {
	r := s
	for _, tr := range codecValues {
		if strings.Index(r, tr.decoded) >= 0 {
			r = strings.Replace(r, tr.decoded, tr.encoded, -1)
		}
	}
	return r
}

/*
	Decode a string per STOMP 1.1+ specifications.
*/
func decode(s string) string {
	r := s
	for _, tr := range codecValues {
		if strings.Index(r, tr.encoded) >= 0 {
			r = strings.Replace(r, tr.encoded, tr.decoded, -1)
		}
	}
	return r
}

/*
	A network helper.  Read from the wire until a 0x00 byte is encountered.
*/
func readUntilNul(r *bufio.Reader) ([]uint8, error) {
	b, e := r.ReadBytes(0)
	if e != nil {
		return b, e
	}
	if len(b) == 1 {
		b = NULLBUFF
	} else {
		b = b[0 : len(b)-1]
	}
	return b, e
}

/*
	A network helper.  Read a full message body with a known length that is
	> 0.  Then read the trailing 'null' byte expected for STOMP frames.
*/
func readBody(r *bufio.Reader, l int) ([]uint8, error) {
	b := make([]byte, l)
	if l == 0 {
		return b, nil
	}
	n, e := io.ReadFull(r, b)
	if n < l { // Short read, e is ErrUnexpectedEOF
		return b[0 : n-1], e
	}
	if e != nil { // Other erors
		return b, e
	}
	_, _ = r.ReadByte() // trailing NUL
	return b, e
}

/*
	Handle data from the wire after CONNECT is sent. Attempt to create a Frame
	from the wire data.

	Called one time per connection at connection start.
*/
func connectResponse(s string) (*Frame, error) {
	//
	f := new(Frame)
	f.Headers = Headers{}
	f.Body = make([]uint8, 0)

	// Get f.Command
	c := strings.SplitN(s, "\n", 2)
	if len(c) < 2 {
		return nil, EBADFRM
	}
	f.Command = c[0]
	if f.Command != CONNECTED && f.Command != ERROR {
		return f, EUNKFRM
	}

	switch c[1] {
	case "\x00", "\n": // No headers, malformed bodies
		f.Body = []uint8(c[1])
		return f, EBADFRM
	case "\n\x00": // No headers, no body is OK
		return f, nil
	default: // Otherwise continue
	}

	b := strings.SplitN(c[1], "\n\n", 2)
	if len(b) == 1 { // No Headers, b[0] == body
		w := []uint8(b[0])
		f.Body = w[0 : len(w)-1]
		if f.Command == CONNECTED && len(f.Body) > 0 {
			return f, EBDYDATA
		}
		return f, nil
	}

	// Here:
	// b[0] - the headers
	// b[1] - the body

	// Get f.Headers
	for _, l := range strings.Split(b[0], "\n") {
		p := strings.SplitN(l, ":", 2)
		if len(p) < 2 {
			f.Body = []uint8(p[0]) // Bad feedback
			return f, EUNKHDR
		}
		f.Headers = append(f.Headers, p[0], p[1])
	}
	// get f.Body
	w := []uint8(b[1])
	f.Body = w[0 : len(w)-1]
	if f.Command == CONNECTED && len(f.Body) > 0 {
		return f, EBDYDATA
	}

	return f, nil
}

/*
	Sha1 returns a SHA1 hash for a specified string.
*/
func Sha1(q string) string {
	g := sha1.New()
	g.Write([]byte(q))
	return fmt.Sprintf("%x", g.Sum(nil))
}

/*
	Uuid returns a type 4 UUID.
*/
func Uuid() string {
	b := make([]byte, 16)
	_, _ = io.ReadFull(rand.Reader, b)
	b[6] = (b[6] & 0x0F) | 0x40
	b[8] = (b[8] &^ 0x40) | 0x80
	return fmt.Sprintf("%x-%x-%x-%x-%x", b[:4], b[4:6], b[6:8], b[8:10], b[10:])
}

/*
	Common Header Validation.
*/
func checkHeaders(h Headers, c *Connection) error {
	if h == nil {
		return EHDRNIL
	}
	if e := h.Validate(); e != nil {
		return e
	}
	if c.Protocol() != SPL_10 {
		_, e := h.ValidateUTF8()
		if e != nil {
			return e
		}
	}
	return nil
}

/*
	Internal function used by heartbeat initialization.
*/
func max(a, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

/*
	Internal function, used only during CONNECT processing.
*/
func hasValue(a []string, w string) bool {
	for _, v := range a {
		if v == w {
			return true
		}
	}
	return false
}
<commit_msg>Remove extraneous len == 0 check.<commit_after>//
// Copyright © 2011-2013 Guy M. Allard
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package stompngo

import (
	"bufio"
	"crypto/rand"
	"crypto/sha1"
	"fmt"
	"io"
	"strings"
)

/*
	Encode a string per STOMP 1.1+ specifications.
*/
func encode(s string) string {
	r := s
	for _, tr := range codecValues {
		if strings.Index(r, tr.decoded) >= 0 {
			r = strings.Replace(r, tr.decoded, tr.encoded, -1)
		}
	}
	return r
}

/*
	Decode a string per STOMP 1.1+ specifications.
*/
func decode(s string) string {
	r := s
	for _, tr := range codecValues {
		if strings.Index(r, tr.encoded) >= 0 {
			r = strings.Replace(r, tr.encoded, tr.decoded, -1)
		}
	}
	return r
}

/*
	A network helper.  Read from the wire until a 0x00 byte is encountered.
*/
func readUntilNul(r *bufio.Reader) ([]uint8, error) {
	b, e := r.ReadBytes(0)
	if e != nil {
		return b, e
	}
	if len(b) == 1 {
		b = NULLBUFF
	} else {
		b = b[0 : len(b)-1]
	}
	return b, e
}

/*
	A network helper.  Read a full message body with a known length that is
	> 0.  Then read the trailing 'null' byte expected for STOMP frames.
*/
func readBody(r *bufio.Reader, l int) ([]uint8, error) {
	b := make([]byte, l)
	n, e := io.ReadFull(r, b)
	if n < l { // Short read, e is ErrUnexpectedEOF
		return b[0 : n-1], e
	}
	if e != nil { // Other erors
		return b, e
	}
	_, _ = r.ReadByte() // trailing NUL
	return b, e
}

/*
	Handle data from the wire after CONNECT is sent. Attempt to create a Frame
	from the wire data.

	Called one time per connection at connection start.
*/
func connectResponse(s string) (*Frame, error) {
	//
	f := new(Frame)
	f.Headers = Headers{}
	f.Body = make([]uint8, 0)

	// Get f.Command
	c := strings.SplitN(s, "\n", 2)
	if len(c) < 2 {
		return nil, EBADFRM
	}
	f.Command = c[0]
	if f.Command != CONNECTED && f.Command != ERROR {
		return f, EUNKFRM
	}

	switch c[1] {
	case "\x00", "\n": // No headers, malformed bodies
		f.Body = []uint8(c[1])
		return f, EBADFRM
	case "\n\x00": // No headers, no body is OK
		return f, nil
	default: // Otherwise continue
	}

	b := strings.SplitN(c[1], "\n\n", 2)
	if len(b) == 1 { // No Headers, b[0] == body
		w := []uint8(b[0])
		f.Body = w[0 : len(w)-1]
		if f.Command == CONNECTED && len(f.Body) > 0 {
			return f, EBDYDATA
		}
		return f, nil
	}

	// Here:
	// b[0] - the headers
	// b[1] - the body

	// Get f.Headers
	for _, l := range strings.Split(b[0], "\n") {
		p := strings.SplitN(l, ":", 2)
		if len(p) < 2 {
			f.Body = []uint8(p[0]) // Bad feedback
			return f, EUNKHDR
		}
		f.Headers = append(f.Headers, p[0], p[1])
	}
	// get f.Body
	w := []uint8(b[1])
	f.Body = w[0 : len(w)-1]
	if f.Command == CONNECTED && len(f.Body) > 0 {
		return f, EBDYDATA
	}

	return f, nil
}

/*
	Sha1 returns a SHA1 hash for a specified string.
*/
func Sha1(q string) string {
	g := sha1.New()
	g.Write([]byte(q))
	return fmt.Sprintf("%x", g.Sum(nil))
}

/*
	Uuid returns a type 4 UUID.
*/
func Uuid() string {
	b := make([]byte, 16)
	_, _ = io.ReadFull(rand.Reader, b)
	b[6] = (b[6] & 0x0F) | 0x40
	b[8] = (b[8] &^ 0x40) | 0x80
	return fmt.Sprintf("%x-%x-%x-%x-%x", b[:4], b[4:6], b[6:8], b[8:10], b[10:])
}

/*
	Common Header Validation.
*/
func checkHeaders(h Headers, c *Connection) error {
	if h == nil {
		return EHDRNIL
	}
	if e := h.Validate(); e != nil {
		return e
	}
	if c.Protocol() != SPL_10 {
		_, e := h.ValidateUTF8()
		if e != nil {
			return e
		}
	}
	return nil
}

/*
	Internal function used by heartbeat initialization.
*/
func max(a, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

/*
	Internal function, used only during CONNECT processing.
*/
func hasValue(a []string, w string) bool {
	for _, v := range a {
		if v == w {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package clui

import (
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

/*
EditField is a single-line text edit contol. Edit field consumes some keyboard
events when it is active: all printable charaters; Delete, BackSpace, Home,
End, left and right arrows; Ctrl+R to clear EditField.
Edit text can be limited. By default a user can enter text of any length.
Use SetMaxWidth to limit the maximum text length. If the text is longer than
maximun then the text is automatically truncated.
EditField calls onChage in case of its text is changed. Event field Msg contains the new text
*/
type EditField struct {
	ControlBase
	// cursor position in edit text
	cursorPos int
	// the number of the first displayed text character - it is used in case of text is longer than edit width
	offset   int
	readonly bool
	maxWidth int

	onChange func(Event)
}

// NewEditField creates a new EditField control
// view - is a View that manages the control
// parent - is container that keeps the control. The same View can be a view and a parent at the same time.
// width - is minimal width of the control.
// text - text to edit.
// scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
//  control should keep its original size.
func NewEditField(view View, parent Control, width int, text string, scale int) *EditField {
	e := new(EditField)
	e.onChange = nil
	e.SetTitle(text)
	e.SetEnabled(true)

	if width == AutoSize {
		width = xs.Len(text) + 1
	}

	e.SetSize(width, 1)
	e.cursorPos = xs.Len(text)
	e.offset = 0
	e.parent = parent
	e.view = view
	e.parent = parent
	e.readonly = false

	e.SetConstraints(width, 1)

	e.end()

	if parent != nil {
		parent.AddChild(e, scale)
	}

	return e
}

// OnChange sets the callback that is called when EditField content is changed
func (e *EditField) OnChange(fn func(Event)) {
	e.onChange = fn
}

// SetTitle changes the EditField content and emits OnChage eventif the new value does not equal to old one
func (e *EditField) SetTitle(title string) {
	if e.title != title {
		e.title = title
		if e.onChange != nil {
			ev := Event{Msg: title, Sender: e}
			go e.onChange(ev)
		}
	}
}

// Repaint draws the control on its View surface
func (e *EditField) Repaint() {
	canvas := e.view.Canvas()

	x, y := e.Pos()
	w, _ := e.Size()

	tm := e.view.Screen().Theme()
	parts := []rune(tm.SysObject(ObjEdit))
	chLeft, chRight := string(parts[0]), string(parts[1])

	var textOut string
	curOff := 0
	if e.offset == 0 && xs.Len(e.title) < e.width {
		textOut = e.title
	} else {
		fromIdx := 0
		toIdx := 0
		if e.offset == 0 {
			toIdx = e.width - 1
			textOut = xs.Slice(e.title, 0, toIdx) + chRight
			curOff = -e.offset
		} else {
			curOff = 1 - e.offset
			fromIdx = e.offset
			if e.width-1 <= xs.Len(e.title)-e.offset {
				toIdx = e.offset + e.width - 2
				textOut = chLeft + xs.Slice(e.title, fromIdx, toIdx) + chRight
			} else {
				textOut = chLeft + xs.Slice(e.title, fromIdx, -1)
			}
		}
	}

	fg, bg := RealColor(tm, e.fg, ColorEditText), RealColor(tm, e.bg, ColorEditBack)
	if !e.Enabled() {
		fg, bg = RealColor(tm, e.fg, ColorDisabledText), RealColor(tm, e.fg, ColorDisabledBack)
	} else if e.Active() {
		fg, bg = RealColor(tm, e.fg, ColorEditActiveText), RealColor(tm, e.bg, ColorEditActiveBack)

	}

	canvas.FillRect(x, y, w, 1, term.Cell{Ch: ' ', Bg: bg})
	canvas.PutText(x, y, textOut, fg, bg)
	if e.active {
		wx, wy := e.view.Pos()
		canvas.SetCursorPos(e.cursorPos+curOff+wx+e.x, wy+e.y)
	}
}

func (e *EditField) insertRune(ch rune) {
	if e.readonly {
		return
	}

	if e.maxWidth > 0 && xs.Len(e.title) >= e.maxWidth {
		return
	}

	idx := e.cursorPos

	if idx == 0 {
		e.SetTitle(string(ch) + e.title)
	} else if idx >= xs.Len(e.title) {
		e.SetTitle(e.title + string(ch))
	} else {
		e.SetTitle(xs.Slice(e.title, 0, idx) + string(ch) + xs.Slice(e.title, idx, -1))
	}

	e.cursorPos++

	if e.cursorPos >= e.width {
		if e.offset == 0 {
			e.offset = 2
		} else {
			e.offset++
		}
	}
}

func (e *EditField) backspace() {
	if e.title == "" || e.cursorPos == 0 || e.readonly {
		return
	}

	length := xs.Len(e.title)
	if e.cursorPos >= length {
		e.cursorPos--
		e.SetTitle(xs.Slice(e.title, 0, length-1))
	} else if e.cursorPos == 1 {
		e.cursorPos = 0
		e.SetTitle(xs.Slice(e.title, 1, -1))
		e.offset = 0
	} else {
		e.cursorPos--
		e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
	}

	if length-1 < e.width {
		e.offset = 0
	}
}

func (e *EditField) del() {
	length := xs.Len(e.title)

	if e.title == "" || e.cursorPos == length || e.readonly {
		return
	}

	if e.cursorPos == length-1 {
		e.SetTitle(xs.Slice(e.title, 0, length-1))
	} else {
		e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
	}

	if length-1 < e.width {
		e.offset = 0
	}
}

func (e *EditField) charLeft() {
	if e.cursorPos == 0 || e.title == "" {
		return
	}

	if e.cursorPos == e.offset {
		e.offset--
	}

	e.cursorPos--
}

func (e *EditField) charRight() {
	length := xs.Len(e.title)
	if e.cursorPos == length || e.title == "" {
		return
	}

	e.cursorPos++
	if e.cursorPos != length && e.cursorPos >= e.offset+e.width-2 {
		e.offset++
	}
}

func (e *EditField) home() {
	e.offset = 0
	e.cursorPos = 0
}

func (e *EditField) end() {
	length := xs.Len(e.title)
	e.cursorPos = length

	if length < e.width {
		return
	}

	e.offset = length - (e.width - 2)
}

// Clear empties the EditField and emits OnChange event
func (e *EditField) Clear() {
	e.home()
	e.SetTitle("")
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent
*/
func (e *EditField) ProcessEvent(event Event) bool {
	if !e.Active() || !e.Enabled() {
		return false
	}

	if event.Type == EventActivate && event.X == 0 {
		term.HideCursor()
	}

	if event.Type == EventKey && event.Key != term.KeyTab && event.Key != term.KeyEnter {
		switch event.Key {
		case term.KeySpace:
			e.insertRune(' ')
			return true
		case term.KeyBackspace:
			e.backspace()
			return true
		case term.KeyDelete:
			e.del()
			return true
		case term.KeyArrowLeft:
			e.charLeft()
			return true
		case term.KeyHome:
			e.home()
			return true
		case term.KeyEnd:
			e.end()
			return true
		case term.KeyCtrlR:
			if !e.readonly {
				e.Clear()
			}
			return true
		case term.KeyArrowRight:
			e.charRight()
			return true
		default:
			if event.Ch != 0 {
				e.insertRune(event.Ch)
				return true
			}
		}
		return false
	}

	return false
}

// SetMaxWidth sets the maximum lenght of the EditField text. If the current text is longer it is truncated
func (e *EditField) SetMaxWidth(w int) {
	e.maxWidth = w
	if w > 0 && xs.Len(e.title) > w {
		e.title = xs.Slice(e.title, 0, w)
		e.end()
	}
}

// MaxWidth returns the current maximum text length. Zero means no limit
func (e *EditField) MaxWidth() int {
	return e.maxWidth
}

// SetSize changes control size. Constant DoNotChange can be
// used as placeholder to indicate that the control attrubute
// should be unchanged.
// Method does nothing if new size is less than minimal size
// EditField height cannot be changed - it equals 1 always
func (e *EditField) SetSize(width, height int) {
	if width != DoNotChange && (width > 1000 || width < e.minW) {
		return
	}
	if height != DoNotChange && (height > 200 || height < e.minH) {
		return
	}

	if width != DoNotChange {
		e.width = width
	}

	e.height = 1
}
<commit_msg>closes #42 - editField clipboard support<commit_after>package clui

import (
	clip "github.com/atotto/clipboard"
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

/*
EditField is a single-line text edit contol. Edit field consumes some keyboard
events when it is active: all printable charaters; Delete, BackSpace, Home,
End, left and right arrows; Ctrl+R to clear EditField.
Edit text can be limited. By default a user can enter text of any length.
Use SetMaxWidth to limit the maximum text length. If the text is longer than
maximun then the text is automatically truncated.
EditField calls onChage in case of its text is changed. Event field Msg contains the new text
*/
type EditField struct {
	ControlBase
	// cursor position in edit text
	cursorPos int
	// the number of the first displayed text character - it is used in case of text is longer than edit width
	offset   int
	readonly bool
	maxWidth int

	onChange func(Event)
}

// NewEditField creates a new EditField control
// view - is a View that manages the control
// parent - is container that keeps the control. The same View can be a view and a parent at the same time.
// width - is minimal width of the control.
// text - text to edit.
// scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
//  control should keep its original size.
func NewEditField(view View, parent Control, width int, text string, scale int) *EditField {
	e := new(EditField)
	e.onChange = nil
	e.SetTitle(text)
	e.SetEnabled(true)

	if width == AutoSize {
		width = xs.Len(text) + 1
	}

	e.SetSize(width, 1)
	e.cursorPos = xs.Len(text)
	e.offset = 0
	e.parent = parent
	e.view = view
	e.parent = parent
	e.readonly = false

	e.SetConstraints(width, 1)

	e.end()

	if parent != nil {
		parent.AddChild(e, scale)
	}

	return e
}

// OnChange sets the callback that is called when EditField content is changed
func (e *EditField) OnChange(fn func(Event)) {
	e.onChange = fn
}

// SetTitle changes the EditField content and emits OnChage eventif the new value does not equal to old one
func (e *EditField) SetTitle(title string) {
	if e.title != title {
		e.title = title
		if e.onChange != nil {
			ev := Event{Msg: title, Sender: e}
			go e.onChange(ev)
		}
	}
}

// Repaint draws the control on its View surface
func (e *EditField) Repaint() {
	canvas := e.view.Canvas()

	x, y := e.Pos()
	w, _ := e.Size()

	tm := e.view.Screen().Theme()
	parts := []rune(tm.SysObject(ObjEdit))
	chLeft, chRight := string(parts[0]), string(parts[1])

	var textOut string
	curOff := 0
	if e.offset == 0 && xs.Len(e.title) < e.width {
		textOut = e.title
	} else {
		fromIdx := 0
		toIdx := 0
		if e.offset == 0 {
			toIdx = e.width - 1
			textOut = xs.Slice(e.title, 0, toIdx) + chRight
			curOff = -e.offset
		} else {
			curOff = 1 - e.offset
			fromIdx = e.offset
			if e.width-1 <= xs.Len(e.title)-e.offset {
				toIdx = e.offset + e.width - 2
				textOut = chLeft + xs.Slice(e.title, fromIdx, toIdx) + chRight
			} else {
				textOut = chLeft + xs.Slice(e.title, fromIdx, -1)
			}
		}
	}

	fg, bg := RealColor(tm, e.fg, ColorEditText), RealColor(tm, e.bg, ColorEditBack)
	if !e.Enabled() {
		fg, bg = RealColor(tm, e.fg, ColorDisabledText), RealColor(tm, e.fg, ColorDisabledBack)
	} else if e.Active() {
		fg, bg = RealColor(tm, e.fg, ColorEditActiveText), RealColor(tm, e.bg, ColorEditActiveBack)

	}

	canvas.FillRect(x, y, w, 1, term.Cell{Ch: ' ', Bg: bg})
	canvas.PutText(x, y, textOut, fg, bg)
	if e.active {
		wx, wy := e.view.Pos()
		canvas.SetCursorPos(e.cursorPos+curOff+wx+e.x, wy+e.y)
	}
}

func (e *EditField) insertRune(ch rune) {
	if e.readonly {
		return
	}

	if e.maxWidth > 0 && xs.Len(e.title) >= e.maxWidth {
		return
	}

	idx := e.cursorPos

	if idx == 0 {
		e.SetTitle(string(ch) + e.title)
	} else if idx >= xs.Len(e.title) {
		e.SetTitle(e.title + string(ch))
	} else {
		e.SetTitle(xs.Slice(e.title, 0, idx) + string(ch) + xs.Slice(e.title, idx, -1))
	}

	e.cursorPos++

	if e.cursorPos >= e.width {
		if e.offset == 0 {
			e.offset = 2
		} else {
			e.offset++
		}
	}
}

func (e *EditField) backspace() {
	if e.title == "" || e.cursorPos == 0 || e.readonly {
		return
	}

	length := xs.Len(e.title)
	if e.cursorPos >= length {
		e.cursorPos--
		e.SetTitle(xs.Slice(e.title, 0, length-1))
	} else if e.cursorPos == 1 {
		e.cursorPos = 0
		e.SetTitle(xs.Slice(e.title, 1, -1))
		e.offset = 0
	} else {
		e.cursorPos--
		e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
	}

	if length-1 < e.width {
		e.offset = 0
	}
}

func (e *EditField) del() {
	length := xs.Len(e.title)

	if e.title == "" || e.cursorPos == length || e.readonly {
		return
	}

	if e.cursorPos == length-1 {
		e.SetTitle(xs.Slice(e.title, 0, length-1))
	} else {
		e.SetTitle(xs.Slice(e.title, 0, e.cursorPos) + xs.Slice(e.title, e.cursorPos+1, -1))
	}

	if length-1 < e.width {
		e.offset = 0
	}
}

func (e *EditField) charLeft() {
	if e.cursorPos == 0 || e.title == "" {
		return
	}

	if e.cursorPos == e.offset {
		e.offset--
	}

	e.cursorPos--
}

func (e *EditField) charRight() {
	length := xs.Len(e.title)
	if e.cursorPos == length || e.title == "" {
		return
	}

	e.cursorPos++
	if e.cursorPos != length && e.cursorPos >= e.offset+e.width-2 {
		e.offset++
	}
}

func (e *EditField) home() {
	e.offset = 0
	e.cursorPos = 0
}

func (e *EditField) end() {
	length := xs.Len(e.title)
	e.cursorPos = length

	if length < e.width {
		return
	}

	e.offset = length - (e.width - 2)
}

// Clear empties the EditField and emits OnChange event
func (e *EditField) Clear() {
	e.home()
	e.SetTitle("")
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent
*/
func (e *EditField) ProcessEvent(event Event) bool {
	if !e.Active() || !e.Enabled() {
		return false
	}

	if event.Type == EventActivate && event.X == 0 {
		term.HideCursor()
	}

	if event.Type == EventKey && event.Key != term.KeyTab && event.Key != term.KeyEnter {
		switch event.Key {
		case term.KeySpace:
			e.insertRune(' ')
			return true
		case term.KeyBackspace:
			e.backspace()
			return true
		case term.KeyDelete:
			e.del()
			return true
		case term.KeyArrowLeft:
			e.charLeft()
			return true
		case term.KeyHome:
			e.home()
			return true
		case term.KeyEnd:
			e.end()
			return true
		case term.KeyCtrlR:
			if !e.readonly {
				e.Clear()
			}
			return true
		case term.KeyArrowRight:
			e.charRight()
			return true
		case term.KeyCtrlC:
			clip.WriteAll(e.Title())
			return true
		case term.KeyCtrlV:
			s, _ := clip.ReadAll()
			e.SetTitle(s)
			return true
		default:
			if event.Ch != 0 {
				e.insertRune(event.Ch)
				return true
			}
		}
		return false
	}

	return false
}

// SetMaxWidth sets the maximum lenght of the EditField text. If the current text is longer it is truncated
func (e *EditField) SetMaxWidth(w int) {
	e.maxWidth = w
	if w > 0 && xs.Len(e.title) > w {
		e.title = xs.Slice(e.title, 0, w)
		e.end()
	}
}

// MaxWidth returns the current maximum text length. Zero means no limit
func (e *EditField) MaxWidth() int {
	return e.maxWidth
}

// SetSize changes control size. Constant DoNotChange can be
// used as placeholder to indicate that the control attrubute
// should be unchanged.
// Method does nothing if new size is less than minimal size
// EditField height cannot be changed - it equals 1 always
func (e *EditField) SetSize(width, height int) {
	if width != DoNotChange && (width > 1000 || width < e.minW) {
		return
	}
	if height != DoNotChange && (height > 200 || height < e.minH) {
		return
	}

	if width != DoNotChange {
		e.width = width
	}

	e.height = 1
}
<|endoftext|>


<commit_before>//+build x

// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a BSD-style license found in the LICENSE file.

package codec

import (
	"bytes"
	"testing"

	gcbor "code.google.com/p/cbor/go"
	"github.com/Sereal/Sereal/Go/sereal"
	"github.com/davecgh/go-xdr/xdr2"
	"github.com/philhofer/msgp/msgp"
	vmsgpack "github.com/vmihailenco/msgpack"
	"gopkg.in/mgo.v2/bson" //"labix.org/v2/mgo/bson"
)

func init() {
	testPreInitFns = append(testPreInitFns, benchXPreInit)
}

func benchXPreInit() {
	benchCheckers = append(benchCheckers,
		benchChecker{"v-msgpack", fnVMsgpackEncodeFn, fnVMsgpackDecodeFn},
		benchChecker{"bson", fnBsonEncodeFn, fnBsonDecodeFn},
		benchChecker{"msgp", fnMsgpEncodeFn, fnMsgpDecodeFn},
		// place codecs with issues at the end, so as not to make results too ugly
		benchChecker{"gcbor", fnGcborEncodeFn, fnGcborDecodeFn},
		benchChecker{"xdr", fnXdrEncodeFn, fnXdrDecodeFn},
		benchChecker{"sereal", fnSerealEncodeFn, fnSerealDecodeFn},
	)
}

func fnVMsgpackEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	return vmsgpack.Marshal(ts)
}

func fnVMsgpackDecodeFn(buf []byte, ts interface{}) error {
	return vmsgpack.Unmarshal(buf, ts)
}

func fnBsonEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	return bson.Marshal(ts)
}

func fnBsonDecodeFn(buf []byte, ts interface{}) error {
	return bson.Unmarshal(buf, ts)
}

func fnXdrEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	buf := fnBenchmarkByteBuf(bsIn)
	_, err := xdr.Marshal(buf, ts)
	return buf.Bytes(), err
}

func fnXdrDecodeFn(buf []byte, ts interface{}) error {
	_, err := xdr.Unmarshal(bytes.NewReader(buf), ts)
	return err
}

func fnSerealEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	return sereal.Marshal(ts)
}

func fnSerealDecodeFn(buf []byte, ts interface{}) error {
	return sereal.Unmarshal(buf, ts)
}

func fnMsgpEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	if benchUseIO {
		buf := fnBenchmarkByteBuf(bsIn)
		err := ts.(msgp.Encodable).EncodeMsg(msgp.NewWriter(buf))
		return buf.Bytes(), err
	}
	return ts.(msgp.Marshaler).MarshalMsg(bsIn[:0]) // msgp appends to slice.
}

func fnMsgpDecodeFn(buf []byte, ts interface{}) (err error) {
	if benchUseIO {
		err = ts.(msgp.Decodable).DecodeMsg(msgp.NewReader(bytes.NewReader(buf)))
	} else {
		_, err = ts.(msgp.Unmarshaler).UnmarshalMsg(buf)
	}
	return
}

func fnGcborEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
	buf := fnBenchmarkByteBuf(bsIn)
	err = gcbor.NewEncoder(buf).Encode(ts)
	return buf.Bytes(), err
}

func fnGcborDecodeFn(buf []byte, ts interface{}) error {
	return gcbor.NewDecoder(bytes.NewReader(buf)).Decode(ts)
}

func Benchmark__Bson_______Encode(b *testing.B) {
	fnBenchmarkEncode(b, "bson", benchTs, fnBsonEncodeFn)
}

func Benchmark__Bson_______Decode(b *testing.B) {
	fnBenchmarkDecode(b, "bson", benchTs, fnBsonEncodeFn, fnBsonDecodeFn, fnBenchNewTs)
}

func Benchmark__VMsgpack___Encode(b *testing.B) {
	fnBenchmarkEncode(b, "v-msgpack", benchTs, fnVMsgpackEncodeFn)
}

func Benchmark__VMsgpack___Decode(b *testing.B) {
	fnBenchmarkDecode(b, "v-msgpack", benchTs, fnVMsgpackEncodeFn, fnVMsgpackDecodeFn, fnBenchNewTs)
}

func Benchmark__Msgp_______Encode(b *testing.B) {
	fnBenchmarkEncode(b, "msgp", benchTs, fnMsgpEncodeFn)
}

func Benchmark__Msgp_______Decode(b *testing.B) {
	fnBenchmarkDecode(b, "msgp", benchTs, fnMsgpEncodeFn, fnMsgpDecodeFn, fnBenchNewTs)
}

// Place codecs with issues at the bottom, so as not to make results look too ugly.

func Benchmark__Gcbor_______Encode(b *testing.B) {
	fnBenchmarkEncode(b, "gcbor", benchTs, fnGcborEncodeFn)
}

func Benchmark__Gcbor_______Decode(b *testing.B) {
	fnBenchmarkDecode(b, "gcbor", benchTs, fnGcborEncodeFn, fnGcborDecodeFn, fnBenchNewTs)
}

func Benchmark__Xdr________Encode(b *testing.B) {
	fnBenchmarkEncode(b, "xdr", benchTs, fnXdrEncodeFn)
}

func Benchmark__Xdr________Decode(b *testing.B) {
	fnBenchmarkDecode(b, "xdr", benchTs, fnXdrEncodeFn, fnXdrDecodeFn, fnBenchNewTs)
}

func Benchmark__Sereal_____Encode(b *testing.B) {
	fnBenchmarkEncode(b, "sereal", benchTs, fnSerealEncodeFn)
}

func Benchmark__Sereal_____Decode(b *testing.B) {
	fnBenchmarkDecode(b, "sereal", benchTs, fnSerealEncodeFn, fnSerealDecodeFn, fnBenchNewTs)
}
<commit_msg>Improve ffjson benchmark integration.<commit_after>//+build x

// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a BSD-style license found in the LICENSE file.

package codec

import (
	"bytes"
	"testing"

	gcbor "code.google.com/p/cbor/go"
	"github.com/Sereal/Sereal/Go/sereal"
	"github.com/davecgh/go-xdr/xdr2"
	"github.com/philhofer/msgp/msgp"
	"github.com/pquerna/ffjson/ffjson"
	"gopkg.in/mgo.v2/bson"                     //"labix.org/v2/mgo/bson"
	vmsgpack "gopkg.in/vmihailenco/msgpack.v2" //"github.com/vmihailenco/msgpack"
)

func init() {
	testPreInitFns = append(testPreInitFns, benchXPreInit)
}

func benchXPreInit() {
	benchCheckers = append(benchCheckers,
		benchChecker{"v-msgpack", fnVMsgpackEncodeFn, fnVMsgpackDecodeFn},
		benchChecker{"bson", fnBsonEncodeFn, fnBsonDecodeFn},
		benchChecker{"ffjson", fnFfjsonEncodeFn, fnFfjsonDecodeFn},
		benchChecker{"msgp", fnMsgpEncodeFn, fnMsgpDecodeFn},
		// place codecs with issues at the end, so as not to make results too ugly
		benchChecker{"gcbor", fnGcborEncodeFn, fnGcborDecodeFn},
		benchChecker{"xdr", fnXdrEncodeFn, fnXdrDecodeFn},
		benchChecker{"sereal", fnSerealEncodeFn, fnSerealDecodeFn},
	)
}

func fnVMsgpackEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	return vmsgpack.Marshal(ts)
}

func fnVMsgpackDecodeFn(buf []byte, ts interface{}) error {
	return vmsgpack.Unmarshal(buf, ts)
}

func fnBsonEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	return bson.Marshal(ts)
}

func fnBsonDecodeFn(buf []byte, ts interface{}) error {
	return bson.Unmarshal(buf, ts)
}

func fnFfjsonEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	return ffjson.Marshal(ts)
	// return ts.(json.Marshaler).MarshalJSON()
}

func fnFfjsonDecodeFn(buf []byte, ts interface{}) error {
	return ffjson.Unmarshal(buf, ts)
	// return ts.(json.Unmarshaler).UnmarshalJSON(buf)
}

func fnXdrEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	buf := fnBenchmarkByteBuf(bsIn)
	_, err := xdr.Marshal(buf, ts)
	return buf.Bytes(), err
}

func fnXdrDecodeFn(buf []byte, ts interface{}) error {
	_, err := xdr.Unmarshal(bytes.NewReader(buf), ts)
	return err
}

func fnSerealEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	return sereal.Marshal(ts)
}

func fnSerealDecodeFn(buf []byte, ts interface{}) error {
	return sereal.Unmarshal(buf, ts)
}

func fnMsgpEncodeFn(ts interface{}, bsIn []byte) ([]byte, error) {
	if benchUseIO {
		buf := fnBenchmarkByteBuf(bsIn)
		err := ts.(msgp.Encodable).EncodeMsg(msgp.NewWriter(buf))
		return buf.Bytes(), err
	}
	return ts.(msgp.Marshaler).MarshalMsg(bsIn[:0]) // msgp appends to slice.
}

func fnMsgpDecodeFn(buf []byte, ts interface{}) (err error) {
	if benchUseIO {
		err = ts.(msgp.Decodable).DecodeMsg(msgp.NewReader(bytes.NewReader(buf)))
	} else {
		_, err = ts.(msgp.Unmarshaler).UnmarshalMsg(buf)
	}
	return
}

func fnGcborEncodeFn(ts interface{}, bsIn []byte) (bs []byte, err error) {
	buf := fnBenchmarkByteBuf(bsIn)
	err = gcbor.NewEncoder(buf).Encode(ts)
	return buf.Bytes(), err
}

func fnGcborDecodeFn(buf []byte, ts interface{}) error {
	return gcbor.NewDecoder(bytes.NewReader(buf)).Decode(ts)
}

func Benchmark__Bson_______Encode(b *testing.B) {
	fnBenchmarkEncode(b, "bson", benchTs, fnBsonEncodeFn)
}

func Benchmark__Bson_______Decode(b *testing.B) {
	fnBenchmarkDecode(b, "bson", benchTs, fnBsonEncodeFn, fnBsonDecodeFn, fnBenchNewTs)
}

func Benchmark__VMsgpack___Encode(b *testing.B) {
	fnBenchmarkEncode(b, "v-msgpack", benchTs, fnVMsgpackEncodeFn)
}

func Benchmark__VMsgpack___Decode(b *testing.B) {
	fnBenchmarkDecode(b, "v-msgpack", benchTs, fnVMsgpackEncodeFn, fnVMsgpackDecodeFn, fnBenchNewTs)
}

func Benchmark__Msgp_______Encode(b *testing.B) {
	fnBenchmarkEncode(b, "msgp", benchTs, fnMsgpEncodeFn)
}

func Benchmark__Msgp_______Decode(b *testing.B) {
	fnBenchmarkDecode(b, "msgp", benchTs, fnMsgpEncodeFn, fnMsgpDecodeFn, fnBenchNewTs)
}

// Place codecs with issues at the bottom, so as not to make results look too ugly.

func Benchmark__Ffjson_____Encode(b *testing.B) {
	fnBenchmarkEncode(b, "ffjson", benchTs, fnFfjsonEncodeFn)
}

func Benchmark__Ffjson_____Decode(b *testing.B) {
	fnBenchmarkDecode(b, "ffjson", benchTs, fnFfjsonEncodeFn, fnFfjsonDecodeFn, fnBenchNewTs)
}

func Benchmark__Gcbor_______Encode(b *testing.B) {
	fnBenchmarkEncode(b, "gcbor", benchTs, fnGcborEncodeFn)
}

func Benchmark__Gcbor_______Decode(b *testing.B) {
	fnBenchmarkDecode(b, "gcbor", benchTs, fnGcborEncodeFn, fnGcborDecodeFn, fnBenchNewTs)
}

func Benchmark__Xdr________Encode(b *testing.B) {
	fnBenchmarkEncode(b, "xdr", benchTs, fnXdrEncodeFn)
}

func Benchmark__Xdr________Decode(b *testing.B) {
	fnBenchmarkDecode(b, "xdr", benchTs, fnXdrEncodeFn, fnXdrDecodeFn, fnBenchNewTs)
}

func Benchmark__Sereal_____Encode(b *testing.B) {
	fnBenchmarkEncode(b, "sereal", benchTs, fnSerealEncodeFn)
}

func Benchmark__Sereal_____Decode(b *testing.B) {
	fnBenchmarkDecode(b, "sereal", benchTs, fnSerealEncodeFn, fnSerealDecodeFn, fnBenchNewTs)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package contiv defines flavor used for Contiv-VPP agent.
package contiv

import (
	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/flavors/local"

	"github.com/contiv/vpp/flavors/ksr"
	"github.com/contiv/vpp/plugins/contiv"
	"github.com/contiv/vpp/plugins/kvdbproxy"
	"github.com/contiv/vpp/plugins/policy"
	"github.com/golang/protobuf/proto"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/datasync/kvdbsync"
	local_sync "github.com/ligato/cn-infra/datasync/kvdbsync/local"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/db/keyval/etcdv3"
	"github.com/ligato/cn-infra/flavors/connectors"
	"github.com/ligato/cn-infra/rpc/grpc"
	"github.com/ligato/vpp-agent/clientv1/linux/localclient"
	"github.com/ligato/vpp-agent/plugins/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	"github.com/ligato/vpp-agent/plugins/linuxplugin"
)

// FlavorContiv glues together multiple plugins to manage VPP and Linux
// configuration using the local client.
type FlavorContiv struct {
	*local.FlavorLocal

	ETCD            etcdv3.Plugin
	ETCDDataSync    kvdbsync.Plugin
	KsrETCDDataSync kvdbsync.Plugin

	KVProxy kvdbproxy.Plugin

	LinuxLocalClient localclient.Plugin
	GoVPP            govppmux.GOVPPPlugin
	Linux            linuxplugin.Plugin
	VPP              defaultplugins.Plugin
	GRPC             grpc.Plugin
	Contiv           contiv.Plugin
	Policy           policy.Plugin
	ResyncOrch       resync.Plugin
	injected         bool
}

// Inject sets inter-plugin references.
func (f *FlavorContiv) Inject() bool {
	if f.injected {
		return false
	}
	f.injected = true

	if f.FlavorLocal == nil {
		f.FlavorLocal = &local.FlavorLocal{}
	}
	f.FlavorLocal.Inject()

	f.ETCD.Deps.PluginInfraDeps = *f.InfraDeps("etcdv3", local.WithConf())
	connectors.InjectKVDBSync(&f.ETCDDataSync, &f.ETCD, f.ETCD.PluginName, f.FlavorLocal, &f.ResyncOrch)
	f.KsrETCDDataSync = *f.ETCDDataSync.OfDifferentAgent(ksr.MicroserviceLabel, f)

	f.KVProxy.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("kvproxy")
	f.KVProxy.Deps.KVDB = &f.ETCDDataSync

	f.GoVPP.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("govpp")
	f.Linux.Watcher = &datasync.CompositeKVProtoWatcher{Adapters: []datasync.KeyValProtoWatcher{local_sync.Get()}}
	f.Linux.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("linuxplugin")

	f.VPP.Watch = &datasync.CompositeKVProtoWatcher{Adapters: []datasync.KeyValProtoWatcher{local_sync.Get(), &f.KVProxy}}
	f.VPP.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("default-plugins")
	f.VPP.Deps.Linux = &f.Linux
	f.VPP.Deps.GoVppmux = &f.GoVPP
	f.VPP.Deps.PublishStatistics = &datasync.CompositeKVProtoWriter{Adapters: []datasync.KeyProtoValWriter{&devNullWriter{}}}
	f.VPP.Deps.IfStatePub = &datasync.CompositeKVProtoWriter{Adapters: []datasync.KeyProtoValWriter{&devNullWriter{}}}

	grpc.DeclareGRPCPortFlag("grpc")
	grpcInfraDeps := f.FlavorLocal.InfraDeps("grpc")
	f.GRPC.Deps.Log = grpcInfraDeps.Log
	f.GRPC.Deps.PluginName = grpcInfraDeps.PluginName
	f.GRPC.Deps.PluginConfig = grpcInfraDeps.PluginConfig

	f.Contiv.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("cni-grpc")
	f.Contiv.Deps.GRPC = &f.GRPC
	f.Contiv.Deps.Proxy = &f.KVProxy
	f.Contiv.GoVPP = &f.GoVPP
	f.Contiv.VPP = &f.VPP

	f.Policy.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("policy")
	f.Policy.Deps.Watcher = &f.KsrETCDDataSync
	f.Policy.Deps.Contiv = &f.Contiv

	f.ResyncOrch.PluginLogDeps = *f.LogDeps("resync-orch")

	return true
}

// Plugins combines all Plugins in the flavor to a list.
func (f *FlavorContiv) Plugins() []*core.NamedPlugin {
	f.Inject()
	return core.ListPluginsInFlavor(f)
}

type devNullWriter struct {
}

func (d *devNullWriter) Put(key string, data proto.Message, opts ...datasync.PutOption) error {
	return nil
}
<commit_msg>Health check exposed via RPC<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package contiv defines flavor used for Contiv-VPP agent.
package contiv

import (
	"github.com/ligato/cn-infra/core"
	"github.com/ligato/cn-infra/flavors/local"

	"github.com/contiv/vpp/flavors/ksr"
	"github.com/contiv/vpp/plugins/contiv"
	"github.com/contiv/vpp/plugins/kvdbproxy"
	"github.com/contiv/vpp/plugins/policy"
	"github.com/golang/protobuf/proto"
	"github.com/ligato/cn-infra/datasync"
	"github.com/ligato/cn-infra/datasync/kvdbsync"
	local_sync "github.com/ligato/cn-infra/datasync/kvdbsync/local"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/db/keyval/etcdv3"
	"github.com/ligato/cn-infra/flavors/connectors"
	"github.com/ligato/cn-infra/health/probe"
	"github.com/ligato/cn-infra/rpc/grpc"
	"github.com/ligato/cn-infra/rpc/rest"
	"github.com/ligato/vpp-agent/clientv1/linux/localclient"
	"github.com/ligato/vpp-agent/plugins/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	"github.com/ligato/vpp-agent/plugins/linuxplugin"
)

// FlavorContiv glues together multiple plugins to manage VPP and Linux
// configuration using the local client.
type FlavorContiv struct {
	*local.FlavorLocal
	HTTP      rest.Plugin
	HealthRPC probe.Plugin

	ETCD            etcdv3.Plugin
	ETCDDataSync    kvdbsync.Plugin
	KsrETCDDataSync kvdbsync.Plugin

	KVProxy kvdbproxy.Plugin

	LinuxLocalClient localclient.Plugin
	GoVPP            govppmux.GOVPPPlugin
	Linux            linuxplugin.Plugin
	VPP              defaultplugins.Plugin
	GRPC             grpc.Plugin
	Contiv           contiv.Plugin
	Policy           policy.Plugin
	ResyncOrch       resync.Plugin
	injected         bool
}

// Inject sets inter-plugin references.
func (f *FlavorContiv) Inject() bool {
	if f.injected {
		return false
	}
	f.injected = true

	if f.FlavorLocal == nil {
		f.FlavorLocal = &local.FlavorLocal{}
	}
	f.FlavorLocal.Inject()

	rest.DeclareHTTPPortFlag("http")
	httpPlugDeps := *f.InfraDeps("http", local.WithConf())
	f.HTTP.Deps.Log = httpPlugDeps.Log
	f.HTTP.Deps.PluginConfig = httpPlugDeps.PluginConfig
	f.HTTP.Deps.PluginName = httpPlugDeps.PluginName

	f.Logs.HTTP = &f.HTTP

	f.HealthRPC.Deps.PluginInfraDeps = *f.InfraDeps("health-rpc")
	f.HealthRPC.Deps.HTTP = &f.HTTP
	f.HealthRPC.Deps.StatusCheck = &f.StatusCheck

	f.ETCD.Deps.PluginInfraDeps = *f.InfraDeps("etcdv3", local.WithConf())
	connectors.InjectKVDBSync(&f.ETCDDataSync, &f.ETCD, f.ETCD.PluginName, f.FlavorLocal, &f.ResyncOrch)
	f.KsrETCDDataSync = *f.ETCDDataSync.OfDifferentAgent(ksr.MicroserviceLabel, f)

	f.KVProxy.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("kvproxy")
	f.KVProxy.Deps.KVDB = &f.ETCDDataSync

	f.GoVPP.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("govpp")
	f.Linux.Watcher = &datasync.CompositeKVProtoWatcher{Adapters: []datasync.KeyValProtoWatcher{local_sync.Get()}}
	f.Linux.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("linuxplugin")

	f.VPP.Watch = &datasync.CompositeKVProtoWatcher{Adapters: []datasync.KeyValProtoWatcher{local_sync.Get(), &f.KVProxy}}
	f.VPP.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("default-plugins")
	f.VPP.Deps.Linux = &f.Linux
	f.VPP.Deps.GoVppmux = &f.GoVPP
	f.VPP.Deps.PublishStatistics = &datasync.CompositeKVProtoWriter{Adapters: []datasync.KeyProtoValWriter{&devNullWriter{}}}
	f.VPP.Deps.IfStatePub = &datasync.CompositeKVProtoWriter{Adapters: []datasync.KeyProtoValWriter{&devNullWriter{}}}

	grpc.DeclareGRPCPortFlag("grpc")
	grpcInfraDeps := f.FlavorLocal.InfraDeps("grpc")
	f.GRPC.Deps.Log = grpcInfraDeps.Log
	f.GRPC.Deps.PluginName = grpcInfraDeps.PluginName
	f.GRPC.Deps.PluginConfig = grpcInfraDeps.PluginConfig

	f.Contiv.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("cni-grpc")
	f.Contiv.Deps.GRPC = &f.GRPC
	f.Contiv.Deps.Proxy = &f.KVProxy
	f.Contiv.GoVPP = &f.GoVPP
	f.Contiv.VPP = &f.VPP

	f.Policy.Deps.PluginInfraDeps = *f.FlavorLocal.InfraDeps("policy")
	f.Policy.Deps.Watcher = &f.KsrETCDDataSync
	f.Policy.Deps.Contiv = &f.Contiv

	f.ResyncOrch.PluginLogDeps = *f.LogDeps("resync-orch")

	return true
}

// Plugins combines all Plugins in the flavor to a list.
func (f *FlavorContiv) Plugins() []*core.NamedPlugin {
	f.Inject()
	return core.ListPluginsInFlavor(f)
}

type devNullWriter struct {
}

func (d *devNullWriter) Put(key string, data proto.Message, opts ...datasync.PutOption) error {
	return nil
}
<|endoftext|>


<commit_before>package xml // import "github.com/tdewolff/minify/xml"

import (
	"bytes"
	"os"
	"regexp"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/tdewolff/minify"
	"github.com/tdewolff/test"
)

func TestXML(t *testing.T) {
	var xmlTests = []struct {
		xml      string
		expected string
	}{
		{"", ""},
		{"x", "x"},
		{"x", "x"},
		{"x\ny", "x y"},
		{" <![CDATA[ a ]]> ", "a"},
		{"a", "a"},
		{"<?xml  version=\"1.0\" ?>", "<?xml version=\"1.0\"?>"},
		{"<x></x>", "<x/>"},
		{"<x> </x>", "<x/>"},
		{"<x a=\"b\"></x>", "<x a=\"b\"/>"},
		{"<x a=\"\"></x>", "<x a=\"\"/>"},
		{"<x a=a></x>", "<x a=a/>"},
		{"<x a=\" a \n\r\t b \"/>", "<x a=\" a     b \"/>"},
		{"<x a=\"'b"\"></x>", "<x a=\"'b"\"/>"},
		{"<x a=\"""'\"></x>", "<x a='\"\"''/>"},
		{"<!DOCTYPE foo SYSTEM \"Foo.dtd\">", "<!DOCTYPE foo SYSTEM \"Foo.dtd\">"},
		{"text  text", "text text"},
	}

	m := minify.New()
	for _, tt := range xmlTests {
		b := &bytes.Buffer{}
		assert.Nil(t, Minify(m, b, bytes.NewBufferString(tt.xml), nil), "Minify must not return error in "+tt.xml)
		assert.Equal(t, tt.expected, b.String(), "Minify must give expected result in "+tt.xml)
	}
}

func TestReaderErrors(t *testing.T) {
	m := minify.New()
	r := test.NewErrorReader(0)
	w := &bytes.Buffer{}
	assert.Equal(t, test.ErrPlain, Minify(m, w, r, nil), "Minify must return error at first read")
}

func TestWriterErrors(t *testing.T) {
	var errorTests = []int{0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 18, 19}

	m := minify.New()
	for _, n := range errorTests {
		// writes:                  0             1    2 3 45678901    23 4 5 6    7   8                    9
		r := bytes.NewBufferString(`<!DOCTYPE foo><?xml?><a x=y z="val"><b/><c></c></a><![CDATA[data<<<<<]]>text`)
		w := test.NewErrorWriter(n)
		assert.Equal(t, test.ErrPlain, Minify(m, w, r, nil), "Minify must return error at write "+strconv.FormatInt(int64(n), 10))
	}
}

////////////////////////////////////////////////////////////////

func ExampleMinify() {
	m := minify.New()
	m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), Minify)

	if err := m.Minify("text/xml", os.Stdout, os.Stdin); err != nil {
		panic(err)
	}
}
<commit_msg>Update test to keep newline<commit_after>package xml // import "github.com/tdewolff/minify/xml"

import (
	"bytes"
	"os"
	"regexp"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/tdewolff/minify"
	"github.com/tdewolff/test"
)

func TestXML(t *testing.T) {
	var xmlTests = []struct {
		xml      string
		expected string
	}{
		{"", ""},
		{"x", "x"},
		{"x", "x"},
		{"x\ny", "x\ny"},
		{" <![CDATA[ a ]]> ", "a"},
		{"a", "a"},
		{"<?xml  version=\"1.0\" ?>", "<?xml version=\"1.0\"?>"},
		{"<x></x>", "<x/>"},
		{"<x> </x>", "<x/>"},
		{"<x a=\"b\"></x>", "<x a=\"b\"/>"},
		{"<x a=\"\"></x>", "<x a=\"\"/>"},
		{"<x a=a></x>", "<x a=a/>"},
		{"<x a=\" a \n\r\t b \"/>", "<x a=\" a     b \"/>"},
		{"<x a=\"'b"\"></x>", "<x a=\"'b"\"/>"},
		{"<x a=\"""'\"></x>", "<x a='\"\"''/>"},
		{"<!DOCTYPE foo SYSTEM \"Foo.dtd\">", "<!DOCTYPE foo SYSTEM \"Foo.dtd\">"},
		{"text  text", "text text"},
	}

	m := minify.New()
	for _, tt := range xmlTests {
		b := &bytes.Buffer{}
		assert.Nil(t, Minify(m, b, bytes.NewBufferString(tt.xml), nil), "Minify must not return error in "+tt.xml)
		assert.Equal(t, tt.expected, b.String(), "Minify must give expected result in "+tt.xml)
	}
}

func TestReaderErrors(t *testing.T) {
	m := minify.New()
	r := test.NewErrorReader(0)
	w := &bytes.Buffer{}
	assert.Equal(t, test.ErrPlain, Minify(m, w, r, nil), "Minify must return error at first read")
}

func TestWriterErrors(t *testing.T) {
	var errorTests = []int{0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 18, 19}

	m := minify.New()
	for _, n := range errorTests {
		// writes:                  0             1    2 3 45678901    23 4 5 6    7   8                    9
		r := bytes.NewBufferString(`<!DOCTYPE foo><?xml?><a x=y z="val"><b/><c></c></a><![CDATA[data<<<<<]]>text`)
		w := test.NewErrorWriter(n)
		assert.Equal(t, test.ErrPlain, Minify(m, w, r, nil), "Minify must return error at write "+strconv.FormatInt(int64(n), 10))
	}
}

////////////////////////////////////////////////////////////////

func ExampleMinify() {
	m := minify.New()
	m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), Minify)

	if err := m.Minify("text/xml", os.Stdout, os.Stdin); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>/*
Package epub generates valid EPUB 3.0 files with additional EPUB 2.0 table of
contents (as seen here: https://github.com/bmaupin/epub-samples) for maximum
compatibility.

Basic usage:

	// Create a new EPUB
	e := epub.NewEpub("My title")

	// Set the author
	e.SetAuthor("Hingle McCringleberry")

	// Add a section
	section1Content := `
Section 1

This is a paragraph.
`
	e.AddSection("Section 1", section1Content)

	section2Content := `
Section 2

This is a paragraph.
`
	e.AddSection("Section 2", section2Content)

	// Write the EPUB
	err = e.Write("My EPUB.epub")
	if err != nil {
		// handle error
	}

*/
package epub

import (
	"fmt"
	"path/filepath"

	"github.com/satori/go.uuid"
)

const (
	urnUUID = "urn:uuid:"
)

// Epub implements an EPUB file.
type Epub struct {
	author string
	images map[string]string // Images added to the EPUB
	lang   string            // Language
	pkg    *pkg              // The package file (package.opf)
	//	sections []section
	sections []xhtml // Sections (chapters)
	title    string
	toc      *toc // Table of contents
	uuid     string
}

// NewEpub returns a new Epub.
func NewEpub(title string) *Epub {
	e := &Epub{}
	e.images = make(map[string]string)
	e.pkg = newPackage()
	e.toc = newToc()
	// Set minimal required attributes
	e.SetLang("en")
	e.SetTitle(title)
	e.SetUUID(urnUUID + uuid.NewV4().String())

	return e
}

// AddImage adds an image to the EPUB and returns a relative path that can be
// used in the content of a section. The image source should either be a URL or
// a path to a local file; in either case, the image will be retrieved and
// stored in the EPUB. The image filename will be used when storing the image in
// the EPUB and must be unique.
func (e *Epub) AddImage(imageSource string, imageFilename string) (string, error) {
	if _, ok := e.images[imageFilename]; ok {
		return "", fmt.Errorf("Image filename %s already used", imageFilename)
	}

	e.images[imageFilename] = imageSource

	return filepath.Join(
		"..",
		imageFolderName,
		imageFilename,
	), nil
}

// AddSection adds a new section (chapter, etc) to the EPUB. The title will be
// used for the table of contents. The content must be valid XHTML that will go
// between the  tags. The content will not be validated.
func (e *Epub) AddSection(title string, content string) {
	x := newXhtml(content)
	x.setTitle(title)

	e.sections = append(e.sections, *x)
}

// Author returns the author of the EPUB.
func (e *Epub) Author() string {
	return e.author
}

// Lang returns the language of the EPUB.
func (e *Epub) Lang() string {
	return e.lang
}

// SetAuthor sets the author of the EPUB.
func (e *Epub) SetAuthor(author string) {
	e.author = author
	e.pkg.setAuthor(author)
}

// SetLang sets the language of the EPUB.
func (e *Epub) SetLang(lang string) {
	e.lang = lang
	e.pkg.setLang(lang)
}

// SetTitle sets the title of the EPUB.
func (e *Epub) SetTitle(title string) {
	e.title = title
	e.pkg.setTitle(title)
	e.toc.setTitle(title)
}

// SetUUID sets the UUID of the EPUB. A UUID will be automatically be generated
// for you when the NewEpub method is run.
func (e *Epub) SetUUID(uuid string) {
	e.uuid = uuid
	e.pkg.setUUID(uuid)
	e.toc.setUUID(uuid)
}

// Title returns the title of the EPUB.
func (e *Epub) Title() string {
	return e.title
}

// UUID returns the UUID of the EPUB.
func (e *Epub) UUID() string {
	return e.uuid
}
<commit_msg>Make SetUUID() more obvious<commit_after>/*
Package epub generates valid EPUB 3.0 files with additional EPUB 2.0 table of
contents (as seen here: https://github.com/bmaupin/epub-samples) for maximum
compatibility.

Basic usage:

	// Create a new EPUB
	e := epub.NewEpub("My title")

	// Set the author
	e.SetAuthor("Hingle McCringleberry")

	// Add a section
	section1Content := `
Section 1

This is a paragraph.
`
	e.AddSection("Section 1", section1Content)

	section2Content := `
Section 2

This is a paragraph.
`
	e.AddSection("Section 2", section2Content)

	// Write the EPUB
	err = e.Write("My EPUB.epub")
	if err != nil {
		// handle error
	}

*/
package epub

import (
	"fmt"
	"path/filepath"

	"github.com/satori/go.uuid"
)

const (
	urnUUID = "urn:uuid:"
)

// Epub implements an EPUB file.
type Epub struct {
	author string
	images map[string]string // Images added to the EPUB
	lang   string            // Language
	pkg    *pkg              // The package file (package.opf)
	//	sections []section
	sections []xhtml // Sections (chapters)
	title    string
	toc      *toc // Table of contents
	uuid     string
}

// NewEpub returns a new Epub.
func NewEpub(title string) *Epub {
	e := &Epub{}
	e.images = make(map[string]string)
	e.pkg = newPackage()
	e.toc = newToc()
	// Set minimal required attributes
	e.SetLang("en")
	e.SetTitle(title)
	e.SetUUID(uuid.NewV4().String())

	return e
}

// AddImage adds an image to the EPUB and returns a relative path that can be
// used in the content of a section. The image source should either be a URL or
// a path to a local file; in either case, the image will be retrieved and
// stored in the EPUB. The image filename will be used when storing the image in
// the EPUB and must be unique.
func (e *Epub) AddImage(imageSource string, imageFilename string) (string, error) {
	if _, ok := e.images[imageFilename]; ok {
		return "", fmt.Errorf("Image filename %s already used", imageFilename)
	}

	e.images[imageFilename] = imageSource

	return filepath.Join(
		"..",
		imageFolderName,
		imageFilename,
	), nil
}

// AddSection adds a new section (chapter, etc) to the EPUB. The title will be
// used for the table of contents. The content must be valid XHTML that will go
// between the  tags. The content will not be validated.
func (e *Epub) AddSection(title string, content string) {
	x := newXhtml(content)
	x.setTitle(title)

	e.sections = append(e.sections, *x)
}

// Author returns the author of the EPUB.
func (e *Epub) Author() string {
	return e.author
}

// Lang returns the language of the EPUB.
func (e *Epub) Lang() string {
	return e.lang
}

// SetAuthor sets the author of the EPUB.
func (e *Epub) SetAuthor(author string) {
	e.author = author
	e.pkg.setAuthor(author)
}

// SetLang sets the language of the EPUB.
func (e *Epub) SetLang(lang string) {
	e.lang = lang
	e.pkg.setLang(lang)
}

// SetTitle sets the title of the EPUB.
func (e *Epub) SetTitle(title string) {
	e.title = title
	e.pkg.setTitle(title)
	e.toc.setTitle(title)
}

// SetUUID sets the UUID of the EPUB. A UUID will be automatically be generated
// for you when the NewEpub method is run.
func (e *Epub) SetUUID(uuid string) {
	e.uuid = uuid
	e.pkg.setUUID(urnUUID + uuid)
	e.toc.setUUID(urnUUID + uuid)
}

// Title returns the title of the EPUB.
func (e *Epub) Title() string {
	return e.title
}

// UUID returns the UUID of the EPUB.
func (e *Epub) UUID() string {
	return e.uuid
}
<|endoftext|>