<commit_before>package net

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"net"
	"strconv"

	"github.com/hashicorp/packer/common/filelock"
	"github.com/hashicorp/packer/packer"
)

var _ net.Listener = &Listener{}

// Listener wraps a net.Lister with some magic packer capabilies. For example
// until you call Listener.Close, any call to ListenRangeConfig.Listen cannot
// bind to Port. Packer tries tells moving parts which port they can use, but
// often the port has to be released before a 3rd party is started, like a VNC
// server.
type Listener struct {
	// Listener can be closed but Port will be file locked by packer until
	// Close is called.
	net.Listener
	Port    int
	Address string
	lock    *filelock.Flock
}

func (l *Listener) Close() error {
	err := l.lock.Unlock()
	if err != nil {
		log.Printf("cannot unlock lockfile %#v: %v", l, err)
	}
	return l.Listener.Close()
}

// ListenRangeConfig contains options for listening to a free address [Min,Max)
// range. ListenRangeConfig wraps a net.ListenConfig.
type ListenRangeConfig struct {
	// like "tcp" or "udp". defaults to "tcp".
	Network  string
	Addr     string
	Min, Max int
	net.ListenConfig
}

// Listen tries to Listen to a random open TCP port in the [min, max) range
// until ctx is cancelled.
// Listen uses net.ListenConfig.Listen internally.
func (lc ListenRangeConfig) Listen(ctx context.Context) (*Listener, error) {
	if lc.Network == "" {
		lc.Network = "tcp"
	}
	portRange := lc.Max - lc.Min
	for {
		if err := ctx.Err(); err != nil {
			return nil, err
		}

		port := lc.Min
		if portRange > 0 {
			port += rand.Intn(portRange)
		}

		lockFilePath, err := packer.CachePath("port", strconv.Itoa(port))
		if err != nil {
			return nil, err
		}

		lock := filelock.New(lockFilePath)
		locked, err := lock.TryLock()
		if err != nil {
			return nil, err
		}
		if !locked {
			continue // this port seems to be locked by another packer goroutine
		}

		log.Printf("Trying port: %d", port)

		l, err := lc.ListenConfig.Listen(ctx, lc.Network, fmt.Sprintf("%s:%d", lc.Addr, port))
		if err != nil {
			if err := lock.Unlock(); err != nil {
				log.Printf("Could not unlock file lock for port %d: %v", port, err)
			}

			continue // this port is most likely already open
		}

		log.Printf("Found available port: %d on IP: %s", port, lc.Addr)
		return &Listener{
			Address:  lc.Addr,
			Port:     port,
			Listener: l,
			lock:     lock,
		}, err

	}
}
<commit_msg>add a timeout trying to open a random port<commit_after>package net

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"net"
	"strconv"
	"time"

	"github.com/hashicorp/packer/common/filelock"
	"github.com/hashicorp/packer/packer"
)

var _ net.Listener = &Listener{}

// Listener wraps a net.Lister with some magic packer capabilies. For example
// until you call Listener.Close, any call to ListenRangeConfig.Listen cannot
// bind to Port. Packer tries tells moving parts which port they can use, but
// often the port has to be released before a 3rd party is started, like a VNC
// server.
type Listener struct {
	// Listener can be closed but Port will be file locked by packer until
	// Close is called.
	net.Listener
	Port    int
	Address string
	lock    *filelock.Flock
}

func (l *Listener) Close() error {
	err := l.lock.Unlock()
	if err != nil {
		log.Printf("cannot unlock lockfile %#v: %v", l, err)
	}
	return l.Listener.Close()
}

// ListenRangeConfig contains options for listening to a free address [Min,Max)
// range. ListenRangeConfig wraps a net.ListenConfig.
type ListenRangeConfig struct {
	// like "tcp" or "udp". defaults to "tcp".
	Network  string
	Addr     string
	Min, Max int
	net.ListenConfig
}

