<commit_before>package dawg

// status ok
import (
	"encoding/binary"
	"unicode/utf8"

	"github.com/ReanGD/go-morphy/std"
)

// RecordDAWG ...
type RecordDAWG struct {
	BytesDAWG
	// "HH" == 2, "3H" == 3
	fmt uint8
	// ">" - binary.BigEndian, "<" - binary.LittleEndian (default)
	order binary.ByteOrder
}

// Get - Returns a list of payloads (as uint16 objects) for a given key
func (d *RecordDAWG) Get(key string) ([][]uint16, bool) {
	index, ok := d.followKey([]byte(key))
	if !ok {
		return [][]uint16{}, false
	}
	res := d.valueForIndex(index)

	return res, len(res) != 0
}

func (d *RecordDAWG) bytesToUints16(src []byte) []uint16 {
	if len(src) != int(d.fmt)*2 {
		panic("source len error")
	}
	res := make([]uint16, d.fmt)
	for i := range res {
		res[i] = d.order.Uint16(src[2*i:])
	}
	return res
}

func (d *RecordDAWG) valueForIndex(index uint32) [][]uint16 {
	value := d.BytesDAWG.valueForIndex(index)
	res := make([][]uint16, len(value))
	for i, val := range value {
		res[i] = d.bytesToUints16(val)
	}

	return res
}

// Items ...
func (d *RecordDAWG) Items(prefix string) []std.StrUints16 {
	items := d.BytesDAWG.Items(prefix)
	res := make([]std.StrUints16, len(items))
	for i, item := range items {
		res[i].Key = item.Key
		res[i].Value = d.bytesToUints16(item.Value)
	}

	return res
}

func (d *RecordDAWG) similarItems(currentPrefix string, key string, index uint32,
	replaceChars map[rune]rune) []std.StrUints16Arr {

	res := []std.StrUints16Arr{}
	exitByBreak := false
	startPos := len(currentPrefix)

	for curPos, bStep := range key[startPos:] {
		ReplaceChar, ok := replaceChars[bStep]

		if ok {
			nextIndex, ok := d.dct.followBytes([]byte(string(ReplaceChar)), index)
			if ok {
				prefix := currentPrefix + key[startPos:curPos] + string(ReplaceChar)
				extraItems := d.similarItems(prefix, key, nextIndex, replaceChars)
				res = append(res, extraItems...)
			}
		}

		index, ok = d.dct.followBytes([]byte(string(bStep)), index)
		if !ok {
			exitByBreak = true
			break
		}
	}

	if !exitByBreak {
		index, ok := d.dct.followChar(constPayloadSeparatorUint, index)
		if ok {
			foundKey := currentPrefix + key[startPos:]
			value := d.valueForIndex(index)
			item := std.StrUints16Arr{foundKey, value}
			res = append([]std.StrUints16Arr{item}, res...)
		}
	}

	return res
}

// SimilarItems -
// Returns a list of (key, value) tuples for all variants of 'key'
// in this DAWG according to 'replaces'.
func (d *RecordDAWG) SimilarItems(key string, replaceChars map[rune]rune) []std.StrUints16Arr {
	return d.similarItems("", key, constRoot, replaceChars)
}

func (d *RecordDAWG) similarItemsValues(startPos int, key string, index uint32, replaceChars map[rune]rune) [][][]uint16 {
	res := [][][]uint16{}
	exitByBreak := false

	for curPos, bStep := range key[startPos:] {
		ReplaceChar, ok := replaceChars[bStep]

		if ok {
			nextIndex, ok := d.dct.followBytes([]byte(string(ReplaceChar)), index)
			if ok {
				extraItems := d.similarItemsValues(curPos+utf8.RuneLen(bStep), key, nextIndex, replaceChars)
				res = append(res, extraItems...)
			}
		}

		index, ok = d.dct.followBytes([]byte(string(bStep)), index)
		if !ok {
			exitByBreak = true
			break
		}
	}

	if !exitByBreak {
		index, ok := d.dct.followChar(constPayloadSeparatorUint, index)
		if ok {
			value := d.valueForIndex(index)
			res = append([][][]uint16{value}, res...)
		}
	}

	return res
}

// SimilarItemsValues -
// Returns a list of values tuples for all variants of 'key'
// in this DAWG according to 'replaces'.
func (d *RecordDAWG) SimilarItemsValues(key string, replaceChars map[rune]rune) [][][]uint16 {
	return d.similarItemsValues(0, key, constRoot, replaceChars)
}

func (d *RecordDAWG) initRecordDAWG(fmt uint8, order binary.ByteOrder) {
	d.initBytesDAWG()
	d.fmt = fmt
	d.order = order
}

// NewRecordDAWG - constructor for RecordDAWG
func NewRecordDAWG(fmt uint8, order binary.ByteOrder) *RecordDAWG {
	dawg := &RecordDAWG{}
	dawg.vDAWG = dawg
	dawg.initRecordDAWG(fmt, order)

	return dawg
}
<commit_msg>fix warning<commit_after>package dawg

// status ok
import (
	"encoding/binary"
	"unicode/utf8"

	"github.com/ReanGD/go-morphy/std"
)

// RecordDAWG ...
type RecordDAWG struct {
	BytesDAWG
	// "HH" == 2, "3H" == 3
	fmt uint8
	// ">" - binary.BigEndian, "<" - binary.LittleEndian (default)
	order binary.ByteOrder
}

// Get - Returns a list of payloads (as uint16 objects) for a given key
func (d *RecordDAWG) Get(key string) ([][]uint16, bool) {
	index, ok := d.followKey([]byte(key))
	if !ok {
		return [][]uint16{}, false
	}
	res := d.valueForIndex(index)

	return res, len(res) != 0
}

func (d *RecordDAWG) bytesToUints16(src []byte) []uint16 {
	if len(src) != int(d.fmt)*2 {
		panic("source len error")
	}
	res := make([]uint16, d.fmt)
	for i := range res {
		res[i] = d.order.Uint16(src[2*i:])
	}
	return res
}

func (d *RecordDAWG) valueForIndex(index uint32) [][]uint16 {
	value := d.BytesDAWG.valueForIndex(index)
	res := make([][]uint16, len(value))
	for i, val := range value {
		res[i] = d.bytesToUints16(val)
	}

	return res
}

// Items ...
func (d *RecordDAWG) Items(prefix string) []std.StrUints16 {
	items := d.BytesDAWG.Items(prefix)
	res := make([]std.StrUints16, len(items))
	for i, item := range items {
		res[i].Key = item.Key
		res[i].Value = d.bytesToUints16(item.Value)
	}

	return res
}

func (d *RecordDAWG) similarItems(currentPrefix string, key string, index uint32,
	replaceChars map[rune]rune) []std.StrUints16Arr {

	res := []std.StrUints16Arr{}
	exitByBreak := false
	startPos := len(currentPrefix)

	for curPos, bStep := range key[startPos:] {
		ReplaceChar, ok := replaceChars[bStep]

		if ok {
			nextIndex, ok := d.dct.followBytes([]byte(string(ReplaceChar)), index)
			if ok {
				prefix := currentPrefix + key[startPos:curPos] + string(ReplaceChar)
				extraItems := d.similarItems(prefix, key, nextIndex, replaceChars)
				res = append(res, extraItems...)
			}
		}

		index, ok = d.dct.followBytes([]byte(string(bStep)), index)
		if !ok {
			exitByBreak = true
			break
		}
	}

	if !exitByBreak {
		index, ok := d.dct.followChar(constPayloadSeparatorUint, index)
		if ok {
			foundKey := currentPrefix + key[startPos:]
			value := d.valueForIndex(index)
			item := std.StrUints16Arr{Key: foundKey, Value: value}
			res = append([]std.StrUints16Arr{item}, res...)
		}
	}

	return res
}

// SimilarItems -
// Returns a list of (key, value) tuples for all variants of 'key'
// in this DAWG according to 'replaces'.
func (d *RecordDAWG) SimilarItems(key string, replaceChars map[rune]rune) []std.StrUints16Arr {
	return d.similarItems("", key, constRoot, replaceChars)
}

func (d *RecordDAWG) similarItemsValues(startPos int, key string, index uint32, replaceChars map[rune]rune) [][][]uint16 {
	res := [][][]uint16{}

	for curPos, bStep := range key[startPos:] {
		ReplaceChar, ok := replaceChars[bStep]

		if ok {
			nextIndex, ok := d.dct.followBytes([]byte(string(ReplaceChar)), index)
			if ok {
				extraItems := d.similarItemsValues(curPos+utf8.RuneLen(bStep), key, nextIndex, replaceChars)
				res = append(res, extraItems...)
			}
		}

		index, ok = d.dct.followBytes([]byte(string(bStep)), index)
		if !ok {
			return res
		}
	}

	index, ok := d.dct.followChar(constPayloadSeparatorUint, index)
	if ok {
		value := d.valueForIndex(index)
		res = append([][][]uint16{value}, res...)
	}

	return res
}

// SimilarItemsValues -
// Returns a list of values tuples for all variants of 'key'
// in this DAWG according to 'replaces'.
func (d *RecordDAWG) SimilarItemsValues(key string, replaceChars map[rune]rune) [][][]uint16 {
	return d.similarItemsValues(0, key, constRoot, replaceChars)
}

func (d *RecordDAWG) initRecordDAWG(fmt uint8, order binary.ByteOrder) {
	d.initBytesDAWG()
	d.fmt = fmt
	d.order = order
}

// NewRecordDAWG - constructor for RecordDAWG
func NewRecordDAWG(fmt uint8, order binary.ByteOrder) *RecordDAWG {
	dawg := &RecordDAWG{}
	dawg.vDAWG = dawg
	dawg.initRecordDAWG(fmt, order)

	return dawg
}
<|endoftext|>


<commit_before>package neo4j

