<commit_before>package farm

// This file provides a 32-bit hash equivalent to CityHash32 (v1.1.1)
// and a 128-bit hash equivalent to CityHash128 (v1.1.1).  It also provides
// a seeded 32-bit hash function similar to CityHash32.

func hash32Len13to24Seed(s []byte, seed uint32) uint32 {
	slen := len(s)
	a := fetch32(s, -4+(slen>>1))
	b := fetch32(s, 4)
	c := fetch32(s, slen-8)
	d := fetch32(s, (slen >> 1))
	e := fetch32(s, 0)
	f := fetch32(s, slen-4)
	h := d*c1 + uint32(slen) + seed
	a = rotate32(a, 12) + f
	h = mur(c, h) + a
	a = rotate32(a, 3) + c
	h = mur(e, h) + a
	a = rotate32(a+f, 12) + d
	h = mur(b^seed, h) + a
	return fmix(h)
}

func hash32Len0to4(s []byte, seed uint32) uint32 {
	slen := len(s)
	b := seed
	c := uint32(9)
	for i := 0; i < slen; i++ {
		v := int8(s[i])
		b = uint32(b*c1) + uint32(v)
		c ^= b
	}
	return fmix(mur(b, mur(uint32(slen), c)))
}

func hash128to64(x uint128) uint64 {
	// Murmur-inspired hashing.
	const kMul uint64 = 0x9ddfea08eb382d69
	a := (x.lo ^ x.hi) * kMul
	a ^= (a >> 47)
	b := (x.hi ^ a) * kMul
	b ^= (b >> 47)
	b *= kMul
	return b
}

type uint128 struct {
	lo uint64
	hi uint64
}

// A subroutine for CityHash128().  Returns a decent 128-bit hash for strings
// of any length representable in signed long.  Based on City and Murmur.
func cityMurmur(s []byte, seed uint128) uint128 {
	slen := len(s)
	a := seed.lo
	b := seed.hi
	c := uint64(0)
	d := uint64(0)
	l := slen - 16
	if l <= 0 { // len <= 16
		a = shiftMix(a*k1) * k1
		c = b*k1 + hashLen0to16(s)
		if slen >= 8 {
			d = shiftMix(a + fetch64(s, 0))
		} else {
			d = shiftMix(a + c)
		}
	} else { // len > 16
		c = hashLen16(fetch64(s, int(slen-8))+k1, a)
		d = hashLen16(b+uint64(slen), c+fetch64(s, int(slen-16)))
		a += d
		for {
			a ^= shiftMix(fetch64(s, 0)*k1) * k1
			a *= k1
			b ^= a
			c ^= shiftMix(fetch64(s, 8)*k1) * k1
			c *= k1
			d ^= c
			s = s[16:]
			l -= 16
			if l <= 0 {
				break
			}
		}
	}
	a = hashLen16(a, c)
	b = hashLen16(d, b)
	return uint128{a ^ b, hashLen16(b, a)}
}

func cityHash128WithSeed(s []byte, seed uint128) uint128 {
	slen := len(s)
	if slen < 128 {
		return cityMurmur(s, seed)
	}

	// We expect len >= 128 to be the common case.  Keep 56 bytes of state:
	// v, w, x, y, and z.
	var v1, v2 uint64
	var w1, w2 uint64
	x := seed.lo
	y := seed.hi
	z := uint64(slen) * k1
	v1 = rotate64(y^k1, 49)*k1 + fetch64(s, 0)
	v2 = rotate64(v1, 42)*k1 + fetch64(s, 8)
	w1 = rotate64(y+z, 35)*k1 + x
	w2 = rotate64(x+fetch64(s, 88), 53) * k1

	// This is the same inner loop as CityHash64(), manually unrolled.
	for {
		x = rotate64(x+y+v1+fetch64(s, 8), 37) * k1
		y = rotate64(y+v2+fetch64(s, 48), 42) * k1
		x ^= w2
		y += v1 + fetch64(s, 40)
		z = rotate64(z+w1, 33) * k1
		v1, v2 = weakHashLen32WithSeeds(s, v2*k1, x+w1)
		w1, w2 = weakHashLen32WithSeeds(s[32:], z+w2, y+fetch64(s, 16))
		z, x = x, z
		s = s[64:]
		x = rotate64(x+y+v1+fetch64(s, 8), 37) * k1
		y = rotate64(y+v2+fetch64(s, 48), 42) * k1
		x ^= w2
		y += v1 + fetch64(s, 40)
		z = rotate64(z+w1, 33) * k1
		v1, v2 = weakHashLen32WithSeeds(s, v2*k1, x+w1)
		w1, w2 = weakHashLen32WithSeeds(s[32:], z+w2, y+fetch64(s, 16))
		z, x = x, z
		s = s[64:]
		slen -= 128
		if slen < 128 {
			break
		}
	}
	x += rotate64(v1+z, 49) * k0
	y = y*k0 + rotate64(w2, 37)
	z = z*k0 + rotate64(w1, 27)
	w1 *= 9
	v1 *= k0
	// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
	for tail_done := 0; tail_done < slen; {
		tail_done += 32
		y = rotate64(x+y, 42)*k0 + v2
		w1 += fetch64(s, slen-tail_done+16)
		x = x*k0 + w1
		z += w2 + fetch64(s, slen-tail_done)
		w2 += v1
		v1, v2 = weakHashLen32WithSeeds(s[slen-tail_done:], v1+z, v2)
		v1 *= k0
	}
	// At this point our 56 bytes of state should contain more than
	// enough information for a strong 128-bit hash.  We use two
	// different 56-byte-to-8-byte hashes to get a 16-byte final result.
	x = hashLen16(x, v1)
	y = hashLen16(y+z, w1)
	return uint128{hashLen16(x+v2, w2) + y,
		hashLen16(x+w2, y+v2)}
}

func cityHash128(s []byte) uint128 {
	slen := len(s)
	if slen >= 16 {
		cityHash128WithSeed(s, uint128{fetch64(s, 0), fetch64(s, 8) + k0})
	}
	return cityHash128WithSeed(s, uint128{k0, k1})
}

func Fingerprint128(s []byte) uint128 {
	return cityHash128(s)
}

func Fingerprint64(s []byte) uint64 {
	return Hash64(s)
}

func Fingerprint32(s []byte) uint32 {
	return Hash32(s)
}

func Hash128(s []byte) uint128 {
	return Fingerprint128(s)
}

func Hash128WithSeed(s []byte, seed uint128) uint128 {
	return cityHash128WithSeed(s, seed)
}
<commit_msg>fix accidental fallthrough on if/return<commit_after>package farm

// This file provides a 32-bit hash equivalent to CityHash32 (v1.1.1)
// and a 128-bit hash equivalent to CityHash128 (v1.1.1).  It also provides
// a seeded 32-bit hash function similar to CityHash32.

func hash32Len13to24Seed(s []byte, seed uint32) uint32 {
	slen := len(s)
	a := fetch32(s, -4+(slen>>1))
	b := fetch32(s, 4)
	c := fetch32(s, slen-8)
	d := fetch32(s, (slen >> 1))
	e := fetch32(s, 0)
	f := fetch32(s, slen-4)
	h := d*c1 + uint32(slen) + seed
	a = rotate32(a, 12) + f
	h = mur(c, h) + a
	a = rotate32(a, 3) + c
	h = mur(e, h) + a
	a = rotate32(a+f, 12) + d
	h = mur(b^seed, h) + a
	return fmix(h)
}

func hash32Len0to4(s []byte, seed uint32) uint32 {
	slen := len(s)
	b := seed
	c := uint32(9)
	for i := 0; i < slen; i++ {
		v := int8(s[i])
		b = uint32(b*c1) + uint32(v)
		c ^= b
	}
	return fmix(mur(b, mur(uint32(slen), c)))
}

func hash128to64(x uint128) uint64 {
	// Murmur-inspired hashing.
	const kMul uint64 = 0x9ddfea08eb382d69
	a := (x.lo ^ x.hi) * kMul
	a ^= (a >> 47)
	b := (x.hi ^ a) * kMul
	b ^= (b >> 47)
	b *= kMul
	return b
}

type uint128 struct {
	lo uint64
	hi uint64
}

// A subroutine for CityHash128().  Returns a decent 128-bit hash for strings
// of any length representable in signed long.  Based on City and Murmur.
func cityMurmur(s []byte, seed uint128) uint128 {
	slen := len(s)
	a := seed.lo
	b := seed.hi
	c := uint64(0)
	d := uint64(0)
	l := slen - 16
	if l <= 0 { // len <= 16
		a = shiftMix(a*k1) * k1
		c = b*k1 + hashLen0to16(s)
		if slen >= 8 {
			d = shiftMix(a + fetch64(s, 0))
		} else {
			d = shiftMix(a + c)
		}
	} else { // len > 16
		c = hashLen16(fetch64(s, int(slen-8))+k1, a)
		d = hashLen16(b+uint64(slen), c+fetch64(s, int(slen-16)))
		a += d
		for {
			a ^= shiftMix(fetch64(s, 0)*k1) * k1
			a *= k1
			b ^= a
			c ^= shiftMix(fetch64(s, 8)*k1) * k1
			c *= k1
			d ^= c
			s = s[16:]
			l -= 16
			if l <= 0 {
				break
			}
		}
	}
	a = hashLen16(a, c)
	b = hashLen16(d, b)
	return uint128{a ^ b, hashLen16(b, a)}
}

func cityHash128WithSeed(s []byte, seed uint128) uint128 {
	slen := len(s)
	if slen < 128 {
		return cityMurmur(s, seed)
	}

	// We expect len >= 128 to be the common case.  Keep 56 bytes of state:
	// v, w, x, y, and z.
	var v1, v2 uint64
	var w1, w2 uint64
	x := seed.lo
	y := seed.hi
	z := uint64(slen) * k1
	v1 = rotate64(y^k1, 49)*k1 + fetch64(s, 0)
	v2 = rotate64(v1, 42)*k1 + fetch64(s, 8)
	w1 = rotate64(y+z, 35)*k1 + x
	w2 = rotate64(x+fetch64(s, 88), 53) * k1

	// This is the same inner loop as CityHash64(), manually unrolled.
	for {
		x = rotate64(x+y+v1+fetch64(s, 8), 37) * k1
		y = rotate64(y+v2+fetch64(s, 48), 42) * k1
		x ^= w2
		y += v1 + fetch64(s, 40)
		z = rotate64(z+w1, 33) * k1
		v1, v2 = weakHashLen32WithSeeds(s, v2*k1, x+w1)
		w1, w2 = weakHashLen32WithSeeds(s[32:], z+w2, y+fetch64(s, 16))
		z, x = x, z
		s = s[64:]
		x = rotate64(x+y+v1+fetch64(s, 8), 37) * k1
		y = rotate64(y+v2+fetch64(s, 48), 42) * k1
		x ^= w2
		y += v1 + fetch64(s, 40)
		z = rotate64(z+w1, 33) * k1
		v1, v2 = weakHashLen32WithSeeds(s, v2*k1, x+w1)
		w1, w2 = weakHashLen32WithSeeds(s[32:], z+w2, y+fetch64(s, 16))
		z, x = x, z
		s = s[64:]
		slen -= 128
		if slen < 128 {
			break
		}
	}
	x += rotate64(v1+z, 49) * k0
	y = y*k0 + rotate64(w2, 37)
	z = z*k0 + rotate64(w1, 27)
	w1 *= 9
	v1 *= k0
	// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
	for tail_done := 0; tail_done < slen; {
		tail_done += 32
		y = rotate64(x+y, 42)*k0 + v2
		w1 += fetch64(s, slen-tail_done+16)
		x = x*k0 + w1
		z += w2 + fetch64(s, slen-tail_done)
		w2 += v1
		v1, v2 = weakHashLen32WithSeeds(s[slen-tail_done:], v1+z, v2)
		v1 *= k0
	}
	// At this point our 56 bytes of state should contain more than
	// enough information for a strong 128-bit hash.  We use two
	// different 56-byte-to-8-byte hashes to get a 16-byte final result.
	x = hashLen16(x, v1)
	y = hashLen16(y+z, w1)
	return uint128{hashLen16(x+v2, w2) + y,
		hashLen16(x+w2, y+v2)}
}

func cityHash128(s []byte) uint128 {
	slen := len(s)
	if slen >= 16 {
		return cityHash128WithSeed(s[16:], uint128{fetch64(s, 0), fetch64(s, 8) + k0})
	}
	return cityHash128WithSeed(s, uint128{k0, k1})
}

