<commit_before>package semaphore

import (
	"context"
	"fmt"
	"strconv"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func testNewCloseableInvalidCount(t *testing.T) {
	for _, c := range []int{0, -1} {
		t.Run(strconv.Itoa(c), func(t *testing.T) {
			assert.Panics(t, func() {
				NewCloseable(c)
			})
		})
	}
}

func testNewCloseableValidCount(t *testing.T) {
	for _, c := range []int{1, 2, 5} {
		t.Run(strconv.Itoa(c), func(t *testing.T) {
			s := NewCloseable(c)
			assert.NotNil(t, s)
		})
	}
}

func TestNewCloseable(t *testing.T) {
	t.Run("InvalidCount", testNewCloseableInvalidCount)
	t.Run("ValidCount", testNewCloseableValidCount)
}

func testCloseableTryAcquire(t *testing.T, cs Closeable, totalCount int) {
	assert := assert.New(t)
	for i := 0; i < totalCount; i++ {
		assert.True(cs.TryAcquire())
	}

	assert.False(cs.TryAcquire())
	assert.NoError(cs.Release())
	assert.True(cs.TryAcquire())
	assert.False(cs.TryAcquire())

	assert.NoError(cs.Release())
	assert.NoError(cs.Close())
	assert.False(cs.TryAcquire())
	assert.Equal(ErrClosed, cs.Close())
	assert.Equal(ErrClosed, cs.Release())
}

func testCloseableAcquireSuccess(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
	)

	// acquire all the things!
	for i := 0; i < totalCount; i++ {
		done := make(chan struct{})
		go func() {
			defer close(done)
			cs.Acquire()
		}()

		select {
		case <-done:
			// passing
		case <-time.After(time.Second):
			assert.FailNow("Acquire blocked unexpectedly")
		}
	}

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	var (
		ready    = make(chan struct{})
		acquired = make(chan struct{})
	)

	go func() {
		defer close(acquired)
		close(ready)
		cs.Acquire() // this should now block
	}()

	select {
	case <-ready:
		// passing
		require.False(cs.TryAcquire())
		cs.Release()
	case <-time.After(time.Second):
		require.FailNow("Unable to spawn acquire goroutine")
	}

	select {
	case <-acquired:
		require.False(cs.TryAcquire())
	case <-time.After(time.Second):
		require.FailNow("Acquire blocked unexpectedly")
	}

	assert.NoError(cs.Release())
	assert.True(cs.TryAcquire())
	assert.NoError(cs.Release())
}

func testCloseableAcquireClose(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert  = assert.New(t)
		require = require.New(t)

		acquiredAll = make(chan struct{})
		results     = make(chan error, totalCount)
		closeWait   = make(chan struct{})
	)

	go func() {
		defer close(acquiredAll)
		for i := 0; i < totalCount; i++ {
			assert.NoError(cs.Acquire())
		}
	}()

	select {
	case <-acquiredAll:
		// passing
	case <-time.After(5 * time.Second):
		assert.FailNow("Unable to acquire all resources")
	}

	// block multiple routines waiting to acquire the semaphore
	for i := 0; i < totalCount; i++ {
		ready := make(chan struct{})
		go func() {
			close(ready)
			results <- cs.Acquire()
		}()

		select {
		case <-ready:
			// passing
		case <-time.After(time.Second):
			assert.FailNow("Failed to spawn Acquire goroutine")
		}
	}

	go func() {
		defer close(closeWait)
		<-cs.Closed()
	}()

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	assert.NoError(cs.Close())
	for i := 0; i < totalCount; i++ {
		select {
		case err := <-results:
			assert.Equal(ErrClosed, err)
		case <-time.After(5 * time.Second):
			assert.FailNow("Acquire blocked unexpectedly")
		}
	}

	select {
	case <-closeWait:
		assert.False(cs.TryAcquire())
		assert.Equal(ErrClosed, cs.Close())
		assert.Equal(ErrClosed, cs.Acquire())
		assert.Equal(ErrClosed, cs.Release())

	case <-time.After(5 * time.Second):
		assert.FailNow("Closed channel did not get signaled")
	}
}

func testCloseableAcquireWait(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		timer   = make(chan time.Time)
	)

	// acquire all the things!
	for i := 0; i < totalCount; i++ {
		result := make(chan error)
		go func() {
			result <- cs.AcquireWait(timer)
		}()

		select {
		case err := <-result:
			assert.NoError(err)
		case <-time.After(time.Second):
			assert.FailNow("Acquire blocked unexpectedly")
		}
	}

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	var (
		ready  = make(chan struct{})
		result = make(chan error)
	)

	go func() {
		close(ready)
		result <- cs.AcquireWait(timer)
	}()

	select {
	case <-ready:
		timer <- time.Time{}
	case <-time.After(time.Second):
		require.FailNow("Unable to spawn acquire goroutine")
	}

	select {
	case err := <-result:
		assert.Equal(ErrTimeout, err)
	case <-time.After(time.Second):
		require.FailNow("AcquireWait blocked unexpectedly")
	}
}

func testCloseableAcquireCtx(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert      = assert.New(t)
		require     = require.New(t)
		ctx, cancel = context.WithCancel(context.Background())
	)

	defer cancel()

	// acquire all the things!
	for i := 0; i < totalCount; i++ {
		result := make(chan error)
		go func() {
			result <- cs.AcquireCtx(ctx)
		}()

		select {
		case err := <-result:
			assert.NoError(err)
		case <-time.After(time.Second):
			assert.FailNow("Acquire blocked unexpectedly")
		}
	}

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	var (
		ready  = make(chan struct{})
		result = make(chan error)
	)

	go func() {
		close(ready)
		result <- cs.AcquireCtx(ctx)
	}()

	select {
	case <-ready:
		cancel()
	case <-time.After(time.Second):
		require.FailNow("Unable to spawn acquire goroutine")
	}

	select {
	case err := <-result:
		assert.Equal(ctx.Err(), err)
	case <-time.After(time.Second):
		require.FailNow("AcquireWait blocked unexpectedly")
	}
}

func TestCloseable(t *testing.T) {
	for _, c := range []int{1, 2, 5} {
		t.Run(fmt.Sprintf("count=%d", c), func(t *testing.T) {
			t.Run("TryAcquire", func(t *testing.T) {
				testCloseableTryAcquire(t, NewCloseable(c), c)
			})

			t.Run("Acquire", func(t *testing.T) {
				t.Run("Success", func(t *testing.T) {
					testCloseableAcquireSuccess(t, NewCloseable(c), c)
				})

				t.Run("Close", func(t *testing.T) {
					testCloseableAcquireClose(t, NewCloseable(c), c)
				})
			})

			t.Run("AcquireWait", func(t *testing.T) {
				testCloseableAcquireWait(t, NewCloseable(c), c)
			})

			t.Run("AcquireCtx", func(t *testing.T) {
				testCloseableAcquireCtx(t, NewCloseable(c), c)
			})
		})
	}
}

func TestCloseableMutex(t *testing.T) {
	t.Run("TryAcquire", func(t *testing.T) {
		testCloseableTryAcquire(t, CloseableMutex(), 1)
	})

	t.Run("Acquire", func(t *testing.T) {
		t.Run("Success", func(t *testing.T) {
			testCloseableAcquireSuccess(t, CloseableMutex(), 1)
		})

		t.Run("Close", func(t *testing.T) {
			testCloseableAcquireClose(t, CloseableMutex(), 1)
		})
	})

	t.Run("AcquireWait", func(t *testing.T) {
		testCloseableAcquireWait(t, CloseableMutex(), 1)
	})

	t.Run("AcquireCtx", func(t *testing.T) {
		testCloseableAcquireCtx(t, CloseableMutex(), 1)
	})
}
<commit_msg>Full tests for Closeable<commit_after>package semaphore

import (
	"context"
	"fmt"
	"strconv"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func testNewCloseableInvalidCount(t *testing.T) {
	for _, c := range []int{0, -1} {
		t.Run(strconv.Itoa(c), func(t *testing.T) {
			assert.Panics(t, func() {
				NewCloseable(c)
			})
		})
	}
}

func testNewCloseableValidCount(t *testing.T) {
	for _, c := range []int{1, 2, 5} {
		t.Run(strconv.Itoa(c), func(t *testing.T) {
			s := NewCloseable(c)
			assert.NotNil(t, s)
		})
	}
}

func TestNewCloseable(t *testing.T) {
	t.Run("InvalidCount", testNewCloseableInvalidCount)
	t.Run("ValidCount", testNewCloseableValidCount)
}

func testCloseableTryAcquire(t *testing.T, cs Closeable, totalCount int) {
	assert := assert.New(t)
	for i := 0; i < totalCount; i++ {
		assert.True(cs.TryAcquire())
	}

	assert.False(cs.TryAcquire())
	assert.NoError(cs.Release())
	assert.True(cs.TryAcquire())
	assert.False(cs.TryAcquire())

	assert.NoError(cs.Release())
	assert.NoError(cs.Close())
	assert.False(cs.TryAcquire())
	assert.Equal(ErrClosed, cs.Close())
	assert.Equal(ErrClosed, cs.Release())
}

func testCloseableAcquireSuccess(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
	)

	// acquire all the things!
	for i := 0; i < totalCount; i++ {
		done := make(chan struct{})
		go func() {
			defer close(done)
			cs.Acquire()
		}()

		select {
		case <-done:
			// passing
		case <-time.After(time.Second):
			assert.FailNow("Acquire blocked unexpectedly")
		}
	}

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	var (
		ready    = make(chan struct{})
		acquired = make(chan struct{})
	)

	go func() {
		defer close(acquired)
		close(ready)
		cs.Acquire() // this should now block
	}()

	select {
	case <-ready:
		// passing
		require.False(cs.TryAcquire())
		cs.Release()
	case <-time.After(time.Second):
		require.FailNow("Unable to spawn acquire goroutine")
	}

	select {
	case <-acquired:
		require.False(cs.TryAcquire())
	case <-time.After(time.Second):
		require.FailNow("Acquire blocked unexpectedly")
	}

	assert.NoError(cs.Release())
	assert.True(cs.TryAcquire())
	assert.NoError(cs.Release())
}

func testCloseableAcquireClose(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert  = assert.New(t)
		require = require.New(t)

		acquiredAll = make(chan struct{})
		results     = make(chan error, totalCount)
		closeWait   = make(chan struct{})
	)

	defer cs.Close()

	go func() {
		defer close(acquiredAll)
		for i := 0; i < totalCount; i++ {
			assert.NoError(cs.Acquire())
		}
	}()

	select {
	case <-acquiredAll:
		// passing
	case <-time.After(5 * time.Second):
		assert.FailNow("Unable to acquire all resources")
	}

	// block multiple routines waiting to acquire the semaphore
	for i := 0; i < totalCount; i++ {
		ready := make(chan struct{})
		go func() {
			close(ready)
			results <- cs.Acquire()
		}()

		select {
		case <-ready:
			// passing
		case <-time.After(time.Second):
			assert.FailNow("Failed to spawn Acquire goroutine")
		}
	}

	go func() {
		defer close(closeWait)
		<-cs.Closed()
	}()

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	assert.NoError(cs.Close())
	for i := 0; i < totalCount; i++ {
		select {
		case err := <-results:
			assert.Equal(ErrClosed, err)
		case <-time.After(5 * time.Second):
			assert.FailNow("Acquire blocked unexpectedly")
		}
	}

	select {
	case <-closeWait:
		assert.False(cs.TryAcquire())
		assert.Equal(ErrClosed, cs.Close())
		assert.Equal(ErrClosed, cs.Acquire())
		assert.Equal(ErrClosed, cs.Release())

	case <-time.After(5 * time.Second):
		assert.FailNow("Closed channel did not get signaled")
	}
}

func testCloseableAcquireWaitSuccess(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		timer   = make(chan time.Time)
	)

	// acquire all the things!
	for i := 0; i < totalCount; i++ {
		result := make(chan error)
		go func() {
			result <- cs.AcquireWait(timer)
		}()

		select {
		case err := <-result:
			assert.NoError(err)
		case <-time.After(time.Second):
			assert.FailNow("Acquire blocked unexpectedly")
		}
	}

	defer cs.Close()

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	var (
		ready  = make(chan struct{})
		result = make(chan error)
	)

	go func() {
		close(ready)
		result <- cs.AcquireWait(timer)
	}()

	select {
	case <-ready:
		timer <- time.Time{}
	case <-time.After(time.Second):
		require.FailNow("Unable to spawn acquire goroutine")
	}

	select {
	case err := <-result:
		assert.Equal(ErrTimeout, err)
	case <-time.After(time.Second):
		require.FailNow("AcquireWait blocked unexpectedly")
	}
}