// Now this is for Neo4J.
var queries = map[string]string{
	// Add Version:
	"releaseVersion": `
		CREATE (v:Version { version: {version}, forced: {forced} })
		RETURN v;
	`,
	// Check Version:
	"getAllVersion": `
		MATCH (ver:Version)
		WHERE ver.forced = TRUE
		RETURN ver ORDER BY ver.version DESC;
	`,
	"getAllForcedVersion": `
		MATCH (v:Version)
		RETURN v ORDER BY v.version DESC;
	`,
	// SMS Verification:
	"mergeVerificationRequest": `
		MERGE (vr:Verification { phoneNumber: {phoneNumber} })
		SET
			vr.code        = {code},
			vr.token       = {token},
			vr.verified    = FALSE,
			vr.ttl         = timestamp();
	`,
	"verifyRequest": `
		MATCH (vr:Verification)
		WHERE vr.phoneNumber = {phoneNumber} AND vr.code = {code}
		SET vr.verified = TRUE
		RETURN vr.token;
	`,
	"isVerified": `
		MATCH (vr:Verification)
		WHERE vr.token = {token}
		RETURN vr.verified, vr.phoneNumber;
	`,
	// Auth:
	"touchDevice": `
		MERGE (device:Device { uid: {uid} })
		ON CREATE SET
			device.deviceToken = {maybeDeviceToken},
			device.uid = {uid},
			device.name = {name},
			device.platform = {platform},
			device.capacity = {capacity},
			device.os_type = {os_type},
			device.os_version = {os_version}
		WITH device
		OPTIONAL MATCH (device)-[:SIGNED]->(user:User)
		RETURN device.deviceToken, user IS NOT NULL AS signedIn, user.username;
	`,
	"ensureDeviceToken": `
		MATCH (d:Device)
		WHERE d.deviceToken = {deviceToken}
		RETURN d.deviceToken;
	`,
	"whoAmI": `
		MATCH (d:Device)-[:SIGNED]-(u:User)
		WHERE d.deviceToken = {deviceToken}
		RETURN u.username;
	`,
	"isUniquePhoneNumber": `
		MATCH (u:User)
		WHERE u.phoneNumber = {phoneNumber}
		RETURN u;
	`,
	"isUniqueUsername": `
		MATCH (u:User)
		WHERE u.username = {username}
		RETURN u;
	`,
	"signDeviceIn": `
		MATCH (d:Device) WHERE d.deviceToken = {deviceToken}
		WITH d
		MATCH (u:User) WHERE u.username = {username} AND u.password = {password}
		MERGE (d)-[:SIGNED]->(u)
		RETURN d, u;
	`,
	"signDeviceOut": `
		MATCH (d:Device)
		WITH d
		OPTIONAL MATCH (d)-[s:SIGNED]-(:User)
		WHERE d.deviceToken = {deviceToken}
		DELETE s
		RETURN d;
	`,
	"signUp": `
		MATCH (d:Device) WHERE d.deviceToken = {deviceToken}
		CREATE (d)-[:SIGNED]->(u:User)-[:BIND]->(p:Profile)
		SET
			u.username = {username},
			u.password = {password},
			u.phoneNumber = {phoneNumber},

			p.fullName   = "",
			p.bio        = "",
			p.location   = "",
			p.followedBy = 0,
			p.follows    = 0

		RETURN d;
		`,
	// Profile:
	"getProfile": `
		MATCH (u:User)-[:BIND]-(p:Profile)
		WHERE u.username = {username}
		RETURN p;
	`,
	"updateProfile": `
		MATCH (u:User) WHERE u.username = {username}
		MERGE (u)-[:BIND]-(p:Profile)
			ON CREATE SET
				u.fullName   = "",
				u.bio        = "",
				u.location   = "",
				u.followedBy = 0,
				u.follows    = 0
		SET p += {change}
		RETURN u;
	`,
	"isFollowedBy": `
		OPTIONAL MATCH (u1:User)-[:BIND]-(p1:Profile)-[f:FOLLOW]->(p2:Profile)-[:BIND]-(u2:User)
		WHERE u1.username = {username1} AND u2.username = {username2}
		RETURN f IS NOT NULL;
	`,
	"follow": `
		MATCH (u1:User)-[:BIND]-(p1:Profile), (u2:User)-[:BIND]-(p2:Profile)
		WHERE u1.username = {username1} AND u2.username = {username2}
		MERGE (p1)-[f:FOLLOW]->(p2)
			ON CREATE SET
				f.created_at  = timestamp(),
				p1.follows    = p1.follows + 1,
				p2.followedBy = p1.followedBy + 1

		RETURN TRUE;
	`,
	"unfollow": `
		MATCH (u1:User)-[:BIND]->(p1:Profile)-[f:FOLLOW]->(p2:Profile)<-[:BIND]-(u2:User)
		WHERE u1.username = {username1} AND u2.username = {username2}
		DELETE f
		WITH p1, p2
		SET
			p1.follows    = p1.follows - 1,
			p2.followedBy = p1.followedBy - 1
		RETURN TRUE;
	`,
	"post": `
		MATCH (u:User)-[:BIND]-(p:Profile) WHERE u.username = {username}
		OPTIONAL MATCH (p)-[r:Post]-(secondlatestupdate)
		DELETE r
		CREATE (p)-[:Post]->(lu:Post)
		WITH lu, secondlatestupdate
		SET
			lu.artID = {artID},
			lu.title = {title},
			lu.desc  = {desc},
			lu.likes_count = 0,
			lu.comments_count = 0,
			lu.tags = {tags},
			lu.date = timestamp(),
			lu.displaySource = {displaySource}
		WITH lu, collect(secondlatestupdate) AS seconds
		FOREACH (x IN seconds | CREATE (lu)-[:Next]->(x))
		RETURN lu;
	`,
	"like": `
		MATCH (u:User)-[:BIND]-(profile:Profile) WHERE u.username = {username}
		WITH profile
		MATCH (post:Post) WHERE post.artID = {artID}
		MERGE (profile)-[r1:OWN]->(like:Like)-[r2:THAT]->(post)
			ON CREATE SET
				post.like_count = post.like_count + 1,
				r1.created_at = timestamp(),
				r2.created_at = timestamp(),
				like.flag = 1 // for if like statement

		WITH post, profile, like

		MATCH (like { flag: 1 })
		OPTIONAL MATCH (post)-[r:LIKED_BY]-(secondLatestUpdate)
		DELETE r
		CREATE (post)-[:LIKED_BY]->(like)
		WITH like, collect(secondLatestUpdate) AS seconds
		FOREACH (x IN seconds | CREATE (like)-[:NEXT]->(x))
		REMOVE like.flag
		RETURN like
	`,
	"getPosts": `
		MATCH (u:User)-[:BIND]-(p:Profile) WHERE u.username = {username}
		WITH p
		MATCH (p)-[:Post]-(start)-[:Next*0..]-(post) WHERE post.date < {cursur}
		RETURN post ORDER BY p.date DESC LIMIT {count};
	`,
	// WARNING: max depth is 20
	"getTimeline": `
		MATCH (u:User)-[:BIND]-(p:Profile) WHERE u.username = {username}
		WITH p
		MATCH (p)-[:FOLLOW*0..1]->(f:Profile)
		WITH f
		MATCH (f)-[:Post]-(start)-[:Next*0..20]-(post) WHERE post.date < {cursur}
		RETURN post ORDER BY post.date DESC LIMIT {count};
	`,
}

func (db *neo4jDB) GetQuery(key string) interface{} {
	if value, ok := queries[key]; ok {
		return value
	}
	panic("query " + key + " does not exist")
}
<commit_msg>Remove ported queries.<commit_after>package neo4j

// Now this is for Neo4J.
var queries = map[string]string{
	// Auth:
	"touchDevice": `
		MERGE (device:Device { uid: {uid} })
		ON CREATE SET
			device.deviceToken = {maybeDeviceToken},
			device.uid = {uid},
			device.name = {name},
			device.platform = {platform},
			device.capacity = {capacity},
			device.os_type = {os_type},
			device.os_version = {os_version}
		WITH device
		OPTIONAL MATCH (device)-[:SIGNED]->(user:User)
		RETURN device.deviceToken, user IS NOT NULL AS signedIn, user.username;
	`,
	"ensureDeviceToken": `
		MATCH (d:Device)
		WHERE d.deviceToken = {deviceToken}
		RETURN d.deviceToken;
	`,
	"whoAmI": `
		MATCH (d:Device)-[:SIGNED]-(u:User)
		WHERE d.deviceToken = {deviceToken}
		RETURN u.username;
	`,
	"isUniquePhoneNumber": `
		MATCH (u:User)
		WHERE u.phoneNumber = {phoneNumber}
		RETURN u;
	`,
	"isUniqueUsername": `
		MATCH (u:User)
		WHERE u.username = {username}
		RETURN u;
	`,
	"signDeviceIn": `
		MATCH (d:Device) WHERE d.deviceToken = {deviceToken}
		WITH d
		MATCH (u:User) WHERE u.username = {username} AND u.password = {password}
		MERGE (d)-[:SIGNED]->(u)
		RETURN d, u;
	`,
	"signDeviceOut": `
		MATCH (d:Device)
		WITH d
		OPTIONAL MATCH (d)-[s:SIGNED]-(:User)
		WHERE d.deviceToken = {deviceToken}
		DELETE s
		RETURN d;
	`,
	"signUp": `
		MATCH (d:Device) WHERE d.deviceToken = {deviceToken}
		CREATE (d)-[:SIGNED]->(u:User)-[:BIND]->(p:Profile)
		SET
			u.username = {username},
			u.password = {password},
			u.phoneNumber = {phoneNumber},

			p.fullName   = "",
			p.bio        = "",
			p.location   = "",
			p.followedBy = 0,
			p.follows    = 0

		RETURN d;
		`,
	// Profile:
	"getProfile": `
		MATCH (u:User)-[:BIND]-(p:Profile)
		WHERE u.username = {username}
		RETURN p;
	`,
	"updateProfile": `
		MATCH (u:User) WHERE u.username = {username}
		MERGE (u)-[:BIND]-(p:Profile)
			ON CREATE SET
				u.fullName   = "",
				u.bio        = "",
				u.location   = "",
				u.followedBy = 0,
				u.follows    = 0
		SET p += {change}
		RETURN u;
	`,
	"isFollowedBy": `
		OPTIONAL MATCH (u1:User)-[:BIND]-(p1:Profile)-[f:FOLLOW]->(p2:Profile)-[:BIND]-(u2:User)
		WHERE u1.username = {username1} AND u2.username = {username2}
		RETURN f IS NOT NULL;
	`,
	"follow": `
		MATCH (u1:User)-[:BIND]-(p1:Profile), (u2:User)-[:BIND]-(p2:Profile)
		WHERE u1.username = {username1} AND u2.username = {username2}
		MERGE (p1)-[f:FOLLOW]->(p2)
			ON CREATE SET
				f.created_at  = timestamp(),
				p1.follows    = p1.follows + 1,
				p2.followedBy = p1.followedBy + 1

		RETURN TRUE;
	`,
	"unfollow": `
		MATCH (u1:User)-[:BIND]->(p1:Profile)-[f:FOLLOW]->(p2:Profile)<-[:BIND]-(u2:User)
		WHERE u1.username = {username1} AND u2.username = {username2}
		DELETE f
		WITH p1, p2
		SET
			p1.follows    = p1.follows - 1,
			p2.followedBy = p1.followedBy - 1
		RETURN TRUE;
	`,
	"post": `
		MATCH (u:User)-[:BIND]-(p:Profile) WHERE u.username = {username}
		OPTIONAL MATCH (p)-[r:Post]-(secondlatestupdate)
		DELETE r
		CREATE (p)-[:Post]->(lu:Post)
		WITH lu, secondlatestupdate
		SET
			lu.artID = {artID},
			lu.title = {title},
			lu.desc  = {desc},
			lu.likes_count = 0,
			lu.comments_count = 0,
			lu.tags = {tags},
			lu.date = timestamp(),
			lu.displaySource = {displaySource}
		WITH lu, collect(secondlatestupdate) AS seconds
		FOREACH (x IN seconds | CREATE (lu)-[:Next]->(x))
		RETURN lu;
	`,
	"like": `
		MATCH (u:User)-[:BIND]-(profile:Profile) WHERE u.username = {username}
		WITH profile
		MATCH (post:Post) WHERE post.artID = {artID}
		MERGE (profile)-[r1:OWN]->(like:Like)-[r2:THAT]->(post)
			ON CREATE SET
				post.like_count = post.like_count + 1,
				r1.created_at = timestamp(),
				r2.created_at = timestamp(),
				like.flag = 1 // for if like statement

		WITH post, profile, like

		MATCH (like { flag: 1 })
		OPTIONAL MATCH (post)-[r:LIKED_BY]-(secondLatestUpdate)
		DELETE r
		CREATE (post)-[:LIKED_BY]->(like)
		WITH like, collect(secondLatestUpdate) AS seconds
		FOREACH (x IN seconds | CREATE (like)-[:NEXT]->(x))
		REMOVE like.flag
		RETURN like
	`,
	"getPosts": `
		MATCH (u:User)-[:BIND]-(p:Profile) WHERE u.username = {username}
		WITH p
		MATCH (p)-[:Post]-(start)-[:Next*0..]-(post) WHERE post.date < {cursur}
		RETURN post ORDER BY p.date DESC LIMIT {count};
	`,
	// WARNING: max depth is 20
	"getTimeline": `
		MATCH (u:User)-[:BIND]-(p:Profile) WHERE u.username = {username}
		WITH p
		MATCH (p)-[:FOLLOW*0..1]->(f:Profile)
		WITH f
		MATCH (f)-[:Post]-(start)-[:Next*0..20]-(post) WHERE post.date < {cursur}
		RETURN post ORDER BY post.date DESC LIMIT {count};
	`,
}

func (db *neo4jDB) GetQuery(key string) interface{} {
	if value, ok := queries[key]; ok {
		return value
	}
	panic("query " + key + " does not exist")
}
<|endoftext|>


<commit_before>// Copyright 2014 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 dwarf

// This file implemetns the mapping from PC to lines.
// TODO: Also map from line to PC.
// TODO: Find a way to test this properly.

// http://www.dwarfstd.org/doc/DWARF4.pdf Section 6.2 page 108

import (
	"fmt"
)

// PCToLine returns the file and line number corresponding to the PC value.
// If a correspondence cannot be found, ok will be false.
// TODO: Return a function descriptor as well.
func (d *Data) PCToLine(pc uint64) (file string, line int, err error) {
	if len(d.line) == 0 {
		return "", 0, fmt.Errorf("PCToLine: no line table")
	}
	var m lineMachine
	// Assume the first info unit is the same as us. Extremely likely. TODO?
	if len(d.unit) == 0 {
		return "", 0, fmt.Errorf("no info section")
	}
	buf := makeBuf(d, &d.unit[0], "line", 0, d.line)
	for len(buf.data) > 0 {
		var found bool
		found, err = m.evalCompilationUnit(&buf, pc)
		if err != nil {
			return "", 0, err
		}
		if found {
			return m.prologue.file[m.file].name, int(m.line), nil
		}
	}
	return "", 0, fmt.Errorf("no source line defined for PC %#x", pc)
}

// Standard opcodes. Figure 37, page 178.
// If an opcode >= lineMachine.prologue.opcodeBase, it is a special
// opcode rather than the opcode defined in this table.
const (
	lineStdCopy             = 0x01
	lineStdAdvancePC        = 0x02
	lineStdAdvanceLine      = 0x03
	lineStdSetFile          = 0x04
	lineStdSetColumn        = 0x05
	lineStdNegateStmt       = 0x06
	lineStdSetBasicBlock    = 0x07
	lineStdConstAddPC       = 0x08
	lineStdFixedAdvancePC   = 0x09
	lineStdSetPrologueEnd   = 0x0a
	lineStdSetEpilogueBegin = 0x0b
	lineStdSetISA           = 0x0c
)

// Extended opcodes. Figure 38, page 179.
const (
	lineStartExtendedOpcode = 0x00 // Not defined as a named constant in the spec.
	lineExtEndSequence      = 0x01
	lineExtSetAddress       = 0x02
	lineExtDefineFile       = 0x03
	lineExtSetDiscriminator = 0x04 // New in version 4.
	lineExtLoUser           = 0x80
	lineExtHiUser           = 0xff
)

