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

package telemetry

import (
	"strconv"
	"time"

	govppapi "git.fd.io/govpp.git/api"
	"github.com/ligato/cn-infra/flavors/local"
	prom "github.com/ligato/cn-infra/rpc/prometheus"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	"github.com/ligato/vpp-agent/plugins/govppmux/vppcalls"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
	// Period between metric updates
	updatePeriod = time.Second * 5

	// Registry path for telemetry metrics
	registryPath = "/vpp"

	// Metrics label used for agent label
	agentLabel = "agent"
)

const (
	// Runtime
	runtimeThreadLabel   = "thread"
	runtimeThreadIDLabel = "threadID"
	runtimeItemLabel     = "item"

	runtimeCallsMetric          = "calls"
	runtimeVectorsMetric        = "vectors"
	runtimeSuspendsMetric       = "suspends"
	runtimeClocksMetric         = "clocks"
	runtimeVectorsPerCallMetric = "vectors_per_call"

	// Memory
	memoryThreadLabel   = "thread"
	memoryThreadIDLabel = "threadID"

	memoryObjectsMetric   = "objects"
	memoryUsedMetric      = "used"
	memoryTotalMetric     = "total"
	memoryFreeMetric      = "free"
	memoryReclaimedMetric = "reclaimed"
	memoryOverheadMetric  = "overhead"
	memoryCapacityMetric  = "capacity"

	// Buffers
	buffersThreadIDLabel = "threadID"
	buffersItemLabel     = "item"
	buffersIndexLabel    = "index"

	buffersSizeMetric     = "size"
	buffersAllocMetric    = "alloc"
	buffersFreeMetric     = "free"
	buffersNumAllocMetric = "num_alloc"
	buffersNumFreeMetric  = "num_free"

	// Node counters
	nodeCounterItemLabel   = "item"
	nodeCounterReasonLabel = "reason"

	nodeCounterCountMetric = "count"
)

// Plugin registers Telemetry Plugin
type Plugin struct {
	Deps

	vppCh govppapi.Channel

	runtimeGaugeVecs map[string]*prometheus.GaugeVec
	runtimeStats     map[string]*runtimeStats

	memoryGaugeVecs map[string]*prometheus.GaugeVec
	memoryStats     map[string]*memoryStats

	buffersGaugeVecs map[string]*prometheus.GaugeVec
	buffersStats     map[string]*buffersStats

	nodeCounterGaugeVecs map[string]*prometheus.GaugeVec
	nodeCounterStats     map[string]*nodeCounterStats
}

// Deps represents dependencies of Telemetry Plugin
type Deps struct {
	local.PluginInfraDeps

	GoVppmux   govppmux.API
	Prometheus prom.API
}

type runtimeStats struct {
	threadName string
	threadID   uint
	itemName   string
	metrics    map[string]prometheus.Gauge
}

type memoryStats struct {
	threadName string
	threadID   uint
	metrics    map[string]prometheus.Gauge
}

type buffersStats struct {
	threadID  uint
	itemName  string
	itemIndex uint
	metrics   map[string]prometheus.Gauge
}

type nodeCounterStats struct {
	itemName string
	metrics  map[string]prometheus.Gauge
}

// Init initializes Telemetry Plugin
func (p *Plugin) Init() error {
	// Register '/vpp' registry path
	err := p.Prometheus.NewRegistry(registryPath, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError})
	if err != nil {
		return err
	}

	// Runtime metrics
	p.runtimeGaugeVecs = make(map[string]*prometheus.GaugeVec)
	p.runtimeStats = make(map[string]*runtimeStats)

	for _, metric := range [][2]string{
		{runtimeCallsMetric, "Number of calls"},
		{runtimeVectorsMetric, "Number of vectors"},
		{runtimeSuspendsMetric, "Number of suspends"},
		{runtimeClocksMetric, "Number of clocks"},
		{runtimeVectorsPerCallMetric, "Number of vectors per call"},
	} {
		name := metric[0]
		p.runtimeGaugeVecs[name] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: "vpp",
			Subsystem: "runtime",
			Name:      name,
			Help:      metric[1],
			ConstLabels: prometheus.Labels{
				agentLabel: p.ServiceLabel.GetAgentLabel(),
			},
		}, []string{runtimeItemLabel, runtimeThreadLabel, runtimeThreadIDLabel})

	}

	// register created vectors to prometheus
	for name, metric := range p.runtimeGaugeVecs {
		if err := p.Prometheus.Register(registryPath, metric); err != nil {
			p.Log.Errorf("failed to register %v metric: %v", name, err)
			return err
		}
	}

	// Memory metrics
	p.memoryGaugeVecs = make(map[string]*prometheus.GaugeVec)
	p.memoryStats = make(map[string]*memoryStats)

	for _, metric := range [][2]string{
		{memoryObjectsMetric, "Number of objects"},
		{memoryUsedMetric, "Used memory"},
		{memoryTotalMetric, "Total memory"},
		{memoryFreeMetric, "Free memory"},
		{memoryReclaimedMetric, "Reclaimed memory"},
		{memoryOverheadMetric, "Overhead"},
		{memoryCapacityMetric, "Capacity"},
	} {
		name := metric[0]
		p.memoryGaugeVecs[name] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: "vpp",
			Subsystem: "memory",
			Name:      name,
			Help:      metric[1],
			ConstLabels: prometheus.Labels{
				agentLabel: p.ServiceLabel.GetAgentLabel(),
			},
		}, []string{memoryThreadLabel, memoryThreadIDLabel})

	}

	// register created vectors to prometheus
	for name, metric := range p.memoryGaugeVecs {
		if err := p.Prometheus.Register(registryPath, metric); err != nil {
			p.Log.Errorf("failed to register %v metric: %v", name, err)
			return err
		}
	}

	// Buffers metrics
	p.buffersGaugeVecs = make(map[string]*prometheus.GaugeVec)
	p.buffersStats = make(map[string]*buffersStats)

	for _, metric := range [][2]string{
		{buffersSizeMetric, "Size of buffer"},
		{buffersAllocMetric, "Allocated"},
		{buffersFreeMetric, "Free"},
		{buffersNumAllocMetric, "Number of allocated"},
		{buffersNumFreeMetric, "Number of free"},
	} {
		name := metric[0]
		p.buffersGaugeVecs[name] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: "vpp",
			Subsystem: "buffers",
			Name:      name,
			Help:      metric[1],
			ConstLabels: prometheus.Labels{
				agentLabel: p.ServiceLabel.GetAgentLabel(),
			},
		}, []string{buffersThreadIDLabel, buffersItemLabel, buffersIndexLabel})

	}

	// register created vectors to prometheus
	for name, metric := range p.buffersGaugeVecs {
		if err := p.Prometheus.Register(registryPath, metric); err != nil {
			p.Log.Errorf("failed to register %v metric: %v", name, err)
			return err
		}
	}

	// Node counters metrics
	p.nodeCounterGaugeVecs = make(map[string]*prometheus.GaugeVec)
	p.nodeCounterStats = make(map[string]*nodeCounterStats)

	for _, metric := range [][2]string{
		{nodeCounterCountMetric, "Count"},
	} {
		name := metric[0]
		p.nodeCounterGaugeVecs[name] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: "vpp",
			Subsystem: "node_counter",
			Name:      name,
			Help:      metric[1],
			ConstLabels: prometheus.Labels{
				agentLabel: p.ServiceLabel.GetAgentLabel(),
			},
		}, []string{nodeCounterItemLabel, nodeCounterReasonLabel})

	}

	// register created vectors to prometheus
	for name, metric := range p.nodeCounterGaugeVecs {
		if err := p.Prometheus.Register(registryPath, metric); err != nil {
			p.Log.Errorf("failed to register %v metric: %v", name, err)
			return err
		}
	}

	// Create GoVPP channel
	p.vppCh, err = p.GoVppmux.NewAPIChannel()
	if err != nil {
		p.Log.Errorf("Error creating channel: %v", err)
		return err
	}

	return nil
}

// AfterInit executes after initializion of Telemetry Plugin
func (p *Plugin) AfterInit() error {
	// Periodically update data
	go func() {
		for {
			// Update runtime
			runtimeInfo, err := vppcalls.GetRuntimeInfo(p.vppCh)
			if err != nil {
				p.Log.Errorf("Command failed: %v", err)
			} else {
				for _, thread := range runtimeInfo.Threads {
					for _, item := range thread.Items {
						stats, ok := p.runtimeStats[item.Name]
						if !ok {
							stats = &runtimeStats{
								threadID:   thread.ID,
								threadName: thread.Name,
								itemName:   item.Name,
								metrics:    map[string]prometheus.Gauge{},
							}

							// add gauges with corresponding labels into vectors
							for k, vec := range p.runtimeGaugeVecs {
								stats.metrics[k], err = vec.GetMetricWith(prometheus.Labels{
									runtimeItemLabel:     item.Name,
									runtimeThreadLabel:   thread.Name,
									runtimeThreadIDLabel: strconv.Itoa(int(thread.ID)),
								})
								if err != nil {
									p.Log.Error(err)
								}
							}
						}

						stats.metrics[runtimeCallsMetric].Set(float64(item.Calls))
						stats.metrics[runtimeVectorsMetric].Set(float64(item.Vectors))
						stats.metrics[runtimeSuspendsMetric].Set(float64(item.Suspends))
						stats.metrics[runtimeClocksMetric].Set(item.Clocks)
						stats.metrics[runtimeVectorsPerCallMetric].Set(item.VectorsPerCall)
					}
				}
			}

			// Update memory
			memoryInfo, err := vppcalls.GetMemory(p.vppCh)
			if err != nil {
				p.Log.Errorf("Command failed: %v", err)
			} else {
				for _, thread := range memoryInfo.Threads {
					stats, ok := p.memoryStats[thread.Name]
					if !ok {
						stats = &memoryStats{
							threadName: thread.Name,
							threadID:   thread.ID,
							metrics:    map[string]prometheus.Gauge{},
						}

						// add gauges with corresponding labels into vectors
						for k, vec := range p.memoryGaugeVecs {
							stats.metrics[k], err = vec.GetMetricWith(prometheus.Labels{
								memoryThreadLabel:   thread.Name,
								memoryThreadIDLabel: strconv.Itoa(int(thread.ID)),
							})
							if err != nil {
								p.Log.Error(err)
							}
						}
					}

					stats.metrics[memoryObjectsMetric].Set(float64(thread.Objects))
					stats.metrics[memoryUsedMetric].Set(float64(thread.Used))
					stats.metrics[memoryTotalMetric].Set(float64(thread.Total))
					stats.metrics[memoryFreeMetric].Set(float64(thread.Free))
					stats.metrics[memoryReclaimedMetric].Set(float64(thread.Reclaimed))
					stats.metrics[memoryOverheadMetric].Set(float64(thread.Overhead))
					stats.metrics[memoryCapacityMetric].Set(float64(thread.Capacity))
				}
			}

			// Update buffers
			buffersInfo, err := vppcalls.GetBuffersInfo(p.vppCh)
			if err != nil {
				p.Log.Errorf("Command failed: %v", err)
			} else {
				for _, item := range buffersInfo.Items {
					stats, ok := p.buffersStats[item.Name]
					if !ok {
						stats = &buffersStats{
							threadID:  item.ThreadID,
							itemName:  item.Name,
							itemIndex: item.Index,
							metrics:   map[string]prometheus.Gauge{},
						}

						// add gauges with corresponding labels into vectors
						for k, vec := range p.buffersGaugeVecs {
							stats.metrics[k], err = vec.GetMetricWith(prometheus.Labels{
								buffersThreadIDLabel: strconv.Itoa(int(item.ThreadID)),
								buffersItemLabel:     item.Name,
								buffersIndexLabel:    strconv.Itoa(int(item.Index)),
							})
							if err != nil {
								p.Log.Error(err)
							}
						}
					}

					stats.metrics[buffersSizeMetric].Set(float64(item.Size))
					stats.metrics[buffersAllocMetric].Set(float64(item.Alloc))
					stats.metrics[buffersFreeMetric].Set(float64(item.Free))
					stats.metrics[buffersNumAllocMetric].Set(float64(item.NumAlloc))
					stats.metrics[buffersNumFreeMetric].Set(float64(item.NumFree))
				}
			}

			// Update node counters
			nodeCountersInfo, err := vppcalls.GetNodeCounters(p.vppCh)
			if err != nil {
				p.Log.Errorf("Command failed: %v", err)
			} else {
				for _, item := range nodeCountersInfo.Counters {
					stats, ok := p.nodeCounterStats[item.Node]
					if !ok {
						stats = &nodeCounterStats{
							itemName: item.Node,
							metrics:  map[string]prometheus.Gauge{},
						}

						// add gauges with corresponding labels into vectors
						for k, vec := range p.nodeCounterGaugeVecs {
							stats.metrics[k], err = vec.GetMetricWith(prometheus.Labels{
								nodeCounterItemLabel:   item.Node,
								nodeCounterReasonLabel: item.Reason,
							})
							if err != nil {
								p.Log.Error(err)
							}
						}
					}

					stats.metrics[nodeCounterCountMetric].Set(float64(item.Count))
				}
			}

			// Delay period between updates
			time.Sleep(updatePeriod)
		}
	}()
	return nil
}

