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

package cmd

import (
	"context"
	"sort"
	"sync"

	"github.com/minio/minio/cmd/logger"
	"github.com/minio/minio/pkg/bpool"
)

// XL constants.
const (
	// XL metadata file carries per object metadata.
	xlMetaJSONFile = "xl.json"
)

// OfflineDisk represents an unavailable disk.
var OfflineDisk StorageAPI // zero value is nil

// xlObjects - Implements XL object layer.
type xlObjects struct {
	// name space mutex for object layer.
	nsMutex *nsLockMap

	// getDisks returns list of storageAPIs.
	getDisks func() []StorageAPI

	// Byte pools used for temporary i/o buffers.
	bp *bpool.BytePoolCap

	// TODO: Deprecated only kept here for tests, should be removed in future.
	storageDisks []StorageAPI

	// TODO: ListObjects pool management, should be removed in future.
	listPool *TreeWalkPool
}

// Shutdown function for object storage interface.
func (xl xlObjects) Shutdown(ctx context.Context) error {
	// Add any object layer shutdown activities here.
	closeStorageDisks(xl.getDisks())
	return nil
}

// byDiskTotal is a collection satisfying sort.Interface.
type byDiskTotal []DiskInfo

func (d byDiskTotal) Len() int      { return len(d) }
func (d byDiskTotal) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
func (d byDiskTotal) Less(i, j int) bool {
	return d[i].Total < d[j].Total
}

// getDisksInfo - fetch disks info across all other storage API.
func getDisksInfo(disks []StorageAPI) (disksInfo []DiskInfo, onlineDisks int, offlineDisks int) {
	disksInfo = make([]DiskInfo, len(disks))
	var wg sync.WaitGroup
	for i, storageDisk := range disks {
		if storageDisk == nil {
			// Storage disk is empty, perhaps ignored disk or not available.
			offlineDisks++
			continue
		}
		wg.Add(1)
		go func(id int, sDisk StorageAPI) {
			defer wg.Done()
			info, err := sDisk.DiskInfo()
			if err != nil {
				reqInfo := (&logger.ReqInfo{}).AppendTags("disk", sDisk.String())
				ctx := logger.SetReqInfo(context.Background(), reqInfo)
				logger.LogIf(ctx, err)
				if IsErr(err, baseErrs...) {
					offlineDisks++
					return
				}
			}
			onlineDisks++
			disksInfo[id] = info
		}(i, storageDisk)
	}
	// Wait for the routines.
	wg.Wait()
	// Success.
	return disksInfo, onlineDisks, offlineDisks
}

// returns sorted disksInfo slice which has only valid entries.
// i.e the entries where the total size of the disk is not stated
// as 0Bytes, this means that the disk is not online or ignored.
func sortValidDisksInfo(disksInfo []DiskInfo) []DiskInfo {
	var validDisksInfo []DiskInfo
	for _, diskInfo := range disksInfo {
		if diskInfo.Total == 0 {
			continue
		}
		validDisksInfo = append(validDisksInfo, diskInfo)
	}
	sort.Sort(byDiskTotal(validDisksInfo))
	return validDisksInfo
}

// Get an aggregated storage info across all disks.
func getStorageInfo(disks []StorageAPI) StorageInfo {
	disksInfo, onlineDisks, offlineDisks := getDisksInfo(disks)

	// Sort so that the first element is the smallest.
	validDisksInfo := sortValidDisksInfo(disksInfo)
	// If there are no valid disks, set total and free disks to 0
	if len(validDisksInfo) == 0 {
		return StorageInfo{}
	}

	// Combine all disks to get total usage
	var used, total, available uint64
	for _, di := range validDisksInfo {
		used = used + di.Used
		total = total + di.Total
		available = available + di.Free
	}

	_, sscParity := getRedundancyCount(standardStorageClass, len(disks))
	_, rrscparity := getRedundancyCount(reducedRedundancyStorageClass, len(disks))

	storageInfo := StorageInfo{
		Used:      used,
		Total:     total,
		Available: available,
	}

	storageInfo.Backend.Type = BackendErasure
	storageInfo.Backend.OnlineDisks = onlineDisks
	storageInfo.Backend.OfflineDisks = offlineDisks

	storageInfo.Backend.StandardSCParity = sscParity
	storageInfo.Backend.RRSCParity = rrscparity

	return storageInfo
}

// StorageInfo - returns underlying storage statistics.
func (xl xlObjects) StorageInfo(ctx context.Context) StorageInfo {
	return getStorageInfo(xl.getDisks())
}
<commit_msg>Avoid data-race in getDisksInfo call (#8126)<commit_after>/*
 * MinIO Cloud Storage, (C) 2016, 2017, 2018 MinIO, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"context"
	"sort"
	"sync"

	"github.com/minio/minio/cmd/logger"
	"github.com/minio/minio/pkg/bpool"
)

// XL constants.
const (
	// XL metadata file carries per object metadata.
	xlMetaJSONFile = "xl.json"
)

// OfflineDisk represents an unavailable disk.
var OfflineDisk StorageAPI // zero value is nil

// xlObjects - Implements XL object layer.
type xlObjects struct {
	// name space mutex for object layer.
	nsMutex *nsLockMap

	// getDisks returns list of storageAPIs.
	getDisks func() []StorageAPI

	// Byte pools used for temporary i/o buffers.
	bp *bpool.BytePoolCap

	// TODO: Deprecated only kept here for tests, should be removed in future.
	storageDisks []StorageAPI

	// TODO: ListObjects pool management, should be removed in future.
	listPool *TreeWalkPool
}

// Shutdown function for object storage interface.
func (xl xlObjects) Shutdown(ctx context.Context) error {
	// Add any object layer shutdown activities here.
	closeStorageDisks(xl.getDisks())
	return nil
}

// byDiskTotal is a collection satisfying sort.Interface.
type byDiskTotal []DiskInfo

func (d byDiskTotal) Len() int      { return len(d) }
func (d byDiskTotal) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
func (d byDiskTotal) Less(i, j int) bool {
	return d[i].Total < d[j].Total
}

// getDisksInfo - fetch disks info across all other storage API.
func getDisksInfo(disks []StorageAPI) (disksInfo []DiskInfo, onlineDisks int, offlineDisks int) {
	disksInfo = make([]DiskInfo, len(disks))
	errs := make([]error, len(disks))
	var wg sync.WaitGroup
	for i, storageDisk := range disks {
		if storageDisk == nil {
			// Storage disk is empty, perhaps ignored disk or not available.
			errs[i] = errDiskNotFound
			continue
		}
		wg.Add(1)
		go func(id int, sDisk StorageAPI) {
			defer wg.Done()
			info, err := sDisk.DiskInfo()
			if err != nil {
				reqInfo := (&logger.ReqInfo{}).AppendTags("disk", sDisk.String())
				ctx := logger.SetReqInfo(context.Background(), reqInfo)
				logger.LogIf(ctx, err)
				if IsErr(err, baseErrs...) {
					errs[id] = err
					return
				}
			}
			disksInfo[id] = info
		}(i, storageDisk)
	}
	// Wait for the routines.
	wg.Wait()

	for _, err := range errs {
		if err != nil {
			offlineDisks++
		}
		onlineDisks++
	}

	// Success.
	return disksInfo, onlineDisks, offlineDisks
}

// returns sorted disksInfo slice which has only valid entries.
// i.e the entries where the total size of the disk is not stated
// as 0Bytes, this means that the disk is not online or ignored.
func sortValidDisksInfo(disksInfo []DiskInfo) []DiskInfo {
	var validDisksInfo []DiskInfo
	for _, diskInfo := range disksInfo {
		if diskInfo.Total == 0 {
			continue
		}
		validDisksInfo = append(validDisksInfo, diskInfo)
	}
	sort.Sort(byDiskTotal(validDisksInfo))
	return validDisksInfo
}

// Get an aggregated storage info across all disks.
func getStorageInfo(disks []StorageAPI) StorageInfo {
	disksInfo, onlineDisks, offlineDisks := getDisksInfo(disks)

	// Sort so that the first element is the smallest.
	validDisksInfo := sortValidDisksInfo(disksInfo)
	// If there are no valid disks, set total and free disks to 0
	if len(validDisksInfo) == 0 {
		return StorageInfo{}
	}

	// Combine all disks to get total usage
	var used, total, available uint64
	for _, di := range validDisksInfo {
		used = used + di.Used
		total = total + di.Total
		available = available + di.Free
	}

	_, sscParity := getRedundancyCount(standardStorageClass, len(disks))
	_, rrscparity := getRedundancyCount(reducedRedundancyStorageClass, len(disks))

	storageInfo := StorageInfo{
		Used:      used,
		Total:     total,
		Available: available,
	}

	storageInfo.Backend.Type = BackendErasure
	storageInfo.Backend.OnlineDisks = onlineDisks
	storageInfo.Backend.OfflineDisks = offlineDisks

	storageInfo.Backend.StandardSCParity = sscParity
	storageInfo.Backend.RRSCParity = rrscparity

	return storageInfo
}

// StorageInfo - returns underlying storage statistics.
func (xl xlObjects) StorageInfo(ctx context.Context) StorageInfo {
	return getStorageInfo(xl.getDisks())
}
<|endoftext|>


<commit_before>package socketio

import (
	"strconv"
)

type IOMessage struct {
	Type     int
	Id       int
	Endpoint *Endpoint
	Data     string
}

func (m IOMessage) Marshall() string {
	raw := strconv.Itoa(m.Type)

	raw += ":"
	if m.Id != 0 {
		raw += strconv.Itoa(m.Id)
	}

	raw += ":"
	if m.Endpoint != nil {
		raw += m.Endpoint.String()
	}

	if m.Data != "" {
		raw += ":" + m.Data
	}

	return raw
}

func Unmarshall(raw string) *IOMessage {
	return &IOMessage{Type: 0}
}

// type Disconnect IOMessage
// type Connect IOMessage
// type Heartbeat IOMessage
// type Message IOMessage
// type JSON IOMessage
// type Event IOMessage
// type ACK IOMessage
// type Error IOMessage
// type Noop IOMessage

func NewDisconnect() *IOMessage {
	return &IOMessage{Type: 0}
}

func NewConnect(endpoint *Endpoint) *IOMessage {
	return &IOMessage{Type: 1, Endpoint: endpoint}
}

func NewHeartbeat() *IOMessage {
	return &IOMessage{Type: 2}
}

func NewMessage(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 3, Endpoint: endpoint, Data: data}
}

func NewJSONMessage(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 4, Endpoint: endpoint, Data: data}
}

func NewEvent(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 5, Endpoint: endpoint, Data: data}
}

func NewACK(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 6, Endpoint: endpoint, Data: data}
}

func NewError(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 7, Endpoint: endpoint, Data: data}
}

func NewNoop() *IOMessage {
	return &IOMessage{Type: 8}
}
<commit_msg>Implement NewIOMessage()<commit_after>package socketio

import (
	"errors"
	"strconv"
	"strings"
)

type IOMessage struct {
	Type     int
	Id       int
	Endpoint *Endpoint
	Data     string
}

func NewIOMessage(rawMsg string) (*IOMessage, error) {
	if len(rawMsg) == 0 {
		return nil, errors.New("Empty message")
	}

	msgType, err := strconv.Atoi(string(rawMsg[0]))
	if err != nil {
		return nil, err
	}

	switch msgType {
	case 3, 4, 5:
		parts := strings.SplitN(rawMsg, ":", 4)

		id, err := strconv.Atoi(parts[1])
		if err != nil {
			return nil, err
		}

		return &IOMessage{msgType, id, nil, parts[3]}, nil
	default:
		return &IOMessage{Type: msgType}, nil
	}
}