// linePrologue holds the information stored in the prologue of the line
// table for a single compilation unit. Also called the header.
// Section 6.2.4, page 112.
type linePrologue struct {
	unitLength           int
	version              int
	headerLength         int
	minInstructionLength int
	maxOpsPerInstruction int
	defaultIsStmt        bool
	lineBase             int
	lineRange            int
	opcodeBase           byte
	stdOpcodeLengths     []byte
	include              []string   // entry 0 is empty; means current directory
	file                 []lineFile // entry 0 is empty.
}

// lineFile represents a file name stored in the PC/line table, usually the prologue.
type lineFile struct {
	name   string
	index  int // index into include directories
	time   int // implementation-defined time of last modification
	length int // length in bytes, 0 if not available.
}

// lineMachine holds the registers evaluated during executing of the PC/line mapping engine.
// Section 6.2.2, page 109.
type lineMachine struct {
	// The program-counter value corresponding to a machine instruction generated by the compiler.
	address uint64

	// An unsigned integer representing the index of an operation within a VLIW
	// instruction. The index of the first operation is 0. For non-VLIW
	// architectures, this register will always be 0.
	// The address and op_index registers, taken together, form an operation
	// pointer that can reference any individual operation with the instruction
	// stream.
	opIndex uint64

	// An unsigned integer indicating the identity of the source file corresponding to a machine instruction.
	file uint64

	// An unsigned integer indicating a source line number. Lines are numbered
	// beginning at 1. The compiler may emit the value 0 in cases where an
	// instruction cannot be attributed to any source line.
	line uint64

	// An unsigned integer indicating a column number within a source line.
	// Columns are numbered beginning at 1. The value 0 is reserved to indicate
	// that a statement begins at the “left edge” of the line.
	column uint64

	// A boolean indicating that the current instruction is a recommended
	// breakpoint location. A recommended breakpoint location is intended to
	// “represent” a line, a statement and/or a semantically distinct subpart of a
	// statement.
	isStmt bool

	// A boolean indicating that the current instruction is the beginning of a basic
	// block.
	basicBlock bool

	// A boolean indicating that the current address is that of the first byte after
	// the end of a sequence of target machine instructions. end_sequence
	// terminates a sequence of lines; therefore other information in the same
	// row is not meaningful.
	endSequence bool

	// A boolean indicating that the current address is one (of possibly many)
	// where execution should be suspended for an entry breakpoint of a
	// function.
	prologueEnd bool

	// A boolean indicating that the current address is one (of possibly many)
	// where execution should be suspended for an exit breakpoint of a function.
	epilogueBegin bool

	// An unsigned integer whose value encodes the applicable instruction set
	// architecture for the current instruction.
	// The encoding of instruction sets should be shared by all users of a given
	// architecture. It is recommended that this encoding be defined by the ABI
	// authoring committee for each architecture.
	isa uint64

	// An unsigned integer identifying the block to which the current instruction
	// belongs. Discriminator values are assigned arbitrarily by the DWARF
	// producer and serve to distinguish among multiple blocks that may all be
	// associated with the same source file, line, and column. Where only one
	// block exists for a given source position, the discriminator value should be
	// zero.
	discriminator uint64

	// The prologue for the current compilation unit.
	// Not an actual register, but stored here for cleanlineness.
	prologue linePrologue
}

// parseLinePrologue parses the prologue/header describing the compilation
// unit in the line table starting at the specified offset.
func (m *lineMachine) parseLinePrologue(b *buf) error {
	m.prologue = linePrologue{}
	m.prologue.unitLength = int(b.uint32()) // Note: We are assuming 32-bit DWARF format.
	if m.prologue.unitLength > len(b.data) {
		return fmt.Errorf("DWARF: bad PC/line header length")
	}
	m.prologue.version = int(b.uint16())
	m.prologue.headerLength = int(b.uint32())
	m.prologue.minInstructionLength = int(b.uint8())
	if m.prologue.version >= 4 {
		m.prologue.maxOpsPerInstruction = int(b.uint8())
	} else {
		m.prologue.maxOpsPerInstruction = 1
	}
	m.prologue.defaultIsStmt = b.uint8() != 0
	m.prologue.lineBase = int(int8(b.uint8()))
	m.prologue.lineRange = int(b.uint8())
	m.prologue.opcodeBase = b.uint8()
	m.prologue.stdOpcodeLengths = make([]byte, m.prologue.opcodeBase-1)
	copy(m.prologue.stdOpcodeLengths, b.bytes(int(m.prologue.opcodeBase-1)))
	m.prologue.include = make([]string, 1) // First entry is empty; file index entries are 1-indexed.
	// Includes
	name := make([]byte, 0, 64)
	// TODO: use b.string()
	zeroTerminatedString := func() string {
		name = name[:0]
		for {
			c := b.uint8()
			if c == 0 {
				break
			}
			name = append(name, c)
		}
		return string(name)
	}
	for {
		name := zeroTerminatedString()
		if name == "" {
			break
		}
		m.prologue.include = append(m.prologue.include, name)
	}
	// Files
	m.prologue.file = make([]lineFile, 1, 10) // entries are 1-indexed in line number program.
	for {
		name := zeroTerminatedString()
		if name == "" {
			break
		}
		index := b.uint()
		time := b.uint()
		length := b.uint()
		f := lineFile{
			name:   name,
			index:  int(index),
			time:   int(time),
			length: int(length),
		}
		m.prologue.file = append(m.prologue.file, f)
	}
	return nil
}

// Special opcodes, page 117.
func (m *lineMachine) specialOpcode(opcode byte) {
	adjustedOpcode := int(opcode - m.prologue.opcodeBase)
	advance := adjustedOpcode / m.prologue.lineRange
	delta := (int(m.opIndex) + advance) / m.prologue.maxOpsPerInstruction
	m.address += uint64(m.prologue.minInstructionLength * delta)
	m.opIndex = (m.opIndex + uint64(advance)) % uint64(m.prologue.maxOpsPerInstruction)
	lineAdvance := m.prologue.lineBase + (adjustedOpcode % m.prologue.lineRange)
	m.line += uint64(lineAdvance)
	m.basicBlock = false
	m.prologueEnd = false
	m.epilogueBegin = false
	m.discriminator = 0
}

// evalCompilationUnit reads the next compilation unit to see if it contains the PC.
// The return value reports whether the PC was found; if so, the machine's registers
// contain the relevant information.
func (m *lineMachine) evalCompilationUnit(b *buf, pc uint64) (bool, error) {
	err := m.parseLinePrologue(b)
	if err != nil {
		return false, err
	}
	m.reset()
	for len(b.data) > 0 {
		op := b.uint8()
		if op >= m.prologue.opcodeBase {
			m.specialOpcode(op)
			continue
		}
		switch op {
		case lineStartExtendedOpcode:
			if len(b.data) == 0 {
				return false, fmt.Errorf("DWARF: short extended opcode (1)")
			}
			size := b.uint()
			if uint64(len(b.data)) < size {
				return false, fmt.Errorf("DWARF: short extended opcode (2)")
			}
			op = b.uint8()
			switch op {
			case lineExtEndSequence:
				m.endSequence = true
				m.reset()
				return false, nil
			case lineExtSetAddress:
				m.address = b.addr()
				m.opIndex = 0
			case lineExtDefineFile:
				return false, fmt.Errorf("DWARF: unimplemented define_file op")
			case lineExtSetDiscriminator:
				discriminator := b.uint()
				m.line = discriminator
			default:
				return false, fmt.Errorf("DWARF: unknown extended opcode %#x", op)
			}
		case lineStdCopy:
			m.discriminator = 0
			m.basicBlock = false
			m.prologueEnd = false
			m.epilogueBegin = false
			if m.address >= pc {
				// TODO: if m.address > pc, is this one step too far?
				return true, nil
			}
		case lineStdAdvancePC:
			advance := b.uint()
			delta := (int(m.opIndex) + int(advance)) / m.prologue.maxOpsPerInstruction
			m.address += uint64(m.prologue.minInstructionLength * delta)
			m.opIndex = (m.opIndex + uint64(advance)) % uint64(m.prologue.maxOpsPerInstruction)
			m.basicBlock = false
			m.prologueEnd = false
			m.epilogueBegin = false
			m.discriminator = 0
		case lineStdAdvanceLine:
			advance := b.int()
			m.line = uint64(int64(m.line) + advance)
		case lineStdSetFile:
			index := b.uint()
			m.file = index
		case lineStdSetColumn:
			column := b.uint()
			m.column = column
		case lineStdNegateStmt:
			m.isStmt = !m.isStmt
		case lineStdSetBasicBlock:
			m.basicBlock = true
		case lineStdFixedAdvancePC:
			m.address += uint64(b.uint16())
			m.opIndex = 0
		case lineStdSetPrologueEnd:
			m.prologueEnd = true
		case lineStdSetEpilogueBegin:
			m.epilogueBegin = true
		case lineStdSetISA:
			m.isa = b.uint()
		case lineStdConstAddPC:
			// TODO: Is this right? Seems crazy - why not just use 255 as a special opcode?
			m.specialOpcode(255)
		default:
			panic("not reached")
		}
	}
	panic("not reached")
}

// reset sets the machine's registers to the initial state. Page 111.
func (m *lineMachine) reset() {
	m.address = 0
	m.opIndex = 0
	m.file = 1
	m.line = 1
	m.column = 0
	m.isStmt = m.prologue.defaultIsStmt
	m.basicBlock = false
	m.endSequence = false
	m.prologueEnd = false
	m.epilogueBegin = false
	m.isa = 0
	m.discriminator = 0
}
<commit_msg>ogle/debug/dwarf: use buf.string to read a string Removes some duplicated functionality; cleans up a TODO. No functional change.<commit_after>// Copyright 2014 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 dwarf

// This file implemetns the mapping from PC to lines.
// TODO: Also map from line to PC.
// TODO: Find a way to test this properly.

// http://www.dwarfstd.org/doc/DWARF4.pdf Section 6.2 page 108

import (
	"fmt"
)

// PCToLine returns the file and line number corresponding to the PC value.
// If a correspondence cannot be found, ok will be false.
// TODO: Return a function descriptor as well.
func (d *Data) PCToLine(pc uint64) (file string, line int, err error) {
	if len(d.line) == 0 {
		return "", 0, fmt.Errorf("PCToLine: no line table")
	}
	var m lineMachine
	// Assume the first info unit is the same as us. Extremely likely. TODO?
	if len(d.unit) == 0 {
		return "", 0, fmt.Errorf("no info section")
	}
	buf := makeBuf(d, &d.unit[0], "line", 0, d.line)
	for len(buf.data) > 0 {
		var found bool
		found, err = m.evalCompilationUnit(&buf, pc)
		if err != nil {
			return "", 0, err
		}
		if found {
			return m.prologue.file[m.file].name, int(m.line), nil
		}
	}
	return "", 0, fmt.Errorf("no source line defined for PC %#x", pc)
}

// Standard opcodes. Figure 37, page 178.
// If an opcode >= lineMachine.prologue.opcodeBase, it is a special
// opcode rather than the opcode defined in this table.
const (
	lineStdCopy             = 0x01
	lineStdAdvancePC        = 0x02
	lineStdAdvanceLine      = 0x03
	lineStdSetFile          = 0x04
	lineStdSetColumn        = 0x05
	lineStdNegateStmt       = 0x06
	lineStdSetBasicBlock    = 0x07
	lineStdConstAddPC       = 0x08
	lineStdFixedAdvancePC   = 0x09
	lineStdSetPrologueEnd   = 0x0a
	lineStdSetEpilogueBegin = 0x0b
	lineStdSetISA           = 0x0c
)

// Extended opcodes. Figure 38, page 179.
const (
	lineStartExtendedOpcode = 0x00 // Not defined as a named constant in the spec.
	lineExtEndSequence      = 0x01
	lineExtSetAddress       = 0x02
	lineExtDefineFile       = 0x03
	lineExtSetDiscriminator = 0x04 // New in version 4.
	lineExtLoUser           = 0x80
	lineExtHiUser           = 0xff
)

// linePrologue holds the information stored in the prologue of the line
// table for a single compilation unit. Also called the header.
// Section 6.2.4, page 112.
type linePrologue struct {
	unitLength           int
	version              int
	headerLength         int
	minInstructionLength int
	maxOpsPerInstruction int
	defaultIsStmt        bool
	lineBase             int
	lineRange            int
	opcodeBase           byte
	stdOpcodeLengths     []byte
	include              []string   // entry 0 is empty; means current directory
	file                 []lineFile // entry 0 is empty.
}

// lineFile represents a file name stored in the PC/line table, usually the prologue.
type lineFile struct {
	name   string
	index  int // index into include directories
	time   int // implementation-defined time of last modification
	length int // length in bytes, 0 if not available.
}

