<commit_before>package irckit

import (
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/mattermost/platform/model"
	"github.com/sorcix/irc"
)

// Channel is a representation of a room in our server
type Channel interface {
	Prefixer

	// ID is a normalized unique identifier for the channel
	ID() string

	// Created returns the time when the Channel was created.
	Created() time.Time

	// Names returns a sorted slice of Nicks in the channel
	Names() []string

	// Users returns a slice of Users in the channel.
	Users() []*User

	// HasUser returns whether a User is in the channel.
	HasUser(*User) bool

	// Invite prompts the User to join the Channel on behalf of Prefixer.
	Invite(from Prefixer, u *User) error

	// Join introduces the User to the channel (handler for JOIN).
	Join(u *User) error

	// Part removes the User from the channel (handler for PART).
	Part(u *User, text string)

	// Message transmits a message from a User to the channel (handler for PRIVMSG).
	Message(u *User, text string)

	// Topic sets the topic of the channel (handler for TOPIC).
	Topic(from Prefixer, text string)

	// Unlink will disassociate the Channel from its Server.
	Unlink()

	// Len returns the number of Users in the channel.
	Len() int

	// String returns the name of the channel
	String() string

	// Spoof message
	SpoofMessage(from string, text string)
}

type channel struct {
	created time.Time
	name    string
	server  Server
	id      string

	mu       sync.RWMutex
	topic    string
	usersIdx map[*User]struct{}
}

// NewChannel returns a Channel implementation for a given Server.
func NewChannel(server Server, channelId string, name string) Channel {
	return &channel{
		created:  time.Now(),
		server:   server,
		id:       channelId,
		name:     name,
		usersIdx: map[*User]struct{}{},
	}
}

func (ch *channel) Prefix() *irc.Prefix {
	return ch.server.Prefix()
}

func (ch *channel) String() string {
	return ch.name
}

// Created returns the time when the Channel was created.
func (ch *channel) Created() time.Time {
	return ch.created
}

// ID returns a normalized unique identifier for the channel.
func (ch *channel) ID() string {
	return ID(ch.id)
}

func (ch *channel) Message(from *User, text string) {
	msg := &irc.Message{
		Prefix:   from.Prefix(),
		Command:  irc.PRIVMSG,
		Params:   []string{ch.name},
		Trailing: text,
	}
	ch.mu.RLock()
	for to := range ch.usersIdx {
		// TODO: Check err and kick failures?
		/*		if to == from {
					continue
				}
		*/
		to.Encode(msg)
	}
	ch.mu.RUnlock()
}

// Quit will remove the user from the channel and emit a PART message.
func (ch *channel) Part(u *User, text string) {
	msg := &irc.Message{
		Prefix:   u.Prefix(),
		Command:  irc.PART,
		Params:   []string{ch.name},
		Trailing: text,
	}
	ch.mu.Lock()
	if _, ok := ch.usersIdx[u]; !ok {
		ch.mu.Unlock()
		u.Encode(&irc.Message{
			Prefix:   ch.Prefix(),
			Command:  irc.ERR_NOTONCHANNEL,
			Params:   []string{ch.name},
			Trailing: "You're not on that channel",
		})
		return
	}
	u.Encode(msg)
	delete(ch.usersIdx, u)
	ch.mu.Unlock()
	u.Lock()
	delete(u.channels, ch)
	u.Unlock()
}

// Unlink will disassociate the Channel from the Server.
func (ch *channel) Unlink() {
	ch.server.UnlinkChannel(ch)
}

// Close will evict all users in the channel.
func (ch *channel) Close() error {
	ch.mu.Lock()
	for to := range ch.usersIdx {
		to.Encode(&irc.Message{
			Prefix:  to.Prefix(),
			Command: irc.PART,
			Params:  []string{ch.name},
		})
	}
	ch.usersIdx = map[*User]struct{}{}
	ch.mu.Unlock()
	return nil
}

// Invite prompts the User to join the Channel on behalf of Prefixer.
func (ch *channel) Invite(from Prefixer, u *User) error {
	return u.Encode(&irc.Message{
		Prefix:  from.Prefix(),
		Command: irc.INVITE,
		Params:  []string{u.Nick, ch.name},
	})
	// TODO: Save state that the user is invited?
}

// Topic sets the topic of the channel (handler for TOPIC).
func (ch *channel) Topic(from Prefixer, text string) {
	ch.mu.RLock()
	ch.topic = text
	// no newlines in topic
	ch.topic = strings.Replace(ch.topic, "\n", " ", -1)

	msg := &irc.Message{
		Prefix:   from.Prefix(),
		Command:  irc.TOPIC,
		Params:   []string{ch.name},
		Trailing: text,
	}
	for to := range ch.usersIdx {
		to.Encode(msg)
	}

	ch.mu.RUnlock()
}

// Join introduces the User to the channel (sends relevant messages, stores).
func (ch *channel) Join(u *User) error {
	// TODO: Check if user is already here?
	ch.mu.Lock()
	if _, exists := ch.usersIdx[u]; exists {
		ch.mu.Unlock()
		return nil
	}
	topic := ch.topic
	ch.usersIdx[u] = struct{}{}
	ch.mu.Unlock()
	u.Lock()
	u.channels[ch] = struct{}{}
	u.Unlock()

	// only send join messages to real users
	if u.MmGhostUser {
		return nil
	}

	msg := &irc.Message{
		Prefix:  u.Prefix(),
		Command: irc.JOIN,
		Params:  []string{ch.name},
	}
	for to := range ch.usersIdx {
		to.Encode(msg)
	}

	msgs := []*irc.Message{}
	if topic != "" {
		msgs = append(msgs, &irc.Message{
			Prefix:   ch.Prefix(),
			Command:  irc.RPL_TOPIC,
			Params:   []string{u.Nick, ch.name},
			Trailing: topic,
		})
	}

	line := ""
	i := 0
	for _, name := range ch.Names() {
		if i+len(name) < 400 {
			line += name + " "
			i += len(name)
		} else {
			msgs = append(msgs, &irc.Message{
				Prefix:   ch.Prefix(),
				Command:  irc.RPL_NAMREPLY,
				Params:   []string{u.Nick, "=", ch.name},
				Trailing: line,
			})
			line = ""
			line += name + " "
			i = len(name)
		}
	}
	msgs = append(msgs, &irc.Message{
		Prefix:   ch.Prefix(),
		Command:  irc.RPL_NAMREPLY,
		Params:   []string{u.Nick, "=", ch.name},
		Trailing: line,
	})

	msgs = append(msgs, &irc.Message{
		Prefix:   ch.Prefix(),
		Params:   []string{u.Nick, ch.name},
		Command:  irc.RPL_ENDOFNAMES,
		Trailing: "End of /NAMES list.",
	},
	)

	return u.Encode(msgs...)
}

func (ch *channel) HasUser(u *User) bool {
	ch.mu.RLock()
	_, ok := ch.usersIdx[u]
	ch.mu.RUnlock()
	return ok
}

// Users returns an unsorted slice of users who are in the channel.
func (ch *channel) Users() []*User {
	ch.mu.RLock()
	users := make([]*User, 0, len(ch.usersIdx))
	for u := range ch.usersIdx {
		users = append(users, u)
	}
	ch.mu.RUnlock()
	return users
}

// Names returns a sorted slice of Nick strings of users who are in the channel.
func (ch *channel) Names() []string {
	users := ch.Users()
	names := make([]string, 0, len(users))
	for _, u := range users {
		if strings.Contains(u.Roles, model.ROLE_SYSTEM_ADMIN.Id) {
			names = append(names, "@"+u.Nick)
		} else {
			names = append(names, u.Nick)
		}
	}
	// TODO: Append in sorted order?
	sort.Strings(names)
	return names
}

// Len returns the number of users in the channel.
func (ch *channel) Len() int {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return len(ch.usersIdx)
}
<commit_msg>Fix join/parts of mattermost users<commit_after>package irckit

import (
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/mattermost/platform/model"
	"github.com/sorcix/irc"
)

// Channel is a representation of a room in our server
type Channel interface {
	Prefixer

	// ID is a normalized unique identifier for the channel
	ID() string

	// Created returns the time when the Channel was created.
	Created() time.Time

	// Names returns a sorted slice of Nicks in the channel
	Names() []string

	// Users returns a slice of Users in the channel.
	Users() []*User

	// HasUser returns whether a User is in the channel.
	HasUser(*User) bool

	// Invite prompts the User to join the Channel on behalf of Prefixer.
	Invite(from Prefixer, u *User) error

	// Join introduces the User to the channel (handler for JOIN).
	Join(u *User) error

	// Part removes the User from the channel (handler for PART).
	Part(u *User, text string)

	// Message transmits a message from a User to the channel (handler for PRIVMSG).
	Message(u *User, text string)

	// Topic sets the topic of the channel (handler for TOPIC).
	Topic(from Prefixer, text string)

	// Unlink will disassociate the Channel from its Server.
	Unlink()

	// Len returns the number of Users in the channel.
	Len() int

	// String returns the name of the channel
	String() string

	// Spoof message
	SpoofMessage(from string, text string)
}

type channel struct {
	created time.Time
	name    string
	server  Server
	id      string

	mu       sync.RWMutex
	topic    string
	usersIdx map[*User]struct{}
}

// NewChannel returns a Channel implementation for a given Server.
func NewChannel(server Server, channelId string, name string) Channel {
	return &channel{
		created:  time.Now(),
		server:   server,
		id:       channelId,
		name:     name,
		usersIdx: map[*User]struct{}{},
	}
}

func (ch *channel) Prefix() *irc.Prefix {
	return ch.server.Prefix()
}

func (ch *channel) String() string {
	return ch.name
}

// Created returns the time when the Channel was created.
func (ch *channel) Created() time.Time {
	return ch.created
}

// ID returns a normalized unique identifier for the channel.
func (ch *channel) ID() string {
	return ID(ch.id)
}

func (ch *channel) Message(from *User, text string) {
	msg := &irc.Message{
		Prefix:   from.Prefix(),
		Command:  irc.PRIVMSG,
		Params:   []string{ch.name},
		Trailing: text,
	}
	ch.mu.RLock()
	for to := range ch.usersIdx {
		// TODO: Check err and kick failures?
		/*		if to == from {
					continue
				}
		*/
		to.Encode(msg)
	}
	ch.mu.RUnlock()
}

// Quit will remove the user from the channel and emit a PART message.
func (ch *channel) Part(u *User, text string) {
	msg := &irc.Message{
		Prefix:   u.Prefix(),
		Command:  irc.PART,
		Params:   []string{ch.name},
		Trailing: text,
	}
	ch.mu.Lock()
	if _, ok := ch.usersIdx[u]; !ok {
		ch.mu.Unlock()
		u.Encode(&irc.Message{
			Prefix:   ch.Prefix(),
			Command:  irc.ERR_NOTONCHANNEL,
			Params:   []string{ch.name},
			Trailing: "You're not on that channel",
		})
		return
	}

	// only send join messages to real users
	for to := range ch.usersIdx {
		if to.MmGhostUser == false {
			to.Encode(msg)
		}
	}
	u.Encode(msg)
	delete(ch.usersIdx, u)
	ch.mu.Unlock()
	u.Lock()
	delete(u.channels, ch)
	u.Unlock()
}

// Unlink will disassociate the Channel from the Server.
func (ch *channel) Unlink() {
	ch.server.UnlinkChannel(ch)
}

// Close will evict all users in the channel.
func (ch *channel) Close() error {
	ch.mu.Lock()
	for to := range ch.usersIdx {
		to.Encode(&irc.Message{
			Prefix:  to.Prefix(),
			Command: irc.PART,
			Params:  []string{ch.name},
		})
	}
	ch.usersIdx = map[*User]struct{}{}
	ch.mu.Unlock()
	return nil
}

// Invite prompts the User to join the Channel on behalf of Prefixer.
func (ch *channel) Invite(from Prefixer, u *User) error {
	return u.Encode(&irc.Message{
		Prefix:  from.Prefix(),
		Command: irc.INVITE,
		Params:  []string{u.Nick, ch.name},
	})
	// TODO: Save state that the user is invited?
}