func (m IOMessage) String() string {
	raw := strconv.Itoa(m.Type)

	raw += ":"
	if m.Id != 0 {
		raw += strconv.Itoa(m.Id)
	}

	raw += ":"
	if m.Endpoint != nil {
		raw += m.Endpoint.String()
	}

	if m.Data != "" {
		raw += ":" + m.Data
	}

	return raw
}

func NewDisconnect() *IOMessage {
	return &IOMessage{Type: 0}
}

func NewConnect(endpoint *Endpoint) *IOMessage {
	return &IOMessage{Type: 1, Endpoint: endpoint}
}

func NewHeartbeat() *IOMessage {
	return &IOMessage{Type: 2}
}

func NewMessage(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 3, Endpoint: endpoint, Data: data}
}

func NewJSONMessage(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 4, Endpoint: endpoint, Data: data}
}

func NewEvent(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 5, Endpoint: endpoint, Data: data}
}

func NewACK(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 6, Endpoint: endpoint, Data: data}
}

func NewError(endpoint *Endpoint, data string) *IOMessage {
	return &IOMessage{Type: 7, Endpoint: endpoint, Data: data}
}

func NewNoop() *IOMessage {
	return &IOMessage{Type: 8}
}
<|endoftext|>


<commit_before>package twitch

import (
	"strconv"
	"strings"
	"time"
)

// ADDING A NEW MESSAGE TYPE:
// 1. Add the message type at the bottom of the MessageType "const enum", with a unique index
// 2. Add a function at the bottom of file in the "parseXXXMessage" format, that parses your message type and returns a Message
// 3. Register the message into the map in the init function, specifying the message type value and the parser you just made

// MessageType different message types possible to receive via IRC
type MessageType int

const (
	// UNSET is for message types we currently don't support
	UNSET MessageType = -1
	// WHISPER private messages
	WHISPER MessageType = 0
	// PRIVMSG standard chat message
	PRIVMSG MessageType = 1
	// CLEARCHAT timeout messages
	CLEARCHAT MessageType = 2
	// ROOMSTATE changes like sub mode
	ROOMSTATE MessageType = 3
	// USERNOTICE messages like subs, resubs, raids, etc
	USERNOTICE MessageType = 4
	// USERSTATE messages
	USERSTATE MessageType = 5
	// NOTICE messages like sub mode, host on
	NOTICE MessageType = 6
	// JOIN whenever a user joins a channel
	JOIN MessageType = 7
	// PART whenever a user parts from a channel
	PART MessageType = 8
	// RECONNECT is sent from Twitch when they request the client to reconnect (i.e. for an irc server restart) https://dev.twitch.tv/docs/irc/commands/#reconnect-twitch-commands
	RECONNECT MessageType = 9
	// NAMES (or 353 https://www.alien.net.au/irc/irc2numerics.html#353) is the response sent from the server when the client requests a list of names for a channel
	NAMES MessageType = 10
	// PING is a message that can be sent from the IRC server. go-twitch-irc responds to PINGs automatically
	PING MessageType = 11
	// PONG is a message that should be sent from the IRC server as a response to us sending a PING message.
	PONG MessageType = 12
)

type messageTypeDescription struct {
	Type   MessageType
	Parser func(*ircMessage) Message
}

var messageTypeMap map[string]messageTypeDescription

func init() {
	messageTypeMap = map[string]messageTypeDescription{
		"WHISPER":    messageTypeDescription{WHISPER, parseWhisperMessage},
		"PRIVMSG":    messageTypeDescription{PRIVMSG, parsePrivateMessage},
		"CLEARCHAT":  messageTypeDescription{CLEARCHAT, parseClearChatMessage},
		"ROOMSTATE":  messageTypeDescription{ROOMSTATE, parseRoomStateMessage},
		"USERNOTICE": messageTypeDescription{USERNOTICE, parseUserNoticeMessage},
		"USERSTATE":  messageTypeDescription{USERSTATE, parseUserStateMessage},
		"NOTICE":     messageTypeDescription{NOTICE, parseNoticeMessage},
		"JOIN":       messageTypeDescription{JOIN, parseUserJoinMessage},
		"PART":       messageTypeDescription{PART, parseUserPartMessage},
		"RECONNECT":  messageTypeDescription{RECONNECT, parseReconnectMessage},
		"353":        messageTypeDescription{NAMES, parseNamesMessage},
		"PING":       messageTypeDescription{PING, parsePingMessage},
		"PONG":       messageTypeDescription{PONG, parsePongMessage},
	}
}

// Emote twitch emotes
type Emote struct {
	Name  string
	ID    string
	Count int
}

// ParseMessage parse a raw Twitch IRC message
func ParseMessage(line string) Message {
	ircMessage, err := parseIRCMessage(line)
	if err != nil {
		return parseRawMessage(ircMessage)
	}

	if mt, ok := messageTypeMap[ircMessage.Command]; ok {
		return mt.Parser(ircMessage)
	}

	return parseRawMessage(ircMessage)
}

// parseMessageType parses a message type from an irc COMMAND string
func parseMessageType(messageType string) MessageType {
	if mt, ok := messageTypeMap[messageType]; ok {
		return mt.Type
	}

	return UNSET
}

func parseUser(message *ircMessage) User {
	user := User{
		ID:          message.Tags["user-id"],
		Name:        message.Source.Username,
		DisplayName: message.Tags["display-name"],
		Color:       message.Tags["color"],
		Badges:      make(map[string]int),
	}

	if rawBadges := message.Tags["badges"]; rawBadges != "" {
		user.Badges = parseBadges(rawBadges)
	}

	// USERSTATE doesn't contain a Username, but it does have a display-name tag
	if user.Name == "" && user.DisplayName != "" {
		user.Name = strings.ToLower(user.DisplayName)
		user.Name = strings.Replace(user.Name, " ", "", 1)
	}

	return user
}

func parseBadges(rawBadges string) map[string]int {
	badges := make(map[string]int)

	for _, badge := range strings.Split(rawBadges, ",") {
		pair := strings.SplitN(badge, "/", 2)
		badges[pair[0]], _ = strconv.Atoi(pair[1])
	}

	return badges
}

func parseRawMessage(message *ircMessage) *RawMessage {
	rawMessage := RawMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
	}

	for i, v := range message.Params {
		if !strings.Contains(v, "#") {
			rawMessage.Message = strings.Join(message.Params[i:], " ")
			break
		}
	}

	return &rawMessage
}

func parseWhisperMessage(message *ircMessage) Message {
	whisperMessage := WhisperMessage{
		User: parseUser(message),

		Raw:       message.Raw,
		Type:      parseMessageType(message.Command),
		RawType:   message.Command,
		Tags:      message.Tags,
		MessageID: message.Tags["message-id"],
		ThreadID:  message.Tags["thread-id"],
	}

	if len(message.Params) == 2 {
		whisperMessage.Message = message.Params[1]
	}

	whisperMessage.Target = message.Params[0]
	whisperMessage.Emotes = parseEmotes(message.Tags["emotes"], whisperMessage.Message)

	if strings.Contains(whisperMessage.Message, "/me") {
		whisperMessage.Message = strings.TrimPrefix(whisperMessage.Message, "/me")
		whisperMessage.Action = true
	}

	return &whisperMessage
}

func parsePrivateMessage(message *ircMessage) Message {
	privateMessage := PrivateMessage{
		User: parseUser(message),

		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
		RoomID:  message.Tags["room-id"],
		ID:      message.Tags["id"],
		Time:    parseTime(message.Tags["tmi-sent-ts"]),
	}

	if len(message.Params) == 2 {
		privateMessage.Message = message.Params[1]
	}

	privateMessage.Channel = strings.TrimPrefix(message.Params[0], "#")
	privateMessage.Emotes = parseEmotes(message.Tags["emotes"], privateMessage.Message)

	rawBits, ok := message.Tags["bits"]
	if ok {
		bits, _ := strconv.Atoi(rawBits)
		privateMessage.Bits = bits
	}

	text := privateMessage.Message
	if strings.HasPrefix(text, "\u0001ACTION") && strings.HasSuffix(text, "\u0001") {
		privateMessage.Message = text[8 : len(text)-1]
		privateMessage.Action = true
	}

	return &privateMessage
}

func parseClearChatMessage(message *ircMessage) Message {
	clearChatMessage := ClearChatMessage{
		Raw:          message.Raw,
		Type:         parseMessageType(message.Command),
		RawType:      message.Command,
		Tags:         message.Tags,
		RoomID:       message.Tags["room-id"],
		Time:         parseTime(message.Tags["tmi-sent-ts"]),
		TargetUserID: message.Tags["target-user-id"],
	}

	clearChatMessage.Channel = strings.TrimPrefix(message.Params[0], "#")

	rawBanDuration, ok := message.Tags["ban-duration"]
	if ok {
		duration, _ := strconv.Atoi(rawBanDuration)
		clearChatMessage.BanDuration = duration
	}

	if len(message.Params) == 2 {
		clearChatMessage.TargetUsername = message.Params[1]
	}

	return &clearChatMessage
}

func parseRoomStateMessage(message *ircMessage) Message {
	roomStateMessage := RoomStateMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
		RoomID:  message.Tags["room-id"],
		State:   make(map[string]int),
	}

	roomStateMessage.Channel = strings.TrimPrefix(message.Params[0], "#")

	stateTags := []string{"emote-only", "followers-only", "r9k", "rituals", "slow", "subs-only"}
	for _, tag := range stateTags {
		rawValue, ok := message.Tags[tag]
		if !ok {
			continue
		}

		value, _ := strconv.Atoi(rawValue)
		roomStateMessage.State[tag] = value
	}

	return &roomStateMessage
}

func parseUserNoticeMessage(message *ircMessage) Message {
	userNoticeMessage := UserNoticeMessage{
		User: parseUser(message),

		Raw:       message.Raw,
		Type:      parseMessageType(message.Command),
		RawType:   message.Command,
		Tags:      message.Tags,
		RoomID:    message.Tags["room-id"],
		ID:        message.Tags["id"],
		Time:      parseTime(message.Tags["tmi-sent-ts"]),
		MsgID:     message.Tags["msg-id"],
		MsgParams: make(map[string]string),
		SystemMsg: message.Tags["system-msg"],
	}

	if len(message.Params) == 2 {
		userNoticeMessage.Message = message.Params[1]
	}

	userNoticeMessage.Channel = strings.TrimPrefix(message.Params[0], "#")
	userNoticeMessage.Emotes = parseEmotes(message.Tags["emotes"], userNoticeMessage.Message)

	for tag, value := range message.Tags {
		if strings.Contains(tag, "msg-param") {
			userNoticeMessage.MsgParams[tag] = value
		}
	}

	return &userNoticeMessage
}

func parseUserStateMessage(message *ircMessage) Message {
	userStateMessage := UserStateMessage{
		User: parseUser(message),

		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
	}

	userStateMessage.Channel = strings.TrimPrefix(message.Params[0], "#")

	rawEmoteSets, ok := message.Tags["emote-sets"]
	if ok {
		userStateMessage.EmoteSets = strings.Split(rawEmoteSets, ",")
	}

	return &userStateMessage
}

func parseNoticeMessage(message *ircMessage) Message {
	noticeMessage := NoticeMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
		MsgID:   message.Tags["msg-id"],
	}

	if len(message.Params) == 2 {
		noticeMessage.Message = message.Params[1]
	}

	noticeMessage.Channel = strings.TrimPrefix(message.Params[0], "#")

	return &noticeMessage
}