// Listen tries to Listen to a random open TCP port in the [min, max) range
// until ctx is cancelled.
// Listen uses net.ListenConfig.Listen internally.
func (lc ListenRangeConfig) Listen(ctx context.Context) (*Listener, error) {
	if lc.Network == "" {
		lc.Network = "tcp"
	}
	portRange := lc.Max - lc.Min
	for {
		if err := ctx.Err(); err != nil {
			return nil, err
		}

		port := lc.Min
		if portRange > 0 {
			port += rand.Intn(portRange)
		}

		lockFilePath, err := packer.CachePath("port", strconv.Itoa(port))
		if err != nil {
			return nil, err
		}

		lock := filelock.New(lockFilePath)
		locked, err := lock.TryLock()
		if err != nil {
			return nil, err
		}
		if !locked {
			continue // this port seems to be locked by another packer goroutine
		}

		log.Printf("Trying port: %d", port)

		l, err := lc.ListenConfig.Listen(ctx, lc.Network, fmt.Sprintf("%s:%d", lc.Addr, port))
		if err != nil {
			if err := lock.Unlock(); err != nil {
				log.Printf("Could not unlock file lock for port %d: %v", port, err)
			}

			continue // this port is most likely already open
		}

		log.Printf("Found available port: %d on IP: %s", port, lc.Addr)
		return &Listener{
			Address:  lc.Addr,
			Port:     port,
			Listener: l,
			lock:     lock,
		}, err

		time.Sleep(20 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>package cpu

import (
	"github.com/valerio/go-jeebie/jeebie/addr"
	"github.com/valerio/go-jeebie/jeebie/bit"
	"github.com/valerio/go-jeebie/jeebie/memory"
)

// Flag is one of the 4 possible flags used in the flag register (high part of AF)
type Flag uint8

const (
	zeroFlag      Flag = 0x80
	subFlag            = 0x40
	halfCarryFlag      = 0x20
	carryFlag          = 0x10
)

const (
	baseInterruptAddress uint16 = 0x40
)

// CPU is the main struct holding Z80 state
type CPU struct {
	// registers
	a  uint8
	f  uint8
	b  uint8
	c  uint8
	d  uint8
	e  uint8
	h  uint8
	l  uint8
	sp uint16
	pc uint16

	// metadata
	interruptsEnabled bool
	currentOpcode     uint16
	stopped           bool
	cycles            uint64

	memory *memory.MMU
}

func initializeMemory(mmu *memory.MMU) {
	mmu.Write(addr.TIMA, 0x00)
	mmu.Write(addr.TMA, 0x00)
	mmu.Write(addr.TAC, 0x00)
	mmu.Write(addr.LCDC, 0x91)
	mmu.Write(addr.SCY, 0x00)
	mmu.Write(addr.SCX, 0x00)
	mmu.Write(addr.LYC, 0x00)
	mmu.Write(addr.BGP, 0xFC)
	mmu.Write(addr.OBP0, 0xFF)
	mmu.Write(addr.OBP1, 0xFF)
	mmu.Write(addr.WY, 0x00)
	mmu.Write(addr.WX, 0x00)
	mmu.Write(addr.IE, 0x00)

	// TODO: make the audio registers constant
	mmu.Write(0xFF10, 0x80) //    ; NR10
	mmu.Write(0xFF11, 0xBF) //    ; NR11
	mmu.Write(0xFF12, 0xF3) //    ; NR12
	mmu.Write(0xFF14, 0xBF) //    ; NR14
	mmu.Write(0xFF16, 0x3F) //    ; NR21
	mmu.Write(0xFF17, 0x00) //    ; NR22
	mmu.Write(0xFF19, 0xBF) //    ; NR24
	mmu.Write(0xFF1A, 0x7F) //    ; NR30
	mmu.Write(0xFF1B, 0xFF) //    ; NR31
	mmu.Write(0xFF1C, 0x9F) //    ; NR32
	mmu.Write(0xFF1E, 0xBF) //    ; NR33
	mmu.Write(0xFF20, 0xFF) //    ; NR41
	mmu.Write(0xFF21, 0x00) //    ; NR42
	mmu.Write(0xFF22, 0x00) //    ; NR43
	mmu.Write(0xFF23, 0xBF) //    ; NR30
	mmu.Write(0xFF24, 0x77) //    ; NR50
	mmu.Write(0xFF25, 0xF3) //    ; NR51
	mmu.Write(0xFF26, 0xF1) //    ; NR52  -- should be 0xF0 on SGB
}

// New returns an initialized CPU instance
func New(memory *memory.MMU) *CPU {
	initializeMemory(memory)

	cpu := &CPU{
		memory: memory,
	}

	cpu.setAF(0x01B0)
	cpu.setBC(0x0013)
	cpu.setDE(0x00D8)
	cpu.setHL(0x014D)
	cpu.sp = 0xFFFE
	cpu.pc = 0x0100

	return cpu
}

// Tick emulates a single step during the main loop for the cpu.
// Returns the amount of cycles that execution has taken.
func (c *CPU) Tick() int {
	c.handleInterrupts()

	instruction := Decode(c)
	cycles := instruction(c)
	c.cycles += uint64(cycles)

	return cycles
}

// handleInterrupts checks for an interrupt and handles it if necessary.
func (c *CPU) handleInterrupts() {
	if c.interruptsEnabled == false {
		return
	}

	// retrieve the two masks
	enabledInterruptsMask := c.memory.Read(addr.IE)
	firedInterrupts := c.memory.Read(addr.IF)

	// if zero, no interrupts that are enabled were fired
	if (enabledInterruptsMask & firedInterrupts) == 0 {
		return
	}

	for i := uint8(0); i < 5; i++ {
		if bit.IsSet(i, firedInterrupts) {
			// interrupt handlers are offset by 8
			// 0x40 - 0x48 - 0x50 - 0x58 - 0x60
			address := uint16(i)*8 + baseInterruptAddress

			// mark as handled by clearing the bit at i
			c.memory.Write(addr.IF, bit.Clear(i, firedInterrupts))

			// move PC to interrupt handler address
			c.pushStack(c.pc)
			c.pc = address

			// add cycles equivalent to a JMP.
			c.cycles += 20

			// disable interrupts
			c.interruptsEnabled = false

			// return on the first served interrupt.
			// will serve the next when interrupts are enabled again.
			return
		}
	}
}

// peekImmediate returns the byte at the memory address pointed by the PC
// this value is known as immediate ('n' in mnemonics), some opcodes use it as a parameter
func (c CPU) peekImmediate() uint8 {
	n := c.memory.Read(c.pc)
	return n
}

// peekImmediateWord returns the two bytes at the memory address pointed by PC and PC+1
// this value is known as immediate ('nn' in mnemonics), some opcodes use it as a parameter
func (c CPU) peekImmediateWord() uint16 {
	low := c.memory.Read(c.pc + 1)
	high := c.memory.Read(c.pc)

	return bit.Combine(low, high)
}

// peekSignedImmediate returns signed byte value at the memory address pointed by PC
// this value is known as immediate ('*' in mnemonics), some opcodes use it as a parameter
func (c CPU) peekSignedImmediate() int8 {
	return int8(c.peekImmediate())
}

// readImmediate acts similarly as its peek counterpart, but increments the PC once after reading
func (c *CPU) readImmediate() uint8 {
	n := c.peekImmediate()
	c.pc++
	return n
}

// readImmediateWord acts similarly as its peek counterpart, but increments the PC twice after reading
func (c *CPU) readImmediateWord() uint16 {
	nn := c.peekImmediateWord()
	c.pc += 2
	return nn
}

// readSignedImmediate acts similarly as its peek counterpart, but increments the PC once after reading
func (c *CPU) readSignedImmediate() int8 {
	n := c.peekSignedImmediate()
	c.pc++
	return n
}

func (c *CPU) setFlag(flag Flag) {
	c.f |= uint8(flag)
}

func (c *CPU) resetFlag(flag Flag) {
	c.f &= uint8(flag ^ 0xFF)
}

func (c CPU) isSetFlag(flag Flag) bool {
	return c.f&uint8(flag) != 0
}

// flagToBit will return 1 if the passed flag is set, 0 otherwise
func (c CPU) flagToBit(flag Flag) uint8 {
	if c.isSetFlag(flag) {
		return 1
	}

	return 0
}

func (c *CPU) setFlagToCondition(flag Flag, condition bool) {
	if condition {
		c.setFlag(flag)
	} else {
		c.resetFlag(flag)
	}
}

func (c *CPU) setBC(value uint16) {
	c.b = bit.High(value)
	c.c = bit.Low(value)
}

func (c CPU) getBC() uint16 {
	return bit.Combine(c.b, c.c)
}

func (c *CPU) setDE(value uint16) {
	c.d = bit.High(value)
	c.e = bit.Low(value)
}

func (c CPU) getDE() uint16 {
	return bit.Combine(c.d, c.d)
}

func (c *CPU) setHL(value uint16) {
	c.h = bit.High(value)
	c.l = bit.Low(value)
}

func (c CPU) getHL() uint16 {
	return bit.Combine(c.h, c.l)
}

func (c *CPU) setAF(value uint16) {
	c.a = bit.High(value)
	c.f = bit.Low(value)
}

func (c CPU) getAF() uint16 {
	return bit.Combine(c.a, c.f)
}
<commit_msg>simplify setFlagToCondition control flow<commit_after>package cpu

import (
	"github.com/valerio/go-jeebie/jeebie/addr"
	"github.com/valerio/go-jeebie/jeebie/bit"
	"github.com/valerio/go-jeebie/jeebie/memory"
)

// Flag is one of the 4 possible flags used in the flag register (high part of AF)
type Flag uint8

const (
	zeroFlag      Flag = 0x80
	subFlag            = 0x40
	halfCarryFlag      = 0x20
	carryFlag          = 0x10
)

const (
	baseInterruptAddress uint16 = 0x40
)

// CPU is the main struct holding Z80 state
type CPU struct {
	// registers
	a  uint8
	f  uint8
	b  uint8
	c  uint8
	d  uint8
	e  uint8
	h  uint8
	l  uint8
	sp uint16
	pc uint16

	// metadata
	interruptsEnabled bool
	currentOpcode     uint16
	stopped           bool
	cycles            uint64

	memory *memory.MMU
}

func initializeMemory(mmu *memory.MMU) {
	mmu.Write(addr.TIMA, 0x00)
	mmu.Write(addr.TMA, 0x00)
	mmu.Write(addr.TAC, 0x00)
	mmu.Write(addr.LCDC, 0x91)
	mmu.Write(addr.SCY, 0x00)
	mmu.Write(addr.SCX, 0x00)
	mmu.Write(addr.LYC, 0x00)
	mmu.Write(addr.BGP, 0xFC)
	mmu.Write(addr.OBP0, 0xFF)
	mmu.Write(addr.OBP1, 0xFF)
	mmu.Write(addr.WY, 0x00)
	mmu.Write(addr.WX, 0x00)
	mmu.Write(addr.IE, 0x00)

	// TODO: make the audio registers constant
	mmu.Write(0xFF10, 0x80) //    ; NR10
	mmu.Write(0xFF11, 0xBF) //    ; NR11
	mmu.Write(0xFF12, 0xF3) //    ; NR12
	mmu.Write(0xFF14, 0xBF) //    ; NR14
	mmu.Write(0xFF16, 0x3F) //    ; NR21
	mmu.Write(0xFF17, 0x00) //    ; NR22
	mmu.Write(0xFF19, 0xBF) //    ; NR24
	mmu.Write(0xFF1A, 0x7F) //    ; NR30
	mmu.Write(0xFF1B, 0xFF) //    ; NR31
	mmu.Write(0xFF1C, 0x9F) //    ; NR32
	mmu.Write(0xFF1E, 0xBF) //    ; NR33
	mmu.Write(0xFF20, 0xFF) //    ; NR41
	mmu.Write(0xFF21, 0x00) //    ; NR42
	mmu.Write(0xFF22, 0x00) //    ; NR43
	mmu.Write(0xFF23, 0xBF) //    ; NR30
	mmu.Write(0xFF24, 0x77) //    ; NR50
	mmu.Write(0xFF25, 0xF3) //    ; NR51
	mmu.Write(0xFF26, 0xF1) //    ; NR52  -- should be 0xF0 on SGB
}

// New returns an initialized CPU instance
func New(memory *memory.MMU) *CPU {
	initializeMemory(memory)

	cpu := &CPU{
		memory: memory,
	}

	cpu.setAF(0x01B0)
	cpu.setBC(0x0013)
	cpu.setDE(0x00D8)
	cpu.setHL(0x014D)
	cpu.sp = 0xFFFE
	cpu.pc = 0x0100

	return cpu
}

// Tick emulates a single step during the main loop for the cpu.
// Returns the amount of cycles that execution has taken.
func (c *CPU) Tick() int {
	c.handleInterrupts()

	instruction := Decode(c)
	cycles := instruction(c)
	c.cycles += uint64(cycles)

	return cycles
}

// handleInterrupts checks for an interrupt and handles it if necessary.
func (c *CPU) handleInterrupts() {
	if c.interruptsEnabled == false {
		return
	}

	// retrieve the two masks
	enabledInterruptsMask := c.memory.Read(addr.IE)
	firedInterrupts := c.memory.Read(addr.IF)

	// if zero, no interrupts that are enabled were fired
	if (enabledInterruptsMask & firedInterrupts) == 0 {
		return
	}

	for i := uint8(0); i < 5; i++ {
		if bit.IsSet(i, firedInterrupts) {
			// interrupt handlers are offset by 8
			// 0x40 - 0x48 - 0x50 - 0x58 - 0x60
			address := uint16(i)*8 + baseInterruptAddress

			// mark as handled by clearing the bit at i
			c.memory.Write(addr.IF, bit.Clear(i, firedInterrupts))

			// move PC to interrupt handler address
			c.pushStack(c.pc)
			c.pc = address

			// add cycles equivalent to a JMP.
			c.cycles += 20

			// disable interrupts
			c.interruptsEnabled = false

			// return on the first served interrupt.
			// will serve the next when interrupts are enabled again.
			return
		}
	}
}

// peekImmediate returns the byte at the memory address pointed by the PC
// this value is known as immediate ('n' in mnemonics), some opcodes use it as a parameter
func (c CPU) peekImmediate() uint8 {
	n := c.memory.Read(c.pc)
	return n
}

// peekImmediateWord returns the two bytes at the memory address pointed by PC and PC+1
// this value is known as immediate ('nn' in mnemonics), some opcodes use it as a parameter
func (c CPU) peekImmediateWord() uint16 {
	low := c.memory.Read(c.pc + 1)
	high := c.memory.Read(c.pc)

	return bit.Combine(low, high)
}

// peekSignedImmediate returns signed byte value at the memory address pointed by PC
// this value is known as immediate ('*' in mnemonics), some opcodes use it as a parameter
func (c CPU) peekSignedImmediate() int8 {
	return int8(c.peekImmediate())
}

// readImmediate acts similarly as its peek counterpart, but increments the PC once after reading
func (c *CPU) readImmediate() uint8 {
	n := c.peekImmediate()
	c.pc++
	return n
}

// readImmediateWord acts similarly as its peek counterpart, but increments the PC twice after reading
func (c *CPU) readImmediateWord() uint16 {
	nn := c.peekImmediateWord()
	c.pc += 2
	return nn
}

// readSignedImmediate acts similarly as its peek counterpart, but increments the PC once after reading
func (c *CPU) readSignedImmediate() int8 {
	n := c.peekSignedImmediate()
	c.pc++
	return n
}

func (c *CPU) setFlag(flag Flag) {
	c.f |= uint8(flag)
}

func (c *CPU) resetFlag(flag Flag) {
	c.f &= uint8(flag ^ 0xFF)
}

func (c CPU) isSetFlag(flag Flag) bool {
	return c.f&uint8(flag) != 0
}

// flagToBit will return 1 if the passed flag is set, 0 otherwise
func (c CPU) flagToBit(flag Flag) uint8 {
	if c.isSetFlag(flag) {
		return 1
	}

	return 0
}

func (c *CPU) setFlagToCondition(flag Flag, condition bool) {
	if !condition {
		c.resetFlag(flag)
		return
	}

	c.setFlag(flag)
}

func (c *CPU) setBC(value uint16) {
	c.b = bit.High(value)
	c.c = bit.Low(value)
}

func (c CPU) getBC() uint16 {
	return bit.Combine(c.b, c.c)
}

func (c *CPU) setDE(value uint16) {
	c.d = bit.High(value)
	c.e = bit.Low(value)
}

func (c CPU) getDE() uint16 {
	return bit.Combine(c.d, c.d)
}

func (c *CPU) setHL(value uint16) {
	c.h = bit.High(value)
	c.l = bit.Low(value)
}

func (c CPU) getHL() uint16 {
	return bit.Combine(c.h, c.l)
}

func (c *CPU) setAF(value uint16) {
	c.a = bit.High(value)
	c.f = bit.Low(value)
}

func (c CPU) getAF() uint16 {
	return bit.Combine(c.a, c.f)
}
<|endoftext|>


<commit_before>package cpu

import "github.com/valep27/go-jeebie/jeebie/memory"

// Flag is one of the 4 possible flags used in the flag register (high part of AF)
type Flag uint8

const (
	zeroFlag      Flag = 0x80
	subFlag            = 0x40
	halfCarryFlag      = 0x20
	carryFlag          = 0x10
)

// CPU is the main struct holding Z80 state
type CPU struct {
	memory *memory.MMU
	af     Register16
	bc     Register16
	de     Register16
	hl     Register16
	sp     Register16
	pc     Register16
}

// New returns an uninitialized CPU instance
func New() CPU {
	return CPU{}
}

//Tick emulates a single step during the main loop for the cpu.
func (c *CPU) Tick() {

}

func (c *CPU) setFlag(flag Flag) {
	c.af.setLow(uint8(flag))
}

func (c *CPU) resetFlag(flag Flag) {
	c.af.setLow(uint8(flag) ^ 0xFF)
}

func (c CPU) isSetFlag(flag Flag) bool {
	return c.af.getHigh() & uint8(flag) != 0
}
<commit_msg>Add funcs for getting immediate values<commit_after>package cpu

import "github.com/valep27/go-jeebie/jeebie/memory"

// Flag is one of the 4 possible flags used in the flag register (high part of AF)
type Flag uint8

const (
	zeroFlag      Flag = 0x80
	subFlag            = 0x40
	halfCarryFlag      = 0x20
	carryFlag          = 0x10
)

// CPU is the main struct holding Z80 state
type CPU struct {
	memory *memory.MMU
	af     Register16
	bc     Register16
	de     Register16
	hl     Register16
	sp     Register16
	pc     Register16
}

// New returns an uninitialized CPU instance
func New() CPU {
	return CPU{}
}

//Tick emulates a single step during the main loop for the cpu.
func (c *CPU) Tick() {

}

func (c *CPU) getImmediate() uint8 {
	n := c.memory.ReadByte(c.pc.get())
	c.pc.incr()
	return n
}

func (c *CPU) getImmediateWord() uint16 {
	low := uint16(c.memory.ReadByte(c.pc.get()))
	c.pc.incr()

	high := uint16(c.memory.ReadByte(c.pc.get()))
	c.pc.incr()

	return high << 8 | low
}

func (c *CPU) getImmediateSigned() int8 {
	return int8(c.getImmediate())
}

func (c *CPU) setFlag(flag Flag) {
	c.af.setLow(uint8(flag))
}

func (c *CPU) resetFlag(flag Flag) {
	c.af.setLow(uint8(flag) ^ 0xFF)
}

func (c CPU) isSetFlag(flag Flag) bool {
	return c.af.getHigh() & uint8(flag) != 0
}
<|endoftext|>


<commit_before>package cpu

import "github.com/valep27/go-jeebie/jeebie/memory"
import "github.com/valep27/go-jeebie/jeebie/bit"

// Flag is one of the 4 possible flags used in the flag register (high part of AF)
type Flag uint8

const (
	zeroFlag      Flag = 0x80
	subFlag            = 0x40
	halfCarryFlag      = 0x20
	carryFlag          = 0x10
)

// CPU is the main struct holding Z80 state
type CPU struct {
	memory *memory.MMU
	af     Register16
	bc     Register16
	de     Register16
	hl     Register16
	sp     Register16
	pc     Register16
}

// New returns an uninitialized CPU instance
func New() CPU {
	return CPU{}
}

//Tick emulates a single step during the main loop for the cpu.
func (c *CPU) Tick() {

}

func (c *CPU) getImmediate() uint8 {
	n := c.memory.ReadByte(c.pc.get())
	c.pc.incr()
	return n
}

func (c *CPU) getImmediateWord() uint16 {
	low := c.getImmediate()
	high := c.getImmediate()

	return bit.CombineBytes(low, high)
}

func (c *CPU) getImmediateSigned() int8 {
	return int8(c.getImmediate())
}

func (c *CPU) setFlag(flag Flag) {
	c.af.setLow(uint8(flag))
}

func (c *CPU) resetFlag(flag Flag) {
	c.af.setLow(uint8(flag) ^ 0xFF)
}

func (c CPU) isSetFlag(flag Flag) bool {
	return c.af.getHigh() & uint8(flag) != 0
}
<commit_msg>add inc and dec instructions<commit_after>package cpu

import "github.com/valep27/go-jeebie/jeebie/memory"
import "github.com/valep27/go-jeebie/jeebie/bit"

// Flag is one of the 4 possible flags used in the flag register (high part of AF)
type Flag uint8

const (
	zeroFlag      Flag = 0x80
	subFlag            = 0x40
	halfCarryFlag      = 0x20
	carryFlag          = 0x10
)

// CPU is the main struct holding Z80 state
type CPU struct {
	memory *memory.MMU
	af     Register16
	bc     Register16
	de     Register16
	hl     Register16
	sp     Register16
	pc     Register16
}

// New returns an uninitialized CPU instance
func New() CPU {
	return CPU{}
}

//Tick emulates a single step during the main loop for the cpu.
func (c *CPU) Tick() {

}

func (c *CPU) getImmediate() uint8 {
	n := c.memory.ReadByte(c.pc.get())
	c.pc.incr()
	return n
}

func (c *CPU) getImmediateWord() uint16 {
	low := c.getImmediate()
	high := c.getImmediate()

	return bit.CombineBytes(low, high)
}

func (c *CPU) getImmediateSigned() int8 {
	return int8(c.getImmediate())
}

func (c *CPU) setFlag(flag Flag) {
	c.af.setLow(uint8(flag))
}

func (c *CPU) resetFlag(flag Flag) {
	c.af.setLow(uint8(flag) ^ 0xFF)
}

func (c CPU) isSetFlag(flag Flag) bool {
	return c.af.getHigh()&uint8(flag) != 0
}

func (c *CPU) setFlagToCondition(flag Flag, condition bool) {
	if condition {
		c.setFlag(flag)
	} else {
		c.resetFlag(flag)
	}
}

func (c *CPU) inc(r *Register8) {
	r.incr()
	value := r.get()

	c.setFlagToCondition(zeroFlag, value == 0)
	c.setFlagToCondition(halfCarryFlag, (value&0xF) == 0xF)
	c.resetFlag(subFlag)
}

func (c *CPU) dec(r *Register8) {
	r.decr()
	value := r.get()

	c.setFlagToCondition(zeroFlag, value == 0)
	c.setFlagToCondition(halfCarryFlag, (value&0xF) == 0xF)
	c.setFlag(subFlag)
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	sum := 1
	for sum < 1000 {
		sum += sum
	}
	fmt.Println(sum)
}
<commit_msg>go-tour: Adding semicolons that were dropped by gofmt<commit_after>package main

import "fmt"

func main() {
	sum := 1
	for ; sum < 1000; {
		sum += sum
	}
	fmt.Println(sum)
}
<|endoftext|>


<commit_before>// Copyright 2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package gce provides wrappers around Google Compute Engine (GCE) APIs.
// It is assumed that the program itself also runs on GCE as APIs operate on the current project/zone.
//
// See https://cloud.google.com/compute/docs for details.
// In particular, API reference:
// https://cloud.google.com/compute/docs/reference/latest
// and Go API wrappers:
// https://godoc.org/google.golang.org/api/compute/v0.beta
package gce

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/compute/v0.beta"
	"google.golang.org/api/googleapi"
)

type Context struct {
	ProjectID  string
	ZoneID     string
	Instance   string
	InternalIP string

	computeService *compute.Service

	// apiCallTicker ticks regularly, preventing us from accidentally making
	// GCE API calls too quickly. Our quota is 20 QPS, but we temporarily
	// limit ourselves to less than that.
	apiRateGate <-chan time.Time
}

func NewContext() (*Context, error) {
	ctx := &Context{
		apiRateGate: time.NewTicker(time.Second / 10).C,
	}
	background := context.Background()
	tokenSource, err := google.DefaultTokenSource(background, compute.CloudPlatformScope)
	if err != nil {
		return nil, fmt.Errorf("failed to get a token source: %v", err)
	}
	httpClient := oauth2.NewClient(background, tokenSource)
	ctx.computeService, _ = compute.New(httpClient)
	// Obtain project name, zone and current instance IP address.
	ctx.ProjectID, err = ctx.getMeta("project/project-id")
	if err != nil {
		return nil, fmt.Errorf("failed to query gce project-id: %v", err)
	}
	ctx.ZoneID, err = ctx.getMeta("instance/zone")
	if err != nil {
		return nil, fmt.Errorf("failed to query gce zone: %v", err)
	}
	if i := strings.LastIndexByte(ctx.ZoneID, '/'); i != -1 {
		ctx.ZoneID = ctx.ZoneID[i+1:] // the query returns some nonsense prefix
	}
	instID, err := ctx.getMeta("instance/id")
	if err != nil {
		return nil, fmt.Errorf("failed to query gce instance id: %v", err)
	}
	instances, err := ctx.computeService.Instances.List(ctx.ProjectID, ctx.ZoneID).Do()
	if err != nil {
		return nil, fmt.Errorf("error getting instance list: %v", err)
	}
	// Finds this instance internal IP.
	for _, inst := range instances.Items {
		if fmt.Sprint(inst.Id) != instID {
			continue
		}
		ctx.Instance = inst.Name
		for _, iface := range inst.NetworkInterfaces {
			if strings.HasPrefix(iface.NetworkIP, "10.") {
				ctx.InternalIP = iface.NetworkIP
				break
			}
		}
		break
	}
	if ctx.Instance == "" || ctx.InternalIP == "" {
		return nil, fmt.Errorf("failed to get current instance name and internal IP")
	}
	return ctx, nil
}

func (ctx *Context) CreateInstance(name, machineType, image, sshkey string) (string, error) {
	prefix := "https://www.googleapis.com/compute/v1/projects/" + ctx.ProjectID
	instance := &compute.Instance{
		Name:        name,
		Description: "syzkaller worker",
		MachineType: prefix + "/zones/" + ctx.ZoneID + "/machineTypes/" + machineType,
		Disks: []*compute.AttachedDisk{
			{
				AutoDelete: true,
				Boot:       true,
				Type:       "PERSISTENT",
				InitializeParams: &compute.AttachedDiskInitializeParams{
					DiskName:    name,
					SourceImage: prefix + "/global/images/" + image,
				},
			},
		},
		Metadata: &compute.Metadata{
			Items: []*compute.MetadataItems{
				{
					Key:   "ssh-keys",
					Value: "syzkaller:" + sshkey,
				},
				{
					Key:   "serial-port-enable",
					Value: "1",
				},
			},
		},
		NetworkInterfaces: []*compute.NetworkInterface{
			&compute.NetworkInterface{
				Network: "global/networks/default",
			},
		},
		Scheduling: &compute.Scheduling{
			AutomaticRestart:  false,
			Preemptible:       true,
			OnHostMaintenance: "TERMINATE",
		},
	}

retry:
	<-ctx.apiRateGate
	op, err := ctx.computeService.Instances.Insert(ctx.ProjectID, ctx.ZoneID, instance).Do()
	if err != nil {
		return "", fmt.Errorf("failed to create instance: %v", err)
	}
	if err := ctx.waitForCompletion("zone", "create image", op.Name, false); err != nil {
		if _, ok := err.(resourcePoolExhaustedError); ok && instance.Scheduling.Preemptible {
			instance.Scheduling.Preemptible = false
			goto retry
		}
		return "", err
	}

	<-ctx.apiRateGate
	inst, err := ctx.computeService.Instances.Get(ctx.ProjectID, ctx.ZoneID, name).Do()
	if err != nil {
		return "", fmt.Errorf("error getting instance %s details after creation: %v", name, err)
	}

	// Finds its internal IP.
	ip := ""
	for _, iface := range inst.NetworkInterfaces {
		if strings.HasPrefix(iface.NetworkIP, "10.") {
			ip = iface.NetworkIP
			break
		}
	}
	if ip == "" {
		return "", fmt.Errorf("didn't find instance internal IP address")
	}
	return ip, nil
}

func (ctx *Context) DeleteInstance(name string, wait bool) error {
	<-ctx.apiRateGate
	op, err := ctx.computeService.Instances.Delete(ctx.ProjectID, ctx.ZoneID, name).Do()
	if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Code == 404 {
		return nil
	}
	if err != nil {
		return fmt.Errorf("failed to delete instance: %v", err)
	}
	if wait {
		if err := ctx.waitForCompletion("zone", "delete image", op.Name, true); err != nil {
			return err
		}
	}
	return nil
}

func (ctx *Context) IsInstanceRunning(name string) bool {
	<-ctx.apiRateGate
	instance, err := ctx.computeService.Instances.Get(ctx.ProjectID, ctx.ZoneID, name).Do()
	if err != nil {
		return false
	}
	return instance.Status == "RUNNING"
}

func (ctx *Context) CreateImage(imageName, gcsFile string) error {
	image := &compute.Image{
		Name: imageName,
		RawDisk: &compute.ImageRawDisk{
			Source: "https://storage.googleapis.com/" + gcsFile,
		},
		Licenses: []string{
			"https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx",
		},
	}
	<-ctx.apiRateGate
	op, err := ctx.computeService.Images.Insert(ctx.ProjectID, image).Do()
	if err != nil {
		// Try again without the vmx license in case it is not supported.
		image.Licenses = nil
		<-ctx.apiRateGate
		op, err = ctx.computeService.Images.Insert(ctx.ProjectID, image).Do()
	}
	if err != nil {
		return fmt.Errorf("failed to create image: %v", err)
	}
	if err := ctx.waitForCompletion("global", "create image", op.Name, false); err != nil {
		return err
	}
	return nil
}

func (ctx *Context) DeleteImage(imageName string) error {
	<-ctx.apiRateGate
	op, err := ctx.computeService.Images.Delete(ctx.ProjectID, imageName).Do()
	if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Code == 404 {
		return nil
	}
	if err != nil {
		return fmt.Errorf("failed to delete image: %v", err)
	}
	if err := ctx.waitForCompletion("global", "delete image", op.Name, true); err != nil {
		return err
	}
	return nil
}

type resourcePoolExhaustedError string

func (err resourcePoolExhaustedError) Error() string {
	return string(err)
}

func (ctx *Context) waitForCompletion(typ, desc, opName string, ignoreNotFound bool) error {
	for {
		time.Sleep(2 * time.Second)
		<-ctx.apiRateGate
		var err error
		var op *compute.Operation
		switch typ {
		case "global":
			op, err = ctx.computeService.GlobalOperations.Get(ctx.ProjectID, opName).Do()
		case "zone":
			op, err = ctx.computeService.ZoneOperations.Get(ctx.ProjectID, ctx.ZoneID, opName).Do()
		default:
			panic("unknown operation type: " + typ)
		}
		if err != nil {
			return fmt.Errorf("failed to get %v operation %v: %v", desc, opName, err)
		}
		switch op.Status {
		case "PENDING", "RUNNING":
			continue
		case "DONE":
			if op.Error != nil {
				reason := ""
				for _, operr := range op.Error.Errors {
					if operr.Code == "ZONE_RESOURCE_POOL_EXHAUSTED" {
						return resourcePoolExhaustedError(fmt.Sprintf("%+v", operr))
					}
					if ignoreNotFound && operr.Code == "RESOURCE_NOT_FOUND" {
						return nil
					}
					reason += fmt.Sprintf("%+v.", operr)
				}
				return fmt.Errorf("%v operation failed: %v", desc, reason)
			}
			return nil
		default:
			return fmt.Errorf("unknown %v operation status %q: %+v", desc, op.Status, op)
		}
	}
}

func (ctx *Context) getMeta(path string) (string, error) {
	req, err := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/"+path, nil)
	if err != nil {
		return "", err
	}
	req.Header.Add("Metadata-Flavor", "Google")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}
<commit_msg>gce: temporary add external IPs to instances<commit_after>// Copyright 2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package gce provides wrappers around Google Compute Engine (GCE) APIs.
// It is assumed that the program itself also runs on GCE as APIs operate on the current project/zone.
//
// See https://cloud.google.com/compute/docs for details.
// In particular, API reference:
// https://cloud.google.com/compute/docs/reference/latest
// and Go API wrappers:
// https://godoc.org/google.golang.org/api/compute/v0.beta
package gce

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/compute/v0.beta"
	"google.golang.org/api/googleapi"
)

type Context struct {
	ProjectID  string
	ZoneID     string
	Instance   string
	InternalIP string

	computeService *compute.Service

	// apiCallTicker ticks regularly, preventing us from accidentally making
	// GCE API calls too quickly. Our quota is 20 QPS, but we temporarily
	// limit ourselves to less than that.
	apiRateGate <-chan time.Time
}

func NewContext() (*Context, error) {
	ctx := &Context{
		apiRateGate: time.NewTicker(time.Second / 10).C,
	}
	background := context.Background()
	tokenSource, err := google.DefaultTokenSource(background, compute.CloudPlatformScope)
	if err != nil {
		return nil, fmt.Errorf("failed to get a token source: %v", err)
	}
	httpClient := oauth2.NewClient(background, tokenSource)
	ctx.computeService, _ = compute.New(httpClient)
	// Obtain project name, zone and current instance IP address.
	ctx.ProjectID, err = ctx.getMeta("project/project-id")
	if err != nil {
		return nil, fmt.Errorf("failed to query gce project-id: %v", err)
	}
	ctx.ZoneID, err = ctx.getMeta("instance/zone")
	if err != nil {
		return nil, fmt.Errorf("failed to query gce zone: %v", err)
	}
	if i := strings.LastIndexByte(ctx.ZoneID, '/'); i != -1 {
		ctx.ZoneID = ctx.ZoneID[i+1:] // the query returns some nonsense prefix
	}
	instID, err := ctx.getMeta("instance/id")
	if err != nil {
		return nil, fmt.Errorf("failed to query gce instance id: %v", err)
	}
	instances, err := ctx.computeService.Instances.List(ctx.ProjectID, ctx.ZoneID).Do()
	if err != nil {
		return nil, fmt.Errorf("error getting instance list: %v", err)
	}
	// Finds this instance internal IP.
	for _, inst := range instances.Items {
		if fmt.Sprint(inst.Id) != instID {
			continue
		}
		ctx.Instance = inst.Name
		for _, iface := range inst.NetworkInterfaces {
			if strings.HasPrefix(iface.NetworkIP, "10.") {
				ctx.InternalIP = iface.NetworkIP
				break
			}
		}
		break
	}
	if ctx.Instance == "" || ctx.InternalIP == "" {
		return nil, fmt.Errorf("failed to get current instance name and internal IP")
	}
	return ctx, nil
}

func (ctx *Context) CreateInstance(name, machineType, image, sshkey string) (string, error) {
	prefix := "https://www.googleapis.com/compute/v1/projects/" + ctx.ProjectID
	instance := &compute.Instance{
		Name:        name,
		Description: "syzkaller worker",
		MachineType: prefix + "/zones/" + ctx.ZoneID + "/machineTypes/" + machineType,
		Disks: []*compute.AttachedDisk{
			{
				AutoDelete: true,
				Boot:       true,
				Type:       "PERSISTENT",
				InitializeParams: &compute.AttachedDiskInitializeParams{
					DiskName:    name,
					SourceImage: prefix + "/global/images/" + image,
				},
			},
		},
		Metadata: &compute.Metadata{
			Items: []*compute.MetadataItems{
				{
					Key:   "ssh-keys",
					Value: "syzkaller:" + sshkey,
				},
				{
					Key:   "serial-port-enable",
					Value: "1",
				},
			},
		},
		NetworkInterfaces: []*compute.NetworkInterface{
			&compute.NetworkInterface{
				Network: "global/networks/default",
				AccessConfigs: []*compute.AccessConfig{
					{
						Name: "External NAT",
						Type: "ONE_TO_ONE_NAT",
					},
				},
			},
		},
		Scheduling: &compute.Scheduling{
			AutomaticRestart:  false,
			Preemptible:       true,
			OnHostMaintenance: "TERMINATE",
		},
	}

retry:
	<-ctx.apiRateGate
	op, err := ctx.computeService.Instances.Insert(ctx.ProjectID, ctx.ZoneID, instance).Do()
	if err != nil {
		return "", fmt.Errorf("failed to create instance: %v", err)
	}
	if err := ctx.waitForCompletion("zone", "create image", op.Name, false); err != nil {
		if _, ok := err.(resourcePoolExhaustedError); ok && instance.Scheduling.Preemptible {
			instance.Scheduling.Preemptible = false
			goto retry
		}
		return "", err
	}

	<-ctx.apiRateGate
	inst, err := ctx.computeService.Instances.Get(ctx.ProjectID, ctx.ZoneID, name).Do()
	if err != nil {
		return "", fmt.Errorf("error getting instance %s details after creation: %v", name, err)
	}

	// Finds its internal IP.
	ip := ""
	for _, iface := range inst.NetworkInterfaces {
		if strings.HasPrefix(iface.NetworkIP, "10.") {
			ip = iface.NetworkIP
			break
		}
	}
	if ip == "" {
		return "", fmt.Errorf("didn't find instance internal IP address")
	}
	return ip, nil
}

func (ctx *Context) DeleteInstance(name string, wait bool) error {
	<-ctx.apiRateGate
	op, err := ctx.computeService.Instances.Delete(ctx.ProjectID, ctx.ZoneID, name).Do()
	if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Code == 404 {
		return nil
	}
	if err != nil {
		return fmt.Errorf("failed to delete instance: %v", err)
	}
	if wait {
		if err := ctx.waitForCompletion("zone", "delete image", op.Name, true); err != nil {
			return err
		}
	}
	return nil
}

func (ctx *Context) IsInstanceRunning(name string) bool {
	<-ctx.apiRateGate
	instance, err := ctx.computeService.Instances.Get(ctx.ProjectID, ctx.ZoneID, name).Do()
	if err != nil {
		return false
	}
	return instance.Status == "RUNNING"
}

func (ctx *Context) CreateImage(imageName, gcsFile string) error {
	image := &compute.Image{
		Name: imageName,
		RawDisk: &compute.ImageRawDisk{
			Source: "https://storage.googleapis.com/" + gcsFile,
		},
		Licenses: []string{
			"https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx",
		},
	}
	<-ctx.apiRateGate
	op, err := ctx.computeService.Images.Insert(ctx.ProjectID, image).Do()
	if err != nil {
		// Try again without the vmx license in case it is not supported.
		image.Licenses = nil
		<-ctx.apiRateGate
		op, err = ctx.computeService.Images.Insert(ctx.ProjectID, image).Do()
	}
	if err != nil {
		return fmt.Errorf("failed to create image: %v", err)
	}
	if err := ctx.waitForCompletion("global", "create image", op.Name, false); err != nil {
		return err
	}
	return nil
}

func (ctx *Context) DeleteImage(imageName string) error {
	<-ctx.apiRateGate
	op, err := ctx.computeService.Images.Delete(ctx.ProjectID, imageName).Do()
	if apiErr, ok := err.(*googleapi.Error); ok && apiErr.Code == 404 {
		return nil
	}
	if err != nil {
		return fmt.Errorf("failed to delete image: %v", err)
	}
	if err := ctx.waitForCompletion("global", "delete image", op.Name, true); err != nil {
		return err
	}
	return nil
}

type resourcePoolExhaustedError string

func (err resourcePoolExhaustedError) Error() string {
	return string(err)
}

func (ctx *Context) waitForCompletion(typ, desc, opName string, ignoreNotFound bool) error {
	for {
		time.Sleep(2 * time.Second)
		<-ctx.apiRateGate
		var err error
		var op *compute.Operation
		switch typ {
		case "global":
			op, err = ctx.computeService.GlobalOperations.Get(ctx.ProjectID, opName).Do()
		case "zone":
			op, err = ctx.computeService.ZoneOperations.Get(ctx.ProjectID, ctx.ZoneID, opName).Do()
		default:
			panic("unknown operation type: " + typ)
		}
		if err != nil {
			return fmt.Errorf("failed to get %v operation %v: %v", desc, opName, err)
		}
		switch op.Status {
		case "PENDING", "RUNNING":
			continue
		case "DONE":
			if op.Error != nil {
				reason := ""
				for _, operr := range op.Error.Errors {
					if operr.Code == "ZONE_RESOURCE_POOL_EXHAUSTED" {
						return resourcePoolExhaustedError(fmt.Sprintf("%+v", operr))
					}
					if ignoreNotFound && operr.Code == "RESOURCE_NOT_FOUND" {
						return nil
					}
					reason += fmt.Sprintf("%+v.", operr)
				}
				return fmt.Errorf("%v operation failed: %v", desc, reason)
			}
			return nil
		default:
			return fmt.Errorf("unknown %v operation status %q: %+v", desc, op.Status, op)
		}
	}
}

func (ctx *Context) getMeta(path string) (string, error) {
	req, err := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/"+path, nil)
	if err != nil {
		return "", err
	}
	req.Header.Add("Metadata-Flavor", "Google")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}
<|endoftext|>


<commit_before><commit_msg>removing dead type init code<commit_after><|endoftext|>


<commit_before>// Package gen does code generation to automate tedious tasks.
//
// Although you can use this package and its subpackages directly, that's not
// recommended. Instead, you should create a genfile.yaml for your package and
// use the gondola gen command to perform the code generation.
package gen

import (
	"fmt"
	"github.com/kylelemons/go-gypsy/yaml"
	"gnd.la/gen/genutil"
	"gnd.la/gen/json"
	"gnd.la/gen/strings"
	"path/filepath"
	"regexp"
	"strconv"
	str "strings"
)

// Gen generates code according to the given config file. If the config file
// can't be found or it can't be parsed, an error will be returned. Previously
// autogenerated files will be overwritten, but trying to overwrite any files
// which were not autogenerated will also return an error. See the package
// documentation for the format of the config file.
func Gen(pkgName string, config string) error {
	if config == "" {
		pkg, err := genutil.NewPackage(pkgName)
		if err != nil {
			return err
		}
		config = filepath.Join(pkg.Dir(), "genfile.yaml")
	}
	f, err := yaml.ReadFile(config)
	if err != nil {
		return err
	}
	root, ok := f.Root.(yaml.Map)
	if !ok {
		return fmt.Errorf("top level object in genfile.yaml must be a map")
	}
	for k, v := range root {
		switch k {
		case "json":
			opts, err := jsonOptions(v)
			if err != nil {
				return err
			}
			if err := json.Gen(pkgName, opts); err != nil {
				return err
			}
		case "strings":
			opts, err := stringsOptions(v)
			if err != nil {
				return err
			}
			if err := strings.Gen(pkgName, opts); err != nil {
				return err
			}
		}
	}
	return nil
}

func jsonOptions(node yaml.Node) (*json.Options, error) {
	if m, ok := node.(yaml.Map); ok {
		opts := &json.Options{}
		var err error
		for k, v := range m {
			switch k {
			case "marshal-json":
				opts.MarshalJSON = nodeToBool(v)
			case "buffer-size":
				if opts.BufferSize, err = nodeToInt(v); err != nil {
					return nil, err
				}
			case "max-buffer-size":
				if opts.MaxBufferSize, err = nodeToInt(v); err != nil {
					return nil, err
				}
			case "buffer-count":
				if opts.BufferCount, err = nodeToInt(v); err != nil {
					return nil, err
				}
			case "buffers-per-proc":
				if opts.BuffersPerProc, err = nodeToInt(v); err != nil {
					return nil, err
				}
			case "include":
				if val := nodeToString(v); val != "" {
					include, err := regexp.Compile(val)
					if err != nil {
						return nil, err
					}
					opts.Include = include
				}
			case "exclude":
				if val := nodeToString(v); val != "" {
					exclude, err := regexp.Compile(val)
					if err != nil {
						return nil, err
					}
					opts.Exclude = exclude
				}
			case "methods":
				if methods, ok := v.(yaml.Map); ok {
					opts.Methods = make(map[string][]*json.Method)
					for typeName, val := range methods {
						if typeMethods, ok := val.(yaml.Map); ok {
							for methodName, node := range typeMethods {
								method := &json.Method{
									Name: methodName,
								}
								switch value := node.(type) {
								case yaml.Map:
									method.Key = nodeToString(value["key"])
									method.OmitEmpty = nodeToBool(value["omitempty"])
								case yaml.Scalar:
									method.Key = nodeToString(value)
								default:
									return nil, fmt.Errorf("method value for %s must be scalar or map", methodName)
								}
								if method.Key == "" {
									method.Key = method.Name
								}
								opts.Methods[typeName] = append(opts.Methods[typeName], method)
							}
						}
					}
				}
			}
		}
		return opts, nil
	}
	return nil, nil
}

func stringsOptions(node yaml.Node) (*strings.Options, error) {
	if m, ok := node.(yaml.Map); ok {
		opts := &strings.Options{}
		for k, v := range m {
			switch k {
			case "include":
				if val := nodeToString(v); val != "" {
					include, err := regexp.Compile(val)
					if err != nil {
						return nil, err
					}
					opts.Include = include
				}
			case "exclude":
				if val := nodeToString(v); val != "" {
					exclude, err := regexp.Compile(val)
					if err != nil {
						return nil, err
					}
					opts.Exclude = exclude
				}
			case "options":
				if options, ok := v.(yaml.Map); ok {
					opts.TypeOptions = make(map[string]*strings.TypeOptions)
					for typeName, val := range options {
						valMap, ok := val.(yaml.Map)
						if !ok {
							return nil, fmt.Errorf("%s type options must be a map", typeName)
						}
						typeOptions := &strings.TypeOptions{}
						tr := nodeToString(valMap["transform"])
						switch tr {
						case "":
						case "uppercase":
							typeOptions.Transform = strings.ToUpper
						case "lowercase":
							typeOptions.Transform = strings.ToLower
						default:
							return nil, fmt.Errorf("invalid transform %q", tr)
						}
						if slice := valMap["slice"]; slice != nil {
							var err error
							if typeOptions.SliceBegin, typeOptions.SliceEnd, err = parseSlice(slice); err != nil {
								return nil, err
							}
						}
						opts.TypeOptions[typeName] = typeOptions
					}
				}
			}
		}
		return opts, nil
	}
	return nil, nil
}

func nodeToBool(node yaml.Node) bool {
	switch n := node.(type) {
	case yaml.List:
		return len(n) > 0
	case yaml.Map:
		return len(n) > 0
	case yaml.Scalar:
		val, _ := strconv.ParseBool(n.String())
		return val
	}
	return false
}

func nodeToString(node yaml.Node) string {
	if s, ok := node.(yaml.Scalar); ok {
		return s.String()
	}
	return ""
}

func nodeToInt(node yaml.Node) (int, error) {
	switch n := node.(type) {
	case yaml.Scalar:
		return strconv.Atoi(n.String())
	}
	return 0, fmt.Errorf("invalid int node %v", node)
}

func parseSlice(node yaml.Node) (int, int, error) {
	switch n := node.(type) {
	case yaml.Scalar:
		return _parseSlice(n.String())
	case yaml.Map:
		if len(n) == 1 {
			for k, v := range n {
				if v == nil {
					v = yaml.Scalar("0")
				}
				return _parseSlice(fmt.Sprintf("%v:%v", k, v))
			}
		}
	}
	return 0, 0, fmt.Errorf("invalid slice spec %v", node)
}

func _parseSlice(s string) (int, int, error) {
	idx := str.Index(s, ":")
	if idx < 0 {
		return 0, 0, fmt.Errorf("slice spec must contain :")
	}
	begin := s[:idx]
	end := s[idx+1:]
	b, err := strconv.Atoi(begin)
	if err != nil {
		return 0, 0, err
	}
	if end == "" {
		return b, 0, nil
	}
	e, err := strconv.Atoi(end)
	if err != nil {
		return 0, 0, err
	}
	return b, e, nil
}
<commit_msg>Use goyaml for parsing the genfile<commit_after>// Package gen does code generation to automate tedious tasks.
//
// Although you can use this package and its subpackages directly, that's not
// recommended. Instead, you should create a genfile.yaml for your package and
// use the gondola gen command to perform the code generation.
package gen

import (
	"fmt"
	"gnd.la/gen/genutil"
	"gnd.la/gen/json"
	"gnd.la/gen/strings"
	"gnd.la/util/types"
	"io/ioutil"
	"launchpad.net/goyaml"
	"path/filepath"
	"regexp"
	"strconv"
	str "strings"
)

// Gen generates code according to the given config file. If the config file
// can't be found or it can't be parsed, an error will be returned. Previously
// autogenerated files will be overwritten, but trying to overwrite any files
// which were not autogenerated will also return an error. See the package
// documentation for the format of the config file.
func Gen(pkgName string, config string) error {
	if config == "" {
		pkg, err := genutil.NewPackage(pkgName)
		if err != nil {
			return err
		}
		config = filepath.Join(pkg.Dir(), "genfile.yaml")
	}
	data, err := ioutil.ReadFile(config)
	if err != nil {
		return fmt.Errorf("could not read %s: %s", config, err)
	}
	var opts map[string]interface{}
	if err := goyaml.Unmarshal(data, &opts); err != nil {
		return fmt.Errorf("could not decode YAML: %s", err)
	}
	for k, v := range opts {
		switch k {
		case "json":
			opts, err := jsonOptions(v)
			if err != nil {
				return err
			}
			if err := json.Gen(pkgName, opts); err != nil {
				return err
			}
		case "strings":
			opts, err := stringsOptions(v)
			if err != nil {
				return err
			}
			if err := strings.Gen(pkgName, opts); err != nil {
				return err
			}
		case "template":
		}
	}
	return nil
}

func jsonOptions(val interface{}) (*json.Options, error) {
	m, ok := toMap(val)
	if !ok {
		return nil, fmt.Errorf("JSON options must be a map")
	}
	opts := &json.Options{}
	var err error
	for k, v := range m {
		switch k {
		case "marshal-json":
			opts.MarshalJSON, _ = types.IsTrue(v)
		case "buffer-size":
			if opts.BufferSize, err = types.ToInt(v); err != nil {
				return nil, err
			}
		case "max-buffer-size":
			if opts.MaxBufferSize, err = types.ToInt(v); err != nil {
				return nil, err
			}
		case "buffer-count":
			if opts.BufferCount, err = types.ToInt(v); err != nil {
				return nil, err
			}
		case "buffers-per-proc":
			if opts.BuffersPerProc, err = types.ToInt(v); err != nil {
				return nil, err
			}
		case "include":
			if val := types.ToString(v); val != "" {
				include, err := regexp.Compile(val)
				if err != nil {
					return nil, err
				}
				opts.Include = include
			}
		case "exclude":
			if val := types.ToString(v); val != "" {
				exclude, err := regexp.Compile(val)
				if err != nil {
					return nil, err
				}
				opts.Exclude = exclude
			}
		case "methods":
			methods, ok := toMap(v)
			if !ok {
				return nil, fmt.Errorf("JSON methods must be a map")
			}
			opts.Methods = make(map[string][]*json.Method)
			for typeName, val := range methods {
				if typeMethods, ok := toMap(val); ok {
					for methodName, node := range typeMethods {
						method := &json.Method{
							Name: methodName,
						}
						switch value := node.(type) {
						case map[interface{}]interface{}:
							method.Key = types.ToString(value["key"])
							method.OmitEmpty, _ = types.IsTrue(value["omitempty"])
						case string:
							method.Key = types.ToString(value)
						default:
							return nil, fmt.Errorf("method value for %s must be string or map", methodName)
						}
						if method.Key == "" {
							method.Key = method.Name
						}
						opts.Methods[typeName] = append(opts.Methods[typeName], method)
					}
				}
			}
		}
	}
	return opts, nil
}

func stringsOptions(val interface{}) (*strings.Options, error) {
	m, ok := toMap(val)
	if !ok {
		return nil, fmt.Errorf("strings options must be a map, not %T", val)
	}
	opts := &strings.Options{}
	for k, v := range m {
		switch k {
		case "include":
			if val := types.ToString(v); val != "" {
				include, err := regexp.Compile(val)
				if err != nil {
					return nil, err
				}
				opts.Include = include
			}
		case "exclude":
			if val := types.ToString(v); val != "" {
				exclude, err := regexp.Compile(val)
				if err != nil {
					return nil, err
				}
				opts.Exclude = exclude
			}
		case "options":
			options, ok := toMap(v)
			if !ok {
				return nil, fmt.Errorf("options inside string options must be a map")
			}
			opts.TypeOptions = make(map[string]*strings.TypeOptions)
			for typeName, val := range options {
				valMap, ok := toMap(val)
				if !ok {
					return nil, fmt.Errorf("%s type options must be a map", typeName)
				}
				typeOptions := &strings.TypeOptions{}
				tr := types.ToString(valMap["transform"])
				switch tr {
				case "":
				case "uppercase":
					typeOptions.Transform = strings.ToUpper
				case "lowercase":
					typeOptions.Transform = strings.ToLower
				default:
					return nil, fmt.Errorf("invalid transform %q", tr)
				}
				if slice := valMap["slice"]; slice != nil {
					var err error
					if typeOptions.SliceBegin, typeOptions.SliceEnd, err = parseSlice(slice); err != nil {
						return nil, err
					}
				}
				opts.TypeOptions[typeName] = typeOptions
			}
		}
	}
	return opts, nil
}

func toMap(val interface{}) (map[string]interface{}, bool) {
	switch v := val.(type) {
	case map[string]interface{}:
		return v, true
	case map[interface{}]interface{}:
		m := make(map[string]interface{})
		for k, v := range v {
			if s, ok := k.(string); ok {
				m[s] = v
			} else {
				return nil, false
			}
		}
		return m, true
	}
	return nil, false
}

func parseSlice(val interface{}) (int, int, error) {
	switch v := val.(type) {
	case string:
		return _parseSlice(v)
	case map[interface{}]interface{}:
		if len(v) == 1 {
			for k, v := range v {
				if v == nil {
					v = "0"
				}
				return _parseSlice(fmt.Sprintf("%v:%v", k, v))
			}
		}
	}
	return 0, 0, fmt.Errorf("invalid slice spec %v", val)
}

func _parseSlice(s string) (int, int, error) {
	idx := str.Index(s, ":")
	if idx < 0 {
		return 0, 0, fmt.Errorf("slice spec must contain :")
	}
	begin := s[:idx]
	end := s[idx+1:]
	b, err := strconv.Atoi(begin)
	if err != nil {
		return 0, 0, err
	}
	if end == "" {
		return b, 0, nil
	}
	e, err := strconv.Atoi(end)
	if err != nil {
		return 0, 0, err
	}
	return b, e, nil
}
<|endoftext|>


<commit_before>package gen

import (
	"fmt"
	"path/filepath"
	"sync"
)

type IosGenerator struct {
	opt  *Options
	mock *Mock
}

var iwd WidgetsDef

func defineIosWidgets() {
	iwd = WidgetsDef{}
	iwd.Add("button", Widget{
		Name:     "UIButton",
		Textable: true,
		Gravity:  GravityCenter,
		SizeW:    SizeFill,
		SizeH:    SizeWrap,
	})
	iwd.Add("label", Widget{
		Name:     "UILabel",
		Textable: true,
		Gravity:  GravityCenter,
		SizeW:    SizeFill,
		SizeH:    SizeWrap,
	})
	iwd.Add("linear", Widget{
		Textable:    false,
		Orientation: OrientationVertical,
		SizeW:       SizeFill,
		SizeH:       SizeFill,
	})
	iwd.Add("relative", Widget{
		Textable: false,
		SizeW:    SizeFill,
		SizeH:    SizeFill,
	})
}

func (g *IosGenerator) Generate() {
	defineIosWidgets()

	outDir := g.opt.OutDir
	projectDir := filepath.Join(outDir, g.mock.Meta.Ios.Project)

	// TODO Generate base file set

	var wg sync.WaitGroup

	// Generate contents.xcworkspacedata
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosContentsXcWorkspaceData(mock, dir)
	}(g.mock, outDir)

	// Generate main.m
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosMain(mock, dir)
	}(g.mock, outDir)

	// Generate Info.plist
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosInfoPlist(mock, dir)
	}(g.mock, outDir)

	// Generate InfoPlist.strings
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosInfoPlistStrings(mock, dir)
	}(g.mock, outDir)

	// Generate Prefix.pch
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosPch(mock, dir)
	}(g.mock, outDir)

	// Generate Images.xcassets
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosImagesXcAssetsAppIcon(mock, dir)
	}(g.mock, outDir)
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosImagesXcAssetsLaunchImage(mock, dir)
	}(g.mock, outDir)

	// Generate AppDelegate
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosAppDelegateHeader(mock, dir)
	}(g.mock, outDir)
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosAppDelegateImplementation(mock, dir)
	}(g.mock, outDir)

	// Generate ViewControllers
	for _, screen := range g.mock.Screens {
		wg.Add(1)
		go func(mock *Mock, dir string, screen Screen) {
			defer wg.Done()
			genIosViewController(mock, dir, screen)
			genIosViewControllerLayout(mock, dir, screen)
		}(g.mock, projectDir, screen)
	}

	// Generate resources
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosLocalizedStrings(mock, dir)
	}(g.mock, projectDir)
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosColors(mock, dir)
	}(g.mock, projectDir)

	wg.Wait()
}

