<commit_before>// +build js,wasm

package websocket

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
	"sync"
	"syscall/js"
	"time"
)

const (
	webSocketStateConnecting = 0
	webSocketStateOpen       = 1
	webSocketStateClosing    = 2
	webSocketStateClosed     = 3
)

var errConnectionClosed = errors.New("connection is closed")

// Conn implements net.Conn interface for WebSockets in js/wasm.
type Conn struct {
	js.Value
	messageHandler  *js.Func
	closeHandler    *js.Func
	errorHandler    *js.Func
	mut             sync.Mutex
	currDataMut     sync.RWMutex
	currData        bytes.Buffer
	closeOnce       sync.Once
	closeSignalOnce sync.Once
	closeSignal     chan struct{}
	dataSignal      chan struct{}
	localAddr       net.Addr
	remoteAddr      net.Addr
	firstErr        error
}

// NewConn creates a Conn given a regular js/wasm WebSocket Conn.
func NewConn(raw js.Value) *Conn {
	conn := &Conn{
		Value:       raw,
		closeSignal: make(chan struct{}),
		dataSignal:  make(chan struct{}, 1),
		localAddr:   NewAddr("0.0.0.0:0"),
		remoteAddr:  getRemoteAddr(raw),
	}
	// Force the JavaScript WebSockets API to use the ArrayBuffer type for
	// incoming messages instead of the Blob type. This is better for us because
	// ArrayBuffer can be converted to []byte synchronously but Blob cannot.
	conn.Set("binaryType", "arraybuffer")
	conn.setUpHandlers()
	return conn
}

func (c *Conn) Read(b []byte) (int, error) {
	if err := c.checkOpen(); err != nil {
		return c.readAfterErr(b)
	}

	for {
		c.currDataMut.RLock()
		n, _ := c.currData.Read(b)
		c.currDataMut.RUnlock()

		if n != 0 {
			// Data was ready. Return the number of bytes read.
			return n, nil
		}

		// There is no data ready to be read. Wait for more data or for the
		// connection to be closed.
		select {
		case <-c.dataSignal:
			continue
		case <-c.closeSignal:
			return c.readAfterErr(b)
		}
	}
}

// readAfterError reads from c.currData. If there is no more data left it
// returns c.firstErr if non-nil and otherwise returns io.EOF.
func (c *Conn) readAfterErr(b []byte) (int, error) {
	c.currDataMut.RLock()
	n, err := c.currData.Read(b)
	c.currDataMut.RUnlock()
	if n == 0 {
		if c.firstErr != nil {
			return 0, c.firstErr
		} else {
			return 0, io.EOF
		}
	}
	return n, err
}

// checkOpen returns an error if the connection is not open. Otherwise, it
// returns nil.
func (c *Conn) checkOpen() error {
	state := c.Get("readyState").Int()
	switch state {
	case webSocketStateClosed, webSocketStateClosing:
		return errConnectionClosed
	}
	return nil
}

func (c *Conn) Write(b []byte) (n int, err error) {
	if err := c.checkOpen(); err != nil {
		return 0, err
	}
	uint8Array := js.Global().Get("Uint8Array").New(len(b))
	for i, bi := range b {
		uint8Array.SetIndex(i, bi)
	}
	c.Call("send", uint8Array.Get("buffer"))
	return len(b), nil
}

// Close closes the connection. Only the first call to Close will receive the
// close error, subsequent and concurrent calls will return nil.
// This method is thread-safe.
func (c *Conn) Close() error {
	c.closeOnce.Do(func() {
		c.signalClose()
		c.Call("close")
		c.releaseHandlers()
	})
	return nil
}

func (c *Conn) signalClose() {
	c.closeSignalOnce.Do(func() {
		close(c.closeSignal)
	})
}

func (c *Conn) releaseHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		c.Call("removeEventListener", "message", *c.messageHandler)
		c.messageHandler.Release()
		c.messageHandler = nil
	}
	if c.closeHandler != nil {
		c.Call("removeEventListener", "close", *c.closeHandler)
		c.closeHandler.Release()
		c.closeHandler = nil
	}
	if c.errorHandler != nil {
		c.Call("removeEventListener", "error", *c.errorHandler)
		c.errorHandler.Release()
		c.errorHandler = nil
	}
}

func (c *Conn) LocalAddr() net.Addr {
	return c.localAddr
}

func getRemoteAddr(val js.Value) net.Addr {
	rawURL := val.Get("url").String()
	withoutPrefix := strings.TrimPrefix(rawURL, "ws://")
	withoutSuffix := strings.TrimSuffix(withoutPrefix, "/")
	return NewAddr(withoutSuffix)
}

func (c *Conn) RemoteAddr() net.Addr {
	return c.remoteAddr
}

// TODO: Return os.ErrNoDeadline. For now we return nil because multiplexers
// don't handle the error correctly.
func (c *Conn) SetDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetReadDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetWriteDeadline(t time.Time) error {
	return nil
}

func (c *Conn) setUpHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		// Message handlers already created. Nothing to do.
		return
	}
	messageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		arrayBuffer := args[0].Get("data")
		data := arrayBufferToBytes(arrayBuffer)
		c.currDataMut.Lock()
		if _, err := c.currData.Write(data); err != nil {
			c.currDataMut.Unlock()
			return err
		}
		c.currDataMut.Unlock()

		// Non-blocking signal
		select {
		case c.dataSignal <- struct{}{}:
		default:
		}

		return nil
	})
	c.messageHandler = &messageHandler
	c.Call("addEventListener", "message", messageHandler)

	closeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		go func() {
			c.signalClose()
			c.mut.Lock()
			// Store the error in c.firstErr. It will be returned by Read later on.
			c.firstErr = errorEventToError(args[0])
			c.mut.Unlock()
			c.releaseHandlers()
		}()
		return nil
	})
	c.closeHandler = &closeHandler
	c.Call("addEventListener", "close", closeHandler)

	errorHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Unfortunately, the "error" event doesn't appear to give us any useful
		// information. All we can do is close the connection.
		c.Close()
		return nil
	})
	c.errorHandler = &errorHandler
	c.Call("addEventListener", "error", errorHandler)
}

func (c *Conn) waitForOpen() error {
	openSignal := make(chan struct{})
	handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		close(openSignal)
		return nil
	})
	defer c.Call("removeEventListener", "open", handler)
	defer handler.Release()
	c.Call("addEventListener", "open", handler)
	select {
	case <-openSignal:
		return nil
	case <-c.closeSignal:
		// c.closeSignal means there was an error when trying to open the
		// connection.
		return c.firstErr
	}
}

// arrayBufferToBytes converts a JavaScript ArrayBuffer to a slice of bytes.
func arrayBufferToBytes(buffer js.Value) []byte {
	view := js.Global().Get("Uint8Array").New(buffer)
	dataLen := view.Get("length").Int()
	data := make([]byte, dataLen)
	for i := 0; i < dataLen; i++ {
		data[i] = byte(view.Index(i).Int())
	}
	return data
}

func errorEventToError(val js.Value) error {
	var typ string
	if gotType := val.Get("type"); gotType != js.Undefined() {
		typ = gotType.String()
	} else {
		typ = val.Type().String()
	}
	var reason string
	if gotReason := val.Get("reason"); gotReason != js.Undefined() && gotReason.String() != "" {
		reason = gotReason.String()
	} else {
		code := val.Get("code")
		if code != js.Undefined() {
			switch code := code.Int(); code {
			case 1006:
				reason = "code 1006: connection unexpectedly closed"
			default:
				reason = fmt.Sprintf("unexpected code: %d", code)
			}
		}
	}
	return fmt.Errorf("JavaScript error: (%s) %s", typ, reason)
}
<commit_msg>Recover from uncaught JavaScript exceptions in Conn.Write<commit_after>// +build js,wasm

package websocket

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
	"sync"
	"syscall/js"
	"time"
)

const (
	webSocketStateConnecting = 0
	webSocketStateOpen       = 1
	webSocketStateClosing    = 2
	webSocketStateClosed     = 3
)

var errConnectionClosed = errors.New("connection is closed")

// Conn implements net.Conn interface for WebSockets in js/wasm.
type Conn struct {
	js.Value
	messageHandler  *js.Func
	closeHandler    *js.Func
	errorHandler    *js.Func
	mut             sync.Mutex
	currDataMut     sync.RWMutex
	currData        bytes.Buffer
	closeOnce       sync.Once
	closeSignalOnce sync.Once
	closeSignal     chan struct{}
	dataSignal      chan struct{}
	localAddr       net.Addr
	remoteAddr      net.Addr
	firstErr        error
}

// NewConn creates a Conn given a regular js/wasm WebSocket Conn.
func NewConn(raw js.Value) *Conn {
	conn := &Conn{
		Value:       raw,
		closeSignal: make(chan struct{}),
		dataSignal:  make(chan struct{}, 1),
		localAddr:   NewAddr("0.0.0.0:0"),
		remoteAddr:  getRemoteAddr(raw),
	}
	// Force the JavaScript WebSockets API to use the ArrayBuffer type for
	// incoming messages instead of the Blob type. This is better for us because
	// ArrayBuffer can be converted to []byte synchronously but Blob cannot.
	conn.Set("binaryType", "arraybuffer")
	conn.setUpHandlers()
	return conn
}

func (c *Conn) Read(b []byte) (int, error) {
	if err := c.checkOpen(); err != nil {
		return c.readAfterErr(b)
	}

	for {
		c.currDataMut.RLock()
		n, _ := c.currData.Read(b)
		c.currDataMut.RUnlock()

		if n != 0 {
			// Data was ready. Return the number of bytes read.
			return n, nil
		}

		// There is no data ready to be read. Wait for more data or for the
		// connection to be closed.
		select {
		case <-c.dataSignal:
			continue
		case <-c.closeSignal:
			return c.readAfterErr(b)
		}
	}
}

// readAfterError reads from c.currData. If there is no more data left it
// returns c.firstErr if non-nil and otherwise returns io.EOF.
func (c *Conn) readAfterErr(b []byte) (int, error) {
	c.currDataMut.RLock()
	n, err := c.currData.Read(b)
	c.currDataMut.RUnlock()
	if n == 0 {
		if c.firstErr != nil {
			return 0, c.firstErr
		} else {
			return 0, io.EOF
		}
	}
	return n, err
}

// checkOpen returns an error if the connection is not open. Otherwise, it
// returns nil.
func (c *Conn) checkOpen() error {
	state := c.Get("readyState").Int()
	switch state {
	case webSocketStateClosed, webSocketStateClosing:
		return errConnectionClosed
	}
	return nil
}

func (c *Conn) Write(b []byte) (n int, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = recoveredValueToError(e)
		}
	}()
	if err := c.checkOpen(); err != nil {
		return 0, err
	}
	uint8Array := js.Global().Get("Uint8Array").New(len(b))
	for i, bi := range b {
		uint8Array.SetIndex(i, bi)
	}
	c.Call("send", uint8Array.Get("buffer"))
	return len(b), nil
}

// Close closes the connection. Only the first call to Close will receive the
// close error, subsequent and concurrent calls will return nil.
// This method is thread-safe.
func (c *Conn) Close() error {
	c.closeOnce.Do(func() {
		c.signalClose()
		c.Call("close")
		c.releaseHandlers()
	})
	return nil
}

