<commit_before>package search

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"text/template"
	"time"

	"github.com/mholt/caddy/config/setup"
	"github.com/mholt/caddy/middleware"
	"github.com/pedronasser/caddy-search/indexer"
	"github.com/pedronasser/caddy-search/indexer/bleve"
)

// Setup creates a new middleware with the given configuration
func Setup(c *setup.Controller) (mid middleware.Middleware, err error) {
	var config *Config

	config, err = parseSearch(c)
	if err != nil {
		return nil, err
	}

	index, err := NewIndexer(config.Engine, indexer.Config{
		HostName:       config.HostName,
		IndexDirectory: config.IndexDirectory,
	})

	if err != nil {
		return nil, err
	}

	ppl, err := NewPipeline(config, index)

	if err != nil {
		return nil, err
	}

	c.Startup = append(c.Startup, func() error {
		return ScanToPipe(c.Root, ppl, index)
	})

	expire := time.NewTicker(config.Expire)

	go func() {
		for {
			select {
			case <-expire.C:
				if !lastScanned.Indexed().IsZero() || lastScanned.Ignored() {
					ScanToPipe(c.Root, ppl, index)
				}
			}
		}
	}()

	mid = func(next middleware.Handler) middleware.Handler {
		return &Search{
			Next:     next,
			Config:   config,
			Indexer:  index,
			Pipeline: ppl,
		}
	}

	return
}

var lastScanned indexer.Record

// ScanToPipe ...
func ScanToPipe(fp string, pipeline *Pipeline, index indexer.Handler) error {
	filepath.Walk(fp, func(path string, info os.FileInfo, err error) error {
		if info.Name() == "." {
			return nil
		}

		if info.Name() == "" || info.Name()[0] == '.' {
			if info.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		if !info.IsDir() {
			reqPath, err := filepath.Rel(fp, path)
			if err != nil {
				return err
			}
			reqPath = "/" + reqPath

			if pipeline.ValidatePath(reqPath) {
				body, err := ioutil.ReadFile(path)
				if err != nil {
					return err
				}

				record := index.Record(reqPath)
				record.SetModified(info.ModTime())
				if len(body) > 0 {
					record.Write(body)
				} else {
					record.Ignore()
				}
				pipeline.Pipe(record)
				lastScanned = record
			}
		}

		return nil
	})

	return nil
}

// NewIndexer creates a new Indexer with the received config
func NewIndexer(engine string, config indexer.Config) (index indexer.Handler, err error) {
	switch engine {
	case "bleve":
		index, err = bleve.New(config)
		break
	default:
		index, err = bleve.New(config)
		break
	}
	return
}

// Config represents this middleware configuration structure
type Config struct {
	HostName       string
	Engine         string
	Path           string
	IncludePaths   []*regexp.Regexp
	ExcludePaths   []*regexp.Regexp
	Endpoint       string
	IndexDirectory string
	Template       *template.Template
	Expire         time.Duration
	SiteRoot       string
}

// parseSearch controller information to create a IndexSearch config
func parseSearch(c *setup.Controller) (*Config, error) {
	conf := &Config{
		HostName:       c.Address(),
		Engine:         `bleve`,
		IndexDirectory: `/tmp/caddyIndex`,
		IncludePaths:   []*regexp.Regexp{},
		ExcludePaths:   []*regexp.Regexp{},
		Endpoint:       `/search`,
		SiteRoot:       c.Root,
		Expire:         60 * time.Second,
		Template:       nil,
	}

	incPaths := []string{}
	excPaths := []string{}

	for c.Next() {
		args := c.RemainingArgs()

		switch len(args) {
		case 2:
			conf.Endpoint = args[1]
			fallthrough
		case 1:
			incPaths = append(incPaths, args[0])
		}

		for c.NextBlock() {
			switch c.Val() {
			case "engine":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				conf.Engine = c.Val()
			case "+path":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				incPaths = append(incPaths, c.Val())
				incPaths = append(incPaths, c.RemainingArgs()...)
			case "-path":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				excPaths = append(excPaths, c.Val())
				excPaths = append(excPaths, c.RemainingArgs()...)
			case "endpoint":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				conf.Endpoint = c.Val()
			case "expire":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				exp, err := strconv.Atoi(c.Val())
				if err != nil {
					return nil, err
				}
				conf.Expire = time.Duration(exp) * time.Second
			case "datadir":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				conf.IndexDirectory = c.Val()
			case "template":
				var err error
				if c.NextArg() {
					conf.Template, err = template.ParseFiles(filepath.Join(conf.SiteRoot, c.Val()))
					if err != nil {
						return nil, err
					}
				}
			}
		}
	}

	if len(incPaths) == 0 {
		incPaths = append(incPaths, "^/")
	}

	conf.IncludePaths = convertToRegExp(incPaths)
	conf.ExcludePaths = convertToRegExp(excPaths)

	dir := conf.IndexDirectory
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err := os.MkdirAll(dir, os.ModePerm); err != nil {
			return nil, c.Err("Given 'datadir' not a valid path.")
		}
	}

	if conf.Template == nil {
		var err error
		conf.Template, err = template.New("search-results").Parse(defaultTemplate)
		if err != nil {
			return nil, err
		}
	}

	return conf, nil
}

func convertToRegExp(rexp []string) (r []*regexp.Regexp) {
	r = make([]*regexp.Regexp, 0)
	for _, exp := range rexp {
		var rule *regexp.Regexp
		var err error
		rule, err = regexp.Compile(exp)
		if err != nil {
			continue
		}
		r = append(r, rule)
	}
	return
}

// The default template to use when serving up HTML search results
const defaultTemplate = `<!DOCTYPE html>

		<title>Search results for: {{.Query}}</title>
		<meta charset="utf-8">

Site Search

		<form method="GET" action="{{.URL.Path}}">
			<input type="text" name="q" value="{{.Query}}"> <input type="submit" value="Search">
		</form>

		{{if .Query}}

			Found {{len .Results}} result{{if ne (len .Results) 1}}s{{end}} for {{.Query}}

			{{range .Results}}

{{.Title}}

{{$.Req.Host}}{{.Path}}

				{{.Body}}

			{{end}}

		{{end}}

`
<commit_msg>new setup for caddys new Setup implementation<commit_after>package search

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"text/template"
	"time"

	"github.com/mholt/caddy/config/setup"
	"github.com/mholt/caddy/middleware"
	"github.com/pedronasser/caddy-search/indexer"
	"github.com/pedronasser/caddy-search/indexer/bleve"
)

// Setup creates a new middleware with the given configuration
func Setup(c *setup.Controller) (mid middleware.Middleware, err error) {
	s := &Search{}

	c.OncePerServerBlock(func() error {
		var config *Config

		config, err = parseSearch(c)
		if err != nil {
			return err
		}

		index, err := NewIndexer(config.Engine, indexer.Config{
			HostName:       config.HostName,
			IndexDirectory: config.IndexDirectory,
		})

		if err != nil {
			return err
		}

		ppl, err := NewPipeline(config, index)

		if err != nil {
			return err
		}

		c.Startup = append(c.Startup, func() error {
			return ScanToPipe(c.Root, ppl, index)
		})

		expire := time.NewTicker(config.Expire)

		go func() {
			for {
				select {
				case <-expire.C:
					if !lastScanned.Indexed().IsZero() || lastScanned.Ignored() {
						ScanToPipe(c.Root, ppl, index)
					}
				}
			}
		}()

		s.Config = config
		s.Indexer = index
		s.Pipeline = ppl

		return nil
	})

	mid = func(next middleware.Handler) middleware.Handler {
		return s
	}

	return
}

var lastScanned indexer.Record

// ScanToPipe ...
func ScanToPipe(fp string, pipeline *Pipeline, index indexer.Handler) error {
	filepath.Walk(fp, func(path string, info os.FileInfo, err error) error {
		if info.Name() == "." {
			return nil
		}

		if info.Name() == "" || info.Name()[0] == '.' {
			if info.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}

		if !info.IsDir() {
			reqPath, err := filepath.Rel(fp, path)
			if err != nil {
				return err
			}
			reqPath = "/" + reqPath

			if pipeline.ValidatePath(reqPath) {
				body, err := ioutil.ReadFile(path)
				if err != nil {
					return err
				}

				record := index.Record(reqPath)
				record.SetModified(info.ModTime())
				if len(body) > 0 {
					record.Write(body)
				} else {
					record.Ignore()
				}
				pipeline.Pipe(record)
				lastScanned = record
			}
		}

		return nil
	})

	return nil
}

// NewIndexer creates a new Indexer with the received config
func NewIndexer(engine string, config indexer.Config) (index indexer.Handler, err error) {
	switch engine {
	case "bleve":
		index, err = bleve.New(config)
		break
	default:
		index, err = bleve.New(config)
		break
	}
	return
}

// Config represents this middleware configuration structure
type Config struct {
	HostName       string
	Engine         string
	Path           string
	IncludePaths   []*regexp.Regexp
	ExcludePaths   []*regexp.Regexp
	Endpoint       string
	IndexDirectory string
	Template       *template.Template
	Expire         time.Duration
	SiteRoot       string
}

// parseSearch controller information to create a IndexSearch config
func parseSearch(c *setup.Controller) (*Config, error) {
	conf := &Config{
		HostName:       c.Address(),
		Engine:         `bleve`,
		IndexDirectory: `/tmp/caddyIndex`,
		IncludePaths:   []*regexp.Regexp{},
		ExcludePaths:   []*regexp.Regexp{},
		Endpoint:       `/search`,
		SiteRoot:       c.Root,
		Expire:         60 * time.Second,
		Template:       nil,
	}

	incPaths := []string{}
	excPaths := []string{}

	for c.Next() {
		args := c.RemainingArgs()

		switch len(args) {
		case 2:
			conf.Endpoint = args[1]
			fallthrough
		case 1:
			incPaths = append(incPaths, args[0])
		}

		for c.NextBlock() {
			switch c.Val() {
			case "engine":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				conf.Engine = c.Val()
			case "+path":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				incPaths = append(incPaths, c.Val())
				incPaths = append(incPaths, c.RemainingArgs()...)
			case "-path":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				excPaths = append(excPaths, c.Val())
				excPaths = append(excPaths, c.RemainingArgs()...)
			case "endpoint":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				conf.Endpoint = c.Val()
			case "expire":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				exp, err := strconv.Atoi(c.Val())
				if err != nil {
					return nil, err
				}
				conf.Expire = time.Duration(exp) * time.Second
			case "datadir":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				conf.IndexDirectory = c.Val()
			case "template":
				var err error
				if c.NextArg() {
					conf.Template, err = template.ParseFiles(filepath.Join(conf.SiteRoot, c.Val()))
					if err != nil {
						return nil, err
					}
				}
			}
		}
	}

	if len(incPaths) == 0 {
		incPaths = append(incPaths, "^/")
	}

	conf.IncludePaths = convertToRegExp(incPaths)
	conf.ExcludePaths = convertToRegExp(excPaths)

	dir := conf.IndexDirectory
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err := os.MkdirAll(dir, os.ModePerm); err != nil {
			return nil, c.Err("Given 'datadir' not a valid path.")
		}
	}

	if conf.Template == nil {
		var err error
		conf.Template, err = template.New("search-results").Parse(defaultTemplate)
		if err != nil {
			return nil, err
		}
	}

	return conf, nil
}

func convertToRegExp(rexp []string) (r []*regexp.Regexp) {
	r = make([]*regexp.Regexp, 0)
	for _, exp := range rexp {
		var rule *regexp.Regexp
		var err error
		rule, err = regexp.Compile(exp)
		if err != nil {
			continue
		}
		r = append(r, rule)
	}
	return
}

// The default template to use when serving up HTML search results
const defaultTemplate = `<!DOCTYPE html>

		<title>Search results for: {{.Query}}</title>
		<meta charset="utf-8">

Site Search

		<form method="GET" action="{{.URL.Path}}">
			<input type="text" name="q" value="{{.Query}}"> <input type="submit" value="Search">
		</form>

		{{if .Query}}

			Found {{len .Results}} result{{if ne (len .Results) 1}}s{{end}} for {{.Query}}

			{{range .Results}}

{{.Title}}

{{$.Req.Host}}{{.Path}}

				{{.Body}}

			{{end}}

		{{end}}

`
<|endoftext|>


<commit_before>package fubsy

