<commit_before>package common

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
)

const Version = 0.60

type ClientSetting struct {
	AccessKey string
	ClientKey string

	Name       string
	ClientType string
	Version    float32
	Delay      int
	Mode       int
	PipeNum    int
	AesKey     string
}

var encodingData []byte = []byte("bertdfvuifu4359c")
var encodingLen int = 16
var headerLen int = 4

func init() {
	encodingLen = len(encodingData)
	headerLen = binary.Size(uint32(1))
}

func Xor(s string) string {
	n := len(s)
	if n == 0 {
		return ""
	}
	r := make([]byte, n)
	for i := 0; i < n; i++ {
		r[i] = s[i] ^ encodingData[i%encodingLen]
	}
	return string(r)
}

func Write(conn net.Conn, id string, action string, content string) error {
	if conn == nil {
		return nil
	}
	l1 := len(id)
	action = Xor(action)
	l2 := len(action)
	l3 := len(content)
	var buf bytes.Buffer
	binary.Write(&buf, binary.LittleEndian, uint32(l1))
	binary.Write(&buf, binary.LittleEndian, uint32(l2))
	binary.Write(&buf, binary.LittleEndian, uint32(l3))
	binary.Write(&buf, binary.LittleEndian, []byte(id))
	binary.Write(&buf, binary.LittleEndian, []byte(action))
	binary.Write(&buf, binary.LittleEndian, []byte(content))
	_, err := conn.Write(buf.Bytes())
	//println("write!!!", old, l1, l2, l3)
	if err != nil {
		println("write err", err.Error())
	}
	return err
}

type ReadCallBack func(conn net.Conn, id string, action string, arg string)
type ReadUDPCallBack func(conn *net.UDPConn, addr *net.UDPAddr, id string, action string, arg string)

func Read(conn net.Conn, callback ReadCallBack) {
	scanner := bufio.NewScanner(conn)
	split := func(data []byte, atEOF bool) (adv int, token []byte, err error) {
		l := len(data)
		if l < headerLen*3 {
			return 0, nil, nil
		}
		if l > 1024*1024 {
			conn.Close()
			log.Println("invalid query!")
			return 0, nil, errors.New("to large data!")
		}
		var l1, l2, l3 uint32
		buf := bytes.NewReader(data)
		binary.Read(buf, binary.LittleEndian, &l1)
		binary.Read(buf, binary.LittleEndian, &l2)
		binary.Read(buf, binary.LittleEndian, &l3)
		tail := l - headerLen*3
		if tail < int(l1+l2+l3) {
			return 0, nil, nil
		}
		id := make([]byte, l1)
		action := make([]byte, l2)
		content := make([]byte, l3)
		binary.Read(buf, binary.LittleEndian, &id)
		binary.Read(buf, binary.LittleEndian, &action)
		binary.Read(buf, binary.LittleEndian, &content)
		callback(conn, string(id), Xor(string(action)), string(content))
		//println("read11!!", l1,l2, l3,string(id), Xor(string(action)), string(content))
		return headerLen*3 + int(l1+l2+l3), []byte{}, nil
	}
	scanner.Split(split)
	for scanner.Scan() {
	}
	if scanner.Err() != nil {
		println(scanner.Err().Error())
	}
}

