<commit_before>package main

import (
	"encoding/json"
	"reflect"
	"strings"
)

const (
	networkDeviceStatsMetricPrefix = "net.dev."
	netstatStatsMetricPrefix       = "net.netstat."
)

func collectNetworkDeviceStats() {
	stats, err := readNetworkDeviceStats()

	if err != nil {
		Log.WithField("error", err).Error("Error reading network device stats. Can't collect network device stats.")
	}

	for _, stat := range stats {
		if ifaceRegExp == nil || !ifaceRegExp.MatchString(stat.Iface) {
			elem := reflect.ValueOf(&stat).Elem()
			typeOfElem := elem.Type()

			for i := 0; i < elem.NumField(); i++ {
				field := typeOfElem.Field(i)

				if field.Name == "Iface" {
					continue
				}

				value := elem.Field(i).Uint()

				_, collect := StatsMap[field.Name]

				if metricName := field.Tag.Get("json"); collect && metricName != "" {
					if err := StatsdClient.Count(strings.Join([]string{networkDeviceStatsMetricPrefix, metricName}, ""), int64(value), []string{"iface:" + stat.Iface}, 1); err != nil {
						Log.WithField("error", err).Error("Couldn't submit event to statsd.")
					}
				}
			}
		} else {
			Log.Infof("Filtered interface %s from network device stat collection.", stat.Iface)
		}
	}
}

func logNetworkDeviceStats() {
	stats, err := readNetworkDeviceStats()

	if err != nil {
		Log.WithField("error", err).Error("Error reading network device stats. Can't collect network device stats.")
	}

	jsonBytes, err := json.Marshal(stats)
	if err != nil {
		Log.WithField("error", err).Errorf("Error JSON marshalling: %+v.", stats)
	}

	Log.WithField("stat", NetworkStatPath).Infof(string(jsonBytes))
}

func collectNetstatStats() {
	stats, err := readNetworkDeviceStats()

	if err != nil {
		Log.WithField("error", err).Error("Error reading network device stats. Can't collect network device stats.")
	}

	elem := reflect.ValueOf(stats).Elem()
	typeOfElem := elem.Type()

	for i := 0; i < elem.NumField(); i++ {
		field := typeOfElem.Field(i)

		value := elem.Field(i).Uint()

		_, collect := StatsMap[field.Name]

		if metricName := field.Tag.Get("json"); collect && metricName != "" {
			if err := StatsdClient.Count(strings.Join([]string{netstatStatsMetricPrefix, metricName}, ""), int64(value), []string{}, 1); err != nil {
				Log.WithField("error", err).Error("Couldn't submit event to statsd.")
			}
		}
	}
}

func logNetstatStats() {
	stats, err := readNetstatStats()

	if err != nil {
		Log.WithField("error", err).Error("Error reading network device stats. Can't collect netstat stats.")
	}

	jsonBytes, err := json.Marshal(stats)
	if err != nil {
		Log.WithField("error", err).Errorf("Error JSON marshalling: %+v.", stats)
	}

	Log.WithField("stat", NetstatStatPath).Infof(string(jsonBytes))
}
<commit_msg>Fix log messages.<commit_after>package main

import (
	"encoding/json"
	"reflect"
	"strings"
)

const (
	networkDeviceStatsMetricPrefix = "net.dev."
	netstatStatsMetricPrefix       = "net.netstat."
)

func collectNetworkDeviceStats() {
	stats, err := readNetworkDeviceStats()

	if err != nil {
		Log.WithField("error", err).Error("Error reading network device stats. Can't collect network device stats.")
	}

	for _, stat := range stats {
		if ifaceRegExp == nil || !ifaceRegExp.MatchString(stat.Iface) {
			elem := reflect.ValueOf(&stat).Elem()
			typeOfElem := elem.Type()

			for i := 0; i < elem.NumField(); i++ {
				field := typeOfElem.Field(i)

				if field.Name == "Iface" {
					continue
				}

				value := elem.Field(i).Uint()

				_, collect := StatsMap[field.Name]

				if metricName := field.Tag.Get("json"); collect && metricName != "" {
					if err := StatsdClient.Count(strings.Join([]string{networkDeviceStatsMetricPrefix, metricName}, ""), int64(value), []string{"iface:" + stat.Iface}, 1); err != nil {
						Log.WithField("error", err).Error("Couldn't submit event to statsd.")
					}
				}
			}
		} else {
			Log.Infof("Filtered interface %s from network device stat collection.", stat.Iface)
		}
	}
}

func logNetworkDeviceStats() {
	stats, err := readNetworkDeviceStats()

	if err != nil {
		Log.WithField("error", err).Error("Error reading network device stats. Can't log network device stats.")
	}

	jsonBytes, err := json.Marshal(stats)
	if err != nil {
		Log.WithField("error", err).Errorf("Error JSON marshalling: %+v.", stats)
	}

	Log.WithField("stat", NetworkStatPath).Infof(string(jsonBytes))
}

func collectNetstatStats() {
	stats, err := readNetworkDeviceStats()

	if err != nil {
		Log.WithField("error", err).Error("Error reading network device stats. Can't collect netstat stats.")
	}

	elem := reflect.ValueOf(stats).Elem()
	typeOfElem := elem.Type()

	for i := 0; i < elem.NumField(); i++ {
		field := typeOfElem.Field(i)

		value := elem.Field(i).Uint()

		_, collect := StatsMap[field.Name]

		if metricName := field.Tag.Get("json"); collect && metricName != "" {
			if err := StatsdClient.Count(strings.Join([]string{netstatStatsMetricPrefix, metricName}, ""), int64(value), []string{}, 1); err != nil {
				Log.WithField("error", err).Error("Couldn't submit event to statsd.")
			}
		}
	}
}

func logNetstatStats() {
	stats, err := readNetstatStats()

	if err != nil {
		Log.WithField("error", err).Error("Error reading network device stats. Can't log netstat stats.")
	}

	jsonBytes, err := json.Marshal(stats)
	if err != nil {
		Log.WithField("error", err).Errorf("Error JSON marshalling: %+v.", stats)
	}

	Log.WithField("stat", NetstatStatPath).Infof(string(jsonBytes))
}
<|endoftext|>


<commit_before>package irc

import (
	"bufio"
	"io"
	"sync"
	"net"
)

const delim byte = '\n'

// A Conn represents an IRC network protocol connection.
// It consists of an Encoder and Decoder to manage I/O.
type Conn struct {
	Encoder
	Decoder

	conn io.ReadWriteCloser
}

// NewConn returns a new Conn using rwc for I/O.
func NewConn(rwc io.ReadWriteCloser) *Conn {
	return &Conn{
		Encoder: Encoder {
			writer: rwc,
		},
		Decoder: Decoder {
			reader: bufio.NewReader(rwc),
		},
		conn: rwc,
	}
}

// Dial connects to the given address using net.Dial and
// then returns a new Conn for the connection.
func Dial(addr string) (*Conn, error) {
	if c, err := net.Dial("tcp", addr); err != nil {
		return nil, err
	} else {
		return NewConn(c), nil
	}
}

// Send is an alias for Decode and implements the Sender interface.
func (c *Conn) Send(m *Message) error {
	return c.Encoder.Encode(m)
}

// Close closes the underlying ReadWriteCloser.
func (c *Conn) Close() error {
	return c.conn.Close()
}

// A decoder reads Message objects from an input stream.
type Decoder struct {
	reader *bufio.Reader
	line   string
	err    error
	mu     sync.Mutex
}

// NewDecoder returns a new Decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{
		reader: bufio.NewReader(r),
	}
}

// Decode attempts to read a single Message from the stream.
//
// Returns a non-nil error if the read failed.
func (dec *Decoder) Decode() (*Message, error) {

	dec.mu.Lock()
	defer dec.mu.Unlock()

	dec.line, dec.err = dec.reader.ReadString(delim)
	if dec.err != nil {
		return nil, dec.err
	}

	return ParseMessage(dec.line), nil
}

// An encoder writes Message objects to an output stream.
type Encoder struct {
	writer io.Writer
	err    error
	mu     sync.Mutex
}

// NewEncoder returns a new Encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{
		writer: w,
	}
}

// Encode writes the IRC encoding of m to the stream.
//
// This method may be used from multiple goroutines.
//
// Returns an non-nil error if the write to the underlying stream stopped early.
func (enc *Encoder) Encode(m *Message) error {

	enc.mu.Lock()
	defer enc.mu.Unlock()

	if _, enc.err = enc.writer.Write(m.Bytes()); enc.err != nil {
		return enc.err
	}
	return nil
}
<commit_msg>Fixed typo in Send comment.<commit_after>package irc

import (
	"bufio"
	"io"
	"sync"
	"net"
)

const delim byte = '\n'

// A Conn represents an IRC network protocol connection.
// It consists of an Encoder and Decoder to manage I/O.
type Conn struct {
	Encoder
	Decoder

	conn io.ReadWriteCloser
}

// NewConn returns a new Conn using rwc for I/O.
func NewConn(rwc io.ReadWriteCloser) *Conn {
	return &Conn{
		Encoder: Encoder {
			writer: rwc,
		},
		Decoder: Decoder {
			reader: bufio.NewReader(rwc),
		},
		conn: rwc,
	}
}

// Dial connects to the given address using net.Dial and
// then returns a new Conn for the connection.
func Dial(addr string) (*Conn, error) {
	if c, err := net.Dial("tcp", addr); err != nil {
		return nil, err
	} else {
		return NewConn(c), nil
	}
}

// Send is an alias for Encode and implements the Sender interface.
func (c *Conn) Send(m *Message) error {
	return c.Encoder.Encode(m)
}

// Close closes the underlying ReadWriteCloser.
func (c *Conn) Close() error {
	return c.conn.Close()
}

// A decoder reads Message objects from an input stream.
type Decoder struct {
	reader *bufio.Reader
	line   string
	err    error
	mu     sync.Mutex
}

// NewDecoder returns a new Decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{
		reader: bufio.NewReader(r),
	}
}

// Decode attempts to read a single Message from the stream.
//
// Returns a non-nil error if the read failed.
func (dec *Decoder) Decode() (*Message, error) {

	dec.mu.Lock()
	defer dec.mu.Unlock()

	dec.line, dec.err = dec.reader.ReadString(delim)
	if dec.err != nil {
		return nil, dec.err
	}

	return ParseMessage(dec.line), nil
}

// An encoder writes Message objects to an output stream.
type Encoder struct {
	writer io.Writer
	err    error
	mu     sync.Mutex
}

// NewEncoder returns a new Encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
	return &Encoder{
		writer: w,
	}
}

// Encode writes the IRC encoding of m to the stream.
//
// This method may be used from multiple goroutines.
//
// Returns an non-nil error if the write to the underlying stream stopped early.
func (enc *Encoder) Encode(m *Message) error {

	enc.mu.Lock()
	defer enc.mu.Unlock()

	if _, enc.err = enc.writer.Write(m.Bytes()); enc.err != nil {
		return enc.err
	}
	return nil
}
<|endoftext|>


<commit_before>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-08-22
//  Update on 2014-12-08
//  Email  slowfei(#)foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"container/list"
	"github.com/slowfei/gosfcore/encoding/json"
	"github.com/slowfei/gosfcore/utils/filemanager"
	"io/ioutil"
	"os"
	"regexp"
)

var (
	_defaultAbout = []byte(`
## About
------

gosfdoc document generator

More references: [https://github.com/slowfei/gosfdoc][0]

The MIT license (MIT) - [http://opensource.org/licenses/MIT][1]

Copyright (c) 2014 slowfei

Email: slowfei#foxmail.com

[0]:https://github.com/slowfei/gosfdoc
[1]:http://opensource.org/licenses/MIT
`)

	_defaultIntro = []byte(`
##Document Introduction

Sorry! Document author did not write any information.

----
This is a good tool, Can help you make beautiful documents.

More references: [https://github.com/slowfei/gosfdoc][0]

[0]:https://github.com/slowfei/gosfdoc
`)
)

/**
 *  file content buffer
 */
type FileBuf struct {
	path       string
	fileInfo   os.FileInfo
	buf        []byte
	lineLenSum []int // 记录每行长度的总和
}

/**
 *  new file buffer
 *
 *  @param `fileContent`
 *  @param `path` file path
 *  @param `info` file info
 *  @param replace regexp, replace text to empty(''), call regexp.ReplaceAll func
 */
func NewFileBuf(fileContent []byte, path string, info os.FileInfo, filter *regexp.Regexp) *FileBuf {
	buf := new(FileBuf)
	if nil != filter {
		buf.buf = filter.ReplaceAll(fileContent, nil)
	} else {
		buf.buf = fileContent
	}

	if 0 != len(buf.buf) {
		buf.lineLenSum = make([]int, 0, 0)
		count := len(buf.buf)
		for i := 0; i < count; i++ {
			b := buf.buf[i]
			if '\n' == b {
				buf.lineLenSum = append(buf.lineLenSum, i)
			}
		}
		//	add end line
		buf.lineLenSum = append(buf.lineLenSum, count)
	}

	buf.fileInfo = info
	buf.path = path
	return buf
}

