<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

// The params package holds types that are a part of the charm store's external
// contract - they will be marshalled (or unmarshalled) as JSON
// and delivered through the HTTP API.
package params

import (
	"time"

	"gopkg.in/juju/charm.v4"
)

// ContentHashHeader specifies the header attribute
// that will hold the content hash for archive GET responses.
const ContentHashHeader = "x-content-sha384"

// MetaAnyResponse holds the result of a meta/any
// request. See http://tinyurl.com/q5vcjpk
type MetaAnyResponse struct {
	Id   *charm.Reference
	Meta map[string]interface{} `json:",omitempty"`
}

// ArchiveUploadResponse holds the result of
// a post or a put to /$id/archive. See http://tinyurl.com/lzrzrgb
type ArchiveUploadResponse struct {
	Id *charm.Reference
}

// ExpandedId holds a charm or bundle fully qualified id.
// A slice of ExpandedId is used as response for
// id/expand-id GET requests.
type ExpandedId struct {
	Id string
}

// ArchiveSizeResponse holds the result of an
// id/meta/archive-size GET request. See http://tinyurl.com/m8b9geq
type ArchiveSizeResponse struct {
	Size int64
}

// ManifestFile holds information about a charm or bundle file.
// A slice of ManifestFile is used as response for
// id/meta/manifest GET requests. See http://tinyurl.com/p3xdcto
type ManifestFile struct {
	Name string
	Size int64
}

// ArchiveUploadTimeResponse holds the result of an
// id/meta/archive-upload-time GET request. See http://tinyurl.com/nmujuqk
type ArchiveUploadTimeResponse struct {
	UploadTime time.Time
}

// RelatedResponse holds the result of an
// id/meta/charm-related GET request. See http://tinyurl.com/q7vdmzl
type RelatedResponse struct {
	// Requires holds an entry for each interface provided by
	// the charm, containing all charms that require that interface.
	Requires map[string][]MetaAnyResponse `json:",omitempty"`

	// Provides holds an entry for each interface required by the
	// the charm, containing all charms that provide that interface.
	Provides map[string][]MetaAnyResponse `json:",omitempty"`
}

// RevisionInfoResponse holds the result of an
// id/meta/revision-info GET request. See http://tinyurl.com/q6xos7f
type RevisionInfoResponse struct {
	Revisions []*charm.Reference
}

// BundleCount holds the result of an id/meta/bundle-unit-count
// or bundle-machine-count GET request. See http://tinyurl.com/mkvowub
// and http://tinyurl.com/qfuubrv
type BundleCount struct {
	Count int
}

type Published struct {
	Id          *charm.Reference
	PublishTime time.Time
}

// DebugStatus holds the result of the status checks
type DebugStatus struct {
	Name   string
	Value  string
	Passed bool
}

// SearchResult holds a single result from a search operation
type SearchResult struct {
	Id string
	// Meta holds at most one entry for each meta value
	// specified in the include flags, holding the
	// data that would be returned by reading /meta/meta?id=id.
	// Metadata not relevant to a particular result will not
	// be included.
	Meta map[string]interface{} `json:",omitempty"`
}
<commit_msg>params: rename content hash header<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

// The params package holds types that are a part of the charm store's external
// contract - they will be marshalled (or unmarshalled) as JSON
// and delivered through the HTTP API.
package params

import (
	"time"

	"gopkg.in/juju/charm.v4"
)

// ContentHashHeader specifies the header attribute
// that will hold the content hash for archive GET responses.
const ContentHashHeader = "Content-SHA384"

// MetaAnyResponse holds the result of a meta/any
// request. See http://tinyurl.com/q5vcjpk
type MetaAnyResponse struct {
	Id   *charm.Reference
	Meta map[string]interface{} `json:",omitempty"`
}

// ArchiveUploadResponse holds the result of
// a post or a put to /$id/archive. See http://tinyurl.com/lzrzrgb
type ArchiveUploadResponse struct {
	Id *charm.Reference
}

// ExpandedId holds a charm or bundle fully qualified id.
// A slice of ExpandedId is used as response for
// id/expand-id GET requests.
type ExpandedId struct {
	Id string
}

// ArchiveSizeResponse holds the result of an
// id/meta/archive-size GET request. See http://tinyurl.com/m8b9geq
type ArchiveSizeResponse struct {
	Size int64
}

// ManifestFile holds information about a charm or bundle file.
// A slice of ManifestFile is used as response for
// id/meta/manifest GET requests. See http://tinyurl.com/p3xdcto
type ManifestFile struct {
	Name string
	Size int64
}

// ArchiveUploadTimeResponse holds the result of an
// id/meta/archive-upload-time GET request. See http://tinyurl.com/nmujuqk
type ArchiveUploadTimeResponse struct {
	UploadTime time.Time
}

// RelatedResponse holds the result of an
// id/meta/charm-related GET request. See http://tinyurl.com/q7vdmzl
type RelatedResponse struct {
	// Requires holds an entry for each interface provided by
	// the charm, containing all charms that require that interface.
	Requires map[string][]MetaAnyResponse `json:",omitempty"`

	// Provides holds an entry for each interface required by the
	// the charm, containing all charms that provide that interface.
	Provides map[string][]MetaAnyResponse `json:",omitempty"`
}

// RevisionInfoResponse holds the result of an
// id/meta/revision-info GET request. See http://tinyurl.com/q6xos7f
type RevisionInfoResponse struct {
	Revisions []*charm.Reference
}

// BundleCount holds the result of an id/meta/bundle-unit-count
// or bundle-machine-count GET request. See http://tinyurl.com/mkvowub
// and http://tinyurl.com/qfuubrv
type BundleCount struct {
	Count int
}

type Published struct {
	Id          *charm.Reference
	PublishTime time.Time
}

// DebugStatus holds the result of the status checks
type DebugStatus struct {
	Name   string
	Value  string
	Passed bool
}

// SearchResult holds a single result from a search operation
type SearchResult struct {
	Id string
	// Meta holds at most one entry for each meta value
	// specified in the include flags, holding the
	// data that would be returned by reading /meta/meta?id=id.
	// Metadata not relevant to a particular result will not
	// be included.
	Meta map[string]interface{} `json:",omitempty"`
}
<|endoftext|>


<commit_before>package pgn

import (
	. "launchpad.net/gocheck"
)

type BoardSuite struct{}

var _ = Suite(&BoardSuite{})