// Close is used to clean up resources used by Telemetry Plugin
func (p *Plugin) Close() error {
	p.vppCh.Close()
	return nil
}
<commit_msg>Use safeclose for closing VPP channel in Telemetry plugin<commit_after>//  Copyright (c) 2018 Cisco and/or its affiliates.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at:
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package telemetry

import (
	"strconv"
	"time"

	govppapi "git.fd.io/govpp.git/api"
	"github.com/ligato/cn-infra/flavors/local"
	prom "github.com/ligato/cn-infra/rpc/prometheus"
	"github.com/ligato/cn-infra/utils/safeclose"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	"github.com/ligato/vpp-agent/plugins/govppmux/vppcalls"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const (
	// Period between metric updates
	updatePeriod = time.Second * 5

	// Registry path for telemetry metrics
	registryPath = "/vpp"

	// Metrics label used for agent label
	agentLabel = "agent"
)

const (
	// Runtime
	runtimeThreadLabel   = "thread"
	runtimeThreadIDLabel = "threadID"
	runtimeItemLabel     = "item"

	runtimeCallsMetric          = "calls"
	runtimeVectorsMetric        = "vectors"
	runtimeSuspendsMetric       = "suspends"
	runtimeClocksMetric         = "clocks"
	runtimeVectorsPerCallMetric = "vectors_per_call"

	// Memory
	memoryThreadLabel   = "thread"
	memoryThreadIDLabel = "threadID"

	memoryObjectsMetric   = "objects"
	memoryUsedMetric      = "used"
	memoryTotalMetric     = "total"
	memoryFreeMetric      = "free"
	memoryReclaimedMetric = "reclaimed"
	memoryOverheadMetric  = "overhead"
	memoryCapacityMetric  = "capacity"

	// Buffers
	buffersThreadIDLabel = "threadID"
	buffersItemLabel     = "item"
	buffersIndexLabel    = "index"

	buffersSizeMetric     = "size"
	buffersAllocMetric    = "alloc"
	buffersFreeMetric     = "free"
	buffersNumAllocMetric = "num_alloc"
	buffersNumFreeMetric  = "num_free"

	// Node counters
	nodeCounterItemLabel   = "item"
	nodeCounterReasonLabel = "reason"

	nodeCounterCountMetric = "count"
)

// Plugin registers Telemetry Plugin
type Plugin struct {
	Deps

	vppCh govppapi.Channel

	runtimeGaugeVecs map[string]*prometheus.GaugeVec
	runtimeStats     map[string]*runtimeStats

	memoryGaugeVecs map[string]*prometheus.GaugeVec
	memoryStats     map[string]*memoryStats

	buffersGaugeVecs map[string]*prometheus.GaugeVec
	buffersStats     map[string]*buffersStats

	nodeCounterGaugeVecs map[string]*prometheus.GaugeVec
	nodeCounterStats     map[string]*nodeCounterStats
}

// Deps represents dependencies of Telemetry Plugin
type Deps struct {
	local.PluginInfraDeps

	GoVppmux   govppmux.API
	Prometheus prom.API
}

type runtimeStats struct {
	threadName string
	threadID   uint
	itemName   string
	metrics    map[string]prometheus.Gauge
}

type memoryStats struct {
	threadName string
	threadID   uint
	metrics    map[string]prometheus.Gauge
}

type buffersStats struct {
	threadID  uint
	itemName  string
	itemIndex uint
	metrics   map[string]prometheus.Gauge
}

type nodeCounterStats struct {
	itemName string
	metrics  map[string]prometheus.Gauge
}

// Init initializes Telemetry Plugin
func (p *Plugin) Init() error {
	// Register '/vpp' registry path
	err := p.Prometheus.NewRegistry(registryPath, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError})
	if err != nil {
		return err
	}

	// Runtime metrics
	p.runtimeGaugeVecs = make(map[string]*prometheus.GaugeVec)
	p.runtimeStats = make(map[string]*runtimeStats)

	for _, metric := range [][2]string{
		{runtimeCallsMetric, "Number of calls"},
		{runtimeVectorsMetric, "Number of vectors"},
		{runtimeSuspendsMetric, "Number of suspends"},
		{runtimeClocksMetric, "Number of clocks"},
		{runtimeVectorsPerCallMetric, "Number of vectors per call"},
	} {
		name := metric[0]
		p.runtimeGaugeVecs[name] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: "vpp",
			Subsystem: "runtime",
			Name:      name,
			Help:      metric[1],
			ConstLabels: prometheus.Labels{
				agentLabel: p.ServiceLabel.GetAgentLabel(),
			},
		}, []string{runtimeItemLabel, runtimeThreadLabel, runtimeThreadIDLabel})

	}

	// register created vectors to prometheus
	for name, metric := range p.runtimeGaugeVecs {
		if err := p.Prometheus.Register(registryPath, metric); err != nil {
			p.Log.Errorf("failed to register %v metric: %v", name, err)
			return err
		}
	}

	// Memory metrics
	p.memoryGaugeVecs = make(map[string]*prometheus.GaugeVec)
	p.memoryStats = make(map[string]*memoryStats)

	for _, metric := range [][2]string{
		{memoryObjectsMetric, "Number of objects"},
		{memoryUsedMetric, "Used memory"},
		{memoryTotalMetric, "Total memory"},
		{memoryFreeMetric, "Free memory"},
		{memoryReclaimedMetric, "Reclaimed memory"},
		{memoryOverheadMetric, "Overhead"},
		{memoryCapacityMetric, "Capacity"},
	} {
		name := metric[0]
		p.memoryGaugeVecs[name] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: "vpp",
			Subsystem: "memory",
			Name:      name,
			Help:      metric[1],
			ConstLabels: prometheus.Labels{
				agentLabel: p.ServiceLabel.GetAgentLabel(),
			},
		}, []string{memoryThreadLabel, memoryThreadIDLabel})

	}

	// register created vectors to prometheus
	for name, metric := range p.memoryGaugeVecs {
		if err := p.Prometheus.Register(registryPath, metric); err != nil {
			p.Log.Errorf("failed to register %v metric: %v", name, err)
			return err
		}
	}

	// Buffers metrics
	p.buffersGaugeVecs = make(map[string]*prometheus.GaugeVec)
	p.buffersStats = make(map[string]*buffersStats)

	for _, metric := range [][2]string{
		{buffersSizeMetric, "Size of buffer"},
		{buffersAllocMetric, "Allocated"},
		{buffersFreeMetric, "Free"},
		{buffersNumAllocMetric, "Number of allocated"},
		{buffersNumFreeMetric, "Number of free"},
	} {
		name := metric[0]
		p.buffersGaugeVecs[name] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: "vpp",
			Subsystem: "buffers",
			Name:      name,
			Help:      metric[1],
			ConstLabels: prometheus.Labels{
				agentLabel: p.ServiceLabel.GetAgentLabel(),
			},
		}, []string{buffersThreadIDLabel, buffersItemLabel, buffersIndexLabel})

	}

	// register created vectors to prometheus
	for name, metric := range p.buffersGaugeVecs {
		if err := p.Prometheus.Register(registryPath, metric); err != nil {
			p.Log.Errorf("failed to register %v metric: %v", name, err)
			return err
		}
	}

	// Node counters metrics
	p.nodeCounterGaugeVecs = make(map[string]*prometheus.GaugeVec)
	p.nodeCounterStats = make(map[string]*nodeCounterStats)

	for _, metric := range [][2]string{
		{nodeCounterCountMetric, "Count"},
	} {
		name := metric[0]
		p.nodeCounterGaugeVecs[name] = prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Namespace: "vpp",
			Subsystem: "node_counter",
			Name:      name,
			Help:      metric[1],
			ConstLabels: prometheus.Labels{
				agentLabel: p.ServiceLabel.GetAgentLabel(),
			},
		}, []string{nodeCounterItemLabel, nodeCounterReasonLabel})

	}

	// register created vectors to prometheus
	for name, metric := range p.nodeCounterGaugeVecs {
		if err := p.Prometheus.Register(registryPath, metric); err != nil {
			p.Log.Errorf("failed to register %v metric: %v", name, err)
			return err
		}
	}

	// Create GoVPP channel
	p.vppCh, err = p.GoVppmux.NewAPIChannel()
	if err != nil {
		p.Log.Errorf("Error creating channel: %v", err)
		return err
	}

	return nil
}

// AfterInit executes after initializion of Telemetry Plugin
func (p *Plugin) AfterInit() error {
	// Periodically update data
	go func() {
		for {
			// Update runtime
			runtimeInfo, err := vppcalls.GetRuntimeInfo(p.vppCh)
			if err != nil {
				p.Log.Errorf("Command failed: %v", err)
			} else {
				for _, thread := range runtimeInfo.Threads {
					for _, item := range thread.Items {
						stats, ok := p.runtimeStats[item.Name]
						if !ok {
							stats = &runtimeStats{
								threadID:   thread.ID,
								threadName: thread.Name,
								itemName:   item.Name,
								metrics:    map[string]prometheus.Gauge{},
							}

							// add gauges with corresponding labels into vectors
							for k, vec := range p.runtimeGaugeVecs {
								stats.metrics[k], err = vec.GetMetricWith(prometheus.Labels{
									runtimeItemLabel:     item.Name,
									runtimeThreadLabel:   thread.Name,
									runtimeThreadIDLabel: strconv.Itoa(int(thread.ID)),
								})
								if err != nil {
									p.Log.Error(err)
								}
							}
						}

						stats.metrics[runtimeCallsMetric].Set(float64(item.Calls))
						stats.metrics[runtimeVectorsMetric].Set(float64(item.Vectors))
						stats.metrics[runtimeSuspendsMetric].Set(float64(item.Suspends))
						stats.metrics[runtimeClocksMetric].Set(item.Clocks)
						stats.metrics[runtimeVectorsPerCallMetric].Set(item.VectorsPerCall)
					}
				}
			}

			// Update memory
			memoryInfo, err := vppcalls.GetMemory(p.vppCh)
			if err != nil {
				p.Log.Errorf("Command failed: %v", err)
			} else {
				for _, thread := range memoryInfo.Threads {
					stats, ok := p.memoryStats[thread.Name]
					if !ok {
						stats = &memoryStats{
							threadName: thread.Name,
							threadID:   thread.ID,
							metrics:    map[string]prometheus.Gauge{},
						}

						// add gauges with corresponding labels into vectors
						for k, vec := range p.memoryGaugeVecs {
							stats.metrics[k], err = vec.GetMetricWith(prometheus.Labels{
								memoryThreadLabel:   thread.Name,
								memoryThreadIDLabel: strconv.Itoa(int(thread.ID)),
							})
							if err != nil {
								p.Log.Error(err)
							}
						}
					}

					stats.metrics[memoryObjectsMetric].Set(float64(thread.Objects))
					stats.metrics[memoryUsedMetric].Set(float64(thread.Used))
					stats.metrics[memoryTotalMetric].Set(float64(thread.Total))
					stats.metrics[memoryFreeMetric].Set(float64(thread.Free))
					stats.metrics[memoryReclaimedMetric].Set(float64(thread.Reclaimed))
					stats.metrics[memoryOverheadMetric].Set(float64(thread.Overhead))
					stats.metrics[memoryCapacityMetric].Set(float64(thread.Capacity))
				}
			}

			// Update buffers
			buffersInfo, err := vppcalls.GetBuffersInfo(p.vppCh)
			if err != nil {
				p.Log.Errorf("Command failed: %v", err)
			} else {
				for _, item := range buffersInfo.Items {
					stats, ok := p.buffersStats[item.Name]
					if !ok {
						stats = &buffersStats{
							threadID:  item.ThreadID,
							itemName:  item.Name,
							itemIndex: item.Index,
							metrics:   map[string]prometheus.Gauge{},
						}

						// add gauges with corresponding labels into vectors
						for k, vec := range p.buffersGaugeVecs {
							stats.metrics[k], err = vec.GetMetricWith(prometheus.Labels{
								buffersThreadIDLabel: strconv.Itoa(int(item.ThreadID)),
								buffersItemLabel:     item.Name,
								buffersIndexLabel:    strconv.Itoa(int(item.Index)),
							})
							if err != nil {
								p.Log.Error(err)
							}
						}
					}

					stats.metrics[buffersSizeMetric].Set(float64(item.Size))
					stats.metrics[buffersAllocMetric].Set(float64(item.Alloc))
					stats.metrics[buffersFreeMetric].Set(float64(item.Free))
					stats.metrics[buffersNumAllocMetric].Set(float64(item.NumAlloc))
					stats.metrics[buffersNumFreeMetric].Set(float64(item.NumFree))
				}
			}

			// Update node counters
			nodeCountersInfo, err := vppcalls.GetNodeCounters(p.vppCh)
			if err != nil {
				p.Log.Errorf("Command failed: %v", err)
			} else {
				for _, item := range nodeCountersInfo.Counters {
					stats, ok := p.nodeCounterStats[item.Node]
					if !ok {
						stats = &nodeCounterStats{
							itemName: item.Node,
							metrics:  map[string]prometheus.Gauge{},
						}

						// add gauges with corresponding labels into vectors
						for k, vec := range p.nodeCounterGaugeVecs {
							stats.metrics[k], err = vec.GetMetricWith(prometheus.Labels{
								nodeCounterItemLabel:   item.Node,
								nodeCounterReasonLabel: item.Reason,
							})
							if err != nil {
								p.Log.Error(err)
							}
						}
					}

					stats.metrics[nodeCounterCountMetric].Set(float64(item.Count))
				}
			}

			// Delay period between updates
			time.Sleep(updatePeriod)
		}
	}()
	return nil
}