/**
 *	out file
 *
 *	@param `path` out path
 */
func (f *FileBuf) WriteFilepath(path string) error {
	return SFFileManager.WirteFilepath(path, f.buf)
}

/**
 *  regexp find bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) Find(rex *regexp.Regexp) []byte {
	return rex.Find(f.buf)
}

/**
 *  regexp find all bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) FindAll(rex *regexp.Regexp) [][]byte {
	return rex.FindAll(f.buf, -1)
}

/**
 *	Regexp.FindAllSubmatch
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) FindAllSubmatch(rex *regexp.Regexp) [][][]byte {
	return rex.FindAllSubmatch(f.buf, -1)
}

/**
 *	Regexp.FindAllSubmatchIndex
 *
 *	@param `rex`
 *	@return
 */
func (f *FileBuf) FindAllSubmatchIndex(rex *regexp.Regexp) [][]int {
	return rex.FindAllSubmatchIndex(f.buf, -1)
}

/**
 *	line number by begin and end index
 *
 *	@param `beginIndex` buffer byte begin index
 *	@param `endIndex`	end index
 *	@return []int [start line,end line], line number 1 start.
 */
func (f *FileBuf) LineNumberByIndex(beginIndex, endIndex int) []int {
	result := []int{-1, -1}

	if endIndex > beginIndex {
		beginIndex, endIndex = endIndex, beginIndex
	}

	isBegin := false
	isEnd := false

	/*
		text:
		abcde  [a=0,b=1,c=2,d=3,e=4,\n=5]
		fghij  [f=6,g=8,h=9,i=10,j=11]

	*/

	lineLen := len(f.lineLenSum)
	for i := 0; i < lineLen; i++ {
		lineSum := f.lineLenSum[i]

		if !isBegin && lineSum >= beginIndex {
			result[0] = i + 1
			isBegin = true
		}

		if !isEnd && lineSum >= endIndex {
			result[1] = i + 1
			isEnd = true
		}

		if isBegin && isEnd {
			break
		}
	}

	if !isBegin || !isEnd {
		result[0] = -1
		result[1] = -1
	}

	return result
}

/**
 *	get row content by line number 1 start.
 *
 *	@param `lineNumber` line number
 *	@param	content of the specified line number
 */
func (f *FileBuf) RowByIndex(lineNumber int) []byte {
	var result []byte = nil
	lineLen := len(f.lineLenSum)

	if 0 >= lineNumber {
		lineNumber = 1
	}

	if 0 < lineLen && lineLen >= lineNumber {
		realIndex := lineNumber - 1
		upIndex := realIndex - 1 // 上一行下标，

		/*
			text:
			12345
			67890

			line length sum:
			[0] = 5 = len("12345\n")
			[1] = 11 = len("67890")

			lineNumber = 2; 获取第二行数据 buf[5+1,11]; (5+1)除去上一节点的换行
		*/

		startIndex := 0
		endIndex := f.lineLenSum[realIndex]

		if 0 <= upIndex {
			startIndex = f.lineLenSum[upIndex] + 1
		}

		result = f.SubBytes(startIndex, endIndex)
	}

	return result
}

/**
 *	extracts the file buffer from a bytes
 *
 *	@param `beginIndex`
 *	@param `endIndex`
 *	@return bytes
 */
func (f *FileBuf) SubBytes(beginIndex, endIndex int) []byte {
	if beginIndex > endIndex {
		return nil
	}

	if 0 > beginIndex {
		beginIndex = 0
	}

	if beginIndex == endIndex {
		endIndex = endIndex + 1
	}

	bufLen := len(f.buf)
	if bufLen < endIndex {
		endIndex = bufLen
	}

	result := f.buf[beginIndex:endIndex]
	return result
}

/**
 *  get file path
 *
 *  @return
 */
func (f *FileBuf) Path() string {
	return f.path
}

/**
 *  get file info
 *
 *  @return
 */
func (f *FileBuf) FileInfo() os.FileInfo {
	return f.fileInfo
}

/**
 *  buffer to string
 *
 *  @return
 */
func (f *FileBuf) String() string {
	return string(f.buf)
}

/**
 *  source code file
 */
type CodeFile struct {
	parser      DocParser  // file parser
	docs        []Document // current file public documents
	FileCont    *FileBuf   // file buffer content
	PrivateDoc  bool       // if private document not output
	PrivateCode bool       // if private source code not output
}

/**
 *  source code file list
 */
type CodeFiles struct {
	files *list.List
}

/**
 *  new CodeFiles
 */
func NewCodeFiles() *CodeFiles {
	cf := new(CodeFiles)
	cf.files = list.New()
	return cf
}

/**
 *  add file
 *
 *  @param file
 */
func (c *CodeFiles) addFile(file CodeFile) {
	if nil == c.files {
		c.files = list.New()
	}
	c.files.PushBack(file)
}

/**
 *  each CodeFile
 *
 *  @param `f` func return true continue
 */
func (c *CodeFiles) Each(f func(file CodeFile) bool) {
	if nil == f {
		return
	}
	for e := c.files.Front(); e != nil; e = e.Next() {
		if !f(e.Value.(CodeFile)) {
			break
		}
	}
}

/**
 *	file list storage length
 *
 *	@return file number
 */
func (c *CodeFiles) FilesLen() int {
	return c.files.Len()
}

/**
 *  output `content.json`
 */
type ContentJson struct {
	HtmlTitle string // html document title
	DocTitle  string // html top show title
	MenuTitle string // html left menu title
}

/**
 *	output write file path
 */
func (c ContentJson) WriteFilepath(path string) error {
	json, err := SFJson.NewJson(c, "", "")
	if nil != err {
		return err
	}
	return json.WriteFilepath(path, true)
}

/**
 *	markdown about
 */
type About struct {
	Content []byte
}

/**
 *	new default about
 *
 *	@return pointer type
 */
func NewDefaultAbout() *About {
	return &About{Content: _defaultAbout}
}

/**
 *	output file
 *
 *	@param `path` output full path
 *	@return
 */
func (a *About) WriteFilepath(path string) error {
	if 0 == len(a.Content) {
		a.Content = _defaultAbout
	}
	return ioutil.WriteFile(path, a.Content, 0660)
}

/**
 *	markdown intro
 */
type Intro struct {
	Content []byte
}

/**
 *	new default intro
 *
 *	@return pointer type
 */
func NewDefaultIntro() *Intro {
	return &Intro{Content: _defaultIntro}
}

/**
 *	output file
 *
 *	@param `path` output full path
 *	@return
 */
func (c *Intro) WriteFilepath(path string) error {
	if 0 == len(c.Content) {
		c.Content = _defaultIntro
	}
	return ioutil.WriteFile(path, c.Content, 0660)
}

/**
 *  package info
 */
type PackageInfo struct {
	menuName string `json:"-"` // type belongs
	Name     string // package name plain text
	Desc     string // description plain text
}

type FileLink struct {
	menuName string `json:"-"` // type belongs
	Filename string // a tag show text
	Link     string // a tag link

}

/**
 *  document struct info
 */
type Document struct {
	SortTag int    // sort tag
	Title   string // title plain text
	Content string // markdown text or plain text
}

/**
 *  preview struct info
 */
type Preview struct {
	SortTag  string // sort tag
	Level    int    // hierarchy level show. 0 is >, 1 is >>, 3 is >>> ...(markdown syntax)
	ShowText string // show plain text
	Anchor   string // preferably unique, with the func link
	DescText string // markdown brief description or implement objects, can empty.
}

/**
 *  body code block struct
 */
type CodeBlock struct {
	SortTag        string // sort tag
	MenuTitle      string // left navigation menu title
	Title          string // function name or custom title
	Anchor         string // function anchor text.
	Desc           string // description markdown text or plain text
	Code           string // show code text
	CodeLang       string // source code lang type string
	SourceFileName string // source code file name
	FileLines      []int  // block where the file line [5,10] is L5-L10
}

/**
 *	Preview,CodeBlock,Document sort implement
 */
type SortSet struct {
	previews   []Preview
	documents  []Document
	codeBlocks []CodeBlock
}

/**
 *	sort Len() implement
 */
func (s SortSet) Len() int {

	if 0 != len(s.previews) {
		return len(s.previews)
	} else if 0 != len(s.documents) {
		return len(s.documents)
	} else if 0 != len(s.codeBlocks) {
		return len(s.codeBlocks)
	} else {
		return 0
	}

}

/**
 *	sort Less(...) implement
 */
func (s SortSet) Less(i, j int) bool {

	if 0 != len(s.previews) {
		return s.previews[i].SortTag < s.previews[j].SortTag
	} else if 0 != len(s.documents) {
		return s.documents[i].SortTag < s.documents[j].SortTag
	} else if 0 != len(s.codeBlocks) {
		return s.codeBlocks[i].SortTag < s.codeBlocks[j].SortTag
	} else {
		return false
	}

}

/**
 *	sort Swap(...) implement
 */
func (s SortSet) Swap(i, j int) {

	if 0 != len(s.previews) {
		s.previews[i], s.previews[j] = s.previews[j], s.previews[i]
	} else if 0 != len(s.documents) {
		s.documents[i], s.documents[j] = s.documents[j], s.documents[i]
	} else if 0 != len(s.codeBlocks) {
		s.codeBlocks[i], s.codeBlocks[j] = s.codeBlocks[j], s.codeBlocks[i]
	}

}
<commit_msg>增加：FileBuf获取总行数LineLen()<commit_after>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-08-22
//  Update on 2014-12-11
//  Email  slowfei(#)foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"container/list"
	"github.com/slowfei/gosfcore/encoding/json"
	"github.com/slowfei/gosfcore/utils/filemanager"
	"io/ioutil"
	"os"
	"regexp"
)

var (
	_defaultAbout = []byte(`
## About
------

gosfdoc document generator

More references: [https://github.com/slowfei/gosfdoc][0]

The MIT license (MIT) - [http://opensource.org/licenses/MIT][1]

Copyright (c) 2014 slowfei

Email: slowfei#foxmail.com

[0]:https://github.com/slowfei/gosfdoc
[1]:http://opensource.org/licenses/MIT
`)

	_defaultIntro = []byte(`
##Document Introduction

Sorry! Document author did not write any information.

----
This is a good tool, Can help you make beautiful documents.

More references: [https://github.com/slowfei/gosfdoc][0]

[0]:https://github.com/slowfei/gosfdoc
`)
)

/**
 *  file content buffer
 */
type FileBuf struct {
	path       string
	fileInfo   os.FileInfo
	buf        []byte
	lineLenSum []int // 记录每行长度的总和
}

/**
 *  new file buffer
 *
 *  @param `fileContent`
 *  @param `path` file path
 *  @param `info` file info
 *  @param replace regexp, replace text to empty(''), call regexp.ReplaceAll func
 */
func NewFileBuf(fileContent []byte, path string, info os.FileInfo, filter *regexp.Regexp) *FileBuf {
	buf := new(FileBuf)
	if nil != filter {
		buf.buf = filter.ReplaceAll(fileContent, nil)
	} else {
		buf.buf = fileContent
	}

	if 0 != len(buf.buf) {
		buf.lineLenSum = make([]int, 0, 0)
		count := len(buf.buf)
		for i := 0; i < count; i++ {
			b := buf.buf[i]
			if '\n' == b {
				buf.lineLenSum = append(buf.lineLenSum, i)
			}
		}
		//	add end line
		buf.lineLenSum = append(buf.lineLenSum, count)
	}

	buf.fileInfo = info
	buf.path = path
	return buf
}

/**
 *	out file
 *
 *	@param `path` out path
 */
func (f *FileBuf) WriteFilepath(path string) error {
	return SFFileManager.WirteFilepath(path, f.buf)
}

/**
 *  regexp find bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) Find(rex *regexp.Regexp) []byte {
	return rex.Find(f.buf)
}

/**
 *  regexp find all bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) FindAll(rex *regexp.Regexp) [][]byte {
	return rex.FindAll(f.buf, -1)
}

/**
 *	Regexp.FindAllSubmatch
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) FindAllSubmatch(rex *regexp.Regexp) [][][]byte {
	return rex.FindAllSubmatch(f.buf, -1)
}

/**
 *	Regexp.FindAllSubmatchIndex
 *
 *	@param `rex`
 *	@return
 */
func (f *FileBuf) FindAllSubmatchIndex(rex *regexp.Regexp) [][]int {
	return rex.FindAllSubmatchIndex(f.buf, -1)
}

/**
 *	line number by begin and end index
 *
 *	@param `beginIndex` buffer byte begin index
 *	@param `endIndex`	end index
 *	@return []int [start line,end line], line number 1 start.
 */