func Md5(msg string) string {
	h := md5.New()
	io.WriteString(h, msg)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func HashPasswd(pass string) string {
	return Md5(pass + "testzc222sf")
}

type _reuseTbl struct {
	tbl map[string]bool
}

var currIdMap map[string]int
var reuseTbl map[string]*_reuseTbl

func GetId(name string) string {
	if reuseTbl != nil {
		tbl, bHave := reuseTbl[name]
		if bHave {
			if len(tbl.tbl) > 0 {
				for key := range tbl.tbl {
					delete(tbl.tbl, key)
					//					println("got old id", key)
					return key
				}
			}
		}
	}
	if currIdMap == nil {
		currIdMap = make(map[string]int)
		currIdMap[name] = 0
	}
	currIdMap[name]++
	//	println("gen new id", currIdMap[name])
	return strconv.Itoa(currIdMap[name])
}

func RmId(name, id string) {
	return
	if currIdMap == nil {
		currIdMap = make(map[string]int)
		currIdMap[name] = 0
	}
	n, err := strconv.Atoi(id)
	if err != nil {
		return
	}
	if n > currIdMap[name] {
		return
	}
	if reuseTbl == nil {
		reuseTbl = make(map[string]*_reuseTbl)
	}
	tbl, bHave := reuseTbl[name]
	if !bHave {
		reuseTbl[name] = &_reuseTbl{tbl: make(map[string]bool)}
		tbl = reuseTbl[name]
	}
	tbl.tbl[id] = true
	//	println("can reuse ", name, id)
}

func Id_test(name string) {
	id1 := GetId(name)
	id2 := GetId(name)
	id3 := GetId(name)
	id4 := GetId(name)

	RmId(name, id2)
	RmId(name, id4)
	println(GetId(name))
	println(GetId(name))
	RmId(name, id1)
	println(GetId(name))
	RmId(name, id3)
	println(GetId(name))
	println(GetId(name))
}
<commit_msg>version update to 0.8<commit_after>package common

import (
	"bufio"
	"bytes"
	"crypto/md5"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
)

const Version = 0.70

type ClientSetting struct {
	AccessKey string
	ClientKey string

	Name       string
	ClientType string
	Version    float32
	Delay      int
	Mode       int
	PipeNum    int
	AesKey     string
}

var encodingData []byte = []byte("bertdfvuifu4359c")
var encodingLen int = 16
var headerLen int = 4

func init() {
	encodingLen = len(encodingData)
	headerLen = binary.Size(uint32(1))
}

func Xor(s string) string {
	n := len(s)
	if n == 0 {
		return ""
	}
	r := make([]byte, n)
	for i := 0; i < n; i++ {
		r[i] = s[i] ^ encodingData[i%encodingLen]
	}
	return string(r)
}

func Write(conn net.Conn, id string, action string, content string) error {
	if conn == nil {
		return nil
	}
	l1 := len(id)
	action = Xor(action)
	l2 := len(action)
	l3 := len(content)
	var buf bytes.Buffer
	binary.Write(&buf, binary.LittleEndian, uint32(l1))
	binary.Write(&buf, binary.LittleEndian, uint32(l2))
	binary.Write(&buf, binary.LittleEndian, uint32(l3))
	binary.Write(&buf, binary.LittleEndian, []byte(id))
	binary.Write(&buf, binary.LittleEndian, []byte(action))
	binary.Write(&buf, binary.LittleEndian, []byte(content))
	_, err := conn.Write(buf.Bytes())
	//println("write!!!", old, l1, l2, l3)
	if err != nil {
		println("write err", err.Error())
	}
	return err
}

type ReadCallBack func(conn net.Conn, id string, action string, arg string)
type ReadUDPCallBack func(conn *net.UDPConn, addr *net.UDPAddr, id string, action string, arg string)

func Read(conn net.Conn, callback ReadCallBack) {
	scanner := bufio.NewScanner(conn)
	split := func(data []byte, atEOF bool) (adv int, token []byte, err error) {
		l := len(data)
		if l < headerLen*3 {
			return 0, nil, nil
		}
		if l > 1024*1024 {
			conn.Close()
			log.Println("invalid query!")
			return 0, nil, errors.New("to large data!")
		}
		var l1, l2, l3 uint32
		buf := bytes.NewReader(data)
		binary.Read(buf, binary.LittleEndian, &l1)
		binary.Read(buf, binary.LittleEndian, &l2)
		binary.Read(buf, binary.LittleEndian, &l3)
		tail := l - headerLen*3
		if tail < int(l1+l2+l3) {
			return 0, nil, nil
		}
		id := make([]byte, l1)
		action := make([]byte, l2)
		content := make([]byte, l3)
		binary.Read(buf, binary.LittleEndian, &id)
		binary.Read(buf, binary.LittleEndian, &action)
		binary.Read(buf, binary.LittleEndian, &content)
		callback(conn, string(id), Xor(string(action)), string(content))
		//println("read11!!", l1,l2, l3,string(id), Xor(string(action)), string(content))
		return headerLen*3 + int(l1+l2+l3), []byte{}, nil
	}
	scanner.Split(split)
	for scanner.Scan() {
	}
	if scanner.Err() != nil {
		println(scanner.Err().Error())
	}
}

func Md5(msg string) string {
	h := md5.New()
	io.WriteString(h, msg)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func HashPasswd(pass string) string {
	return Md5(pass + "testzc222sf")
}

type _reuseTbl struct {
	tbl map[string]bool
}

var currIdMap map[string]int
var reuseTbl map[string]*_reuseTbl

func GetId(name string) string {
	if reuseTbl != nil {
		tbl, bHave := reuseTbl[name]
		if bHave {
			if len(tbl.tbl) > 0 {
				for key := range tbl.tbl {
					delete(tbl.tbl, key)
					//					println("got old id", key)
					return key
				}
			}
		}
	}
	if currIdMap == nil {
		currIdMap = make(map[string]int)
		currIdMap[name] = 0
	}
	currIdMap[name]++
	//	println("gen new id", currIdMap[name])
	return strconv.Itoa(currIdMap[name])
}

func RmId(name, id string) {
	return
	if currIdMap == nil {
		currIdMap = make(map[string]int)
		currIdMap[name] = 0
	}
	n, err := strconv.Atoi(id)
	if err != nil {
		return
	}
	if n > currIdMap[name] {
		return
	}
	if reuseTbl == nil {
		reuseTbl = make(map[string]*_reuseTbl)
	}
	tbl, bHave := reuseTbl[name]
	if !bHave {
		reuseTbl[name] = &_reuseTbl{tbl: make(map[string]bool)}
		tbl = reuseTbl[name]
	}
	tbl.tbl[id] = true
	//	println("can reuse ", name, id)
}

func Id_test(name string) {
	id1 := GetId(name)
	id2 := GetId(name)
	id3 := GetId(name)
	id4 := GetId(name)

	RmId(name, id2)
	RmId(name, id4)
	println(GetId(name))
	println(GetId(name))
	RmId(name, id1)
	println(GetId(name))
	RmId(name, id3)
	println(GetId(name))
	println(GetId(name))
}
<|endoftext|>


<commit_before>package gateway

import (
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	"strings"
)

type Gateway struct {
	*config.Config
	MyConfig    *config.Gateway
	Bridges     []bridge.Bridge
	ChannelsOut map[string][]string
	ChannelsIn  map[string][]string
	ignoreNicks map[string][]string
	Name        string
}

func New(cfg *config.Config, gateway *config.Gateway) error {
	c := make(chan config.Message)
	gw := &Gateway{}
	gw.Name = gateway.Name
	gw.Config = cfg
	gw.MyConfig = gateway
	for _, br := range gateway.In {
		gw.Bridges = append(gw.Bridges, bridge.New(cfg, &br, c))
	}
	gw.mapChannels()
	gw.mapIgnores()
	for _, br := range gw.Bridges {
		br.Connect()
		for _, channel := range gw.ChannelsOut[br.FullOrigin()] {
			br.JoinChannel(channel)
		}
	}
	gw.handleReceive(c)
	return nil
}

func (gw *Gateway) handleReceive(c chan config.Message) {
	for {
		select {
		case msg := <-c:
			for _, br := range gw.Bridges {
				gw.handleMessage(msg, br)
			}
		}
	}
}

func (gw *Gateway) mapChannels() error {
	m := make(map[string][]string)
	for _, br := range gw.MyConfig.Out {
		m[br.Account] = append(m[br.Account], br.Channel)
	}
	gw.ChannelsOut = m
	m = nil
	m = make(map[string][]string)
	for _, br := range gw.MyConfig.In {
		m[br.Account] = append(m[br.Account], br.Channel)
	}
	gw.ChannelsIn = m
	return nil
}

func (gw *Gateway) mapIgnores() {
	m := make(map[string][]string)
	for _, br := range gw.MyConfig.In {
		accInfo := strings.Split(br.Account, ".")
		m[br.Account] = strings.Fields(gw.Config.IRC[accInfo[1]].IgnoreNicks)
	}
	gw.ignoreNicks = m
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest string) []string {
	return gw.ChannelsOut[dest]
}

func (gw *Gateway) handleMessage(msg config.Message, dest bridge.Bridge) {
	if gw.ignoreMessage(&msg) {
		return
	}
	channels := gw.getDestChannel(&msg, dest.FullOrigin())
	for _, channel := range channels {
		// do not send the message to the bridge we come from if also the channel is the same
		if msg.FullOrigin == dest.FullOrigin() && msg.Channel == channel {
			log.Debug("continue", msg.Protocol, msg.Origin, dest.Protocol(), dest.Origin())
			continue
		}
		msg.Channel = channel
		if msg.Channel == "" {
			log.Debug("empty channel")
			return
		}
		gw.modifyMessage(&msg, dest)
		log.Debugf("sending %#v from %s to %s", msg, msg.Origin, dest.Origin())
		dest.Send(msg)
	}
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	// should we discard messages ?
	for _, entry := range gw.ignoreNicks[msg.FullOrigin] {
		if msg.Username == entry {
			return true
		}
	}
	return false
}

func setNickFormat(msg *config.Message, format string) {
	if format == "" {
		msg.Username = msg.Protocol + "." + msg.Origin + "-" + msg.Username + ": "
		return
	}
	msg.Username = strings.Replace(format, "{NICK}", msg.Username, -1)
	msg.Username = strings.Replace(msg.Username, "{BRIDGE}", msg.Origin, -1)
	msg.Username = strings.Replace(msg.Username, "{PROTOCOL}", msg.Protocol, -1)
}

func (gw *Gateway) modifyMessage(msg *config.Message, dest bridge.Bridge) {
	switch dest.Protocol() {
	case "irc":
		setNickFormat(msg, gw.Config.IRC[dest.Origin()].RemoteNickFormat)
	case "gitter":
		setNickFormat(msg, gw.Config.Gitter[dest.Origin()].RemoteNickFormat)
	case "xmpp":
		setNickFormat(msg, gw.Config.Xmpp[dest.Origin()].RemoteNickFormat)
	case "mattermost":
		setNickFormat(msg, gw.Config.Mattermost[dest.Origin()].RemoteNickFormat)
	case "slack":
		setNickFormat(msg, gw.Config.Slack[dest.Origin()].RemoteNickFormat)
	case "discord":
		setNickFormat(msg, gw.Config.Discord[dest.Origin()].RemoteNickFormat)
	}
}
<commit_msg>Connect only once to each bridge<commit_after>package gateway

import (
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	"strings"
)

type Gateway struct {
	*config.Config
	MyConfig    *config.Gateway
	Bridges     []bridge.Bridge
	ChannelsOut map[string][]string
	ChannelsIn  map[string][]string
	ignoreNicks map[string][]string
	Name        string
}

func New(cfg *config.Config, gateway *config.Gateway) error {
	c := make(chan config.Message)
	gw := &Gateway{}
	gw.Name = gateway.Name
	gw.Config = cfg
	gw.MyConfig = gateway
	exists := make(map[string]bool)
	for _, br := range append(gateway.In, gateway.Out...) {
		if exists[br.Account+br.Channel] {
			continue
		}
		log.Infof("Starting bridge: %s channel: %s", br.Account, br.Channel)
		gw.Bridges = append(gw.Bridges, bridge.New(cfg, &br, c))
		exists[br.Account+br.Channel] = true
	}
	gw.mapChannels()
	gw.mapIgnores()
	exists = make(map[string]bool)
	for _, br := range gw.Bridges {
		br.Connect()
		for _, channel := range append(gw.ChannelsOut[br.FullOrigin()], gw.ChannelsIn[br.FullOrigin()]...) {
			if exists[br.FullOrigin()+channel] {
				continue
			}
			log.Infof("%s: joining %s", br.FullOrigin(), channel)
			br.JoinChannel(channel)
			exists[br.FullOrigin()+channel] = true
		}
	}
	gw.handleReceive(c)
	return nil
}

func (gw *Gateway) handleReceive(c chan config.Message) {
	for {
		select {
		case msg := <-c:
			for _, br := range gw.Bridges {
				gw.handleMessage(msg, br)
			}
		}
	}
}

func (gw *Gateway) mapChannels() error {
	m := make(map[string][]string)
	for _, br := range gw.MyConfig.Out {
		m[br.Account] = append(m[br.Account], br.Channel)
	}
	gw.ChannelsOut = m
	m = nil
	m = make(map[string][]string)
	for _, br := range gw.MyConfig.In {
		m[br.Account] = append(m[br.Account], br.Channel)
	}
	gw.ChannelsIn = m
	return nil
}

func (gw *Gateway) mapIgnores() {
	m := make(map[string][]string)
	for _, br := range gw.MyConfig.In {
		accInfo := strings.Split(br.Account, ".")
		m[br.Account] = strings.Fields(gw.Config.IRC[accInfo[1]].IgnoreNicks)
	}
	gw.ignoreNicks = m
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest string) []string {
	return gw.ChannelsOut[dest]
}

func (gw *Gateway) handleMessage(msg config.Message, dest bridge.Bridge) {
	if gw.ignoreMessage(&msg) {
		return
	}
	channels := gw.getDestChannel(&msg, dest.FullOrigin())
	for _, channel := range channels {
		// do not send the message to the bridge we come from if also the channel is the same
		if msg.FullOrigin == dest.FullOrigin() && msg.Channel == channel {
			continue
		}
		msg.Channel = channel
		if msg.Channel == "" {
			log.Debug("empty channel")
			return
		}
		gw.modifyMessage(&msg, dest)
		log.Debugf("sending %#v from %s to %s", msg, msg.Origin, dest.Origin())
		dest.Send(msg)
	}
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	// should we discard messages ?
	for _, entry := range gw.ignoreNicks[msg.FullOrigin] {
		if msg.Username == entry {
			return true
		}
	}
	return false
}

func setNickFormat(msg *config.Message, format string) {
	if format == "" {
		msg.Username = msg.Protocol + "." + msg.Origin + "-" + msg.Username + ": "
		return
	}
	msg.Username = strings.Replace(format, "{NICK}", msg.Username, -1)
	msg.Username = strings.Replace(msg.Username, "{BRIDGE}", msg.Origin, -1)
	msg.Username = strings.Replace(msg.Username, "{PROTOCOL}", msg.Protocol, -1)
}

func (gw *Gateway) modifyMessage(msg *config.Message, dest bridge.Bridge) {
	switch dest.Protocol() {
	case "irc":
		setNickFormat(msg, gw.Config.IRC[dest.Origin()].RemoteNickFormat)
	case "gitter":
		setNickFormat(msg, gw.Config.Gitter[dest.Origin()].RemoteNickFormat)
	case "xmpp":
		setNickFormat(msg, gw.Config.Xmpp[dest.Origin()].RemoteNickFormat)
	case "mattermost":
		setNickFormat(msg, gw.Config.Mattermost[dest.Origin()].RemoteNickFormat)
	case "slack":
		setNickFormat(msg, gw.Config.Slack[dest.Origin()].RemoteNickFormat)
	case "discord":
		setNickFormat(msg, gw.Config.Discord[dest.Origin()].RemoteNickFormat)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
)

func performChecksum(c *cli.Context) {
	fileInfo, filepath := checkFilepathArgument(c)
	if fileInfo == nil {
		return
	}

	singleMode := c.GlobalBool("single")
	mode := "single"
	if singleMode {
		mode = "single"
	}

	fmt.Printf("The following filepath (%s mode) will be processed: %s\n", mode, filepath)
	notifyDeleteMode(c)

	if !shouldContinue(c) {
		return
	}

	processPath(filepath, fileInfo.Name(), c.GlobalBool("delete"), singleMode)

	if !singleMode {
		files, _ := ioutil.ReadDir(filepath)
		for _, file := range files {
			if file.IsDir() {
				processPath(path.Join(filepath, file.Name()), file.Name(), c.GlobalBool("delete"), true)
			}
		}
	}
}

func processPath(directory string, name string, deleteMode bool, singleMode bool) {
	filename := path.Join(directory, name+".")

	depth := 0
	if singleMode {
		depth = 1
	}

	if deleteMode {
		removeFile(filename + "ffp")
		removeFile(filename + "md5")
		return
	}

	ffp := createFile(filename + "ffp")
	processDirectory(directory, depth, ffp, "ffp")
	ffp.Close()

	md5 := createFile(filename + "md5")
	processDirectory(directory, depth, md5, "md5")
	md5.Close()
}

func processDirectory(filepath string, depth int, out *os.File, mode string) {
	files, _ := ioutil.ReadDir(filepath)
	if len(files) == 0 {
		if mode == "ffp" {
			fmt.Println("Empty folder found:", filepath)
		}
		return
	}

	var parser func(string, string, int) string
	if mode == "ffp" {
		parser = ffpParse
	} else if mode == "md5" {
		parser = md5Parse
	}

	for _, file := range files {
		name := file.Name()

		if file.IsDir() && depth != 0 {
			processDirectory(path.Join(filepath, name), depth+1, out, mode)
		} else if (path.Ext(name) != ".md5") && !file.IsDir() {
			if result := parser(filepath, name, depth); result != "" {
				out.WriteString(result)
			}
		}
	}
}

func md5Parse(filepath string, name string, depth int) string {
	data, err := ioutil.ReadFile(path.Join(filepath, name))

	if err != nil {
		panic(err)
	}

	return fmt.Sprintf("%x *%s%s\r\n", md5.Sum(data), getLastPathComponents(filepath, depth), name)
}

func ffpParse(filepath string, name string, depth int) string {
	if path.Ext(name) != ".flac" {
		return ""
	}

	data, err := exec.Command(
		"metaflac",
		"--show-md5sum",
		path.Join(filepath, name),
	).Output()

	if err != nil {
		panic(err)
	}

	return fmt.Sprintf("%s%s:%s", getLastPathComponents(filepath, depth), name, data)
}
<commit_msg>Fix incorrect checksum output<commit_after>package main

import (
	"crypto/md5"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
)

func performChecksum(c *cli.Context) {
	fileInfo, filepath := checkFilepathArgument(c)
	if fileInfo == nil {
		return
	}

	singleMode := c.GlobalBool("single")
	mode := "batch"
	if singleMode {
		mode = "single"
	}

	fmt.Printf("The following filepath (%s mode) will be processed: %s\n", mode, filepath)
	notifyDeleteMode(c)

	if !shouldContinue(c) {
		return
	}

	processPath(filepath, fileInfo.Name(), c.GlobalBool("delete"), singleMode)

	if !singleMode {
		files, _ := ioutil.ReadDir(filepath)
		for _, file := range files {
			if file.IsDir() {
				processPath(path.Join(filepath, file.Name()), file.Name(), c.GlobalBool("delete"), true)
			}
		}
	}
}

func processPath(directory string, name string, deleteMode bool, singleMode bool) {
	filename := path.Join(directory, name+".")

	depth := 0
	if singleMode {
		depth = 1
	}

	if deleteMode {
		removeFile(filename + "ffp")
		removeFile(filename + "md5")
		return
	}

	ffp := createFile(filename + "ffp")
	processDirectory(directory, depth, ffp, "ffp")
	ffp.Close()

	md5 := createFile(filename + "md5")
	processDirectory(directory, depth, md5, "md5")
	md5.Close()
}

func processDirectory(filepath string, depth int, out *os.File, mode string) {
	files, _ := ioutil.ReadDir(filepath)
	if len(files) == 0 {
		if mode == "ffp" {
			fmt.Println("Empty folder found:", filepath)
		}
		return
	}

	var parser func(string, string, int) string
	if mode == "ffp" {
		parser = ffpParse
	} else if mode == "md5" {
		parser = md5Parse
	}

	for _, file := range files {
		name := file.Name()

		if file.IsDir() && depth != 0 {
			processDirectory(path.Join(filepath, name), depth+1, out, mode)
		} else if (path.Ext(name) != ".md5") && !file.IsDir() {
			if result := parser(filepath, name, depth); result != "" {
				out.WriteString(result)
			}
		}
	}
}

func md5Parse(filepath string, name string, depth int) string {
	data, err := ioutil.ReadFile(path.Join(filepath, name))

	if err != nil {
		panic(err)
	}

	return fmt.Sprintf("%x *%s%s\r\n", md5.Sum(data), getLastPathComponents(filepath, depth), name)
}

func ffpParse(filepath string, name string, depth int) string {
	if path.Ext(name) != ".flac" {
		return ""
	}

	data, err := exec.Command(
		"metaflac",
		"--show-md5sum",
		path.Join(filepath, name),
	).Output()

	if err != nil {
		panic(err)
	}

	return fmt.Sprintf("%s%s:%s", getLastPathComponents(filepath, depth), name, data)
}
<|endoftext|>


<commit_before>package gateway

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"strings"

	"github.com/backstage/backstage/account"
	"github.com/backstage/backstage/api"
	"github.com/backstage/backstage/db"
	. "github.com/backstage/backstage/gateway/filter"
)

type Settings struct {
	ChannelName string
	Host        string
	Port        string
}

type ServiceHandler struct {
	handler http.Handler
	service *account.Service
	filters []Filter
}

type Gateway struct {
	Settings    *Settings
	filters     Filters
	redisClient *db.RedisClient
	services    map[string]*ServiceHandler
}

func (g *Gateway) Filter() Filters {
	if g.filters == nil {
		g.filters = map[string]Filter{}
	}
	return g.filters
}

func (g *Gateway) loadFilters() {
	//g.Filter().Add("AddSecurityHeaders", AddHeaders)
	log.Print("Default filters loaded.")
}

func NewGateway(config *Settings) *Gateway {
	g := &Gateway{
		Settings:    config,
		redisClient: db.NewRedisClient(),
	}

	g.loadFilters()
	return g
}

func (g *Gateway) LoadServices(services []*account.Service) {
	s := make(map[string]*ServiceHandler)
	if services != nil {
		s = g.wrapServices(services)
	}
	g.services = s
	for _, e := range g.services {
		e.handler = createProxy(e)
	}
	log.Print("Services loaded.")
}

func (g *Gateway) Run() {
	log.Print("Backstage Gateway starting...")
	l, err := net.Listen("tcp", g.Settings.Port)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("The proxy is now ready to accept connections on port %s.", g.Settings.Port)
	log.Fatal(http.Serve(l, g))
}

func (g *Gateway) Close() {
	g.redisClient.Close()
}

func (g *Gateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h := g.handler(r); h != nil {
		h.ServeHTTP(w, r)
		return
	}
	notFound(w)
}

func (g *Gateway) wrapServices(services []*account.Service) map[string]*ServiceHandler {
	s := make(map[string]*ServiceHandler)
	for _, serv := range services {
		h := &ServiceHandler{service: serv}
		s[serv.Subdomain] = h
		for _, f := range serv.Filters {
			if filter := g.Filter().Get(f); filter != nil {
				h.filters = append(h.filters, filter)
			}
		}
	}
	return s
}

//FIXME: need to figure out how to test this since it's running on its own goroutine.
func (g *Gateway) RefreshServices() {
	go func() {
		channel := g.Settings.ChannelName
		if channel == "" {
			log.Fatal("Missing channel name.")
		}
		if err := g.redisClient.Subscribe(channel); err != nil {
			fmt.Println("Could not connect to Redis: " + err.Error())
			os.Exit(1)
		}

		for {
			var service account.Service
			data := g.redisClient.Receive().Data
			err := json.Unmarshal([]byte(data), &service)
			if err == nil {
				if service.Disabled {
					g.removeService(&service)
				} else {
					g.addService(&service)
				}
			}
		}
	}()
}

func (g *Gateway) addService(s *account.Service) {
	h := &ServiceHandler{service: s}
	h.handler = createProxy(h)
	g.services[s.Subdomain] = h
	log.Printf("New service has been added: %s -> %s.", s.Subdomain, s.Endpoint)
}

func (g *Gateway) removeService(s *account.Service) {
	delete(g.services, s.Subdomain)
	log.Printf("Service has been removed: %s -> %s.", s.Subdomain, s.Endpoint)
}

func (g *Gateway) handler(r *http.Request) http.Handler {
	h := strings.TrimSpace(r.Host)
	if i := strings.Index(h, ":"); i >= 0 {
		h = h[:i]
	}

	subdomain := extractSubdomain(h)
	if _, ok := g.services[subdomain]; ok {
		return g.services[subdomain].handler
	}
	return nil
}

func extractSubdomain(host string) string {
	var subdomain string
	host_parts := strings.Split(host, ".")
	if len(host_parts) > 2 {
		subdomain = host_parts[0]
	}
	return subdomain
}

func createProxy(e *ServiceHandler) http.Handler {
	if h := e.service.Endpoint; h != "" {
		rp := NewDispatcher(e)
		return rp.proxy
	}
	return nil
}

func notFound(w http.ResponseWriter) {
	nf := api.NotFound(ERR_NOT_FOUND)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(nf.StatusCode)
	fmt.Fprintln(w, nf.Output())
}
<commit_msg>Refactor LoadService to be used on RefreshServices as well.<commit_after>package gateway

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"strings"

	"github.com/backstage/backstage/account"
	"github.com/backstage/backstage/api"
	"github.com/backstage/backstage/db"
	. "github.com/backstage/backstage/gateway/filter"
)

type Settings struct {
	ChannelName string
	Host        string
	Port        string
}

type ServiceHandler struct {
	handler http.Handler
	service *account.Service
	filters []Filter
}

type Gateway struct {
	Settings    *Settings
	filters     Filters
	redisClient *db.RedisClient
	services    map[string]*ServiceHandler
}

func (g *Gateway) Filter() Filters {
	if g.filters == nil {
		g.filters = map[string]Filter{}
	}
	return g.filters
}

func (g *Gateway) loadFilters() {
	//g.Filter().Add("AddSecurityHeaders", AddHeaders)
	log.Print("Default filters loaded.")
}

func NewGateway(config *Settings) *Gateway {
	g := &Gateway{
		Settings:    config,
		redisClient: db.NewRedisClient(),
		services:    make(map[string]*ServiceHandler),
	}

	g.loadFilters()
	return g
}

func (g *Gateway) LoadServices(services []*account.Service) {
	if services != nil {
		s := make(map[string]*ServiceHandler)
		s = g.wrapServices(services)
		for _, e := range s {
			e.handler = createProxy(e)
			g.services[e.service.Subdomain] = e
		}
		log.Print("Services loaded.")
	}
}

func (g *Gateway) Run() {
	log.Print("Backstage Gateway starting...")
	l, err := net.Listen("tcp", g.Settings.Port)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("The proxy is now ready to accept connections on port %s.", g.Settings.Port)
	log.Fatal(http.Serve(l, g))
}

func (g *Gateway) Close() {
	g.redisClient.Close()
}

func (g *Gateway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h := g.handler(r); h != nil {
		h.ServeHTTP(w, r)
		return
	}
	notFound(w)
}

func (g *Gateway) wrapServices(services []*account.Service) map[string]*ServiceHandler {
	s := make(map[string]*ServiceHandler)
	for _, serv := range services {
		h := &ServiceHandler{service: serv}
		s[serv.Subdomain] = h
		for _, f := range serv.Filters {
			if filter := g.Filter().Get(f); filter != nil {
				h.filters = append(h.filters, filter)
			}
		}
	}
	return s
}

//FIXME: need to figure out how to test this since it's running on its own goroutine.
func (g *Gateway) RefreshServices() {
	go func() {
		channel := g.Settings.ChannelName
		if channel == "" {
			log.Fatal("Missing channel name.")
		}
		if err := g.redisClient.Subscribe(channel); err != nil {
			fmt.Println("Could not connect to Redis: " + err.Error())
			os.Exit(1)
		}

		for {
			var service account.Service
			data := g.redisClient.Receive().Data
			err := json.Unmarshal([]byte(data), &service)
			if err == nil {
				if service.Disabled {
					g.removeService(&service)
				} else {
					g.addService(&service)
				}
			}
		}
	}()
}

func (g *Gateway) addService(s *account.Service) {
	g.LoadServices([]*account.Service{s})
	log.Printf("New service has been added: %s -> %s.", s.Subdomain, s.Endpoint)
}

func (g *Gateway) removeService(s *account.Service) {
	delete(g.services, s.Subdomain)
	log.Printf("Service has been removed: %s -> %s.", s.Subdomain, s.Endpoint)
}

func (g *Gateway) handler(r *http.Request) http.Handler {
	h := strings.TrimSpace(r.Host)
	if i := strings.Index(h, ":"); i >= 0 {
		h = h[:i]
	}

	subdomain := extractSubdomain(h)
	if _, ok := g.services[subdomain]; ok {
		return g.services[subdomain].handler
	}
	return nil
}

func extractSubdomain(host string) string {
	var subdomain string
	host_parts := strings.Split(host, ".")
	if len(host_parts) > 2 {
		subdomain = host_parts[0]
	}
	return subdomain
}

func createProxy(e *ServiceHandler) http.Handler {
	if h := e.service.Endpoint; h != "" {
		rp := NewDispatcher(e)
		return rp.proxy
	}
	return nil
}

func notFound(w http.ResponseWriter) {
	nf := api.NotFound(ERR_NOT_FOUND)
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(nf.StatusCode)
	fmt.Fprintln(w, nf.Output())
}
<|endoftext|>


<commit_before><commit_msg>Add relay metrics assertions to connection tests<commit_after><|endoftext|>


<commit_before>package boil

import (
	"math/rand"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"time"

	null "gopkg.in/nullbio/null.v4"

	"github.com/pkg/errors"
	"github.com/satori/go.uuid"
	"github.com/vattle/sqlboiler/strmangle"
)

var (
	typeNullFloat32 = reflect.TypeOf(null.Float32{})
	typeNullFloat64 = reflect.TypeOf(null.Float64{})
	typeNullInt     = reflect.TypeOf(null.Int{})
	typeNullInt8    = reflect.TypeOf(null.Int8{})
	typeNullInt16   = reflect.TypeOf(null.Int16{})
	typeNullInt32   = reflect.TypeOf(null.Int32{})
	typeNullInt64   = reflect.TypeOf(null.Int64{})
	typeNullUint    = reflect.TypeOf(null.Uint{})
	typeNullUint8   = reflect.TypeOf(null.Uint8{})
	typeNullUint16  = reflect.TypeOf(null.Uint16{})
	typeNullUint32  = reflect.TypeOf(null.Uint32{})
	typeNullUint64  = reflect.TypeOf(null.Uint64{})
	typeNullString  = reflect.TypeOf(null.String{})
	typeNullBool    = reflect.TypeOf(null.Bool{})
	typeNullTime    = reflect.TypeOf(null.Time{})
	typeTime        = reflect.TypeOf(time.Time{})

	rgxValidTime = regexp.MustCompile(`[2-9]+`)
)

type seed int

var sd = new(seed)

func (s *seed) nextInt() int {
	nextInt := int(*s)
	*s++
	return nextInt
}

// RandomizeStruct takes an object and fills it with random data.
// It will ignore the fields in the blacklist.
func RandomizeStruct(str interface{}, colTypes map[string]string, includeInvalid bool, blacklist ...string) error {
	// Don't modify blacklist
	copyBlacklist := make([]string, len(blacklist))
	copy(copyBlacklist, blacklist)
	blacklist = copyBlacklist

	sort.Strings(blacklist)

	// Check if it's pointer
	value := reflect.ValueOf(str)
	kind := value.Kind()
	if kind != reflect.Ptr {
		return errors.Errorf("Outer element should be a pointer, given a non-pointer: %T", str)
	}

	// Check if it's a struct
	value = value.Elem()
	kind = value.Kind()
	if kind != reflect.Struct {
		return errors.Errorf("Inner element should be a struct, given a non-struct: %T", str)
	}

	typ := value.Type()
	nFields := value.NumField()

	// Iterate through fields, randomizing
	for i := 0; i < nFields; i++ {
		fieldVal := value.Field(i)
		fieldTyp := typ.Field(i)

		var found bool
		for _, v := range blacklist {
			if strmangle.TitleCase(v) == fieldTyp.Name {
				found = true
				break
			}
		}

		if found {
			continue
		}

		fieldDBType := colTypes[fieldTyp.Name]
		if err := randomizeField(fieldVal, fieldDBType, includeInvalid); err != nil {
			return err
		}
	}

	return nil
}

// randDate generates a random time.Time between 1850 and 2050.
// Only the Day/Month/Year columns are set so that Dates and DateTimes do
// not cause mismatches in the test data comparisons.
func randDate(sd int) time.Time {
	t := time.Date(
		1850+rand.Intn(sd),
		time.Month(1+rand.Intn(12)),
		1+rand.Intn(25),
		0,
		0,
		0,
		0,
		time.UTC,
	)

	return t
}

func randomizeField(field reflect.Value, fieldType string, includeInvalid bool) error {
	kind := field.Kind()
	typ := field.Type()

	var newVal interface{}

	if kind == reflect.Struct {
		var b bool
		if includeInvalid {
			b = rand.Intn(2) == 1
		} else {
			b = true
		}
		switch typ {
		case typeNullBool:
			if b {
				newVal = null.NewBool(sd.nextInt()%2 == 0, b)
			} else {
				newVal = null.NewBool(false, false)
			}
		case typeNullString:
			if fieldType == "uuid" {
				newVal = null.NewString(uuid.NewV4().String(), true)
			} else if b {
				switch fieldType {
				case "interval":
					newVal = null.NewString(strconv.Itoa((sd.nextInt()%26)+2)+" days", b)
				default:
					newVal = null.NewString(randStr(1, sd.nextInt()), b)
				}
			} else {
				newVal = null.NewString("", false)
			}
		case typeNullTime:
			if b {
				newVal = null.NewTime(randDate(sd.nextInt()), b)
			} else {
				newVal = null.NewTime(time.Time{}, false)
			}
		case typeTime:
			newVal = randDate(sd.nextInt())
		case typeNullFloat32:
			if b {
				newVal = null.NewFloat32(float32(sd.nextInt()%10)/10.0+float32(sd.nextInt()%10), b)
			} else {
				newVal = null.NewFloat32(0.0, false)
			}
		case typeNullFloat64:
			if b {
				newVal = null.NewFloat64(float64(sd.nextInt()%10)/10.0+float64(sd.nextInt()%10), b)
			} else {
				newVal = null.NewFloat64(0.0, false)
			}
		case typeNullInt:
			if b {
				newVal = null.NewInt(sd.nextInt(), b)
			} else {
				newVal = null.NewInt(0, false)
			}
		case typeNullInt8:
			if b {
				newVal = null.NewInt8(int8(sd.nextInt()), b)
			} else {
				newVal = null.NewInt8(0, false)
			}
		case typeNullInt16:
			if b {
				newVal = null.NewInt16(int16(sd.nextInt()), b)
			} else {
				newVal = null.NewInt16(0, false)
			}
		case typeNullInt32:
			if b {
				newVal = null.NewInt32(int32(sd.nextInt()), b)
			} else {
				newVal = null.NewInt32(0, false)
			}
		case typeNullInt64:
			if b {
				newVal = null.NewInt64(int64(sd.nextInt()), b)
			} else {
				newVal = null.NewInt64(0, false)
			}
		case typeNullUint:
			if b {
				newVal = null.NewUint(uint(sd.nextInt()), b)
			} else {
				newVal = null.NewUint(0, false)
			}
		case typeNullUint8:
			if b {
				newVal = null.NewUint8(uint8(sd.nextInt()), b)
			} else {
				newVal = null.NewUint8(0, false)
			}
		case typeNullUint16:
			if b {
				newVal = null.NewUint16(uint16(sd.nextInt()), b)
			} else {
				newVal = null.NewUint16(0, false)
			}
		case typeNullUint32:
			if b {
				newVal = null.NewUint32(uint32(sd.nextInt()), b)
			} else {
				newVal = null.NewUint32(0, false)
			}
		case typeNullUint64:
			if b {
				newVal = null.NewUint64(uint64(sd.nextInt()), b)
			} else {
				newVal = null.NewUint64(0, false)
			}
		}
	} else {
		switch kind {
		case reflect.Float32:
			newVal = float32(float32(sd.nextInt()%10)/10.0 + float32(sd.nextInt()%10))
		case reflect.Float64:
			newVal = float64(float64(sd.nextInt()%10)/10.0 + float64(sd.nextInt()%10))
		case reflect.Int:
			newVal = sd.nextInt()
		case reflect.Int8:
			newVal = int8(sd.nextInt())
		case reflect.Int16:
			newVal = int16(sd.nextInt())
		case reflect.Int32:
			newVal = int32(sd.nextInt())
		case reflect.Int64:
			newVal = int64(sd.nextInt())
		case reflect.Uint:
			newVal = uint(sd.nextInt())
		case reflect.Uint8:
			newVal = uint8(sd.nextInt())
		case reflect.Uint16:
			newVal = uint16(sd.nextInt())
		case reflect.Uint32:
			newVal = uint32(sd.nextInt())
		case reflect.Uint64:
			newVal = uint64(sd.nextInt())
		case reflect.Bool:
			var b bool
			if includeInvalid {
				b = sd.nextInt()%2 == 0
			} else {
				b = true
			}
			newVal = b
		case reflect.String:
			switch fieldType {
			case "interval":
				newVal = strconv.Itoa((sd.nextInt()%26)+2) + " days"
			case "uuid":
				newVal = uuid.NewV4().String()
			default:
				newVal = randStr(1, sd.nextInt())
			}
		case reflect.Slice:
			sliceVal := typ.Elem()
			if sliceVal.Kind() != reflect.Uint8 {
				return errors.Errorf("unsupported slice type: %T", typ.String())
			}
			newVal = randByteSlice(5+rand.Intn(20), sd.nextInt())
		default:
			return errors.Errorf("unsupported type: %T", typ.String())
		}
	}

	field.Set(reflect.ValueOf(newVal))

	return nil
}

const alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func randStr(ln int, s int) string {
	str := make([]byte, ln)
	for i := 0; i < ln; i++ {
		str[i] = byte(alphabet[s%len(alphabet)])
	}

	return string(str)
}

func randByteSlice(ln int, s int) []byte {
	str := make([]byte, ln)
	for i := 0; i < ln; i++ {
		str[i] = byte(s % 256)
	}

	return str
}
<commit_msg>Make counters atomic<commit_after>package boil

import (
	"math/rand"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"sync/atomic"
	"time"

	null "gopkg.in/nullbio/null.v4"

	"github.com/pkg/errors"
	"github.com/satori/go.uuid"
	"github.com/vattle/sqlboiler/strmangle"
)

var (
	typeNullFloat32 = reflect.TypeOf(null.Float32{})
	typeNullFloat64 = reflect.TypeOf(null.Float64{})
	typeNullInt     = reflect.TypeOf(null.Int{})
	typeNullInt8    = reflect.TypeOf(null.Int8{})
	typeNullInt16   = reflect.TypeOf(null.Int16{})
	typeNullInt32   = reflect.TypeOf(null.Int32{})
	typeNullInt64   = reflect.TypeOf(null.Int64{})
	typeNullUint    = reflect.TypeOf(null.Uint{})
	typeNullUint8   = reflect.TypeOf(null.Uint8{})
	typeNullUint16  = reflect.TypeOf(null.Uint16{})
	typeNullUint32  = reflect.TypeOf(null.Uint32{})
	typeNullUint64  = reflect.TypeOf(null.Uint64{})
	typeNullString  = reflect.TypeOf(null.String{})
	typeNullBool    = reflect.TypeOf(null.Bool{})
	typeNullTime    = reflect.TypeOf(null.Time{})
	typeTime        = reflect.TypeOf(time.Time{})

	rgxValidTime = regexp.MustCompile(`[2-9]+`)
)

type seed int64

var sd = new(seed)

func (s *seed) nextInt() int {
	return int(atomic.AddInt64((*int64)(s), 1))
}

// RandomizeStruct takes an object and fills it with random data.
// It will ignore the fields in the blacklist.
func RandomizeStruct(str interface{}, colTypes map[string]string, includeInvalid bool, blacklist ...string) error {
	// Don't modify blacklist
	copyBlacklist := make([]string, len(blacklist))
	copy(copyBlacklist, blacklist)
	blacklist = copyBlacklist

	sort.Strings(blacklist)

	// Check if it's pointer
	value := reflect.ValueOf(str)
	kind := value.Kind()
	if kind != reflect.Ptr {
		return errors.Errorf("Outer element should be a pointer, given a non-pointer: %T", str)
	}

	// Check if it's a struct
	value = value.Elem()
	kind = value.Kind()
	if kind != reflect.Struct {
		return errors.Errorf("Inner element should be a struct, given a non-struct: %T", str)
	}

	typ := value.Type()
	nFields := value.NumField()

	// Iterate through fields, randomizing
	for i := 0; i < nFields; i++ {
		fieldVal := value.Field(i)
		fieldTyp := typ.Field(i)

		var found bool
		for _, v := range blacklist {
			if strmangle.TitleCase(v) == fieldTyp.Name {
				found = true
				break
			}
		}

		if found {
			continue
		}

		fieldDBType := colTypes[fieldTyp.Name]
		if err := randomizeField(fieldVal, fieldDBType, includeInvalid); err != nil {
			return err
		}
	}

	return nil
}

// randDate generates a random time.Time between 1850 and 2050.
// Only the Day/Month/Year columns are set so that Dates and DateTimes do
// not cause mismatches in the test data comparisons.
func randDate(sd int) time.Time {
	t := time.Date(
		1850+rand.Intn(sd),
		time.Month(1+rand.Intn(12)),
		1+rand.Intn(25),
		0,
		0,
		0,
		0,
		time.UTC,
	)

	return t
}

func randomizeField(field reflect.Value, fieldType string, includeInvalid bool) error {
	kind := field.Kind()
	typ := field.Type()

	var newVal interface{}

	if kind == reflect.Struct {
		var b bool
		if includeInvalid {
			b = rand.Intn(2) == 1
		} else {
			b = true
		}
		switch typ {
		case typeNullBool:
			if b {
				newVal = null.NewBool(sd.nextInt()%2 == 0, b)
			} else {
				newVal = null.NewBool(false, false)
			}
		case typeNullString:
			if fieldType == "uuid" {
				newVal = null.NewString(uuid.NewV4().String(), true)
			} else if b {
				switch fieldType {
				case "interval":
					newVal = null.NewString(strconv.Itoa((sd.nextInt()%26)+2)+" days", b)
				default:
					newVal = null.NewString(randStr(1, sd.nextInt()), b)
				}
			} else {
				newVal = null.NewString("", false)
			}
		case typeNullTime:
			if b {
				newVal = null.NewTime(randDate(sd.nextInt()), b)
			} else {
				newVal = null.NewTime(time.Time{}, false)
			}
		case typeTime:
			newVal = randDate(sd.nextInt())
		case typeNullFloat32:
			if b {
				newVal = null.NewFloat32(float32(sd.nextInt()%10)/10.0+float32(sd.nextInt()%10), b)
			} else {
				newVal = null.NewFloat32(0.0, false)
			}
		case typeNullFloat64:
			if b {
				newVal = null.NewFloat64(float64(sd.nextInt()%10)/10.0+float64(sd.nextInt()%10), b)
			} else {
				newVal = null.NewFloat64(0.0, false)
			}
		case typeNullInt:
			if b {
				newVal = null.NewInt(sd.nextInt(), b)
			} else {
				newVal = null.NewInt(0, false)
			}
		case typeNullInt8:
			if b {
				newVal = null.NewInt8(int8(sd.nextInt()), b)
			} else {
				newVal = null.NewInt8(0, false)
			}
		case typeNullInt16:
			if b {
				newVal = null.NewInt16(int16(sd.nextInt()), b)
			} else {
				newVal = null.NewInt16(0, false)
			}
		case typeNullInt32:
			if b {
				newVal = null.NewInt32(int32(sd.nextInt()), b)
			} else {
				newVal = null.NewInt32(0, false)
			}
		case typeNullInt64:
			if b {
				newVal = null.NewInt64(int64(sd.nextInt()), b)
			} else {
				newVal = null.NewInt64(0, false)
			}
		case typeNullUint:
			if b {
				newVal = null.NewUint(uint(sd.nextInt()), b)
			} else {
				newVal = null.NewUint(0, false)
			}
		case typeNullUint8:
			if b {
				newVal = null.NewUint8(uint8(sd.nextInt()), b)
			} else {
				newVal = null.NewUint8(0, false)
			}
		case typeNullUint16:
			if b {
				newVal = null.NewUint16(uint16(sd.nextInt()), b)
			} else {
				newVal = null.NewUint16(0, false)
			}
		case typeNullUint32:
			if b {
				newVal = null.NewUint32(uint32(sd.nextInt()), b)
			} else {
				newVal = null.NewUint32(0, false)
			}
		case typeNullUint64:
			if b {
				newVal = null.NewUint64(uint64(sd.nextInt()), b)
			} else {
				newVal = null.NewUint64(0, false)
			}
		}
	} else {
		switch kind {
		case reflect.Float32:
			newVal = float32(float32(sd.nextInt()%10)/10.0 + float32(sd.nextInt()%10))
		case reflect.Float64:
			newVal = float64(float64(sd.nextInt()%10)/10.0 + float64(sd.nextInt()%10))
		case reflect.Int:
			newVal = sd.nextInt()
		case reflect.Int8:
			newVal = int8(sd.nextInt())
		case reflect.Int16:
			newVal = int16(sd.nextInt())
		case reflect.Int32:
			newVal = int32(sd.nextInt())
		case reflect.Int64:
			newVal = int64(sd.nextInt())
		case reflect.Uint:
			newVal = uint(sd.nextInt())
		case reflect.Uint8:
			newVal = uint8(sd.nextInt())
		case reflect.Uint16:
			newVal = uint16(sd.nextInt())
		case reflect.Uint32:
			newVal = uint32(sd.nextInt())
		case reflect.Uint64:
			newVal = uint64(sd.nextInt())
		case reflect.Bool:
			var b bool
			if includeInvalid {
				b = sd.nextInt()%2 == 0
			} else {
				b = true
			}
			newVal = b
		case reflect.String:
			switch fieldType {
			case "interval":
				newVal = strconv.Itoa((sd.nextInt()%26)+2) + " days"
			case "uuid":
				newVal = uuid.NewV4().String()
			default:
				newVal = randStr(1, sd.nextInt())
			}
		case reflect.Slice:
			sliceVal := typ.Elem()
			if sliceVal.Kind() != reflect.Uint8 {
				return errors.Errorf("unsupported slice type: %T", typ.String())
			}
			newVal = randByteSlice(5+rand.Intn(20), sd.nextInt())
		default:
			return errors.Errorf("unsupported type: %T", typ.String())
		}
	}

	field.Set(reflect.ValueOf(newVal))

	return nil
}

const alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func randStr(ln int, s int) string {
	str := make([]byte, ln)
	for i := 0; i < ln; i++ {
		str[i] = byte(alphabet[s%len(alphabet)])
	}

	return string(str)
}

func randByteSlice(ln int, s int) []byte {
	str := make([]byte, ln)
	for i := 0; i < ln; i++ {
		str[i] = byte(s % 256)
	}

	return str
}
<|endoftext|>


<commit_before>package bolt

import (
	"fmt"
	"os"
	"syscall"
	"time"
	"unsafe"
)

// LockFileEx code derived from golang build filemutex_windows.go @ v1.5.1
var (
	modkernel32      = syscall.NewLazyDLL("kernel32.dll")
	procLockFileEx   = modkernel32.NewProc("LockFileEx")
	procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
)

const (
	lockExt = ".lock"

	// see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
	flagLockExclusive       = 2
	flagLockFailImmediately = 1

	// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
	errLockViolation syscall.Errno = 0x21
)

func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
	r, _, err := procLockFileEx.Call(uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
	if r == 0 {
		return err
	}
	return nil
}

func unlockFileEx(h syscall.Handle, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
	r, _, err := procUnlockFileEx.Call(uintptr(h), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)), 0)
	if r == 0 {
		return err
	}
	return nil
}