func (c *Conn) signalClose() {
	c.closeSignalOnce.Do(func() {
		close(c.closeSignal)
	})
}

func (c *Conn) releaseHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		c.Call("removeEventListener", "message", *c.messageHandler)
		c.messageHandler.Release()
		c.messageHandler = nil
	}
	if c.closeHandler != nil {
		c.Call("removeEventListener", "close", *c.closeHandler)
		c.closeHandler.Release()
		c.closeHandler = nil
	}
	if c.errorHandler != nil {
		c.Call("removeEventListener", "error", *c.errorHandler)
		c.errorHandler.Release()
		c.errorHandler = nil
	}
}

func (c *Conn) LocalAddr() net.Addr {
	return c.localAddr
}

func getRemoteAddr(val js.Value) net.Addr {
	rawURL := val.Get("url").String()
	withoutPrefix := strings.TrimPrefix(rawURL, "ws://")
	withoutSuffix := strings.TrimSuffix(withoutPrefix, "/")
	return NewAddr(withoutSuffix)
}

func (c *Conn) RemoteAddr() net.Addr {
	return c.remoteAddr
}

// TODO: Return os.ErrNoDeadline. For now we return nil because multiplexers
// don't handle the error correctly.
func (c *Conn) SetDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetReadDeadline(t time.Time) error {
	return nil
}

func (c *Conn) SetWriteDeadline(t time.Time) error {
	return nil
}

func (c *Conn) setUpHandlers() {
	c.mut.Lock()
	defer c.mut.Unlock()
	if c.messageHandler != nil {
		// Message handlers already created. Nothing to do.
		return
	}
	messageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		arrayBuffer := args[0].Get("data")
		data := arrayBufferToBytes(arrayBuffer)
		c.currDataMut.Lock()
		if _, err := c.currData.Write(data); err != nil {
			c.currDataMut.Unlock()
			return err
		}
		c.currDataMut.Unlock()

		// Non-blocking signal
		select {
		case c.dataSignal <- struct{}{}:
		default:
		}

		return nil
	})
	c.messageHandler = &messageHandler
	c.Call("addEventListener", "message", messageHandler)

	closeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		go func() {
			c.signalClose()
			c.mut.Lock()
			// Store the error in c.firstErr. It will be returned by Read later on.
			c.firstErr = errorEventToError(args[0])
			c.mut.Unlock()
			c.releaseHandlers()
		}()
		return nil
	})
	c.closeHandler = &closeHandler
	c.Call("addEventListener", "close", closeHandler)

	errorHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Unfortunately, the "error" event doesn't appear to give us any useful
		// information. All we can do is close the connection.
		c.Close()
		return nil
	})
	c.errorHandler = &errorHandler
	c.Call("addEventListener", "error", errorHandler)
}

func (c *Conn) waitForOpen() error {
	openSignal := make(chan struct{})
	handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		close(openSignal)
		return nil
	})
	defer c.Call("removeEventListener", "open", handler)
	defer handler.Release()
	c.Call("addEventListener", "open", handler)
	select {
	case <-openSignal:
		return nil
	case <-c.closeSignal:
		// c.closeSignal means there was an error when trying to open the
		// connection.
		return c.firstErr
	}
}

// arrayBufferToBytes converts a JavaScript ArrayBuffer to a slice of bytes.
func arrayBufferToBytes(buffer js.Value) []byte {
	view := js.Global().Get("Uint8Array").New(buffer)
	dataLen := view.Get("length").Int()
	data := make([]byte, dataLen)
	for i := 0; i < dataLen; i++ {
		data[i] = byte(view.Index(i).Int())
	}
	return data
}

func errorEventToError(val js.Value) error {
	var typ string
	if gotType := val.Get("type"); gotType != js.Undefined() {
		typ = gotType.String()
	} else {
		typ = val.Type().String()
	}
	var reason string
	if gotReason := val.Get("reason"); gotReason != js.Undefined() && gotReason.String() != "" {
		reason = gotReason.String()
	} else {
		code := val.Get("code")
		if code != js.Undefined() {
			switch code := code.Int(); code {
			case 1006:
				reason = "code 1006: connection unexpectedly closed"
			default:
				reason = fmt.Sprintf("unexpected code: %d", code)
			}
		}
	}
	return fmt.Errorf("JavaScript error: (%s) %s", typ, reason)
}

func recoveredValueToError(e interface{}) error {
	switch e := e.(type) {
	case error:
		return e
	default:
		return fmt.Errorf("recovered from unexpected panic: %T %s", e, e)
	}
}
<|endoftext|>


<commit_before>package version

const (
	Name      = "cf-stackdriver-nozzle"
	Release   = "0.0.0"
	UserAgent = Name + "/" + Release
)
<commit_msg>Bump version for tile release<commit_after>package version

const (
	Name      = "cf-stackdriver-nozzle"
	Release   = "0.0.1"
	UserAgent = Name + "/" + Release
)
<|endoftext|>


<commit_before>package erasure

import (
	"bytes"
	"math/rand"
	"sort"
	"testing"
)

func corrupt(source, errList []byte, vectorLength int) []byte {
	corrupted := make([]byte, len(source))
	copy(corrupted, source)
	for _, err := range errList {
		for i := 0; i < vectorLength; i++ {
			corrupted[int(err)*vectorLength+i] = 0x00
		}
	}
	return corrupted
}

func randomErrorList(m, numberOfErrs int) []byte {
	set := make(map[int]bool, m)
	errListInts := make([]int, numberOfErrs)
	for i := 0; i < numberOfErrs; i++ {
		err := rand.Intn(m)
		for set[err] {
			err = rand.Intn(m)
		}
		set[err] = true
		errListInts[i] = err
	}

	sort.Ints(errListInts)

	errList := make([]byte, numberOfErrs)
	for i, err := range errListInts {
		errList[i] = byte(err)
	}

	return errList
}

func TestBasicErasure_12_8(t *testing.T) {
	m := 12
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := []byte{0, 2, 3, 4}

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestBasicErasure_16_8(t *testing.T) {
	m := 16
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := []byte{0, 1, 2, 3, 4, 5, 6, 7}

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 8 errors")
	}
}

func TestBasicErasure_20_8(t *testing.T) {
	m := 20
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17}

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestBasicErasure_9_5(t *testing.T) {
	m := 9
	k := 5
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := []byte{0, 2, 3, 4}

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestRandomErasure_12_8(t *testing.T) {
	m := 12
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := randomErrorList(m, m-k)

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestRandomErasure_16_8(t *testing.T) {
	m := 16
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := randomErrorList(m, m-k)

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 8 errors")
	}
}

func TestRandomErasure_20_8(t *testing.T) {
	m := 20
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := randomErrorList(m, m-k)

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestRandomErasure_9_5(t *testing.T) {
	m := 9
	k := 5
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := randomErrorList(m, m-k)

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}
<commit_msg>Add benchmark tests<commit_after>package erasure

import (
	"bytes"
	"math/rand"
	"sort"
	"testing"
)

func corrupt(source, errList []byte, vectorLength int) []byte {
	corrupted := make([]byte, len(source))
	copy(corrupted, source)
	for _, err := range errList {
		for i := 0; i < vectorLength; i++ {
			corrupted[int(err)*vectorLength+i] = 0x00
		}
	}
	return corrupted
}

func randomErrorList(m, numberOfErrs int) []byte {
	set := make(map[int]bool, m)
	errListInts := make([]int, numberOfErrs)
	for i := 0; i < numberOfErrs; i++ {
		err := rand.Intn(m)
		for set[err] {
			err = rand.Intn(m)
		}
		set[err] = true
		errListInts[i] = err
	}

	sort.Ints(errListInts)

	errList := make([]byte, numberOfErrs)
	for i, err := range errListInts {
		errList[i] = byte(err)
	}

	return errList
}

func TestBasicErasure_12_8(t *testing.T) {
	m := 12
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := []byte{0, 2, 3, 4}

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestBasicErasure_16_8(t *testing.T) {
	m := 16
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := []byte{0, 1, 2, 3, 4, 5, 6, 7}

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 8 errors")
	}
}

func TestBasicErasure_20_8(t *testing.T) {
	m := 20
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17}

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestBasicErasure_9_5(t *testing.T) {
	m := 9
	k := 5
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := []byte{0, 2, 3, 4}

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestRandomErasure_12_8(t *testing.T) {
	m := 12
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := randomErrorList(m, m-k)

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestRandomErasure_16_8(t *testing.T) {
	m := 16
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := randomErrorList(m, m-k)

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 8 errors")
	}
}

func TestRandomErasure_20_8(t *testing.T) {
	m := 20
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := randomErrorList(m, m-k)

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func TestRandomErasure_9_5(t *testing.T) {
	m := 9
	k := 5
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := randomErrorList(m, m-k)

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	recovered := code.Decode(corrupted, errList)

	if !bytes.Equal(source, recovered) {
		t.Error("Source was not sucessfully recovered with 4 errors")
	}
}

func BenchmarkEncode_12_8(b *testing.B) {
	m := 12
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	b.ResetTimer()

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			code.Encode(source)
		}
	})
}

func BenchmarkDecode_12_8(b *testing.B) {
	m := 12
	k := 8
	vectorLength := 16
	size := k * vectorLength

	code := NewCode(m, k, size)

	source := make([]byte, size)
	for i := range source {
		source[i] = byte(rand.Int63() & 0xff) //0x62
	}

	encoded := code.Encode(source)

	errList := []byte{0, 2, 3, 4}

	corrupted := corrupt(append(source, encoded...), errList, vectorLength)

	b.ResetTimer()

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			recovered := code.Decode(corrupted, errList)

			if !bytes.Equal(source, recovered) {
				b.Error("Source was not sucessfully recovered with 4 errors")
			}
		}
	})
}
<|endoftext|>


<commit_before>// Package inject 提供类似Guice Injector的依赖注入功能,可以用于简化服务的组装
package inject

import (
	"fmt"
	"reflect"
	"strings"

	c "github.com/d0ngw/go/common"
)

// Injector 类似Guice的注入器
// 绑定约束:相同类型的实例和名称在一个Injector中必须唯一
// Injector对于struct中可导出的字段进行注入,需要注入的字段应该使用inject tag进行说明
//  type InjectStruct struct{
//   s ServiceInterface{} `inject:"<name>,[optional]"`
//  }
// inject格式:
//  name: 绑定的名称,必须的,如果没有名称,使用`_`代替
//  optional: 标识可选的注入,即不是必须注入
type Injector struct {
	ununamed []*internalBind            //未命名的绑定
	named    map[string][]*internalBind //命名的绑定
	all      []*internalBind            //所有的绑定
}

// Inject tag常量
const (
	injectTagNoname = "_" //无名称注入
)

func (p *Injector) String() string {
	return fmt.Sprintf("Injector,unnamed binds:%v,named binds:%v", len(p.ununamed), len(p.named))
}

// NewInjector 创建一个Injector
func NewInjector(modules []*Module) *Injector {
	injector := &Injector{}
	injectorModule := NewModule()
	injectorModule.Bind(injector)
	modules = append(modules, injectorModule)
	unnamed, named, all := mergeBinds(modules)
	injector.ununamed = unnamed
	injector.named = named
	injector.all = all
	injector.injectModules()
	return injector
}

