<commit_before>package surf

import (
	"bytes"
	"database/sql"
	"errors"
	"fmt"
	"strconv"
)

// PqModel is a github.com/lib/pq implementation of a Model
type PqModel struct {
	Database *sql.DB       `json:"-"`
	Config   Configuration `json:"-"`
}

// GetConfiguration returns the configuration for the model
func (w *PqModel) GetConfiguration() *Configuration {
	return &w.Config
}

// Insert inserts the model into the database
func (w *PqModel) Insert() error {
	// Get Insertable Fields
	var insertableFields []Field
	for _, field := range w.Config.Fields {
		if field.Insertable {
			insertableFields = append(insertableFields, field)
		}
	}

	// Generate Query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("INSERT INTO ")
	queryBuffer.WriteString(w.Config.TableName)
	queryBuffer.WriteString("(")
	for i, field := range insertableFields {
		queryBuffer.WriteString(field.Name)
		if (i + 1) < len(insertableFields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(") VALUES(")
	for i := range insertableFields {
		queryBuffer.WriteString("$")
		queryBuffer.WriteString(strconv.Itoa(i + 1))
		if (i + 1) < len(insertableFields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(") RETURNING *;")

	// Get Value Fields
	var valueFields []interface{}
	for _, value := range insertableFields {
		valueFields = append(valueFields, value.Pointer)
	}

	// Log Query
	query := queryBuffer.String()
	printQuery(query, valueFields...)

	// Execute Query
	row := w.Database.QueryRow(query, valueFields...)
	err := consumeRow(w, row)
	if err != nil {
		return err
	}

	// Expand foreign references
	return expandForeign(w)
}

// Load loads the model from the database from its unique identifier
// and then loads those values into the struct
func (w *PqModel) Load() error {
	// Get Unique Identifier
	uniqueIdentifierField, err := getUniqueIdentifier(w)
	if err != nil {
		return err
	}

	// Generate Query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("SELECT ")
	for i, field := range w.Config.Fields {
		queryBuffer.WriteString(field.Name)
		if (i + 1) < len(w.Config.Fields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(" FROM ")
	queryBuffer.WriteString(w.Config.TableName)
	queryBuffer.WriteString(" WHERE ")
	queryBuffer.WriteString(uniqueIdentifierField.Name)
	queryBuffer.WriteString("=$1;")

	// Log Query
	query := queryBuffer.String()
	printQuery(query, uniqueIdentifierField.Pointer)

	// Execute Query
	row := w.Database.QueryRow(query, uniqueIdentifierField.Pointer)
	err = consumeRow(w, row)
	if err != nil {
		return err
	}

	// Expand foreign references
	return expandForeign(w)
}

// Update updates the model with the current values in the struct
func (w *PqModel) Update() error {
	// Get Unique Identifier
	uniqueIdentifierField, err := getUniqueIdentifier(w)
	if err != nil {
		return err
	}

	// Get updatable fields
	var updatableFields []Field
	for _, field := range w.Config.Fields {
		if field.Updatable {
			updatableFields = append(updatableFields, field)
		}
	}

	// Generate Query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("UPDATE ")
	queryBuffer.WriteString(w.Config.TableName)
	queryBuffer.WriteString(" SET ")
	for i, field := range updatableFields {
		queryBuffer.WriteString(field.Name)
		queryBuffer.WriteString("=$")
		queryBuffer.WriteString(strconv.Itoa(i + 1))
		if (i + 1) < len(updatableFields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(" WHERE ")
	queryBuffer.WriteString(uniqueIdentifierField.Name)
	queryBuffer.WriteString("=$")
	queryBuffer.WriteString(strconv.Itoa(len(updatableFields) + 1))
	queryBuffer.WriteString(" RETURNING *;")

	// Get Value Fields
	var valueFields []interface{}
	for _, value := range updatableFields {
		valueFields = append(valueFields, value.Pointer)
	}
	valueFields = append(valueFields, uniqueIdentifierField.Pointer)

	// Log Query
	query := queryBuffer.String()
	printQuery(query, valueFields...)

	// Execute Query
	row := w.Database.QueryRow(query, valueFields...)
	err = consumeRow(w, row)
	if err != nil {
		return err
	}

	// Expand foreign references
	return expandForeign(w)
}

// Delete deletes the model
func (w *PqModel) Delete() error {
	// Get Unique Identifier
	uniqueIdentifierField, err := getUniqueIdentifier(w)
	if err != nil {
		return err
	}

	// Generate Query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("DELETE FROM ")
	queryBuffer.WriteString(w.Config.TableName)
	queryBuffer.WriteString(" WHERE ")
	queryBuffer.WriteString(uniqueIdentifierField.Name)
	queryBuffer.WriteString("=$1;")

	// Log Query
	query := queryBuffer.String()
	printQuery(query, uniqueIdentifierField.Pointer)

	// Execute Query
	res, err := w.Database.Exec(queryBuffer.String(), uniqueIdentifierField.Pointer)
	if err != nil {
		return err
	}
	numRows, _ := res.RowsAffected()
	if numRows != 1 {
		return errors.New("Nothing was deleted")
	}
	return nil
}

// BulkFetch gets an array of models
func (w *PqModel) BulkFetch(fetchConfig BulkFetchConfig, buildModel BuildModel) ([]Model, error) {
	// Set up values
	values := make([]interface{}, 0)

	// Generate query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("SELECT ")
	for i, field := range w.Config.Fields {
		queryBuffer.WriteString(field.Name)
		if (i + 1) < len(w.Config.Fields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(" FROM ")
	queryBuffer.WriteString(buildModel().GetConfiguration().TableName)
	if len(fetchConfig.Predicates) > 0 {
		// WHERE
		queryBuffer.WriteString(" ")
		predicatesStr, predicateValues := predicatesToString(1, fetchConfig.Predicates)

		values = append(values, predicateValues...)
		queryBuffer.WriteString(predicatesStr)
	}
	if len(fetchConfig.OrderBys) > 0 {
		queryBuffer.WriteString(" ORDER BY ")
	}
	for i, orderBy := range fetchConfig.OrderBys {
		// Validate that the orderBy.Field is a field
		valid := false
		for _, field := range w.Config.Fields {
			if field.Name == orderBy.Field {
				valid = true
				break
			}
		}
		if !valid {
			return nil, fmt.Errorf("Could not order table '%v' by the invalid column '%v'",
				w.Config.TableName, orderBy.Field)
		}
		// Write to query
		queryBuffer.WriteString(orderBy.toString())
		if (i + 1) < len(fetchConfig.OrderBys) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(" LIMIT ")
	queryBuffer.WriteString(strconv.Itoa(fetchConfig.Limit))
	queryBuffer.WriteString(" OFFSET ")
	queryBuffer.WriteString(strconv.Itoa(fetchConfig.Offset))
	queryBuffer.WriteString(";")

	// Log Query
	query := queryBuffer.String()
	printQuery(query, values...)

	// Execute Query
	rows, err := w.Database.Query(query, values...)
	if err != nil {
		return nil, err
	}

	// Stuff into []Model
	var models []Model
	for rows.Next() {
		model := buildModel()

		// Consume Rows
		fields := model.GetConfiguration().Fields
		var s []interface{}
		for _, value := range fields {
			s = append(s, value.Pointer)
		}
		err := rows.Scan(s...)
		if err != nil {
			return nil, err
		}

		models = append(models, model.(Model))
	}

	// Expand foreign references
	err = expandForeigns(buildModel, models)
	if err != nil {
		return nil, err
	}

	// OK
	return models, nil
}
<commit_msg>Only return Surf fields in Create + Update<commit_after>package surf

import (
	"bytes"
	"database/sql"
	"errors"
	"fmt"
	"strconv"
)

// PqModel is a github.com/lib/pq implementation of a Model
type PqModel struct {
	Database *sql.DB       `json:"-"`
	Config   Configuration `json:"-"`
}

// GetConfiguration returns the configuration for the model
func (w *PqModel) GetConfiguration() *Configuration {
	return &w.Config
}

// Insert inserts the model into the database
func (w *PqModel) Insert() error {
	// Get Insertable Fields
	var insertableFields []Field
	for _, field := range w.Config.Fields {
		if field.Insertable {
			insertableFields = append(insertableFields, field)
		}
	}

	// Generate Query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("INSERT INTO ")
	queryBuffer.WriteString(w.Config.TableName)
	queryBuffer.WriteString("(")
	for i, field := range insertableFields {
		queryBuffer.WriteString(field.Name)
		if (i + 1) < len(insertableFields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(") VALUES(")
	for i := range insertableFields {
		queryBuffer.WriteString("$")
		queryBuffer.WriteString(strconv.Itoa(i + 1))
		if (i + 1) < len(insertableFields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(") RETURNING ")
	for i, field := range w.Config.Fields {
		queryBuffer.WriteString(field.Name)
		if(i + 1) < len(w.Config.Fields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(";")

	// Get Value Fields
	var valueFields []interface{}
	for _, value := range insertableFields {
		valueFields = append(valueFields, value.Pointer)
	}

	// Log Query
	query := queryBuffer.String()
	printQuery(query, valueFields...)

	// Execute Query
	row := w.Database.QueryRow(query, valueFields...)
	err := consumeRow(w, row)
	if err != nil {
		return err
	}

	// Expand foreign references
	return expandForeign(w)
}

// Load loads the model from the database from its unique identifier
// and then loads those values into the struct
func (w *PqModel) Load() error {
	// Get Unique Identifier
	uniqueIdentifierField, err := getUniqueIdentifier(w)
	if err != nil {
		return err
	}

	// Generate Query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("SELECT ")
	for i, field := range w.Config.Fields {
		queryBuffer.WriteString(field.Name)
		if (i + 1) < len(w.Config.Fields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(" FROM ")
	queryBuffer.WriteString(w.Config.TableName)
	queryBuffer.WriteString(" WHERE ")
	queryBuffer.WriteString(uniqueIdentifierField.Name)
	queryBuffer.WriteString("=$1;")

	// Log Query
	query := queryBuffer.String()
	printQuery(query, uniqueIdentifierField.Pointer)

	// Execute Query
	row := w.Database.QueryRow(query, uniqueIdentifierField.Pointer)
	err = consumeRow(w, row)
	if err != nil {
		return err
	}

	// Expand foreign references
	return expandForeign(w)
}

// Update updates the model with the current values in the struct
func (w *PqModel) Update() error {
	// Get Unique Identifier
	uniqueIdentifierField, err := getUniqueIdentifier(w)
	if err != nil {
		return err
	}

	// Get updatable fields
	var updatableFields []Field
	for _, field := range w.Config.Fields {
		if field.Updatable {
			updatableFields = append(updatableFields, field)
		}
	}

	// Generate Query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("UPDATE ")
	queryBuffer.WriteString(w.Config.TableName)
	queryBuffer.WriteString(" SET ")
	for i, field := range updatableFields {
		queryBuffer.WriteString(field.Name)
		queryBuffer.WriteString("=$")
		queryBuffer.WriteString(strconv.Itoa(i + 1))
		if (i + 1) < len(updatableFields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(" WHERE ")
	queryBuffer.WriteString(uniqueIdentifierField.Name)
	queryBuffer.WriteString("=$")
	queryBuffer.WriteString(strconv.Itoa(len(updatableFields) + 1))
	queryBuffer.WriteString(" RETURNING ")
	for i, field := range w.Config.Fields {
		queryBuffer.WriteString(field.Name)
		if(i + 1) < len(w.Config.Fields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(";")

	// Get Value Fields
	var valueFields []interface{}
	for _, value := range updatableFields {
		valueFields = append(valueFields, value.Pointer)
	}
	valueFields = append(valueFields, uniqueIdentifierField.Pointer)

	// Log Query
	query := queryBuffer.String()
	printQuery(query, valueFields...)

	// Execute Query
	row := w.Database.QueryRow(query, valueFields...)
	err = consumeRow(w, row)
	if err != nil {
		return err
	}

	// Expand foreign references
	return expandForeign(w)
}

// Delete deletes the model
func (w *PqModel) Delete() error {
	// Get Unique Identifier
	uniqueIdentifierField, err := getUniqueIdentifier(w)
	if err != nil {
		return err
	}

	// Generate Query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("DELETE FROM ")
	queryBuffer.WriteString(w.Config.TableName)
	queryBuffer.WriteString(" WHERE ")
	queryBuffer.WriteString(uniqueIdentifierField.Name)
	queryBuffer.WriteString("=$1;")

	// Log Query
	query := queryBuffer.String()
	printQuery(query, uniqueIdentifierField.Pointer)

	// Execute Query
	res, err := w.Database.Exec(queryBuffer.String(), uniqueIdentifierField.Pointer)
	if err != nil {
		return err
	}
	numRows, _ := res.RowsAffected()
	if numRows != 1 {
		return errors.New("Nothing was deleted")
	}
	return nil
}

// BulkFetch gets an array of models
func (w *PqModel) BulkFetch(fetchConfig BulkFetchConfig, buildModel BuildModel) ([]Model, error) {
	// Set up values
	values := make([]interface{}, 0)

	// Generate query
	var queryBuffer bytes.Buffer
	queryBuffer.WriteString("SELECT ")
	for i, field := range w.Config.Fields {
		queryBuffer.WriteString(field.Name)
		if (i + 1) < len(w.Config.Fields) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(" FROM ")
	queryBuffer.WriteString(buildModel().GetConfiguration().TableName)
	if len(fetchConfig.Predicates) > 0 {
		// WHERE
		queryBuffer.WriteString(" ")
		predicatesStr, predicateValues := predicatesToString(1, fetchConfig.Predicates)

		values = append(values, predicateValues...)
		queryBuffer.WriteString(predicatesStr)
	}
	if len(fetchConfig.OrderBys) > 0 {
		queryBuffer.WriteString(" ORDER BY ")
	}
	for i, orderBy := range fetchConfig.OrderBys {
		// Validate that the orderBy.Field is a field
		valid := false
		for _, field := range w.Config.Fields {
			if field.Name == orderBy.Field {
				valid = true
				break
			}
		}
		if !valid {
			return nil, fmt.Errorf("Could not order table '%v' by the invalid column '%v'",
				w.Config.TableName, orderBy.Field)
		}
		// Write to query
		queryBuffer.WriteString(orderBy.toString())
		if (i + 1) < len(fetchConfig.OrderBys) {
			queryBuffer.WriteString(", ")
		}
	}
	queryBuffer.WriteString(" LIMIT ")
	queryBuffer.WriteString(strconv.Itoa(fetchConfig.Limit))
	queryBuffer.WriteString(" OFFSET ")
	queryBuffer.WriteString(strconv.Itoa(fetchConfig.Offset))
	queryBuffer.WriteString(";")

	// Log Query
	query := queryBuffer.String()
	printQuery(query, values...)

	// Execute Query
	rows, err := w.Database.Query(query, values...)
	if err != nil {
		return nil, err
	}

	// Stuff into []Model
	var models []Model
	for rows.Next() {
		model := buildModel()

		// Consume Rows
		fields := model.GetConfiguration().Fields
		var s []interface{}
		for _, value := range fields {
			s = append(s, value.Pointer)
		}
		err := rows.Scan(s...)
		if err != nil {
			return nil, err
		}

		models = append(models, model.(Model))
	}

	// Expand foreign references
	err = expandForeigns(buildModel, models)
	if err != nil {
		return nil, err
	}

	// OK
	return models, nil
}
<|endoftext|>


<commit_before>package mpb

import (
	"bytes"
	"container/heap"
	"context"
	"fmt"
	"io"
	"math"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/v8/cwriter"
)

const (
	prr = 150 * time.Millisecond // default RefreshRate
)

// DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
var DoneError = fmt.Errorf("%T instance can't be reused after it's done!", (*Progress)(nil))

// Progress represents a container that renders one or more progress bars.
type Progress struct {
	ctx          context.Context
	uwg          *sync.WaitGroup
	cwg          *sync.WaitGroup
	bwg          *sync.WaitGroup
	operateState chan func(*pState)
	interceptIo  chan func(io.Writer)
	done         chan struct{}
	refreshCh    chan time.Time
	once         sync.Once
}

// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
type pState struct {
	bHeap       priorityQueue
	heapUpdated bool
	pMatrix     map[int][]chan int
	aMatrix     map[int][]chan int

	// for reuse purposes
	rows []io.Reader
	pool []*Bar

	// following are provided/overrided by user
	idCount          int
	reqWidth         int
	popPriority      int
	popCompleted     bool
	outputDiscarded  bool
	rr               time.Duration
	uwg              *sync.WaitGroup
	externalRefresh  <-chan interface{}
	renderDelay      <-chan struct{}
	shutdownNotifier chan struct{}
	queueBars        map[*Bar]*Bar
	output           io.Writer
	debugOut         io.Writer
}

// New creates new Progress container instance. It's not possible to
// reuse instance after (*Progress).Wait method has been called.
func New(options ...ContainerOption) *Progress {
	return NewWithContext(context.Background(), options...)
}

// NewWithContext creates new Progress container instance with provided
// context. It's not possible to reuse instance after (*Progress).Wait
// method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
	s := &pState{
		bHeap:       priorityQueue{},
		rows:        make([]io.Reader, 0, 64),
		pool:        make([]*Bar, 0, 64),
		rr:          prr,
		queueBars:   make(map[*Bar]*Bar),
		output:      os.Stdout,
		popPriority: math.MinInt32,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		ctx:          ctx,
		uwg:          s.uwg,
		cwg:          new(sync.WaitGroup),
		bwg:          new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		interceptIo:  make(chan func(io.Writer)),
		done:         make(chan struct{}),
	}

	p.cwg.Add(1)
	go p.serve(s, cwriter.New(s.output))
	return p
}

// AddBar creates a bar with default bar filler.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	return p.New(total, BarStyle(), options...)
}

// AddSpinner creates a bar with default spinner filler.
func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
	return p.New(total, SpinnerStyle(), options...)
}

// New creates a bar which renders itself by `BarFiller`, which is obtained
// via provided `BarFillerBuilder`. If `total <= 0` triggering complete event
// by increment methods is disabled. Panics if *Progress instance is done,
// i.e. called after (*Progress).Wait().
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
	filler := builder.Build()
	if filler == nil {
		filler = NopStyle().Build()
	}
	p.bwg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(ps *pState) {
		bs := ps.makeBarState(total, filler, options...)
		bar := newBar(p, bs)
		if bs.wait.bar != nil {
			ps.queueBars[bs.wait.bar] = bar
		} else {
			heap.Push(&ps.bHeap, bar)
			ps.heapUpdated = true
		}
		ps.idCount++
		result <- bar
	}:
		bar := <-result
		return bar
	case <-p.done:
		p.bwg.Done()
		panic(DoneError)
	}
}

func (p *Progress) traverseBars(cb func(b *Bar) bool) {
	sync := make(chan struct{})
	select {
	case p.operateState <- func(s *pState) {
		for i := 0; i < s.bHeap.Len(); i++ {
			bar := s.bHeap[i]
			if !cb(bar) {
				break
			}
		}
		close(sync)
	}:
		<-sync
	case <-p.done:
	}
}

// UpdateBarPriority same as *Bar.SetPriority(int).
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		b.priority = priority
		heap.Fix(&s.bHeap, b.index)
	}:
	case <-p.done:
	}
}

// BarCount returns bars count.
func (p *Progress) BarCount() int {
	result := make(chan int)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Write is implementation of io.Writer.
// Writing to `*mpb.Progress` will print lines above a running bar.
// Writes aren't flushed immediatly, but at next refresh cycle.
// If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
// is returned.
func (p *Progress) Write(b []byte) (int, error) {
	type result struct {
		n   int
		err error
	}
	ch := make(chan *result)
	select {
	case p.interceptIo <- func(w io.Writer) {
		n, err := w.Write(b)
		ch <- &result{n, err}
	}:
		res := <-ch
		return res.n, res.err
	case <-p.done:
		return 0, DoneError
	}
}

// Wait waits for all bars to complete and finally shutdowns container.
// After this method has been called, there is no way to reuse *Progress
// instance.
func (p *Progress) Wait() {
	// wait for user wg, if any
	if p.uwg != nil {
		p.uwg.Wait()
	}

	// wait for bars to quit, if any
	p.bwg.Wait()

	p.once.Do(p.shutdown)

	// wait for container to quit
	p.cwg.Wait()
}

func (p *Progress) shutdown() {
	close(p.done)
}

func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
	defer p.cwg.Done()

	render := func(out io.Writer) {
		err := s.render(cw)
		for err != nil {
			if out != nil {
				_, err = fmt.Fprintln(out, err)
			} else {
				panic(err)
			}
			out = nil
		}
	}

	p.refreshCh = s.newTicker(p.done)

	for {
		select {
		case op := <-p.operateState:
			op(s)
		case fn := <-p.interceptIo:
			fn(cw)
		case <-p.refreshCh:
			render(s.debugOut)
		case <-s.shutdownNotifier:
			for s.heapUpdated {
				render(s.debugOut)
			}
			return
		}
	}
}

func (s *pState) render(cw *cwriter.Writer) error {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	width, height, err := cw.GetTermSize()
	if err != nil {
		width = s.reqWidth
		height = s.bHeap.Len()
	}
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		go bar.render(width)
	}

	return s.flush(cw, height)
}

func (s *pState) flush(cw *cwriter.Writer, height int) error {
	var wg sync.WaitGroup
	var popCount int

	for s.bHeap.Len() > 0 {
		var usedRows int
		b := heap.Pop(&s.bHeap).(*Bar)
		frame := <-b.frameCh
		if frame.recovered {
			s.heapUpdated = true
			continue
		}
		for i := len(frame.rows) - 1; i >= 0; i-- {
			if row := frame.rows[i]; len(s.rows) < height {
				s.rows = append(s.rows, row)
				usedRows++
			} else {
				wg.Add(1)
				go func() {
					_, _ = io.Copy(io.Discard, row)
					wg.Done()
				}()
			}
		}
		if frame.shutdown != 0 {
			b.Wait() // waiting for b.done, so it's safe to read b.bs
			drop := b.bs.dropOnComplete
			if qb, ok := s.queueBars[b]; ok {
				delete(s.queueBars, b)
				qb.priority = b.priority
				s.pool = append(s.pool, qb)
				drop = true
			} else if s.popCompleted && !b.bs.noPop {
				if frame.shutdown > 1 {
					popCount += usedRows
					drop = true
				} else {
					s.popPriority++
					b.priority = s.popPriority
				}
			}
			if drop {
				s.heapUpdated = true
				continue
			}
		}
		s.pool = append(s.pool, b)
	}

	wg.Add(1)
	go func() {
		for _, b := range s.pool {
			heap.Push(&s.bHeap, b)
		}
		wg.Done()
	}()

	readRows := len(s.rows)
	for i := readRows - 1; i >= 0; i-- {
		_, err := cw.ReadFrom(s.rows[i])
		if err != nil {
			if s.debugOut != nil {
				fmt.Fprintf(s.debugOut, "cw.ReadFrom: %s\n", err.Error())
			}
			readRows--
		}
	}

	err := cw.Flush(readRows - popCount)
	wg.Wait()
	s.rows = s.rows[:0]
	s.pool = s.pool[:0]
	return err
}

func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
	ch := make(chan time.Time)
	if s.shutdownNotifier == nil {
		s.shutdownNotifier = make(chan struct{})
	}
	go func() {
		if s.renderDelay != nil {
			<-s.renderDelay
		}
		var internalRefresh <-chan time.Time
		if !s.outputDiscarded {
			if s.externalRefresh == nil {
				ticker := time.NewTicker(s.rr)
				defer ticker.Stop()
				internalRefresh = ticker.C
			}
		} else {
			s.externalRefresh = nil
		}
		for {
			select {
			case t := <-internalRefresh:
				ch <- t
			case x := <-s.externalRefresh:
				if t, ok := x.(time.Time); ok {
					ch <- t
				} else {
					ch <- time.Now()
				}
			case <-done:
				close(s.shutdownNotifier)
				return
			}
		}
	}()
	return ch
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
	bs := &bState{
		id:       s.idCount,
		priority: s.idCount,
		reqWidth: s.reqWidth,
		total:    total,
		filler:   filler,
		debugOut: s.debugOut,
	}

	if total > 0 {
		bs.triggerComplete = true
	}

	for _, opt := range options {
		if opt != nil {
			opt(bs)
		}
	}

	if bs.middleware != nil {
		bs.filler = bs.middleware(filler)
		bs.middleware = nil
	}

	for i := 0; i < len(bs.buffers); i++ {
		bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
	}

	bs.subscribeDecorators()

	return bs
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		go maxWidthDistributor(column)
	}
}

func maxWidthDistributor(column []chan int) {
	var maxWidth int
	for _, ch := range column {
		if w := <-ch; w > maxWidth {
			maxWidth = w
		}
	}
	for _, ch := range column {
		ch <- maxWidth
	}
}
<commit_msg>Revert "*Progress.Add is redundant, removing"<commit_after>package mpb

import (
	"bytes"
	"container/heap"
	"context"
	"fmt"
	"io"
	"math"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/v8/cwriter"
)

const (
	prr = 150 * time.Millisecond // default RefreshRate
)

// DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
var DoneError = fmt.Errorf("%T instance can't be reused after it's done!", (*Progress)(nil))

// Progress represents a container that renders one or more progress bars.
type Progress struct {
	ctx          context.Context
	uwg          *sync.WaitGroup
	cwg          *sync.WaitGroup
	bwg          *sync.WaitGroup
	operateState chan func(*pState)
	interceptIo  chan func(io.Writer)
	done         chan struct{}
	refreshCh    chan time.Time
	once         sync.Once
}

// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
type pState struct {
	bHeap       priorityQueue
	heapUpdated bool
	pMatrix     map[int][]chan int
	aMatrix     map[int][]chan int

	// for reuse purposes
	rows []io.Reader
	pool []*Bar

	// following are provided/overrided by user
	idCount          int
	reqWidth         int
	popPriority      int
	popCompleted     bool
	outputDiscarded  bool
	rr               time.Duration
	uwg              *sync.WaitGroup
	externalRefresh  <-chan interface{}
	renderDelay      <-chan struct{}
	shutdownNotifier chan struct{}
	queueBars        map[*Bar]*Bar
	output           io.Writer
	debugOut         io.Writer
}

// New creates new Progress container instance. It's not possible to
// reuse instance after (*Progress).Wait method has been called.
func New(options ...ContainerOption) *Progress {
	return NewWithContext(context.Background(), options...)
}

// NewWithContext creates new Progress container instance with provided
// context. It's not possible to reuse instance after (*Progress).Wait
// method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
	s := &pState{
		bHeap:       priorityQueue{},
		rows:        make([]io.Reader, 0, 64),
		pool:        make([]*Bar, 0, 64),
		rr:          prr,
		queueBars:   make(map[*Bar]*Bar),
		output:      os.Stdout,
		popPriority: math.MinInt32,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		ctx:          ctx,
		uwg:          s.uwg,
		cwg:          new(sync.WaitGroup),
		bwg:          new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		interceptIo:  make(chan func(io.Writer)),
		done:         make(chan struct{}),
	}

	p.cwg.Add(1)
	go p.serve(s, cwriter.New(s.output))
	return p
}

// AddBar creates a bar with default bar filler.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	return p.New(total, BarStyle(), options...)
}

// AddSpinner creates a bar with default spinner filler.
func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
	return p.New(total, SpinnerStyle(), options...)
}

// New creates a bar with provided BarFillerBuilder.
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
	return p.Add(total, builder.Build(), options...)
}

// Add creates a bar which renders itself by provided filler.
// If `total <= 0` triggering complete event by increment methods is disabled.
// Panics if *Progress instance is done, i.e. called after (*Progress).Wait().
func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
	if filler == nil {
		filler = NopStyle().Build()
	}
	p.bwg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(ps *pState) {
		bs := ps.makeBarState(total, filler, options...)
		bar := newBar(p, bs)
		if bs.wait.bar != nil {
			ps.queueBars[bs.wait.bar] = bar
		} else {
			heap.Push(&ps.bHeap, bar)
			ps.heapUpdated = true
		}
		ps.idCount++
		result <- bar
	}:
		bar := <-result
		return bar
	case <-p.done:
		p.bwg.Done()
		panic(DoneError)
	}
}

func (p *Progress) traverseBars(cb func(b *Bar) bool) {
	sync := make(chan struct{})
	select {
	case p.operateState <- func(s *pState) {
		for i := 0; i < s.bHeap.Len(); i++ {
			bar := s.bHeap[i]
			if !cb(bar) {
				break
			}
		}
		close(sync)
	}:
		<-sync
	case <-p.done:
	}
}

// UpdateBarPriority same as *Bar.SetPriority(int).
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		b.priority = priority
		heap.Fix(&s.bHeap, b.index)
	}:
	case <-p.done:
	}
}

// BarCount returns bars count.
func (p *Progress) BarCount() int {
	result := make(chan int)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Write is implementation of io.Writer.
// Writing to `*mpb.Progress` will print lines above a running bar.
// Writes aren't flushed immediatly, but at next refresh cycle.
// If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
// is returned.
func (p *Progress) Write(b []byte) (int, error) {
	type result struct {
		n   int
		err error
	}
	ch := make(chan *result)
	select {
	case p.interceptIo <- func(w io.Writer) {
		n, err := w.Write(b)
		ch <- &result{n, err}
	}:
		res := <-ch
		return res.n, res.err
	case <-p.done:
		return 0, DoneError
	}
}

// Wait waits for all bars to complete and finally shutdowns container.
// After this method has been called, there is no way to reuse *Progress
// instance.
func (p *Progress) Wait() {
	// wait for user wg, if any
	if p.uwg != nil {
		p.uwg.Wait()
	}

	// wait for bars to quit, if any
	p.bwg.Wait()

	p.once.Do(p.shutdown)

	// wait for container to quit
	p.cwg.Wait()
}

func (p *Progress) shutdown() {
	close(p.done)
}

func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
	defer p.cwg.Done()

	render := func(out io.Writer) {
		err := s.render(cw)
		for err != nil {
			if out != nil {
				_, err = fmt.Fprintln(out, err)
			} else {
				panic(err)
			}
			out = nil
		}
	}

	p.refreshCh = s.newTicker(p.done)

	for {
		select {
		case op := <-p.operateState:
			op(s)
		case fn := <-p.interceptIo:
			fn(cw)
		case <-p.refreshCh:
			render(s.debugOut)
		case <-s.shutdownNotifier:
			for s.heapUpdated {
				render(s.debugOut)
			}
			return
		}
	}
}

func (s *pState) render(cw *cwriter.Writer) error {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	width, height, err := cw.GetTermSize()
	if err != nil {
		width = s.reqWidth
		height = s.bHeap.Len()
	}
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		go bar.render(width)
	}

	return s.flush(cw, height)
}

func (s *pState) flush(cw *cwriter.Writer, height int) error {
	var wg sync.WaitGroup
	var popCount int

	for s.bHeap.Len() > 0 {
		var usedRows int
		b := heap.Pop(&s.bHeap).(*Bar)
		frame := <-b.frameCh
		if frame.recovered {
			s.heapUpdated = true
			continue
		}
		for i := len(frame.rows) - 1; i >= 0; i-- {
			if row := frame.rows[i]; len(s.rows) < height {
				s.rows = append(s.rows, row)
				usedRows++
			} else {
				wg.Add(1)
				go func() {
					_, _ = io.Copy(io.Discard, row)
					wg.Done()
				}()
			}
		}
		if frame.shutdown != 0 {
			b.Wait() // waiting for b.done, so it's safe to read b.bs
			drop := b.bs.dropOnComplete
			if qb, ok := s.queueBars[b]; ok {
				delete(s.queueBars, b)
				qb.priority = b.priority
				s.pool = append(s.pool, qb)
				drop = true
			} else if s.popCompleted && !b.bs.noPop {
				if frame.shutdown > 1 {
					popCount += usedRows
					drop = true
				} else {
					s.popPriority++
					b.priority = s.popPriority
				}
			}
			if drop {
				s.heapUpdated = true
				continue
			}
		}
		s.pool = append(s.pool, b)
	}

	wg.Add(1)
	go func() {
		for _, b := range s.pool {
			heap.Push(&s.bHeap, b)
		}
		wg.Done()
	}()

	readRows := len(s.rows)
	for i := readRows - 1; i >= 0; i-- {
		_, err := cw.ReadFrom(s.rows[i])
		if err != nil {
			if s.debugOut != nil {
				fmt.Fprintf(s.debugOut, "cw.ReadFrom: %s\n", err.Error())
			}
			readRows--
		}
	}

	err := cw.Flush(readRows - popCount)
	wg.Wait()
	s.rows = s.rows[:0]
	s.pool = s.pool[:0]
	return err
}

func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
	ch := make(chan time.Time)
	if s.shutdownNotifier == nil {
		s.shutdownNotifier = make(chan struct{})
	}
	go func() {
		if s.renderDelay != nil {
			<-s.renderDelay
		}
		var internalRefresh <-chan time.Time
		if !s.outputDiscarded {
			if s.externalRefresh == nil {
				ticker := time.NewTicker(s.rr)
				defer ticker.Stop()
				internalRefresh = ticker.C
			}
		} else {
			s.externalRefresh = nil
		}
		for {
			select {
			case t := <-internalRefresh:
				ch <- t
			case x := <-s.externalRefresh:
				if t, ok := x.(time.Time); ok {
					ch <- t
				} else {
					ch <- time.Now()
				}
			case <-done:
				close(s.shutdownNotifier)
				return
			}
		}
	}()
	return ch
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := s.bHeap[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
	bs := &bState{
		id:       s.idCount,
		priority: s.idCount,
		reqWidth: s.reqWidth,
		total:    total,
		filler:   filler,
		debugOut: s.debugOut,
	}

	if total > 0 {
		bs.triggerComplete = true
	}

	for _, opt := range options {
		if opt != nil {
			opt(bs)
		}
	}

	if bs.middleware != nil {
		bs.filler = bs.middleware(filler)
		bs.middleware = nil
	}

	for i := 0; i < len(bs.buffers); i++ {
		bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
	}

	bs.subscribeDecorators()

	return bs
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		go maxWidthDistributor(column)
	}
}

func maxWidthDistributor(column []chan int) {
	var maxWidth int
	for _, ch := range column {
		if w := <-ch; w > maxWidth {
			maxWidth = w
		}
	}
	for _, ch := range column {
		ch <- maxWidth
	}
}
<|endoftext|>


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

// nolint:gocritic
package network // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/dotnetdiagnosticsreceiver/network"

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"unicode/utf16"
)

// MultiReader provides an abstraction to make reading from the diagnostics
// stream easier for callers.
type MultiReader interface {
	// ReadByte reads and returns a single byte from the stream
	ReadByte() (byte, error)
	// ReadCompressedInt32 reads a compressed int32 from the stream
	ReadCompressedInt32() (int32, error)
	// ReadCompressedUInt32 reads a compressed uint32 from the stream
	ReadCompressedUInt32() (uint32, error)
	// ReadCompressedInt64 reads a compressed int64 from the stream
	ReadCompressedInt64() (int64, error)
	// ReadCompressedUInt64 reads a compressed uint64 from the stream
	ReadCompressedUInt64() (uint64, error)
	// Read reads bytes from the underlying stream into the passed in reference
	Read(interface{}) error
	// ReadUTF16 reads and returns a zero-terminated UTF16 string from the stream
	ReadUTF16() (string, error)
	// ReadASCII reads an ASCII string of the given length from the underlying stream
	ReadASCII(int) (string, error)
	// AssertNextByteEquals reads the next byte and returns an error if it doesn't
	// equal the passed in byte
	AssertNextByteEquals(byte) error
	// Seek moves the current position forward by reading and throwing away the
	// specified number of bytes
	Seek(i int) error
	// Align moves the current position forward for 4-byte alignment
	// https://github.com/Microsoft/perfview/blob/main/src/TraceEvent/EventPipe/EventPipeFormat.md
	Align() error
	// Pos returns the position (byte offset) from the underlying PositionalReader
	Pos() int
	// Reset resets the underlying PositionalReader
	Reset()
	// Flush flushes the underlying BlobWriter
	Flush()
}

// mReader is an implementation of MultiReader. It wraps a PositionalReader,
// which keeps track of the byte count.
type mReader struct {
	pr PositionalReader
}

func NewMultiReader(r io.Reader, bw BlobWriter) MultiReader {
	pr := NewPositionalReader(r, bw)
	return &mReader{pr: pr}
}

var _ MultiReader = (*mReader)(nil)

var singleByte = make([]byte, 1)

// ReadByte reads and returns a single byte from the stream
func (r *mReader) ReadByte() (byte, error) {
	err := r.Read(singleByte)
	if err != nil {
		return 0, err
	}
	return singleByte[0], nil
}

// From https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/EventPipe/EventPipeFormat.md
// > Variable length encodings of int and long. This is a standard technique where
// > the least significant 7 bits are encoded in a byte. If all remaining bits in the
// > starting number are zero then the 8th bit is set to zero to mark the end of the
// > encoding. Otherwise the 8th bit is set to 1 to indicate another byte is
// > forthcoming containing the next 7 least significant bits. This pattern repeats
// > until there are no more non-zero bits to encode, at most 5 bytes for a 32 bit
// > int and 10 bytes for a 64 bit long.

const shiftWidth = 7
const valueMask = 0x7f
const continueFlag = 0x80

// ReadCompressedInt32 reads a compressed int32 from the stream
func (r *mReader) ReadCompressedInt32() (int32, error) {
	uInt32, err := r.ReadCompressedUInt32()
	return int32(uInt32), err
}

// ReadCompressedUInt32 reads a compressed uint32 from the stream
func (r *mReader) ReadCompressedUInt32() (out uint32, err error) {
	const maxLen = 5
	for i := 0; ; i++ {
		if i > maxLen {
			return 0, errors.New("CompressedInt32 is too long")
		}
		b, err := r.ReadByte()
		if err != nil {
			return 0, err
		}
		out |= uint32(b&valueMask) << (i * shiftWidth)
		if (b & continueFlag) == 0 {
			break
		}
	}
	return out, nil
}

// ReadCompressedInt64 reads a compressed int64 from the stream
func (r *mReader) ReadCompressedInt64() (int64, error) {
	uInt64, err := r.ReadCompressedUInt64()
	return int64(uInt64), err
}

// ReadCompressedUInt64 reads a compressed uint64 from the stream
func (r *mReader) ReadCompressedUInt64() (out uint64, err error) {
	const maxLen = 10
	for i := 0; ; i++ {
		if i > maxLen {
			return 0, errors.New("CompressedInt64 is too long")
		}
		b, err := r.ReadByte()
		if err != nil {
			return 0, err
		}
		out |= uint64(b&valueMask) << (i * shiftWidth)
		if (b & continueFlag) == 0 {
			break
		}
	}
	return out, nil
}

// Read reads bytes from the underlying stream into v
func (r *mReader) Read(v interface{}) error {
	return binary.Read(r.pr, ByteOrder, v)
}

// ReadUTF16 reads and returns a zero-terminated UTF16 string from the stream
func (r *mReader) ReadUTF16() (s string, err error) {
	var a []uint16
	for {
		var c uint16
		err = r.Read(&c)
		if err != nil {
			return "", err
		}
		if c == 0 {
			break
		}
		a = append(a, c)
	}
	return string(utf16.Decode(a)), nil
}

// ReadASCII reads an ASCII string of the given length from the underlying stream
func (r *mReader) ReadASCII(strlen int) (string, error) {
	b := make([]byte, strlen)
	_, err := r.pr.Read(b)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

// AssertNextByteEquals reads the next byte and returns an error if it doesn't
// equal the passed in byte
func (r *mReader) AssertNextByteEquals(b byte) error {
	found, err := r.ReadByte()
	if err != nil {
		return err
	}
	if found != b {
		return fmt.Errorf("next byte assertion failed, expected %d, got %d", b, found)
	}
	return nil
}

// Seek moves the current position forward by reading and throwing away the
// specified number of bytes
func (r *mReader) Seek(i int) error {
	_, err := r.pr.Read(make([]byte, i))
	if err != nil {
		return err
	}
	return nil
}

// Align moves the current position forward for 4-byte alignment
// https://github.com/Microsoft/perfview/blob/main/src/TraceEvent/EventPipe/EventPipeFormat.md
func (r *mReader) Align() error {
	mod := r.Pos() % 4
	if mod > 0 {
		err := r.Seek(4 - mod)
		if err != nil {
			return err
		}
	}
	return nil
}

// Pos returns the position (byte offset) from the underlying PositionalReader
func (r *mReader) Pos() int {
	return r.pr.Position()
}

// Reset resets the underlying PositionalReader
func (r *mReader) Reset() {
	r.pr.Reset()
}

// Flush flushes the underlying BlobWriter
func (r *mReader) Flush() {
	r.pr.Flush()
}

// PositionalReader is an interface extracted from posReader so it can be
// swapped for testing. It implements io.Reader, can return the number of bytes
// read, and can reset the current position.
type PositionalReader interface {
	Read(p []byte) (n int, err error)
	Position() int
	Flush()
	Reset()
}

// posReader wraps an io.Reader, and keeps track of the number of bytes read.
// Implements PositionalReader.
type posReader struct {
	r   io.Reader
	pos int
	w   BlobWriter
}

// NewPositionalReader creates a PositionalReader, wrapping the passed-in
// io.Reader, and keeping track of the number of bytes read.
func NewPositionalReader(r io.Reader, w BlobWriter) PositionalReader {
	return &posReader{r: r, w: w}
}

// Read implements io.Reader incrementing the current position by the number of
// bytes read.
func (r *posReader) Read(p []byte) (n int, err error) {
	n, err = r.r.Read(p)
	r.w.append(p, n)
	r.pos += n
	return n, err
}

// Position returns the number of bytes read from the underlying io.Reader.
func (r *posReader) Position() int {
	return r.pos
}

// Reset resets the position to as close to zero as possible without altering
// the current 4-byte alignment. This is to prevent integer overflow during
// long-running usage.
func (r *posReader) Reset() {
	r.pos = r.pos % 4
}

func (r *posReader) Flush() {
	r.w.flush()
}
<commit_msg>[receiver/dotnetdiagnosticsreceiver/network] Enable gocritic (#12511)<commit_after>// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package network // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/dotnetdiagnosticsreceiver/network"

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"unicode/utf16"
)

// MultiReader provides an abstraction to make reading from the diagnostics
// stream easier for callers.
type MultiReader interface {
	// ReadByte reads and returns a single byte from the stream
	ReadByte() (byte, error)
	// ReadCompressedInt32 reads a compressed int32 from the stream
	ReadCompressedInt32() (int32, error)
	// ReadCompressedUInt32 reads a compressed uint32 from the stream
	ReadCompressedUInt32() (uint32, error)
	// ReadCompressedInt64 reads a compressed int64 from the stream
	ReadCompressedInt64() (int64, error)
	// ReadCompressedUInt64 reads a compressed uint64 from the stream
	ReadCompressedUInt64() (uint64, error)
	// Read reads bytes from the underlying stream into the passed in reference
	Read(interface{}) error
	// ReadUTF16 reads and returns a zero-terminated UTF16 string from the stream
	ReadUTF16() (string, error)
	// ReadASCII reads an ASCII string of the given length from the underlying stream
	ReadASCII(int) (string, error)
	// AssertNextByteEquals reads the next byte and returns an error if it doesn't
	// equal the passed in byte
	AssertNextByteEquals(byte) error
	// Seek moves the current position forward by reading and throwing away the
	// specified number of bytes
	Seek(i int) error
	// Align moves the current position forward for 4-byte alignment
	// https://github.com/Microsoft/perfview/blob/main/src/TraceEvent/EventPipe/EventPipeFormat.md
	Align() error
	// Pos returns the position (byte offset) from the underlying PositionalReader
	Pos() int
	// Reset resets the underlying PositionalReader
	Reset()
	// Flush flushes the underlying BlobWriter
	Flush()
}

// mReader is an implementation of MultiReader. It wraps a PositionalReader,
// which keeps track of the byte count.
type mReader struct {
	pr PositionalReader
}

func NewMultiReader(r io.Reader, bw BlobWriter) MultiReader {
	pr := NewPositionalReader(r, bw)
	return &mReader{pr: pr}
}

var _ MultiReader = (*mReader)(nil)

var singleByte = make([]byte, 1)

// ReadByte reads and returns a single byte from the stream
func (r *mReader) ReadByte() (byte, error) {
	err := r.Read(singleByte)
	if err != nil {
		return 0, err
	}
	return singleByte[0], nil
}

// From https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/EventPipe/EventPipeFormat.md
// > Variable length encodings of int and long. This is a standard technique where
// > the least significant 7 bits are encoded in a byte. If all remaining bits in the
// > starting number are zero then the 8th bit is set to zero to mark the end of the
// > encoding. Otherwise the 8th bit is set to 1 to indicate another byte is
// > forthcoming containing the next 7 least significant bits. This pattern repeats
// > until there are no more non-zero bits to encode, at most 5 bytes for a 32 bit
// > int and 10 bytes for a 64 bit long.

const shiftWidth = 7
const valueMask = 0x7f
const continueFlag = 0x80

// ReadCompressedInt32 reads a compressed int32 from the stream
func (r *mReader) ReadCompressedInt32() (int32, error) {
	uInt32, err := r.ReadCompressedUInt32()
	return int32(uInt32), err
}

// ReadCompressedUInt32 reads a compressed uint32 from the stream
func (r *mReader) ReadCompressedUInt32() (out uint32, err error) {
	const maxLen = 5
	for i := 0; ; i++ {
		if i > maxLen {
			return 0, errors.New("CompressedInt32 is too long")
		}
		b, err := r.ReadByte()
		if err != nil {
			return 0, err
		}
		out |= uint32(b&valueMask) << (i * shiftWidth)
		if (b & continueFlag) == 0 {
			break
		}
	}
	return out, nil
}

// ReadCompressedInt64 reads a compressed int64 from the stream
func (r *mReader) ReadCompressedInt64() (int64, error) {
	uInt64, err := r.ReadCompressedUInt64()
	return int64(uInt64), err
}

// ReadCompressedUInt64 reads a compressed uint64 from the stream
func (r *mReader) ReadCompressedUInt64() (out uint64, err error) {
	const maxLen = 10
	for i := 0; ; i++ {
		if i > maxLen {
			return 0, errors.New("CompressedInt64 is too long")
		}
		b, err := r.ReadByte()
		if err != nil {
			return 0, err
		}
		out |= uint64(b&valueMask) << (i * shiftWidth)
		if (b & continueFlag) == 0 {
			break
		}
	}
	return out, nil
}

// Read reads bytes from the underlying stream into v
func (r *mReader) Read(v interface{}) error {
	return binary.Read(r.pr, ByteOrder, v)
}

// ReadUTF16 reads and returns a zero-terminated UTF16 string from the stream
func (r *mReader) ReadUTF16() (s string, err error) {
	var a []uint16
	for {
		var c uint16
		err = r.Read(&c)
		if err != nil {
			return "", err
		}
		if c == 0 {
			break
		}
		a = append(a, c)
	}
	return string(utf16.Decode(a)), nil
}

// ReadASCII reads an ASCII string of the given length from the underlying stream
func (r *mReader) ReadASCII(strlen int) (string, error) {
	b := make([]byte, strlen)
	_, err := r.pr.Read(b)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

// AssertNextByteEquals reads the next byte and returns an error if it doesn't
// equal the passed in byte
func (r *mReader) AssertNextByteEquals(b byte) error {
	found, err := r.ReadByte()
	if err != nil {
		return err
	}
	if found != b {
		return fmt.Errorf("next byte assertion failed, expected %d, got %d", b, found)
	}
	return nil
}

// Seek moves the current position forward by reading and throwing away the
// specified number of bytes
func (r *mReader) Seek(i int) error {
	_, err := r.pr.Read(make([]byte, i))
	if err != nil {
		return err
	}
	return nil
}

// Align moves the current position forward for 4-byte alignment
// https://github.com/Microsoft/perfview/blob/main/src/TraceEvent/EventPipe/EventPipeFormat.md
func (r *mReader) Align() error {
	mod := r.Pos() % 4
	if mod > 0 {
		err := r.Seek(4 - mod)
		if err != nil {
			return err
		}
	}
	return nil
}

// Pos returns the position (byte offset) from the underlying PositionalReader
func (r *mReader) Pos() int {
	return r.pr.Position()
}

// Reset resets the underlying PositionalReader
func (r *mReader) Reset() {
	r.pr.Reset()
}

// Flush flushes the underlying BlobWriter
func (r *mReader) Flush() {
	r.pr.Flush()
}

// PositionalReader is an interface extracted from posReader so it can be
// swapped for testing. It implements io.Reader, can return the number of bytes
// read, and can reset the current position.
type PositionalReader interface {
	Read(p []byte) (n int, err error)
	Position() int
	Flush()
	Reset()
}

// posReader wraps an io.Reader, and keeps track of the number of bytes read.
// Implements PositionalReader.
type posReader struct {
	r   io.Reader
	pos int
	w   BlobWriter
}

// NewPositionalReader creates a PositionalReader, wrapping the passed-in
// io.Reader, and keeping track of the number of bytes read.
func NewPositionalReader(r io.Reader, w BlobWriter) PositionalReader {
	return &posReader{r: r, w: w}
}

// Read implements io.Reader incrementing the current position by the number of
// bytes read.
func (r *posReader) Read(p []byte) (n int, err error) {
	n, err = r.r.Read(p)
	r.w.append(p, n)
	r.pos += n
	return n, err
}

// Position returns the number of bytes read from the underlying io.Reader.
func (r *posReader) Position() int {
	return r.pos
}

// Reset resets the position to as close to zero as possible without altering
// the current 4-byte alignment. This is to prevent integer overflow during
// long-running usage.
func (r *posReader) Reset() {
	r.pos %= 4
}

func (r *posReader) Flush() {
	r.w.flush()
}
<|endoftext|>


<commit_before>package gen

func entryScript() string {
	return `
var system=sys();
`
}
<commit_msg>Add Tpl to the entry script<commit_after>package gen

func entryScript() string {
	return `
var system=sys();
var Tpl={};
Tpl.funcs={};
Tpl.funcs.world=function(name){
	return name+",world"
}
Tpl.getTplFuncs=function(){
	var rst=[]
	for (var prop in Tpl.funcs){
		if (Tpl.funcs.hasOwnProperty(prop)){
			rst.push(prop)
		}
	}
	return rst
}
`
}
<|endoftext|>


<commit_before>package golang

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"strings"

	"sourcegraph.com/sourcegraph/srcgraph/config"
	"sourcegraph.com/sourcegraph/srcgraph/container"
	"sourcegraph.com/sourcegraph/srcgraph/graph"
	"sourcegraph.com/sourcegraph/srcgraph/grapher2"
	"sourcegraph.com/sourcegraph/srcgraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/toolchain/golang/gog"
	"sourcegraph.com/sourcegraph/srcgraph/unit"
)

func init() {
	grapher2.Register(&Package{}, grapher2.DockerGrapher{defaultGoVersion})
}

func (v *goVersion) BuildGrapher(dir string, unit unit.SourceUnit, c *config.Repository) (*container.Command, error) {
	gogBinPath := filepath.Join(os.Getenv("GOBIN"), "gog")

	pkg := unit.(*Package)

	cont, err := v.containerForRepo(dir, unit, c)
	if err != nil {
		return nil, err
	}

	// Install VCS tools in Docker container.
	cont.Dockerfile = append(cont.Dockerfile, []byte("RUN apt-get -yq install git mercurial bzr subversion\n")...)

	cont.AddFiles = append(cont.AddFiles, [2]string{gogBinPath, "/usr/local/bin/gog"})
	cont.Cmd = []string{"bash", "-c", fmt.Sprintf("go get -v -t %s; gog %s", pkg.ImportPath, pkg.ImportPath)}

	cmd := container.Command{
		Container: *cont,
		Transform: func(in []byte) ([]byte, error) {
			var o gog.Output
			err := json.Unmarshal(in, &o)
			if err != nil {
				return nil, err
			}

			o2 := grapher2.Output{
				Symbols: make([]*graph.Symbol, len(o.Symbols)),
				Refs:    make([]*graph.Ref, len(o.Refs)),
				Docs:    make([]*graph.Doc, len(o.Docs)),
			}

			for i, gs := range o.Symbols {
				o2.Symbols[i], err = v.convertGoSymbol(gs, c)
				if err != nil {
					return nil, err
				}
			}
			for i, gr := range o.Refs {
				o2.Refs[i], err = v.convertGoRef(gr, c)
				if err != nil {
					return nil, err
				}
			}
			for i, gd := range o.Docs {
				o2.Docs[i], err = v.convertGoDoc(gd, c)
				if err != nil {
					return nil, err
				}
			}

			return json.Marshal(o2)
		},
	}

	return &cmd, nil
}

// SymbolData is extra Go-specific data about a symbol.
type SymbolData struct {
	gog.SymbolInfo

	// PackageImportPath is the import path of the package containing this
	// symbol (if this symbol is not a package). If this symbol is a package,
	// PackageImportPath is its own import path.
	PackageImportPath string `json:",omitempty"`
}

func (v *goVersion) convertGoSymbol(gs *gog.Symbol, c *config.Repository) (*graph.Symbol, error) {
	resolvedTarget, err := v.resolveGoImportDep(gs.SymbolKey.PackageImportPath, c)
	if err != nil {
		return nil, err
	}
	path := graph.SymbolPath(pathOrDot(strings.Join(gs.Path, "/")))
	treePath := graph.TreePath(path)
	if !treePath.IsValid() {
		return nil, fmt.Errorf("'%s' is not a valid tree-path")
	}

	sym := &graph.Symbol{
		SymbolKey: graph.SymbolKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: graph.SymbolKind(gog.GeneralKindMap[gs.Kind]),

		File:     gs.File,
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.SymbolInfo.Exported,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := SymbolData{
		PackageImportPath: gs.SymbolKey.PackageImportPath,
		SymbolInfo:        gs.SymbolInfo,
	}
	sym.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if sym.Kind == "func" {
		sym.Callable = true
	}

	return sym, nil
}

func (v *goVersion) convertGoRef(gr *gog.Ref, c *config.Repository) (*graph.Ref, error) {
	resolvedTarget, err := v.resolveGoImportDep(gr.Symbol.PackageImportPath, c)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	return &graph.Ref{
		SymbolRepo:     uriOrEmpty(resolvedTarget.ToRepoCloneURL),
		SymbolPath:     graph.SymbolPath(pathOrDot(strings.Join(gr.Symbol.Path, "/"))),
		SymbolUnit:     resolvedTarget.ToUnit,
		SymbolUnitType: resolvedTarget.ToUnitType,
		Def:            gr.Def,
		File:           gr.File,
		Start:          gr.Span[0],
		End:            gr.Span[1],
	}, nil
}

func (v *goVersion) convertGoDoc(gd *gog.Doc, c *config.Repository) (*graph.Doc, error) {
	resolvedTarget, err := v.resolveGoImportDep(gd.PackageImportPath, c)
	if err != nil {
		return nil, err
	}
	return &graph.Doc{
		SymbolKey: graph.SymbolKey{
			Path:     graph.SymbolPath(pathOrDot(strings.Join(gd.Path, "/"))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		},
		Format: gd.Format,
		Data:   gd.Data,
		File:   gd.File,
		Start:  gd.Span[0],
		End:    gd.Span[1],
	}, nil
}

func uriOrEmpty(cloneURL string) repo.URI {
	if cloneURL == "" {
		return ""
	}
	return repo.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return fmt.Sprintf("./%s", path)
}
<commit_msg>keep original go symbol path<commit_after>package golang

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

	"strings"

	"sourcegraph.com/sourcegraph/srcgraph/config"
	"sourcegraph.com/sourcegraph/srcgraph/container"
	"sourcegraph.com/sourcegraph/srcgraph/graph"
	"sourcegraph.com/sourcegraph/srcgraph/grapher2"
	"sourcegraph.com/sourcegraph/srcgraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/toolchain/golang/gog"
	"sourcegraph.com/sourcegraph/srcgraph/unit"
)

func init() {
	grapher2.Register(&Package{}, grapher2.DockerGrapher{defaultGoVersion})
}

func (v *goVersion) BuildGrapher(dir string, unit unit.SourceUnit, c *config.Repository) (*container.Command, error) {
	gogBinPath := filepath.Join(os.Getenv("GOBIN"), "gog")

	pkg := unit.(*Package)

	cont, err := v.containerForRepo(dir, unit, c)
	if err != nil {
		return nil, err
	}

	// Install VCS tools in Docker container.
	cont.Dockerfile = append(cont.Dockerfile, []byte("RUN apt-get -yq install git mercurial bzr subversion\n")...)

	cont.AddFiles = append(cont.AddFiles, [2]string{gogBinPath, "/usr/local/bin/gog"})
	cont.Cmd = []string{"bash", "-c", fmt.Sprintf("go get -v -t %s; gog %s", pkg.ImportPath, pkg.ImportPath)}

	cmd := container.Command{
		Container: *cont,
		Transform: func(in []byte) ([]byte, error) {
			var o gog.Output
			err := json.Unmarshal(in, &o)
			if err != nil {
				return nil, err
			}

			o2 := grapher2.Output{
				Symbols: make([]*graph.Symbol, len(o.Symbols)),
				Refs:    make([]*graph.Ref, len(o.Refs)),
				Docs:    make([]*graph.Doc, len(o.Docs)),
			}

			for i, gs := range o.Symbols {
				o2.Symbols[i], err = v.convertGoSymbol(gs, c)
				if err != nil {
					return nil, err
				}
			}
			for i, gr := range o.Refs {
				o2.Refs[i], err = v.convertGoRef(gr, c)
				if err != nil {
					return nil, err
				}
			}
			for i, gd := range o.Docs {
				o2.Docs[i], err = v.convertGoDoc(gd, c)
				if err != nil {
					return nil, err
				}
			}

			return json.Marshal(o2)
		},
	}

	return &cmd, nil
}

// SymbolData is extra Go-specific data about a symbol.
type SymbolData struct {
	gog.SymbolInfo

	// PackageImportPath is the import path of the package containing this
	// symbol (if this symbol is not a package). If this symbol is a package,
	// PackageImportPath is its own import path.
	PackageImportPath string `json:",omitempty"`
}

func (v *goVersion) convertGoSymbol(gs *gog.Symbol, c *config.Repository) (*graph.Symbol, error) {
	resolvedTarget, err := v.resolveGoImportDep(gs.SymbolKey.PackageImportPath, c)
	if err != nil {
		return nil, err
	}
	path := graph.SymbolPath(pathOrDot(strings.Join(gs.Path, "/")))
	treePath := treePath(string(path))
	if !treePath.IsValid() {
		return nil, fmt.Errorf("'%s' is not a valid tree-path")
	}

	sym := &graph.Symbol{
		SymbolKey: graph.SymbolKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: graph.SymbolKind(gog.GeneralKindMap[gs.Kind]),

		File:     gs.File,
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.SymbolInfo.Exported,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := SymbolData{
		PackageImportPath: gs.SymbolKey.PackageImportPath,
		SymbolInfo:        gs.SymbolInfo,
	}
	sym.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if sym.Kind == "func" {
		sym.Callable = true
	}

	return sym, nil
}

func (v *goVersion) convertGoRef(gr *gog.Ref, c *config.Repository) (*graph.Ref, error) {
	resolvedTarget, err := v.resolveGoImportDep(gr.Symbol.PackageImportPath, c)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	return &graph.Ref{
		SymbolRepo:     uriOrEmpty(resolvedTarget.ToRepoCloneURL),
		SymbolPath:     graph.SymbolPath(pathOrDot(strings.Join(gr.Symbol.Path, "/"))),
		SymbolUnit:     resolvedTarget.ToUnit,
		SymbolUnitType: resolvedTarget.ToUnitType,
		Def:            gr.Def,
		File:           gr.File,
		Start:          gr.Span[0],
		End:            gr.Span[1],
	}, nil
}

func (v *goVersion) convertGoDoc(gd *gog.Doc, c *config.Repository) (*graph.Doc, error) {
	resolvedTarget, err := v.resolveGoImportDep(gd.PackageImportPath, c)
	if err != nil {
		return nil, err
	}
	return &graph.Doc{
		SymbolKey: graph.SymbolKey{
			Path:     graph.SymbolPath(pathOrDot(strings.Join(gd.Path, "/"))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		},
		Format: gd.Format,
		Data:   gd.Data,
		File:   gd.File,
		Start:  gd.Span[0],
		End:    gd.Span[1],
	}, nil
}

func uriOrEmpty(cloneURL string) repo.URI {
	if cloneURL == "" {
		return ""
	}
	return repo.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return path
}

func treePath(path string) graph.TreePath {
	if path == "" || path == "." {
		return graph.TreePath(".")
	}
	return graph.TreePath(fmt.Sprintf("./%s", path))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)

// BufferLineCount is the number of lines of buffer to keep in memory from logcat.
const BufferLineCount = 1000

// devices is the list of devices that we currently know about.
var devices []*Device

// deviceIndex the index into devices that we're currently displaying.
var deviceIndex int

// LogBuffer represents a fixed-size buffer of log lines. The lines are indexed with 0 being the
// oldest line and N being the most recent log line. Older logs will be expired, but the index
// will never be reused (this means a LogView can reference expired lines without having to
// update the views every time a line is expired).
type LogBuffer struct {
	lines []string

	// nextLineIndex is the index into the lines[] slice that the *next* log line will go.
	nextLineIndex int

	// lineNo is the line number (starting from 1) that the *most recent* log line has. This number
	// increments for every line that's added to the buffer, whereas nextLineIndex wraps around as
	// the buffer fills up.
	lineNo int64
}

// LogView is a "view" over a device's logs. There's a special view that represents all logs, and
// then there is zero or more LogView's for filtered results.
type LogView struct {
}

// Device is all the stuff we know about a single attached device.
type Device struct {
	// ID is the identfiied of the device, that you'd pass to adb's "-s" parameter
	ID string

	// Name is the display name of the device, that we show in the UI.
	Name string

	logBuffer *LogBuffer

	// mutex is used to synchronize access to the log buffer.
	mutex *sync.Mutex

	waiting bool
	ping    chan int
}

func (d *Device) appendLine(line string) {
	d.mutex.Lock()
	d.logBuffer.lines[d.logBuffer.nextLineIndex] = line
	d.logBuffer.lineNo++
	d.logBuffer.nextLineIndex++
	if d.logBuffer.nextLineIndex >= len(d.logBuffer.lines) {
		d.logBuffer.nextLineIndex = 0
	}
	d.mutex.Unlock()

	if d.waiting {
		d.ping <- 1
	}
}

// NewDevice creates a new instance of Device for the device with the given ID and name.
func NewDevice(id, name string) *Device {
	return &Device{
		ID:   id,
		Name: name,
		logBuffer: &LogBuffer{
			lines:         make([]string, BufferLineCount),
			nextLineIndex: 0,
			lineNo:        0,
		},
		mutex:   &sync.Mutex{},
		ping:    make(chan int),
		waiting: false,
	}
}

// Open opens a connection to the given device via an adb command. Basically we start streaming
// logcat output to the device's AbdContext.
func (d *Device) Open() {
	cmd := exec.Command("adb", "-s", d.ID, "logcat", "-v", "threadtime")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic("An error occurred reading output: " + err.Error())
	}
	scanner := bufio.NewScanner(stdout)
	go func() {
		lastTime := time.Now()
		for scanner.Scan() {
			if !d.waiting {
				thisTime := time.Now()
				if thisTime.UnixNano()-lastTime.UnixNano() > 500000000 {
					// More than 1/2 second passed, we can start notifying listeners of new updates
					d.waiting = true
				}
				lastTime = thisTime
			}
			d.appendLine(scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			panic("An error occurred reading output: " + err.Error())
		}
	}()
	err = cmd.Start()
	if err != nil {
		panic("Error starting adb logcat: " + err.Error())
	}
}

// GetLastLineNo returns the index of the last line in the log buffer.
// You should only call this method when you've got the device's mutex locked.
func (lb *LogBuffer) GetLastLineNo() int64 {
	return lb.lineNo
}

// GetLines returns a slice of the lines from the given line number to the given line number.
// You should only call this method when you've got the device's mutex locked.
func (lb *LogBuffer) GetLines(from, to int64) []string {
	if from < 0 {
		from = 0
	}
	if from == to {
		return make([]string, 0)
	}

	// TODO: can we keep these in a buffer to avoid allocating the new array each time?
	res := make([]string, int(to-from))
	i := 0
	for lineNo := to; lineNo > from; lineNo-- {
		index := lb.nextLineIndex - int(lb.lineNo-lineNo) - 1
		if index < 0 {
			index += len(lb.lines)
		}
		if index < 0 {
			break
		}
		res[i] = lb.lines[index]
		i++
	}
	return res
}

func tbprint(x, y int, fg, bg termbox.Attribute, msg string) int {
	n := 0
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		width := runewidth.RuneWidth(c)
		x += width
		n += width
	}
	return n
}

func render() {
	coldef := termbox.ColorDefault
	termbox.Clear(coldef, coldef)
	w, h := termbox.Size()

	// Top line, device list
	x := 0
	coldef = termbox.ColorDefault | termbox.AttrReverse
	for _, d := range devices {
		x += tbprint(x, 0, coldef, coldef, "［")
		coldef = termbox.ColorDefault
		x += tbprint(x, 0, coldef, coldef, d.Name)
		coldef = termbox.ColorDefault | termbox.AttrReverse
		x += tbprint(x, 0, coldef, coldef, "］")
	}
	for ; x < w; x++ {
		termbox.SetCell(x, 0, ' ', coldef, coldef)
	}

	// Start from bottom and write up
	if len(devices) > deviceIndex {
		logBuffer := devices[deviceIndex].logBuffer
		devices[deviceIndex].mutex.Lock()
		lastLineNo := logBuffer.GetLastLineNo()
		firstLineNo := lastLineNo - int64(h) - 3
		lines := logBuffer.GetLines(firstLineNo, lastLineNo)
		devices[deviceIndex].mutex.Unlock()

		coldef = termbox.ColorDefault
		for i := 0; i < len(lines); i++ {
			y := h - 2 - i
			tbprint(0, y, coldef, coldef, lines[i])
		}
	}

	// Second from bottom line, filter.
	// TODO: the first tab ("no filter") should have no filter line
	y := h - 2
	termbox.SetCursor(1, y)

	// Last line, tabs, one tab per configured filter
	x = 0
	y = h - 1
	// TODO: for tabs:
	coldef = termbox.ColorDefault
	x += tbprint(x, y, coldef, coldef, "［")
	coldef = termbox.ColorDefault | termbox.AttrReverse
	x += tbprint(x, y, coldef, coldef, "no filter")
	coldef = termbox.ColorDefault
	x += tbprint(x, y, coldef, coldef, "］")
	x += tbprint(x, y, coldef, coldef, "［+filter］")
	for ; x < w; x++ {
		termbox.SetCell(x, y, ' ', coldef, coldef)
	}

	termbox.Flush()
}

// refreshDevices refreshes the list of attached devices (by running 'adb devices' basically).
func refreshDevices() {
	cmd := exec.Command("adb", "devices", "-l")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic("'adb devices' error: " + err.Error())
	}

	scanner := bufio.NewScanner(stdout)
	err = cmd.Start()
	if err != nil {
		panic("'adb devices' error: " + err.Error())
	}

	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.Fields(line)
		if len(parts) < 2 || parts[1] != "device" {
			fmt.Fprintf(os.Stderr, "Not a device line: '%s'\n", line)
			continue
		}

		id := parts[0]
		name := id
		for i := 2; i < len(parts); i++ {
			kvp := strings.Split(parts[i], ":")
			if len(kvp) == 2 && kvp[0] == "model" {
				name = kvp[1]
			}
		}

		d := NewDevice(id, strings.Replace(name, "_", " ", -1))
		d.Open()
		devices = append(devices, d)
	}
	if err := scanner.Err(); err != nil {
		panic("An error occurred reading output: " + err.Error())
	}
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)

	refreshDevices()
	render()

	events := make(chan termbox.Event)
	go func() {
		for {
			events <- termbox.PollEvent()
		}
	}()

mainloop:
	for {
		select {
		case ev := <-events:
			if ev.Key == termbox.KeyEsc {
				break mainloop
			}
			render()

		case <-devices[deviceIndex].ping:
			render()
		}
	}
}
<commit_msg>Added an EditBox class for working with editing text.<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)