// Close is used to clean up resources used by Telemetry Plugin
func (p *Plugin) Close() error {
	return safeclose.Close(p.vppCh)
}
<|endoftext|>


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

package gen

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"hash"
	"hash/fnv"
	"io"
	"log"
	"os"
	"reflect"
	"strings"
	"unicode"
	"unicode/utf8"
)

// This file contains utilities for generating code.

// TODO: other write methods like:
// - slices, maps, types, etc.

// CodeWriter is a utility for writing structured code. It computes the content
// hash and size of written content. It ensures there are newlines between
// written code blocks.
type CodeWriter struct {
	buf  bytes.Buffer
	Size int
	Hash hash.Hash32 // content hash
	gob  *gob.Encoder
	// For comments we skip the usual one-line separator if they are followed by
	// a code block.
	skipSep bool
}

func (w *CodeWriter) Write(p []byte) (n int, err error) {
	return w.buf.Write(p)
}

// NewCodeWriter returns a new CodeWriter.
func NewCodeWriter() *CodeWriter {
	h := fnv.New32()
	return &CodeWriter{Hash: h, gob: gob.NewEncoder(h)}
}

// WriteGoFile appends the buffer with the total size of all created structures
// and writes it as a Go file to the the given file with the given package name.
func (w *CodeWriter) WriteGoFile(filename, pkg string) {
	f, err := os.Create(filename)
	if err != nil {
		log.Fatalf("Could not create file %s: %v", filename, err)
	}
	defer f.Close()
	if _, err = w.WriteGo(f, pkg); err != nil {
		log.Fatalf("Error writing file %s: %v", filename, err)
	}
}

// WriteGo appends the buffer with the total size of all created structures and
// writes it as a Go file to the the given writer with the given package name.
func (w *CodeWriter) WriteGo(out io.Writer, pkg string) (n int, err error) {
	sz := w.Size
	w.WriteComment("Total table size %d bytes (%dKiB); checksum: %X\n", sz, sz/1024, w.Hash.Sum32())
	defer w.buf.Reset()
	return WriteGo(out, pkg, w.buf.Bytes())
}

func (w *CodeWriter) printf(f string, x ...interface{}) {
	fmt.Fprintf(w, f, x...)
}

func (w *CodeWriter) insertSep() {
	if w.skipSep {
		w.skipSep = false
		return
	}
	// Use at least two newlines to ensure a blank space between the previous
	// block. WriteGoFile will remove extraneous newlines.
	w.printf("\n\n")
}

// WriteComment writes a comment block. All line starts are prefixed with "//".
// Initial empty lines are gobbled. The indentation for the first line is
// stripped from consecutive lines.
func (w *CodeWriter) WriteComment(comment string, args ...interface{}) {
	s := fmt.Sprintf(comment, args...)
	s = strings.Trim(s, "\n")

	// Use at least two newlines to ensure a blank space between the previous
	// block. WriteGoFile will remove extraneous newlines.
	w.printf("\n\n// ")
	w.skipSep = true

	// strip first indent level.
	sep := "\n"
	for ; len(s) > 0 && (s[0] == '\t' || s[0] == ' '); s = s[1:] {
		sep += s[:1]
	}

	strings.NewReplacer(sep, "\n// ", "\n", "\n// ").WriteString(w, s)

	w.printf("\n")
}

func (w *CodeWriter) writeSizeInfo(size int) {
	w.printf("// Size: %d bytes\n", size)
}

// WriteConst writes a constant of the given name and value.
func (w *CodeWriter) WriteConst(name string, x interface{}) {
	w.insertSep()
	v := reflect.ValueOf(x)

	switch v.Type().Kind() {
	case reflect.String:
		// See golang.org/issue/13145.
		const arbitraryCutoff = 16
		if v.Len() > arbitraryCutoff {
			w.printf("var %s %s = ", name, typeName(x))
		} else {
			w.printf("const %s %s = ", name, typeName(x))
		}
		w.WriteString(v.String())
		w.printf("\n")
	default:
		w.printf("const %s = %#v\n", name, x)
	}
}

// WriteVar writes a variable of the given name and value.
func (w *CodeWriter) WriteVar(name string, x interface{}) {
	w.insertSep()
	v := reflect.ValueOf(x)
	oldSize := w.Size
	sz := int(v.Type().Size())
	w.Size += sz

	switch v.Type().Kind() {
	case reflect.String:
		w.printf("var %s %s = ", name, typeName(x))
		w.WriteString(v.String())
	case reflect.Struct:
		w.gob.Encode(x)
		fallthrough
	case reflect.Slice, reflect.Array:
		w.printf("var %s = ", name)
		w.writeValue(v)
		w.writeSizeInfo(w.Size - oldSize)
	default:
		w.printf("var %s %s = ", name, typeName(x))
		w.gob.Encode(x)
		w.writeValue(v)
		w.writeSizeInfo(w.Size - oldSize)
	}
	w.printf("\n")
}

func (w *CodeWriter) writeValue(v reflect.Value) {
	x := v.Interface()
	switch v.Kind() {
	case reflect.String:
		w.WriteString(v.String())
	case reflect.Array:
		// Don't double count: callers of WriteArray count on the size being
		// added, so we need to discount it here.
		w.Size -= int(v.Type().Size())
		w.writeSlice(x, true)
	case reflect.Slice:
		w.writeSlice(x, false)
	case reflect.Struct:
		w.printf("%s{\n", typeName(v.Interface()))
		t := v.Type()
		for i := 0; i < v.NumField(); i++ {
			w.printf("%s: ", t.Field(i).Name)
			w.writeValue(v.Field(i))
			w.printf(",\n")
		}
		w.printf("}")
	default:
		w.printf("%#v", x)
	}
}

// WriteString writes a string literal.
func (w *CodeWriter) WriteString(s string) {
	io.WriteString(w.Hash, s) // content hash
	w.Size += len(s)

	const maxInline = 40
	if len(s) <= maxInline {
		w.printf("%q", s)
		return
	}

	// We will render the string as a multi-line string.
	const maxWidth = 80 - 4 - len(`"`) - len(`" +`)

	// When starting on its own line, go fmt indents line 2+ an extra level.
	n, max := maxWidth, maxWidth-4

	// Print "" +\n, if a string does not start on its own line.
	b := w.buf.Bytes()
	if p := len(bytes.TrimRight(b, " \t")); p > 0 && b[p-1] != '\n' {
		w.printf("\"\" + // Size: %d bytes\n", len(s))
		n, max = maxWidth, maxWidth
	}

	w.printf(`"`)

	for sz, p := 0, 0; p < len(s); {
		var r rune
		r, sz = utf8.DecodeRuneInString(s[p:])
		out := s[p : p+sz]
		chars := 1
		if !unicode.IsPrint(r) || r == utf8.RuneError || r == '"' {
			switch sz {
			case 1:
				out = fmt.Sprintf("\\x%02x", s[p])
			case 2, 3:
				out = fmt.Sprintf("\\u%04x", r)
			case 4:
				out = fmt.Sprintf("\\U%08x", r)
			}
			chars = len(out)
		}
		if n -= chars; n < 0 {
			w.printf("\" +\n\"")
			n = max - len(out)
		}
		w.printf("%s", out)
		p += sz
	}
	w.printf(`"`)
}

// WriteSlice writes a slice value.
func (w *CodeWriter) WriteSlice(x interface{}) {
	w.writeSlice(x, false)
}

// WriteArray writes an array value.
func (w *CodeWriter) WriteArray(x interface{}) {
	w.writeSlice(x, true)
}

func (w *CodeWriter) writeSlice(x interface{}, isArray bool) {
	v := reflect.ValueOf(x)
	w.gob.Encode(v.Len())
	w.Size += v.Len() * int(v.Type().Elem().Size())
	name := typeName(x)
	if isArray {
		name = fmt.Sprintf("[%d]%s", v.Len(), name[strings.Index(name, "]")+1:])
	}
	if isArray {
		w.printf("%s{\n", name)
	} else {
		w.printf("%s{ // %d elements\n", name, v.Len())
	}

	switch kind := v.Type().Elem().Kind(); kind {
	case reflect.String:
		for _, s := range x.([]string) {
			w.WriteString(s)
			w.printf(",\n")
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		// nLine and nBlock are the number of elements per line and block.
		nLine, nBlock, format := 8, 64, "%d,"
		switch kind {
		case reflect.Uint8:
			format = "%#02x,"
		case reflect.Uint16:
			format = "%#04x,"
		case reflect.Uint32:
			nLine, nBlock, format = 4, 32, "%#08x,"
		case reflect.Uint, reflect.Uint64:
			nLine, nBlock, format = 4, 32, "%#016x,"
		case reflect.Int8:
			nLine = 16
		}
		n := nLine
		for i := 0; i < v.Len(); i++ {
			if i%nBlock == 0 && v.Len() > nBlock {
				w.printf("// Entry %X - %X\n", i, i+nBlock-1)
			}
			x := v.Index(i).Interface()
			w.gob.Encode(x)
			w.printf(format, x)
			if n--; n == 0 {
				n = nLine
				w.printf("\n")
			}
		}
		w.printf("\n")
	case reflect.Struct:
		zero := reflect.Zero(v.Type().Elem()).Interface()
		for i := 0; i < v.Len(); i++ {
			x := v.Index(i).Interface()
			w.gob.EncodeValue(v)
			if !reflect.DeepEqual(zero, x) {
				line := fmt.Sprintf("%#v,\n", x)
				line = line[strings.IndexByte(line, '{'):]
				w.printf("%d: ", i)
				w.printf(line)
			}
		}
	case reflect.Array:
		for i := 0; i < v.Len(); i++ {
			w.printf("%d: %#v,\n", i, v.Index(i).Interface())
		}
	default:
		panic("gen: slice elem type not supported")
	}
	w.printf("}")
}

// WriteType writes a definition of the type of the given value and returns the
// type name.
func (w *CodeWriter) WriteType(x interface{}) string {
	t := reflect.TypeOf(x)
	w.printf("type %s struct {\n", t.Name())
	for i := 0; i < t.NumField(); i++ {
		w.printf("\t%s %s\n", t.Field(i).Name, t.Field(i).Type)
	}
	w.printf("}\n")
	return t.Name()
}

// typeName returns the name of the go type of x.
func typeName(x interface{}) string {
	t := reflect.ValueOf(x).Type()
	return strings.Replace(fmt.Sprint(t), "main.", "", 1)
}
<commit_msg>internal/gen: bugfix: escape the escape character<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gen

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"hash"
	"hash/fnv"
	"io"
	"log"
	"os"
	"reflect"
	"strings"
	"unicode"
	"unicode/utf8"
)

// This file contains utilities for generating code.

// TODO: other write methods like:
// - slices, maps, types, etc.

// CodeWriter is a utility for writing structured code. It computes the content
// hash and size of written content. It ensures there are newlines between
// written code blocks.
type CodeWriter struct {
	buf  bytes.Buffer
	Size int
	Hash hash.Hash32 // content hash
	gob  *gob.Encoder
	// For comments we skip the usual one-line separator if they are followed by
	// a code block.
	skipSep bool
}

func (w *CodeWriter) Write(p []byte) (n int, err error) {
	return w.buf.Write(p)
}

// NewCodeWriter returns a new CodeWriter.
func NewCodeWriter() *CodeWriter {
	h := fnv.New32()
	return &CodeWriter{Hash: h, gob: gob.NewEncoder(h)}
}

// WriteGoFile appends the buffer with the total size of all created structures
// and writes it as a Go file to the the given file with the given package name.
func (w *CodeWriter) WriteGoFile(filename, pkg string) {
	f, err := os.Create(filename)
	if err != nil {
		log.Fatalf("Could not create file %s: %v", filename, err)
	}
	defer f.Close()
	if _, err = w.WriteGo(f, pkg); err != nil {
		log.Fatalf("Error writing file %s: %v", filename, err)
	}
}

// WriteGo appends the buffer with the total size of all created structures and
// writes it as a Go file to the the given writer with the given package name.
func (w *CodeWriter) WriteGo(out io.Writer, pkg string) (n int, err error) {
	sz := w.Size
	w.WriteComment("Total table size %d bytes (%dKiB); checksum: %X\n", sz, sz/1024, w.Hash.Sum32())
	defer w.buf.Reset()
	return WriteGo(out, pkg, w.buf.Bytes())
}

func (w *CodeWriter) printf(f string, x ...interface{}) {
	fmt.Fprintf(w, f, x...)
}

func (w *CodeWriter) insertSep() {
	if w.skipSep {
		w.skipSep = false
		return
	}
	// Use at least two newlines to ensure a blank space between the previous
	// block. WriteGoFile will remove extraneous newlines.
	w.printf("\n\n")
}