// fdatasync flushes written data to a file descriptor.
func fdatasync(db *DB) error {
	return db.file.Sync()
}

// flock acquires an advisory lock on a file descriptor.
func flock(db *DB, mode os.FileMode, exclusive bool, timeout time.Duration) error {
	// Create a separate lock file on windows because a process
	// cannot share an exclusive lock on the same file. This is
	// needed during Tx.WriteTo().
	f, err := os.OpenFile(db.path+lockExt, os.O_CREATE, mode)
	if err != nil {
		return err
	}
	db.lockfile = f

	var t time.Time
	if timeout != 0 {
		t = time.Now()
	}
	fd := db.file.Fd()
	var flag uint32 = flagLockFailImmediately
	if exclusive {
		flag |= flagLockExclusive
	}
	for {
		// Attempt to obtain an exclusive lock.
		err := lockFileEx(syscall.Handle(fd), flag, 0, 1, 0, &syscall.Overlapped{})
		if err == nil {
			return nil
		} else if err != errLockViolation {
			return err
		}

		// If we timed oumercit then return an error.
		if timeout != 0 && time.Since(t) > timeout-flockRetryTimeout {
			return ErrTimeout
		}

		// Wait for a bit and try again.
		time.Sleep(flockRetryTimeout)
	}
}