// BufferLineCount is the number of lines of buffer to keep in memory from logcat.
const BufferLineCount = 1000

// PreferredHorizontalThreshold ??
const PreferredHorizontalThreshold = 5

// devices is the list of devices that we currently know about.
var devices []*Device

// deviceIndex the index into devices that we're currently displaying.
var deviceIndex int

// viewIndex the current LogView that we're looking at (0 == the full LogBuffer, 1 == the first one
// etc)
var viewIndex int

// The EditBox we're writing into
var editbox EditBox

// EditBox represents the box where you're currently typing text.
type EditBox struct {
	text              []byte
	visualOffset      int
	cursorOffsetBytes int
	cursorOffsetCells int
	cursorOffsetRunes int
}

// LogBuffer represents a fixed-size buffer of log lines. The lines are indexed with 0 being the
// oldest line and N being the most recent log line. Older logs will be expired, but the index
// will never be reused (this means a LogView can reference expired lines without having to
// update the views every time a line is expired).
type LogBuffer struct {
	lines []string

	// nextLineIndex is the index into the lines[] slice that the *next* log line will go.
	nextLineIndex int

	// lineNo is the line number (starting from 1) that the *most recent* log line has. This number
	// increments for every line that's added to the buffer, whereas nextLineIndex wraps around as
	// the buffer fills up.
	lineNo int64
}

