<commit_before>package herd

import (
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/filter"
	"github.com/Symantec/Dominator/lib/hash"
	subproto "github.com/Symantec/Dominator/proto/sub"
	"path"
	"syscall"
	"time"
)

type state struct {
	subFS                   *filesystem.FileSystem
	requiredFS              *filesystem.FileSystem
	requiredInodeToSubInode map[uint64]uint64
	inodesChanged           map[uint64]bool   // Required inode number.
	inodesCreated           map[uint64]string // Required inode number.
	subFilenameToInode      map[string]uint64
	subObjectCacheUsage     map[hash.Hash]uint64
}

// Returns true if no update needs to be performed.
func (sub *Sub) buildUpdateRequest(request *subproto.UpdateRequest) bool {
	var state state
	state.subFS = sub.fileSystem
	requiredImage := sub.herd.getImageNoError(sub.requiredImage)
	state.requiredFS = requiredImage.FileSystem
	filter := requiredImage.Filter
	request.Triggers = requiredImage.Triggers
	state.requiredInodeToSubInode = make(map[uint64]uint64)
	state.inodesChanged = make(map[uint64]bool)
	state.inodesCreated = make(map[uint64]string)
	state.subObjectCacheUsage = make(map[hash.Hash]uint64, len(sub.objectCache))
	var rusageStart, rusageStop syscall.Rusage
	syscall.Getrusage(syscall.RUSAGE_SELF, &rusageStart)
	// Populate subObjectCacheUsage.
	for _, hash := range sub.objectCache {
		state.subObjectCacheUsage[hash] = 0
	}
	compareDirectories(request, &state,
		&state.subFS.DirectoryInode, &state.requiredFS.DirectoryInode,
		"/", filter)
	// Look for multiply used objects and tell the sub.
	for obj, useCount := range state.subObjectCacheUsage {
		if useCount > 1 {
			if request.MultiplyUsedObjects == nil {
				request.MultiplyUsedObjects = make(map[hash.Hash]uint64)
			}
			request.MultiplyUsedObjects[obj] = useCount
		}
	}
	syscall.Getrusage(syscall.RUSAGE_SELF, &rusageStop)
	sub.lastComputeUpdateCpuDuration = time.Duration(
		rusageStop.Utime.Sec)*time.Second +
		time.Duration(rusageStop.Utime.Usec)*time.Microsecond -
		time.Duration(rusageStart.Utime.Sec)*time.Second -
		time.Duration(rusageStart.Utime.Usec)*time.Microsecond
	computeCpuTimeDistribution.Add(sub.lastComputeUpdateCpuDuration)
	if len(request.FilesToCopyToCache) > 0 ||
		len(request.InodesToMake) > 0 ||
		len(request.HardlinksToMake) > 0 ||
		len(request.PathsToDelete) > 0 ||
		len(request.DirectoriesToMake) > 0 ||
		len(request.InodesToChange) > 0 {
		sub.herd.logger.Printf(
			"buildUpdateRequest(%s) took: %s user CPU time\n",
			sub.hostname, sub.lastComputeUpdateCpuDuration)
		return false
	}
	return true
}

func compareDirectories(request *subproto.UpdateRequest, state *state,
	subDirectory, requiredDirectory *filesystem.DirectoryInode,
	myPathName string, filter *filter.Filter) {
	// First look for entries that should be deleted.
	if filter != nil && subDirectory != nil {
		for name := range subDirectory.EntriesByName {
			pathname := path.Join(myPathName, name)
			if filter.Match(pathname) {
				continue
			}
			if _, ok := requiredDirectory.EntriesByName[name]; !ok {
				request.PathsToDelete = append(request.PathsToDelete, pathname)
			}
		}
	}
	for name, requiredEntry := range requiredDirectory.EntriesByName {
		pathname := path.Join(myPathName, name)
		if filter != nil && filter.Match(pathname) {
			continue
		}
		var subEntry *filesystem.DirectoryEntry
		if subDirectory != nil {
			if se, ok := subDirectory.EntriesByName[name]; ok {
				subEntry = se
			}
		}
		if subEntry == nil {
			addEntry(request, state, requiredEntry, pathname)
		} else {
			compareEntries(request, state, subEntry, requiredEntry, pathname)
		}
		// If a directory: descend (possibly with the directory for the sub).
		requiredInode := requiredEntry.Inode()
		if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
			var subInode *filesystem.DirectoryInode
			if subEntry != nil {
				if si, ok := subEntry.Inode().(*filesystem.DirectoryInode); ok {
					subInode = si
				}
			}
			compareDirectories(request, state, subInode, requiredInode,
				pathname, filter)
		}
	}
}

func addEntry(request *subproto.UpdateRequest, state *state,
	requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	requiredInode := requiredEntry.Inode()
	if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
		makeDirectory(request, requiredInode, myPathName, true)
	} else {
		addInode(request, state, requiredEntry, myPathName)
	}
}

func compareEntries(request *subproto.UpdateRequest, state *state,
	subEntry, requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	subInode := subEntry.Inode()
	requiredInode := requiredEntry.Inode()
	sameType, sameMetadata, sameData := filesystem.CompareInodes(
		subInode, requiredInode, nil)
	if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
		if sameMetadata {
			return
		}
		if sameType {
			makeDirectory(request, requiredInode, myPathName, false)
		} else {
			makeDirectory(request, requiredInode, myPathName, true)
		}
		return
	}
	if sameType && sameData && sameMetadata {
		relink(request, state, subEntry, requiredEntry, myPathName)
		return
	}
	if sameType && sameData {
		updateMetadata(request, state, requiredEntry, myPathName)
		relink(request, state, subEntry, requiredEntry, myPathName)
		return
	}
	addInode(request, state, requiredEntry, myPathName)
}

func relink(request *subproto.UpdateRequest, state *state,
	subEntry, requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	subInum, ok := state.requiredInodeToSubInode[requiredEntry.InodeNumber]
	if !ok {
		state.requiredInodeToSubInode[requiredEntry.InodeNumber] =
			subEntry.InodeNumber
		return
	}
	if subInum == subEntry.InodeNumber {
		return
	}
	makeHardlink(request,
		myPathName, state.subFS.InodeToFilenamesTable[subInum][0])
}

func makeHardlink(request *subproto.UpdateRequest, newLink, target string) {
	var hardlink subproto.Hardlink
	hardlink.NewLink = newLink
	hardlink.Target = target
	request.HardlinksToMake = append(request.HardlinksToMake, hardlink)
}

func updateMetadata(request *subproto.UpdateRequest, state *state,
	requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	if state.inodesChanged[requiredEntry.InodeNumber] {
		return
	}
	var inode subproto.Inode
	inode.Name = myPathName
	inode.GenericInode = requiredEntry.Inode()
	request.InodesToChange = append(request.InodesToChange, inode)
	state.inodesChanged[requiredEntry.InodeNumber] = true
}

func makeDirectory(request *subproto.UpdateRequest,
	requiredInode *filesystem.DirectoryInode, pathName string, create bool) {
	var newInode subproto.Inode
	newInode.Name = pathName
	var newDirectoryInode filesystem.DirectoryInode
	newDirectoryInode.Mode = requiredInode.Mode
	newDirectoryInode.Uid = requiredInode.Uid
	newDirectoryInode.Gid = requiredInode.Gid
	newInode.GenericInode = &newDirectoryInode
	if create {
		request.DirectoriesToMake = append(request.DirectoriesToMake, newInode)
	} else {
		request.InodesToChange = append(request.InodesToChange, newInode)
	}
}

func addInode(request *subproto.UpdateRequest, state *state,
	requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	requiredInode := requiredEntry.Inode()
	if name, ok := state.inodesCreated[requiredEntry.InodeNumber]; ok {
		makeHardlink(request, myPathName, name)
		return
	}
	// Try to find a sibling inode.
	names := state.requiredFS.InodeToFilenamesTable[requiredEntry.InodeNumber]
	if len(names) > 1 {
		var sameDataInode filesystem.GenericInode
		var sameDataName string
		for _, name := range names {
			if inum, found := state.getSubInodeFromFilename(name); found {
				subInode := state.subFS.InodeTable[inum]
				_, sameMetadata, sameData := filesystem.CompareInodes(
					subInode, requiredInode, nil)
				if sameMetadata && sameData {
					makeHardlink(request, myPathName, name)
					return
				}
				if sameData {
					sameDataInode = subInode
					sameDataName = name
				}
			}
		}
		if sameDataInode != nil {
			updateMetadata(request, state, requiredEntry, sameDataName)
			makeHardlink(request, myPathName, sameDataName)
			return
		}
	}
	if inode, ok := requiredEntry.Inode().(*filesystem.RegularInode); ok {
		if inode.Size > 0 {
			if _, ok := state.subObjectCacheUsage[inode.Hash]; ok {
				state.subObjectCacheUsage[inode.Hash]++
			} else {
				// Not in object cache: grab it from file-system.
				if state.subFS.HashToInodesTable == nil {
					state.subFS.BuildHashToInodesTable()
				}
				if ilist, ok := state.subFS.HashToInodesTable[inode.Hash]; ok {
					var fileToCopy subproto.FileToCopyToCache
					fileToCopy.Name =
						state.subFS.InodeToFilenamesTable[ilist[0]][0]
					fileToCopy.Hash = inode.Hash
					request.FilesToCopyToCache = append(
						request.FilesToCopyToCache, fileToCopy)
					state.subObjectCacheUsage[inode.Hash] = 1
				} else {
					panic("No object in cache for: " + myPathName)
				}
			}
		}
	}
	var inode subproto.Inode
	inode.Name = myPathName
	inode.GenericInode = requiredEntry.Inode()
	request.InodesToMake = append(request.InodesToMake, inode)
	state.inodesCreated[requiredEntry.InodeNumber] = myPathName
}

func (state *state) getSubInodeFromFilename(name string) (uint64, bool) {
	if state.subFilenameToInode == nil {
		state.subFilenameToInode = make(map[string]uint64)
		for inum, names := range state.subFS.InodeToFilenamesTable {
			for _, n := range names {
				state.subFilenameToInode[n] = inum
			}
		}
	}
	inum, ok := state.subFilenameToInode[name]
	return inum, ok
}
<commit_msg>Fix bug: update metadata for root directory.<commit_after>package herd

import (
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/filter"
	"github.com/Symantec/Dominator/lib/hash"
	subproto "github.com/Symantec/Dominator/proto/sub"
	"path"
	"syscall"
	"time"
)

type state struct {
	subFS                   *filesystem.FileSystem
	requiredFS              *filesystem.FileSystem
	requiredInodeToSubInode map[uint64]uint64
	inodesChanged           map[uint64]bool   // Required inode number.
	inodesCreated           map[uint64]string // Required inode number.
	subFilenameToInode      map[string]uint64
	subObjectCacheUsage     map[hash.Hash]uint64
}

// Returns true if no update needs to be performed.
func (sub *Sub) buildUpdateRequest(request *subproto.UpdateRequest) bool {
	var state state
	state.subFS = sub.fileSystem
	requiredImage := sub.herd.getImageNoError(sub.requiredImage)
	state.requiredFS = requiredImage.FileSystem
	filter := requiredImage.Filter
	request.Triggers = requiredImage.Triggers
	state.requiredInodeToSubInode = make(map[uint64]uint64)
	state.inodesChanged = make(map[uint64]bool)
	state.inodesCreated = make(map[uint64]string)
	state.subObjectCacheUsage = make(map[hash.Hash]uint64, len(sub.objectCache))
	var rusageStart, rusageStop syscall.Rusage
	syscall.Getrusage(syscall.RUSAGE_SELF, &rusageStart)
	// Populate subObjectCacheUsage.
	for _, hash := range sub.objectCache {
		state.subObjectCacheUsage[hash] = 0
	}
	if !filesystem.CompareDirectoriesMetadata(&state.subFS.DirectoryInode,
		&state.requiredFS.DirectoryInode, nil) {
		makeDirectory(request, &state.requiredFS.DirectoryInode, "/", false)
	}
	compareDirectories(request, &state,
		&state.subFS.DirectoryInode, &state.requiredFS.DirectoryInode,
		"/", filter)
	// Look for multiply used objects and tell the sub.
	for obj, useCount := range state.subObjectCacheUsage {
		if useCount > 1 {
			if request.MultiplyUsedObjects == nil {
				request.MultiplyUsedObjects = make(map[hash.Hash]uint64)
			}
			request.MultiplyUsedObjects[obj] = useCount
		}
	}
	syscall.Getrusage(syscall.RUSAGE_SELF, &rusageStop)
	sub.lastComputeUpdateCpuDuration = time.Duration(
		rusageStop.Utime.Sec)*time.Second +
		time.Duration(rusageStop.Utime.Usec)*time.Microsecond -
		time.Duration(rusageStart.Utime.Sec)*time.Second -
		time.Duration(rusageStart.Utime.Usec)*time.Microsecond
	computeCpuTimeDistribution.Add(sub.lastComputeUpdateCpuDuration)
	if len(request.FilesToCopyToCache) > 0 ||
		len(request.InodesToMake) > 0 ||
		len(request.HardlinksToMake) > 0 ||
		len(request.PathsToDelete) > 0 ||
		len(request.DirectoriesToMake) > 0 ||
		len(request.InodesToChange) > 0 {
		sub.herd.logger.Printf(
			"buildUpdateRequest(%s) took: %s user CPU time\n",
			sub.hostname, sub.lastComputeUpdateCpuDuration)
		return false
	}
	return true
}