// Topic sets the topic of the channel (handler for TOPIC).
func (ch *channel) Topic(from Prefixer, text string) {
	ch.mu.RLock()
	ch.topic = text
	// no newlines in topic
	ch.topic = strings.Replace(ch.topic, "\n", " ", -1)

	msg := &irc.Message{
		Prefix:   from.Prefix(),
		Command:  irc.TOPIC,
		Params:   []string{ch.name},
		Trailing: text,
	}
	for to := range ch.usersIdx {
		to.Encode(msg)
	}

	ch.mu.RUnlock()
}

// Join introduces the User to the channel (sends relevant messages, stores).
func (ch *channel) Join(u *User) error {
	// TODO: Check if user is already here?
	ch.mu.Lock()
	if _, exists := ch.usersIdx[u]; exists {
		ch.mu.Unlock()
		return nil
	}
	topic := ch.topic
	ch.usersIdx[u] = struct{}{}
	ch.mu.Unlock()
	u.Lock()
	u.channels[ch] = struct{}{}
	u.Unlock()

	msg := &irc.Message{
		Prefix:  u.Prefix(),
		Command: irc.JOIN,
		Params:  []string{ch.name},
	}

	// only send join messages to real users
	for to := range ch.usersIdx {
		if to.MmGhostUser == false {
			to.Encode(msg)
		}
	}

	msgs := []*irc.Message{}
	if topic != "" {
		msgs = append(msgs, &irc.Message{
			Prefix:   ch.Prefix(),
			Command:  irc.RPL_TOPIC,
			Params:   []string{u.Nick, ch.name},
			Trailing: topic,
		})
	}

	line := ""
	i := 0
	for _, name := range ch.Names() {
		if i+len(name) < 400 {
			line += name + " "
			i += len(name)
		} else {
			msgs = append(msgs, &irc.Message{
				Prefix:   ch.Prefix(),
				Command:  irc.RPL_NAMREPLY,
				Params:   []string{u.Nick, "=", ch.name},
				Trailing: line,
			})
			line = ""
			line += name + " "
			i = len(name)
		}
	}
	msgs = append(msgs, &irc.Message{
		Prefix:   ch.Prefix(),
		Command:  irc.RPL_NAMREPLY,
		Params:   []string{u.Nick, "=", ch.name},
		Trailing: line,
	})

	msgs = append(msgs, &irc.Message{
		Prefix:   ch.Prefix(),
		Params:   []string{u.Nick, ch.name},
		Command:  irc.RPL_ENDOFNAMES,
		Trailing: "End of /NAMES list.",
	},
	)

	return u.Encode(msgs...)
}

func (ch *channel) HasUser(u *User) bool {
	ch.mu.RLock()
	_, ok := ch.usersIdx[u]
	ch.mu.RUnlock()
	return ok
}

// Users returns an unsorted slice of users who are in the channel.
func (ch *channel) Users() []*User {
	ch.mu.RLock()
	users := make([]*User, 0, len(ch.usersIdx))
	for u := range ch.usersIdx {
		users = append(users, u)
	}
	ch.mu.RUnlock()
	return users
}

// Names returns a sorted slice of Nick strings of users who are in the channel.
func (ch *channel) Names() []string {
	users := ch.Users()
	names := make([]string, 0, len(users))
	for _, u := range users {
		if strings.Contains(u.Roles, model.ROLE_SYSTEM_ADMIN.Id) {
			names = append(names, "@"+u.Nick)
		} else {
			names = append(names, u.Nick)
		}
	}
	// TODO: Append in sorted order?
	sort.Strings(names)
	return names
}

// Len returns the number of users in the channel.
func (ch *channel) Len() int {
	ch.mu.RLock()
	defer ch.mu.RUnlock()
	return len(ch.usersIdx)
}
<|endoftext|>


<commit_before>// 迅雷下载sdk 翻译by: ying32  qq:1444386932
// api说明见：http://open.xunlei.com/wiki/api_doc.html#1
package xldl

import (
	"syscall"
	"unsafe"
)

var (
	xldldll                   = syscall.NewLazyDLL("xldl.dll")
	xl_Init                   = xldldll.NewProc("XL_Init")
	xl_UnInit                 = xldldll.NewProc("XL_UnInit")
	xl_CreateTask             = xldldll.NewProc("XL_CreateTask")
	xl_DeleteTask             = xldldll.NewProc("XL_DeleteTask")
	xl_StartTask              = xldldll.NewProc("XL_StartTask")
	xl_StopTask               = xldldll.NewProc("XL_StopTask")
	xl_ForceStopTask          = xldldll.NewProc("XL_ForceStopTask")
	xl_QueryTaskInfoEx        = xldldll.NewProc("XL_QueryTaskInfoEx")
	xl_DelTempFile            = xldldll.NewProc("XL_DelTempFile")
	xl_SetSpeedLimit          = xldldll.NewProc("XL_SetSpeedLimit")
	xl_SetUploadSpeedLimit    = xldldll.NewProc("XL_SetUploadSpeedLimit")
	xl_SetProxy               = xldldll.NewProc("XL_SetProxy")
	xl_SetUserAgent           = xldldll.NewProc("XL_SetUserAgent")
	xl_ParseThunderPrivateUrl = xldldll.NewProc("XL_ParseThunderPrivateUrl")
	xl_GetFileSizeWithUrl     = xldldll.NewProc("XL_GetFileSizeWithUrl")
	xl_SetFileIdAndSize       = xldldll.NewProc("XL_SetFileIdAndSize")

	//	xl_SetAdditionInfo        = xldldll.NewProc("XL_SetAdditionInfo")

	xl_CreateTaskByURL       = xldldll.NewProc("XL_CreateTaskByURL")
	xl_CreateTaskByThunder   = xldldll.NewProc("XL_CreateTaskByThunder")
	xl_CreateBTTaskByThunder = xldldll.NewProc("XL_CreateBTTaskByThunder")
)

const (
	MAX_PATH = 260
)

type DOWN_TASK_STATUS int32

const (
	NOITEM = 0 + iota
	TSC_ERROR
	TSC_PAUSE
	TSC_DOWNLOAD
	TSC_COMPLETE
	TSC_STARTPENDING
	TSC_STOPPENDING
)

type TASK_ERROR_TYPE int32

const (
	TASK_ERROR_UNKNOWN                = 0x00 // 未知错误
	TASK_ERROR_DISK_CREATE            = 0x01 // 创建文件失败
	TASK_ERROR_DISK_WRITE             = 0x02 // 写文件失败
	TASK_ERROR_DISK_READ              = 0x03 // 读文件失败
	TASK_ERROR_DISK_RENAME            = 0x04 // 重命名失败
	TASK_ERROR_DISK_PIECEHASH         = 0x05 // 文件片校验失败
	TASK_ERROR_DISK_FILEHASH          = 0x06 // 文件全文校验失败
	TASK_ERROR_DISK_DELETE            = 0x07 // 删除文件失败失败
	TASK_ERROR_DOWN_INVALID           = 0x10 // 无效的DOWN地址
	TASK_ERROR_PROXY_AUTH_TYPE_UNKOWN = 0x20 // 代理类型未知
	TASK_ERROR_PROXY_AUTH_TYPE_FAILED = 0x21 // 代理认证失败
	TASK_ERROR_HTTPMGR_NOT_IP         = 0x30 // http下载中无ip可用
	TASK_ERROR_TIMEOUT                = 0x40 // 任务超时
	TASK_ERROR_CANCEL                 = 0x41 // 任务取消
	TASK_ERROR_TP_CRASHED             = 0x42 // MINITP崩溃
	TASK_ERROR_ID_INVALID             = 0x43 // TaskId 非法
)

// 他按4字节对齐的，然迅雷要求按1字节对齐，so下面两个有问题了，要换种方式了
type DownTaskInfo struct {
	Stat           DOWN_TASK_STATUS
	FailCode       TASK_ERROR_TYPE
	Filename       [MAX_PATH]uint16
	Reserved0      [MAX_PATH]uint16
	TotalSize      int64   // 该任务总大小(字节)
	TotalDownload  int64   // 下载有效字节数(可能存在回退的情况)
	Percent        float32 // 下载进度
	reserved0      int32
	SrcTotal       int32 // 总资源数
	SrcUsing       int32 // 可用资源数
	reserved1      int32
	reserved2      int32
	reserved3      int32
	reserved4      int32
	reserved5      int64
	DonationP2P    int64 // p2p贡献字节数
	reserved6      int64
	DonationOrgin  int64 // 原始资源共享字节数
	DonationP2S    int64 // 镜像资源共享字节数
	reserved7      int64
	reserved8      int64
	Speed          int32   // 即时速度(字节/秒)
	SpeedP2S       int32   // 即时速度(字节/秒)
	SpeedP2P       int32   // 即时速度(字节/秒)
	IsOriginUsable bool    // 原始资源是否有效
	HashPercent    float32 // 现不提供该值
	IsCreatingFile bool    // 是否正在创建文件
	reserved       [64]uint32
}

func (c *DownTaskInfo) SetDefault() {
	c.Stat = TSC_PAUSE
	c.FailCode = TASK_ERROR_UNKNOWN
	c.Percent = 0.0
	c.IsOriginUsable = false
	c.HashPercent = 0
}

type DownTaskParam struct {
	nReserved         int32
	szTaskUrl         [2084]uint16     // 任务URL
	szRefUrl          [2084]uint16     // 引用页
	szCookies         [4096]uint16     // 浏览器cookie
	szFilename        [MAX_PATH]uint16 // 下载保存文件名.
	szReserved0       [MAX_PATH]uint16
	szSavePath        [MAX_PATH]uint16 // 文件保存目录
	hReserved         int32
	bReserved         int32
	szReserved1       [64]uint16
	szReserved2       [64]uint16
	IsOnlyOriginal    int32 // 是否只从原始地址下载
	nReserved1        uint32
	DisableAutoRename int32 // 禁止智能命名
	IsResume          int32 // 是否用续传
	reserved          [2048]uint32
}

func (c *DownTaskParam) SetDefault() {
	c.nReserved1 = 5
	c.bReserved = 0
	c.DisableAutoRename = 0
	c.IsOnlyOriginal = 0
	c.IsResume = 1
}

type DOWN_PROXY_TYPE int32

const (
	PROXY_TYPE_IE     = 0
	PROXY_TYPE_HTTP   = 1
	PROXY_TYPE_SOCK4  = 2
	PROXY_TYPE_SOCK5  = 3
	PROXY_TYPE_FTP    = 4
	PROXY_TYPE_UNKOWN = 255
)

type DOWN_PROXY_AUTH_TYPE int32

const (
	PROXY_AUTH_NONE = 0 + iota
	PROXY_AUTH_AUTO
	PROXY_AUTH_BASE64
	PROXY_AUTH_NTLM
	PROXY_AUTH_DEGEST
	PROXY_AUTH_UNKOWN
)

type DOWN_PROXY_INFO struct {
	IEProxy int32
	Proxy   int32
	PType   DOWN_PROXY_TYPE
	AType   DOWN_PROXY_AUTH_TYPE
	Host    [2048]uint16
	Port    int32
	User    [50]uint16
	Pwd     [50]uint16
	Domain  [2048]uint16
}

func XL_Init() bool {
	ret, _, _ := xl_Init.Call()
	return ret != 0
}

func XL_UnInit() bool {
	ret, _, _ := xl_UnInit.Call()
	return ret != 0
}

func XL_CreateTask(param *DownTaskParam) uintptr {
	ret, _, _ := xl_CreateTask.Call(uintptr(unsafe.Pointer(param)))
	return ret
}

func XL_DeleteTask(hTask uintptr) bool {
	ret, _, _ := xl_DeleteTask.Call(hTask)
	return ret != 0
}

func XL_StartTask(hTask uintptr) bool {
	ret, _, _ := xl_StartTask.Call(hTask)
	return ret != 0
}

func XL_StopTask(hTask uintptr) bool {
	ret, _, _ := xl_StopTask.Call(hTask)
	return ret != 0
}

func XL_ForceStopTask(hTask uintptr) bool {
	ret, _, _ := xl_ForceStopTask.Call(hTask)
	return ret != 0
}

func XL_SetSpeedLimit(nKBps int32) {
	xl_SetSpeedLimit.Call(uintptr(nKBps))
}

func XL_SetUploadSpeedLimit(nTcpKBps, nOtherKBps uint32) {
	xl_SetUploadSpeedLimit.Call(uintptr(nTcpKBps), uintptr(nOtherKBps))
}

func XL_SetUserAgent(pszUserAgent string) {
	xl_SetUserAgent.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszUserAgent))))
}