// LogView is a "view" over a device's logs. There's a special view that represents all logs, and
// then there is zero or more LogView's for filtered results.
type LogView struct {
	Name string
}

// Device is all the stuff we know about a single attached device.
type Device struct {
	// ID is the identfiied of the device, that you'd pass to adb's "-s" parameter
	ID string

	// Name is the display name of the device, that we show in the UI.
	Name string

	logBuffer *LogBuffer
	logViews  []*LogView

	// mutex is used to synchronize access to the log buffer.
	mutex *sync.Mutex

	waiting bool
	ping    chan int
}

func (d *Device) appendLine(line string) {
	d.mutex.Lock()
	d.logBuffer.lines[d.logBuffer.nextLineIndex] = line
	d.logBuffer.lineNo++
	d.logBuffer.nextLineIndex++
	if d.logBuffer.nextLineIndex >= len(d.logBuffer.lines) {
		d.logBuffer.nextLineIndex = 0
	}
	d.mutex.Unlock()

	if d.waiting {
		d.ping <- 1
	}
}

// NewDevice creates a new instance of Device for the device with the given ID and name.
func NewDevice(id, name string) *Device {
	return &Device{
		ID:   id,
		Name: name,
		logBuffer: &LogBuffer{
			lines:         make([]string, BufferLineCount),
			nextLineIndex: 0,
			lineNo:        0,
		},
		mutex:   &sync.Mutex{},
		ping:    make(chan int),
		waiting: false,
	}
}