func compareDirectories(request *subproto.UpdateRequest, state *state,
	subDirectory, requiredDirectory *filesystem.DirectoryInode,
	myPathName string, filter *filter.Filter) {
	// First look for entries that should be deleted.
	if filter != nil && subDirectory != nil {
		for name := range subDirectory.EntriesByName {
			pathname := path.Join(myPathName, name)
			if filter.Match(pathname) {
				continue
			}
			if _, ok := requiredDirectory.EntriesByName[name]; !ok {
				request.PathsToDelete = append(request.PathsToDelete, pathname)
			}
		}
	}
	for name, requiredEntry := range requiredDirectory.EntriesByName {
		pathname := path.Join(myPathName, name)
		if filter != nil && filter.Match(pathname) {
			continue
		}
		var subEntry *filesystem.DirectoryEntry
		if subDirectory != nil {
			if se, ok := subDirectory.EntriesByName[name]; ok {
				subEntry = se
			}
		}
		if subEntry == nil {
			addEntry(request, state, requiredEntry, pathname)
		} else {
			compareEntries(request, state, subEntry, requiredEntry, pathname)
		}
		// If a directory: descend (possibly with the directory for the sub).
		requiredInode := requiredEntry.Inode()
		if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
			var subInode *filesystem.DirectoryInode
			if subEntry != nil {
				if si, ok := subEntry.Inode().(*filesystem.DirectoryInode); ok {
					subInode = si
				}
			}
			compareDirectories(request, state, subInode, requiredInode,
				pathname, filter)
		}
	}
}

func addEntry(request *subproto.UpdateRequest, state *state,
	requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	requiredInode := requiredEntry.Inode()
	if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
		makeDirectory(request, requiredInode, myPathName, true)
	} else {
		addInode(request, state, requiredEntry, myPathName)
	}
}

func compareEntries(request *subproto.UpdateRequest, state *state,
	subEntry, requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	subInode := subEntry.Inode()
	requiredInode := requiredEntry.Inode()
	sameType, sameMetadata, sameData := filesystem.CompareInodes(
		subInode, requiredInode, nil)
	if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
		if sameMetadata {
			return
		}
		if sameType {
			makeDirectory(request, requiredInode, myPathName, false)
		} else {
			makeDirectory(request, requiredInode, myPathName, true)
		}
		return
	}
	if sameType && sameData && sameMetadata {
		relink(request, state, subEntry, requiredEntry, myPathName)
		return
	}
	if sameType && sameData {
		updateMetadata(request, state, requiredEntry, myPathName)
		relink(request, state, subEntry, requiredEntry, myPathName)
		return
	}
	addInode(request, state, requiredEntry, myPathName)
}

func relink(request *subproto.UpdateRequest, state *state,
	subEntry, requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	subInum, ok := state.requiredInodeToSubInode[requiredEntry.InodeNumber]
	if !ok {
		state.requiredInodeToSubInode[requiredEntry.InodeNumber] =
			subEntry.InodeNumber
		return
	}
	if subInum == subEntry.InodeNumber {
		return
	}
	makeHardlink(request,
		myPathName, state.subFS.InodeToFilenamesTable[subInum][0])
}

func makeHardlink(request *subproto.UpdateRequest, newLink, target string) {
	var hardlink subproto.Hardlink
	hardlink.NewLink = newLink
	hardlink.Target = target
	request.HardlinksToMake = append(request.HardlinksToMake, hardlink)
}

func updateMetadata(request *subproto.UpdateRequest, state *state,
	requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	if state.inodesChanged[requiredEntry.InodeNumber] {
		return
	}
	var inode subproto.Inode
	inode.Name = myPathName
	inode.GenericInode = requiredEntry.Inode()
	request.InodesToChange = append(request.InodesToChange, inode)
	state.inodesChanged[requiredEntry.InodeNumber] = true
}

func makeDirectory(request *subproto.UpdateRequest,
	requiredInode *filesystem.DirectoryInode, pathName string, create bool) {
	var newInode subproto.Inode
	newInode.Name = pathName
	var newDirectoryInode filesystem.DirectoryInode
	newDirectoryInode.Mode = requiredInode.Mode
	newDirectoryInode.Uid = requiredInode.Uid
	newDirectoryInode.Gid = requiredInode.Gid
	newInode.GenericInode = &newDirectoryInode
	if create {
		request.DirectoriesToMake = append(request.DirectoriesToMake, newInode)
	} else {
		request.InodesToChange = append(request.InodesToChange, newInode)
	}
}

func addInode(request *subproto.UpdateRequest, state *state,
	requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	requiredInode := requiredEntry.Inode()
	if name, ok := state.inodesCreated[requiredEntry.InodeNumber]; ok {
		makeHardlink(request, myPathName, name)
		return
	}
	// Try to find a sibling inode.
	names := state.requiredFS.InodeToFilenamesTable[requiredEntry.InodeNumber]
	if len(names) > 1 {
		var sameDataInode filesystem.GenericInode
		var sameDataName string
		for _, name := range names {
			if inum, found := state.getSubInodeFromFilename(name); found {
				subInode := state.subFS.InodeTable[inum]
				_, sameMetadata, sameData := filesystem.CompareInodes(
					subInode, requiredInode, nil)
				if sameMetadata && sameData {
					makeHardlink(request, myPathName, name)
					return
				}
				if sameData {
					sameDataInode = subInode
					sameDataName = name
				}
			}
		}
		if sameDataInode != nil {
			updateMetadata(request, state, requiredEntry, sameDataName)
			makeHardlink(request, myPathName, sameDataName)
			return
		}
	}
	if inode, ok := requiredEntry.Inode().(*filesystem.RegularInode); ok {
		if inode.Size > 0 {
			if _, ok := state.subObjectCacheUsage[inode.Hash]; ok {
				state.subObjectCacheUsage[inode.Hash]++
			} else {
				// Not in object cache: grab it from file-system.
				if state.subFS.HashToInodesTable == nil {
					state.subFS.BuildHashToInodesTable()
				}
				if ilist, ok := state.subFS.HashToInodesTable[inode.Hash]; ok {
					var fileToCopy subproto.FileToCopyToCache
					fileToCopy.Name =
						state.subFS.InodeToFilenamesTable[ilist[0]][0]
					fileToCopy.Hash = inode.Hash
					request.FilesToCopyToCache = append(
						request.FilesToCopyToCache, fileToCopy)
					state.subObjectCacheUsage[inode.Hash] = 1
				} else {
					panic("No object in cache for: " + myPathName)
				}
			}
		}
	}
	var inode subproto.Inode
	inode.Name = myPathName
	inode.GenericInode = requiredEntry.Inode()
	request.InodesToMake = append(request.InodesToMake, inode)
	state.inodesCreated[requiredEntry.InodeNumber] = myPathName
}

func (state *state) getSubInodeFromFilename(name string) (uint64, bool) {
	if state.subFilenameToInode == nil {
		state.subFilenameToInode = make(map[string]uint64)
		for inum, names := range state.subFS.InodeToFilenamesTable {
			for _, n := range names {
				state.subFilenameToInode[n] = inum
			}
		}
	}
	inum, ok := state.subFilenameToInode[name]
	return inum, ok
}
<|endoftext|>


<commit_before>package isogrids

import (
	"image/color"
	"math"
	"net/http"

	"github.com/ajstarks/svgo"
	"github.com/taironas/tinygraphs/draw"
)

// Hexa builds an image with 10x10 grids of half diagonals
func Hexa(w http.ResponseWriter, key string, colors []color.RGBA, size, lines int) {
	canvas := svg.New(w)
	canvas.Start(size, size)

	fringeSize := size / lines
	// distance of center of vector to third point of equilateral triangles
	// ABC triangle, O is the center of AB vector
	// OC = SQRT(AC^2 - AO^2)
	distance := int(math.Ceil(math.Sqrt((float64(fringeSize) * float64(fringeSize)) - (float64(fringeSize)/float64(2))*(float64(fringeSize)/float64(2)))))
	fringeSize = distance
	for xL := -1; xL < lines/2; xL++ {
		for yL := -1; yL <= lines; yL++ {

			fill1 := fillWhite()
			fill2 := fillWhite()
			if isFill1InHexagon(xL, yL, lines) {
				fill1 = draw.FillFromRGBA(draw.PickColor(key, colors, (xL+3*yL+lines)%15))
			}
			if isFill2InHexagon(xL, yL, lines) {
				fill2 = draw.FillFromRGBA(draw.PickColor(key, colors, (xL+3*yL+1+lines)%15))
			}

			if !isFill1InHexagon(xL, yL, lines) && !isFill2InHexagon(xL, yL, lines) {
				continue
			}

			var x1, x2, x3, y1, y2, y3 int
			if (xL % 2) == 0 {
				x1 = xL * fringeSize
				x2 = xL*fringeSize + distance
				x3 = x1
				y1 = yL * fringeSize
				y2 = y1 + fringeSize/2
				y3 = yL*fringeSize + distance
			} else {
				x1 = xL*fringeSize + distance
				x2 = xL * fringeSize
				x3 = x1
				y1 = yL * fringeSize
				y2 = y1 + fringeSize/2
				y3 = yL*fringeSize + distance
			}
			xs := []int{x1, x2, x3}
			ys := []int{y1, y2, y3}

			if lines%4 != 0 {
				xs[0] = x2
				xs[1] = x1
				xs[2] = x2
			}

			canvas.Polygon(xs, ys, fill1)

			xs[0] = (lines * fringeSize) - xs[0]
			xs[1] = (lines * fringeSize) - xs[1]
			xs[2] = (lines * fringeSize) - xs[2]
			canvas.Polygon(xs, ys, fill1)

			var x11, x12, x13, y11, y12, y13 int
			if (xL % 2) == 0 {
				x11 = xL*fringeSize + distance
				x12 = (xL) * fringeSize
				x13 = x11
				y11 = yL*fringeSize + fringeSize/2
				y12 = y11 + fringeSize/2
				y13 = yL*fringeSize + (fringeSize / 2) + distance
			} else {
				x11 = xL * fringeSize
				x12 = xL*fringeSize + distance
				x13 = x11
				y11 = yL*fringeSize + fringeSize/2
				y12 = y1 + fringeSize
				y13 = yL*fringeSize + (fringeSize / 2) + distance
			}
			xs1 := []int{x11, x12, x13}
			ys1 := []int{y11, y12, y13}
			if lines%4 != 0 {
				xs1[0] = x12
				xs1[1] = x11
				xs1[2] = x12
			}

			canvas.Polygon(xs1, ys1, fill2)
			xs1[0] = (lines * fringeSize) - xs1[0]
			xs1[1] = (lines * fringeSize) - xs1[1]
			xs1[2] = (lines * fringeSize) - xs1[2]
			canvas.Polygon(xs1, ys1, fill2)
		}
	}
	canvas.End()
}

func isFill1InHexagon(xL, yL, lines int) bool {
	if lines%6 == 0 {
		half := lines / 2
		start := half / 2
		if xL < start+1 {
			if yL > start-1 && yL < start+half+1 {
				return true
			}
		}
		if xL == half-1 {
			if yL > start-1-1 && yL < start+half+1+1 {
				return true
			}
		}
		return false
	} else if lines%4 == 0 {
		if xL == 0 {
			if yL > 1 && yL < 6 {
				return true
			}
		}
		if xL == 1 || xL == 2 {
			if yL > 0 && yL < 7 {
				return true
			}
		}
		if xL == 3 {
			if yL >= 0 && yL <= 7 {
				return true
			}
		}
		return false
	}
	return false
}