import (
	"testing"
)

func TestScan_valid(t *testing.T) {
	scanner := NewScanner("nofile", []byte("]  [\"foo!bar\"\n ]"))
	scanner.scan()
	expect := []toktext{
		{"nofile", 1, ']', "]"},
		{"nofile", 1, '[', "["},
		{"nofile", 1, QSTRING, "\"foo!bar\""},
		{"nofile", 2, ']', "]"},
	}
	checkTokens(t, expect, scanner.tokens)
}

func TestScan_inline_1(t *testing.T) {
	scanner := NewScanner(
		"blop.txt", []byte(" \n{{{yo\nhello\nthere\n}}}"))
	scanner.scan()
	expect := []toktext{
		{"blop.txt", 2, L3BRACE, "{{{"},
		{"blop.txt", 2, INLINE, "yo\nhello\nthere\n"},
		{"blop.txt", 5, R3BRACE, "}}}"},
	}
	checkTokens(t, expect, scanner.tokens)
}

func TestScan_inline_2(t *testing.T) {
	// despite appearances, the original motivation for this test case
	// was newline (or indeed anything at all) after }}} -- I just
	// threw a bunch of punctuation into the inline text to be sure
	// that works too
	scanner := NewScanner(
		"blop.txt", []byte("{{{ any!chars\"are\nallowed'here\n}}}\n"))
	scanner.scan()
	expect := []toktext{
		{"blop.txt", 1, L3BRACE, "{{{"},
		{"blop.txt", 1, INLINE, " any!chars\"are\nallowed'here\n"},
		{"blop.txt", 3, R3BRACE, "}}}"},
	}
	checkTokens(t, expect, scanner.tokens)
}

func TestScan_inline_open(t *testing.T) {
	// bad input: unclosed {{{ (should be a syntax error, not an
	// infinite loop!) (hmmm: would be nice to report the trailing
	// inline contents as a BADTOKEN; might give a better syntax error)
	scanner := NewScanner("foo", []byte("] {{{bip\nbop!["))
	scanner.scan()
	expect := []toktext{
		{"foo", 1, ']', "]"},
		{"foo", 1, L3BRACE, "{{{"},
	}
	checkTokens(t, expect, scanner.tokens)

	// same result on incomplete }}}
	scanner = NewScanner("foo", append(scanner.input, "\n}}"...))
	scanner.scan()
	checkTokens(t, expect, scanner.tokens)
}

func TestScan_invalid(t *testing.T) {
	scanner := NewScanner("fwob", []byte("]]\n!-\"whee]\" x whizz\nbang"))
	scanner.scan()
	expect := []toktext{
		{"fwob", 1, ']', "]"},
		{"fwob", 1, ']', "]"},
		{"fwob", 2, BADTOKEN, "!-"},
		{"fwob", 2, QSTRING, "\"whee]\""},
		{"fwob", 2, BADTOKEN, "x"},
		{"fwob", 2, BADTOKEN, "whizz"},
		{"fwob", 3, BADTOKEN, "bang"},
		}
	checkTokens(t, expect, scanner.tokens)
}

func checkTokens(t *testing.T, expect []toktext, actual []toktext) {
	if len(expect) != len(actual) {
		t.Fatalf("expected %d tokens, but got %d",
			len(expect), len(actual))
	}
	for i, etok := range expect {
		atok := actual[i]
		if etok != atok {
			t.Errorf("token %d: expected\n%#v\nbut got\n%#v", i, etok, atok)
		}
	}

}
<commit_msg>lexer: refactor tests<commit_after>package fubsy

import (
	"testing"
)

func TestScan_valid_1(t *testing.T) {
	input := "]  [\"foo!bar\"\n ]"
	expect := []toktext{
		{"nofile", 1, ']', "]"},
		{"nofile", 1, '[', "["},
		{"nofile", 1, QSTRING, "\"foo!bar\""},
		{"nofile", 2, ']', "]"},
	}
	assertScan(t, expect, "nofile", input)
}

func TestScan_inline_1(t *testing.T) {
	input := " \n{{{yo\nhello\nthere\n}}}"
	expect := []toktext{
		{"blop.txt", 2, L3BRACE, "{{{"},
		{"blop.txt", 2, INLINE, "yo\nhello\nthere\n"},
		{"blop.txt", 5, R3BRACE, "}}}"},
	}
	assertScan(t, expect, "blop.txt", input)
}

func TestScan_inline_2(t *testing.T) {
	// despite appearances, the original motivation for this test case
	// was newline (or indeed anything at all) after }}} -- I just
	// threw a bunch of punctuation into the inline text to be sure
	// that works too
	input := "{{{ any!chars\"are\nallowed'here\n}}}\n"
	expect := []toktext{
		{"blop.txt", 1, L3BRACE, "{{{"},
		{"blop.txt", 1, INLINE, " any!chars\"are\nallowed'here\n"},
		{"blop.txt", 3, R3BRACE, "}}}"},
	}
	assertScan(t, expect, "blop.txt", input)
}

func TestScan_inline_open(t *testing.T) {
	// bad input: unclosed {{{ (should be a syntax error, not an
	// infinite loop!) (hmmm: would be nice to report the trailing
	// inline contents as a BADTOKEN; might give a better syntax error)
	input := "] {{{bip\nbop!["
	expect := []toktext{
		{"foo", 1, ']', "]"},
		{"foo", 1, L3BRACE, "{{{"},
	}
	assertScan(t, expect, "foo", input)

	// same result on incomplete }}}
	input += "\n}}"
	assertScan(t, expect, "foo", input)
}

func TestScan_invalid(t *testing.T) {
	input := "]]\n!-\"whee]\" x whizz\nbang"
	expect := []toktext{
		{"fwob", 1, ']', "]"},
		{"fwob", 1, ']', "]"},
		{"fwob", 2, BADTOKEN, "!-"},
		{"fwob", 2, QSTRING, "\"whee]\""},
		{"fwob", 2, BADTOKEN, "x"},
		{"fwob", 2, BADTOKEN, "whizz"},
		{"fwob", 3, BADTOKEN, "bang"},
		}
	assertScan(t, expect, "fwob", input)
}

func assertScan(t *testing.T, expect []toktext, filename string, input string) {
	scanner := NewScanner(filename, []byte(input))
	scanner.scan()
	assertTokens(t, expect, scanner.tokens)
}

func assertTokens(t *testing.T, expect []toktext, actual []toktext) {
	if len(expect) != len(actual) {
		t.Fatalf("expected %d tokens, but got %d",
			len(expect), len(actual))
	}
	for i, etok := range expect {
		atok := actual[i]
		if etok != atok {
			t.Errorf("token %d: expected\n%#v\nbut got\n%#v", i, etok, atok)
		}
	}

}
<|endoftext|>


<commit_before>package git_comment

import (
	"errors"
	"fmt"
	git "gopkg.in/libgit2/git2go.v22"
	"path"
	"time"
)

const (
	authorNotFoundError  = "No name or email found in git config for commenting"
	commitNotFoundError  = "Commit not found"
	commentNotFoundError = "Comment not found"
	headCommit           = "HEAD"
	defaultMessageFormat = "Created a comment ref on [%v] to [%v]"
)

// Create a new comment on a commit, optionally with a file and line
func CreateComment(repoPath string, commit *string, fileRef *FileRef, message string) (*string, error) {
	repo, err := repo(repoPath)
	if err != nil {
		return nil, err
	}
	author, err := author(repo)
	if err != nil {
		return nil, err
	}
	hash, err := parseCommit(repo, commit)
	if err != nil {
		return nil, err
	}
	comment, err := NewComment(message, *hash, fileRef, author)
	if err != nil {
		return nil, err
	}
	if err := writeCommentToDisk(repo, comment); err != nil {
		return nil, err
	}

	return comment.ID, nil
}

// Update an existing comment with a new message
func UpdateComment(repoPath string, ID string, message string) (*string, error) {
	repo, err := repo(repoPath)
	if err != nil {
		return nil, err
	}
	comment, err := CommentByID(repo, ID)
	if err != nil {
		return nil, err
	}
	author, err := author(repo)
	if err != nil {
		return nil, err
	}
	comment.Amend(message, author)
	if err := writeCommentToDisk(repo, comment); err != nil {
		return nil, err
	}

	return comment.ID, nil
}

// Remove a comment from a commit
func DeleteComment(repoPath string, ID string) error {
	repo, err := repo(repoPath)
	if err != nil {
		return err
	}
	comment, err := CommentByID(repo, ID)
	if err != nil {
		return err
	}
	comment.Deleted = true
	return writeCommentToDisk(repo, comment)
}

// Finds a comment by a given ID
func CommentByID(repo *git.Repository, identifier string) (*Comment, error) {
	return &Comment{}, errors.New(commentNotFoundError)
}

// Finds all comments on a given commit
func CommentsOnCommit(repoPath string, commit string) []*Comment {
	return []*Comment{}
}

// Configure a remote to fetch and push comment changes by default
func ConfigureRemoteForComments(repoPath string, remoteName string) error {
	const (
		commentDefaultFetch = "+refs/comments/*:refs/remotes/%v/comments/*"
		commentDefaultPush  = "refs/comments/*"
	)
	repo, err := repo(repoPath)
	if err != nil {
		return err
	}
	remote, err := repo.LookupRemote(remoteName)
	if err != nil {
		return err
	}
	fetch := fmt.Sprintf(commentDefaultFetch, remoteName)
	fetches, err := remote.FetchRefspecs()
	if err != nil {
		return err
	}
	if !contains(fetches, fetch) {
		err = remote.AddFetch(fetch)
		if err != nil {
			return err
		}
	}
	pushes, err := remote.PushRefspecs()
	if err != nil {
		return err
	}
	if !contains(pushes, commentDefaultPush) {
		err = remote.AddPush(commentDefaultPush)
		if err != nil {
			return err
		}
	}
	err = remote.Save()
	if err != nil {
		return err
	}
	return nil
}

// Write git object for a given comment and update the
// comment refs
func writeCommentToDisk(repo *git.Repository, comment *Comment) error {
	oid, err := repo.CreateBlobFromBuffer([]byte(comment.Serialize()))
	if err != nil {
		return err
	}
	committer := comment.Amender
	sig := &git.Signature{committer.Name, committer.Email, time.Now()}
	id := fmt.Sprintf("%v", oid)
	file, err := refPath(comment, &id)
	if err != nil {
		return err
	}
	commit := *comment.Commit
	message := fmt.Sprintf(defaultMessageFormat, commit[:7], id[:7])
	_, err = repo.CreateReference(*file, oid, false, sig, message)
	if err != nil {
		return err
	}
	comment.ID = &id
	return nil
}

func contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// Generate the path within refs for a given comment
//
// Comment refs are nested under refs/comments. The
// format is as follows:
//
// ```
// refs/comments/[<commit prefix>]/[<rest of commit>]/[<comment id>]
// ```
//
func refPath(comment *Comment, id *string) (*string, error) {
	dir, err := commitRefDir(comment.Commit)
	if err != nil {
		return nil, err
	}
	hash := path.Join(*dir, *id)
	return &hash, nil
}

func commitRefDir(commit *string) (*string, error) {
	const invalidHash = "Invalid commit hash for storage"
	const commentPath = "refs/comments"
	hash := *commit
	if len(hash) > 4 {
		dir := path.Join(commentPath,
			hash[:4],
			hash[4:len(hash)])
		return &dir, nil
	}
	return nil, errors.New(invalidHash)
}

func repo(repoPath string) (*git.Repository, error) {
	return git.OpenRepository(repoPath)
}

func author(repo *git.Repository) (*Person, error) {
	sig, err := repo.DefaultSignature()
	if err != nil {
		return nil, err
	}
	return &Person{sig.Name, sig.Email}, nil
}

// parse a commit hash, converting to the HEAD commit where needed
func parseCommit(repo *git.Repository, commit *string) (*string, error) {
	var hash string
	var id string
	if commit == nil {
		hash = headCommit
	} else {
		hash = *commit
	}
	ref, err := repo.LookupReference(hash)
	if err != nil {
		oid, err := git.NewOid(hash)
		if err != nil {
			return nil, errors.New(commitNotFoundError)
		}
		obj, err := repo.Lookup(oid)
		if err != nil {
			return nil, errors.New(commitNotFoundError)
		}
		id = obj.Id().String()
		return nil, errors.New(commitNotFoundError)
	}
	res, err := ref.Resolve()
	if err != nil {
		return nil, err
	}
	id = res.Target().String()
	return &id, nil
}