func genIosContentsXcWorkspaceData(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosContentsXcWorkspaceData(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project+".xcodeproj", "project.xcworkspace", "contents.xcworkspacedata"))
}

func genCodeIosContentsXcWorkspaceData(mock *Mock, buf *CodeBuffer) {
	buf.add(`<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:%s.xcodeproj">
   </FileRef>
</Workspace>
`,
		mock.Meta.Ios.Project)
}

func genIosMain(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosMain(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "main.m"))
}

func genCodeIosMain(mock *Mock, buf *CodeBuffer) {
	buf.add(`#import <UIKit/UIKit.h>

#import "%sAppDelegate.h"

int main(int argc, char * argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([%sAppDelegate class]));
    }
}`,
		mock.Meta.Ios.ClassPrefix,
		mock.Meta.Ios.ClassPrefix)
}

func genIosInfoPlist(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosInfoPlist(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, mock.Meta.Ios.Project+"-Info.plist"))
}

func genCodeIosInfoPlist(mock *Mock, buf *CodeBuffer) {
	buf.add(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleDisplayName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundleExecutable</key>
	<string>${EXECUTABLE_NAME}</string>
	<key>CFBundleIdentifier</key>
	<string>%s.${PRODUCT_NAME:rfc1034identifier}</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1.0</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
</dict>
</plist>`,
		mock.Meta.Ios.CompanyIdentifier)
}

func genIosInfoPlistStrings(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosInfoPlistStrings(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "en.lproj", "InfoPlist.strings"))
}

func genCodeIosInfoPlistStrings(mock *Mock, buf *CodeBuffer) {
	buf.add(`/* Localized versions of Info.plist keys */`)
}

func genIosPch(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosPch(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, mock.Meta.Ios.Project+"-Prefix.pch"))
}

func genCodeIosPch(mock *Mock, buf *CodeBuffer) {
	buf.add(`//
//  Prefix header
//
//  The contents of this file are implicitly included at the beginning of every source file.
//

#import <Availability.h>

#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
#endif`)
}

func genIosImagesXcAssetsAppIcon(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosImagesXcAssetsAppIcon(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "Images.xcassets", "AppIcon.appiconset", "Contents.json"))
}