func XL_QueryTaskInfoEx(hTask uintptr) (*DownTaskInfo, bool) {
	info := &DownTaskInfo{}
	info.SetDefault()
	ret, _, _ := xl_QueryTaskInfoEx.Call(hTask, uintptr(unsafe.Pointer(info)))
	return info, ret != 0
}

func XL_SetProxy(stProxyInfo *DOWN_PROXY_INFO) bool {
	ret, _, _ := xl_SetProxy.Call(uintptr(unsafe.Pointer(stProxyInfo)))
	return ret != 0
}

func XL_DelTempFile(stParam *DownTaskParam) bool {
	ret, _, _ := xl_DelTempFile.Call(uintptr(unsafe.Pointer(stParam)))
	return ret != 0
}

func XL_ParseThunderPrivateUrl(pszThunderUrl string) (string, bool) {
	buffer := make([]uint16, 2084)
	ret, _, _ := xl_ParseThunderPrivateUrl.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszThunderUrl))),
		uintptr(unsafe.Pointer(&buffer[0])),
		2084)
	return syscall.UTF16ToString(buffer), ret != 0
}

func XL_GetFileSizeWithUrl(lpURL string) (int64, bool) {
	var iFileSize int64
	ret, _, _ := xl_GetFileSizeWithUrl.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpURL))),
		uintptr(unsafe.Pointer(&iFileSize)))
	return iFileSize, ret != 0
}

type FileId [40]byte

func XL_SetFileIdAndSize(hTask uintptr, szFileId FileId, nFileSize uint64) bool {
	ret, _, _ := xl_SetFileIdAndSize.Call(hTask,
		uintptr(unsafe.Pointer(&szFileId[0])),
		uintptr(unsafe.Pointer(&szFileId)))
	return ret != 0
}

func XL_CreateTaskByURL(url, path, filename string, IsResume int32) uintptr {
	ret, _, _ := xl_CreateTaskByURL.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(url))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(filename))),
		uintptr(IsResume))
	return ret
}

func XL_CreateTaskByThunder(pszUrl, pszFileName, pszReferUrl, pszCharSet, pszCookie string) uint32 {
	ret, _, _ := xl_CreateTaskByThunder.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszUrl))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszFileName))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszReferUrl))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszCharSet))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszCookie))))
	return uint32(ret)
}

func XL_CreateBTTaskByThunder(pszPath string) uint32 {
	ret, _, _ := xl_CreateBTTaskByThunder.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszPath))))
	return uint32(ret)
}
<commit_msg>DownTaskInfo的改变，<commit_after>// 迅雷下载sdk 翻译by: ying32  qq:1444386932
// api说明见：http://open.xunlei.com/wiki/api_doc.html#1
package xldl

import (
	"syscall"
	"unsafe"
)

var (
	xldldll                   = syscall.NewLazyDLL("xldl.dll")
	xl_Init                   = xldldll.NewProc("XL_Init")
	xl_UnInit                 = xldldll.NewProc("XL_UnInit")
	xl_CreateTask             = xldldll.NewProc("XL_CreateTask")
	xl_DeleteTask             = xldldll.NewProc("XL_DeleteTask")
	xl_StartTask              = xldldll.NewProc("XL_StartTask")
	xl_StopTask               = xldldll.NewProc("XL_StopTask")
	xl_ForceStopTask          = xldldll.NewProc("XL_ForceStopTask")
	xl_QueryTaskInfoEx        = xldldll.NewProc("XL_QueryTaskInfoEx")
	xl_DelTempFile            = xldldll.NewProc("XL_DelTempFile")
	xl_SetSpeedLimit          = xldldll.NewProc("XL_SetSpeedLimit")
	xl_SetUploadSpeedLimit    = xldldll.NewProc("XL_SetUploadSpeedLimit")
	xl_SetProxy               = xldldll.NewProc("XL_SetProxy")
	xl_SetUserAgent           = xldldll.NewProc("XL_SetUserAgent")
	xl_ParseThunderPrivateUrl = xldldll.NewProc("XL_ParseThunderPrivateUrl")
	xl_GetFileSizeWithUrl     = xldldll.NewProc("XL_GetFileSizeWithUrl")
	xl_SetFileIdAndSize       = xldldll.NewProc("XL_SetFileIdAndSize")

	//	xl_SetAdditionInfo        = xldldll.NewProc("XL_SetAdditionInfo")

	xl_CreateTaskByURL       = xldldll.NewProc("XL_CreateTaskByURL")
	xl_CreateTaskByThunder   = xldldll.NewProc("XL_CreateTaskByThunder")
	xl_CreateBTTaskByThunder = xldldll.NewProc("XL_CreateBTTaskByThunder")
)

const (
	MAX_PATH = 260
)

type DOWN_TASK_STATUS int32

const (
	NOITEM = 0 + iota
	TSC_ERROR
	TSC_PAUSE
	TSC_DOWNLOAD
	TSC_COMPLETE
	TSC_STARTPENDING
	TSC_STOPPENDING
)

type TASK_ERROR_TYPE int32

const (
	TASK_ERROR_UNKNOWN                = 0x00 // 未知错误
	TASK_ERROR_DISK_CREATE            = 0x01 // 创建文件失败
	TASK_ERROR_DISK_WRITE             = 0x02 // 写文件失败
	TASK_ERROR_DISK_READ              = 0x03 // 读文件失败
	TASK_ERROR_DISK_RENAME            = 0x04 // 重命名失败
	TASK_ERROR_DISK_PIECEHASH         = 0x05 // 文件片校验失败
	TASK_ERROR_DISK_FILEHASH          = 0x06 // 文件全文校验失败
	TASK_ERROR_DISK_DELETE            = 0x07 // 删除文件失败失败
	TASK_ERROR_DOWN_INVALID           = 0x10 // 无效的DOWN地址
	TASK_ERROR_PROXY_AUTH_TYPE_UNKOWN = 0x20 // 代理类型未知
	TASK_ERROR_PROXY_AUTH_TYPE_FAILED = 0x21 // 代理认证失败
	TASK_ERROR_HTTPMGR_NOT_IP         = 0x30 // http下载中无ip可用
	TASK_ERROR_TIMEOUT                = 0x40 // 任务超时
	TASK_ERROR_CANCEL                 = 0x41 // 任务取消
	TASK_ERROR_TP_CRASHED             = 0x42 // MINITP崩溃
	TASK_ERROR_ID_INVALID             = 0x43 // TaskId 非法
)

// 他按4字节对齐的，然迅雷要求按1字节对齐，so下面两个有问题了，要换种方式了
type DownTaskInfo struct {
	Stat          DOWN_TASK_STATUS
	FailCode      TASK_ERROR_TYPE
	Filename      [MAX_PATH]uint16
	Reserved0     [MAX_PATH]uint16
	TotalSize     int64   // 该任务总大小(字节)
	TotalDownload int64   // 下载有效字节数(可能存在回退的情况)
	Percent       float32 // 下载进度
	reserved0     int32
	SrcTotal      int32 // 总资源数
	SrcUsing      int32 // 可用资源数
	reserved1     int32
	reserved2     int32
	reserved3     int32
	reserved4     int32
	reserved5     int64
	DonationP2P   int64 // p2p贡献字节数
	reserved6     int64
	DonationOrgin int64 // 原始资源共享字节数
	DonationP2S   int64 // 镜像资源共享字节数
	reserved7     int64
	reserved8     int64
	Speed         int32 // 即时速度(字节/秒)
	SpeedP2S      int32 // 即时速度(字节/秒)
	SpeedP2P      int32 // 即时速度(字节/秒)
	// 下面要按1字节对齐就得换种方式了
	// IsOriginUsable bool    // 原始资源是否有效
	// HashPercent    float32 // 现不提供该值
	// IsCreatingFile bool    // 是否正在创建文件
	value    [6]byte
	reserved [64]uint32
}

func (c *DownTaskInfo) IsOriginUsable() bool {
	if c.value[0] == 1 {
		return true
	}
	return false
}

func (c *DownTaskInfo) SetIsOriginUsable(val bool) {
	if val {
		c.value[0] = 1
	} else {
		c.value[0] = 0
	}
}

func (c *DownTaskInfo) IsCreatingFile() bool {
	if c.value[5] == 1 {
		return true
	}
	return false
}

func (c *DownTaskInfo) SetIsCreatingFile(val bool) {
	if val {
		c.value[5] = 1
	} else {
		c.value[5] = 0
	}
}

//--------------------------------------------------

func (c *DownTaskInfo) SetDefault() {
	c.Stat = TSC_PAUSE
	c.FailCode = TASK_ERROR_UNKNOWN
	c.Percent = 0.0
	c.SetIsOriginUsable(false)
	c.HashPercent = 0
}

type DownTaskParam struct {
	nReserved         int32
	szTaskUrl         [2084]uint16     // 任务URL
	szRefUrl          [2084]uint16     // 引用页
	szCookies         [4096]uint16     // 浏览器cookie
	szFilename        [MAX_PATH]uint16 // 下载保存文件名.
	szReserved0       [MAX_PATH]uint16
	szSavePath        [MAX_PATH]uint16 // 文件保存目录
	hReserved         int32
	bReserved         int32
	szReserved1       [64]uint16
	szReserved2       [64]uint16
	IsOnlyOriginal    int32 // 是否只从原始地址下载
	nReserved1        uint32
	DisableAutoRename int32 // 禁止智能命名
	IsResume          int32 // 是否用续传
	reserved          [2048]uint32
}

func (c *DownTaskParam) SetDefault() {
	c.nReserved1 = 5
	c.bReserved = 0
	c.DisableAutoRename = 0
	c.IsOnlyOriginal = 0
	c.IsResume = 1
}

type DOWN_PROXY_TYPE int32

const (
	PROXY_TYPE_IE     = 0
	PROXY_TYPE_HTTP   = 1
	PROXY_TYPE_SOCK4  = 2
	PROXY_TYPE_SOCK5  = 3
	PROXY_TYPE_FTP    = 4
	PROXY_TYPE_UNKOWN = 255
)

type DOWN_PROXY_AUTH_TYPE int32

const (
	PROXY_AUTH_NONE = 0 + iota
	PROXY_AUTH_AUTO
	PROXY_AUTH_BASE64
	PROXY_AUTH_NTLM
	PROXY_AUTH_DEGEST
	PROXY_AUTH_UNKOWN
)

type DOWN_PROXY_INFO struct {
	IEProxy int32
	Proxy   int32
	PType   DOWN_PROXY_TYPE
	AType   DOWN_PROXY_AUTH_TYPE
	Host    [2048]uint16
	Port    int32
	User    [50]uint16
	Pwd     [50]uint16
	Domain  [2048]uint16
}

func XL_Init() bool {
	ret, _, _ := xl_Init.Call()
	return ret != 0
}

func XL_UnInit() bool {
	ret, _, _ := xl_UnInit.Call()
	return ret != 0
}

func XL_CreateTask(param *DownTaskParam) uintptr {
	ret, _, _ := xl_CreateTask.Call(uintptr(unsafe.Pointer(param)))
	return ret
}

func XL_DeleteTask(hTask uintptr) bool {
	ret, _, _ := xl_DeleteTask.Call(hTask)
	return ret != 0
}

func XL_StartTask(hTask uintptr) bool {
	ret, _, _ := xl_StartTask.Call(hTask)
	return ret != 0
}

func XL_StopTask(hTask uintptr) bool {
	ret, _, _ := xl_StopTask.Call(hTask)
	return ret != 0
}

func XL_ForceStopTask(hTask uintptr) bool {
	ret, _, _ := xl_ForceStopTask.Call(hTask)
	return ret != 0
}

func XL_SetSpeedLimit(nKBps int32) {
	xl_SetSpeedLimit.Call(uintptr(nKBps))
}

func XL_SetUploadSpeedLimit(nTcpKBps, nOtherKBps uint32) {
	xl_SetUploadSpeedLimit.Call(uintptr(nTcpKBps), uintptr(nOtherKBps))
}

func XL_SetUserAgent(pszUserAgent string) {
	xl_SetUserAgent.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszUserAgent))))
}

func XL_QueryTaskInfoEx(hTask uintptr) (*DownTaskInfo, bool) {
	info := &DownTaskInfo{}
	info.SetDefault()
	ret, _, _ := xl_QueryTaskInfoEx.Call(hTask, uintptr(unsafe.Pointer(info)))
	return info, ret != 0
}