func parseUserJoinMessage(message *ircMessage) Message {
	parsedMessage := UserJoinMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,

		User: message.Source.Username,
	}

	if len(message.Params) == 1 {
		parsedMessage.Channel = strings.TrimPrefix(message.Params[0], "#")
	}

	return &parsedMessage
}

func parseUserPartMessage(message *ircMessage) Message {
	parsedMessage := UserPartMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,

		User: message.Source.Username,
	}

	if len(message.Params) == 1 {
		parsedMessage.Channel = strings.TrimPrefix(message.Params[0], "#")
	}

	return &parsedMessage
}

func parseReconnectMessage(message *ircMessage) Message {
	return &ReconnectMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
	}
}

func parseNamesMessage(message *ircMessage) Message {
	parsedMessage := NamesMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
	}

	if len(message.Params) == 4 {
		parsedMessage.Channel = strings.TrimPrefix(message.Params[2], "#")
		parsedMessage.Users = strings.Split(message.Params[3], " ")
	}

	return &parsedMessage
}

func parsePingMessage(message *ircMessage) Message {
	parsedMessage := PingMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
	}

	if len(message.Params) == 1 {
		parsedMessage.Message = strings.Split(message.Params[0], " ")[0]
	}

	return &parsedMessage
}

func parsePongMessage(message *ircMessage) Message {
	parsedMessage := PongMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
	}

	if len(message.Params) == 2 {
		parsedMessage.Message = strings.Split(message.Params[1], " ")[0]
	}

	return &parsedMessage
}

func parseTime(rawTime string) time.Time {
	if rawTime == "" {
		return time.Time{}
	}

	time64, _ := strconv.ParseInt(rawTime, 10, 64)
	return time.Unix(0, int64(time64*1e6))
}

func parseEmotes(rawEmotes, message string) []*Emote {
	var emotes []*Emote

	if rawEmotes == "" {
		return emotes
	}

	runes := []rune(message)

	for _, v := range strings.Split(rawEmotes, "/") {
		split := strings.SplitN(v, ":", 2)
		pairs := strings.SplitN(split[1], ",", 2)
		pair := strings.SplitN(pairs[0], "-", 2)

		firstIndex, _ := strconv.Atoi(pair[0])
		lastIndex, _ := strconv.Atoi(pair[1])

		emote := &Emote{
			Name:  string(runes[firstIndex : lastIndex+1]),
			ID:    split[0],
			Count: strings.Count(split[1], ",") + 1,
		}

		emotes = append(emotes, emote)
	}

	return emotes
}
<commit_msg>Add recoverMessage function<commit_after>package twitch

import (
	"log"
	"runtime/debug"
	"strconv"
	"strings"
	"time"
)

// ADDING A NEW MESSAGE TYPE:
// 1. Add the message type at the bottom of the MessageType "const enum", with a unique index
// 2. Add a function at the bottom of file in the "parseXXXMessage" format, that parses your message type and returns a Message
// 3. Register the message into the map in the init function, specifying the message type value and the parser you just made

// MessageType different message types possible to receive via IRC
type MessageType int

const (
	// UNSET is for message types we currently don't support
	UNSET MessageType = -1
	// WHISPER private messages
	WHISPER MessageType = 0
	// PRIVMSG standard chat message
	PRIVMSG MessageType = 1
	// CLEARCHAT timeout messages
	CLEARCHAT MessageType = 2
	// ROOMSTATE changes like sub mode
	ROOMSTATE MessageType = 3
	// USERNOTICE messages like subs, resubs, raids, etc
	USERNOTICE MessageType = 4
	// USERSTATE messages
	USERSTATE MessageType = 5
	// NOTICE messages like sub mode, host on
	NOTICE MessageType = 6
	// JOIN whenever a user joins a channel
	JOIN MessageType = 7
	// PART whenever a user parts from a channel
	PART MessageType = 8
	// RECONNECT is sent from Twitch when they request the client to reconnect (i.e. for an irc server restart) https://dev.twitch.tv/docs/irc/commands/#reconnect-twitch-commands
	RECONNECT MessageType = 9
	// NAMES (or 353 https://www.alien.net.au/irc/irc2numerics.html#353) is the response sent from the server when the client requests a list of names for a channel
	NAMES MessageType = 10
	// PING is a message that can be sent from the IRC server. go-twitch-irc responds to PINGs automatically
	PING MessageType = 11
	// PONG is a message that should be sent from the IRC server as a response to us sending a PING message.
	PONG MessageType = 12
)

type messageTypeDescription struct {
	Type   MessageType
	Parser func(*ircMessage) Message
}

var messageTypeMap map[string]messageTypeDescription

func init() {
	messageTypeMap = map[string]messageTypeDescription{
		"WHISPER":    messageTypeDescription{WHISPER, parseWhisperMessage},
		"PRIVMSG":    messageTypeDescription{PRIVMSG, parsePrivateMessage},
		"CLEARCHAT":  messageTypeDescription{CLEARCHAT, parseClearChatMessage},
		"ROOMSTATE":  messageTypeDescription{ROOMSTATE, parseRoomStateMessage},
		"USERNOTICE": messageTypeDescription{USERNOTICE, parseUserNoticeMessage},
		"USERSTATE":  messageTypeDescription{USERSTATE, parseUserStateMessage},
		"NOTICE":     messageTypeDescription{NOTICE, parseNoticeMessage},
		"JOIN":       messageTypeDescription{JOIN, parseUserJoinMessage},
		"PART":       messageTypeDescription{PART, parseUserPartMessage},
		"RECONNECT":  messageTypeDescription{RECONNECT, parseReconnectMessage},
		"353":        messageTypeDescription{NAMES, parseNamesMessage},
		"PING":       messageTypeDescription{PING, parsePingMessage},
		"PONG":       messageTypeDescription{PONG, parsePongMessage},
	}
}

// Emote twitch emotes
type Emote struct {
	Name  string
	ID    string
	Count int
}

// ParseMessage parse a raw Twitch IRC message
func ParseMessage(line string) Message {
	defer recoverMessage(line)

	ircMessage, err := parseIRCMessage(line)
	if err != nil {
		return parseRawMessage(ircMessage)
	}

	if mt, ok := messageTypeMap[ircMessage.Command]; ok {
		return mt.Parser(ircMessage)
	}

	return parseRawMessage(ircMessage)
}

func recoverMessage(line string) {
	if err := recover(); err != nil {
		log.Println(line)
		log.Println(err)
		log.Println(string(debug.Stack()))
	}
}

// parseMessageType parses a message type from an irc COMMAND string
func parseMessageType(messageType string) MessageType {
	if mt, ok := messageTypeMap[messageType]; ok {
		return mt.Type
	}

	return UNSET
}

func parseUser(message *ircMessage) User {
	user := User{
		ID:          message.Tags["user-id"],
		Name:        message.Source.Username,
		DisplayName: message.Tags["display-name"],
		Color:       message.Tags["color"],
		Badges:      make(map[string]int),
	}

	if rawBadges := message.Tags["badges"]; rawBadges != "" {
		user.Badges = parseBadges(rawBadges)
	}

	// USERSTATE doesn't contain a Username, but it does have a display-name tag
	if user.Name == "" && user.DisplayName != "" {
		user.Name = strings.ToLower(user.DisplayName)
		user.Name = strings.Replace(user.Name, " ", "", 1)
	}

	return user
}

func parseBadges(rawBadges string) map[string]int {
	badges := make(map[string]int)

	for _, badge := range strings.Split(rawBadges, ",") {
		pair := strings.SplitN(badge, "/", 2)
		badges[pair[0]], _ = strconv.Atoi(pair[1])
	}

	return badges
}

func parseRawMessage(message *ircMessage) *RawMessage {
	rawMessage := RawMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
	}

	for i, v := range message.Params {
		if !strings.Contains(v, "#") {
			rawMessage.Message = strings.Join(message.Params[i:], " ")
			break
		}
	}

	return &rawMessage
}

func parseWhisperMessage(message *ircMessage) Message {
	whisperMessage := WhisperMessage{
		User: parseUser(message),

		Raw:       message.Raw,
		Type:      parseMessageType(message.Command),
		RawType:   message.Command,
		Tags:      message.Tags,
		MessageID: message.Tags["message-id"],
		ThreadID:  message.Tags["thread-id"],
	}

	if len(message.Params) == 2 {
		whisperMessage.Message = message.Params[1]
	}

	whisperMessage.Target = message.Params[0]
	whisperMessage.Emotes = parseEmotes(message.Tags["emotes"], whisperMessage.Message)

	if strings.Contains(whisperMessage.Message, "/me") {
		whisperMessage.Message = strings.TrimPrefix(whisperMessage.Message, "/me")
		whisperMessage.Action = true
	}

	return &whisperMessage
}

func parsePrivateMessage(message *ircMessage) Message {
	privateMessage := PrivateMessage{
		User: parseUser(message),

		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
		RoomID:  message.Tags["room-id"],
		ID:      message.Tags["id"],
		Time:    parseTime(message.Tags["tmi-sent-ts"]),
	}

	if len(message.Params) == 2 {
		privateMessage.Message = message.Params[1]
	}

	privateMessage.Channel = strings.TrimPrefix(message.Params[0], "#")
	privateMessage.Emotes = parseEmotes(message.Tags["emotes"], privateMessage.Message)

	rawBits, ok := message.Tags["bits"]
	if ok {
		bits, _ := strconv.Atoi(rawBits)
		privateMessage.Bits = bits
	}

	text := privateMessage.Message
	if strings.HasPrefix(text, "\u0001ACTION") && strings.HasSuffix(text, "\u0001") {
		privateMessage.Message = text[8 : len(text)-1]
		privateMessage.Action = true
	}

	return &privateMessage
}

func parseClearChatMessage(message *ircMessage) Message {
	clearChatMessage := ClearChatMessage{
		Raw:          message.Raw,
		Type:         parseMessageType(message.Command),
		RawType:      message.Command,
		Tags:         message.Tags,
		RoomID:       message.Tags["room-id"],
		Time:         parseTime(message.Tags["tmi-sent-ts"]),
		TargetUserID: message.Tags["target-user-id"],
	}

	clearChatMessage.Channel = strings.TrimPrefix(message.Params[0], "#")

	rawBanDuration, ok := message.Tags["ban-duration"]
	if ok {
		duration, _ := strconv.Atoi(rawBanDuration)
		clearChatMessage.BanDuration = duration
	}

	if len(message.Params) == 2 {
		clearChatMessage.TargetUsername = message.Params[1]
	}

	return &clearChatMessage
}

func parseRoomStateMessage(message *ircMessage) Message {
	roomStateMessage := RoomStateMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
		RoomID:  message.Tags["room-id"],
		State:   make(map[string]int),
	}

	roomStateMessage.Channel = strings.TrimPrefix(message.Params[0], "#")

	stateTags := []string{"emote-only", "followers-only", "r9k", "rituals", "slow", "subs-only"}
	for _, tag := range stateTags {
		rawValue, ok := message.Tags[tag]
		if !ok {
			continue
		}

		value, _ := strconv.Atoi(rawValue)
		roomStateMessage.State[tag] = value
	}

	return &roomStateMessage
}

func parseUserNoticeMessage(message *ircMessage) Message {
	userNoticeMessage := UserNoticeMessage{
		User: parseUser(message),

		Raw:       message.Raw,
		Type:      parseMessageType(message.Command),
		RawType:   message.Command,
		Tags:      message.Tags,
		RoomID:    message.Tags["room-id"],
		ID:        message.Tags["id"],
		Time:      parseTime(message.Tags["tmi-sent-ts"]),
		MsgID:     message.Tags["msg-id"],
		MsgParams: make(map[string]string),
		SystemMsg: message.Tags["system-msg"],
	}

	if len(message.Params) == 2 {
		userNoticeMessage.Message = message.Params[1]
	}

	userNoticeMessage.Channel = strings.TrimPrefix(message.Params[0], "#")
	userNoticeMessage.Emotes = parseEmotes(message.Tags["emotes"], userNoticeMessage.Message)

	for tag, value := range message.Tags {
		if strings.Contains(tag, "msg-param") {
			userNoticeMessage.MsgParams[tag] = value
		}
	}

	return &userNoticeMessage
}