// WriteComment writes a comment block. All line starts are prefixed with "//".
// Initial empty lines are gobbled. The indentation for the first line is
// stripped from consecutive lines.
func (w *CodeWriter) WriteComment(comment string, args ...interface{}) {
	s := fmt.Sprintf(comment, args...)
	s = strings.Trim(s, "\n")

	// Use at least two newlines to ensure a blank space between the previous
	// block. WriteGoFile will remove extraneous newlines.
	w.printf("\n\n// ")
	w.skipSep = true

	// strip first indent level.
	sep := "\n"
	for ; len(s) > 0 && (s[0] == '\t' || s[0] == ' '); s = s[1:] {
		sep += s[:1]
	}

	strings.NewReplacer(sep, "\n// ", "\n", "\n// ").WriteString(w, s)

	w.printf("\n")
}

func (w *CodeWriter) writeSizeInfo(size int) {
	w.printf("// Size: %d bytes\n", size)
}

// WriteConst writes a constant of the given name and value.
func (w *CodeWriter) WriteConst(name string, x interface{}) {
	w.insertSep()
	v := reflect.ValueOf(x)

	switch v.Type().Kind() {
	case reflect.String:
		// See golang.org/issue/13145.
		const arbitraryCutoff = 16
		if v.Len() > arbitraryCutoff {
			w.printf("var %s %s = ", name, typeName(x))
		} else {
			w.printf("const %s %s = ", name, typeName(x))
		}
		w.WriteString(v.String())
		w.printf("\n")
	default:
		w.printf("const %s = %#v\n", name, x)
	}
}

// WriteVar writes a variable of the given name and value.
func (w *CodeWriter) WriteVar(name string, x interface{}) {
	w.insertSep()
	v := reflect.ValueOf(x)
	oldSize := w.Size
	sz := int(v.Type().Size())
	w.Size += sz

	switch v.Type().Kind() {
	case reflect.String:
		w.printf("var %s %s = ", name, typeName(x))
		w.WriteString(v.String())
	case reflect.Struct:
		w.gob.Encode(x)
		fallthrough
	case reflect.Slice, reflect.Array:
		w.printf("var %s = ", name)
		w.writeValue(v)
		w.writeSizeInfo(w.Size - oldSize)
	default:
		w.printf("var %s %s = ", name, typeName(x))
		w.gob.Encode(x)
		w.writeValue(v)
		w.writeSizeInfo(w.Size - oldSize)
	}
	w.printf("\n")
}

func (w *CodeWriter) writeValue(v reflect.Value) {
	x := v.Interface()
	switch v.Kind() {
	case reflect.String:
		w.WriteString(v.String())
	case reflect.Array:
		// Don't double count: callers of WriteArray count on the size being
		// added, so we need to discount it here.
		w.Size -= int(v.Type().Size())
		w.writeSlice(x, true)
	case reflect.Slice:
		w.writeSlice(x, false)
	case reflect.Struct:
		w.printf("%s{\n", typeName(v.Interface()))
		t := v.Type()
		for i := 0; i < v.NumField(); i++ {
			w.printf("%s: ", t.Field(i).Name)
			w.writeValue(v.Field(i))
			w.printf(",\n")
		}
		w.printf("}")
	default:
		w.printf("%#v", x)
	}
}

// WriteString writes a string literal.
func (w *CodeWriter) WriteString(s string) {
	s = strings.Replace(s, `\`, `\\`, -1)
	io.WriteString(w.Hash, s) // content hash
	w.Size += len(s)

	const maxInline = 40
	if len(s) <= maxInline {
		w.printf("%q", s)
		return
	}

	// We will render the string as a multi-line string.
	const maxWidth = 80 - 4 - len(`"`) - len(`" +`)

	// When starting on its own line, go fmt indents line 2+ an extra level.
	n, max := maxWidth, maxWidth-4

	// Print "" +\n, if a string does not start on its own line.
	b := w.buf.Bytes()
	if p := len(bytes.TrimRight(b, " \t")); p > 0 && b[p-1] != '\n' {
		w.printf("\"\" + // Size: %d bytes\n", len(s))
		n, max = maxWidth, maxWidth
	}

	w.printf(`"`)

	for sz, p := 0, 0; p < len(s); {
		var r rune
		r, sz = utf8.DecodeRuneInString(s[p:])
		out := s[p : p+sz]
		chars := 1
		if !unicode.IsPrint(r) || r == utf8.RuneError || r == '"' {
			switch sz {
			case 1:
				out = fmt.Sprintf("\\x%02x", s[p])
			case 2, 3:
				out = fmt.Sprintf("\\u%04x", r)
			case 4:
				out = fmt.Sprintf("\\U%08x", r)
			}
			chars = len(out)
		}
		if n -= chars; n < 0 {
			w.printf("\" +\n\"")
			n = max - len(out)
		}
		w.printf("%s", out)
		p += sz
	}
	w.printf(`"`)
}

// WriteSlice writes a slice value.
func (w *CodeWriter) WriteSlice(x interface{}) {
	w.writeSlice(x, false)
}

// WriteArray writes an array value.
func (w *CodeWriter) WriteArray(x interface{}) {
	w.writeSlice(x, true)
}

func (w *CodeWriter) writeSlice(x interface{}, isArray bool) {
	v := reflect.ValueOf(x)
	w.gob.Encode(v.Len())
	w.Size += v.Len() * int(v.Type().Elem().Size())
	name := typeName(x)
	if isArray {
		name = fmt.Sprintf("[%d]%s", v.Len(), name[strings.Index(name, "]")+1:])
	}
	if isArray {
		w.printf("%s{\n", name)
	} else {
		w.printf("%s{ // %d elements\n", name, v.Len())
	}

	switch kind := v.Type().Elem().Kind(); kind {
	case reflect.String:
		for _, s := range x.([]string) {
			w.WriteString(s)
			w.printf(",\n")
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		// nLine and nBlock are the number of elements per line and block.
		nLine, nBlock, format := 8, 64, "%d,"
		switch kind {
		case reflect.Uint8:
			format = "%#02x,"
		case reflect.Uint16:
			format = "%#04x,"
		case reflect.Uint32:
			nLine, nBlock, format = 4, 32, "%#08x,"
		case reflect.Uint, reflect.Uint64:
			nLine, nBlock, format = 4, 32, "%#016x,"
		case reflect.Int8:
			nLine = 16
		}
		n := nLine
		for i := 0; i < v.Len(); i++ {
			if i%nBlock == 0 && v.Len() > nBlock {
				w.printf("// Entry %X - %X\n", i, i+nBlock-1)
			}
			x := v.Index(i).Interface()
			w.gob.Encode(x)
			w.printf(format, x)
			if n--; n == 0 {
				n = nLine
				w.printf("\n")
			}
		}
		w.printf("\n")
	case reflect.Struct:
		zero := reflect.Zero(v.Type().Elem()).Interface()
		for i := 0; i < v.Len(); i++ {
			x := v.Index(i).Interface()
			w.gob.EncodeValue(v)
			if !reflect.DeepEqual(zero, x) {
				line := fmt.Sprintf("%#v,\n", x)
				line = line[strings.IndexByte(line, '{'):]
				w.printf("%d: ", i)
				w.printf(line)
			}
		}
	case reflect.Array:
		for i := 0; i < v.Len(); i++ {
			w.printf("%d: %#v,\n", i, v.Index(i).Interface())
		}
	default:
		panic("gen: slice elem type not supported")
	}
	w.printf("}")
}

// WriteType writes a definition of the type of the given value and returns the
// type name.
func (w *CodeWriter) WriteType(x interface{}) string {
	t := reflect.TypeOf(x)
	w.printf("type %s struct {\n", t.Name())
	for i := 0; i < t.NumField(); i++ {
		w.printf("\t%s %s\n", t.Field(i).Name, t.Field(i).Type)
	}
	w.printf("}\n")
	return t.Name()
}

// typeName returns the name of the go type of x.
func typeName(x interface{}) string {
	t := reflect.ValueOf(x).Type()
	return strings.Replace(fmt.Sprint(t), "main.", "", 1)
}
<|endoftext|>


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

// +build ignore

//go:generate go run gen.go

// This program generates internet protocol constants and tables by
// reading IANA protocol registries.
package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"go/format"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
)

var registries = []struct {
	url   string
	parse func(io.Writer, io.Reader) error
}{
	{
		"http://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
		parseDSCPRegistry,
	},
	{
		"http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
		parseTOSTCByte,
	},
	{
		"http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
		parseProtocolNumbers,
	},
}

func main() {
	var bb bytes.Buffer
	fmt.Fprintf(&bb, "// go generate gen.go\n")
	fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
	fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
	fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"\n\n`)
	for _, r := range registries {
		resp, err := http.Get(r.url)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		defer resp.Body.Close()
		if resp.StatusCode != http.StatusOK {
			fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
			os.Exit(1)
		}
		if err := r.parse(&bb, resp.Body); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		fmt.Fprintf(&bb, "\n")
	}
	b, err := format.Source(bb.Bytes())
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if err := ioutil.WriteFile("const.go", b, 0644); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func parseDSCPRegistry(w io.Writer, r io.Reader) error {
	dec := xml.NewDecoder(r)
	var dr dscpRegistry
	if err := dec.Decode(&dr); err != nil {
		return err
	}
	drs := dr.escape()
	fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
	fmt.Fprintf(w, "const (\n")
	for _, dr := range drs {
		fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value)
		fmt.Fprintf(w, "// %s\n", dr.OrigName)
	}
	fmt.Fprintf(w, ")\n")
	return nil
}

type dscpRegistry struct {
	XMLName     xml.Name `xml:"registry"`
	Title       string   `xml:"title"`
	Updated     string   `xml:"updated"`
	Note        string   `xml:"note"`
	RegTitle    string   `xml:"registry>title"`
	PoolRecords []struct {
		Name  string `xml:"name"`
		Space string `xml:"space"`
	} `xml:"registry>record"`
	Records []struct {
		Name  string `xml:"name"`
		Space string `xml:"space"`
	} `xml:"registry>registry>record"`
}

type canonDSCPRecord struct {
	OrigName string
	Name     string
	Value    int
}

func (drr *dscpRegistry) escape() []canonDSCPRecord {
	drs := make([]canonDSCPRecord, len(drr.Records))
	sr := strings.NewReplacer(
		"+", "",
		"-", "",
		"/", "",
		".", "",
		" ", "",
	)
	for i, dr := range drr.Records {
		s := strings.TrimSpace(dr.Name)
		drs[i].OrigName = s
		drs[i].Name = sr.Replace(s)
		n, err := strconv.ParseUint(dr.Space, 2, 8)
		if err != nil {
			continue
		}
		drs[i].Value = int(n) << 2
	}
	return drs
}

func parseTOSTCByte(w io.Writer, r io.Reader) error {
	dec := xml.NewDecoder(r)
	var ttb tosTCByte
	if err := dec.Decode(&ttb); err != nil {
		return err
	}
	trs := ttb.escape()
	fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated)
	fmt.Fprintf(w, "const (\n")
	for _, tr := range trs {
		fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value)
		fmt.Fprintf(w, "// %s\n", tr.OrigKeyword)
	}
	fmt.Fprintf(w, ")\n")
	return nil
}

type tosTCByte struct {
	XMLName  xml.Name `xml:"registry"`
	Title    string   `xml:"title"`
	Updated  string   `xml:"updated"`
	Note     string   `xml:"note"`
	RegTitle string   `xml:"registry>title"`
	Records  []struct {
		Binary  string `xml:"binary"`
		Keyword string `xml:"keyword"`
	} `xml:"registry>record"`
}

type canonTOSTCByteRecord struct {
	OrigKeyword string
	Keyword     string
	Value       int
}

func (ttb *tosTCByte) escape() []canonTOSTCByteRecord {
	trs := make([]canonTOSTCByteRecord, len(ttb.Records))
	sr := strings.NewReplacer(
		"Capable", "",
		"(", "",
		")", "",
		"+", "",
		"-", "",
		"/", "",
		".", "",
		" ", "",
	)
	for i, tr := range ttb.Records {
		s := strings.TrimSpace(tr.Keyword)
		trs[i].OrigKeyword = s
		ss := strings.Split(s, " ")
		if len(ss) > 1 {
			trs[i].Keyword = strings.Join(ss[1:], " ")
		} else {
			trs[i].Keyword = ss[0]
		}
		trs[i].Keyword = sr.Replace(trs[i].Keyword)
		n, err := strconv.ParseUint(tr.Binary, 2, 8)
		if err != nil {
			continue
		}
		trs[i].Value = int(n)
	}
	return trs
}

func parseProtocolNumbers(w io.Writer, r io.Reader) error {
	dec := xml.NewDecoder(r)
	var pn protocolNumbers
	if err := dec.Decode(&pn); err != nil {
		return err
	}
	prs := pn.escape()
	prs = append([]canonProtocolRecord{{
		Name:  "IP",
		Descr: "IPv4 encapsulation, pseudo protocol number",
		Value: 0,
	}}, prs...)
	fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated)
	fmt.Fprintf(w, "const (\n")
	for _, pr := range prs {
		if pr.Name == "" {
			continue
		}
		fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value)
		s := pr.Descr
		if s == "" {
			s = pr.OrigName
		}
		fmt.Fprintf(w, "// %s\n", s)
	}
	fmt.Fprintf(w, ")\n")
	return nil
}

type protocolNumbers struct {
	XMLName  xml.Name `xml:"registry"`
	Title    string   `xml:"title"`
	Updated  string   `xml:"updated"`
	RegTitle string   `xml:"registry>title"`
	Note     string   `xml:"registry>note"`
	Records  []struct {
		Value string `xml:"value"`
		Name  string `xml:"name"`
		Descr string `xml:"description"`
	} `xml:"registry>record"`
}