func (f *FileBuf) LineNumberByIndex(beginIndex, endIndex int) []int {
	result := []int{-1, -1}

	if endIndex > beginIndex {
		beginIndex, endIndex = endIndex, beginIndex
	}

	isBegin := false
	isEnd := false

	/*
		text:
		abcde  [a=0,b=1,c=2,d=3,e=4,\n=5]
		fghij  [f=6,g=8,h=9,i=10,j=11]

	*/

	lineLen := len(f.lineLenSum)
	for i := 0; i < lineLen; i++ {
		lineSum := f.lineLenSum[i]

		if !isBegin && lineSum >= beginIndex {
			result[0] = i + 1
			isBegin = true
		}

		if !isEnd && lineSum >= endIndex {
			result[1] = i + 1
			isEnd = true
		}

		if isBegin && isEnd {
			break
		}
	}

	if !isBegin || !isEnd {
		result[0] = -1
		result[1] = -1
	}

	return result
}

/**
 *	get row content by line number 1 start.
 *
 *	@param `lineNumber` line number
 *	@param	content of the specified line number
 */
func (f *FileBuf) RowByIndex(lineNumber int) []byte {
	var result []byte = nil
	lineLen := len(f.lineLenSum)

	if 0 >= lineNumber {
		lineNumber = 1
	}

	if 0 < lineLen && lineLen >= lineNumber {
		realIndex := lineNumber - 1
		upIndex := realIndex - 1 // 上一行下标，

		/*
			text:
			12345
			67890

			line length sum:
			[0] = 5 = len("12345\n")
			[1] = 11 = len("67890")

			lineNumber = 2; 获取第二行数据 buf[5+1,11]; (5+1)除去上一节点的换行
		*/

		startIndex := 0
		endIndex := f.lineLenSum[realIndex]

		if 0 <= upIndex {
			startIndex = f.lineLenSum[upIndex] + 1
		}

		result = f.SubBytes(startIndex, endIndex)
	}

	return result
}

/**
 *	extracts the file buffer from a bytes
 *
 *	@param `beginIndex`
 *	@param `endIndex`
 *	@return bytes
 */
func (f *FileBuf) SubBytes(beginIndex, endIndex int) []byte {
	if beginIndex > endIndex {
		return nil
	}

	if 0 > beginIndex {
		beginIndex = 0
	}

	if beginIndex == endIndex {
		endIndex = endIndex + 1
	}

	bufLen := len(f.buf)
	if bufLen < endIndex {
		endIndex = bufLen
	}

	result := f.buf[beginIndex:endIndex]
	return result
}

/**
 *	get line length
 *
 *	@return int
 */
func (f *FileBuf) LineLen() int {
	return len(f.lineLenSum)
}

/**
 *  get file path
 *
 *  @return
 */
func (f *FileBuf) Path() string {
	return f.path
}

/**
 *  get file info
 *
 *  @return
 */
func (f *FileBuf) FileInfo() os.FileInfo {
	return f.fileInfo
}

/**
 *  buffer to string
 *
 *  @return
 */
func (f *FileBuf) String() string {
	return string(f.buf)
}

/**
 *  source code file
 */
type CodeFile struct {
	parser      DocParser  // file parser
	docs        []Document // current file public documents
	FileCont    *FileBuf   // file buffer content
	PrivateDoc  bool       // if private document not output
	PrivateCode bool       // if private source code not output
}

/**
 *  source code file list
 */
type CodeFiles struct {
	files *list.List
}

/**
 *  new CodeFiles
 */
func NewCodeFiles() *CodeFiles {
	cf := new(CodeFiles)
	cf.files = list.New()
	return cf
}

/**
 *  add file
 *
 *  @param file
 */
func (c *CodeFiles) addFile(file CodeFile) {
	if nil == c.files {
		c.files = list.New()
	}
	c.files.PushBack(file)
}

/**
 *  each CodeFile
 *
 *  @param `f` func return true continue
 */
func (c *CodeFiles) Each(f func(file CodeFile) bool) {
	if nil == f {
		return
	}
	for e := c.files.Front(); e != nil; e = e.Next() {
		if !f(e.Value.(CodeFile)) {
			break
		}
	}
}

/**
 *	file list storage length
 *
 *	@return file number
 */
func (c *CodeFiles) FilesLen() int {
	return c.files.Len()
}

/**
 *  output `content.json`
 */
type ContentJson struct {
	HtmlTitle string // html document title
	DocTitle  string // html top show title
	MenuTitle string // html left menu title
}

/**
 *	output write file path
 */
func (c ContentJson) WriteFilepath(path string) error {
	json, err := SFJson.NewJson(c, "", "")
	if nil != err {
		return err
	}
	return json.WriteFilepath(path, true)
}

/**
 *	markdown about
 */
type About struct {
	Content []byte
}

/**
 *	new default about
 *
 *	@return pointer type
 */
func NewDefaultAbout() *About {
	return &About{Content: _defaultAbout}
}

/**
 *	output file
 *
 *	@param `path` output full path
 *	@return
 */
func (a *About) WriteFilepath(path string) error {
	if 0 == len(a.Content) {
		a.Content = _defaultAbout
	}
	return ioutil.WriteFile(path, a.Content, 0660)
}

/**
 *	markdown intro
 */
type Intro struct {
	Content []byte
}

/**
 *	new default intro
 *
 *	@return pointer type
 */
func NewDefaultIntro() *Intro {
	return &Intro{Content: _defaultIntro}
}

/**
 *	output file
 *
 *	@param `path` output full path
 *	@return
 */
func (c *Intro) WriteFilepath(path string) error {
	if 0 == len(c.Content) {
		c.Content = _defaultIntro
	}
	return ioutil.WriteFile(path, c.Content, 0660)
}

/**
 *  package info
 */
type PackageInfo struct {
	menuName string `json:"-"` // type belongs
	Name     string // package name plain text
	Desc     string // description plain text
}

type FileLink struct {
	menuName string `json:"-"` // type belongs
	Filename string // a tag show text
	Link     string // a tag link

}

/**
 *  document struct info
 */
type Document struct {
	SortTag int    // sort tag
	Title   string // title plain text
	Content string // markdown text or plain text
}

/**
 *  preview struct info
 */
type Preview struct {
	SortTag  string // sort tag
	Level    int    // hierarchy level show. 0 is >, 1 is >>, 3 is >>> ...(markdown syntax)
	ShowText string // show plain text
	Anchor   string // preferably unique, with the func link
	DescText string // markdown brief description or implement objects, can empty.
}

/**
 *  body code block struct
 */
type CodeBlock struct {
	SortTag        string // sort tag
	MenuTitle      string // left navigation menu title
	Title          string // function name or custom title
	Anchor         string // function anchor text.
	Desc           string // description markdown text or plain text
	Code           string // show code text
	CodeLang       string // source code lang type string
	SourceFileName string // source code file name
	FileLines      []int  // block where the file line [5,10] is L5-L10
}

/**
 *	Preview,CodeBlock,Document sort implement
 */
type SortSet struct {
	previews   []Preview
	documents  []Document
	codeBlocks []CodeBlock
}

/**
 *	sort Len() implement
 */
func (s SortSet) Len() int {

	if 0 != len(s.previews) {
		return len(s.previews)
	} else if 0 != len(s.documents) {
		return len(s.documents)
	} else if 0 != len(s.codeBlocks) {
		return len(s.codeBlocks)
	} else {
		return 0
	}

}

/**
 *	sort Less(...) implement
 */
func (s SortSet) Less(i, j int) bool {

	if 0 != len(s.previews) {
		return s.previews[i].SortTag < s.previews[j].SortTag
	} else if 0 != len(s.documents) {
		return s.documents[i].SortTag < s.documents[j].SortTag
	} else if 0 != len(s.codeBlocks) {
		return s.codeBlocks[i].SortTag < s.codeBlocks[j].SortTag
	} else {
		return false
	}

}

/**
 *	sort Swap(...) implement
 */
func (s SortSet) Swap(i, j int) {

	if 0 != len(s.previews) {
		s.previews[i], s.previews[j] = s.previews[j], s.previews[i]
	} else if 0 != len(s.documents) {
		s.documents[i], s.documents[j] = s.documents[j], s.documents[i]
	} else if 0 != len(s.codeBlocks) {
		s.codeBlocks[i], s.codeBlocks[j] = s.codeBlocks[j], s.codeBlocks[i]
	}

}
<|endoftext|>


<commit_before>package fzf

import (
	"fmt"
	"os"
	"runtime"
	"time"
)

const COORDINATOR_DELAY time.Duration = 100 * time.Millisecond

func initProcs() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

/*
Reader   -> EVT_READ_FIN
Reader   -> EVT_READ_NEW        -> Matcher  (restart)
Terminal -> EVT_SEARCH_NEW      -> Matcher  (restart)
Matcher  -> EVT_SEARCH_PROGRESS -> Terminal (update info)
Matcher  -> EVT_SEARCH_FIN      -> Terminal (update list)
*/

func Run(options *Options) {
	initProcs()

	opts := ParseOptions()

	if opts.Version {
		fmt.Println(VERSION)
		os.Exit(0)
	}

	// Event channel
	eventBox := NewEventBox()

	// Chunk list
	var chunkList *ChunkList
	if len(opts.WithNth) == 0 {
		chunkList = NewChunkList(func(data *string, index int) *Item {
			return &Item{text: data, index: index}
		})
	} else {
		chunkList = NewChunkList(func(data *string, index int) *Item {
			item := Item{text: data, index: index}
			tokens := Tokenize(item.text, opts.Delimiter)
			item.origText = item.text
			item.text = Transform(tokens, opts.WithNth).whole
			return &item
		})
	}

	// Reader
	reader := Reader{func(str string) { chunkList.Push(str) }, eventBox}
	go reader.ReadSource()

	// Matcher
	patternBuilder := func(runes []rune) *Pattern {
		return BuildPattern(
			opts.Mode, opts.Case, opts.Nth, opts.Delimiter, runes)
	}
	matcher := NewMatcher(patternBuilder, opts.Sort > 0, eventBox)

	// Defered-interactive / Non-interactive
	//   --select-1 | --exit-0 | --filter
	if filtering := opts.Filter != nil; filtering || opts.Select1 || opts.Exit0 {
		limit := 0
		var patternString string
		if filtering {
			patternString = *opts.Filter
		} else {
			if opts.Select1 || opts.Exit0 {
				limit = 1
			}
			patternString = opts.Query
		}
		pattern := patternBuilder([]rune(patternString))

		looping := true
		for looping {
			eventBox.Wait(func(events *Events) {
				for evt, _ := range *events {
					switch evt {
					case EVT_READ_FIN:
						looping = false
						return
					}
				}
			})
			time.Sleep(COORDINATOR_DELAY)
		}

		matches, cancelled := matcher.scan(MatchRequest{
			chunks:  chunkList.Snapshot(),
			pattern: pattern}, limit)

		if !cancelled && (filtering || opts.Exit0) {
			if opts.PrintQuery {
				fmt.Println(patternString)
			}
			for _, item := range matches {
				item.Print()
			}
			os.Exit(0)
		}
	}

	// Go interactive
	go matcher.Loop()

	// Terminal I/O
	terminal := NewTerminal(opts, eventBox)
	go terminal.Loop()

	// Event coordination
	reading := true
	ticks := 0
	for {
		delay := true
		ticks += 1
		eventBox.Wait(func(events *Events) {
			defer events.Clear()
			for evt, value := range *events {
				switch evt {

				case EVT_READ_NEW, EVT_READ_FIN:
					reading = reading && evt == EVT_READ_NEW
					terminal.UpdateCount(chunkList.Count(), !reading)
					matcher.Reset(chunkList.Snapshot(), terminal.Input(), false)

				case EVT_SEARCH_NEW:
					matcher.Reset(chunkList.Snapshot(), terminal.Input(), true)
					delay = false

				case EVT_SEARCH_PROGRESS:
					switch val := value.(type) {
					case float32:
						terminal.UpdateProgress(val)
					}

				case EVT_SEARCH_FIN:
					switch val := value.(type) {
					case []*Item:
						terminal.UpdateList(val)
					}
				}
			}
		})
		if ticks > 3 && delay && reading {
			time.Sleep(COORDINATOR_DELAY)
		}
	}
}
<commit_msg>Fix exit conditions of --select-1 and --exit-0<commit_after>package fzf

import (
	"fmt"
	"os"
	"runtime"
	"time"
)

const COORDINATOR_DELAY time.Duration = 100 * time.Millisecond

func initProcs() {
	runtime.GOMAXPROCS(runtime.NumCPU())
}

/*
Reader   -> EVT_READ_FIN
Reader   -> EVT_READ_NEW        -> Matcher  (restart)
Terminal -> EVT_SEARCH_NEW      -> Matcher  (restart)
Matcher  -> EVT_SEARCH_PROGRESS -> Terminal (update info)
Matcher  -> EVT_SEARCH_FIN      -> Terminal (update list)
*/