func Fingerprint128(s []byte) uint128 {
	return cityHash128(s)
}

func Fingerprint64(s []byte) uint64 {
	return Hash64(s)
}

func Fingerprint32(s []byte) uint32 {
	return Hash32(s)
}

func Hash128(s []byte) uint128 {
	return Fingerprint128(s)
}

func Hash128WithSeed(s []byte, seed uint128) uint128 {
	return cityHash128WithSeed(s, seed)
}
<|endoftext|>


<commit_before>// Results from the competition are here:
// https://www.codingame.com/challengereport/5708992986028c48464b3d45bbb4a490b2d6015

package main

import "fmt"
//import "os"
import "bytes"
import "strconv"

// These indicate the contents of each cell of the floor in the input
const WALL   = -2
const CELL   = -1
const BOX    = 0
const ITEM_RANGE  = 1
const ITEM_BOMB   = 2
const EXPLOSION = 32

// These are the values given to each type of cell during scoring of the floor
const WALL_SCORE    = -20
const DANGER_SCORE  = -30
const CELL_SCORE    = -1
const TOO_FAR       = 100

// These are entity types
const PLAYER = 0
const BOMB   = 1
const ITEM   = 2

// floor constraints
const WIDTH  = 13
const HEIGHT = 11

// time it takes for a bomb to explode, in turns (useless?)
const MAX_BOMB_TIME = 8
// turns I'm willing to spend searching for a path to a particular cell
// I had timeouts when this was > 8, which of course would also depend upon the ineffficiency of my code at the time
const SEARCH_DEPTH_LIMIT = 8

type Bomb struct {
    x, y int    // coordinates of the bomb
    time int    // number of turns until explosion
    reach int   // number of cells it reaches in each direction
}
type Cell struct {
    score int
    distance int
}

func main() {
    var width, height, myId int
    fmt.Scan(&width, &height, &myId)

    turnCounter := 0
    myX, myY := 2, 2
    var myReach int       // explosion range of my bombs
    var myBombCount int
    var floor [WIDTH][HEIGHT]int
    for {
        bombsOnTheFloor := []Bomb{}
        floor = [WIDTH][HEIGHT]int{}
        for i := 0; i < height; i++ {   // the grid of boxes and empty cells
            var row string
            fmt.Scan(&row)
//            fmt.Fprintln(os.Stderr, row)
            floor = buildTheFloor(row, i, floor)
        }
//        fmt.Fprintln(os.Stderr, floorToString(floor))

        var entities int
        fmt.Scan(&entities)             // how many players & bombs are on the grid
        for i := 0; i < entities; i++ { // info about all the players & bombs on the grid
            var entityType, owner, x, y, param1, param2 int
            fmt.Scan(&entityType, &owner, &x, &y, &param1, &param2)
            if entityType == PLAYER && owner == myId {
                myBombCount = param1
                myReach = param2 // may have changed due to power ups
                myX = x
                myY = y
            }
            if entityType == BOMB { // don't bother going here (get x,y and affect their score somehow)
                bombsOnTheFloor = append(bombsOnTheFloor,
                                  Bomb{x: x, y: y, time: param1, reach: param2})
            }
        }
//        fmt.Fprintln(os.Stderr, bombsOnTheFloor)

        xT, yT := myX, myY
        maxScore := Cell{score: WALL_SCORE, distance: TOO_FAR}
        scoreFloor := scoreTheFloor(myX, myY, bombsOnTheFloor, myReach, floor)
        scoreFloor = markBombs(bombsOnTheFloor, scoreFloor)
        floor = transferExplosions(scoreFloor, floor)

        for i := 0; i < WIDTH; i++ {
            for j := 0; j < HEIGHT; j++ {
                if scoreFloor[i][j].score > maxScore.score || (scoreFloor[i][j].score == maxScore.score && scoreFloor[i][j].distance < maxScore.distance) {
                    maxScore = scoreFloor[i][j]
                    xT = i
                    yT = j
                }
            }
        }
//        fmt.Fprintln(os.Stderr, fmt.Sprintf("MAX: %d, TARGET: %d, x: %d, y: %d", maxScore, scoreFloor[xT][yT], xT, yT))
//        fmt.Fprintln(os.Stderr, scoreFloorToString(scoreFloor))
//        fmt.Fprintln(os.Stderr, floorToString(floor))
//        if canIBeHere(myX, myY, 0, bombsOnTheFloor, floor) && !canIBeHere(x, y, 1, bombsOnTheFloor, floor)
        if  myBombCount > 0 && myX == xT && myY == yT && canIEscapeThisBomb(myX, myY, Bomb{x: myX, y: myY, reach: myReach, time: MAX_BOMB_TIME}, MAX_BOMB_TIME, 0, myReach, bombsOnTheFloor, floor) { // drop bomb on current cell while moving toward target cell (could be equivalent)
            fmt.Println(fmt.Sprintf("BOMB %d %d BUTT SOUP", xT, yT))
        } else {
            fmt.Println(fmt.Sprintf("MOVE %d %d (%d, %d) = %d", xT, yT, xT, yT, scoreFloor[xT][yT]))
        }

        turnCounter++
    }
}

/**
 *
 **/
func canIEscapeThisBomb(myX int, myY int, bomb Bomb, turnLimit int, numTurns int, reach int, bombs []Bomb, floor [WIDTH][HEIGHT]int) bool {
//    fmt.Fprintln(os.Stderr, fmt.Sprintf("MX: %d, MY: %d, BX: %d, BY: %d, t: %d, r: %d", myX, myY, bombX, bombY, numTurns, reach))
    // Already safe on a diagonal from the bomb's cell, don't need to move
    if myX != bomb.x && myY != bomb.y {return true}
    // I'm lined up with the bomb, but out of its reach
//    fmt.Fprintln(os.Stderr, fmt.Sprintf("myX: %d, myY: %d, BX: %d, BY: %d, turns: %d", myX, myY, bombX, bombY, numTurns))
    if myX > bomb.x + reach || myX < bomb.x - reach || myY > bomb.y + reach || myY < bomb.y - reach {return true}
//    fmt.Fprintln(os.Stderr, "MARKER 1")
    // In danger, need to move, but there is no time left
    if turnLimit - numTurns < 1 {return false}
    // In danger, need to move, have some time left
    if canIBeHere(myX+1, myY, numTurns+1, bombs, floor) && canIEscapeThisBomb(myX+1, myY, bomb, turnLimit, numTurns+1, reach, bombs, floor) {return true}
    if canIBeHere(myX-1, myY, numTurns+1, bombs, floor) && canIEscapeThisBomb(myX-1, myY, bomb, turnLimit, numTurns+1, reach, bombs, floor) {return true}
    if canIBeHere(myX, myY+1, numTurns+1, bombs, floor) && canIEscapeThisBomb(myX, myY+1, bomb, turnLimit, numTurns+1, reach, bombs, floor) {return true}
    if canIBeHere(myX, myY-1, numTurns+1, bombs, floor) && canIEscapeThisBomb(myX, myY-1, bomb, turnLimit, numTurns+1, reach, bombs, floor) {return true}
    // in danger, no where to go
    return false
}

func willIDieHere(x int, y int, bombs []Bomb, floor [WIDTH][HEIGHT]int) bool {
    for _, bomb := range bombs {
        if canIEscapeThisBomb(x, y, bomb, bomb.time, 0, bomb.reach, bombs, floor) {
            return false
        }
    }
    return true
}

/**
 * How many boxes are within bombing range of the given cell, are there items in those boxes, and can I get there?
 **/
func scoreACell(x int, y int, myX int, myY int, bombsOnTheFloor []Bomb, myReach int, floor [WIDTH][HEIGHT]int) Cell {
    if (myX != x || myY != y) { // I'm not already standing here
        if !canIBeHere(x, y, 1, bombsOnTheFloor, floor) {return Cell{score: WALL_SCORE, distance: TOO_FAR}} // cannot move to here next turn
    }
    moves, maybe := canIGoToThere(myX, myY, myX, myY, x, y, SEARCH_DEPTH_LIMIT, bombsOnTheFloor, floor)
    if !maybe {return Cell{score: WALL_SCORE, distance: TOO_FAR}} // cannot get here, even after multiple turns
    if willIDieHere(x, y, bombsOnTheFloor, floor) {return Cell{score: DANGER_SCORE, distance: TOO_FAR}} // does not account for time left on the bomb, could optimize here rather than walling it off
    score := 0
    for i := 0; i < myReach; i++ {
        if x+i < WIDTH && floor[x+i][y] >= BOX {score++}
        if x-i > 0 && floor[x-i][y] >= BOX {score++}
        if y+i < HEIGHT && floor[x][y+i] >= BOX {score++}
        if y-i > 0 && floor[x][y-i] >= BOX {score++}
    }
    if floor[x][y] > BOX {score++} // there's an item in the box
    return Cell{score: score, distance: moves}
}

func scoreTheFloor(myX int, myY int, bombsOnTheFloor []Bomb, myReach int, floor [WIDTH][HEIGHT]int) [WIDTH][HEIGHT]Cell{
    scoreFloor := [WIDTH][HEIGHT]Cell{}
    for i := 0; i < WIDTH; i++ {
        for j := 0; j < HEIGHT; j++ {
            scoreFloor[i][j] = scoreACell(i, j, myX, myY, bombsOnTheFloor, myReach, floor)
        }
    }
    return scoreFloor
}

func canIGoToThere(x int, y int, myX int, myY int, xT int, yT int, moveLimit int, bombs []Bomb, floor [WIDTH][HEIGHT]int) (distance int, maybe bool) {
//    fmt.Fprintln(os.Stderr, fmt.Sprintf("GO - x: %d, y: %d, m: %d", myX, myY, moves))
    moves, minMoves := 0, TOO_FAR
    yes, isPathFound := false, false
    if moveLimit < 1 {return TOO_FAR, false}
    // if it's not the cell that I'm already standing on, then ensure that I can stand on it when I get there
    if (x != myX || y != myY) && !canIBeHere(myX, myY, 0, bombs, floor) {return TOO_FAR, false}
    if myX == xT && myY == yT {return moves, true}

    // try moving Right
    moves, yes = canIGoToThere(x, y, myX+1, myY, xT, yT, moveLimit-1, bombs, floor)
    if yes {
        moves++
        if moves < minMoves {minMoves = moves}
        isPathFound = true
    }
    // try moving Left
    moves, yes = canIGoToThere(x, y, myX-1, myY, xT, yT, moveLimit-1, bombs, floor)
    if yes {
        moves++
        if moves < minMoves {minMoves = moves}
        isPathFound = true
    }
    // try moving Down
    moves, yes = canIGoToThere(x, y, myX, myY+1, xT, yT, moveLimit-1, bombs, floor)
    if yes {
        moves++
        if moves < minMoves {minMoves = moves}
        isPathFound = true
    }
    // try moving Up
    moves, yes = canIGoToThere(x, y, myX, myY-1, xT, yT, moveLimit-1, bombs, floor)
    if yes {
        moves++
        if moves < minMoves {minMoves = moves}
        isPathFound = true
    }

    // all possibilities exhausted
    return minMoves, isPathFound
}