func parseUserStateMessage(message *ircMessage) Message {
	userStateMessage := UserStateMessage{
		User: parseUser(message),

		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
	}

	userStateMessage.Channel = strings.TrimPrefix(message.Params[0], "#")

	rawEmoteSets, ok := message.Tags["emote-sets"]
	if ok {
		userStateMessage.EmoteSets = strings.Split(rawEmoteSets, ",")
	}

	return &userStateMessage
}

func parseNoticeMessage(message *ircMessage) Message {
	noticeMessage := NoticeMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
		Tags:    message.Tags,
		MsgID:   message.Tags["msg-id"],
	}

	if len(message.Params) == 2 {
		noticeMessage.Message = message.Params[1]
	}

	noticeMessage.Channel = strings.TrimPrefix(message.Params[0], "#")

	return &noticeMessage
}

func parseUserJoinMessage(message *ircMessage) Message {
	parsedMessage := UserJoinMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,

		User: message.Source.Username,
	}

	if len(message.Params) == 1 {
		parsedMessage.Channel = strings.TrimPrefix(message.Params[0], "#")
	}

	return &parsedMessage
}

func parseUserPartMessage(message *ircMessage) Message {
	parsedMessage := UserPartMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,

		User: message.Source.Username,
	}

	if len(message.Params) == 1 {
		parsedMessage.Channel = strings.TrimPrefix(message.Params[0], "#")
	}

	return &parsedMessage
}

func parseReconnectMessage(message *ircMessage) Message {
	return &ReconnectMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
	}
}

func parseNamesMessage(message *ircMessage) Message {
	parsedMessage := NamesMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
	}

	if len(message.Params) == 4 {
		parsedMessage.Channel = strings.TrimPrefix(message.Params[2], "#")
		parsedMessage.Users = strings.Split(message.Params[3], " ")
	}

	return &parsedMessage
}

func parsePingMessage(message *ircMessage) Message {
	parsedMessage := PingMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
	}

	if len(message.Params) == 1 {
		parsedMessage.Message = strings.Split(message.Params[0], " ")[0]
	}

	return &parsedMessage
}

func parsePongMessage(message *ircMessage) Message {
	parsedMessage := PongMessage{
		Raw:     message.Raw,
		Type:    parseMessageType(message.Command),
		RawType: message.Command,
	}

	if len(message.Params) == 2 {
		parsedMessage.Message = strings.Split(message.Params[1], " ")[0]
	}

	return &parsedMessage
}

func parseTime(rawTime string) time.Time {
	if rawTime == "" {
		return time.Time{}
	}

	time64, _ := strconv.ParseInt(rawTime, 10, 64)
	return time.Unix(0, int64(time64*1e6))
}

func parseEmotes(rawEmotes, message string) []*Emote {
	var emotes []*Emote

	if rawEmotes == "" {
		return emotes
	}

	runes := []rune(message)

	for _, v := range strings.Split(rawEmotes, "/") {
		split := strings.SplitN(v, ":", 2)
		pairs := strings.SplitN(split[1], ",", 2)
		pair := strings.SplitN(pairs[0], "-", 2)

		firstIndex, _ := strconv.Atoi(pair[0])
		lastIndex, _ := strconv.Atoi(pair[1])

		emote := &Emote{
			Name:  string(runes[firstIndex : lastIndex+1]),
			ID:    split[0],
			Count: strings.Count(split[1], ",") + 1,
		}

		emotes = append(emotes, emote)
	}

	return emotes
}
<|endoftext|>


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

package reservoir

import (
	"bytes"
	"github.com/bitmark-inc/bitmarkd/account"
	"github.com/bitmark-inc/bitmarkd/asset"
	"github.com/bitmark-inc/bitmarkd/background"
	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/bitmarkd/merkle"
	"github.com/bitmark-inc/bitmarkd/storage"
	"github.com/bitmark-inc/bitmarkd/transactionrecord"
	"github.com/bitmark-inc/logger"
	"sync"
	"time"
)

const (
	expiryTime    = 2 * time.Hour
	maximumIssues = 100 // allowed issues in a single submission
)

// globals
type globalDataType struct {
	sync.RWMutex
	log        *logger.L
	enabled    bool
	unverified unverifiedEntry
	verified   map[merkle.Digest][]byte        // key: tx id
	pending    map[merkle.Digest]merkle.Digest // key: link tx id

	count      counters
	expiry     expiryData
	background *background.T
}

type unverifiedEntry struct {
	entries map[PayId]*unverifiedItem
	index   map[merkle.Digest]PayId
}

type unverifiedItem struct {
	txIds        []merkle.Digest
	links        []merkle.Digest
	transactions [][]byte
	expires      time.Time
}

type counters struct {
	pending  int
	verified int
}

// expiry background
type expiryData struct {
	log *logger.L
}

// gobal storage
var globalData globalDataType

// create the cache
func Initialise() error {

	globalData.Lock()
	defer globalData.Unlock()

	globalData.log = logger.New("reservoir")
	if nil == globalData.log {
		return fault.ErrInvalidLoggerChannel
	}
	globalData.log.Info("starting…")

	globalData.unverified.entries = make(map[PayId]*unverifiedItem)
	globalData.unverified.index = make(map[merkle.Digest]PayId)
	globalData.verified = make(map[merkle.Digest][]byte)
	globalData.pending = make(map[merkle.Digest]merkle.Digest)

	globalData.enabled = true

	globalData.expiry.log = logger.New("reservoir-expiry")
	if nil == globalData.expiry.log {
		return fault.ErrInvalidLoggerChannel
	}

	// start background processes
	globalData.log.Info("start background…")

	// list of background processes to start
	var processes = background.Processes{
		&globalData.expiry,
	}

	globalData.background = background.Start(processes, &globalData)

	return nil
}

// stop all
func Finalise() {

	globalData.log.Info("shutting down…")
	globalData.log.Flush()

	globalData.enabled = false

	// stop background
	globalData.background.Stop()

	globalData.log.Info("finished")
	globalData.log.Flush()
}

// read counter
func ReadCounters() (int, int) {
	return globalData.count.pending, globalData.count.verified
}

// result returned by store issues
type IssueInfo struct {
	Id     PayId
	TxIds  []merkle.Digest
	Packed []byte
}

// store packed record(s) in the Unverified table
//
// return payment id and a duplicate flag
//
// for duplicate to be true all transactions must all match exactly to a
// previous set - this is to allow for multiple submission from client
// without receiving a duplicate transaction error
func StoreIssues(issues []*transactionrecord.BitmarkIssue) (*IssueInfo, bool, error) {

	count := len(issues)
	if count > maximumIssues {
		return nil, false, fault.ErrTooManyItemsToProcess
	} else if 0 == count {
		return nil, false, fault.ErrMissingParameters
	}

	// critical code - prevent overlapping blocks of issues
	globalData.Lock()
	defer globalData.Unlock()

	// individual packed issues
	separated := make([][]byte, 100)

	// all the tx id corresponding to separated
	txIds := make([]merkle.Digest, count)

	// this flags in already stored issues
	// used to flags an error if pay id is different
	// as this would be an overlapping block of issues
	duplicate := false

	// verify each transaction
	for i, issue := range issues {

		// validate issue record
		packedIssue, err := issue.Pack(issue.Owner)
		if nil != err {
			return nil, false, err
		}

		if !asset.Exists(issue.AssetIndex) {
			return nil, false, fault.ErrAssetNotFound
		}

		txId := packedIssue.MakeLink()

		// an unverified issue tag the block as possible duplicate
		// (if pay id matched later)
		if _, ok := globalData.unverified.index[txId]; ok {
			// if duplicate, activate pay id check
			duplicate = true
		}

		// a single verified issue fails the whole block
		if _, ok := globalData.verified[txId]; ok {
			return nil, false, fault.ErrTransactionAlreadyExists
		}
		// a single confirmed issue fails the whole block
		if storage.Pool.Transactions.Has(txId[:]) {
			return nil, false, fault.ErrTransactionAlreadyExists
		}

		// accumulate the data
		txIds[i] = txId
		separated[i] = packedIssue

	}

	// compute pay id
	payId := NewPayId(separated)

	result := &IssueInfo{
		Id:     payId,
		TxIds:  txIds,
		Packed: bytes.Join(separated, []byte{}),
	}

	// if already seen just return pay id
	if _, ok := globalData.unverified.entries[payId]; ok {
		globalData.log.Debugf("duplicate pay id: %s", payId)
		return result, true, nil
	}

	// if duplicates were detected, but duplicates were present
	// then it is an error
	if duplicate {
		globalData.log.Debugf("overlapping pay id: %s", payId)
		return nil, false, fault.ErrTransactionAlreadyExists
	}

	globalData.log.Infof("creating pay id: %s", payId)

	globalData.count.pending += len(issues)

	expiresAt := time.Now().Add(expiryTime)

	// create index entries
	for _, txId := range txIds {
		globalData.unverified.index[txId] = payId
	}

	// save transactions
	entry := &unverifiedItem{
		txIds:        txIds,
		links:        nil,
		transactions: separated,
		expires:      expiresAt,
	}
	//copy(entry.txIds, txIds)
	//copy(entry.transactions, transactions)

	globalData.unverified.entries[payId] = entry

	return result, false, nil
}

// result returned by store transfer
type TransferInfo struct {
	Id               PayId
	TxId             merkle.Digest
	Packed           []byte
	PreviousTransfer *transactionrecord.BitmarkTransfer
	OwnerData        []byte
}

// store a single transfer
func StoreTransfer(transfer *transactionrecord.BitmarkTransfer) (*TransferInfo, bool, error) {

	// critical code - prevent overlapping blocks of transactions
	globalData.Lock()
	defer globalData.Unlock()

	verifyResult, duplicate, err := verifyTransfer(transfer)
	if nil != err {
		return nil, false, err
	}

	// compute pay id
	packedTransfer := verifyResult.packedTransfer
	payId := NewPayId([][]byte{packedTransfer})

	txId := verifyResult.txId
	link := transfer.Link

	result := &TransferInfo{
		Id:               payId,
		TxId:             txId,
		Packed:           packedTransfer,
		PreviousTransfer: verifyResult.previousTransfer,
		OwnerData:        verifyResult.ownerData,
	}

	// if already seen just return pay id
	if _, ok := globalData.unverified.entries[payId]; ok {
		return result, true, nil
	}

	// if duplicates were detected, but different duplicates were present
	// then it is an error
	if duplicate {
		return nil, true, fault.ErrTransactionAlreadyExists
	}

	// one new item
	globalData.count.pending += 1

	expiresAt := time.Now().Add(expiryTime)

	// create index and pending entries
	globalData.unverified.index[txId] = payId
	if link != txId {
		globalData.pending[link] = txId
	}

	// save transactions
	entry := &unverifiedItem{
		txIds:        []merkle.Digest{txId},
		links:        []merkle.Digest{link},
		transactions: [][]byte{packedTransfer},
		expires:      expiresAt,
	}

	globalData.unverified.entries[payId] = entry

	return result, false, nil
}

// returned data from veriftyTransfer
type verifiedInfo struct {
	txId             merkle.Digest
	packedTransfer   []byte
	previousTransfer *transactionrecord.BitmarkTransfer
	ownerData        []byte
}