// Initialize 初始化injector
func (p *Injector) Initialize() error {
	// 执行初始化操作
	inits := p.GetInstancesByPrototype(struct{ s c.Initable }{})
	for _, init := range inits {
		if service, ok := init.(c.Service); ok {
			if !c.ServiceInit(service) {
				return fmt.Errorf("Init %s fail", service.Name())
			}
		} else {
			if err := init.(c.Initable).Init(); err != nil {
				return fmt.Errorf("Init %T fail,err:%s", init, err)
			}

		}
	}
	return nil
}

// Start 启动服务
func (p *Injector) Start() error {
	return p.startOrStop(true)
}

// Stop 停止服务
func (p *Injector) Stop() error {
	return p.startOrStop(false)
}

func (p *Injector) startOrStop(start bool) error {
	var services []c.Service
	for _, service := range p.GetInstancesByPrototype(struct{ s c.Service }{}) {
		services = append(services, service.(c.Service))
	}
	if len(services) == 0 {
		c.Infof("No service found,skip")
		return nil
	}

	sortedServices := c.NewServices(services, start)
	if start {
		if !sortedServices.Start() {
			return fmt.Errorf("Start servcie fail")
		}
	} else {
		if !sortedServices.Stop() {
			return fmt.Errorf("Stop servcie fail")
		}
	}
	return nil
}

// RequireInject 由Injector向targets注入服务
func (p *Injector) RequireInject(targets ...interface{}) {
	for _, target := range targets {
		p.injectInstance(target)
	}
}

// RequireInjectWithOverrideTags 由Injector向target注入服务,injectTags用于覆盖target struct字段中定义的inject tag
func (p *Injector) RequireInjectWithOverrideTags(target interface{}, injectTags map[string]string) {
	p.injectInstanceWithOverrideTags(target, injectTags)
}

// GetInstance 从Injector中查找与name和expectedType匹配的实例,如果没有找到返回nil
func (p *Injector) GetInstance(name string, expectedType reflect.Type) interface{} {
	bind := p.findBind(name, expectedType)
	if bind == nil {
		return nil
	}
	return bind.instance
}

// GetInstanceByPrototype 从Injector重查找与name和prototype匹配的实例,如果没有找到返回nil
func (p *Injector) GetInstanceByPrototype(name string, prototype interface{}) interface{} {
	return p.GetInstance(name, getFieldType(prototype, 0))
}

// GetInstancesByPrototype 从Injector里查找与prototype匹配的所有实例
func (p *Injector) GetInstancesByPrototype(prototype interface{}) []interface{} {
	expectedType := getFieldType(prototype, 0)
	var ret = p.findAllBind(p.all, expectedType)
	var retInstances []interface{}
	for _, bind := range ret {
		retInstances = append(retInstances, bind.instance)
	}
	return retInstances
}

func (p *Injector) findAllBind(binds []*internalBind, expectedType reflect.Type) []*internalBind {
	var ret []*internalBind
	for _, bind := range binds {
		canAssign := bind.injectType.AssignableTo(expectedType)
		if canAssign {
			ret = append(ret, bind)
		}
	}
	return ret
}

// findBind 寻找一个可用的绑定
func (p *Injector) findBind(name string, expectedType reflect.Type) *internalBind {
	var toFind []*internalBind
	if len(name) != 0 {
		//按名称查找
		if binds, ok := p.named[name]; ok {
			toFind = binds
		}
	} else {
		toFind = p.ununamed
	}

	var ret []*internalBind
	for _, bind := range toFind {
		canAssign := bind.injectType.AssignableTo(expectedType)
		if canAssign {
			ret = append(ret, bind)
		}
	}

	if len(ret) == 0 {
		return nil
	} else if len(ret) > 1 {
		bindsDesc := make([]string, 0, len(ret))
		for _, bind := range ret {
			bindsDesc = append(bindsDesc, bind.String())
		}
		panic(fmt.Errorf("Duplicate for %v#%s,available binds:[%v]", expectedType, name, strings.Join(bindsDesc, ",")))
	} else {
		return ret[0]
	}
}

// injectInstance 向target注入实例
func (p *Injector) injectInstance(target interface{}) {
	p.injectInstanceWithOverrideTags(target, map[string]string{})
}

// injectInstanceWithOverrideTAgs 向target注入实例,injectTags用于覆盖target struct中定义的inject tag定义
func (p *Injector) injectInstanceWithOverrideTags(target interface{}, injectTags map[string]string) {
	if len(injectTags) == 0 {
		if ok, injected := IsInjected(target); ok && injected {
			//已经完成注入,直接返回
			c.Debugf("%T has been injected,skip", target)
			return
		}
	}
	val := reflect.ValueOf(target)
	ind := reflect.Indirect(val)
	typ := ind.Type()

	//仅struct的指针才能被注入
	if val.Kind() != reflect.Ptr || ind.Kind() != reflect.Struct {
		c.Debugf("Skip inject:%#v", target)
		return
	}

	fieldNum := ind.NumField()
	for i := 0; i < fieldNum; i++ {
		field := typ.Field(i)
		fieldVal := ind.Field(i)
		if !fieldVal.CanSet() {
			c.Debugf("skip inject filed %s", field.Name)
			continue
		}

		if reflect.Indirect(fieldVal).Kind() == reflect.Struct {
			if fieldVal.Kind() == reflect.Ptr {
				p.injectInstanceWithOverrideTags(fieldVal.Interface(), injectTags)
			} else if fieldVal.CanAddr() && fieldVal.CanInterface() {
				p.injectInstanceWithOverrideTags(fieldVal.Addr().Interface(), injectTags)
			}
			continue
		}
		structField := typ.Field(i)
		sfTag := structField.Tag

		tag, ok := injectTags[structField.Name]
		if !ok {
			tag = sfTag.Get("inject")
		}
		found, name, optional := parseInjectTag(tag)
		if !found {
			continue
		}

		if name == injectTagNoname {
			name = ""
		}

		foundBind := p.findBind(name, structField.Type)
		if foundBind == nil {
			if !optional {
				panic(fmt.Errorf("Can't find bind instance for %v.%s", typ, structField.Name))
			} else {
				c.Infof("skip optional %s %s ", name, structField.Type)
				continue
			}
		}

		if !fieldVal.CanSet() {
			panic(fmt.Errorf("Can't inject unexported  %v.%s", typ, structField.Name))
		}

		fieldInjectDesc := fmt.Sprintf("%s.%s", typ.Name(), structField.Name)
		c.Debugf("Inject %T#%s to %s", foundBind.instance, foundBind.name, fieldInjectDesc)
		if foundBind.injectValue.Kind() == reflect.Ptr && foundBind.injectValue.Pointer() == val.Pointer() {
			injectSrcDesc := fmt.Sprintf("%#v(addr:%p)", foundBind.instance, foundBind.instance)
			injectTargetDesc := fmt.Sprintf("%s of %#v(addr:%p)", fieldInjectDesc, target, target)
			panic(fmt.Errorf("Found circular inject,src:%s,target:%s", injectSrcDesc, injectTargetDesc))
		}
		fieldVal.Set(foundBind.injectValue)
	}
}

// injectModules 注入Injector中各个Module中的绑定
func (p *Injector) injectModules() {
	c.Debugf("Inject internal")
	for _, bind := range p.ununamed {
		p.injectInstanceWithOverrideTags(bind.instance, bind.injectTags)
	}

	for _, binds := range p.named {
		for _, bind := range binds {
			p.injectInstanceWithOverrideTags(bind.instance, bind.injectTags)
		}
	}
}

// getFieldType 取得struct中field的类型
func getFieldType(structObj interface{}, fieldIndex int) reflect.Type {
	val := reflect.Indirect(reflect.ValueOf(structObj))
	return val.Field(fieldIndex).Type()
}

// parseInjectTag 解析inject tag,inject tag的格式
func parseInjectTag(tag string) (found bool, name string, optional bool) {
	tag = strings.TrimSpace(tag)
	if len(tag) == 0 {
		return
	}

	vals := strings.Split(tag, ",")
	if len(vals) > 0 {
		found = true
		name = strings.TrimSpace(vals[0])
	}

	others := vals[1:]
	for _, o := range others {
		o = strings.ToLower(strings.TrimSpace(o))
		if o == "optional" {
			optional = true
		}
	}
	return
}
<commit_msg>add panic log<commit_after>// Package inject 提供类似Guice Injector的依赖注入功能,可以用于简化服务的组装
package inject

import (
	"fmt"
	"reflect"
	"strings"

	c "github.com/d0ngw/go/common"
)

// Injector 类似Guice的注入器
// 绑定约束:相同类型的实例和名称在一个Injector中必须唯一
// Injector对于struct中可导出的字段进行注入,需要注入的字段应该使用inject tag进行说明
//  type InjectStruct struct{
//   s ServiceInterface{} `inject:"<name>,[optional]"`
//  }
// inject格式:
//  name: 绑定的名称,必须的,如果没有名称,使用`_`代替
//  optional: 标识可选的注入,即不是必须注入
type Injector struct {
	ununamed []*internalBind            //未命名的绑定
	named    map[string][]*internalBind //命名的绑定
	all      []*internalBind            //所有的绑定
}

// Inject tag常量
const (
	injectTagNoname = "_" //无名称注入
)

func (p *Injector) String() string {
	return fmt.Sprintf("Injector,unnamed binds:%v,named binds:%v", len(p.ununamed), len(p.named))
}

// NewInjector 创建一个Injector
func NewInjector(modules []*Module) *Injector {
	injector := &Injector{}
	injectorModule := NewModule()
	injectorModule.Bind(injector)
	modules = append(modules, injectorModule)
	unnamed, named, all := mergeBinds(modules)
	injector.ununamed = unnamed
	injector.named = named
	injector.all = all
	injector.injectModules()
	return injector
}

// Initialize 初始化injector
func (p *Injector) Initialize() error {
	// 执行初始化操作
	inits := p.GetInstancesByPrototype(struct{ s c.Initable }{})
	for _, init := range inits {
		if service, ok := init.(c.Service); ok {
			if !c.ServiceInit(service) {
				return fmt.Errorf("Init %s fail", service.Name())
			}
		} else {
			if err := init.(c.Initable).Init(); err != nil {
				return fmt.Errorf("Init %T fail,err:%s", init, err)
			}

		}
	}
	return nil
}

// Start 启动服务
func (p *Injector) Start() error {
	return p.startOrStop(true)
}

// Stop 停止服务
func (p *Injector) Stop() error {
	return p.startOrStop(false)
}

func (p *Injector) startOrStop(start bool) error {
	var services []c.Service
	for _, service := range p.GetInstancesByPrototype(struct{ s c.Service }{}) {
		services = append(services, service.(c.Service))
	}
	if len(services) == 0 {
		c.Infof("No service found,skip")
		return nil
	}

	sortedServices := c.NewServices(services, start)
	if start {
		if !sortedServices.Start() {
			return fmt.Errorf("Start servcie fail")
		}
	} else {
		if !sortedServices.Stop() {
			return fmt.Errorf("Stop servcie fail")
		}
	}
	return nil
}