func markBombs(bombs []Bomb, scoreFloor [WIDTH][HEIGHT]Cell) [WIDTH][HEIGHT]Cell {
    var dangerScore int
    for _, bomb := range bombs {
        if bomb.time < 2 {
            dangerScore = DANGER_SCORE
            // on the bomb and it's exploding
            scoreFloor[bomb.x][bomb.y].score = dangerScore
        } else {
            dangerScore = DANGER_SCORE / (bomb.time - 1) // treat the bomb's timer as if we'd already advanced to the next turn
        }
        // on the bomb
        if scoreFloor[bomb.x][bomb.y].score > dangerScore {scoreFloor[bomb.x][bomb.y].score = WALL_SCORE}
        // left of the bomb
        for i := bomb.x; i >= bomb.x - bomb.reach; i-- {
            if amIWithinTheBoundaries(i, 0) { // prevent array index out of bounds
                if scoreFloor[i][bomb.y].score > dangerScore { // do not overwrite a score that's already even lower
                    scoreFloor[i][bomb.y].score = dangerScore
                    if scoreFloor[i][bomb.y].score == WALL_SCORE {break} // stop propagating the explosion in this direction, there is a blocker (wall or box)
                }
            }
        }
        // right of the bomb
        for i := bomb.x; i <= bomb.x+bomb.reach; i++ {
            if amIWithinTheBoundaries(i, 0) { // prevent array index out of bounds
                if scoreFloor[i][bomb.y].score > dangerScore { // do not overwrite a score that's already even lower
                    scoreFloor[i][bomb.y].score = dangerScore
                    if scoreFloor[i][bomb.y].score == WALL_SCORE {break} // stop propagating the explosion in this direction, there is a blocker (wall or box)
                }
            }
        }
        // below the bomb
        for i := bomb.y; i >= bomb.y - bomb.reach; i-- {
            if amIWithinTheBoundaries(0, i) {
                if scoreFloor[bomb.x][i].score > dangerScore {
                    scoreFloor[bomb.x][i].score = dangerScore
                    if scoreFloor[bomb.x][i].score == WALL_SCORE {break} // stop propagating the explosion in this direction, there is a blocker (wall or box)
                }
            }
        }
        // above the bomb
        for i := bomb.y; i <= bomb.y+bomb.reach; i++ {
            if amIWithinTheBoundaries(0, i) {
                if scoreFloor[bomb.x][i].score > dangerScore {
                    scoreFloor[bomb.x][i].score = dangerScore
                    if scoreFloor[bomb.x][i].score == WALL_SCORE {break} // stop propagating the explosion in this direction, there is a blocker (wall or box)
                }
            }
        }
    }
    return scoreFloor
}

func buildTheFloor(row string, y int, floor [WIDTH][HEIGHT]int) [WIDTH][HEIGHT]int {
    width := len(row)
    for x := 0; x < width; x++ {
        if string(row[x]) == "." {
            floor[x][y] = CELL
        } else if string(row[x]) == "X" {
            floor[x][y] = WALL
        } else {
            floor[x][y] = int(row[x] - '0')
        }
    }
    return floor
}

func canIBeHere(x int, y int, timeElapsed int, bombs []Bomb, floor [WIDTH][HEIGHT]int) bool {
    if !amIWithinTheBoundaries(x, y) {return false}
    if floor[x][y] == WALL || floor[x][y] >= BOX || floor[x][y] == EXPLOSION {return false}
    for _, bomb := range bombs {
        if x == bomb.x && y == bomb.y {return false} // can't walk through bombs once they're placed
    }
    return true
}

func amIWithinTheBoundaries(x int, y int) bool {
    if x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT {return false}
    return true
}

// TODO: this does not account for walls & boxes, which block propagation of the explosion
func amIWithinTheBlastRadius(myX int, myY int, bomb Bomb) bool{
    if myX > bomb.x + bomb.reach || myX < bomb.x - bomb.reach || myY > bomb.y + bomb.reach || myY < bomb.y - bomb.reach {return true}
    return false
}

func transferExplosions(scoreFloor [WIDTH][HEIGHT]Cell, floor [WIDTH][HEIGHT]int) [WIDTH][HEIGHT]int {
    for i := 0; i < WIDTH; i++ {
        for j := 0; j < HEIGHT; j++ {
            if scoreFloor[i][j].score == DANGER_SCORE {floor[i][j] = EXPLOSION}
        }
    }
    return floor
}

func scoreFloorToString(floor [WIDTH][HEIGHT]Cell) string {
    var buffer bytes.Buffer
    var scoreStr, distanceStr string
    for i := 0; i < HEIGHT; i++ {
        for j := 0; j < WIDTH; j++ {
            scoreStr = strconv.Itoa(floor[j][i].score)
            distanceStr = strconv.Itoa(floor[j][i].distance)
            buffer.WriteString("[")
            for f := 0; f < 3 - len(scoreStr); f++ {buffer.WriteString(" ")}
            buffer.WriteString(scoreStr)
            buffer.WriteString(", ")
            for f := 0; f < 3 - len(distanceStr); f++ {buffer.WriteString(" ")}
            buffer.WriteString(distanceStr)
            buffer.WriteString("]")
        }
        buffer.WriteString("\n")
    }
    return buffer.String()
}

func floorToString(floor [WIDTH][HEIGHT]int) string {
    var buffer bytes.Buffer
    var cell int
    for i := 0; i < HEIGHT; i++ {
        for j := 0; j < WIDTH; j++ {
            cell = floor[j][i]
            buffer.WriteString(" ")
            if cell == BOX {buffer.WriteString("B")}
            if cell == WALL {buffer.WriteString("W")}
            if cell == CELL {buffer.WriteString("_")}
            if cell == EXPLOSION {buffer.WriteString("E")}
            if cell == ITEM_RANGE || cell == ITEM_BOMB {buffer.WriteString("I")}
            buffer.WriteString(" ")
        }
        buffer.WriteString("\n")
    }
    return buffer.String()
}

<commit_msg>Delete hypersonic.go<commit_after><|endoftext|>


<commit_before>// This file contains XML structures for communicating with UPnP devices.

package goupnp

import (
	"encoding/xml"
	"errors"
	"fmt"
	"net/url"
	"strings"

	"github.com/huin/goupnp/scpd"
	"github.com/huin/goupnp/soap"
)

const (
	DeviceXMLNamespace = "urn:schemas-upnp-org:device-1-0"
)

// RootDevice is the device description as described by section 2.3 "Device
// description" in
// http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf
type RootDevice struct {
	XMLName     xml.Name    `xml:"root"`
	SpecVersion SpecVersion `xml:"specVersion"`
	URLBase     url.URL     `xml:"-"`
	URLBaseStr  string      `xml:"URLBase"`
	Device      Device      `xml:"device"`
}

// SetURLBase sets the URLBase for the RootDevice and its underlying components.
func (root *RootDevice) SetURLBase(urlBase *url.URL) {
	root.URLBase = *urlBase
	root.URLBaseStr = urlBase.String()
	root.Device.SetURLBase(urlBase)
}

// SpecVersion is part of a RootDevice, describes the version of the
// specification that the data adheres to.
type SpecVersion struct {
	Major int32 `xml:"major"`
	Minor int32 `xml:"minor"`
}

// Device is a UPnP device. It can have child devices.
type Device struct {
	DeviceType       string    `xml:"deviceType"`
	FriendlyName     string    `xml:"friendlyName"`
	Manufacturer     string    `xml:"manufacturer"`
	ManufacturerURL  URLField  `xml:"manufacturerURL"`
	ModelDescription string    `xml:"modelDescription"`
	ModelName        string    `xml:"modelName"`
	ModelNumber      string    `xml:"modelNumber"`
	ModelURL         URLField  `xml:"modelURL"`
	SerialNumber     string    `xml:"serialNumber"`
	UDN              string    `xml:"UDN"`
	UPC              string    `xml:"UPC,omitempty"`
	Icons            []Icon    `xml:"iconList>icon,omitempty"`
	Services         []Service `xml:"serviceList>service,omitempty"`
	Devices          []Device  `xml:"deviceList>device,omitempty"`

	// Extra observed elements:
	PresentationURL URLField `xml:"presentationURL"`
}

// VisitDevices calls visitor for the device, and all its descendent devices.
func (device *Device) VisitDevices(visitor func(*Device)) {
	visitor(device)
	for i := range device.Devices {
		device.Devices[i].VisitDevices(visitor)
	}
}

// VisitServices calls visitor for all Services under the device and all its
// descendent devices.
func (device *Device) VisitServices(visitor func(*Service)) {
	device.VisitDevices(func(d *Device) {
		for i := range d.Services {
			visitor(&d.Services[i])
		}
	})
}

// FindService finds all (if any) Services under the device and its descendents
// that have the given ServiceType.
func (device *Device) FindService(serviceType string) []*Service {
	var services []*Service
	device.VisitServices(func(s *Service) {
		if s.ServiceType == serviceType {
			services = append(services, s)
		}
	})
	return services
}

// SetURLBase sets the URLBase for the Device and its underlying components.
func (device *Device) SetURLBase(urlBase *url.URL) {
	device.ManufacturerURL.SetURLBase(urlBase)
	device.ModelURL.SetURLBase(urlBase)
	device.PresentationURL.SetURLBase(urlBase)
	for i := range device.Icons {
		device.Icons[i].SetURLBase(urlBase)
	}
	for i := range device.Services {
		device.Services[i].SetURLBase(urlBase)
	}
	for i := range device.Devices {
		device.Devices[i].SetURLBase(urlBase)
	}
}

func (device *Device) String() string {
	return fmt.Sprintf("Device ID %s : %s (%s)", device.UDN, device.DeviceType, device.FriendlyName)
}

// Icon is a representative image that a device might include in its
// description.
type Icon struct {
	Mimetype string   `xml:"mimetype"`
	Width    int32    `xml:"width"`
	Height   int32    `xml:"height"`
	Depth    int32    `xml:"depth"`
	URL      URLField `xml:"url"`
}

// SetURLBase sets the URLBase for the Icon.
func (icon *Icon) SetURLBase(url *url.URL) {
	icon.URL.SetURLBase(url)
}

// Service is a service provided by a UPnP Device.
type Service struct {
	ServiceType string   `xml:"serviceType"`
	ServiceId   string   `xml:"serviceId"`
	SCPDURL     URLField `xml:"SCPDURL"`
	ControlURL  URLField `xml:"controlURL"`
	EventSubURL URLField `xml:"eventSubURL"`
}

// SetURLBase sets the URLBase for the Service.
func (srv *Service) SetURLBase(urlBase *url.URL) {
	srv.SCPDURL.SetURLBase(urlBase)
	srv.ControlURL.SetURLBase(urlBase)
	srv.EventSubURL.SetURLBase(urlBase)
}

func (srv *Service) String() string {
	return fmt.Sprintf("Service ID %s : %s", srv.ServiceId, srv.ServiceType)
}

// RequestSCPD requests the SCPD (soap actions and state variables description)
// for the service.
func (srv *Service) RequestSCPD() (*scpd.SCPD, error) {
	if !srv.SCPDURL.Ok {
		return nil, errors.New("bad/missing SCPD URL, or no URLBase has been set")
	}
	s := new(scpd.SCPD)
	if err := requestXml(srv.SCPDURL.URL.String(), scpd.SCPDXMLNamespace, s); err != nil {
		return nil, err
	}
	return s, nil
}

// RequestSCDP is for compatibility only, prefer RequestSCPD. This was a
// misspelling of RequestSCDP.
func (srv *Service) RequestSCDP() (*scpd.SCPD, error) {
	return srv.RequestSCPD()
}

func (srv *Service) NewSOAPClient() *soap.SOAPClient {
	return soap.NewSOAPClient(srv.ControlURL.URL)
}

// URLField is a URL that is part of a device description.
type URLField struct {
	URL url.URL `xml:"-"`
	Ok  bool    `xml:"-"`
	Str string  `xml:",chardata"`
}

func (uf *URLField) SetURLBase(urlBase *url.URL) {
	str := uf.Str
	if !strings.Contains(str, "://") && !strings.HasPrefix(str, "/") {
		str = "/" + str
	}

	refUrl, err := url.Parse(str)
	if err != nil {
		uf.URL = url.URL{}
		uf.Ok = false
		return
	}

	uf.URL = *urlBase.ResolveReference(refUrl)
	uf.Ok = true
}
<commit_msg>adding ModelType to Device<commit_after>// This file contains XML structures for communicating with UPnP devices.

package goupnp

import (
	"encoding/xml"
	"errors"
	"fmt"
	"net/url"
	"strings"

	"github.com/huin/goupnp/scpd"
	"github.com/huin/goupnp/soap"
)

const (
	DeviceXMLNamespace = "urn:schemas-upnp-org:device-1-0"
)

// RootDevice is the device description as described by section 2.3 "Device
// description" in
// http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf
type RootDevice struct {
	XMLName     xml.Name    `xml:"root"`
	SpecVersion SpecVersion `xml:"specVersion"`
	URLBase     url.URL     `xml:"-"`
	URLBaseStr  string      `xml:"URLBase"`
	Device      Device      `xml:"device"`
}

// SetURLBase sets the URLBase for the RootDevice and its underlying components.
func (root *RootDevice) SetURLBase(urlBase *url.URL) {
	root.URLBase = *urlBase
	root.URLBaseStr = urlBase.String()
	root.Device.SetURLBase(urlBase)
}