type canonProtocolRecord struct {
	OrigName string
	Name     string
	Descr    string
	Value    int
}

func (pn *protocolNumbers) escape() []canonProtocolRecord {
	prs := make([]canonProtocolRecord, len(pn.Records))
	sr := strings.NewReplacer(
		"-in-", "in",
		"-within-", "within",
		"-over-", "over",
		"+", "P",
		"-", "",
		"/", "",
		".", "",
		" ", "",
	)
	for i, pr := range pn.Records {
		prs[i].OrigName = pr.Name
		s := strings.TrimSpace(pr.Name)
		switch pr.Name {
		case "ISIS over IPv4":
			prs[i].Name = "ISIS"
		case "manet":
			prs[i].Name = "MANET"
		default:
			prs[i].Name = sr.Replace(s)
		}
		ss := strings.Split(pr.Descr, "\n")
		for i := range ss {
			ss[i] = strings.TrimSpace(ss[i])
		}
		if len(ss) > 1 {
			prs[i].Descr = strings.Join(ss, " ")
		} else {
			prs[i].Descr = ss[0]
		}
		prs[i].Value, _ = strconv.Atoi(pr.Value)
	}
	return prs
}
<commit_msg>internal/iana: fix a import comment in code generator.<commit_after>// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

//go:generate go run gen.go

// This program generates internet protocol constants and tables by
// reading IANA protocol registries.
package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"go/format"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
)

var registries = []struct {
	url   string
	parse func(io.Writer, io.Reader) error
}{
	{
		"http://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
		parseDSCPRegistry,
	},
	{
		"http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
		parseTOSTCByte,
	},
	{
		"http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
		parseProtocolNumbers,
	},
}

func main() {
	var bb bytes.Buffer
	fmt.Fprintf(&bb, "// go generate gen.go\n")
	fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
	fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
	fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n")
	for _, r := range registries {
		resp, err := http.Get(r.url)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		defer resp.Body.Close()
		if resp.StatusCode != http.StatusOK {
			fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
			os.Exit(1)
		}
		if err := r.parse(&bb, resp.Body); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		fmt.Fprintf(&bb, "\n")
	}
	b, err := format.Source(bb.Bytes())
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if err := ioutil.WriteFile("const.go", b, 0644); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func parseDSCPRegistry(w io.Writer, r io.Reader) error {
	dec := xml.NewDecoder(r)
	var dr dscpRegistry
	if err := dec.Decode(&dr); err != nil {
		return err
	}
	drs := dr.escape()
	fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
	fmt.Fprintf(w, "const (\n")
	for _, dr := range drs {
		fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value)
		fmt.Fprintf(w, "// %s\n", dr.OrigName)
	}
	fmt.Fprintf(w, ")\n")
	return nil
}

type dscpRegistry struct {
	XMLName     xml.Name `xml:"registry"`
	Title       string   `xml:"title"`
	Updated     string   `xml:"updated"`
	Note        string   `xml:"note"`
	RegTitle    string   `xml:"registry>title"`
	PoolRecords []struct {
		Name  string `xml:"name"`
		Space string `xml:"space"`
	} `xml:"registry>record"`
	Records []struct {
		Name  string `xml:"name"`
		Space string `xml:"space"`
	} `xml:"registry>registry>record"`
}

type canonDSCPRecord struct {
	OrigName string
	Name     string
	Value    int
}

func (drr *dscpRegistry) escape() []canonDSCPRecord {
	drs := make([]canonDSCPRecord, len(drr.Records))
	sr := strings.NewReplacer(
		"+", "",
		"-", "",
		"/", "",
		".", "",
		" ", "",
	)
	for i, dr := range drr.Records {
		s := strings.TrimSpace(dr.Name)
		drs[i].OrigName = s
		drs[i].Name = sr.Replace(s)
		n, err := strconv.ParseUint(dr.Space, 2, 8)
		if err != nil {
			continue
		}
		drs[i].Value = int(n) << 2
	}
	return drs
}

func parseTOSTCByte(w io.Writer, r io.Reader) error {
	dec := xml.NewDecoder(r)
	var ttb tosTCByte
	if err := dec.Decode(&ttb); err != nil {
		return err
	}
	trs := ttb.escape()
	fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated)
	fmt.Fprintf(w, "const (\n")
	for _, tr := range trs {
		fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value)
		fmt.Fprintf(w, "// %s\n", tr.OrigKeyword)
	}
	fmt.Fprintf(w, ")\n")
	return nil
}

type tosTCByte struct {
	XMLName  xml.Name `xml:"registry"`
	Title    string   `xml:"title"`
	Updated  string   `xml:"updated"`
	Note     string   `xml:"note"`
	RegTitle string   `xml:"registry>title"`
	Records  []struct {
		Binary  string `xml:"binary"`
		Keyword string `xml:"keyword"`
	} `xml:"registry>record"`
}

type canonTOSTCByteRecord struct {
	OrigKeyword string
	Keyword     string
	Value       int
}

func (ttb *tosTCByte) escape() []canonTOSTCByteRecord {
	trs := make([]canonTOSTCByteRecord, len(ttb.Records))
	sr := strings.NewReplacer(
		"Capable", "",
		"(", "",
		")", "",
		"+", "",
		"-", "",
		"/", "",
		".", "",
		" ", "",
	)
	for i, tr := range ttb.Records {
		s := strings.TrimSpace(tr.Keyword)
		trs[i].OrigKeyword = s
		ss := strings.Split(s, " ")
		if len(ss) > 1 {
			trs[i].Keyword = strings.Join(ss[1:], " ")
		} else {
			trs[i].Keyword = ss[0]
		}
		trs[i].Keyword = sr.Replace(trs[i].Keyword)
		n, err := strconv.ParseUint(tr.Binary, 2, 8)
		if err != nil {
			continue
		}
		trs[i].Value = int(n)
	}
	return trs
}

func parseProtocolNumbers(w io.Writer, r io.Reader) error {
	dec := xml.NewDecoder(r)
	var pn protocolNumbers
	if err := dec.Decode(&pn); err != nil {
		return err
	}
	prs := pn.escape()
	prs = append([]canonProtocolRecord{{
		Name:  "IP",
		Descr: "IPv4 encapsulation, pseudo protocol number",
		Value: 0,
	}}, prs...)
	fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated)
	fmt.Fprintf(w, "const (\n")
	for _, pr := range prs {
		if pr.Name == "" {
			continue
		}
		fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value)
		s := pr.Descr
		if s == "" {
			s = pr.OrigName
		}
		fmt.Fprintf(w, "// %s\n", s)
	}
	fmt.Fprintf(w, ")\n")
	return nil
}

type protocolNumbers struct {
	XMLName  xml.Name `xml:"registry"`
	Title    string   `xml:"title"`
	Updated  string   `xml:"updated"`
	RegTitle string   `xml:"registry>title"`
	Note     string   `xml:"registry>note"`
	Records  []struct {
		Value string `xml:"value"`
		Name  string `xml:"name"`
		Descr string `xml:"description"`
	} `xml:"registry>record"`
}

type canonProtocolRecord struct {
	OrigName string
	Name     string
	Descr    string
	Value    int
}

func (pn *protocolNumbers) escape() []canonProtocolRecord {
	prs := make([]canonProtocolRecord, len(pn.Records))
	sr := strings.NewReplacer(
		"-in-", "in",
		"-within-", "within",
		"-over-", "over",
		"+", "P",
		"-", "",
		"/", "",
		".", "",
		" ", "",
	)
	for i, pr := range pn.Records {
		prs[i].OrigName = pr.Name
		s := strings.TrimSpace(pr.Name)
		switch pr.Name {
		case "ISIS over IPv4":
			prs[i].Name = "ISIS"
		case "manet":
			prs[i].Name = "MANET"
		default:
			prs[i].Name = sr.Replace(s)
		}
		ss := strings.Split(pr.Descr, "\n")
		for i := range ss {
			ss[i] = strings.TrimSpace(ss[i])
		}
		if len(ss) > 1 {
			prs[i].Descr = strings.Join(ss, " ")
		} else {
			prs[i].Descr = ss[0]
		}
		prs[i].Value, _ = strconv.Atoi(pr.Value)
	}
	return prs
}
<|endoftext|>


<commit_before>package inventory

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"log"
	"time"

	"github.com/aarbt/bitcoin-network"
	"github.com/aarbt/bitcoin-wallet/database"
	"github.com/aarbt/bitcoin-wallet/messages"
	"github.com/aarbt/bitcoin-wallet/utils"
)

type HeaderGetter struct {
	inv    *Inventory
	input  chan network.Message
	output chan *messages.ExtendedHeader
	db     *database.DB

	closing bool
	closeCh chan struct{}
	doneCh  chan struct{}
}

func GetHeaders(inv *Inventory, db *database.DB) *HeaderGetter {
	getter := HeaderGetter{
		inv: inv,
		db:  db,

		closeCh: make(chan struct{}),
		doneCh:  make(chan struct{}),
	}
	getter.Run()
	return &getter
}

// Close shuts down the header getter. It is guaranteed to not read or write to
// any channels after Close returns.
func (getter *HeaderGetter) Close() {
	getter.closing = true
	close(getter.closeCh)
	<-getter.doneCh
}

func (getter *HeaderGetter) Run() {
	go func() {
		for !getter.closing {
			incomplete := getter.requestMoreHeaders()
			if getter.closing {
				break
			}
			if !incomplete {
				time.Sleep(10 * time.Second)
			}
		}
		close(getter.doneCh)
	}()
}

func (getter *HeaderGetter) Input() chan<- network.Message {
	// TODO Find a better way to avoid dropped messages.
	getter.input = make(chan network.Message, 10)
	return getter.input
}

func (getter *HeaderGetter) loadHeader(hash []byte) *messages.ExtendedHeader {
	header := &messages.ExtendedHeader{}
	err := getter.db.Get(hash, header)
	if err != nil {
		log.Printf("Getting header failed: %v", err)
		return nil
	}
	return header
}

func (getter *HeaderGetter) setHighestKnownBlock(hash []byte, height uint) {
	getter.db.Set(kHighestKnownBlock, databaseBlockHeight{
		Hash:   hash,
		Height: height,
	})
}

type databaseBlockHeight struct {
	Hash   []byte
	Height uint
}

func (getter *HeaderGetter) highestKnownBlock() ([]byte, uint) {
	// TODO read from memory if no database is set.
	last := databaseBlockHeight{}
	err := getter.db.Get(kHighestKnownBlock, &last)
	if err != nil {
		log.Printf("Couldn't retrieve highest known block: %v", err)
		return make([]byte, 32), 0
	}
	return last.Hash, last.Height
}

func (getter *HeaderGetter) formGetHeaderMessage() network.Message {
	hash, height := getter.highestKnownBlock()
	msg := messages.GetBlocks{
		Version:  70001,
		HashStop: make([]byte, 32),
		Locators: [][]byte{hash},
	}
	loc := hash
	// Add 20 recent known headers with larger and larger gaps between them
	// as they get less recent (up to a step of 40 between the two least
	// recent ones.)
	for i := 0; i < 20 && loc != nil; i++ {
		for j := 0; j < i*2; j++ {
			header := getter.loadHeader(loc)
			if header == nil {
				loc = nil
				break
			}
			loc = header.PrevBlock
		}
		if loc != nil {
			msg.Locators = append(msg.Locators, loc)
		}
	}
	var msgBytes bytes.Buffer
	msg.Serialize(&msgBytes)
	log.Printf("Getting more headers, starting from %x at height %d.\n",
		utils.ReverseBytes(hash), height)
	return network.Message{
		Type: "getheaders",
		Data: msgBytes.Bytes(),
	}
}

func (getter *HeaderGetter) requestMoreHeaders() bool {
	// Wait for connection.
	select {
	case <-getter.inv.Connected():
		// do nothing.
	case <-getter.closeCh:
		return false
	}

	msg := getter.formGetHeaderMessage()
	endpoint := getter.inv.Send(msg)
	if endpoint == "" {
		log.Printf("No endpoint connected; wait for timeout and retry.")
	}

	var incomplete, received bool
	// TODO Increase timeout on large requests.
	timer := time.NewTimer(45 * time.Second)
	for !received {
		select {
		case m := <-getter.input:
			if m.Endpoint == endpoint {
				incomplete = getter.handleHeaders(m)
				received = true
			} else {
				log.Printf("Received unexpected message from %q "+
					"while waiting for message from %q.",
					m.Endpoint, endpoint)
			}
		case <-timer.C:
			if endpoint != "" {
				getter.inv.reportMisbehaviour(endpoint, 5,
					"getheaders request timed out.")
			}
			return true
		case <-getter.closeCh:
			timer.Stop()
			return false
		}
	}
	return incomplete
}

func (getter *HeaderGetter) setMessageHeight(m *messages.ExtendedHeader) (uint, error) {
	prev := getter.loadHeader(m.PrevBlock)
	if prev != nil {
		m.Height = prev.Height + 1
		return m.Height, nil
	} else {
		block0, _ := hex.DecodeString(
			"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")
		if bytes.Equal(m.PrevBlock, utils.ReverseBytes(block0)) {
			m.Height = 1
			return 1, nil
		}
		return 0, fmt.Errorf("Couldn't find prev block.")
	}
}