// lineMachine holds the registers evaluated during executing of the PC/line mapping engine.
// Section 6.2.2, page 109.
type lineMachine struct {
	// The program-counter value corresponding to a machine instruction generated by the compiler.
	address uint64

	// An unsigned integer representing the index of an operation within a VLIW
	// instruction. The index of the first operation is 0. For non-VLIW
	// architectures, this register will always be 0.
	// The address and op_index registers, taken together, form an operation
	// pointer that can reference any individual operation with the instruction
	// stream.
	opIndex uint64

	// An unsigned integer indicating the identity of the source file corresponding to a machine instruction.
	file uint64

	// An unsigned integer indicating a source line number. Lines are numbered
	// beginning at 1. The compiler may emit the value 0 in cases where an
	// instruction cannot be attributed to any source line.
	line uint64

	// An unsigned integer indicating a column number within a source line.
	// Columns are numbered beginning at 1. The value 0 is reserved to indicate
	// that a statement begins at the “left edge” of the line.
	column uint64

	// A boolean indicating that the current instruction is a recommended
	// breakpoint location. A recommended breakpoint location is intended to
	// “represent” a line, a statement and/or a semantically distinct subpart of a
	// statement.
	isStmt bool

	// A boolean indicating that the current instruction is the beginning of a basic
	// block.
	basicBlock bool

	// A boolean indicating that the current address is that of the first byte after
	// the end of a sequence of target machine instructions. end_sequence
	// terminates a sequence of lines; therefore other information in the same
	// row is not meaningful.
	endSequence bool

	// A boolean indicating that the current address is one (of possibly many)
	// where execution should be suspended for an entry breakpoint of a
	// function.
	prologueEnd bool

	// A boolean indicating that the current address is one (of possibly many)
	// where execution should be suspended for an exit breakpoint of a function.
	epilogueBegin bool

	// An unsigned integer whose value encodes the applicable instruction set
	// architecture for the current instruction.
	// The encoding of instruction sets should be shared by all users of a given
	// architecture. It is recommended that this encoding be defined by the ABI
	// authoring committee for each architecture.
	isa uint64

	// An unsigned integer identifying the block to which the current instruction
	// belongs. Discriminator values are assigned arbitrarily by the DWARF
	// producer and serve to distinguish among multiple blocks that may all be
	// associated with the same source file, line, and column. Where only one
	// block exists for a given source position, the discriminator value should be
	// zero.
	discriminator uint64

	// The prologue for the current compilation unit.
	// Not an actual register, but stored here for cleanlineness.
	prologue linePrologue
}

// parseLinePrologue parses the prologue/header describing the compilation
// unit in the line table starting at the specified offset.
func (m *lineMachine) parseLinePrologue(b *buf) error {
	m.prologue = linePrologue{}
	m.prologue.unitLength = int(b.uint32()) // Note: We are assuming 32-bit DWARF format.
	if m.prologue.unitLength > len(b.data) {
		return fmt.Errorf("DWARF: bad PC/line header length")
	}
	m.prologue.version = int(b.uint16())
	m.prologue.headerLength = int(b.uint32())
	m.prologue.minInstructionLength = int(b.uint8())
	if m.prologue.version >= 4 {
		m.prologue.maxOpsPerInstruction = int(b.uint8())
	} else {
		m.prologue.maxOpsPerInstruction = 1
	}
	m.prologue.defaultIsStmt = b.uint8() != 0
	m.prologue.lineBase = int(int8(b.uint8()))
	m.prologue.lineRange = int(b.uint8())
	m.prologue.opcodeBase = b.uint8()
	m.prologue.stdOpcodeLengths = make([]byte, m.prologue.opcodeBase-1)
	copy(m.prologue.stdOpcodeLengths, b.bytes(int(m.prologue.opcodeBase-1)))
	m.prologue.include = make([]string, 1) // First entry is empty; file index entries are 1-indexed.
	// Includes
	for {
		name := b.string()
		if name == "" {
			break
		}
		m.prologue.include = append(m.prologue.include, name)
	}
	// Files
	m.prologue.file = make([]lineFile, 1, 10) // entries are 1-indexed in line number program.
	for {
		name := b.string()
		if name == "" {
			break
		}
		index := b.uint()
		time := b.uint()
		length := b.uint()
		f := lineFile{
			name:   name,
			index:  int(index),
			time:   int(time),
			length: int(length),
		}
		m.prologue.file = append(m.prologue.file, f)
	}
	return nil
}

// Special opcodes, page 117.
func (m *lineMachine) specialOpcode(opcode byte) {
	adjustedOpcode := int(opcode - m.prologue.opcodeBase)
	advance := adjustedOpcode / m.prologue.lineRange
	delta := (int(m.opIndex) + advance) / m.prologue.maxOpsPerInstruction
	m.address += uint64(m.prologue.minInstructionLength * delta)
	m.opIndex = (m.opIndex + uint64(advance)) % uint64(m.prologue.maxOpsPerInstruction)
	lineAdvance := m.prologue.lineBase + (adjustedOpcode % m.prologue.lineRange)
	m.line += uint64(lineAdvance)
	m.basicBlock = false
	m.prologueEnd = false
	m.epilogueBegin = false
	m.discriminator = 0
}

// evalCompilationUnit reads the next compilation unit to see if it contains the PC.
// The return value reports whether the PC was found; if so, the machine's registers
// contain the relevant information.
func (m *lineMachine) evalCompilationUnit(b *buf, pc uint64) (bool, error) {
	err := m.parseLinePrologue(b)
	if err != nil {
		return false, err
	}
	m.reset()
	for len(b.data) > 0 {
		op := b.uint8()
		if op >= m.prologue.opcodeBase {
			m.specialOpcode(op)
			continue
		}
		switch op {
		case lineStartExtendedOpcode:
			if len(b.data) == 0 {
				return false, fmt.Errorf("DWARF: short extended opcode (1)")
			}
			size := b.uint()
			if uint64(len(b.data)) < size {
				return false, fmt.Errorf("DWARF: short extended opcode (2)")
			}
			op = b.uint8()
			switch op {
			case lineExtEndSequence:
				m.endSequence = true
				m.reset()
				return false, nil
			case lineExtSetAddress:
				m.address = b.addr()
				m.opIndex = 0
			case lineExtDefineFile:
				return false, fmt.Errorf("DWARF: unimplemented define_file op")
			case lineExtSetDiscriminator:
				discriminator := b.uint()
				m.line = discriminator
			default:
				return false, fmt.Errorf("DWARF: unknown extended opcode %#x", op)
			}
		case lineStdCopy:
			m.discriminator = 0
			m.basicBlock = false
			m.prologueEnd = false
			m.epilogueBegin = false
			if m.address >= pc {
				// TODO: if m.address > pc, is this one step too far?
				return true, nil
			}
		case lineStdAdvancePC:
			advance := b.uint()
			delta := (int(m.opIndex) + int(advance)) / m.prologue.maxOpsPerInstruction
			m.address += uint64(m.prologue.minInstructionLength * delta)
			m.opIndex = (m.opIndex + uint64(advance)) % uint64(m.prologue.maxOpsPerInstruction)
			m.basicBlock = false
			m.prologueEnd = false
			m.epilogueBegin = false
			m.discriminator = 0
		case lineStdAdvanceLine:
			advance := b.int()
			m.line = uint64(int64(m.line) + advance)
		case lineStdSetFile:
			index := b.uint()
			m.file = index
		case lineStdSetColumn:
			column := b.uint()
			m.column = column
		case lineStdNegateStmt:
			m.isStmt = !m.isStmt
		case lineStdSetBasicBlock:
			m.basicBlock = true
		case lineStdFixedAdvancePC:
			m.address += uint64(b.uint16())
			m.opIndex = 0
		case lineStdSetPrologueEnd:
			m.prologueEnd = true
		case lineStdSetEpilogueBegin:
			m.epilogueBegin = true
		case lineStdSetISA:
			m.isa = b.uint()
		case lineStdConstAddPC:
			// TODO: Is this right? Seems crazy - why not just use 255 as a special opcode?
			m.specialOpcode(255)
		default:
			panic("not reached")
		}
	}
	panic("not reached")
}

// reset sets the machine's registers to the initial state. Page 111.
func (m *lineMachine) reset() {
	m.address = 0
	m.opIndex = 0
	m.file = 1
	m.line = 1
	m.column = 0
	m.isStmt = m.prologue.defaultIsStmt
	m.basicBlock = false
	m.endSequence = false
	m.prologueEnd = false
	m.epilogueBegin = false
	m.isa = 0
	m.discriminator = 0
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2017 Mainflux
//
// SPDX-License-Identifier: Apache-2.0
//

package distro

import (
	"fmt"
	"github.com/drasko/edgex-export"
	"testing"
)

// Probably not a good test as it requires external infrastucture
func TestHttpNew(t *testing.T) {

	sender := NewHttpSender(export.Addressable{
		Name:     "test",
		Method:   export.MethodGet,
		Protocol: export.ProtoHTTP,
		Address:  "http://127.0.0.1",
		Port:     80,
		Path:     "/"})

	for i := 0; i < 1000; i++ {
		sender.Send(fmt.Sprintf("hola %d", i))
	}

	logger.Info("Test ok")
}<commit_msg>Added http server to test to be standalone<commit_after>//
// Copyright (c) 2017 Mainflux
//
// SPDX-License-Identifier: Apache-2.0
//

package distro

import (
	"fmt"
	"github.com/drasko/edgex-export"
	"go.uber.org/zap"
	"net/http"
	"net/http/httputil"
	"testing"
)

var log *zap.Logger

func handler(w http.ResponseWriter, r *http.Request) {
	if r.Method == export.MethodGet {
		requestDump, err := httputil.DumpRequest(r, true)
		if err != nil {
			log.Error("err", zap.Error(err))
		}
		fmt.Println(string(requestDump))
	}

	if r.Method == export.MethodPost {
		requestDump, err := httputil.DumpRequest(r, true)
		if err != nil {
			log.Fatal("err", zap.Error(err))
		}
		fmt.Println(string(requestDump))
	}

	w.WriteHeader(http.StatusOK)
}

func RunServer() {
	http.HandleFunc("/", handler)            // set router
	err := http.ListenAndServe(":9090", nil) // set listen port
	if err != nil {
		log.Error("ListenAndServe: ", zap.Error(err))
	}
}

// Probably not a good test as it requires external infrastucture
func TestHttpNew(t *testing.T) {
	log, _ = zap.NewProduction()
	defer log.Sync()

	InitLogger(log)

	go RunServer()

	sender := NewHttpSender(export.Addressable{
		Name:     "test",
		Method:   export.MethodGet,
		Protocol: export.ProtoHTTP,
		Address:  "http://127.0.0.1",
		Port:     9090,
		Path:     "/"})

	for i := 0; i < 10; i++ {
		sender.Send(fmt.Sprintf("hola %d", i))
	}

	log.Info("Test ok")

	senderPost := NewHttpSender(export.Addressable{
		Name:     "test",
		Method:   export.MethodPost,
		Protocol: export.ProtoHTTP,
		Address:  "http://127.0.0.1",
		Port:     9090,
		Path:     "/"})

	for i := 0; i < 10; i++ {
		senderPost.Send(fmt.Sprintf("hola %d", i))
	}

	log.Info("Test ok")
}
<|endoftext|>


<commit_before>package docker

import (
	"bufio"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"regexp"
	"strings"

	"github.com/ViBiOh/dashboard/auth"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/gorilla/websocket"
)

const ignoredByteLogSize = 8
const tailSize = `100`
const start = `start`
const stop = `stop`
const busPrefix = `/bus`

var (
	eventsDemand = regexp.MustCompile(`^events (\S+)`)
	logsDemand   = regexp.MustCompile(`^logs (\S+)(?: (.+))?`)
	statsDemand  = regexp.MustCompile(`^stats (\S+)(?: (.+))?`)
)

var (
	eventsPrefix = []byte(`events `)
	logsPrefix   = []byte(`logs `)
	statsPrefix  = []byte(`stats `)
)

var (
	hostCheck       *regexp.Regexp
	websocketOrigin = flag.String(`ws`, `^dashboard`, `Allowed WebSocket Origin pattern`)
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return hostCheck.MatchString(r.Host)
	},
}

// InitWebsocket configure websocket handler
func InitWebsocket() error {
	hostCheck = regexp.MustCompile(*websocketOrigin)

	return nil
}