func Run(options *Options) {
	initProcs()

	opts := ParseOptions()

	if opts.Version {
		fmt.Println(VERSION)
		os.Exit(0)
	}

	// Event channel
	eventBox := NewEventBox()

	// Chunk list
	var chunkList *ChunkList
	if len(opts.WithNth) == 0 {
		chunkList = NewChunkList(func(data *string, index int) *Item {
			return &Item{text: data, index: index}
		})
	} else {
		chunkList = NewChunkList(func(data *string, index int) *Item {
			item := Item{text: data, index: index}
			tokens := Tokenize(item.text, opts.Delimiter)
			item.origText = item.text
			item.text = Transform(tokens, opts.WithNth).whole
			return &item
		})
	}

	// Reader
	reader := Reader{func(str string) { chunkList.Push(str) }, eventBox}
	go reader.ReadSource()

	// Matcher
	patternBuilder := func(runes []rune) *Pattern {
		return BuildPattern(
			opts.Mode, opts.Case, opts.Nth, opts.Delimiter, runes)
	}
	matcher := NewMatcher(patternBuilder, opts.Sort > 0, eventBox)

	// Defered-interactive / Non-interactive
	//   --select-1 | --exit-0 | --filter
	if filtering := opts.Filter != nil; filtering || opts.Select1 || opts.Exit0 {
		limit := 0
		var patternString string
		if filtering {
			patternString = *opts.Filter
		} else {
			if opts.Select1 || opts.Exit0 {
				limit = 1
			}
			patternString = opts.Query
		}
		pattern := patternBuilder([]rune(patternString))

		looping := true
		for looping {
			eventBox.Wait(func(events *Events) {
				for evt, _ := range *events {
					switch evt {
					case EVT_READ_FIN:
						looping = false
						return
					}
				}
			})
			time.Sleep(COORDINATOR_DELAY)
		}

		matches, cancelled := matcher.scan(MatchRequest{
			chunks:  chunkList.Snapshot(),
			pattern: pattern}, limit)

		if !cancelled && (filtering ||
			opts.Exit0 && len(matches) == 0 || opts.Select1 && len(matches) == 1) {
			if opts.PrintQuery {
				fmt.Println(patternString)
			}
			for _, item := range matches {
				item.Print()
			}
			os.Exit(0)
		}
	}

	// Go interactive
	go matcher.Loop()

	// Terminal I/O
	terminal := NewTerminal(opts, eventBox)
	go terminal.Loop()

	// Event coordination
	reading := true
	ticks := 0
	for {
		delay := true
		ticks += 1
		eventBox.Wait(func(events *Events) {
			defer events.Clear()
			for evt, value := range *events {
				switch evt {

				case EVT_READ_NEW, EVT_READ_FIN:
					reading = reading && evt == EVT_READ_NEW
					terminal.UpdateCount(chunkList.Count(), !reading)
					matcher.Reset(chunkList.Snapshot(), terminal.Input(), false)

				case EVT_SEARCH_NEW:
					matcher.Reset(chunkList.Snapshot(), terminal.Input(), true)
					delay = false

				case EVT_SEARCH_PROGRESS:
					switch val := value.(type) {
					case float32:
						terminal.UpdateProgress(val)
					}

				case EVT_SEARCH_FIN:
					switch val := value.(type) {
					case []*Item:
						terminal.UpdateList(val)
					}
				}
			}
		})
		if ticks > 3 && delay && reading {
			time.Sleep(COORDINATOR_DELAY)
		}
	}
}
<|endoftext|>


<commit_before>package sudoku

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
)

type Availables struct {
	Size    int
	Numbers map[int]bool
	Length  int
}

type Sudoku struct {
	Debug      bool
	Size       int
	SquareSize int
	Grid       [][]int
	Availables [][]Availables
	Groups     Groups
	BruteLimit int
	Missings   int
	Chars      string
	CharsSlice []string

	DoResolveNumbersThatAreOnlyInOnePosition bool
	DoResolveOnlyOne                         bool
}

type GroupType int

const (
	HorizontalGroup GroupType = iota
	VerticalGroup
	RegionGroup
)

type Groups map[GroupType][]Group

type Position struct {
	Y int
	X int
}

type Group struct {
	Size      int
	Positions []Position
}

func NewSudoku() Sudoku {
	return NewSudokuWithSize(3)
}

func (s *Sudoku) Clone(dest *Sudoku) {
	dest.Debug = s.Debug
	dest.Size = s.Size
	dest.SquareSize = s.SquareSize
	dest.BruteLimit = s.BruteLimit
	dest.Missings = s.Size * s.Size

	dest.DoResolveNumbersThatAreOnlyInOnePosition = s.DoResolveNumbersThatAreOnlyInOnePosition
	dest.DoResolveOnlyOne = s.DoResolveOnlyOne

	dest.initFields()
	dest.Groups = s.Groups

	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] > 0 {
				dest.SetNumber(y, x, s.Grid[y][x])
			}
		}
	}
}

func (s *Sudoku) initFields() {
	s.Grid = make([][]int, s.Size)
	s.Availables = make([][]Availables, s.Size)
	s.Groups = make(map[GroupType][]Group, 0)
	for i := 0; i < s.Size; i++ {
		s.Grid[i] = make([]int, s.Size)
		s.Availables[i] = make([]Availables, s.Size)
		for j := 0; j < s.Size; j++ {
			s.Availables[i][j] = NewAvailables(s.Size)
		}
	}
}

func NewSudokuWithSize(sqsize int) Sudoku {
	size := sqsize * sqsize
	sudoku := Sudoku{
		Debug:      os.Getenv("DEBUG") == "1",
		SquareSize: sqsize,
		Size:       size,
		BruteLimit: 2,
		Missings:   size * size,

		DoResolveNumbersThatAreOnlyInOnePosition: true,
		DoResolveOnlyOne:                         true,
	}
	if sqsize == 5 {
		sudoku.CharsSlice = []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"}
	} else {
		sudoku.CharsSlice = []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G"}
	}
	sudoku.Chars = strings.Join(sudoku.CharsSlice, "")

	sudoku.initFields()
	sudoku.initGroups()
	return sudoku
}

func (s *Sudoku) initGroups() {
	// horizontal groups
	for y := 0; y < s.Size; y++ {
		group := Group{}
		for x := 0; x < s.Size; x++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		s.Groups[HorizontalGroup] = append(s.Groups[HorizontalGroup], group)
	}

	// vertical groups
	for x := 0; x < s.Size; x++ {
		group := Group{}
		for y := 0; y < s.Size; y++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		s.Groups[VerticalGroup] = append(s.Groups[VerticalGroup], group)
	}
	// zone groups
	for a := 0; a < s.SquareSize; a++ {
		for b := 0; b < s.SquareSize; b++ {
			group := Group{}
			for c := 0; c < s.SquareSize; c++ {
				for d := 0; d < s.SquareSize; d++ {
					y := a*s.SquareSize + c
					x := b*s.SquareSize + d
					group.Positions = append(group.Positions, Position{y, x})
				}
			}
			s.Groups[RegionGroup] = append(s.Groups[RegionGroup], group)
		}
	}
}

func NewAvailables(size int) Availables {
	availables := Availables{
		Size:    size,
		Numbers: make(map[int]bool, size),
		Length:  size,
	}
	for i := 1; i <= size; i++ {
		availables.Numbers[i] = true
	}
	return availables
}

func (a *Availables) String() string {
	output := ""
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			output += strconv.Itoa(i)
		} else {
			output += "."
		}
	}
	return output
}

func (a *Availables) SetNumber(number int) {
	for i := 1; i <= a.Size; i++ {
		a.Numbers[i] = i == number
	}
	a.Length = 1
}

func (a *Availables) Availables() []int {
	availables := []int{}
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			availables = append(availables, i)
		}
	}
	return availables
}

func (a *Availables) RemoveNumber(number int) bool {
	if a.Numbers[number] {
		a.Length--
		a.Numbers[number] = false
		return true
	}
	return false
}

func (g *Groups) AllGroups() []Group {
	groups := []Group{}
	for _, typeGroups := range *g {
		groups = append(groups, typeGroups...)
	}
	return groups
}

func (g *Groups) MatchCoords(y, x int) []Group {
	groups := []Group{}
	for _, group := range g.AllGroups() {
		for _, pos := range group.Positions {
			if pos.Y == y && pos.X == x {
				groups = append(groups, group)
				break
			}
		}
	}
	return groups
}

func (s *Sudoku) SetNumber(y, x, number int) {
	s.Grid[y][x] = number
	s.Availables[y][x].SetNumber(number)
	s.Missings--
	for _, group := range s.Groups.MatchCoords(y, x) {
		for _, pos := range group.Positions {
			s.Availables[pos.Y][pos.X].RemoveNumber(number)
		}
	}
}

func (s *Sudoku) ParseString(input string) error {
	input = strings.TrimSpace(input)
	lines := strings.Split(input, "\n")
	for y, line := range lines[1 : s.Size+1] {
		for x := 0; x < s.Size; x++ {
			col := line[1+x*2 : 1+x*2+1]
			if col == " " {
				continue
			}
			idx := strings.Index(s.Chars, col)
			if idx == -1 {
				return fmt.Errorf("Invalid character: %q", col)
			}
			s.SetNumber(y, x, idx+1)
		}
	}
	return nil
}

func (s *Sudoku) String() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))

	for _, gridLine := range s.Grid {
		line := []string{}
		for _, col := range gridLine {
			line = append(line, s.CharsSlice[col-1])
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lineStr = strings.Replace(lineStr, "0", " ", -1)
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) AvailablesString() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	for _, availablesLine := range s.Availables {
		line := []string{}
		for _, availables := range availablesLine {
			line = append(line, availables.String())
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) ResolveOnlyOne() int {
	changed := 0
	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] != 0 {
				continue
			}
			if s.Availables[y][x].Length == 1 {
				s.SetNumber(y, x, s.Availables[y][x].Availables()[0])
				changed++
			}
		}
	}
	return changed
}

func (s *Sudoku) ResolveNumbersThatAreOnlyInOnePosition() int {
	changes := 0
	for _, group := range s.Groups.AllGroups() {
		for number := 1; number <= s.Size; number++ {
			count := 0
			for _, pos := range group.Positions {
				if s.Availables[pos.Y][pos.X].Numbers[number] {
					if count++; count > 1 {
						break
					}
				}
			}
			if count != 1 {
				continue
			}
			for _, pos := range group.Positions {
				if s.Availables[pos.Y][pos.X].Numbers[number] {
					s.SetNumber(pos.Y, pos.X, number)
					changes++
				}
			}
		}
	}
	return changes
}

func (s *Sudoku) ResolveNonBrute(depth int) {
	changes := 0
	iteration := 0
	kind := "start"

start:
	if s.Missings == 0 {
		return
	}
	if s.Debug {
		logrus.Infof("#######  depth=%-2d iteration=%-3d changes=%-2d missings=%d kind=%s\n%s\n%s", depth, iteration, changes, s.Missings, kind, s.String(), s.AvailablesString())
		logrus.Infof(strings.Repeat("#", 42))
	}
	iteration++

	if s.DoResolveOnlyOne {
		kind = "resolve-only-one"
		if changes = s.ResolveOnlyOne(); changes > 0 {
			goto start
		}
	}

	if s.DoResolveNumbersThatAreOnlyInOnePosition {
		kind = "resolve-numbers-that-are-only-in-one-position"
		if changes = s.ResolveNumbersThatAreOnlyInOnePosition(); changes > 0 {
			goto start
		}
	}
}

type Clone struct {
	Sudoku      *Sudoku
	BrutePos    Position
	BruteNumber int
}

func (s *Sudoku) ResolveRec(depth int) (*Sudoku, error) {
	s.ResolveNonBrute(depth)
	if s.Missings == 0 {
		return s, nil
	}

	if depth >= s.BruteLimit {
		return s, fmt.Errorf("Too deep")
	}

	var clones []Clone
	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Availables[y][x].Length == 0 {
				continue
			}
			for i := 0; i < s.Size; i++ {
				if !s.Availables[y][x].Numbers[i] {
					continue
				}
				clones = append(clones, Clone{
					Sudoku:      &Sudoku{},
					BrutePos:    Position{Y: y, X: x},
					BruteNumber: i,
				})
			}
		}
	}

	for _, clone := range clones {
		s.Clone(clone.Sudoku)
		clone.Sudoku.SetNumber(clone.BrutePos.Y, clone.BrutePos.X, clone.BruteNumber)
		newSudoku, err := clone.Sudoku.ResolveRec(depth + 1)
		if err != nil {
			s.Availables[clone.BrutePos.Y][clone.BrutePos.X].RemoveNumber(clone.BruteNumber)
			continue
		}
		if newSudoku.Missings == 0 {
			return newSudoku, nil
		}
	}

	return s, nil
}

func (s *Sudoku) Resolve() error {
	newSudoku, err := s.ResolveRec(0)
	if err != nil {
		return err
	}

	if newSudoku != s {
		newSudoku.Clone(s)
	}

	return nil
}
<commit_msg>Avoid nil pointer dereference for unresolved sudokus<commit_after>package sudoku

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
)

type Availables struct {
	Size    int
	Numbers map[int]bool
	Length  int
}

type Sudoku struct {
	Debug      bool
	Size       int
	SquareSize int
	Grid       [][]int
	Availables [][]Availables
	Groups     Groups
	BruteLimit int
	Missings   int
	Chars      string
	CharsSlice []string

	DoResolveNumbersThatAreOnlyInOnePosition bool
	DoResolveOnlyOne                         bool
}

type GroupType int