func isFill2InHexagon(xL, yL, lines int) bool {
	if lines%6 == 0 {
		half := lines / 2
		start := half / 2

		if xL < start {
			if yL > start-1 && yL < start+half {
				return true
			}
		}
		if xL == 1 {
			if yL > start-1-1 && yL < start+half+1 {
				return true
			}
		}
		if xL == half-1 {
			if yL > start-1-1 && yL < start+half+1 {
				return true
			}
		}
	} else if lines%4 == 0 {
		if xL == 0 || xL == 1 {
			if yL > 0 && yL < 6 {
				return true
			}
		}
		if xL == 1 {
			if yL > 0 && yL < 6 {
				return true
			}
		}
		if xL == 2 || xL == 3 {
			if yL >= 0 && yL <= 6 {
				return true
			}
		}
	}
	return false
}

func fillWhite() string {
	return "fill:rgb(255,255,255)"

}
<commit_msg>fix bug in hexa display when hexalines = 6 #37<commit_after>package isogrids

import (
	"image/color"
	"math"
	"net/http"

	"github.com/ajstarks/svgo"
	"github.com/taironas/tinygraphs/draw"
)

// Hexa builds an image with 10x10 grids of half diagonals
func Hexa(w http.ResponseWriter, key string, colors []color.RGBA, size, lines int) {
	canvas := svg.New(w)
	canvas.Start(size, size)

	fringeSize := size / lines
	// distance of center of vector to third point of equilateral triangles
	// ABC triangle, O is the center of AB vector
	// OC = SQRT(AC^2 - AO^2)
	distance := int(math.Ceil(math.Sqrt((float64(fringeSize) * float64(fringeSize)) - (float64(fringeSize)/float64(2))*(float64(fringeSize)/float64(2)))))
	fringeSize = distance
	for xL := 0; xL < lines/2; xL++ {
		for yL := 0; yL < lines; yL++ {

			fill1 := fillWhite()
			fill2 := fillWhite()
			if isFill1InHexagon(xL, yL, lines) {
				fill1 = draw.FillFromRGBA(draw.PickColor(key, colors, (xL+3*yL+lines)%15))
			}
			if isFill2InHexagon(xL, yL, lines) {
				fill2 = draw.FillFromRGBA(draw.PickColor(key, colors, (xL+3*yL+1+lines)%15))
			}

			if !isFill1InHexagon(xL, yL, lines) && !isFill2InHexagon(xL, yL, lines) {
				continue
			}

			var x1, x2, x3, y1, y2, y3 int
			if (xL % 2) == 0 {
				x1 = xL * fringeSize
				x2 = xL*fringeSize + distance
				x3 = x1
				y1 = yL * fringeSize
				y2 = y1 + fringeSize/2
				y3 = yL*fringeSize + distance
			} else {
				x1 = xL*fringeSize + distance
				x2 = xL * fringeSize
				x3 = x1
				y1 = yL * fringeSize
				y2 = y1 + fringeSize/2
				y3 = yL*fringeSize + distance
			}
			xs := []int{x1, x2, x3}
			ys := []int{y1, y2, y3}

			if lines%4 != 0 {
				xs[0] = x2
				xs[1] = x1
				xs[2] = x2
			}

			canvas.Polygon(xs, ys, fill1)

			xs[0] = (lines * fringeSize) - xs[0]
			xs[1] = (lines * fringeSize) - xs[1]
			xs[2] = (lines * fringeSize) - xs[2]
			canvas.Polygon(xs, ys, fill1)

			var x11, x12, x13, y11, y12, y13 int
			if (xL % 2) == 0 {
				x11 = xL*fringeSize + distance
				x12 = (xL) * fringeSize
				x13 = x11
				y11 = yL*fringeSize + fringeSize/2
				y12 = y11 + fringeSize/2
				y13 = yL*fringeSize + (fringeSize / 2) + distance
			} else {
				x11 = xL * fringeSize
				x12 = xL*fringeSize + distance
				x13 = x11
				y11 = yL*fringeSize + fringeSize/2
				y12 = y1 + fringeSize
				y13 = yL*fringeSize + (fringeSize / 2) + distance
			}
			xs1 := []int{x11, x12, x13}
			ys1 := []int{y11, y12, y13}
			if lines%4 != 0 {
				xs1[0] = x12
				xs1[1] = x11
				xs1[2] = x12
			}

			canvas.Polygon(xs1, ys1, fill2)
			xs1[0] = (lines * fringeSize) - xs1[0]
			xs1[1] = (lines * fringeSize) - xs1[1]
			xs1[2] = (lines * fringeSize) - xs1[2]
			canvas.Polygon(xs1, ys1, fill2)
		}
	}
	canvas.End()
}

func isFill1InHexagon(xL, yL, lines int) bool {
	if lines%6 == 0 {
		half := lines / 2
		start := half / 2
		if xL < start+1 {
			if yL > start-1 && yL < start+half+1 {
				return true
			}
		}
		if xL == half-1 {
			if yL > start-1-1 && yL < start+half+1+1 {
				return true
			}
		}
		return false
	} else if lines%4 == 0 {
		if xL == 0 {
			if yL > 1 && yL < 6 {
				return true
			}
		}
		if xL == 1 || xL == 2 {
			if yL > 0 && yL < 7 {
				return true
			}
		}
		if xL == 3 {
			if yL >= 0 && yL <= 7 {
				return true
			}
		}
		return false
	}
	return false
}

func isFill2InHexagon(xL, yL, lines int) bool {
	if lines%6 == 0 {
		half := lines / 2
		start := half / 2

		if xL < start {
			if yL > start-1 && yL < start+half {
				return true
			}
		}
		if xL == 1 {
			if yL > start-1-1 && yL < start+half+1 {
				return true
			}
		}
		if xL == half-1 {
			if yL > start-1-1 && yL < start+half+1 {
				return true
			}
		}
	} else if lines%4 == 0 {
		if xL == 0 || xL == 1 {
			if yL > 0 && yL < 6 {
				return true
			}
		}
		if xL == 1 {
			if yL > 0 && yL < 6 {
				return true
			}
		}
		if xL == 2 || xL == 3 {
			if yL >= 0 && yL <= 6 {
				return true
			}
		}
	}
	return false
}

func fillWhite() string {
	return "fill:rgb(255,255,255)"

}
<|endoftext|>


<commit_before>package chat

import (
	"bytes"
	"crypto/rand"
	"crypto/sha256"
	"io/ioutil"
	"strings"
	"testing"

	"github.com/keybase/client/go/chat/s3"
	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/chat1"
	"golang.org/x/net/context"
)

func TestSignEncrypter(t *testing.T) {
	e := NewSignEncrypter()
	el := e.EncryptedLen(100)
	if el != 180 {
		t.Errorf("enc len: %d, expected 180", el)
	}

	el = e.EncryptedLen(50 * 1024 * 1024)
	if el != 52432880 {
		t.Errorf("enc len: %d, expected 52432880", el)
	}

	pt := "plain text"
	er, err := e.Encrypt(strings.NewReader(pt))
	if err != nil {
		t.Fatal(err)
	}
	ct, err := ioutil.ReadAll(er)
	if err != nil {
		t.Fatal(err)
	}

	if string(ct) == pt {
		t.Fatal("Encrypt did not change plaintext")
	}

	d := NewSignDecrypter()
	dr := d.Decrypt(bytes.NewReader(ct), e.EncryptKey(), e.VerifyKey())
	ptOut, err := ioutil.ReadAll(dr)
	if err != nil {
		t.Fatal(err)
	}
	if string(ptOut) != pt {
		t.Errorf("decrypted ciphertext doesn't match plaintext: %q, expected %q", ptOut, pt)
	}

	// reuse e to do another Encrypt, make sure keys change:
	firstEncKey := e.EncryptKey()
	firstVerifyKey := e.VerifyKey()

	er2, err := e.Encrypt(strings.NewReader(pt))
	if err != nil {
		t.Fatal(err)
	}
	ct2, err := ioutil.ReadAll(er2)
	if err != nil {
		t.Fatal(err)
	}

	if string(ct2) == pt {
		t.Fatal("Encrypt did not change plaintext")
	}
	if bytes.Equal(ct, ct2) {
		t.Fatal("second Encrypt result same as first")
	}
	if bytes.Equal(firstEncKey, e.EncryptKey()) {
		t.Fatal("first enc key reused")
	}
	if bytes.Equal(firstVerifyKey, e.VerifyKey()) {
		t.Fatal("first verify key reused")
	}

	dr2 := d.Decrypt(bytes.NewReader(ct2), e.EncryptKey(), e.VerifyKey())
	ptOut2, err := ioutil.ReadAll(dr2)
	if err != nil {
		t.Fatal(err)
	}
	if string(ptOut2) != pt {
		t.Errorf("decrypted ciphertext doesn't match plaintext: %q, expected %q", ptOut2, pt)
	}
}

func makeTestStore(t *testing.T) *AttachmentStore {
	s := NewAttachmentStore(logger.NewTestLogger(t))

	// use in-memory implementation of s3 interface
	s.s3c = &s3.Mem{}

	return s
}

func randBytes(t *testing.T, n int) []byte {
	buf := make([]byte, n)
	if _, err := rand.Read(buf); err != nil {
		t.Fatal(err)
	}
	return buf
}

type ptsigner struct{}

func (p *ptsigner) Sign(payload []byte) ([]byte, error) {
	s := sha256.Sum256(payload)
	return s[:], nil
}

func TestUploadAssetSmall(t *testing.T) {
	s := makeTestStore(t)
	ctx := context.Background()
	plaintext := randBytes(t, 1024)
	task := &UploadTask{
		S3Params:       chat1.S3Params{},
		LocalSrc:       chat1.LocalSource{},
		Plaintext:      bytes.NewReader(plaintext),
		PlaintextHash:  randBytes(t, 16),
		S3Signer:       &ptsigner{},
		ConversationID: randBytes(t, 16),
	}
	a, err := s.UploadAsset(ctx, task)
	if err != nil {
		t.Fatal(err)
	}
	_ = a
}
<commit_msg>Add Upload small, large tests<commit_after>package chat

import (
	"bytes"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"io/ioutil"
	"strings"
	"testing"

	"github.com/keybase/client/go/chat/s3"
	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/chat1"
	"golang.org/x/net/context"
)

const MB = 1024 * 1024

func TestSignEncrypter(t *testing.T) {
	e := NewSignEncrypter()
	el := e.EncryptedLen(100)
	if el != 180 {
		t.Errorf("enc len: %d, expected 180", el)
	}

	el = e.EncryptedLen(50 * 1024 * 1024)
	if el != 52432880 {
		t.Errorf("enc len: %d, expected 52432880", el)
	}

	pt := "plain text"
	er, err := e.Encrypt(strings.NewReader(pt))
	if err != nil {
		t.Fatal(err)
	}
	ct, err := ioutil.ReadAll(er)
	if err != nil {
		t.Fatal(err)
	}

	if string(ct) == pt {
		t.Fatal("Encrypt did not change plaintext")
	}

	d := NewSignDecrypter()
	dr := d.Decrypt(bytes.NewReader(ct), e.EncryptKey(), e.VerifyKey())
	ptOut, err := ioutil.ReadAll(dr)
	if err != nil {
		t.Fatal(err)
	}
	if string(ptOut) != pt {
		t.Errorf("decrypted ciphertext doesn't match plaintext: %q, expected %q", ptOut, pt)
	}

	// reuse e to do another Encrypt, make sure keys change:
	firstEncKey := e.EncryptKey()
	firstVerifyKey := e.VerifyKey()

	er2, err := e.Encrypt(strings.NewReader(pt))
	if err != nil {
		t.Fatal(err)
	}
	ct2, err := ioutil.ReadAll(er2)
	if err != nil {
		t.Fatal(err)
	}

	if string(ct2) == pt {
		t.Fatal("Encrypt did not change plaintext")
	}
	if bytes.Equal(ct, ct2) {
		t.Fatal("second Encrypt result same as first")
	}
	if bytes.Equal(firstEncKey, e.EncryptKey()) {
		t.Fatal("first enc key reused")
	}
	if bytes.Equal(firstVerifyKey, e.VerifyKey()) {
		t.Fatal("first verify key reused")
	}

	dr2 := d.Decrypt(bytes.NewReader(ct2), e.EncryptKey(), e.VerifyKey())
	ptOut2, err := ioutil.ReadAll(dr2)
	if err != nil {
		t.Fatal(err)
	}
	if string(ptOut2) != pt {
		t.Errorf("decrypted ciphertext doesn't match plaintext: %q, expected %q", ptOut2, pt)
	}
}