// SpecVersion is part of a RootDevice, describes the version of the
// specification that the data adheres to.
type SpecVersion struct {
	Major int32 `xml:"major"`
	Minor int32 `xml:"minor"`
}

// Device is a UPnP device. It can have child devices.
type Device struct {
	DeviceType       string    `xml:"deviceType"`
	FriendlyName     string    `xml:"friendlyName"`
	Manufacturer     string    `xml:"manufacturer"`
	ManufacturerURL  URLField  `xml:"manufacturerURL"`
	ModelDescription string    `xml:"modelDescription"`
	ModelName        string    `xml:"modelName"`
	ModelNumber      string    `xml:"modelNumber"`
	ModelType        string    `xml:"modelType"`
	ModelURL         URLField  `xml:"modelURL"`
	SerialNumber     string    `xml:"serialNumber"`
	UDN              string    `xml:"UDN"`
	UPC              string    `xml:"UPC,omitempty"`
	Icons            []Icon    `xml:"iconList>icon,omitempty"`
	Services         []Service `xml:"serviceList>service,omitempty"`
	Devices          []Device  `xml:"deviceList>device,omitempty"`

	// Extra observed elements:
	PresentationURL URLField `xml:"presentationURL"`
}

// VisitDevices calls visitor for the device, and all its descendent devices.
func (device *Device) VisitDevices(visitor func(*Device)) {
	visitor(device)
	for i := range device.Devices {
		device.Devices[i].VisitDevices(visitor)
	}
}

// VisitServices calls visitor for all Services under the device and all its
// descendent devices.
func (device *Device) VisitServices(visitor func(*Service)) {
	device.VisitDevices(func(d *Device) {
		for i := range d.Services {
			visitor(&d.Services[i])
		}
	})
}

// FindService finds all (if any) Services under the device and its descendents
// that have the given ServiceType.
func (device *Device) FindService(serviceType string) []*Service {
	var services []*Service
	device.VisitServices(func(s *Service) {
		if s.ServiceType == serviceType {
			services = append(services, s)
		}
	})
	return services
}

// SetURLBase sets the URLBase for the Device and its underlying components.
func (device *Device) SetURLBase(urlBase *url.URL) {
	device.ManufacturerURL.SetURLBase(urlBase)
	device.ModelURL.SetURLBase(urlBase)
	device.PresentationURL.SetURLBase(urlBase)
	for i := range device.Icons {
		device.Icons[i].SetURLBase(urlBase)
	}
	for i := range device.Services {
		device.Services[i].SetURLBase(urlBase)
	}
	for i := range device.Devices {
		device.Devices[i].SetURLBase(urlBase)
	}
}

func (device *Device) String() string {
	return fmt.Sprintf("Device ID %s : %s (%s)", device.UDN, device.DeviceType, device.FriendlyName)
}

// Icon is a representative image that a device might include in its
// description.
type Icon struct {
	Mimetype string   `xml:"mimetype"`
	Width    int32    `xml:"width"`
	Height   int32    `xml:"height"`
	Depth    int32    `xml:"depth"`
	URL      URLField `xml:"url"`
}

// SetURLBase sets the URLBase for the Icon.
func (icon *Icon) SetURLBase(url *url.URL) {
	icon.URL.SetURLBase(url)
}

// Service is a service provided by a UPnP Device.
type Service struct {
	ServiceType string   `xml:"serviceType"`
	ServiceId   string   `xml:"serviceId"`
	SCPDURL     URLField `xml:"SCPDURL"`
	ControlURL  URLField `xml:"controlURL"`
	EventSubURL URLField `xml:"eventSubURL"`
}

// SetURLBase sets the URLBase for the Service.
func (srv *Service) SetURLBase(urlBase *url.URL) {
	srv.SCPDURL.SetURLBase(urlBase)
	srv.ControlURL.SetURLBase(urlBase)
	srv.EventSubURL.SetURLBase(urlBase)
}

func (srv *Service) String() string {
	return fmt.Sprintf("Service ID %s : %s", srv.ServiceId, srv.ServiceType)
}

// RequestSCPD requests the SCPD (soap actions and state variables description)
// for the service.
func (srv *Service) RequestSCPD() (*scpd.SCPD, error) {
	if !srv.SCPDURL.Ok {
		return nil, errors.New("bad/missing SCPD URL, or no URLBase has been set")
	}
	s := new(scpd.SCPD)
	if err := requestXml(srv.SCPDURL.URL.String(), scpd.SCPDXMLNamespace, s); err != nil {
		return nil, err
	}
	return s, nil
}

// RequestSCDP is for compatibility only, prefer RequestSCPD. This was a
// misspelling of RequestSCDP.
func (srv *Service) RequestSCDP() (*scpd.SCPD, error) {
	return srv.RequestSCPD()
}

func (srv *Service) NewSOAPClient() *soap.SOAPClient {
	return soap.NewSOAPClient(srv.ControlURL.URL)
}

// URLField is a URL that is part of a device description.
type URLField struct {
	URL url.URL `xml:"-"`
	Ok  bool    `xml:"-"`
	Str string  `xml:",chardata"`
}

func (uf *URLField) SetURLBase(urlBase *url.URL) {
	str := uf.Str
	if !strings.Contains(str, "://") && !strings.HasPrefix(str, "/") {
		str = "/" + str
	}

	refUrl, err := url.Parse(str)
	if err != nil {
		uf.URL = url.URL{}
		uf.Ok = false
		return
	}

	uf.URL = *urlBase.ResolveReference(refUrl)
	uf.Ok = true
}
<|endoftext|>


<commit_before>package apiserver

import (
	"bufio"
	"compress/gzip"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	"github.com/openshift/origin/pkg/test/ginkgo/result"
	clihelpers "github.com/openshift/origin/test/extended/cli"
	exutil "github.com/openshift/origin/test/extended/util"
)

var _ = g.Describe("[sig-api-machinery][Feature:APIServer][Late]", func() {
	defer g.GinkgoRecover()

	oc := exutil.NewCLIWithoutNamespace("terminating-kube-apiserver")

	// This test checks whether the apiserver reports any events that may indicate a problem at any time,
	// not just when the suite is running. We already have invariant tests that fail if these are violated
	// during suite execution, but we want to know if there are fingerprints of these failures outside of tests.
	g.It("kubelet terminates kube-apiserver gracefully", func() {
		client, err := kubernetes.NewForConfig(oc.AdminConfig())
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		evs, err := client.CoreV1().Events("openshift-kube-apiserver").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		var messages []string
		eventsAfterTime := exutil.LimitTestsToStartTime()
		for _, ev := range evs.Items {
			if ev.LastTimestamp.Time.Before(eventsAfterTime) {
				continue
			}
			if ev.Reason != "NonGracefulTermination" {
				continue
			}
			data, _ := json.Marshal(ev)
			messages = append(messages, string(data))
		}
		if len(messages) > 0 {
			result.Flakef("kube-apiserver reported a non-graceful termination (after %s which is test environment dependent). Probably kubelet or CRI-O is not giving the time to cleanly shut down. This can lead to connection refused and network I/O timeout errors in other components.\n\n%s", eventsAfterTime, strings.Join(messages, "\n"))
		}
	})

	// This test checks whether the apiserver reports any events that may indicate a problem at any time,
	// not just when the suite is running. We already have invariant tests that fail if these are violated
	// during suite execution, but we want to know if there are fingerprints of these failures outside of tests.
	g.It("kube-apiserver terminates within graceful termination period", func() {
		client, err := kubernetes.NewForConfig(oc.AdminConfig())
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		evs, err := client.CoreV1().Events("openshift-kube-apiserver").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		var messages []string
		eventsAfterTime := exutil.LimitTestsToStartTime()
		for _, ev := range evs.Items {
			if ev.LastTimestamp.Time.Before(eventsAfterTime) {
				continue
			}
			if ev.Reason != "GracefulTerminationTimeout" {
				continue
			}
			data, _ := json.Marshal(ev)
			messages = append(messages, string(data))
		}
		if len(messages) > 0 {
			result.Flakef("kube-apiserver didn't terminate by itself during the graceful termination period (after %s which is test environment dependent). This is a bug in kube-apiserver. It probably means that network connections are not closed cleanly, and this leads to network I/O timeout errors in other components.\n\n%s", eventsAfterTime, strings.Join(messages, "\n"))
		}
	})

	g.It("API LBs follow /readyz of kube-apiserver and stop sending requests", func() {
		t := g.GinkgoT()

		client, err := kubernetes.NewForConfig(oc.AdminConfig())
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		evs, err := client.CoreV1().Events("openshift-kube-apiserver").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		eventsAfterTime := exutil.LimitTestsToStartTime()
		for _, ev := range evs.Items {
			if ev.LastTimestamp.Time.Before(eventsAfterTime) {
				continue
			}
			if ev.Reason != "LateConnections" {
				continue
			}

			t.Errorf("API LBs or the kubernetes service send requests to kube-apiserver far too late in termination process, probably due to broken LB configuration: %#v. This can lead to connection refused and network I/O timeout errors in other components.", ev)
		}
	})

	g.It("API LBs follow /readyz of kube-apiserver and don't send request early", func() {
		t := g.GinkgoT()

		client, err := kubernetes.NewForConfig(oc.AdminConfig())
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		evs, err := client.CoreV1().Events("openshift-kube-apiserver").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		eventsAfterTime := exutil.LimitTestsToStartTime()
		for _, ev := range evs.Items {
			if ev.LastTimestamp.Time.Before(eventsAfterTime) {
				continue
			}
			if ev.Reason != "NonReadyRequests" {
				continue
			}

			t.Errorf("API LBs or the kubernetes service send requests to kube-apiserver before it is ready, probably due to broken LB configuration: %#v. This can lead to inconsistent responses like 403s in other components.", ev)
		}
	})

	g.It("API LBs follow /readyz of kube-apiserver and stop sending requests before server shutdowns for external clients", func() {
		// set up
		results := map[string]int{}
		tempDir, err := ioutil.TempDir("", "test.oc-adm-must-gather.")
		o.Expect(err).NotTo(o.HaveOccurred())
		defer os.RemoveAll(tempDir)
		args := []string{"--dest-dir", tempDir, "--", "/usr/bin/gather_audit_logs"}

		// download the audit logs from the cluster
		o.Expect(oc.Run("adm", "must-gather").Args(args...).Execute()).To(o.Succeed())

		// act
		expectedDirectoriesToExpectedCount := []string{path.Join(clihelpers.GetPluginOutputDir(tempDir), "audit_logs", "kube-apiserver")}
		for _, auditDirectory := range expectedDirectoriesToExpectedCount {
			err := filepath.Walk(auditDirectory, func(path string, info os.FileInfo, err error) error {
				g.By(path)
				o.Expect(err).NotTo(o.HaveOccurred())

				if info.IsDir() {
					return nil
				}
				fileName := filepath.Base(path)
				if !clihelpers.IsAuditFile(fileName) {
					return nil
				}

				file, err := os.Open(path)
				o.Expect(err).NotTo(o.HaveOccurred())
				defer file.Close()
				fi, err := file.Stat()
				o.Expect(err).NotTo(o.HaveOccurred())
				if fi.Size() == 0 {
					return nil
				}

				gzipReader, err := gzip.NewReader(file)
				o.Expect(err).NotTo(o.HaveOccurred())

				apiServerName := extractAPIServerNameFromAuditFile(fileName)
				o.Expect(apiServerName).ToNot(o.BeEmpty())
				lateRequestCounter := 0
				readFile := false

				scanner := bufio.NewScanner(gzipReader)
				for scanner.Scan() {
					text := scanner.Text()
					if !strings.HasSuffix(text, "}") {
						continue // ignore truncated data
					}
					o.Expect(text).To(o.HavePrefix(`{"kind":"Event",`))

					if strings.Contains(text, "openshift.io/during-graceful") && strings.Contains(text, "openshift-origin-external-backend-sampler") {
						lateRequestCounter++
					}
					readFile = true
				}
				o.Expect(readFile).To(o.BeTrue())

				if lateRequestCounter > 0 {
					previousLateRequestCounter, _ := results[apiServerName]
					results[apiServerName] = previousLateRequestCounter + lateRequestCounter
				}

				return nil
			})
			o.Expect(err).NotTo(o.HaveOccurred())

			var finalMessageBuilder strings.Builder
			for apiServerName, lateRequestCounter := range results {
				// tolerate a few late request
				if lateRequestCounter > 10 {
					finalMessageBuilder.WriteString(fmt.Sprintf("\n %v observed %v late requests", apiServerName, lateRequestCounter))
				}
			}
			// for now, we will report it as flaky, change it to fail once it proves itself
			if len(finalMessageBuilder.String()) > 0 {
				result.Flakef("the following API Servers observed late requests: %v", finalMessageBuilder.String())
			}
		}
	})
})