func genCodeIosImagesXcAssetsAppIcon(mock *Mock, buf *CodeBuffer) {
	buf.add(`{
  "images" : [
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}`)
}

func genIosImagesXcAssetsLaunchImage(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosImagesXcAssetsLaunchImage(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "Images.xcassets", "LaunchImage.launchimage", "Contents.json"))
}

func genCodeIosImagesXcAssetsLaunchImage(mock *Mock, buf *CodeBuffer) {
	buf.add(`{
  "images" : [
    {
      "orientation" : "portrait",
      "idiom" : "iphone",
      "extent" : "full-screen",
      "minimum-system-version" : "7.0",
      "scale" : "2x"
    },
    {
      "orientation" : "portrait",
      "idiom" : "iphone",
      "subtype" : "retina4",
      "extent" : "full-screen",
      "minimum-system-version" : "7.0",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}`)
}

func genIosAppDelegateHeader(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosAppDelegateHeader(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, mock.Meta.Ios.ClassPrefix+"AppDelegate.h"))
}

func genCodeIosAppDelegateHeader(mock *Mock, buf *CodeBuffer) {
	buf.add(`#import <UIKit/UIKit.h>

@interface %sAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end`, mock.Meta.Ios.ClassPrefix)
}

func genIosAppDelegateImplementation(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosAppDelegateImplementation(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, mock.Meta.Ios.ClassPrefix+"AppDelegate.m"))
}