func makeTestStore(t *testing.T) *AttachmentStore {
	s := NewAttachmentStore(logger.NewTestLogger(t))

	// use in-memory implementation of s3 interface
	s.s3c = &s3.Mem{}

	return s
}

func randBytes(t *testing.T, n int) []byte {
	buf := make([]byte, n)
	if _, err := rand.Read(buf); err != nil {
		t.Fatal(err)
	}
	return buf
}

func randString(t *testing.T, nbytes int) string {
	return hex.EncodeToString(randBytes(t, nbytes))
}

type ptsigner struct{}

func (p *ptsigner) Sign(payload []byte) ([]byte, error) {
	s := sha256.Sum256(payload)
	return s[:], nil
}

func makeUploadTask(t *testing.T, size int) (plaintext []byte, task *UploadTask) {
	plaintext = randBytes(t, size)
	task = &UploadTask{
		S3Params: chat1.S3Params{
			Bucket:    "upload-test",
			ObjectKey: randString(t, 8),
		},
		LocalSrc: chat1.LocalSource{
			Filename: randString(t, 8),
			Size:     size,
		},
		Plaintext:      bytes.NewReader(plaintext),
		PlaintextHash:  randBytes(t, 16),
		S3Signer:       &ptsigner{},
		ConversationID: randBytes(t, 16),
	}
	return plaintext, task
}

func TestUploadAssetSmall(t *testing.T) {
	s := makeTestStore(t)
	ctx := context.Background()
	plaintext, task := makeUploadTask(t, 1*MB)
	_ = plaintext
	a, err := s.UploadAsset(ctx, task)
	if err != nil {
		t.Fatal(err)
	}

	var buf bytes.Buffer
	if err = s.DownloadAsset(ctx, task.S3Params, a, &buf, task.S3Signer, nil); err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(plaintext, buf.Bytes()) {
		t.Errorf("downloaded asset did not match uploaded asset")
	}
}

func TestUploadAssetLarge(t *testing.T) {
	s := makeTestStore(t)
	ctx := context.Background()
	plaintext, task := makeUploadTask(t, 12*MB)
	_ = plaintext
	a, err := s.UploadAsset(ctx, task)
	if err != nil {
		t.Fatal(err)
	}

	var buf bytes.Buffer
	if err = s.DownloadAsset(ctx, task.S3Params, a, &buf, task.S3Signer, nil); err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(plaintext, buf.Bytes()) {
		t.Errorf("downloaded asset did not match uploaded asset")
	}
}
<|endoftext|>


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

package importer

import (
	"bufio"
	"bytes"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"testing"
	"time"

	"code.google.com/p/go.tools/go/gcimporter"
	"code.google.com/p/go.tools/go/types"
)

var tests = []string{
	`package p`,

	// consts
	`package p; const X = true`,
	`package p; const X, y, Z = true, false, 0 != 0`,
	`package p; const ( A float32 = 1<<iota; B; C; D)`,
	`package p; const X = "foo"`,
	`package p; const X string = "foo"`,
	`package p; const X = 0`,
	`package p; const X = -42`,
	`package p; const X = 3.14159265`,
	`package p; const X = -1e-10`,
	`package p; const X = 1.2 + 2.3i`,
	`package p; const X = -1i`,
	`package p; import "math"; const Pi = math.Pi`,
	`package p; import m "math"; const Pi = m.Pi`,

	// types
	`package p; type T int`,
	`package p; type T [10]int`,
	`package p; type T []int`,
	`package p; type T struct{}`,
	`package p; type T struct{x int}`,
	`package p; type T *int`,
	`package p; type T func()`,
	`package p; type T *T`,
	`package p; type T interface{}`,
	`package p; type T interface{ foo() }`,
	`package p; type T interface{ m() T }`,
	// TODO(gri) disabled for now - import/export works but
	// types.Type.String() used in the test cannot handle cases
	// like this yet
	// `package p; type T interface{ m() interface{T} }`,
	`package p; type T map[string]bool`,
	`package p; type T chan int`,
	`package p; type T <-chan complex64`,
	`package p; type T chan<- map[int]string`,
	// test case for issue 8177
	`package p; type T1 interface { F(T2) }; type T2 interface { T1 }`,

	// vars
	`package p; var X int`,
	`package p; var X, Y, Z struct{f int "tag"}`,

	// funcs
	`package p; func F()`,
	`package p; func F(x int, y struct{}) bool`,
	`package p; type T int; func (*T) F(x int, y struct{}) T`,

	// selected special cases
	`package p; type T int`,
	`package p; type T uint8`,
	`package p; type T byte`,
	`package p; type T error`,
	`package p; import "net/http"; type T http.Client`,
	`package p; import "net/http"; type ( T1 http.Client; T2 struct { http.Client } )`,
	`package p; import "unsafe"; type ( T1 unsafe.Pointer; T2 unsafe.Pointer )`,
	`package p; import "unsafe"; type T struct { p unsafe.Pointer }`,
}

func TestImportSrc(t *testing.T) {
	for _, src := range tests {
		pkg, err := pkgForSource(src)
		if err != nil {
			t.Errorf("typecheck failed: %s", err)
			continue
		}
		testExportImport(t, pkg, "")
	}
}

func TestImportStdLib(t *testing.T) {
	start := time.Now()

	libs, err := stdLibs()
	if err != nil {
		t.Fatalf("could not compute list of std libraries: %s", err)
	}

	// make sure printed go/types types and gc-imported types
	// can be compared reasonably well
	types.GcCompatibilityMode = true

	var totSize, totGcSize int
	for _, lib := range libs {
		// limit run time for short tests
		if testing.Short() && time.Since(start) >= 750*time.Millisecond {
			return
		}

		pkg, err := pkgForPath(lib)
		switch err := err.(type) {
		case nil:
			// ok
		case *build.NoGoError:
			// no Go files - ignore
			continue
		default:
			t.Errorf("typecheck failed: %s", err)
			continue
		}

		size, gcsize := testExportImport(t, pkg, lib)
		if gcsize == 0 {
			// if gc import didn't happen, assume same size
			// (and avoid division by zero below)
			gcsize = size
		}

		if testing.Verbose() {
			fmt.Printf("%s\t%d\t%d\t%d%%\n", lib, size, gcsize, int(float64(size)*100/float64(gcsize)))
		}
		totSize += size
		totGcSize += gcsize
	}

	if testing.Verbose() {
		fmt.Printf("\n%d\t%d\t%d%%\n", totSize, totGcSize, int(float64(totSize)*100/float64(totGcSize)))
	}

	types.GcCompatibilityMode = false
}

func testExportImport(t *testing.T, pkg0 *types.Package, path string) (size, gcsize int) {
	data := ExportData(pkg0)
	size = len(data)

	imports := make(map[string]*types.Package)
	n, pkg1, err := ImportData(imports, data)
	if err != nil {
		t.Errorf("package %s: import failed: %s", pkg0.Name(), err)
		return
	}
	if n != size {
		t.Errorf("package %s: not all input data consumed", pkg0.Name())
		return
	}

	s0 := pkgString(pkg0)
	s1 := pkgString(pkg1)
	if s1 != s0 {
		t.Errorf("package %s: \nimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s1, s0)
	}

	// If we have a standard library, compare also against the gcimported package.
	if path == "" {
		return // not std library
	}

	gcdata, err := gcExportData(path)
	gcsize = len(gcdata)

	imports = make(map[string]*types.Package)
	pkg2, err := gcImportData(imports, gcdata, path)
	if err != nil {
		t.Errorf("package %s: gcimport failed: %s", pkg0.Name(), err)
		return
	}

	s2 := pkgString(pkg2)
	if s2 != s0 {
		t.Errorf("package %s: \ngcimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s2, s0)
	}

	return
}

func pkgForSource(src string) (*types.Package, error) {
	// parse file
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "", src, 0)
	if err != nil {
		return nil, err
	}

	// typecheck file
	conf := types.Config{
		// strconv exports IntSize as a constant. The type-checker must
		// use the same word size otherwise the result of the type-checker
		// and gc imports is different. We don't care about alignment
		// since none of the tests have exported constants depending
		// on alignment (see also issue 8366).
		Sizes: &types.StdSizes{WordSize: strconv.IntSize / 8, MaxAlign: 8},
	}
	return conf.Check("import-test", fset, []*ast.File{f}, nil)
}

func pkgForPath(path string) (*types.Package, error) {
	// collect filenames
	ctxt := build.Default
	pkginfo, err := ctxt.Import(path, "", 0)
	if err != nil {
		return nil, err
	}
	filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)

	// parse files
	fset := token.NewFileSet()
	files := make([]*ast.File, len(filenames))
	for i, filename := range filenames {
		var err error
		files[i], err = parser.ParseFile(fset, filepath.Join(pkginfo.Dir, filename), nil, 0)
		if err != nil {
			return nil, err
		}
	}

	// typecheck files
	// (we only care about exports and thus can ignore function bodies)
	conf := types.Config{IgnoreFuncBodies: true, FakeImportC: true}
	return conf.Check(path, fset, files, nil)
}

// pkgString returns a string representation of a package's exported interface.
func pkgString(pkg *types.Package) string {
	var buf bytes.Buffer

	fmt.Fprintf(&buf, "package %s\n", pkg.Name())

	scope := pkg.Scope()
	for _, name := range scope.Names() {
		if exported(name) {
			obj := scope.Lookup(name)
			buf.WriteString(obj.String())

			switch obj := obj.(type) {
			case *types.Const:
				// For now only print constant values if they are not float
				// or complex. This permits comparing go/types results with
				// gc-generated gcimported package interfaces.
				info := obj.Type().Underlying().(*types.Basic).Info()
				if info&types.IsFloat == 0 && info&types.IsComplex == 0 {
					fmt.Fprintf(&buf, " = %s", obj.Val())
				}

			case *types.TypeName:
				// Print associated methods.
				// Basic types (e.g., unsafe.Pointer) have *types.Basic
				// type rather than *types.Named; so we need to check.
				if typ, _ := obj.Type().(*types.Named); typ != nil {
					if n := typ.NumMethods(); n > 0 {
						// Sort methods by name so that we get the
						// same order independent of whether the
						// methods got imported or coming directly
						// for the source.
						// TODO(gri) This should probably be done
						// in go/types.
						list := make([]*types.Func, n)
						for i := 0; i < n; i++ {
							list[i] = typ.Method(i)
						}
						sort.Sort(byName(list))

						buf.WriteString("\nmethods (\n")
						for _, m := range list {
							fmt.Fprintf(&buf, "\t%s\n", m)
						}
						buf.WriteString(")")
					}
				}
			}
			buf.WriteByte('\n')
		}
	}

	return buf.String()
}

var stdLibRoot = filepath.Join(runtime.GOROOT(), "src", "pkg") + string(filepath.Separator)

// The following std libraries are excluded from the stdLibs list.
var excluded = map[string]bool{
	"builtin": true, // contains type declarations with cycles
	"unsafe":  true, // contains fake declarations
}

// stdLibs returns the list if standard library package paths.
func stdLibs() (list []string, err error) {
	err = filepath.Walk(stdLibRoot, func(path string, info os.FileInfo, err error) error {
		if err == nil && info.IsDir() {
			if info.Name() == "testdata" {
				return filepath.SkipDir
			}
			pkgPath := path[len(stdLibRoot):] // remove stdLibRoot
			if len(pkgPath) > 0 && !excluded[pkgPath] {
				list = append(list, pkgPath)
			}
		}
		return nil
	})
	return
}

type byName []*types.Func

func (a byName) Len() int           { return len(a) }
func (a byName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byName) Less(i, j int) bool { return a[i].Name() < a[j].Name() }