// Open opens a connection to the given device via an adb command. Basically we start streaming
// logcat output to the device's AbdContext.
func (d *Device) Open() {
	cmd := exec.Command("adb", "-s", d.ID, "logcat", "-v", "threadtime")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic("An error occurred reading output: " + err.Error())
	}
	scanner := bufio.NewScanner(stdout)
	go func() {
		lastTime := time.Now()
		for scanner.Scan() {
			if !d.waiting {
				thisTime := time.Now()
				if thisTime.UnixNano()-lastTime.UnixNano() > 500000000 {
					// More than 1/2 second passed, we can start notifying listeners of new updates
					d.waiting = true
				}
				lastTime = thisTime
			}
			d.appendLine(scanner.Text())
		}
		if err := scanner.Err(); err != nil {
			panic("An error occurred reading output: " + err.Error())
		}
	}()
	err = cmd.Start()
	if err != nil {
		panic("Error starting adb logcat: " + err.Error())
	}
}

// GetLastLineNo returns the index of the last line in the log buffer.
// You should only call this method when you've got the device's mutex locked.
func (lb *LogBuffer) GetLastLineNo() int64 {
	return lb.lineNo
}

// GetLines returns a slice of the lines from the given line number to the given line number.
// You should only call this method when you've got the device's mutex locked.
func (lb *LogBuffer) GetLines(from, to int64) []string {
	if from < 0 {
		from = 0
	}
	if from == to {
		return make([]string, 0)
	}

	// TODO: can we keep these in a buffer to avoid allocating the new array each time?
	res := make([]string, int(to-from))
	i := 0
	for lineNo := to; lineNo > from; lineNo-- {
		index := lb.nextLineIndex - int(lb.lineNo-lineNo) - 1
		if index < 0 {
			index += len(lb.lines)
		}
		if index < 0 {
			break
		}
		res[i] = lb.lines[index]
		i++
	}
	return res
}