func testCloseableAcquireWaitClose(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		timer   = make(chan time.Time)

		acquiredAll = make(chan struct{})
		results     = make(chan error, totalCount)
		closeWait   = make(chan struct{})
	)

	defer cs.Close()

	go func() {
		defer close(acquiredAll)
		for i := 0; i < totalCount; i++ {
			assert.NoError(cs.Acquire())
		}
	}()

	select {
	case <-acquiredAll:
		// passing
	case <-time.After(5 * time.Second):
		assert.FailNow("Unable to acquire all resources")
	}

	// acquire all the things!
	for i := 0; i < totalCount; i++ {
		ready := make(chan struct{})
		go func() {
			close(ready)
			results <- cs.AcquireWait(timer)
		}()

		select {
		case <-ready:
			// passing
		case <-time.After(5 * time.Second):
			assert.FailNow("Failed to spawn AcquireWait goroutine")
		}
	}

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	go func() {
		defer close(closeWait)
		<-cs.Closed()
	}()

	assert.NoError(cs.Close())
	for i := 0; i < totalCount; i++ {
		select {
		case err := <-results:
			assert.Equal(ErrClosed, err)
		case <-time.After(5 * time.Second):
			assert.FailNow("AcquireWait blocked unexpectedly")
		}
	}

	select {
	case <-closeWait:
		assert.False(cs.TryAcquire())
		assert.Equal(ErrClosed, cs.Close())
		assert.Equal(ErrClosed, cs.Acquire())
		assert.Equal(ErrClosed, cs.Release())

	case <-time.After(5 * time.Second):
		assert.FailNow("Closed channel did not get signaled")
	}
}

func testCloseableAcquireCtxSuccess(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert      = assert.New(t)
		require     = require.New(t)
		ctx, cancel = context.WithCancel(context.Background())
	)

	defer cancel()

	// acquire all the things!
	for i := 0; i < totalCount; i++ {
		result := make(chan error)
		go func() {
			result <- cs.AcquireCtx(ctx)
		}()

		select {
		case err := <-result:
			assert.NoError(err)
		case <-time.After(time.Second):
			assert.FailNow("Acquire blocked unexpectedly")
		}
	}

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	var (
		ready  = make(chan struct{})
		result = make(chan error)
	)

	go func() {
		close(ready)
		result <- cs.AcquireCtx(ctx)
	}()

	select {
	case <-ready:
		cancel()
	case <-time.After(time.Second):
		require.FailNow("Unable to spawn acquire goroutine")
	}

	select {
	case err := <-result:
		assert.Equal(ctx.Err(), err)
	case <-time.After(time.Second):
		require.FailNow("AcquireWait blocked unexpectedly")
	}
}

func testCloseableAcquireCtxClose(t *testing.T, cs Closeable, totalCount int) {
	var (
		assert      = assert.New(t)
		require     = require.New(t)
		ctx, cancel = context.WithCancel(context.Background())

		acquiredAll = make(chan struct{})
		results     = make(chan error, totalCount)
		closeWait   = make(chan struct{})
	)

	defer cancel()

	go func() {
		defer close(acquiredAll)
		for i := 0; i < totalCount; i++ {
			assert.NoError(cs.Acquire())
		}
	}()

	select {
	case <-acquiredAll:
		// passing
	case <-time.After(5 * time.Second):
		assert.FailNow("Unable to acquire all resources")
	}

	// acquire all the things!
	for i := 0; i < totalCount; i++ {
		ready := make(chan struct{})
		go func() {
			close(ready)
			results <- cs.AcquireCtx(ctx)
		}()

		select {
		case <-ready:
			// passing
		case <-time.After(5 * time.Second):
			assert.FailNow("Could not spawn AcquireCtx goroutine")
		}
	}

	// post condition: no point continuing if this fails
	require.False(cs.TryAcquire())

	go func() {
		defer close(closeWait)
		<-cs.Closed()
	}()

	assert.NoError(cs.Close())
	for i := 0; i < totalCount; i++ {
		select {
		case err := <-results:
			assert.Equal(ErrClosed, err)
		case <-time.After(5 * time.Second):
			assert.FailNow("AcquireCtx blocked unexpectedly")
		}
	}

	select {
	case <-closeWait:
		assert.False(cs.TryAcquire())
		assert.Equal(ErrClosed, cs.Close())
		assert.Equal(ErrClosed, cs.Acquire())
		assert.Equal(ErrClosed, cs.Release())

	case <-time.After(5 * time.Second):
		assert.FailNow("Closed channel did not get signaled")
	}
}

func TestCloseable(t *testing.T) {
	for _, c := range []int{1, 2, 5} {
		t.Run(fmt.Sprintf("count=%d", c), func(t *testing.T) {
			t.Run("TryAcquire", func(t *testing.T) {
				testCloseableTryAcquire(t, NewCloseable(c), c)
			})

			t.Run("Acquire", func(t *testing.T) {
				t.Run("Success", func(t *testing.T) {
					testCloseableAcquireSuccess(t, NewCloseable(c), c)
				})

				t.Run("Close", func(t *testing.T) {
					testCloseableAcquireClose(t, NewCloseable(c), c)
				})
			})

			t.Run("AcquireWait", func(t *testing.T) {
				t.Run("Success", func(t *testing.T) {
					testCloseableAcquireWaitSuccess(t, NewCloseable(c), c)
				})

				t.Run("Close", func(t *testing.T) {
					testCloseableAcquireWaitClose(t, NewCloseable(c), c)
				})
			})

			t.Run("AcquireCtx", func(t *testing.T) {
				t.Run("Success", func(t *testing.T) {
					testCloseableAcquireCtxSuccess(t, NewCloseable(c), c)
				})

				t.Run("Close", func(t *testing.T) {
					testCloseableAcquireCtxClose(t, NewCloseable(c), c)
				})
			})
		})
	}
}

func TestCloseableMutex(t *testing.T) {
	t.Run("TryAcquire", func(t *testing.T) {
		testCloseableTryAcquire(t, CloseableMutex(), 1)
	})

	t.Run("Acquire", func(t *testing.T) {
		t.Run("Success", func(t *testing.T) {
			testCloseableAcquireSuccess(t, CloseableMutex(), 1)
		})

		t.Run("Close", func(t *testing.T) {
			testCloseableAcquireClose(t, CloseableMutex(), 1)
		})
	})

	t.Run("AcquireWait", func(t *testing.T) {
		t.Run("Success", func(t *testing.T) {
			testCloseableAcquireWaitSuccess(t, CloseableMutex(), 1)
		})

		t.Run("Close", func(t *testing.T) {
			testCloseableAcquireWaitClose(t, CloseableMutex(), 1)
		})
	})

	t.Run("AcquireCtx", func(t *testing.T) {
		t.Run("Success", func(t *testing.T) {
			testCloseableAcquireCtxSuccess(t, CloseableMutex(), 1)
		})

		t.Run("Close", func(t *testing.T) {
			testCloseableAcquireCtxClose(t, CloseableMutex(), 1)
		})
	})
}
<|endoftext|>


<commit_before>package main

import "fmt"

type NodeId int
type EdgeId int

type Graph struct {
	nodes map[NodeId]*Node
	edges map[EdgeId]*Edge
}

func NewGraph() *Graph {
	return &Graph{
		nodes: make(map[NodeId]*Node),
		edges: make(map[EdgeId]*Edge),
	}
}

func (g *Graph) AddNode(node Node) {
	g.nodes[node.Id] = &node
}

func (g *Graph) GetNode(nodeId NodeId) Node {
	return *g.nodes[nodeId]
}

func (g *Graph) AddEdge(edge Edge) {
	g.edges[edge.Id] = &edge
}

func (g *Graph) String() string {
	return fmt.Sprintf("%+v\n %+v", g.nodes, g.edges)
}

type Node struct {
	Id NodeId
}

type Edge struct {
	Id   EdgeId
	Head NodeId
	Tail NodeId
}
<commit_msg>Removed pointers<commit_after>package main

import "fmt"

type NodeId int
type EdgeId int

type Graph struct {
	nodes map[NodeId]Node
	edges map[EdgeId]Edge
}

func NewGraph() *Graph {
	return &Graph{
		nodes: make(map[NodeId]Node),
		edges: make(map[EdgeId]Edge),
	}
}

func (g *Graph) AddNode(node Node) {
	g.nodes[node.Id] = node
}

func (g *Graph) GetNode(nodeId NodeId) Node {
	return g.nodes[nodeId]
}

func (g *Graph) AddEdge(edge Edge) {
	g.edges[edge.Id] = edge
}

func (g *Graph) String() string {
	return fmt.Sprintf("%+v\n %+v", g.nodes, g.edges)
}

type Node struct {
	Id NodeId
}

type Edge struct {
	Id   EdgeId
	Head NodeId
	Tail NodeId
}
<|endoftext|>


<commit_before>package x86_16

import (
	"fmt"
	"strings"
	"syscall"

	"github.com/lunixbochs/argjoy"
	"github.com/pkg/errors"

	co "github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/models"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
)

const (
	STACK_BASE = 0x8000
	STACK_SIZE = 0x1000
	NUM_FDS    = 256

	// Registers
	AH    = uc.X86_REG_AH
	AL    = uc.X86_REG_AL
	AX    = uc.X86_REG_AX
	BH    = uc.X86_REG_BH
	BL    = uc.X86_REG_BL
	BP    = uc.X86_REG_BP
	BX    = uc.X86_REG_BX
	CH    = uc.X86_REG_CH
	CL    = uc.X86_REG_CL
	CS    = uc.X86_REG_CS
	CX    = uc.X86_REG_CX
	DH    = uc.X86_REG_DH
	DI    = uc.X86_REG_DI
	DL    = uc.X86_REG_DL
	DS    = uc.X86_REG_DS
	DX    = uc.X86_REG_DX
	ES    = uc.X86_REG_ES
	FS    = uc.X86_REG_FS
	GS    = uc.X86_REG_GS
	IP    = uc.X86_REG_IP
	SI    = uc.X86_REG_SI
	SP    = uc.X86_REG_SP
	SS    = uc.X86_REG_SS
	FLAGS = uc.X86_REG_EFLAGS
)

func (k *DosKernel) reg16(enum int) uint16 {
	r, _ := k.U.RegRead(enum)
	return uint16(r)
}
func (k *DosKernel) reg8(enum int) uint8 {
	r, _ := k.U.RegRead(enum)
	return uint8(r)
}
func (k *DosKernel) wreg16(enum int, val uint16) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) wreg8(enum int, val uint8) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) setFlagC(set bool) {
	// TODO: Write setFlagX with enum for each flag
	// Unicorn doesn't have the non-extended FLAGS register, so we're
	// dealing with 32 bits here
	flags, _ := k.U.RegRead(FLAGS)
	if set {
		flags |= 1 // CF = 1
	} else {
		flags &= 0xfffffffe // CF = 0
	}
	k.U.RegWrite(FLAGS, flags)
}

var dosSysNum = map[int]string{
	0x00: "terminate",
	0x01: "char_in",
	0x02: "char_out",
	0x09: "display",
	0x30: "get_dos_version",
	0x3C: "create_or_truncate",
	0x3D: "open",
	0x3E: "close",
	0x3F: "read",
	0x40: "write",
	0x41: "unlink",
	0x4C: "terminate_with_code",
}

// TODO: Create a reverse map of this for conciseness
var abiMap = map[int][]int{
	0x00: {},
	0x01: {DX},
	0x02: {DX},
	0x09: {DX},
	0x30: {},
	0x3C: {DX, CX},
	0x3D: {DX, AL},
	0x3E: {BX},
	0x3F: {BX, DX, CX},
	0x40: {BX, DX, CX},
	0x41: {DX, CX},
	0x4C: {AL},
}