func head(repo *git.Repository) (*string, error) {
	head, hErr := repo.Head()
	if hErr != nil {
		return nil, hErr
	}
	hash := head.Name()
	return &hash, nil
}
<commit_msg>Implement comment fetching from git/objects<commit_after>package git_comment

import (
	"errors"
	"fmt"
	git "gopkg.in/libgit2/git2go.v22"
	"path"
	"time"
)

const (
	authorNotFoundError  = "No name or email found in git config for commenting"
	invalidHashError     = "Invalid commit hash for storage"
	commitNotFoundError  = "Commit not found"
	commentNotFoundError = "Comment not found"
	headCommit           = "HEAD"
	defaultMessageFormat = "Created a comment ref on [%v] to [%v]"
)

// Create a new comment on a commit, optionally with a file and line
func CreateComment(repoPath string, commit *string, fileRef *FileRef, message string) (*string, error) {
	repo, err := repo(repoPath)
	if err != nil {
		return nil, err
	}
	author, err := author(repo)
	if err != nil {
		return nil, err
	}
	hash, err := parseCommit(repo, commit)
	if err != nil {
		return nil, err
	}
	comment, err := NewComment(message, *hash, fileRef, author)
	if err != nil {
		return nil, err
	}
	if err := writeCommentToDisk(repo, comment); err != nil {
		return nil, err
	}

	return comment.ID, nil
}

// Update an existing comment with a new message
func UpdateComment(repoPath string, ID string, message string) (*string, error) {
	repo, err := repo(repoPath)
	if err != nil {
		return nil, err
	}
	comment, err := CommentByID(repo, ID)
	if err != nil {
		return nil, err
	}
	author, err := author(repo)
	if err != nil {
		return nil, err
	}
	comment.Amend(message, author)
	if err := writeCommentToDisk(repo, comment); err != nil {
		return nil, err
	}

	return comment.ID, nil
}

// Remove a comment from a commit
func DeleteComment(repoPath string, ID string) error {
	repo, err := repo(repoPath)
	if err != nil {
		return err
	}
	comment, err := CommentByID(repo, ID)
	if err != nil {
		return err
	}
	comment.Deleted = true
	return writeCommentToDisk(repo, comment)
}

// Finds a comment by a given ID
func CommentByID(repo *git.Repository, identifier string) (*Comment, error) {
	oid, err := git.NewOid(identifier)
	if err != nil {
		return nil, errors.New(commentNotFoundError)
	}
	blob, err := repo.LookupBlob(oid)
	if err != nil {
		return nil, errors.New(commentNotFoundError)
	}
	comment, err := DeserializeComment(string(blob.Contents()))
	if err != nil {
		return nil, err
	}
	comment.ID = &identifier
	return comment, nil
}

// Finds all comments on a given commit
func CommentsOnCommit(repoPath string, commit string) []*Comment {
	return []*Comment{}
}

// Configure a remote to fetch and push comment changes by default
func ConfigureRemoteForComments(repoPath string, remoteName string) error {
	const (
		commentDefaultFetch = "+refs/comments/*:refs/remotes/%v/comments/*"
		commentDefaultPush  = "refs/comments/*"
	)
	repo, err := repo(repoPath)
	if err != nil {
		return err
	}
	remote, err := repo.LookupRemote(remoteName)
	if err != nil {
		return err
	}
	fetch := fmt.Sprintf(commentDefaultFetch, remoteName)
	fetches, err := remote.FetchRefspecs()
	if err != nil {
		return err
	}
	if !contains(fetches, fetch) {
		err = remote.AddFetch(fetch)
		if err != nil {
			return err
		}
	}
	pushes, err := remote.PushRefspecs()
	if err != nil {
		return err
	}
	if !contains(pushes, commentDefaultPush) {
		err = remote.AddPush(commentDefaultPush)
		if err != nil {
			return err
		}
	}
	err = remote.Save()
	if err != nil {
		return err
	}
	return nil
}

// Write git object for a given comment and update the
// comment refs
func writeCommentToDisk(repo *git.Repository, comment *Comment) error {
	if comment.ID != nil {
		err := deleteReference(repo, comment, *comment.ID)
		if err != nil {
			return err
		}
	}
	oid, err := repo.CreateBlobFromBuffer([]byte(comment.Serialize()))
	if err != nil {
		return err
	}
	committer := comment.Amender
	sig := &git.Signature{committer.Name, committer.Email, time.Now()}
	id := fmt.Sprintf("%v", oid)
	file, err := refPath(comment, &id)
	if err != nil {
		return err
	}
	commit := *comment.Commit
	message := fmt.Sprintf(defaultMessageFormat, commit[:7], id[:7])
	_, err = repo.CreateReference(*file, oid, false, sig, message)
	if err != nil {
		return err
	}
	comment.ID = &id
	return nil
}

func contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

func deleteReference(repo *git.Repository, comment *Comment, identifier string) error {
	refPath, err := refPath(comment, &identifier)
	if err != nil {
		return nil
	}
	ref, err := repo.LookupReference(*refPath)
	if err != nil {
		return errors.New(commentNotFoundError)
	}
	err = ref.Delete()
	if err != nil {
		return err
	}
	return nil
}

// Generate the path within refs for a given comment
//
// Comment refs are nested under refs/comments. The
// format is as follows:
//
// ```
// refs/comments/[<commit prefix>]/[<rest of commit>]/[<comment id>]
// ```
//
func refPath(comment *Comment, id *string) (*string, error) {
	dir, err := commitRefDir(comment.Commit)
	if err != nil {
		return nil, err
	}
	hash := path.Join(*dir, *id)
	return &hash, nil
}

func commitRefDir(commit *string) (*string, error) {
	const commentPath = "refs/comments"
	hash := *commit
	if len(hash) > 4 {
		dir := path.Join(commentPath,
			hash[:4],
			hash[4:len(hash)])
		return &dir, nil
	}
	return nil, errors.New(invalidHashError)
}

func repo(repoPath string) (*git.Repository, error) {
	return git.OpenRepository(repoPath)
}

func author(repo *git.Repository) (*Person, error) {
	sig, err := repo.DefaultSignature()
	if err != nil {
		return nil, err
	}
	return &Person{sig.Name, sig.Email}, nil
}

// parse a commit hash, converting to the HEAD commit where needed
func parseCommit(repo *git.Repository, commit *string) (*string, error) {
	var hash string
	var id string
	if commit == nil {
		hash = headCommit
	} else {
		hash = *commit
	}
	ref, err := repo.LookupReference(hash)
	if err != nil {
		oid, err := git.NewOid(hash)
		if err != nil {
			return nil, errors.New(commitNotFoundError)
		}
		obj, err := repo.Lookup(oid)
		if err != nil {
			return nil, errors.New(commitNotFoundError)
		}
		id = obj.Id().String()
		return nil, errors.New(commitNotFoundError)
	}
	res, err := ref.Resolve()
	if err != nil {
		return nil, err
	}
	id = res.Target().String()
	return &id, nil
}

func head(repo *git.Repository) (*string, error) {
	head, hErr := repo.Head()
	if hErr != nil {
		return nil, hErr
	}
	hash := head.Name()
	return &hash, nil
}
<|endoftext|>


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

package bytes

// Simple byte buffer for marshaling data.

import (
	"errors"
	"io"
	"unicode/utf8"
)

// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	buf       []byte            // contents are the bytes buf[off : len(buf)]
	off       int               // read at &buf[off], write at &buf[len(buf)]
	runeBytes [utf8.UTFMax]byte // avoid allocation of slice on each WriteByte or Rune
	bootstrap [64]byte          // memory to hold first slice; helps small buffers (Printf) avoid allocation.
	lastRead  readOp            // last read operation, so that Unread* can work correctly.
}

// The readOp constants describe the last action performed on
// the buffer, so that UnreadRune and UnreadByte can
// check for invalid usage.
type readOp int

const (
	opInvalid  readOp = iota // Non-read operation.
	opReadRune               // Read rune.
	opRead                   // Any other read operation.
)

// ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer.
var ErrTooLarge = errors.New("bytes.Buffer: too large")

// Bytes returns a slice of the contents of the unread portion of the buffer;
// len(b.Bytes()) == b.Len().  If the caller changes the contents of the
// returned slice, the contents of the buffer will change provided there
// are no intervening method calls on the Buffer.
func (b *Buffer) Bytes() []byte { return b.buf[b.off:] }

// String returns the contents of the unread portion of the buffer
// as a string.  If the Buffer is a nil pointer, it returns "<nil>".
func (b *Buffer) String() string {
	if b == nil {
		// Special case, useful in debugging.
		return "<nil>"
	}
	return string(b.buf[b.off:])
}

// Len returns the number of bytes of the unread portion of the buffer;
// b.Len() == len(b.Bytes()).
func (b *Buffer) Len() int { return len(b.buf) - b.off }

// Truncate discards all but the first n unread bytes from the buffer.
// It is an error to call b.Truncate(n) with n > b.Len().
func (b *Buffer) Truncate(n int) {
	b.lastRead = opInvalid
	if n == 0 {
		// Reuse buffer space.
		b.off = 0
	}
	b.buf = b.buf[0 : b.off+n]
}

// Reset resets the buffer so it has no content.
// b.Reset() is the same as b.Truncate(0).
func (b *Buffer) Reset() { b.Truncate(0) }

// grow grows the buffer to guarantee space for n more bytes.
// It returns the index where bytes should be written.
// If the buffer can't grow it will panic with ErrTooLarge.
func (b *Buffer) grow(n int) int {
	m := b.Len()
	// If buffer is empty, reset to recover space.
	if m == 0 && b.off != 0 {
		b.Truncate(0)
	}
	if len(b.buf)+n > cap(b.buf) {
		var buf []byte
		if b.buf == nil && n <= len(b.bootstrap) {
			buf = b.bootstrap[0:]
		} else {
			// not enough space anywhere
			buf = makeSlice(2*cap(b.buf) + n)
			copy(buf, b.buf[b.off:])
		}
		b.buf = buf
		b.off = 0
	}
	b.buf = b.buf[0 : b.off+m+n]
	return b.off + m
}

// Write appends the contents of p to the buffer.  The return
// value n is the length of p; err is always nil.
// If the buffer becomes too large, Write will panic with
// ErrTooLarge.
func (b *Buffer) Write(p []byte) (n int, err error) {
	b.lastRead = opInvalid
	m := b.grow(len(p))
	if m < 0 {
		return 0, ErrTooLarge
	}
	return copy(b.buf[m:], p), nil
}

// WriteString appends the contents of s to the buffer.  The return
// value n is the length of s; err is always nil.
func (b *Buffer) WriteString(s string) (n int, err error) {
	b.lastRead = opInvalid
	m := b.grow(len(s))
	if m < 0 {
		return 0, ErrTooLarge
	}
	return copy(b.buf[m:], s), nil
}

// MinRead is the minimum slice size passed to a Read call by
// Buffer.ReadFrom.  As long as the Buffer has at least MinRead bytes beyond
// what is required to hold the contents of r, ReadFrom will not grow the
// underlying buffer.
const MinRead = 512

// ReadFrom reads data from r until EOF and appends it to the buffer.
// The return value n is the number of bytes read.
// Any error except io.EOF encountered during the read
// is also returned.
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
	b.lastRead = opInvalid
	// If buffer is empty, reset to recover space.
	if b.off >= len(b.buf) {
		b.Truncate(0)
	}
	for {
		if free := cap(b.buf) - len(b.buf); free < MinRead {
			// not enough space at end
			newBuf := b.buf
			if b.off+free < MinRead {
				// not enough space using beginning of buffer;
				// double buffer capacity
				newBuf = makeSlice(2*cap(b.buf) + MinRead)
			}
			copy(newBuf, b.buf[b.off:])
			b.buf = newBuf[:len(b.buf)-b.off]
			b.off = 0
		}
		m, e := r.Read(b.buf[len(b.buf):cap(b.buf)])
		b.buf = b.buf[0 : len(b.buf)+m]
		n += int64(m)
		if e == io.EOF {
			break
		}
		if e != nil {
			return n, e
		}
	}
	return n, nil // err is EOF, so return nil explicitly
}