func upgradeAndAuth(w http.ResponseWriter, r *http.Request) (*websocket.Conn, *auth.User, error) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while upgrading connection: %v`, err)
	}

	_, basicAuth, err := ws.ReadMessage()
	if err != nil {
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while reading authentification message: %v`, err)
	}

	log.Printf(`RemoteIP %v and %v`, auth.GetRemoteIP(r), r.Header)
	user, err := auth.IsAuthenticatedByAuth(string(basicAuth), auth.GetRemoteIP(r))
	if err != nil {
		ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while checking authentification: %v`, err)
	}

	return ws, user, nil
}

func readContent(user *auth.User, ws *websocket.Conn, name string, done chan<- int, content chan<- []byte) {
	for {
		messageType, message, err := ws.ReadMessage()

		if messageType == websocket.CloseMessage {
			close(done)
			return
		}

		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived, websocket.CloseAbnormalClosure) {
				log.Printf(`[%s] Error while reading from %s socket: %v`, user.Username, name, err)
			}

			close(done)
			return
		}

		content <- message
	}
}

func streamEvents(ctx context.Context, cancel context.CancelFunc, user *auth.User, _ string, output chan<- []byte) {
	defer cancel()

	filtersArgs := filters.NewArgs()
	labelFilters(user, &filtersArgs, ``)
	eventFilters(&filtersArgs)

	messages, errors := docker.Events(ctx, types.EventsOptions{Filters: filtersArgs})

	for {
		select {
		case <-ctx.Done():
			return

		case message := <-messages:
			messageJSON, err := json.Marshal(message)
			if err != nil {
				log.Printf(`[%s] Events marshalling in error: %v`, user.Username, err)
				cancel()
			} else {
				output <- append(eventsPrefix, messageJSON...)
			}

		case err := <-errors:
			log.Printf(`[%s] Events reading in error: %v`, user.Username, err)
			cancel()
		}
	}
}

func streamLogs(ctx context.Context, cancel context.CancelFunc, user *auth.User, containerID string, output chan<- []byte) {
	logs, err := docker.ContainerLogs(ctx, containerID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: tailSize})
	defer cancel()

	if err != nil {
		log.Printf(`[%s] Logs opening in error: %v`, user.Username, err)
		return
	}
	defer logs.Close()

	scanner := bufio.NewScanner(logs)
	for scanner.Scan() {
		logLine := scanner.Bytes()
		if len(logLine) > ignoredByteLogSize {
			output <- append(logsPrefix, logLine[ignoredByteLogSize:]...)
		}
	}
}

func streamStats(ctx context.Context, cancel context.CancelFunc, user *auth.User, containerID string, output chan<- []byte) {
	stats, err := docker.ContainerStats(ctx, containerID, true)
	defer cancel()

	if err != nil {
		log.Printf(`[%s] Stats opening in error for %s: %v`, user.Username, containerID, err)
		return
	}
	defer stats.Body.Close()

	scanner := bufio.NewScanner(stats.Body)
	for scanner.Scan() {
		output <- append(statsPrefix, scanner.Bytes()...)
	}
}

func handleBusDemand(user *auth.User, name string, input []byte, demand *regexp.Regexp, cancel context.CancelFunc, output chan<- []byte, streamFn func(context.Context, context.CancelFunc, *auth.User, string, chan<- []byte)) context.CancelFunc {
	demandGroups := demand.FindSubmatch(input)
	if len(demandGroups) < 2 {
		log.Printf(`[%s] Unable to parse bus demand %s for %s`, user.Username, input, name)
	}

	action := string(demandGroups[1])

	containerID := ``
	if len(demandGroups) > 2 {
		containerID = string(demandGroups[2])
	}

	if action == stop && cancel != nil {
		cancel()
	} else if action == start {
		if cancel != nil {
			cancel()
		}

		ctx, newCancel := context.WithCancel(context.Background())
		go streamFn(ctx, newCancel, user, string(containerID), output)

		return newCancel
	}

	return nil
}

func busWebsocketHandler(w http.ResponseWriter, r *http.Request) {
	ws, user, err := upgradeAndAuth(w, r)
	if err != nil {
		log.Printf(`Error while upgrading connection to websocket: %v`, err)
		return
	}
	defer ws.Close()

	done := make(chan int)

	output := make(chan []byte)
	defer close(output)

	input := make(chan []byte)
	defer close(input)

	go readContent(user, ws, `streaming`, done, input)

	var eventsCancelFunc context.CancelFunc
	var logsCancelFunc context.CancelFunc
	var statsCancelFunc context.CancelFunc

	if err = ws.WriteMessage(websocket.TextMessage, []byte(`ready`)); err != nil {
		log.Printf(`[%s] Error while saying ready: %v`, user.Username, err)
	}

	for {
		select {
		case <-done:
			return

		case inputBytes := <-input:
			if eventsDemand.Match(inputBytes) {
				eventsCancelFunc = handleBusDemand(user, `events`, inputBytes, eventsDemand, eventsCancelFunc, output, streamEvents)
				if eventsCancelFunc != nil {
					defer eventsCancelFunc()
				}
			} else if logsDemand.Match(inputBytes) {
				logsCancelFunc = handleBusDemand(user, `logs`, inputBytes, logsDemand, logsCancelFunc, output, streamLogs)
				if logsCancelFunc != nil {
					defer logsCancelFunc()
				}
			} else if statsDemand.Match(inputBytes) {
				statsCancelFunc = handleBusDemand(user, `stats`, inputBytes, statsDemand, statsCancelFunc, output, streamStats)
				if statsCancelFunc != nil {
					defer statsCancelFunc()
				}
			}

		case outputBytes := <-output:
			if err = ws.WriteMessage(websocket.TextMessage, outputBytes); err != nil {
				log.Printf(`[%s] Error while writing to streaming: %v`, user.Username, err)
				close(done)
			}
		}
	}
}

// WebsocketHandler for Docker Websocket request. Should be use with net/http
type WebsocketHandler struct {
}

func (handler WebsocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if strings.HasPrefix(r.URL.Path, busPrefix) {
		busWebsocketHandler(w, r)
	}
}
<commit_msg>Update websocket.go<commit_after>package docker

import (
	"bufio"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"regexp"
	"strings"

	"github.com/ViBiOh/dashboard/auth"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/gorilla/websocket"
)

const ignoredByteLogSize = 8
const tailSize = `100`
const start = `start`
const stop = `stop`
const busPrefix = `/bus`

var (
	eventsDemand = regexp.MustCompile(`^events (\S+)`)
	logsDemand   = regexp.MustCompile(`^logs (\S+)(?: (.+))?`)
	statsDemand  = regexp.MustCompile(`^stats (\S+)(?: (.+))?`)
)

var (
	eventsPrefix = []byte(`events `)
	logsPrefix   = []byte(`logs `)
	statsPrefix  = []byte(`stats `)
)

var (
	hostCheck       *regexp.Regexp
	websocketOrigin = flag.String(`ws`, `^dashboard`, `Allowed WebSocket Origin pattern`)
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return hostCheck.MatchString(r.Host)
	},
}

// InitWebsocket configure websocket handler
func InitWebsocket() error {
	hostCheck = regexp.MustCompile(*websocketOrigin)

	return nil
}

func upgradeAndAuth(w http.ResponseWriter, r *http.Request) (*websocket.Conn, *auth.User, error) {
	remoteIP := auth.GetRemoteIP(r)
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while upgrading connection: %v`, err)
	}

	_, basicAuth, err := ws.ReadMessage()
	if err != nil {
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while reading authentification message: %v`, err)
	}

	user, err := auth.IsAuthenticatedByAuth(string(basicAuth), remoteIP)
	if err != nil {
		ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
		defer ws.Close()
		return nil, nil, fmt.Errorf(`Error while checking authentification: %v`, err)
	}

	return ws, user, nil
}

func readContent(user *auth.User, ws *websocket.Conn, name string, done chan<- int, content chan<- []byte) {
	for {
		messageType, message, err := ws.ReadMessage()

		if messageType == websocket.CloseMessage {
			close(done)
			return
		}

		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived, websocket.CloseAbnormalClosure) {
				log.Printf(`[%s] Error while reading from %s socket: %v`, user.Username, name, err)
			}

			close(done)
			return
		}

		content <- message
	}
}

func streamEvents(ctx context.Context, cancel context.CancelFunc, user *auth.User, _ string, output chan<- []byte) {
	defer cancel()

	filtersArgs := filters.NewArgs()
	labelFilters(user, &filtersArgs, ``)
	eventFilters(&filtersArgs)

	messages, errors := docker.Events(ctx, types.EventsOptions{Filters: filtersArgs})

	for {
		select {
		case <-ctx.Done():
			return

		case message := <-messages:
			messageJSON, err := json.Marshal(message)
			if err != nil {
				log.Printf(`[%s] Events marshalling in error: %v`, user.Username, err)
				cancel()
			} else {
				output <- append(eventsPrefix, messageJSON...)
			}

		case err := <-errors:
			log.Printf(`[%s] Events reading in error: %v`, user.Username, err)
			cancel()
		}
	}
}

func streamLogs(ctx context.Context, cancel context.CancelFunc, user *auth.User, containerID string, output chan<- []byte) {
	logs, err := docker.ContainerLogs(ctx, containerID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: tailSize})
	defer cancel()

	if err != nil {
		log.Printf(`[%s] Logs opening in error: %v`, user.Username, err)
		return
	}
	defer logs.Close()

	scanner := bufio.NewScanner(logs)
	for scanner.Scan() {
		logLine := scanner.Bytes()
		if len(logLine) > ignoredByteLogSize {
			output <- append(logsPrefix, logLine[ignoredByteLogSize:]...)
		}
	}
}

func streamStats(ctx context.Context, cancel context.CancelFunc, user *auth.User, containerID string, output chan<- []byte) {
	stats, err := docker.ContainerStats(ctx, containerID, true)
	defer cancel()

	if err != nil {
		log.Printf(`[%s] Stats opening in error for %s: %v`, user.Username, containerID, err)
		return
	}
	defer stats.Body.Close()

	scanner := bufio.NewScanner(stats.Body)
	for scanner.Scan() {
		output <- append(statsPrefix, scanner.Bytes()...)
	}
}

func handleBusDemand(user *auth.User, name string, input []byte, demand *regexp.Regexp, cancel context.CancelFunc, output chan<- []byte, streamFn func(context.Context, context.CancelFunc, *auth.User, string, chan<- []byte)) context.CancelFunc {
	demandGroups := demand.FindSubmatch(input)
	if len(demandGroups) < 2 {
		log.Printf(`[%s] Unable to parse bus demand %s for %s`, user.Username, input, name)
	}

	action := string(demandGroups[1])

	containerID := ``
	if len(demandGroups) > 2 {
		containerID = string(demandGroups[2])
	}

	if action == stop && cancel != nil {
		cancel()
	} else if action == start {
		if cancel != nil {
			cancel()
		}

		ctx, newCancel := context.WithCancel(context.Background())
		go streamFn(ctx, newCancel, user, string(containerID), output)

		return newCancel
	}

	return nil
}

func busWebsocketHandler(w http.ResponseWriter, r *http.Request) {
	ws, user, err := upgradeAndAuth(w, r)
	if err != nil {
		log.Printf(`Error while upgrading connection to websocket: %v`, err)
		return
	}
	defer ws.Close()

	done := make(chan int)

	output := make(chan []byte)
	defer close(output)

	input := make(chan []byte)
	defer close(input)

	go readContent(user, ws, `streaming`, done, input)

	var eventsCancelFunc context.CancelFunc
	var logsCancelFunc context.CancelFunc
	var statsCancelFunc context.CancelFunc

	if err = ws.WriteMessage(websocket.TextMessage, []byte(`ready`)); err != nil {
		log.Printf(`[%s] Error while saying ready: %v`, user.Username, err)
	}

	for {
		select {
		case <-done:
			return

		case inputBytes := <-input:
			if eventsDemand.Match(inputBytes) {
				eventsCancelFunc = handleBusDemand(user, `events`, inputBytes, eventsDemand, eventsCancelFunc, output, streamEvents)
				if eventsCancelFunc != nil {
					defer eventsCancelFunc()
				}
			} else if logsDemand.Match(inputBytes) {
				logsCancelFunc = handleBusDemand(user, `logs`, inputBytes, logsDemand, logsCancelFunc, output, streamLogs)
				if logsCancelFunc != nil {
					defer logsCancelFunc()
				}
			} else if statsDemand.Match(inputBytes) {
				statsCancelFunc = handleBusDemand(user, `stats`, inputBytes, statsDemand, statsCancelFunc, output, streamStats)
				if statsCancelFunc != nil {
					defer statsCancelFunc()
				}
			}

		case outputBytes := <-output:
			if err = ws.WriteMessage(websocket.TextMessage, outputBytes); err != nil {
				log.Printf(`[%s] Error while writing to streaming: %v`, user.Username, err)
				close(done)
			}
		}
	}
}

// WebsocketHandler for Docker Websocket request. Should be use with net/http
type WebsocketHandler struct {
}

func (handler WebsocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if strings.HasPrefix(r.URL.Path, busPrefix) {
		busWebsocketHandler(w, r)
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 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.

// This file implements FormatSelections and FormatText.
// FormatText is used to HTML-format Go and non-Go source
// text with line numbers and highlighted sections. It is
// built on top of FormatSelections, a generic formatter
// for "selected" text.

package main

import (
	"fmt"
	"go/scanner"
	"go/token"
	"io"
	"regexp"
	"strconv"
	"text/template"
)

// ----------------------------------------------------------------------------
// Implementation of FormatSelections

// A Selection is a function returning offset pairs []int{a, b}
// describing consecutive non-overlapping text segments [a, b).
// If there are no more segments, a Selection must return nil.
//
// TODO It's more efficient to return a pair (a, b int) instead
//      of creating lots of slices. Need to determine how to
//      indicate the end of a Selection.
//
type Selection func() []int

// A LinkWriter writes some start or end "tag" to w for the text offset offs.
// It is called by FormatSelections at the start or end of each link segment.
//
type LinkWriter func(w io.Writer, offs int, start bool)

// A SegmentWriter formats a text according to selections and writes it to w.
// The selections parameter is a bit set indicating which selections provided
// to FormatSelections overlap with the text segment: If the n'th bit is set
// in selections, the n'th selection provided to FormatSelections is overlapping
// with the text.
//
type SegmentWriter func(w io.Writer, text []byte, selections int)

// FormatSelections takes a text and writes it to w using link and segment
// writers lw and sw as follows: lw is invoked for consecutive segment starts
// and ends as specified through the links selection, and sw is invoked for
// consecutive segments of text overlapped by the same selections as specified
// by selections. The link writer lw may be nil, in which case the links
// Selection is ignored.
//
func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) {
	// If we have a link writer, make the links
	// selection the last entry in selections
	if lw != nil {
		selections = append(selections, links)
	}

	// compute the sequence of consecutive segment changes
	changes := newMerger(selections)

	// The i'th bit in bitset indicates that the text
	// at the current offset is covered by selections[i].
	bitset := 0
	lastOffs := 0

	// Text segments are written in a delayed fashion
	// such that consecutive segments belonging to the
	// same selection can be combined (peephole optimization).
	// last describes the last segment which has not yet been written.
	var last struct {
		begin, end int // valid if begin < end
		bitset     int
	}

	// flush writes the last delayed text segment
	flush := func() {
		if last.begin < last.end {
			sw(w, text[last.begin:last.end], last.bitset)
		}
		last.begin = last.end // invalidate last
	}

	// segment runs the segment [lastOffs, end) with the selection
	// indicated by bitset through the segment peephole optimizer.
	segment := func(end int) {
		if lastOffs < end { // ignore empty segments
			if last.end != lastOffs || last.bitset != bitset {
				// the last segment is not adjacent to or
				// differs from the new one
				flush()
				// start a new segment
				last.begin = lastOffs
			}
			last.end = end
			last.bitset = bitset
		}
	}

	for {
		// get the next segment change
		index, offs, start := changes.next()
		if index < 0 || offs > len(text) {
			// no more segment changes or the next change
			// is past the end of the text - we're done
			break
		}
		// determine the kind of segment change
		if lw != nil && index == len(selections)-1 {
			// we have a link segment change (see start of this function):
			// format the previous selection segment, write the
			// link tag and start a new selection segment
			segment(offs)
			flush()
			lastOffs = offs
			lw(w, offs, start)
		} else {
			// we have a selection change:
			// format the previous selection segment, determine
			// the new selection bitset and start a new segment
			segment(offs)
			lastOffs = offs
			mask := 1 << uint(index)
			if start {
				bitset |= mask
			} else {
				bitset &^= mask
			}
		}
	}
	segment(len(text))
	flush()
}

// A merger merges a slice of Selections and produces a sequence of
// consecutive segment change events through repeated next() calls.
//
type merger struct {
	selections []Selection
	segments   [][]int // segments[i] is the next segment of selections[i]
}

const infinity int = 2e9

func newMerger(selections []Selection) *merger {
	segments := make([][]int, len(selections))
	for i, sel := range selections {
		segments[i] = []int{infinity, infinity}
		if sel != nil {
			if seg := sel(); seg != nil {
				segments[i] = seg
			}
		}
	}
	return &merger{selections, segments}
}

// next returns the next segment change: index specifies the Selection
// to which the segment belongs, offs is the segment start or end offset
// as determined by the start value. If there are no more segment changes,
// next returns an index value < 0.
//
func (m *merger) next() (index, offs int, start bool) {
	// find the next smallest offset where a segment starts or ends
	offs = infinity
	index = -1
	for i, seg := range m.segments {
		switch {
		case seg[0] < offs:
			offs = seg[0]
			index = i
			start = true
		case seg[1] < offs:
			offs = seg[1]
			index = i
			start = false
		}
	}
	if index < 0 {
		// no offset found => all selections merged
		return
	}
	// offset found - it's either the start or end offset but
	// either way it is ok to consume the start offset: set it
	// to infinity so it won't be considered in the following
	// next call
	m.segments[index][0] = infinity
	if start {
		return
	}
	// end offset found - consume it
	m.segments[index][1] = infinity
	// advance to the next segment for that selection
	seg := m.selections[index]()
	if seg == nil {
		return
	}
	m.segments[index] = seg
	return
}

// ----------------------------------------------------------------------------
// Implementation of FormatText

// lineSelection returns the line segments for text as a Selection.
func lineSelection(text []byte) Selection {
	i, j := 0, 0
	return func() (seg []int) {
		// find next newline, if any
		for j < len(text) {
			j++
			if text[j-1] == '\n' {
				break
			}
		}
		if i < j {
			// text[i:j] constitutes a line
			seg = []int{i, j}
			i = j
		}
		return
	}
}

// tokenSelection returns, as a selection, the sequence of
// consecutive occurrences of token sel in the Go src text.
//
func tokenSelection(src []byte, sel token.Token) Selection {
	var s scanner.Scanner
	fset := token.NewFileSet()
	file := fset.AddFile("", fset.Base(), len(src))
	s.Init(file, src, nil, scanner.ScanComments)
	return func() (seg []int) {
		for {
			pos, tok, lit := s.Scan()
			if tok == token.EOF {
				break
			}
			offs := file.Offset(pos)
			if tok == sel {
				seg = []int{offs, offs + len(lit)}
				break
			}
		}
		return
	}
}

// makeSelection is a helper function to make a Selection from a slice of pairs.
func makeSelection(matches [][]int) Selection {
	return func() (seg []int) {
		if len(matches) > 0 {
			seg = matches[0]
			matches = matches[1:]
		}
		return
	}
}

// regexpSelection computes the Selection for the regular expression expr in text.
func regexpSelection(text []byte, expr string) Selection {
	var matches [][]int
	if rx, err := regexp.Compile(expr); err == nil {
		matches = rx.FindAllIndex(text, -1)
	}
	return makeSelection(matches)
}

var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`)