func genCodeIosAppDelegateImplementation(mock *Mock, buf *CodeBuffer) {
	buf.add(`#import "%sAppDelegate.h"

@implementation %sAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

@end`,
		mock.Meta.Ios.ClassPrefix,
		mock.Meta.Ios.ClassPrefix)
}

func genIosViewController(mock *Mock, dir string, screen Screen) {
	// TODO
	fmt.Println("iOS: ViewController generator: Not implemented...")
}

func genIosViewControllerLayout(mock *Mock, dir string, screen Screen) {
	// TODO
	fmt.Println("iOS: Layout generator: Not implemented...")
}

func genIosLocalizedStrings(mock *Mock, dir string) {
	// TODO
	fmt.Println("iOS: LocalizedString generator: Not implemented...")
}

func genIosColors(mock *Mock, dir string) {
	// TODO
	fmt.Println("iOS: Colors generator: Not implemented...")
}
<commit_msg>Added iOS project.pbxproj generator(WIP).<commit_after>package gen

import (
	"fmt"
	"path/filepath"
	"sync"
)

type IosGenerator struct {
	opt  *Options
	mock *Mock
}

var iwd WidgetsDef

func defineIosWidgets() {
	iwd = WidgetsDef{}
	iwd.Add("button", Widget{
		Name:     "UIButton",
		Textable: true,
		Gravity:  GravityCenter,
		SizeW:    SizeFill,
		SizeH:    SizeWrap,
	})
	iwd.Add("label", Widget{
		Name:     "UILabel",
		Textable: true,
		Gravity:  GravityCenter,
		SizeW:    SizeFill,
		SizeH:    SizeWrap,
	})
	iwd.Add("linear", Widget{
		Textable:    false,
		Orientation: OrientationVertical,
		SizeW:       SizeFill,
		SizeH:       SizeFill,
	})
	iwd.Add("relative", Widget{
		Textable: false,
		SizeW:    SizeFill,
		SizeH:    SizeFill,
	})
}