// makeSlice allocates a slice of size n. If the allocation fails, it panics
// with ErrTooLarge.
func makeSlice(n int) []byte {
	// If the make fails, give a known error.
	defer func() {
		if recover() != nil {
			panic(ErrTooLarge)
		}
	}()
	return make([]byte, n)
}

// WriteTo writes data to w until the buffer is drained or an error
// occurs. The return value n is the number of bytes written; it always
// fits into an int, but it is int64 to match the io.WriterTo interface.
// Any error encountered during the write is also returned.
func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
	b.lastRead = opInvalid
	if b.off < len(b.buf) {
		m, e := w.Write(b.buf[b.off:])
		b.off += m
		n = int64(m)
		if e != nil {
			return n, e
		}
		// otherwise all bytes were written, by definition of
		// Write method in io.Writer
	}
	// Buffer is now empty; reset.
	b.Truncate(0)
	return
}

// WriteByte appends the byte c to the buffer.
// The returned error is always nil, but is included
// to match bufio.Writer's WriteByte.
func (b *Buffer) WriteByte(c byte) error {
	b.lastRead = opInvalid
	m := b.grow(1)
	if m < 0 {
		return ErrTooLarge
	}
	b.buf[m] = c
	return nil
}

// WriteRune appends the UTF-8 encoding of Unicode
// code point r to the buffer, returning its length and
// an error, which is always nil but is included
// to match bufio.Writer's WriteRune.
func (b *Buffer) WriteRune(r rune) (n int, err error) {
	if r < utf8.RuneSelf {
		b.WriteByte(byte(r))
		return 1, nil
	}
	n = utf8.EncodeRune(b.runeBytes[0:], r)
	b.Write(b.runeBytes[0:n])
	return n, nil
}

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained.  The return value n is the number of bytes read.  If the
// buffer has no data to return, err is io.EOF (unless len(p) is zero);
// otherwise it is nil.
func (b *Buffer) Read(p []byte) (n int, err error) {
	b.lastRead = opInvalid
	if b.off >= len(b.buf) {
		// Buffer is empty, reset to recover space.
		b.Truncate(0)
		if len(p) == 0 {
			return
		}
		return 0, io.EOF
	}
	n = copy(p, b.buf[b.off:])
	b.off += n
	if n > 0 {
		b.lastRead = opRead
	}
	return
}

// Next returns a slice containing the next n bytes from the buffer,
// advancing the buffer as if the bytes had been returned by Read.
// If there are fewer than n bytes in the buffer, Next returns the entire buffer.
// The slice is only valid until the next call to a read or write method.
func (b *Buffer) Next(n int) []byte {
	b.lastRead = opInvalid
	m := b.Len()
	if n > m {
		n = m
	}
	data := b.buf[b.off : b.off+n]
	b.off += n
	if n > 0 {
		b.lastRead = opRead
	}
	return data
}

// ReadByte reads and returns the next byte from the buffer.
// If no byte is available, it returns error io.EOF.
func (b *Buffer) ReadByte() (c byte, err error) {
	b.lastRead = opInvalid
	if b.off >= len(b.buf) {
		// Buffer is empty, reset to recover space.
		b.Truncate(0)
		return 0, io.EOF
	}
	c = b.buf[b.off]
	b.off++
	b.lastRead = opRead
	return c, nil
}

// ReadRune reads and returns the next UTF-8-encoded
// Unicode code point from the buffer.
// If no bytes are available, the error returned is io.EOF.
// If the bytes are an erroneous UTF-8 encoding, it
// consumes one byte and returns U+FFFD, 1.
func (b *Buffer) ReadRune() (r rune, size int, err error) {
	b.lastRead = opInvalid
	if b.off >= len(b.buf) {
		// Buffer is empty, reset to recover space.
		b.Truncate(0)
		return 0, 0, io.EOF
	}
	b.lastRead = opReadRune
	c := b.buf[b.off]
	if c < utf8.RuneSelf {
		b.off++
		return rune(c), 1, nil
	}
	r, n := utf8.DecodeRune(b.buf[b.off:])
	b.off += n
	return r, n, nil
}

// UnreadRune unreads the last rune returned by ReadRune.
// If the most recent read or write operation on the buffer was
// not a ReadRune, UnreadRune returns an error.  (In this regard
// it is stricter than UnreadByte, which will unread the last byte
// from any read operation.)
func (b *Buffer) UnreadRune() error {
	if b.lastRead != opReadRune {
		return errors.New("bytes.Buffer: UnreadRune: previous operation was not ReadRune")
	}
	b.lastRead = opInvalid
	if b.off > 0 {
		_, n := utf8.DecodeLastRune(b.buf[0:b.off])
		b.off -= n
	}
	return nil
}

// UnreadByte unreads the last byte returned by the most recent
// read operation.  If write has happened since the last read, UnreadByte
// returns an error.
func (b *Buffer) UnreadByte() error {
	if b.lastRead != opReadRune && b.lastRead != opRead {
		return errors.New("bytes.Buffer: UnreadByte: previous operation was not a read")
	}
	b.lastRead = opInvalid
	if b.off > 0 {
		b.off--
	}
	return nil
}

// ReadBytes reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and including the delimiter.
// If ReadBytes encounters an error before finding a delimiter,
// it returns the data read before the error and the error itself (often io.EOF).
// ReadBytes returns err != nil if and only if the returned data does not end in
// delim.
func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
	i := IndexByte(b.buf[b.off:], delim)
	size := i + 1
	if i < 0 {
		size = len(b.buf) - b.off
		err = io.EOF
	}
	line = make([]byte, size)
	copy(line, b.buf[b.off:])
	b.off += size
	return
}

// ReadString reads until the first occurrence of delim in the input,
// returning a string containing the data up to and including the delimiter.
// If ReadString encounters an error before finding a delimiter,
// it returns the data read before the error and the error itself (often io.EOF).
// ReadString returns err != nil if and only if the returned data does not end
// in delim.
func (b *Buffer) ReadString(delim byte) (line string, err error) {
	bytes, err := b.ReadBytes(delim)
	return string(bytes), err
}

// NewBuffer creates and initializes a new Buffer using buf as its initial
// contents.  It is intended to prepare a Buffer to read existing data.  It
// can also be used to size the internal buffer for writing. To do that,
// buf should have the desired capacity but a length of zero.
//
// In most cases, new(Buffer) (or just declaring a Buffer variable) is
// preferable to NewBuffer.  In particular, passing a non-empty buf to
// NewBuffer and then writing to the Buffer will overwrite buf, not append to
// it.
func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} }

// NewBufferString creates and initializes a new Buffer using string s as its
// initial contents.  It is intended to prepare a buffer to read an existing
// string.  See the warnings about NewBuffer; similar issues apply here.
func NewBufferString(s string) *Buffer {
	return &Buffer{buf: []byte(s)}
}
<commit_msg>bytes.Buffer: remove dead code, complete documentation<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bytes

// Simple byte buffer for marshaling data.

import (
	"errors"
	"io"
	"unicode/utf8"
)

// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
	buf       []byte            // contents are the bytes buf[off : len(buf)]
	off       int               // read at &buf[off], write at &buf[len(buf)]
	runeBytes [utf8.UTFMax]byte // avoid allocation of slice on each WriteByte or Rune
	bootstrap [64]byte          // memory to hold first slice; helps small buffers (Printf) avoid allocation.
	lastRead  readOp            // last read operation, so that Unread* can work correctly.
}

// The readOp constants describe the last action performed on
// the buffer, so that UnreadRune and UnreadByte can
// check for invalid usage.
type readOp int

const (
	opInvalid  readOp = iota // Non-read operation.
	opReadRune               // Read rune.
	opRead                   // Any other read operation.
)

// ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer.
var ErrTooLarge = errors.New("bytes.Buffer: too large")

// Bytes returns a slice of the contents of the unread portion of the buffer;
// len(b.Bytes()) == b.Len().  If the caller changes the contents of the
// returned slice, the contents of the buffer will change provided there
// are no intervening method calls on the Buffer.
func (b *Buffer) Bytes() []byte { return b.buf[b.off:] }

// String returns the contents of the unread portion of the buffer
// as a string.  If the Buffer is a nil pointer, it returns "<nil>".
func (b *Buffer) String() string {
	if b == nil {
		// Special case, useful in debugging.
		return "<nil>"
	}
	return string(b.buf[b.off:])
}

// Len returns the number of bytes of the unread portion of the buffer;
// b.Len() == len(b.Bytes()).
func (b *Buffer) Len() int { return len(b.buf) - b.off }

// Truncate discards all but the first n unread bytes from the buffer.
// It is an error to call b.Truncate(n) with n > b.Len().
func (b *Buffer) Truncate(n int) {
	b.lastRead = opInvalid
	if n == 0 {
		// Reuse buffer space.
		b.off = 0
	}
	b.buf = b.buf[0 : b.off+n]
}

// Reset resets the buffer so it has no content.
// b.Reset() is the same as b.Truncate(0).
func (b *Buffer) Reset() { b.Truncate(0) }

// grow grows the buffer to guarantee space for n more bytes.
// It returns the index where bytes should be written.
// If the buffer can't grow it will panic with ErrTooLarge.
func (b *Buffer) grow(n int) int {
	m := b.Len()
	// If buffer is empty, reset to recover space.
	if m == 0 && b.off != 0 {
		b.Truncate(0)
	}
	if len(b.buf)+n > cap(b.buf) {
		var buf []byte
		if b.buf == nil && n <= len(b.bootstrap) {
			buf = b.bootstrap[0:]
		} else {
			// not enough space anywhere
			buf = makeSlice(2*cap(b.buf) + n)
			copy(buf, b.buf[b.off:])
		}
		b.buf = buf
		b.off = 0
	}
	b.buf = b.buf[0 : b.off+m+n]
	return b.off + m
}

// Write appends the contents of p to the buffer.  The return
// value n is the length of p; err is always nil.
// If the buffer becomes too large, Write will panic with
// ErrTooLarge.
func (b *Buffer) Write(p []byte) (n int, err error) {
	b.lastRead = opInvalid
	m := b.grow(len(p))
	return copy(b.buf[m:], p), nil
}

// WriteString appends the contents of s to the buffer.  The return
// value n is the length of s; err is always nil.
// If the buffer becomes too large, WriteString will panic with
// ErrTooLarge.
func (b *Buffer) WriteString(s string) (n int, err error) {
	b.lastRead = opInvalid
	m := b.grow(len(s))
	return copy(b.buf[m:], s), nil
}

// MinRead is the minimum slice size passed to a Read call by
// Buffer.ReadFrom.  As long as the Buffer has at least MinRead bytes beyond
// what is required to hold the contents of r, ReadFrom will not grow the
// underlying buffer.
const MinRead = 512

// ReadFrom reads data from r until EOF and appends it to the buffer.
// The return value n is the number of bytes read.
// Any error except io.EOF encountered during the read
// is also returned.
// If the buffer becomes too large, ReadFrom will panic with
// ErrTooLarge.
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
	b.lastRead = opInvalid
	// If buffer is empty, reset to recover space.
	if b.off >= len(b.buf) {
		b.Truncate(0)
	}
	for {
		if free := cap(b.buf) - len(b.buf); free < MinRead {
			// not enough space at end
			newBuf := b.buf
			if b.off+free < MinRead {
				// not enough space using beginning of buffer;
				// double buffer capacity
				newBuf = makeSlice(2*cap(b.buf) + MinRead)
			}
			copy(newBuf, b.buf[b.off:])
			b.buf = newBuf[:len(b.buf)-b.off]
			b.off = 0
		}
		m, e := r.Read(b.buf[len(b.buf):cap(b.buf)])
		b.buf = b.buf[0 : len(b.buf)+m]
		n += int64(m)
		if e == io.EOF {
			break
		}
		if e != nil {
			return n, e
		}
	}
	return n, nil // err is EOF, so return nil explicitly
}

// makeSlice allocates a slice of size n. If the allocation fails, it panics
// with ErrTooLarge.
func makeSlice(n int) []byte {
	// If the make fails, give a known error.
	defer func() {
		if recover() != nil {
			panic(ErrTooLarge)
		}
	}()
	return make([]byte, n)
}