// funlock releases an advisory lock on a file descriptor.
func funlock(db *DB) error {
	err := unlockFileEx(syscall.Handle(db.lockfile.Fd()), 0, 1, 0, &syscall.Overlapped{})
	db.lockfile.Close()
	os.Remove(db.path + lockExt)
	return err
}

// mmap memory maps a DB's data file.
// Based on: https://github.com/edsrzf/mmap-go
func mmap(db *DB, sz int) error {
	if !db.readOnly {
		// Truncate the database to the size of the mmap.
		if err := db.file.Truncate(int64(sz)); err != nil {
			return fmt.Errorf("truncate: %s", err)
		}
	}

	// Open a file mapping handle.
	sizelo := uint32(sz >> 32)
	sizehi := uint32(sz) & 0xffffffff
	h, errno := syscall.CreateFileMapping(syscall.Handle(db.file.Fd()), nil, syscall.PAGE_READONLY, sizelo, sizehi, nil)
	if h == 0 {
		return os.NewSyscallError("CreateFileMapping", errno)
	}

	// Create the memory map.
	addr, errno := syscall.MapViewOfFile(h, syscall.FILE_MAP_READ, 0, 0, uintptr(sz))
	if addr == 0 {
		return os.NewSyscallError("MapViewOfFile", errno)
	}

	// Close mapping handle.
	if err := syscall.CloseHandle(syscall.Handle(h)); err != nil {
		return os.NewSyscallError("CloseHandle", err)
	}

	// Convert to a byte array.
	db.data = ((*[maxMapSize]byte)(unsafe.Pointer(addr)))
	db.datasz = sz

	return nil
}