// verify that a transfer is ok
func verifyTransfer(arguments *transactionrecord.BitmarkTransfer) (*verifiedInfo, bool, error) {

	// find the current owner via the link
	previousPacked := storage.Pool.Transactions.Get(arguments.Link[:])
	if nil == previousPacked {
		return nil, false, fault.ErrLinkToInvalidOrUnconfirmedTransaction
	}

	previousTransaction, _, err := transactionrecord.Packed(previousPacked).Unpack()
	if nil != err {
		return nil, false, err
	}

	var currentOwner *account.Account
	var previousTransfer *transactionrecord.BitmarkTransfer

	switch tx := previousTransaction.(type) {
	case *transactionrecord.BitmarkIssue:
		currentOwner = tx.Owner

	case *transactionrecord.BitmarkTransfer:
		currentOwner = tx.Owner
		previousTransfer = tx

	default:
		return nil, false, fault.ErrLinkToInvalidOrUnconfirmedTransaction
	}

	// pack transfer and check signature
	packedTransfer, err := arguments.Pack(currentOwner)
	if nil != err {
		return nil, false, err
	}

	// transfer identifier and check for duplicate
	txId := packedTransfer.MakeLink()

	// check if this transfer was already received
	_, okP := globalData.pending[arguments.Link]
	_, okU := globalData.unverified.index[txId]
	duplicate := false
	if okU && okP {
		// if both then it is a possible duplicate
		// (depends on later pay id check)
		duplicate = true
	} else if okU || okP {
		// not an exact match - must be a double transfer
		return nil, false, fault.ErrDoubleTransferAttempt
	}

	// a single verified transfer fails the whole block
	if _, ok := globalData.verified[txId]; ok {
		return nil, false, fault.ErrTransactionAlreadyExists
	}
	// a single confirmed transfer fails the whole block
	if storage.Pool.Transactions.Has(txId[:]) {
		return nil, false, fault.ErrTransactionAlreadyExists
	}

	// log.Infof("packed transfer: %x", packedTransfer)
	// log.Infof("id: %v", txId)

	// get count for current owner record
	// to make sure that the record has not already been transferred
	dKey := append(currentOwner.Bytes(), arguments.Link[:]...)
	// log.Infof("dKey: %x", dKey)
	dCount := storage.Pool.OwnerDigest.Get(dKey)
	if nil == dCount {
		return nil, false, fault.ErrDoubleTransferAttempt
	}

	// get ownership data
	oKey := append(currentOwner.Bytes(), dCount...)
	// log.Infof("oKey: %x", oKey)
	ownerData := storage.Pool.Ownership.Get(oKey)
	if nil == ownerData {
		return nil, false, fault.ErrDoubleTransferAttempt
	}
	// log.Infof("ownerData: %x", ownerData)

	result := &verifiedInfo{
		txId:             txId,
		packedTransfer:   packedTransfer,
		previousTransfer: previousTransfer,
		ownerData:        ownerData,
	}
	return result, duplicate, nil
}

// move transaction(s) to verified cache
func SetVerified(payId PayId) {
	globalData.Lock()
	entry, ok := globalData.unverified.entries[payId]
	if ok {
		// move the record
		for i, txId := range entry.txIds {
			globalData.verified[txId] = entry.transactions[i]
			delete(globalData.unverified.index, txId)
		}
		delete(globalData.unverified.entries, payId)
		globalData.count.pending -= len(entry.txIds)
		globalData.count.verified += len(entry.txIds)
	}
	globalData.Unlock()
}

// fetch a series of verified transactions
func FetchVerified(count int) ([]merkle.Digest, []transactionrecord.Packed, int, error) {
	if count <= 0 {
		return nil, nil, 0, fault.ErrInvalidCount
	}

	txIds := make([]merkle.Digest, 0, count)
	txData := make([]transactionrecord.Packed, 0, count)

	n := 0
	totalBytes := 0
	globalData.RLock()
	if globalData.enabled {
		for txId, data := range globalData.verified {
			txIds = append(txIds, txId)
			txData = append(txData, data)
			totalBytes += len(data)
			n += 1
			if n >= count {
				break
			}
		}
	}
	globalData.RUnlock()
	return txIds, txData, totalBytes, nil
}

// lock down to prevent proofer from getting data
func Lock() {
	globalData.Lock()
	globalData.enabled = false
	globalData.Unlock()
}

// allow proofer to run again
func Unlock() {
	globalData.Lock()
	globalData.enabled = true
	globalData.Unlock()
}

// remove a record using a transaction id
func DeleteByTxId(txId merkle.Digest) {
	globalData.Lock()
	if globalData.enabled {
		fault.Panic("reservoir delete tx id when not locked")
	}
	if payId, ok := globalData.unverified.index[txId]; ok {
		internalDelete(payId)
	}
	if _, ok := globalData.verified[txId]; ok {
		delete(globalData.verified, txId)
		globalData.count.verified -= 1
	}
	globalData.Unlock()
}

// remove a record using a link id
func DeleteByLink(link merkle.Digest) {
	globalData.Lock()
	if globalData.enabled {
		fault.Panic("reservoir delete link when not locked")
	}
	if txId, ok := globalData.pending[link]; ok {
		if payId, ok := globalData.unverified.index[txId]; ok {
			internalDelete(payId)
		}
		if _, ok := globalData.verified[txId]; ok {
			delete(globalData.verified, txId)
			globalData.count.verified -= 1
		}
	}
	globalData.Unlock()
}

// hold lock before calling
// delete unverified transactions
func internalDelete(payId PayId) {
	entry, ok := globalData.unverified.entries[payId]
	if ok {
		for i, txId := range entry.txIds {
			delete(globalData.unverified.index, txId)
			delete(globalData.verified, txId)
			link := entry.links[i]
			if link != txId {
				delete(globalData.pending, link)
			}
		}
		delete(globalData.unverified.entries, payId)
	}
}
<commit_msg>[reservoir] better transaction count tracking<commit_after>// Copyright (c) 2014-2016 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package reservoir

import (
	"bytes"
	"github.com/bitmark-inc/bitmarkd/account"
	"github.com/bitmark-inc/bitmarkd/asset"
	"github.com/bitmark-inc/bitmarkd/background"
	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/bitmarkd/merkle"
	"github.com/bitmark-inc/bitmarkd/storage"
	"github.com/bitmark-inc/bitmarkd/transactionrecord"
	"github.com/bitmark-inc/logger"
	"sync"
	"time"
)

const (
	expiryTime    = 2 * time.Hour
	maximumIssues = 100 // allowed issues in a single submission
)

// globals
type globalDataType struct {
	sync.RWMutex
	log        *logger.L
	enabled    bool
	unverified unverifiedEntry
	verified   map[merkle.Digest][]byte        // key: tx id
	pending    map[merkle.Digest]merkle.Digest // key: link tx id

	expiry     expiryData
	background *background.T
}

type unverifiedEntry struct {
	entries map[PayId]*unverifiedItem
	index   map[merkle.Digest]PayId
}

type unverifiedItem struct {
	txIds        []merkle.Digest
	links        []merkle.Digest
	transactions [][]byte
	expires      time.Time
}

// expiry background
type expiryData struct {
	log *logger.L
}

// gobal storage
var globalData globalDataType

// create the cache
func Initialise() error {

	globalData.Lock()
	defer globalData.Unlock()

	globalData.log = logger.New("reservoir")
	if nil == globalData.log {
		return fault.ErrInvalidLoggerChannel
	}
	globalData.log.Info("starting…")

	globalData.unverified.entries = make(map[PayId]*unverifiedItem)
	globalData.unverified.index = make(map[merkle.Digest]PayId)
	globalData.verified = make(map[merkle.Digest][]byte)
	globalData.pending = make(map[merkle.Digest]merkle.Digest)

	globalData.enabled = true

	globalData.expiry.log = logger.New("reservoir-expiry")
	if nil == globalData.expiry.log {
		return fault.ErrInvalidLoggerChannel
	}

	// start background processes
	globalData.log.Info("start background…")

	// list of background processes to start
	var processes = background.Processes{
		&globalData.expiry,
	}

	globalData.background = background.Start(processes, &globalData)

	return nil
}

// stop all
func Finalise() {

	globalData.log.Info("shutting down…")
	globalData.log.Flush()

	globalData.enabled = false

	// stop background
	globalData.background.Stop()

	globalData.log.Info("finished")
	globalData.log.Flush()
}

// read counter
func ReadCounters() (int, int) {
	return len(globalData.unverified.index), len(globalData.verified)
}

// result returned by store issues
type IssueInfo struct {
	Id     PayId
	TxIds  []merkle.Digest
	Packed []byte
}

// store packed record(s) in the Unverified table
//
// return payment id and a duplicate flag
//
// for duplicate to be true all transactions must all match exactly to a
// previous set - this is to allow for multiple submission from client
// without receiving a duplicate transaction error
func StoreIssues(issues []*transactionrecord.BitmarkIssue) (*IssueInfo, bool, error) {

	count := len(issues)
	if count > maximumIssues {
		return nil, false, fault.ErrTooManyItemsToProcess
	} else if 0 == count {
		return nil, false, fault.ErrMissingParameters
	}

	// critical code - prevent overlapping blocks of issues
	globalData.Lock()
	defer globalData.Unlock()

	// individual packed issues
	separated := make([][]byte, 100)

	// all the tx id corresponding to separated
	txIds := make([]merkle.Digest, count)

	// this flags in already stored issues
	// used to flags an error if pay id is different
	// as this would be an overlapping block of issues
	duplicate := false

	// verify each transaction
	for i, issue := range issues {

		// validate issue record
		packedIssue, err := issue.Pack(issue.Owner)
		if nil != err {
			return nil, false, err
		}

		if !asset.Exists(issue.AssetIndex) {
			return nil, false, fault.ErrAssetNotFound
		}

		txId := packedIssue.MakeLink()

		// an unverified issue tag the block as possible duplicate
		// (if pay id matched later)
		if _, ok := globalData.unverified.index[txId]; ok {
			// if duplicate, activate pay id check
			duplicate = true
		}

		// a single verified issue fails the whole block
		if _, ok := globalData.verified[txId]; ok {
			return nil, false, fault.ErrTransactionAlreadyExists
		}
		// a single confirmed issue fails the whole block
		if storage.Pool.Transactions.Has(txId[:]) {
			return nil, false, fault.ErrTransactionAlreadyExists
		}

		// accumulate the data
		txIds[i] = txId
		separated[i] = packedIssue

	}

	// compute pay id
	payId := NewPayId(separated)

	result := &IssueInfo{
		Id:     payId,
		TxIds:  txIds,
		Packed: bytes.Join(separated, []byte{}),
	}

	// if already seen just return pay id
	if _, ok := globalData.unverified.entries[payId]; ok {
		globalData.log.Debugf("duplicate pay id: %s", payId)
		return result, true, nil
	}

	// if duplicates were detected, but duplicates were present
	// then it is an error
	if duplicate {
		globalData.log.Debugf("overlapping pay id: %s", payId)
		return nil, false, fault.ErrTransactionAlreadyExists
	}

	globalData.log.Infof("creating pay id: %s", payId)

	expiresAt := time.Now().Add(expiryTime)

	// create index entries
	for _, txId := range txIds {
		globalData.unverified.index[txId] = payId
	}

	// save transactions
	entry := &unverifiedItem{
		txIds:        txIds,
		links:        nil,
		transactions: separated,
		expires:      expiresAt,
	}
	//copy(entry.txIds, txIds)
	//copy(entry.transactions, transactions)

	globalData.unverified.entries[payId] = entry

	return result, false, nil
}