func (g *IosGenerator) Generate() {
	defineIosWidgets()

	outDir := g.opt.OutDir
	projectDir := filepath.Join(outDir, g.mock.Meta.Ios.Project)

	var wg sync.WaitGroup

	// Generate contents.xcworkspacedata
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosContentsXcWorkspaceData(mock, dir)
	}(g.mock, outDir)

	// Generate main.m
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosMain(mock, dir)
	}(g.mock, outDir)

	// Generate Info.plist
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosInfoPlist(mock, dir)
	}(g.mock, outDir)

	// Generate InfoPlist.strings
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosInfoPlistStrings(mock, dir)
	}(g.mock, outDir)

	// Generate Prefix.pch
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosPch(mock, dir)
	}(g.mock, outDir)

	// Generate Images.xcassets
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosImagesXcAssetsAppIcon(mock, dir)
	}(g.mock, outDir)
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosImagesXcAssetsLaunchImage(mock, dir)
	}(g.mock, outDir)

	// Generate AppDelegate
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosAppDelegateHeader(mock, dir)
	}(g.mock, outDir)
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosAppDelegateImplementation(mock, dir)
	}(g.mock, outDir)

	// Generate ViewControllers
	for _, screen := range g.mock.Screens {
		wg.Add(1)
		go func(mock *Mock, dir string, screen Screen) {
			defer wg.Done()
			genIosViewController(mock, dir, screen)
			genIosViewControllerLayout(mock, dir, screen)
		}(g.mock, projectDir, screen)
	}

	// Generate resources
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosLocalizedStrings(mock, dir)
	}(g.mock, projectDir)
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosColors(mock, dir)
	}(g.mock, projectDir)

	// Generate project.pbxproj
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosProjectPbxproj(mock, dir)
	}(g.mock, outDir)

	wg.Wait()
}

func genIosContentsXcWorkspaceData(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosContentsXcWorkspaceData(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project+".xcodeproj", "project.xcworkspace", "contents.xcworkspacedata"))
}

func genCodeIosContentsXcWorkspaceData(mock *Mock, buf *CodeBuffer) {
	buf.add(`<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:%s.xcodeproj">
   </FileRef>
</Workspace>
`,
		mock.Meta.Ios.Project)
}

func genIosMain(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosMain(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "main.m"))
}

func genCodeIosMain(mock *Mock, buf *CodeBuffer) {
	buf.add(`#import <UIKit/UIKit.h>

#import "%sAppDelegate.h"

int main(int argc, char * argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([%sAppDelegate class]));
    }
}`,
		mock.Meta.Ios.ClassPrefix,
		mock.Meta.Ios.ClassPrefix)
}

func genIosInfoPlist(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosInfoPlist(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, mock.Meta.Ios.Project+"-Info.plist"))
}

func genCodeIosInfoPlist(mock *Mock, buf *CodeBuffer) {
	buf.add(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleDisplayName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundleExecutable</key>
	<string>${EXECUTABLE_NAME}</string>
	<key>CFBundleIdentifier</key>
	<string>%s.${PRODUCT_NAME:rfc1034identifier}</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1.0</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
</dict>
</plist>`,
		mock.Meta.Ios.CompanyIdentifier)
}

func genIosInfoPlistStrings(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosInfoPlistStrings(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "en.lproj", "InfoPlist.strings"))
}

func genCodeIosInfoPlistStrings(mock *Mock, buf *CodeBuffer) {
	buf.add(`/* Localized versions of Info.plist keys */`)
}

func genIosPch(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosPch(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, mock.Meta.Ios.Project+"-Prefix.pch"))
}

func genCodeIosPch(mock *Mock, buf *CodeBuffer) {
	buf.add(`//
//  Prefix header
//
//  The contents of this file are implicitly included at the beginning of every source file.
//

#import <Availability.h>

#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
#endif`)
}

func genIosImagesXcAssetsAppIcon(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosImagesXcAssetsAppIcon(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "Images.xcassets", "AppIcon.appiconset", "Contents.json"))
}

func genCodeIosImagesXcAssetsAppIcon(mock *Mock, buf *CodeBuffer) {
	buf.add(`{
  "images" : [
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}`)
}

func genIosImagesXcAssetsLaunchImage(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosImagesXcAssetsLaunchImage(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "Images.xcassets", "LaunchImage.launchimage", "Contents.json"))
}

func genCodeIosImagesXcAssetsLaunchImage(mock *Mock, buf *CodeBuffer) {
	buf.add(`{
  "images" : [
    {
      "orientation" : "portrait",
      "idiom" : "iphone",
      "extent" : "full-screen",
      "minimum-system-version" : "7.0",
      "scale" : "2x"
    },
    {
      "orientation" : "portrait",
      "idiom" : "iphone",
      "subtype" : "retina4",
      "extent" : "full-screen",
      "minimum-system-version" : "7.0",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}`)
}

func genIosAppDelegateHeader(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosAppDelegateHeader(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, mock.Meta.Ios.ClassPrefix+"AppDelegate.h"))
}

func genCodeIosAppDelegateHeader(mock *Mock, buf *CodeBuffer) {
	buf.add(`#import <UIKit/UIKit.h>

@interface %sAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end`, mock.Meta.Ios.ClassPrefix)
}

func genIosAppDelegateImplementation(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosAppDelegateImplementation(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, mock.Meta.Ios.ClassPrefix+"AppDelegate.m"))
}