// munmap unmaps a pointer from a file.
// Based on: https://github.com/edsrzf/mmap-go
func munmap(db *DB) error {
	if db.data == nil {
		return nil
	}

	addr := (uintptr)(unsafe.Pointer(&db.data[0]))
	if err := syscall.UnmapViewOfFile(addr); err != nil {
		return os.NewSyscallError("UnmapViewOfFile", err)
	}
	return nil
}
<commit_msg>fix funlock error when call db.Close on windows<commit_after>package bolt

import (
	"fmt"
	"os"
	"syscall"
	"time"
	"unsafe"
)

// LockFileEx code derived from golang build filemutex_windows.go @ v1.5.1
var (
	modkernel32      = syscall.NewLazyDLL("kernel32.dll")
	procLockFileEx   = modkernel32.NewProc("LockFileEx")
	procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
)

const (
	lockExt = ".lock"

	// see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
	flagLockExclusive       = 2
	flagLockFailImmediately = 1

	// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
	errLockViolation syscall.Errno = 0x21
)

func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
	r, _, err := procLockFileEx.Call(uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
	if r == 0 {
		return err
	}
	return nil
}

func unlockFileEx(h syscall.Handle, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
	r, _, err := procUnlockFileEx.Call(uintptr(h), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)), 0)
	if r == 0 {
		return err
	}
	return nil
}

// fdatasync flushes written data to a file descriptor.
func fdatasync(db *DB) error {
	return db.file.Sync()
}

// flock acquires an advisory lock on a file descriptor.
func flock(db *DB, mode os.FileMode, exclusive bool, timeout time.Duration) error {
	// Create a separate lock file on windows because a process
	// cannot share an exclusive lock on the same file. This is
	// needed during Tx.WriteTo().
	f, err := os.OpenFile(db.path+lockExt, os.O_CREATE, mode)
	if err != nil {
		return err
	}
	db.lockfile = f

	var t time.Time
	if timeout != 0 {
		t = time.Now()
	}
	fd := f.Fd()
	var flag uint32 = flagLockFailImmediately
	if exclusive {
		flag |= flagLockExclusive
	}
	for {
		// Attempt to obtain an exclusive lock.
		err := lockFileEx(syscall.Handle(fd), flag, 0, 1, 0, &syscall.Overlapped{})
		if err == nil {
			return nil
		} else if err != errLockViolation {
			return err
		}

		// If we timed oumercit then return an error.
		if timeout != 0 && time.Since(t) > timeout-flockRetryTimeout {
			return ErrTimeout
		}

		// Wait for a bit and try again.
		time.Sleep(flockRetryTimeout)
	}
}

// funlock releases an advisory lock on a file descriptor.
func funlock(db *DB) error {
	err := unlockFileEx(syscall.Handle(db.lockfile.Fd()), 0, 1, 0, &syscall.Overlapped{})
	db.lockfile.Close()
	os.Remove(db.path + lockExt)
	return err
}

// mmap memory maps a DB's data file.
// Based on: https://github.com/edsrzf/mmap-go
func mmap(db *DB, sz int) error {
	if !db.readOnly {
		// Truncate the database to the size of the mmap.
		if err := db.file.Truncate(int64(sz)); err != nil {
			return fmt.Errorf("truncate: %s", err)
		}
	}

	// Open a file mapping handle.
	sizelo := uint32(sz >> 32)
	sizehi := uint32(sz) & 0xffffffff
	h, errno := syscall.CreateFileMapping(syscall.Handle(db.file.Fd()), nil, syscall.PAGE_READONLY, sizelo, sizehi, nil)
	if h == 0 {
		return os.NewSyscallError("CreateFileMapping", errno)
	}

	// Create the memory map.
	addr, errno := syscall.MapViewOfFile(h, syscall.FILE_MAP_READ, 0, 0, uintptr(sz))
	if addr == 0 {
		return os.NewSyscallError("MapViewOfFile", errno)
	}

	// Close mapping handle.
	if err := syscall.CloseHandle(syscall.Handle(h)); err != nil {
		return os.NewSyscallError("CloseHandle", err)
	}

	// Convert to a byte array.
	db.data = ((*[maxMapSize]byte)(unsafe.Pointer(addr)))
	db.datasz = sz

	return nil
}

// munmap unmaps a pointer from a file.
// Based on: https://github.com/edsrzf/mmap-go
func munmap(db *DB) error {
	if db.data == nil {
		return nil
	}

	addr := (uintptr)(unsafe.Pointer(&db.data[0]))
	if err := syscall.UnmapViewOfFile(addr); err != nil {
		return os.NewSyscallError("UnmapViewOfFile", err)
	}
	return nil
}
<|endoftext|>


<commit_before><commit_msg>Add default handle for null exec<commit_after><|endoftext|>


<commit_before>package wbrules