// WriteTo writes data to w until the buffer is drained or an error
// occurs. The return value n is the number of bytes written; it always
// fits into an int, but it is int64 to match the io.WriterTo interface.
// Any error encountered during the write is also returned.
func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
	b.lastRead = opInvalid
	if b.off < len(b.buf) {
		m, e := w.Write(b.buf[b.off:])
		b.off += m
		n = int64(m)
		if e != nil {
			return n, e
		}
		// otherwise all bytes were written, by definition of
		// Write method in io.Writer
	}
	// Buffer is now empty; reset.
	b.Truncate(0)
	return
}

// WriteByte appends the byte c to the buffer.
// The returned error is always nil, but is included
// to match bufio.Writer's WriteByte.
// If the buffer becomes too large, WriteByte will panic with
// ErrTooLarge.
func (b *Buffer) WriteByte(c byte) error {
	b.lastRead = opInvalid
	m := b.grow(1)
	b.buf[m] = c
	return nil
}

// WriteRune appends the UTF-8 encoding of Unicode
// code point r to the buffer, returning its length and
// an error, which is always nil but is included
// to match bufio.Writer's WriteRune.
// If the buffer becomes too large, WriteRune will panic with
// ErrTooLarge.
func (b *Buffer) WriteRune(r rune) (n int, err error) {
	if r < utf8.RuneSelf {
		b.WriteByte(byte(r))
		return 1, nil
	}
	n = utf8.EncodeRune(b.runeBytes[0:], r)
	b.Write(b.runeBytes[0:n])
	return n, nil
}

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained.  The return value n is the number of bytes read.  If the
// buffer has no data to return, err is io.EOF (unless len(p) is zero);
// otherwise it is nil.
func (b *Buffer) Read(p []byte) (n int, err error) {
	b.lastRead = opInvalid
	if b.off >= len(b.buf) {
		// Buffer is empty, reset to recover space.
		b.Truncate(0)
		if len(p) == 0 {
			return
		}
		return 0, io.EOF
	}
	n = copy(p, b.buf[b.off:])
	b.off += n
	if n > 0 {
		b.lastRead = opRead
	}
	return
}

// Next returns a slice containing the next n bytes from the buffer,
// advancing the buffer as if the bytes had been returned by Read.
// If there are fewer than n bytes in the buffer, Next returns the entire buffer.
// The slice is only valid until the next call to a read or write method.
func (b *Buffer) Next(n int) []byte {
	b.lastRead = opInvalid
	m := b.Len()
	if n > m {
		n = m
	}
	data := b.buf[b.off : b.off+n]
	b.off += n
	if n > 0 {
		b.lastRead = opRead
	}
	return data
}

// ReadByte reads and returns the next byte from the buffer.
// If no byte is available, it returns error io.EOF.
func (b *Buffer) ReadByte() (c byte, err error) {
	b.lastRead = opInvalid
	if b.off >= len(b.buf) {
		// Buffer is empty, reset to recover space.
		b.Truncate(0)
		return 0, io.EOF
	}
	c = b.buf[b.off]
	b.off++
	b.lastRead = opRead
	return c, nil
}

// ReadRune reads and returns the next UTF-8-encoded
// Unicode code point from the buffer.
// If no bytes are available, the error returned is io.EOF.
// If the bytes are an erroneous UTF-8 encoding, it
// consumes one byte and returns U+FFFD, 1.
func (b *Buffer) ReadRune() (r rune, size int, err error) {
	b.lastRead = opInvalid
	if b.off >= len(b.buf) {
		// Buffer is empty, reset to recover space.
		b.Truncate(0)
		return 0, 0, io.EOF
	}
	b.lastRead = opReadRune
	c := b.buf[b.off]
	if c < utf8.RuneSelf {
		b.off++
		return rune(c), 1, nil
	}
	r, n := utf8.DecodeRune(b.buf[b.off:])
	b.off += n
	return r, n, nil
}

// UnreadRune unreads the last rune returned by ReadRune.
// If the most recent read or write operation on the buffer was
// not a ReadRune, UnreadRune returns an error.  (In this regard
// it is stricter than UnreadByte, which will unread the last byte
// from any read operation.)
func (b *Buffer) UnreadRune() error {
	if b.lastRead != opReadRune {
		return errors.New("bytes.Buffer: UnreadRune: previous operation was not ReadRune")
	}
	b.lastRead = opInvalid
	if b.off > 0 {
		_, n := utf8.DecodeLastRune(b.buf[0:b.off])
		b.off -= n
	}
	return nil
}

// UnreadByte unreads the last byte returned by the most recent
// read operation.  If write has happened since the last read, UnreadByte
// returns an error.
func (b *Buffer) UnreadByte() error {
	if b.lastRead != opReadRune && b.lastRead != opRead {
		return errors.New("bytes.Buffer: UnreadByte: previous operation was not a read")
	}
	b.lastRead = opInvalid
	if b.off > 0 {
		b.off--
	}
	return nil
}

// ReadBytes reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and including the delimiter.
// If ReadBytes encounters an error before finding a delimiter,
// it returns the data read before the error and the error itself (often io.EOF).
// ReadBytes returns err != nil if and only if the returned data does not end in
// delim.
func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
	i := IndexByte(b.buf[b.off:], delim)
	size := i + 1
	if i < 0 {
		size = len(b.buf) - b.off
		err = io.EOF
	}
	line = make([]byte, size)
	copy(line, b.buf[b.off:])
	b.off += size
	return
}

// ReadString reads until the first occurrence of delim in the input,
// returning a string containing the data up to and including the delimiter.
// If ReadString encounters an error before finding a delimiter,
// it returns the data read before the error and the error itself (often io.EOF).
// ReadString returns err != nil if and only if the returned data does not end
// in delim.
func (b *Buffer) ReadString(delim byte) (line string, err error) {
	bytes, err := b.ReadBytes(delim)
	return string(bytes), err
}

// NewBuffer creates and initializes a new Buffer using buf as its initial
// contents.  It is intended to prepare a Buffer to read existing data.  It
// can also be used to size the internal buffer for writing. To do that,
// buf should have the desired capacity but a length of zero.
//
// In most cases, new(Buffer) (or just declaring a Buffer variable) is
// preferable to NewBuffer.  In particular, passing a non-empty buf to
// NewBuffer and then writing to the Buffer will overwrite buf, not append to
// it.
func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} }

// NewBufferString creates and initializes a new Buffer using string s as its
// initial contents.  It is intended to prepare a buffer to read an existing
// string.  See the warnings about NewBuffer; similar issues apply here.
func NewBufferString(s string) *Buffer {
	return &Buffer{buf: []byte(s)}
}
<|endoftext|>


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

package http

import (
	"io"
	"os"
	"strconv"
)

// NewChunkedWriter returns a new writer that translates writes into HTTP
// "chunked" format before writing them to w.  Closing the returned writer
// sends the final 0-length chunk that marks the end of the stream.
func NewChunkedWriter(w io.Writer) io.WriteCloser {
	return &chunkedWriter{w}
}

// Writing to ChunkedWriter translates to writing in HTTP chunked Transfer
// Encoding wire format to the underlying Wire writer.
type chunkedWriter struct {
	Wire io.Writer
}

// Write the contents of data as one chunk to Wire.
// NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has
// a bug since it does not check for success of io.WriteString
func (cw *chunkedWriter) Write(data []byte) (n int, err os.Error) {

	// Don't send 0-length data. It looks like EOF for chunked encoding.
	if len(data) == 0 {
		return 0, nil
	}

	head := strconv.Itob(len(data), 16) + "\r\n"

	if _, err = io.WriteString(cw.Wire, head); err != nil {
		return 0, err
	}
	if n, err = cw.Wire.Write(data); err != nil {
		return
	}
	if n != len(data) {
		err = io.ErrShortWrite
		return
	}
	_, err = io.WriteString(cw.Wire, "\r\n")

	return
}

func (cw *chunkedWriter) Close() os.Error {
	_, err := io.WriteString(cw.Wire, "0\r\n")
	return err
}
<commit_msg>http: add docs/warning on incorrect use of NewChunkedWriter<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http

import (
	"io"
	"log"
	"os"
	"strconv"
)

// NewChunkedWriter returns a new writer that translates writes into HTTP
// "chunked" format before writing them to w. Closing the returned writer
// sends the final 0-length chunk that marks the end of the stream.
//
// NewChunkedWriter is not needed by normal applications. The http
// package adds chunking automatically if handlers don't set a
// Content-Length header. Using NewChunkedWriter inside a handler
// would result in double chunking or chunking with a Content-Length
// length, both of which are wrong.
func NewChunkedWriter(w io.Writer) io.WriteCloser {
	if _, bad := w.(*response); bad {
		log.Printf("warning: using NewChunkedWriter in an http.Handler; expect corrupt output")
	}
	return &chunkedWriter{w}
}

// Writing to ChunkedWriter translates to writing in HTTP chunked Transfer
// Encoding wire format to the underlying Wire writer.
type chunkedWriter struct {
	Wire io.Writer
}

// Write the contents of data as one chunk to Wire.
// NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has
// a bug since it does not check for success of io.WriteString
func (cw *chunkedWriter) Write(data []byte) (n int, err os.Error) {

	// Don't send 0-length data. It looks like EOF for chunked encoding.
	if len(data) == 0 {
		return 0, nil
	}

	head := strconv.Itob(len(data), 16) + "\r\n"

	if _, err = io.WriteString(cw.Wire, head); err != nil {
		return 0, err
	}
	if n, err = cw.Wire.Write(data); err != nil {
		return
	}
	if n != len(data) {
		err = io.ErrShortWrite
		return
	}
	_, err = io.WriteString(cw.Wire, "\r\n")

	return
}

func (cw *chunkedWriter) Close() os.Error {
	_, err := io.WriteString(cw.Wire, "0\r\n")
	return err
}
<|endoftext|>


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

package http_test

import (
	"fmt"
	. "http"
	"http/httptest"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

const (
	testFile       = "testdata/file"
	testFileLength = 11
)

var ServeFileRangeTests = []struct {
	start, end int
	r          string
	code       int
}{
	{0, testFileLength, "", StatusOK},
	{0, 5, "0-4", StatusPartialContent},
	{2, testFileLength, "2-", StatusPartialContent},
	{testFileLength - 5, testFileLength, "-5", StatusPartialContent},
	{3, 8, "3-7", StatusPartialContent},
	{0, 0, "20-", StatusRequestedRangeNotSatisfiable},
}

func TestServeFile(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()

	var err os.Error

	file, err := ioutil.ReadFile(testFile)
	if err != nil {
		t.Fatal("reading file:", err)
	}

	// set up the Request (re-used for all tests)
	var req Request
	req.Header = make(Header)
	if req.URL, err = ParseURL(ts.URL); err != nil {
		t.Fatal("ParseURL:", err)
	}
	req.Method = "GET"

	// straight GET
	_, body := getBody(t, req)
	if !equal(body, file) {
		t.Fatalf("body mismatch: got %q, want %q", body, file)
	}

	// Range tests
	for _, rt := range ServeFileRangeTests {
		req.Header.Set("Range", "bytes="+rt.r)
		if rt.r == "" {
			req.Header["Range"] = nil
		}
		r, body := getBody(t, req)
		if r.StatusCode != rt.code {
			t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code)
		}
		if rt.code == StatusRequestedRangeNotSatisfiable {
			continue
		}
		h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength)
		if rt.r == "" {
			h = ""
		}
		cr := r.Header.Get("Content-Range")
		if cr != h {
			t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h)
		}
		if !equal(body, file[rt.start:rt.end]) {
			t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end])
		}
	}
}

var fsRedirectTestData = []struct {
	original, redirect string
}{
	{"/test/index.html", "/test/"},
	{"/test/testdata", "/test/testdata/"},
	{"/test/testdata/file/", "/test/testdata/file"},
}

func TestFSRedirect(t *testing.T) {
	ts := httptest.NewServer(StripPrefix("/test", FileServer(Dir("."))))
	defer ts.Close()

	for _, data := range fsRedirectTestData {
		res, err := Get(ts.URL + data.original)
		if err != nil {
			t.Fatal(err)
		}
		res.Body.Close()
		if g, e := res.Request.URL.Path, data.redirect; g != e {
			t.Errorf("redirect from %s: got %s, want %s", data.original, g, e)
		}
	}
}

type testFileSystem struct {
	open func(name string) (File, os.Error)
}

func (fs *testFileSystem) Open(name string) (File, os.Error) {
	return fs.open(name)
}