func genCodeIosAppDelegateImplementation(mock *Mock, buf *CodeBuffer) {
	buf.add(`#import "%sAppDelegate.h"

@implementation %sAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

@end`,
		mock.Meta.Ios.ClassPrefix,
		mock.Meta.Ios.ClassPrefix)
}

func genIosViewController(mock *Mock, dir string, screen Screen) {
	// TODO
	fmt.Println("iOS: ViewController generator: Not implemented...")
}

func genIosViewControllerLayout(mock *Mock, dir string, screen Screen) {
	// TODO
	fmt.Println("iOS: Layout generator: Not implemented...")
}

func genIosLocalizedStrings(mock *Mock, dir string) {
	// TODO
	fmt.Println("iOS: LocalizedString generator: Not implemented...")
}

func genIosColors(mock *Mock, dir string) {
	// TODO
	fmt.Println("iOS: Colors generator: Not implemented...")
}

func genIosProjectPbxproj(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosProjectPbxproj(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project+".xcodeproj", "project.pbxproj"))
}

func genCodeIosProjectPbxproj(mock *Mock, buf *CodeBuffer) {
	buf.add(`
`)
}
<|endoftext|>


<commit_before>package apns

import (
	"crypto/tls"
	"encoding/binary"
	"log"
	"time"
)

//ResponseQueueSize indicates how many APNS responses may be buffered.
var ResponseQueueSize = 10000

//SentBufferSize is the maximum number of sent notifications which may be buffered.
var SentBufferSize = 10000

var maxBackoff = 20 * time.Second

//Connection represents a single connection to APNS.
type Connection struct {
	Client
	conn   *tls.Conn
	queue  chan PushNotification
	errors chan *BadPushNotification
}

//Response is a reply from APNS - see apns.ApplePushResponses.
type Response struct {
	Status     uint8
	Identifier uint32
}

func newResponse() *Response {
	return new(Response)
}

//BadPushNotification represents a notification which APNS didn't like.
type BadPushNotification struct {
	PushNotification
	Status uint8
}

//Enqueue adds a push notification to the end of the "sending" queue.
func (conn *Connection) Enqueue(pn *PushNotification) {
	go func(pn *PushNotification) {
		conn.queue <- *pn
	}(pn)
}

//Errors gives you a channel of the push notifications Apple rejected.
func (conn *Connection) Errors() (errors <-chan *BadPushNotification) {
	return conn.errors
}

//Start initiates a connection to APNS and asnchronously sends notifications which have been queued.
func (conn *Connection) Start() error {
	//Connect to APNS. The reason this is here as well as in sender is that this probably catches any unavoidable errors in a synchronous fashion, while in sender it can reconnect after temporary errors (which should work most of the time.)
	err := conn.connect()
	if err != nil {
		return err
	}
	//Start sender goroutine
	sent := make(chan PushNotification)
	go conn.sender(conn.queue, sent)
	//Start reader goroutine
	responses := make(chan *Response, ResponseQueueSize)
	go conn.reader(responses)
	//Start limbo goroutine
	return nil
}

//Stop gracefully closes the connection - it waits for the sending queue to clear, and then shuts down.
func (conn *Connection) Stop() {
	//We can't just close the main queue channel, because retries might still need to be sent there.
	//
}

func (conn *Connection) sender(queue <-chan PushNotification, sent chan PushNotification) {
	defer conn.conn.Close()
	var backoff = time.Duration(100)
	for {
		pn, ok := <-conn.queue
		if !ok {
			//That means the Connection is stopped
			//close sent?
			return
		} else {
			//If not connected, connect
			if conn.conn == nil {
				for {
					err := conn.connect()
					if err != nil {
						//Exponential backoff up to a limit
						log.Println("APNS: Error connecting to server: ", err)
						backoff = backoff * 2
						if backoff > maxBackoff {
							backoff = maxBackoff
						}
						time.Sleep(backoff)
					} else {
						backoff = 100
						break
					}
				}
			}
			//Then send the push notification
			//TODO(draaglom): Do buffering as per the APNS docs
		}
	}
}

func (conn *Connection) reader(responses chan<- *Response) {
	buffer := make([]byte, 6)
	for {
		_, err := conn.conn.Read(buffer)
		if err != nil {
			log.Println("APNS: Error reading from connection: ", err)
			conn.conn.Close()
			return
		}
		resp := newResponse()
		resp.Identifier = binary.BigEndian.Uint32(buffer[2:6])
		resp.Status = uint8(buffer[1])
		responses <- resp
	}
}

func (conn *Connection) limbo(sent <-chan PushNotification, responses chan Response, errors chan BadPushNotification, queue chan PushNotification) {
	limbo := make(chan PushNotification, SentBufferSize)
	ticker := time.NewTicker(1 * time.Second)
	timeNextNotification := true
	for {
		select {
		case pn := <-sent:
			//Drop it into the array
			limbo <- pn
			if timeNextNotification {
				//Is there a cleaner way of doing this?
				go func(pn PushNotification) {
					<-time.After(TimeoutSeconds * time.Second)
					successResp := newResponse()
					successResp.Identifier = pn.Identifier
					responses <- *successResp
				}(pn)
				timeNextNotification = false
			}
		case resp, ok := <-responses:
			if !ok {
				//If the responses channel is closed,
				//that means we're shutting down the connection.
			}
			switch {
			case resp.Status == 0:
				//Status 0 is a "success" response generated by a timeout in the library.
				for pn := range limbo {
					//Drop all the notifications until we get to the timed-out one.
					//(and leave the others in limbo)
					if pn.Identifier == resp.Identifier {
						break
					}
				}
			default:
				hit := false
				for pn := range limbo {
					switch {
					case pn.Identifier != resp.Identifier && !hit:
						//We haven't seen the identified notification yet
						//so these are all successful (drop silently)
					case pn.Identifier == resp.Identifier:
						hit = true
						if resp.Status != 10 {
							//It was an error, we should report this on the error channel
							bad := BadPushNotification{PushNotification: pn, Status: resp.Status}
							go func(bad BadPushNotification) {
								errors <- bad
							}(bad)
						}
					case pn.Identifier != resp.Identifier && hit:
						//We've already seen the identified notification,
						//so these should be requeued
						conn.Enqueue(&pn)
					}
				}
			}
		case <-ticker.C:
			timeNextNotification = true
		}
	}
}

func (conn *Connection) connect() error {
	if conn.conn != nil {
		conn.conn.Close()
	}

	var cert tls.Certificate
	var err error
	if len(conn.CertificateBase64) == 0 && len(conn.KeyBase64) == 0 {
		// The user did not specify raw block contents, so check the filesystem.
		cert, err = tls.LoadX509KeyPair(conn.CertificateFile, conn.KeyFile)
	} else {
		// The user provided the raw block contents, so use that.
		cert, err = tls.X509KeyPair([]byte(conn.CertificateBase64), []byte(conn.KeyBase64))
	}

	if err != nil {
		return err
	}

	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	tlsConn, err := tls.Dial("tcp", conn.Gateway, conf)
	if err != nil {
		return err
	}
	err = tlsConn.Handshake()
	if err != nil {
		_ = tlsConn.Close()
		return err
	}
	conn.conn = tlsConn
	return nil
}
<commit_msg>actually write the notification to apns<commit_after>package apns

import (
	"crypto/tls"
	"encoding/binary"
	"log"
	"time"
)

//ResponseQueueSize indicates how many APNS responses may be buffered.
var ResponseQueueSize = 10000

//SentBufferSize is the maximum number of sent notifications which may be buffered.
var SentBufferSize = 10000

var maxBackoff = 20 * time.Second

//Connection represents a single connection to APNS.
type Connection struct {
	Client
	conn   *tls.Conn
	queue  chan PushNotification
	errors chan *BadPushNotification
}

//Response is a reply from APNS - see apns.ApplePushResponses.
type Response struct {
	Status     uint8
	Identifier uint32
}

func newResponse() *Response {
	return new(Response)
}

//BadPushNotification represents a notification which APNS didn't like.
type BadPushNotification struct {
	PushNotification
	Status uint8
}

//Enqueue adds a push notification to the end of the "sending" queue.
func (conn *Connection) Enqueue(pn *PushNotification) {
	go func(pn *PushNotification) {
		conn.queue <- *pn
	}(pn)
}

//Errors gives you a channel of the push notifications Apple rejected.
func (conn *Connection) Errors() (errors <-chan *BadPushNotification) {
	return conn.errors
}

//Start initiates a connection to APNS and asnchronously sends notifications which have been queued.
func (conn *Connection) Start() error {
	//Connect to APNS. The reason this is here as well as in sender is that this probably catches any unavoidable errors in a synchronous fashion, while in sender it can reconnect after temporary errors (which should work most of the time.)
	err := conn.connect()
	if err != nil {
		return err
	}
	//Start sender goroutine
	sent := make(chan PushNotification)
	go conn.sender(conn.queue, sent)
	//Start reader goroutine
	responses := make(chan *Response, ResponseQueueSize)
	go conn.reader(responses)
	//Start limbo goroutine
	return nil
}

//Stop gracefully closes the connection - it waits for the sending queue to clear, and then shuts down.
func (conn *Connection) Stop() {
	//We can't just close the main queue channel, because retries might still need to be sent there.
	//
}

func (conn *Connection) sender(queue <-chan PushNotification, sent chan PushNotification) {
	defer conn.conn.Close()
	var backoff = time.Duration(100)
	for {
		pn, ok := <-conn.queue
		if !ok {
			//That means the Connection is stopped
			//close sent?
			return
		} else {
			//If not connected, connect
			if conn.conn == nil {
				for {
					err := conn.connect()
					if err != nil {
						//Exponential backoff up to a limit
						log.Println("APNS: Error connecting to server: ", err)
						backoff = backoff * 2
						if backoff > maxBackoff {
							backoff = maxBackoff
						}
						time.Sleep(backoff)
					} else {
						backoff = 100
						break
					}
				}
			}
			//Then send the push notification
			//TODO(draaglom): Do buffering as per the APNS docs
			payload, err := pn.ToBytes()
			if err != nil {
				//Should report this on the bad notifications channel probably
			} else {
				_, err = conn.conn.Write(payload)
				if err != nil {
					//Disconnect?
				} else {
					sent <- pn
				}
			}

		}
	}
}

func (conn *Connection) reader(responses chan<- *Response) {
	buffer := make([]byte, 6)
	for {
		_, err := conn.conn.Read(buffer)
		if err != nil {
			log.Println("APNS: Error reading from connection: ", err)
			conn.conn.Close()
			return
		}
		resp := newResponse()
		resp.Identifier = binary.BigEndian.Uint32(buffer[2:6])
		resp.Status = uint8(buffer[1])
		responses <- resp
	}
}

func (conn *Connection) limbo(sent <-chan PushNotification, responses chan Response, errors chan BadPushNotification, queue chan PushNotification) {
	limbo := make(chan PushNotification, SentBufferSize)
	ticker := time.NewTicker(1 * time.Second)
	timeNextNotification := true
	for {
		select {
		case pn := <-sent:
			//Drop it into the array
			limbo <- pn
			if timeNextNotification {
				//Is there a cleaner way of doing this?
				go func(pn PushNotification) {
					<-time.After(TimeoutSeconds * time.Second)
					successResp := newResponse()
					successResp.Identifier = pn.Identifier
					responses <- *successResp
				}(pn)
				timeNextNotification = false
			}
		case resp, ok := <-responses:
			if !ok {
				//If the responses channel is closed,
				//that means we're shutting down the connection.
			}
			switch {
			case resp.Status == 0:
				//Status 0 is a "success" response generated by a timeout in the library.
				for pn := range limbo {
					//Drop all the notifications until we get to the timed-out one.
					//(and leave the others in limbo)
					if pn.Identifier == resp.Identifier {
						break
					}
				}
			default:
				hit := false
				for pn := range limbo {
					switch {
					case pn.Identifier != resp.Identifier && !hit:
						//We haven't seen the identified notification yet
						//so these are all successful (drop silently)
					case pn.Identifier == resp.Identifier:
						hit = true
						if resp.Status != 10 {
							//It was an error, we should report this on the error channel
							bad := BadPushNotification{PushNotification: pn, Status: resp.Status}
							go func(bad BadPushNotification) {
								errors <- bad
							}(bad)
						}
					case pn.Identifier != resp.Identifier && hit:
						//We've already seen the identified notification,
						//so these should be requeued
						conn.Enqueue(&pn)
					}
				}
			}
		case <-ticker.C:
			timeNextNotification = true
		}
	}
}