func XL_SetProxy(stProxyInfo *DOWN_PROXY_INFO) bool {
	ret, _, _ := xl_SetProxy.Call(uintptr(unsafe.Pointer(stProxyInfo)))
	return ret != 0
}

func XL_DelTempFile(stParam *DownTaskParam) bool {
	ret, _, _ := xl_DelTempFile.Call(uintptr(unsafe.Pointer(stParam)))
	return ret != 0
}

func XL_ParseThunderPrivateUrl(pszThunderUrl string) (string, bool) {
	buffer := make([]uint16, 2084)
	ret, _, _ := xl_ParseThunderPrivateUrl.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszThunderUrl))),
		uintptr(unsafe.Pointer(&buffer[0])),
		2084)
	return syscall.UTF16ToString(buffer), ret != 0
}

func XL_GetFileSizeWithUrl(lpURL string) (int64, bool) {
	var iFileSize int64
	ret, _, _ := xl_GetFileSizeWithUrl.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpURL))),
		uintptr(unsafe.Pointer(&iFileSize)))
	return iFileSize, ret != 0
}

type FileId [40]byte

func XL_SetFileIdAndSize(hTask uintptr, szFileId FileId, nFileSize uint64) bool {
	ret, _, _ := xl_SetFileIdAndSize.Call(hTask,
		uintptr(unsafe.Pointer(&szFileId[0])),
		uintptr(unsafe.Pointer(&szFileId)))
	return ret != 0
}

func XL_CreateTaskByURL(url, path, filename string, IsResume int32) uintptr {
	ret, _, _ := xl_CreateTaskByURL.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(url))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(filename))),
		uintptr(IsResume))
	return ret
}

func XL_CreateTaskByThunder(pszUrl, pszFileName, pszReferUrl, pszCharSet, pszCookie string) uint32 {
	ret, _, _ := xl_CreateTaskByThunder.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszUrl))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszFileName))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszReferUrl))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszCharSet))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszCookie))))
	return uint32(ret)
}

func XL_CreateBTTaskByThunder(pszPath string) uint32 {
	ret, _, _ := xl_CreateBTTaskByThunder.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(pszPath))))
	return uint32(ret)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/mattn/go-colorable"
	"github.com/zetamatta/go-box"
)

var optionIndex = flag.Bool("index", false, "print index as result")

func main1(args []string) error {
	data, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		return err
	}
	data = box.AnsiCutter.ReplaceAll(data, []byte{})
	list := strings.Split(string(data), "\n")
	switch len(list) {
	case 0:
		return nil
	case 1:
		fmt.Println(strings.TrimSpace(list[0]))
		return nil
	}
	for i := 0; i < len(list); i++ {
		list[i] = strings.TrimSpace(list[i])
	}
	console := colorable.NewColorableStderr()
	index := box.Choose(list, console)
	fmt.Fprintln(console)

	if *optionIndex {
		fmt.Println(index)
	} else {
		if index >= 0 {
			fmt.Println(list[index])
		} else {
			fmt.Println("canceled")
		}
	}
	return nil
}

func main() {
	flag.Parse()
	if err := main1(flag.Args()); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}
	os.Exit(0)
}
<commit_msg>Let sample use v2<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/mattn/go-colorable"
	"github.com/zetamatta/go-box/v2"
)

var optionIndex = flag.Bool("index", false, "print index as result")

func main1(args []string) error {
	data, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		return err
	}
	data = box.AnsiCutter.ReplaceAll(data, []byte{})
	list := strings.Split(string(data), "\n")
	switch len(list) {
	case 0:
		return nil
	case 1:
		fmt.Println(strings.TrimSpace(list[0]))
		return nil
	}
	for i := 0; i < len(list); i++ {
		list[i] = strings.TrimSpace(list[i])
	}
	console := colorable.NewColorableStderr()
	index := box.Choose(list, console)
	fmt.Fprintln(console)

	if *optionIndex {
		fmt.Println(index)
	} else {
		if index >= 0 {
			fmt.Println(list[index])
		} else {
			fmt.Println("canceled")
		}
	}
	return nil
}

func main() {
	flag.Parse()
	if err := main1(flag.Args()); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

func main() {
	CallC()
}

<commit_msg>old file<commit_after><|endoftext|>


<commit_before>package mem

import (
	"encoding/json"

	"github.com/shirou/gopsutil/internal/common"
)

var invoke common.Invoker = common.Invoke{}

// Memory usage statistics. Total, Available and Used contain numbers of bytes
// for human consumption.
//
// The other fields in this struct contain kernel specific values.
type VirtualMemoryStat struct {
	// Total amount of RAM on this system
	Total uint64 `json:"total"`

	// RAM available for programs to allocate
	//
	// This value is computed from the kernel specific values.
	Available uint64 `json:"available"`

	// RAM used by programs
	//
	// This value is computed from the kernel specific values.
	Used uint64 `json:"used"`

	// Percentage of RAM used by programs
	//
	// This value is computed from the kernel specific values.
	UsedPercent float64 `json:"usedPercent"`

	// This is the kernel's notion of free memory; RAM chips whose bits nobody
	// cares about the value of right now. For a human consumable number,
	// Available is what you really want.
	Free uint64 `json:"free"`

	// OS X / BSD specific numbers:
	// http://www.macyourself.com/2010/02/17/what-is-free-wired-active-and-inactive-system-memory-ram/
	Active   uint64 `json:"active"`
	Inactive uint64 `json:"inactive"`
	Wired    uint64 `json:"wired"`

	// Linux specific numbers
	// https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html
	// https://www.kernel.org/doc/Documentation/filesystems/proc.txt
	Buffers      uint64 `json:"buffers"`
	Cached       uint64 `json:"cached"`
	Writeback    uint64 `json:"writeback"`
	Dirty        uint64 `json:"dirty"`
	WritebackTmp uint64 `json:"writebacktmp"`
	Shared       uint64 `json:"shared"`
	Slab         uint64 `json:"slab"`
	PageTables   uint64 `json:"pagetables"`
	SwapCached   uint64 `json:"swapcached"`
	CommitLimit  uint64 `json:"commitlimit"`
	CommittedAS  uint64 `json:"committedas"`
}

type SwapMemoryStat struct {
	Total       uint64  `json:"total"`
	Used        uint64  `json:"used"`
	Free        uint64  `json:"free"`
	UsedPercent float64 `json:"usedPercent"`
	Sin         uint64  `json:"sin"`
	Sout        uint64  `json:"sout"`
}

func (m VirtualMemoryStat) String() string {
	s, _ := json.Marshal(m)
	return string(s)
}

func (m SwapMemoryStat) String() string {
	s, _ := json.Marshal(m)
	return string(s)
}
<commit_msg>[memory] Linux: add document url about OverCommiting.<commit_after>package mem

import (
	"encoding/json"

	"github.com/shirou/gopsutil/internal/common"
)

var invoke common.Invoker = common.Invoke{}

// Memory usage statistics. Total, Available and Used contain numbers of bytes
// for human consumption.
//
// The other fields in this struct contain kernel specific values.
type VirtualMemoryStat struct {
	// Total amount of RAM on this system
	Total uint64 `json:"total"`

	// RAM available for programs to allocate
	//
	// This value is computed from the kernel specific values.
	Available uint64 `json:"available"`

	// RAM used by programs
	//
	// This value is computed from the kernel specific values.
	Used uint64 `json:"used"`

	// Percentage of RAM used by programs
	//
	// This value is computed from the kernel specific values.
	UsedPercent float64 `json:"usedPercent"`

	// This is the kernel's notion of free memory; RAM chips whose bits nobody
	// cares about the value of right now. For a human consumable number,
	// Available is what you really want.
	Free uint64 `json:"free"`

	// OS X / BSD specific numbers:
	// http://www.macyourself.com/2010/02/17/what-is-free-wired-active-and-inactive-system-memory-ram/
	Active   uint64 `json:"active"`
	Inactive uint64 `json:"inactive"`
	Wired    uint64 `json:"wired"`

	// Linux specific numbers
	// https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html
	// https://www.kernel.org/doc/Documentation/filesystems/proc.txt
	// https://www.kernel.org/doc/Documentation/vm/overcommit-accounting
	Buffers      uint64 `json:"buffers"`
	Cached       uint64 `json:"cached"`
	Writeback    uint64 `json:"writeback"`
	Dirty        uint64 `json:"dirty"`
	WritebackTmp uint64 `json:"writebacktmp"`
	Shared       uint64 `json:"shared"`
	Slab         uint64 `json:"slab"`
	PageTables   uint64 `json:"pagetables"`
	SwapCached   uint64 `json:"swapcached"`
	CommitLimit  uint64 `json:"commitlimit"`
	CommittedAS  uint64 `json:"committedas"`
}

type SwapMemoryStat struct {
	Total       uint64  `json:"total"`
	Used        uint64  `json:"used"`
	Free        uint64  `json:"free"`
	UsedPercent float64 `json:"usedPercent"`
	Sin         uint64  `json:"sin"`
	Sout        uint64  `json:"sout"`
}

func (m VirtualMemoryStat) String() string {
	s, _ := json.Marshal(m)
	return string(s)
}

func (m SwapMemoryStat) String() string {
	s, _ := json.Marshal(m)
	return string(s)
}
<|endoftext|>


<commit_before>package thrift

import (
	"reflect"
	// "runtime"
)

type Decoder struct {
	Protocol Protocol
}

func (d *Decoder) error(err interface{}) {
	panic(err)
}

func (d *Decoder) ReadStruct(v interface{}) (err error) {
	// defer func() {
	// 	if r := recover(); r != nil {
	// 		if _, ok := r.(runtime.Error); ok {
	// 			panic(r)
	// 		}
	// 		err = r.(error)
	// 	}
	// }()
	vo := reflect.ValueOf(v)
	for vo.Kind() != reflect.Ptr {
		d.error(&UnsupportedValueError{Value: vo, Str: "pointer to struct expected"})
	}
	if vo.Elem().Kind() != reflect.Struct {
		d.error(&UnsupportedValueError{Value: vo, Str: "expected a struct"})
	}
	d.readValue(typeStruct, vo.Elem())
	return nil
}

func (d *Decoder) readValue(ftype byte, rf reflect.Value) {
	v := rf
	if rf.Kind() == reflect.Ptr {
		if rf.IsNil() {
			rf.Set(reflect.New(rf.Type().Elem()))
		}
		v = rf.Elem()
	}

	if ftype != fieldType(v.Type()) {
		d.error(&UnsupportedValueError{Value: v, Str: "type mistmatch"})
	}

	var err error = nil
	switch v.Kind() {
	case reflect.Bool:
		if val, err := d.Protocol.ReadBool(); err != nil {
			d.error(err)
		} else {
			v.SetBool(val)
		}
	case reflect.Int8:
		if val, err := d.Protocol.ReadByte(); err != nil {
			d.error(err)
		} else {
			v.SetInt(int64(val))
		}
	case reflect.Int16:
		if val, err := d.Protocol.ReadI16(); err != nil {
			d.error(err)
		} else {
			v.SetInt(int64(val))
		}
	case reflect.Int32, reflect.Int:
		if val, err := d.Protocol.ReadI32(); err != nil {
			d.error(err)
		} else {
			v.SetInt(int64(val))
		}
	case reflect.Int64:
		if val, err := d.Protocol.ReadI64(); err != nil {
			d.error(err)
		} else {
			v.SetInt(val)
		}
	case reflect.Float64:
		if val, err := d.Protocol.ReadDouble(); err != nil {
			d.error(err)
		} else {
			v.SetFloat(val)
		}
	case reflect.String:
		if val, err := d.Protocol.ReadString(); err != nil {
			d.error(err)
		} else {
			v.SetString(val)
		}
	case reflect.Struct:
		if err := d.Protocol.ReadStructBegin(); err != nil {
			d.error(err)
		}

		fields := encodeFields(v.Type())
		for {
			ftype, id, err := d.Protocol.ReadFieldBegin()
			if err != nil {
				d.error(err)
			}
			if ftype == typeStop {
				break
			}

			ef, ok := fields[int(id)]
			if !ok {
				// Ignore unknown fields
				// TODO
				d.error(&UnsupportedValueError{Str: "TODO"})
			} else {
				fieldValue := v.Field(ef.i)
				d.readValue(ftype, fieldValue)
			}

			if err = d.Protocol.ReadFieldEnd(); err != nil {
				d.error(err)
			}
		}

		if err := d.Protocol.ReadStructEnd(); err != nil {
			d.error(err)
		}
	case reflect.Map:
		keyType := v.Type().Key()
		valueType := v.Type().Elem()
		ktype, vtype, n, err := d.Protocol.ReadMapBegin()
		if err != nil {
			d.error(err)
		}
		v.Set(reflect.MakeMap(v.Type()))
		for i := 0; i < n; i++ {
			key := reflect.New(keyType).Elem()
			val := reflect.New(valueType).Elem()
			d.readValue(ktype, key)
			d.readValue(vtype, val)
			v.SetMapIndex(key, val)
		}
		if err := d.Protocol.ReadMapEnd(); err != nil {
			d.error(err)
		}
	case reflect.Slice, reflect.Array:
		elemType := v.Type().Elem()
		et, n, err := d.Protocol.ReadListBegin()
		if err != nil {
			d.error(err)
		}
		for i := 0; i < n; i++ {
			val := reflect.New(elemType)
			d.readValue(et, val.Elem())
			v.Set(reflect.Append(v, val.Elem()))
		}
		if err := d.Protocol.ReadListEnd(); err != nil {
			d.error(err)
		}
	default:
		d.error(&UnsupportedTypeError{v.Type()})
	}

	if err != nil {
		d.error(err)
	}

	return
}

func (d *Decoder) readSimpleValue(fieldType int) (val interface{}) {
	var err error = nil
	switch fieldType {
	case typeBool:
		val, err = d.Protocol.ReadBool()
	case typeByte:
		val, err = d.Protocol.ReadByte()
	case typeI16:
		val, err = d.Protocol.ReadI16()
	case typeI32:
		val, err = d.Protocol.ReadI32()
	case typeI64:
		val, err = d.Protocol.ReadI64()
	case typeDouble:
		val, err = d.Protocol.ReadDouble()
	case typeString:
		val, err = d.Protocol.ReadString()
	default:
		d.error(&UnsupportedTypeError{})
	}

	if err != nil {
		d.error(err)
	}

	return
}
<commit_msg>Fix error in decoder<commit_after>package thrift

import (
	"reflect"
	// "runtime"
)

type Decoder struct {
	Protocol Protocol
}

func (d *Decoder) error(err interface{}) {
	panic(err)
}

func (d *Decoder) ReadStruct(v interface{}) (err error) {
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(runtime.Error); ok {
				panic(r)
			}
			err = r.(error)
		}
	}()
	vo := reflect.ValueOf(v)
	for vo.Kind() != reflect.Ptr {
		d.error(&UnsupportedValueError{Value: vo, Str: "pointer to struct expected"})
	}
	if vo.Elem().Kind() != reflect.Struct {
		d.error(&UnsupportedValueError{Value: vo, Str: "expected a struct"})
	}
	d.readValue(typeStruct, vo.Elem())
	return nil
}