// Draw draws the EditBox in the given location
func (eb *EditBox) Draw(x, y, w int) {
	eb.AdjustVisualOffset(w)

	const coldef = termbox.ColorDefault
	fill(x, y, w, 1, termbox.Cell{Ch: ' '})

	t := eb.text
	lx := 0
	tabstop := 0
	for {
		rx := lx - eb.visualOffset
		if len(t) == 0 {
			break
		}

		if rx >= w {
			termbox.SetCell(x+w-1, y, '→',
				coldef, coldef)
			break
		}

		r, size := utf8.DecodeRune(t)
		if r == '\t' {
			for ; lx < tabstop; lx++ {
				rx = lx - eb.visualOffset
				if rx >= w {
					goto next
				}

				if rx >= 0 {
					termbox.SetCell(x+rx, y, ' ', coldef, coldef)
				}
			}
		} else {
			if rx >= 0 {
				termbox.SetCell(x+rx, y, r, coldef, coldef)
			}
			lx += runewidth.RuneWidth(r)
		}
	next:
		t = t[size:]
	}

	if eb.visualOffset != 0 {
		termbox.SetCell(x, y, '←', coldef, coldef)
	}
}

// AdjustVisualOffset adjusts line visual offset to a proper value depending on width
func (eb *EditBox) AdjustVisualOffset(width int) {
	ht := PreferredHorizontalThreshold
	maxHorizontalThreshold := (width - 1) / 2
	if ht > maxHorizontalThreshold {
		ht = maxHorizontalThreshold
	}

	threshold := width - 1
	if eb.visualOffset != 0 {
		threshold = width - ht
	}
	if eb.cursorOffsetCells-eb.visualOffset >= threshold {
		eb.visualOffset = eb.cursorOffsetCells + (ht - width + 1)
	}

	if eb.visualOffset != 0 && eb.cursorOffsetCells-eb.visualOffset < ht {
		eb.visualOffset = eb.cursorOffsetCells - ht
		if eb.visualOffset < 0 {
			eb.visualOffset = 0
		}
	}
}