// RequireInject 由Injector向targets注入服务
func (p *Injector) RequireInject(targets ...interface{}) {
	for _, target := range targets {
		p.injectInstance(target)
	}
}

// RequireInjectWithOverrideTags 由Injector向target注入服务,injectTags用于覆盖target struct字段中定义的inject tag
func (p *Injector) RequireInjectWithOverrideTags(target interface{}, injectTags map[string]string) {
	p.injectInstanceWithOverrideTags(target, injectTags)
}

// GetInstance 从Injector中查找与name和expectedType匹配的实例,如果没有找到返回nil
func (p *Injector) GetInstance(name string, expectedType reflect.Type) interface{} {
	bind := p.findBind(name, expectedType)
	if bind == nil {
		return nil
	}
	return bind.instance
}

// GetInstanceByPrototype 从Injector重查找与name和prototype匹配的实例,如果没有找到返回nil
func (p *Injector) GetInstanceByPrototype(name string, prototype interface{}) interface{} {
	return p.GetInstance(name, getFieldType(prototype, 0))
}

// GetInstancesByPrototype 从Injector里查找与prototype匹配的所有实例
func (p *Injector) GetInstancesByPrototype(prototype interface{}) []interface{} {
	expectedType := getFieldType(prototype, 0)
	var ret = p.findAllBind(p.all, expectedType)
	var retInstances []interface{}
	for _, bind := range ret {
		retInstances = append(retInstances, bind.instance)
	}
	return retInstances
}

func (p *Injector) findAllBind(binds []*internalBind, expectedType reflect.Type) []*internalBind {
	var ret []*internalBind
	for _, bind := range binds {
		canAssign := bind.injectType.AssignableTo(expectedType)
		if canAssign {
			ret = append(ret, bind)
		}
	}
	return ret
}

// findBind 寻找一个可用的绑定
func (p *Injector) findBind(name string, expectedType reflect.Type) *internalBind {
	var toFind []*internalBind
	if len(name) != 0 {
		//按名称查找
		if binds, ok := p.named[name]; ok {
			toFind = binds
		}
	} else {
		toFind = p.ununamed
	}

	var ret []*internalBind
	for _, bind := range toFind {
		canAssign := bind.injectType.AssignableTo(expectedType)
		if canAssign {
			ret = append(ret, bind)
		}
	}

	if len(ret) == 0 {
		return nil
	} else if len(ret) > 1 {
		bindsDesc := make([]string, 0, len(ret))
		for _, bind := range ret {
			bindsDesc = append(bindsDesc, bind.String())
		}
		panic(fmt.Errorf("Duplicate for %v#%s,available binds:[%v]", expectedType, name, strings.Join(bindsDesc, ",")))
	} else {
		return ret[0]
	}
}

// injectInstance 向target注入实例
func (p *Injector) injectInstance(target interface{}) {
	p.injectInstanceWithOverrideTags(target, map[string]string{})
}

// injectInstanceWithOverrideTAgs 向target注入实例,injectTags用于覆盖target struct中定义的inject tag定义
func (p *Injector) injectInstanceWithOverrideTags(target interface{}, injectTags map[string]string) {
	if len(injectTags) == 0 {
		if ok, injected := IsInjected(target); ok && injected {
			//已经完成注入,直接返回
			c.Debugf("%T has been injected,skip", target)
			return
		}
	}
	val := reflect.ValueOf(target)
	ind := reflect.Indirect(val)
	typ := ind.Type()

	//仅struct的指针才能被注入
	if val.Kind() != reflect.Ptr || ind.Kind() != reflect.Struct {
		c.Debugf("Skip inject:%#v", target)
		return
	}

	fieldNum := ind.NumField()
	for i := 0; i < fieldNum; i++ {
		field := typ.Field(i)
		fieldVal := ind.Field(i)
		if !fieldVal.CanSet() {
			c.Debugf("skip inject filed %s", field.Name)
			continue
		}

		if reflect.Indirect(fieldVal).Kind() == reflect.Struct {
			if fieldVal.Kind() == reflect.Ptr {
				p.injectInstanceWithOverrideTags(fieldVal.Interface(), injectTags)
			} else if fieldVal.CanAddr() && fieldVal.CanInterface() {
				p.injectInstanceWithOverrideTags(fieldVal.Addr().Interface(), injectTags)
			}
			continue
		}
		structField := typ.Field(i)
		sfTag := structField.Tag

		tag, ok := injectTags[structField.Name]
		if !ok {
			tag = sfTag.Get("inject")
		}
		found, name, optional := parseInjectTag(tag)
		if !found {
			continue
		}

		if name == injectTagNoname {
			name = ""
		}

		foundBind := p.findBind(name, structField.Type)
		if foundBind == nil {
			if !optional {
				panic(fmt.Errorf("Can't find bind instance for %v.%s with name %s", typ, structField.Name, name))
			} else {
				c.Infof("skip optional %s %s ", name, structField.Type)
				continue
			}
		}

		if !fieldVal.CanSet() {
			panic(fmt.Errorf("Can't inject unexported  %v.%s", typ, structField.Name))
		}

		fieldInjectDesc := fmt.Sprintf("%s.%s", typ.Name(), structField.Name)
		c.Debugf("Inject %T#%s to %s", foundBind.instance, foundBind.name, fieldInjectDesc)
		if foundBind.injectValue.Kind() == reflect.Ptr && foundBind.injectValue.Pointer() == val.Pointer() {
			injectSrcDesc := fmt.Sprintf("%#v(addr:%p)", foundBind.instance, foundBind.instance)
			injectTargetDesc := fmt.Sprintf("%s of %#v(addr:%p)", fieldInjectDesc, target, target)
			panic(fmt.Errorf("Found circular inject,src:%s,target:%s", injectSrcDesc, injectTargetDesc))
		}
		fieldVal.Set(foundBind.injectValue)
	}
}

// injectModules 注入Injector中各个Module中的绑定
func (p *Injector) injectModules() {
	c.Debugf("Inject internal")
	for _, bind := range p.ununamed {
		p.injectInstanceWithOverrideTags(bind.instance, bind.injectTags)
	}

	for _, binds := range p.named {
		for _, bind := range binds {
			p.injectInstanceWithOverrideTags(bind.instance, bind.injectTags)
		}
	}
}

// getFieldType 取得struct中field的类型
func getFieldType(structObj interface{}, fieldIndex int) reflect.Type {
	val := reflect.Indirect(reflect.ValueOf(structObj))
	return val.Field(fieldIndex).Type()
}