func extractAPIServerNameFromAuditFile(auditFileName string) string {
	pos := strings.Index(auditFileName, "-audit")
	if pos == -1 {
		return ""
	}
	return auditFileName[0:pos]
}
<commit_msg>Report late requests verification: Fix for Hypershift<commit_after>package apiserver

import (
	"bufio"
	"compress/gzip"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	configv1 "github.com/openshift/api/config/v1"
	"github.com/openshift/origin/pkg/test/ginkgo/result"
	clihelpers "github.com/openshift/origin/test/extended/cli"
	exutil "github.com/openshift/origin/test/extended/util"
)

var _ = g.Describe("[sig-api-machinery][Feature:APIServer][Late]", func() {
	defer g.GinkgoRecover()

	oc := exutil.NewCLIWithoutNamespace("terminating-kube-apiserver")

	// This test checks whether the apiserver reports any events that may indicate a problem at any time,
	// not just when the suite is running. We already have invariant tests that fail if these are violated
	// during suite execution, but we want to know if there are fingerprints of these failures outside of tests.
	g.It("kubelet terminates kube-apiserver gracefully", func() {
		client, err := kubernetes.NewForConfig(oc.AdminConfig())
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		evs, err := client.CoreV1().Events("openshift-kube-apiserver").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		var messages []string
		eventsAfterTime := exutil.LimitTestsToStartTime()
		for _, ev := range evs.Items {
			if ev.LastTimestamp.Time.Before(eventsAfterTime) {
				continue
			}
			if ev.Reason != "NonGracefulTermination" {
				continue
			}
			data, _ := json.Marshal(ev)
			messages = append(messages, string(data))
		}
		if len(messages) > 0 {
			result.Flakef("kube-apiserver reported a non-graceful termination (after %s which is test environment dependent). Probably kubelet or CRI-O is not giving the time to cleanly shut down. This can lead to connection refused and network I/O timeout errors in other components.\n\n%s", eventsAfterTime, strings.Join(messages, "\n"))
		}
	})

	// This test checks whether the apiserver reports any events that may indicate a problem at any time,
	// not just when the suite is running. We already have invariant tests that fail if these are violated
	// during suite execution, but we want to know if there are fingerprints of these failures outside of tests.
	g.It("kube-apiserver terminates within graceful termination period", func() {
		client, err := kubernetes.NewForConfig(oc.AdminConfig())
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		evs, err := client.CoreV1().Events("openshift-kube-apiserver").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		var messages []string
		eventsAfterTime := exutil.LimitTestsToStartTime()
		for _, ev := range evs.Items {
			if ev.LastTimestamp.Time.Before(eventsAfterTime) {
				continue
			}
			if ev.Reason != "GracefulTerminationTimeout" {
				continue
			}
			data, _ := json.Marshal(ev)
			messages = append(messages, string(data))
		}
		if len(messages) > 0 {
			result.Flakef("kube-apiserver didn't terminate by itself during the graceful termination period (after %s which is test environment dependent). This is a bug in kube-apiserver. It probably means that network connections are not closed cleanly, and this leads to network I/O timeout errors in other components.\n\n%s", eventsAfterTime, strings.Join(messages, "\n"))
		}
	})

	g.It("API LBs follow /readyz of kube-apiserver and stop sending requests", func() {
		t := g.GinkgoT()

		client, err := kubernetes.NewForConfig(oc.AdminConfig())
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		evs, err := client.CoreV1().Events("openshift-kube-apiserver").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		eventsAfterTime := exutil.LimitTestsToStartTime()
		for _, ev := range evs.Items {
			if ev.LastTimestamp.Time.Before(eventsAfterTime) {
				continue
			}
			if ev.Reason != "LateConnections" {
				continue
			}

			t.Errorf("API LBs or the kubernetes service send requests to kube-apiserver far too late in termination process, probably due to broken LB configuration: %#v. This can lead to connection refused and network I/O timeout errors in other components.", ev)
		}
	})

	g.It("API LBs follow /readyz of kube-apiserver and don't send request early", func() {
		t := g.GinkgoT()

		client, err := kubernetes.NewForConfig(oc.AdminConfig())
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		evs, err := client.CoreV1().Events("openshift-kube-apiserver").List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			g.Fail(fmt.Sprintf("Unexpected error: %v", err))
		}

		eventsAfterTime := exutil.LimitTestsToStartTime()
		for _, ev := range evs.Items {
			if ev.LastTimestamp.Time.Before(eventsAfterTime) {
				continue
			}
			if ev.Reason != "NonReadyRequests" {
				continue
			}

			t.Errorf("API LBs or the kubernetes service send requests to kube-apiserver before it is ready, probably due to broken LB configuration: %#v. This can lead to inconsistent responses like 403s in other components.", ev)
		}
	})

	g.It("API LBs follow /readyz of kube-apiserver and stop sending requests before server shutdowns for external clients", func() {
		controlPlaneTopology, err := exutil.GetControlPlaneTopology(oc)
		o.Expect(err).NotTo(o.HaveOccurred())

		// set up
		// apiserverName -> reader
		logReaders := map[string]io.Reader{}
		results := map[string]int{}

		if *controlPlaneTopology == configv1.ExternalTopologyMode {
			mgmtClusterOC := exutil.NewHypershiftManagementCLI("default").AsAdmin().WithoutNamespace()
			pods, err := mgmtClusterOC.KubeClient().CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{LabelSelector: "hypershift.openshift.io/control-plane-component=kube-apiserver"})
			o.Expect(err).To(o.BeNil())
			for _, pod := range pods.Items {
				fileName, err := mgmtClusterOC.Run("logs", "-n", pod.Namespace, pod.Name, "-c", "audit-logs").OutputToFile(pod.Name + "-audit.log")
				o.Expect(err).NotTo(o.HaveOccurred())
				reader, err := os.Open(fileName)
				o.Expect(err).NotTo(o.HaveOccurred())
				defer reader.Close()
				logReaders[pod.Namespace+"-"+pod.Name] = reader
			}
		} else {
			tempDir, err := ioutil.TempDir("", "test.oc-adm-must-gather.")
			o.Expect(err).NotTo(o.HaveOccurred())
			defer os.RemoveAll(tempDir)
			args := []string{"--dest-dir", tempDir, "--", "/usr/bin/gather_audit_logs"}

			// download the audit logs from the cluster
			o.Expect(oc.Run("adm", "must-gather").Args(args...).Execute()).To(o.Succeed())

			// act
			expectedDirectoriesToExpectedCount := []string{path.Join(clihelpers.GetPluginOutputDir(tempDir), "audit_logs", "kube-apiserver")}
			for _, auditDirectory := range expectedDirectoriesToExpectedCount {
				err := filepath.Walk(auditDirectory, func(path string, info os.FileInfo, err error) error {
					g.By(path)
					o.Expect(err).NotTo(o.HaveOccurred())

					if info.IsDir() {
						return nil
					}
					fileName := filepath.Base(path)
					if !clihelpers.IsAuditFile(fileName) {
						return nil
					}

					file, err := os.Open(path)
					o.Expect(err).NotTo(o.HaveOccurred())
					defer file.Close()
					fi, err := file.Stat()
					o.Expect(err).NotTo(o.HaveOccurred())
					if fi.Size() == 0 {
						return nil
					}

					gzipReader, err := gzip.NewReader(file)
					o.Expect(err).NotTo(o.HaveOccurred())

					apiServerName := extractAPIServerNameFromAuditFile(fileName)
					o.Expect(apiServerName).ToNot(o.BeEmpty())

					logReaders[apiServerName] = gzipReader

					return nil
				})
				o.Expect(err).NotTo(o.HaveOccurred())
			}
		}

		for apiServerName, reader := range logReaders {
			lateRequestCounter := 0
			readFile := false

			scanner := bufio.NewScanner(reader)
			for scanner.Scan() {
				text := scanner.Text()
				if !strings.HasSuffix(text, "}") {
					continue // ignore truncated data
				}
				o.Expect(text).To(o.HavePrefix(`{"kind":"Event",`))

				if strings.Contains(text, "openshift.io/during-graceful") && strings.Contains(text, "openshift-origin-external-backend-sampler") {
					lateRequestCounter++
				}
				readFile = true
			}
			o.Expect(readFile).To(o.BeTrue())

			if lateRequestCounter > 0 {
				previousLateRequestCounter, _ := results[apiServerName]
				results[apiServerName] = previousLateRequestCounter + lateRequestCounter
			}
		}

		var finalMessageBuilder strings.Builder
		for apiServerName, lateRequestCounter := range results {
			// tolerate a few late request
			if lateRequestCounter > 10 {
				finalMessageBuilder.WriteString(fmt.Sprintf("\n %v observed %v late requests", apiServerName, lateRequestCounter))
			}
		}
		// for now, we will report it as flaky, change it to fail once it proves itself
		if len(finalMessageBuilder.String()) > 0 {
			result.Flakef("the following API Servers observed late requests: %v", finalMessageBuilder.String())
		}
	})
})

func extractAPIServerNameFromAuditFile(auditFileName string) string {
	pos := strings.Index(auditFileName, "-audit")
	if pos == -1 {
		return ""
	}
	return auditFileName[0:pos]
}
<|endoftext|>


<commit_before>package mesh

import (
	"fmt"
	"sync"
	"testing"

	"github.com/stretchr/testify/require"
)

// TODO test gossip unicast; atm we only test topology gossip and
// surrogates, neither of which employ unicast.

type mockGossipConnection struct {
	RemoteConnection
	router *Router
	dest   *Router
	start  chan struct{}
}

func NewTestRouter(name string) *Router {
	peerName, _ := PeerNameFromString(name)
	router := NewRouter(Config{}, peerName, "nick", nil)
	router.Start()
	return router
}

func (conn *mockGossipConnection) SendProtocolMsg(protocolMsg ProtocolMsg) {
	<-conn.start
	if err := conn.dest.handleGossip(protocolMsg.tag, protocolMsg.msg); err != nil {
		panic(err)
	}
}

func (conn *mockGossipConnection) Connect() {
	conn.router.Ourself.handleAddConnection(conn)
	conn.router.Ourself.handleConnectionEstablished(conn)
}

func (conn *mockGossipConnection) Start() {
	close(conn.start)
}

func sendPendingGossip(routers ...*Router) {
	// Loop until all routers report they didn't send anything
	for sentSomething := true; sentSomething; {
		sentSomething = false
		for _, router := range routers {
			sentSomething = router.sendPendingGossip() || sentSomething
		}
	}
}

func AddTestGossipConnection(r1, r2 *Router) {
	c1 := r1.NewTestGossipConnection(r2)
	c2 := r2.NewTestGossipConnection(r1)
	c1.Connect()
	c2.Start()
	c2.Connect()
	c1.Start()
}

func (router *Router) NewTestGossipConnection(r *Router) *mockGossipConnection {
	to := r.Ourself.Peer
	toPeer := NewPeer(to.Name, to.NickName, to.UID, 0, to.ShortID)
	toPeer = router.Peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount

	return &mockGossipConnection{
		RemoteConnection{router.Ourself.Peer, toPeer, "", false, true}, router, r, make(chan struct{})}
}

func (router *Router) DeleteTestGossipConnection(r *Router) {
	fromName := router.Ourself.Peer.Name
	toName := r.Ourself.Peer.Name

	fromPeer := r.Peers.Fetch(fromName)
	toPeer := router.Peers.Fetch(toName)

	r.Peers.Dereference(fromPeer)
	router.Peers.Dereference(toPeer)

	conn, _ := router.Ourself.ConnectionTo(toName)
	router.Ourself.handleDeleteConnection(conn)
}

// Create a Peer representing the receiver router, with connections to
// the routers supplied as arguments, carrying across all UID and
// version information.
func (router *Router) tp(routers ...*Router) *Peer {
	peer := NewPeerFrom(router.Ourself.Peer)
	connections := make(map[PeerName]Connection)
	for _, r := range routers {
		p := NewPeerFrom(r.Ourself.Peer)
		connections[r.Ourself.Peer.Name] = newMockConnection(peer, p)
	}
	peer.Version = router.Ourself.Peer.Version
	peer.connections = connections
	return peer
}