// result returned by store transfer
type TransferInfo struct {
	Id               PayId
	TxId             merkle.Digest
	Packed           []byte
	PreviousTransfer *transactionrecord.BitmarkTransfer
	OwnerData        []byte
}

// store a single transfer
func StoreTransfer(transfer *transactionrecord.BitmarkTransfer) (*TransferInfo, bool, error) {

	// critical code - prevent overlapping blocks of transactions
	globalData.Lock()
	defer globalData.Unlock()

	verifyResult, duplicate, err := verifyTransfer(transfer)
	if nil != err {
		return nil, false, err
	}

	// compute pay id
	packedTransfer := verifyResult.packedTransfer
	payId := NewPayId([][]byte{packedTransfer})

	txId := verifyResult.txId
	link := transfer.Link

	result := &TransferInfo{
		Id:               payId,
		TxId:             txId,
		Packed:           packedTransfer,
		PreviousTransfer: verifyResult.previousTransfer,
		OwnerData:        verifyResult.ownerData,
	}

	// if already seen just return pay id
	if _, ok := globalData.unverified.entries[payId]; ok {
		return result, true, nil
	}

	// if duplicates were detected, but different duplicates were present
	// then it is an error
	if duplicate {
		return nil, true, fault.ErrTransactionAlreadyExists
	}

	expiresAt := time.Now().Add(expiryTime)

	// create index and pending entries
	globalData.unverified.index[txId] = payId
	if link != txId {
		globalData.pending[link] = txId
	}

	// save transactions
	entry := &unverifiedItem{
		txIds:        []merkle.Digest{txId},
		links:        []merkle.Digest{link},
		transactions: [][]byte{packedTransfer},
		expires:      expiresAt,
	}

	globalData.unverified.entries[payId] = entry

	return result, false, nil
}

// returned data from veriftyTransfer
type verifiedInfo struct {
	txId             merkle.Digest
	packedTransfer   []byte
	previousTransfer *transactionrecord.BitmarkTransfer
	ownerData        []byte
}

// verify that a transfer is ok
func verifyTransfer(arguments *transactionrecord.BitmarkTransfer) (*verifiedInfo, bool, error) {

	// find the current owner via the link
	previousPacked := storage.Pool.Transactions.Get(arguments.Link[:])
	if nil == previousPacked {
		return nil, false, fault.ErrLinkToInvalidOrUnconfirmedTransaction
	}

	previousTransaction, _, err := transactionrecord.Packed(previousPacked).Unpack()
	if nil != err {
		return nil, false, err
	}

	var currentOwner *account.Account
	var previousTransfer *transactionrecord.BitmarkTransfer

	switch tx := previousTransaction.(type) {
	case *transactionrecord.BitmarkIssue:
		currentOwner = tx.Owner

	case *transactionrecord.BitmarkTransfer:
		currentOwner = tx.Owner
		previousTransfer = tx

	default:
		return nil, false, fault.ErrLinkToInvalidOrUnconfirmedTransaction
	}

	// pack transfer and check signature
	packedTransfer, err := arguments.Pack(currentOwner)
	if nil != err {
		return nil, false, err
	}

	// transfer identifier and check for duplicate
	txId := packedTransfer.MakeLink()

	// check if this transfer was already received
	_, okP := globalData.pending[arguments.Link]
	_, okU := globalData.unverified.index[txId]
	duplicate := false
	if okU && okP {
		// if both then it is a possible duplicate
		// (depends on later pay id check)
		duplicate = true
	} else if okU || okP {
		// not an exact match - must be a double transfer
		return nil, false, fault.ErrDoubleTransferAttempt
	}

	// a single verified transfer fails the whole block
	if _, ok := globalData.verified[txId]; ok {
		return nil, false, fault.ErrTransactionAlreadyExists
	}
	// a single confirmed transfer fails the whole block
	if storage.Pool.Transactions.Has(txId[:]) {
		return nil, false, fault.ErrTransactionAlreadyExists
	}

	// log.Infof("packed transfer: %x", packedTransfer)
	// log.Infof("id: %v", txId)

	// get count for current owner record
	// to make sure that the record has not already been transferred
	dKey := append(currentOwner.Bytes(), arguments.Link[:]...)
	// log.Infof("dKey: %x", dKey)
	dCount := storage.Pool.OwnerDigest.Get(dKey)
	if nil == dCount {
		return nil, false, fault.ErrDoubleTransferAttempt
	}

	// get ownership data
	oKey := append(currentOwner.Bytes(), dCount...)
	// log.Infof("oKey: %x", oKey)
	ownerData := storage.Pool.Ownership.Get(oKey)
	if nil == ownerData {
		return nil, false, fault.ErrDoubleTransferAttempt
	}
	// log.Infof("ownerData: %x", ownerData)

	result := &verifiedInfo{
		txId:             txId,
		packedTransfer:   packedTransfer,
		previousTransfer: previousTransfer,
		ownerData:        ownerData,
	}
	return result, duplicate, nil
}

// move transaction(s) to verified cache
func SetVerified(payId PayId) {
	globalData.Lock()
	entry, ok := globalData.unverified.entries[payId]
	if ok {
		// move the record
		for i, txId := range entry.txIds {
			globalData.verified[txId] = entry.transactions[i]
			delete(globalData.unverified.index, txId)
		}
		delete(globalData.unverified.entries, payId)
	}
	globalData.Unlock()
}

// fetch a series of verified transactions
func FetchVerified(count int) ([]merkle.Digest, []transactionrecord.Packed, int, error) {
	if count <= 0 {
		return nil, nil, 0, fault.ErrInvalidCount
	}

	txIds := make([]merkle.Digest, 0, count)
	txData := make([]transactionrecord.Packed, 0, count)

	n := 0
	totalBytes := 0
	globalData.RLock()
	if globalData.enabled {
		for txId, data := range globalData.verified {
			txIds = append(txIds, txId)
			txData = append(txData, data)
			totalBytes += len(data)
			n += 1
			if n >= count {
				break
			}
		}
	}
	globalData.RUnlock()
	return txIds, txData, totalBytes, nil
}

// lock down to prevent proofer from getting data
func Lock() {
	globalData.Lock()
	globalData.enabled = false
	globalData.Unlock()
}

// allow proofer to run again
func Unlock() {
	globalData.Lock()
	globalData.enabled = true
	globalData.Unlock()
}

// remove a record using a transaction id
func DeleteByTxId(txId merkle.Digest) {
	globalData.Lock()
	if globalData.enabled {
		fault.Panic("reservoir delete tx id when not locked")
	}
	if payId, ok := globalData.unverified.index[txId]; ok {
		internalDelete(payId)
	}
	if _, ok := globalData.verified[txId]; ok {
		delete(globalData.verified, txId)
	}
	globalData.Unlock()
}

// remove a record using a link id
func DeleteByLink(link merkle.Digest) {
	globalData.Lock()
	if globalData.enabled {
		fault.Panic("reservoir delete link when not locked")
	}
	if txId, ok := globalData.pending[link]; ok {
		if payId, ok := globalData.unverified.index[txId]; ok {
			internalDelete(payId)
		}
		if _, ok := globalData.verified[txId]; ok {
			delete(globalData.verified, txId)
		}
	}
	globalData.Unlock()
}

// hold lock before calling
// delete unverified transactions
func internalDelete(payId PayId) {
	entry, ok := globalData.unverified.entries[payId]
	if ok {
		for i, txId := range entry.txIds {
			delete(globalData.unverified.index, txId)
			delete(globalData.verified, txId)
			link := entry.links[i]
			if link != txId {
				delete(globalData.pending, link)
			}
		}
		delete(globalData.unverified.entries, payId)
	}
}
<|endoftext|>


<commit_before>package resizer

import (
	"bytes"
	"fmt"
	"os/exec"
	"sync"
)

// Number of simultaneous resizes that we should perform.
const numWorkers = 5

// ResizeJob represents work to resize a single image.
type ResizeJob struct {
	SourcePath  string
	TargetPath  string
	TargetWidth int

	Err error
}

// Resize resizes a set of images based on the parameters of the jobs it receives.
func Resize(jobs []*ResizeJob) error {
	var wg sync.WaitGroup
	wg.Add(len(jobs))

	jobsChan := make(chan *ResizeJob, len(jobs))

	// Signal workers to stop looping and shut down.
	defer close(jobsChan)

	for i := 0; i < numWorkers; i++ {
		go workJobs(jobsChan, &wg)
	}

	for _, job := range jobs {
		jobsChan <- job
	}

	wg.Wait()

	// This is not the greatest possible approach because we have to wait for
	// all jobs to be processed, but practically problems should be relatively
	// rare.
	for _, job := range jobs {
		if job.Err != nil {
			return job.Err
		}
	}

	return nil
}

func resize(job *ResizeJob) error {
	cmd := exec.Command(
		"gm",
		"convert",
		job.SourcePath,
		"-resize",
		fmt.Sprintf("%vx", job.TargetWidth),
		"-quality",
		"85",
		job.TargetPath,
	)

	var errOut bytes.Buffer
	cmd.Stderr = &errOut

	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("%v (stderr: %v)", err, errOut.String())
	}

	return nil
}

func workJobs(jobsChan chan *ResizeJob, wg *sync.WaitGroup) {
	// Note that this loop falls through when the channel is closed.
	for job := range jobsChan {
		err := resize(job)
		if err != nil {
			job.Err = err
		}
		wg.Done()
	}
}
<commit_msg>Add `-auto-orient` option<commit_after>package resizer

import (
	"bytes"
	"fmt"
	"os/exec"
	"sync"
)

// Number of simultaneous resizes that we should perform.
const numWorkers = 5

// ResizeJob represents work to resize a single image.
type ResizeJob struct {
	SourcePath  string
	TargetPath  string
	TargetWidth int

	Err error
}

// Resize resizes a set of images based on the parameters of the jobs it receives.
func Resize(jobs []*ResizeJob) error {
	var wg sync.WaitGroup
	wg.Add(len(jobs))

	jobsChan := make(chan *ResizeJob, len(jobs))

	// Signal workers to stop looping and shut down.
	defer close(jobsChan)

	for i := 0; i < numWorkers; i++ {
		go workJobs(jobsChan, &wg)
	}

	for _, job := range jobs {
		jobsChan <- job
	}

	wg.Wait()

	// This is not the greatest possible approach because we have to wait for
	// all jobs to be processed, but practically problems should be relatively
	// rare.
	for _, job := range jobs {
		if job.Err != nil {
			return job.Err
		}
	}

	return nil
}

func resize(job *ResizeJob) error {
	cmd := exec.Command(
		"gm",
		"convert",
		job.SourcePath,
		"-auto-orient",
		"-resize",
		fmt.Sprintf("%vx", job.TargetWidth),
		"-quality",
		"85",
		job.TargetPath,
	)

	var errOut bytes.Buffer
	cmd.Stderr = &errOut

	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("%v (stderr: %v)", err, errOut.String())
	}

	return nil
}

func workJobs(jobsChan chan *ResizeJob, wg *sync.WaitGroup) {
	// Note that this loop falls through when the channel is closed.
	for job := range jobsChan {
		err := resize(job)
		if err != nil {
			job.Err = err
		}
		wg.Done()
	}
}
<|endoftext|>


<commit_before>package mjp

type MJP int

const (
	// ピンズ
	P1 MJP = (1 + iota)
	P2
	P3
	P4
	P5
	P6
	P7
	P8
	P9
	// マンズ
	M1
	M2
	M3
	M4
	M5
	M6
	M7
	M8
	M9
	// ソーズ
	S1
	S2
	S3
	S4
	S5
	S6
	S7
	S8
	S9
	// 風牌
	TON
	NAN
	SHA
	PEI
	// 三元牌
	HAK
	HAT
	CHN
)

