Skip to content

Commit

Permalink
Benchmark implementation to ensure it can keep up
Browse files Browse the repository at this point in the history
  • Loading branch information
quartercastle committed Mar 11, 2019
1 parent 18a42a8 commit 109e751
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 21 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ go:
- 1.10.x
- 1.11.x
- master
scripts:
- go test -race && go test -bench=.
57 changes: 37 additions & 20 deletions dualshock.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package dualshock
import (
"encoding/binary"
"io"
"time"
)

// Controller describes the reference to the hardware device
type Controller struct {
reader io.Reader
queue chan State
queue chan []byte
errors chan error
interrupt chan int
}

Expand Down Expand Up @@ -88,13 +88,13 @@ func transform(b []byte) State {
TrackPad0: TrackPad{
ID: int(b[35] & 0x7f),
Active: (b[35] >> 7) == 0,
X: int(((b[37] & 0x0f) << 8) | b[36]),
X: int(((b[37] & 0x0f) << 4) | b[36]),
Y: int(b[38]<<4 | ((b[37] & 0xf0) >> 4)),
},
TrackPad1: TrackPad{
ID: int(b[39] & 0x7f),
Active: (b[39] >> 7) == 0,
X: int(((b[41] & 0x0f) << 8) | b[40]),
X: int(((b[41] & 0x0f) << 4) | b[40]),
Y: int(b[42]<<4 | ((b[41] & 0xf0) >> 4)),
},
LeftDPad: DPad{
Expand Down Expand Up @@ -127,43 +127,60 @@ func transform(b []byte) State {
// New returns a new controller which transforms input from the device to a valid
// controller state
func New(reader io.Reader) *Controller {
c := &Controller{reader, make(chan State), make(chan int, 2)}
c := &Controller{
reader,
make(chan []byte),
make(chan error),
make(chan int),
}
go c.read()
return c
}

// read transforms data from the io.Reader and pushes it to the queue of
// states
func (c *Controller) read() {
var b []byte
for {
select {
case <-c.interrupt:
close(c.errors)
close(c.queue)
return
default:
b := make([]byte, 64)
c.reader.Read(b)
c.queue <- transform(b)
time.Sleep((1000 / 254) * time.Millisecond)
b = make([]byte, 64)
n, err := c.reader.Read(b)

if err != nil {
c.errors <- err
continue
}

c.queue <- b[:n]
}
}
}

// Listen for controller state changes
func (c *Controller) Listen(handler func(State)) {
for {
select {
case <-c.interrupt:
return
default:
handler(<-c.queue)
func (c *Controller) Listen(handle func(State)) {
go func() {
for {
select {
case <-c.interrupt:
return
default:
handle(transform(<-c.queue))
}
}
}
}()
}

// Errors returns a channel of reader errors
func (c *Controller) Errors() <-chan error {
return c.errors
}

// Close the listener
func (c *Controller) Close() {
c.interrupt <- 1 // close reader
c.interrupt <- 1 // close listener
close(c.interrupt)
close(c.queue)
}
18 changes: 17 additions & 1 deletion dualshock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ func (f fakeDevice) Read(b []byte) (int, error) {
129, 115, 70, 27, 130, 62, 97, 32, 0, 128, 0, 0, 0, 128, 0, 0, 0, 0,
128, 0, 0, 0, 128, 0, 0, 0, 0, 128, 0,
})
return 0, nil
return 64, nil
}

func TestDualshock(t *testing.T) {
controller := dualshock.New(fakeDevice{})

result := make(chan dualshock.State, 1)
defer close(result)

controller.Listen(func(state dualshock.State) {
controller.Close()
result <- state
Expand All @@ -31,3 +33,17 @@ func TestDualshock(t *testing.T) {
t.Errorf("Invalid state L2 should be true; got %v", r.L2)
}
}

func BenchmarkDualshock(b *testing.B) {
controller := dualshock.New(fakeDevice{})

result := make(chan dualshock.State, 1)

controller.Listen(func(state dualshock.State) {
result <- state
})

for n := 0; n < b.N; n++ {
<-result
}
}

0 comments on commit 109e751

Please sign in to comment.