// Check that the topology of router matches the peers and all of their connections
func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
	router.Peers.RLock()
	checkTopologyPeers(t, true, router.Peers.allPeers(), wantedPeers...)
	router.Peers.RUnlock()
}

func flushAndCheckTopology(t *testing.T, routers []*Router, wantedPeers ...*Peer) {
	sendPendingGossip(routers...)
	for _, r := range routers {
		checkTopology(t, r, wantedPeers...)
	}
}

func TestGossipTopology(t *testing.T) {
	// Create some peers that will talk to each other
	r1 := NewTestRouter("01:00:00:01:00:00")
	r2 := NewTestRouter("02:00:00:02:00:00")
	r3 := NewTestRouter("03:00:00:03:00:00")
	routers := []*Router{r1, r2, r3}
	// Check state when they have no connections
	checkTopology(t, r1, r1.tp())
	checkTopology(t, r2, r2.tp())

	// Now try adding some connections
	AddTestGossipConnection(r1, r2)
	sendPendingGossip(r1, r2)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))

	AddTestGossipConnection(r2, r3)
	flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))

	AddTestGossipConnection(r3, r1)
	flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1, r2))

	// Drop the connection from 2 to 3
	r2.DeleteTestGossipConnection(r3)
	flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))

	// Drop the connection from 1 to 3
	r1.DeleteTestGossipConnection(r3)
	sendPendingGossip(r1, r2, r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1, r2))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1), r3.tp(r1, r2))
	// r3 still thinks r1 has a connection to it
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))

	// On a timer, r3 will gossip to r1
	r3.SendAllGossip()
	sendPendingGossip(r1, r2, r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1, r2))
}

func TestGossipSurrogate(t *testing.T) {
	// create the topology r1 <-> r2 <-> r3
	r1 := NewTestRouter("01:00:00:01:00:00")
	r2 := NewTestRouter("02:00:00:02:00:00")
	r3 := NewTestRouter("03:00:00:03:00:00")
	routers := []*Router{r1, r2, r3}
	AddTestGossipConnection(r1, r2)
	AddTestGossipConnection(r3, r2)
	flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))

	// create a gossiper at either end, but not the middle
	g1 := newTestGossiper()
	g3 := newTestGossiper()
	s1 := r1.NewGossip("Test", g1)
	s3 := r3.NewGossip("Test", g3)

	// broadcast a message from each end, check it reaches the other
	broadcast(s1, 1)
	broadcast(s3, 2)
	sendPendingGossip(r1, r2, r3)
	g1.checkHas(t, 2)
	g3.checkHas(t, 1)

	// check that each end gets their message back through periodic
	// gossip
	r1.SendAllGossip()
	r3.SendAllGossip()
	sendPendingGossip(r1, r2, r3)
	g1.checkHas(t, 1, 2)
	g3.checkHas(t, 1, 2)
}

type testGossiper struct {
	sync.RWMutex
	state map[byte]struct{}
}

func newTestGossiper() *testGossiper {
	return &testGossiper{state: make(map[byte]struct{})}
}

func (g *testGossiper) OnGossipUnicast(sender PeerName, msg []byte) error {
	return nil
}

func (g *testGossiper) OnGossipBroadcast(_ PeerName, update []byte) (GossipData, error) {
	g.Lock()
	defer g.Unlock()
	for _, v := range update {
		g.state[v] = void
	}
	return NewSurrogateGossipData(update), nil
}

func (g *testGossiper) Gossip() GossipData {
	g.RLock()
	defer g.RUnlock()
	state := make([]byte, len(g.state))
	for v := range g.state {
		state = append(state, v)
	}
	return NewSurrogateGossipData(state)
}

func (g *testGossiper) OnGossip(update []byte) (GossipData, error) {
	g.Lock()
	defer g.Unlock()
	var delta []byte
	for _, v := range update {
		if _, found := g.state[v]; !found {
			delta = append(delta, v)
			g.state[v] = void
		}
	}
	if len(delta) == 0 {
		return nil, nil
	}
	return NewSurrogateGossipData(delta), nil
}

func (g *testGossiper) checkHas(t *testing.T, vs ...byte) {
	g.RLock()
	defer g.RUnlock()
	for _, v := range vs {
		if _, found := g.state[v]; !found {
			require.FailNow(t, fmt.Sprintf("%d is missing", v))
		}
	}
}

func broadcast(s Gossip, v byte) {
	s.GossipBroadcast(NewSurrogateGossipData([]byte{v}))
}
<commit_msg>refactor: inline mockGossipConnection.Connect<commit_after>package mesh

import (
	"fmt"
	"sync"
	"testing"

	"github.com/stretchr/testify/require"
)

// TODO test gossip unicast; atm we only test topology gossip and
// surrogates, neither of which employ unicast.

type mockGossipConnection struct {
	RemoteConnection
	dest  *Router
	start chan struct{}
}

func NewTestRouter(name string) *Router {
	peerName, _ := PeerNameFromString(name)
	router := NewRouter(Config{}, peerName, "nick", nil)
	router.Start()
	return router
}

func (conn *mockGossipConnection) SendProtocolMsg(protocolMsg ProtocolMsg) {
	<-conn.start
	if err := conn.dest.handleGossip(protocolMsg.tag, protocolMsg.msg); err != nil {
		panic(err)
	}
}

func (conn *mockGossipConnection) Start() {
	close(conn.start)
}

func sendPendingGossip(routers ...*Router) {
	// Loop until all routers report they didn't send anything
	for sentSomething := true; sentSomething; {
		sentSomething = false
		for _, router := range routers {
			sentSomething = router.sendPendingGossip() || sentSomething
		}
	}
}

func AddTestGossipConnection(r1, r2 *Router) {
	c1 := r1.NewTestGossipConnection(r2)
	c2 := r2.NewTestGossipConnection(r1)
	c1.Start()
	c2.Start()
}

func (router *Router) NewTestGossipConnection(r *Router) *mockGossipConnection {
	to := r.Ourself.Peer
	toPeer := NewPeer(to.Name, to.NickName, to.UID, 0, to.ShortID)
	toPeer = router.Peers.FetchWithDefault(toPeer) // Has side-effect of incrementing refcount

	conn := &mockGossipConnection{
		RemoteConnection{router.Ourself.Peer, toPeer, "", false, true}, r, make(chan struct{})}
	router.Ourself.handleAddConnection(conn)
	router.Ourself.handleConnectionEstablished(conn)
	return conn
}

func (router *Router) DeleteTestGossipConnection(r *Router) {
	fromName := router.Ourself.Peer.Name
	toName := r.Ourself.Peer.Name

	fromPeer := r.Peers.Fetch(fromName)
	toPeer := router.Peers.Fetch(toName)

	r.Peers.Dereference(fromPeer)
	router.Peers.Dereference(toPeer)

	conn, _ := router.Ourself.ConnectionTo(toName)
	router.Ourself.handleDeleteConnection(conn)
}

// Create a Peer representing the receiver router, with connections to
// the routers supplied as arguments, carrying across all UID and
// version information.
func (router *Router) tp(routers ...*Router) *Peer {
	peer := NewPeerFrom(router.Ourself.Peer)
	connections := make(map[PeerName]Connection)
	for _, r := range routers {
		p := NewPeerFrom(r.Ourself.Peer)
		connections[r.Ourself.Peer.Name] = newMockConnection(peer, p)
	}
	peer.Version = router.Ourself.Peer.Version
	peer.connections = connections
	return peer
}

// Check that the topology of router matches the peers and all of their connections
func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
	router.Peers.RLock()
	checkTopologyPeers(t, true, router.Peers.allPeers(), wantedPeers...)
	router.Peers.RUnlock()
}

func flushAndCheckTopology(t *testing.T, routers []*Router, wantedPeers ...*Peer) {
	sendPendingGossip(routers...)
	for _, r := range routers {
		checkTopology(t, r, wantedPeers...)
	}
}

func TestGossipTopology(t *testing.T) {
	// Create some peers that will talk to each other
	r1 := NewTestRouter("01:00:00:01:00:00")
	r2 := NewTestRouter("02:00:00:02:00:00")
	r3 := NewTestRouter("03:00:00:03:00:00")
	routers := []*Router{r1, r2, r3}
	// Check state when they have no connections
	checkTopology(t, r1, r1.tp())
	checkTopology(t, r2, r2.tp())

	// Now try adding some connections
	AddTestGossipConnection(r1, r2)
	sendPendingGossip(r1, r2)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))

	AddTestGossipConnection(r2, r3)
	flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))

	AddTestGossipConnection(r3, r1)
	flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1, r2))

	// Drop the connection from 2 to 3
	r2.DeleteTestGossipConnection(r3)
	flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))

	// Drop the connection from 1 to 3
	r1.DeleteTestGossipConnection(r3)
	sendPendingGossip(r1, r2, r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1, r2))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1), r3.tp(r1, r2))
	// r3 still thinks r1 has a connection to it
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))

	// On a timer, r3 will gossip to r1
	r3.SendAllGossip()
	sendPendingGossip(r1, r2, r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1), r3.tp(r1, r2))
}

func TestGossipSurrogate(t *testing.T) {
	// create the topology r1 <-> r2 <-> r3
	r1 := NewTestRouter("01:00:00:01:00:00")
	r2 := NewTestRouter("02:00:00:02:00:00")
	r3 := NewTestRouter("03:00:00:03:00:00")
	routers := []*Router{r1, r2, r3}
	AddTestGossipConnection(r1, r2)
	AddTestGossipConnection(r3, r2)
	flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))

	// create a gossiper at either end, but not the middle
	g1 := newTestGossiper()
	g3 := newTestGossiper()
	s1 := r1.NewGossip("Test", g1)
	s3 := r3.NewGossip("Test", g3)

	// broadcast a message from each end, check it reaches the other
	broadcast(s1, 1)
	broadcast(s3, 2)
	sendPendingGossip(r1, r2, r3)
	g1.checkHas(t, 2)
	g3.checkHas(t, 1)

	// check that each end gets their message back through periodic
	// gossip
	r1.SendAllGossip()
	r3.SendAllGossip()
	sendPendingGossip(r1, r2, r3)
	g1.checkHas(t, 1, 2)
	g3.checkHas(t, 1, 2)
}

type testGossiper struct {
	sync.RWMutex
	state map[byte]struct{}
}

func newTestGossiper() *testGossiper {
	return &testGossiper{state: make(map[byte]struct{})}
}

func (g *testGossiper) OnGossipUnicast(sender PeerName, msg []byte) error {
	return nil
}

func (g *testGossiper) OnGossipBroadcast(_ PeerName, update []byte) (GossipData, error) {
	g.Lock()
	defer g.Unlock()
	for _, v := range update {
		g.state[v] = void
	}
	return NewSurrogateGossipData(update), nil
}

func (g *testGossiper) Gossip() GossipData {
	g.RLock()
	defer g.RUnlock()
	state := make([]byte, len(g.state))
	for v := range g.state {
		state = append(state, v)
	}
	return NewSurrogateGossipData(state)
}

func (g *testGossiper) OnGossip(update []byte) (GossipData, error) {
	g.Lock()
	defer g.Unlock()
	var delta []byte
	for _, v := range update {
		if _, found := g.state[v]; !found {
			delta = append(delta, v)
			g.state[v] = void
		}
	}
	if len(delta) == 0 {
		return nil, nil
	}
	return NewSurrogateGossipData(delta), nil
}

func (g *testGossiper) checkHas(t *testing.T, vs ...byte) {
	g.RLock()
	defer g.RUnlock()
	for _, v := range vs {
		if _, found := g.state[v]; !found {
			require.FailNow(t, fmt.Sprintf("%d is missing", v))
		}
	}
}

func broadcast(s Gossip, v byte) {
	s.GossipBroadcast(NewSurrogateGossipData([]byte{v}))
}
<|endoftext|>


<commit_before>package slack

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"testing"
)

type fileCommentHandler struct {
	gotParams map[string]string
}

func newFileCommentHandler() *fileCommentHandler {
	return &fileCommentHandler{
		gotParams: make(map[string]string),
	}
}

func (h *fileCommentHandler) accumulateFormValue(k string, r *http.Request) {
	if v := r.FormValue(k); v != "" {
		h.gotParams[k] = v
	}
}