type PSP struct {
	CPMExit                     [2]byte
	FirstFreeSegment            uint16
	Reserved1                   uint8
	CPMCall5Compat              [5]byte
	OldTSRAddress               uint32
	OldBreakAddress             uint32
	CriticalErrorHandlerAddress uint32
	CallerPSPSegment            uint16
	JobFileTable                [20]byte
	EnvironmentSegment          uint16
	INT21SSSP                   uint32
	JobFileTableSize            uint16
	JobFileTablePointer         uint32
	PreviousPSP                 uint32
	Reserved2                   uint32
	DOSVersion                  uint16
	Reserved3                   [14]byte
	DOSFarCall                  [3]byte
	Reserved4                   uint16
	ExtendedFCB1                [7]byte
	FCB1                        [16]byte
	FCB2                        [20]byte
	CommandLineLength           uint8
	CommandLine                 [127]byte
}

type DosKernel struct {
	*co.KernelBase
	fds [NUM_FDS]int
}

func initPsp(argc int, argv []string) *PSP {
	psp := &PSP{
		CPMExit:    [2]byte{0xcd, 0x20},       // int 0x20
		DOSFarCall: [3]byte{0xcd, 0x21, 0xcd}, // int 0x21 + retf
	}

	psp.FCB1[0] = 0x01
	psp.FCB1[1] = 0x20

	// Combine all args into one string
	commandline := strings.Join(argv, " ")
	copy(psp.CommandLine[:126], commandline)
	if len(commandline) > 126 {
		psp.CommandLineLength = 126
	} else {
		psp.CommandLineLength = uint8(len(commandline))
	}

	return psp
}

func (k *DosKernel) readUntilChar(addr uint64, c byte) []byte {
	var mem []byte
	var i uint64
	var char byte = 0

	// TODO: Read ahead? This'll be slow
	for i = 1; char != c || i == 1; i++ {
		mem, _ = k.U.MemRead(addr, i)
		char = mem[i-1]
	}
	return mem[:i-2]
}

func (k *DosKernel) getFd(fd int) (uint16, error) {
	for i := uint16(0); i < NUM_FDS; i++ {
		if k.fds[i] == -1 {
			k.fds[i] = fd
			return i, nil
		}
	}
	return 0xFFFF, errors.New("DOS FD table exhausted")
}

func (k *DosKernel) freeFd(fd int) (int, error) {
	realfd := k.fds[fd]
	if realfd == -1 {
		return 0xFFFF, errors.New("FD not found in FD table")
	}
	k.fds[fd] = -1
	return realfd, nil
}

func (k *DosKernel) Terminate() {
	k.U.Exit(models.ExitStatus(0))
}

func (k *DosKernel) CharIn(buf co.Buf) byte {
	var char byte
	fmt.Scanf("%c", &char)
	k.U.MemWrite(buf.Addr, []byte{char})
	return char
}

func (k *DosKernel) CharOut(char uint16) byte {
	fmt.Printf("%c", byte(char&0xFF))
	return byte(char & 0xFF)
}

func (k *DosKernel) Display(buf co.Buf) int {
	mem := k.readUntilChar(buf.Addr, '$')

	syscall.Write(1, mem)
	k.wreg8(AL, 0x24)
	return 0x24
}

func (k *DosKernel) GetDosVersion() int {
	k.wreg16(AX, 0x7)
	return 0x7
}

func (k *DosKernel) openFile(filename string, mode int) uint16 {
	realfd, err := syscall.Open(filename, mode, 0)
	if err != nil {
		k.wreg16(AX, 0xFFFF)
		k.setFlagC(true)
		return 0xFFFF
	}

	// Find an internal fd number
	dosfd, err := k.getFd(realfd)
	if err != nil {
		k.wreg16(AX, dosfd)
		k.setFlagC(true)
		return 0xFFFF
	}
	k.setFlagC(false)
	k.wreg16(AX, dosfd)
	return dosfd
}

func (k *DosKernel) CreateOrTruncate(buf co.Buf, attr int) uint16 {
	filename := string(k.readUntilChar(buf.Addr, '$'))
	return k.openFile(filename, syscall.O_CREAT|syscall.O_TRUNC|syscall.O_RDWR)
}

func (k *DosKernel) Open(filename string, mode int) uint16 {
	return k.openFile(filename, mode)
}

func (k *DosKernel) Close(fd int) {
	// Find and free the internal fd
	realfd, _ := k.freeFd(fd)
	err := syscall.Close(realfd)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, 0)
}

func (k *DosKernel) Read(fd int, buf co.Obuf, len co.Len) int {
	mem := make([]byte, len)
	n, err := syscall.Read(fd, mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.U.MemWrite(buf.Addr, mem)
	k.setFlagC(false)
	k.wreg16(AX, uint16(n))
	return n
}

func (k *DosKernel) Write(fd uint, buf co.Buf, n co.Len) int {
	mem, _ := k.U.MemRead(buf.Addr, uint64(n))
	written, err := syscall.Write(k.fds[fd], mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, uint16(written))
	return written
}

func (k *DosKernel) Unlink(filename string, attr int) int {
	err := syscall.Unlink(filename)
	if err != nil {
		k.setFlagC(true)
		k.wreg16(AX, 0xFFFF)
		return 0xFFFF
	}
	k.setFlagC(false)
	k.wreg16(AX, 0)
	return 0
}

func (k *DosKernel) TerminateWithCode(code int) {
	k.U.Exit(models.ExitStatus(code))
}

func NewKernel() *DosKernel {
	k := &DosKernel{
		KernelBase: &co.KernelBase{},
	}

	// Init FDs
	for i := 0; i < NUM_FDS; i++ {
		k.fds[i] = -1
	}
	k.fds[0] = 0
	k.fds[1] = 1
	k.fds[2] = 2

	k.Argjoy.Register(k.getDosArgCodec())
	return k
}

func DosInit(u models.Usercorn, args, env []string) error {
	// Setup PSP
	// TODO: Setup args
	psp := initPsp(0, nil)
	u.StrucAt(0).Pack(psp)

	// Setup stack
	u.RegWrite(u.Arch().SP, STACK_BASE+STACK_SIZE)
	u.SetStackBase(STACK_BASE)
	u.SetStackSize(STACK_SIZE)
	u.SetEntry(0x100)
	return nil
}

func DosSyscall(u models.Usercorn) {
	num, _ := u.RegRead(AH)
	name, _ := dosSysNum[int(num)]
	// TODO: How are registers numbered from here?
	u.Syscall(int(num), name, dosArgs(u, int(num)))
	// TODO: Set error
}

func (k *DosKernel) getDosArgCodec() func(interface{}, []interface{}) error {
	return func(arg interface{}, vals []interface{}) error {
		// DOS takes address as DS+DX
		if reg, ok := vals[0].(uint64); ok && len(vals) > 1 {
			ds, _ := k.U.RegRead(DS)
			reg += ds
			switch v := arg.(type) {
			case *co.Buf:
				*v = co.NewBuf(k, reg)
			case *co.Obuf:
				*v = co.Obuf{co.NewBuf(k, reg)}
			case *co.Ptr:
				*v = co.Ptr(reg)
			case *string:
				s, err := k.U.Mem().ReadStrAt(reg)
				if err != nil {
					return errors.Wrapf(err, "ReadStrAt(%#x) failed", reg)
				}
				*v = s
			default:
				return argjoy.NoMatch
			}
			return nil
		}
		return argjoy.NoMatch
	}
}

func dosArgs(u models.Usercorn, num int) func(int) ([]uint64, error) {
	return co.RegArgs(u, abiMap[num])
}

func DosInterrupt(u models.Usercorn, cause uint32) {
	intno := cause & 0xFF
	if intno == 0x21 {
		DosSyscall(u)
	} else if intno == 0x20 {
		u.Syscall(0, "terminate", func(int) ([]uint64, error) { return []uint64{}, nil })
	} else {
		panic(fmt.Sprintf("unhandled X86 interrupt %#X", intno))
	}
}
func DosKernels(u models.Usercorn) []interface{} {
	return []interface{}{NewKernel()}
}

func init() {
	Arch.RegisterOS(&models.OS{
		Name:      "DOS",
		Init:      DosInit,
		Interrupt: DosInterrupt,
		Kernels:   DosKernels,
	})
}
<commit_msg>DOS: Use FD type where appropriate<commit_after>package x86_16

import (
	"fmt"
	"strings"
	"syscall"

	"github.com/lunixbochs/argjoy"
	"github.com/pkg/errors"

	co "github.com/lunixbochs/usercorn/go/kernel/common"
	"github.com/lunixbochs/usercorn/go/models"
	uc "github.com/unicorn-engine/unicorn/bindings/go/unicorn"
)

const (
	STACK_BASE = 0x8000
	STACK_SIZE = 0x1000
	NUM_FDS    = 256

	// Registers
	AH    = uc.X86_REG_AH
	AL    = uc.X86_REG_AL
	AX    = uc.X86_REG_AX
	BH    = uc.X86_REG_BH
	BL    = uc.X86_REG_BL
	BP    = uc.X86_REG_BP
	BX    = uc.X86_REG_BX
	CH    = uc.X86_REG_CH
	CL    = uc.X86_REG_CL
	CS    = uc.X86_REG_CS
	CX    = uc.X86_REG_CX
	DH    = uc.X86_REG_DH
	DI    = uc.X86_REG_DI
	DL    = uc.X86_REG_DL
	DS    = uc.X86_REG_DS
	DX    = uc.X86_REG_DX
	ES    = uc.X86_REG_ES
	FS    = uc.X86_REG_FS
	GS    = uc.X86_REG_GS
	IP    = uc.X86_REG_IP
	SI    = uc.X86_REG_SI
	SP    = uc.X86_REG_SP
	SS    = uc.X86_REG_SS
	FLAGS = uc.X86_REG_EFLAGS
)

func (k *DosKernel) reg16(enum int) uint16 {
	r, _ := k.U.RegRead(enum)
	return uint16(r)
}
func (k *DosKernel) reg8(enum int) uint8 {
	r, _ := k.U.RegRead(enum)
	return uint8(r)
}
func (k *DosKernel) wreg16(enum int, val uint16) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) wreg8(enum int, val uint8) {
	k.U.RegWrite(enum, uint64(val))
}
func (k *DosKernel) setFlagC(set bool) {
	// TODO: Write setFlagX with enum for each flag
	// Unicorn doesn't have the non-extended FLAGS register, so we're
	// dealing with 32 bits here
	flags, _ := k.U.RegRead(FLAGS)
	if set {
		flags |= 1 // CF = 1
	} else {
		flags &= 0xfffffffe // CF = 0
	}
	k.U.RegWrite(FLAGS, flags)
}

var dosSysNum = map[int]string{
	0x00: "terminate",
	0x01: "char_in",
	0x02: "char_out",
	0x09: "display",
	0x30: "get_dos_version",
	0x3C: "create_or_truncate",
	0x3D: "open",
	0x3E: "close",
	0x3F: "read",
	0x40: "write",
	0x41: "unlink",
	0x4C: "terminate_with_code",
}

// TODO: Create a reverse map of this for conciseness
var abiMap = map[int][]int{
	0x00: {},
	0x01: {DX},
	0x02: {DX},
	0x09: {DX},
	0x30: {},
	0x3C: {DX, CX},
	0x3D: {DX, AL},
	0x3E: {BX},
	0x3F: {BX, DX, CX},
	0x40: {BX, DX, CX},
	0x41: {DX, CX},
	0x4C: {AL},
}

type PSP struct {
	CPMExit                     [2]byte
	FirstFreeSegment            uint16
	Reserved1                   uint8
	CPMCall5Compat              [5]byte
	OldTSRAddress               uint32
	OldBreakAddress             uint32
	CriticalErrorHandlerAddress uint32
	CallerPSPSegment            uint16
	JobFileTable                [20]byte
	EnvironmentSegment          uint16
	INT21SSSP                   uint32
	JobFileTableSize            uint16
	JobFileTablePointer         uint32
	PreviousPSP                 uint32
	Reserved2                   uint32
	DOSVersion                  uint16
	Reserved3                   [14]byte
	DOSFarCall                  [3]byte
	Reserved4                   uint16
	ExtendedFCB1                [7]byte
	FCB1                        [16]byte
	FCB2                        [20]byte
	CommandLineLength           uint8
	CommandLine                 [127]byte
}

type DosKernel struct {
	*co.KernelBase
	fds [NUM_FDS]int
}