func (d *Decoder) readValue(ftype byte, rf reflect.Value) {
	v := rf
	if rf.Kind() == reflect.Ptr {
		if rf.IsNil() {
			rf.Set(reflect.New(rf.Type().Elem()))
		}
		v = rf.Elem()
	}

	if ftype != fieldType(v.Type()) {
		d.error(&UnsupportedValueError{Value: v, Str: "type mistmatch"})
	}

	var err error = nil
	switch v.Kind() {
	case reflect.Bool:
		if val, err := d.Protocol.ReadBool(); err != nil {
			d.error(err)
		} else {
			v.SetBool(val)
		}
	case reflect.Int8:
		if val, err := d.Protocol.ReadByte(); err != nil {
			d.error(err)
		} else {
			v.SetInt(int64(val))
		}
	case reflect.Int16:
		if val, err := d.Protocol.ReadI16(); err != nil {
			d.error(err)
		} else {
			v.SetInt(int64(val))
		}
	case reflect.Int32, reflect.Int:
		if val, err := d.Protocol.ReadI32(); err != nil {
			d.error(err)
		} else {
			v.SetInt(int64(val))
		}
	case reflect.Int64:
		if val, err := d.Protocol.ReadI64(); err != nil {
			d.error(err)
		} else {
			v.SetInt(val)
		}
	case reflect.Float64:
		if val, err := d.Protocol.ReadDouble(); err != nil {
			d.error(err)
		} else {
			v.SetFloat(val)
		}
	case reflect.String:
		if val, err := d.Protocol.ReadString(); err != nil {
			d.error(err)
		} else {
			v.SetString(val)
		}
	case reflect.Struct:
		if err := d.Protocol.ReadStructBegin(); err != nil {
			d.error(err)
		}

		fields := encodeFields(v.Type())
		for {
			ftype, id, err := d.Protocol.ReadFieldBegin()
			if err != nil {
				d.error(err)
			}
			if ftype == typeStop {
				break
			}

			ef, ok := fields[int(id)]
			if !ok {
				// Ignore unknown fields
				// TODO
				d.error(&UnsupportedValueError{Str: "TODO"})
			} else {
				fieldValue := v.Field(ef.i)
				d.readValue(ftype, fieldValue)
			}

			if err = d.Protocol.ReadFieldEnd(); err != nil {
				d.error(err)
			}
		}

		if err := d.Protocol.ReadStructEnd(); err != nil {
			d.error(err)
		}
	case reflect.Map:
		keyType := v.Type().Key()
		valueType := v.Type().Elem()
		ktype, vtype, n, err := d.Protocol.ReadMapBegin()
		if err != nil {
			d.error(err)
		}
		v.Set(reflect.MakeMap(v.Type()))
		for i := 0; i < n; i++ {
			key := reflect.New(keyType).Elem()
			val := reflect.New(valueType).Elem()
			d.readValue(ktype, key)
			d.readValue(vtype, val)
			v.SetMapIndex(key, val)
		}
		if err := d.Protocol.ReadMapEnd(); err != nil {
			d.error(err)
		}
	case reflect.Slice, reflect.Array:
		elemType := v.Type().Elem()
		et, n, err := d.Protocol.ReadListBegin()
		if err != nil {
			d.error(err)
		}
		for i := 0; i < n; i++ {
			val := reflect.New(elemType)
			d.readValue(et, val.Elem())
			v.Set(reflect.Append(v, val.Elem()))
		}
		if err := d.Protocol.ReadListEnd(); err != nil {
			d.error(err)
		}
	default:
		d.error(&UnsupportedTypeError{v.Type()})
	}

	if err != nil {
		d.error(err)
	}

	return
}

func (d *Decoder) readSimpleValue(fieldType int) (val interface{}) {
	var err error = nil
	switch fieldType {
	case typeBool:
		val, err = d.Protocol.ReadBool()
	case typeByte:
		val, err = d.Protocol.ReadByte()
	case typeI16:
		val, err = d.Protocol.ReadI16()
	case typeI32:
		val, err = d.Protocol.ReadI32()
	case typeI64:
		val, err = d.Protocol.ReadI64()
	case typeDouble:
		val, err = d.Protocol.ReadDouble()
	case typeString:
		val, err = d.Protocol.ReadString()
	default:
		d.error(&UnsupportedTypeError{})
	}

	if err != nil {
		d.error(err)
	}

	return
}
<|endoftext|>


<commit_before>package toolbox

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
)

//Decoder represents a decoder.
type Decoder interface {
	//Decode  reads and decodes objects from an input stream.
	Decode(v interface{}) error
}

//UnMarshaler represent an struct that can be converted to bytes
type UnMarshaler interface {

	//Unmarshal converts a struct to bytes
	Unmarshal(data []byte) error
}

//DecoderFactory create an decoder for passed in  input stream
type DecoderFactory interface {
	//Create a decoder for passed in io reader
	Create(reader io.Reader) Decoder
}

type jsonDecoderFactory struct{ useNumber bool }

func (d jsonDecoderFactory) Create(reader io.Reader) Decoder {
	decoder := json.NewDecoder(reader)
	if d.useNumber {
		decoder.UseNumber()
	}
	return decoder
}

//NewJSONDecoderFactory create a new JSONDecoderFactory
func NewJSONDecoderFactory() DecoderFactory {
	return &jsonDecoderFactory{}
}

//NewJSONDecoderFactoryWithOption create a new JSONDecoderFactory, it takes useNumber decoder parameter
func NewJSONDecoderFactoryWithOption(useNumber bool) DecoderFactory {
	return &jsonDecoderFactory{useNumber: useNumber}
}

type unMarshalerDecoderFactory struct {
}

func (f *unMarshalerDecoderFactory) Create(reader io.Reader) Decoder {
	return &unMarshalerDecoder{
		reader: reader,
	}
}

type unMarshalerDecoder struct {
	reader   io.Reader
	provider func() UnMarshaler
}

func (d *unMarshalerDecoder) Decode(v interface{}) error {
	bytes, err := ioutil.ReadAll(d.reader)
	if err != nil {
		return fmt.Errorf("Failed to decode %v", err)
	}
	result, casted := v.(UnMarshaler)
	if !casted {
		return fmt.Errorf("Failed to decode - unable cast %T to %s", v, (*UnMarshaler)(nil))
	}
	return result.Unmarshal(bytes)
}

//NewUnMarshalerDecoderFactory returns a decoder factory
func NewUnMarshalerDecoderFactory() DecoderFactory {
	return &unMarshalerDecoderFactory{}
}

//DelimiteredRecord represents a delimitered record
type DelimiteredRecord struct {
	Columns   []string
	Delimiter string
	Record    map[string]interface{}
}

//IsEmpty returns true if all values are empty or null
func (r *DelimiteredRecord) IsEmpty() bool {
	var result = true
	for _, value := range r.Record {
		if value == nil {
			continue
		}
		if AsString(value) == "" {
			continue
		}
		return false
	}
	return result
}

type delimiterDecoder struct {
	reader io.Reader
}

func (d *delimiterDecoder) Decode(target interface{}) error {
	delimiteredRecord, ok := target.(*DelimiteredRecord)
	if !ok {
		return fmt.Errorf("Invalid target type, expected %T but had %T", &DelimiteredRecord{}, target)
	}
	if delimiteredRecord.Record == nil {
		delimiteredRecord.Record = make(map[string]interface{})
	}

	var isInDoubleQuote = false
	var index = 0
	var value = ""
	var delimiter = delimiteredRecord.Delimiter
	payload, err := ioutil.ReadAll(d.reader)
	if err != nil {
		return err
	}
	encoded := string(payload)
	hasColumns := len(delimiteredRecord.Columns) > 0
	if !hasColumns {
		delimiteredRecord.Columns = make([]string, 0)
	}
	for i := 0; i < len(encoded); i++ {
		aChar := string(encoded[i : i+1])
		//escape " only if value is already inside "s
		if isInDoubleQuote && ((aChar == "\\" || aChar == "\"") && i+2 <len(encoded) ) {
			nextChar := encoded[i+1 : i+2]
			if nextChar == "\"" {
				i++
				value = value + nextChar
				continue
			}
		}
		//allow unescaped " be inside text if the whole text is not enclosed in "s
		if aChar == "\"" && (len(value) == 0 || isInDoubleQuote) {
			isInDoubleQuote = !isInDoubleQuote
			continue
		}

		if encoded[i:i+1] == delimiter && !isInDoubleQuote {
			if !hasColumns {
				delimiteredRecord.Columns = append(delimiteredRecord.Columns, strings.TrimSpace(value))
			} else {
				var columnName = delimiteredRecord.Columns[index]
				delimiteredRecord.Record[columnName] = value
			}

			value = ""
			index++
			continue
		}
		value = value + aChar
	}
	if len(value) > 0 {
		if !hasColumns {
			delimiteredRecord.Columns = append(delimiteredRecord.Columns, strings.TrimSpace(value))
		} else {
			if index >= len(delimiteredRecord.Columns) {
				return fmt.Errorf("Index %v out of bound: columns: %v, values:%v", index, delimiteredRecord.Columns, encoded)
			}
			var columnName = delimiteredRecord.Columns[index]
			delimiteredRecord.Record[columnName] = value
		}
	}
	return nil
}

type delimiterDecoderFactory struct{}

func (f *delimiterDecoderFactory) Create(reader io.Reader) Decoder {
	return &delimiterDecoder{reader: reader}
}

//NewDelimiterDecoderFactory returns a new delimitered decoder factory.
func NewDelimiterDecoderFactory() DecoderFactory {
	return &delimiterDecoderFactory{}
}
<commit_msg>reformated<commit_after>package toolbox

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
)