// gcExportData returns the gc-generated export data for the given path.
// It is based on a trimmed-down version of gcimporter.Import which does
// not do the actual import, does not handle package unsafe, and assumes
// that path is a correct standard library package path (no canonicalization,
// or handling of local import paths).
func gcExportData(path string) ([]byte, error) {
	filename, id := gcimporter.FindPkg(path, "")
	if filename == "" {
		return nil, fmt.Errorf("can't find import: %s", path)
	}
	if id != path {
		panic("path should be canonicalized")
	}

	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	buf := bufio.NewReader(f)
	if err = gcimporter.FindExportData(buf); err != nil {
		return nil, err
	}

	var data []byte
	for {
		line, err := buf.ReadBytes('\n')
		if err != nil {
			return nil, err
		}
		data = append(data, line...)
		// export data ends in "$$\n"
		if len(line) == 3 && line[0] == '$' && line[1] == '$' {
			return data, nil
		}
	}
}

func gcImportData(imports map[string]*types.Package, data []byte, path string) (*types.Package, error) {
	filename := fmt.Sprintf("<filename for %s>", path) // so we have a decent error message if necessary
	return gcimporter.ImportData(imports, filename, path, bufio.NewReader(bytes.NewBuffer(data)))
}
<commit_msg>go.tools/go/importer: fix test (src/pkg -> src)<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package importer

import (
	"bufio"
	"bytes"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"testing"
	"time"

	"code.google.com/p/go.tools/go/gcimporter"
	"code.google.com/p/go.tools/go/types"
)

var tests = []string{
	`package p`,

	// consts
	`package p; const X = true`,
	`package p; const X, y, Z = true, false, 0 != 0`,
	`package p; const ( A float32 = 1<<iota; B; C; D)`,
	`package p; const X = "foo"`,
	`package p; const X string = "foo"`,
	`package p; const X = 0`,
	`package p; const X = -42`,
	`package p; const X = 3.14159265`,
	`package p; const X = -1e-10`,
	`package p; const X = 1.2 + 2.3i`,
	`package p; const X = -1i`,
	`package p; import "math"; const Pi = math.Pi`,
	`package p; import m "math"; const Pi = m.Pi`,

	// types
	`package p; type T int`,
	`package p; type T [10]int`,
	`package p; type T []int`,
	`package p; type T struct{}`,
	`package p; type T struct{x int}`,
	`package p; type T *int`,
	`package p; type T func()`,
	`package p; type T *T`,
	`package p; type T interface{}`,
	`package p; type T interface{ foo() }`,
	`package p; type T interface{ m() T }`,
	// TODO(gri) disabled for now - import/export works but
	// types.Type.String() used in the test cannot handle cases
	// like this yet
	// `package p; type T interface{ m() interface{T} }`,
	`package p; type T map[string]bool`,
	`package p; type T chan int`,
	`package p; type T <-chan complex64`,
	`package p; type T chan<- map[int]string`,
	// test case for issue 8177
	`package p; type T1 interface { F(T2) }; type T2 interface { T1 }`,

	// vars
	`package p; var X int`,
	`package p; var X, Y, Z struct{f int "tag"}`,

	// funcs
	`package p; func F()`,
	`package p; func F(x int, y struct{}) bool`,
	`package p; type T int; func (*T) F(x int, y struct{}) T`,

	// selected special cases
	`package p; type T int`,
	`package p; type T uint8`,
	`package p; type T byte`,
	`package p; type T error`,
	`package p; import "net/http"; type T http.Client`,
	`package p; import "net/http"; type ( T1 http.Client; T2 struct { http.Client } )`,
	`package p; import "unsafe"; type ( T1 unsafe.Pointer; T2 unsafe.Pointer )`,
	`package p; import "unsafe"; type T struct { p unsafe.Pointer }`,
}

func TestImportSrc(t *testing.T) {
	for _, src := range tests {
		pkg, err := pkgForSource(src)
		if err != nil {
			t.Errorf("typecheck failed: %s", err)
			continue
		}
		testExportImport(t, pkg, "")
	}
}

func TestImportStdLib(t *testing.T) {
	start := time.Now()

	libs, err := stdLibs()
	if err != nil {
		t.Fatalf("could not compute list of std libraries: %s", err)
	}
	if len(libs) < 100 {
		t.Fatalf("only %d std libraries found - something's not right", len(libs))
	}

	// make sure printed go/types types and gc-imported types
	// can be compared reasonably well
	types.GcCompatibilityMode = true

	var totSize, totGcSize int
	for _, lib := range libs {
		// limit run time for short tests
		if testing.Short() && time.Since(start) >= 750*time.Millisecond {
			return
		}

		pkg, err := pkgForPath(lib)
		switch err := err.(type) {
		case nil:
			// ok
		case *build.NoGoError:
			// no Go files - ignore
			continue
		default:
			t.Errorf("typecheck failed: %s", err)
			continue
		}

		size, gcsize := testExportImport(t, pkg, lib)
		if gcsize == 0 {
			// if gc import didn't happen, assume same size
			// (and avoid division by zero below)
			gcsize = size
		}

		if testing.Verbose() {
			fmt.Printf("%s\t%d\t%d\t%d%%\n", lib, size, gcsize, int(float64(size)*100/float64(gcsize)))
		}
		totSize += size
		totGcSize += gcsize
	}

	if testing.Verbose() {
		fmt.Printf("\n%d\t%d\t%d%%\n", totSize, totGcSize, int(float64(totSize)*100/float64(totGcSize)))
	}

	types.GcCompatibilityMode = false
}

func testExportImport(t *testing.T, pkg0 *types.Package, path string) (size, gcsize int) {
	data := ExportData(pkg0)
	size = len(data)

	imports := make(map[string]*types.Package)
	n, pkg1, err := ImportData(imports, data)
	if err != nil {
		t.Errorf("package %s: import failed: %s", pkg0.Name(), err)
		return
	}
	if n != size {
		t.Errorf("package %s: not all input data consumed", pkg0.Name())
		return
	}

	s0 := pkgString(pkg0)
	s1 := pkgString(pkg1)
	if s1 != s0 {
		t.Errorf("package %s: \nimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s1, s0)
	}

	// If we have a standard library, compare also against the gcimported package.
	if path == "" {
		return // not std library
	}

	gcdata, err := gcExportData(path)
	if err != nil {
		if pkg0.Name() == "main" {
			return // no export data present for main package
		}
		t.Errorf("package %s: couldn't get export data: %s", pkg0.Name(), err)
	}
	gcsize = len(gcdata)

	imports = make(map[string]*types.Package)
	pkg2, err := gcImportData(imports, gcdata, path)
	if err != nil {
		t.Errorf("package %s: gcimport failed: %s", pkg0.Name(), err)
		return
	}

	s2 := pkgString(pkg2)
	if s2 != s0 {
		t.Errorf("package %s: \ngcimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s2, s0)
	}

	return
}

func pkgForSource(src string) (*types.Package, error) {
	// parse file
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "", src, 0)
	if err != nil {
		return nil, err
	}

	// typecheck file
	conf := types.Config{
		// strconv exports IntSize as a constant. The type-checker must
		// use the same word size otherwise the result of the type-checker
		// and gc imports is different. We don't care about alignment
		// since none of the tests have exported constants depending
		// on alignment (see also issue 8366).
		Sizes: &types.StdSizes{WordSize: strconv.IntSize / 8, MaxAlign: 8},
	}
	return conf.Check("import-test", fset, []*ast.File{f}, nil)
}

func pkgForPath(path string) (*types.Package, error) {
	// collect filenames
	ctxt := build.Default
	pkginfo, err := ctxt.Import(path, "", 0)
	if err != nil {
		return nil, err
	}
	filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)

	// parse files
	fset := token.NewFileSet()
	files := make([]*ast.File, len(filenames))
	for i, filename := range filenames {
		var err error
		files[i], err = parser.ParseFile(fset, filepath.Join(pkginfo.Dir, filename), nil, 0)
		if err != nil {
			return nil, err
		}
	}

	// typecheck files
	// (we only care about exports and thus can ignore function bodies)
	conf := types.Config{IgnoreFuncBodies: true, FakeImportC: true}
	return conf.Check(path, fset, files, nil)
}

// pkgString returns a string representation of a package's exported interface.
func pkgString(pkg *types.Package) string {
	var buf bytes.Buffer

	fmt.Fprintf(&buf, "package %s\n", pkg.Name())

	scope := pkg.Scope()
	for _, name := range scope.Names() {
		if exported(name) {
			obj := scope.Lookup(name)
			buf.WriteString(obj.String())

			switch obj := obj.(type) {
			case *types.Const:
				// For now only print constant values if they are not float
				// or complex. This permits comparing go/types results with
				// gc-generated gcimported package interfaces.
				info := obj.Type().Underlying().(*types.Basic).Info()
				if info&types.IsFloat == 0 && info&types.IsComplex == 0 {
					fmt.Fprintf(&buf, " = %s", obj.Val())
				}

			case *types.TypeName:
				// Print associated methods.
				// Basic types (e.g., unsafe.Pointer) have *types.Basic
				// type rather than *types.Named; so we need to check.
				if typ, _ := obj.Type().(*types.Named); typ != nil {
					if n := typ.NumMethods(); n > 0 {
						// Sort methods by name so that we get the
						// same order independent of whether the
						// methods got imported or coming directly
						// for the source.
						// TODO(gri) This should probably be done
						// in go/types.
						list := make([]*types.Func, n)
						for i := 0; i < n; i++ {
							list[i] = typ.Method(i)
						}
						sort.Sort(byName(list))

						buf.WriteString("\nmethods (\n")
						for _, m := range list {
							fmt.Fprintf(&buf, "\t%s\n", m)
						}
						buf.WriteString(")")
					}
				}
			}
			buf.WriteByte('\n')
		}
	}

	return buf.String()
}

var stdLibRoot = filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator)

// The following std libraries are excluded from the stdLibs list.
var excluded = map[string]bool{
	"builtin": true, // contains type declarations with cycles
	"unsafe":  true, // contains fake declarations
}

// stdLibs returns the list of standard library package paths.
func stdLibs() (list []string, err error) {
	err = filepath.Walk(stdLibRoot, func(path string, info os.FileInfo, err error) error {
		if err == nil && info.IsDir() {
			// testdata directories don't contain importable libraries
			if info.Name() == "testdata" {
				return filepath.SkipDir
			}
			pkgPath := path[len(stdLibRoot):] // remove stdLibRoot
			if len(pkgPath) > 0 && !excluded[pkgPath] {
				list = append(list, pkgPath)
			}
		}
		return nil
	})
	return
}

type byName []*types.Func

func (a byName) Len() int           { return len(a) }
func (a byName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byName) Less(i, j int) bool { return a[i].Name() < a[j].Name() }

// gcExportData returns the gc-generated export data for the given path.
// It is based on a trimmed-down version of gcimporter.Import which does
// not do the actual import, does not handle package unsafe, and assumes
// that path is a correct standard library package path (no canonicalization,
// or handling of local import paths).
func gcExportData(path string) ([]byte, error) {
	filename, id := gcimporter.FindPkg(path, "")
	if filename == "" {
		return nil, fmt.Errorf("can't find import: %s", path)
	}
	if id != path {
		panic("path should be canonicalized")
	}

	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	buf := bufio.NewReader(f)
	if err = gcimporter.FindExportData(buf); err != nil {
		return nil, err
	}

	var data []byte
	for {
		line, err := buf.ReadBytes('\n')
		if err != nil {
			return nil, err
		}
		data = append(data, line...)
		// export data ends in "$$\n"
		if len(line) == 3 && line[0] == '$' && line[1] == '$' {
			return data, nil
		}
	}
}

func gcImportData(imports map[string]*types.Package, data []byte, path string) (*types.Package, error) {
	filename := fmt.Sprintf("<filename for %s>", path) // so we have a decent error message if necessary
	return gcimporter.ImportData(imports, filename, path, bufio.NewReader(bytes.NewBuffer(data)))
}
<|endoftext|>


<commit_before>// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package wrangler

import (
	"flag"
	"time"

	"github.com/youtube/vitess/go/vt/tabletmanager/initiator"
	"github.com/youtube/vitess/go/vt/topo"
)

const (
	// DefaultActionTimeout is a good default for interactive
	// remote actions.
	DefaultActionTimeout = 30 * time.Second
)

var tabletManagerProtocol = flag.String("tablet_manager_protocol", "bson", "the protocol to use to talk to vttablet")

type Wrangler struct {
	ts          topo.Server
	ai          *initiator.ActionInitiator
	deadline    time.Time
	lockTimeout time.Duration

	// Configuration parameters, mostly for tests.

	// UseRPCs makes the wrangler use RPCs to trigger short live
	// remote actions. It is faster in production, as we don't
	// fork a vtaction. However, unit tests don't support it.
	UseRPCs bool
}