func initPsp(argc int, argv []string) *PSP {
	psp := &PSP{
		CPMExit:    [2]byte{0xcd, 0x20},       // int 0x20
		DOSFarCall: [3]byte{0xcd, 0x21, 0xcd}, // int 0x21 + retf
	}

	psp.FCB1[0] = 0x01
	psp.FCB1[1] = 0x20

	// Combine all args into one string
	commandline := strings.Join(argv, " ")
	copy(psp.CommandLine[:126], commandline)
	if len(commandline) > 126 {
		psp.CommandLineLength = 126
	} else {
		psp.CommandLineLength = uint8(len(commandline))
	}

	return psp
}

func (k *DosKernel) readUntilChar(addr uint64, c byte) []byte {
	var mem []byte
	var i uint64
	var char byte = 0

	// TODO: Read ahead? This'll be slow
	for i = 1; char != c || i == 1; i++ {
		mem, _ = k.U.MemRead(addr, i)
		char = mem[i-1]
	}
	return mem[:i-2]
}

func (k *DosKernel) getFd(fd int) (uint16, error) {
	for i := uint16(0); i < NUM_FDS; i++ {
		if k.fds[i] == -1 {
			k.fds[i] = fd
			return i, nil
		}
	}
	return 0xFFFF, errors.New("DOS FD table exhausted")
}

func (k *DosKernel) freeFd(fd int) (int, error) {
	realfd := k.fds[fd]
	if realfd == -1 {
		return 0xFFFF, errors.New("FD not found in FD table")
	}
	k.fds[fd] = -1
	return realfd, nil
}

func (k *DosKernel) Terminate() {
	k.U.Exit(models.ExitStatus(0))
}

func (k *DosKernel) CharIn(buf co.Buf) byte {
	var char byte
	fmt.Scanf("%c", &char)
	k.U.MemWrite(buf.Addr, []byte{char})
	return char
}

func (k *DosKernel) CharOut(char uint16) byte {
	fmt.Printf("%c", byte(char&0xFF))
	return byte(char & 0xFF)
}

func (k *DosKernel) Display(buf co.Buf) int {
	mem := k.readUntilChar(buf.Addr, '$')

	syscall.Write(1, mem)
	k.wreg8(AL, 0x24)
	return 0x24
}

func (k *DosKernel) GetDosVersion() int {
	k.wreg16(AX, 0x7)
	return 0x7
}

func (k *DosKernel) openFile(filename string, mode int) co.Fd {
	realfd, err := syscall.Open(filename, mode, 0)
	if err != nil {
		k.wreg16(AX, 0xFFFF)
		k.setFlagC(true)
		return 0xFFFF
	}

	// Find an internal fd number
	dosfd, err := k.getFd(realfd)
	if err != nil {
		k.wreg16(AX, dosfd)
		k.setFlagC(true)
		return 0xFFFF
	}
	k.setFlagC(false)
	k.wreg16(AX, dosfd)
	return co.Fd(dosfd)
}

func (k *DosKernel) CreateOrTruncate(buf co.Buf, attr int) co.Fd {
	filename := string(k.readUntilChar(buf.Addr, '$'))
	return k.openFile(filename, syscall.O_CREAT|syscall.O_TRUNC|syscall.O_RDWR)
}

func (k *DosKernel) Open(filename string, mode int) co.Fd {
	return k.openFile(filename, mode)
}

func (k *DosKernel) Close(fd co.Fd) {
	// Find and free the internal fd
	realfd, _ := k.freeFd(int(fd))
	err := syscall.Close(realfd)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, 0)
}