func TestFileServerCleans(t *testing.T) {
	ch := make(chan string, 1)
	fs := FileServer(&testFileSystem{func(name string) (File, os.Error) {
		ch <- name
		return nil, os.ENOENT
	}})
	tests := []struct {
		reqPath, openArg string
	}{
		{"/foo.txt", "/foo.txt"},
		{"//foo.txt", "/foo.txt"},
		{"/../foo.txt", "/foo.txt"},
	}
	req, _ := NewRequest("GET", "http://example.com", nil)
	for n, test := range tests {
		rec := httptest.NewRecorder()
		req.URL.Path = test.reqPath
		fs.ServeHTTP(rec, req)
		if got := <-ch; got != test.openArg {
			t.Errorf("test %d: got %q, want %q", n, got, test.openArg)
		}
	}
}

func TestFileServerImplicitLeadingSlash(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatalf("TempDir: %v", err)
	}
	defer os.RemoveAll(tempDir)
	if err := ioutil.WriteFile(filepath.Join(tempDir, "foo.txt"), []byte("Hello world"), 0644); err != nil {
		t.Fatalf("WriteFile: %v", err)
	}
	ts := httptest.NewServer(StripPrefix("/bar/", FileServer(Dir(tempDir))))
	defer ts.Close()
	get := func(suffix string) string {
		res, err := Get(ts.URL + suffix)
		if err != nil {
			t.Fatalf("Get %s: %v", suffix, err)
		}
		b, err := ioutil.ReadAll(res.Body)
		if err != nil {
			t.Fatalf("ReadAll %s: %v", suffix, err)
		}
		return string(b)
	}
	if s := get("/bar/"); !strings.Contains(s, ">foo.txt<") {
		t.Logf("expected a directory listing with foo.txt, got %q", s)
	}
	if s := get("/bar/foo.txt"); s != "Hello world" {
		t.Logf("expected %q, got %q", "Hello world", s)
	}
}

func TestDirJoin(t *testing.T) {
	wfi, err := os.Stat("/etc/hosts")
	if err != nil {
		t.Logf("skipping test; no /etc/hosts file")
		return
	}
	test := func(d Dir, name string) {
		f, err := d.Open(name)
		if err != nil {
			t.Fatalf("open of %s: %v", name, err)
		}
		defer f.Close()
		gfi, err := f.Stat()
		if err != nil {
			t.Fatalf("stat of %s: %v", name, err)
		}
		if gfi.Ino != wfi.Ino {
			t.Errorf("%s got different inode", name)
		}
	}
	test(Dir("/etc/"), "/hosts")
	test(Dir("/etc/"), "hosts")
	test(Dir("/etc/"), "../../../../hosts")
	test(Dir("/etc"), "/hosts")
	test(Dir("/etc"), "hosts")
	test(Dir("/etc"), "../../../../hosts")

	// Not really directories, but since we use this trick in
	// ServeFile, test it:
	test(Dir("/etc/hosts"), "")
	test(Dir("/etc/hosts"), "/")
	test(Dir("/etc/hosts"), "../")
}

func TestServeFileContentType(t *testing.T) {
	const ctype = "icecream/chocolate"
	override := false
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		if override {
			w.Header().Set("Content-Type", ctype)
		}
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()
	get := func(want string) {
		resp, err := Get(ts.URL)
		if err != nil {
			t.Fatal(err)
		}
		if h := resp.Header.Get("Content-Type"); h != want {
			t.Errorf("Content-Type mismatch: got %d, want %d", h, want)
		}
	}
	get("text/plain; charset=utf-8")
	override = true
	get(ctype)
}

func TestServeFileMimeType(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		ServeFile(w, r, "testdata/style.css")
	}))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	want := "text/css; charset=utf-8"
	if h := resp.Header.Get("Content-Type"); h != want {
		t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
	}
}

func TestServeFileWithContentEncoding(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		w.Header().Set("Content-Encoding", "foo")
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	if g, e := resp.ContentLength, int64(-1); g != e {
		t.Errorf("Content-Length mismatch: got %q, want %q", g, e)
	}
}

func getBody(t *testing.T, req Request) (*Response, []byte) {
	r, err := DefaultClient.Do(&req)
	if err != nil {
		t.Fatal(req.URL.String(), "send:", err)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Fatal("reading Body:", err)
	}
	return r, b
}

func equal(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}
<commit_msg>http: correct format flags when printing errors during tests<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http_test

import (
	"fmt"
	. "http"
	"http/httptest"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

const (
	testFile       = "testdata/file"
	testFileLength = 11
)

var ServeFileRangeTests = []struct {
	start, end int
	r          string
	code       int
}{
	{0, testFileLength, "", StatusOK},
	{0, 5, "0-4", StatusPartialContent},
	{2, testFileLength, "2-", StatusPartialContent},
	{testFileLength - 5, testFileLength, "-5", StatusPartialContent},
	{3, 8, "3-7", StatusPartialContent},
	{0, 0, "20-", StatusRequestedRangeNotSatisfiable},
}

func TestServeFile(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()

	var err os.Error

	file, err := ioutil.ReadFile(testFile)
	if err != nil {
		t.Fatal("reading file:", err)
	}

	// set up the Request (re-used for all tests)
	var req Request
	req.Header = make(Header)
	if req.URL, err = ParseURL(ts.URL); err != nil {
		t.Fatal("ParseURL:", err)
	}
	req.Method = "GET"

	// straight GET
	_, body := getBody(t, req)
	if !equal(body, file) {
		t.Fatalf("body mismatch: got %q, want %q", body, file)
	}

	// Range tests
	for _, rt := range ServeFileRangeTests {
		req.Header.Set("Range", "bytes="+rt.r)
		if rt.r == "" {
			req.Header["Range"] = nil
		}
		r, body := getBody(t, req)
		if r.StatusCode != rt.code {
			t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code)
		}
		if rt.code == StatusRequestedRangeNotSatisfiable {
			continue
		}
		h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength)
		if rt.r == "" {
			h = ""
		}
		cr := r.Header.Get("Content-Range")
		if cr != h {
			t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h)
		}
		if !equal(body, file[rt.start:rt.end]) {
			t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end])
		}
	}
}

var fsRedirectTestData = []struct {
	original, redirect string
}{
	{"/test/index.html", "/test/"},
	{"/test/testdata", "/test/testdata/"},
	{"/test/testdata/file/", "/test/testdata/file"},
}

func TestFSRedirect(t *testing.T) {
	ts := httptest.NewServer(StripPrefix("/test", FileServer(Dir("."))))
	defer ts.Close()

	for _, data := range fsRedirectTestData {
		res, err := Get(ts.URL + data.original)
		if err != nil {
			t.Fatal(err)
		}
		res.Body.Close()
		if g, e := res.Request.URL.Path, data.redirect; g != e {
			t.Errorf("redirect from %s: got %s, want %s", data.original, g, e)
		}
	}
}

type testFileSystem struct {
	open func(name string) (File, os.Error)
}

func (fs *testFileSystem) Open(name string) (File, os.Error) {
	return fs.open(name)
}

func TestFileServerCleans(t *testing.T) {
	ch := make(chan string, 1)
	fs := FileServer(&testFileSystem{func(name string) (File, os.Error) {
		ch <- name
		return nil, os.ENOENT
	}})
	tests := []struct {
		reqPath, openArg string
	}{
		{"/foo.txt", "/foo.txt"},
		{"//foo.txt", "/foo.txt"},
		{"/../foo.txt", "/foo.txt"},
	}
	req, _ := NewRequest("GET", "http://example.com", nil)
	for n, test := range tests {
		rec := httptest.NewRecorder()
		req.URL.Path = test.reqPath
		fs.ServeHTTP(rec, req)
		if got := <-ch; got != test.openArg {
			t.Errorf("test %d: got %q, want %q", n, got, test.openArg)
		}
	}
}

func TestFileServerImplicitLeadingSlash(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatalf("TempDir: %v", err)
	}
	defer os.RemoveAll(tempDir)
	if err := ioutil.WriteFile(filepath.Join(tempDir, "foo.txt"), []byte("Hello world"), 0644); err != nil {
		t.Fatalf("WriteFile: %v", err)
	}
	ts := httptest.NewServer(StripPrefix("/bar/", FileServer(Dir(tempDir))))
	defer ts.Close()
	get := func(suffix string) string {
		res, err := Get(ts.URL + suffix)
		if err != nil {
			t.Fatalf("Get %s: %v", suffix, err)
		}
		b, err := ioutil.ReadAll(res.Body)
		if err != nil {
			t.Fatalf("ReadAll %s: %v", suffix, err)
		}
		return string(b)
	}
	if s := get("/bar/"); !strings.Contains(s, ">foo.txt<") {
		t.Logf("expected a directory listing with foo.txt, got %q", s)
	}
	if s := get("/bar/foo.txt"); s != "Hello world" {
		t.Logf("expected %q, got %q", "Hello world", s)
	}
}

func TestDirJoin(t *testing.T) {
	wfi, err := os.Stat("/etc/hosts")
	if err != nil {
		t.Logf("skipping test; no /etc/hosts file")
		return
	}
	test := func(d Dir, name string) {
		f, err := d.Open(name)
		if err != nil {
			t.Fatalf("open of %s: %v", name, err)
		}
		defer f.Close()
		gfi, err := f.Stat()
		if err != nil {
			t.Fatalf("stat of %s: %v", name, err)
		}
		if gfi.Ino != wfi.Ino {
			t.Errorf("%s got different inode", name)
		}
	}
	test(Dir("/etc/"), "/hosts")
	test(Dir("/etc/"), "hosts")
	test(Dir("/etc/"), "../../../../hosts")
	test(Dir("/etc"), "/hosts")
	test(Dir("/etc"), "hosts")
	test(Dir("/etc"), "../../../../hosts")

	// Not really directories, but since we use this trick in
	// ServeFile, test it:
	test(Dir("/etc/hosts"), "")
	test(Dir("/etc/hosts"), "/")
	test(Dir("/etc/hosts"), "../")
}

func TestServeFileContentType(t *testing.T) {
	const ctype = "icecream/chocolate"
	override := false
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		if override {
			w.Header().Set("Content-Type", ctype)
		}
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()
	get := func(want string) {
		resp, err := Get(ts.URL)
		if err != nil {
			t.Fatal(err)
		}
		if h := resp.Header.Get("Content-Type"); h != want {
			t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
		}
	}
	get("text/plain; charset=utf-8")
	override = true
	get(ctype)
}

func TestServeFileMimeType(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		ServeFile(w, r, "testdata/style.css")
	}))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	want := "text/css; charset=utf-8"
	if h := resp.Header.Get("Content-Type"); h != want {
		t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
	}
}

func TestServeFileWithContentEncoding(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		w.Header().Set("Content-Encoding", "foo")
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	if g, e := resp.ContentLength, int64(-1); g != e {
		t.Errorf("Content-Length mismatch: got %d, want %d", g, e)
	}
}

func getBody(t *testing.T, req Request) (*Response, []byte) {
	r, err := DefaultClient.Do(&req)
	if err != nil {
		t.Fatal(req.URL.String(), "send:", err)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Fatal("reading Body:", err)
	}
	return r, b
}

func equal(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}
<|endoftext|>


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

package strconv

// FormatUint returns the string representation of i in the given base.
func FormatUint(i uint64, base int) string {
	_, s := formatBits(nil, i, base, false, false)
	return s
}

// FormatInt returns the string representation of i in the given base.
func FormatInt(i int64, base int) string {
	_, s := formatBits(nil, uint64(i), base, i < 0, false)
	return s
}

// Itoa is shorthand for FormatInt(i, 10).
func Itoa(i int) string {
	return FormatInt(int64(i), 10)
}

// AppendInt appends the string form of the integer i,
// as generated by FormatInt, to dst and returns the extended buffer.
func AppendInt(dst []byte, i int64, base int) []byte {
	dst, _ = formatBits(dst, uint64(i), base, i < 0, true)
	return dst
}

// AppendUint appends the string form of the unsigned integer i,
// as generated by FormatUint, to dst and returns the extended buffer.
func AppendUint(dst []byte, i uint64, base int) []byte {
	dst, _ = formatBits(dst, i, base, false, true)
	return dst
}

const digits = "0123456789abcdefghijklmnopqrstuvwxyz"