func (i MJP) String() string {
	switch i {
	case P1:
		return "P1"
	case P2:
		return "P2"
	case P3:
		return "P3"
	case P4:
		return "P4"
	case P5:
		return "P5"
	case P6:
		return "P6"
	case P7:
		return "P7"
	case P8:
		return "P8"
	case P9:
		return "P9"

	case M1:
		return "M1"
	case M2:
		return "M2"
	case M3:
		return "M3"
	case M4:
		return "M4"
	case M5:
		return "M5"
	case M6:
		return "M6"
	case M7:
		return "M7"
	case M8:
		return "M8"
	case M9:
		return "M9"

	case S1:
		return "S1"
	case S2:
		return "S2"
	case S3:
		return "S3"
	case S4:
		return "S4"
	case S5:
		return "S5"
	case S6:
		return "S6"
	case S7:
		return "S7"
	case S8:
		return "S8"
	case S9:
		return "S9"

		// 風牌
	case TON:
		return "東"
	case NAN:
		return "南"
	case SHA:
		return "西"
	case PEI:
		return "北"

		// 三元牌
	case HAK:
		return "白"
	case HAT:
		return "發"
	case CHN:
		return "中"
	}
	return ""
}
<commit_msg>fix: 種類を追加<commit_after>package mjp

type MJPType int
type MJP int

const (
	NONE_TYPE MJPType = iota
	P_TYPE
	M_TYPE
	S_TYPE
	K_TYPE
	G_TYPE
)
const (
	// ピンズ
	P1 MJP = (1 + iota)
	P2
	P3
	P4
	P5
	P6
	P7
	P8
	P9
	// マンズ
	M1
	M2
	M3
	M4
	M5
	M6
	M7
	M8
	M9
	// ソーズ
	S1
	S2
	S3
	S4
	S5
	S6
	S7
	S8
	S9
	// 風牌
	TON
	NAN
	SHA
	PEI
	// 三元牌
	HAK
	HAT
	CHN
)

func (i MJP) Type() MJPType {
	if i >= P1 && i <= P9 {
		return P_TYPE
	} else if i >= M1 && i <= M9 {
		return M_TYPE
	} else if i >= S1 && i <= S9 {
		return S_TYPE
	} else if i >= TON && i <= PEI {
		return K_TYPE
	} else if i >= HAK && i <= CHN {
		return G_TYPE
	}
	return NONE_TYPE
}

func (i MJP) String() string {
	switch i {
	case P1:
		return "P1"
	case P2:
		return "P2"
	case P3:
		return "P3"
	case P4:
		return "P4"
	case P5:
		return "P5"
	case P6:
		return "P6"
	case P7:
		return "P7"
	case P8:
		return "P8"
	case P9:
		return "P9"

	case M1:
		return "M1"
	case M2:
		return "M2"
	case M3:
		return "M3"
	case M4:
		return "M4"
	case M5:
		return "M5"
	case M6:
		return "M6"
	case M7:
		return "M7"
	case M8:
		return "M8"
	case M9:
		return "M9"

	case S1:
		return "S1"
	case S2:
		return "S2"
	case S3:
		return "S3"
	case S4:
		return "S4"
	case S5:
		return "S5"
	case S6:
		return "S6"
	case S7:
		return "S7"
	case S8:
		return "S8"
	case S9:
		return "S9"

		// 風牌
	case TON:
		return "東"
	case NAN:
		return "南"
	case SHA:
		return "西"
	case PEI:
		return "北"

		// 三元牌
	case HAK:
		return "白"
	case HAT:
		return "發"
	case CHN:
		return "中"
	}
	return ""
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/weed-fs/go/glog"
	"code.google.com/p/weed-fs/go/util"
	"code.google.com/p/weed-fs/go/weed/weed_server"
	"github.com/gorilla/mux"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"
)

func init() {
	cmdMaster.Run = runMaster // break init cycle
}

var cmdMaster = &Command{
	UsageLine: "master -port=9333",
	Short:     "start a master server",
	Long: `start a master server to provide volume=>location mapping service
  and sequence number of file ids

  `,
}

var (
	mport                   = cmdMaster.Flag.Int("port", 9333, "http listen port")
	masterIp                = cmdMaster.Flag.String("ip", "", "master ip address")
	metaFolder              = cmdMaster.Flag.String("mdir", os.TempDir(), "data directory to store meta data")
	masterPeers             = cmdMaster.Flag.String("peers", "", "other master nodes in comma separated ip:port list")
	volumeSizeLimitMB       = cmdMaster.Flag.Uint("volumeSizeLimitMB", 32*1000, "Default Volume Size in MegaBytes")
	mpulse                  = cmdMaster.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
	confFile                = cmdMaster.Flag.String("conf", "/etc/weedfs/weedfs.conf", "xml configuration file")
	defaultReplicaPlacement = cmdMaster.Flag.String("defaultReplication", "000", "Default replication type if not specified.")
	mReadTimeout            = cmdMaster.Flag.Int("readTimeout", 3, "connection read timeout in seconds")
	mMaxCpu                 = cmdMaster.Flag.Int("maxCpu", 0, "maximum number of CPUs. 0 means all available CPUs")
	garbageThreshold        = cmdMaster.Flag.String("garbageThreshold", "0.3", "threshold to vacuum and reclaim spaces")
	masterWhiteListOption   = cmdMaster.Flag.String("whiteList", "", "comma separated Ip addresses having write permission. No limit if empty.")

	masterWhiteList []string
)

func runMaster(cmd *Command, args []string) bool {
	if *mMaxCpu < 1 {
		*mMaxCpu = runtime.NumCPU()
	}
	runtime.GOMAXPROCS(*mMaxCpu)
	if err := util.TestFolderWritable(*metaFolder); err != nil {
		glog.Fatalf("Check Meta Folder (-mdir) Writable %s : %s", *metaFolder, err)
	}
	if *masterWhiteListOption != "" {
		masterWhiteList = strings.Split(*masterWhiteListOption, ",")
	}

	r := mux.NewRouter()
	ms := weed_server.NewMasterServer(r, VERSION, *mport, *metaFolder,
		*volumeSizeLimitMB, *mpulse, *confFile, *defaultReplicaPlacement, *garbageThreshold, masterWhiteList,
	)

	glog.V(0).Infoln("Start Weed Master", VERSION, "at port", *masterIp+":"+strconv.Itoa(*mport))

	srv := &http.Server{
		Addr:        *masterIp + ":" + strconv.Itoa(*mport),
		Handler:     r,
		ReadTimeout: time.Duration(*mReadTimeout) * time.Second,
	}

	go func() {
		time.Sleep(100 * time.Millisecond)
		var peers []string
		if *masterPeers != "" {
			peers = strings.Split(*masterPeers, ",")
		}
		raftServer := weed_server.NewRaftServer(r, VERSION, peers, *masterIp+":"+strconv.Itoa(*mport), *metaFolder)
		ms.SetRaftServer(raftServer)
	}()

	e := srv.ListenAndServe()
	if e != nil {
		glog.Fatalf("Fail to start:%s", e)
	}
	return true
}
<commit_msg>fix https://code.google.com/p/weed-fs/issues/detail?id=64<commit_after>package main

import (
	"code.google.com/p/weed-fs/go/glog"
	"code.google.com/p/weed-fs/go/util"
	"code.google.com/p/weed-fs/go/weed/weed_server"
	"github.com/gorilla/mux"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"
)

func init() {
	cmdMaster.Run = runMaster // break init cycle
}

var cmdMaster = &Command{
	UsageLine: "master -port=9333",
	Short:     "start a master server",
	Long: `start a master server to provide volume=>location mapping service
  and sequence number of file ids

  `,
}

var (
	mport                   = cmdMaster.Flag.Int("port", 9333, "http listen port")
	masterIp                = cmdMaster.Flag.String("ip", "localhost", "master ip address")
	metaFolder              = cmdMaster.Flag.String("mdir", os.TempDir(), "data directory to store meta data")
	masterPeers             = cmdMaster.Flag.String("peers", "", "other master nodes in comma separated ip:port list")
	volumeSizeLimitMB       = cmdMaster.Flag.Uint("volumeSizeLimitMB", 32*1000, "Default Volume Size in MegaBytes")
	mpulse                  = cmdMaster.Flag.Int("pulseSeconds", 5, "number of seconds between heartbeats")
	confFile                = cmdMaster.Flag.String("conf", "/etc/weedfs/weedfs.conf", "xml configuration file")
	defaultReplicaPlacement = cmdMaster.Flag.String("defaultReplication", "000", "Default replication type if not specified.")
	mReadTimeout            = cmdMaster.Flag.Int("readTimeout", 3, "connection read timeout in seconds")
	mMaxCpu                 = cmdMaster.Flag.Int("maxCpu", 0, "maximum number of CPUs. 0 means all available CPUs")
	garbageThreshold        = cmdMaster.Flag.String("garbageThreshold", "0.3", "threshold to vacuum and reclaim spaces")
	masterWhiteListOption   = cmdMaster.Flag.String("whiteList", "", "comma separated Ip addresses having write permission. No limit if empty.")

	masterWhiteList []string
)

func runMaster(cmd *Command, args []string) bool {
	if *mMaxCpu < 1 {
		*mMaxCpu = runtime.NumCPU()
	}
	runtime.GOMAXPROCS(*mMaxCpu)
	if err := util.TestFolderWritable(*metaFolder); err != nil {
		glog.Fatalf("Check Meta Folder (-mdir) Writable %s : %s", *metaFolder, err)
	}
	if *masterWhiteListOption != "" {
		masterWhiteList = strings.Split(*masterWhiteListOption, ",")
	}

	r := mux.NewRouter()
	ms := weed_server.NewMasterServer(r, VERSION, *mport, *metaFolder,
		*volumeSizeLimitMB, *mpulse, *confFile, *defaultReplicaPlacement, *garbageThreshold, masterWhiteList,
	)

	glog.V(0).Infoln("Start Weed Master", VERSION, "at port", *masterIp+":"+strconv.Itoa(*mport))

	srv := &http.Server{
		Addr:        *masterIp + ":" + strconv.Itoa(*mport),
		Handler:     r,
		ReadTimeout: time.Duration(*mReadTimeout) * time.Second,
	}

	go func() {
		time.Sleep(100 * time.Millisecond)
		var peers []string
		if *masterPeers != "" {
			peers = strings.Split(*masterPeers, ",")
		}
		raftServer := weed_server.NewRaftServer(r, VERSION, peers, *masterIp+":"+strconv.Itoa(*mport), *metaFolder)
		ms.SetRaftServer(raftServer)
	}()

	e := srv.ListenAndServe()
	if e != nil {
		glog.Fatalf("Fail to start:%s", e)
	}
	return true
}
<|endoftext|>


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

package gomock

import (
	"bytes"
	"errors"
	"fmt"
)

// callSet represents a set of expected calls, indexed by receiver and method
// name.
type callSet map[interface{}]map[string][]*Call

// Add adds a new expected call.
func (cs callSet) Add(call *Call) {
	methodMap, ok := cs[call.receiver]
	if !ok {
		methodMap = make(map[string][]*Call)
		cs[call.receiver] = methodMap
	}
	methodMap[call.method] = append(methodMap[call.method], call)
}

// Remove removes an expected call.
func (cs callSet) Remove(call *Call) {
	methodMap, ok := cs[call.receiver]
	if !ok {
		return
	}
	sl := methodMap[call.method]
	for i, c := range sl {
		if c == call {
			// quick removal; we don't need to maintain call order
			if len(sl) > 1 {
				sl[i] = sl[len(sl)-1]
			}
			methodMap[call.method] = sl[:len(sl)-1]
			break
		}
	}
}