const (
	HorizontalGroup GroupType = iota
	VerticalGroup
	RegionGroup
)

type Groups map[GroupType][]Group

type Position struct {
	Y int
	X int
}

type Group struct {
	Size      int
	Positions []Position
}

func NewSudoku() Sudoku {
	return NewSudokuWithSize(3)
}

func (s *Sudoku) Clone(dest *Sudoku) {
	dest.Debug = s.Debug
	dest.Size = s.Size
	dest.SquareSize = s.SquareSize
	dest.BruteLimit = s.BruteLimit
	dest.Missings = s.Size * s.Size

	dest.DoResolveNumbersThatAreOnlyInOnePosition = s.DoResolveNumbersThatAreOnlyInOnePosition
	dest.DoResolveOnlyOne = s.DoResolveOnlyOne

	dest.initFields()
	dest.Groups = s.Groups

	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] > 0 {
				dest.SetNumber(y, x, s.Grid[y][x])
			}
		}
	}
}

func (s *Sudoku) initFields() {
	s.Grid = make([][]int, s.Size)
	s.Availables = make([][]Availables, s.Size)
	s.Groups = make(map[GroupType][]Group, 0)
	for i := 0; i < s.Size; i++ {
		s.Grid[i] = make([]int, s.Size)
		s.Availables[i] = make([]Availables, s.Size)
		for j := 0; j < s.Size; j++ {
			s.Availables[i][j] = NewAvailables(s.Size)
		}
	}
}

func NewSudokuWithSize(sqsize int) Sudoku {
	size := sqsize * sqsize
	sudoku := Sudoku{
		Debug:      os.Getenv("DEBUG") == "1",
		SquareSize: sqsize,
		Size:       size,
		BruteLimit: 2,
		Missings:   size * size,

		DoResolveNumbersThatAreOnlyInOnePosition: true,
		DoResolveOnlyOne:                         true,
	}
	if sqsize == 5 {
		sudoku.CharsSlice = []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"}
	} else {
		sudoku.CharsSlice = []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G"}
	}
	sudoku.Chars = strings.Join(sudoku.CharsSlice, "")

	sudoku.initFields()
	sudoku.initGroups()
	return sudoku
}

func (s *Sudoku) initGroups() {
	// horizontal groups
	for y := 0; y < s.Size; y++ {
		group := Group{}
		for x := 0; x < s.Size; x++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		s.Groups[HorizontalGroup] = append(s.Groups[HorizontalGroup], group)
	}

	// vertical groups
	for x := 0; x < s.Size; x++ {
		group := Group{}
		for y := 0; y < s.Size; y++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		s.Groups[VerticalGroup] = append(s.Groups[VerticalGroup], group)
	}
	// zone groups
	for a := 0; a < s.SquareSize; a++ {
		for b := 0; b < s.SquareSize; b++ {
			group := Group{}
			for c := 0; c < s.SquareSize; c++ {
				for d := 0; d < s.SquareSize; d++ {
					y := a*s.SquareSize + c
					x := b*s.SquareSize + d
					group.Positions = append(group.Positions, Position{y, x})
				}
			}
			s.Groups[RegionGroup] = append(s.Groups[RegionGroup], group)
		}
	}
}

func NewAvailables(size int) Availables {
	availables := Availables{
		Size:    size,
		Numbers: make(map[int]bool, size),
		Length:  size,
	}
	for i := 1; i <= size; i++ {
		availables.Numbers[i] = true
	}
	return availables
}

func (a *Availables) String() string {
	output := ""
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			output += strconv.Itoa(i)
		} else {
			output += "."
		}
	}
	return output
}

func (a *Availables) SetNumber(number int) {
	for i := 1; i <= a.Size; i++ {
		a.Numbers[i] = i == number
	}
	a.Length = 1
}

func (a *Availables) Availables() []int {
	availables := []int{}
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			availables = append(availables, i)
		}
	}
	return availables
}

func (a *Availables) RemoveNumber(number int) bool {
	if a.Numbers[number] {
		a.Length--
		a.Numbers[number] = false
		return true
	}
	return false
}

func (g *Groups) AllGroups() []Group {
	groups := []Group{}
	for _, typeGroups := range *g {
		groups = append(groups, typeGroups...)
	}
	return groups
}

func (g *Groups) MatchCoords(y, x int) []Group {
	groups := []Group{}
	for _, group := range g.AllGroups() {
		for _, pos := range group.Positions {
			if pos.Y == y && pos.X == x {
				groups = append(groups, group)
				break
			}
		}
	}
	return groups
}

func (s *Sudoku) SetNumber(y, x, number int) {
	s.Grid[y][x] = number
	s.Availables[y][x].SetNumber(number)
	s.Missings--
	for _, group := range s.Groups.MatchCoords(y, x) {
		for _, pos := range group.Positions {
			s.Availables[pos.Y][pos.X].RemoveNumber(number)
		}
	}
}

func (s *Sudoku) ParseString(input string) error {
	input = strings.TrimSpace(input)
	lines := strings.Split(input, "\n")
	for y, line := range lines[1 : s.Size+1] {
		for x := 0; x < s.Size; x++ {
			col := line[1+x*2 : 1+x*2+1]
			if col == " " {
				continue
			}
			idx := strings.Index(s.Chars, col)
			if idx == -1 {
				return fmt.Errorf("Invalid character: %q", col)
			}
			s.SetNumber(y, x, idx+1)
		}
	}
	return nil
}

func (s *Sudoku) String() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))

	for _, gridLine := range s.Grid {
		line := []string{}
		for _, col := range gridLine {
			if col == 0 {
				line = append(line, " ")
			} else {
				line = append(line, s.CharsSlice[col-1])
			}
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lineStr = strings.Replace(lineStr, "0", " ", -1)
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) AvailablesString() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	for _, availablesLine := range s.Availables {
		line := []string{}
		for _, availables := range availablesLine {
			line = append(line, availables.String())
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) ResolveOnlyOne() int {
	changed := 0
	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] != 0 {
				continue
			}
			if s.Availables[y][x].Length == 1 {
				s.SetNumber(y, x, s.Availables[y][x].Availables()[0])
				changed++
			}
		}
	}
	return changed
}

func (s *Sudoku) ResolveNumbersThatAreOnlyInOnePosition() int {
	changes := 0
	for _, group := range s.Groups.AllGroups() {
		for number := 1; number <= s.Size; number++ {
			count := 0
			for _, pos := range group.Positions {
				if s.Availables[pos.Y][pos.X].Numbers[number] {
					if count++; count > 1 {
						break
					}
				}
			}
			if count != 1 {
				continue
			}
			for _, pos := range group.Positions {
				if s.Availables[pos.Y][pos.X].Numbers[number] {
					s.SetNumber(pos.Y, pos.X, number)
					changes++
				}
			}
		}
	}
	return changes
}

func (s *Sudoku) ResolveNonBrute(depth int) {
	changes := 0
	iteration := 0
	kind := "start"

start:
	if s.Missings == 0 {
		return
	}
	if s.Debug {
		logrus.Infof("#######  depth=%-2d iteration=%-3d changes=%-2d missings=%d kind=%s\n%s\n%s", depth, iteration, changes, s.Missings, kind, s.String(), s.AvailablesString())
		logrus.Infof(strings.Repeat("#", 42))
	}
	iteration++

	if s.DoResolveOnlyOne {
		kind = "resolve-only-one"
		if changes = s.ResolveOnlyOne(); changes > 0 {
			goto start
		}
	}

	if s.DoResolveNumbersThatAreOnlyInOnePosition {
		kind = "resolve-numbers-that-are-only-in-one-position"
		if changes = s.ResolveNumbersThatAreOnlyInOnePosition(); changes > 0 {
			goto start
		}
	}
}

type Clone struct {
	Sudoku      *Sudoku
	BrutePos    Position
	BruteNumber int
}

func (s *Sudoku) ResolveRec(depth int) (*Sudoku, error) {
	s.ResolveNonBrute(depth)
	if s.Missings == 0 {
		return s, nil
	}

	if depth >= s.BruteLimit {
		return s, fmt.Errorf("Too deep")
	}

	var clones []Clone
	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Availables[y][x].Length == 0 {
				continue
			}
			for i := 0; i < s.Size; i++ {
				if !s.Availables[y][x].Numbers[i] {
					continue
				}
				clones = append(clones, Clone{
					Sudoku:      &Sudoku{},
					BrutePos:    Position{Y: y, X: x},
					BruteNumber: i,
				})
			}
		}
	}

	for _, clone := range clones {
		s.Clone(clone.Sudoku)
		clone.Sudoku.SetNumber(clone.BrutePos.Y, clone.BrutePos.X, clone.BruteNumber)
		newSudoku, err := clone.Sudoku.ResolveRec(depth + 1)
		if err != nil {
			s.Availables[clone.BrutePos.Y][clone.BrutePos.X].RemoveNumber(clone.BruteNumber)
			continue
		}
		if newSudoku.Missings == 0 {
			return newSudoku, nil
		}
	}

	return s, nil
}