// rangeSelection computes the Selection for a text range described
// by the argument str; the range description must match the selRx
// regular expression.
//
func rangeSelection(str string) Selection {
	m := selRx.FindStringSubmatch(str)
	if len(m) >= 2 {
		from, _ := strconv.Atoi(m[1])
		to, _ := strconv.Atoi(m[2])
		if from < to {
			return makeSelection([][]int{{from, to}})
		}
	}
	return nil
}

// Span tags for all the possible selection combinations that may
// be generated by FormatText. Selections are indicated by a bitset,
// and the value of the bitset specifies the tag to be used.
//
// bit 0: comments
// bit 1: highlights
// bit 2: selections
//
var startTags = [][]byte{
	/* 000 */ []byte(``),
	/* 001 */ []byte(`<span class="comment">`),
	/* 010 */ []byte(`<span class="highlight">`),
	/* 011 */ []byte(`<span class="highlight-comment">`),
	/* 100 */ []byte(`<span class="selection">`),
	/* 101 */ []byte(`<span class="selection-comment">`),
	/* 110 */ []byte(`<span class="selection-highlight">`),
	/* 111 */ []byte(`<span class="selection-highlight-comment">`),
}

var endTag = []byte(`</span>`)

func selectionTag(w io.Writer, text []byte, selections int) {
	if selections < len(startTags) {
		if tag := startTags[selections]; len(tag) > 0 {
			w.Write(tag)
			template.HTMLEscape(w, text)
			w.Write(endTag)
			return
		}
	}
	template.HTMLEscape(w, text)
}

// FormatText HTML-escapes text and writes it to w.
// Consecutive text segments are wrapped in HTML spans (with tags as
// defined by startTags and endTag) as follows:
//
//	- if line >= 0, line number (ln) spans are inserted before each line,
//	  starting with the value of line
//	- if the text is Go source, comments get the "comment" span class
//	- each occurrence of the regular expression pattern gets the "highlight"
//	  span class
//	- text segments covered by selection get the "selection" span class
//
// Comments, highlights, and selections may overlap arbitrarily; the respective
// HTML span classes are specified in the startTags variable.
//
func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) {
	var comments, highlights Selection
	if goSource {
		comments = tokenSelection(text, token.COMMENT)
	}
	if pattern != "" {
		highlights = regexpSelection(text, pattern)
	}
	if line >= 0 || comments != nil || highlights != nil || selection != nil {
		var lineTag LinkWriter
		if line >= 0 {
			lineTag = func(w io.Writer, _ int, start bool) {
				if start {
					fmt.Fprintf(w, "%6d\t", line, line)
					line++
				}
			}
		}
		FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
	} else {
		template.HTMLEscape(w, text)
	}
}
<commit_msg>godoc: internal cleanup: remove a TODO<commit_after>// Copyright 2011 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.

// This file implements FormatSelections and FormatText.
// FormatText is used to HTML-format Go and non-Go source
// text with line numbers and highlighted sections. It is
// built on top of FormatSelections, a generic formatter
// for "selected" text.

package main

import (
	"fmt"
	"go/scanner"
	"go/token"
	"io"
	"regexp"
	"strconv"
	"text/template"
)

// ----------------------------------------------------------------------------
// Implementation of FormatSelections

// A Segment describes a text segment [start, end).
// The zero value of a Segment is a ready-to-use empty segment.
//
type Segment struct {
	start, end int
}

func (seg *Segment) isEmpty() bool { return seg.start >= seg.end }

// A Selection is an "iterator" function returning a text segment.
// Repeated calls to a selection return consecutive, non-overlapping,
// non-empty segments, followed by an infinite sequence of empty
// segments. The first empty segment marks the end of the selection.
//
type Selection func() Segment

// A LinkWriter writes some start or end "tag" to w for the text offset offs.
// It is called by FormatSelections at the start or end of each link segment.
//
type LinkWriter func(w io.Writer, offs int, start bool)

// A SegmentWriter formats a text according to selections and writes it to w.
// The selections parameter is a bit set indicating which selections provided
// to FormatSelections overlap with the text segment: If the n'th bit is set
// in selections, the n'th selection provided to FormatSelections is overlapping
// with the text.
//
type SegmentWriter func(w io.Writer, text []byte, selections int)

// FormatSelections takes a text and writes it to w using link and segment
// writers lw and sw as follows: lw is invoked for consecutive segment starts
// and ends as specified through the links selection, and sw is invoked for
// consecutive segments of text overlapped by the same selections as specified
// by selections. The link writer lw may be nil, in which case the links
// Selection is ignored.
//
func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) {
	// If we have a link writer, make the links
	// selection the last entry in selections
	if lw != nil {
		selections = append(selections, links)
	}

	// compute the sequence of consecutive segment changes
	changes := newMerger(selections)

	// The i'th bit in bitset indicates that the text
	// at the current offset is covered by selections[i].
	bitset := 0
	lastOffs := 0

	// Text segments are written in a delayed fashion
	// such that consecutive segments belonging to the
	// same selection can be combined (peephole optimization).
	// last describes the last segment which has not yet been written.
	var last struct {
		begin, end int // valid if begin < end
		bitset     int
	}

	// flush writes the last delayed text segment
	flush := func() {
		if last.begin < last.end {
			sw(w, text[last.begin:last.end], last.bitset)
		}
		last.begin = last.end // invalidate last
	}

	// segment runs the segment [lastOffs, end) with the selection
	// indicated by bitset through the segment peephole optimizer.
	segment := func(end int) {
		if lastOffs < end { // ignore empty segments
			if last.end != lastOffs || last.bitset != bitset {
				// the last segment is not adjacent to or
				// differs from the new one
				flush()
				// start a new segment
				last.begin = lastOffs
			}
			last.end = end
			last.bitset = bitset
		}
	}

	for {
		// get the next segment change
		index, offs, start := changes.next()
		if index < 0 || offs > len(text) {
			// no more segment changes or the next change
			// is past the end of the text - we're done
			break
		}
		// determine the kind of segment change
		if lw != nil && index == len(selections)-1 {
			// we have a link segment change (see start of this function):
			// format the previous selection segment, write the
			// link tag and start a new selection segment
			segment(offs)
			flush()
			lastOffs = offs
			lw(w, offs, start)
		} else {
			// we have a selection change:
			// format the previous selection segment, determine
			// the new selection bitset and start a new segment
			segment(offs)
			lastOffs = offs
			mask := 1 << uint(index)
			if start {
				bitset |= mask
			} else {
				bitset &^= mask
			}
		}
	}
	segment(len(text))
	flush()
}

// A merger merges a slice of Selections and produces a sequence of
// consecutive segment change events through repeated next() calls.
//
type merger struct {
	selections []Selection
	segments   []Segment // segments[i] is the next segment of selections[i]
}

const infinity int = 2e9

func newMerger(selections []Selection) *merger {
	segments := make([]Segment, len(selections))
	for i, sel := range selections {
		segments[i] = Segment{infinity, infinity}
		if sel != nil {
			if seg := sel(); !seg.isEmpty() {
				segments[i] = seg
			}
		}
	}
	return &merger{selections, segments}
}