// actionTimeout: how long should we wait for an action to complete?
// lockTimeout: how long should we wait for the initial lock to start a complex action?
//   This is distinct from actionTimeout because most of the time, we want to immediately
//   know that out action will fail. However, automated action will need some time to
//   arbitrate the locks.
func New(ts topo.Server, actionTimeout, lockTimeout time.Duration) *Wrangler {
	return &Wrangler{ts, initiator.NewActionInitiator(ts, *tabletManagerProtocol), time.Now().Add(actionTimeout), lockTimeout, true}
}

func (wr *Wrangler) actionTimeout() time.Duration {
	return wr.deadline.Sub(time.Now())
}

func (wr *Wrangler) TopoServer() topo.Server {
	return wr.ts
}

func (wr *Wrangler) ActionInitiator() *initiator.ActionInitiator {
	return wr.ai
}

// ResetActionTimeout should be used before every action on a wrangler
// object that is going to be re-used:
// - vtctl will not call this, as it does one action
// - vtctld will call this, as it re-uses the same wrangler for actions
func (wr *Wrangler) ResetActionTimeout(actionTimeout time.Duration) {
	wr.deadline = time.Now().Add(actionTimeout)
}

// signal handling
var interrupted = make(chan struct{})

func SignalInterrupt() {
	close(interrupted)
}
<commit_msg>More doc in wrangler package.<commit_after>// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// wrangler contains the Wrangler object to manage complex topology actions.
package wrangler

import (
	"flag"
	"time"

	"github.com/youtube/vitess/go/vt/tabletmanager/initiator"
	"github.com/youtube/vitess/go/vt/topo"
)

const (
	// DefaultActionTimeout is a good default for interactive
	// remote actions.
	DefaultActionTimeout = 30 * time.Second
)

var tabletManagerProtocol = flag.String("tablet_manager_protocol", "bson", "the protocol to use to talk to vttablet")

// Wrangler manages complex actions on the topology, like reparents,
// snapshots, restores, ...
type Wrangler struct {
	ts          topo.Server
	ai          *initiator.ActionInitiator
	deadline    time.Time
	lockTimeout time.Duration

	// Configuration parameters, mostly for tests.

	// UseRPCs makes the wrangler use RPCs to trigger short live
	// remote actions. It is faster in production, as we don't
	// fork a vtaction. However, unit tests don't support it.
	UseRPCs bool
}

// New creates a new Wrangler object.
//
// actionTimeout: how long should we wait for an action to complete?
// - if using wrangler for just one action, this is set properly
//   upon wrangler creation.
// - if re-using wrangler multiple times, call ResetActionTimeout before
//   every action.
//
// lockTimeout: how long should we wait for the initial lock to start
// a complex action?  This is distinct from actionTimeout because most
// of the time, we want to immediately know that out action will
// fail. However, automated action will need some time to arbitrate
// the locks.
func New(ts topo.Server, actionTimeout, lockTimeout time.Duration) *Wrangler {
	return &Wrangler{ts, initiator.NewActionInitiator(ts, *tabletManagerProtocol), time.Now().Add(actionTimeout), lockTimeout, true}
}

func (wr *Wrangler) actionTimeout() time.Duration {
	return wr.deadline.Sub(time.Now())
}

// TopoServer returns the topo.Server this wrangler is using.
func (wr *Wrangler) TopoServer() topo.Server {
	return wr.ts
}

// ActionInitiator returns the initiator.ActionInitiator this wrangler is using.
func (wr *Wrangler) ActionInitiator() *initiator.ActionInitiator {
	return wr.ai
}

// ResetActionTimeout should be used before every action on a wrangler
// object that is going to be re-used:
// - vtctl will not call this, as it does one action
// - vtctld will call this, as it re-uses the same wrangler for actions
func (wr *Wrangler) ResetActionTimeout(actionTimeout time.Duration) {
	wr.deadline = time.Now().Add(actionTimeout)
}

// signal handling
var interrupted = make(chan struct{})

// SignalInterrupt needs to be called when a signal interrupts the current
// process.
func SignalInterrupt() {
	close(interrupted)
}
<|endoftext|>


<commit_before>package vault

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/armon/go-metrics"
	"github.com/armon/go-radix"
	"github.com/hashicorp/vault/helper/salt"
	"github.com/hashicorp/vault/logical"
)

// Router is used to do prefix based routing of a request to a logical backend
type Router struct {
	l                  sync.RWMutex
	root               *radix.Tree
	mountUUIDCache     *radix.Tree
	mountAccessorCache *radix.Tree
	tokenStoreSaltFunc func() (*salt.Salt, error)

	// storagePrefix maps the prefix used for storage (ala the BarrierView)
	// to the backend. This is used to map a key back into the backend that owns it.
	// For example, logical/uuid1/foobar -> secrets/ (generic backend) + foobar
	storagePrefix *radix.Tree
}

// NewRouter returns a new router
func NewRouter() *Router {
	r := &Router{
		root:               radix.New(),
		storagePrefix:      radix.New(),
		mountUUIDCache:     radix.New(),
		mountAccessorCache: radix.New(),
	}
	return r
}

// routeEntry is used to represent a mount point in the router
type routeEntry struct {
	tainted     bool
	backend     logical.Backend
	mountEntry  *MountEntry
	storageView *BarrierView
	rootPaths   *radix.Tree
	loginPaths  *radix.Tree
}

// SaltID is used to apply a salt and hash to an ID to make sure its not reversible
func (re *routeEntry) SaltID(id string) string {
	return salt.SaltID(re.mountEntry.UUID, id, salt.SHA1Hash)
}

// Mount is used to expose a logical backend at a given prefix, using a unique salt,
// and the barrier view for that path.
func (r *Router) Mount(backend logical.Backend, prefix string, mountEntry *MountEntry, storageView *BarrierView) error {
	r.l.Lock()
	defer r.l.Unlock()

	// Check if this is a nested mount
	if existing, _, ok := r.root.LongestPrefix(prefix); ok && existing != "" {
		return fmt.Errorf("cannot mount under existing mount '%s'", existing)
	}

	// Build the paths
	paths := backend.SpecialPaths()
	if paths == nil {
		paths = new(logical.Paths)
	}

	// Create a mount entry
	re := &routeEntry{
		tainted:     false,
		backend:     backend,
		mountEntry:  mountEntry,
		storageView: storageView,
		rootPaths:   pathsToRadix(paths.Root),
		loginPaths:  pathsToRadix(paths.Unauthenticated),
	}

	switch {
	case prefix == "":
		return fmt.Errorf("missing prefix to be used for router entry; mount_path: %q, mount_type: %q", re.mountEntry.Path, re.mountEntry.Type)
	case storageView.prefix == "":
		return fmt.Errorf("missing storage view prefix; mount_path: %q, mount_type: %q", re.mountEntry.Path, re.mountEntry.Type)
	case re.mountEntry.UUID == "":
		return fmt.Errorf("missing mount identifier; mount_path: %q, mount_type: %q", re.mountEntry.Path, re.mountEntry.Type)
	case re.mountEntry.Accessor == "":
		return fmt.Errorf("missing mount accessor; mount_path: %q, mount_type: %q", re.mountEntry.Path, re.mountEntry.Type)
	}

	r.root.Insert(prefix, re)
	r.storagePrefix.Insert(storageView.prefix, re)
	r.mountUUIDCache.Insert(re.mountEntry.UUID, re.mountEntry)
	r.mountAccessorCache.Insert(re.mountEntry.Accessor, re.mountEntry)

	return nil
}

// Unmount is used to remove a logical backend from a given prefix
func (r *Router) Unmount(prefix string) error {
	r.l.Lock()
	defer r.l.Unlock()

	// Fast-path out if the backend doesn't exist
	raw, ok := r.root.Get(prefix)
	if !ok {
		return nil
	}

	// Call backend's Cleanup routine
	re := raw.(*routeEntry)
	re.backend.Cleanup()

	// Purge from the radix trees
	r.root.Delete(prefix)
	r.storagePrefix.Delete(re.storageView.prefix)
	r.mountUUIDCache.Delete(re.mountEntry.UUID)
	r.mountAccessorCache.Delete(re.mountEntry.Accessor)

	return nil
}

// Remount is used to change the mount location of a logical backend
func (r *Router) Remount(src, dst string) error {
	r.l.Lock()
	defer r.l.Unlock()

	// Check for existing mount
	raw, ok := r.root.Get(src)
	if !ok {
		return fmt.Errorf("no mount at '%s'", src)
	}

	// Update the mount point
	r.root.Delete(src)
	r.root.Insert(dst, raw)
	return nil
}

// Taint is used to mark a path as tainted. This means only RollbackOperation
// RevokeOperation requests are allowed to proceed
func (r *Router) Taint(path string) error {
	r.l.Lock()
	defer r.l.Unlock()
	_, raw, ok := r.root.LongestPrefix(path)
	if ok {
		raw.(*routeEntry).tainted = true
	}
	return nil
}

// Untaint is used to unmark a path as tainted.
func (r *Router) Untaint(path string) error {
	r.l.Lock()
	defer r.l.Unlock()
	_, raw, ok := r.root.LongestPrefix(path)
	if ok {
		raw.(*routeEntry).tainted = false
	}
	return nil
}

func (r *Router) MatchingMountByUUID(mountID string) *MountEntry {
	if mountID == "" {
		return nil
	}

	r.l.RLock()
	defer r.l.RUnlock()

	_, raw, ok := r.mountUUIDCache.LongestPrefix(mountID)
	if !ok {
		return nil
	}

	return raw.(*MountEntry)
}

// MatchingMountByAccessor returns the MountEntry by accessor lookup
func (r *Router) MatchingMountByAccessor(mountAccessor string) *MountEntry {
	if mountAccessor == "" {
		return nil
	}

	r.l.RLock()
	defer r.l.RUnlock()

	_, raw, ok := r.mountAccessorCache.LongestPrefix(mountAccessor)
	if !ok {
		return nil
	}

	return raw.(*MountEntry)
}

// MatchingMount returns the mount prefix that would be used for a path
func (r *Router) MatchingMount(path string) string {
	r.l.RLock()
	mount, _, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return ""
	}
	return mount
}

// MatchingStorageView returns the storageView used for a path
func (r *Router) MatchingStorageView(path string) *BarrierView {
	r.l.RLock()
	_, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return nil
	}
	return raw.(*routeEntry).storageView
}

// MatchingMountEntry returns the MountEntry used for a path
func (r *Router) MatchingMountEntry(path string) *MountEntry {
	r.l.RLock()
	_, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return nil
	}
	return raw.(*routeEntry).mountEntry
}

// MatchingBackend returns the backend used for a path
func (r *Router) MatchingBackend(path string) logical.Backend {
	r.l.RLock()
	_, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return nil
	}
	return raw.(*routeEntry).backend
}

// MatchingSystemView returns the SystemView used for a path
func (r *Router) MatchingSystemView(path string) logical.SystemView {
	r.l.RLock()
	_, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return nil
	}
	return raw.(*routeEntry).backend.System()
}

// MatchingStoragePrefix returns the mount path matching and storage prefix
// matching the given path
func (r *Router) MatchingStoragePrefix(path string) (string, string, bool) {
	r.l.RLock()
	_, raw, ok := r.storagePrefix.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return "", "", false
	}

	// Extract the mount path and storage prefix
	re := raw.(*routeEntry)
	mountPath := re.mountEntry.Path
	prefix := re.storageView.prefix

	// Add back the prefix for credential backends
	if strings.HasPrefix(path, credentialBarrierPrefix) {
		mountPath = credentialRoutePrefix + mountPath
	}

	return mountPath, prefix, true
}

// Route is used to route a given request
func (r *Router) Route(req *logical.Request) (*logical.Response, error) {
	resp, _, _, err := r.routeCommon(req, false)
	return resp, err
}

// Route is used to route a given existence check request
func (r *Router) RouteExistenceCheck(req *logical.Request) (bool, bool, error) {
	_, ok, exists, err := r.routeCommon(req, true)
	return ok, exists, err
}