// parseInjectTag 解析inject tag,inject tag的格式
func parseInjectTag(tag string) (found bool, name string, optional bool) {
	tag = strings.TrimSpace(tag)
	if len(tag) == 0 {
		return
	}

	vals := strings.Split(tag, ",")
	if len(vals) > 0 {
		found = true
		name = strings.TrimSpace(vals[0])
	}

	others := vals[1:]
	for _, o := range others {
		o = strings.ToLower(strings.TrimSpace(o))
		if o == "optional" {
			optional = true
		}
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package csv reads and writes comma-separated values (CSV) files.
//
// A csv file contains zero or more records of one or more fields per record.
// Each record is separated by the newline character. The final record may
// optionally be followed by a newline character.
//
//	field1,field2,field3
//
// White space is considered part of a field.
//
// Carriage returns before newline characters are silently removed.
//
// Blank lines are ignored. A line with only whitespace characters (excluding
// the ending newline character) is not considered a blank line.
//
// Fields which start and stop with the quote character " are called
// quoted-fields. The beginning and ending quote are not part of the
// field.
//
// The source:
//
//	normal string,"quoted-field"
//
// results in the fields
//
//	{`normal string`, `quoted-field`}
//
// Within a quoted-field a quote character followed by a second quote
// character is considered a single quote.
//
//	"the ""word"" is true","a ""quoted-field"""
//
// results in
//
//	{`the "word" is true`, `a "quoted-field"`}
//
// Newlines and commas may be included in a quoted-field
//
//	"Multi-line
//	field","comma is ,"
//
// results in
//
//	{`Multi-line
//	field`, `comma is ,`}
package csv

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"unicode"
)

// A ParseError is returned for parsing errors.
// The first line is 1.  The first column is 0.
type ParseError struct {
	Line   int   // Line where the error occurred
	Column int   // Column (rune index) where the error occurred
	Err    error // The actual error
}

func (e *ParseError) Error() string {
	return fmt.Sprintf("line %d, column %d: %s", e.Line, e.Column, e.Err)
}

// These are the errors that can be returned in ParseError.Error
var (
	ErrTrailingComma = errors.New("extra delimiter at end of line") // no longer used
	ErrBareQuote     = errors.New("bare \" in non-quoted-field")
	ErrQuote         = errors.New("extraneous \" in field")
	ErrFieldCount    = errors.New("wrong number of fields in line")
)

// A Reader reads records from a CSV-encoded file.
//
// As returned by NewReader, a Reader expects input conforming to RFC 4180.
// The exported fields can be changed to customize the details before the
// first call to Read or ReadAll.
//
// Comma is the field delimiter. It defaults to ','.
//
// Comment, if not 0, is the comment character. Lines beginning with the
// Comment character are ignored.
//
// If FieldsPerRecord is positive, Read requires each record to
// have the given number of fields. If FieldsPerRecord is 0, Read sets it to
// the number of fields in the first record, so that future records must
// have the same field count. If FieldsPerRecord is negative, no check is
// made and records may have a variable number of fields.
//
// If LazyQuotes is true, a quote may appear in an unquoted field and a
// non-doubled quote may appear in a quoted field.
//
// If TrimLeadingSpace is true, leading white space in a field is ignored.
// If the field delimiter is white space, TrimLeadingSpace will trim the
// delimiter.
type Reader struct {
	Comma            rune // field delimiter (set to ',' by NewReader)
	Comment          rune // comment character for start of line
	FieldsPerRecord  int  // number of expected fields per record
	LazyQuotes       bool // allow lazy quotes
	TrailingComma    bool // ignored; here for backwards compatibility
	TrimLeadingSpace bool // trim leading space
	line             int
	column           int
	r                *bufio.Reader
	field            bytes.Buffer
}

// NewReader returns a new Reader that reads from r.
func NewReader(r io.Reader) *Reader {
	return &Reader{
		Comma: ',',
		r:     bufio.NewReader(r),
	}
}

// error creates a new ParseError based on err.
func (r *Reader) error(err error) error {
	return &ParseError{
		Line:   r.line,
		Column: r.column,
		Err:    err,
	}
}

// Read reads one record from r. The record is a slice of strings with each
// string representing one field.
func (r *Reader) Read() (record []string, err error) {
	for {
		record, err = r.parseRecord()
		if record != nil {
			break
		}
		if err != nil {
			return nil, err
		}
	}

	if r.FieldsPerRecord > 0 {
		if len(record) != r.FieldsPerRecord {
			r.column = 0 // report at start of record
			return record, r.error(ErrFieldCount)
		}
	} else if r.FieldsPerRecord == 0 {
		r.FieldsPerRecord = len(record)
	}
	return record, nil
}

// ReadAll reads all the remaining records from r.
// Each record is a slice of fields.
// A successful call returns err == nil, not err == io.EOF. Because ReadAll is
// defined to read until EOF, it does not treat end of file as an error to be
// reported.
func (r *Reader) ReadAll() (records [][]string, err error) {
	for {
		record, err := r.Read()
		if err == io.EOF {
			return records, nil
		}
		if err != nil {
			return nil, err
		}
		records = append(records, record)
	}
}

// readRune reads one rune from r, folding \r\n to \n and keeping track
// of how far into the line we have read.  r.column will point to the start
// of this rune, not the end of this rune.
func (r *Reader) readRune() (rune, error) {
	r1, _, err := r.r.ReadRune()

	// Handle \r\n here. We make the simplifying assumption that
	// anytime \r is followed by \n that it can be folded to \n.
	// We will not detect files which contain both \r\n and bare \n.
	if r1 == '\r' {
		r1, _, err = r.r.ReadRune()
		if err == nil {
			if r1 != '\n' {
				r.r.UnreadRune()
				r1 = '\r'
			}
		}
	}
	r.column++
	return r1, err
}

// skip reads runes up to and including the rune delim or until error.
func (r *Reader) skip(delim rune) error {
	for {
		r1, err := r.readRune()
		if err != nil {
			return err
		}
		if r1 == delim {
			return nil
		}
	}
}

// parseRecord reads and parses a single csv record from r.
func (r *Reader) parseRecord() (fields []string, err error) {
	// Each record starts on a new line. We increment our line
	// number (lines start at 1, not 0) and set column to -1
	// so as we increment in readRune it points to the character we read.
	r.line++
	r.column = -1

	// Peek at the first rune. If it is an error we are done.
	// If we support comments and it is the comment character
	// then skip to the end of line.

	r1, _, err := r.r.ReadRune()
	if err != nil {
		return nil, err
	}

	if r.Comment != 0 && r1 == r.Comment {
		return nil, r.skip('\n')
	}
	r.r.UnreadRune()

	// At this point we have at least one field.
	for {
		haveField, delim, err := r.parseField()
		if haveField {
			// If FieldsPerRecord is greater than 0 we can assume the final
			// length of fields to be equal to FieldsPerRecord.
			if r.FieldsPerRecord > 0 && fields == nil {
				fields = make([]string, 0, r.FieldsPerRecord)
			}
			fields = append(fields, r.field.String())
		}
		if delim == '\n' || err == io.EOF {
			return fields, err
		} else if err != nil {
			return nil, err
		}
	}
}

// parseField parses the next field in the record. The read field is
// located in r.field. Delim is the first character not part of the field
// (r.Comma or '\n').
func (r *Reader) parseField() (haveField bool, delim rune, err error) {
	r.field.Reset()

	r1, err := r.readRune()
	for err == nil && r.TrimLeadingSpace && r1 != '\n' && unicode.IsSpace(r1) {
		r1, err = r.readRune()
	}

	if err == io.EOF && r.column != 0 {
		return true, 0, err
	}
	if err != nil {
		return false, 0, err
	}

	switch r1 {
	case r.Comma:
		// will check below

	case '\n':
		// We are a trailing empty field or a blank line
		if r.column == 0 {
			return false, r1, nil
		}
		return true, r1, nil

	case '"':
		// quoted field
	Quoted:
		for {
			r1, err = r.readRune()
			if err != nil {
				if err == io.EOF {
					if r.LazyQuotes {
						return true, 0, err
					}
					return false, 0, r.error(ErrQuote)
				}
				return false, 0, err
			}
			switch r1 {
			case '"':
				r1, err = r.readRune()
				if err != nil || r1 == r.Comma {
					break Quoted
				}
				if r1 == '\n' {
					return true, r1, nil
				}
				if r1 != '"' {
					if !r.LazyQuotes {
						r.column--
						return false, 0, r.error(ErrQuote)
					}
					// accept the bare quote
					r.field.WriteRune('"')
				}
			case '\n':
				r.line++
				r.column = -1
			}
			r.field.WriteRune(r1)
		}

	default:
		// unquoted field
		for {
			r.field.WriteRune(r1)
			r1, err = r.readRune()
			if err != nil || r1 == r.Comma {
				break
			}
			if r1 == '\n' {
				return true, r1, nil
			}
			if !r.LazyQuotes && r1 == '"' {
				return false, 0, r.error(ErrBareQuote)
			}
		}
	}

	if err != nil {
		if err == io.EOF {
			return true, 0, err
		}
		return false, 0, err
	}

	return true, r1, nil
}
<commit_msg>encoding/csv: clarify that this package supports RFC 4180<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package csv reads and writes comma-separated values (CSV) files.
// There are many kinds of CSV files; this package supports the format
// described in RFC 4180.
//
// A csv file contains zero or more records of one or more fields per record.
// Each record is separated by the newline character. The final record may
// optionally be followed by a newline character.
//
//	field1,field2,field3
//
// White space is considered part of a field.
//
// Carriage returns before newline characters are silently removed.
//
// Blank lines are ignored. A line with only whitespace characters (excluding
// the ending newline character) is not considered a blank line.
//
// Fields which start and stop with the quote character " are called
// quoted-fields. The beginning and ending quote are not part of the
// field.
//
// The source:
//
//	normal string,"quoted-field"
//
// results in the fields
//
//	{`normal string`, `quoted-field`}
//
// Within a quoted-field a quote character followed by a second quote
// character is considered a single quote.
//
//	"the ""word"" is true","a ""quoted-field"""
//
// results in
//
//	{`the "word" is true`, `a "quoted-field"`}
//
// Newlines and commas may be included in a quoted-field
//
//	"Multi-line
//	field","comma is ,"
//
// results in
//
//	{`Multi-line
//	field`, `comma is ,`}
package csv

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"unicode"
)

// A ParseError is returned for parsing errors.
// The first line is 1.  The first column is 0.
type ParseError struct {
	Line   int   // Line where the error occurred
	Column int   // Column (rune index) where the error occurred
	Err    error // The actual error
}

func (e *ParseError) Error() string {
	return fmt.Sprintf("line %d, column %d: %s", e.Line, e.Column, e.Err)
}

// These are the errors that can be returned in ParseError.Error
var (
	ErrTrailingComma = errors.New("extra delimiter at end of line") // no longer used
	ErrBareQuote     = errors.New("bare \" in non-quoted-field")
	ErrQuote         = errors.New("extraneous \" in field")
	ErrFieldCount    = errors.New("wrong number of fields in line")
)

// A Reader reads records from a CSV-encoded file.
//
// As returned by NewReader, a Reader expects input conforming to RFC 4180.
// The exported fields can be changed to customize the details before the
// first call to Read or ReadAll.
//
// Comma is the field delimiter. It defaults to ','.
//
// Comment, if not 0, is the comment character. Lines beginning with the
// Comment character are ignored.
//
// If FieldsPerRecord is positive, Read requires each record to
// have the given number of fields. If FieldsPerRecord is 0, Read sets it to
// the number of fields in the first record, so that future records must
// have the same field count. If FieldsPerRecord is negative, no check is
// made and records may have a variable number of fields.
//
// If LazyQuotes is true, a quote may appear in an unquoted field and a
// non-doubled quote may appear in a quoted field.
//
// If TrimLeadingSpace is true, leading white space in a field is ignored.
// If the field delimiter is white space, TrimLeadingSpace will trim the
// delimiter.
type Reader struct {
	Comma            rune // field delimiter (set to ',' by NewReader)
	Comment          rune // comment character for start of line
	FieldsPerRecord  int  // number of expected fields per record
	LazyQuotes       bool // allow lazy quotes
	TrailingComma    bool // ignored; here for backwards compatibility
	TrimLeadingSpace bool // trim leading space
	line             int
	column           int
	r                *bufio.Reader
	field            bytes.Buffer
}

// NewReader returns a new Reader that reads from r.
func NewReader(r io.Reader) *Reader {
	return &Reader{
		Comma: ',',
		r:     bufio.NewReader(r),
	}
}

// error creates a new ParseError based on err.
func (r *Reader) error(err error) error {
	return &ParseError{
		Line:   r.line,
		Column: r.column,
		Err:    err,
	}
}

// Read reads one record from r. The record is a slice of strings with each
// string representing one field.
func (r *Reader) Read() (record []string, err error) {
	for {
		record, err = r.parseRecord()
		if record != nil {
			break
		}
		if err != nil {
			return nil, err
		}
	}

	if r.FieldsPerRecord > 0 {
		if len(record) != r.FieldsPerRecord {
			r.column = 0 // report at start of record
			return record, r.error(ErrFieldCount)
		}
	} else if r.FieldsPerRecord == 0 {
		r.FieldsPerRecord = len(record)
	}
	return record, nil
}

// ReadAll reads all the remaining records from r.
// Each record is a slice of fields.
// A successful call returns err == nil, not err == io.EOF. Because ReadAll is
// defined to read until EOF, it does not treat end of file as an error to be
// reported.
func (r *Reader) ReadAll() (records [][]string, err error) {
	for {
		record, err := r.Read()
		if err == io.EOF {
			return records, nil
		}
		if err != nil {
			return nil, err
		}
		records = append(records, record)
	}
}

// readRune reads one rune from r, folding \r\n to \n and keeping track
// of how far into the line we have read.  r.column will point to the start
// of this rune, not the end of this rune.
func (r *Reader) readRune() (rune, error) {
	r1, _, err := r.r.ReadRune()

	// Handle \r\n here. We make the simplifying assumption that
	// anytime \r is followed by \n that it can be folded to \n.
	// We will not detect files which contain both \r\n and bare \n.
	if r1 == '\r' {
		r1, _, err = r.r.ReadRune()
		if err == nil {
			if r1 != '\n' {
				r.r.UnreadRune()
				r1 = '\r'
			}
		}
	}
	r.column++
	return r1, err
}

// skip reads runes up to and including the rune delim or until error.
func (r *Reader) skip(delim rune) error {
	for {
		r1, err := r.readRune()
		if err != nil {
			return err
		}
		if r1 == delim {
			return nil
		}
	}
}

// parseRecord reads and parses a single csv record from r.
func (r *Reader) parseRecord() (fields []string, err error) {
	// Each record starts on a new line. We increment our line
	// number (lines start at 1, not 0) and set column to -1
	// so as we increment in readRune it points to the character we read.
	r.line++
	r.column = -1

	// Peek at the first rune. If it is an error we are done.
	// If we support comments and it is the comment character
	// then skip to the end of line.

	r1, _, err := r.r.ReadRune()
	if err != nil {
		return nil, err
	}

	if r.Comment != 0 && r1 == r.Comment {
		return nil, r.skip('\n')
	}
	r.r.UnreadRune()

	// At this point we have at least one field.
	for {
		haveField, delim, err := r.parseField()
		if haveField {
			// If FieldsPerRecord is greater than 0 we can assume the final
			// length of fields to be equal to FieldsPerRecord.
			if r.FieldsPerRecord > 0 && fields == nil {
				fields = make([]string, 0, r.FieldsPerRecord)
			}
			fields = append(fields, r.field.String())
		}
		if delim == '\n' || err == io.EOF {
			return fields, err
		} else if err != nil {
			return nil, err
		}
	}
}