func adjustOffset(text []byte, offsetBytes int) (offsetCells, offsetRunes int) {
	text = text[:offsetBytes]
	for len(text) > 0 {
		r, size := utf8.DecodeRune(text)
		text = text[size:]
		offsetRunes++
		offsetCells += runewidth.RuneWidth(r)
	}
	return
}

// MoveCursorTo moves the cursor to the given byte offset.
func (eb *EditBox) MoveCursorTo(offsetBytes int) {
	eb.cursorOffsetBytes = offsetBytes
	eb.cursorOffsetCells, eb.cursorOffsetCells = adjustOffset(eb.text, offsetBytes)
}

// RuneUnderCursor returns the rune (and it's size) under the cursor.
func (eb *EditBox) RuneUnderCursor() (rune, int) {
	return utf8.DecodeRune(eb.text[eb.cursorOffsetBytes:])
}

// RuneBeforeCursor returns the rune (and it's size) before the cursor.
func (eb *EditBox) RuneBeforeCursor() (rune, int) {
	return utf8.DecodeLastRune(eb.text[:eb.cursorOffsetBytes])
}

// MoveCursorOneRuneBackward moves the cursor one rune to the left (if possible)
func (eb *EditBox) MoveCursorOneRuneBackward() {
	if eb.cursorOffsetBytes == 0 {
		return
	}
	_, size := eb.RuneBeforeCursor()
	eb.MoveCursorTo(eb.cursorOffsetBytes - size)
}

// MoveCursorOneRuneForward moves the cursor one run to the right (if possible)
func (eb *EditBox) MoveCursorOneRuneForward() {
	if eb.cursorOffsetBytes == len(eb.text) {
		return
	}
	_, size := eb.RuneUnderCursor()
	eb.MoveCursorTo(eb.cursorOffsetBytes + size)
}