func (r *Router) routeCommon(req *logical.Request, existenceCheck bool) (*logical.Response, bool, bool, error) {
	// Find the mount point
	r.l.RLock()
	mount, raw, ok := r.root.LongestPrefix(req.Path)
	if !ok {
		// Re-check for a backend by appending a slash. This lets "foo" mean
		// "foo/" at the root level which is almost always what we want.
		req.Path += "/"
		mount, raw, ok = r.root.LongestPrefix(req.Path)
	}
	r.l.RUnlock()
	if !ok {
		return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s'", req.Path)), false, false, logical.ErrUnsupportedPath
	}
	defer metrics.MeasureSince([]string{"route", string(req.Operation),
		strings.Replace(mount, "/", "-", -1)}, time.Now())
	re := raw.(*routeEntry)

	// If the path is tainted, we reject any operation except for
	// Rollback and Revoke
	if re.tainted {
		switch req.Operation {
		case logical.RevokeOperation, logical.RollbackOperation:
		default:
			return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s'", req.Path)), false, false, logical.ErrUnsupportedPath
		}
	}

	// Adjust the path to exclude the routing prefix
	originalPath := req.Path
	req.Path = strings.TrimPrefix(req.Path, mount)
	req.MountPoint = mount
	req.MountType = re.mountEntry.Type
	if req.Path == "/" {
		req.Path = ""
	}

	// Attach the storage view for the request
	req.Storage = re.storageView

	// Hash the request token unless this is the token backend
	clientToken := req.ClientToken
	switch {
	case strings.HasPrefix(originalPath, "auth/token/"):
	case strings.HasPrefix(originalPath, "sys/"):
	case strings.HasPrefix(originalPath, "cubbyhole/"):
		// In order for the token store to revoke later, we need to have the same
		// salted ID, so we double-salt what's going to the cubbyhole backend
		salt, err := r.tokenStoreSaltFunc()
		if err != nil {
			return nil, false, false, err
		}
		req.ClientToken = re.SaltID(salt.SaltID(req.ClientToken))
	default:
		req.ClientToken = re.SaltID(req.ClientToken)
	}

	// Cache the pointer to the original connection object
	originalConn := req.Connection

	// Cache the identifier of the request
	originalReqID := req.ID

	// Cache the client token's number of uses in the request
	originalClientTokenRemainingUses := req.ClientTokenRemainingUses
	req.ClientTokenRemainingUses = 0

	// Cache the headers and hide them from backends
	headers := req.Headers
	req.Headers = nil

	// Cache the wrap info of the request
	var wrapInfo *logical.RequestWrapInfo
	if req.WrapInfo != nil {
		wrapInfo = &logical.RequestWrapInfo{
			TTL:    req.WrapInfo.TTL,
			Format: req.WrapInfo.Format,
		}
	}

	// Reset the request before returning
	defer func() {
		req.Path = originalPath
		req.MountPoint = mount
		req.MountType = re.mountEntry.Type
		req.Connection = originalConn
		req.ID = originalReqID
		req.Storage = nil
		req.ClientToken = clientToken
		req.ClientTokenRemainingUses = originalClientTokenRemainingUses
		req.WrapInfo = wrapInfo
		req.Headers = headers
		// This is only set in one place, after routing, so should never be set
		// by a backend
		req.SetLastRemoteWAL(0)
	}()

	// Invoke the backend
	if existenceCheck {
		ok, exists, err := re.backend.HandleExistenceCheck(req)
		return nil, ok, exists, err
	} else {
		resp, err := re.backend.HandleRequest(req)
		return resp, false, false, err
	}
}

// RootPath checks if the given path requires root privileges
func (r *Router) RootPath(path string) bool {
	r.l.RLock()
	mount, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return false
	}
	re := raw.(*routeEntry)

	// Trim to get remaining path
	remain := strings.TrimPrefix(path, mount)

	// Check the rootPaths of this backend
	match, raw, ok := re.rootPaths.LongestPrefix(remain)
	if !ok {
		return false
	}
	prefixMatch := raw.(bool)

	// Handle the prefix match case
	if prefixMatch {
		return strings.HasPrefix(remain, match)
	}

	// Handle the exact match case
	return match == remain
}

// LoginPath checks if the given path is used for logins
func (r *Router) LoginPath(path string) bool {
	r.l.RLock()
	mount, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return false
	}
	re := raw.(*routeEntry)

	// Trim to get remaining path
	remain := strings.TrimPrefix(path, mount)

	// Check the loginPaths of this backend
	match, raw, ok := re.loginPaths.LongestPrefix(remain)
	if !ok {
		return false
	}
	prefixMatch := raw.(bool)

	// Handle the prefix match case
	if prefixMatch {
		return strings.HasPrefix(remain, match)
	}

	// Handle the exact match case
	return match == remain
}

// pathsToRadix converts a the mapping of special paths to a mapping
// of special paths to radix trees.
func pathsToRadix(paths []string) *radix.Tree {
	tree := radix.New()
	for _, path := range paths {
		// Check if this is a prefix or exact match
		prefixMatch := len(path) >= 1 && path[len(path)-1] == '*'
		if prefixMatch {
			path = path[:len(path)-1]
		}

		tree.Insert(path, prefixMatch)
	}

	return tree
}
<commit_msg>Don't append a trailing slash to the request path if it doesn't actually help find something (#3271)<commit_after>package vault

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/armon/go-metrics"
	"github.com/armon/go-radix"
	"github.com/hashicorp/vault/helper/salt"
	"github.com/hashicorp/vault/logical"
)

// Router is used to do prefix based routing of a request to a logical backend
type Router struct {
	l                  sync.RWMutex
	root               *radix.Tree
	mountUUIDCache     *radix.Tree
	mountAccessorCache *radix.Tree
	tokenStoreSaltFunc func() (*salt.Salt, error)

	// storagePrefix maps the prefix used for storage (ala the BarrierView)
	// to the backend. This is used to map a key back into the backend that owns it.
	// For example, logical/uuid1/foobar -> secrets/ (generic backend) + foobar
	storagePrefix *radix.Tree
}

// NewRouter returns a new router
func NewRouter() *Router {
	r := &Router{
		root:               radix.New(),
		storagePrefix:      radix.New(),
		mountUUIDCache:     radix.New(),
		mountAccessorCache: radix.New(),
	}
	return r
}

// routeEntry is used to represent a mount point in the router
type routeEntry struct {
	tainted     bool
	backend     logical.Backend
	mountEntry  *MountEntry
	storageView *BarrierView
	rootPaths   *radix.Tree
	loginPaths  *radix.Tree
}

// SaltID is used to apply a salt and hash to an ID to make sure its not reversible
func (re *routeEntry) SaltID(id string) string {
	return salt.SaltID(re.mountEntry.UUID, id, salt.SHA1Hash)
}

// Mount is used to expose a logical backend at a given prefix, using a unique salt,
// and the barrier view for that path.
func (r *Router) Mount(backend logical.Backend, prefix string, mountEntry *MountEntry, storageView *BarrierView) error {
	r.l.Lock()
	defer r.l.Unlock()

	// Check if this is a nested mount
	if existing, _, ok := r.root.LongestPrefix(prefix); ok && existing != "" {
		return fmt.Errorf("cannot mount under existing mount '%s'", existing)
	}

	// Build the paths
	paths := backend.SpecialPaths()
	if paths == nil {
		paths = new(logical.Paths)
	}

	// Create a mount entry
	re := &routeEntry{
		tainted:     false,
		backend:     backend,
		mountEntry:  mountEntry,
		storageView: storageView,
		rootPaths:   pathsToRadix(paths.Root),
		loginPaths:  pathsToRadix(paths.Unauthenticated),
	}

	switch {
	case prefix == "":
		return fmt.Errorf("missing prefix to be used for router entry; mount_path: %q, mount_type: %q", re.mountEntry.Path, re.mountEntry.Type)
	case storageView.prefix == "":
		return fmt.Errorf("missing storage view prefix; mount_path: %q, mount_type: %q", re.mountEntry.Path, re.mountEntry.Type)
	case re.mountEntry.UUID == "":
		return fmt.Errorf("missing mount identifier; mount_path: %q, mount_type: %q", re.mountEntry.Path, re.mountEntry.Type)
	case re.mountEntry.Accessor == "":
		return fmt.Errorf("missing mount accessor; mount_path: %q, mount_type: %q", re.mountEntry.Path, re.mountEntry.Type)
	}

	r.root.Insert(prefix, re)
	r.storagePrefix.Insert(storageView.prefix, re)
	r.mountUUIDCache.Insert(re.mountEntry.UUID, re.mountEntry)
	r.mountAccessorCache.Insert(re.mountEntry.Accessor, re.mountEntry)

	return nil
}

// Unmount is used to remove a logical backend from a given prefix
func (r *Router) Unmount(prefix string) error {
	r.l.Lock()
	defer r.l.Unlock()

	// Fast-path out if the backend doesn't exist
	raw, ok := r.root.Get(prefix)
	if !ok {
		return nil
	}

	// Call backend's Cleanup routine
	re := raw.(*routeEntry)
	re.backend.Cleanup()

	// Purge from the radix trees
	r.root.Delete(prefix)
	r.storagePrefix.Delete(re.storageView.prefix)
	r.mountUUIDCache.Delete(re.mountEntry.UUID)
	r.mountAccessorCache.Delete(re.mountEntry.Accessor)

	return nil
}

// Remount is used to change the mount location of a logical backend
func (r *Router) Remount(src, dst string) error {
	r.l.Lock()
	defer r.l.Unlock()

	// Check for existing mount
	raw, ok := r.root.Get(src)
	if !ok {
		return fmt.Errorf("no mount at '%s'", src)
	}

	// Update the mount point
	r.root.Delete(src)
	r.root.Insert(dst, raw)
	return nil
}

// Taint is used to mark a path as tainted. This means only RollbackOperation
// RevokeOperation requests are allowed to proceed
func (r *Router) Taint(path string) error {
	r.l.Lock()
	defer r.l.Unlock()
	_, raw, ok := r.root.LongestPrefix(path)
	if ok {
		raw.(*routeEntry).tainted = true
	}
	return nil
}

// Untaint is used to unmark a path as tainted.
func (r *Router) Untaint(path string) error {
	r.l.Lock()
	defer r.l.Unlock()
	_, raw, ok := r.root.LongestPrefix(path)
	if ok {
		raw.(*routeEntry).tainted = false
	}
	return nil
}

func (r *Router) MatchingMountByUUID(mountID string) *MountEntry {
	if mountID == "" {
		return nil
	}

	r.l.RLock()
	defer r.l.RUnlock()

	_, raw, ok := r.mountUUIDCache.LongestPrefix(mountID)
	if !ok {
		return nil
	}

	return raw.(*MountEntry)
}

// MatchingMountByAccessor returns the MountEntry by accessor lookup
func (r *Router) MatchingMountByAccessor(mountAccessor string) *MountEntry {
	if mountAccessor == "" {
		return nil
	}

	r.l.RLock()
	defer r.l.RUnlock()

	_, raw, ok := r.mountAccessorCache.LongestPrefix(mountAccessor)
	if !ok {
		return nil
	}

	return raw.(*MountEntry)
}

// MatchingMount returns the mount prefix that would be used for a path
func (r *Router) MatchingMount(path string) string {
	r.l.RLock()
	mount, _, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return ""
	}
	return mount
}

// MatchingStorageView returns the storageView used for a path
func (r *Router) MatchingStorageView(path string) *BarrierView {
	r.l.RLock()
	_, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return nil
	}
	return raw.(*routeEntry).storageView
}

// MatchingMountEntry returns the MountEntry used for a path
func (r *Router) MatchingMountEntry(path string) *MountEntry {
	r.l.RLock()
	_, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return nil
	}
	return raw.(*routeEntry).mountEntry
}

// MatchingBackend returns the backend used for a path
func (r *Router) MatchingBackend(path string) logical.Backend {
	r.l.RLock()
	_, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return nil
	}
	return raw.(*routeEntry).backend
}

// MatchingSystemView returns the SystemView used for a path
func (r *Router) MatchingSystemView(path string) logical.SystemView {
	r.l.RLock()
	_, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return nil
	}
	return raw.(*routeEntry).backend.System()
}

// MatchingStoragePrefix returns the mount path matching and storage prefix
// matching the given path
func (r *Router) MatchingStoragePrefix(path string) (string, string, bool) {
	r.l.RLock()
	_, raw, ok := r.storagePrefix.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return "", "", false
	}

	// Extract the mount path and storage prefix
	re := raw.(*routeEntry)
	mountPath := re.mountEntry.Path
	prefix := re.storageView.prefix

	// Add back the prefix for credential backends
	if strings.HasPrefix(path, credentialBarrierPrefix) {
		mountPath = credentialRoutePrefix + mountPath
	}

	return mountPath, prefix, true
}