// parseField parses the next field in the record. The read field is
// located in r.field. Delim is the first character not part of the field
// (r.Comma or '\n').
func (r *Reader) parseField() (haveField bool, delim rune, err error) {
	r.field.Reset()

	r1, err := r.readRune()
	for err == nil && r.TrimLeadingSpace && r1 != '\n' && unicode.IsSpace(r1) {
		r1, err = r.readRune()
	}

	if err == io.EOF && r.column != 0 {
		return true, 0, err
	}
	if err != nil {
		return false, 0, err
	}

	switch r1 {
	case r.Comma:
		// will check below

	case '\n':
		// We are a trailing empty field or a blank line
		if r.column == 0 {
			return false, r1, nil
		}
		return true, r1, nil

	case '"':
		// quoted field
	Quoted:
		for {
			r1, err = r.readRune()
			if err != nil {
				if err == io.EOF {
					if r.LazyQuotes {
						return true, 0, err
					}
					return false, 0, r.error(ErrQuote)
				}
				return false, 0, err
			}
			switch r1 {
			case '"':
				r1, err = r.readRune()
				if err != nil || r1 == r.Comma {
					break Quoted
				}
				if r1 == '\n' {
					return true, r1, nil
				}
				if r1 != '"' {
					if !r.LazyQuotes {
						r.column--
						return false, 0, r.error(ErrQuote)
					}
					// accept the bare quote
					r.field.WriteRune('"')
				}
			case '\n':
				r.line++
				r.column = -1
			}
			r.field.WriteRune(r1)
		}

	default:
		// unquoted field
		for {
			r.field.WriteRune(r1)
			r1, err = r.readRune()
			if err != nil || r1 == r.Comma {
				break
			}
			if r1 == '\n' {
				return true, r1, nil
			}
			if !r.LazyQuotes && r1 == '"' {
				return false, 0, r.error(ErrBareQuote)
			}
		}
	}

	if err != nil {
		if err == io.EOF {
			return true, 0, err
		}
		return false, 0, err
	}

	return true, r1, nil
}
<|endoftext|>


<commit_before>package imagick

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/gographics/imagick/imagick"
	"github.com/pressly/imgry"
)

var (
	ErrEngineReleased = errors.New("imagick: engine has been released.")
	ErrEngineFailure  = errors.New("imagick: unable to request a MagickWand")
)

type Engine struct {
	tmpDir string
	// TODO: perhaps we have counter of wands here..
	// it ensures we'll always release them..
	// AvailableWands .. .. set to MaxSizers ..
	// then, NewMagickWand() will limit, or error..
	// perhaps we make a channel of these things..
	// then block.. + ctx will have a timeout if it wants to stop waiting..
	// we can log these timeouts etc..
}

func (ng Engine) Version() string {
	v, _ := imagick.GetVersion()
	return fmt.Sprintf("%s", v)
}

func (ng Engine) Initialize(tmpDir string) error {
	if tmpDir != "" {
		if err := os.MkdirAll(tmpDir, 0755); err != nil {
			return err
		}
		ng.tmpDir = tmpDir
		os.Setenv("MAGICK_TMPDIR", tmpDir)
		ng.SweepTmpDir()
	}
	imagick.Initialize()
	return nil
}

func (ng Engine) Terminate() {
	imagick.Terminate()
	ng.SweepTmpDir()
}

func (ng Engine) SweepTmpDir() error {
	if ng.tmpDir == "" {
		return nil
	}
	err := filepath.Walk(
		ng.tmpDir,
		func(path string, info os.FileInfo, err error) error {
			if ng.tmpDir == path {
				return nil // skip the root
			}
			if strings.Index(filepath.Base(path), "magick") >= 0 {
				if err = os.Remove(path); err != nil {
					return fmt.Errorf("failed to sweet engine tmpdir %s, because: %s", path, err)
				}
			}
			return nil
		},
	)
	if err != nil {
		return err
	}
	return nil
}

func (ng Engine) LoadFile(filename string, srcFormat ...string) (imgry.Image, error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return ng.LoadBlob(b, srcFormat...)
}

func (ng Engine) LoadBlob(b []byte, srcFormat ...string) (imgry.Image, error) {
	if len(b) == 0 {
		return nil, imgry.ErrInvalidImageData
	}

	mw := imagick.NewMagickWand()
	if !mw.IsVerified() {
		return nil, ErrEngineFailure
	}

	// Offer a hint to the decoder of the file format
	if len(srcFormat) > 0 {
		f := srcFormat[0]
		if f != "" {
			mw.SetFormat(f)
		}
	}

	err := mw.ReadImageBlob(b)
	if err != nil {
		mw.Destroy()
		return nil, imgry.ErrInvalidImageData
	}

	// TODO: perhaps we pass the engine instance like Image{engine: i}
	im := &Image{mw: mw, data: b}
	if err := im.sync(); err != nil {
		mw.Destroy()
		return nil, err
	}

	return im, nil
}

func (ng Engine) GetImageInfo(b []byte, srcFormat ...string) (*imgry.ImageInfo, error) {
	if len(b) == 0 {
		return nil, imgry.ErrInvalidImageData
	}

	mw := imagick.NewMagickWand()
	defer mw.Destroy()

	if !mw.IsVerified() {
		return nil, ErrEngineFailure
	}

	err := mw.PingImageBlob(b)
	if err != nil {
		return nil, imgry.ErrInvalidImageData
	}

	w, h := int(mw.GetImageWidth()), int(mw.GetImageHeight())
	ar := float64(int(float64(w)/float64(h)*10000)) / 10000

	format := strings.ToLower(mw.GetImageFormat())
	if format == "jpeg" {
		format = "jpg"
	}

	imfo := &imgry.ImageInfo{
		Format: format, Width: w, Height: h,
		AspectRatio: ar, ContentLength: len(b),
	}

	return imfo, nil
}

type Image struct {
	mw *imagick.MagickWand

	data   []byte
	width  int
	height int
	format string
}

func (i *Image) Data() []byte {
	return i.data
}

func (i *Image) Width() int {
	return i.width
}

func (i *Image) Height() int {
	return i.height
}

func (i *Image) Format() string {
	return i.format
}

func (i *Image) SetFormat(format string) error {
	if i.Released() {
		return ErrEngineReleased
	}
	if err := i.mw.SetImageFormat(format); err != nil {
		return err
	}
	if err := i.sync(); err != nil {
		return err
	}
	return nil
}

func (i *Image) Released() bool {
	return i.mw == nil
}

func (i *Image) Release() {
	if i.mw != nil {
		i.mw.Destroy()
		i.mw = nil
	}
}

func (i *Image) Clone() imgry.Image {
	i2 := &Image{}
	i2.data = i.data
	i2.width = i.width
	i2.height = i.height
	i2.format = i.format
	if i.mw != nil && i.mw.IsVerified() {
		i2.mw = i.mw.Clone()
	}
	return i2
}

func (i *Image) SizeIt(sz *imgry.Sizing) error {
	if i.Released() {
		return ErrEngineReleased
	}

	if err := i.sizeFrames(sz); err != nil {
		return err
	}

	if sz.Format != "" {
		if err := i.mw.SetFormat(sz.Format); err != nil {
			return err
		}
	}

	if sz.Quality > 0 {
		if err := i.mw.SetImageCompressionQuality(uint(sz.Quality)); err != nil {
			return err
		}
	}

	if err := i.sync(sz.Flatten); err != nil {
		return err
	}

	return nil
}

func (i *Image) sizeFrames(sz *imgry.Sizing) error {
	// Shortcut if there is nothing to size
	if sz.Size.Equal(imgry.ZeroRect) && sz.CropBox.Equal(imgry.ZeroFloatingRect) {
		return nil
	}

	numberImages := i.mw.GetNumberImages()

	// TODO: instead of Coalesce, better to change the offsets.
	// This is required for animated image resizing
	if !sz.Flatten && numberImages > 1 {
		i.mw = i.mw.CoalesceImages()
	}

	i.mw.SetFirstIterator()
	for n := true; n; n = i.mw.NextImage() {

		pw, ph := int(i.mw.GetImageWidth()), int(i.mw.GetImageHeight())
		srcSize := imgry.NewRect(pw, ph)

		// Initial crop of the source image
		cropBox, cropOrigin, err := sz.CalcCropBox(srcSize)
		if err != nil {
			return err
		}
		if cropBox != nil && cropOrigin != nil && !cropBox.Equal(imgry.ZeroRect) {
			err := i.mw.CropImage(uint(cropBox.Width), uint(cropBox.Height), cropOrigin.X, cropOrigin.Y)
			if err != nil {
				return err
			}
			srcSize = cropBox
			i.mw.ResetImagePage("")
		}

		// Resize the image
		resizeRect, cropBox, cropOrigin := sz.CalcResizeRect(srcSize)
		if resizeRect != nil && !resizeRect.Equal(imgry.ZeroRect) {
			err := i.mw.ResizeImage(uint(resizeRect.Width), uint(resizeRect.Height), imagick.FILTER_LANCZOS, 1.0)
			if err != nil {
				return err
			}
			i.mw.ResetImagePage("")
		}

		// Perform any final crops from an operation
		if cropBox != nil && cropOrigin != nil && !cropBox.Equal(imgry.ZeroRect) {
			err := i.mw.CropImage(uint(cropBox.Width), uint(cropBox.Height), cropOrigin.X, cropOrigin.Y)
			if err != nil {
				return err
			}
			i.mw.ResetImagePage("")
		}

		if sz.Flatten {
			break
		}
	}

	if numberImages > 1 {
		i.mw = i.mw.DeconstructImages()
	}

	return nil
}

func (i *Image) WriteToFile(fn string) error {
	err := ioutil.WriteFile(fn, i.Data(), 0664)
	return err
}

func (i *Image) sync(optFlatten ...bool) error {
	if i.Released() {
		return ErrEngineReleased
	}

	var flatten bool
	if len(optFlatten) > 0 {
		flatten = optFlatten[0]
	}

	if flatten {
		i.data = i.mw.GetImageBlob()
	} else {
		i.data = i.mw.GetImagesBlob()
	}

	i.width = int(i.mw.GetImageWidth())
	i.height = int(i.mw.GetImageHeight())

	i.format = strings.ToLower(i.mw.GetImageFormat())
	if i.format == "jpeg" {
		i.format = "jpg"
	}

	return nil
}
<commit_msg>Only coalesce and deconstruct when the image is not meant to be flattened and it has more than one frame.<commit_after>package imagick

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/gographics/imagick/imagick"
	"github.com/pressly/imgry"
)

var (
	ErrEngineReleased = errors.New("imagick: engine has been released.")
	ErrEngineFailure  = errors.New("imagick: unable to request a MagickWand")
)

type Engine struct {
	tmpDir string
	// TODO: perhaps we have counter of wands here..
	// it ensures we'll always release them..
	// AvailableWands .. .. set to MaxSizers ..
	// then, NewMagickWand() will limit, or error..
	// perhaps we make a channel of these things..
	// then block.. + ctx will have a timeout if it wants to stop waiting..
	// we can log these timeouts etc..
}

func (ng Engine) Version() string {
	v, _ := imagick.GetVersion()
	return fmt.Sprintf("%s", v)
}

func (ng Engine) Initialize(tmpDir string) error {
	if tmpDir != "" {
		if err := os.MkdirAll(tmpDir, 0755); err != nil {
			return err
		}
		ng.tmpDir = tmpDir
		os.Setenv("MAGICK_TMPDIR", tmpDir)
		ng.SweepTmpDir()
	}
	imagick.Initialize()
	return nil
}