func (conn *Connection) connect() error {
	if conn.conn != nil {
		conn.conn.Close()
	}

	var cert tls.Certificate
	var err error
	if len(conn.CertificateBase64) == 0 && len(conn.KeyBase64) == 0 {
		// The user did not specify raw block contents, so check the filesystem.
		cert, err = tls.LoadX509KeyPair(conn.CertificateFile, conn.KeyFile)
	} else {
		// The user provided the raw block contents, so use that.
		cert, err = tls.X509KeyPair([]byte(conn.CertificateBase64), []byte(conn.KeyBase64))
	}

	if err != nil {
		return err
	}

	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	tlsConn, err := tls.Dial("tcp", conn.Gateway, conf)
	if err != nil {
		return err
	}
	err = tlsConn.Handshake()
	if err != nil {
		_ = tlsConn.Close()
		return err
	}
	conn.conn = tlsConn
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// some code borrowed from example chat program of
// https://github.com/gorilla/websocket
package conductor

import (
	"encoding/json"
	"fmt"
	"github.com/acmacalister/skittles"
	"github.com/gorilla/websocket"
	"log"
	"time"
)

const (
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second

	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second

	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10

	// Maximum message size allowed from peer.
	maxMessageSize = 512 * 500 // Don't leave me like this!!
)

// connection is an middleman between the websocket connection and the hub.
type connection struct {
	ws       *websocket.Conn
	send     chan Message
	channels []string
	peer     bool
	name     string
	token    string
}

// broadcasting message
type broadcastWriter struct {
	conn    *connection
	message *Message
	peer    bool
}

// readPump pumps messages from the websocket connection to the hub.
func (c *connection) readPump(server *Server) {
	defer c.closeConnection(server)

	c.ws.SetReadLimit(maxMessageSize)
	c.ws.SetReadDeadline(time.Now().Add(pongWait))
	c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		var message Message
		err := c.ws.ReadJSON(&message)
		if err != nil {
			log.Println(skittles.BoldRed(err))
			break
		}

		message.Name = c.name
		if message.OpCode == PeerBindOpCode && c.peer {
			server.connectToPeer(message.Body)
		} else if message.OpCode == ServerOpCode {
			if server.ServerQuery != nil {
				c.send <- server.ServerQuery.QueryHandler(message, c.token)
			}
		} else if message.OpCode == InviteOpCode {
			log.Println("Invite Op Code")
			if c.canWrite(&message, server) {
				log.Println("Yeah we can write. Shooting out to the hub.")
				server.hub.invite <- broadcastWriter{conn: c, message: &message, peer: false}
			}
		} else {
			if message.OpCode == BindOpCode {
				c.bind(&message, server)
				if server.Notification != nil {
					server.Notification.BindHandler(message, c.token)
				}
			} else if message.OpCode == UnBindOpCode {
				c.unbind(&message, server)
				if server.Notification != nil {
					server.Notification.UnBindHandler(message, c.token)
				}
			}
			if server.Notification != nil && message.OpCode == WriteOpCode {
				server.Notification.PersistentHandler(message, c.token)
			}
			if c.canWrite(&message, server) {
				server.hub.broadcast <- broadcastWriter{conn: c, message: &message, peer: false}
			}
		}
	}
}

// bind to a channel.
func (c *connection) bind(message *Message, server *Server) {
	authStatus := true
	if !c.peer && server.Auth != nil {
		authStatus = server.Auth.ChannelAuthHandler(*message, c.token)
	}
	if authStatus {
		addChannel := true
		for _, channel := range c.channels {
			if channel == message.ChannelName {
				addChannel = false
				break
			}
		}
		if addChannel {
			server.hub.bind <- broadcastWriter{conn: c, message: message, peer: false}
			c.channels = append(c.channels, message.ChannelName)
		}
	} else {
		log.Println(skittles.BoldRed(fmt.Sprintf("%s: was unable to connect to the channel", c.name)))
		c.closeConnection(server)
	}
}

//unbind from a channel
func (c *connection) unbind(message *Message, server *Server) {
	authStatus := true
	if !c.peer && server.Auth != nil {
		authStatus = server.Auth.ChannelAuthHandler(*message, c.token)
	}

	if authStatus {
		for i, channel := range c.channels {
			if channel == message.ChannelName {
				c.channels = append(c.channels[:i], c.channels[i+1:]...)
				break
			}
		}
		server.hub.unbind <- broadcastWriter{conn: c, message: message, peer: false}
	} else {
		log.Println(skittles.BoldRed(fmt.Sprintf("%s: was unable to connect to the channel", c.name)))
		c.closeConnection(server)
	}
}

//check and make sure we can write this
func (c *connection) canWrite(message *Message, server *Server) bool {
	if c.peer {
		return true
	}
	authStatus := true
	if !c.peer && server.Auth != nil {
		authStatus = server.Auth.MessageAuthHandler(*message, c.token)
	}
	//check if this a channel the client is bound to
	if authStatus {
		authStatus = false
		for _, channel := range c.channels {
			if channel == message.ChannelName {
				return true
			}
		}
	}
	return authStatus
}

//closes the connection
func (c *connection) closeConnection(server *Server) {
	//unbind from all the channels if client is disconnected
	if server.Notification != nil {
		for _, name := range c.channels {
			server.Notification.UnBindHandler(Message{Name: c.name, Body: "", ChannelName: name, OpCode: UnBindOpCode}, c.token)
		}
	}
	server.hub.unregister <- c
	c.ws.Close()
}

// writePump pumps messages from the hub to the websocket connection.
func (c *connection) writePump(server *Server) {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		c.ws.Close()
	}()

	for {
		select {
		case message, ok := <-c.send:
			if !ok {
				c.write(websocket.CloseMessage, Message{})
				return
			}
			authStatus := true
			if server.Auth != nil {
				authStatus = server.Auth.MessageAuthHandler(message, c.token)
			}
			err := c.write(websocket.TextMessage, message)
			if err != nil && authStatus {
				return
			}
		case <-ticker.C:
			if err := c.write(websocket.PingMessage, Message{}); err != nil {
				return
			}
		}
	}
}

// write writes a message with the given message type and payload.
func (c *connection) write(mt int, payload Message) error {
	c.ws.SetWriteDeadline(time.Now().Add(writeWait))
	buf, _ := json.Marshal(payload)
	return c.ws.WriteMessage(mt, buf)
}
<commit_msg>removed can write on invite opcode<commit_after>// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// some code borrowed from example chat program of
// https://github.com/gorilla/websocket
package conductor

import (
	"encoding/json"
	"fmt"
	"github.com/acmacalister/skittles"
	"github.com/gorilla/websocket"
	"log"
	"time"
)

const (
	// Time allowed to write a message to the peer.
	writeWait = 10 * time.Second

	// Time allowed to read the next pong message from the peer.
	pongWait = 60 * time.Second

	// Send pings to peer with this period. Must be less than pongWait.
	pingPeriod = (pongWait * 9) / 10

	// Maximum message size allowed from peer.
	maxMessageSize = 512 * 500 // Don't leave me like this!!
)

// connection is an middleman between the websocket connection and the hub.
type connection struct {
	ws       *websocket.Conn
	send     chan Message
	channels []string
	peer     bool
	name     string
	token    string
}

// broadcasting message
type broadcastWriter struct {
	conn    *connection
	message *Message
	peer    bool
}

// readPump pumps messages from the websocket connection to the hub.
func (c *connection) readPump(server *Server) {
	defer c.closeConnection(server)

	c.ws.SetReadLimit(maxMessageSize)
	c.ws.SetReadDeadline(time.Now().Add(pongWait))
	c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		var message Message
		err := c.ws.ReadJSON(&message)
		if err != nil {
			log.Println(skittles.BoldRed(err))
			break
		}

		message.Name = c.name
		if message.OpCode == PeerBindOpCode && c.peer {
			server.connectToPeer(message.Body)
		} else if message.OpCode == ServerOpCode {
			if server.ServerQuery != nil {
				c.send <- server.ServerQuery.QueryHandler(message, c.token)
			}
		} else if message.OpCode == InviteOpCode {
			log.Println("Invite Op Code")
			server.hub.invite <- broadcastWriter{conn: c, message: &message, peer: false}
		} else {
			if message.OpCode == BindOpCode {
				c.bind(&message, server)
				if server.Notification != nil {
					server.Notification.BindHandler(message, c.token)
				}
			} else if message.OpCode == UnBindOpCode {
				c.unbind(&message, server)
				if server.Notification != nil {
					server.Notification.UnBindHandler(message, c.token)
				}
			}
			if server.Notification != nil && message.OpCode == WriteOpCode {
				server.Notification.PersistentHandler(message, c.token)
			}
			if c.canWrite(&message, server) {
				server.hub.broadcast <- broadcastWriter{conn: c, message: &message, peer: false}
			}
		}
	}
}

// bind to a channel.
func (c *connection) bind(message *Message, server *Server) {
	authStatus := true
	if !c.peer && server.Auth != nil {
		authStatus = server.Auth.ChannelAuthHandler(*message, c.token)
	}
	if authStatus {
		addChannel := true
		for _, channel := range c.channels {
			if channel == message.ChannelName {
				addChannel = false
				break
			}
		}
		if addChannel {
			server.hub.bind <- broadcastWriter{conn: c, message: message, peer: false}
			c.channels = append(c.channels, message.ChannelName)
		}
	} else {
		log.Println(skittles.BoldRed(fmt.Sprintf("%s: was unable to connect to the channel", c.name)))
		c.closeConnection(server)
	}
}

//unbind from a channel
func (c *connection) unbind(message *Message, server *Server) {
	authStatus := true
	if !c.peer && server.Auth != nil {
		authStatus = server.Auth.ChannelAuthHandler(*message, c.token)
	}

	if authStatus {
		for i, channel := range c.channels {
			if channel == message.ChannelName {
				c.channels = append(c.channels[:i], c.channels[i+1:]...)
				break
			}
		}
		server.hub.unbind <- broadcastWriter{conn: c, message: message, peer: false}
	} else {
		log.Println(skittles.BoldRed(fmt.Sprintf("%s: was unable to connect to the channel", c.name)))
		c.closeConnection(server)
	}
}

//check and make sure we can write this
func (c *connection) canWrite(message *Message, server *Server) bool {
	if c.peer {
		return true
	}
	authStatus := true
	if !c.peer && server.Auth != nil {
		authStatus = server.Auth.MessageAuthHandler(*message, c.token)
	}
	//check if this a channel the client is bound to
	if authStatus {
		authStatus = false
		for _, channel := range c.channels {
			if channel == message.ChannelName {
				return true
			}
		}
	}
	return authStatus
}

//closes the connection
func (c *connection) closeConnection(server *Server) {
	//unbind from all the channels if client is disconnected
	if server.Notification != nil {
		for _, name := range c.channels {
			server.Notification.UnBindHandler(Message{Name: c.name, Body: "", ChannelName: name, OpCode: UnBindOpCode}, c.token)
		}
	}
	server.hub.unregister <- c
	c.ws.Close()
}

// writePump pumps messages from the hub to the websocket connection.
func (c *connection) writePump(server *Server) {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
		c.ws.Close()
	}()

	for {
		select {
		case message, ok := <-c.send:
			if !ok {
				c.write(websocket.CloseMessage, Message{})
				return
			}
			authStatus := true
			if server.Auth != nil {
				authStatus = server.Auth.MessageAuthHandler(message, c.token)
			}
			err := c.write(websocket.TextMessage, message)
			if err != nil && authStatus {
				return
			}
		case <-ticker.C:
			if err := c.write(websocket.PingMessage, Message{}); err != nil {
				return
			}
		}
	}
}

// write writes a message with the given message type and payload.
func (c *connection) write(mt int, payload Message) error {
	c.ws.SetWriteDeadline(time.Now().Add(writeWait))
	buf, _ := json.Marshal(payload)
	return c.ws.WriteMessage(mt, buf)
}
<|endoftext|>