//Decoder represents a decoder.
type Decoder interface {
	//Decode  reads and decodes objects from an input stream.
	Decode(v interface{}) error
}

//UnMarshaler represent an struct that can be converted to bytes
type UnMarshaler interface {

	//Unmarshal converts a struct to bytes
	Unmarshal(data []byte) error
}

//DecoderFactory create an decoder for passed in  input stream
type DecoderFactory interface {
	//Create a decoder for passed in io reader
	Create(reader io.Reader) Decoder
}

type jsonDecoderFactory struct{ useNumber bool }

func (d jsonDecoderFactory) Create(reader io.Reader) Decoder {
	decoder := json.NewDecoder(reader)
	if d.useNumber {
		decoder.UseNumber()
	}
	return decoder
}

//NewJSONDecoderFactory create a new JSONDecoderFactory
func NewJSONDecoderFactory() DecoderFactory {
	return &jsonDecoderFactory{}
}

//NewJSONDecoderFactoryWithOption create a new JSONDecoderFactory, it takes useNumber decoder parameter
func NewJSONDecoderFactoryWithOption(useNumber bool) DecoderFactory {
	return &jsonDecoderFactory{useNumber: useNumber}
}

type unMarshalerDecoderFactory struct {
}

func (f *unMarshalerDecoderFactory) Create(reader io.Reader) Decoder {
	return &unMarshalerDecoder{
		reader: reader,
	}
}

type unMarshalerDecoder struct {
	reader   io.Reader
	provider func() UnMarshaler
}

func (d *unMarshalerDecoder) Decode(v interface{}) error {
	bytes, err := ioutil.ReadAll(d.reader)
	if err != nil {
		return fmt.Errorf("Failed to decode %v", err)
	}
	result, casted := v.(UnMarshaler)
	if !casted {
		return fmt.Errorf("Failed to decode - unable cast %T to %s", v, (*UnMarshaler)(nil))
	}
	return result.Unmarshal(bytes)
}

//NewUnMarshalerDecoderFactory returns a decoder factory
func NewUnMarshalerDecoderFactory() DecoderFactory {
	return &unMarshalerDecoderFactory{}
}

//DelimiteredRecord represents a delimitered record
type DelimiteredRecord struct {
	Columns   []string
	Delimiter string
	Record    map[string]interface{}
}

//IsEmpty returns true if all values are empty or null
func (r *DelimiteredRecord) IsEmpty() bool {
	var result = true
	for _, value := range r.Record {
		if value == nil {
			continue
		}
		if AsString(value) == "" {
			continue
		}
		return false
	}
	return result
}

type delimiterDecoder struct {
	reader io.Reader
}

func (d *delimiterDecoder) Decode(target interface{}) error {
	delimiteredRecord, ok := target.(*DelimiteredRecord)
	if !ok {
		return fmt.Errorf("Invalid target type, expected %T but had %T", &DelimiteredRecord{}, target)
	}
	if delimiteredRecord.Record == nil {
		delimiteredRecord.Record = make(map[string]interface{})
	}

	var isInDoubleQuote = false
	var index = 0
	var value = ""
	var delimiter = delimiteredRecord.Delimiter
	payload, err := ioutil.ReadAll(d.reader)
	if err != nil {
		return err
	}
	encoded := string(payload)
	hasColumns := len(delimiteredRecord.Columns) > 0
	if !hasColumns {
		delimiteredRecord.Columns = make([]string, 0)
	}
	for i := 0; i < len(encoded); i++ {
		aChar := string(encoded[i : i+1])
		//escape " only if value is already inside "s
		if isInDoubleQuote && ((aChar == "\\" || aChar == "\"") && i+2 < len(encoded)) {
			nextChar := encoded[i+1 : i+2]
			if nextChar == "\"" {
				i++
				value = value + nextChar
				continue
			}
		}
		//allow unescaped " be inside text if the whole text is not enclosed in "s
		if aChar == "\"" && (len(value) == 0 || isInDoubleQuote) {
			isInDoubleQuote = !isInDoubleQuote
			continue
		}

		if encoded[i:i+1] == delimiter && !isInDoubleQuote {
			if !hasColumns {
				delimiteredRecord.Columns = append(delimiteredRecord.Columns, strings.TrimSpace(value))
			} else {
				var columnName = delimiteredRecord.Columns[index]
				delimiteredRecord.Record[columnName] = value
			}

			value = ""
			index++
			continue
		}
		value = value + aChar
	}
	if len(value) > 0 {
		if !hasColumns {
			delimiteredRecord.Columns = append(delimiteredRecord.Columns, strings.TrimSpace(value))
		} else {
			if index >= len(delimiteredRecord.Columns) {
				return fmt.Errorf("Index %v out of bound: columns: %v, values:%v", index, delimiteredRecord.Columns, encoded)
			}
			var columnName = delimiteredRecord.Columns[index]
			delimiteredRecord.Record[columnName] = value
		}
	}
	return nil
}

type delimiterDecoderFactory struct{}

func (f *delimiterDecoderFactory) Create(reader io.Reader) Decoder {
	return &delimiterDecoder{reader: reader}
}

//NewDelimiterDecoderFactory returns a new delimitered decoder factory.
func NewDelimiterDecoderFactory() DecoderFactory {
	return &delimiterDecoderFactory{}
}
<|endoftext|>


<commit_before>package rmarsh

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

// A Decoder decodes a Ruby Marshal stream into concrete Golang structures.
type Decoder struct {
	p        *Parser
	objCache map[int]reflect.Value
	curToken Token
}

// NewDecoder builds a new Decoder that uses given Parser to decode a Ruby Marshal stream.
func NewDecoder(p *Parser) *Decoder {
	return &Decoder{p: p, objCache: make(map[int]reflect.Value)}
}

// ReadValue will consume a full Ruby Marshal stream from the given io.Reader and return a fully decoded Golang object.
func ReadValue(r io.Reader, val interface{}) error {
	// TODO: grab Parser instance from a sync.Pool
	return NewDecoder(NewParser(r)).Decode(val)
}

// Decode will consume a value from the underlying parser and marshal it into the provided Golang type.
func (d *Decoder) Decode(val interface{}) error {
	v := reflect.ValueOf(val)
	if v.Kind() != reflect.Ptr {
		return fmt.Errorf("Invalid decode target %T, did you forget to pass a pointer?", val)
	}

	return d.valueDecoder(v.Elem())(d, v.Elem())
}

func (d *Decoder) nextToken() (Token, error) {
	if d.curToken != tokenInvalid {
		tok := d.curToken
		d.curToken = tokenInvalid
		return tok, nil
	}
	return d.p.Next()
}

type decoderFunc func(*Decoder, reflect.Value) error

func (d *Decoder) valueDecoder(v reflect.Value) decoderFunc {
	return d.typeDecoder(v.Type())
}

var decCache struct {
	sync.RWMutex
	m map[reflect.Type]decoderFunc
}

func (d *Decoder) typeDecoder(t reflect.Type) decoderFunc {
	decCache.RLock()
	dec := decCache.m[t]
	decCache.RUnlock()
	if dec != nil {
		return dec
	}

	decCache.Lock()
	defer decCache.Unlock()
	if decCache.m == nil {
		decCache.m = make(map[reflect.Type]decoderFunc)
	}
	decCache.m[t] = newTypeDecoder(t)
	return decCache.m[t]
}

func newTypeDecoder(t reflect.Type) decoderFunc {
	switch t.Kind() {
	case reflect.Invalid:
		return skipDecoder
	case reflect.Bool:
		return boolDecoder
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intDecoder
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return uintDecoder
	case reflect.Float32, reflect.Float64:
		return floatDecoder
	case reflect.String:
		return stringDecoder
	case reflect.Slice:
		return newSliceDecoder(t)
	case reflect.Struct:
		return newStructDecoder(t)
	case reflect.Ptr:
		// Since Ruby doesn't offer pointer types
		// We can simplify our type handling by normalising all Go pointers down to
		// single depth. e.g if we've been passed a ***string, we'll normalise it
		// down to *string before doing anything with it.
		if t.Elem().Kind() == reflect.Ptr {
			return newPtrIndirector(t)
		}
		return newPtrDecoder(t)
	}
	return unsupportedTypeDecoder
}

func skipDecoder(d *Decoder, v reflect.Value) error {
	_, err := d.p.readNext()
	if err != nil {
		return err
	}
	return d.p.Skip()
}

func boolDecoder(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenTrue, TokenFalse:
		v.SetBool(tok == TokenTrue)
		return nil
	default:
		// TODO: build a path
		return fmt.Errorf("Unexpected token %v encountered while decoding bool", tok)
	}
}
func intDecoder(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenFixnum:
		n, err := d.p.Int()
		if err != nil {
			return err
		}
		nn := int64(n)
		if v.OverflowInt(nn) {
			return fmt.Errorf("Decoded int %d exceeds maximum width of %s", n, v.Type())
		}
		v.SetInt(nn)
		return nil
	default:
		return fmt.Errorf("Unexpected token %v encountered while decoding int", tok)
	}
}

func uintDecoder(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenFixnum:
		n, err := d.p.Int()
		if err != nil {
			return err
		}
		un := uint64(n)
		if v.OverflowUint(un) {
			return fmt.Errorf("Decoded uint %d exceeds maximum width of %s", n, v.Type())
		}
		v.SetUint(un)
		return nil
	default:
		return fmt.Errorf("Unexpected token %v encountered while decoding uint", tok)
	}
}

func floatDecoder(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenFloat:
		f, err := d.p.Float()
		if err != nil {
			return err
		}
		if v.OverflowFloat(f) {
			return fmt.Errorf("Decoded float %f exceeds maximum width of %s", f, v.Type())
		}
		v.SetFloat(f)
		return nil
	default:
		return fmt.Errorf("Unexpected token %v encountered while decoding float", tok)
	}
}

func stringDecoder(d *Decoder, v reflect.Value) (err error) {
	var tok Token

	tok, err = d.nextToken()
	if err != nil {
		return
	}

	if tok == TokenLink {
		lnkID := d.p.LinkID()
		cached, ok := d.objCache[lnkID]
		if ok {
			// Indirect the cached value until we arrive at a non-ptr type.
			for cached.IsValid() && cached.Kind() == reflect.Ptr && !cached.IsNil() {
				cached = cached.Elem()
			}
			if cached.Kind() == reflect.String {
				v.SetString(cached.String())
				return
			}
		}

		err = fmt.Errorf("Unknown link id %d", lnkID)
		return
	}

	isIVar := tok == TokenStartIVar
	lnkID := d.p.LinkID()

	if isIVar {
		tok, err = d.p.Next()
		if err != nil {
			return
		}
	}

	if tok != TokenString && tok != TokenSymbol {
		return fmt.Errorf("Unexpected token %v encountered while decoding string", tok)
	}

	var str string
	str, err = d.p.Text()
	if err != nil {
		return
	}
	v.SetString(str)

	_, ok := d.objCache[lnkID]
	if !ok {
		cacheV := reflect.New(v.Type())
		cacheV.Elem().SetString(str)
		d.objCache[lnkID] = cacheV
	}

	if isIVar {
		// TODO: properly parse IVar. For now, we just skip over encoding and such.
		if err = d.p.ExpectNext(TokenIVarProps); err != nil {
			return
		}
		err = d.p.Skip()
	}

	return
}

type sliceDecoder struct {
	elemDec decoderFunc
}

func (sliceDec *sliceDecoder) decode(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	if tok != TokenStartArray {
		return fmt.Errorf("Unexpected token %v encountered while decoding slice", tok)
	}

	l := d.p.Len()

	// If the underlying slice already has enough capacity for this array, then we just resize
	// and use it.
	cap := v.Cap()
	if cap >= l {
		v.SetLen(l)
		v.SetCap(l)
	} else {
		v.Set(reflect.MakeSlice(v.Type(), l, l))
	}

	lnkID := d.p.LinkID()
	if lnkID > -1 {
		d.objCache[lnkID] = v.Addr()
	}

	for i := 0; i < l; i++ {
		if err := sliceDec.elemDec(d, v.Index(i)); err != nil {
			return err
		}
	}

	if err := d.p.ExpectNext(TokenEndArray); err != nil {
		return err
	}

	return nil
}

func newSliceDecoder(t reflect.Type) decoderFunc {
	dec := &sliceDecoder{newTypeDecoder(t.Elem())}
	return dec.decode
}