func (ng Engine) Terminate() {
	imagick.Terminate()
	ng.SweepTmpDir()
}

func (ng Engine) SweepTmpDir() error {
	if ng.tmpDir == "" {
		return nil
	}
	err := filepath.Walk(
		ng.tmpDir,
		func(path string, info os.FileInfo, err error) error {
			if ng.tmpDir == path {
				return nil // skip the root
			}
			if strings.Index(filepath.Base(path), "magick") >= 0 {
				if err = os.Remove(path); err != nil {
					return fmt.Errorf("failed to sweet engine tmpdir %s, because: %s", path, err)
				}
			}
			return nil
		},
	)
	if err != nil {
		return err
	}
	return nil
}

func (ng Engine) LoadFile(filename string, srcFormat ...string) (imgry.Image, error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return ng.LoadBlob(b, srcFormat...)
}

func (ng Engine) LoadBlob(b []byte, srcFormat ...string) (imgry.Image, error) {
	if len(b) == 0 {
		return nil, imgry.ErrInvalidImageData
	}

	mw := imagick.NewMagickWand()
	if !mw.IsVerified() {
		return nil, ErrEngineFailure
	}

	// Offer a hint to the decoder of the file format
	if len(srcFormat) > 0 {
		f := srcFormat[0]
		if f != "" {
			mw.SetFormat(f)
		}
	}

	err := mw.ReadImageBlob(b)
	if err != nil {
		mw.Destroy()
		return nil, imgry.ErrInvalidImageData
	}

	// TODO: perhaps we pass the engine instance like Image{engine: i}
	im := &Image{mw: mw, data: b}
	if err := im.sync(); err != nil {
		mw.Destroy()
		return nil, err
	}

	return im, nil
}

func (ng Engine) GetImageInfo(b []byte, srcFormat ...string) (*imgry.ImageInfo, error) {
	if len(b) == 0 {
		return nil, imgry.ErrInvalidImageData
	}

	mw := imagick.NewMagickWand()
	defer mw.Destroy()

	if !mw.IsVerified() {
		return nil, ErrEngineFailure
	}

	err := mw.PingImageBlob(b)
	if err != nil {
		return nil, imgry.ErrInvalidImageData
	}

	w, h := int(mw.GetImageWidth()), int(mw.GetImageHeight())
	ar := float64(int(float64(w)/float64(h)*10000)) / 10000

	format := strings.ToLower(mw.GetImageFormat())
	if format == "jpeg" {
		format = "jpg"
	}

	imfo := &imgry.ImageInfo{
		Format: format, Width: w, Height: h,
		AspectRatio: ar, ContentLength: len(b),
	}

	return imfo, nil
}

type Image struct {
	mw *imagick.MagickWand

	data   []byte
	width  int
	height int
	format string
}

func (i *Image) Data() []byte {
	return i.data
}

func (i *Image) Width() int {
	return i.width
}

func (i *Image) Height() int {
	return i.height
}

func (i *Image) Format() string {
	return i.format
}

func (i *Image) SetFormat(format string) error {
	if i.Released() {
		return ErrEngineReleased
	}
	if err := i.mw.SetImageFormat(format); err != nil {
		return err
	}
	if err := i.sync(); err != nil {
		return err
	}
	return nil
}

func (i *Image) Released() bool {
	return i.mw == nil
}

func (i *Image) Release() {
	if i.mw != nil {
		i.mw.Destroy()
		i.mw = nil
	}
}

func (i *Image) Clone() imgry.Image {
	i2 := &Image{}
	i2.data = i.data
	i2.width = i.width
	i2.height = i.height
	i2.format = i.format
	if i.mw != nil && i.mw.IsVerified() {
		i2.mw = i.mw.Clone()
	}
	return i2
}

func (i *Image) SizeIt(sz *imgry.Sizing) error {
	if i.Released() {
		return ErrEngineReleased
	}

	if err := i.sizeFrames(sz); err != nil {
		return err
	}

	if sz.Format != "" {
		if err := i.mw.SetFormat(sz.Format); err != nil {
			return err
		}
	}

	if sz.Quality > 0 {
		if err := i.mw.SetImageCompressionQuality(uint(sz.Quality)); err != nil {
			return err
		}
	}

	if err := i.sync(sz.Flatten); err != nil {
		return err
	}

	return nil
}

func (i *Image) sizeFrames(sz *imgry.Sizing) error {
	// Shortcut if there is nothing to size
	if sz.Size.Equal(imgry.ZeroRect) && sz.CropBox.Equal(imgry.ZeroFloatingRect) {
		return nil
	}

	coalesceAndDeconstruct := !sz.Flatten && i.mw.GetNumberImages() > 1

	if coalesceAndDeconstruct {
		i.mw = i.mw.CoalesceImages()
	}

	i.mw.SetFirstIterator()
	for n := true; n; n = i.mw.NextImage() {

		pw, ph := int(i.mw.GetImageWidth()), int(i.mw.GetImageHeight())
		srcSize := imgry.NewRect(pw, ph)

		// Initial crop of the source image
		cropBox, cropOrigin, err := sz.CalcCropBox(srcSize)
		if err != nil {
			return err
		}
		if cropBox != nil && cropOrigin != nil && !cropBox.Equal(imgry.ZeroRect) {
			err := i.mw.CropImage(uint(cropBox.Width), uint(cropBox.Height), cropOrigin.X, cropOrigin.Y)
			if err != nil {
				return err
			}
			srcSize = cropBox
			i.mw.ResetImagePage("")
		}

		// Resize the image
		resizeRect, cropBox, cropOrigin := sz.CalcResizeRect(srcSize)
		if resizeRect != nil && !resizeRect.Equal(imgry.ZeroRect) {
			err := i.mw.ResizeImage(uint(resizeRect.Width), uint(resizeRect.Height), imagick.FILTER_LANCZOS, 1.0)
			if err != nil {
				return err
			}
			i.mw.ResetImagePage("")
		}

		// Perform any final crops from an operation
		if cropBox != nil && cropOrigin != nil && !cropBox.Equal(imgry.ZeroRect) {
			err := i.mw.CropImage(uint(cropBox.Width), uint(cropBox.Height), cropOrigin.X, cropOrigin.Y)
			if err != nil {
				return err
			}
			i.mw.ResetImagePage("")
		}

		if sz.Flatten {
			break
		}
	}

	if coalesceAndDeconstruct {
		// Compares each frame of the image, removes pixels that are already on the
		// background and updates offsets accordingly.
		i.mw = i.mw.DeconstructImages()
	}

	return nil
}

func (i *Image) WriteToFile(fn string) error {
	err := ioutil.WriteFile(fn, i.Data(), 0664)
	return err
}

func (i *Image) sync(optFlatten ...bool) error {
	if i.Released() {
		return ErrEngineReleased
	}

	var flatten bool
	if len(optFlatten) > 0 {
		flatten = optFlatten[0]
	}

	if flatten {
		i.data = i.mw.GetImageBlob()
	} else {
		i.data = i.mw.GetImagesBlob()
	}

	i.width = int(i.mw.GetImageWidth())
	i.height = int(i.mw.GetImageHeight())

	i.format = strings.ToLower(i.mw.GetImageFormat())
	if i.format == "jpeg" {
		i.format = "jpg"
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sync"
	"time"
)

const (
	UpdateThreadID    = 0
	ScanThreadID      = 1
	WebserverThreadID = 2

	StopCode    = 1000
	ConfirmCode = 1001
	ErrorCode   = 1002
)

type Process struct {
	Channel      chan interface{}
	Subprocesses map[int]Process
}

func CreateMasterProcessList() map[int]Process {
	pmap := make(map[int]Process)

	pmap[0] = Process{
		Channel: make(chan interface{}),
	}
	pmap[1] = Process{
		Channel: make(chan interface{}),
	}
	pmap[2] = Process{
		Channel: make(chan interface{}),
	}
	return pmap
}

func (p Process) Kill(wg *sync.WaitGroup) {
	p.Channel <- StopCode

	for {
		select {
		case callback := <-p.Channel:
			if callback == ErrorCode {
				fmt.Println("Error (%x)\nYou may have to kill btsoot. Shutdown is now unsafe.", ErrorCode)
			}
			wg.Done()
		default:
			time.Sleep(100)
			// NOTE: Wait for the next loop
		}
	}
}

func KillAll(m map[int]Process) {
	go func() {
		fmt.Println("Waiting 10 seconds...")
		time.Sleep(10 * time.Second)
		fmt.Println("One or more threads do not answer. You may have to kill the program or wait.")
	}()
	var wg sync.WaitGroup
	for _, v := range m {
		wg.Add(1)
		go v.Kill(&wg)
	}
	wg.Wait()
	fmt.Println("Everything is shutted down.")
}
<commit_msg>Change kill error message to something more precise<commit_after>package main

import (
	"fmt"
	"sync"
	"time"
)

const (
	UpdateThreadID    = 0
	ScanThreadID      = 1
	WebserverThreadID = 2

	StopCode    = 1000
	ConfirmCode = 1001
	ErrorCode   = 1002
)

type Process struct {
	Channel      chan interface{}
	Subprocesses map[int]Process
}

func CreateMasterProcessList() map[int]Process {
	pmap := make(map[int]Process)

	pmap[0] = Process{
		Channel: make(chan interface{}),
	}
	pmap[1] = Process{
		Channel: make(chan interface{}),
	}
	pmap[2] = Process{
		Channel: make(chan interface{}),
	}
	return pmap
}

func (p Process) Kill(wg *sync.WaitGroup) {
	p.Channel <- StopCode

	for {
		select {
		case callback := <-p.Channel:
			if callback == ErrorCode {
				fmt.Println("Error (%x)\nOne thread did not answer.", ErrorCode)
			}
			wg.Done()
		default:
			time.Sleep(100)
			// NOTE: Wait for the next loop
		}
	}
}

func KillAll(m map[int]Process) {
	go func() {
		fmt.Println("Waiting 10 seconds...")
		time.Sleep(10 * time.Second)
		fmt.Println("One or more threads do not answer. You may have to kill the program or wait.")
	}()
	var wg sync.WaitGroup
	for _, v := range m {
		wg.Add(1)
		go v.Kill(&wg)
	}
	wg.Wait()
	fmt.Println("Everything is shutted down.")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/dmulholl/argo"
)

const version = "2.3.0-alpha3"

var helptext = fmt.Sprintf(`
Usage: %s [command]

  A utility for creating and managing encrypted password databases.

Flags:
  -h, --help        Print the application's help text.
  -v, --version     Print the application's version number.

Basic Commands:
  add               Add a new entry to a password database.
  edit              Edit an existing database entry.
  gen               Generate a new random password.
  init              Initialize a new password database.
  list              List database entries.
  pass              Copy a password to the clipboard.
  retire            Mark one or more entries as inactive.
  show              Show entry content.
  url               Copy a url to the clipboard.
  user              Copy a username to the clipboard.

Additional Commands:
  config            Set or print a configuration option.
  decrypt           Decrypt a file.
  dump              Dump a database's internal JSON data store.
  encrypt           Encrypt a file.
  export            Export entries from a database.
  import            Import entries into a database.
  purge             Purge inactive entries from a database.
  restore           Restore inactive entries to active status.
  setcachepass      Change a database's cache password.
  setmasterpass     Change a database's master password.
  tags              List database tags.

Aliases:
  new               Alias for 'add'.

Command Help:
  help <command>    Print the specified command's help text and exit.
`, filepath.Base(os.Args[0]))

func main() {

	// Instantiate an argument parser.
	parser := argo.NewParser()
	parser.Helptext = helptext
	parser.Version = version

	// Register commands.
	registerAddCmd(parser)
	registerCacheCmd(parser)
	registerConfigCmd(parser)
	registerDecryptCmd(parser)
	registerDumpCmd(parser)
	registerEditCmd(parser)
	registerEncryptCmd(parser)
	registerExportCmd(parser)
	registerGenCmd(parser)
	registerImportCmd(parser)
	registerInitCmd(parser)
	registerListCmd(parser)
	registerPassCmd(parser)
	registerPurgeCmd(parser)
	registerRestoreCmd(parser)
	registerRetireCmd(parser)
	registerSetMasterPassCmd(parser)
	registerSetCachePassCmd(parser)
	registerTagsCmd(parser)
	registerUrlCmd(parser)
	registerUserCmd(parser)
	registerShowCmd(parser)

	// Parse the command line arguments.
	parser.Parse()
	if !parser.HasCommand() {
		parser.ExitWithHelptext()
	}
}
<commit_msg>Bump version to v2.3.0<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/dmulholl/argo"
)