// next returns the next segment change: index specifies the Selection
// to which the segment belongs, offs is the segment start or end offset
// as determined by the start value. If there are no more segment changes,
// next returns an index value < 0.
//
func (m *merger) next() (index, offs int, start bool) {
	// find the next smallest offset where a segment starts or ends
	offs = infinity
	index = -1
	for i, seg := range m.segments {
		switch {
		case seg.start < offs:
			offs = seg.start
			index = i
			start = true
		case seg.end < offs:
			offs = seg.end
			index = i
			start = false
		}
	}
	if index < 0 {
		// no offset found => all selections merged
		return
	}
	// offset found - it's either the start or end offset but
	// either way it is ok to consume the start offset: set it
	// to infinity so it won't be considered in the following
	// next call
	m.segments[index].start = infinity
	if start {
		return
	}
	// end offset found - consume it
	m.segments[index].end = infinity
	// advance to the next segment for that selection
	seg := m.selections[index]()
	if !seg.isEmpty() {
		m.segments[index] = seg
	}
	return
}

// ----------------------------------------------------------------------------
// Implementation of FormatText

// lineSelection returns the line segments for text as a Selection.
func lineSelection(text []byte) Selection {
	i, j := 0, 0
	return func() (seg Segment) {
		// find next newline, if any
		for j < len(text) {
			j++
			if text[j-1] == '\n' {
				break
			}
		}
		if i < j {
			// text[i:j] constitutes a line
			seg = Segment{i, j}
			i = j
		}
		return
	}
}

// tokenSelection returns, as a selection, the sequence of
// consecutive occurrences of token sel in the Go src text.
//
func tokenSelection(src []byte, sel token.Token) Selection {
	var s scanner.Scanner
	fset := token.NewFileSet()
	file := fset.AddFile("", fset.Base(), len(src))
	s.Init(file, src, nil, scanner.ScanComments)
	return func() (seg Segment) {
		for {
			pos, tok, lit := s.Scan()
			if tok == token.EOF {
				break
			}
			offs := file.Offset(pos)
			if tok == sel {
				seg = Segment{offs, offs + len(lit)}
				break
			}
		}
		return
	}
}

// makeSelection is a helper function to make a Selection from a slice of pairs.
// Pairs describing empty segments are ignored.
//
func makeSelection(matches [][]int) Selection {
	i := 0
	return func() Segment {
		for i < len(matches) {
			m := matches[i]
			i++
			if m[0] < m[1] {
				// non-empty segment
				return Segment{m[0], m[1]}
			}
		}
		return Segment{}
	}
}

// regexpSelection computes the Selection for the regular expression expr in text.
func regexpSelection(text []byte, expr string) Selection {
	var matches [][]int
	if rx, err := regexp.Compile(expr); err == nil {
		matches = rx.FindAllIndex(text, -1)
	}
	return makeSelection(matches)
}

var selRx = regexp.MustCompile(`^([0-9]+):([0-9]+)`)

// rangeSelection computes the Selection for a text range described
// by the argument str; the range description must match the selRx
// regular expression.
//
func rangeSelection(str string) Selection {
	m := selRx.FindStringSubmatch(str)
	if len(m) >= 2 {
		from, _ := strconv.Atoi(m[1])
		to, _ := strconv.Atoi(m[2])
		if from < to {
			return makeSelection([][]int{{from, to}})
		}
	}
	return nil
}

// Span tags for all the possible selection combinations that may
// be generated by FormatText. Selections are indicated by a bitset,
// and the value of the bitset specifies the tag to be used.
//
// bit 0: comments
// bit 1: highlights
// bit 2: selections
//
var startTags = [][]byte{
	/* 000 */ []byte(``),
	/* 001 */ []byte(`<span class="comment">`),
	/* 010 */ []byte(`<span class="highlight">`),
	/* 011 */ []byte(`<span class="highlight-comment">`),
	/* 100 */ []byte(`<span class="selection">`),
	/* 101 */ []byte(`<span class="selection-comment">`),
	/* 110 */ []byte(`<span class="selection-highlight">`),
	/* 111 */ []byte(`<span class="selection-highlight-comment">`),
}

var endTag = []byte(`</span>`)

func selectionTag(w io.Writer, text []byte, selections int) {
	if selections < len(startTags) {
		if tag := startTags[selections]; len(tag) > 0 {
			w.Write(tag)
			template.HTMLEscape(w, text)
			w.Write(endTag)
			return
		}
	}
	template.HTMLEscape(w, text)
}

// FormatText HTML-escapes text and writes it to w.
// Consecutive text segments are wrapped in HTML spans (with tags as
// defined by startTags and endTag) as follows:
//
//	- if line >= 0, line number (ln) spans are inserted before each line,
//	  starting with the value of line
//	- if the text is Go source, comments get the "comment" span class
//	- each occurrence of the regular expression pattern gets the "highlight"
//	  span class
//	- text segments covered by selection get the "selection" span class
//
// Comments, highlights, and selections may overlap arbitrarily; the respective
// HTML span classes are specified in the startTags variable.
//
func FormatText(w io.Writer, text []byte, line int, goSource bool, pattern string, selection Selection) {
	var comments, highlights Selection
	if goSource {
		comments = tokenSelection(text, token.COMMENT)
	}
	if pattern != "" {
		highlights = regexpSelection(text, pattern)
	}
	if line >= 0 || comments != nil || highlights != nil || selection != nil {
		var lineTag LinkWriter
		if line >= 0 {
			lineTag = func(w io.Writer, _ int, start bool) {
				if start {
					fmt.Fprintf(w, "%6d\t", line, line)
					line++
				}
			}
		}
		FormatSelections(w, text, lineTag, lineSelection(text), selectionTag, comments, highlights, selection)
	} else {
		template.HTMLEscape(w, text)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 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

// The vet/all command runs go vet on the standard library and commands.
// It compares the output against a set of whitelists
// maintained in the whitelist directory.
package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"internal/testenv"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
)

var (
	flagPlatforms = flag.String("p", "", "platform(s) to use e.g. linux/amd64,darwin/386")
	flagAll       = flag.Bool("all", false, "run all platforms")
	flagNoLines   = flag.Bool("n", false, "don't print line numbers")
)

var cmdGoPath string
var failed uint32 // updated atomically

func main() {
	log.SetPrefix("vet/all: ")
	log.SetFlags(0)

	var err error
	cmdGoPath, err = testenv.GoTool()
	if err != nil {
		log.Print("could not find cmd/go; skipping")
		// We're on a platform that can't run cmd/go.
		// We want this script to be able to run as part of all.bash,
		// so return cleanly rather than with exit code 1.
		return
	}

	flag.Parse()
	switch {
	case *flagAll && *flagPlatforms != "":
		log.Print("-all and -p flags are incompatible")
		flag.Usage()
		os.Exit(2)
	case *flagPlatforms != "":
		vetPlatforms(parseFlagPlatforms())
	case *flagAll:
		vetPlatforms(allPlatforms())
	default:
		hostPlatform.vet(runtime.GOMAXPROCS(-1))
	}
	if atomic.LoadUint32(&failed) != 0 {
		os.Exit(1)
	}
}

var hostPlatform = platform{os: build.Default.GOOS, arch: build.Default.GOARCH}

func allPlatforms() []platform {
	var pp []platform
	cmd := exec.Command(cmdGoPath, "tool", "dist", "list")
	out, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	lines := bytes.Split(out, []byte{'\n'})
	for _, line := range lines {
		if len(line) == 0 {
			continue
		}
		pp = append(pp, parsePlatform(string(line)))
	}
	return pp
}

func parseFlagPlatforms() []platform {
	var pp []platform
	components := strings.Split(*flagPlatforms, ",")
	for _, c := range components {
		pp = append(pp, parsePlatform(c))
	}
	return pp
}

func parsePlatform(s string) platform {
	vv := strings.Split(s, "/")
	if len(vv) != 2 {
		log.Fatalf("could not parse platform %s, must be of form goos/goarch", s)
	}
	return platform{os: vv[0], arch: vv[1]}
}

type whitelist map[string]int

// load adds entries from the whitelist file, if present, for os/arch to w.
func (w whitelist) load(goos string, goarch string) {
	// Look up whether goarch is a 32-bit or 64-bit architecture.
	archbits, ok := nbits[goarch]
	if !ok {
		log.Fatalf("unknown bitwidth for arch %q", goarch)
	}

	// Look up whether goarch has a shared arch suffix,
	// such as mips64x for mips64 and mips64le.
	archsuff := goarch
	if x, ok := archAsmX[goarch]; ok {
		archsuff = x
	}

	// Load whitelists.
	filenames := []string{
		"all.txt",
		goos + ".txt",
		goarch + ".txt",
		goos + "_" + goarch + ".txt",
		fmt.Sprintf("%dbit.txt", archbits),
	}
	if goarch != archsuff {
		filenames = append(filenames,
			archsuff+".txt",
			goos+"_"+archsuff+".txt",
		)
	}

	// We allow error message templates using GOOS and GOARCH.
	if goos == "android" {
		goos = "linux" // so many special cases :(
	}

	// Read whitelists and do template substitution.
	replace := strings.NewReplacer("GOOS", goos, "GOARCH", goarch, "ARCHSUFF", archsuff)

	for _, filename := range filenames {
		path := filepath.Join("whitelist", filename)
		f, err := os.Open(path)
		if err != nil {
			// Allow not-exist errors; not all combinations have whitelists.
			if os.IsNotExist(err) {
				continue
			}
			log.Fatal(err)
		}
		scan := bufio.NewScanner(f)
		for scan.Scan() {
			line := scan.Text()
			if len(line) == 0 || strings.HasPrefix(line, "//") {
				continue
			}
			w[replace.Replace(line)]++
		}
		if err := scan.Err(); err != nil {
			log.Fatal(err)
		}
	}
}

type platform struct {
	os   string
	arch string
}

func (p platform) String() string {
	return p.os + "/" + p.arch
}

// ignorePathPrefixes are file path prefixes that should be ignored wholesale.
var ignorePathPrefixes = [...]string{
	// These testdata dirs have lots of intentionally broken/bad code for tests.
	"cmd/go/testdata/",
	"cmd/vet/testdata/",
	"go/printer/testdata/",
	// fmt_test contains a known bad format string.
	// We cannot add it to any given whitelist,
	// because it won't show up for any non-host platform,
	// due to deficiencies in vet.
	// Just whitelist the whole file.
	// TODO: If vet ever uses go/loader and starts working off source,
	// this problem will likely go away.
	"fmt/fmt_test.go",
}

func vetPlatforms(pp []platform) {
	ncpus := runtime.GOMAXPROCS(-1) / len(pp)
	if ncpus < 1 {
		ncpus = 1
	}
	var wg sync.WaitGroup
	wg.Add(len(pp))
	for _, p := range pp {
		p := p
		go func() {
			p.vet(ncpus)
			wg.Done()
		}()
	}
	wg.Wait()
}

func (p platform) vet(ncpus int) {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "go run main.go -p %s\n", p)

	// Load whitelist(s).
	w := make(whitelist)
	w.load(p.os, p.arch)

	env := append(os.Environ(), "GOOS="+p.os, "GOARCH="+p.arch)

	// Do 'go install std' before running vet.
	// It is cheap when already installed.
	// Not installing leads to non-obvious failures due to inability to typecheck.
	// TODO: If go/loader ever makes it to the standard library, have vet use it,
	// at which point vet can work off source rather than compiled packages.
	gcflags := ""
	if p != hostPlatform {
		gcflags = "-dolinkobj=false"
	}
	cmd := exec.Command(cmdGoPath, "install", "-p", strconv.Itoa(ncpus), "-gcflags="+gcflags, "std")
	cmd.Env = env
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run GOOS=%s GOARCH=%s 'go install std': %v\n%s", p.os, p.arch, err, out)
	}

	// 'go tool vet .' is considerably faster than 'go vet ./...'
	// TODO: The unsafeptr checks are disabled for now,
	// because there are so many false positives,
	// and no clear way to improve vet to eliminate large chunks of them.
	// And having them in the whitelists will just cause annoyance
	// and churn when working on the runtime.
	args := []string{"tool", "vet", "-unsafeptr=false"}
	if p != hostPlatform {
		// When not checking the host platform, vet gets confused by
		// the fmt.Formatters in cmd/compile,
		// so just skip the printf checks on non-host platforms for now.
		// There's not too much platform-specific code anyway.
		// TODO: If vet ever uses go/loader and starts working off source,
		// this problem will likely go away.
		args = append(args, "-printf=false")
	}
	args = append(args, ".")
	cmd = exec.Command(cmdGoPath, args...)
	cmd.Dir = filepath.Join(runtime.GOROOT(), "src")
	cmd.Env = env
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}
	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}

	// Process vet output.
	scan := bufio.NewScanner(stderr)