func (k *DosKernel) Read(fd co.Fd, buf co.Obuf, len co.Len) int {
	mem := make([]byte, len)
	n, err := syscall.Read(int(fd), mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.U.MemWrite(buf.Addr, mem)
	k.setFlagC(false)
	k.wreg16(AX, uint16(n))
	return n
}

func (k *DosKernel) Write(fd co.Fd, buf co.Buf, n co.Len) int {
	mem, _ := k.U.MemRead(buf.Addr, uint64(n))
	written, err := syscall.Write(k.fds[fd], mem)
	if err != nil {
		k.setFlagC(true)
		// TODO: Set AX to error code
		k.wreg16(AX, 0xFFFF)
	}
	k.setFlagC(false)
	k.wreg16(AX, uint16(written))
	return written
}

func (k *DosKernel) Unlink(filename string, attr int) int {
	err := syscall.Unlink(filename)
	if err != nil {
		k.setFlagC(true)
		k.wreg16(AX, 0xFFFF)
		return 0xFFFF
	}
	k.setFlagC(false)
	k.wreg16(AX, 0)
	return 0
}

func (k *DosKernel) TerminateWithCode(code int) {
	k.U.Exit(models.ExitStatus(code))
}

func NewKernel() *DosKernel {
	k := &DosKernel{
		KernelBase: &co.KernelBase{},
	}

	// Init FDs
	for i := 0; i < NUM_FDS; i++ {
		k.fds[i] = -1
	}
	k.fds[0] = 0
	k.fds[1] = 1
	k.fds[2] = 2

	k.Argjoy.Register(k.getDosArgCodec())
	return k
}

func DosInit(u models.Usercorn, args, env []string) error {
	// Setup PSP
	// TODO: Setup args
	psp := initPsp(0, nil)
	u.StrucAt(0).Pack(psp)

	// Setup stack
	u.RegWrite(u.Arch().SP, STACK_BASE+STACK_SIZE)
	u.SetStackBase(STACK_BASE)
	u.SetStackSize(STACK_SIZE)
	u.SetEntry(0x100)
	return nil
}

func DosSyscall(u models.Usercorn) {
	num, _ := u.RegRead(AH)
	name, _ := dosSysNum[int(num)]
	// TODO: How are registers numbered from here?
	u.Syscall(int(num), name, dosArgs(u, int(num)))
	// TODO: Set error
}

func (k *DosKernel) getDosArgCodec() func(interface{}, []interface{}) error {
	return func(arg interface{}, vals []interface{}) error {
		// DOS takes address as DS+DX
		if reg, ok := vals[0].(uint64); ok && len(vals) > 1 {
			ds, _ := k.U.RegRead(DS)
			reg += ds
			switch v := arg.(type) {
			case *co.Buf:
				*v = co.NewBuf(k, reg)
			case *co.Obuf:
				*v = co.Obuf{co.NewBuf(k, reg)}
			case *co.Ptr:
				*v = co.Ptr(reg)
			case *string:
				s, err := k.U.Mem().ReadStrAt(reg)
				if err != nil {
					return errors.Wrapf(err, "ReadStrAt(%#x) failed", reg)
				}
				*v = s
			default:
				return argjoy.NoMatch
			}
			return nil
		}
		return argjoy.NoMatch
	}
}

func dosArgs(u models.Usercorn, num int) func(int) ([]uint64, error) {
	return co.RegArgs(u, abiMap[num])
}

func DosInterrupt(u models.Usercorn, cause uint32) {
	intno := cause & 0xFF
	if intno == 0x21 {
		DosSyscall(u)
	} else if intno == 0x20 {
		u.Syscall(0, "terminate", func(int) ([]uint64, error) { return []uint64{}, nil })
	} else {
		panic(fmt.Sprintf("unhandled X86 interrupt %#X", intno))
	}
}
func DosKernels(u models.Usercorn) []interface{} {
	return []interface{}{NewKernel()}
}

func init() {
	Arch.RegisterOS(&models.OS{
		Name:      "DOS",
		Init:      DosInit,
		Interrupt: DosInterrupt,
		Kernels:   DosKernels,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"sample/sampleworld"

	"v.io/v23"

	"v.io/x/ref/lib/expect"
	"v.io/x/ref/lib/flags/consts"
	"v.io/x/ref/lib/modules"
	"v.io/x/ref/lib/modules/core"
	"v.io/x/ref/lib/signals"
	"v.io/x/ref/profiles"
)

const (
	SampleWorldCommand = "sampleWorld"           // The modules library command.
	stdoutLog          = "tmp/runner.stdout.log" // Used as stdout drain when shutting down.
	stderrLog          = "tmp/runner.stderr.log" // Used as stderr drain when shutting down.
)

var (
	// Flags used as input to this program.
	runSample     bool
	serveHTTP     bool
	portHTTP      string
	rootHTTP      string
	runTests      bool
	runTestsWatch bool
)

func init() {
	modules.RegisterChild(SampleWorldCommand, "desc", sampleWorld)
	flag.BoolVar(&runSample, "runSample", false, "if true, runs sample services")
	flag.BoolVar(&serveHTTP, "serveHTTP", false, "if true, serves HTTP")
	flag.StringVar(&portHTTP, "portHTTP", "9001", "default 9001, the port to serve HTTP on")
	flag.StringVar(&rootHTTP, "rootHTTP", ".", "default '.', the root HTTP folder path")
	flag.BoolVar(&runTests, "runTests", false, "if true, runs the namespace browser tests")
	flag.BoolVar(&runTestsWatch, "runTestsWatch", false, "if true && runTests, runs the tests in watch mode")
}

// Helper function to simply panic on error.
func panicOnError(err error) {
	if err != nil {
		panic(err)
	}
}

// updateVars captures the vars from the given Handle's stdout and adds them to
// the given vars map, overwriting existing entries.
func updateVars(h modules.Handle, vars map[string]string, varNames ...string) error {
	varsToAdd := map[string]bool{}
	for _, v := range varNames {
		varsToAdd[v] = true
	}
	numLeft := len(varsToAdd)

	s := expect.NewSession(nil, h.Stdout(), 30*time.Second)
	for {
		l := s.ReadLine()
		if err := s.OriginalError(); err != nil {
			return err // EOF or otherwise
		}
		parts := strings.Split(l, "=")
		if len(parts) != 2 {
			return fmt.Errorf("Unexpected line: %s", l)
		}
		if _, ok := varsToAdd[parts[0]]; ok {
			numLeft--
			vars[parts[0]] = parts[1]
			if numLeft == 0 {
				break
			}
		}
	}
	return nil
}

// The module command for running the sample world.
func sampleWorld(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
	ctx, shutdown := v23.Init()
	defer shutdown()

	sampleworld.RunSampleWorld(ctx)

	modules.WaitForEOF(stdin)
	return nil
}

func main() {
	if modules.IsModulesProcess() {
		panicOnError(modules.Dispatch())
		return
	}

	// Try running the program; on failure, exit with error status code.
	if !run() {
		os.Exit(1)
	}
}

// Runs the services and cleans up afterwards.
// Returns true if the run was successful.
func run() bool {
	ctx, shutdown := v23.Init()
	defer shutdown()

	// In order to prevent conflicts, tests and webapp use different mounttable ports.
	port := 5180
	cottagePort := 5181
	housePort := 5182
	if runTests {
		port = 8884
		cottagePort = 8885
		housePort = 8886
	}

	// Start a new shell module.
	vars := map[string]string{}
	sh, err := modules.NewShell(ctx, nil)
	if err != nil {
		panic(fmt.Sprintf("modules.NewShell: %s", err))
	}

	// Collect the output of this shell on termination.
	err = os.MkdirAll("tmp", 0750)
	panicOnError(err)
	outFile, err := os.Create(stdoutLog)
	panicOnError(err)
	defer outFile.Close()
	errFile, err := os.Create(stderrLog)
	panicOnError(err)
	defer errFile.Close()
	defer sh.Cleanup(outFile, errFile)

	// Determine the hostname; this name will be used for mounting.
	hostName, err := exec.Command("hostname", "-s").Output()
	panicOnError(err)

	// Run the host mounttable.
	rootName := fmt.Sprintf("%s-home", strings.TrimSpace(string(hostName))) // Must trim; hostname has \n at the end.
	hRoot, err := sh.Start(core.MTCommand, nil, "--veyron.tcp.protocol=ws", fmt.Sprintf("--veyron.tcp.address=127.0.0.1:%d", port), rootName)
	panicOnError(err)
	panicOnError(updateVars(hRoot, vars, "MT_NAME"))
	defer hRoot.Shutdown(outFile, errFile)

	// Set consts.NamespaceRootPrefix env var, consumed downstream.
	sh.SetVar(consts.NamespaceRootPrefix, vars["MT_NAME"])
	v23.GetNamespace(ctx).SetRoots(vars["MT_NAME"])

	// Run the cottage mounttable at host/cottage.
	hCottage, err := sh.Start(core.MTCommand, nil, "--veyron.tcp.protocol=ws", fmt.Sprintf("--veyron.tcp.address=127.0.0.1:%d", cottagePort), "cottage")
	panicOnError(err)
	expect.NewSession(nil, hCottage.Stdout(), 30*time.Second)
	defer hCottage.Shutdown(outFile, errFile)

	// run the house mounttable at host/house.
	hHouse, err := sh.Start(core.MTCommand, nil, "--veyron.tcp.protocol=ws", fmt.Sprintf("--veyron.tcp.address=127.0.0.1:%d", housePort), "house")
	panicOnError(err)
	expect.NewSession(nil, hHouse.Stdout(), 30*time.Second)
	defer hHouse.Shutdown(outFile, errFile)

	// Possibly run the sample world.
	if runSample {
		fmt.Println("Running Sample World")
		hSample, err := sh.Start(SampleWorldCommand, nil, "--veyron.tcp.protocol=ws", "--veyron.tcp.address=127.0.0.1:0")
		panicOnError(err)
		expect.NewSession(nil, hSample.Stdout(), 30*time.Second)
		defer hSample.Shutdown(outFile, errFile)
	}

	// Possibly serve the public bundle at the portHTTP.
	if serveHTTP {
		fmt.Printf("Also serving HTTP at %s for %s\n", portHTTP, rootHTTP)
		http.ListenAndServe(":"+portHTTP, http.FileServer(http.Dir(rootHTTP)))
	}

	// Just print out the collected variables. This is for debugging purposes.
	bytes, err := json.Marshal(vars)
	panicOnError(err)
	fmt.Println(string(bytes))

	// Possibly run the tests in Prova.
	if runTests {
		// Also set HOUSE_MOUNTTABLE (used in the tests)
		os.Setenv("HOUSE_MOUNTTABLE", fmt.Sprintf("/127.0.0.1:%d", housePort))

		proxyShutdown, proxyEndpoint, err := profiles.NewProxy(ctx, "ws", "127.0.0.1:0", "", "test/proxy")
		panicOnError(err)
		defer proxyShutdown()
		vars["PROXY_NAME"] = proxyEndpoint.Name()

		hIdentityd, err := sh.Start(core.TestIdentitydCommand, nil, "--veyron.tcp.protocol=ws", "--veyron.tcp.address=127.0.0.1:0", "--veyron.proxy=test/proxy", "--host=localhost", "--httpaddr=localhost:0")
		panicOnError(err)
		panicOnError(updateVars(hIdentityd, vars, "TEST_IDENTITYD_NAME", "TEST_IDENTITYD_HTTP_ADDR"))
		defer hIdentityd.Shutdown(outFile, errFile)

		// Setup a lot of environment variables; these are used for the tests and building the test extension.
		os.Setenv("NAMESPACE_ROOT", vars["MT_NAME"])
		os.Setenv("PROXY_ADDR", vars["PROXY_NAME"])
		os.Setenv("IDENTITYD", fmt.Sprintf("%s/google", vars["TEST_IDENTITYD_NAME"]))
		os.Setenv("IDENTITYD_BLESSING_URL", fmt.Sprintf("%s/blessing-root", vars["TEST_IDENTITYD_HTTP_ADDR"]))
		os.Setenv("DEBUG", "false")

		testsOk := runProva()

		fmt.Println("Cleaning up launched services...")
		return testsOk
	}

	// Not in a test, so run until the program is killed.
	<-signals.ShutdownOnSignals(ctx)
	return true

}

// Run the prova tests and convert its tap output to xunit.
func runProva() bool {
	// This is also useful information for routing the test output.
	VANADIUM_ROOT := os.Getenv("VANADIUM_ROOT")
	VANADIUM_JS := fmt.Sprintf("%s/release/javascript/core", VANADIUM_ROOT)
	VANADIUM_BROWSER := fmt.Sprintf("%s/release/projects/namespace_browser", VANADIUM_ROOT)

	TAP_XUNIT := fmt.Sprintf("%s/node_modules/.bin/tap-xunit", VANADIUM_BROWSER)
	XUNIT_OUTPUT_FILE := os.Getenv("XUNIT_OUTPUT_FILE")
	if XUNIT_OUTPUT_FILE == "" {
		XUNIT_OUTPUT_FILE = fmt.Sprintf("%s/test_output.xml", os.Getenv("TMPDIR"))
	}
	TAP_XUNIT_OPTIONS := " --package=namespace-browser"

	// Make sure we're in the right folder when we run make test-extension.
	vbroot, err := os.Open(VANADIUM_BROWSER)
	panicOnError(err)
	err = vbroot.Chdir()
	panicOnError(err)

	// Make the test-extension, this should also remove the old one.
	fmt.Println("Rebuilding test extension...")
	cmdExtensionClean := exec.Command("rm", "-fr", fmt.Sprintf("%s/extension/build-test", VANADIUM_JS))
	err = cmdExtensionClean.Run()
	panicOnError(err)
	cmdExtensionBuild := exec.Command("make", "-C", fmt.Sprintf("%s/extension", VANADIUM_JS), "build-test")
	err = cmdExtensionBuild.Run()
	panicOnError(err)

	// These are the basic prova options.
	options := []string{
		"test/**/*.js",
		"--browser",
		"--includeFilenameAsPackage",
		"--launch",
		"chrome",
		"--plugin",
		"proxyquireify/plugin",
		"--transform",
		"envify,./main-transform",
		"--log",
		"tmp/chrome.log",
		fmt.Sprintf("--options=--load-extension=%s/extension/build-test/,--ignore-certificate-errors,--enable-logging=stderr", VANADIUM_JS),
	}

	// Normal tests have a few more options and a different port from the watch tests.
	var PROVA_PORT int
	if !runTestsWatch {
		PROVA_PORT = 8893
		options = append(options, "--headless", "--quit", "--progress", "--tap")
		fmt.Printf("\033[34m-Executing tests. See %s for test xunit output.\033[0m\n", XUNIT_OUTPUT_FILE)
	} else {
		PROVA_PORT = 8894
		fmt.Println("\033[34m-Running tests in watch mode.\033[0m")
	}
	options = append(options, "--port", fmt.Sprintf("%d", PROVA_PORT))

	// This is the prova command.
	cmdProva := exec.Command(
		fmt.Sprintf("%s/node_modules/.bin/prova", VANADIUM_BROWSER),
		options...,
	)
	fmt.Printf("\033[34m-Go to \033[32mhttp://0.0.0.0:%d\033[34m to see tests running.\033[0m\n", PROVA_PORT)
	fmt.Println(cmdProva)

	// Collect the prova stdout. This information needs to be sent to xunit.
	provaOut, err := cmdProva.StdoutPipe()
	panicOnError(err)

	// Setup the tap to xunit command. It uses Prova's stdout as input.
	// The output will got the xunit output file.
	cmdTap := exec.Command(TAP_XUNIT, TAP_XUNIT_OPTIONS)
	cmdTap.Stdin = io.TeeReader(provaOut, os.Stdout) // Tee the prova output to see it on the console too.
	outfile, err := os.Create(XUNIT_OUTPUT_FILE)
	panicOnError(err)
	defer outfile.Close()
	bufferedWriter := bufio.NewWriter(outfile)
	cmdTap.Stdout = bufferedWriter
	defer bufferedWriter.Flush() // Ensure that the full xunit output is written.

	// We start the tap command...
	err = cmdTap.Start()
	panicOnError(err)

	// Meanwhile, run Prova to completion. If there was an error, print ERROR, otherwise PASS.
	err = cmdProva.Run()
	testsOk := true
	if err != nil {
		fmt.Println(err)
		fmt.Println("\033[31m\033[1mERROR\033[0m")
		testsOk = false
	} else {
		fmt.Println("\033[32m\033[1mPASS\033[0m")
	}

	// Wait for tap to xunit to finish itself off. This file will be ready for reading by Jenkins.
	fmt.Println("Converting Tap output to XUnit")
	err = cmdTap.Wait()
	panicOnError(err)

	return testsOk
}
<commit_msg>namespace_browser: devtools: Move many libraries from v.io/x/ref/lib to v.io/x/ref/profiles/internal.<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"sample/sampleworld"

	"v.io/v23"

	"v.io/x/ref/lib/flags/consts"
	"v.io/x/ref/lib/modules"
	"v.io/x/ref/lib/modules/core"
	"v.io/x/ref/lib/signals"
	"v.io/x/ref/lib/testutil/expect"
	"v.io/x/ref/profiles"
)

const (
	SampleWorldCommand = "sampleWorld"           // The modules library command.
	stdoutLog          = "tmp/runner.stdout.log" // Used as stdout drain when shutting down.
	stderrLog          = "tmp/runner.stderr.log" // Used as stderr drain when shutting down.
)

var (
	// Flags used as input to this program.
	runSample     bool
	serveHTTP     bool
	portHTTP      string
	rootHTTP      string
	runTests      bool
	runTestsWatch bool
)

func init() {
	modules.RegisterChild(SampleWorldCommand, "desc", sampleWorld)
	flag.BoolVar(&runSample, "runSample", false, "if true, runs sample services")
	flag.BoolVar(&serveHTTP, "serveHTTP", false, "if true, serves HTTP")
	flag.StringVar(&portHTTP, "portHTTP", "9001", "default 9001, the port to serve HTTP on")
	flag.StringVar(&rootHTTP, "rootHTTP", ".", "default '.', the root HTTP folder path")
	flag.BoolVar(&runTests, "runTests", false, "if true, runs the namespace browser tests")
	flag.BoolVar(&runTestsWatch, "runTestsWatch", false, "if true && runTests, runs the tests in watch mode")
}

// Helper function to simply panic on error.
func panicOnError(err error) {
	if err != nil {
		panic(err)
	}
}

// updateVars captures the vars from the given Handle's stdout and adds them to
// the given vars map, overwriting existing entries.
func updateVars(h modules.Handle, vars map[string]string, varNames ...string) error {
	varsToAdd := map[string]bool{}
	for _, v := range varNames {
		varsToAdd[v] = true
	}
	numLeft := len(varsToAdd)

	s := expect.NewSession(nil, h.Stdout(), 30*time.Second)
	for {
		l := s.ReadLine()
		if err := s.OriginalError(); err != nil {
			return err // EOF or otherwise
		}
		parts := strings.Split(l, "=")
		if len(parts) != 2 {
			return fmt.Errorf("Unexpected line: %s", l)
		}
		if _, ok := varsToAdd[parts[0]]; ok {
			numLeft--
			vars[parts[0]] = parts[1]
			if numLeft == 0 {
				break
			}
		}
	}
	return nil
}

// The module command for running the sample world.
func sampleWorld(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
	ctx, shutdown := v23.Init()
	defer shutdown()

	sampleworld.RunSampleWorld(ctx)

	modules.WaitForEOF(stdin)
	return nil
}

func main() {
	if modules.IsModulesProcess() {
		panicOnError(modules.Dispatch())
		return
	}

	// Try running the program; on failure, exit with error status code.
	if !run() {
		os.Exit(1)
	}
}

// Runs the services and cleans up afterwards.
// Returns true if the run was successful.
func run() bool {
	ctx, shutdown := v23.Init()
	defer shutdown()

	// In order to prevent conflicts, tests and webapp use different mounttable ports.
	port := 5180
	cottagePort := 5181
	housePort := 5182
	if runTests {
		port = 8884
		cottagePort = 8885
		housePort = 8886
	}

	// Start a new shell module.
	vars := map[string]string{}
	sh, err := modules.NewShell(ctx, nil)
	if err != nil {
		panic(fmt.Sprintf("modules.NewShell: %s", err))
	}

	// Collect the output of this shell on termination.
	err = os.MkdirAll("tmp", 0750)
	panicOnError(err)
	outFile, err := os.Create(stdoutLog)
	panicOnError(err)
	defer outFile.Close()
	errFile, err := os.Create(stderrLog)
	panicOnError(err)
	defer errFile.Close()
	defer sh.Cleanup(outFile, errFile)

	// Determine the hostname; this name will be used for mounting.
	hostName, err := exec.Command("hostname", "-s").Output()
	panicOnError(err)

	// Run the host mounttable.
	rootName := fmt.Sprintf("%s-home", strings.TrimSpace(string(hostName))) // Must trim; hostname has \n at the end.
	hRoot, err := sh.Start(core.MTCommand, nil, "--veyron.tcp.protocol=ws", fmt.Sprintf("--veyron.tcp.address=127.0.0.1:%d", port), rootName)
	panicOnError(err)
	panicOnError(updateVars(hRoot, vars, "MT_NAME"))
	defer hRoot.Shutdown(outFile, errFile)

	// Set consts.NamespaceRootPrefix env var, consumed downstream.
	sh.SetVar(consts.NamespaceRootPrefix, vars["MT_NAME"])
	v23.GetNamespace(ctx).SetRoots(vars["MT_NAME"])

	// Run the cottage mounttable at host/cottage.
	hCottage, err := sh.Start(core.MTCommand, nil, "--veyron.tcp.protocol=ws", fmt.Sprintf("--veyron.tcp.address=127.0.0.1:%d", cottagePort), "cottage")
	panicOnError(err)
	expect.NewSession(nil, hCottage.Stdout(), 30*time.Second)
	defer hCottage.Shutdown(outFile, errFile)

	// run the house mounttable at host/house.
	hHouse, err := sh.Start(core.MTCommand, nil, "--veyron.tcp.protocol=ws", fmt.Sprintf("--veyron.tcp.address=127.0.0.1:%d", housePort), "house")
	panicOnError(err)
	expect.NewSession(nil, hHouse.Stdout(), 30*time.Second)
	defer hHouse.Shutdown(outFile, errFile)

	// Possibly run the sample world.
	if runSample {
		fmt.Println("Running Sample World")
		hSample, err := sh.Start(SampleWorldCommand, nil, "--veyron.tcp.protocol=ws", "--veyron.tcp.address=127.0.0.1:0")
		panicOnError(err)
		expect.NewSession(nil, hSample.Stdout(), 30*time.Second)
		defer hSample.Shutdown(outFile, errFile)
	}

	// Possibly serve the public bundle at the portHTTP.
	if serveHTTP {
		fmt.Printf("Also serving HTTP at %s for %s\n", portHTTP, rootHTTP)
		http.ListenAndServe(":"+portHTTP, http.FileServer(http.Dir(rootHTTP)))
	}

	// Just print out the collected variables. This is for debugging purposes.
	bytes, err := json.Marshal(vars)
	panicOnError(err)
	fmt.Println(string(bytes))

	// Possibly run the tests in Prova.
	if runTests {
		// Also set HOUSE_MOUNTTABLE (used in the tests)
		os.Setenv("HOUSE_MOUNTTABLE", fmt.Sprintf("/127.0.0.1:%d", housePort))

		proxyShutdown, proxyEndpoint, err := profiles.NewProxy(ctx, "ws", "127.0.0.1:0", "", "test/proxy")
		panicOnError(err)
		defer proxyShutdown()
		vars["PROXY_NAME"] = proxyEndpoint.Name()

		hIdentityd, err := sh.Start(core.TestIdentitydCommand, nil, "--veyron.tcp.protocol=ws", "--veyron.tcp.address=127.0.0.1:0", "--veyron.proxy=test/proxy", "--host=localhost", "--httpaddr=localhost:0")
		panicOnError(err)
		panicOnError(updateVars(hIdentityd, vars, "TEST_IDENTITYD_NAME", "TEST_IDENTITYD_HTTP_ADDR"))
		defer hIdentityd.Shutdown(outFile, errFile)

		// Setup a lot of environment variables; these are used for the tests and building the test extension.
		os.Setenv("NAMESPACE_ROOT", vars["MT_NAME"])
		os.Setenv("PROXY_ADDR", vars["PROXY_NAME"])
		os.Setenv("IDENTITYD", fmt.Sprintf("%s/google", vars["TEST_IDENTITYD_NAME"]))
		os.Setenv("IDENTITYD_BLESSING_URL", fmt.Sprintf("%s/blessing-root", vars["TEST_IDENTITYD_HTTP_ADDR"]))
		os.Setenv("DEBUG", "false")

		testsOk := runProva()

		fmt.Println("Cleaning up launched services...")
		return testsOk
	}

	// Not in a test, so run until the program is killed.
	<-signals.ShutdownOnSignals(ctx)
	return true

}

// Run the prova tests and convert its tap output to xunit.
func runProva() bool {
	// This is also useful information for routing the test output.
	VANADIUM_ROOT := os.Getenv("VANADIUM_ROOT")
	VANADIUM_JS := fmt.Sprintf("%s/release/javascript/core", VANADIUM_ROOT)
	VANADIUM_BROWSER := fmt.Sprintf("%s/release/projects/namespace_browser", VANADIUM_ROOT)

	TAP_XUNIT := fmt.Sprintf("%s/node_modules/.bin/tap-xunit", VANADIUM_BROWSER)
	XUNIT_OUTPUT_FILE := os.Getenv("XUNIT_OUTPUT_FILE")
	if XUNIT_OUTPUT_FILE == "" {
		XUNIT_OUTPUT_FILE = fmt.Sprintf("%s/test_output.xml", os.Getenv("TMPDIR"))
	}
	TAP_XUNIT_OPTIONS := " --package=namespace-browser"

	// Make sure we're in the right folder when we run make test-extension.
	vbroot, err := os.Open(VANADIUM_BROWSER)
	panicOnError(err)
	err = vbroot.Chdir()
	panicOnError(err)

	// Make the test-extension, this should also remove the old one.
	fmt.Println("Rebuilding test extension...")
	cmdExtensionClean := exec.Command("rm", "-fr", fmt.Sprintf("%s/extension/build-test", VANADIUM_JS))
	err = cmdExtensionClean.Run()
	panicOnError(err)
	cmdExtensionBuild := exec.Command("make", "-C", fmt.Sprintf("%s/extension", VANADIUM_JS), "build-test")
	err = cmdExtensionBuild.Run()
	panicOnError(err)

	// These are the basic prova options.
	options := []string{
		"test/**/*.js",
		"--browser",
		"--includeFilenameAsPackage",
		"--launch",
		"chrome",
		"--plugin",
		"proxyquireify/plugin",
		"--transform",
		"envify,./main-transform",
		"--log",
		"tmp/chrome.log",
		fmt.Sprintf("--options=--load-extension=%s/extension/build-test/,--ignore-certificate-errors,--enable-logging=stderr", VANADIUM_JS),
	}

	// Normal tests have a few more options and a different port from the watch tests.
	var PROVA_PORT int
	if !runTestsWatch {
		PROVA_PORT = 8893
		options = append(options, "--headless", "--quit", "--progress", "--tap")
		fmt.Printf("\033[34m-Executing tests. See %s for test xunit output.\033[0m\n", XUNIT_OUTPUT_FILE)
	} else {
		PROVA_PORT = 8894
		fmt.Println("\033[34m-Running tests in watch mode.\033[0m")
	}
	options = append(options, "--port", fmt.Sprintf("%d", PROVA_PORT))

	// This is the prova command.
	cmdProva := exec.Command(
		fmt.Sprintf("%s/node_modules/.bin/prova", VANADIUM_BROWSER),
		options...,
	)
	fmt.Printf("\033[34m-Go to \033[32mhttp://0.0.0.0:%d\033[34m to see tests running.\033[0m\n", PROVA_PORT)
	fmt.Println(cmdProva)

	// Collect the prova stdout. This information needs to be sent to xunit.
	provaOut, err := cmdProva.StdoutPipe()
	panicOnError(err)

	// Setup the tap to xunit command. It uses Prova's stdout as input.
	// The output will got the xunit output file.
	cmdTap := exec.Command(TAP_XUNIT, TAP_XUNIT_OPTIONS)
	cmdTap.Stdin = io.TeeReader(provaOut, os.Stdout) // Tee the prova output to see it on the console too.
	outfile, err := os.Create(XUNIT_OUTPUT_FILE)
	panicOnError(err)
	defer outfile.Close()
	bufferedWriter := bufio.NewWriter(outfile)
	cmdTap.Stdout = bufferedWriter
	defer bufferedWriter.Flush() // Ensure that the full xunit output is written.

	// We start the tap command...
	err = cmdTap.Start()
	panicOnError(err)

	// Meanwhile, run Prova to completion. If there was an error, print ERROR, otherwise PASS.
	err = cmdProva.Run()
	testsOk := true
	if err != nil {
		fmt.Println(err)
		fmt.Println("\033[31m\033[1mERROR\033[0m")
		testsOk = false
	} else {
		fmt.Println("\033[32m\033[1mPASS\033[0m")
	}

	// Wait for tap to xunit to finish itself off. This file will be ready for reading by Jenkins.
	fmt.Println("Converting Tap output to XUnit")
	err = cmdTap.Wait()
	panicOnError(err)

	return testsOk
}
<|endoftext|>


<commit_before>package worker

import (
	"fmt"

	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/topo/topoproto"
	"github.com/youtube/vitess/go/vt/wrangler"

	tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata"
	topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)

var (
	completeChunk       = chunk{sqltypes.NULL, sqltypes.NULL, 1, 1}
	singleCompleteChunk = []chunk{completeChunk}
)

// chunk holds the information which subset of the table should be worked on.
// The subset is the range of rows in the range [start, end) where start and end
// both refer to the first column of the primary key.
// If the column is not numeric, both start and end will be sqltypes.NULL.
type chunk struct {
	start sqltypes.Value
	end   sqltypes.Value
	// number records the position of this chunk among all "total" chunks.
	// The lowest value is 1.
	number int
	// total is the total number of chunks this chunk belongs to.
	total int
}

// String returns a human-readable presentation of the chunk range.
func (c chunk) String() string {
	// Pad the chunk number such that all log messages align nicely.
	digits := digits(c.total)
	return fmt.Sprintf("%*d/%d", digits, c.number, c.total)
}

func digits(i int) int {
	digits := 1
	for {
		i /= 10
		if i == 0 {
			break
		}
		digits++
	}
	return digits
}

// generateChunks returns an array of chunks to use for splitting up a table
// into multiple data chunks. It only works for tables with a primary key
// whose first column is a numeric type.
func generateChunks(ctx context.Context, wr *wrangler.Wrangler, tablet *topodatapb.Tablet, td *tabletmanagerdatapb.TableDefinition, chunkCount, minRowsPerChunk int) ([]chunk, error) {
	if len(td.PrimaryKeyColumns) == 0 {
		// No explicit primary key. Cannot chunk the rows then.
		wr.Logger().Infof("Not splitting table %v into multiple chunks because it has no primary key columns. This will reduce the performance of the clone.", td.Name)
		return singleCompleteChunk, nil
	}
	if td.RowCount < 2*uint64(minRowsPerChunk) {
		// The automatic adjustment of "chunkCount" based on "minRowsPerChunk"
		// below would set "chunkCount" to less than 2 i.e. 1 or 0 chunks.
		// In practice in this case there should be exactly one chunk.
		// Return early in this case and notice the user about this.
		wr.Logger().Infof("Not splitting table %v into multiple chunks because it has only %d rows.", td.Name, td.RowCount)
		return singleCompleteChunk, nil
	}
	if chunkCount == 1 {
		return singleCompleteChunk, nil
	}

	// Get the MIN and MAX of the leading column of the primary key.
	query := fmt.Sprintf("SELECT MIN(%v), MAX(%v) FROM %v.%v", escape(td.PrimaryKeyColumns[0]), escape(td.PrimaryKeyColumns[0]), escape(topoproto.TabletDbName(tablet)), escape(td.Name))
	shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout)
	qr, err := wr.TabletManagerClient().ExecuteFetchAsApp(shortCtx, tablet, true, []byte(query), 1)
	cancel()
	if err != nil {
		return nil, fmt.Errorf("Cannot determine MIN and MAX of the first primary key column. ExecuteFetchAsApp: %v", err)
	}
	if len(qr.Rows) != 1 {
		return nil, fmt.Errorf("Cannot determine MIN and MAX of the first primary key column. Zero rows were returned for the following query: %v", query)
	}

	result := sqltypes.Proto3ToResult(qr)
	min := result.Rows[0][0].ToNative()
	max := result.Rows[0][1].ToNative()

	if min == nil || max == nil {
		wr.Logger().Infof("Not splitting table %v into multiple chunks, min or max is NULL: %v", td.Name, qr.Rows[0])
		return singleCompleteChunk, nil
	}

	// Determine the average number of rows per chunk for the given chunkCount.
	avgRowsPerChunk := td.RowCount / uint64(chunkCount)
	if avgRowsPerChunk < uint64(minRowsPerChunk) {
		// Reduce the chunkCount to fulfill minRowsPerChunk.
		newChunkCount := td.RowCount / uint64(minRowsPerChunk)
		wr.Logger().Infof("Reducing the number of chunks for table %v from the default %d to %d to make sure that each chunk has at least %d rows.", td.Name, chunkCount, newChunkCount, minRowsPerChunk)
		chunkCount = int(newChunkCount)
	}

	// TODO(mberlin): Write a unit test for this part of the function.
	var interval interface{}
	chunks := make([]chunk, chunkCount)
	switch min := min.(type) {
	case int64:
		max := max.(int64)
		interval = (max - min) / int64(chunkCount)
		if interval == 0 {
			wr.Logger().Infof("Not splitting table %v into multiple chunks, interval=0: %v to %v", td.Name, min, max)
			return singleCompleteChunk, nil
		}
	case uint64:
		max := max.(uint64)
		interval = (max - min) / uint64(chunkCount)
		if interval == 0 {
			wr.Logger().Infof("Not splitting table %v into multiple chunks, interval=0: %v to %v", td.Name, min, max)
			return singleCompleteChunk, nil
		}
	case float64:
		max := max.(float64)
		interval = (max - min) / float64(chunkCount)
		if interval == 0 {
			wr.Logger().Infof("Not splitting table %v into multiple chunks, interval=0: %v to %v", td.Name, min, max)
			return singleCompleteChunk, nil
		}
	default:
		wr.Logger().Infof("Not splitting table %v into multiple chunks, primary key not numeric.", td.Name)
		return singleCompleteChunk, nil
	}

	// Create chunks.
	start := min
	for i := 0; i < chunkCount; i++ {
		end := add(start, interval)
		chunk, err := toChunk(start, end, i+1, chunkCount)
		if err != nil {
			return nil, err
		}
		chunks[i] = chunk
		start = end
	}

	// Clear out the MIN and MAX on the first and last chunk respectively
	// because other shards might have smaller or higher values than the one we
	// looked at.
	chunks[0].start = sqltypes.NULL
	chunks[chunkCount-1].end = sqltypes.NULL
	return chunks, nil
}

func add(start, interval interface{}) interface{} {
	switch start := start.(type) {
	case int64:
		return start + interval.(int64)
	case uint64:
		return start + interval.(uint64)
	case float64:
		return start + interval.(float64)
	default:
		panic(fmt.Sprintf("unsupported type %T for interval start: %v", start, start))
	}
}

func toChunk(start, end interface{}, number, total int) (chunk, error) {
	startValue, err := sqltypes.BuildValue(start)
	if err != nil {
		return chunk{}, fmt.Errorf("Failed to convert calculated start value (%v) into internal sqltypes.Value: %v", start, err)
	}
	endValue, err := sqltypes.BuildValue(end)
	if err != nil {
		return chunk{}, fmt.Errorf("Failed to convert calculated end value (%v) into internal sqltypes.Value: %v", end, err)
	}
	return chunk{startValue, endValue, number, total}, nil
}
<commit_msg>worker: Change the logging prefix in chunk.go. (#2017)<commit_after>package worker

import (
	"fmt"

	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/topo/topoproto"
	"github.com/youtube/vitess/go/vt/wrangler"

	tabletmanagerdatapb "github.com/youtube/vitess/go/vt/proto/tabletmanagerdata"
	topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)

var (
	completeChunk       = chunk{sqltypes.NULL, sqltypes.NULL, 1, 1}
	singleCompleteChunk = []chunk{completeChunk}
)

// chunk holds the information which subset of the table should be worked on.
// The subset is the range of rows in the range [start, end) where start and end
// both refer to the first column of the primary key.
// If the column is not numeric, both start and end will be sqltypes.NULL.
type chunk struct {
	start sqltypes.Value
	end   sqltypes.Value
	// number records the position of this chunk among all "total" chunks.
	// The lowest value is 1.
	number int
	// total is the total number of chunks this chunk belongs to.
	total int
}

// String returns a human-readable presentation of the chunk range.
func (c chunk) String() string {
	// Pad the chunk number such that all log messages align nicely.
	digits := digits(c.total)
	return fmt.Sprintf("%*d/%d", digits, c.number, c.total)
}

func digits(i int) int {
	digits := 1
	for {
		i /= 10
		if i == 0 {
			break
		}
		digits++
	}
	return digits
}

// generateChunks returns an array of chunks to use for splitting up a table
// into multiple data chunks. It only works for tables with a primary key
// whose first column is a numeric type.
func generateChunks(ctx context.Context, wr *wrangler.Wrangler, tablet *topodatapb.Tablet, td *tabletmanagerdatapb.TableDefinition, chunkCount, minRowsPerChunk int) ([]chunk, error) {
	if len(td.PrimaryKeyColumns) == 0 {
		// No explicit primary key. Cannot chunk the rows then.
		wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks because it has no primary key columns. This will reduce the performance of the clone.", td.Name)
		return singleCompleteChunk, nil
	}
	if td.RowCount < 2*uint64(minRowsPerChunk) {
		// The automatic adjustment of "chunkCount" based on "minRowsPerChunk"
		// below would set "chunkCount" to less than 2 i.e. 1 or 0 chunks.
		// In practice in this case there should be exactly one chunk.
		// Return early in this case and notice the user about this.
		wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks because it has only %d rows.", td.Name, td.RowCount)
		return singleCompleteChunk, nil
	}
	if chunkCount == 1 {
		return singleCompleteChunk, nil
	}

	// Get the MIN and MAX of the leading column of the primary key.
	query := fmt.Sprintf("SELECT MIN(%v), MAX(%v) FROM %v.%v", escape(td.PrimaryKeyColumns[0]), escape(td.PrimaryKeyColumns[0]), escape(topoproto.TabletDbName(tablet)), escape(td.Name))
	shortCtx, cancel := context.WithTimeout(ctx, *remoteActionsTimeout)
	qr, err := wr.TabletManagerClient().ExecuteFetchAsApp(shortCtx, tablet, true, []byte(query), 1)
	cancel()
	if err != nil {
		return nil, fmt.Errorf("Cannot determine MIN and MAX of the first primary key column. ExecuteFetchAsApp: %v", err)
	}
	if len(qr.Rows) != 1 {
		return nil, fmt.Errorf("Cannot determine MIN and MAX of the first primary key column. Zero rows were returned for the following query: %v", query)
	}

	result := sqltypes.Proto3ToResult(qr)
	min := result.Rows[0][0].ToNative()
	max := result.Rows[0][1].ToNative()

	if min == nil || max == nil {
		wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, min or max is NULL: %v", td.Name, qr.Rows[0])
		return singleCompleteChunk, nil
	}

	// Determine the average number of rows per chunk for the given chunkCount.
	avgRowsPerChunk := td.RowCount / uint64(chunkCount)
	if avgRowsPerChunk < uint64(minRowsPerChunk) {
		// Reduce the chunkCount to fulfill minRowsPerChunk.
		newChunkCount := td.RowCount / uint64(minRowsPerChunk)
		wr.Logger().Infof("table=%v: Reducing the number of chunks from the default %d to %d to make sure that each chunk has at least %d rows.", td.Name, chunkCount, newChunkCount, minRowsPerChunk)
		chunkCount = int(newChunkCount)
	}

	// TODO(mberlin): Write a unit test for this part of the function.
	var interval interface{}
	chunks := make([]chunk, chunkCount)
	switch min := min.(type) {
	case int64:
		max := max.(int64)
		interval = (max - min) / int64(chunkCount)
		if interval == 0 {
			wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, interval=0: %v to %v", td.Name, min, max)
			return singleCompleteChunk, nil
		}
	case uint64:
		max := max.(uint64)
		interval = (max - min) / uint64(chunkCount)
		if interval == 0 {
			wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, interval=0: %v to %v", td.Name, min, max)
			return singleCompleteChunk, nil
		}
	case float64:
		max := max.(float64)
		interval = (max - min) / float64(chunkCount)
		if interval == 0 {
			wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, interval=0: %v to %v", td.Name, min, max)
			return singleCompleteChunk, nil
		}
	default:
		wr.Logger().Infof("table=%v: Not splitting the table into multiple chunks, primary key not numeric.", td.Name)
		return singleCompleteChunk, nil
	}

	// Create chunks.
	start := min
	for i := 0; i < chunkCount; i++ {
		end := add(start, interval)
		chunk, err := toChunk(start, end, i+1, chunkCount)
		if err != nil {
			return nil, err
		}
		chunks[i] = chunk
		start = end
	}

	// Clear out the MIN and MAX on the first and last chunk respectively
	// because other shards might have smaller or higher values than the one we
	// looked at.
	chunks[0].start = sqltypes.NULL
	chunks[chunkCount-1].end = sqltypes.NULL
	return chunks, nil
}

func add(start, interval interface{}) interface{} {
	switch start := start.(type) {
	case int64:
		return start + interval.(int64)
	case uint64:
		return start + interval.(uint64)
	case float64:
		return start + interval.(float64)
	default:
		panic(fmt.Sprintf("unsupported type %T for interval start: %v", start, start))
	}
}

func toChunk(start, end interface{}, number, total int) (chunk, error) {
	startValue, err := sqltypes.BuildValue(start)
	if err != nil {
		return chunk{}, fmt.Errorf("Failed to convert calculated start value (%v) into internal sqltypes.Value: %v", start, err)
	}
	endValue, err := sqltypes.BuildValue(end)
	if err != nil {
		return chunk{}, fmt.Errorf("Failed to convert calculated end value (%v) into internal sqltypes.Value: %v", end, err)
	}
	return chunk{startValue, endValue, number, total}, nil
}
<|endoftext|>


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

package gossip

import (
	"bytes"
	"errors"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/base"
	"github.com/cockroachdb/cockroach/gossip/resolver"
	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/rpc"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/leaktest"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/cockroachdb/cockroach/util/stop"
)

// TestGossipInfoStore verifies operation of gossip instance infostore.
func TestGossipInfoStore(t *testing.T) {
	defer leaktest.AfterTest(t)
	rpcContext := rpc.NewContext(&base.Context{}, hlc.NewClock(hlc.UnixNano), nil)
	g := New(rpcContext, TestBootstrap)
	// Have to call g.SetNodeID before call g.AddInfo
	g.SetNodeID(roachpb.NodeID(1))
	slice := []byte("b")
	if err := g.AddInfo("s", slice, time.Hour); err != nil {
		t.Fatal(err)
	}
	if val, err := g.GetInfo("s"); !bytes.Equal(val, slice) || err != nil {
		t.Errorf("error fetching string: %v", err)
	}
	if _, err := g.GetInfo("s2"); err == nil {
		t.Errorf("expected error fetching nonexistent key \"s2\"")
	}
}

func TestGossipGetNextBootstrapAddress(t *testing.T) {
	defer leaktest.AfterTest(t)
	resolverSpecs := []string{
		"127.0.0.1:9000",
		"tcp=127.0.0.1:9001",
		"unix=/tmp/unix-socket12345",
		"lb=127.0.0.1:9002",
		"foo=127.0.0.1:9003", // error should not resolve.
		"lb=",                // error should not resolve.
		"localhost:9004",
		"lb=127.0.0.1:9005",
	}

	resolvers := []resolver.Resolver{}
	for _, rs := range resolverSpecs {
		resolver, err := resolver.NewResolver(&base.Context{}, rs)
		if err == nil {
			resolvers = append(resolvers, resolver)
		}
	}
	if len(resolvers) != 6 {
		t.Errorf("expected 6 resolvers; got %d", len(resolvers))
	}
	g := New(nil, resolvers)

	// Using specified resolvers, fetch bootstrap addresses 10 times
	// and verify the results match expected addresses.
	expAddresses := []string{
		"127.0.0.1:9000",
		"127.0.0.1:9001",
		"/tmp/unix-socket12345",
		"127.0.0.1:9002",
		"localhost:9004",
		"127.0.0.1:9005",
		"127.0.0.1:9002",
		"127.0.0.1:9005",
		"127.0.0.1:9002",
		"127.0.0.1:9005",
	}
	for i := 0; i < len(expAddresses); i++ {
		log.Infof("getting next address")
		addr := g.getNextBootstrapAddress()
		if addr == nil {
			t.Errorf("%d: unexpected nil addr when expecting %s", i, expAddresses[i])
		} else if addr.String() != expAddresses[i] {
			t.Errorf("%d: expected addr %s; got %s", i, expAddresses[i], addr.String())
		}
	}
}

// TestGossipCullNetwork verifies that a client will be culled from
// the network periodically (at cullInterval duration intervals).
func TestGossipCullNetwork(t *testing.T) {
	defer leaktest.AfterTest(t)

	// Set the cullInterval to a low value to guarantee it kicks in quickly.
	origCullInterval := cullInterval
	cullInterval = 5 * time.Millisecond
	defer func() {
		cullInterval = origCullInterval
	}()

	// Create the local gossip and minPeers peers.
	stopper := stop.NewStopper()
	defer stopper.Stop()
	local := startGossip(1, stopper, t)
	peers := []*Gossip{}
	for i := 0; i < minPeers; i++ {
		peers = append(peers, startGossip(roachpb.NodeID(i+2), stopper, t))
	}

	// Start clients to all peers and start the local gossip's manage routine.
	local.mu.Lock()
	for _, p := range peers {
		pAddr := p.is.NodeAddr
		local.startClient(pAddr, stopper)
	}
	local.mu.Unlock()
	local.manage(stopper)

	util.SucceedsWithin(t, 10*time.Second, func() error {
		// Verify that a client is closed within the cull interval.
		if len(local.Outgoing()) == minPeers-1 {
			return nil
		}
		return errors.New("no network culling occurred")
	})
}
<commit_msg>disable flaky TestGossipCullNetwork<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package gossip

import (
	"bytes"
	"errors"
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/base"
	"github.com/cockroachdb/cockroach/gossip/resolver"
	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/rpc"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/leaktest"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/cockroachdb/cockroach/util/stop"
)

// TestGossipInfoStore verifies operation of gossip instance infostore.
func TestGossipInfoStore(t *testing.T) {
	defer leaktest.AfterTest(t)
	rpcContext := rpc.NewContext(&base.Context{}, hlc.NewClock(hlc.UnixNano), nil)
	g := New(rpcContext, TestBootstrap)
	// Have to call g.SetNodeID before call g.AddInfo
	g.SetNodeID(roachpb.NodeID(1))
	slice := []byte("b")
	if err := g.AddInfo("s", slice, time.Hour); err != nil {
		t.Fatal(err)
	}
	if val, err := g.GetInfo("s"); !bytes.Equal(val, slice) || err != nil {
		t.Errorf("error fetching string: %v", err)
	}
	if _, err := g.GetInfo("s2"); err == nil {
		t.Errorf("expected error fetching nonexistent key \"s2\"")
	}
}

func TestGossipGetNextBootstrapAddress(t *testing.T) {
	defer leaktest.AfterTest(t)
	resolverSpecs := []string{
		"127.0.0.1:9000",
		"tcp=127.0.0.1:9001",
		"unix=/tmp/unix-socket12345",
		"lb=127.0.0.1:9002",
		"foo=127.0.0.1:9003", // error should not resolve.
		"lb=",                // error should not resolve.
		"localhost:9004",
		"lb=127.0.0.1:9005",
	}

	resolvers := []resolver.Resolver{}
	for _, rs := range resolverSpecs {
		resolver, err := resolver.NewResolver(&base.Context{}, rs)
		if err == nil {
			resolvers = append(resolvers, resolver)
		}
	}
	if len(resolvers) != 6 {
		t.Errorf("expected 6 resolvers; got %d", len(resolvers))
	}
	g := New(nil, resolvers)

	// Using specified resolvers, fetch bootstrap addresses 10 times
	// and verify the results match expected addresses.
	expAddresses := []string{
		"127.0.0.1:9000",
		"127.0.0.1:9001",
		"/tmp/unix-socket12345",
		"127.0.0.1:9002",
		"localhost:9004",
		"127.0.0.1:9005",
		"127.0.0.1:9002",
		"127.0.0.1:9005",
		"127.0.0.1:9002",
		"127.0.0.1:9005",
	}
	for i := 0; i < len(expAddresses); i++ {
		log.Infof("getting next address")
		addr := g.getNextBootstrapAddress()
		if addr == nil {
			t.Errorf("%d: unexpected nil addr when expecting %s", i, expAddresses[i])
		} else if addr.String() != expAddresses[i] {
			t.Errorf("%d: expected addr %s; got %s", i, expAddresses[i], addr.String())
		}
	}
}

// TestGossipCullNetwork verifies that a client will be culled from
// the network periodically (at cullInterval duration intervals).
func TestGossipCullNetwork(t *testing.T) {
	defer leaktest.AfterTest(t)
	t.Skip("#3620")

	// Set the cullInterval to a low value to guarantee it kicks in quickly.
	origCullInterval := cullInterval
	cullInterval = 5 * time.Millisecond
	defer func() {
		cullInterval = origCullInterval
	}()

	// Create the local gossip and minPeers peers.
	stopper := stop.NewStopper()
	defer stopper.Stop()
	local := startGossip(1, stopper, t)
	peers := []*Gossip{}
	for i := 0; i < minPeers; i++ {
		peers = append(peers, startGossip(roachpb.NodeID(i+2), stopper, t))
	}

	// Start clients to all peers and start the local gossip's manage routine.
	local.mu.Lock()
	for _, p := range peers {
		pAddr := p.is.NodeAddr
		local.startClient(pAddr, stopper)
	}
	local.mu.Unlock()
	local.manage(stopper)

	util.SucceedsWithin(t, 10*time.Second, func() error {
		// Verify that a client is closed within the cull interval.
		if len(local.Outgoing()) == minPeers-1 {
			return nil
		}
		return errors.New("no network culling occurred")
	})
}
<|endoftext|>


<commit_before>/*
 * Primitives are basic shapes which can be directly drawn to screen.
 *
 */

package graphics

import "math"
import "image/color"
import "github.com/banthar/Go-SDL/sdl"

type Primitive interface {
	draw(s *sdl.Surface)
}

type Point struct {
	x, y int
	c    color.Color
}

func (p Point) draw(s *sdl.Surface) {
	s.Set(p.x, p.y, p.c)
}

type Rectangle struct {
	x, y int16
	w, h uint16
	c    color.Color
}

func (r Rectangle) draw(s *sdl.Surface) {
	format := sdl.GetVideoInfo().Vfmt
	color := sdl.ColorFromGoColor(r.c)
	colorVal := sdl.MapRGB(format, color.R, color.G, color.B)
	s.FillRect(&sdl.Rect{r.x, r.y, r.w, r.h}, colorVal)
}

type Circle struct {
	x, y int16       // Location on screen
	r    uint16      // Radius
	b    int         // Border thickness
	c    color.Color // Color
}

// Ewwwwww
func (c Circle) draw(s *sdl.Surface) {
	format := sdl.GetVideoInfo().Vfmt
	color := sdl.ColorFromGoColor(c.c)
	colorVal := sdl.MapRGB(format, color.R, color.G, color.B)

	prevL := int16(c.r)
	for h := 0; h <= int(c.r); h++ {
		l := int16(math.Sqrt(float64(int(c.r*c.r) - (h * h))))

		x := int16(c.x) - l

		if c.b == 0 {
			s.FillRect(&sdl.Rect{x, c.y + int16(h), uint16(2 * l), 1}, colorVal)
			s.FillRect(&sdl.Rect{x, c.y - int16(h), uint16(2 * l), 1}, colorVal)
		} else {
			s.FillRect(&sdl.Rect{x, c.y + int16(h), uint16(math.Max(1, math.Abs(float64(l-prevL)))), 1}, colorVal)
			s.FillRect(&sdl.Rect{x, c.y - int16(h), uint16(math.Max(1, math.Abs(float64(l-prevL)))), 1}, colorVal)
			s.FillRect(&sdl.Rect{x + l + prevL, c.y + int16(h), uint16(math.Max(1, math.Abs(float64(l-prevL)))), 1}, colorVal)
			s.FillRect(&sdl.Rect{x + l + prevL, c.y - int16(h), uint16(math.Max(1, math.Abs(float64(l-prevL)))), 1}, colorVal)
		}

		prevL = l
	}
}
<commit_msg>Implemented the integer midpoint circle algorithm to make circle drawing better & faster.<commit_after>/*
 * Primitives are basic shapes which can be directly drawn to screen.
 *
 */

package graphics

import "image/color"
import "github.com/banthar/Go-SDL/sdl"

// A Primitive is a basic shape which can be drawn directly by the artist.
type Primitive interface {
	draw(s *sdl.Surface)
}

// A Point is as it sounds, a single point in space.
type Point struct {
	x, y int
	c    color.Color
}

// Points are drawn by setting a single corresponding pixel.
func (p Point) draw(s *sdl.Surface) {
	safeSet(s, p.x, p.y, p.c)
}

// A Rectangle is... a rectangle.
type Rectangle struct {
	x, y int16
	w, h uint16
	c    color.Color
}

// Rectangles are drawn by directly calling FillRect on the surface.
func (r Rectangle) draw(s *sdl.Surface) {
	format := sdl.GetVideoInfo().Vfmt
	color := sdl.ColorFromGoColor(r.c)
	colorVal := sdl.MapRGB(format, color.R, color.G, color.B)
	s.FillRect(&sdl.Rect{r.x, r.y, r.w, r.h}, colorVal)
}

// Circles are, you guessed it. Circles.
type Circle struct {
	x, y int16       // Location on screen
	r    uint16      // Radius
	b    int         // Border thickness. For now only controls if there IS a border or not, not actually it's thickness.
	c    color.Color // Color
}

// Circles may be filled or not.
func (c Circle) draw(s *sdl.Surface) {
	if c.b == 0 {
		drawFilledCircle(c.x, c.y, c.r, c.c, s)
	} else {
		drawOutlineCircle(c.x, c.y, c.r, c.c, s)
	}
}

// drawFilledCircle uses the integer midpoint circle algorithm to draw a filled
// circle to the given surface.
func drawFilledCircle(x0, y0 int16, r uint16, c color.Color, s *sdl.Surface) {
	format := sdl.GetVideoInfo().Vfmt
	color := sdl.ColorFromGoColor(c)
	colorVal := sdl.MapRGB(format, color.R, color.G, color.B)

	x := int16(r)
	y := int16(0)
	e := 1 - x

	for x >= y {
		s.FillRect(&sdl.Rect{-x + x0, y + y0, uint16(2 * x), 1}, colorVal)
		s.FillRect(&sdl.Rect{-x + x0, -y + y0, uint16(2 * x), 1}, colorVal)
		s.FillRect(&sdl.Rect{-y + x0, x + y0, uint16(2 * y), 1}, colorVal)
		s.FillRect(&sdl.Rect{-y + x0, -x + y0, uint16(2 * y), 1}, colorVal)

		y++

		if e < 0 {
			e += 2*y + 1
		} else {
			x--
			e += 2 * (y - x + 1)
		}
	}
}

// drawOutlineCircle uses the integer midpoint circle algorithm to draw the outline
// of a circle (1 px thick) to the given surface.
func drawOutlineCircle(x0, y0 int16, r uint16, c color.Color, s *sdl.Surface) {
	s.Lock()
	defer s.Unlock()

	color := sdl.ColorFromGoColor(c)

	x := int16(r)
	y := int16(0)
	e := 1 - x

	for x >= y {
		safeSet(s, int(x+x0), int(y+y0), color)
		safeSet(s, int(x+x0), int(-y+y0), color)
		safeSet(s, int(-x+x0), int(y+y0), color)
		safeSet(s, int(-x+x0), int(-y+y0), color)
		safeSet(s, int(y+x0), int(x+y0), color)
		safeSet(s, int(y+x0), int(-x+y0), color)
		safeSet(s, int(-y+x0), int(x+y0), color)
		safeSet(s, int(-y+x0), int(-x+y0), color)

		y++

		if e < 0 {
			e += 2*y + 1
		} else {
			x--
			e += 2 * (y - x + 1)
		}
	}
}

func safeSet(s *sdl.Surface, x, y int, c sdl.Color) {
	if x >= 0 && y >= 0 && x < int(s.W) && y < int(s.H) {
		s.Set(x, y, c)
	}
}
<|endoftext|>