// MoveCursorToBeginningOfTheLine moves the cursor to the beginning of the line.
func (eb *EditBox) MoveCursorToBeginningOfTheLine() {
	eb.MoveCursorTo(0)
}

// MoveCursorToEndOfTheLine moves the cursor to the end of the line.
func (eb *EditBox) MoveCursorToEndOfTheLine() {
	eb.MoveCursorTo(len(eb.text))
}

// DeleteRuneBackward delets the rune to the left of the cursor.
func (eb *EditBox) DeleteRuneBackward() {
	if eb.cursorOffsetBytes == 0 {
		return
	}

	eb.MoveCursorOneRuneBackward()
	_, size := eb.RuneUnderCursor()
	eb.text = byteSliceRemove(eb.text, eb.cursorOffsetBytes, eb.cursorOffsetBytes+size)
}

// DeleteRuneForward deletes the rune to the right of the cursor.
func (eb *EditBox) DeleteRuneForward() {
	if eb.cursorOffsetBytes == len(eb.text) {
		return
	}
	_, size := eb.RuneUnderCursor()
	eb.text = byteSliceRemove(eb.text, eb.cursorOffsetBytes, eb.cursorOffsetBytes+size)
}

// DeleteTheRestOfTheLine deletes everything to the right of the cursor.
func (eb *EditBox) DeleteTheRestOfTheLine() {
	eb.text = eb.text[:eb.cursorOffsetBytes]
}

// InsertRune inserts the given rune at the current cursor position.
func (eb *EditBox) InsertRune(r rune) {
	var buf [utf8.UTFMax]byte
	n := utf8.EncodeRune(buf[:], r)
	eb.text = byteSliceInsert(eb.text, eb.cursorOffsetBytes, buf[:n])
	eb.MoveCursorOneRuneForward()
}

// CursorX ...
// Please, keep in mind that cursor depends on the value of visualOffset, which
// is being set on Draw() call, so.. call this method after Draw() one.
func (eb *EditBox) CursorX() int {
	return eb.cursorOffsetCells - eb.visualOffset
}

func byteSliceRemove(text []byte, from, to int) []byte {
	size := to - from
	copy(text[from:], text[to:])
	text = text[:len(text)-size]
	return text
}

func byteSliceGrow(s []byte, desiredCap int) []byte {
	if cap(s) < desiredCap {
		ns := make([]byte, len(s), desiredCap)
		copy(ns, s)
		return ns
	}
	return s
}

func byteSliceInsert(text []byte, offset int, what []byte) []byte {
	n := len(text) + len(what)
	text = byteSliceGrow(text, n)
	text = text[:n]
	copy(text[offset+len(what):], text[offset:])
	copy(text[offset:], what)
	return text
}

func tbprint(x, y int, fg, bg termbox.Attribute, msg string) int {
	n := 0
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		width := runewidth.RuneWidth(c)
		x += width
		n += width
	}
	return n
}

func fill(x, y, w, h int, cell termbox.Cell) {
	for ly := 0; ly < h; ly++ {
		for lx := 0; lx < w; lx++ {
			termbox.SetCell(x+lx, y+ly, cell.Ch, cell.Fg, cell.Bg)
		}
	}
}

func render() {
	coldef := termbox.ColorDefault
	termbox.Clear(coldef, coldef)
	w, h := termbox.Size()

	// Top line, device list
	x := 0
	coldef = termbox.ColorDefault | termbox.AttrReverse
	for _, d := range devices {
		x += tbprint(x, 0, coldef, coldef, "［")
		coldef = termbox.ColorDefault
		x += tbprint(x, 0, coldef, coldef, d.Name)
		coldef = termbox.ColorDefault | termbox.AttrReverse
		x += tbprint(x, 0, coldef, coldef, "］")
	}
	for ; x < w; x++ {
		termbox.SetCell(x, 0, ' ', coldef, coldef)
	}

	// Start from bottom and write up
	if len(devices) > deviceIndex {
		logBuffer := devices[deviceIndex].logBuffer
		devices[deviceIndex].mutex.Lock()
		lastLineNo := logBuffer.GetLastLineNo()
		firstLineNo := lastLineNo - int64(h) - 12
		lines := logBuffer.GetLines(firstLineNo, lastLineNo)
		devices[deviceIndex].mutex.Unlock()

		coldef = termbox.ColorDefault
		for i := 0; i < len(lines); i++ {
			y := h - 3 - i
			tbprint(0, y, coldef, coldef, lines[i])
		}
	}

	// Second from bottom line, filter.
	// TODO: the first tab ("no filter") should have no filter line
	y := h - 2
	editbox.Draw(1, y, w-2)
	termbox.SetCursor(1+editbox.cursorOffsetCells, y)

	// Last line, tabs, one tab per configured filter
	x = 0
	y = h - 1
	coldef = termbox.ColorDefault
	x += tbprint(x, y, coldef, coldef, "［")
	if viewIndex == 0 {
		coldef = termbox.ColorDefault | termbox.AttrReverse
	}
	x += tbprint(x, y, coldef, coldef, "no filter")
	coldef = termbox.ColorDefault
	x += tbprint(x, y, coldef, coldef, "］")

	for n, view := range devices[deviceIndex].logViews {
		x += tbprint(x, y, coldef, coldef, "［")
		if viewIndex-1 == n {
			coldef = termbox.ColorDefault | termbox.AttrReverse
		}
		x += tbprint(x, y, coldef, coldef, view.Name)
		coldef = termbox.ColorDefault
		x += tbprint(x, y, coldef, coldef, "］")
	}

	x += tbprint(x, y, coldef, coldef, "［+filter］")
	for ; x < w; x++ {
		termbox.SetCell(x, y, ' ', coldef, coldef)
	}

	termbox.Flush()
}

// moveViewRight moves the selected view one to the right. If there's no more views, we'll create
// a new one with an empty filter.
func moveViewRight() {
	device := devices[deviceIndex]
	viewIndex++
	if (viewIndex - 1) == len(device.logViews) {
		device.logViews = append(device.logViews, &LogView{
			Name: "<empty>",
		})
	}
	render()
}

// refreshDevices refreshes the list of attached devices (by running 'adb devices' basically).
func refreshDevices() {
	cmd := exec.Command("adb", "devices", "-l")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic("'adb devices' error: " + err.Error())
	}

	scanner := bufio.NewScanner(stdout)
	err = cmd.Start()
	if err != nil {
		panic("'adb devices' error: " + err.Error())
	}

	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.Fields(line)
		if len(parts) < 2 || parts[1] != "device" {
			fmt.Fprintf(os.Stderr, "Not a device line: '%s'\n", line)
			continue
		}

		id := parts[0]
		name := id
		for i := 2; i < len(parts); i++ {
			kvp := strings.Split(parts[i], ":")
			if len(kvp) == 2 && kvp[0] == "model" {
				name = kvp[1]
			}
		}

		d := NewDevice(id, strings.Replace(name, "_", " ", -1))
		d.Open()
		devices = append(devices, d)

		deviceIndex = 0
		viewIndex = 0
	}
	if err := scanner.Err(); err != nil {
		panic("An error occurred reading output: " + err.Error())
	}
}

func main() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)

	refreshDevices()
	render()

	events := make(chan termbox.Event)
	go func() {
		for {
			events <- termbox.PollEvent()
		}
	}()

mainloop:
	for {
		select {
		case ev := <-events:
			switch ev.Key {
			case termbox.KeyEsc:
				break mainloop
			case termbox.KeyTab:
				// TODO: if shift pressed, move left
				moveViewRight()
			case termbox.KeyArrowLeft, termbox.KeyCtrlB:
				editbox.MoveCursorOneRuneBackward()
			case termbox.KeyArrowRight, termbox.KeyCtrlF:
				editbox.MoveCursorOneRuneForward()
			case termbox.KeyBackspace, termbox.KeyBackspace2:
				editbox.DeleteRuneBackward()
			case termbox.KeyDelete, termbox.KeyCtrlD:
				editbox.DeleteRuneForward()
			case termbox.KeySpace:
				editbox.InsertRune(' ')
			case termbox.KeyCtrlK:
				editbox.DeleteTheRestOfTheLine()
			case termbox.KeyHome, termbox.KeyCtrlA:
				editbox.MoveCursorToBeginningOfTheLine()
			case termbox.KeyEnd, termbox.KeyCtrlE:
				editbox.MoveCursorToEndOfTheLine()
			default:
				if ev.Ch != 0 {
					editbox.InsertRune(ev.Ch)
				}
			}
			render()
		case <-devices[deviceIndex].ping:
			render()
		}
	}
}
<|endoftext|>


<commit_before>// Code generated by "stringer -type=RelocType"; DO NOT EDIT

package obj

import "fmt"

const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLS"

var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 195, 206, 216, 225, 235, 249, 263, 279, 293, 307, 318, 332, 347, 364, 382, 403, 413, 424, 437}

func (i RelocType) String() string {
	i -= 1
	if i < 0 || i >= RelocType(len(_RelocType_index)-1) {
		return fmt.Sprintf("RelocType(%d)", i+1)
	}
	return _RelocType_name[_RelocType_index[i]:_RelocType_index[i+1]]
}
<commit_msg>cmd/internal/obj: regenerate relocation strings<commit_after>// Code generated by "stringer -type=RelocType"; DO NOT EDIT

package obj

import "fmt"

const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLS"

var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 129, 136, 144, 152, 160, 166, 172, 178, 188, 197, 208, 219, 229, 238, 248, 262, 276, 292, 306, 320, 331, 345, 360, 377, 395, 416, 426, 437, 450}

func (i RelocType) String() string {
	i -= 1
	if i < 0 || i >= RelocType(len(_RelocType_index)-1) {
		return fmt.Sprintf("RelocType(%d)", i+1)
	}
	return _RelocType_name[_RelocType_index[i]:_RelocType_index[i+1]]
}
<|endoftext|>


<commit_before>package node

import "golang.org/x/net/html"

// Attr returns attribute of node by given key.
func Attr(n *html.Node, key string) (string, bool) {
	for _, a := range n.Attr {
		if a.Key == key {
			return a.Val, true
		}
	}
	return "", false
}

// Children returns slice of nodes where each node is a child of given node and
// filter function returns true for corresponding child node.
func Children(n *html.Node, filter func(*html.Node) bool) []*html.Node {
	nodes := make([]*html.Node, 0)

	if filter == nil {
		filter = func(*html.Node) bool { return true }
	}

	for c := n.FirstChild; c != nil; c = c.NextSibling {
		if !filter(c) {
			continue
		}
		nodes = append(nodes, c)

	}
	return nodes
}
<commit_msg>update doc string<commit_after>package node

import "golang.org/x/net/html"

// Attr returns attribute of node by given key if any.
func Attr(n *html.Node, key string) (string, bool) {
	for _, a := range n.Attr {
		if a.Key == key {
			return a.Val, true
		}
	}
	return "", false
}

// Children returns slice of nodes where each node is a child of given node and
// filter function returns true for corresponding child node.
func Children(n *html.Node, filter func(*html.Node) bool) []*html.Node {
	nodes := make([]*html.Node, 0)

	if filter == nil {
		filter = func(*html.Node) bool { return true }
	}

	for c := n.FirstChild; c != nil; c = c.NextSibling {
		if !filter(c) {
			continue
		}
		nodes = append(nodes, c)

	}
	return nodes
}
<|endoftext|>


<commit_before>package gontpd

import (
	"context"
	"net"
	"sort"
	"time"
)

const (
	minStep   = 120 * time.Second
	minPoll   = 5
	maxPoll   = 8
	maxAdjust = 128 * time.Millisecond
	initRefer = 0x494e4954 // ascii for INIT
)

var (
	// the only data global variable
	syncLock = make(chan struct{})
)

func NewService(cfg *Config) (s *Service, err error) {
	addr, err := net.ResolveUDPAddr("udp", cfg.Listen)
	if err != nil {
		return nil, err
	}
	s = &Service{
		cfg: cfg,
	}
	s.conn, err = net.ListenUDP("udp", addr)
	if err != nil {
		return
	}

	for _, paddr := range cfg.ServerList {
		p := newPeer(paddr)
		s.peerList = append(s.peerList, p)
	}
	s.template = newTemplate()

	return
}

type Service struct {
	peerList []*peer
	conn     *net.UDPConn
	stats    *statistic
	cfg      *Config
	template []byte
	interval time.Duration
	poll     int8
}

func (s *Service) serveSyncLock() (err error) {

	var (
		lock  uint8
		timer *time.Timer
	)
	s.interval = 30 * time.Second
	timer = time.NewTimer(s.interval)

	for {
		select {
		case <-syncLock:
			lock |= 1
		case <-timer.C:
			lock |= 2
		}
		if lock != 3 {
			continue
		}

		err = s.syncClock()
		if err != nil {
			return
		}
		if !timer.Stop() {
			select {
			case <-timer.C:
			default:
			}
		}
		timer.Reset(s.interval)
		lock = 0
	}
	return
}