func (getter *HeaderGetter) handleHeaders(m network.Message) bool {
	_, oldHeight := getter.highestKnownBlock()
	headers, err := messages.ParseHeaders(m.Data)
	if err != nil {
		log.Printf("Failed to parse headers: %v", err)
		return true
	}
	log.Printf("Received %d headers from %s.\n",
		len(headers), m.Endpoint)
	var hash []byte
	var height uint
	var incomplete bool
	var vector messages.InventoryVector
	for _, h := range headers {
		height, err = getter.setMessageHeight(h)
		if err != nil {
			log.Printf("Received bad header %x from %s.",
				utils.ReverseBytes(h.Hash()), m.Endpoint)
			// TODO handle this
			getter.inv.reportMisbehaviour(m.Endpoint, 10,
				"Sent bad block header.")
			incomplete = true
			break
		}
		hash = h.Hash()
		getter.db.Set(hash, h)

		// Request merkleblocks for headers.
		vector = append(vector, &messages.Inventory{
			Type: messages.TypeMsgFilteredBlock,
			Hash: hash,
		})
	}
	if len(hash) != 0 && height > oldHeight {
		getter.setHighestKnownBlock(hash, height)
		log.Printf("Last header updated to %x (internal byte order), height is %d.",
			utils.ReverseBytes(hash), height)
		if height < oldHeight+uint(len(vector)) {
			// TODO not all blocks are new, consider changing endpoint.
		}
	} else {
		getter.inv.reportMisbehaviour(m.Endpoint, 10,
			"No new headers in received message.")
		incomplete = true
	}
	if len(headers) >= 2000 {
		incomplete = true
	} else {
		// TODO question if endpoint is good.
	}
	if len(vector) > 0 {
		data, err := messages.EncodeInventoryVector(vector)
		if err != nil {
			log.Printf("Failed to encode getdata vector: %v", err)
			return true
		}
		getter.inv.Send(network.Message{
			Type: "getdata",
			Data: data,
		})
	}
	return incomplete
}
<commit_msg>Clean up shut-down process of headers module.<commit_after>package inventory

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"log"
	"time"

	"github.com/aarbt/bitcoin-network"
	"github.com/aarbt/bitcoin-wallet/database"
	"github.com/aarbt/bitcoin-wallet/messages"
	"github.com/aarbt/bitcoin-wallet/utils"
)

type HeaderGetter struct {
	inv    *Inventory
	input  chan network.Message
	output chan *messages.ExtendedHeader
	db     *database.DB

	closeCh chan struct{}
	doneCh  chan struct{}
}

func GetHeaders(inv *Inventory, db *database.DB) *HeaderGetter {
	getter := HeaderGetter{
		inv: inv,
		db:  db,

		closeCh: make(chan struct{}),
		doneCh:  make(chan struct{}),
	}
	getter.Run()
	return &getter
}

// Close shuts down the header getter. It is guaranteed to not read or write to
// any channels after Close returns.
func (getter *HeaderGetter) Close() {
	close(getter.closeCh)
	<-getter.doneCh
}

func (getter *HeaderGetter) Run() {
	go func() {
		var closing bool
		for !closing {
			timer := time.NewTimer(2 * time.Minute)
			incomplete := getter.requestMoreHeaders()
			if !incomplete {
				// We shouldn't need to poll for headers very
				// often as blocks are broadcast by miners.
				select {
				case <-timer.C:
					//nothing
				case <-getter.closeCh:
					closing = true
					break
				}
			}
		}
		close(getter.doneCh)
	}()
}

func (getter *HeaderGetter) Input() chan<- network.Message {
	// TODO Find a better way to avoid dropped messages.
	getter.input = make(chan network.Message, 10)
	return getter.input
}

func (getter *HeaderGetter) loadHeader(hash []byte) (*messages.ExtendedHeader, error) {
	header := &messages.ExtendedHeader{}
	err := getter.db.Get(hash, header)
	if err != nil {
		return nil, fmt.Errorf("Loading header failed: %v", err)
	}
	return header, nil
}

func (getter *HeaderGetter) setHighestKnownBlock(hash []byte, height int) {
	getter.db.Set(kHighestKnownBlock, databaseBlockHeight{
		Hash:   hash,
		Height: height,
	})
}

type databaseBlockHeight struct {
	Hash   []byte
	Height int
}

func (getter *HeaderGetter) highestKnownBlock() ([]byte, int) {
	// TODO read from memory if no database is set.
	last := databaseBlockHeight{}
	err := getter.db.Get(kHighestKnownBlock, &last)
	if err != nil {
		log.Printf("Couldn't retrieve highest known block: %v", err)
		return make([]byte, 32), 0
	}
	return last.Hash, last.Height
}

func (getter *HeaderGetter) formGetHeaderMessage() network.Message {
	hash, height := getter.highestKnownBlock()
	msg := messages.GetBlocks{
		Version:  70001,
		HashStop: make([]byte, 32),
		Locators: [][]byte{hash},
	}
	loc := hash
	// Add 20 recent known headers with larger and larger gaps between them
	// as they get less recent (up to a step of 40 between the two least
	// recent ones.)
	for i := 0; i < 20 && loc != nil; i++ {
		for j := 0; j < i*2; j++ {
			header, err := getter.loadHeader(loc)
			if err != nil {
				log.Printf("Failed to get recent header, %d steps back: %v",
					i, err)
				loc = nil
				break
			}
			loc = header.PrevBlock
		}
		if loc != nil {
			msg.Locators = append(msg.Locators, loc)
		}
	}
	var msgBytes bytes.Buffer
	msg.Serialize(&msgBytes)
	log.Printf("Getting more headers, starting from %x at height %d.\n",
		utils.ReverseBytes(hash), height)
	return network.Message{
		Type: "getheaders",
		Data: msgBytes.Bytes(),
	}
}

func (getter *HeaderGetter) requestMoreHeaders() bool {
	// Wait for connection.
	select {
	case <-getter.inv.Connected():
		// do nothing.
	case <-getter.closeCh:
		return false
	}

	msg := getter.formGetHeaderMessage()
	endpoint := getter.inv.Send(msg)
	if endpoint == "" {
		log.Printf("No endpoint connected; wait for timeout and retry.")
	}

	var incomplete, received bool
	// TODO Increase timeout on large requests.
	timer := time.NewTimer(45 * time.Second)
	for !received {
		select {
		case m := <-getter.input:
			if m.Endpoint == endpoint {
				incomplete = getter.handleHeaders(m)
				received = true
			} else {
				log.Printf("Received unexpected message from %q "+
					"while waiting for message from %q.",
					m.Endpoint, endpoint)
			}
		case <-timer.C:
			if endpoint != "" {
				getter.inv.reportMisbehaviour(endpoint, 5,
					"getheaders request timed out.")
			}
			return true
		case <-getter.closeCh:
			timer.Stop()
			return false
		}
	}
	return incomplete
}

func (getter *HeaderGetter) getBlockHeight(hash []byte) (int, error) {
	prev, err := getter.loadHeader(hash)
	if err != nil {
		block0, _ := hex.DecodeString(
			"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")
		if bytes.Equal(hash, utils.ReverseBytes(block0)) {
			return 0, nil
		}
		return -1, fmt.Errorf("Couldn't find prev block.")
	}
	return int(prev.Height), nil
}

func (getter *HeaderGetter) handleHeaders(m network.Message) bool {
	_, oldHeight := getter.highestKnownBlock()
	headers, err := messages.ParseHeaders(m.Data)
	if err != nil {
		log.Printf("Failed to parse headers: %v", err)
		return true
	}
	log.Printf("Received %d headers from %s.\n",
		len(headers), m.Endpoint)
	var hash []byte
	var height int
	var incomplete bool
	var vector messages.InventoryVector
	for _, h := range headers {
		prevHeight, err := getter.getBlockHeight(h.PrevBlock)
		if err != nil {
			log.Printf("Received bad header %x from %s.",
				utils.ReverseBytes(h.Hash()), m.Endpoint)
			// TODO handle this
			getter.inv.reportMisbehaviour(m.Endpoint, 20,
				"Sent bad block header.")
			incomplete = true
			break
		}
		height = prevHeight + 1
		h.Height = height
		hash = h.Hash()
		getter.db.Set(hash, h)

		// Request merkleblocks for headers.
		vector = append(vector, &messages.Inventory{
			Type: messages.TypeMsgFilteredBlock,
			Hash: hash,
		})
	}
	if len(hash) != 0 && height > oldHeight {
		getter.setHighestKnownBlock(hash, height)
		log.Printf("Last header updated to %x (internal byte order), height is %d.",
			utils.ReverseBytes(hash), height)
		if height < oldHeight+len(vector) {
			// TODO not all blocks are new, consider changing endpoint.
		}
	} else {
		// TODO If we haven't caught up yet, this means the endpoint is
		// misbehaving.
	}
	if len(headers) >= 2000 {
		incomplete = true
	} else {
		// TODO question if endpoint is good.
	}
	err = getter.inv.SendGetData(vector, m.Endpoint)
	if err != nil {
		return true
	}
	return incomplete
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package logger

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"time"

	"strings"

	"sync"
	"sync/atomic"
)

// Level represents the level to log messages at.
type Level int

const (
	// LogDebug represents debug messages.
	LogDebug Level = iota
	// LogInfo represents informational messages.
	LogInfo
	// LogWarning represents warnings.
	LogWarning
	// LogError represents errors.
	LogError
)

var (
	// LogLevelNames takes a config name and gives the real log level.
	LogLevelNames = map[string]Level{
		"debug":    LogDebug,
		"info":     LogInfo,
		"warn":     LogWarning,
		"warning":  LogWarning,
		"warnings": LogWarning,
		"error":    LogError,
		"errors":   LogError,
	}
	// LogLevelDisplayNames gives the display name to use for our log levels.
	LogLevelDisplayNames = map[Level]string{
		LogDebug:   "debug",
		LogInfo:    "info",
		LogWarning: "warning",
		LogError:   "error",
	}
)

// Manager is the main interface used to log debug/info/error messages.
type Manager struct {
	configMutex     sync.RWMutex
	loggers         []singleLogger
	stdoutWriteLock sync.Mutex // use one lock for both stdout and stderr
	fileWriteLock   sync.Mutex
	loggingRawIO    uint32
}

// LoggingConfig represents the configuration of a single logger.
type LoggingConfig struct {
	Method        string
	MethodStdout  bool
	MethodStderr  bool
	MethodFile    bool
	Filename      string
	TypeString    string   `yaml:"type"`
	Types         []string `yaml:"real-types"`
	ExcludedTypes []string `yaml:"real-excluded-types"`
	LevelString   string   `yaml:"level"`
	Level         Level    `yaml:"level-real"`
}

// NewManager returns a new log manager.
func NewManager(config []LoggingConfig) (*Manager, error) {
	var logger Manager

	if err := logger.ApplyConfig(config); err != nil {
		return nil, err
	}

	return &logger, nil
}