func (s *BoardSuite) TestBoardString(c *C) {
	b := NewBoard()
	c.Assert(b.String(), Equals, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
}

func (s *BoardSuite) TestBoardNewFEN(c *C) {
	b, _ := NewBoardFEN("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2")
	c.Assert(b.String(), Equals, "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2")
}

func (s *BoardSuite) TestBoardColorWhitePawn(c *C) {
	c.Assert(WhitePawn.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteKnight(c *C) {
	c.Assert(WhiteKnight.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteBishop(c *C) {
	c.Assert(WhiteBishop.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteRook(c *C) {
	c.Assert(WhiteRook.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteQueen(c *C) {
	c.Assert(WhiteQueen.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteKing(c *C) {
	c.Assert(WhiteKing.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorBlackPawn(c *C) {
	c.Assert(BlackPawn.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackKnight(c *C) {
	c.Assert(BlackKnight.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackBishop(c *C) {
	c.Assert(BlackBishop.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackRook(c *C) {
	c.Assert(BlackRook.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackQueen(c *C) {
	c.Assert(BlackQueen.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackKing(c *C) {
	c.Assert(BlackKing.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhitePawn(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("d4", White)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, D2)
	c.Assert(move.To, Equals, D4)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackPawn(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("d4", White)
	c.Assert(err, IsNil)
	b.MakeMove(move)
	move, err = b.MoveFromAlgebraic("d5", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, D7)
	c.Assert(move.To, Equals, D5)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteKingsideCastle(c *C) {
	b, err := NewBoardFEN("rnbq1rk1/pppp1ppp/5n2/2b1p3/4P3/3P1N2/PPP1BPPP/RNBQK2R w KQ - 3 5")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O", White)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, E1)
	c.Assert(move.To, Equals, G1)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteQueensideCastle(c *C) {
	b, err := NewBoardFEN("r3kbnr/ppp2ppp/2nqb3/3pp3/3PP3/2NQB3/PPP2PPP/R3KBNR w KQkq - 4 6")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O-O", White)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, E1)
	c.Assert(move.To, Equals, B1)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteKingsideCastleBad(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("O-O", White)
	c.Assert(err, Equals, ErrMoveThroughPiece)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteQueensideCastleBad(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("O-O-O", White)
	c.Assert(err, Equals, ErrMoveThroughPiece)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteQueensideCastleQueenCheck(c *C) {
	c.Skip("not ready")
	b, err := NewBoardFEN("r3kbnr/ppp2ppp/2n4B/3pp1q1/3PP1Q1/2N4b/PPP2PPP/R3KBNR w KQkq - 4 6")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O-O", White)
	c.Assert(err, Equals, ErrMoveThroughCheck)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackKingsideCastle(c *C) {
	b, err := NewBoardFEN("rnbqk2r/pppp1ppp/5n2/2b1p3/4P3/3P1N2/PPP1BPPP/RNBQK2R b KQkq - 2 4")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, E8)
	c.Assert(move.To, Equals, G8)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackQueensideCastle(c *C) {
	b, err := NewBoardFEN("r3kbnr/ppp2ppp/2nqb3/3pp3/3PP3/2NQB3/PPP2PPP/2KR1BNR b kq - 5 6")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O-O", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, E8)
	c.Assert(move.To, Equals, B8)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackKingsideCastleBad(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("e4", White)
	c.Assert(err, IsNil)
	b.MakeMove(move)
	move, err = b.MoveFromAlgebraic("O-O", Black)
	c.Assert(err, Equals, ErrMoveThroughPiece)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackQueensideCastleBad(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("e4", White)
	c.Assert(err, IsNil)
	b.MakeMove(move)
	move, err = b.MoveFromAlgebraic("O-O-O", Black)
	c.Assert(err, Equals, ErrMoveThroughPiece)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackQueensideCastleQueenCheck(c *C) {
	c.Skip("not ready")
	b, err := NewBoardFEN("r3kbnr/ppp2ppp/2n4B/3pp1q1/3PP1Q1/2N4b/PPP2PPP/R2K1BNR b kq - 5 6")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O-O", Black)
	c.Assert(err, Equals, ErrMoveThroughCheck)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteKnight(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("Nf3", White)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, G1)
	c.Assert(move.To, Equals, F3)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackKnight(c *C) {
	b, err := NewBoardFEN("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("Nf6", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, G8)
	c.Assert(move.To, Equals, F6)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackBishop(c *C) {
	b, err := NewBoardFEN("rnbqkbnr/ppp1pppp/8/3p4/3PP3/8/PPP2PPP/RNBQKBNR b KQkq - 0 2")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("Bg4", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, C8)
	c.Assert(move.To, Equals, G4)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackBishopBad(c *C) {
	b, err := NewBoardFEN("rnbqkbnr/ppp1pppp/8/3p4/3PP3/8/PPP2PPP/RNBQKBNR b KQkq - 0 2")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("Bg5", Black)
	c.Assert(err, Equals, ErrAttackerNotFound)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackBishopAmbiguous(c *C) {
	b, err := NewBoardFEN("r5nr/p2k2pp/5p2/3b4/P7/b1B5/5PPP/2b2K1R b - - 6 26")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("Bb2", Black)
	c.Assert(err, Equals, ErrAmbiguousMove)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardContainsPieceAtA1(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A1), Equals, true)
}

func (s *BoardSuite) TestBoardContainsPieceAtA2(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A2), Equals, true)
}

func (s *BoardSuite) TestBoardContainsPieceAtA3(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A3), Equals, false)
}

func (s *BoardSuite) TestBoardContainsPieceAtA4(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A4), Equals, false)
}

func (s *BoardSuite) TestBoardContainsPieceAtA5(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A5), Equals, false)
}

func (s *BoardSuite) TestBoardContainsPieceAtA6(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A6), Equals, false)
}

func (s *BoardSuite) TestBoardContainsPieceAtA7(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A7), Equals, true)
}

func (s *BoardSuite) TestBoardContainsPieceAtA8(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A8), Equals, true)
}
<commit_msg>adding a few more board tests<commit_after>package pgn

import (
	. "launchpad.net/gocheck"
)

type BoardSuite struct{}

var _ = Suite(&BoardSuite{})

func (s *BoardSuite) TestBoardString(c *C) {
	b := NewBoard()
	c.Assert(b.String(), Equals, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
}

func (s *BoardSuite) TestBoardNewFEN(c *C) {
	b, _ := NewBoardFEN("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2")
	c.Assert(b.String(), Equals, "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2")
}

func (s *BoardSuite) TestBoardColorWhitePawn(c *C) {
	c.Assert(WhitePawn.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteKnight(c *C) {
	c.Assert(WhiteKnight.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteBishop(c *C) {
	c.Assert(WhiteBishop.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteRook(c *C) {
	c.Assert(WhiteRook.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteQueen(c *C) {
	c.Assert(WhiteQueen.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorWhiteKing(c *C) {
	c.Assert(WhiteKing.Color(), Equals, White)
}

func (s *BoardSuite) TestBoardColorBlackPawn(c *C) {
	c.Assert(BlackPawn.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackKnight(c *C) {
	c.Assert(BlackKnight.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackBishop(c *C) {
	c.Assert(BlackBishop.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackRook(c *C) {
	c.Assert(BlackRook.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackQueen(c *C) {
	c.Assert(BlackQueen.Color(), Equals, Black)
}

func (s *BoardSuite) TestBoardColorBlackKing(c *C) {
	c.Assert(BlackKing.Color(), Equals, Black)
}

func (s *BoardSuite) TestNoColor(c *C) {
	c.Assert(Empty.Color(), Equals, NoColor)
}

func (s *BoardSuite) TestNoColorString(c *C) {
	c.Assert(Empty.Color().String(), Equals, " ")
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhitePawn(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("d4", White)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, D2)
	c.Assert(move.To, Equals, D4)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackPawn(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("d4", White)
	c.Assert(err, IsNil)
	b.MakeMove(move)
	move, err = b.MoveFromAlgebraic("d5", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, D7)
	c.Assert(move.To, Equals, D5)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteKingsideCastle(c *C) {
	b, err := NewBoardFEN("rnbq1rk1/pppp1ppp/5n2/2b1p3/4P3/3P1N2/PPP1BPPP/RNBQK2R w KQ - 3 5")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O", White)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, E1)
	c.Assert(move.To, Equals, G1)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteQueensideCastle(c *C) {
	b, err := NewBoardFEN("r3kbnr/ppp2ppp/2nqb3/3pp3/3PP3/2NQB3/PPP2PPP/R3KBNR w KQkq - 4 6")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O-O", White)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, E1)
	c.Assert(move.To, Equals, B1)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteKingsideCastleBad(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("O-O", White)
	c.Assert(err, Equals, ErrMoveThroughPiece)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteQueensideCastleBad(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("O-O-O", White)
	c.Assert(err, Equals, ErrMoveThroughPiece)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteQueensideCastleQueenCheck(c *C) {
	c.Skip("not ready")
	b, err := NewBoardFEN("r3kbnr/ppp2ppp/2n4B/3pp1q1/3PP1Q1/2N4b/PPP2PPP/R3KBNR w KQkq - 4 6")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O-O", White)
	c.Assert(err, Equals, ErrMoveThroughCheck)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackKingsideCastle(c *C) {
	b, err := NewBoardFEN("rnbqk2r/pppp1ppp/5n2/2b1p3/4P3/3P1N2/PPP1BPPP/RNBQK2R b KQkq - 2 4")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, E8)
	c.Assert(move.To, Equals, G8)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackQueensideCastle(c *C) {
	b, err := NewBoardFEN("r3kbnr/ppp2ppp/2nqb3/3pp3/3PP3/2NQB3/PPP2PPP/2KR1BNR b kq - 5 6")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O-O", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, E8)
	c.Assert(move.To, Equals, B8)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackKingsideCastleBad(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("e4", White)
	c.Assert(err, IsNil)
	b.MakeMove(move)
	move, err = b.MoveFromAlgebraic("O-O", Black)
	c.Assert(err, Equals, ErrMoveThroughPiece)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackQueensideCastleBad(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("e4", White)
	c.Assert(err, IsNil)
	b.MakeMove(move)
	move, err = b.MoveFromAlgebraic("O-O-O", Black)
	c.Assert(err, Equals, ErrMoveThroughPiece)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackQueensideCastleQueenCheck(c *C) {
	c.Skip("not ready")
	b, err := NewBoardFEN("r3kbnr/ppp2ppp/2n4B/3pp1q1/3PP1Q1/2N4b/PPP2PPP/R2K1BNR b kq - 5 6")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("O-O-O", Black)
	c.Assert(err, Equals, ErrMoveThroughCheck)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicWhiteKnight(c *C) {
	b := NewBoard()
	move, err := b.MoveFromAlgebraic("Nf3", White)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, G1)
	c.Assert(move.To, Equals, F3)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackKnight(c *C) {
	b, err := NewBoardFEN("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("Nf6", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, G8)
	c.Assert(move.To, Equals, F6)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackBishop(c *C) {
	b, err := NewBoardFEN("rnbqkbnr/ppp1pppp/8/3p4/3PP3/8/PPP2PPP/RNBQKBNR b KQkq - 0 2")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("Bg4", Black)
	c.Assert(err, IsNil)
	c.Assert(move.From, Equals, C8)
	c.Assert(move.To, Equals, G4)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackBishopBad(c *C) {
	b, err := NewBoardFEN("rnbqkbnr/ppp1pppp/8/3p4/3PP3/8/PPP2PPP/RNBQKBNR b KQkq - 0 2")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("Bg5", Black)
	c.Assert(err, Equals, ErrAttackerNotFound)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardMoveFromAlgebraicBlackBishopAmbiguous(c *C) {
	b, err := NewBoardFEN("r5nr/p2k2pp/5p2/3b4/P7/b1B5/5PPP/2b2K1R b - - 6 26")
	c.Assert(err, IsNil)
	move, err := b.MoveFromAlgebraic("Bb2", Black)
	c.Assert(err, Equals, ErrAmbiguousMove)
	c.Assert(move, Equals, NilMove)
}

func (s *BoardSuite) TestBoardContainsPieceAtA1(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A1), Equals, true)
}

func (s *BoardSuite) TestBoardContainsPieceAtA2(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A2), Equals, true)
}

func (s *BoardSuite) TestBoardContainsPieceAtA3(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A3), Equals, false)
}

func (s *BoardSuite) TestBoardContainsPieceAtA4(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A4), Equals, false)
}

func (s *BoardSuite) TestBoardContainsPieceAtA5(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A5), Equals, false)
}

func (s *BoardSuite) TestBoardContainsPieceAtA6(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A6), Equals, false)
}

func (s *BoardSuite) TestBoardContainsPieceAtA7(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A7), Equals, true)
}

func (s *BoardSuite) TestBoardContainsPieceAtA8(c *C) {
	b := NewBoard()
	c.Assert(b.containsPieceAt(A8), Equals, true)
}
<|endoftext|>


<commit_before>package fastq

import (
	"biofile/fastq"
	"bytes"
	"fmt"
	"gongs/scan"
	"gongs/xopen"
	"io"
	"strings"
)

type Fastq struct {
	Name string
	Seq  []byte
	Qual []byte
}

func (fq Fastq) String() string {
	return fmt.Sprintf("@%v\n%v\n+\n%v", fq.Name, string(fq.Seq), string(fq.Qual))
}

func (fq Fastq) Id() string {
	if n := strings.IndexByte(fq.Name, ' '); n >= 0 {
		return fq.Name[:n]
	}

	// for old solexa data format
	if n := strings.IndexByte(fq.Name, '#'); n >= 0 {
		return fq.Name[:n]
	}

	return fq.Name
}

type FastqFile struct {
	Name  string
	file  io.ReadCloser
	s     *scan.Scanner
	name  []byte
	seq   []byte
	qual  []byte
	err   error
	stage int
}

func Open(filename string) (*FastqFile, error) {
	file, err := xopen.Xopen(filename)
	if err != nil {
		return nil, err
	}

	return &FastqFile{
		Name: filename,
		s:    scan.New(file),
		file: file,
	}, nil
}

func (ff *FastqFile) Close() error {
	return ff.file.Close()
}

func (ff *FastqFile) Err() error {
	if ff.err == nil || ff.err == io.EOF {
		if err := ff.s.Err(); err != nil {
			return err
		}
		return nil
	}
	return ff.err
}

func (ff *FastqFile) setErr(err error) {
	if ff.err == nil {
		ff.err = err
	}
}

func (ff *FastqFile) Next() bool {
	if ff.err != nil {
		return false
	}

	var line []byte
	for ff.s.Scan() {
		line = bytes.TrimSpace(ff.s.Bytes())
		if len(line) == 0 { // ingore empty line
			continue
		}
		switch ff.stage {
		case 0: // get fastq name
			if len(line) > 0 && line[0] != '@' {
				ff.setErr(fmt.Errorf("file: %v Wrong Fastq Record Name %s at line: %d", ff.Name, string(line), ff.s.Lid()))
				return false
			}
			ff.stage++
			ff.name = line[1:]
			ff.seq = ff.seq[:0]   // clear seq
			ff.qual = ff.qual[:0] // clear qual
		case 1: // get fastq seq
			if len(line) > 0 && line[0] == '+' {
				ff.stage += 2
				break
			}
			ff.seq = append(ff.seq, line...)
		case 2: // get + line
		case 3: // get fastq qual
			ff.qual = append(ff.qual, line...)
			if len(ff.qual) == len(ff.seq) {
				ff.stage = 0
				return true
			} else if len(ff.qual) > len(ff.seq) {
				ff.setErr(fmt.Errorf("file: %v Fastq Record (%s) qual length (%d) != seq length (%d) at line: %d",
					ff.Name, string(ff.name), len(ff.qual), len(ff.seq), ff.s.Lid()))
				return false
			}
		}
	}
	if len(ff.qual) < len(ff.seq) {
		ff.setErr(fmt.Errorf("file: %v Fastq Record (%s) qual length (%d) != seq length (%d) at line: %d",
			ff.Name, string(ff.name), len(ff.qual), len(ff.seq), ff.s.Lid()))
	}
	ff.setErr(io.EOF)
	return false
}

func (ff *FastqFile) Value() *Fastq {
	return &Fastq{Name: string(ff.name), Seq: ff.seq, Qual: ff.qual}
}

func (ff *FastqFile) Iter() <-chan *Fastq {
	ch := make(chan *Fastq)
	go func(ch chan *Fastq) {
		for ff.Next() {
			ch <- ff.Value()
		}
		close(ch)
	}(ch)
	return ch
}

func Opens(filenames ...string) ([]*FastqFile, error) {
	fqfiles := make([]*fastq.FastqFile, len(filenames))
	for i, filename := range filenames {
		fqfile, err := fastq.Open(filename)
		if err != nil {
			return err
		}
		fqfiles[i] = fqfile
	}
	return fqfiles, nil
}
<commit_msg>add Opens function<commit_after>package fastq

import (
	"bytes"
	"fmt"
	"gongs/scan"
	"gongs/xopen"
	"io"
	"strings"
)

type Fastq struct {
	Name string
	Seq  []byte
	Qual []byte
}

func (fq Fastq) String() string {
	return fmt.Sprintf("@%v\n%v\n+\n%v", fq.Name, string(fq.Seq), string(fq.Qual))
}

func (fq Fastq) Id() string {
	if n := strings.IndexByte(fq.Name, ' '); n >= 0 {
		return fq.Name[:n]
	}

	// for old solexa data format
	if n := strings.IndexByte(fq.Name, '#'); n >= 0 {
		return fq.Name[:n]
	}

	return fq.Name
}

type FastqFile struct {
	Name  string
	file  io.ReadCloser
	s     *scan.Scanner
	name  []byte
	seq   []byte
	qual  []byte
	err   error
	stage int
}

func Open(filename string) (*FastqFile, error) {
	file, err := xopen.Xopen(filename)
	if err != nil {
		return nil, err
	}

	return &FastqFile{
		Name: filename,
		s:    scan.New(file),
		file: file,
	}, nil
}

func (ff *FastqFile) Close() error {
	return ff.file.Close()
}

func (ff *FastqFile) Err() error {
	if ff.err == nil || ff.err == io.EOF {
		if err := ff.s.Err(); err != nil {
			return err
		}
		return nil
	}
	return ff.err
}

func (ff *FastqFile) setErr(err error) {
	if ff.err == nil {
		ff.err = err
	}
}

func (ff *FastqFile) Next() bool {
	if ff.err != nil {
		return false
	}

	var line []byte
	for ff.s.Scan() {
		line = bytes.TrimSpace(ff.s.Bytes())
		if len(line) == 0 { // ingore empty line
			continue
		}
		switch ff.stage {
		case 0: // get fastq name
			if len(line) > 0 && line[0] != '@' {
				ff.setErr(fmt.Errorf("file: %v Wrong Fastq Record Name %s at line: %d", ff.Name, string(line), ff.s.Lid()))
				return false
			}
			ff.stage++
			ff.name = line[1:]
			ff.seq = ff.seq[:0]   // clear seq
			ff.qual = ff.qual[:0] // clear qual
		case 1: // get fastq seq
			if len(line) > 0 && line[0] == '+' {
				ff.stage += 2
				break
			}
			ff.seq = append(ff.seq, line...)
		case 2: // get + line
		case 3: // get fastq qual
			ff.qual = append(ff.qual, line...)
			if len(ff.qual) == len(ff.seq) {
				ff.stage = 0
				return true
			} else if len(ff.qual) > len(ff.seq) {
				ff.setErr(fmt.Errorf("file: %v Fastq Record (%s) qual length (%d) != seq length (%d) at line: %d",
					ff.Name, string(ff.name), len(ff.qual), len(ff.seq), ff.s.Lid()))
				return false
			}
		}
	}
	if len(ff.qual) < len(ff.seq) {
		ff.setErr(fmt.Errorf("file: %v Fastq Record (%s) qual length (%d) != seq length (%d) at line: %d",
			ff.Name, string(ff.name), len(ff.qual), len(ff.seq), ff.s.Lid()))
	}
	ff.setErr(io.EOF)
	return false
}

func (ff *FastqFile) Value() *Fastq {
	return &Fastq{Name: string(ff.name), Seq: ff.seq, Qual: ff.qual}
}

func (ff *FastqFile) Iter() <-chan *Fastq {
	ch := make(chan *Fastq)
	go func(ch chan *Fastq) {
		for ff.Next() {
			ch <- ff.Value()
		}
		close(ch)
	}(ch)
	return ch
}

func Opens(filenames ...string) ([]*FastqFile, error) {
	fqfiles := make([]*FastqFile, len(filenames))
	for i, filename := range filenames {
		fqfile, err := Open(filename)
		if err != nil {
			return nil, err
		}
		fqfiles[i] = fqfile
	}
	return fqfiles, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The clang-server 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 parser

import (
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/go-clang/v3.9/clang"
	"github.com/pkg/errors"
	"github.com/pkgutil/osutil"
	"github.com/pkgutil/stringsutil"
	"github.com/zchee/clang-server/compilationdatabase"
	"github.com/zchee/clang-server/indexdb"
	"github.com/zchee/clang-server/internal/pathutil"
	"github.com/zchee/clang-server/parser/builtinheader"
	"github.com/zchee/clang-server/symbol"
)

// defaultClangOption defalut global clang options.
// clang.TranslationUnit_DetailedPreprocessingRecord = 0x01
// clang.TranslationUnit_Incomplete = 0x02
// clang.TranslationUnit_PrecompiledPreamble = 0x04
// clang.TranslationUnit_CacheCompletionResults = 0x08
// clang.TranslationUnit_ForSerialization = 0x10
// clang.TranslationUnit_CXXChainedPCH = 0x20
// clang.TranslationUnit_SkipFunctionBodies = 0x40
// clang.TranslationUnit_IncludeBriefCommentsInCodeCompletion = 0x80
// clang.TranslationUnit_CreatePreambleOnFirstParse = 0x100
// clang.TranslationUnit_KeepGoing = 0x200
// const defaultClangOption uint32 = 0x445 // Use all flags for now
var defaultClangOption = clang.DefaultEditingTranslationUnitOptions() | uint32(clang.TranslationUnit_KeepGoing)

// Parser represents a C/C++ AST parser.
type Parser struct {
	root        string
	clangOption uint32

	idx clang.Index
	cd  *compilationdatabase.CompilationDatabase
	db  *indexdb.IndexDB

	dispatcher *dispatcher

	debugUncatched bool                     // for debug
	uncachedKind   map[clang.CursorKind]int // for debug
}

// Config represents a parser config.
type Config struct {
	JSONName    string
	PathRange   []string
	ClangOption uint32

	Debug bool
}

// NewParser return the new Parser.
func NewParser(path string, config Config) *Parser {
	root, err := pathutil.FindProjectRoot(path)
	if err != nil {
		log.Fatal(err)
	}

	cd := compilationdatabase.NewCompilationDatabase(root)
	if err := cd.Parse(config.JSONName, config.PathRange); err != nil {
		log.Fatal(err)
	}

	db, err := indexdb.NewIndexDB(root)
	if err != nil {
		log.Fatal(err)
	}

	clangOption := config.ClangOption
	if clangOption == 0 {
		clangOption = defaultClangOption
	}

	p := &Parser{
		root:        root,
		clangOption: clangOption,
		idx:         clang.NewIndex(0, 1), // disable excludeDeclarationsFromPCH, enable displayDiagnostics
		cd:          cd,
		db:          db,
	}

	if config.Debug {
		p.debugUncatched = true
		p.uncachedKind = make(map[clang.CursorKind]int)
	}

	if err := CreateBulitinHeaders(); err != nil {
		log.Fatal(err)
	}

	return p
}

// CreateBulitinHeaders creates(dumps) a clang builtin header to cache directory.
func CreateBulitinHeaders() error {
	builtinHdrDir := filepath.Join(pathutil.CacheDir(), "clang", "include")
	if !osutil.IsExist(builtinHdrDir) {
		if err := os.MkdirAll(builtinHdrDir, 0700); err != nil {
			return errors.WithStack(err)
		}
	}

	for _, fname := range builtinheader.AssetNames() {
		data, err := builtinheader.AssetInfo(fname)
		if err != nil {
			return errors.WithStack(err)
		}

		if strings.Contains(data.Name(), string(filepath.Separator)) {
			dir, _ := filepath.Split(data.Name())
			if err := os.MkdirAll(filepath.Join(builtinHdrDir, dir), 0700); err != nil {
				return errors.WithStack(err)
			}
		}

		buf, err := builtinheader.Asset(data.Name())
		if err != nil {
			return errors.WithStack(err)
		}

		if err := ioutil.WriteFile(filepath.Join(builtinHdrDir, data.Name()), buf, 0600); err != nil {
			return errors.WithStack(err)
		}
	}

	return nil
}

// Parse parses the project directories.
func (p *Parser) Parse() {
	defer p.db.Close()

	ccs := p.cd.CompileCommands()
	if len(ccs) == 0 {
		log.Fatal("not walk")
	}

	compilerConfig := p.cd.CompilerConfig
	flags := append(compilerConfig.SystemCIncludeDir, compilerConfig.SystemFrameworkDir...)

	// TODO(zchee): needs include stdint.h?
	if i := stringsutil.IndexContainsSlice(ccs[0].Arguments, "-std="); i > 0 {
		std := ccs[0].Arguments[i][5:]
		switch {
		case strings.HasPrefix(std, "c"), strings.HasPrefix(std, "gnu"):
			if std[len(std)-2] == '8' || std[len(std)-2] == '9' {
				flags = append(flags, "-include", "/usr/include/stdint.h")
			}
		}
	} else {
		flags = append(flags, "-include", "/usr/include/stdint.h")
	}
	if !(filepath.Ext(ccs[0].File) == ".c") {
		flags = append(flags, compilerConfig.SystemCXXIncludeDir...)
	}

	builtinHdrDir := filepath.Join(pathutil.CacheDir(), "clang", "include")
	flags = append(flags, "-I"+builtinHdrDir)

	p.dispatcher = newDispatcher(p.ParseFile)
	p.dispatcher.Start()
	for i := 0; i < len(ccs); i++ {
		args := ccs[i].Arguments
		args = append(flags, args...)
		p.dispatcher.Add(parseArg{ccs[i].File, args})
	}
	p.dispatcher.Wait()
}

type parseArg struct {
	filename string
	flag     []string
}

// ParseFile parses the C/C++ file.
func (p *Parser) ParseFile(arg parseArg) error {
	var tu clang.TranslationUnit

	if p.db.Has(arg.filename) {
		buf, err := p.db.Get(arg.filename)
		if err != nil {
			return err
		}

		tu, err = deserializeTranslationUnit(p.idx, buf)
		if err != nil {
			return err
		}
		defer tu.Dispose()

		log.Printf("tu.Spelling(): %T => %+v\n", tu.Spelling(), tu.Spelling()) // for debug

		return nil
	}

	if cErr := p.idx.ParseTranslationUnit2(arg.filename, arg.flag, nil, p.clangOption, &tu); clang.ErrorCode(cErr) != clang.Error_Success {
		return errors.New(clang.ErrorCode(cErr).Spelling())
	}
	defer tu.Dispose()

	tuch := make(chan []byte)
	go func() {
		tuch <- serializeTranslationUnit(arg.filename, tu)
	}()

	// printDiagnostics(tu.Diagnostics())

	rootCursor := tu.TranslationUnitCursor()
	file := symbol.NewFile(arg.filename)
	visitNode := func(cursor, parent clang.Cursor) clang.ChildVisitResult {
		if cursor.IsNull() {
			log.Printf("cursor: <none>")
			return clang.ChildVisit_Continue
		}

		cursorLoc := symbol.FromCursor(cursor)
		if cursorLoc.FileName() == "" || cursorLoc.FileName() == "." {
			// TODO(zchee): Ignore system header(?)
			return clang.ChildVisit_Continue
		}

		kind := cursor.Kind()
		switch kind {
		case clang.Cursor_FunctionDecl, clang.Cursor_StructDecl, clang.Cursor_FieldDecl, clang.Cursor_TypedefDecl, clang.Cursor_EnumDecl, clang.Cursor_EnumConstantDecl:
			defCursor := cursor.Definition()
			if defCursor.IsNull() {
				file.AddDecl(cursorLoc)
			} else {
				defLoc := symbol.FromCursor(defCursor)
				file.AddDefinition(cursorLoc, defLoc)
			}
		case clang.Cursor_MacroDefinition:
			file.AddDefinition(cursorLoc, cursorLoc)
		case clang.Cursor_VarDecl:
			file.AddDecl(cursorLoc)
		case clang.Cursor_ParmDecl:
			if cursor.Spelling() != "" {
				file.AddDecl(cursorLoc)
			}
		case clang.Cursor_CallExpr:
			refCursor := cursor.Referenced()
			refLoc := symbol.FromCursor(refCursor)
			file.AddCaller(cursorLoc, refLoc, true)
		case clang.Cursor_DeclRefExpr, clang.Cursor_TypeRef, clang.Cursor_MemberRefExpr, clang.Cursor_MacroExpansion:
			refCursor := cursor.Referenced()
			refLoc := symbol.FromCursor(refCursor)
			file.AddCaller(cursorLoc, refLoc, false)
		case clang.Cursor_InclusionDirective:
			incFile := cursor.IncludedFile()
			file.AddHeader(cursor.Spelling(), incFile)
		default:
			if p.debugUncatched {
				p.uncachedKind[kind]++
			}
		}

		return clang.ChildVisit_Recurse
	}

	rootCursor.Visit(visitNode)
	file.AddTranslationUnit(<-tuch)
	buf := file.Serialize()

	out := symbol.GetRootAsFile(buf.FinishedBytes(), 0)
	printFile(out) // for debug

	log.Printf("Goroutine:%d", runtime.NumGoroutine())
	fmt.Printf("\n================== DONE: filename: %+v ==================\n\n\n", arg.filename)

	return p.db.Put(arg.filename, buf.FinishedBytes())
}

// serializeTranslationUnit selialize the TranslationUnit to Clang serialized representation.
// TODO(zchee): Avoid ioutil.TempFile if possible.
func serializeTranslationUnit(filename string, tu clang.TranslationUnit) []byte {
	tmpFile, err := ioutil.TempFile(os.TempDir(), filepath.Base(filename))
	if err != nil {
		log.Fatal(err)
	}

	saveOptions := uint32(clang.TranslationUnit_KeepGoing)
	if cErr := tu.SaveTranslationUnit(tmpFile.Name(), saveOptions); clang.SaveError(cErr) != clang.SaveError_None {
		log.Fatal(clang.SaveError(cErr))
	}

	buf, err := ioutil.ReadFile(tmpFile.Name())
	if err != nil {
		log.Fatal(err)
	}
	os.Remove(tmpFile.Name())

	return buf
}

func deserializeTranslationUnit(idx clang.Index, buf []byte) (clang.TranslationUnit, error) {
	var tu clang.TranslationUnit

	tmpfile, err := ioutil.TempFile(os.TempDir(), "clang-server")
	if err != nil {
		return tu, err
	}
	binary.Write(tmpfile, binary.LittleEndian, buf)

	if err := idx.TranslationUnit2(tmpfile.Name(), &tu); clang.ErrorCode(err) != clang.Error_Success {
		return tu, errors.New(err.Spelling())
	}
	// finished create a translation unit from an AST file, remove tmpfile
	os.Remove(tmpfile.Name())

	return tu, nil
}

// ClangVersion return the current clang version.
func ClangVersion() string {
	return clang.GetClangVersion()
}
<commit_msg>parser: add symbol.Serve after parsed C/C++ files AST<commit_after>// Copyright 2016 The clang-server 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 parser

import (
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/go-clang/v3.9/clang"
	"github.com/pkg/errors"
	"github.com/pkgutil/osutil"
	"github.com/pkgutil/stringsutil"
	"github.com/zchee/clang-server/compilationdatabase"
	"github.com/zchee/clang-server/indexdb"
	"github.com/zchee/clang-server/internal/pathutil"
	"github.com/zchee/clang-server/parser/builtinheader"
	"github.com/zchee/clang-server/symbol"
)

// defaultClangOption defalut global clang options.
// clang.TranslationUnit_DetailedPreprocessingRecord = 0x01
// clang.TranslationUnit_Incomplete = 0x02
// clang.TranslationUnit_PrecompiledPreamble = 0x04
// clang.TranslationUnit_CacheCompletionResults = 0x08
// clang.TranslationUnit_ForSerialization = 0x10
// clang.TranslationUnit_CXXChainedPCH = 0x20
// clang.TranslationUnit_SkipFunctionBodies = 0x40
// clang.TranslationUnit_IncludeBriefCommentsInCodeCompletion = 0x80
// clang.TranslationUnit_CreatePreambleOnFirstParse = 0x100
// clang.TranslationUnit_KeepGoing = 0x200
// const defaultClangOption uint32 = 0x445 // Use all flags for now
var defaultClangOption = clang.DefaultEditingTranslationUnitOptions() | uint32(clang.TranslationUnit_KeepGoing)

// Parser represents a C/C++ AST parser.
type Parser struct {
	root        string
	clangOption uint32

	idx clang.Index
	cd  *compilationdatabase.CompilationDatabase
	db  *indexdb.IndexDB

	dispatcher *dispatcher

	debugUncatched bool                     // for debug
	uncachedKind   map[clang.CursorKind]int // for debug
}

// Config represents a parser config.
type Config struct {
	JSONName    string
	PathRange   []string
	ClangOption uint32

	Debug bool
}

// NewParser return the new Parser.
func NewParser(path string, config Config) *Parser {
	root, err := pathutil.FindProjectRoot(path)
	if err != nil {
		log.Fatal(err)
	}

	cd := compilationdatabase.NewCompilationDatabase(root)
	if err := cd.Parse(config.JSONName, config.PathRange); err != nil {
		log.Fatal(err)
	}

	db, err := indexdb.NewIndexDB(root)
	if err != nil {
		log.Fatal(err)
	}

	clangOption := config.ClangOption
	if clangOption == 0 {
		clangOption = defaultClangOption
	}

	p := &Parser{
		root:        root,
		clangOption: clangOption,
		idx:         clang.NewIndex(0, 1), // disable excludeDeclarationsFromPCH, enable displayDiagnostics
		cd:          cd,
		db:          db,
	}

	if config.Debug {
		p.debugUncatched = true
		p.uncachedKind = make(map[clang.CursorKind]int)
	}

	if err := CreateBulitinHeaders(); err != nil {
		log.Fatal(err)
	}

	return p
}

// CreateBulitinHeaders creates(dumps) a clang builtin header to cache directory.
func CreateBulitinHeaders() error {
	builtinHdrDir := filepath.Join(pathutil.CacheDir(), "clang", "include")
	if !osutil.IsExist(builtinHdrDir) {
		if err := os.MkdirAll(builtinHdrDir, 0700); err != nil {
			return errors.WithStack(err)
		}
	}

	for _, fname := range builtinheader.AssetNames() {
		data, err := builtinheader.AssetInfo(fname)
		if err != nil {
			return errors.WithStack(err)
		}

		if strings.Contains(data.Name(), string(filepath.Separator)) {
			dir, _ := filepath.Split(data.Name())
			if err := os.MkdirAll(filepath.Join(builtinHdrDir, dir), 0700); err != nil {
				return errors.WithStack(err)
			}
		}

		buf, err := builtinheader.Asset(data.Name())
		if err != nil {
			return errors.WithStack(err)
		}

		if err := ioutil.WriteFile(filepath.Join(builtinHdrDir, data.Name()), buf, 0600); err != nil {
			return errors.WithStack(err)
		}
	}

	return nil
}

// Parse parses the project directories.
func (p *Parser) Parse() {
	defer func() {
		p.db.Close()
		symbol.Serve()
	}()

	ccs := p.cd.CompileCommands()
	if len(ccs) == 0 {
		log.Fatal("not walk")
	}

	compilerConfig := p.cd.CompilerConfig
	flags := append(compilerConfig.SystemCIncludeDir, compilerConfig.SystemFrameworkDir...)

	// TODO(zchee): needs include stdint.h?
	if i := stringsutil.IndexContainsSlice(ccs[0].Arguments, "-std="); i > 0 {
		std := ccs[0].Arguments[i][5:]
		switch {
		case strings.HasPrefix(std, "c"), strings.HasPrefix(std, "gnu"):
			if std[len(std)-2] == '8' || std[len(std)-2] == '9' {
				flags = append(flags, "-include", "/usr/include/stdint.h")
			}
		}
	} else {
		flags = append(flags, "-include", "/usr/include/stdint.h")
	}
	if !(filepath.Ext(ccs[0].File) == ".c") {
		flags = append(flags, compilerConfig.SystemCXXIncludeDir...)
	}

	builtinHdrDir := filepath.Join(pathutil.CacheDir(), "clang", "include")
	flags = append(flags, "-I"+builtinHdrDir)

	p.dispatcher = newDispatcher(p.ParseFile)
	p.dispatcher.Start()
	for i := 0; i < len(ccs); i++ {
		args := ccs[i].Arguments
		args = append(flags, args...)
		p.dispatcher.Add(parseArg{ccs[i].File, args})
	}
	p.dispatcher.Wait()
}

type parseArg struct {
	filename string
	flag     []string
}

// ParseFile parses the C/C++ file.
func (p *Parser) ParseFile(arg parseArg) error {
	var tu clang.TranslationUnit

	if p.db.Has(arg.filename) {
		buf, err := p.db.Get(arg.filename)
		if err != nil {
			return err
		}

		tu, err = deserializeTranslationUnit(p.idx, buf)
		if err != nil {
			return err
		}
		defer tu.Dispose()

		log.Printf("tu.Spelling(): %T => %+v\n", tu.Spelling(), tu.Spelling()) // for debug

		return nil
	}

	if cErr := p.idx.ParseTranslationUnit2(arg.filename, arg.flag, nil, p.clangOption, &tu); clang.ErrorCode(cErr) != clang.Error_Success {
		return errors.New(clang.ErrorCode(cErr).Spelling())
	}
	defer tu.Dispose()

	tuch := make(chan []byte)
	go func() {
		tuch <- serializeTranslationUnit(arg.filename, tu)
	}()

	// printDiagnostics(tu.Diagnostics())

	rootCursor := tu.TranslationUnitCursor()
	file := symbol.NewFile(arg.filename)
	visitNode := func(cursor, parent clang.Cursor) clang.ChildVisitResult {
		if cursor.IsNull() {
			log.Printf("cursor: <none>")
			return clang.ChildVisit_Continue
		}

		cursorLoc := symbol.FromCursor(cursor)
		if cursorLoc.FileName() == "" || cursorLoc.FileName() == "." {
			// TODO(zchee): Ignore system header(?)
			return clang.ChildVisit_Continue
		}

		kind := cursor.Kind()
		switch kind {
		case clang.Cursor_FunctionDecl, clang.Cursor_StructDecl, clang.Cursor_FieldDecl, clang.Cursor_TypedefDecl, clang.Cursor_EnumDecl, clang.Cursor_EnumConstantDecl:
			defCursor := cursor.Definition()
			if defCursor.IsNull() {
				file.AddDecl(cursorLoc)
			} else {
				defLoc := symbol.FromCursor(defCursor)
				file.AddDefinition(cursorLoc, defLoc)
			}
		case clang.Cursor_MacroDefinition:
			file.AddDefinition(cursorLoc, cursorLoc)
		case clang.Cursor_VarDecl:
			file.AddDecl(cursorLoc)
		case clang.Cursor_ParmDecl:
			if cursor.Spelling() != "" {
				file.AddDecl(cursorLoc)
			}
		case clang.Cursor_CallExpr:
			refCursor := cursor.Referenced()
			refLoc := symbol.FromCursor(refCursor)
			file.AddCaller(cursorLoc, refLoc, true)
		case clang.Cursor_DeclRefExpr, clang.Cursor_TypeRef, clang.Cursor_MemberRefExpr, clang.Cursor_MacroExpansion:
			refCursor := cursor.Referenced()
			refLoc := symbol.FromCursor(refCursor)
			file.AddCaller(cursorLoc, refLoc, false)
		case clang.Cursor_InclusionDirective:
			incFile := cursor.IncludedFile()
			file.AddHeader(cursor.Spelling(), incFile)
		default:
			if p.debugUncatched {
				p.uncachedKind[kind]++
			}
		}

		return clang.ChildVisit_Recurse
	}

	rootCursor.Visit(visitNode)
	file.AddTranslationUnit(<-tuch)
	buf := file.Serialize()

	out := symbol.GetRootAsFile(buf.FinishedBytes(), 0)
	printFile(out) // for debug

	log.Printf("Goroutine:%d", runtime.NumGoroutine())
	fmt.Printf("\n================== DONE: filename: %+v ==================\n\n\n", arg.filename)

	return p.db.Put(arg.filename, buf.FinishedBytes())
}

// serializeTranslationUnit selialize the TranslationUnit to Clang serialized representation.
// TODO(zchee): Avoid ioutil.TempFile if possible.
func serializeTranslationUnit(filename string, tu clang.TranslationUnit) []byte {
	tmpFile, err := ioutil.TempFile(os.TempDir(), filepath.Base(filename))
	if err != nil {
		log.Fatal(err)
	}

	saveOptions := uint32(clang.TranslationUnit_KeepGoing)
	if cErr := tu.SaveTranslationUnit(tmpFile.Name(), saveOptions); clang.SaveError(cErr) != clang.SaveError_None {
		log.Fatal(clang.SaveError(cErr))
	}

	buf, err := ioutil.ReadFile(tmpFile.Name())
	if err != nil {
		log.Fatal(err)
	}
	os.Remove(tmpFile.Name())

	return buf
}

func deserializeTranslationUnit(idx clang.Index, buf []byte) (clang.TranslationUnit, error) {
	var tu clang.TranslationUnit

	tmpfile, err := ioutil.TempFile(os.TempDir(), "clang-server")
	if err != nil {
		return tu, err
	}
	binary.Write(tmpfile, binary.LittleEndian, buf)

	if err := idx.TranslationUnit2(tmpfile.Name(), &tu); clang.ErrorCode(err) != clang.Error_Success {
		return tu, errors.New(err.Spelling())
	}
	// finished create a translation unit from an AST file, remove tmpfile
	os.Remove(tmpfile.Name())

	return tu, nil
}

// ClangVersion return the current clang version.
func ClangVersion() string {
	return clang.GetClangVersion()
}
<|endoftext|>


<commit_before>package lrp_bbs

import (
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/bbserrors"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/services_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/shared"
	"github.com/cloudfoundry-incubator/runtime-schema/cb"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/cloudfoundry/gunk/timeprovider"
	"github.com/cloudfoundry/storeadapter"
	"github.com/pivotal-golang/lager"
)

type LRPBBS struct {
	store            storeadapter.StoreAdapter
	timeProvider     timeprovider.TimeProvider
	cellClient       cb.CellClient
	auctioneerClient cb.AuctioneerClient
	services         *services_bbs.ServicesBBS
	logger           lager.Logger
}

func New(
	store storeadapter.StoreAdapter,
	timeProvider timeprovider.TimeProvider,
	cellClient cb.CellClient,
	auctioneerClient cb.AuctioneerClient,
	services *services_bbs.ServicesBBS,
	logger lager.Logger,
) *LRPBBS {
	return &LRPBBS{
		store:            store,
		timeProvider:     timeProvider,
		cellClient:       cellClient,
		auctioneerClient: auctioneerClient,
		services:         services,
		logger:           logger,
	}
}

func (bbs *LRPBBS) DesireLRP(lrp models.DesiredLRP) error {
	err := lrp.Validate()
	if err != nil {
		return err
	}

	value, err := models.ToJSON(lrp)
	if err != nil {
		return err
	}

	err = shared.RetryIndefinitelyOnStoreTimeout(func() error {
		return bbs.store.Create(storeadapter.StoreNode{
			Key:   shared.DesiredLRPSchemaPath(lrp),
			Value: value,
		})
	})

	switch err {
	case bbserrors.ErrStoreResourceExists:
		existingLRP, index, err := bbs.desiredLRPByProcessGuidWithIndex(lrp.ProcessGuid)
		if err != nil {
			return err
		}

		err = existingLRP.ValidateModifications(lrp)
		if err != nil {
			return err
		}

		value, err := models.ToJSON(lrp)
		if err != nil {
			return err
		}

		err = shared.RetryIndefinitelyOnStoreTimeout(func() error {
			return bbs.store.CompareAndSwapByIndex(index, storeadapter.StoreNode{
				Key:   shared.DesiredLRPSchemaPath(lrp),
				Value: value,
			})
		})
		if err != nil {
			return err
		}

		bbs.processDesiredChange(models.DesiredLRPChange{
			Before: &existingLRP,
			After:  &lrp,
		}, bbs.logger)

		return nil

	case nil:
		bbs.processDesiredChange(models.DesiredLRPChange{
			Before: nil,
			After:  &lrp,
		}, bbs.logger)

		return nil

	default:
		return err
	}
}

func (bbs *LRPBBS) RemoveDesiredLRPByProcessGuid(processGuid string) error {
	lrp, err := bbs.DesiredLRPByProcessGuid(processGuid)
	if err != nil {
		return err
	}

	err = shared.RetryIndefinitelyOnStoreTimeout(func() error {
		return bbs.store.Delete(shared.DesiredLRPSchemaPathByProcessGuid(processGuid))
	})
	if err != nil {
		return err
	}

	bbs.processDesiredChange(models.DesiredLRPChange{
		Before: &lrp,
		After:  nil,
	}, bbs.logger)

	return nil
}

func (bbs *LRPBBS) ChangeDesiredLRP(change models.DesiredLRPChange) error {
	beforeValue, err := models.ToJSON(change.Before)
	if err != nil {
		return err
	}
	afterValue, err := models.ToJSON(change.After)
	if err != nil {
		return err
	}

	return shared.RetryIndefinitelyOnStoreTimeout(func() error {
		if change.Before != nil && change.After != nil {
			return bbs.store.CompareAndSwap(
				storeadapter.StoreNode{
					Key:   shared.DesiredLRPSchemaPath(*change.Before),
					Value: beforeValue,
				},
				storeadapter.StoreNode{
					Key:   shared.DesiredLRPSchemaPath(*change.After),
					Value: afterValue,
				},
			)
		}

		if change.Before != nil {
			return bbs.store.CompareAndDelete(
				storeadapter.StoreNode{
					Key:   shared.DesiredLRPSchemaPath(*change.Before),
					Value: beforeValue,
				},
			)
		}

		if change.After != nil {
			return bbs.store.Create(
				storeadapter.StoreNode{
					Key:   shared.DesiredLRPSchemaPath(*change.After),
					Value: afterValue,
				},
			)
		}

		return nil
	})
}

func (bbs *LRPBBS) UpdateDesiredLRP(processGuid string, update models.DesiredLRPUpdate) error {
	existing, err := bbs.DesiredLRPByProcessGuid(processGuid)
	if err != nil {
		return err
	}

	updatedLRP := existing.ApplyUpdate(update)
	err = updatedLRP.Validate()
	if err != nil {
		return err
	}

	value, err := models.ToJSON(updatedLRP)
	if err != nil {
		return err
	}

	return shared.RetryIndefinitelyOnStoreTimeout(func() error {
		err := bbs.store.SetMulti([]storeadapter.StoreNode{
			{
				Key:   shared.DesiredLRPSchemaPath(updatedLRP),
				Value: value,
			},
		})
		if err != nil {
			return err
		}

		bbs.processDesiredChange(models.DesiredLRPChange{
			Before: &existing,
			After:  &updatedLRP,
		}, bbs.logger)

		return nil
	})
}
<commit_msg>CAS in UpdateDesiredLRP<commit_after>package lrp_bbs

import (
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/bbserrors"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/services_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/shared"
	"github.com/cloudfoundry-incubator/runtime-schema/cb"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/cloudfoundry/gunk/timeprovider"
	"github.com/cloudfoundry/storeadapter"
	"github.com/pivotal-golang/lager"
)

type LRPBBS struct {
	store            storeadapter.StoreAdapter
	timeProvider     timeprovider.TimeProvider
	cellClient       cb.CellClient
	auctioneerClient cb.AuctioneerClient
	services         *services_bbs.ServicesBBS
	logger           lager.Logger
}

func New(
	store storeadapter.StoreAdapter,
	timeProvider timeprovider.TimeProvider,
	cellClient cb.CellClient,
	auctioneerClient cb.AuctioneerClient,
	services *services_bbs.ServicesBBS,
	logger lager.Logger,
) *LRPBBS {
	return &LRPBBS{
		store:            store,
		timeProvider:     timeProvider,
		cellClient:       cellClient,
		auctioneerClient: auctioneerClient,
		services:         services,
		logger:           logger,
	}
}

func (bbs *LRPBBS) DesireLRP(lrp models.DesiredLRP) error {
	err := lrp.Validate()
	if err != nil {
		return err
	}

	value, err := models.ToJSON(lrp)
	if err != nil {
		return err
	}

	err = shared.RetryIndefinitelyOnStoreTimeout(func() error {
		return bbs.store.Create(storeadapter.StoreNode{
			Key:   shared.DesiredLRPSchemaPath(lrp),
			Value: value,
		})
	})

	switch err {
	case bbserrors.ErrStoreResourceExists:
		existingLRP, index, err := bbs.desiredLRPByProcessGuidWithIndex(lrp.ProcessGuid)
		if err != nil {
			return err
		}

		err = existingLRP.ValidateModifications(lrp)
		if err != nil {
			return err
		}

		value, err := models.ToJSON(lrp)
		if err != nil {
			return err
		}

		err = shared.RetryIndefinitelyOnStoreTimeout(func() error {
			return bbs.store.CompareAndSwapByIndex(index, storeadapter.StoreNode{
				Key:   shared.DesiredLRPSchemaPath(lrp),
				Value: value,
			})
		})
		if err != nil {
			return err
		}

		bbs.processDesiredChange(models.DesiredLRPChange{
			Before: &existingLRP,
			After:  &lrp,
		}, bbs.logger)

		return nil

	case nil:
		bbs.processDesiredChange(models.DesiredLRPChange{
			Before: nil,
			After:  &lrp,
		}, bbs.logger)

		return nil

	default:
		return err
	}
}

func (bbs *LRPBBS) RemoveDesiredLRPByProcessGuid(processGuid string) error {
	lrp, err := bbs.DesiredLRPByProcessGuid(processGuid)
	if err != nil {
		return err
	}

	err = shared.RetryIndefinitelyOnStoreTimeout(func() error {
		return bbs.store.Delete(shared.DesiredLRPSchemaPathByProcessGuid(processGuid))
	})
	if err != nil {
		return err
	}

	bbs.processDesiredChange(models.DesiredLRPChange{
		Before: &lrp,
		After:  nil,
	}, bbs.logger)

	return nil
}

func (bbs *LRPBBS) ChangeDesiredLRP(change models.DesiredLRPChange) error {
	beforeValue, err := models.ToJSON(change.Before)
	if err != nil {
		return err
	}
	afterValue, err := models.ToJSON(change.After)
	if err != nil {
		return err
	}

	return shared.RetryIndefinitelyOnStoreTimeout(func() error {
		if change.Before != nil && change.After != nil {
			return bbs.store.CompareAndSwap(
				storeadapter.StoreNode{
					Key:   shared.DesiredLRPSchemaPath(*change.Before),
					Value: beforeValue,
				},
				storeadapter.StoreNode{
					Key:   shared.DesiredLRPSchemaPath(*change.After),
					Value: afterValue,
				},
			)
		}

		if change.Before != nil {
			return bbs.store.CompareAndDelete(
				storeadapter.StoreNode{
					Key:   shared.DesiredLRPSchemaPath(*change.Before),
					Value: beforeValue,
				},
			)
		}

		if change.After != nil {
			return bbs.store.Create(
				storeadapter.StoreNode{
					Key:   shared.DesiredLRPSchemaPath(*change.After),
					Value: afterValue,
				},
			)
		}

		return nil
	})
}

func (bbs *LRPBBS) UpdateDesiredLRP(processGuid string, update models.DesiredLRPUpdate) error {
	existing, index, err := bbs.desiredLRPByProcessGuidWithIndex(processGuid)
	if err != nil {
		return err
	}

	updatedLRP := existing.ApplyUpdate(update)
	err = updatedLRP.Validate()
	if err != nil {
		return err
	}

	value, err := models.ToJSON(updatedLRP)
	if err != nil {
		return err
	}

	return shared.RetryIndefinitelyOnStoreTimeout(func() error {
		err := bbs.store.CompareAndSwapByIndex(index, storeadapter.StoreNode{
			Key:   shared.DesiredLRPSchemaPath(updatedLRP),
			Value: value,
		})
		if err != nil {
			return err
		}

		bbs.processDesiredChange(models.DesiredLRPChange{
			Before: &existing,
			After:  &updatedLRP,
		}, bbs.logger)

		return nil
	})
}
<|endoftext|>


<commit_before>// +build !nofilesystem

package collector

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"regexp"
	"strings"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/log"
)

const (
	procMounts          = "/proc/mounts"
	filesystemSubsystem = "filesystem"
)

var (
	ignoredMountPoints = flag.String("collector.filesystem.ignored-mount-points", "^/(sys|proc|dev)($|/)", "Regexp of mount points to ignore for filesystem collector.")
)

type filesystemDetails struct {
	device     string
	mountPoint string
	fsType     string
}

type filesystemCollector struct {
	ignoredMountPointsPattern *regexp.Regexp

	size, free, avail, files, filesFree *prometheus.GaugeVec
}

func init() {
	Factories["filesystem"] = NewFilesystemCollector
}

// Takes a prometheus registry and returns a new Collector exposing
// network device filesystems.
func NewFilesystemCollector() (Collector, error) {
	var filesystemLabelNames = []string{"device", "filesystem", "fstype"}

	return &filesystemCollector{
		ignoredMountPointsPattern: regexp.MustCompile(*ignoredMountPoints),
		size: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "size",
				Help:      "Filesystem size in bytes.",
			},
			filesystemLabelNames,
		),
		free: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "free",
				Help:      "Filesystem free space in bytes.",
			},
			filesystemLabelNames,
		),
		avail: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "avail",
				Help:      "Filesystem space available to non-root users in bytes.",
			},
			filesystemLabelNames,
		),
		files: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "files",
				Help:      "Filesystem total file nodes.",
			},
			filesystemLabelNames,
		),
		filesFree: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "files_free",
				Help:      "Filesystem total free file nodes.",
			},
			filesystemLabelNames,
		),
	}, nil
}

// Expose filesystem fullness.
func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) (err error) {
	mpds, err := mountPointDetails()
	if err != nil {
		return err
	}
	for _, mpd := range mpds {
		if c.ignoredMountPointsPattern.MatchString(mpd.mountPoint) {
			log.Debugf("Ignoring mount point: %s", mpd.mountPoint)
			continue
		}
		buf := new(syscall.Statfs_t)
		err := syscall.Statfs(mpd.mountPoint, buf)
		if err != nil {
			return fmt.Errorf("Statfs on %s returned %s", mpd.mountPoint, err)
		}

		c.size.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Blocks) * float64(buf.Bsize))
		c.free.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Bfree) * float64(buf.Bsize))
		c.avail.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Bavail) * float64(buf.Bsize))
		c.files.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Files))
		c.filesFree.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Ffree))
	}
	c.size.Collect(ch)
	c.free.Collect(ch)
	c.avail.Collect(ch)
	c.files.Collect(ch)
	c.filesFree.Collect(ch)
	return err
}

func mountPointDetails() ([]filesystemDetails, error) {
	file, err := os.Open(procMounts)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	filesystems := []filesystemDetails{}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		parts := strings.Fields(scanner.Text())
		filesystems = append(filesystems, filesystemDetails{parts[0], parts[1], parts[2]})
	}
	return filesystems, nil
}
<commit_msg>Change "filesystem" to "mountpoint" to better reflect the new labels.<commit_after>// +build !nofilesystem

package collector

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"regexp"
	"strings"
	"syscall"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/log"
)

const (
	procMounts          = "/proc/mounts"
	filesystemSubsystem = "filesystem"
)

var (
	ignoredMountPoints = flag.String("collector.filesystem.ignored-mount-points", "^/(sys|proc|dev)($|/)", "Regexp of mount points to ignore for filesystem collector.")
)

type filesystemDetails struct {
	device     string
	mountPoint string
	fsType     string
}

type filesystemCollector struct {
	ignoredMountPointsPattern *regexp.Regexp

	size, free, avail, files, filesFree *prometheus.GaugeVec
}

func init() {
	Factories["filesystem"] = NewFilesystemCollector
}

// Takes a prometheus registry and returns a new Collector exposing
// network device filesystems.
func NewFilesystemCollector() (Collector, error) {
	var filesystemLabelNames = []string{"device", "mountpoint", "fstype"}

	return &filesystemCollector{
		ignoredMountPointsPattern: regexp.MustCompile(*ignoredMountPoints),
		size: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "size",
				Help:      "Filesystem size in bytes.",
			},
			filesystemLabelNames,
		),
		free: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "free",
				Help:      "Filesystem free space in bytes.",
			},
			filesystemLabelNames,
		),
		avail: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "avail",
				Help:      "Filesystem space available to non-root users in bytes.",
			},
			filesystemLabelNames,
		),
		files: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "files",
				Help:      "Filesystem total file nodes.",
			},
			filesystemLabelNames,
		),
		filesFree: prometheus.NewGaugeVec(
			prometheus.GaugeOpts{
				Namespace: Namespace,
				Subsystem: filesystemSubsystem,
				Name:      "files_free",
				Help:      "Filesystem total free file nodes.",
			},
			filesystemLabelNames,
		),
	}, nil
}

// Expose filesystem fullness.
func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) (err error) {
	mpds, err := mountPointDetails()
	if err != nil {
		return err
	}
	for _, mpd := range mpds {
		if c.ignoredMountPointsPattern.MatchString(mpd.mountPoint) {
			log.Debugf("Ignoring mount point: %s", mpd.mountPoint)
			continue
		}
		buf := new(syscall.Statfs_t)
		err := syscall.Statfs(mpd.mountPoint, buf)
		if err != nil {
			return fmt.Errorf("Statfs on %s returned %s", mpd.mountPoint, err)
		}

		c.size.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Blocks) * float64(buf.Bsize))
		c.free.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Bfree) * float64(buf.Bsize))
		c.avail.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Bavail) * float64(buf.Bsize))
		c.files.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Files))
		c.filesFree.WithLabelValues(mpd.device, mpd.mountPoint, mpd.fsType).Set(float64(buf.Ffree))
	}
	c.size.Collect(ch)
	c.free.Collect(ch)
	c.avail.Collect(ch)
	c.files.Collect(ch)
	c.filesFree.Collect(ch)
	return err
}

func mountPointDetails() ([]filesystemDetails, error) {
	file, err := os.Open(procMounts)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	filesystems := []filesystemDetails{}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		parts := strings.Fields(scanner.Text())
		filesystems = append(filesystems, filesystemDetails{parts[0], parts[1], parts[2]})
	}
	return filesystems, nil
}
<|endoftext|>


<commit_before>package gesclient

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/satori/go.uuid"
	"io"
	"net"
	"os"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

type Connection interface {
	WaitForConnection()
	Close() error
	AppendToStream(stream string, expectedVersion int, events []*EventData,
		userCredentials *UserCredentials) (*WriteResult, error)
	AppendToStreamAsync(stream string, expectedVersion int, events []*EventData,
		userCredentials *UserCredentials) (<-chan *WriteResult, error)
	ReadStreamEventsForward(stream string, start int, max int,
		userCredentials *UserCredentials) (*StreamEventsSlice, error)
	ReadStreamEventsForwardAsync(stream string, start int, max int,
		userCredentials *UserCredentials) (<-chan *StreamEventsSlice, error)
	SubscribeToStream(stream string, userCredentials *UserCredentials) (Subscription, error)
	SubscribeToStreamAsync(stream string, userCredentials *UserCredentials) (<-chan Subscription, error)
}

type connection struct {
	address     string
	opMutex     sync.RWMutex
	operations  map[uuid.UUID]operation
	leftOver    []byte
	reconnect   *int32
	connected   *int32
	output      chan *tcpPacket
	conn        net.Conn
	readerEnded chan struct{}
	writerEnded chan struct{}
}

func NewConnection(addr string) Connection {
	reconnect := int32(1)
	connected := int32(0)
	c := &connection{
		address:     addr,
		operations:  make(map[uuid.UUID]operation),
		output:      make(chan *tcpPacket, 100),
		reconnect:   &reconnect,
		connected:   &connected,
		readerEnded: make(chan struct{}),
		writerEnded: make(chan struct{}),
	}
	go c.connect()
	return c
}

func (c *connection) connect() {
	var err error
	for atomic.LoadInt32(c.reconnect) == 1 {
		log.Info("Connecting to %s", c.address)
		c.conn, err = net.DialTimeout("tcp4", c.address, time.Second*3)
		if err == nil {
			go c.reader()
			go c.writer()

			atomic.StoreInt32(c.connected, 1)
			c.resubscribe()

			<-c.writerEnded

			atomic.StoreInt32(c.connected, 0)
			log.Info("Disconnected from %s", c.address)
		} else {
			log.Error("Connection failed: %v", err)
		}
		if atomic.LoadInt32(c.reconnect) == 1 {
			time.Sleep(time.Second * 3)
		}
	}
	close(c.readerEnded)
	close(c.writerEnded)
	c.clearOperations()
}

func (c *connection) reader() {
	log.Debug("Starting reader")
	for {
		buff := make([]byte, 4096)
		n, err := c.conn.Read(buff)
		if err == nil {
			c.onData(buff[:n])
		} else if err == io.EOF {
			break
		} else {
			if isClosedConnError(err) {
				break
			}
			log.Error("conn.Read: %v", err)
		}
	}
	c.readerEnded <- struct{}{}
	log.Debug("Reader ended")
}

func (c *connection) writer() {
	log.Debug("Starting writer")
	var packet *tcpPacket
	run := true
	for run {
		if packet == nil {
			select {
			case packet = <-c.output:
			case <-c.readerEnded:
				run = false
				continue
			default:
				continue
			}
		}
		if err := c.writeToConnection(c.conn, packet); err == nil {
			packet = nil
		} else if err == io.EOF {
			break
		}
	}
	c.writerEnded <- struct{}{}
	log.Debug("Writer ended")
}

func (c *connection) writeToConnection(conn net.Conn, packet *tcpPacket) error {
	if err := binary.Write(conn, binary.LittleEndian, packet.Size()); err != nil {
		log.Error("binary.Write failed: %v", err)
		return err
	}
	if _, err := conn.Write(packet.Bytes()); err != nil {
		log.Error("net.Conn.Write failed: %v", err)
		return err
	}
	log.Info("Sent Command: %s | CorrelationId: %s", packet.Command, packet.CorrelationId)
	return nil
}

func (c *connection) assertConnected() error {
	if atomic.LoadInt32(c.connected) == 0 {
		return errors.New("Not connected")
	}
	return nil
}

func (c *connection) WaitForConnection() {
	for atomic.LoadInt32(c.connected) == 0 {
		time.Sleep(10)
	}
}

func (c *connection) Close() error {
	if err := c.assertConnected(); err != nil {
		return err
	}
	atomic.StoreInt32(c.reconnect, 0)
	return c.conn.Close()
}

func (c *connection) AppendToStream(
	stream string,
	expectedVersion int,
	events []*EventData,
	userCredentials *UserCredentials,
) (*WriteResult, error) {
	ch, err := c.AppendToStreamAsync(stream, expectedVersion, events, userCredentials)
	if err != nil {
		return nil, err
	}
	return <-ch, err
}

func (c *connection) AppendToStreamAsync(
	stream string,
	expectedVersion int,
	events []*EventData,
	userCredentials *UserCredentials,
) (<-chan *WriteResult, error) {
	if err := c.assertConnected(); err != nil {
		return nil, err
	}

	res := make(chan *WriteResult)
	return res, c.enqueueOperation(
		newAppendToStreamOperation(stream, events, expectedVersion, res, userCredentials), true)
}

func (c *connection) ReadStreamEventsForward(
	stream string,
	start int,
	max int,
	userCredentials *UserCredentials,
) (*StreamEventsSlice, error) {
	ch, err := c.ReadStreamEventsForwardAsync(stream, start, max, userCredentials)
	if err != nil {
		return nil, err
	}
	return <-ch, nil
}

func (c *connection) ReadStreamEventsForwardAsync(
	stream string,
	start int,
	max int,
	userCredentials *UserCredentials,
) (<-chan *StreamEventsSlice, error) {
	if err := c.assertConnected(); err != nil {
		return nil, err
	}

	res := make(chan *StreamEventsSlice)
	return res, c.enqueueOperation(
		newReadStreamEventsForwardOperation(stream, start, max, res, userCredentials), true)
}

func (c *connection) SubscribeToStream(stream string, userCredentials *UserCredentials) (Subscription, error) {
	ch, err := c.SubscribeToStreamAsync(stream, userCredentials)
	if err != nil {
		return nil, err
	}
	return <-ch, nil
}

func (c *connection) SubscribeToStreamAsync(stream string, userCredentials *UserCredentials) (<-chan Subscription, error) {
	if err := c.assertConnected(); err != nil {
		return nil, err
	}

	res := make(chan Subscription)
	return res, c.enqueueOperation(newSubscribeToStreamOperation(stream, res, c, userCredentials), true)
}

func (c *connection) enqueueOperation(op operation, isNew bool) error {
	payload, err := proto.Marshal(op.GetRequestMessage())
	if err != nil {
		log.Error("Sending command failed: %v", err)
		op.Fail(fmt.Errorf("Sending command failed: %v", err))
		return err
	}

	correlationId := op.GetCorrelationId()
	userCredentials := op.UserCredentials()
	var authFlag byte = 0
	if userCredentials != nil {
		authFlag = 1
	}
	c.output <- newTcpPacket(
		op.GetRequestCommand(),
		authFlag,
		correlationId,
		payload,
		userCredentials,
	)

	if isNew {
		c.addOperation(correlationId, op)
	}

	return nil
}

func (c *connection) onData(data []byte) {
	if c.leftOver != nil && len(c.leftOver) > 0 {
		data = append(c.leftOver, data...)
		c.leftOver = nil
	}

	dataLength := int32(len(data))
	if dataLength < tcpPacketContentLengthSize {
		c.leftOver = data
		return
	}
	var contentLength int32
	var buf = bytes.NewBuffer(data)
	binary.Read(buf, binary.LittleEndian, &contentLength)

	packetSize := contentLength + tcpPacketContentLengthSize
	if dataLength == packetSize {
		go c.process(tcpPacketFromBytes(data[tcpPacketContentLengthSize:]))
	} else if dataLength > packetSize {
		c.onData(data[:packetSize])
		c.onData(data[packetSize:])
	} else {
		c.leftOver = data
	}
}

func (c *connection) process(p *tcpPacket) {
	log.Info("Received Command: %s | CorrelationId: %s", p.Command, p.CorrelationId)

	operation := c.getOperation(p.CorrelationId)

	if operation != nil {
		operation.ParseResponse(p)
		if operation.IsCompleted() {
			c.removeOperation(p.CorrelationId)
		} else if operation.Retry() {
			c.enqueueOperation(operation, false)
		}
		return
	}

	switch p.Command {
	case tcpCommand_HeartbeatRequestCommand:
		c.output <- newTcpPacket(tcpCommand_HeartbeatResponseCommand, 0, p.CorrelationId, nil, nil)
	default:
		log.Debug("Command not supported")
	}
}

func (c *connection) addOperation(correlationId uuid.UUID, operation operation) {
	c.opMutex.Lock()
	c.operations[correlationId] = operation
	c.opMutex.Unlock()
}

func (c *connection) getOperation(correlationId uuid.UUID) operation {
	c.opMutex.RLock()
	operation := c.operations[correlationId]
	c.opMutex.RUnlock()
	return operation
}

func (c *connection) removeOperation(correlationId uuid.UUID) {
	c.opMutex.Lock()
	delete(c.operations, correlationId)
	c.opMutex.Unlock()
}

func (c *connection) resubscribe() {
	c.opMutex.RLock()
	for _, op := range c.operations {
		c.enqueueOperation(op, false)
	}
	c.opMutex.RUnlock()
}

func (c *connection) clearOperations() {
	c.opMutex.RLock()
	for id, op := range c.operations {
		op.Fail(errors.New("Connection closed"))
		delete(c.operations, id)
	}
	c.opMutex.RUnlock()
}

// Copied from http2\server
func isClosedConnError(err error) bool {
	if err == nil {
		return false
	}

	// TODO: remove this string search and be more like the Windows
	// case below. That might involve modifying the standard library
	// to return better error types.
	str := err.Error()
	if strings.Contains(str, "use of closed network connection") {
		return true
	}

	// TODO(bradfitz): x/tools/cmd/bundle doesn't really support
	// build tags, so I can't make an http2_windows.go file with
	// Windows-specific stuff. Fix that and move this, once we
	// have a way to bundle this into std's net/http somehow.
	if runtime.GOOS == "windows" {
		if oe, ok := err.(*net.OpError); ok && oe.Op == "read" {
			if se, ok := oe.Err.(*os.SyscallError); ok && se.Syscall == "wsarecv" {
				const WSAECONNABORTED = 10053
				const WSAECONNRESET = 10054
				if n := errno(se.Err); n == WSAECONNRESET || n == WSAECONNABORTED {
					return true
				}
			}
		}
	}
	return false
}

func errno(v error) uintptr {
	if rv := reflect.ValueOf(v); rv.Kind() == reflect.Uintptr {
		return uintptr(rv.Uint())
	}
	return 0
}
<commit_msg>Change some debug level<commit_after>package gesclient

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/satori/go.uuid"
	"io"
	"net"
	"os"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

type Connection interface {
	WaitForConnection()
	Close() error
	AppendToStream(stream string, expectedVersion int, events []*EventData,
		userCredentials *UserCredentials) (*WriteResult, error)
	AppendToStreamAsync(stream string, expectedVersion int, events []*EventData,
		userCredentials *UserCredentials) (<-chan *WriteResult, error)
	ReadStreamEventsForward(stream string, start int, max int,
		userCredentials *UserCredentials) (*StreamEventsSlice, error)
	ReadStreamEventsForwardAsync(stream string, start int, max int,
		userCredentials *UserCredentials) (<-chan *StreamEventsSlice, error)
	SubscribeToStream(stream string, userCredentials *UserCredentials) (Subscription, error)
	SubscribeToStreamAsync(stream string, userCredentials *UserCredentials) (<-chan Subscription, error)
}

type connection struct {
	address     string
	opMutex     sync.RWMutex
	operations  map[uuid.UUID]operation
	leftOver    []byte
	reconnect   *int32
	connected   *int32
	output      chan *tcpPacket
	conn        net.Conn
	readerEnded chan struct{}
	writerEnded chan struct{}
}

func NewConnection(addr string) Connection {
	reconnect := int32(1)
	connected := int32(0)
	c := &connection{
		address:     addr,
		operations:  make(map[uuid.UUID]operation),
		output:      make(chan *tcpPacket, 100),
		reconnect:   &reconnect,
		connected:   &connected,
		readerEnded: make(chan struct{}),
		writerEnded: make(chan struct{}),
	}
	go c.connect()
	return c
}

func (c *connection) connect() {
	var err error
	for atomic.LoadInt32(c.reconnect) == 1 {
		log.Info("Connecting to %s", c.address)
		c.conn, err = net.DialTimeout("tcp4", c.address, time.Second*3)
		if err == nil {
			go c.reader()
			go c.writer()

			atomic.StoreInt32(c.connected, 1)
			c.resubscribe()

			<-c.writerEnded

			atomic.StoreInt32(c.connected, 0)
			log.Info("Disconnected from %s", c.address)
		} else {
			log.Error("Connection failed: %v", err)
		}
		if atomic.LoadInt32(c.reconnect) == 1 {
			time.Sleep(time.Second * 3)
		}
	}
	close(c.readerEnded)
	close(c.writerEnded)
	c.clearOperations()
}

func (c *connection) reader() {
	log.Info("Starting reader")
	for {
		buff := make([]byte, 4096)
		n, err := c.conn.Read(buff)
		if err == nil {
			c.onData(buff[:n])
		} else if err == io.EOF {
			break
		} else {
			if isClosedConnError(err) {
				break
			}
			log.Error("conn.Read: %v", err)
		}
	}
	c.readerEnded <- struct{}{}
	log.Info("Reader ended")
}

func (c *connection) writer() {
	log.Info("Starting writer")
	var packet *tcpPacket
	run := true
	for run {
		if packet == nil {
			select {
			case packet = <-c.output:
			case <-c.readerEnded:
				run = false
				continue
			default:
				continue
			}
		}
		if err := c.writeToConnection(c.conn, packet); err == nil {
			packet = nil
		} else if err == io.EOF {
			break
		}
	}
	c.writerEnded <- struct{}{}
	log.Info("Writer ended")
}

func (c *connection) writeToConnection(conn net.Conn, packet *tcpPacket) error {
	if err := binary.Write(conn, binary.LittleEndian, packet.Size()); err != nil {
		log.Error("binary.Write failed: %v", err)
		return err
	}
	if _, err := conn.Write(packet.Bytes()); err != nil {
		log.Error("net.Conn.Write failed: %v", err)
		return err
	}
	log.Debug("Sent Command: %s | CorrelationId: %s", packet.Command, packet.CorrelationId)
	return nil
}

func (c *connection) assertConnected() error {
	if atomic.LoadInt32(c.connected) == 0 {
		return errors.New("Not connected")
	}
	return nil
}

func (c *connection) WaitForConnection() {
	for atomic.LoadInt32(c.connected) == 0 {
		time.Sleep(10)
	}
}

func (c *connection) Close() error {
	if err := c.assertConnected(); err != nil {
		return err
	}
	atomic.StoreInt32(c.reconnect, 0)
	return c.conn.Close()
}

func (c *connection) AppendToStream(
	stream string,
	expectedVersion int,
	events []*EventData,
	userCredentials *UserCredentials,
) (*WriteResult, error) {
	ch, err := c.AppendToStreamAsync(stream, expectedVersion, events, userCredentials)
	if err != nil {
		return nil, err
	}
	return <-ch, err
}

func (c *connection) AppendToStreamAsync(
	stream string,
	expectedVersion int,
	events []*EventData,
	userCredentials *UserCredentials,
) (<-chan *WriteResult, error) {
	if err := c.assertConnected(); err != nil {
		return nil, err
	}

	res := make(chan *WriteResult)
	return res, c.enqueueOperation(
		newAppendToStreamOperation(stream, events, expectedVersion, res, userCredentials), true)
}

func (c *connection) ReadStreamEventsForward(
	stream string,
	start int,
	max int,
	userCredentials *UserCredentials,
) (*StreamEventsSlice, error) {
	ch, err := c.ReadStreamEventsForwardAsync(stream, start, max, userCredentials)
	if err != nil {
		return nil, err
	}
	return <-ch, nil
}

func (c *connection) ReadStreamEventsForwardAsync(
	stream string,
	start int,
	max int,
	userCredentials *UserCredentials,
) (<-chan *StreamEventsSlice, error) {
	if err := c.assertConnected(); err != nil {
		return nil, err
	}

	res := make(chan *StreamEventsSlice)
	return res, c.enqueueOperation(
		newReadStreamEventsForwardOperation(stream, start, max, res, userCredentials), true)
}

func (c *connection) SubscribeToStream(stream string, userCredentials *UserCredentials) (Subscription, error) {
	ch, err := c.SubscribeToStreamAsync(stream, userCredentials)
	if err != nil {
		return nil, err
	}
	return <-ch, nil
}

func (c *connection) SubscribeToStreamAsync(stream string, userCredentials *UserCredentials) (<-chan Subscription, error) {
	if err := c.assertConnected(); err != nil {
		return nil, err
	}

	res := make(chan Subscription)
	return res, c.enqueueOperation(newSubscribeToStreamOperation(stream, res, c, userCredentials), true)
}

func (c *connection) enqueueOperation(op operation, isNew bool) error {
	payload, err := proto.Marshal(op.GetRequestMessage())
	if err != nil {
		log.Error("Sending command failed: %v", err)
		op.Fail(fmt.Errorf("Sending command failed: %v", err))
		return err
	}

	correlationId := op.GetCorrelationId()
	userCredentials := op.UserCredentials()
	var authFlag byte = 0
	if userCredentials != nil {
		authFlag = 1
	}
	c.output <- newTcpPacket(
		op.GetRequestCommand(),
		authFlag,
		correlationId,
		payload,
		userCredentials,
	)

	if isNew {
		c.addOperation(correlationId, op)
	}

	return nil
}

func (c *connection) onData(data []byte) {
	if c.leftOver != nil && len(c.leftOver) > 0 {
		data = append(c.leftOver, data...)
		c.leftOver = nil
	}

	dataLength := int32(len(data))
	if dataLength < tcpPacketContentLengthSize {
		c.leftOver = data
		return
	}
	var contentLength int32
	var buf = bytes.NewBuffer(data)
	binary.Read(buf, binary.LittleEndian, &contentLength)

	packetSize := contentLength + tcpPacketContentLengthSize
	if dataLength == packetSize {
		go c.process(tcpPacketFromBytes(data[tcpPacketContentLengthSize:]))
	} else if dataLength > packetSize {
		c.onData(data[:packetSize])
		c.onData(data[packetSize:])
	} else {
		c.leftOver = data
	}
}

func (c *connection) process(p *tcpPacket) {
	log.Debug("Received Command: %s | CorrelationId: %s", p.Command, p.CorrelationId)

	operation := c.getOperation(p.CorrelationId)

	if operation != nil {
		operation.ParseResponse(p)
		if operation.IsCompleted() {
			c.removeOperation(p.CorrelationId)
		} else if operation.Retry() {
			c.enqueueOperation(operation, false)
		}
		return
	}

	switch p.Command {
	case tcpCommand_HeartbeatRequestCommand:
		c.output <- newTcpPacket(tcpCommand_HeartbeatResponseCommand, 0, p.CorrelationId, nil, nil)
	default:
		log.Error("Command not supported")
	}
}

func (c *connection) addOperation(correlationId uuid.UUID, operation operation) {
	c.opMutex.Lock()
	c.operations[correlationId] = operation
	c.opMutex.Unlock()
}

func (c *connection) getOperation(correlationId uuid.UUID) operation {
	c.opMutex.RLock()
	operation := c.operations[correlationId]
	c.opMutex.RUnlock()
	return operation
}

func (c *connection) removeOperation(correlationId uuid.UUID) {
	c.opMutex.Lock()
	delete(c.operations, correlationId)
	c.opMutex.Unlock()
}

func (c *connection) resubscribe() {
	c.opMutex.RLock()
	for _, op := range c.operations {
		c.enqueueOperation(op, false)
	}
	c.opMutex.RUnlock()
}

func (c *connection) clearOperations() {
	c.opMutex.RLock()
	for id, op := range c.operations {
		op.Fail(errors.New("Connection closed"))
		delete(c.operations, id)
	}
	c.opMutex.RUnlock()
}

// Copied from http2\server
func isClosedConnError(err error) bool {
	if err == nil {
		return false
	}

	// TODO: remove this string search and be more like the Windows
	// case below. That might involve modifying the standard library
	// to return better error types.
	str := err.Error()
	if strings.Contains(str, "use of closed network connection") {
		return true
	}

	// TODO(bradfitz): x/tools/cmd/bundle doesn't really support
	// build tags, so I can't make an http2_windows.go file with
	// Windows-specific stuff. Fix that and move this, once we
	// have a way to bundle this into std's net/http somehow.
	if runtime.GOOS == "windows" {
		if oe, ok := err.(*net.OpError); ok && oe.Op == "read" {
			if se, ok := oe.Err.(*os.SyscallError); ok && se.Syscall == "wsarecv" {
				const WSAECONNABORTED = 10053
				const WSAECONNRESET = 10054
				if n := errno(se.Err); n == WSAECONNRESET || n == WSAECONNABORTED {
					return true
				}
			}
		}
	}
	return false
}

func errno(v error) uintptr {
	if rv := reflect.ValueOf(v); rv.Kind() == reflect.Uintptr {
		return uintptr(rv.Uint())
	}
	return 0
}
<|endoftext|>