import (
	"github.com/contactless/wbgo"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

var editorPathRx = regexp.MustCompile(`^[\w/-]{0,256}\w{1,253}\.js$`)

type Editor struct {
	locFileManager LocFileManager
}

type EditorError struct {
	code    int32
	message string
}

func (err *EditorError) Error() string {
	return err.message
}

func (err *EditorError) ErrorCode() int32 {
	return err.code
}

const (
	// no iota here because these values may be used
	// by external software
	EDITOR_ERROR_INVALID_PATH   = 1000
	EDITOR_ERROR_LISTDIR        = 1001
	EDITOR_ERROR_WRITE          = 1002
	EDITOR_ERROR_FILE_NOT_FOUND = 1003
	EDITOR_ERROR_REMOVE         = 1004
)

var invalidPathError = &EditorError{EDITOR_ERROR_INVALID_PATH, "Invalid path"}
var listDirError = &EditorError{EDITOR_ERROR_LISTDIR, "Error listing the directory"}
var writeError = &EditorError{EDITOR_ERROR_WRITE, "Error writing the file"}
var fileNotFoundError = &EditorError{EDITOR_ERROR_FILE_NOT_FOUND, "File not found"}
var rmError = &EditorError{EDITOR_ERROR_REMOVE, "Error removing the file"}

func NewEditor(locFileManager LocFileManager) *Editor {
	return &Editor{locFileManager}
}

func (editor *Editor) List(args *struct{}, reply *[]LocFileEntry) (err error) {
	*reply, err = editor.locFileManager.ListSourceFiles()
	return
}

type EditorSaveArgs struct {
	Path    string `json:"path"`
	Content string `json:"content"`
}

func (editor *Editor) Save(args *EditorSaveArgs, reply *bool) error {
	if !editorPathRx.MatchString(args.Path) {
		return invalidPathError
	}

	targetPath := filepath.Join(editor.locFileManager.ScriptDir(), args.Path)

	if strings.Contains(args.Path, "/") {
		if err := os.MkdirAll(filepath.Dir(targetPath), 0777); err != nil {
			wbgo.Error.Printf("error making dirs for %s: %s", targetPath, err)
			return writeError
		}
	}

	if err := ioutil.WriteFile(targetPath, []byte(args.Content), 0777); err != nil {
		wbgo.Error.Printf("error writing %s: %s", targetPath, err)
		return writeError
	}

	*reply = true
	return nil
}

type EditorRemoveArgs struct {
	Path string `json:"path"`
}

func (editor *Editor) Remove(args *EditorRemoveArgs, reply *bool) error {
	entries, err := editor.locFileManager.ListSourceFiles()
	if err != nil {
		// yes, listing the directory is necessary to check whether
		// the file can be removed
		return listDirError
	}

	for _, entry := range entries {
		if entry.VirtualPath != args.Path {
			continue
		}
		if err := os.Remove(entry.PhysicalPath); err != nil {
			wbgo.Error.Printf("error removing %s: %s", entry.PhysicalPath, err)
			return rmError
		}
		*reply = true
		return nil
	}

	return fileNotFoundError
}
<commit_msg>Relax editor path regexp a bit.<commit_after>package wbrules

import (
	"github.com/contactless/wbgo"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

var editorPathRx = regexp.MustCompile(`^[\w/-]{0,256}[\w-]{1,253}\.js$`)

type Editor struct {
	locFileManager LocFileManager
}

type EditorError struct {
	code    int32
	message string
}

func (err *EditorError) Error() string {
	return err.message
}

func (err *EditorError) ErrorCode() int32 {
	return err.code
}

const (
	// no iota here because these values may be used
	// by external software
	EDITOR_ERROR_INVALID_PATH   = 1000
	EDITOR_ERROR_LISTDIR        = 1001
	EDITOR_ERROR_WRITE          = 1002
	EDITOR_ERROR_FILE_NOT_FOUND = 1003
	EDITOR_ERROR_REMOVE         = 1004
)

var invalidPathError = &EditorError{EDITOR_ERROR_INVALID_PATH, "Invalid path"}
var listDirError = &EditorError{EDITOR_ERROR_LISTDIR, "Error listing the directory"}
var writeError = &EditorError{EDITOR_ERROR_WRITE, "Error writing the file"}
var fileNotFoundError = &EditorError{EDITOR_ERROR_FILE_NOT_FOUND, "File not found"}
var rmError = &EditorError{EDITOR_ERROR_REMOVE, "Error removing the file"}

func NewEditor(locFileManager LocFileManager) *Editor {
	return &Editor{locFileManager}
}

func (editor *Editor) List(args *struct{}, reply *[]LocFileEntry) (err error) {
	*reply, err = editor.locFileManager.ListSourceFiles()
	return
}

type EditorSaveArgs struct {
	Path    string `json:"path"`
	Content string `json:"content"`
}

func (editor *Editor) Save(args *EditorSaveArgs, reply *bool) error {
	if !editorPathRx.MatchString(args.Path) {
		return invalidPathError
	}

	targetPath := filepath.Join(editor.locFileManager.ScriptDir(), args.Path)

	if strings.Contains(args.Path, "/") {
		if err := os.MkdirAll(filepath.Dir(targetPath), 0777); err != nil {
			wbgo.Error.Printf("error making dirs for %s: %s", targetPath, err)
			return writeError
		}
	}

	if err := ioutil.WriteFile(targetPath, []byte(args.Content), 0777); err != nil {
		wbgo.Error.Printf("error writing %s: %s", targetPath, err)
		return writeError
	}

	*reply = true
	return nil
}

type EditorRemoveArgs struct {
	Path string `json:"path"`
}

func (editor *Editor) Remove(args *EditorRemoveArgs, reply *bool) error {
	entries, err := editor.locFileManager.ListSourceFiles()
	if err != nil {
		// yes, listing the directory is necessary to check whether
		// the file can be removed
		return listDirError
	}

	for _, entry := range entries {
		if entry.VirtualPath != args.Path {
			continue
		}
		if err := os.Remove(entry.PhysicalPath); err != nil {
			wbgo.Error.Printf("error removing %s: %s", entry.PhysicalPath, err)
			return rmError
		}
		*reply = true
		return nil
	}

	return fileNotFoundError
}
<|endoftext|>


<commit_before>package api

import (
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/hokkaido/specter/web/api/container"
)

func RegisterRoutes(r *mux.Router, dockerAddr string) {
	apiRouter := mux.NewRouter()
	container.RegisterRoutes(apiRouter)

	r.Handle("/api/", negroni.New(
		NewApiMiddleware(dockerAddr),
		negroni.Wrap(apiRouter),
	))
}
<commit_msg>Rewrite api registration<commit_after>package api

import (
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/hokkaido/specter/web/api/container"
)

func RegisterRoutes(r *mux.Router, dockerAddr string) {
	apiRouter := mux.NewRouter()
	container.RegisterRoutes(apiRouter)

	apiWithMiddleware = negroni.New()
	apiMiddleware = NewApiMiddleware(dockerAddr)
	apiWithMiddleware.Use(negroni.HandlerFunc(apiMiddleware.ServeHTTP))
	apiWithMiddleWare.UseHandler(apiRouter)
	r.Handle("/api/", apiWithMiddleware)
	/*r.Handle("/api/", negroni.New(
		NewApiMiddleware(dockerAddr),
		negroni.Wrap(apiRouter),
	))*/
}
<|endoftext|>


<commit_before>package generator

import (
	"fmt"
)

const arraySerializerTemplate = `
func %v(r %v, w io.Writer) error {
	err := writeLong(int64(len(r)),w)
	if err != nil {
		return err
	}
	for _, e := range r {
		err = %v(e, w)
		if err != nil {
			return err
		}
	}
	return writeLong(0,w)
}
`

const arrayDeserializerTemplate = `
func %v(r io.Reader) (%v, error) {
	var err error
	var blkSize int64
	var arr %v
	for {
		blkSize, err = readLong(r)
		if err != nil {
			return nil, err
		}
		if blkSize == 0 {
			break
		}
		if blkSize < 0 {
			blkSize = -blkSize
			_, err = readLong(r)
			if err != nil {
				return nil, err
			}
		}
		for i := int64(0); i < blkSize; i++ {
			elem, err := %v(r)
			if err != nil {
				return nil, err
			}
			arr = append(arr, elem)
		}
	}
	return arr, nil
}
`

type arrayField struct {
	name     string
	itemType field
}

func (s *arrayField) Name() string {
	return toPublicName(s.name)
}

func (s *arrayField) FieldType() string {
	return "Array" + s.itemType.FieldType()
}

func (s *arrayField) GoType() string {
	return fmt.Sprintf("[]%v", s.itemType.GoType())
}

func (s *arrayField) SerializerMethod() string {
	return fmt.Sprintf("write%v", s.FieldType())
}

func (s *arrayField) DeserializerMethod() string {
	return fmt.Sprintf("read%v", s.FieldType())
}

func (s *arrayField) AddStruct(p *Package) {
	s.itemType.AddStruct(p)
}

func (s *arrayField) AddSerializer(p *Package) {
	itemMethodName := s.itemType.SerializerMethod()
	methodName := s.SerializerMethod()
	arraySerializer := fmt.Sprintf(arraySerializerTemplate, s.SerializerMethod(), s.GoType(), itemMethodName)
	s.itemType.AddSerializer(p)
	p.addFunction(UTIL_FILE, "", methodName, arraySerializer)
	p.addFunction(UTIL_FILE, "", "writeLong", writeLongMethod)
	p.addFunction(UTIL_FILE, "", "encodeInt", encodeIntMethod)
	p.addStruct(UTIL_FILE, "ByteWriter", byteWriterInterface)
	p.addImport(UTIL_FILE, "io")
}

func (s *arrayField) AddDeserializer(p *Package) {
	itemMethodName := s.itemType.DeserializerMethod()
	methodName := s.DeserializerMethod()
	arrayDeserializer := fmt.Sprintf(arrayDeserializerTemplate, methodName, s.GoType(), s.GoType(), itemMethodName)
	s.itemType.AddDeserializer(p)
	p.addFunction(UTIL_FILE, "", methodName, arrayDeserializer)
	p.addFunction(UTIL_FILE, "", "readLong", readLongMethod)
	p.addImport(UTIL_FILE, "io")
}
<commit_msg>Array deserializer creates an empty array instead of a nil array<commit_after>package generator

import (
	"fmt"
)

const arraySerializerTemplate = `
func %v(r %v, w io.Writer) error {
	err := writeLong(int64(len(r)),w)
	if err != nil {
		return err
	}
	for _, e := range r {
		err = %v(e, w)
		if err != nil {
			return err
		}
	}
	return writeLong(0,w)
}
`

const arrayDeserializerTemplate = `
func %v(r io.Reader) (%v, error) {
	var err error
	var blkSize int64
	var arr = make(%v, 0)
	for {
		blkSize, err = readLong(r)
		if err != nil {
			return nil, err
		}
		if blkSize == 0 {
			break
		}
		if blkSize < 0 {
			blkSize = -blkSize
			_, err = readLong(r)
			if err != nil {
				return nil, err
			}
		}
		for i := int64(0); i < blkSize; i++ {
			elem, err := %v(r)
			if err != nil {
				return nil, err
			}
			arr = append(arr, elem)
		}
	}
	return arr, nil
}
`

type arrayField struct {
	name     string
	itemType field
}

func (s *arrayField) Name() string {
	return toPublicName(s.name)
}

func (s *arrayField) FieldType() string {
	return "Array" + s.itemType.FieldType()
}

func (s *arrayField) GoType() string {
	return fmt.Sprintf("[]%v", s.itemType.GoType())
}

func (s *arrayField) SerializerMethod() string {
	return fmt.Sprintf("write%v", s.FieldType())
}

func (s *arrayField) DeserializerMethod() string {
	return fmt.Sprintf("read%v", s.FieldType())
}

func (s *arrayField) AddStruct(p *Package) {
	s.itemType.AddStruct(p)
}

func (s *arrayField) AddSerializer(p *Package) {
	itemMethodName := s.itemType.SerializerMethod()
	methodName := s.SerializerMethod()
	arraySerializer := fmt.Sprintf(arraySerializerTemplate, s.SerializerMethod(), s.GoType(), itemMethodName)
	s.itemType.AddSerializer(p)
	p.addFunction(UTIL_FILE, "", methodName, arraySerializer)
	p.addFunction(UTIL_FILE, "", "writeLong", writeLongMethod)
	p.addFunction(UTIL_FILE, "", "encodeInt", encodeIntMethod)
	p.addStruct(UTIL_FILE, "ByteWriter", byteWriterInterface)
	p.addImport(UTIL_FILE, "io")
}

func (s *arrayField) AddDeserializer(p *Package) {
	itemMethodName := s.itemType.DeserializerMethod()
	methodName := s.DeserializerMethod()
	arrayDeserializer := fmt.Sprintf(arrayDeserializerTemplate, methodName, s.GoType(), s.GoType(), itemMethodName)
	s.itemType.AddDeserializer(p)
	p.addFunction(UTIL_FILE, "", methodName, arrayDeserializer)
	p.addFunction(UTIL_FILE, "", "readLong", readLongMethod)
	p.addImport(UTIL_FILE, "io")
}
<|endoftext|>


<commit_before>package rx

import (
	"math"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

//jig:template MakeObserverObservable
//jig:needs Observer, Observable

const OutOfSubscriptions = RxError("out of subscriptions")

// MakeObserverObservable actually does make an observer observable. It
// creates a buffering multicaster and returns both the Observer and the
// Observable side of it. These are then used as the core of any Subject
// implementation. The Observer side is used to pass items into the buffering
// multicaster. This then multicasts the items to every Observer that
// subscribes to the returned Observable.
//
//	age     age below which items are kept to replay to a new subscriber.
//	length  length of the item buffer, number of items kept to replay to a new subscriber.
//	[cap]   Capacity of the item buffer, number of items that can be observed before blocking.
//	[scap]  Capacity of the subscription list, max number of simultaneous subscribers.
func MakeObserverObservable(age time.Duration, length int, capacity ...int) (Observer, Observable) {
	const (
		ms = time.Millisecond
		us = time.Microsecond
	)

	// Access to subscriptions
	const (
		idle uint32 = iota
		busy
	)

	// State of subscription and buffer
	const (
		active uint64 = iota
		canceled
		closing
		closed
	)

	const (
		// Cursor is parked so it does not influence advancing the commit index.
		parked uint64 = math.MaxUint64
	)

	type subscription struct {
		Cursor     uint64
		State      uint64    // active, canceled, closed
		LastActive time.Time // track activity to deterime backoff
	}

	type subscriptions struct {
		sync.Mutex
		*sync.Cond
		entries []subscription
		access  uint32 // idle, busy
	}

	type item struct {
		Value interface{}
		At    time.Time
	}

	type buffer struct {
		age time.Duration
		len uint64
		cap uint64

		mod    uint64
		items  []item
		begin  uint64
		end    uint64
		commit uint64
		state  uint64 // active, closed

		subscriptions subscriptions

		err error
	}

	make := func(age time.Duration, length int, capacity ...int) *buffer {
		cap, scap := uint64(length), uint64(32)
		switch {
		case len(capacity) >= 2:
			cap, scap = uint64(capacity[0]), uint64(capacity[1])
		case len(capacity) == 1:
			cap = uint64(capacity[0])
		}
		len := uint64(length)
		if cap < len {
			cap = len
		}
		cap = uint64(1) << uint(math.Ceil(math.Log2(float64(cap))))
		buf := &buffer{
			len:   len,
			cap:   cap,
			age:   age,
			items: make([]item, cap),
			mod:   cap - 1,
			end:   cap,
			subscriptions: subscriptions{
				entries: make([]subscription, 0, scap),
			},
		}
		buf.subscriptions.Cond = sync.NewCond(&buf.subscriptions.Mutex)
		return buf
	}
	buf := make(age, length, capacity...)

	accessSubscriptions := func(access func([]subscription)) bool {
		spun := false
		for !atomic.CompareAndSwapUint32(&buf.subscriptions.access, idle, busy) {
			runtime.Gosched()
			spun = true
		}
		access(buf.subscriptions.entries)
		atomic.StoreUint32(&buf.subscriptions.access, idle)
		return spun
	}

	send := func(value interface{}) {
		for buf.commit == buf.end {
			slowest := parked
			spun := accessSubscriptions(func(subscriptions []subscription) {
				for i := range subscriptions {
					cursor := atomic.LoadUint64(&subscriptions[i].Cursor)
					if cursor < slowest {
						slowest = cursor
					}
				}
			})
			if atomic.LoadUint64(&buf.begin) < slowest && slowest <= atomic.LoadUint64(&buf.end) {
				atomic.StoreUint64(&buf.begin, slowest)
				atomic.StoreUint64(&buf.end, slowest+buf.mod+1)
			} else {
				slowest = parked
			}
			if slowest == parked {
				// no subscriptions present...
				if !spun {
					// if we did not spin to access subscriptions, then spin now.
					runtime.Gosched()
				}
				if atomic.LoadUint64(&buf.state) != active {
					return // buffer no longer active
				}
			}
		}
		buf.items[buf.commit&buf.mod] = item{Value: value, At: time.Now()}
		atomic.AddUint64(&buf.commit, 1)
		buf.subscriptions.Broadcast()
	}

	close := func(err error) {
		if atomic.CompareAndSwapUint64(&buf.state, active, closing) {
			buf.err = err
			if atomic.CompareAndSwapUint64(&buf.state, closing, closed) {
				accessSubscriptions(func(subscriptions []subscription) {
					for i := range subscriptions {
						atomic.CompareAndSwapUint64(&subscriptions[i].State, active, closed)
					}
				})
			}
		}
		buf.subscriptions.Broadcast()
	}

	observer := func(next interface{}, err error, done bool) {
		if atomic.LoadUint64(&buf.state) == active {
			if !done {
				send(next)
			} else {
				close(err)
			}
		}
	}

	appendSubscription := func(cursor uint64) (sub *subscription, err error) {
		accessSubscriptions(func([]subscription) {
			s := &buf.subscriptions
			if len(s.entries) < cap(s.entries) {
				s.entries = append(s.entries, subscription{Cursor: cursor})
				sub = &s.entries[len(s.entries)-1]
				return
			}
			for i := range s.entries {
				sub = &s.entries[i]
				if atomic.CompareAndSwapUint64(&sub.Cursor, parked, cursor) {
					return
				}
			}
			err = ErrOutOfSubscriptions
			return
		})
		return
	}

	observable := func(observe Observer, subscribeOn Scheduler, subscriber Subscriber) {
		begin := atomic.LoadUint64(&buf.begin)
		sub, err := appendSubscription(begin)
		if err != nil {
			runner := subscribeOn.Schedule(func() {
				if subscriber.Subscribed() {
					observe(nil, err, true)
				}
			})
			subscriber.OnUnsubscribe(runner.Cancel)
			return
		}
		commit := atomic.LoadUint64(&buf.commit)
		if begin+buf.len < commit {
			atomic.StoreUint64(&sub.Cursor, commit-buf.len)
		}
		atomic.StoreUint64(&sub.State, atomic.LoadUint64(&buf.state))
		sub.LastActive = time.Now()

		receiver := subscribeOn.ScheduleFutureRecursive(0, func(self func(time.Duration)) {
			commit := atomic.LoadUint64(&buf.commit)

			// wait for commit to move away from cursor position, indicating fresh data
			if sub.Cursor == commit {
				if atomic.CompareAndSwapUint64(&sub.State, canceled, canceled) {
					// subscription canceled
					atomic.StoreUint64(&sub.Cursor, parked)
					return
				} else {
					// subscription still active (not canceled)
					now := time.Now()
					if now.Before(sub.LastActive.Add(1 * ms)) {
						// spinlock for 1ms (in increments of 50us) when no data from sender is arriving
						self(50 * us) // 20kHz
						return
					} else if now.Before(sub.LastActive.Add(250 * ms)) {
						if atomic.CompareAndSwapUint64(&sub.State, closed, closed) {
							// buffer has been closed
							observe(nil, buf.err, true)
							atomic.StoreUint64(&sub.Cursor, parked)
							return
						}
						// spinlock between 1ms and 250ms (in increments of 500us) of no data from sender
						self(500 * us) // 2kHz
						return
					} else {
						if subscribeOn.IsConcurrent() {
							// Block goroutine on condition until notified.
							buf.subscriptions.Lock()
							buf.subscriptions.Wait()
							buf.subscriptions.Unlock()
							sub.LastActive = time.Now()
							self(0)
							return
						} else {
							// Spinlock rest of the time (in increments of 5ms). This wakes-up
							// slower than the condition based solution above, but can be used with
							// a trampoline scheduler.
							self(5 * ms) // 200Hz
							return
						}
					}
				}
			}

			// emit data and advance cursor to catch up to commit
			if atomic.LoadUint64(&sub.State) == canceled {
				atomic.StoreUint64(&sub.Cursor, parked)
				return
			}
			for ; sub.Cursor != commit; atomic.AddUint64(&sub.Cursor, 1) {
				item := &buf.items[sub.Cursor&buf.mod]
				if buf.age == 0 || item.At.IsZero() || time.Since(item.At) < buf.age {
					observe(item.Value, nil, false)
				}
				if atomic.LoadUint64(&sub.State) == canceled {
					atomic.StoreUint64(&sub.Cursor, parked)
					return
				}
			}

			// all caught up; record time and loop back to wait for fresh data
			sub.LastActive = time.Now()
			self(0)
		})
		subscriber.OnUnsubscribe(receiver.Cancel)

		subscriber.OnUnsubscribe(func() {
			atomic.CompareAndSwapUint64(&sub.State, active, canceled)
			buf.subscriptions.Broadcast()
		})
	}
	return observer, observable
}

//jig:template Subject<Foo>
//jig:embeds <Foo>Observer, Observable<Foo>

// SubjectFoo is a combination of an FooObserver and ObservableFoo.
// Subjects are special because they are the only reactive constructs that
// support multicasting. The items sent to it through its observer side are
// multicasted to multiple clients subscribed to its observable side.
//
// The SubjectFoo exposes all methods from the embedded FooObserver and
// ObservableFoo. Use the FooObserver Next, Error and Complete methods to feed
// data to it. Use the ObservableFoo methods to subscribe to it.
//
// After a subject has been terminated by calling either Error or Complete,
// it goes into terminated state. All subsequent calls to its observer side
// will be silently ignored. All subsequent subscriptions to the observable
// side will be handled according to the specific behavior of the subject.
// There are different types of subjects, see the different NewXxxSubjectFoo
// functions for more info.
type SubjectFoo struct {
	FooObserver
	ObservableFoo
}

// Next is called by an ObservableFoo to emit the next foo value to the
// Observer.
func (o FooObserver) Next(next foo) {
	o(next, nil, false)
}

// Error is called by an ObservableFoo to report an error to the Observer.
func (o FooObserver) Error(err error) {
	var zero foo
	o(zero, err, true)
}

// Complete is called by an ObservableFoo to signal that no more data is
// forthcoming to the Observer.
func (o FooObserver) Complete() {
	var zero foo
	o(zero, nil, true)
}

//jig:template NewSubject<Foo>
//jig:needs MakeObserverObservable, Subject<Foo>, Observer As<Foo>Observer

// NewSubjectFoo creates a new Subject. After the subject is terminated, all
// subsequent subscriptions to the observable side will be terminated
// immediately with either an Error or Complete notification send to the
// subscribing client
//
// Note that this implementation is blocking. When there are subscribers, the
// observable goroutine is blocked until all subscribers have processed the
// next, error or complete notification.
func NewSubjectFoo() SubjectFoo {
	observer, observable := MakeObserverObservable(0, 0, 1, 16)
	return SubjectFoo{observer.AsFooObserver(), observable.AsObservableFoo()}
}

//jig:template DefaultReplayCapacity

// DefaultReplayCapacity is the default capacity of a replay buffer when
// a bufferCapacity of 0 is passed to the NewReplaySubject function.
const DefaultReplayCapacity = 16380

//jig:template NewReplaySubject<Foo>
//jig:needs MakeObserverObservable, Subject<Foo>, Observer As<Foo>Observer, DefaultReplayCapacity

// NewReplaySubjectFoo creates a new ReplaySubject. ReplaySubject ensures that
// all observers see the same sequence of emitted items, even if they
// subscribe after. When bufferCapacity argument is 0, then DefaultReplayCapacity is
// used (currently 16380). When windowDuration argument is 0, then entries added
// to the buffer will remain fresh forever.
func NewReplaySubjectFoo(bufferCapacity int, windowDuration time.Duration) SubjectFoo {
	if bufferCapacity == 0 {
		bufferCapacity = DefaultReplayCapacity
	}
	observer, observable := MakeObserverObservable(windowDuration, bufferCapacity)
	return SubjectFoo{observer.AsFooObserver(), observable.AsObservableFoo()}
}
<commit_msg>Refactor the subject internals<commit_after>package rx

import (
	"math"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

//jig:template MakeObserverObservable
//jig:needs Observer, Observable

const OutOfSubscriptions = RxError("out of subscriptions")

// MakeObserverObservable actually does make an observer observable. It
// creates a buffering multicaster and returns both the Observer and the
// Observable side of it. These are then used as the core of any Subject
// implementation. The Observer side is used to pass items into the buffering
// multicaster. This then multicasts the items to every Observer that
// subscribes to the returned Observable.
//
//	age     age below which items are kept to replay to a new subscriber.
//	length  length of the item buffer, number of items kept to replay to a new subscriber.
//	[cap]   Capacity of the item buffer, number of items that can be observed before blocking.
//	[scap]  Capacity of the subscription list, max number of simultaneous subscribers.
func MakeObserverObservable(age time.Duration, length int, capacity ...int) (Observer, Observable) {
	const (
		ms = time.Millisecond
		us = time.Microsecond
	)

	type subscription struct {
		cursor    uint64
		state     uint64    // active, canceled, closed
		activated time.Time // track activity to deterime backoff
	}

	// cursor
	const (
		maxuint64 uint64 = math.MaxUint64 // park unused cursor at maxuint64
	)

	// state
	const (
		active uint64 = iota
		canceled
		closing
		closed
	)

	type subscriptions struct {
		sync.Mutex
		*sync.Cond
		entries []subscription
		access  uint32 // unlocked, locked
	}

	// access
	const (
		unlocked uint32 = iota
		locked
	)

	type item struct {
		Value interface{}
		At    time.Time
	}

	type buffer struct {
		age  time.Duration
		keep uint64
		mod  uint64
		size uint64

		items  []item
		begin  uint64
		end    uint64
		commit uint64
		state  uint64 // active, closed

		subscriptions subscriptions

		err error
	}

	make := func(age time.Duration, length int, capacity ...int) *buffer {
		if length < 0 {
			length = 0
		}
		keep := uint64(length)

		cap, scap := length, 32
		switch {
		case len(capacity) >= 2:
			cap, scap = capacity[0], capacity[1]
		case len(capacity) == 1:
			cap = capacity[0]
		}
		if cap < length {
		 	cap = length
		}
		if cap == 0 {
			cap = 1
		}
		size := uint64(1) << uint(math.Ceil(math.Log2(float64(cap)))) // MUST(keep < size)

		if scap < 1 {
		 	scap = 1
		}
		buf := &buffer{
			age:  age,
			keep: keep,
			mod:  size - 1,
			size: size,

			items: make([]item, size),
			end:   size,
			subscriptions: subscriptions{
				entries: make([]subscription, 0, scap),
			},
		}
		buf.subscriptions.Cond = sync.NewCond(&buf.subscriptions.Mutex)
		return buf
	}
	buf := make(age, length, capacity...)

	accessSubscriptions := func(access func([]subscription)) bool {
		gosched := false
		for !atomic.CompareAndSwapUint32(&buf.subscriptions.access, unlocked, locked) {
			runtime.Gosched()
			gosched = true
		}
		access(buf.subscriptions.entries)
		atomic.StoreUint32(&buf.subscriptions.access, unlocked)
		return gosched
	}

	send := func(value interface{}) {
		for buf.commit == buf.end {
			full := false
			gosched := accessSubscriptions(func(subscriptions []subscription) {
				slowest := maxuint64
				for i := range subscriptions {
					current := atomic.LoadUint64(&subscriptions[i].cursor)
					if current < slowest {
						slowest = current
					}
				}
				end := atomic.LoadUint64(&buf.end)
				if atomic.LoadUint64(&buf.begin) < slowest && slowest <= end {
					if slowest+buf.keep > end {
						slowest = end - buf.keep + 1
					}
					atomic.StoreUint64(&buf.begin, slowest)
					atomic.StoreUint64(&buf.end, slowest+buf.size)
				} else {
					if slowest == maxuint64 { // no subscriptions
						atomic.AddUint64(&buf.begin, 1)
						atomic.AddUint64(&buf.end, 1)
					} else {
						full = true
					}
				}
			})
			if full {
				if !gosched {
					runtime.Gosched() // spinlock while full
				}
				if atomic.LoadUint64(&buf.state) != active {
					return // buffer no longer active
				}
			}
		}
		buf.items[buf.commit&buf.mod] = item{Value: value, At: time.Now()}
		atomic.AddUint64(&buf.commit, 1)
		buf.subscriptions.Broadcast()
	}

	close := func(err error) {
		if atomic.CompareAndSwapUint64(&buf.state, active, closing) {
			buf.err = err
			if atomic.CompareAndSwapUint64(&buf.state, closing, closed) {
				accessSubscriptions(func(subscriptions []subscription) {
					for i := range subscriptions {
						atomic.CompareAndSwapUint64(&subscriptions[i].state, active, closed)
					}
				})
			}
		}
		buf.subscriptions.Broadcast()
	}

	observer := func(next interface{}, err error, done bool) {
		if atomic.LoadUint64(&buf.state) == active {
			if !done {
				send(next)
			} else {
				close(err)
			}
		}
	}

	appendSubscription := func() (sub *subscription, err error) {
		accessSubscriptions(func([]subscription) {
			cursor := atomic.LoadUint64(&buf.begin)
			s := &buf.subscriptions
			if len(s.entries) < cap(s.entries) {
				s.entries = append(s.entries, subscription{cursor: cursor})
				sub = &s.entries[len(s.entries)-1]
				return
			}
			for i := range s.entries {
				sub = &s.entries[i]
				if atomic.CompareAndSwapUint64(&sub.cursor, maxuint64, cursor) {
					return
				}
			}
			sub = nil
			err = OutOfSubscriptions
			return
		})
		return
	}

	observable := func(observe Observer, subscribeOn Scheduler, subscriber Subscriber) {
		sub, err := appendSubscription()
		if err != nil {
			runner := subscribeOn.Schedule(func() {
				if subscriber.Subscribed() {
					observe(nil, err, true)
				}
			})
			subscriber.OnUnsubscribe(runner.Cancel)
			return
		}
		commit := atomic.LoadUint64(&buf.commit)
		if atomic.LoadUint64(&buf.begin)+buf.keep < commit {
			atomic.StoreUint64(&sub.cursor, commit-buf.keep)
		}
		atomic.StoreUint64(&sub.state, atomic.LoadUint64(&buf.state))
		sub.activated = time.Now()

		receiver := subscribeOn.ScheduleFutureRecursive(0, func(self func(time.Duration)) {
			commit := atomic.LoadUint64(&buf.commit)

			// wait for commit to move away from cursor position, indicating fresh data
			if sub.cursor == commit {
				if atomic.CompareAndSwapUint64(&sub.state, canceled, canceled) {
					// subscription canceled
					atomic.StoreUint64(&sub.cursor, maxuint64)
					return
				} else {
					// subscription still active (not canceled)
					now := time.Now()
					if now.Before(sub.activated.Add(1 * ms)) {
						// spinlock for 1ms (in increments of 50us) when no data from sender is arriving
						self(50 * us) // 20kHz
						return
					} else if now.Before(sub.activated.Add(250 * ms)) {
						if atomic.CompareAndSwapUint64(&sub.state, closed, closed) {
							// buffer has been closed
							observe(nil, buf.err, true)
							atomic.StoreUint64(&sub.cursor, maxuint64)
							return
						}
						// spinlock between 1ms and 250ms (in increments of 500us) of no data from sender
						self(500 * us) // 2kHz
						return
					} else {
						if subscribeOn.IsConcurrent() {
							// Block goroutine on condition until notified.
							buf.subscriptions.Lock()
							buf.subscriptions.Wait()
							buf.subscriptions.Unlock()
							sub.activated = time.Now()
							self(0)
							return
						} else {
							// Spinlock rest of the time (in increments of 5ms). This wakes-up
							// slower than the condition based solution above, but can be used with
							// a trampoline scheduler.
							self(5 * ms) // 200Hz
							return
						}
					}
				}
			}

			// emit data and advance cursor to catch up to commit
			if atomic.LoadUint64(&sub.state) == canceled {
				atomic.StoreUint64(&sub.cursor, maxuint64)
				return
			}
			for ; sub.cursor != commit; atomic.AddUint64(&sub.cursor, 1) {
				item := &buf.items[sub.cursor&buf.mod]
				if buf.age == 0 || item.At.IsZero() || time.Since(item.At) < buf.age {
					observe(item.Value, nil, false)
				}
				if atomic.LoadUint64(&sub.state) == canceled {
					atomic.StoreUint64(&sub.cursor, maxuint64)
					return
				}
			}

			// all caught up; record time and loop back to wait for fresh data
			sub.activated = time.Now()
			self(0)
		})
		subscriber.OnUnsubscribe(receiver.Cancel)

		subscriber.OnUnsubscribe(func() {
			atomic.CompareAndSwapUint64(&sub.state, active, canceled)
			buf.subscriptions.Broadcast()
		})
	}
	return observer, observable
}

//jig:template Subject<Foo>
//jig:embeds <Foo>Observer, Observable<Foo>

// SubjectFoo is a combination of an FooObserver and ObservableFoo.
// Subjects are special because they are the only reactive constructs that
// support multicasting. The items sent to it through its observer side are
// multicasted to multiple clients subscribed to its observable side.
//
// The SubjectFoo exposes all methods from the embedded FooObserver and
// ObservableFoo. Use the FooObserver Next, Error and Complete methods to feed
// data to it. Use the ObservableFoo methods to subscribe to it.
//
// After a subject has been terminated by calling either Error or Complete,
// it goes into terminated state. All subsequent calls to its observer side
// will be silently ignored. All subsequent subscriptions to the observable
// side will be handled according to the specific behavior of the subject.
// There are different types of subjects, see the different NewXxxSubjectFoo
// functions for more info.
type SubjectFoo struct {
	FooObserver
	ObservableFoo
}

// Next is called by an ObservableFoo to emit the next foo value to the
// Observer.
func (o FooObserver) Next(next foo) {
	o(next, nil, false)
}

// Error is called by an ObservableFoo to report an error to the Observer.
func (o FooObserver) Error(err error) {
	var zero foo
	o(zero, err, true)
}

// Complete is called by an ObservableFoo to signal that no more data is
// forthcoming to the Observer.
func (o FooObserver) Complete() {
	var zero foo
	o(zero, nil, true)
}

//jig:template NewSubject<Foo>
//jig:needs MakeObserverObservable, Subject<Foo>, Observer As<Foo>Observer, Observable AsObservable<Foo>

// NewSubjectFoo creates a new Subject. After the subject is terminated, all
// subsequent subscriptions to the observable side will be terminated
// immediately with either an Error or Complete notification send to the
// subscribing client
//
// Note that this implementation is blocking. When there are subscribers, the
// observable goroutine is blocked until all subscribers have processed the
// next, error or complete notification.
func NewSubjectFoo() SubjectFoo {
	observer, observable := MakeObserverObservable(0, 0)
	return SubjectFoo{observer.AsFooObserver(), observable.AsObservableFoo()}
}

//jig:template DefaultReplayCapacity

// DefaultReplayCapacity is the default capacity of a replay buffer when
// a bufferCapacity of 0 is passed to the NewReplaySubject function.
const DefaultReplayCapacity = 16380

//jig:template NewReplaySubject<Foo>
//jig:needs MakeObserverObservable, Subject<Foo>, Observer As<Foo>Observer, DefaultReplayCapacity

// NewReplaySubjectFoo creates a new ReplaySubject. ReplaySubject ensures that
// all observers see the same sequence of emitted items, even if they
// subscribe after. When bufferCapacity argument is 0, then DefaultReplayCapacity is
// used (currently 16380). When windowDuration argument is 0, then entries added
// to the buffer will remain fresh forever.
func NewReplaySubjectFoo(bufferCapacity int, windowDuration time.Duration) SubjectFoo {
	if bufferCapacity == 0 {
		bufferCapacity = DefaultReplayCapacity
	}
	observer, observable := MakeObserverObservable(windowDuration, bufferCapacity)
	return SubjectFoo{observer.AsFooObserver(), observable.AsObservableFoo()}
}
<|endoftext|>