// ApplyConfig applies the given config to this logger (rehashes the config, in other words).
func (logger *Manager) ApplyConfig(config []LoggingConfig) error {
	logger.configMutex.Lock()
	defer logger.configMutex.Unlock()

	for _, logger := range logger.loggers {
		logger.Close()
	}

	logger.loggers = nil
	atomic.StoreUint32(&logger.loggingRawIO, 0)

	// for safety, this deep-copies all mutable data in `config`
	// XXX let's keep it that way
	var lastErr error
	for _, logConfig := range config {
		typeMap := make(map[string]bool)
		for _, name := range logConfig.Types {
			typeMap[name] = true
		}
		excludedTypeMap := make(map[string]bool)
		for _, name := range logConfig.ExcludedTypes {
			excludedTypeMap[name] = true
		}

		sLogger := singleLogger{
			MethodSTDOUT: logConfig.MethodStdout,
			MethodSTDERR: logConfig.MethodStderr,
			MethodFile: fileMethod{
				Enabled:  logConfig.MethodFile,
				Filename: logConfig.Filename,
			},
			Level:           logConfig.Level,
			Types:           typeMap,
			ExcludedTypes:   excludedTypeMap,
			stdoutWriteLock: &logger.stdoutWriteLock,
			fileWriteLock:   &logger.fileWriteLock,
		}
		ioEnabled := typeMap["userinput"] || typeMap["useroutput"] || (typeMap["*"] && !(excludedTypeMap["userinput"] && excludedTypeMap["useroutput"]))
		// raw I/O is only logged at level debug;
		if ioEnabled && logConfig.Level == LogDebug {
			atomic.StoreUint32(&logger.loggingRawIO, 1)
		}
		if sLogger.MethodFile.Enabled {
			file, err := os.OpenFile(sLogger.MethodFile.Filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
			if err != nil {
				lastErr = fmt.Errorf("Could not open log file %s [%s]", sLogger.MethodFile.Filename, err.Error())
			}
			writer := bufio.NewWriter(file)
			sLogger.MethodFile.File = file
			sLogger.MethodFile.Writer = writer
		}
		logger.loggers = append(logger.loggers, sLogger)
	}

	return lastErr
}

// IsLoggingRawIO returns true if raw user input and output is being logged.
func (logger *Manager) IsLoggingRawIO() bool {
	return atomic.LoadUint32(&logger.loggingRawIO) == 1
}

// Log logs the given message with the given details.
func (logger *Manager) Log(level Level, logType string, messageParts ...string) {
	logger.configMutex.RLock()
	defer logger.configMutex.RUnlock()

	for _, singleLogger := range logger.loggers {
		singleLogger.Log(level, logType, messageParts...)
	}
}

// Debug logs the given message as a debug message.
func (logger *Manager) Debug(logType string, messageParts ...string) {
	logger.Log(LogDebug, logType, messageParts...)
}

// Info logs the given message as an info message.
func (logger *Manager) Info(logType string, messageParts ...string) {
	logger.Log(LogInfo, logType, messageParts...)
}

// Warning logs the given message as a warning message.
func (logger *Manager) Warning(logType string, messageParts ...string) {
	logger.Log(LogWarning, logType, messageParts...)
}

// Error logs the given message as an error message.
func (logger *Manager) Error(logType string, messageParts ...string) {
	logger.Log(LogError, logType, messageParts...)
}

type fileMethod struct {
	Enabled  bool
	Filename string
	File     *os.File
	Writer   *bufio.Writer
}

// singleLogger represents a single logger instance.
type singleLogger struct {
	stdoutWriteLock *sync.Mutex
	fileWriteLock   *sync.Mutex
	MethodSTDOUT    bool
	MethodSTDERR    bool
	MethodFile      fileMethod
	Level           Level
	Types           map[string]bool
	ExcludedTypes   map[string]bool
}

func (logger *singleLogger) Close() error {
	if logger.MethodFile.Enabled {
		flushErr := logger.MethodFile.Writer.Flush()
		closeErr := logger.MethodFile.File.Close()
		if flushErr != nil {
			return flushErr
		}
		return closeErr
	}
	return nil
}

// Log logs the given message with the given details.
func (logger *singleLogger) Log(level Level, logType string, messageParts ...string) {
	// no logging enabled
	if !(logger.MethodSTDOUT || logger.MethodSTDERR || logger.MethodFile.Enabled) {
		return
	}

	// ensure we're logging to the given level
	if level < logger.Level {
		return
	}

	// ensure we're capturing this logType
	logTypeCleaned := strings.ToLower(strings.TrimSpace(logType))
	capturing := (logger.Types["*"] || logger.Types[logTypeCleaned]) && !logger.ExcludedTypes["*"] && !logger.ExcludedTypes[logTypeCleaned]
	if !capturing {
		return
	}

	// assemble full line

	var rawBuf bytes.Buffer
	fmt.Fprintf(&rawBuf, "%s : %s : %s : ", time.Now().UTC().Format("2006-01-02T15:04:05Z"), LogLevelDisplayNames[level], logType)
	for i, p := range messageParts {
		rawBuf.WriteString(p)

		if i != len(messageParts)-1 {
			rawBuf.WriteString(" : ")
		}
	}
	rawBuf.WriteRune('\n')

	// output
	if logger.MethodSTDOUT {
		logger.stdoutWriteLock.Lock()
		os.Stdout.Write(rawBuf.Bytes())
		logger.stdoutWriteLock.Unlock()
	}
	if logger.MethodSTDERR {
		logger.stdoutWriteLock.Lock()
		os.Stderr.Write(rawBuf.Bytes())
		logger.stdoutWriteLock.Unlock()
	}
	if logger.MethodFile.Enabled {
		logger.fileWriteLock.Lock()
		logger.MethodFile.Writer.Write(rawBuf.Bytes())
		logger.MethodFile.Writer.Flush()
		logger.fileWriteLock.Unlock()
	}
}
<commit_msg>fix timestamp formatting<commit_after>// Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package logger

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"time"

	"strings"

	"sync"
	"sync/atomic"
)

// Level represents the level to log messages at.
type Level int

const (
	// LogDebug represents debug messages.
	LogDebug Level = iota
	// LogInfo represents informational messages.
	LogInfo
	// LogWarning represents warnings.
	LogWarning
	// LogError represents errors.
	LogError
)

var (
	// LogLevelNames takes a config name and gives the real log level.
	LogLevelNames = map[string]Level{
		"debug":    LogDebug,
		"info":     LogInfo,
		"warn":     LogWarning,
		"warning":  LogWarning,
		"warnings": LogWarning,
		"error":    LogError,
		"errors":   LogError,
	}
	// LogLevelDisplayNames gives the display name to use for our log levels.
	LogLevelDisplayNames = map[Level]string{
		LogDebug:   "debug",
		LogInfo:    "info",
		LogWarning: "warning",
		LogError:   "error",
	}
)

// Manager is the main interface used to log debug/info/error messages.
type Manager struct {
	configMutex     sync.RWMutex
	loggers         []singleLogger
	stdoutWriteLock sync.Mutex // use one lock for both stdout and stderr
	fileWriteLock   sync.Mutex
	loggingRawIO    uint32
}

// LoggingConfig represents the configuration of a single logger.
type LoggingConfig struct {
	Method        string
	MethodStdout  bool
	MethodStderr  bool
	MethodFile    bool
	Filename      string
	TypeString    string   `yaml:"type"`
	Types         []string `yaml:"real-types"`
	ExcludedTypes []string `yaml:"real-excluded-types"`
	LevelString   string   `yaml:"level"`
	Level         Level    `yaml:"level-real"`
}

// NewManager returns a new log manager.
func NewManager(config []LoggingConfig) (*Manager, error) {
	var logger Manager

	if err := logger.ApplyConfig(config); err != nil {
		return nil, err
	}

	return &logger, nil
}