func (h *fileCommentHandler) handler(w http.ResponseWriter, r *http.Request) {
	h.accumulateFormValue("token", r)
	h.accumulateFormValue("file", r)
	h.accumulateFormValue("id", r)

	w.Header().Set("Content-Type", "application/json")
	if h.gotParams["id"] == "trigger-error" {
		w.Write([]byte(`{ "ok": false, "error": "errored" }`))
	} else {
		w.Write([]byte(`{ "ok": true }`))
	}
}

type mockHTTPClient struct{}

func (m *mockHTTPClient) Do(*http.Request) (*http.Response, error) {
	return &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewBufferString(`OK`))}, nil
}

func TestSlack_GetFile(t *testing.T) {
	APIURL = "http://" + serverAddr + "/"
	api := &Client{
		token:      "testing-token",
		httpclient: &mockHTTPClient{},
	}

	tests := []struct {
		title       string
		file        *File
		expectError bool
	}{
		{
			title: "Testing with valid file",
			file: &File{
				URLPrivateDownload: "https://files.slack.com/files-pri/T99999999-FGGGGGGGG/download/test.csv",
			},
			expectError: false,
		},
		{
			title: "Testing with invalid file (empty URL)",
			file: &File{
				URLPrivateDownload: "",
			},
			expectError: true,
		},
	}

	for _, test := range tests {
		err := api.GetFile(test.file, &bytes.Buffer{})

		if !test.expectError && err != nil {
			log.Fatalf("%s: Unexpected error: %s in test", test.title, err)
		} else if test.expectError == true && err == nil {
			log.Fatalf("Expected error but got none")
		}
	}
}

func TestSlack_DeleteFileComment(t *testing.T) {
	once.Do(startServer)
	APIURL = "http://" + serverAddr + "/"
	api := New("testing-token")
	tests := []struct {
		title       string
		body        url.Values
		wantParams  map[string]string
		expectError bool
	}{
		{
			title: "Testing with proper body",
			body: url.Values{
				"file": {"file12345"},
				"id":   {"id12345"},
			},
			wantParams: map[string]string{
				"token": "testing-token",
				"file":  "file12345",
				"id":    "id12345",
			},
			expectError: false,
		},
		{
			title: "Testing with false body",
			body: url.Values{
				"file": {""},
				"id":   {""},
			},
			wantParams:  map[string]string{},
			expectError: true,
		},
		{
			title: "Testing with error",
			body: url.Values{
				"file": {"file12345"},
				"id":   {"trigger-error"},
			},
			wantParams: map[string]string{
				"token": "testing-token",
				"file":  "file12345",
				"id":    "trigger-error",
			},
			expectError: true,
		},
	}

	var fch *fileCommentHandler
	http.HandleFunc("/files.comments.delete", func(w http.ResponseWriter, r *http.Request) {
		fch.handler(w, r)
	})

	for _, test := range tests {
		fch = newFileCommentHandler()
		err := api.DeleteFileComment(test.body["id"][0], test.body["file"][0])

		if !test.expectError && err != nil {
			log.Fatalf("%s: Unexpected error: %s in test", test.title, err)
		} else if test.expectError == true && err == nil {
			log.Fatalf("Expected error but got none")
		}

		if !reflect.DeepEqual(fch.gotParams, test.wantParams) {
			log.Fatalf("%s: Got params [%#v]\nBut received [%#v]\n", test.title, fch.gotParams, test.wantParams)
		}
	}
}

func authTestHandler(rw http.ResponseWriter, r *http.Request) {
	rw.Header().Set("Content-Type", "application/json")
	response, _ := json.Marshal(authTestResponseFull{
		SlackResponse: SlackResponse{Ok: true}})
	rw.Write(response)
}

func uploadFileHandler(rw http.ResponseWriter, r *http.Request) {
	rw.Header().Set("Content-Type", "application/json")
	response, _ := json.Marshal(fileResponseFull{
		SlackResponse: SlackResponse{Ok: true}})
	rw.Write(response)
}

func TestUploadFile(t *testing.T) {
	http.HandleFunc("/auth.test", authTestHandler)
	http.HandleFunc("/files.upload", uploadFileHandler)
	once.Do(startServer)
	APIURL = "http://" + serverAddr + "/"
	api := New("testing-token")
	params := FileUploadParameters{
		Filename: "test.txt", Content: "test content",
		Channels: []string{"CXXXXXXXX"}}
	if _, err := api.UploadFile(params); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}

	reader := bytes.NewBufferString("test reader")
	params = FileUploadParameters{
		Filename: "test.txt", Reader: reader,
		Channels: []string{"CXXXXXXXX"}}
	if _, err := api.UploadFile(params); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}

	largeByt := make([]byte, 107374200)
	reader = bytes.NewBuffer(largeByt)
	params = FileUploadParameters{
		Filename: "test.txt", Reader: reader,
		Channels: []string{"CXXXXXXXX"}}
	if _, err := api.UploadFile(params); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}
}

func TestUploadFileWithoutFilename(t *testing.T) {
	once.Do(startServer)
	APIURL = "http://" + serverAddr + "/"
	api := New("testing-token")

	reader := bytes.NewBufferString("test reader")
	params := FileUploadParameters{
		Reader:   reader,
		Channels: []string{"CXXXXXXXX"}}
	_, err := api.UploadFile(params)
	if err == nil {
		t.Fatal("Expected error when omitting filename, instead got nil")
	}

	if !strings.Contains(err.Error(), ".Filename is mandatory") {
		t.Errorf("Error message should mention empty FileUploadParameters.Filename")
	}
}
<commit_msg>Fix test<commit_after>package slack

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"testing"
)

type fileCommentHandler struct {
	gotParams map[string]string
}

func newFileCommentHandler() *fileCommentHandler {
	return &fileCommentHandler{
		gotParams: make(map[string]string),
	}
}

func (h *fileCommentHandler) accumulateFormValue(k string, r *http.Request) {
	if v := r.FormValue(k); v != "" {
		h.gotParams[k] = v
	}
}

func (h *fileCommentHandler) handler(w http.ResponseWriter, r *http.Request) {
	h.accumulateFormValue("token", r)
	h.accumulateFormValue("file", r)
	h.accumulateFormValue("id", r)

	w.Header().Set("Content-Type", "application/json")
	if h.gotParams["id"] == "trigger-error" {
		w.Write([]byte(`{ "ok": false, "error": "errored" }`))
	} else {
		w.Write([]byte(`{ "ok": true }`))
	}
}

type mockHTTPClient struct{}

func (m *mockHTTPClient) Do(*http.Request) (*http.Response, error) {
	return &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewBufferString(`OK`))}, nil
}

func TestSlack_GetFile(t *testing.T) {
	APIURL = "http://" + serverAddr + "/"
	api := &Client{
		token:      "testing-token",
		httpclient: &mockHTTPClient{},
	}

	tests := []struct {
		title       string
		downloadURL string
		expectError bool
	}{
		{
			title:       "Testing with valid file",
			downloadURL: "https://files.slack.com/files-pri/T99999999-FGGGGGGGG/download/test.csv",
			expectError: false,
		},
		{
			title:       "Testing with invalid file (empty URL)",
			downloadURL: "",
			expectError: true,
		},
	}

	for _, test := range tests {
		err := api.GetFile(test.downloadURL, &bytes.Buffer{})

		if !test.expectError && err != nil {
			log.Fatalf("%s: Unexpected error: %s in test", test.title, err)
		} else if test.expectError == true && err == nil {
			log.Fatalf("Expected error but got none")
		}
	}
}

func TestSlack_DeleteFileComment(t *testing.T) {
	once.Do(startServer)
	APIURL = "http://" + serverAddr + "/"
	api := New("testing-token")
	tests := []struct {
		title       string
		body        url.Values
		wantParams  map[string]string
		expectError bool
	}{
		{
			title: "Testing with proper body",
			body: url.Values{
				"file": {"file12345"},
				"id":   {"id12345"},
			},
			wantParams: map[string]string{
				"token": "testing-token",
				"file":  "file12345",
				"id":    "id12345",
			},
			expectError: false,
		},
		{
			title: "Testing with false body",
			body: url.Values{
				"file": {""},
				"id":   {""},
			},
			wantParams:  map[string]string{},
			expectError: true,
		},
		{
			title: "Testing with error",
			body: url.Values{
				"file": {"file12345"},
				"id":   {"trigger-error"},
			},
			wantParams: map[string]string{
				"token": "testing-token",
				"file":  "file12345",
				"id":    "trigger-error",
			},
			expectError: true,
		},
	}

	var fch *fileCommentHandler
	http.HandleFunc("/files.comments.delete", func(w http.ResponseWriter, r *http.Request) {
		fch.handler(w, r)
	})

	for _, test := range tests {
		fch = newFileCommentHandler()
		err := api.DeleteFileComment(test.body["id"][0], test.body["file"][0])

		if !test.expectError && err != nil {
			log.Fatalf("%s: Unexpected error: %s in test", test.title, err)
		} else if test.expectError == true && err == nil {
			log.Fatalf("Expected error but got none")
		}

		if !reflect.DeepEqual(fch.gotParams, test.wantParams) {
			log.Fatalf("%s: Got params [%#v]\nBut received [%#v]\n", test.title, fch.gotParams, test.wantParams)
		}
	}
}

func authTestHandler(rw http.ResponseWriter, r *http.Request) {
	rw.Header().Set("Content-Type", "application/json")
	response, _ := json.Marshal(authTestResponseFull{
		SlackResponse: SlackResponse{Ok: true}})
	rw.Write(response)
}

func uploadFileHandler(rw http.ResponseWriter, r *http.Request) {
	rw.Header().Set("Content-Type", "application/json")
	response, _ := json.Marshal(fileResponseFull{
		SlackResponse: SlackResponse{Ok: true}})
	rw.Write(response)
}

func TestUploadFile(t *testing.T) {
	http.HandleFunc("/auth.test", authTestHandler)
	http.HandleFunc("/files.upload", uploadFileHandler)
	once.Do(startServer)
	APIURL = "http://" + serverAddr + "/"
	api := New("testing-token")
	params := FileUploadParameters{
		Filename: "test.txt", Content: "test content",
		Channels: []string{"CXXXXXXXX"}}
	if _, err := api.UploadFile(params); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}

	reader := bytes.NewBufferString("test reader")
	params = FileUploadParameters{
		Filename: "test.txt", Reader: reader,
		Channels: []string{"CXXXXXXXX"}}
	if _, err := api.UploadFile(params); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}

	largeByt := make([]byte, 107374200)
	reader = bytes.NewBuffer(largeByt)
	params = FileUploadParameters{
		Filename: "test.txt", Reader: reader,
		Channels: []string{"CXXXXXXXX"}}
	if _, err := api.UploadFile(params); err != nil {
		t.Errorf("Unexpected error: %s", err)
	}
}