type idxStructField struct {
	idx int // index in the struct
	dec decoderFunc
}
type idxStructDecoder struct {
	fields []idxStructField
}

func (idxDec *idxStructDecoder) decode(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenStartArray:
		l := d.p.Len()

		for i := 0; i < l; i++ {
			if i < len(idxDec.fields) {
				f := idxDec.fields[i]
				if f.dec != nil {
					if err := f.dec(d, v.Field(f.idx)); err != nil {
						return err
					}
					continue
				}
			}

			if _, err := d.p.Next(); err != nil {
				return err
			}

			if err := d.p.Skip(); err != nil {
				return err
			}
		}
		if err := d.p.ExpectNext(TokenEndArray); err != nil {
			return err
		}

		return nil
	default:
		return fmt.Errorf("Unexpected token %v encountered while decoding indexed struct", tok)
	}

}

func newStructDecoder(t reflect.Type) decoderFunc {
	// A struct decoder can either be indexed or named.
	// Indexed decoders expect to decode a Ruby Array into a Go struct.
	// Named decoders expecet to decode a Ruby Hash/Struct into a Go struct.
	var idxFields []idxStructField
	named := make(map[string]decoderFunc)

	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)

		if f.PkgPath != "" {
			continue
		}

		fdec := newTypeDecoder(f.Type)

		meta := strings.Split(f.Tag.Get("rmarsh"), ",")
		if meta[0] == "" {
			continue
		}
		if meta[0] == "_indexed" {
			if len(named) > 0 {
				return newErrorDecoder(fmt.Errorf("Cannot mix named and _indexed fields in struct %s", t))
			}
			idx, err := strconv.ParseInt(meta[1], 10, 32)
			if err != nil {
				return newErrorDecoder(fmt.Errorf("Struct %s field %q has invalid _indexed value %q", t, f.Name, meta[1]))
			}
			if len(idxFields) <= int(idx) {
				idxFields = append(idxFields, make([]idxStructField, int(idx)-len(idxFields)+1)...)
			}
			idxFields[idx] = idxStructField{idx: i, dec: fdec}
		} else {
			if len(idxFields) > 0 {
				return newErrorDecoder(fmt.Errorf("Cannot mix named and _indexed fields in struct %s", t))
			}
			named[f.Name] = fdec
		}
	}

	if len(idxFields) > 0 {
		dec := &idxStructDecoder{idxFields}
		return dec.decode
	}
	if len(named) > 0 {
		dec := &idxStructDecoder{}
		return dec.decode
	}
	return skipDecoder
}

type ptrDecoder struct {
	elemDec decoderFunc
}

func (ptrDec *ptrDecoder) decode(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	// If the token is nil, then we nil the ptr and move on.
	if tok == TokenNil {
		v.Set(reflect.Zero(v.Type()))
		return nil
	}

	// If we've just parsed a link, then we see if we've cached the object already.
	// If we we have, and it's directly assignable to the pointer type we have, then
	// we can just immediately assign it and continue.
	// Otherwise we start a replay parser and run it on the target.
	if tok == TokenLink {
		lnkID := d.p.LinkID()
		cached, ok := d.objCache[lnkID]

		// Keep indirecting the cached value until we arrive at a non-pointer type
		for cached.IsValid() && cached.Kind() == reflect.Ptr && !cached.IsNil() {
			if ok && cached.Type().AssignableTo(v.Type()) {
				v.Set(cached)
				return nil
			}
			cached = cached.Elem()
		}

		// TODO: setup a replay parser and run it against the target.
		return fmt.Errorf("Unhandled link encountered. %d", lnkID)
	}

	v.Set(reflect.New(v.Type().Elem()))

	// Push the token back and decode against resolved ptr.
	d.curToken = tok

	lnkID := d.p.LinkID()
	if _, ok := d.objCache[lnkID]; !ok {
		cacheV := reflect.New(v.Type()).Elem()
		cacheV.Set(v)
		d.objCache[lnkID] = cacheV
	}

	err = ptrDec.elemDec(d, v.Elem())

	return err
}

func newPtrDecoder(t reflect.Type) decoderFunc {
	dec := &ptrDecoder{newTypeDecoder(t.Elem())}
	return dec.decode
}

type ptrIndirector struct {
	types   []reflect.Type
	elemDec decoderFunc
}

func (ptrIndir *ptrIndirector) decode(d *Decoder, v reflect.Value) error {
	for _, typ := range ptrIndir.types {
		if v.IsNil() {
			v.Set(reflect.New(typ))
		}
		v = v.Elem()
	}
	return ptrIndir.elemDec(d, v)
}

func newPtrIndirector(t reflect.Type) decoderFunc {
	var types []reflect.Type
	for t.Elem().Kind() == reflect.Ptr {
		types = append(types, t.Elem())
		t = t.Elem()
	}

	dec := &ptrIndirector{types: types, elemDec: newPtrDecoder(t)}
	return dec.decode
}

type errorDecoder struct {
	err error
}

func (errDec errorDecoder) decode(d *Decoder, v reflect.Value) error {
	return errDec.err
}

func newErrorDecoder(err error) decoderFunc {
	return errorDecoder{err}.decode
}

func unsupportedTypeDecoder(d *Decoder, v reflect.Value) error {
	return fmt.Errorf("unsupported type %s", v.Type())
}
<commit_msg>decoder: clean up some of the pointer handling code<commit_after>package rmarsh

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

// A Decoder decodes a Ruby Marshal stream into concrete Golang structures.
type Decoder struct {
	p        *Parser
	objCache map[int]reflect.Value
	curToken Token
}

// NewDecoder builds a new Decoder that uses given Parser to decode a Ruby Marshal stream.
func NewDecoder(p *Parser) *Decoder {
	return &Decoder{p: p, objCache: make(map[int]reflect.Value)}
}

// ReadValue will consume a full Ruby Marshal stream from the given io.Reader and return a fully decoded Golang object.
func ReadValue(r io.Reader, val interface{}) error {
	// TODO: grab Parser instance from a sync.Pool
	return NewDecoder(NewParser(r)).Decode(val)
}

// Decode will consume a value from the underlying parser and marshal it into the provided Golang type.
func (d *Decoder) Decode(val interface{}) error {
	v := reflect.ValueOf(val)
	if v.Kind() != reflect.Ptr {
		return fmt.Errorf("Invalid decode target %T, did you forget to pass a pointer?", val)
	}

	return d.valueDecoder(v.Elem())(d, v.Elem())
}

func (d *Decoder) nextToken() (Token, error) {
	if d.curToken != tokenInvalid {
		tok := d.curToken
		d.curToken = tokenInvalid
		return tok, nil
	}
	return d.p.Next()
}

type decoderFunc func(*Decoder, reflect.Value) error

func (d *Decoder) valueDecoder(v reflect.Value) decoderFunc {
	return d.typeDecoder(v.Type())
}

var decCache struct {
	sync.RWMutex
	m map[reflect.Type]decoderFunc
}

func (d *Decoder) typeDecoder(t reflect.Type) decoderFunc {
	decCache.RLock()
	dec := decCache.m[t]
	decCache.RUnlock()
	if dec != nil {
		return dec
	}

	decCache.Lock()
	defer decCache.Unlock()
	if decCache.m == nil {
		decCache.m = make(map[reflect.Type]decoderFunc)
	}
	decCache.m[t] = newTypeDecoder(t)
	return decCache.m[t]
}

func newTypeDecoder(t reflect.Type) decoderFunc {
	switch t.Kind() {
	case reflect.Invalid:
		return skipDecoder
	case reflect.Bool:
		return boolDecoder
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intDecoder
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return uintDecoder
	case reflect.Float32, reflect.Float64:
		return floatDecoder
	case reflect.String:
		return stringDecoder
	case reflect.Slice:
		return newSliceDecoder(t)
	case reflect.Struct:
		return newStructDecoder(t)
	case reflect.Ptr:
		// Since Ruby doesn't offer pointer types
		// We can simplify our type handling by normalising all Go pointers down to
		// single depth. e.g if we've been passed a ***string, we'll normalise it
		// down to *string before doing anything with it.
		if t.Elem().Kind() == reflect.Ptr {
			return newPtrIndirector(t)
		}
		return newPtrDecoder(t)
	}
	return unsupportedTypeDecoder
}

func skipDecoder(d *Decoder, v reflect.Value) error {
	_, err := d.p.readNext()
	if err != nil {
		return err
	}
	return d.p.Skip()
}

func boolDecoder(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenTrue, TokenFalse:
		v.SetBool(tok == TokenTrue)
		return nil
	default:
		// TODO: build a path
		return fmt.Errorf("Unexpected token %v encountered while decoding bool", tok)
	}
}
func intDecoder(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenFixnum:
		n, err := d.p.Int()
		if err != nil {
			return err
		}
		nn := int64(n)
		if v.OverflowInt(nn) {
			return fmt.Errorf("Decoded int %d exceeds maximum width of %s", n, v.Type())
		}
		v.SetInt(nn)
		return nil
	default:
		return fmt.Errorf("Unexpected token %v encountered while decoding int", tok)
	}
}

func uintDecoder(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenFixnum:
		n, err := d.p.Int()
		if err != nil {
			return err
		}
		un := uint64(n)
		if v.OverflowUint(un) {
			return fmt.Errorf("Decoded uint %d exceeds maximum width of %s", n, v.Type())
		}
		v.SetUint(un)
		return nil
	default:
		return fmt.Errorf("Unexpected token %v encountered while decoding uint", tok)
	}
}

func floatDecoder(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenFloat:
		f, err := d.p.Float()
		if err != nil {
			return err
		}
		if v.OverflowFloat(f) {
			return fmt.Errorf("Decoded float %f exceeds maximum width of %s", f, v.Type())
		}
		v.SetFloat(f)
		return nil
	default:
		return fmt.Errorf("Unexpected token %v encountered while decoding float", tok)
	}
}

func stringDecoder(d *Decoder, v reflect.Value) (err error) {
	var tok Token

	tok, err = d.nextToken()
	if err != nil {
		return
	}

	if tok == TokenLink {
		lnkID := d.p.LinkID()
		cached, ok := d.objCache[lnkID]
		if ok {
			cached = cached.Elem()
			if cached.Kind() == reflect.String {
				v.SetString(cached.String())
				return
			}
		}

		err = fmt.Errorf("Unknown link id %d", lnkID)
		return
	}

	isIVar := tok == TokenStartIVar
	lnkID := d.p.LinkID()

	if isIVar {
		tok, err = d.p.Next()
		if err != nil {
			return
		}
	}

	if tok != TokenString && tok != TokenSymbol {
		return fmt.Errorf("Unexpected token %v encountered while decoding string", tok)
	}

	var str string
	str, err = d.p.Text()
	if err != nil {
		return
	}
	v.SetString(str)

	_, ok := d.objCache[lnkID]
	if !ok {
		cacheV := reflect.New(v.Type())
		cacheV.Elem().SetString(str)
		d.objCache[lnkID] = cacheV
	}

	if isIVar {
		// TODO: properly parse IVar. For now, we just skip over encoding and such.
		if err = d.p.ExpectNext(TokenIVarProps); err != nil {
			return
		}
		err = d.p.Skip()
	}

	return
}

type sliceDecoder struct {
	elemDec decoderFunc
}

func (sliceDec *sliceDecoder) decode(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	if tok != TokenStartArray {
		return fmt.Errorf("Unexpected token %v encountered while decoding slice", tok)
	}

	l := d.p.Len()

	// If the underlying slice already has enough capacity for this array, then we just resize
	// and use it.
	cap := v.Cap()
	if cap >= l {
		v.SetLen(l)
		v.SetCap(l)
	} else {
		v.Set(reflect.MakeSlice(v.Type(), l, l))
	}

	lnkID := d.p.LinkID()
	if lnkID > -1 {
		d.objCache[lnkID] = v.Addr()
	}

	for i := 0; i < l; i++ {
		if err := sliceDec.elemDec(d, v.Index(i)); err != nil {
			return err
		}
	}

	if err := d.p.ExpectNext(TokenEndArray); err != nil {
		return err
	}

	return nil
}

func newSliceDecoder(t reflect.Type) decoderFunc {
	dec := &sliceDecoder{newTypeDecoder(t.Elem())}
	return dec.decode
}

type idxStructField struct {
	idx int // index in the struct
	dec decoderFunc
}
type idxStructDecoder struct {
	fields []idxStructField
}