NextLine:
	for scan.Scan() {
		line := scan.Text()
		if strings.HasPrefix(line, "vet: ") {
			// Typecheck failure: Malformed syntax or multiple packages or the like.
			// This will yield nicer error messages elsewhere, so ignore them here.
			continue
		}

		fields := strings.SplitN(line, ":", 3)
		var file, lineno, msg string
		switch len(fields) {
		case 2:
			// vet message with no line number
			file, msg = fields[0], fields[1]
		case 3:
			file, lineno, msg = fields[0], fields[1], fields[2]
		default:
			log.Fatalf("could not parse vet output line:\n%s", line)
		}
		msg = strings.TrimSpace(msg)

		for _, ignore := range ignorePathPrefixes {
			if strings.HasPrefix(file, filepath.FromSlash(ignore)) {
				continue NextLine
			}
		}

		key := file + ": " + msg
		if w[key] == 0 {
			// Vet error with no match in the whitelist. Print it.
			if *flagNoLines {
				fmt.Fprintf(&buf, "%s: %s\n", file, msg)
			} else {
				fmt.Fprintf(&buf, "%s:%s: %s\n", file, lineno, msg)
			}
			atomic.StoreUint32(&failed, 1)
			continue
		}
		w[key]--
	}
	if scan.Err() != nil {
		log.Fatalf("failed to scan vet output: %v", scan.Err())
	}
	err = cmd.Wait()
	// We expect vet to fail.
	// Make sure it has failed appropriately, though (for example, not a PathError).
	if _, ok := err.(*exec.ExitError); !ok {
		log.Fatalf("unexpected go vet execution failure: %v", err)
	}
	printedHeader := false
	if len(w) > 0 {
		for k, v := range w {
			if v != 0 {
				if !printedHeader {
					fmt.Fprintln(&buf, "unmatched whitelist entries:")
					printedHeader = true
				}
				for i := 0; i < v; i++ {
					fmt.Fprintln(&buf, k)
				}
				atomic.StoreUint32(&failed, 1)
			}
		}
	}

	os.Stdout.Write(buf.Bytes())
}

// nbits maps from architecture names to the number of bits in a pointer.
// TODO: figure out a clean way to avoid get this info rather than listing it here yet again.
var nbits = map[string]int{
	"386":      32,
	"amd64":    64,
	"amd64p32": 32,
	"arm":      32,
	"arm64":    64,
	"mips":     32,
	"mipsle":   32,
	"mips64":   64,
	"mips64le": 64,
	"ppc64":    64,
	"ppc64le":  64,
	"s390x":    64,
}

// archAsmX maps architectures to the suffix usually used for their assembly files,
// if different than the arch name itself.
var archAsmX = map[string]string{
	"android":  "linux",
	"mips64":   "mips64x",
	"mips64le": "mips64x",
	"mips":     "mipsx",
	"mipsle":   "mipsx",
	"ppc64":    "ppc64x",
	"ppc64le":  "ppc64x",
}
<commit_msg>cmd/vet/all: temporarily ignore vendored pprof<commit_after>// Copyright 2016 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

// The vet/all command runs go vet on the standard library and commands.
// It compares the output against a set of whitelists
// maintained in the whitelist directory.
package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"internal/testenv"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
)

var (
	flagPlatforms = flag.String("p", "", "platform(s) to use e.g. linux/amd64,darwin/386")
	flagAll       = flag.Bool("all", false, "run all platforms")
	flagNoLines   = flag.Bool("n", false, "don't print line numbers")
)

var cmdGoPath string
var failed uint32 // updated atomically

func main() {
	log.SetPrefix("vet/all: ")
	log.SetFlags(0)

	var err error
	cmdGoPath, err = testenv.GoTool()
	if err != nil {
		log.Print("could not find cmd/go; skipping")
		// We're on a platform that can't run cmd/go.
		// We want this script to be able to run as part of all.bash,
		// so return cleanly rather than with exit code 1.
		return
	}

	flag.Parse()
	switch {
	case *flagAll && *flagPlatforms != "":
		log.Print("-all and -p flags are incompatible")
		flag.Usage()
		os.Exit(2)
	case *flagPlatforms != "":
		vetPlatforms(parseFlagPlatforms())
	case *flagAll:
		vetPlatforms(allPlatforms())
	default:
		hostPlatform.vet(runtime.GOMAXPROCS(-1))
	}
	if atomic.LoadUint32(&failed) != 0 {
		os.Exit(1)
	}
}

var hostPlatform = platform{os: build.Default.GOOS, arch: build.Default.GOARCH}

func allPlatforms() []platform {
	var pp []platform
	cmd := exec.Command(cmdGoPath, "tool", "dist", "list")
	out, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	lines := bytes.Split(out, []byte{'\n'})
	for _, line := range lines {
		if len(line) == 0 {
			continue
		}
		pp = append(pp, parsePlatform(string(line)))
	}
	return pp
}

func parseFlagPlatforms() []platform {
	var pp []platform
	components := strings.Split(*flagPlatforms, ",")
	for _, c := range components {
		pp = append(pp, parsePlatform(c))
	}
	return pp
}

func parsePlatform(s string) platform {
	vv := strings.Split(s, "/")
	if len(vv) != 2 {
		log.Fatalf("could not parse platform %s, must be of form goos/goarch", s)
	}
	return platform{os: vv[0], arch: vv[1]}
}

type whitelist map[string]int

// load adds entries from the whitelist file, if present, for os/arch to w.
func (w whitelist) load(goos string, goarch string) {
	// Look up whether goarch is a 32-bit or 64-bit architecture.
	archbits, ok := nbits[goarch]
	if !ok {
		log.Fatalf("unknown bitwidth for arch %q", goarch)
	}

	// Look up whether goarch has a shared arch suffix,
	// such as mips64x for mips64 and mips64le.
	archsuff := goarch
	if x, ok := archAsmX[goarch]; ok {
		archsuff = x
	}

	// Load whitelists.
	filenames := []string{
		"all.txt",
		goos + ".txt",
		goarch + ".txt",
		goos + "_" + goarch + ".txt",
		fmt.Sprintf("%dbit.txt", archbits),
	}
	if goarch != archsuff {
		filenames = append(filenames,
			archsuff+".txt",
			goos+"_"+archsuff+".txt",
		)
	}

	// We allow error message templates using GOOS and GOARCH.
	if goos == "android" {
		goos = "linux" // so many special cases :(
	}

	// Read whitelists and do template substitution.
	replace := strings.NewReplacer("GOOS", goos, "GOARCH", goarch, "ARCHSUFF", archsuff)

	for _, filename := range filenames {
		path := filepath.Join("whitelist", filename)
		f, err := os.Open(path)
		if err != nil {
			// Allow not-exist errors; not all combinations have whitelists.
			if os.IsNotExist(err) {
				continue
			}
			log.Fatal(err)
		}
		scan := bufio.NewScanner(f)
		for scan.Scan() {
			line := scan.Text()
			if len(line) == 0 || strings.HasPrefix(line, "//") {
				continue
			}
			w[replace.Replace(line)]++
		}
		if err := scan.Err(); err != nil {
			log.Fatal(err)
		}
	}
}

type platform struct {
	os   string
	arch string
}

func (p platform) String() string {
	return p.os + "/" + p.arch
}

// ignorePathPrefixes are file path prefixes that should be ignored wholesale.
var ignorePathPrefixes = [...]string{
	// These testdata dirs have lots of intentionally broken/bad code for tests.
	"cmd/go/testdata/",
	"cmd/vet/testdata/",
	"go/printer/testdata/",
	// fmt_test contains a known bad format string.
	// We cannot add it to any given whitelist,
	// because it won't show up for any non-host platform,
	// due to deficiencies in vet.
	// Just whitelist the whole file.
	// TODO: If vet ever uses go/loader and starts working off source,
	// this problem will likely go away.
	"fmt/fmt_test.go",
	// Ignore pprof for the moment to get the builders happy.
	// TODO: Fix all the issues and reinstate.
	"cmd/vendor/github.com/google/pprof",
}

func vetPlatforms(pp []platform) {
	ncpus := runtime.GOMAXPROCS(-1) / len(pp)
	if ncpus < 1 {
		ncpus = 1
	}
	var wg sync.WaitGroup
	wg.Add(len(pp))
	for _, p := range pp {
		p := p
		go func() {
			p.vet(ncpus)
			wg.Done()
		}()
	}
	wg.Wait()
}

func (p platform) vet(ncpus int) {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "go run main.go -p %s\n", p)

	// Load whitelist(s).
	w := make(whitelist)
	w.load(p.os, p.arch)

	env := append(os.Environ(), "GOOS="+p.os, "GOARCH="+p.arch)

	// Do 'go install std' before running vet.
	// It is cheap when already installed.
	// Not installing leads to non-obvious failures due to inability to typecheck.
	// TODO: If go/loader ever makes it to the standard library, have vet use it,
	// at which point vet can work off source rather than compiled packages.
	gcflags := ""
	if p != hostPlatform {
		gcflags = "-dolinkobj=false"
	}
	cmd := exec.Command(cmdGoPath, "install", "-p", strconv.Itoa(ncpus), "-gcflags="+gcflags, "std")
	cmd.Env = env
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("failed to run GOOS=%s GOARCH=%s 'go install std': %v\n%s", p.os, p.arch, err, out)
	}

	// 'go tool vet .' is considerably faster than 'go vet ./...'
	// TODO: The unsafeptr checks are disabled for now,
	// because there are so many false positives,
	// and no clear way to improve vet to eliminate large chunks of them.
	// And having them in the whitelists will just cause annoyance
	// and churn when working on the runtime.
	args := []string{"tool", "vet", "-unsafeptr=false"}
	if p != hostPlatform {
		// When not checking the host platform, vet gets confused by
		// the fmt.Formatters in cmd/compile,
		// so just skip the printf checks on non-host platforms for now.
		// There's not too much platform-specific code anyway.
		// TODO: If vet ever uses go/loader and starts working off source,
		// this problem will likely go away.
		args = append(args, "-printf=false")
	}
	args = append(args, ".")
	cmd = exec.Command(cmdGoPath, args...)
	cmd.Dir = filepath.Join(runtime.GOROOT(), "src")
	cmd.Env = env
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}
	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}

	// Process vet output.
	scan := bufio.NewScanner(stderr)
NextLine:
	for scan.Scan() {
		line := scan.Text()
		if strings.HasPrefix(line, "vet: ") {
			// Typecheck failure: Malformed syntax or multiple packages or the like.
			// This will yield nicer error messages elsewhere, so ignore them here.
			continue
		}

		fields := strings.SplitN(line, ":", 3)
		var file, lineno, msg string
		switch len(fields) {
		case 2:
			// vet message with no line number
			file, msg = fields[0], fields[1]
		case 3:
			file, lineno, msg = fields[0], fields[1], fields[2]
		default:
			log.Fatalf("could not parse vet output line:\n%s", line)
		}
		msg = strings.TrimSpace(msg)

		for _, ignore := range ignorePathPrefixes {
			if strings.HasPrefix(file, filepath.FromSlash(ignore)) {
				continue NextLine
			}
		}

		key := file + ": " + msg
		if w[key] == 0 {
			// Vet error with no match in the whitelist. Print it.
			if *flagNoLines {
				fmt.Fprintf(&buf, "%s: %s\n", file, msg)
			} else {
				fmt.Fprintf(&buf, "%s:%s: %s\n", file, lineno, msg)
			}
			atomic.StoreUint32(&failed, 1)
			continue
		}
		w[key]--
	}
	if scan.Err() != nil {
		log.Fatalf("failed to scan vet output: %v", scan.Err())
	}
	err = cmd.Wait()
	// We expect vet to fail.
	// Make sure it has failed appropriately, though (for example, not a PathError).
	if _, ok := err.(*exec.ExitError); !ok {
		log.Fatalf("unexpected go vet execution failure: %v", err)
	}
	printedHeader := false
	if len(w) > 0 {
		for k, v := range w {
			if v != 0 {
				if !printedHeader {
					fmt.Fprintln(&buf, "unmatched whitelist entries:")
					printedHeader = true
				}
				for i := 0; i < v; i++ {
					fmt.Fprintln(&buf, k)
				}
				atomic.StoreUint32(&failed, 1)
			}
		}
	}

	os.Stdout.Write(buf.Bytes())
}

// nbits maps from architecture names to the number of bits in a pointer.
// TODO: figure out a clean way to avoid get this info rather than listing it here yet again.
var nbits = map[string]int{
	"386":      32,
	"amd64":    64,
	"amd64p32": 32,
	"arm":      32,
	"arm64":    64,
	"mips":     32,
	"mipsle":   32,
	"mips64":   64,
	"mips64le": 64,
	"ppc64":    64,
	"ppc64le":  64,
	"s390x":    64,
}

// archAsmX maps architectures to the suffix usually used for their assembly files,
// if different than the arch name itself.
var archAsmX = map[string]string{
	"android":  "linux",
	"mips64":   "mips64x",
	"mips64le": "mips64x",
	"mips":     "mipsx",
	"mipsle":   "mipsx",
	"ppc64":    "ppc64x",
	"ppc64le":  "ppc64x",
}
<|endoftext|>