const version = "2.3.0"

var helptext = fmt.Sprintf(`
Usage: %s [command]

  A utility for creating and managing encrypted password databases.

Flags:
  -h, --help        Print the application's help text.
  -v, --version     Print the application's version number.

Basic Commands:
  add               Add a new entry to a password database.
  edit              Edit an existing database entry.
  gen               Generate a new random password.
  init              Initialize a new password database.
  list              List database entries.
  pass              Copy a password to the clipboard.
  retire            Mark one or more entries as inactive.
  show              Show entry content.
  url               Copy a url to the clipboard.
  user              Copy a username to the clipboard.

Additional Commands:
  config            Set or print a configuration option.
  decrypt           Decrypt a file.
  dump              Dump a database's internal JSON data store.
  encrypt           Encrypt a file.
  export            Export entries from a database.
  import            Import entries into a database.
  purge             Purge inactive entries from a database.
  restore           Restore inactive entries to active status.
  setcachepass      Change a database's cache password.
  setmasterpass     Change a database's master password.
  tags              List database tags.

Aliases:
  new               Alias for 'add'.

Command Help:
  help <command>    Print the specified command's help text and exit.
`, filepath.Base(os.Args[0]))

func main() {

	// Instantiate an argument parser.
	parser := argo.NewParser()
	parser.Helptext = helptext
	parser.Version = version

	// Register commands.
	registerAddCmd(parser)
	registerCacheCmd(parser)
	registerConfigCmd(parser)
	registerDecryptCmd(parser)
	registerDumpCmd(parser)
	registerEditCmd(parser)
	registerEncryptCmd(parser)
	registerExportCmd(parser)
	registerGenCmd(parser)
	registerImportCmd(parser)
	registerInitCmd(parser)
	registerListCmd(parser)
	registerPassCmd(parser)
	registerPurgeCmd(parser)
	registerRestoreCmd(parser)
	registerRetireCmd(parser)
	registerSetMasterPassCmd(parser)
	registerSetCachePassCmd(parser)
	registerTagsCmd(parser)
	registerUrlCmd(parser)
	registerUserCmd(parser)
	registerShowCmd(parser)

	// Parse the command line arguments.
	parser.Parse()
	if !parser.HasCommand() {
		parser.ExitWithHelptext()
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google Inc.

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.
*/

package netutil

import (
	"bufio"
	"fmt"
	"net"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
)

var _ = log.Printf

// TODO: Linux-specific right now.
// Returns os.ENOENT on not found.
func ConnUserid(conn net.Conn) (uid int, err os.Error) {
	return AddrPairUserid(conn.LocalAddr().String(), conn.RemoteAddr().String())
}

func splitIPPort(param, value string) (ip net.IP, port int, reterr os.Error) {
	addrs, ports, err := net.SplitHostPort(value)
	if err != nil {
		reterr = fmt.Errorf("netutil: AddrPairUserid invalid %s value: %v", param, err)
		return
	}
	ip = net.ParseIP(addrs)
	if ip == nil {
		reterr = fmt.Errorf("netutil: invalid %s IP %q", param, addrs)
		return
	}
	port, err = strconv.Atoi(ports)
	if err != nil || port <= 0 || port > 65535 {
		reterr = fmt.Errorf("netutil: invalid port %q", ports)
		return
	}
	return
}

// AddrPairUserid returns the local userid who owns the TCP connection
// given by the local and remote ip:port (lipport and ripport,
// respectively).  Returns os.ENOENT for the error if the TCP connection
// isn't found.
func AddrPairUserid(lipport, ripport string) (uid int, err os.Error) {
	lip, lport, err := splitIPPort("lipport", lipport)
	if err != nil {
		return -1, err
	}
	rip, rport, err := splitIPPort("ripport", ripport)
	if err != nil {
		return -1, err
	}
	localv4 := (lip.To4() != nil)
	remotev4 := (rip.To4() != nil)
	if localv4 != remotev4 {
		return -1, fmt.Errorf("netutil: address pairs of different families; localv4=%v, remotev4=%v",
			localv4, remotev4)
	}

	file := "/proc/net/tcp"
	if !localv4 {
		file = "/proc/net/tcp6"
	}
	f, err := os.Open(file)
	if err != nil {
		return -1, fmt.Errorf("Error opening %s: %v", file, err)
	}
	defer f.Close()
	return uidFromReader(lip, lport, rip, rport, f)
}

func reverseIPBytes(b []byte) []byte {
	rb := make([]byte, len(b))
	for i, v := range b {
		rb[len(b)-i-1] = v
	}
	return rb
}

func uidFromReader(lip net.IP, lport int, rip net.IP, rport int, r io.Reader) (uid int, err os.Error) {
	buf := bufio.NewReader(r)

	localHex := ""
	remoteHex := ""
	if lip.To4() != nil {
		// In the kernel, the port is run through ntohs(), and
		// the inet_request_socket in
		// include/net/inet_socket.h says the "loc_addr" and
		// "rmt_addr" fields are __be32, but get_openreq4's
		// printf of them is raw, without byte order
		// converstion.
		localHex = fmt.Sprintf("%08X:%04X", reverseIPBytes([]byte(lip.To4())), lport)
		remoteHex = fmt.Sprintf("%08X:%04X", reverseIPBytes([]byte(rip.To4())), rport)
	} else {
		localHex = fmt.Sprintf("%032X:%04X", []byte(lip.To16()), lport)
		remoteHex = fmt.Sprintf("%032X:%04X", []byte(rip.To16()), rport)
	}

	for {
		line, err := buf.ReadString('\n')
		if err != nil {
			return -1, os.ENOENT
		}
		parts := strings.Fields(strings.TrimSpace(line))
		if len(parts) < 8 {
			continue
		}
		// log.Printf("parts[1] = %q; localHex = %q", parts[1], localHex)
		if parts[1] == localHex && parts[2] == remoteHex {
			uid, _ = strconv.Atoi(parts[7])
			return
		}
	}
	panic("unreachable")
}
<commit_msg>make Brad happier<commit_after>/*
Copyright 2011 Google Inc.

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.
*/

package netutil

import (
	"bufio"
	"fmt"
	"net"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
)

var _ = log.Printf

// TODO: Linux-specific right now.
// Returns os.ENOENT on not found.
func ConnUserid(conn net.Conn) (uid int, err os.Error) {
	return AddrPairUserid(conn.LocalAddr().String(), conn.RemoteAddr().String())
}

func splitIPPort(param, value string) (ip net.IP, port int, reterr os.Error) {
	addrs, ports, err := net.SplitHostPort(value)
	if err != nil {
		reterr = fmt.Errorf("netutil: AddrPairUserid invalid %s value of %q: %v", param, value, err)
		return
	}
	ip = net.ParseIP(addrs)
	if ip == nil {
		reterr = fmt.Errorf("netutil: invalid %s IP %q", param, addrs)
		return
	}
	port, err = strconv.Atoi(ports)
	if err != nil || port <= 0 || port > 65535 {
		reterr = fmt.Errorf("netutil: invalid port %q", ports)
		return
	}
	return
}

// AddrPairUserid returns the local userid who owns the TCP connection
// given by the local and remote ip:port (lipport and ripport,
// respectively).  Returns os.ENOENT for the error if the TCP connection
// isn't found.
func AddrPairUserid(lipport, ripport string) (uid int, err os.Error) {
	lip, lport, err := splitIPPort("lipport", lipport)
	if err != nil {
		return -1, err
	}
	rip, rport, err := splitIPPort("ripport", ripport)
	if err != nil {
		return -1, err
	}
	localv4 := (lip.To4() != nil)
	remotev4 := (rip.To4() != nil)
	if localv4 != remotev4 {
		return -1, fmt.Errorf("netutil: address pairs of different families; localv4=%v, remotev4=%v",
			localv4, remotev4)
	}

	file := "/proc/net/tcp"
	if !localv4 {
		file = "/proc/net/tcp6"
	}
	f, err := os.Open(file)
	if err != nil {
		return -1, fmt.Errorf("Error opening %s: %v", file, err)
	}
	defer f.Close()
	return uidFromReader(lip, lport, rip, rport, f)
}

func reverseIPBytes(b []byte) []byte {
	rb := make([]byte, len(b))
	for i, v := range b {
		rb[len(b)-i-1] = v
	}
	return rb
}

func uidFromReader(lip net.IP, lport int, rip net.IP, rport int, r io.Reader) (uid int, err os.Error) {
	buf := bufio.NewReader(r)

	localHex := ""
	remoteHex := ""
	if lip.To4() != nil {
		// In the kernel, the port is run through ntohs(), and
		// the inet_request_socket in
		// include/net/inet_socket.h says the "loc_addr" and
		// "rmt_addr" fields are __be32, but get_openreq4's
		// printf of them is raw, without byte order
		// converstion.
		localHex = fmt.Sprintf("%08X:%04X", reverseIPBytes([]byte(lip.To4())), lport)
		remoteHex = fmt.Sprintf("%08X:%04X", reverseIPBytes([]byte(rip.To4())), rport)
	} else {
		localHex = fmt.Sprintf("%032X:%04X", []byte(lip.To16()), lport)
		remoteHex = fmt.Sprintf("%032X:%04X", []byte(rip.To16()), rport)
	}

	for {
		line, err := buf.ReadString('\n')
		if err != nil {
			return -1, os.ENOENT
		}
		parts := strings.Fields(strings.TrimSpace(line))
		if len(parts) < 8 {
			continue
		}
		// log.Printf("parts[1] = %q; localHex = %q", parts[1], localHex)
		if parts[1] == localHex && parts[2] == remoteHex {
			uid, _ = strconv.Atoi(parts[7])
			return
		}
	}
	panic("unreachable")
}
<|endoftext|>