// Route is used to route a given request
func (r *Router) Route(req *logical.Request) (*logical.Response, error) {
	resp, _, _, err := r.routeCommon(req, false)
	return resp, err
}

// Route is used to route a given existence check request
func (r *Router) RouteExistenceCheck(req *logical.Request) (bool, bool, error) {
	_, ok, exists, err := r.routeCommon(req, true)
	return ok, exists, err
}

func (r *Router) routeCommon(req *logical.Request, existenceCheck bool) (*logical.Response, bool, bool, error) {
	// Find the mount point
	r.l.RLock()
	adjustedPath := req.Path
	mount, raw, ok := r.root.LongestPrefix(adjustedPath)
	if !ok && !strings.HasSuffix(adjustedPath, "/") {
		// Re-check for a backend by appending a slash. This lets "foo" mean
		// "foo/" at the root level which is almost always what we want.
		adjustedPath += "/"
		mount, raw, ok = r.root.LongestPrefix(adjustedPath)
	}
	r.l.RUnlock()
	if !ok {
		return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s'", req.Path)), false, false, logical.ErrUnsupportedPath
	}
	req.Path = adjustedPath
	defer metrics.MeasureSince([]string{"route", string(req.Operation),
		strings.Replace(mount, "/", "-", -1)}, time.Now())
	re := raw.(*routeEntry)

	// If the path is tainted, we reject any operation except for
	// Rollback and Revoke
	if re.tainted {
		switch req.Operation {
		case logical.RevokeOperation, logical.RollbackOperation:
		default:
			return logical.ErrorResponse(fmt.Sprintf("no handler for route '%s'", req.Path)), false, false, logical.ErrUnsupportedPath
		}
	}

	// Adjust the path to exclude the routing prefix
	originalPath := req.Path
	req.Path = strings.TrimPrefix(req.Path, mount)
	req.MountPoint = mount
	req.MountType = re.mountEntry.Type
	if req.Path == "/" {
		req.Path = ""
	}

	// Attach the storage view for the request
	req.Storage = re.storageView

	// Hash the request token unless this is the token backend
	clientToken := req.ClientToken
	switch {
	case strings.HasPrefix(originalPath, "auth/token/"):
	case strings.HasPrefix(originalPath, "sys/"):
	case strings.HasPrefix(originalPath, "cubbyhole/"):
		// In order for the token store to revoke later, we need to have the same
		// salted ID, so we double-salt what's going to the cubbyhole backend
		salt, err := r.tokenStoreSaltFunc()
		if err != nil {
			return nil, false, false, err
		}
		req.ClientToken = re.SaltID(salt.SaltID(req.ClientToken))
	default:
		req.ClientToken = re.SaltID(req.ClientToken)
	}

	// Cache the pointer to the original connection object
	originalConn := req.Connection

	// Cache the identifier of the request
	originalReqID := req.ID

	// Cache the client token's number of uses in the request
	originalClientTokenRemainingUses := req.ClientTokenRemainingUses
	req.ClientTokenRemainingUses = 0

	// Cache the headers and hide them from backends
	headers := req.Headers
	req.Headers = nil

	// Cache the wrap info of the request
	var wrapInfo *logical.RequestWrapInfo
	if req.WrapInfo != nil {
		wrapInfo = &logical.RequestWrapInfo{
			TTL:    req.WrapInfo.TTL,
			Format: req.WrapInfo.Format,
		}
	}

	// Reset the request before returning
	defer func() {
		req.Path = originalPath
		req.MountPoint = mount
		req.MountType = re.mountEntry.Type
		req.Connection = originalConn
		req.ID = originalReqID
		req.Storage = nil
		req.ClientToken = clientToken
		req.ClientTokenRemainingUses = originalClientTokenRemainingUses
		req.WrapInfo = wrapInfo
		req.Headers = headers
		// This is only set in one place, after routing, so should never be set
		// by a backend
		req.SetLastRemoteWAL(0)
	}()

	// Invoke the backend
	if existenceCheck {
		ok, exists, err := re.backend.HandleExistenceCheck(req)
		return nil, ok, exists, err
	} else {
		resp, err := re.backend.HandleRequest(req)
		return resp, false, false, err
	}
}

// RootPath checks if the given path requires root privileges
func (r *Router) RootPath(path string) bool {
	r.l.RLock()
	mount, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return false
	}
	re := raw.(*routeEntry)

	// Trim to get remaining path
	remain := strings.TrimPrefix(path, mount)

	// Check the rootPaths of this backend
	match, raw, ok := re.rootPaths.LongestPrefix(remain)
	if !ok {
		return false
	}
	prefixMatch := raw.(bool)

	// Handle the prefix match case
	if prefixMatch {
		return strings.HasPrefix(remain, match)
	}

	// Handle the exact match case
	return match == remain
}

// LoginPath checks if the given path is used for logins
func (r *Router) LoginPath(path string) bool {
	r.l.RLock()
	mount, raw, ok := r.root.LongestPrefix(path)
	r.l.RUnlock()
	if !ok {
		return false
	}
	re := raw.(*routeEntry)

	// Trim to get remaining path
	remain := strings.TrimPrefix(path, mount)

	// Check the loginPaths of this backend
	match, raw, ok := re.loginPaths.LongestPrefix(remain)
	if !ok {
		return false
	}
	prefixMatch := raw.(bool)

	// Handle the prefix match case
	if prefixMatch {
		return strings.HasPrefix(remain, match)
	}

	// Handle the exact match case
	return match == remain
}

// pathsToRadix converts a the mapping of special paths to a mapping
// of special paths to radix trees.
func pathsToRadix(paths []string) *radix.Tree {
	tree := radix.New()
	for _, path := range paths {
		// Check if this is a prefix or exact match
		prefixMatch := len(path) >= 1 && path[len(path)-1] == '*'
		if prefixMatch {
			path = path[:len(path)-1]
		}

		tree.Insert(path, prefixMatch)
	}

	return tree
}
<|endoftext|>


<commit_before>package arn

import (
	"sort"
	"strings"

	"github.com/fatih/color"
)

// SoundTrack ...
type SoundTrack struct {
	ID        string           `json:"id"`
	Media     []*ExternalMedia `json:"media"`
	Tags      []string         `json:"tags"`
	Likes     []string         `json:"likes"`
	Created   string           `json:"created"`
	CreatedBy string           `json:"createdBy"`
	Edited    string           `json:"edited"`

	mainAnime     *Anime
	createdByUser *User
}

// Link returns the permalink for the track.
func (track *SoundTrack) Link() string {
	return "/tracks/" + track.ID
}

// Anime fetches all tagged anime of the sound track.
func (track *SoundTrack) Anime() []*Anime {
	var animeList []*Anime

	for _, tag := range track.Tags {
		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			anime, err := GetAnime(animeID)

			if err != nil {
				color.Red("Error fetching anime: %v", err)
				continue
			}

			animeList = append(animeList, anime)
		}
	}

	return animeList
}

// MainAnime ...
func (track *SoundTrack) MainAnime() *Anime {
	if track.mainAnime != nil {
		return track.mainAnime
	}

	allAnime := track.Anime()

	if len(allAnime) == 0 {
		return nil
	}

	track.mainAnime = allAnime[0]
	return track.mainAnime
}

// CreatedByUser ...
func (track *SoundTrack) CreatedByUser() *User {
	if track.createdByUser != nil {
		return track.createdByUser
	}

	user, err := GetUser(track.CreatedBy)

	if err != nil {
		color.Red("Error fetching user: %v", err)
		return nil
	}

	track.createdByUser = user
	return track.createdByUser
}

// SortSoundTracksLatestFirst ...
func SortSoundTracksLatestFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		return tracks[i].Created > tracks[j].Created
	})
}

// GetSoundTrack ...
func GetSoundTrack(id string) (*SoundTrack, error) {
	track, err := DB.Get("SoundTrack", id)

	if err != nil {
		return nil, err
	}

	return track.(*SoundTrack), nil
}

// GetSoundTracksByUser ...
func GetSoundTracksByUser(user *User) ([]*SoundTrack, error) {
	var userTracks []*SoundTrack
	tracks, err := StreamSoundTracks()

	if err != nil {
		return nil, err
	}

	for track := range tracks {
		if track.CreatedBy == user.ID {
			userTracks = append(userTracks, track)
		}
	}

	return userTracks, nil
}

// StreamSoundTracks ...
func StreamSoundTracks() (chan *SoundTrack, error) {
	tracks, err := DB.All("SoundTrack")
	return tracks.(chan *SoundTrack), err
}

// AllSoundTracks ...
func AllSoundTracks() ([]*SoundTrack, error) {
	var all []*SoundTrack

	stream, err := StreamSoundTracks()

	if err != nil {
		return nil, err
	}

	for obj := range stream {
		all = append(all, obj)
	}

	return all, nil
}
<commit_msg>GetSoundTracksByTag<commit_after>package arn

import (
	"sort"
	"strings"

	"github.com/fatih/color"
)

// SoundTrack ...
type SoundTrack struct {
	ID        string           `json:"id"`
	Media     []*ExternalMedia `json:"media"`
	Tags      []string         `json:"tags"`
	Likes     []string         `json:"likes"`
	Created   string           `json:"created"`
	CreatedBy string           `json:"createdBy"`
	Edited    string           `json:"edited"`

	mainAnime     *Anime
	createdByUser *User
}

// Link returns the permalink for the track.
func (track *SoundTrack) Link() string {
	return "/tracks/" + track.ID
}

// Anime fetches all tagged anime of the sound track.
func (track *SoundTrack) Anime() []*Anime {
	var animeList []*Anime

	for _, tag := range track.Tags {
		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			anime, err := GetAnime(animeID)

			if err != nil {
				color.Red("Error fetching anime: %v", err)
				continue
			}

			animeList = append(animeList, anime)
		}
	}

	return animeList
}

// MainAnime ...
func (track *SoundTrack) MainAnime() *Anime {
	if track.mainAnime != nil {
		return track.mainAnime
	}

	allAnime := track.Anime()

	if len(allAnime) == 0 {
		return nil
	}

	track.mainAnime = allAnime[0]
	return track.mainAnime
}

// CreatedByUser ...
func (track *SoundTrack) CreatedByUser() *User {
	if track.createdByUser != nil {
		return track.createdByUser
	}

	user, err := GetUser(track.CreatedBy)

	if err != nil {
		color.Red("Error fetching user: %v", err)
		return nil
	}

	track.createdByUser = user
	return track.createdByUser
}

// SortSoundTracksLatestFirst ...
func SortSoundTracksLatestFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		return tracks[i].Created > tracks[j].Created
	})
}

// GetSoundTrack ...
func GetSoundTrack(id string) (*SoundTrack, error) {
	track, err := DB.Get("SoundTrack", id)

	if err != nil {
		return nil, err
	}

	return track.(*SoundTrack), nil
}

// GetSoundTracksByUser ...
func GetSoundTracksByUser(user *User) ([]*SoundTrack, error) {
	var userTracks []*SoundTrack
	tracks, err := StreamSoundTracks()

	if err != nil {
		return nil, err
	}

	for track := range tracks {
		if track.CreatedBy == user.ID {
			userTracks = append(userTracks, track)
		}
	}

	return userTracks, nil
}

// GetSoundTracksByTag ...
func GetSoundTracksByTag(filterTag string) ([]*SoundTrack, error) {
	var filteredTracks []*SoundTrack
	tracks, err := StreamSoundTracks()

	if err != nil {
		return nil, err
	}

	for track := range tracks {
		for _, tag := range track.Tags {
			if tag == filterTag {
				filteredTracks = append(filteredTracks, track)
				break
			}
		}
	}

	return filteredTracks, nil
}

// StreamSoundTracks ...
func StreamSoundTracks() (chan *SoundTrack, error) {
	tracks, err := DB.All("SoundTrack")
	return tracks.(chan *SoundTrack), err
}

// AllSoundTracks ...
func AllSoundTracks() ([]*SoundTrack, error) {
	var all []*SoundTrack

	stream, err := StreamSoundTracks()

	if err != nil {
		return nil, err
	}

	for obj := range stream {
		all = append(all, obj)
	}

	return all, nil
}
<|endoftext|>