// ApplyConfig applies the given config to this logger (rehashes the config, in other words).
func (logger *Manager) ApplyConfig(config []LoggingConfig) error {
	logger.configMutex.Lock()
	defer logger.configMutex.Unlock()

	for _, logger := range logger.loggers {
		logger.Close()
	}

	logger.loggers = nil
	atomic.StoreUint32(&logger.loggingRawIO, 0)

	// for safety, this deep-copies all mutable data in `config`
	// XXX let's keep it that way
	var lastErr error
	for _, logConfig := range config {
		typeMap := make(map[string]bool)
		for _, name := range logConfig.Types {
			typeMap[name] = true
		}
		excludedTypeMap := make(map[string]bool)
		for _, name := range logConfig.ExcludedTypes {
			excludedTypeMap[name] = true
		}

		sLogger := singleLogger{
			MethodSTDOUT: logConfig.MethodStdout,
			MethodSTDERR: logConfig.MethodStderr,
			MethodFile: fileMethod{
				Enabled:  logConfig.MethodFile,
				Filename: logConfig.Filename,
			},
			Level:           logConfig.Level,
			Types:           typeMap,
			ExcludedTypes:   excludedTypeMap,
			stdoutWriteLock: &logger.stdoutWriteLock,
			fileWriteLock:   &logger.fileWriteLock,
		}
		ioEnabled := typeMap["userinput"] || typeMap["useroutput"] || (typeMap["*"] && !(excludedTypeMap["userinput"] && excludedTypeMap["useroutput"]))
		// raw I/O is only logged at level debug;
		if ioEnabled && logConfig.Level == LogDebug {
			atomic.StoreUint32(&logger.loggingRawIO, 1)
		}
		if sLogger.MethodFile.Enabled {
			file, err := os.OpenFile(sLogger.MethodFile.Filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
			if err != nil {
				lastErr = fmt.Errorf("Could not open log file %s [%s]", sLogger.MethodFile.Filename, err.Error())
			}
			writer := bufio.NewWriter(file)
			sLogger.MethodFile.File = file
			sLogger.MethodFile.Writer = writer
		}
		logger.loggers = append(logger.loggers, sLogger)
	}

	return lastErr
}

// IsLoggingRawIO returns true if raw user input and output is being logged.
func (logger *Manager) IsLoggingRawIO() bool {
	return atomic.LoadUint32(&logger.loggingRawIO) == 1
}

// Log logs the given message with the given details.
func (logger *Manager) Log(level Level, logType string, messageParts ...string) {
	logger.configMutex.RLock()
	defer logger.configMutex.RUnlock()

	for _, singleLogger := range logger.loggers {
		singleLogger.Log(level, logType, messageParts...)
	}
}

// Debug logs the given message as a debug message.
func (logger *Manager) Debug(logType string, messageParts ...string) {
	logger.Log(LogDebug, logType, messageParts...)
}

// Info logs the given message as an info message.
func (logger *Manager) Info(logType string, messageParts ...string) {
	logger.Log(LogInfo, logType, messageParts...)
}

// Warning logs the given message as a warning message.
func (logger *Manager) Warning(logType string, messageParts ...string) {
	logger.Log(LogWarning, logType, messageParts...)
}

// Error logs the given message as an error message.
func (logger *Manager) Error(logType string, messageParts ...string) {
	logger.Log(LogError, logType, messageParts...)
}

type fileMethod struct {
	Enabled  bool
	Filename string
	File     *os.File
	Writer   *bufio.Writer
}

// singleLogger represents a single logger instance.
type singleLogger struct {
	stdoutWriteLock *sync.Mutex
	fileWriteLock   *sync.Mutex
	MethodSTDOUT    bool
	MethodSTDERR    bool
	MethodFile      fileMethod
	Level           Level
	Types           map[string]bool
	ExcludedTypes   map[string]bool
}

func (logger *singleLogger) Close() error {
	if logger.MethodFile.Enabled {
		flushErr := logger.MethodFile.Writer.Flush()
		closeErr := logger.MethodFile.File.Close()
		if flushErr != nil {
			return flushErr
		}
		return closeErr
	}
	return nil
}

// Log logs the given message with the given details.
func (logger *singleLogger) Log(level Level, logType string, messageParts ...string) {
	// no logging enabled
	if !(logger.MethodSTDOUT || logger.MethodSTDERR || logger.MethodFile.Enabled) {
		return
	}

	// ensure we're logging to the given level
	if level < logger.Level {
		return
	}

	// ensure we're capturing this logType
	logTypeCleaned := strings.ToLower(strings.TrimSpace(logType))
	capturing := (logger.Types["*"] || logger.Types[logTypeCleaned]) && !logger.ExcludedTypes["*"] && !logger.ExcludedTypes[logTypeCleaned]
	if !capturing {
		return
	}

	// assemble full line

	var rawBuf bytes.Buffer
	fmt.Fprintf(&rawBuf, "%s : %s : %s : ", time.Now().UTC().Format("2006-01-02T15:04:05.000Z"), LogLevelDisplayNames[level], logType)
	for i, p := range messageParts {
		rawBuf.WriteString(p)

		if i != len(messageParts)-1 {
			rawBuf.WriteString(" : ")
		}
	}
	rawBuf.WriteRune('\n')

	// output
	if logger.MethodSTDOUT {
		logger.stdoutWriteLock.Lock()
		os.Stdout.Write(rawBuf.Bytes())
		logger.stdoutWriteLock.Unlock()
	}
	if logger.MethodSTDERR {
		logger.stdoutWriteLock.Lock()
		os.Stderr.Write(rawBuf.Bytes())
		logger.stdoutWriteLock.Unlock()
	}
	if logger.MethodFile.Enabled {
		logger.fileWriteLock.Lock()
		logger.MethodFile.Writer.Write(rawBuf.Bytes())
		logger.MethodFile.Writer.Flush()
		logger.fileWriteLock.Unlock()
	}
}
<|endoftext|>


<commit_before>package mapgen

import (
	"fmt"

	"buildblast/lib/coords"
)

type Chunk [][][]Block

func generateChunk(bg blockGenerator, cc coords.Chunk) (Chunk, []coords.World) {
	cw := coords.ChunkWidth
	ch := coords.ChunkHeight
	cd := coords.ChunkDepth

	spawns := make([]coords.World, 0)
	blocks := make([][][]Block, cw)
	for ox := 0; ox < cw; ox++ {
		blocks[ox] = make([][]Block, ch)
		for oy := 0; oy < ch; oy++ {
			blocks[ox][oy] = make([]Block, cd)
			for oz := 0; oz < cd; oz++ {
				bc := coords.Block{
					X: ox + cc.X*cw,
					Y: oy + cc.Y*ch,
					Z: oz + cc.Z*cd,
				}
				block, isSpawn := bg.Block(bc)
				blocks[ox][oy][oz] = block
				if isSpawn {
					spawns = append(spawns, bc.Center())
				}
			}
		}
	}
	return blocks, spawns
}

func (c Chunk) Block(oc coords.Offset) Block {
	return c[oc.X][oc.Y][oc.Z]
}

func (c Chunk) SetBlock(oc coords.Offset, newBlock Block) {
	c[oc.X][oc.Y][oc.Z] = newBlock
}

// Flatten returns the chunk data as a string. It
// can be used for various forms of serialization
// where a valid UTF-8 string is required, and
// efficiency (in terms of size) is not hugely
// important. We use this format for chunk data
// in JSON documents because:
//  a) It's smaller, at least half the size, of a
//     normal JSON array, potentially more.
//     (depending on the contents)
//  b) It's much faster - the go JSON implementation
//     isn't particulilly fast at serializing large
//     arrays of numbers.
func (c Chunk) Flatten() string {
	cw := coords.ChunkWidth
	ch := coords.ChunkHeight
	cd := coords.ChunkDepth
	data := make([]byte, cw*ch*cd)
	for ox := 0; ox < cw; ox++ {
		for oy := 0; oy < ch; oy++ {
			for oz := 0; oz < cd; oz++ {
				// 32: Space charater. Control charaters
				// are not allowed in JSON strings.
				value := byte(c[ox][oy][oz] + 32)
				if value >= 127 || value < 32 {
					panic(fmt.Sprintf("Attempted to encode out of range value of '%d' to chunk data. (It might work but we need to test it)", value))
				}
				data[ox*cw*ch+oy*cw+oz] = value
			}
		}
	}
	return string(data)
}

func (c Chunk) Clone() Chunk {
	cw := coords.ChunkWidth
	ch := coords.ChunkHeight
	cd := coords.ChunkDepth

	blocks := make([][][]Block, cw)
	for ox := 0; ox < cw; ox++ {
		blocks[ox] = make([][]Block, ch)
		for oy := 0; oy < ch; oy++ {
			blocks[ox][oy] = make([]Block, cd)
			for oz := 0; oz < cd; oz++ {
				blocks[ox][oy][oz] = c[ox][oy][oz]
			}
		}
	}
	return blocks
}
<commit_msg>Sublime is stupid and not making unsaved files evident.<commit_after>package mapgen

import (
	"fmt"

	"buildblast/lib/coords"
)

type Chunk [][][]Block

func generateChunk(bg blockGenerator, cc coords.Chunk) (Chunk, []coords.World) {
	cw := coords.ChunkWidth
	ch := coords.ChunkHeight
	cd := coords.ChunkDepth

	spawns := make([]coords.World, 0)
	blocks := make([][][]Block, cw)
	for ocX := 0; ocX < cw; ocX++ {
		blocks[ocX] = make([][]Block, ch)
		for ocY := 0; ocY < ch; ocY++ {
			blocks[ocX][ocY] = make([]Block, cd)
			for ocZ := 0; ocZ < cd; ocZ++ {
				bc := coords.Block{
					X: ocX + cc.X*cw,
					Y: ocY + cc.Y*ch,
					Z: ocZ + cc.Z*cd,
				}
				block, isSpawn := bg.Block(bc)
				blocks[ocX][ocY][ocZ] = block
				if isSpawn {
					spawns = append(spawns, bc.Center())
				}
			}
		}
	}
	return blocks, spawns
}

func (c Chunk) Block(oc coords.Offset) Block {
	return c[oc.X][oc.Y][oc.Z]
}

func (c Chunk) SetBlock(oc coords.Offset, newBlock Block) {
	c[oc.X][oc.Y][oc.Z] = newBlock
}

// Flatten returns the chunk data as a string. It
// can be used for various forms of serialization
// where a valid UTF-8 string is required, and
// efficiency (in terms of size) is not hugely
// important. We use this format for chunk data
// in JSON documents because:
//  a) It's smaller, at least half the size, of a
//     normal JSON array, potentially more.
//     (depending on the contents)
//  b) It's much faster - the go JSON implementation
//     isn't particulilly fast at serializing large
//     arrays of numbers.
func (c Chunk) Flatten() string {
	cw := coords.ChunkWidth
	ch := coords.ChunkHeight
	cd := coords.ChunkDepth
	data := make([]byte, cw*ch*cd)
	for ocX := 0; ocX < cw; ocX++ {
		for ocY := 0; ocY < ch; ocY++ {
			for ocZ := 0; ocZ < cd; ocZ++ {
				// 32: Space charater. Control charaters
				// are not allowed in JSON strings.
				value := byte(c[ocX][ocY][ocZ] + 32)
				if value >= 127 || value < 32 {
					panic(fmt.Sprintf("Attempted to encode out of range value of '%d' to chunk data. (It might work but we need to test it)", value))
				}
				data[ocX*cw*ch+ocY*cw+ocZ] = value
			}
		}
	}
	return string(data)
}

func (c Chunk) Clone() Chunk {
	cw := coords.ChunkWidth
	ch := coords.ChunkHeight
	cd := coords.ChunkDepth

	blocks := make([][][]Block, cw)
	for ocX := 0; ocX < cw; ocX++ {
		blocks[ocX] = make([][]Block, ch)
		for ocY := 0; ocY < ch; ocY++ {
			blocks[ocX][ocY] = make([]Block, cd)
			for ocZ := 0; ocZ < cd; ocZ++ {
				blocks[ocX][ocY][ocZ] = c[ocX][ocY][ocZ]
			}
		}
	}
	return blocks
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "math/rand"
    "time"
    "strconv"
    "runtime"
    "sync"
    "log"

    "github.com/fatih/color"
)
var wg sync.WaitGroup

var re *color.Color = color.New(color.FgRed)
var red *color.Color = re.Add(color.BgBlack)
var boldRed *color.Color = red.Add(color.Bold)

func main() {

  runtime.GOMAXPROCS(2)
  wg.Add(1)

  var mode, numTables, total, tries int = 0, 0, 0, 0
  var avg float32 = 0
  red.Println("\n\tGame modes:\n1: Pick a random table\n2: Find the avg tries it takes to solve a puzzle\n\nPlease choose 1 or 2: ")
  _, err := fmt.Scanf("%d", &mode)
  if err != nil {
    log.Fatal(err)
  }

  if mode == 1 {
    picker()
  } else if mode == 2 {
    red.Print("How many tables would you like to calculate for: ")
    _, err2 := fmt.Scanf("%d", &numTables)
    if err2 != nil {
      log.Fatal(err2)
    }
    red.Print("How many times would you like to run the experiment: ")
    _, err3 := fmt.Scanf("%d", &tries)
    if err3 != nil {
      log.Fatal(err3)
    }
    fmt.Println("\n")
    go calc()
    go printAvg(numTables, total, avg, tries)
    wg.Wait()
  } else {
    red.Println("Goodbye!")
  }
}

func printAvg(numTables int, total int, avg float32, tries int) {
  defer wg.Done()
  for i := 1; i <= tries; i++ {
    total += avgFinder(numTables)
  }
  //a := color.New(color.FgRed, color.Bold)
  avg = float32(total)/float32(tries)
  boldRed.Printf("\rThe avg tries it took to solve %v tables was %v                   \n\n", numTables, avg)
}

func calc() {
  for {
    for i := "Calculating";; i += "." {
      red.Printf("\r%s", i)
      time.Sleep(500 * time.Millisecond)
    }
  }
}

func printWelcome() {
  welcome := []string{"\nWelcome to Colin's Random Table Picker", "Tables are eliminated when number is drawn,", "And put back in the game when their number is drawn again.", "Last number left wins!", "Good Luck.\n"}
  for _, item := range welcome {
    red.Println(item)
    time.Sleep(400 * time.Millisecond)
  }
}

func picker() {
  var tables, comp, wait int
  var compString string
  printWelcome()
  for {
    red.Print("How many tables are playing: ")
    _, err := fmt.Scanf("%d", &tables)
    if err != nil {
      log.Fatal(err)
    }
    red.Print("How many ms would you like to wait between each iteration: ")
    _, err2 := fmt.Scanf("%d", &wait)
    if err2 != nil {
      log.Fatal(err2)
    }
    if tables > 0 {
      break
    }
  }
  tableArray := make([]string, tables)
  for i, _ := range tableArray {
    tableArray[i] = strconv.Itoa(i + 1)
  }
  var total int = 0
  for {
    r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    comp = r.Intn(tables) + 1;
    compString = strconv.Itoa(comp)
    var numX int = 0
    total++
    red.Println("Random number is:", compString)
    if tableArray[comp - 1] == compString {
      tableArray[comp - 1] = "x"
    } else {
      tableArray[comp - 1] = compString
    }
    red.Println(tableArray)
    for _, element := range tableArray {
      if element == "x" {
        numX++
      }
    }
    if numX >= tables - 1 {
      for _, element := range tableArray {
        if element != "x" {
          boldRed.Println("The lucky winner is", element, "chosen after", total, "rounds.\n")
          break
        }
      }
      break
    }
    time.Sleep(time.Duration(wait) * time.Millisecond)
  }
}

func avgFinder(tables int) int{
  var comp, total int = 0, 0
  var compString string
  tableArray := make([]string, tables)
  for i, _ := range tableArray {
    tableArray[i] = strconv.Itoa(i + 1)
  }
  for {
    r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    comp = r.Intn(tables) + 1;
    compString = strconv.Itoa(comp)
    var numX int = 0
    total++
    if tableArray[comp - 1] == compString {
      tableArray[comp - 1] = "x"
    } else {
      tableArray[comp - 1] = compString
    }
    for _, element := range tableArray {
      if element == "x" {
        numX++
      }
    }
    if numX >= tables - 1 {
      break
    }
  }
  return total
}

/*
AVERAGES:
1:  0
2:  1
3:  2
4:  6
5:  11
6:  19
7:  34
8:  57
9:  95
10: 164
11: 283
12: 494
*/
<commit_msg>better error checking, using scanln instead of scanf<commit_after>package main

import (
    "fmt"
    "math/rand"
    "time"
    "strconv"
    "runtime"
    "sync"
    "log"

    "github.com/fatih/color"
)
var wg sync.WaitGroup

var re *color.Color = color.New(color.FgRed)
var red *color.Color = re.Add(color.BgBlack)
var boldRed *color.Color = red.Add(color.Bold)

func main() {

  runtime.GOMAXPROCS(2)
  wg.Add(1)

  var mode, numTables, total, tries int = 0, 0, 0, 0
  var avg float32 = 0
  red.Println("\n\tGame modes:\n1: Pick a random table\n2: Find the avg tries it takes to solve a puzzle\n\nPlease choose 1 or 2: ")
  if _, err := fmt.Scanln(&mode); err != nil {
    log.Fatal(err)
  }

  if mode == 1 {
    picker()
  } else if mode == 2 {
    red.Print("How many tables would you like to calculate for: ")
    if _, err := fmt.Scanln(&numTables); err != nil {
      log.Fatal(err)
    }
    red.Print("How many times would you like to run the experiment: ")
    if _, err := fmt.Scanln(&tries); err != nil {
      log.Fatal(err)
    }
    fmt.Println("\n")
    go calc()
    go printAvg(numTables, total, avg, tries)
    wg.Wait()
  } else {
    red.Println("Goodbye!")
  }
}

func printAvg(numTables int, total int, avg float32, tries int) {
  defer wg.Done()
  for i := 1; i <= tries; i++ {
    total += avgFinder(numTables)
  }
  //a := color.New(color.FgRed, color.Bold)
  avg = float32(total)/float32(tries)
  boldRed.Printf("\rThe avg tries it took to solve %v tables was %v                   \n\n", numTables, avg)
}

func calc() {
  for {
    for i := "Calculating";; i += "." {
      red.Printf("\r%s", i)
      time.Sleep(500 * time.Millisecond)
    }
  }
}

func printWelcome() {
  welcome := []string{"\nWelcome to Colin's Random Table Picker", "Tables are eliminated when number is drawn,", "And put back in the game when their number is drawn again.", "Last number left wins!", "Good Luck.\n"}
  for _, item := range welcome {
    red.Println(item)
    time.Sleep(400 * time.Millisecond)
  }
}

func picker() {
  var tables, comp, wait int
  var compString string
  printWelcome()
  for {
    red.Print("How many tables are playing: ")
    if _, err := fmt.Scanln(&tables); err != nil {
      log.Fatal(err)
    }
    red.Print("How many ms would you like to wait between each iteration: ")
    if _, err := fmt.Scanln(&wait); err != nil {
      log.Fatal(err)
    }
    if tables > 0 {
      break
    }
  }
  tableArray := make([]string, tables)
  for i, _ := range tableArray {
    tableArray[i] = strconv.Itoa(i + 1)
  }
  var total int = 0
  for {
    r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    comp = r.Intn(tables) + 1;
    compString = strconv.Itoa(comp)
    var numX int = 0
    total++
    red.Println("Random number is:", compString)
    if tableArray[comp - 1] == compString {
      tableArray[comp - 1] = "x"
    } else {
      tableArray[comp - 1] = compString
    }
    red.Println(tableArray)
    for _, element := range tableArray {
      if element == "x" {
        numX++
      }
    }
    if numX >= tables - 1 {
      for _, element := range tableArray {
        if element != "x" {
          boldRed.Println("The lucky winner is", element, "chosen after", total, "rounds.\n")
          break
        }
      }
      break
    }
    time.Sleep(time.Duration(wait) * time.Millisecond)
  }
}

func avgFinder(tables int) int{
  var comp, total int = 0, 0
  var compString string
  tableArray := make([]string, tables)
  for i, _ := range tableArray {
    tableArray[i] = strconv.Itoa(i + 1)
  }
  for {
    r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    comp = r.Intn(tables) + 1;
    compString = strconv.Itoa(comp)
    var numX int = 0
    total++
    if tableArray[comp - 1] == compString {
      tableArray[comp - 1] = "x"
    } else {
      tableArray[comp - 1] = compString
    }
    for _, element := range tableArray {
      if element == "x" {
        numX++
      }
    }
    if numX >= tables - 1 {
      break
    }
  }
  return total
}

/*
AVERAGES:
1:  0
2:  1
3:  2
4:  6
5:  11
6:  19
7:  34
8:  57
9:  95
10: 164
11: 283
12: 494
*/
<|endoftext|>