func (s *Sudoku) Resolve() error {
	newSudoku, err := s.ResolveRec(0)
	if err != nil {
		return err
	}

	if newSudoku != s {
		newSudoku.Clone(s)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"html/template"
	"net/http"
	"regexp"
	"strings"
)

type paste struct {
	Paste  []set
	Title  string
	Author string
	Notes  string
}

type set struct {
	Pokemon uint
	Form    uint
	Item    uint
	Text    template.HTML
}

var (
	reHead = regexp.MustCompile(`^(?:(.* \()([A-Z][a-z0-9:']+\.?(?:[- ][A-Za-z][a-z0-9:']*\.?)*)(\))|([A-Z][a-z0-9:']+\.?(?:[- ][A-Za-z][a-z0-9:']*\.?)*))(?:( \()([MF])(\)))?(?:( @ )([A-Z][a-z0-9:']+(?:[- ][A-Z][a-z0-9:']*)*))?( *)$`)
	reMove = regexp.MustCompile(`^(-)( *([A-Z][a-z\']*(?:[- ][A-Za-z][a-z\']*)*)(?: */ *[A-Z][a-z\']*(?:[- ][A-Za-z][a-z\']*)*)* *)$`)

	tmpl = template.Must(template.ParseFiles("paste.tmpl"))
)

func renderPaste(w http.ResponseWriter, text, title, author, notes []byte) {
	sets := bytes.Split(text, []byte("\r\n\r\n"))
	fpaste := paste{
		Paste:  make([]set, 0, len(sets)),
		Title:  string(title),
		Author: string(author),
		Notes:  string(notes),
	}

	for _, bset := range sets {
		if len(bset) == 0 {
			continue
		}

		var fset set
		var b strings.Builder

		lines := bytes.Split(bset, []byte("\r\n"))

		m := reHead.FindSubmatch(lines[0])
		if m == nil {
			template.HTMLEscape(&b, bset)
			fset.Text = template.HTML(b.String())
			fpaste.Paste = append(fpaste.Paste, fset)
			continue
		}

		if len(m[2]) != 0 {
			if p, ok := pokemonData[string(m[2])]; ok {
				fset.Pokemon = p["id"].(uint)
				fset.Form = p["form"].(uint)
				template.HTMLEscape(&b, m[1])
				b.WriteString(`<span class="type-`)
				b.WriteString(p["type"].(string))
				b.WriteString(`">`)
				template.HTMLEscape(&b, m[2])
				b.WriteString(`</span>`)
				template.HTMLEscape(&b, m[3])
			} else {
				template.HTMLEscape(&b, m[1])
				template.HTMLEscape(&b, m[2])
				template.HTMLEscape(&b, m[3])
			}
		} else if len(m[4]) != 0 {
			if p, ok := pokemonData[string(m[4])]; ok {
				fset.Pokemon = p["id"].(uint)
				fset.Form = p["form"].(uint)
				b.WriteString(`<span class="type-`)
				b.WriteString(p["type"].(string))
				b.WriteString(`">`)
				template.HTMLEscape(&b, m[4])
				b.WriteString(`</span>`)
			} else {
				template.HTMLEscape(&b, m[4])
			}
		}

		if len(m[6]) != 0 {
			template.HTMLEscape(&b, m[5])
			if m[6][0] == 'M' {
				b.WriteString(`<span class="gender-m">`)
			} else if m[6][0] == 'F' {
				b.WriteString(`<span class="gender-f">`)
			} else {
				b.WriteString(`<span>`)
			}
			template.HTMLEscape(&b, m[6])
			b.WriteString(`</span>`)
			template.HTMLEscape(&b, m[7])
		}

		if len(m[9]) != 0 {
			if i, ok := itemData[string(m[9])]; ok {
				fset.Item = i["id"].(uint)
				template.HTMLEscape(&b, m[8])
				if t, ok := i["type"]; ok {
					b.WriteString(`<span class="type-`)
					b.WriteString(t.(string))
					b.WriteString(`">`)
					template.HTMLEscape(&b, m[9])
					b.WriteString(`</span>`)
				} else {
					template.HTMLEscape(&b, m[9])
				}
			} else {
				template.HTMLEscape(&b, m[8])
				template.HTMLEscape(&b, m[9])
			}
		}

		b.Write(m[10])
		b.WriteByte('\n')

		for _, line := range lines[1:] {
			if m := reMove.FindSubmatch(line); m != nil {
				if mv, ok := moveData[string(m[3])]; ok {
					b.WriteString(`<span class="type-`)
					b.WriteString(mv["type"].(string))
					b.WriteString(`">`)
					template.HTMLEscape(&b, m[1])
					b.WriteString(`</span>`)
				} else {
					template.HTMLEscape(&b, m[1])
				}
				template.HTMLEscape(&b, m[2])
			} else if m := bytes.SplitAfterN(line, []byte(": "), 2); len(m) == 2 {
				b.WriteString(`<span class="attr">`)
				template.HTMLEscape(&b, m[0])
				b.WriteString(`</span>`)
				template.HTMLEscape(&b, m[1])
			} else {
				template.HTMLEscape(&b, line)
			}
			b.WriteByte('\n')
		}

		fset.Text = template.HTML(b.String())

		fpaste.Paste = append(fpaste.Paste, fset)
	}

	err := tmpl.Execute(w, fpaste)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<commit_msg>Implement stat colouring<commit_after>package main

import (
	"bytes"
	"html/template"
	"net/http"
	"regexp"
	"strings"
)

type paste struct {
	Paste  []set
	Title  string
	Author string
	Notes  string
}

type set struct {
	Pokemon uint
	Form    uint
	Item    uint
	Text    template.HTML
}

var (
	reHead = regexp.MustCompile(`^(?:(.* \()([A-Z][a-z0-9:']+\.?(?:[- ][A-Za-z][a-z0-9:']*\.?)*)(\))|([A-Z][a-z0-9:']+\.?(?:[- ][A-Za-z][a-z0-9:']*\.?)*))(?:( \()([MF])(\)))?(?:( @ )([A-Z][a-z0-9:']+(?:[- ][A-Z][a-z0-9:']*)*))?( *)$`)
	reMove = regexp.MustCompile(`^(-)( *([A-Z][a-z\']*(?:[- ][A-Za-z][a-z\']*)*)(?: */ *[A-Z][a-z\']*(?:[- ][A-Za-z][a-z\']*)*)* *)$`)
	reStat = regexp.MustCompile(`^(\d+ HP)?( / )?(\d+ Atk)?( / )?(\d+ Def)?( / )?(\d+ SpA)?( / )?(\d+ SpD)?( / )?(\d+ Spe)?( *)$`)

	tmpl = template.Must(template.ParseFiles("paste.tmpl"))
)

func renderPaste(w http.ResponseWriter, text, title, author, notes []byte) {
	sets := bytes.Split(text, []byte("\r\n\r\n"))
	fpaste := paste{
		Paste:  make([]set, 0, len(sets)),
		Title:  string(title),
		Author: string(author),
		Notes:  string(notes),
	}

	for _, bset := range sets {
		if len(bset) == 0 {
			continue
		}

		var fset set
		var b strings.Builder

		lines := bytes.Split(bset, []byte("\r\n"))

		m := reHead.FindSubmatch(lines[0])
		if m == nil {
			template.HTMLEscape(&b, bset)
			fset.Text = template.HTML(b.String())
			fpaste.Paste = append(fpaste.Paste, fset)
			continue
		}

		if len(m[2]) != 0 {
			if p, ok := pokemonData[string(m[2])]; ok {
				fset.Pokemon = p["id"].(uint)
				fset.Form = p["form"].(uint)
				template.HTMLEscape(&b, m[1])
				b.WriteString(`<span class="type-`)
				b.WriteString(p["type"].(string))
				b.WriteString(`">`)
				template.HTMLEscape(&b, m[2])
				b.WriteString(`</span>`)
				template.HTMLEscape(&b, m[3])
			} else {
				template.HTMLEscape(&b, m[1])
				template.HTMLEscape(&b, m[2])
				template.HTMLEscape(&b, m[3])
			}
		} else if len(m[4]) != 0 {
			if p, ok := pokemonData[string(m[4])]; ok {
				fset.Pokemon = p["id"].(uint)
				fset.Form = p["form"].(uint)
				b.WriteString(`<span class="type-`)
				b.WriteString(p["type"].(string))
				b.WriteString(`">`)
				template.HTMLEscape(&b, m[4])
				b.WriteString(`</span>`)
			} else {
				template.HTMLEscape(&b, m[4])
			}
		}

		if len(m[6]) != 0 {
			template.HTMLEscape(&b, m[5])
			if m[6][0] == 'M' {
				b.WriteString(`<span class="gender-m">`)
			} else if m[6][0] == 'F' {
				b.WriteString(`<span class="gender-f">`)
			} else {
				b.WriteString(`<span>`)
			}
			template.HTMLEscape(&b, m[6])
			b.WriteString(`</span>`)
			template.HTMLEscape(&b, m[7])
		}

		if len(m[9]) != 0 {
			if i, ok := itemData[string(m[9])]; ok {
				fset.Item = i["id"].(uint)
				template.HTMLEscape(&b, m[8])
				if t, ok := i["type"]; ok {
					b.WriteString(`<span class="type-`)
					b.WriteString(t.(string))
					b.WriteString(`">`)
					template.HTMLEscape(&b, m[9])
					b.WriteString(`</span>`)
				} else {
					template.HTMLEscape(&b, m[9])
				}
			} else {
				template.HTMLEscape(&b, m[8])
				template.HTMLEscape(&b, m[9])
			}
		}

		b.Write(m[10])
		b.WriteByte('\n')

		for _, line := range lines[1:] {
			if m := reMove.FindSubmatch(line); m != nil {
				if mv, ok := moveData[string(m[3])]; ok {
					b.WriteString(`<span class="type-`)
					b.WriteString(mv["type"].(string))
					b.WriteString(`">`)
					template.HTMLEscape(&b, m[1])
					b.WriteString(`</span>`)
				} else {
					template.HTMLEscape(&b, m[1])
				}
				template.HTMLEscape(&b, m[2])
			} else if m := bytes.SplitAfterN(line, []byte(": "), 2); len(m) == 2 {
				b.WriteString(`<span class="attr">`)
				template.HTMLEscape(&b, m[0])
				b.WriteString(`</span>`)
				if len(m[1]) == 5 && m[1][1] == 'V' && m[1][2] == 's' {
					attr := m[1]
					if m := reStat.FindSubmatch(attr); m != nil {
						for i, stat := range [...]string{"hp", "atk", "def", "spa", "spd", "spe"} {
							if len(m[i * 2 + 1]) > 0 {
								b.WriteString(`<span class="stat-`)
								b.WriteString(stat)
								b.WriteString(`">`)
								template.HTMLEscape(&b, m[i * 2 + 1])
								b.WriteString(`</span>`)
							}
							template.HTMLEscape(&b, m[i * 2 + 2])
						}
					} else {
						template.HTMLEscape(&b, attr)
					}
				} else {
					template.HTMLEscape(&b, m[1])
				}
			} else {
				template.HTMLEscape(&b, line)
			}
			b.WriteByte('\n')
		}

		fset.Text = template.HTML(b.String())

		fpaste.Paste = append(fpaste.Paste, fset)
	}

	err := tmpl.Execute(w, fpaste)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/prest/adapters/postgres"
	"github.com/prest/statements"
)

// GetTables list all (or filter) tables
func GetTables(w http.ResponseWriter, r *http.Request) {
	requestWhere, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform OrderByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if order == "" {
		order = statements.TablesOrderBy
	}

	sqlTables := fmt.Sprint(
		statements.TablesSelect,
		statements.TablesWhere)

	if requestWhere != "" {
		sqlTables = fmt.Sprintf("%s AND %s", sqlTables, requestWhere)
	}

	sqlTables = fmt.Sprint(sqlTables, order)

	sc := postgres.Query(sqlTables, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// GetTablesByDatabaseAndSchema list all (or filter) tables based on database and schema
func GetTablesByDatabaseAndSchema(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]

	requestWhere, values, err := postgres.WhereByRequest(r, 3)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSchemaTables := fmt.Sprint(
		statements.SchemaTablesSelect,
		statements.SchemaTablesWhere)

	if requestWhere != "" {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, " AND ", requestWhere)
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform OrderByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if order != "" {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, order)
	} else {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, statements.SchemaTablesOrderBy)
	}

	page, err := postgres.PaginateIfPossible(r)
	if err != nil {
		err = fmt.Errorf("could not perform PaginateIfPossible: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSchemaTables = fmt.Sprint(sqlSchemaTables, " ", page)

	valuesAux := make([]interface{}, 0)
	valuesAux = append(valuesAux, database)
	valuesAux = append(valuesAux, schema)
	valuesAux = append(valuesAux, values...)

	sc := postgres.Query(sqlSchemaTables, valuesAux...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// SelectFromTables perform select in database
func SelectFromTables(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]
	table := vars["table"]

	// get selected columns, "*" if empty "_columns"
	cols := postgres.FieldsPermissions(r, table, "read")

	if len(cols) == 0 {
		err := fmt.Errorf("you don't have permission for this action, please check the permitted fields for this table")
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	selectStr, err := postgres.SelectFields(cols)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	query := fmt.Sprintf("%s %s.%s.%s", selectStr, database, schema, table)

	countQuery, err := postgres.CountByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform CountByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if countQuery != "" {
		query = fmt.Sprintf("%s %s.%s.%s", countQuery, database, schema, table)
	}

	joinValues, err := postgres.JoinByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform JoinByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	for _, j := range joinValues {
		query = fmt.Sprint(query, j)
	}

	requestWhere, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSelect := query
	if requestWhere != "" {
		sqlSelect = fmt.Sprint(
			query,
			" WHERE ",
			requestWhere)
	}

	groupBySQL := postgres.GroupByClause(r)

	if groupBySQL != "" {
		sqlSelect = fmt.Sprintf("%s %s", sqlSelect, groupBySQL)
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform OrderByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if order != "" {
		sqlSelect = fmt.Sprintf("%s %s", sqlSelect, order)
	}

	page, err := postgres.PaginateIfPossible(r)
	if err != nil {
		err = fmt.Errorf("could not perform PaginateIfPossible: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	sqlSelect = fmt.Sprint(sqlSelect, " ", page)

	runQuery := postgres.Query
	if countQuery != "" {
		runQuery = postgres.QueryCount
	}

	sc := runQuery(sqlSelect, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// InsertInTables perform insert in specific table
func InsertInTables(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]
	table := vars["table"]

	names, placeholders, values, err := postgres.ParseInsertRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform InsertInTables: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sql := fmt.Sprintf(statements.InsertQuery, database, schema, table, names, placeholders)

	sc := postgres.Insert(sql, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// DeleteFromTable perform delete sql
func DeleteFromTable(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]
	table := vars["table"]

	where, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sql := fmt.Sprintf(statements.DeleteQuery, database, schema, table)
	if where != "" {
		sql = fmt.Sprint(sql, " WHERE ", where)
	}

	sc := postgres.Delete(sql, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// UpdateTable perform update table
func UpdateTable(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]
	table := vars["table"]

	where, whereValues, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	pid := len(whereValues) + 1 // placeholder id

	setSyntax, values, err := postgres.SetByRequest(r, pid)
	if err != nil {
		err = fmt.Errorf("could not perform UPDATE: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	sql := fmt.Sprintf(statements.UpdateQuery, database, schema, table, setSyntax)

	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
		values = append(whereValues, values...)
	}

	sc := postgres.Update(sql, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}
<commit_msg>now fields permission return a slice and an error<commit_after>package controllers

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/prest/adapters/postgres"
	"github.com/prest/statements"
)

// GetTables list all (or filter) tables
func GetTables(w http.ResponseWriter, r *http.Request) {
	requestWhere, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform OrderByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if order == "" {
		order = statements.TablesOrderBy
	}

	sqlTables := fmt.Sprint(
		statements.TablesSelect,
		statements.TablesWhere)

	if requestWhere != "" {
		sqlTables = fmt.Sprintf("%s AND %s", sqlTables, requestWhere)
	}

	sqlTables = fmt.Sprint(sqlTables, order)

	sc := postgres.Query(sqlTables, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// GetTablesByDatabaseAndSchema list all (or filter) tables based on database and schema
func GetTablesByDatabaseAndSchema(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]

	requestWhere, values, err := postgres.WhereByRequest(r, 3)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSchemaTables := fmt.Sprint(
		statements.SchemaTablesSelect,
		statements.SchemaTablesWhere)

	if requestWhere != "" {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, " AND ", requestWhere)
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform OrderByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if order != "" {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, order)
	} else {
		sqlSchemaTables = fmt.Sprint(sqlSchemaTables, statements.SchemaTablesOrderBy)
	}

	page, err := postgres.PaginateIfPossible(r)
	if err != nil {
		err = fmt.Errorf("could not perform PaginateIfPossible: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSchemaTables = fmt.Sprint(sqlSchemaTables, " ", page)

	valuesAux := make([]interface{}, 0)
	valuesAux = append(valuesAux, database)
	valuesAux = append(valuesAux, schema)
	valuesAux = append(valuesAux, values...)

	sc := postgres.Query(sqlSchemaTables, valuesAux...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// SelectFromTables perform select in database
func SelectFromTables(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]
	table := vars["table"]

	// get selected columns, "*" if empty "_columns"
	cols, err := postgres.FieldsPermissions(r, table, "read")
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if len(cols) == 0 {
		err = fmt.Errorf("you don't have permission for this action, please check the permitted fields for this table")
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	selectStr, err := postgres.SelectFields(cols)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	query := fmt.Sprintf("%s %s.%s.%s", selectStr, database, schema, table)

	countQuery, err := postgres.CountByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform CountByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if countQuery != "" {
		query = fmt.Sprintf("%s %s.%s.%s", countQuery, database, schema, table)
	}

	joinValues, err := postgres.JoinByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform JoinByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	for _, j := range joinValues {
		query = fmt.Sprint(query, j)
	}

	requestWhere, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sqlSelect := query
	if requestWhere != "" {
		sqlSelect = fmt.Sprint(
			query,
			" WHERE ",
			requestWhere)
	}

	groupBySQL := postgres.GroupByClause(r)

	if groupBySQL != "" {
		sqlSelect = fmt.Sprintf("%s %s", sqlSelect, groupBySQL)
	}

	order, err := postgres.OrderByRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform OrderByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if order != "" {
		sqlSelect = fmt.Sprintf("%s %s", sqlSelect, order)
	}

	page, err := postgres.PaginateIfPossible(r)
	if err != nil {
		err = fmt.Errorf("could not perform PaginateIfPossible: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	sqlSelect = fmt.Sprint(sqlSelect, " ", page)

	runQuery := postgres.Query
	if countQuery != "" {
		runQuery = postgres.QueryCount
	}

	sc := runQuery(sqlSelect, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// InsertInTables perform insert in specific table
func InsertInTables(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]
	table := vars["table"]

	names, placeholders, values, err := postgres.ParseInsertRequest(r)
	if err != nil {
		err = fmt.Errorf("could not perform InsertInTables: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sql := fmt.Sprintf(statements.InsertQuery, database, schema, table, names, placeholders)

	sc := postgres.Insert(sql, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// DeleteFromTable perform delete sql
func DeleteFromTable(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]
	table := vars["table"]

	where, values, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	sql := fmt.Sprintf(statements.DeleteQuery, database, schema, table)
	if where != "" {
		sql = fmt.Sprint(sql, " WHERE ", where)
	}

	sc := postgres.Delete(sql, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}

// UpdateTable perform update table
func UpdateTable(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	database := vars["database"]
	schema := vars["schema"]
	table := vars["table"]

	where, whereValues, err := postgres.WhereByRequest(r, 1)
	if err != nil {
		err = fmt.Errorf("could not perform WhereByRequest: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	pid := len(whereValues) + 1 // placeholder id

	setSyntax, values, err := postgres.SetByRequest(r, pid)
	if err != nil {
		err = fmt.Errorf("could not perform UPDATE: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	sql := fmt.Sprintf(statements.UpdateQuery, database, schema, table, setSyntax)

	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
		values = append(whereValues, values...)
	}

	sc := postgres.Update(sql, values...)
	if sc.Err() != nil {
		http.Error(w, sc.Err().Error(), http.StatusBadRequest)
		return
	}
	w.Write(sc.Bytes())
}
<|endoftext|>


<commit_before>package blackbox

import (
	"os"

	"github.com/ActiveState/tail"

	"github.com/concourse/blackbox/syslog"
)

type Tailer struct {
	Source  SyslogSource
	Drainer syslog.Drainer
}

func (tailer *Tailer) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	t, err := tail.TailFile(tailer.Source.Path, tail.Config{
		Follow: true,
		ReOpen: true,
		Location: &tail.SeekInfo{
			Offset: 0,
			Whence: os.SEEK_END,
		},
	})

	if err != nil {
		return err
	}
	defer tail.Cleanup()

	close(ready)

	for {
		select {
		case line := <-t.Lines:
			tailer.Drainer.Drain(line.Text, tailer.Source.Tag)
		case <-signals:
			return t.Stop()
		}
	}

	return nil
}
<commit_msg>be durable to t.Lines closing<commit_after>package blackbox

import (
	"os"

	"github.com/ActiveState/tail"

	"github.com/concourse/blackbox/syslog"
)

type Tailer struct {
	Source  SyslogSource
	Drainer syslog.Drainer
}

func (tailer *Tailer) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	t, err := tail.TailFile(tailer.Source.Path, tail.Config{
		Follow: true,
		ReOpen: true,
		Location: &tail.SeekInfo{
			Offset: 0,
			Whence: os.SEEK_END,
		},
	})

	if err != nil {
		return err
	}
	defer tail.Cleanup()

	close(ready)

	for {
		select {
		case line, ok := <-t.Lines:
			if !ok {
				println("lines flushed; exiting tailer")
				return nil
			}

			tailer.Drainer.Drain(line.Text, tailer.Source.Tag)
		case <-signals:
			return t.Stop()
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package system

import (
	"errors"
	"strconv"
	"strings"

	"github.com/goesd/format/tgff"
)

// LoadTGFF constructs a platform and an application based on a TGFF file.
func LoadTGFF(path string) (Platform, Application, error) {
	p, a := Platform{}, Application{}

	r, err := tgff.ParseFile(path)
	if err != nil {
		return p, a, err
	}

	p, err = loadPlatform(r.Tables)
	if err != nil {
		return p, a, err
	}

	a, err = loadApplication(r.Graphs)
	if err != nil {
		return p, a, err
	}

	return p, a, nil
}

func loadPlatform(tables []tgff.Table) (Platform, error) {
	p := Platform{}

	size := len(tables)

	if size == 0 {
		return p, errors.New("need at least one table")
	}

	p.Cores = make([]Core, size)

	var err error

	for _, table := range tables {
		i := table.Number

		if i >= uint32(size) {
			return p, errors.New("unknown table indexing scheme")
		}

		p.Cores[i], err = loadCore(table)

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

	rows := len(p.Cores[0].Time)

	for i := 1; i < size; i++ {
		if rows != len(p.Cores[i].Time) {
			return p, errors.New("inconsistent table data")
		}
	}

	return p, nil
}

func loadApplication(graphs []tgff.Graph) (Application, error) {
	a := Application{}

	if len(graphs) != 1 {
		return a, errors.New("need exactly one task graph")
	}

	size := uint32(len(graphs[0].Tasks))

	a.Tasks = make([]Task, size)

	for _, task := range graphs[0].Tasks {
		i, err := extractTaskID(task.Name, size)

		if err != nil {
			return a, err
		}

		a.Tasks[i].ID = i
		a.Tasks[i].Type = task.Type
	}

	for _, arc := range graphs[0].Arcs {
		i, err := extractTaskID(arc.From, size)

		if err != nil {
			return a, err
		}

		j, err := extractTaskID(arc.To, size)

		if err != nil {
			return a, err
		}

		a.Tasks[i].Children = append(a.Tasks[i].Children, &a.Tasks[j])
	}

	return a, nil
}

func loadCore(table tgff.Table) (Core, error) {
	c := Core{
		ID: table.Number,
	}

	var tycol, tmcol, pwcol *tgff.Column

	for i := range table.Columns {
		col := &table.Columns[i]

		name := strings.ToLower(col.Name)

		if strings.Index(name, "type") >= 0 {
			tycol = col
		} else if strings.Index(name, "time") >= 0 {
			tmcol = col
		} else if strings.Index(name, "power") >= 0 {
			pwcol = col
		}

		if tycol != nil && tmcol != nil && pwcol != nil {
			break
		}
	}

	if tycol == nil || tmcol == nil || pwcol == nil {
		return c, errors.New("need columns named type, time, and power")
	}

	size := len(tycol.Data)

	for i := 0; i < size; i++ {
		if int(tycol.Data[i]) != i {
			return c, errors.New("data should be sorted by type")
		}
	}

	c.Time = make([]float64, size)
	copy(c.Time, tmcol.Data)

	c.Power = make([]float64, size)
	copy(c.Power, pwcol.Data)

	return c, nil
}

func extractTaskID(name string, total uint32) (uint32, error) {
	if !strings.HasPrefix(name, "t0_") {
		return 0, errors.New("unknown task naming scheme")
	}

	id, err := strconv.ParseInt(name[3:], 10, 0)

	if err != nil || id < 0 || uint32(id) >= total {
		return 0, errors.New("unknown task indexing scheme")
	}

	return uint32(id), nil
}
<commit_msg>Return pointers instead of values from LoadTGFF<commit_after>package system

import (
	"errors"
	"strconv"
	"strings"

	"github.com/goesd/format/tgff"
)

// LoadTGFF constructs a platform and an application based on a TGFF file.
func LoadTGFF(path string) (*Platform, *Application, error) {
	r, err := tgff.ParseFile(path)
	if err != nil {
		return nil, nil, err
	}

	p, err := loadPlatform(r.Tables)
	if err != nil {
		return nil, nil, err
	}

	a, err := loadApplication(r.Graphs)
	if err != nil {
		return nil, nil, err
	}

	return p, a, nil
}

func loadPlatform(tables []tgff.Table) (*Platform, error) {
	size := len(tables)

	if size == 0 {
		return nil, errors.New("need at least one table")
	}

	p := &Platform{
		Cores: make([]Core, size),
	}

	var err error

	for _, table := range tables {
		i := table.Number

		if i >= uint32(size) {
			return nil, errors.New("unknown table indexing scheme")
		}

		p.Cores[i], err = loadCore(table)

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

	rows := len(p.Cores[0].Time)

	for i := 1; i < size; i++ {
		if rows != len(p.Cores[i].Time) {
			return nil, errors.New("inconsistent table data")
		}
	}

	return p, nil
}

func loadApplication(graphs []tgff.Graph) (*Application, error) {
	if len(graphs) != 1 {
		return nil, errors.New("need exactly one task graph")
	}

	size := uint32(len(graphs[0].Tasks))

	a := &Application{
		Tasks: make([]Task, size),
	}

	for _, task := range graphs[0].Tasks {
		i, err := extractTaskID(task.Name, size)

		if err != nil {
			return nil, err
		}

		a.Tasks[i].ID = i
		a.Tasks[i].Type = task.Type
	}

	for _, arc := range graphs[0].Arcs {
		i, err := extractTaskID(arc.From, size)

		if err != nil {
			return nil, err
		}

		j, err := extractTaskID(arc.To, size)

		if err != nil {
			return nil, err
		}

		a.Tasks[i].Children = append(a.Tasks[i].Children, &a.Tasks[j])
	}

	return a, nil
}

func loadCore(table tgff.Table) (Core, error) {
	c := Core{
		ID: table.Number,
	}

	var tycol, tmcol, pwcol *tgff.Column

	for i := range table.Columns {
		col := &table.Columns[i]

		name := strings.ToLower(col.Name)

		if strings.Index(name, "type") >= 0 {
			tycol = col
		} else if strings.Index(name, "time") >= 0 {
			tmcol = col
		} else if strings.Index(name, "power") >= 0 {
			pwcol = col
		}

		if tycol != nil && tmcol != nil && pwcol != nil {
			break
		}
	}

	if tycol == nil || tmcol == nil || pwcol == nil {
		return c, errors.New("need columns named type, time, and power")
	}

	size := len(tycol.Data)

	for i := 0; i < size; i++ {
		if int(tycol.Data[i]) != i {
			return c, errors.New("data should be sorted by type")
		}
	}

	c.Time = make([]float64, size)
	copy(c.Time, tmcol.Data)

	c.Power = make([]float64, size)
	copy(c.Power, pwcol.Data)

	return c, nil
}

func extractTaskID(name string, total uint32) (uint32, error) {
	if !strings.HasPrefix(name, "t0_") {
		return 0, errors.New("unknown task naming scheme")
	}

	id, err := strconv.ParseInt(name[3:], 10, 0)

	if err != nil || id < 0 || uint32(id) >= total {
		return 0, errors.New("unknown task indexing scheme")
	}

	return uint32(id), nil
}
<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

// tail is responsible for tailing a log file and extracting new log lines to
// be passed into the virtual machines.

// mtail gets notified on modifications (i.e. appends) to log files that are
// being watched, in order to read the new lines. Log files can also be
// rotated, so mtail is also notified of creates in the log file directory.

package tailer

import (
	"expvar"
	"io"
	"os"
	"path"
	"path/filepath"
	"sync"
	"syscall"
	"unicode/utf8"

	"github.com/golang/glog"

	"github.com/google/mtail/watcher"

	"github.com/spf13/afero"
)

var (
	log_count     = expvar.NewInt("log_count")
	log_errors    = expvar.NewMap("log_errors_total")
	log_rotations = expvar.NewMap("log_rotations_total")
)

// tailer receives notification of changes from inotify and extracts new log
// lines from files. It also handles new log file creation events and log
// rotations.
type tailer struct {
	w watcher.Watcher

	watched      map[string]struct{}   // Names of logs being watched.
	watched_lock sync.RWMutex          // protects `watched'
	lines        chan<- string         // Logfile lines being emitted.
	files        map[string]afero.File // File handles for each pathname.
	files_lock   sync.Mutex            // protects `files'
	partials     map[string]string     // Accumulator for the currently read line for each pathname.

	fs afero.Fs // mockable filesystem interface
}

// New returns a new tailer.
func New(lines chan<- string, w watcher.Watcher, fs afero.Fs) *tailer {
	t := &tailer{
		w:        w,
		watched:  make(map[string]struct{}),
		lines:    lines,
		files:    make(map[string]afero.File),
		partials: make(map[string]string),
		fs:       fs,
	}
	go t.start()
	return t
}

func (t *tailer) addWatched(path string) {
	t.watched_lock.Lock()
	defer t.watched_lock.Unlock()
	t.watched[path] = struct{}{}
}

func (t *tailer) isWatching(path string) bool {
	t.watched_lock.RLock()
	defer t.watched_lock.RUnlock()
	_, ok := t.watched[path]
	return ok
}

// Tail registers a file to be tailed.
func (t *tailer) Tail(pathname string) {
	fullpath, err := filepath.Abs(pathname)
	if err != nil {
		glog.Infof("Failed to find absolute path for %q: %s\n", pathname, err)
		return
	}
	if !t.isWatching(fullpath) {
		t.addWatched(fullpath)
		log_count.Add(1)
		t.openLogFile(fullpath, false)
	}
}

// handleLogUpdate reads all available bytes from an already opened file
// identified by pathname, and sends them to be processed on the lines channel.
func (t *tailer) handleLogUpdate(pathname string) {
	for {
		b := make([]byte, 4096)
		f, ok := t.files[pathname]
		if !ok {
			glog.Infof("No file found for %q", pathname)
			return
		}
		n, err := f.Read(b)
		glog.Infof("err: %v, n: %d", err, n)
		if err != nil {
			if err == io.EOF && n == 0 {
				// end of file for now, return
				return
			}
			glog.Infof("Failed to read updates from %q: %s", pathname, err)
			return
		} else {
			for i, width := 0, 0; i < len(b) && i < n; i += width {
				var rune rune
				rune, width = utf8.DecodeRune(b[i:])
				switch {
				case rune != '\n':
					t.partials[pathname] += string(rune)
				default:
					// send off line for processing
					t.lines <- t.partials[pathname]
					// reset accumulator
					t.partials[pathname] = ""
				}
			}
		}
	}
}

// inode returns the inode number of a file, or 0 if the file has no underlying Sys implementation.
func inode(f os.FileInfo) uint64 {
	s := f.Sys()
	if s == nil {
		return 0
	}
	switch s := s.(type) {
	case *syscall.Stat_t:
		return s.Ino
	default:
		return 0
	}
}

// handleLogCreate handles both new and rotated log files.
func (t *tailer) handleLogCreate(pathname string) {
	if !t.isWatching(pathname) {
		glog.V(1).Info("Not watching path %q, ignoring.\n", pathname)
		return
	}

	t.files_lock.Lock()
	fd, ok := t.files[pathname]
	t.files_lock.Unlock()
	if ok {
		s1, err := fd.Stat()
		if err != nil {
			glog.Infof("Stat failed on %q: %s", t.files[pathname].Name(), err)
			return
		}
		s2, err := t.fs.Stat(pathname)
		if err != nil {
			glog.Infof("Stat failed on %q: %s", pathname, err)
			return
		}
		if inode(s1) != inode(s2) {
			log_rotations.Add(pathname, 1)
			// flush the old log, pathname is still an index into t.files with the old inode.
			t.handleLogUpdate(pathname)
			fd.Close()
			err := t.w.Remove(pathname)
			if err != nil {
				glog.Info("Failed removing watches on", pathname)
			}
			// Always seek to start on log rotation.
			t.openLogFile(pathname, true)
		} else {
			glog.Infof("Path %s already being watched, and inode not changed.",
				pathname)
		}
	} else {
		// Freshly opened log file, never seen before, so do not seek to start.
		t.openLogFile(pathname, true)
	}
}

// openLogFile opens a new log file at pathname, and optionally seeks to the
// start or end of the file. Rotated logs should start at the start, but logs
// opened for the first time start at the end.
func (t *tailer) openLogFile(pathname string, seek_to_start bool) {
	if !t.isWatching(pathname) {
		glog.Infof("Not watching %q, ignoring.", pathname)
		return
	}

	d := path.Dir(pathname)
	if !t.isWatching(d) {
		err := t.w.Add(d)
		if err != nil {
			glog.Infof("Adding a create watch failed on %q: %s", d, err)
		}
		t.addWatched(d)
	}

	fd, err := t.fs.Open(pathname)
	if err != nil {
		// Doesn't exist yet. We're watching the directory, so we'll pick it up
		// again on create; return successfully.
		if os.IsNotExist(err) {
			return
		}
		glog.Infof("Failed to open %q for reading: %s", pathname, err)
		log_errors.Add(pathname, 1)
		return
	}
	t.files_lock.Lock()
	t.files[pathname] = fd

	if seek_to_start {
		t.files[pathname].Seek(0, os.SEEK_SET)
	}

	t.files_lock.Unlock()

	err = t.w.Add(pathname)
	if err != nil {
		glog.Infof("Adding a change watch failed on %q: %s", pathname, err)
	}

	// In case the new log has been written to already, attempt to read the first lines.
	t.handleLogUpdate(pathname)
}

// start is the main event loop for the tailer.
// It receives notification of log file changes from the watcher channel, and
// handles them.
func (t *tailer) start() {
	for e := range t.w.Events() {
		switch e := e.(type) {
		case watcher.UpdateEvent:
			glog.Infof("name: %q", e.Pathname)
			t.handleLogUpdate(e.Pathname)
		case watcher.CreateEvent:
			glog.Infof("name: %q", e.Pathname)
			t.handleLogCreate(e.Pathname)
		default:
			glog.Infof("Unexpected event %q", e)
		}
	}
	close(t.lines)
}
<commit_msg>Add log message when tailer is shutting down.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

// tail is responsible for tailing a log file and extracting new log lines to
// be passed into the virtual machines.

// mtail gets notified on modifications (i.e. appends) to log files that are
// being watched, in order to read the new lines. Log files can also be
// rotated, so mtail is also notified of creates in the log file directory.

package tailer

import (
	"expvar"
	"io"
	"os"
	"path"
	"path/filepath"
	"sync"
	"syscall"
	"unicode/utf8"

	"github.com/golang/glog"

	"github.com/google/mtail/watcher"

	"github.com/spf13/afero"
)

var (
	log_count     = expvar.NewInt("log_count")
	log_errors    = expvar.NewMap("log_errors_total")
	log_rotations = expvar.NewMap("log_rotations_total")
)

// tailer receives notification of changes from inotify and extracts new log
// lines from files. It also handles new log file creation events and log
// rotations.
type tailer struct {
	w watcher.Watcher

	watched      map[string]struct{}   // Names of logs being watched.
	watched_lock sync.RWMutex          // protects `watched'
	lines        chan<- string         // Logfile lines being emitted.
	files        map[string]afero.File // File handles for each pathname.
	files_lock   sync.Mutex            // protects `files'
	partials     map[string]string     // Accumulator for the currently read line for each pathname.

	fs afero.Fs // mockable filesystem interface
}

// New returns a new tailer.
func New(lines chan<- string, w watcher.Watcher, fs afero.Fs) *tailer {
	t := &tailer{
		w:        w,
		watched:  make(map[string]struct{}),
		lines:    lines,
		files:    make(map[string]afero.File),
		partials: make(map[string]string),
		fs:       fs,
	}
	go t.start()
	return t
}

func (t *tailer) addWatched(path string) {
	t.watched_lock.Lock()
	defer t.watched_lock.Unlock()
	t.watched[path] = struct{}{}
}

func (t *tailer) isWatching(path string) bool {
	t.watched_lock.RLock()
	defer t.watched_lock.RUnlock()
	_, ok := t.watched[path]
	return ok
}

// Tail registers a file to be tailed.
func (t *tailer) Tail(pathname string) {
	fullpath, err := filepath.Abs(pathname)
	if err != nil {
		glog.Infof("Failed to find absolute path for %q: %s\n", pathname, err)
		return
	}
	if !t.isWatching(fullpath) {
		t.addWatched(fullpath)
		log_count.Add(1)
		t.openLogFile(fullpath, false)
	}
}

// handleLogUpdate reads all available bytes from an already opened file
// identified by pathname, and sends them to be processed on the lines channel.
func (t *tailer) handleLogUpdate(pathname string) {
	for {
		b := make([]byte, 4096)
		f, ok := t.files[pathname]
		if !ok {
			glog.Infof("No file found for %q", pathname)
			return
		}
		n, err := f.Read(b)
		glog.Infof("err: %v, n: %d", err, n)
		if err != nil {
			if err == io.EOF && n == 0 {
				// end of file for now, return
				return
			}
			glog.Infof("Failed to read updates from %q: %s", pathname, err)
			return
		} else {
			for i, width := 0, 0; i < len(b) && i < n; i += width {
				var rune rune
				rune, width = utf8.DecodeRune(b[i:])
				switch {
				case rune != '\n':
					t.partials[pathname] += string(rune)
				default:
					// send off line for processing
					t.lines <- t.partials[pathname]
					// reset accumulator
					t.partials[pathname] = ""
				}
			}
		}
	}
}

// inode returns the inode number of a file, or 0 if the file has no underlying Sys implementation.
func inode(f os.FileInfo) uint64 {
	s := f.Sys()
	if s == nil {
		return 0
	}
	switch s := s.(type) {
	case *syscall.Stat_t:
		return s.Ino
	default:
		return 0
	}
}

// handleLogCreate handles both new and rotated log files.
func (t *tailer) handleLogCreate(pathname string) {
	if !t.isWatching(pathname) {
		glog.V(1).Info("Not watching path %q, ignoring.\n", pathname)
		return
	}

	t.files_lock.Lock()
	fd, ok := t.files[pathname]
	t.files_lock.Unlock()
	if ok {
		s1, err := fd.Stat()
		if err != nil {
			glog.Infof("Stat failed on %q: %s", t.files[pathname].Name(), err)
			return
		}
		s2, err := t.fs.Stat(pathname)
		if err != nil {
			glog.Infof("Stat failed on %q: %s", pathname, err)
			return
		}
		if inode(s1) != inode(s2) {
			log_rotations.Add(pathname, 1)
			// flush the old log, pathname is still an index into t.files with the old inode.
			t.handleLogUpdate(pathname)
			fd.Close()
			err := t.w.Remove(pathname)
			if err != nil {
				glog.Info("Failed removing watches on", pathname)
			}
			// Always seek to start on log rotation.
			t.openLogFile(pathname, true)
		} else {
			glog.Infof("Path %s already being watched, and inode not changed.",
				pathname)
		}
	} else {
		// Freshly opened log file, never seen before, so do not seek to start.
		t.openLogFile(pathname, true)
	}
}

// openLogFile opens a new log file at pathname, and optionally seeks to the
// start or end of the file. Rotated logs should start at the start, but logs
// opened for the first time start at the end.
func (t *tailer) openLogFile(pathname string, seek_to_start bool) {
	if !t.isWatching(pathname) {
		glog.Infof("Not watching %q, ignoring.", pathname)
		return
	}

	d := path.Dir(pathname)
	if !t.isWatching(d) {
		err := t.w.Add(d)
		if err != nil {
			glog.Infof("Adding a create watch failed on %q: %s", d, err)
		}
		t.addWatched(d)
	}

	fd, err := t.fs.Open(pathname)
	if err != nil {
		// Doesn't exist yet. We're watching the directory, so we'll pick it up
		// again on create; return successfully.
		if os.IsNotExist(err) {
			return
		}
		glog.Infof("Failed to open %q for reading: %s", pathname, err)
		log_errors.Add(pathname, 1)
		return
	}
	t.files_lock.Lock()
	t.files[pathname] = fd

	if seek_to_start {
		t.files[pathname].Seek(0, os.SEEK_SET)
	}

	t.files_lock.Unlock()

	err = t.w.Add(pathname)
	if err != nil {
		glog.Infof("Adding a change watch failed on %q: %s", pathname, err)
	}

	// In case the new log has been written to already, attempt to read the first lines.
	t.handleLogUpdate(pathname)
}

// start is the main event loop for the tailer.
// It receives notification of log file changes from the watcher channel, and
// handles them.
func (t *tailer) start() {
	for e := range t.w.Events() {
		switch e := e.(type) {
		case watcher.UpdateEvent:
			glog.Infof("name: %q", e.Pathname)
			t.handleLogUpdate(e.Pathname)
		case watcher.CreateEvent:
			glog.Infof("name: %q", e.Pathname)
			t.handleLogCreate(e.Pathname)
		default:
			glog.Infof("Unexpected event %q", e)
		}
	}
	glog.Infof("Shutting down tailer.")
	close(t.lines)
}
<|endoftext|>