var shifts = [len(digits) + 1]uint{
	1 << 1: 1,
	1 << 2: 2,
	1 << 3: 3,
	1 << 4: 4,
	1 << 5: 5,
}

// formatBits computes the string representation of u in the given base.
// If neg is set, u is treated as negative int64 value. If append_ is
// set, the string is appended to dst and the resulting byte slice is
// returned as the first result value; otherwise the string is returned
// as the second result value.
//
func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s string) {
	if base < 2 || base > len(digits) {
		panic("strconv: illegal AppendInt/FormatInt base")
	}
	// 2 <= base && base <= len(digits)

	var a [64 + 1]byte // +1 for sign of 64bit value in base 2
	i := len(a)

	if neg {
		u = -u
	}

	// convert bits
	if base == 10 {
		// common case: use constant 10 for / and % because
		// the compiler can optimize it into a multiply+shift
		for u >= 10 {
			i--
			a[i] = digits[u%10]
			u /= 10
		}

	} else if s := shifts[base]; s > 0 {
		// base is power of 2: use shifts and masks instead of / and %
		b := uint64(base)
		m := uintptr(b) - 1 // == 1<<s - 1
		for u >= b {
			i--
			a[i] = digits[uintptr(u)&m]
			u >>= s
		}

	} else {
		// general case
		b := uint64(base)
		for u >= b {
			i--
			a[i] = digits[u%b]
			u /= b
		}
	}

	// u < base
	i--
	a[i] = digits[uintptr(u)]

	// add sign, if any
	if neg {
		i--
		a[i] = '-'
	}

	if append_ {
		d = append(dst, a[i:]...)
		return
	}
	s = string(a[i:])
	return
}
<commit_msg>strconv: even faster int conversion<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strconv

// FormatUint returns the string representation of i in the given base.
func FormatUint(i uint64, base int) string {
	_, s := formatBits(nil, i, base, false, false)
	return s
}

// FormatInt returns the string representation of i in the given base.
func FormatInt(i int64, base int) string {
	_, s := formatBits(nil, uint64(i), base, i < 0, false)
	return s
}

// Itoa is shorthand for FormatInt(i, 10).
func Itoa(i int) string {
	return FormatInt(int64(i), 10)
}

// AppendInt appends the string form of the integer i,
// as generated by FormatInt, to dst and returns the extended buffer.
func AppendInt(dst []byte, i int64, base int) []byte {
	dst, _ = formatBits(dst, uint64(i), base, i < 0, true)
	return dst
}

// AppendUint appends the string form of the unsigned integer i,
// as generated by FormatUint, to dst and returns the extended buffer.
func AppendUint(dst []byte, i uint64, base int) []byte {
	dst, _ = formatBits(dst, i, base, false, true)
	return dst
}

const (
	digits   = "0123456789abcdefghijklmnopqrstuvwxyz"
	digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
	digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
)

var shifts = [len(digits) + 1]uint{
	1 << 1: 1,
	1 << 2: 2,
	1 << 3: 3,
	1 << 4: 4,
	1 << 5: 5,
}

// formatBits computes the string representation of u in the given base.
// If neg is set, u is treated as negative int64 value. If append_ is
// set, the string is appended to dst and the resulting byte slice is
// returned as the first result value; otherwise the string is returned
// as the second result value.
//
func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s string) {
	if base < 2 || base > len(digits) {
		panic("strconv: illegal AppendInt/FormatInt base")
	}
	// 2 <= base && base <= len(digits)

	var a [64 + 1]byte // +1 for sign of 64bit value in base 2
	i := len(a)

	if neg {
		u = -u
	}

	// convert bits
	if base == 10 {
		// common case: use constants for / and % because
		// the compiler can optimize it into a multiply+shift,
		// and unroll loop
		for u >= 100 {
			i -= 2
			q := u / 100
			j := u - q*100
			a[i+1] = digits01[j]
			a[i+0] = digits10[j]
			u = q
		}
		if u >= 10 {
			i--
			q := u / 10
			a[i] = digits[u-q*10]
			u = q
		}

	} else if s := shifts[base]; s > 0 {
		// base is power of 2: use shifts and masks instead of / and %
		b := uint64(base)
		m := uintptr(b) - 1 // == 1<<s - 1
		for u >= b {
			i--
			a[i] = digits[uintptr(u)&m]
			u >>= s
		}

	} else {
		// general case
		b := uint64(base)
		for u >= b {
			i--
			a[i] = digits[u%b]
			u /= b
		}
	}

	// u < base
	i--
	a[i] = digits[uintptr(u)]

	// add sign, if any
	if neg {
		i--
		a[i] = '-'
	}

	if append_ {
		d = append(dst, a[i:]...)
		return
	}
	s = string(a[i:])
	return
}
<|endoftext|>


<commit_before>package db

/**
 * Barista DB Manager
 *
 * @author: Anant Bhardwaj
 * @date: 03/23/2014
 */

import _ "github.com/lib/pq"
import "database/sql"
import "fmt"

const HOST = "localhost"
const PORT = 5432

type DBManager struct {
  db *sql.DB
}

func NewDBManager() *DBManager {
  manager := new(DBManager)
  return manager
}

func (manager *DBManager) OpenConnection(
    user string, password string, dbname string) error {

  var err error
  manager.db, err = sql.Open(
    "postgres",
    fmt.Sprintf(
        "host=%s port=%v user=%s dbname=%s password=%s sslmode=disable",
        HOST, PORT, user, password, dbname))

  return err
}

func (manager *DBManager) ExecuteSql(
    query string, args interface{}) ([][][]byte, []string, error) {

  rows, err := manager.db.Query(query)

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

  tuples := make([][][]byte, 0)
  columns := make([]string)

  if rows != nil {
     columns, err = rows.Columns()
     n_columns := len(columns)

     for rows.Next() {
        cells := make([][]byte, n_columns)

    	dest := make([]interface{}, n_columns)
    	for i, _ := range cells {
      	   dest[i] = &cells[i]
        }

    	rows.Scan(dest...)
    	tuples = append(tuples, cells)
     }
  }

  return tuples, columns, err
}

func (manager *DBManager) CloseConnection() error {
  return manager.db.Close()
}<commit_msg>fixed db.go<commit_after>package db

/**
 * Barista DB Manager
 *
 * @author: Anant Bhardwaj
 * @date: 03/23/2014
 */

import _ "github.com/lib/pq"
import "database/sql"
import "fmt"

const HOST = "localhost"
const PORT = 5432

type DBManager struct {
  db *sql.DB
}

func NewDBManager() *DBManager {
  manager := new(DBManager)
  return manager
}

func (manager *DBManager) OpenConnection(
    user string, password string, dbname string) error {

  var err error
  manager.db, err = sql.Open(
    "postgres",
    fmt.Sprintf(
        "host=%s port=%v user=%s dbname=%s password=%s sslmode=disable",
        HOST, PORT, user, password, dbname))

  return err
}

func (manager *DBManager) ExecuteSql(
    query string, args interface{}) ([][][]byte, []string, error) {

  rows, err := manager.db.Query(query)

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

  tuples := make([][][]byte, 0)
  columns := make([]string, 0)

  if rows != nil {
     columns, err = rows.Columns()
     n_columns := len(columns)

     for rows.Next() {
        cells := make([][]byte, n_columns)

    	dest := make([]interface{}, n_columns)
    	for i, _ := range cells {
      	   dest[i] = &cells[i]
        }

    	rows.Scan(dest...)
    	tuples = append(tuples, cells)
     }
  }

  return tuples, columns, err
}

func (manager *DBManager) CloseConnection() error {
  return manager.db.Close()
}<|endoftext|>


<commit_before>package sipparser

import (
	"bytes"
	//"fmt"
	"strconv"
	"unsafe"
)

type SipMsg struct {
	startLine SipStartLine
	headers   SipHeaders
	bodies    SipMsgBodies
}

func NewSipMsg(context *ParseContext) AbnfPtr {
	addr := context.allocator.Alloc(int32(unsafe.Sizeof(SipMsg{})))
	if addr == ABNF_PTR_NIL {
		return ABNF_PTR_NIL
	}
	addr.GetSipMsg(context).Init()
	return addr
}

func (this *SipMsg) Init() {
	this.startLine.Init()
	this.headers.Init()
}

func (this *SipMsg) Parse(context *ParseContext, src []byte, pos int) (newPos int, err error) {
	this.Init()
	newPos = pos
	newPos, err = this.startLine.ParseWithoutInit(context, src, newPos)
	if err != nil {
		return newPos, err
	}

	if newPos >= len(src) {
		return newPos, &AbnfError{"SipMsg parse: no headers or CRLF", src, newPos}
	}

	newPos, err = this.headers.Parse(context, src, newPos)
	if err != nil {
		return newPos, err
	}

	return this.ParseMsgBody(context, src, newPos)
}

func (this *SipMsg) ParseMsgBody(context *ParseContext, src []byte, pos int) (newPos int, err error) {
	newPos = pos
	headerPtr, ok := this.headers.GetSingleHeaderParsedByIndex(context, ABNF_SIP_HDR_CONTENT_TYPE)
	if !ok {
		// no Content-Type means no msg-body
		return newPos, nil
	}

	contentType := headerPtr.GetSipHeaderContentType(context)
	if contentType.mainType.EqualStringNoCase(context, "multipart") {
		// mime bodies
		// get boundary
		boundary, ok := contentType.GetBoundary(context)
		if !ok {
			return newPos, &AbnfError{"SipMsg parse: no boundary for multipart body", src, newPos}
		}
		return this.ParseMultiBody(context, src, newPos, boundary)
	}

	return this.ParseSingleBody(context, src, newPos)
}

func (this *SipMsg) ParseSingleBody(context *ParseContext, src []byte, pos int) (newPos int, err error) {
	left := len(src) - pos
	bodySize := 0
	parsedPtr, ok := this.headers.GetSingleHeaderParsedByIndex(context, ABNF_SIP_HDR_CONTENT_LENGTH)
	if ok {
		bodySize = int(parsedPtr.GetSipHeaderContentLength(context).size)
		if bodySize > left {
			bodySize = left
		}
	}

	if bodySize == 0 {
		return newPos, nil
	}

	addr := NewSipMsgBody(context)
	if addr == ABNF_PTR_NIL {
		return newPos, &AbnfError{"SipMsg parse: out of memory for sip-mshg-body", src, newPos}
	}

	// copy Content-* headers from sip-msg to sip-msg-body's headers
	addr.GetSipMsgBody(context).headers.CopyContentHeaders(context, &this.headers)
	addr.GetSipMsgBody(context).SetBody(context, src[pos:pos+bodySize])
	this.bodies.AddBody(context, addr)
	return newPos, nil
}

func (this *SipMsg) ParseMultiBody(context *ParseContext, src []byte, pos int, boundary []byte) (newPos int, err error) {
	var ok bool

	dash_boundary := append([]byte{'-', '-'}, boundary...)
	delimiter := append([]byte{'\r', '\n'}, dash_boundary...)

	newPos = pos
	pos1 := bytes.Index(src[newPos:], dash_boundary)
	if pos1 != 0 {
		return newPos, &AbnfError{"SipMsg ParseMultiBody: no first dash-bounday", src, newPos}
	}

	newPos += len(dash_boundary)

	for newPos < len(src) {
		if newPos+1 >= len(src) {

			return newPos, &AbnfError{"SipMsg ParseMultiBody: reach end without close-delimiter", src, newPos}
		}

		if src[newPos] == '-' || src[newPos+1] == '-' {
			// reach close-delimiter
			return newPos, nil
		}

		// skip transport-padding CRLF
		_, newPos, ok = FindCrlfRFC3261(src, newPos)
		if !ok {
			return newPos, &AbnfError{"SipMsg ParseMultiBody: no CRLF after dash-bounday", src, newPos}
		}

		addr := NewSipMsgBody(context)
		if addr == ABNF_PTR_NIL {
			return newPos, &AbnfError{"SipMsg ParseMultiBody: out of memory for body", src, newPos}
		}

		body := addr.GetSipMsgBody(context)

		newPos, err = body.headers.Parse(context, src, newPos)
		if err != nil {
			return newPos, &AbnfError{"SipMsg ParseMultiBody: parse headers failed", src, newPos}
		}

		begin := newPos
		end := bytes.Index(src[newPos:], delimiter)
		if end == -1 {
			return newPos, &AbnfError{"SipMsg ParseMultiBody: no delimiter", src, newPos}
		}

		end += newPos
		newPos = end + len(delimiter)

		body.body.SetByteSlice(context, src[begin:end])
		this.bodies.AddBody(context, addr)
	}

	return newPos, &AbnfError{"SipMsg ParseMultiBody: no close-delimiter", src, newPos}
}