func (s *Service) syncClock() (err error) {
	availablePeers := []*peer{}
	for _, p := range s.peerList {
		if p.state < stateSyncing {
			Debug.Printf("%s state not Syncing", p.addr)
			continue
		}
		if p.stratum >= 15 {
			Debug.Printf("%s stratum is bad", p.addr)
			continue
		}
		if p.delay >= MaxRootDelay {
			Debug.Printf("%s delay %s >= MaxRootDelay %s", p.addr, p.delay, MaxRootDelay)
			continue
		}
		availablePeers = append(availablePeers, p)
	}

	switch len(availablePeers) {
	case 0:
		Warn.Print("no availablePeers")
		return nil
	case 1:
		s.setParams(availablePeers[0])
		return s.setOffset(availablePeers[0])
	default:
		sort.Sort(byOffset(availablePeers))
		Debug.Printf("availablePeers %d", len(availablePeers))
		for _, p := range availablePeers {
			Debug.Printf(" |-- %s", p)
		}
		bestPeer := availablePeers[len(availablePeers)/2]
		s.setParams(bestPeer)
		return s.setOffset(bestPeer)
	}
}

func (s *Service) setParams(p *peer) {

	SetLi(s.template, p.leap)
	SetVersion(s.template, 4)
	SetMode(s.template, ModeServer)

	SetUint8(s.template, Stratum, p.stratum+1)

	SetInt8(s.template, ClockPrecision, systemPrecision())

	SetUint32(s.template, RootDelayPos, toNtpShortTime(p.delay))
	s.stats.delayGauge.Set(p.delay.Seconds())

	s.stats.offsetGauge.Set(p.offset.Seconds())
	s.stats.dispGauge.Set(p.dispersion.Seconds())

	SetUint32(s.template, RootDispersionPos, toNtpShortTime(p.dispersion))
	SetUint64(s.template, ReferenceTimeStamp, toNtpTime(p.updateAt))
	SetUint32(s.template, ReferIDPos, p.referId)

	switch p.state {
	case stateStable:
		s.interval = maxInterval
	case stateSyncing:
		s.interval = p.interval * 2
	default:
		s.interval = 30 * time.Second
	}
	s.poll = int8(durationToPoll(s.interval))
	Debug.Printf("try to set poll %d, by %s", s.poll, s.interval)
	if s.poll > maxPoll {
		s.poll = maxPoll
	}
	if s.poll < minPoll {
		s.poll = minPoll
	}
	SetInt8(s.template, Poll, s.poll)
}

type byOffset []*peer

func (b byOffset) Len() int {
	return len(b)
}

func (b byOffset) Swap(i, j int) {
	b[i], b[j] = b[j], b[i]
}

func (b byOffset) Less(i, j int) bool {
	return b[i].offset < b[j].offset
}

func (s *Service) workerDo(i int) {
	var (
		n           int
		remoteAddr  *net.UDPAddr
		err         error
		receiveTime time.Time
		limiter     *secondLimitter
	)

	p := make([]byte, 48)
	if s.cfg.ReqRateSec > 0 {
		Info.Printf("limiter %d", s.cfg.ReqRateSec)
		limiter = newLimitter(s.cfg.ReqRateSec)
		go limiter.run()
	}

	defer func(i int) {
		if r := recover(); r != nil {
			Error.Printf("Worker: %d fatal, reason:%s, read:%d", i, r, n)
		} else {
			Info.Printf("Worker: %d exited, reason:%s, read:%d", i, err, n)
		}
	}(i)

	for {
		n, remoteAddr, err = s.conn.ReadFromUDP(p)
		if err != nil {
			return
		}

		receiveTime = time.Now()
		if n < 48 {
			Warn.Printf("worker: %s get small packet %d",
				remoteAddr.String(), n)
			continue
		}

		if limiter != nil {
			if !limiter.allow(remoteAddr.IP, receiveTime) {
				Warn.Printf("worker[%d]: get limitted ip %s",
					i, remoteAddr.String())
				continue
			}
		}

		// GetMode
		switch p[LiVnMode] &^ 0xf8 {
		case ModeReserved:
			fallthrough
		case ModeClient:
			copy(p[0:OriginTimeStamp], s.template)
			copy(p[OriginTimeStamp:OriginTimeStamp+8],
				p[TransmitTimeStamp:TransmitTimeStamp+8])
			SetUint64(p, ReceiveTimeStamp, toNtpTime(receiveTime))
			SetUint64(p, TransmitTimeStamp, toNtpTime(time.Now()))
			_, err = s.conn.WriteToUDP(p, remoteAddr)
			if err != nil {
				Error.Printf("worker: %s write failed. %s", remoteAddr.String(), err)
				continue
			}
			s.stats.logIP(remoteAddr)
		default:
			Warn.Printf("%s not client request mode:%x",
				remoteAddr.String(), p[LiVnMode]&^0xf8)
		}
	}
}

func (s *Service) Serve() error {
	if s.cfg.ExpoMetric != "" {
		s.stats = newStatistic(s.cfg)
	}
	ctx := context.TODO()
	for _, p := range s.peerList {
		go p.run(ctx)
	}

	for i := 0; i < s.cfg.WorkerNum; i++ {
		go s.workerDo(i)
	}

	return s.serveSyncLock()
}

func newTemplate() (t []byte) {
	t = make([]byte, 48)
	SetLi(t, 0)
	SetVersion(t, 4)
	SetMode(t, ModeServer)
	SetUint32(t, ReferIDPos, initRefer)
	SetInt8(t, Poll, 4)
	SetUint64(t, ReferenceTimeStamp, toNtpTime(time.Now()))
	return
}
<commit_msg>add kod acst<commit_after>package gontpd

import (
	"context"
	"net"
	"sort"
	"time"
)

const (
	minStep   = 120 * time.Second
	minPoll   = 5
	maxPoll   = 8
	maxAdjust = 128 * time.Millisecond
	initRefer = 0x494e4954 // ascii for INIT
)

var (
	// the only data global variable
	syncLock = make(chan struct{})
)

func NewService(cfg *Config) (s *Service, err error) {
	addr, err := net.ResolveUDPAddr("udp", cfg.Listen)
	if err != nil {
		return nil, err
	}
	s = &Service{
		cfg: cfg,
	}
	s.conn, err = net.ListenUDP("udp", addr)
	if err != nil {
		return
	}

	for _, paddr := range cfg.ServerList {
		p := newPeer(paddr)
		s.peerList = append(s.peerList, p)
	}
	s.template = newTemplate()

	return
}

type Service struct {
	peerList []*peer
	conn     *net.UDPConn
	stats    *statistic
	cfg      *Config
	template []byte
	interval time.Duration
	poll     int8
}

func (s *Service) serveSyncLock() (err error) {

	var (
		lock  uint8
		timer *time.Timer
	)
	s.interval = 30 * time.Second
	timer = time.NewTimer(s.interval)

	for {
		select {
		case <-syncLock:
			lock |= 1
		case <-timer.C:
			lock |= 2
		}
		if lock != 3 {
			continue
		}

		err = s.syncClock()
		if err != nil {
			return
		}
		if !timer.Stop() {
			select {
			case <-timer.C:
			default:
			}
		}
		timer.Reset(s.interval)
		lock = 0
	}
	return
}

func (s *Service) syncClock() (err error) {
	availablePeers := []*peer{}
	for _, p := range s.peerList {
		if p.state < stateSyncing {
			Debug.Printf("%s state not Syncing", p.addr)
			continue
		}
		if p.stratum >= 15 {
			Debug.Printf("%s stratum is bad", p.addr)
			continue
		}
		if p.delay >= MaxRootDelay {
			Debug.Printf("%s delay %s >= MaxRootDelay %s", p.addr, p.delay, MaxRootDelay)
			continue
		}
		availablePeers = append(availablePeers, p)
	}

	switch len(availablePeers) {
	case 0:
		Warn.Print("no availablePeers")
		return nil
	case 1:
		s.setParams(availablePeers[0])
		return s.setOffset(availablePeers[0])
	default:
		sort.Sort(byOffset(availablePeers))
		Debug.Printf("availablePeers %d", len(availablePeers))
		for _, p := range availablePeers {
			Debug.Printf(" |-- %s", p)
		}
		bestPeer := availablePeers[len(availablePeers)/2]
		s.setParams(bestPeer)
		return s.setOffset(bestPeer)
	}
}

func (s *Service) setParams(p *peer) {

	SetLi(s.template, p.leap)
	SetVersion(s.template, 4)
	SetMode(s.template, ModeServer)

	SetUint8(s.template, Stratum, p.stratum+1)

	SetInt8(s.template, ClockPrecision, systemPrecision())

	SetUint32(s.template, RootDelayPos, toNtpShortTime(p.delay))
	s.stats.delayGauge.Set(p.delay.Seconds())

	s.stats.offsetGauge.Set(p.offset.Seconds())
	s.stats.dispGauge.Set(p.dispersion.Seconds())

	SetUint32(s.template, RootDispersionPos, toNtpShortTime(p.dispersion))
	SetUint64(s.template, ReferenceTimeStamp, toNtpTime(p.updateAt))
	SetUint32(s.template, ReferIDPos, p.referId)

	switch p.state {
	case stateStable:
		s.interval = maxInterval
	case stateSyncing:
		s.interval = p.interval * 2
	default:
		s.interval = 30 * time.Second
	}
	s.poll = int8(durationToPoll(s.interval))
	Debug.Printf("try to set poll %d, by %s", s.poll, s.interval)
	if s.poll > maxPoll {
		s.poll = maxPoll
	}
	if s.poll < minPoll {
		s.poll = minPoll
	}
	SetInt8(s.template, Poll, s.poll)
}

type byOffset []*peer

func (b byOffset) Len() int {
	return len(b)
}

func (b byOffset) Swap(i, j int) {
	b[i], b[j] = b[j], b[i]
}

func (b byOffset) Less(i, j int) bool {
	return b[i].offset < b[j].offset
}

func (s *Service) workerDo(i int) {
	var (
		n           int
		remoteAddr  *net.UDPAddr
		err         error
		receiveTime time.Time
		limiter     *secondLimitter
	)

	p := make([]byte, 48)
	if s.cfg.ReqRateSec > 0 {
		Info.Printf("limiter %d", s.cfg.ReqRateSec)
		limiter = newLimitter(s.cfg.ReqRateSec)
		go limiter.run()
	}

	defer func(i int) {
		if r := recover(); r != nil {
			Error.Printf("Worker: %d fatal, reason:%s, read:%d", i, r, n)
		} else {
			Info.Printf("Worker: %d exited, reason:%s, read:%d", i, err, n)
		}
	}(i)

	for {
		n, remoteAddr, err = s.conn.ReadFromUDP(p)
		if err != nil {
			return
		}

		receiveTime = time.Now()
		if n < 48 {
			Warn.Printf("worker: %s get small packet %d",
				remoteAddr.String(), n)
			continue
		}

		if limiter != nil {
			if !limiter.allow(remoteAddr.IP, receiveTime) {
				Warn.Printf("worker[%d]: get limitted ip %s",
					i, remoteAddr.String())
				continue
			}
		}

		// GetMode
		switch p[LiVnMode] &^ 0xf8 {
		case ModeSymmetricActive:
			// return
			errBuf := make([]byte, 48)
			copy(errBuf, s.template)
			SetUint8(errBuf, Stratum, 0)
			SetUint32(errBuf, ReferIDPos, 0x41435354)
			s.conn.WriteToUDP(errBuf, remoteAddr)

		case ModeReserved:
			fallthrough
		case ModeClient:
			copy(p[0:OriginTimeStamp], s.template)
			copy(p[OriginTimeStamp:OriginTimeStamp+8],
				p[TransmitTimeStamp:TransmitTimeStamp+8])
			SetUint64(p, ReceiveTimeStamp, toNtpTime(receiveTime))
			SetUint64(p, TransmitTimeStamp, toNtpTime(time.Now()))
			_, err = s.conn.WriteToUDP(p, remoteAddr)
			if err != nil {
				Error.Printf("worker: %s write failed. %s", remoteAddr.String(), err)
				continue
			}
			s.stats.logIP(remoteAddr)
		default:
			Warn.Printf("%s not support client request mode:%x",
				remoteAddr.String(), p[LiVnMode]&^0xf8)
		}
	}
}

func (s *Service) Serve() error {
	if s.cfg.ExpoMetric != "" {
		s.stats = newStatistic(s.cfg)
	}
	ctx := context.TODO()
	for _, p := range s.peerList {
		go p.run(ctx)
	}

	for i := 0; i < s.cfg.WorkerNum; i++ {
		go s.workerDo(i)
	}

	return s.serveSyncLock()
}

func newTemplate() (t []byte) {
	t = make([]byte, 48)
	SetLi(t, 0)
	SetVersion(t, 4)
	SetMode(t, ModeServer)
	SetUint32(t, ReferIDPos, initRefer)
	SetInt8(t, Poll, 4)
	SetUint64(t, ReferenceTimeStamp, toNtpTime(time.Now()))
	return
}
<|endoftext|>