// FindMatch searches for a matching call. Returns error with explanation message if no call matched.
func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) {
	methodMap, ok := cs[receiver]
	if !ok {
		return nil, errors.New("there are no expected method calls for that receiver")
	}
	calls, ok := methodMap[method]
	if !ok {
		return nil, fmt.Errorf("there are no expected calls of the method: %s for that receiver", method)
	}

	// Search through the unordered set of calls expected on a method on a
	// receiver.
	var callsErrors bytes.Buffer
	for _, call := range calls {
		// A call should not normally still be here if exhausted,
		// but it can happen if, for instance, .Times(0) was used.
		// Pretend the call doesn't match.
		if call.exhausted() {
			callsErrors.WriteString("\nThe call was exhausted.")
			continue
		}
		err := call.matches(args)
		if err != nil {
			fmt.Fprintf(&callsErrors, "\n%v", err)
		} else {
			return call, nil
		}
	}

	return nil, fmt.Errorf(callsErrors.String())
}
<commit_msg>retain ordering of expected calls<commit_after>// Copyright 2011 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gomock

import (
	"bytes"
	"errors"
	"fmt"
)

// callSet represents a set of expected calls, indexed by receiver and method
// name.
type callSet map[interface{}]map[string][]*Call

// Add adds a new expected call.
func (cs callSet) Add(call *Call) {
	methodMap, ok := cs[call.receiver]
	if !ok {
		methodMap = make(map[string][]*Call)
		cs[call.receiver] = methodMap
	}
	methodMap[call.method] = append(methodMap[call.method], call)
}

// Remove removes an expected call.
func (cs callSet) Remove(call *Call) {
	methodMap, ok := cs[call.receiver]
	if !ok {
		return
	}
	sl := methodMap[call.method]
	for i, c := range sl {
		if c == call {
			// maintain order for remaining calls
			methodMap[call.method] = append(sl[:i], sl[i+1:]...)
			break
		}
	}
}

// FindMatch searches for a matching call. Returns error with explanation message if no call matched.
func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) {
	methodMap, ok := cs[receiver]
	if !ok {
		return nil, errors.New("there are no expected method calls for that receiver")
	}
	calls, ok := methodMap[method]
	if !ok {
		return nil, fmt.Errorf("there are no expected calls of the method: %s for that receiver", method)
	}

	// Search through the unordered set of calls expected on a method on a
	// receiver.
	var callsErrors bytes.Buffer
	for _, call := range calls {
		// A call should not normally still be here if exhausted,
		// but it can happen if, for instance, .Times(0) was used.
		// Pretend the call doesn't match.
		if call.exhausted() {
			callsErrors.WriteString("\nThe call was exhausted.")
			continue
		}
		err := call.matches(args)
		if err != nil {
			fmt.Fprintf(&callsErrors, "\n%v", err)
		} else {
			return call, nil
		}
	}

	return nil, fmt.Errorf(callsErrors.String())
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/packer/helper/communicator"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

// Create statebag for running test
func tStateSpot() multistep.StateBag {
	state := new(multistep.BasicStateBag)
	state.Put("ui", &packer.BasicUi{
		Reader: new(bytes.Buffer),
		Writer: new(bytes.Buffer),
	})
	state.Put("availability_zone", "us-east-1c")
	state.Put("securityGroupIds", []string{"sg-0b8984db72f213dc3"})
	state.Put("iamInstanceProfile", "packer-123")
	state.Put("subnet_id", "subnet-077fde4e")
	state.Put("source_image", "")
	return state
}

func getBasicStep() *StepRunSpotInstance {
	stepRunSpotInstance := StepRunSpotInstance{
		AssociatePublicIpAddress: false,
		LaunchMappings:           BlockDevices{},
		BlockDurationMinutes:     0,
		Debug:                    false,
		Comm: &communicator.Config{
			SSH: communicator.SSH{
				SSHKeyPairName: "foo",
			},
		},
		EbsOptimized:                      false,
		ExpectedRootDevice:                "ebs",
		InstanceInitiatedShutdownBehavior: "stop",
		InstanceType:                      "t2.micro",
		SourceAMI:                         "",
		SpotPrice:                         "auto",
		SpotTags:                          nil,
		Tags:                              map[string]string{},
		VolumeTags:                        nil,
		UserData:                          "",
		UserDataFile:                      "",
	}

	return &stepRunSpotInstance
}

func TestCreateTemplateData(t *testing.T) {
	state := tStateSpot()
	stepRunSpotInstance := getBasicStep()
	template := stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
		&ec2.LaunchTemplateInstanceMarketOptionsRequest{})

	// expected := []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
	// 	&ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
	// 		DeleteOnTermination: aws.Bool(true),
	// 		DeviceIndex:         aws.Int64(0),
	// 		Groups:              aws.StringSlice([]string{"sg-0b8984db72f213dc3"}),
	// 		SubnetId:            aws.String("subnet-077fde4e"),
	// 	},
	// }
	// if expected != template.NetworkInterfaces {
	if template.NetworkInterfaces == nil {
		t.Fatalf("Template should have contained a networkInterface object: recieved %#v", template.NetworkInterfaces)
	}

	if *template.IamInstanceProfile.Name != state.Get("iamInstanceProfile") {
		t.Fatalf("Template should have contained a InstanceProfile name: recieved %#v", template.IamInstanceProfile.Name)
	}

	// Rerun, this time testing that we set security group IDs
	state.Put("subnet_id", "")
	template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
		&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
	if template.NetworkInterfaces != nil {
		t.Fatalf("Template shouldn't contain network interfaces object if subnet_id is unset.")
	}

	// Rerun, this time testing that instance doesn't have instance profile is iamInstanceProfile is unset
	state.Put("iamInstanceProfile", "")
	template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
		&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
	fmt.Println(template.IamInstanceProfile)
	if *template.IamInstanceProfile.Name != "" {
		t.Fatalf("Template shouldn't contain instance profile if iamInstanceProfile is unset.")
	}
}
<commit_msg>add test of NoEphemeral<commit_after>package common

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/packer/helper/communicator"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

// Create statebag for running test
func tStateSpot() multistep.StateBag {
	state := new(multistep.BasicStateBag)
	state.Put("ui", &packer.BasicUi{
		Reader: new(bytes.Buffer),
		Writer: new(bytes.Buffer),
	})
	state.Put("availability_zone", "us-east-1c")
	state.Put("securityGroupIds", []string{"sg-0b8984db72f213dc3"})
	state.Put("iamInstanceProfile", "packer-123")
	state.Put("subnet_id", "subnet-077fde4e")
	state.Put("source_image", "")
	return state
}

func getBasicStep() *StepRunSpotInstance {
	stepRunSpotInstance := StepRunSpotInstance{
		AssociatePublicIpAddress: false,
		LaunchMappings:           BlockDevices{},
		BlockDurationMinutes:     0,
		Debug:                    false,
		Comm: &communicator.Config{
			SSH: communicator.SSH{
				SSHKeyPairName: "foo",
			},
		},
		EbsOptimized:                      false,
		ExpectedRootDevice:                "ebs",
		InstanceInitiatedShutdownBehavior: "stop",
		InstanceType:                      "t2.micro",
		SourceAMI:                         "",
		SpotPrice:                         "auto",
		SpotTags:                          nil,
		Tags:                              map[string]string{},
		VolumeTags:                        nil,
		UserData:                          "",
		UserDataFile:                      "",
	}

	return &stepRunSpotInstance
}

func TestCreateTemplateData(t *testing.T) {
	state := tStateSpot()
	stepRunSpotInstance := getBasicStep()
	template := stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
		&ec2.LaunchTemplateInstanceMarketOptionsRequest{})

	// expected := []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
	// 	&ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
	// 		DeleteOnTermination: aws.Bool(true),
	// 		DeviceIndex:         aws.Int64(0),
	// 		Groups:              aws.StringSlice([]string{"sg-0b8984db72f213dc3"}),
	// 		SubnetId:            aws.String("subnet-077fde4e"),
	// 	},
	// }
	// if expected != template.NetworkInterfaces {
	if template.NetworkInterfaces == nil {
		t.Fatalf("Template should have contained a networkInterface object: recieved %#v", template.NetworkInterfaces)
	}

	if *template.IamInstanceProfile.Name != state.Get("iamInstanceProfile") {
		t.Fatalf("Template should have contained a InstanceProfile name: recieved %#v", template.IamInstanceProfile.Name)
	}

	// Rerun, this time testing that we set security group IDs
	state.Put("subnet_id", "")
	template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
		&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
	if template.NetworkInterfaces != nil {
		t.Fatalf("Template shouldn't contain network interfaces object if subnet_id is unset.")
	}

	// Rerun, this time testing that instance doesn't have instance profile is iamInstanceProfile is unset
	state.Put("iamInstanceProfile", "")
	template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
		&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
	fmt.Println(template.IamInstanceProfile)
	if *template.IamInstanceProfile.Name != "" {
		t.Fatalf("Template shouldn't contain instance profile if iamInstanceProfile is unset.")
	}
}

func TestCreateTemplateData_NoEphemeral(t *testing.T) {
	state := tStateSpot()
	stepRunSpotInstance := getBasicStep()
	stepRunSpotInstance.NoEphemeral = true
	template := stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
		&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
	if len(template.BlockDeviceMappings) != 26 {
		t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
	}

	// Now check that noEphemeral doesn't mess with the mappings in real life.
	// state = tStateSpot()
	// stepRunSpotInstance = getBasicStep()
	// stepRunSpotInstance.NoEphemeral = true
	// mappings := []*ec2.InstanceBlockDeviceMapping{
	// 	&ec2.InstanceBlockDeviceMapping{
	// 		DeviceName: "xvda",
	// 		Ebs: {
	// 			DeleteOnTermination: true,
	// 			Status:              "attaching",
	// 			VolumeId:            "vol-044cd49c330f21c05",
	// 		},
	// 	},
	// 	&ec2.InstanceBlockDeviceMapping{
	// 		DeviceName: "/dev/xvdf",
	// 		Ebs: {
	// 			DeleteOnTermination: false,
	// 			Status:              "attaching",
	// 			VolumeId:            "vol-0eefaf2d6ae35827e",
	// 		},
	// 	},
	// }
	// template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
	// 	&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
	// if len(*template.BlockDeviceMappings) != 26 {
	// 	t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
	// }
}
<|endoftext|>


<commit_before>package main

import (
	_ "github.com/gliderlabs/logspout/adapters/raw"
	_ "github.com/gliderlabs/logspout/adapters/syslog"
	_ "github.com/gliderlabs/logspout/httpstream"
	_ "github.com/gliderlabs/logspout/routesapi"
	_ "github.com/gliderlabs/logspout/transports/tcp"
	_ "github.com/gliderlabs/logspout/transports/udp"
	_ "github.com/gliderlabs/logspout/transports/tls"
)
<commit_msg>adding github.com/billimek/logspout-gelf module<commit_after>package main

import (
	_ "github.com/gliderlabs/logspout/adapters/raw"
	_ "github.com/gliderlabs/logspout/adapters/syslog"
	_ "github.com/gliderlabs/logspout/httpstream"
	_ "github.com/gliderlabs/logspout/routesapi"
	_ "github.com/gliderlabs/logspout/transports/tcp"
	_ "github.com/gliderlabs/logspout/transports/udp"
	_ "github.com/gliderlabs/logspout/transports/tls"
  _ "github.com/billimek/logspout-gelf"
)
<|endoftext|>