func (idxDec *idxStructDecoder) decode(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	switch tok {
	case TokenStartArray:
		l := d.p.Len()

		for i := 0; i < l; i++ {
			if i < len(idxDec.fields) {
				f := idxDec.fields[i]
				if f.dec != nil {
					if err := f.dec(d, v.Field(f.idx)); err != nil {
						return err
					}
					continue
				}
			}

			if _, err := d.p.Next(); err != nil {
				return err
			}

			if err := d.p.Skip(); err != nil {
				return err
			}
		}
		if err := d.p.ExpectNext(TokenEndArray); err != nil {
			return err
		}

		return nil
	default:
		return fmt.Errorf("Unexpected token %v encountered while decoding indexed struct", tok)
	}

}

func newStructDecoder(t reflect.Type) decoderFunc {
	// A struct decoder can either be indexed or named.
	// Indexed decoders expect to decode a Ruby Array into a Go struct.
	// Named decoders expecet to decode a Ruby Hash/Struct into a Go struct.
	var idxFields []idxStructField
	named := make(map[string]decoderFunc)

	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)

		if f.PkgPath != "" {
			continue
		}

		fdec := newTypeDecoder(f.Type)

		meta := strings.Split(f.Tag.Get("rmarsh"), ",")
		if meta[0] == "" {
			continue
		}
		if meta[0] == "_indexed" {
			if len(named) > 0 {
				return newErrorDecoder(fmt.Errorf("Cannot mix named and _indexed fields in struct %s", t))
			}
			idx, err := strconv.ParseInt(meta[1], 10, 32)
			if err != nil {
				return newErrorDecoder(fmt.Errorf("Struct %s field %q has invalid _indexed value %q", t, f.Name, meta[1]))
			}
			if len(idxFields) <= int(idx) {
				idxFields = append(idxFields, make([]idxStructField, int(idx)-len(idxFields)+1)...)
			}
			idxFields[idx] = idxStructField{idx: i, dec: fdec}
		} else {
			if len(idxFields) > 0 {
				return newErrorDecoder(fmt.Errorf("Cannot mix named and _indexed fields in struct %s", t))
			}
			named[f.Name] = fdec
		}
	}

	if len(idxFields) > 0 {
		dec := &idxStructDecoder{idxFields}
		return dec.decode
	}
	if len(named) > 0 {
		dec := &idxStructDecoder{}
		return dec.decode
	}
	return skipDecoder
}

type ptrDecoder struct {
	elemDec decoderFunc
}

func (ptrDec *ptrDecoder) decode(d *Decoder, v reflect.Value) error {
	tok, err := d.nextToken()
	if err != nil {
		return err
	}

	// If the token is nil, then we nil the ptr and move on.
	if tok == TokenNil {
		v.Set(reflect.Zero(v.Type()))
		return nil
	}

	// If we've just parsed a link, then we see if we've cached the object already.
	// If we we have, and it's directly assignable to the pointer type we have, then
	// we can just immediately assign it and continue.
	// Otherwise we start a replay parser and run it on the target.
	if tok == TokenLink {
		lnkID := d.p.LinkID()
		cached, ok := d.objCache[lnkID]

		if ok && cached.Type().AssignableTo(v.Type()) {
			v.Set(cached)
			return nil
		}

		// TODO: setup a replay parser and run it against the target.
		return fmt.Errorf("Unhandled link encountered. %d", lnkID)
	}

	v.Set(reflect.New(v.Type().Elem()))

	// Push the token back and decode against resolved ptr.
	d.curToken = tok

	lnkID := d.p.LinkID()
	if _, ok := d.objCache[lnkID]; !ok {
		cacheV := reflect.New(v.Type()).Elem()
		cacheV.Set(v)
		d.objCache[lnkID] = cacheV
	}

	err = ptrDec.elemDec(d, v.Elem())

	return err
}

func newPtrDecoder(t reflect.Type) decoderFunc {
	dec := &ptrDecoder{newTypeDecoder(t.Elem())}
	return dec.decode
}

type ptrIndirector struct {
	types   []reflect.Type
	elemDec decoderFunc
}

func (ptrIndir *ptrIndirector) decode(d *Decoder, v reflect.Value) error {
	for _, typ := range ptrIndir.types {
		if v.IsNil() {
			v.Set(reflect.New(typ))
		}
		v = v.Elem()
	}
	return ptrIndir.elemDec(d, v)
}

func newPtrIndirector(t reflect.Type) decoderFunc {
	var types []reflect.Type
	for t.Elem().Kind() == reflect.Ptr {
		types = append(types, t.Elem())
		t = t.Elem()
	}

	dec := &ptrIndirector{types: types, elemDec: newPtrDecoder(t)}
	return dec.decode
}

type errorDecoder struct {
	err error
}

func (errDec errorDecoder) decode(d *Decoder, v reflect.Value) error {
	return errDec.err
}

func newErrorDecoder(err error) decoderFunc {
	return errorDecoder{err}.decode
}

func unsupportedTypeDecoder(d *Decoder, v reflect.Value) error {
	return fmt.Errorf("unsupported type %s", v.Type())
}
<|endoftext|>


<commit_before>package sse

import (
	"bufio"
	"bytes"
	"io"
)

const (
	defaultBufferSize = 8096
)

var (
	// DefaultDecoder is the decoder used by EventSource by default.
	DefaultDecoder = &decoder{defaultBufferSize}

	bytesLF    = []byte("\n")
	bytesCRLF  = []byte("\r\n")
	bytesSPACE = []byte(" ")
	bytesCOLON = []byte(":")
)

type (
	// Decoder interface decodes events from a reader input
	Decoder interface {
		Decode(in io.Reader) (out <-chan Event)
	}
	decoder struct {
		bufferSize int
	}
)

// NewDecoder builds an SSE decoder with the specified buffer size.
func NewDecoder(bufferSize int) Decoder {
	d := &decoder{}
	d.initialise(bufferSize)
	return d
}

func (d *decoder) initialise(bufferSize int) {
	d.bufferSize = bufferSize
}

// Returns a channel of SSE events from a reader input.
func (d *decoder) Decode(in io.Reader) <-chan Event {
	buffIn := bufio.NewReaderSize(in, d.bufferSize)
	out := make(chan Event)
	go process(buffIn, out)
	return out
}

// Processes a reader and sends the parsed SSE events
// to the output channel.
// This function is intended to run in a go-routine.
func process(in *bufio.Reader, out chan Event) {
	// Stores event data, which is filled after one or many lines from the reader
	var eventID, eventType, dataBuffer = new(bytes.Buffer), new(bytes.Buffer), new(bytes.Buffer)

	// Stores data about the current line being processed
	var field, value = new(bytes.Buffer), new(bytes.Buffer)

	for {
		line, err := in.ReadSlice('\n')
		if err != nil {
			close(out)
			return
		}

		// Dispatch event
		if bytes.Equal(line, bytesLF) || bytes.Equal(line, bytesCRLF) {
			// Skip event if Data buffer its empty
			if dataBuffer.Len() == 0 {
				dataBuffer.Reset()
				eventType.Reset()
				continue
			}

			data := dataBuffer.Bytes()

			// Trim last byte if line feed
			data = bytes.TrimSuffix(data, bytesLF)

			// Create event
			event := newEvent(eventID.String(), eventType.String(), data)

			// Clear event buffers
			eventType.Reset()
			dataBuffer.Reset()

			// Dispatch event
			out <- event
			continue
		}

		colonIndex := bytes.Index(line, bytesCOLON)

		// Sanitise line feeds
		line = sanitiseLineFeed(line)

		// Extract field/value for current line
		field.Reset()
		value.Reset()

		switch colonIndex {
		case 0:
			continue
		case -1:
			field.Write(line)
		default:
			field.Write(line[:colonIndex])
			line = line[colonIndex+1:]
			line = bytes.TrimPrefix(line, bytesSPACE)
			value.Write(line)
		}

		// Process field
		fieldName := field.String()
		switch fieldName {
		case "event":
			eventType.Write(value.Bytes())
		case "data":
			dataBuffer.Write(value.Bytes())
			dataBuffer.WriteByte('\n')
		case "id":
			eventID.Reset()
			eventID.Write(value.Bytes())
		case "retry":
			// TODO(alevinval): unused at the moment, will need refactor
			// or change on the internal API, as decoder has no knowledge on the underlying connection.
		default:
			// Ignore field
		}
	}
}

// Sanitises line feed ending.
func sanitiseLineFeed(line []byte) []byte {
	if bytes.HasSuffix(line, bytesCRLF) {
		line = bytes.TrimSuffix(line, bytesCRLF)
	} else {
		line = bytes.TrimSuffix(line, bytesLF)
	}
	return line
}
<commit_msg>decoder: faster colon/space parsing<commit_after>package sse

import (
	"bufio"
	"bytes"
	"io"
)

const (
	defaultBufferSize = 8096
)

const (
	byteLF    = '\n'
	byteCR    = '\r'
	byteSPACE = ' '
	byteCOLON = ':'
)

var (
	// DefaultDecoder is the decoder used by EventSource by default.
	DefaultDecoder = &decoder{defaultBufferSize}

	bytesLF   = []byte{byteLF}
	bytesCRLF = []byte{byteCR, byteLF}
)

type (
	// Decoder interface decodes events from a reader input
	Decoder interface {
		Decode(in io.Reader) (out <-chan Event)
	}
	decoder struct {
		bufferSize int
	}
)

// NewDecoder builds an SSE decoder with the specified buffer size.
func NewDecoder(bufferSize int) Decoder {
	d := &decoder{}
	d.initialise(bufferSize)
	return d
}

func (d *decoder) initialise(bufferSize int) {
	d.bufferSize = bufferSize
}

// Returns a channel of SSE events from a reader input.
func (d *decoder) Decode(in io.Reader) <-chan Event {
	buffIn := bufio.NewReaderSize(in, d.bufferSize)
	out := make(chan Event)
	go process(buffIn, out)
	return out
}

// Processes a reader and sends the parsed SSE events
// to the output channel.
// This function is intended to run in a go-routine.
func process(in *bufio.Reader, out chan Event) {
	// Stores event data, which is filled after one or many lines from the reader
	var eventID, eventType, dataBuffer = new(bytes.Buffer), new(bytes.Buffer), new(bytes.Buffer)

	// Stores data about the current line being processed
	var field, value = new(bytes.Buffer), new(bytes.Buffer)

	for {
		line, err := in.ReadSlice('\n')
		if err != nil {
			close(out)
			return
		}

		// Dispatch event
		if bytes.Equal(line, bytesLF) || bytes.Equal(line, bytesCRLF) {
			// Skip event if Data buffer its empty
			if dataBuffer.Len() == 0 {
				dataBuffer.Reset()
				eventType.Reset()
				continue
			}

			data := dataBuffer.Bytes()

			// Trim last byte if line feed
			data = bytes.TrimSuffix(data, bytesLF)

			// Create event
			event := newEvent(eventID.String(), eventType.String(), data)

			// Clear event buffers
			eventType.Reset()
			dataBuffer.Reset()

			// Dispatch event
			out <- event
			continue
		}

		colonIndex := bytes.IndexByte(line, byteCOLON)

		// Sanitise line feeds
		line = sanitiseLineFeed(line)

		// Extract field/value for current line
		field.Reset()
		value.Reset()

		switch colonIndex {
		case 0:
			continue
		case -1:
			field.Write(line)
		default:
			field.Write(line[:colonIndex])
			line = line[colonIndex+1:]
			if len(line) > 0 && line[0] == byteSPACE {
				line = line[1:]
			}
			value.Write(line)
		}

		// Process field
		fieldName := field.String()
		switch fieldName {
		case "event":
			eventType.Write(value.Bytes())
		case "data":
			dataBuffer.Write(value.Bytes())
			dataBuffer.WriteByte('\n')
		case "id":
			eventID.Reset()
			eventID.Write(value.Bytes())
		case "retry":
			// TODO(alevinval): unused at the moment, will need refactor
			// or change on the internal API, as decoder has no knowledge on the underlying connection.
		default:
			// Ignore field
		}
	}
}

// Sanitises line feed ending.
func sanitiseLineFeed(line []byte) []byte {
	if bytes.HasSuffix(line, bytesCRLF) {
		line = bytes.TrimSuffix(line, bytesCRLF)
	} else {
		line = bytes.TrimSuffix(line, bytesLF)
	}
	return line
}
<|endoftext|>