func (this *SipMsg) FindOrCreateBoundary(context *ParseContext) (boundary []byte) {
	var parsedContentType *SipHeaderContentType

	parsedPtr, ok := this.headers.GetSingleHeaderParsedByIndex(context, ABNF_SIP_HDR_CONTENT_TYPE)
	if ok {
		parsedContentType = parsedPtr.GetSipHeaderContentType(context)
		boundary, ok = parsedContentType.GetBoundary(context)
		if ok {
			return boundary
		}

	} else {
		// create Content-Type header
		addr := this.headers.CreateSingleHeaderByIndex(context, ABNF_SIP_HDR_CONTENT_TYPE)
		if addr == ABNF_PTR_NIL {
			return nil
		}
		parsedPtr = NewSipHeaderContentType(context)
		if parsedPtr == ABNF_PTR_NIL {
			return nil
		}
		parsedContentType = parsedPtr.GetSipHeaderContentType(context)
		parsedContentType.SetMainType(context, "multipart")
		parsedContentType.SetSubType(context, "mixed")
		addr.GetSipSingleHeader(context).parsed = parsedPtr
		this.headers.singleHeaders.PushBack(context, addr)
	}

	/* create boundary */
	boundary = StringToByteSlice(ABNF_SIP_DEFAULT_BOUNDARY)
	parsedContentType.AddBoundary(context, boundary)

	return boundary
}

func (this *SipMsg) Encode(context *ParseContext, buf *bytes.Buffer) error {
	// create Content-Length header if not exist
	err := this.headers.CreateContentLength(context, 0)
	if err != nil {
		return err
	}

	this.startLine.Encode(context, buf)

	var boundary []byte

	if this.bodies.Size() > 1 {
		// remove Content-* headers from sip message except Content-Length and Content-Type*/
		this.headers.RemoveContentHeaders(context)

		_, ok := this.headers.GetSingleHeader(context, "MIME-Version")
		if !ok {
			addr := this.headers.GenerateAndAddSingleHeader(context, "MIME-Version", "1.0")
			if addr == ABNF_PTR_NIL {
				return &AbnfError{"SipMsg encode: out of memory for adding MIME-Version", nil, 0}
			}
		}

		boundary = this.FindOrCreateBoundary(context)
	}

	this.headers.Encode(context, buf)
	buf.WriteString("\r\n")

	if this.bodies.Empty() {
		return nil
	}

	bodyStart := len(buf.Bytes())

	if this.bodies.Size() == 1 {
		this.bodies.EncodeSingle(context, buf)
	} else {
		this.bodies.EncodeMulti(context, buf, boundary)
	}

	// modify Content-Length size or create Content-Length
	bodySize := StringToByteSlice(strconv.FormatUint(uint64(len(buf.Bytes())-bodyStart), 10))
	contentLength, ok := this.headers.GetSingleHeaderParsedByIndex(context, ABNF_SIP_HDR_CONTENT_LENGTH)
	if !ok {
		return &AbnfError{"SipMsg encode: no Content-Length after encoding msg-body", nil, 0}
	}
	encodeEnd := int(contentLength.GetSipHeaderContentLength(context).encodeEnd)
	copy(buf.Bytes()[encodeEnd-len(bodySize):encodeEnd], bodySize)

	return nil
}

func (this *SipMsg) String(context *ParseContext) string {
	var buf bytes.Buffer
	this.Encode(context, &buf)
	return buf.String()
}
<commit_msg>minor optimize for sipparser<commit_after>package sipparser

import (
	"bytes"
	//"fmt"
	"strconv"
	"unsafe"
)

type SipMsg struct {
	startLine SipStartLine
	headers   SipHeaders
	bodies    SipMsgBodies
}

func NewSipMsg(context *ParseContext) AbnfPtr {
	addr := context.allocator.Alloc(int32(unsafe.Sizeof(SipMsg{})))
	if addr == ABNF_PTR_NIL {
		return ABNF_PTR_NIL
	}
	addr.GetSipMsg(context).Init()
	return addr
}

func (this *SipMsg) Init() {
	this.startLine.Init()
	this.headers.Init()
	this.bodies.Init()
}

func (this *SipMsg) Parse(context *ParseContext, src []byte, pos int) (newPos int, err error) {
	this.Init()
	newPos = pos
	newPos, err = this.startLine.ParseWithoutInit(context, src, newPos)
	if err != nil {
		return newPos, err
	}

	if newPos >= len(src) {
		return newPos, &AbnfError{"SipMsg parse: no headers or CRLF", src, newPos}
	}

	newPos, err = this.headers.Parse(context, src, newPos)
	if err != nil {
		return newPos, err
	}

	return this.ParseMsgBody(context, src, newPos)
}

func (this *SipMsg) ParseMsgBody(context *ParseContext, src []byte, pos int) (newPos int, err error) {
	newPos = pos
	headerPtr, ok := this.headers.GetSingleHeaderParsedByIndex(context, ABNF_SIP_HDR_CONTENT_TYPE)
	if !ok {
		// no Content-Type means no msg-body
		return newPos, nil
	}

	contentType := headerPtr.GetSipHeaderContentType(context)
	if contentType.mainType.EqualStringNoCase(context, "multipart") {
		// mime bodies
		// get boundary
		boundary, ok := contentType.GetBoundary(context)
		if !ok {
			return newPos, &AbnfError{"SipMsg parse: no boundary for multipart body", src, newPos}
		}
		return this.ParseMultiBody(context, src, newPos, boundary)
	}

	return this.ParseSingleBody(context, src, newPos)
}

func (this *SipMsg) ParseSingleBody(context *ParseContext, src []byte, pos int) (newPos int, err error) {
	left := len(src) - pos
	bodySize := 0
	parsedPtr, ok := this.headers.GetSingleHeaderParsedByIndex(context, ABNF_SIP_HDR_CONTENT_LENGTH)
	if ok {
		bodySize = int(parsedPtr.GetSipHeaderContentLength(context).size)
		if bodySize > left {
			bodySize = left
		}
	}

	if bodySize == 0 {
		return newPos, nil
	}

	addr := NewSipMsgBody(context)
	if addr == ABNF_PTR_NIL {
		return newPos, &AbnfError{"SipMsg parse: out of memory for sip-mshg-body", src, newPos}
	}

	// copy Content-* headers from sip-msg to sip-msg-body's headers
	addr.GetSipMsgBody(context).headers.CopyContentHeaders(context, &this.headers)
	addr.GetSipMsgBody(context).SetBody(context, src[pos:pos+bodySize])
	this.bodies.AddBody(context, addr)
	return newPos, nil
}

func (this *SipMsg) ParseMultiBody(context *ParseContext, src []byte, pos int, boundary []byte) (newPos int, err error) {
	var ok bool

	dash_boundary := append([]byte{'-', '-'}, boundary...)
	delimiter := append([]byte{'\r', '\n'}, dash_boundary...)

	newPos = pos
	pos1 := bytes.Index(src[newPos:], dash_boundary)
	if pos1 != 0 {
		return newPos, &AbnfError{"SipMsg ParseMultiBody: no first dash-bounday", src, newPos}
	}

	newPos += len(dash_boundary)

	for newPos < len(src) {
		if newPos+1 >= len(src) {

			return newPos, &AbnfError{"SipMsg ParseMultiBody: reach end without close-delimiter", src, newPos}
		}

		if src[newPos] == '-' || src[newPos+1] == '-' {
			// reach close-delimiter
			return newPos, nil
		}

		// skip transport-padding CRLF
		_, newPos, ok = FindCrlfRFC3261(src, newPos)
		if !ok {
			return newPos, &AbnfError{"SipMsg ParseMultiBody: no CRLF after dash-bounday", src, newPos}
		}

		addr := NewSipMsgBody(context)
		if addr == ABNF_PTR_NIL {
			return newPos, &AbnfError{"SipMsg ParseMultiBody: out of memory for body", src, newPos}
		}

		body := addr.GetSipMsgBody(context)

		newPos, err = body.headers.Parse(context, src, newPos)
		if err != nil {
			return newPos, &AbnfError{"SipMsg ParseMultiBody: parse headers failed", src, newPos}
		}

		begin := newPos
		end := bytes.Index(src[newPos:], delimiter)
		if end == -1 {
			return newPos, &AbnfError{"SipMsg ParseMultiBody: no delimiter", src, newPos}
		}

		end += newPos
		newPos = end + len(delimiter)

		body.body.SetByteSlice(context, src[begin:end])
		this.bodies.AddBody(context, addr)
	}

	return newPos, &AbnfError{"SipMsg ParseMultiBody: no close-delimiter", src, newPos}
}

func (this *SipMsg) FindOrCreateBoundary(context *ParseContext) (boundary []byte) {
	var parsedContentType *SipHeaderContentType

	parsedPtr, ok := this.headers.GetSingleHeaderParsedByIndex(context, ABNF_SIP_HDR_CONTENT_TYPE)
	if ok {
		parsedContentType = parsedPtr.GetSipHeaderContentType(context)
		boundary, ok = parsedContentType.GetBoundary(context)
		if ok {
			return boundary
		}

	} else {
		// create Content-Type header
		addr := this.headers.CreateSingleHeaderByIndex(context, ABNF_SIP_HDR_CONTENT_TYPE)
		if addr == ABNF_PTR_NIL {
			return nil
		}
		parsedPtr = NewSipHeaderContentType(context)
		if parsedPtr == ABNF_PTR_NIL {
			return nil
		}
		parsedContentType = parsedPtr.GetSipHeaderContentType(context)
		parsedContentType.SetMainType(context, "multipart")
		parsedContentType.SetSubType(context, "mixed")
		addr.GetSipSingleHeader(context).parsed = parsedPtr
		this.headers.singleHeaders.PushBack(context, addr)
	}

	/* create boundary */
	boundary = StringToByteSlice(ABNF_SIP_DEFAULT_BOUNDARY)
	parsedContentType.AddBoundary(context, boundary)

	return boundary
}

func (this *SipMsg) Encode(context *ParseContext, buf *bytes.Buffer) error {
	// create Content-Length header if not exist
	err := this.headers.CreateContentLength(context, 0)
	if err != nil {
		return err
	}

	this.startLine.Encode(context, buf)

	var boundary []byte

	if this.bodies.Size() > 1 {
		// remove Content-* headers from sip message except Content-Length and Content-Type*/
		this.headers.RemoveContentHeaders(context)

		_, ok := this.headers.GetSingleHeader(context, "MIME-Version")
		if !ok {
			addr := this.headers.GenerateAndAddSingleHeader(context, "MIME-Version", "1.0")
			if addr == ABNF_PTR_NIL {
				return &AbnfError{"SipMsg encode: out of memory for adding MIME-Version", nil, 0}
			}
		}

		boundary = this.FindOrCreateBoundary(context)
	}

	this.headers.Encode(context, buf)
	buf.WriteString("\r\n")

	if this.bodies.Empty() {
		return nil
	}

	bodyStart := len(buf.Bytes())

	if this.bodies.Size() == 1 {
		this.bodies.EncodeSingle(context, buf)
	} else {
		this.bodies.EncodeMulti(context, buf, boundary)
	}

	// modify Content-Length size or create Content-Length
	bodySize := StringToByteSlice(strconv.FormatUint(uint64(len(buf.Bytes())-bodyStart), 10))
	contentLength, ok := this.headers.GetSingleHeaderParsedByIndex(context, ABNF_SIP_HDR_CONTENT_LENGTH)
	if !ok {
		return &AbnfError{"SipMsg encode: no Content-Length after encoding msg-body", nil, 0}
	}
	encodeEnd := int(contentLength.GetSipHeaderContentLength(context).encodeEnd)
	copy(buf.Bytes()[encodeEnd-len(bodySize):encodeEnd], bodySize)

	return nil
}

func (this *SipMsg) String(context *ParseContext) string {
	var buf bytes.Buffer
	this.Encode(context, &buf)
	return buf.String()
}
<|endoftext|>