func TestUploadFileWithoutFilename(t *testing.T) {
	once.Do(startServer)
	APIURL = "http://" + serverAddr + "/"
	api := New("testing-token")

	reader := bytes.NewBufferString("test reader")
	params := FileUploadParameters{
		Reader:   reader,
		Channels: []string{"CXXXXXXXX"}}
	_, err := api.UploadFile(params)
	if err == nil {
		t.Fatal("Expected error when omitting filename, instead got nil")
	}

	if !strings.Contains(err.Error(), ".Filename is mandatory") {
		t.Errorf("Error message should mention empty FileUploadParameters.Filename")
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package messagebus

import (
	"fmt"
	"reflect"
	"strconv"
	"sync"
)

// internal constants
const (
	defaultQueueSize = 1000 // if specific size is not specified
)

// Message - message to put into a queue
type Message struct {
	Command    string   // type of packed data
	Parameters [][]byte // array of parameters
}

// Queue - a 1:1 queue
type Queue struct {
	c    chan Message
	size int
	used bool
}

// BroadcastQueue - a 1:M queue
// out is synchronous, so messages to routines not waiting are dropped
type BroadcastQueue struct {
	in               chan Message
	out              []chan Message
	defaultSize      int
	deliveredMessage map[string]Message // cache for the delivered message
	sync.RWMutex
}

// the exported message queues and their sizes
// any item with a size option will be allocated that size
// absent then default size is used
type busses struct {
	Broadcast  *BroadcastQueue `size:"1000"` // to broadcast to other nodes
	Connector  *Queue          `size:"50"`   // to control connector
	Announce   *Queue          `size:"50"`   // to control the announcer
	Blockstore *Queue          `size:"50"`   // to sequentially store blocks
	TestQueue  *Queue          `size:"50"`   // for testing use
}

// Bus - all available message queues
var Bus busses

// initialise all queues with preset size
func init() {

	// this will be a struct type
	busType := reflect.TypeOf(Bus)

	// get write access by using pointer + Elem()
	busValue := reflect.ValueOf(&Bus).Elem()

	// scan each field
	for i := 0; i < busType.NumField(); i += 1 {

		fieldInfo := busType.Field(i)

		sizeTag := fieldInfo.Tag.Get("size")

		queueSize := defaultQueueSize

		// if size specified and valid positive integer override default
		if len(sizeTag) > 0 {
			s, err := strconv.Atoi(sizeTag)
			if nil == err && s > 0 {
				queueSize = s
			} else {
				m := fmt.Sprintf("queue: %v  has invalid size: %q", fieldInfo, sizeTag)
				panic(m)
			}
		}

		switch qt := busValue.Field(i).Type(); qt {

		case reflect.TypeOf((*BroadcastQueue)(nil)):
			q := &BroadcastQueue{
				in:               make(chan Message, queueSize),
				out:              make([]chan Message, 0, 10),
				defaultSize:      queueSize,
				deliveredMessage: make(map[string]Message),
			}
			go q.multicast()

			newQueue := reflect.ValueOf(q)
			busValue.Field(i).Set(newQueue)

		case reflect.TypeOf((*Queue)(nil)):
			q := &Queue{
				c:    make(chan Message, queueSize),
				size: queueSize,
				used: false,
			}
			newQueue := reflect.ValueOf(q)
			busValue.Field(i).Set(newQueue)
		default:
			panic(fmt.Sprintf("queue type: %q is not handled", qt))
		}
	}
}

// Send - send a message to a 1:1 queue
// but only if listener is connected
func (queue *Queue) Send(command string, parameters ...[]byte) {
	queue.c <- Message{
		Command:    command,
		Parameters: parameters,
	}
}

// Chan - channel to read from 1:1 queue
// can only be called once
func (queue *Queue) Chan() <-chan Message {
	if queue.used {
		panic("cannot get a second receive channel from a 1:1 queue")
	}
	queue.used = true
	return queue.c
}

// Release - give the channel back
func (queue *Queue) Release() {
	queue.used = false
	close(queue.c)
	queue.c = make(chan Message, queue.size)
}

// Send - send a message to a 1:M queue
func (queue *BroadcastQueue) Send(command string, parameters ...[]byte) {
	m := Message{
		Command:    command,
		Parameters: parameters,
	}

	if queue.isCached(m) {
		// Drop the message that already cached
		return
	}

	queue.in <- m

	// Ignore caching rpc/peer command
	if "rpc" == command || "peer" == command {
		return
	}
	queue.cache(m)
}

// Chan - get a new channel to read from a 1:M queue
// each call gets a distinct channel
func (queue *BroadcastQueue) Chan(size int) <-chan Message {
	if size < 0 {
		size = queue.defaultSize
	}
	c := make(chan Message, size)
	queue.out = append(queue.out, c)
	return c
}

// Release - release the incomming and outgoing queues
func (queue *BroadcastQueue) Release() {
	// close all channel
	close(queue.in)
	for _, o := range queue.out {
		close(o)
	}

	// give them back
	queue.in = make(chan Message, queue.defaultSize)
	queue.out = make([]chan Message, 0, 10)
}

// DropCache - drop the item from cache
func (queue *BroadcastQueue) DropCache(msgs ...Message) {

	if nil == msgs {
		queue.clearCache()
		return
	}

	for _, m := range msgs {

		if !queue.isCached(m) {
			continue
		}

		queue.Lock()
		delete(queue.deliveredMessage, m.packHex())
		queue.Unlock()
	}
}

// clear the cache
func (queue *BroadcastQueue) clearCache() {
	queue.Lock()
	defer queue.Unlock()
	queue.deliveredMessage = map[string]Message{}
}

// cache the delivered message
func (queue *BroadcastQueue) cache(m Message) {
	queue.Lock()
	defer queue.Unlock()
	queue.deliveredMessage[m.packHex()] = m
}

// check whether Message is cached
func (queue *BroadcastQueue) isCached(m Message) bool {
	queue.RLock()
	defer queue.RUnlock()
	_, ok := queue.deliveredMessage[m.packHex()]
	return ok
}

// background processing for the 1:M queue
//
// if an outgoing queue is full, determine whether it is block message
// drop lower priority message for taking slot for new one
func (queue *BroadcastQueue) multicast() {
loop:
	for {
		data, ok := <-queue.in
		if !ok {
			// invalid data
			continue loop
		}
		for _, out := range queue.out {
			select {
			case out <- data:
			default:
				// the outgoing queue is full
				if "block" == data.Command {
					// drop lower priority item for taking more slot
					ci := make([]Message, 0)

					found := false
					for len(out) > 0 {
						m := <-out
						if !found && "block" != m.Command {
							found = true
							continue
						} else {
							ci = append(ci, m)
						}
					}

					if !found {
						// couldn't find any existing message type block in queue
						// just drop cache
						queue.DropCache(data)
					} else {
						// reput item to the queue
						for _, m := range ci {
							out <- m
						}
						out <- data
					}

				} else {
					// drop existing cache item
					queue.DropCache(data)
				}
			}
		}
	}
}

// pack Message
func (m Message) pack() []byte {
	s := make([]byte, 0)
	s = append(s, []byte(m.Command)...)

	for _, a := range m.Parameters {
		s = append(s, a...)
	}
	return s
}

// pack Message in hex string
func (m Message) packHex() string {
	return fmt.Sprintf("%x", m.pack())
}
<commit_msg>[messagebus] update some comments<commit_after>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package messagebus

import (
	"fmt"
	"reflect"
	"strconv"
	"sync"
)

// internal constants
const (
	defaultQueueSize = 1000 // if specific size is not specified
)

// Message - message to put into a queue
type Message struct {
	Command    string   // type of packed data
	Parameters [][]byte // array of parameters
}

// Queue - a 1:1 queue
type Queue struct {
	c    chan Message
	size int
	used bool
}

// BroadcastQueue - a 1:M queue
// out is synchronous, so messages to routines not waiting are dropped
type BroadcastQueue struct {
	in               chan Message
	out              []chan Message
	defaultSize      int
	deliveredMessage map[string]Message // cache for the delivered message
	sync.RWMutex
}

// the exported message queues and their sizes
// any item with a size option will be allocated that size
// absent then default size is used
type busses struct {
	Broadcast  *BroadcastQueue `size:"1000"` // to broadcast to other nodes
	Connector  *Queue          `size:"50"`   // to control connector
	Announce   *Queue          `size:"50"`   // to control the announcer
	Blockstore *Queue          `size:"50"`   // to sequentially store blocks
	TestQueue  *Queue          `size:"50"`   // for testing use
}

// Bus - all available message queues
var Bus busses

// initialise all queues with preset size
func init() {

	// this will be a struct type
	busType := reflect.TypeOf(Bus)

	// get write access by using pointer + Elem()
	busValue := reflect.ValueOf(&Bus).Elem()

	// scan each field
	for i := 0; i < busType.NumField(); i += 1 {

		fieldInfo := busType.Field(i)

		sizeTag := fieldInfo.Tag.Get("size")

		queueSize := defaultQueueSize

		// if size specified and valid positive integer override default
		if len(sizeTag) > 0 {
			s, err := strconv.Atoi(sizeTag)
			if nil == err && s > 0 {
				queueSize = s
			} else {
				m := fmt.Sprintf("queue: %v  has invalid size: %q", fieldInfo, sizeTag)
				panic(m)
			}
		}

		switch qt := busValue.Field(i).Type(); qt {

		case reflect.TypeOf((*BroadcastQueue)(nil)):
			q := &BroadcastQueue{
				in:               make(chan Message, queueSize),
				out:              make([]chan Message, 0, 10),
				defaultSize:      queueSize,
				deliveredMessage: make(map[string]Message),
			}
			go q.multicast()

			newQueue := reflect.ValueOf(q)
			busValue.Field(i).Set(newQueue)

		case reflect.TypeOf((*Queue)(nil)):
			q := &Queue{
				c:    make(chan Message, queueSize),
				size: queueSize,
				used: false,
			}
			newQueue := reflect.ValueOf(q)
			busValue.Field(i).Set(newQueue)
		default:
			panic(fmt.Sprintf("queue type: %q is not handled", qt))
		}
	}
}

// Send - send a message to a 1:1 queue
// but only if listener is connected
func (queue *Queue) Send(command string, parameters ...[]byte) {
	queue.c <- Message{
		Command:    command,
		Parameters: parameters,
	}
}

// Chan - channel to read from 1:1 queue
// can only be called once
func (queue *Queue) Chan() <-chan Message {
	if queue.used {
		panic("cannot get a second receive channel from a 1:1 queue")
	}
	queue.used = true
	return queue.c
}

// Release - give the channel back
func (queue *Queue) Release() {
	queue.used = false
	close(queue.c)
	queue.c = make(chan Message, queue.size)
}

// Send - send a message to a 1:M queue
func (queue *BroadcastQueue) Send(command string, parameters ...[]byte) {
	m := Message{
		Command:    command,
		Parameters: parameters,
	}

	if queue.isCached(m) {
		// Drop the message that already cached
		return
	}

	queue.in <- m

	// Ignore caching rpc/peer command
	if "rpc" == command || "peer" == command {
		return
	}
	queue.cache(m)
}

// Chan - get a new channel to read from a 1:M queue
// each call gets a distinct channel
func (queue *BroadcastQueue) Chan(size int) <-chan Message {
	if size < 0 {
		size = queue.defaultSize
	}
	c := make(chan Message, size)
	queue.out = append(queue.out, c)
	return c
}

// Release - release the incomming and outgoing queue
func (queue *BroadcastQueue) Release() {
	// close all channel
	close(queue.in)
	for _, o := range queue.out {
		close(o)
	}

	// give them back
	queue.in = make(chan Message, queue.defaultSize)
	queue.out = make([]chan Message, 0, 10)
}

// DropCache - drop the items from cache
func (queue *BroadcastQueue) DropCache(msgs ...Message) {

	if nil == msgs {
		queue.clearCache()
		return
	}

	for _, m := range msgs {

		if !queue.isCached(m) {
			continue
		}

		queue.Lock()
		delete(queue.deliveredMessage, m.packHex())
		queue.Unlock()
	}
}

func (queue *BroadcastQueue) clearCache() {
	queue.Lock()
	defer queue.Unlock()
	queue.deliveredMessage = map[string]Message{}
}

func (queue *BroadcastQueue) cache(m Message) {
	queue.Lock()
	defer queue.Unlock()
	queue.deliveredMessage[m.packHex()] = m
}

func (queue *BroadcastQueue) isCached(m Message) bool {
	queue.RLock()
	defer queue.RUnlock()
	_, ok := queue.deliveredMessage[m.packHex()]
	return ok
}

// background processing for the 1:M queue
//
// if an outgoing queue is full, determine whether it is block message
// drop lower priority message for taking slot for new one
func (queue *BroadcastQueue) multicast() {
loop:
	for {
		data, ok := <-queue.in
		if !ok {
			// invalid data
			continue loop
		}
		for _, out := range queue.out {
			select {
			case out <- data:
			default:
				// the outgoing queue is full
				if "block" == data.Command {
					// drop lower priority item for taking more slot
					ci := make([]Message, 0)

					found := false
					for len(out) > 0 {
						m := <-out
						if !found && "block" != m.Command {
							found = true
							continue
						} else {
							ci = append(ci, m)
						}
					}

					if !found {
						// couldn't find any existing message type block in queue
						// just drop cache
						queue.DropCache(data)
					} else {
						// reput item to the queue
						for _, m := range ci {
							out <- m
						}
						out <- data
					}

				} else {
					// drop existing cache item
					queue.DropCache(data)
				}
			}
		}
	}
}

// pack Message
func (m Message) pack() []byte {
	s := make([]byte, 0)
	s = append(s, []byte(m.Command)...)

	for _, a := range m.Parameters {
		s = append(s, a...)
	}
	return s
}

// pack Message in hex string
func (m Message) packHex() string {
	return fmt.Sprintf("%x", m.pack())
}
<|endoftext|>