<commit_before>// Copyright 2013 Julien Schmidt. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file at https://raw.githubusercontent.com/julienschmidt/httprouter/master/LICENSE.

package lars

import "net/url"

type nodeType uint8

const (
	isStatic nodeType = iota // default
	isRoot
	hasParams
	matchesAny
)

type methodChain struct {
	handlerName string
	chain       HandlersChain
}

type existingParams map[string]struct{}

type node struct {
	path      string
	wildChild bool
	nType     nodeType
	indices   string
	children  []*node
	handler   *methodChain
	priority  uint32
}

func (e existingParams) Check(param string, path string) {

	if _, ok := e[param]; ok {
		panic("Duplicate param name '" + param + "' detected for route '" + path + "'")
	}

	e[param] = struct{}{}
}

// increments priority of the given child and reorders if necessary
func (n *node) incrementChildPrio(pos int) int {

	n.children[pos].priority++
	prio := n.children[pos].priority

	// adjust position (move to front)
	newPos := pos
	for newPos > 0 && n.children[newPos-1].priority < prio {
		// swap node positions
		tmpN := n.children[newPos-1]
		n.children[newPos-1] = n.children[newPos]
		n.children[newPos] = tmpN

		newPos--
	}

	// build new index char string
	if newPos != pos {
		n.indices = n.indices[:newPos] + // unchanged prefix, might be empty
			n.indices[pos:pos+1] + // the index char we move
			n.indices[newPos:pos] + n.indices[pos+1:] // rest without char at 'pos'
	}

	return newPos
}

// addRoute adds a node with the given handle to the path.
// here we set a Middleware because we have  to transfer all route's middlewares (it's a chain of functions) (with it's handler) to the node
func (n *node) add(path string, handlerName string, handler HandlersChain) (lp uint8) {

	var err error

	if path == "" {
		path = "/"
	}

	existing := make(existingParams)
	fullPath := path

	if path, err = url.QueryUnescape(path); err != nil {
		panic("Query Unescape Error on path '" + fullPath + "': " + err.Error())
	}

	fullPath = path

	n.priority++
	numParams := countParams(path)
	lp = numParams

	// non-empty tree
	if len(n.path) > 0 || len(n.children) > 0 {
	walk:
		for {
			// Find the longest common prefix.
			// This also implies that the common prefix contains no : or *
			// since the existing key can't contain those chars.
			i := 0
			max := min(len(path), len(n.path))
			for i < max && path[i] == n.path[i] {
				i++
			}

			// Split edge
			if i < len(n.path) {
				child := node{
					path:      n.path[i:],
					wildChild: n.wildChild,
					indices:   n.indices,
					children:  n.children,
					handler:   n.handler,
					priority:  n.priority - 1,
				}

				n.children = []*node{&child}
				// []byte for proper unicode char conversion, see httprouter #65
				n.indices = string([]byte{n.path[i]})
				n.path = path[:i]
				n.handler = nil
				n.wildChild = false
			}

			// Make new node a child of this node
			if i < len(path) {
				path = path[i:]

				if n.wildChild {
					n = n.children[0]
					n.priority++
					numParams--

					existing.Check(n.path, fullPath)

					// Check if the wildcard matches
					if len(path) >= len(n.path) && n.path == path[:len(n.path)] {

						// check for longer wildcard, e.g. :name and :names
						if len(n.path) >= len(path) || path[len(n.path)] == '/' {
							continue walk
						}
					}

					panic("path segment '" + path +
						"' conflicts with existing wildcard '" + n.path +
						"' in path '" + fullPath + "'")
				}

				c := path[0]

				// slash after param
				if n.nType == hasParams && c == '/' && len(n.children) == 1 {
					n = n.children[0]
					n.priority++
					continue walk
				}

				// Check if a child with the next path byte exists
				for i := 0; i < len(n.indices); i++ {
					if c == n.indices[i] {
						i = n.incrementChildPrio(i)
						n = n.children[i]
						continue walk
					}
				}

				// Otherwise insert it
				if c != paramByte && c != wildByte {

					// []byte for proper unicode char conversion, see httprouter #65
					n.indices += string([]byte{c})
					child := &node{}
					n.children = append(n.children, child)
					n.incrementChildPrio(len(n.indices) - 1)
					n = child
				}
				n.insertChild(numParams, existing, path, fullPath, handlerName, handler)
				return

			} else if i == len(path) { // Make node a (in-path) leaf
				if n.handler != nil {
					panic("handlers are already registered for path '" + fullPath + "'")
				}
				n.handler = &methodChain{
					handlerName: handlerName,
					chain:       handler,
				}
			}
			return
		}
	} else { // Empty tree
		n.insertChild(numParams, existing, path, fullPath, handlerName, handler)
		n.nType = isRoot
	}

	return
}

func (n *node) insertChild(numParams uint8, existing existingParams, path string, fullPath string, handlerName string, handler HandlersChain) {

	var offset int // already handled bytes of the path

	// find prefix until first wildcard (beginning with paramByte' or wildByte')
	for i, max := 0, len(path); numParams > 0; i++ {

		c := path[i]
		if c != paramByte && c != wildByte {
			continue
		}

		// find wildcard end (either '/' or path end)
		end := i + 1
		for end < max && path[end] != '/' {
			switch path[end] {
			// the wildcard name must not contain ':' and '*'
			case paramByte, wildByte:
				panic("only one wildcard per path segment is allowed, has: '" +
					path[i:] + "' in path '" + fullPath + "'")
			default:
				end++
			}
		}

		// check if this Node existing children which would be
		// unreachable if we insert the wildcard here
		if len(n.children) > 0 {
			panic("wildcard route '" + path[i:end] +
				"' conflicts with existing children in path '" + fullPath + "'")
		}

		if c == paramByte { // param

			// check if the wildcard has a name
			if end-i < 2 {
				panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
			}

			// split path at the beginning of the wildcard
			if i > 0 {
				n.path = path[offset:i]
				offset = i
			}

			child := &node{
				nType: hasParams,
			}
			n.children = []*node{child}
			n.wildChild = true
			n = child
			n.priority++
			numParams--

			// if the path doesn't end with the wildcard, then there
			// will be another non-wildcard subpath starting with '/'
			if end < max {

				existing.Check(path[offset:end], fullPath)

				n.path = path[offset:end]
				offset = end

				child := &node{
					priority: 1,
				}
				n.children = []*node{child}
				n = child
			}

		} else { // catchAll
			if end != max || numParams > 1 {
				panic("Character after the * symbol is not permitted, path '" + fullPath + "'")
			}

			if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
				panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
			}

			// currently fixed width 1 for '/'
			i--
			if path[i] != '/' {
				panic("no / before catch-all in path '" + fullPath + "'")
			}

			n.path = path[offset:i]

			// first node: catchAll node with empty path
			child := &node{
				wildChild: true,
				nType:     matchesAny,
			}
			n.children = []*node{child}
			n.indices = string(path[i])
			n = child
			n.priority++

			// second node: node holding the variable
			child = &node{
				path:     path[i:],
				nType:    matchesAny,
				handler:  &methodChain{handlerName: handlerName, chain: handler},
				priority: 1,
			}
			n.children = []*node{child}

			return
		}
	}

	if n.nType == hasParams {
		existing.Check(path[offset:], fullPath)
	}

	// insert remaining path part and handle to the leaf
	n.path = path[offset:]
	n.handler = &methodChain{handlerName: handlerName, chain: handler}
}

// Returns the handle registered with the given path (key).
func (n *node) find(path string, po Params) (handler HandlersChain, p Params, handlerName string) {

	p = po

walk: // Outer loop for walking the tree
	for {
		if len(path) > len(n.path) {

			if path[:len(n.path)] == n.path {
				path = path[len(n.path):]

				// If this node does not have a wildcard (param or catchAll)
				// child,  we can just look up the next child node and continue
				// to walk down the tree
				if !n.wildChild {
					c := path[0]
					for i := 0; i < len(n.indices); i++ {
						if c == n.indices[i] {
							n = n.children[i]
							continue walk
						}
					}

					return
				}

				// handle wildcard child
				n = n.children[0]
				switch n.nType {
				case hasParams:

					// find param end (either '/' or path end)
					end := 0
					for end < len(path) && path[end] != '/' {
						end++
					}

					// save param value
					i := len(p)
					p = p[:i+1] // expand slice within preallocated capacity
					p[i].Key = n.path[1:]
					p[i].Value = path[:end]

					// we need to go deeper!
					if end < len(path) {
						if len(n.children) > 0 {
							path = path[end:]
							n = n.children[0]
							continue walk
						}

						return
					}

					if n.handler != nil {
						handler = n.handler.chain
						handlerName = n.handler.handlerName
					}

					if handler != nil {
						return
					} else if len(n.children) == 1 {
						// No handle found. Check if a handle for this path
						n = n.children[0]
					}

					return

				case matchesAny:

					// save param value
					i := len(p)
					p = p[:i+1] // expand slice within preallocated capacity
					p[i].Key = WildcardParam
					p[i].Value = path[1:]

					handler = n.handler.chain
					handlerName = n.handler.handlerName
					return

					// can't happen, but left here in case I'm wrong
					// default:
					// 	panic("invalid node type")
				}
			}

		} else if path == n.path {

			// We should have reached the node containing the handle.
			// Check if this node has a handle registered.
			if n.handler != nil {
				if handler, handlerName = n.handler.chain, n.handler.handlerName; handler != nil {
					return
				}
			}
		}

		// Nothing found
		return
	}
}
<commit_msg>Add self to copyright notice.<commit_after>// Copyright 2016 Dean Karn.
// Copyright 2013 Julien Schmidt.
// All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file at https://raw.githubusercontent.com/julienschmidt/httprouter/master/LICENSE.

package lars

import "net/url"

type nodeType uint8

const (
	isStatic nodeType = iota // default
	isRoot
	hasParams
	matchesAny
)

type methodChain struct {
	handlerName string
	chain       HandlersChain
}

type existingParams map[string]struct{}

type node struct {
	path      string
	wildChild bool
	nType     nodeType
	indices   string
	children  []*node
	handler   *methodChain
	priority  uint32
}

func (e existingParams) Check(param string, path string) {

	if _, ok := e[param]; ok {
		panic("Duplicate param name '" + param + "' detected for route '" + path + "'")
	}

	e[param] = struct{}{}
}

// increments priority of the given child and reorders if necessary
func (n *node) incrementChildPrio(pos int) int {

	n.children[pos].priority++
	prio := n.children[pos].priority

	// adjust position (move to front)
	newPos := pos
	for newPos > 0 && n.children[newPos-1].priority < prio {
		// swap node positions
		tmpN := n.children[newPos-1]
		n.children[newPos-1] = n.children[newPos]
		n.children[newPos] = tmpN

		newPos--
	}

	// build new index char string
	if newPos != pos {
		n.indices = n.indices[:newPos] + // unchanged prefix, might be empty
			n.indices[pos:pos+1] + // the index char we move
			n.indices[newPos:pos] + n.indices[pos+1:] // rest without char at 'pos'
	}

	return newPos
}

// addRoute adds a node with the given handle to the path.
// here we set a Middleware because we have  to transfer all route's middlewares (it's a chain of functions) (with it's handler) to the node
func (n *node) add(path string, handlerName string, handler HandlersChain) (lp uint8) {

	var err error

	if path == "" {
		path = "/"
	}

	existing := make(existingParams)
	fullPath := path

	if path, err = url.QueryUnescape(path); err != nil {
		panic("Query Unescape Error on path '" + fullPath + "': " + err.Error())
	}

	fullPath = path

	n.priority++
	numParams := countParams(path)
	lp = numParams

	// non-empty tree
	if len(n.path) > 0 || len(n.children) > 0 {
	walk:
		for {
			// Find the longest common prefix.
			// This also implies that the common prefix contains no : or *
			// since the existing key can't contain those chars.
			i := 0
			max := min(len(path), len(n.path))
			for i < max && path[i] == n.path[i] {
				i++
			}

			// Split edge
			if i < len(n.path) {
				child := node{
					path:      n.path[i:],
					wildChild: n.wildChild,
					indices:   n.indices,
					children:  n.children,
					handler:   n.handler,
					priority:  n.priority - 1,
				}

				n.children = []*node{&child}
				// []byte for proper unicode char conversion, see httprouter #65
				n.indices = string([]byte{n.path[i]})
				n.path = path[:i]
				n.handler = nil
				n.wildChild = false
			}

			// Make new node a child of this node
			if i < len(path) {
				path = path[i:]

				if n.wildChild {
					n = n.children[0]
					n.priority++
					numParams--

					existing.Check(n.path, fullPath)

					// Check if the wildcard matches
					if len(path) >= len(n.path) && n.path == path[:len(n.path)] {

						// check for longer wildcard, e.g. :name and :names
						if len(n.path) >= len(path) || path[len(n.path)] == '/' {
							continue walk
						}
					}

					panic("path segment '" + path +
						"' conflicts with existing wildcard '" + n.path +
						"' in path '" + fullPath + "'")
				}

				c := path[0]

				// slash after param
				if n.nType == hasParams && c == '/' && len(n.children) == 1 {
					n = n.children[0]
					n.priority++
					continue walk
				}

				// Check if a child with the next path byte exists
				for i := 0; i < len(n.indices); i++ {
					if c == n.indices[i] {
						i = n.incrementChildPrio(i)
						n = n.children[i]
						continue walk
					}
				}

				// Otherwise insert it
				if c != paramByte && c != wildByte {

					// []byte for proper unicode char conversion, see httprouter #65
					n.indices += string([]byte{c})
					child := &node{}
					n.children = append(n.children, child)
					n.incrementChildPrio(len(n.indices) - 1)
					n = child
				}
				n.insertChild(numParams, existing, path, fullPath, handlerName, handler)
				return

			} else if i == len(path) { // Make node a (in-path) leaf
				if n.handler != nil {
					panic("handlers are already registered for path '" + fullPath + "'")
				}
				n.handler = &methodChain{
					handlerName: handlerName,
					chain:       handler,
				}
			}
			return
		}
	} else { // Empty tree
		n.insertChild(numParams, existing, path, fullPath, handlerName, handler)
		n.nType = isRoot
	}

	return
}

func (n *node) insertChild(numParams uint8, existing existingParams, path string, fullPath string, handlerName string, handler HandlersChain) {

	var offset int // already handled bytes of the path

	// find prefix until first wildcard (beginning with paramByte' or wildByte')
	for i, max := 0, len(path); numParams > 0; i++ {

		c := path[i]
		if c != paramByte && c != wildByte {
			continue
		}

		// find wildcard end (either '/' or path end)
		end := i + 1
		for end < max && path[end] != '/' {
			switch path[end] {
			// the wildcard name must not contain ':' and '*'
			case paramByte, wildByte:
				panic("only one wildcard per path segment is allowed, has: '" +
					path[i:] + "' in path '" + fullPath + "'")
			default:
				end++
			}
		}

		// check if this Node existing children which would be
		// unreachable if we insert the wildcard here
		if len(n.children) > 0 {
			panic("wildcard route '" + path[i:end] +
				"' conflicts with existing children in path '" + fullPath + "'")
		}

		if c == paramByte { // param

			// check if the wildcard has a name
			if end-i < 2 {
				panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
			}

			// split path at the beginning of the wildcard
			if i > 0 {
				n.path = path[offset:i]
				offset = i
			}

			child := &node{
				nType: hasParams,
			}
			n.children = []*node{child}
			n.wildChild = true
			n = child
			n.priority++
			numParams--

			// if the path doesn't end with the wildcard, then there
			// will be another non-wildcard subpath starting with '/'
			if end < max {

				existing.Check(path[offset:end], fullPath)

				n.path = path[offset:end]
				offset = end

				child := &node{
					priority: 1,
				}
				n.children = []*node{child}
				n = child
			}

		} else { // catchAll
			if end != max || numParams > 1 {
				panic("Character after the * symbol is not permitted, path '" + fullPath + "'")
			}

			if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
				panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
			}

			// currently fixed width 1 for '/'
			i--
			if path[i] != '/' {
				panic("no / before catch-all in path '" + fullPath + "'")
			}

			n.path = path[offset:i]

			// first node: catchAll node with empty path
			child := &node{
				wildChild: true,
				nType:     matchesAny,
			}
			n.children = []*node{child}
			n.indices = string(path[i])
			n = child
			n.priority++

			// second node: node holding the variable
			child = &node{
				path:     path[i:],
				nType:    matchesAny,
				handler:  &methodChain{handlerName: handlerName, chain: handler},
				priority: 1,
			}
			n.children = []*node{child}

			return
		}
	}

	if n.nType == hasParams {
		existing.Check(path[offset:], fullPath)
	}

	// insert remaining path part and handle to the leaf
	n.path = path[offset:]
	n.handler = &methodChain{handlerName: handlerName, chain: handler}
}

// Returns the handle registered with the given path (key).
func (n *node) find(path string, po Params) (handler HandlersChain, p Params, handlerName string) {

	p = po

walk: // Outer loop for walking the tree
	for {
		if len(path) > len(n.path) {

			if path[:len(n.path)] == n.path {
				path = path[len(n.path):]

				// If this node does not have a wildcard (param or catchAll)
				// child,  we can just look up the next child node and continue
				// to walk down the tree
				if !n.wildChild {
					c := path[0]
					for i := 0; i < len(n.indices); i++ {
						if c == n.indices[i] {
							n = n.children[i]
							continue walk
						}
					}

					return
				}

				// handle wildcard child
				n = n.children[0]
				switch n.nType {
				case hasParams:

					// find param end (either '/' or path end)
					end := 0
					for end < len(path) && path[end] != '/' {
						end++
					}

					// save param value
					i := len(p)
					p = p[:i+1] // expand slice within preallocated capacity
					p[i].Key = n.path[1:]
					p[i].Value = path[:end]

					// we need to go deeper!
					if end < len(path) {
						if len(n.children) > 0 {
							path = path[end:]
							n = n.children[0]
							continue walk
						}

						return
					}

					if n.handler != nil {
						handler = n.handler.chain
						handlerName = n.handler.handlerName
					}

					if handler != nil {
						return
					} else if len(n.children) == 1 {
						// No handle found. Check if a handle for this path
						n = n.children[0]
					}

					return

				case matchesAny:

					// save param value
					i := len(p)
					p = p[:i+1] // expand slice within preallocated capacity
					p[i].Key = WildcardParam
					p[i].Value = path[1:]

					handler = n.handler.chain
					handlerName = n.handler.handlerName
					return

					// can't happen, but left here in case I'm wrong
					// default:
					// 	panic("invalid node type")
				}
			}

		} else if path == n.path {

			// We should have reached the node containing the handle.
			// Check if this node has a handle registered.
			if n.handler != nil {
				if handler, handlerName = n.handler.chain, n.handler.handlerName; handler != nil {
					return
				}
			}
		}

		// Nothing found
		return
	}
}
<|endoftext|>


<commit_before>package node

import (
	"encoding/binary"
	"erlang/dist"
	"erlang/epmd"
	"flag"
	"fmt"
	erl "github.com/goerlang/etf/types"
	"log"
	"net"
	"strconv"
	"strings"
)

var nTrace bool

func init() {
	flag.BoolVar(&nTrace, "erlang.node.trace", false, "trace erlang node")
}

func nLog(f string, a ...interface{}) {
	if nTrace {
		log.Printf(f, a...)
	}
}

type regReq struct {
	replyTo  chan erl.Pid
	channels procChannels
}

type regNameReq struct {
	name erl.Atom
	pid  erl.Pid
}

type unregNameReq struct {
	name erl.Atom
}

type registryChan struct {
	storeChan     chan regReq
	regNameChan   chan regNameReq
	unregNameChan chan unregNameReq
}

type nodeConn struct {
	conn  net.Conn
	wchan chan []erl.Term
}

type Node struct {
	epmd.NodeInfo
	Cookie     string
	port       int32
	registry   *registryChan
	channels   map[erl.Pid]procChannels
	registered map[erl.Atom]erl.Pid
	neighbors  map[erl.Atom]nodeConn
}

type procChannels struct {
	in     chan erl.Term
	inFrom chan erl.Tuple
	ctl    chan erl.Term
}

type Behaviour interface {
	ProcessLoop(n *Node, pid erl.Pid, pcs procChannels, pd Process, args ...interface{})
	setNode(node *Node)
	setPid(pid erl.Pid)
}

type Process interface {
	Behaviour() (behaviour Behaviour, options map[string]interface{})
}

func NewNode(name string, cookie string) (node *Node) {
	nLog("Start with name '%s' and cookie '%s'", name, cookie)
	// TODO: add fqdn support
	ns := strings.Split(name, "@")
	nodeInfo := epmd.NodeInfo{
		FullName: name,
		Name:     ns[0],
		Domain:   ns[1],
		Port:     0,
		Type:     77, // or 72 if hidden
		Protocol: 0,
		HighVsn:  5,
		LowVsn:   5,
		Creation: 0,
	}

	registry := &registryChan{
		storeChan:     make(chan regReq),
		regNameChan:   make(chan regNameReq),
		unregNameChan: make(chan unregNameReq),
	}

	node = &Node{
		NodeInfo:   nodeInfo,
		Cookie:     cookie,
		registry:   registry,
		channels:   make(map[erl.Pid]procChannels),
		registered: make(map[erl.Atom]erl.Pid),
		neighbors:  make(map[erl.Atom]nodeConn),
	}
	return node
}

func (n *Node) prepareProcesses() {
	nk := new(netKernel)
	nkPid := n.Spawn(nk)
	n.Register(erl.Atom("net_kernel"), nkPid)

	gns := new(globalNameServer)
	gnsPid := n.Spawn(gns)
	n.Register(erl.Atom("global_name_server"), gnsPid)

	rex := new(rexRPC)
	rexPid := n.Spawn(rex)
	n.Register(erl.Atom("rex"), rexPid)
}

func (n *Node) Spawn(pd Process, args ...interface{}) (pid erl.Pid) {
	behaviour, options := pd.Behaviour()
	chanSize, ok := options["chan-size"].(int)
	if !ok {
		chanSize = 100
	}
	ctlChanSize, ok := options["chan-size"].(int)
	if !ok {
		chanSize = 100
	}
	in := make(chan erl.Term, chanSize)
	inFrom := make(chan erl.Tuple, chanSize)
	ctl := make(chan erl.Term, ctlChanSize)
	pcs := procChannels{
		in:     in,
		inFrom: inFrom,
		ctl:    ctl,
	}
	pid = n.storeProcess(pcs)
	go behaviour.ProcessLoop(n, pid, pcs, pd, args...)
	return
}

func (n *Node) Register(name erl.Atom, pid erl.Pid) {
	r := regNameReq{name: name, pid: pid}
	n.registry.regNameChan <- r
}

func (n *Node) Unregister(name erl.Atom) {
	r := unregNameReq{name: name}
	n.registry.unregNameChan <- r
}

func (n *Node) Registered() (pids []erl.Atom) {
	pids = make([]erl.Atom, len(n.registered))
	i := 0
	for p, _ := range n.registered {
		pids[i] = p
		i++
	}
	return
}

func (n *Node) registrator() {
	for {
		select {
		case req := <-n.registry.storeChan:
			// FIXME: make proper allocation, now it just stub
			var id uint32 = 0
			for k, _ := range n.channels {
				if k.Id >= id {
					id = k.Id + 1
				}
			}
			var pid erl.Pid
			pid.Node = erl.Atom(n.FullName)
			pid.Id = id
			pid.Serial = 0 // FIXME
			pid.Creation = byte(n.Creation)

			n.channels[pid] = req.channels
			req.replyTo <- pid
		case req := <-n.registry.regNameChan:
			n.registered[req.name] = req.pid
		case req := <-n.registry.unregNameChan:
			delete(n.registered, req.name)
		}
	}
}

func (n *Node) storeProcess(chs procChannels) (pid erl.Pid) {
	myChan := make(chan erl.Pid)
	n.registry.storeChan <- regReq{replyTo: myChan, channels: chs}
	pid = <-myChan
	return pid
}

func (n *Node) Connect(remote string) {

}

func (n *Node) Publish(port int) (err error) {
	nLog("Publish ENode at %d", port)
	l, err := net.Listen("tcp", net.JoinHostPort("", strconv.Itoa(port)))
	if err != nil {
		return
	}
	n.Port = uint16(port)
	aliveResp := make(chan uint16)
	go epmdC(n, aliveResp)
	creation := <-aliveResp
	switch creation {
	case 99:
		return fmt.Errorf("Duplicate name '%s'", n.Name)
	case 100:
		return fmt.Errorf("Cannot connect to EPMD")
	default:
		n.Creation = creation
	}

	go func() {
		for {
			conn, err := l.Accept()
			nLog("Accept new at ENode")
			if err != nil {
				nLog(err.Error())
			} else {
				wchan := make(chan []erl.Term, 10)
				ndchan := make(chan *dist.NodeDesc)
				go n.mLoopReader(conn, wchan, ndchan)
				go n.mLoopWriter(conn, wchan, ndchan)
			}
		}
	}()
	go n.registrator()
	n.prepareProcesses()
	return nil
}

func (currNode *Node) mLoopReader(c net.Conn, wchan chan []erl.Term, ndchan chan *dist.NodeDesc) {

	currNd := dist.NewNodeDesc(currNode.FullName, currNode.Cookie, false)
	ndchan <- currNd
	for {
		terms, err := currNd.ReadMessage(c)
		if err != nil {
			nLog("Enode error: %s", err.Error())
			break
		}
		currNode.handleTerms(c, wchan, terms)
	}
	c.Close()
}

func (currNode *Node) mLoopWriter(c net.Conn, wchan chan []erl.Term, ndchan chan *dist.NodeDesc) {

	currNd := <-ndchan

	for {
		terms := <-wchan
		err := currNd.WriteMessage(c, terms)
		if err != nil {
			nLog("Enode error: %s", err.Error())
			break
		}
	}
	c.Close()
}

func (currNode *Node) handleTerms(c net.Conn, wchan chan []erl.Term, terms []erl.Term) {
	nLog("Node terms: %#v", terms)

	if len(terms) == 0 {
		return
	}
	switch t := terms[0].(type) {
	case erl.Tuple:
		if len(t) > 0 {
			switch act := t.Element(1).(type) {
			case int64:
				switch act {
				case int64(REG_SEND):
					if len(terms) == 2 {
						currNode.RegSend(t.Element(2), t.Element(4), terms[1])
					} else {
						nLog("*** ERROR: bad REG_SEND: %#v", terms)
					}
				default:
					nLog("Unhandled node message (act %d): %#v", act, t)
				}
			case erl.Atom:
				switch act {
				case erl.Atom("$go_set_node"):
					nLog("SET NODE %#v", t)
					currNode.neighbors[t[1].(erl.Atom)] = nodeConn{conn: c, wchan: wchan}
				}
			default:
				nLog("UNHANDLED ACT: %#v", t.Element(1))
			}
		}
	}
}

func (currNode *Node) RegSend(from, to erl.Term, message erl.Term) {
	nLog("REG_SEND: From: %#v, To: %#v, Message: %#v", from, to, message)
	var toPid erl.Pid
	switch tp := to.(type) {
	case erl.Pid:
		toPid = tp
	case erl.Atom:
		toPid = currNode.Whereis(tp)
	}
	currNode.SendFrom(from, toPid, message)
}

func (currNode *Node) Whereis(who erl.Atom) (pid erl.Pid) {
	pid, _ = currNode.registered[who]
	return
}

func (currNode *Node) SendFrom(from erl.Term, to erl.Pid, message erl.Term) {
	nLog("SendFrom: %#v, %#v, %#v", from, to, message)
	pcs := currNode.channels[to]
	pcs.inFrom <- erl.Tuple{from, message}
}

func (currNode *Node) Send(to erl.Pid, message erl.Term) {
	nLog("Send: %#v, %#v", to, message)
	if string(to.Node) == currNode.FullName {
		nLog("Send to local node")
		pcs := currNode.channels[to]
		pcs.in <- message
	} else {
		nLog("Send to remote node: %#v, %#v", to, currNode.neighbors[to.Node])

		msg := []erl.Term{erl.Tuple{SEND, erl.Atom(""), to}, message}
		currNode.neighbors[to.Node].wchan <- msg
	}
}

func epmdC(n *Node, resp chan uint16) {
	conn, err := net.Dial("tcp", ":4369")
	defer conn.Close()
	if err != nil {
		resp <- 100
		return
	}

	epmdFROM := make(chan []byte)
	go epmdREADER(conn, epmdFROM)

	epmdTO := make(chan []byte)
	go epmdWRITER(conn, epmdFROM, epmdTO)

	epmdTO <- epmd.Compose_ALIVE2_REQ(&n.NodeInfo)

	for {

		select {
		case reply := <-epmdFROM:
			nLog("From EPMD: %v", reply)

			switch epmd.MessageId(reply[0]) {
			case epmd.ALIVE2_RESP:
				if creation, ok := epmd.Read_ALIVE2_RESP(reply); ok {
					resp <- creation
				} else {
					resp <- 99
				}
			}

		}

	}
}

func epmdREADER(conn net.Conn, in chan []byte) {
	for {
		buf := make([]byte, 1024)
		n, err := conn.Read(buf)
		if err != nil {
			in <- buf[0:n]
			in <- []byte{}
			return
		}
		nLog("Read from EPMD %d: %v", n, buf[:n])
		in <- buf[:n]
	}
}

func epmdWRITER(conn net.Conn, in chan []byte, out chan []byte) {
	for {
		select {
		case data := <-out:
			buf := make([]byte, 2)
			binary.BigEndian.PutUint16(buf[0:2], uint16(len(data)))
			buf = append(buf, data...)
			_, err := conn.Write(buf)
			if err != nil {
				in <- []byte{}
			}
		}
	}
}
<commit_msg>Fix path to erlang libs<commit_after>package node

import (
	"encoding/binary"
	"github.com/goerlang/dist"
	"github.com/goerlang/epmd"
	"flag"
	"fmt"
	erl "github.com/goerlang/etf/types"
	"log"
	"net"
	"strconv"
	"strings"
)

var nTrace bool

func init() {
	flag.BoolVar(&nTrace, "erlang.node.trace", false, "trace erlang node")
}

func nLog(f string, a ...interface{}) {
	if nTrace {
		log.Printf(f, a...)
	}
}

type regReq struct {
	replyTo  chan erl.Pid
	channels procChannels
}

type regNameReq struct {
	name erl.Atom
	pid  erl.Pid
}

type unregNameReq struct {
	name erl.Atom
}

type registryChan struct {
	storeChan     chan regReq
	regNameChan   chan regNameReq
	unregNameChan chan unregNameReq
}

type nodeConn struct {
	conn  net.Conn
	wchan chan []erl.Term
}

type Node struct {
	epmd.NodeInfo
	Cookie     string
	port       int32
	registry   *registryChan
	channels   map[erl.Pid]procChannels
	registered map[erl.Atom]erl.Pid
	neighbors  map[erl.Atom]nodeConn
}

type procChannels struct {
	in     chan erl.Term
	inFrom chan erl.Tuple
	ctl    chan erl.Term
}

type Behaviour interface {
	ProcessLoop(n *Node, pid erl.Pid, pcs procChannels, pd Process, args ...interface{})
	setNode(node *Node)
	setPid(pid erl.Pid)
}

type Process interface {
	Behaviour() (behaviour Behaviour, options map[string]interface{})
}

func NewNode(name string, cookie string) (node *Node) {
	nLog("Start with name '%s' and cookie '%s'", name, cookie)
	// TODO: add fqdn support
	ns := strings.Split(name, "@")
	nodeInfo := epmd.NodeInfo{
		FullName: name,
		Name:     ns[0],
		Domain:   ns[1],
		Port:     0,
		Type:     77, // or 72 if hidden
		Protocol: 0,
		HighVsn:  5,
		LowVsn:   5,
		Creation: 0,
	}

	registry := &registryChan{
		storeChan:     make(chan regReq),
		regNameChan:   make(chan regNameReq),
		unregNameChan: make(chan unregNameReq),
	}

	node = &Node{
		NodeInfo:   nodeInfo,
		Cookie:     cookie,
		registry:   registry,
		channels:   make(map[erl.Pid]procChannels),
		registered: make(map[erl.Atom]erl.Pid),
		neighbors:  make(map[erl.Atom]nodeConn),
	}
	return node
}

func (n *Node) prepareProcesses() {
	nk := new(netKernel)
	nkPid := n.Spawn(nk)
	n.Register(erl.Atom("net_kernel"), nkPid)

	gns := new(globalNameServer)
	gnsPid := n.Spawn(gns)
	n.Register(erl.Atom("global_name_server"), gnsPid)

	rex := new(rexRPC)
	rexPid := n.Spawn(rex)
	n.Register(erl.Atom("rex"), rexPid)
}

func (n *Node) Spawn(pd Process, args ...interface{}) (pid erl.Pid) {
	behaviour, options := pd.Behaviour()
	chanSize, ok := options["chan-size"].(int)
	if !ok {
		chanSize = 100
	}
	ctlChanSize, ok := options["chan-size"].(int)
	if !ok {
		chanSize = 100
	}
	in := make(chan erl.Term, chanSize)
	inFrom := make(chan erl.Tuple, chanSize)
	ctl := make(chan erl.Term, ctlChanSize)
	pcs := procChannels{
		in:     in,
		inFrom: inFrom,
		ctl:    ctl,
	}
	pid = n.storeProcess(pcs)
	go behaviour.ProcessLoop(n, pid, pcs, pd, args...)
	return
}

func (n *Node) Register(name erl.Atom, pid erl.Pid) {
	r := regNameReq{name: name, pid: pid}
	n.registry.regNameChan <- r
}

func (n *Node) Unregister(name erl.Atom) {
	r := unregNameReq{name: name}
	n.registry.unregNameChan <- r
}

func (n *Node) Registered() (pids []erl.Atom) {
	pids = make([]erl.Atom, len(n.registered))
	i := 0
	for p, _ := range n.registered {
		pids[i] = p
		i++
	}
	return
}

func (n *Node) registrator() {
	for {
		select {
		case req := <-n.registry.storeChan:
			// FIXME: make proper allocation, now it just stub
			var id uint32 = 0
			for k, _ := range n.channels {
				if k.Id >= id {
					id = k.Id + 1
				}
			}
			var pid erl.Pid
			pid.Node = erl.Atom(n.FullName)
			pid.Id = id
			pid.Serial = 0 // FIXME
			pid.Creation = byte(n.Creation)

			n.channels[pid] = req.channels
			req.replyTo <- pid
		case req := <-n.registry.regNameChan:
			n.registered[req.name] = req.pid
		case req := <-n.registry.unregNameChan:
			delete(n.registered, req.name)
		}
	}
}

func (n *Node) storeProcess(chs procChannels) (pid erl.Pid) {
	myChan := make(chan erl.Pid)
	n.registry.storeChan <- regReq{replyTo: myChan, channels: chs}
	pid = <-myChan
	return pid
}

func (n *Node) Connect(remote string) {

}

func (n *Node) Publish(port int) (err error) {
	nLog("Publish ENode at %d", port)
	l, err := net.Listen("tcp", net.JoinHostPort("", strconv.Itoa(port)))
	if err != nil {
		return
	}
	n.Port = uint16(port)
	aliveResp := make(chan uint16)
	go epmdC(n, aliveResp)
	creation := <-aliveResp
	switch creation {
	case 99:
		return fmt.Errorf("Duplicate name '%s'", n.Name)
	case 100:
		return fmt.Errorf("Cannot connect to EPMD")
	default:
		n.Creation = creation
	}

	go func() {
		for {
			conn, err := l.Accept()
			nLog("Accept new at ENode")
			if err != nil {
				nLog(err.Error())
			} else {
				wchan := make(chan []erl.Term, 10)
				ndchan := make(chan *dist.NodeDesc)
				go n.mLoopReader(conn, wchan, ndchan)
				go n.mLoopWriter(conn, wchan, ndchan)
			}
		}
	}()
	go n.registrator()
	n.prepareProcesses()
	return nil
}

func (currNode *Node) mLoopReader(c net.Conn, wchan chan []erl.Term, ndchan chan *dist.NodeDesc) {

	currNd := dist.NewNodeDesc(currNode.FullName, currNode.Cookie, false)
	ndchan <- currNd
	for {
		terms, err := currNd.ReadMessage(c)
		if err != nil {
			nLog("Enode error: %s", err.Error())
			break
		}
		currNode.handleTerms(c, wchan, terms)
	}
	c.Close()
}

func (currNode *Node) mLoopWriter(c net.Conn, wchan chan []erl.Term, ndchan chan *dist.NodeDesc) {

	currNd := <-ndchan

	for {
		terms := <-wchan
		err := currNd.WriteMessage(c, terms)
		if err != nil {
			nLog("Enode error: %s", err.Error())
			break
		}
	}
	c.Close()
}

func (currNode *Node) handleTerms(c net.Conn, wchan chan []erl.Term, terms []erl.Term) {
	nLog("Node terms: %#v", terms)

	if len(terms) == 0 {
		return
	}
	switch t := terms[0].(type) {
	case erl.Tuple:
		if len(t) > 0 {
			switch act := t.Element(1).(type) {
			case int64:
				switch act {
				case int64(REG_SEND):
					if len(terms) == 2 {
						currNode.RegSend(t.Element(2), t.Element(4), terms[1])
					} else {
						nLog("*** ERROR: bad REG_SEND: %#v", terms)
					}
				default:
					nLog("Unhandled node message (act %d): %#v", act, t)
				}
			case erl.Atom:
				switch act {
				case erl.Atom("$go_set_node"):
					nLog("SET NODE %#v", t)
					currNode.neighbors[t[1].(erl.Atom)] = nodeConn{conn: c, wchan: wchan}
				}
			default:
				nLog("UNHANDLED ACT: %#v", t.Element(1))
			}
		}
	}
}

func (currNode *Node) RegSend(from, to erl.Term, message erl.Term) {
	nLog("REG_SEND: From: %#v, To: %#v, Message: %#v", from, to, message)
	var toPid erl.Pid
	switch tp := to.(type) {
	case erl.Pid:
		toPid = tp
	case erl.Atom:
		toPid = currNode.Whereis(tp)
	}
	currNode.SendFrom(from, toPid, message)
}

func (currNode *Node) Whereis(who erl.Atom) (pid erl.Pid) {
	pid, _ = currNode.registered[who]
	return
}

func (currNode *Node) SendFrom(from erl.Term, to erl.Pid, message erl.Term) {
	nLog("SendFrom: %#v, %#v, %#v", from, to, message)
	pcs := currNode.channels[to]
	pcs.inFrom <- erl.Tuple{from, message}
}

func (currNode *Node) Send(to erl.Pid, message erl.Term) {
	nLog("Send: %#v, %#v", to, message)
	if string(to.Node) == currNode.FullName {
		nLog("Send to local node")
		pcs := currNode.channels[to]
		pcs.in <- message
	} else {
		nLog("Send to remote node: %#v, %#v", to, currNode.neighbors[to.Node])

		msg := []erl.Term{erl.Tuple{SEND, erl.Atom(""), to}, message}
		currNode.neighbors[to.Node].wchan <- msg
	}
}

func epmdC(n *Node, resp chan uint16) {
	conn, err := net.Dial("tcp", ":4369")
	defer conn.Close()
	if err != nil {
		resp <- 100
		return
	}

	epmdFROM := make(chan []byte)
	go epmdREADER(conn, epmdFROM)

	epmdTO := make(chan []byte)
	go epmdWRITER(conn, epmdFROM, epmdTO)

	epmdTO <- epmd.Compose_ALIVE2_REQ(&n.NodeInfo)

	for {

		select {
		case reply := <-epmdFROM:
			nLog("From EPMD: %v", reply)

			switch epmd.MessageId(reply[0]) {
			case epmd.ALIVE2_RESP:
				if creation, ok := epmd.Read_ALIVE2_RESP(reply); ok {
					resp <- creation
				} else {
					resp <- 99
				}
			}

		}

	}
}

func epmdREADER(conn net.Conn, in chan []byte) {
	for {
		buf := make([]byte, 1024)
		n, err := conn.Read(buf)
		if err != nil {
			in <- buf[0:n]
			in <- []byte{}
			return
		}
		nLog("Read from EPMD %d: %v", n, buf[:n])
		in <- buf[:n]
	}
}

func epmdWRITER(conn net.Conn, in chan []byte, out chan []byte) {
	for {
		select {
		case data := <-out:
			buf := make([]byte, 2)
			binary.BigEndian.PutUint16(buf[0:2], uint16(len(data)))
			buf = append(buf, data...)
			_, err := conn.Write(buf)
			if err != nil {
				in <- []byte{}
			}
		}
	}
}
<|endoftext|>


<commit_before>package gocelot

import (
	"net/http"
)

type node struct {
	path string
	next []*node
	handlers *handlerArray
}

func newNode() *node {
	return &node{}
}

// nodeSeqFromPath returns the first and the last node of the sequence.
// If the path doesn't contain params(':'), the first and the last nodes are
// the same.
// All the params(':') are stored in seperate nodes.
// Eg.
// nodeSeqFromPath('/path/:param/end/')
// returns node('/path/'), node('end/')
// and the sequence is node('/path') -> node(':param/') -> node('end/')
func nodeSeq(path string) (*node, *node) {
	first := newNode()
	last := first
	start, isParam := 0, false
	extendSeq := func(end int) {
		if start != end {
			if last.path == "" {
				last.path = path[start:end]
			} else {
				next := newNode()
				next.path = path[start:end]
				last.next = []*node{next}
				last = next
			}
		}
		start = end
	}
	for end, letter := range path {
		switch isParam {
		case false && letter == ':':
			extendSeq(end)
			isParam = true
		case true && letter == '/':
			extendSeq(end + 1)
			isParam = false
		}
	}
	if start != len(path) {
		extendSeq(len(path))
	}
	return first, last
}

// min returns the minimum of two intergers
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// lcp returns the longest common prefix of 2 words
func lcp(a, b string) int {
	lcp, minLen := 0, min(len(a), len(b))
	for lcp < minLen && a[lcp] == b[lcp] {
		lcp++
	}
	return lcp
}

// slice returns a slice of given length starting at start of the given string
func slice(path string, start, length int) string {
	return path[start:min(start+length, len(path))]
}

// addPath adds the path to the node and returns the last node which belongs
// to the path being added.
// The path in the node and the path which is added should have a common prefix.
// Otherwise after addition the current node will contain an empty path.
// Eg.
// node('/path/').AddPath('/paths/')
// returns node('s/')
// and the result sequence is
// node('/path') -> node('/')
// node('/path') -> node('s/')
// Note that different params are stored in different nodes.
// Eg.
// For starting sequence node('/path/') -> node(':param1/') -> node('end/')
// node('/path/').Add('/path/param2/finish/')
// returns node('finish/')
// and the result sequence is
// node('/path/') -> node(':param1/') -> node('end/')
// node('/path/') -> node(':param2/') -> node('finish/')
// NOT
// node('/path/') -> node(':param') -> node('1/end/')
// node('/path/') -> node(':param') -> node('2/finish/')
func (n *node) add(path string) *node {
	diff := lcp(n.path, path)
	// The given path already exists
	if len(n.path) == diff && len(path) == diff {
		return n
	}
	switch diff {
	case len(n.path):
		// diff < len(path)
		// split the path and add to next
		// check the current next for match first
		// params have to match entirely
		if path[diff] == ':' {
			for _, next := range n.next {
				if next.path[0] == ':' {
					if next.path == slice(path, diff, len(next.path)) {
						if next.path[len(next.path) - 1] == '/' ||
							len(path) == diff + len(next.path) {
							return next.add(path[diff:])
						}
					}
				}
			}
		} else {
			for _, next := range n.next {
				if next.path[0] == path[diff] {
					return next.add(path[diff:])
				}
			}
		}
		// add new node if no match was found
		first, last := nodeSeq(path[diff:])

		n.next = append(n.next, first)

		return last
	case len(path):
		// diff < len(n.path)
		// split the n.path and set the node to path
		remainderNode := newNode()
		remainderNode.path = n.path[diff:]
		remainderNode.next = n.next
		remainderNode.handlers = n.handlers

		n.path = n.path[:diff]
		n.next = []*node{remainderNode}
		n.handlers = nil

		return n
	default:
		// diff < len(n.path) && diff < len(path)
		// split the n.path into two
		remainderNode := newNode()
		remainderNode.path = n.path[diff:]
		remainderNode.next = n.next
		remainderNode.handlers = n.handlers

		first, last := nodeSeq(path[diff:])

		n.path = n.path[:diff]
		n.next = []*node{remainderNode, first}
		n.handlers = nil

		return last
	}
}

// indexOf returns index of the next c occurence in s
// or len(s) if not found
func indexOf(s string, c rune) int {
	for i, letter := range s {
		if letter == c {
			return i
		}
	}
	return len(s)
}

func (n *node) get(path string, paramsCount int) (*node, []string) {
	switch n.path[0] {
	case ':':
		paramLen := indexOf(path, '/')
		// current node is a param
		if n.path[len(n.path) - 1] != '/' {
			if paramLen == len(path) {
				paramsPos := (paramsCount + 1) * 2
				paramsList := make([]string, paramsPos)
				paramsList[paramsPos - 2] = n.path[1:]
				paramsList[paramsPos - 1] = path
				return n, paramsList
			}
			// path exceeds the pattern
			return nil, nil
		}
		if paramLen == len(path) {
			// pattern exceeds the path
			return nil, nil
		}
		if paramLen + 1 == len(path) {
			paramsPos := (paramsCount + 1) * 2
			paramsList := make([]string, paramsPos)
			paramsList[paramsPos - 2] = n.path[1:len(n.path) - 1]
			paramsList[paramsPos - 1] = path[:paramLen]
			return n, paramsList
		}

		// check the current next for match
		for _, next := range n.next {
			firstLetter := next.path[0]
			if firstLetter == ':' || firstLetter == path[paramLen + 1] {
				node, paramsList := next.get(path[paramLen + 1:],
					paramsCount + 1)
				if node != nil {
					// exhaustive path was found
					// add param
					paramsPos := (paramsCount + 1) * 2
					if (paramsList == nil) {
						paramsList = make([]string, paramsPos)
					}
					paramsList[paramsPos - 2] = n.path[1:len(n.path) - 1]
					paramsList[paramsPos - 1] = path[:paramLen]
					return node, paramsList
				}
			}
		}
		// no exhaustive path
		return nil, nil
	default:
		// current node is regular
		if len(path) < len(n.path) {
			//no match
			return nil, nil
		}
		if len(path) == len(n.path) {
			//possible match
			if path == n.path {
				return n, nil
			}
			return nil, nil
		}
		if path[:len(n.path)] != n.path {
			return nil, nil
		}
		//if it's a begining of a new segment, can match param next
		canParam := path[len(n.path) - 1] == '/'
		for _, next := range n.next {
			firstLetter := next.path[0]
			if firstLetter == path[len(n.path)] || (canParam && firstLetter == ':') {
				node, paramsList := next.get(path[len(n.path):], paramsCount)
				if node != nil {
					return node, paramsList
				}
			}
		}
		return nil, nil
	}
}

func (n *node) handle(method string, handler http.Handler) {
	if n.handlers == nil {
		n.handlers = newHandlerArray()
	}
	n.handlers.add(method, handler)
}<commit_msg>Fix seq switch<commit_after>package gocelot

import (
	"net/http"
)

type node struct {
	path string
	next []*node
	handlers *handlerArray
}

func newNode() *node {
	return &node{}
}

// nodeSeqFromPath returns the first and the last node of the sequence.
// If the path doesn't contain params(':'), the first and the last nodes are
// the same.
// All the params(':') are stored in seperate nodes.
// Eg.
// nodeSeqFromPath('/path/:param/end/')
// returns node('/path/'), node('end/')
// and the sequence is node('/path') -> node(':param/') -> node('end/')
func nodeSeq(path string) (*node, *node) {
	first := newNode()
	last := first
	start, isParam := 0, false
	extendSeq := func(end int) {
		if start != end {
			if last.path == "" {
				last.path = path[start:end]
			} else {
				next := newNode()
				next.path = path[start:end]
				last.next = []*node{next}
				last = next
			}
		}
		start = end
	}
	for end, letter := range path {
		switch isParam {
		case false:
			if letter == ':' {
				extendSeq(end)
				isParam = true
			}
		case true:
			if letter == '/' {
				extendSeq(end + 1)
				isParam = false
			}
		}
	}
	if start != len(path) {
		extendSeq(len(path))
	}
	return first, last
}

// min returns the minimum of two intergers
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// lcp returns the longest common prefix of 2 words
func lcp(a, b string) int {
	lcp, minLen := 0, min(len(a), len(b))
	for lcp < minLen && a[lcp] == b[lcp] {
		lcp++
	}
	return lcp
}

// slice returns a slice of given length starting at start of the given string
func slice(path string, start, length int) string {
	return path[start:min(start+length, len(path))]
}

// addPath adds the path to the node and returns the last node which belongs
// to the path being added.
// The path in the node and the path which is added should have a common prefix.
// Otherwise after addition the current node will contain an empty path.
// Eg.
// node('/path/').AddPath('/paths/')
// returns node('s/')
// and the result sequence is
// node('/path') -> node('/')
// node('/path') -> node('s/')
// Note that different params are stored in different nodes.
// Eg.
// For starting sequence node('/path/') -> node(':param1/') -> node('end/')
// node('/path/').Add('/path/param2/finish/')
// returns node('finish/')
// and the result sequence is
// node('/path/') -> node(':param1/') -> node('end/')
// node('/path/') -> node(':param2/') -> node('finish/')
// NOT
// node('/path/') -> node(':param') -> node('1/end/')
// node('/path/') -> node(':param') -> node('2/finish/')
func (n *node) add(path string) *node {
	diff := lcp(n.path, path)
	// The given path already exists
	if len(n.path) == diff && len(path) == diff {
		return n
	}
	switch diff {
	case len(n.path):
		// diff < len(path)
		// split the path and add to next
		// check the current next for match first
		// params have to match entirely
		if path[diff] == ':' {
			for _, next := range n.next {
				if next.path[0] == ':' {
					if next.path == slice(path, diff, len(next.path)) {
						if next.path[len(next.path) - 1] == '/' ||
							len(path) == diff + len(next.path) {
							return next.add(path[diff:])
						}
					}
				}
			}
		} else {
			for _, next := range n.next {
				if next.path[0] == path[diff] {
					return next.add(path[diff:])
				}
			}
		}
		// add new node if no match was found
		first, last := nodeSeq(path[diff:])

		n.next = append(n.next, first)

		return last
	case len(path):
		// diff < len(n.path)
		// split the n.path and set the node to path
		remainderNode := newNode()
		remainderNode.path = n.path[diff:]
		remainderNode.next = n.next
		remainderNode.handlers = n.handlers

		n.path = n.path[:diff]
		n.next = []*node{remainderNode}
		n.handlers = nil

		return n
	default:
		// diff < len(n.path) && diff < len(path)
		// split the n.path into two
		remainderNode := newNode()
		remainderNode.path = n.path[diff:]
		remainderNode.next = n.next
		remainderNode.handlers = n.handlers

		first, last := nodeSeq(path[diff:])

		n.path = n.path[:diff]
		n.next = []*node{remainderNode, first}
		n.handlers = nil

		return last
	}
}

// indexOf returns index of the next c occurence in s
// or len(s) if not found
func indexOf(s string, c rune) int {
	for i, letter := range s {
		if letter == c {
			return i
		}
	}
	return len(s)
}

func (n *node) get(path string, paramsCount int) (*node, []string) {
	switch n.path[0] {
	case ':':
		paramLen := indexOf(path, '/')
		// current node is a param
		if n.path[len(n.path) - 1] != '/' {
			if paramLen == len(path) {
				paramsPos := (paramsCount + 1) * 2
				paramsList := make([]string, paramsPos)
				paramsList[paramsPos - 2] = n.path[1:]
				paramsList[paramsPos - 1] = path
				return n, paramsList
			}
			// path exceeds the pattern
			return nil, nil
		}
		if paramLen == len(path) {
			// pattern exceeds the path
			return nil, nil
		}
		if paramLen + 1 == len(path) {
			paramsPos := (paramsCount + 1) * 2
			paramsList := make([]string, paramsPos)
			paramsList[paramsPos - 2] = n.path[1:len(n.path) - 1]
			paramsList[paramsPos - 1] = path[:paramLen]
			return n, paramsList
		}

		// check the current next for match
		for _, next := range n.next {
			firstLetter := next.path[0]
			if firstLetter == ':' || firstLetter == path[paramLen + 1] {
				node, paramsList := next.get(path[paramLen + 1:],
					paramsCount + 1)
				if node != nil {
					// exhaustive path was found
					// add param
					paramsPos := (paramsCount + 1) * 2
					if (paramsList == nil) {
						paramsList = make([]string, paramsPos)
					}
					paramsList[paramsPos - 2] = n.path[1:len(n.path) - 1]
					paramsList[paramsPos - 1] = path[:paramLen]
					return node, paramsList
				}
			}
		}
		// no exhaustive path
		return nil, nil
	default:
		// current node is regular
		if len(path) < len(n.path) {
			//no match
			return nil, nil
		}
		if len(path) == len(n.path) {
			//possible match
			if path == n.path {
				return n, nil
			}
			return nil, nil
		}
		if path[:len(n.path)] != n.path {
			return nil, nil
		}
		//if it's a begining of a new segment, can match param next
		canParam := path[len(n.path) - 1] == '/'
		for _, next := range n.next {
			firstLetter := next.path[0]
			if firstLetter == path[len(n.path)] || (canParam && firstLetter == ':') {
				node, paramsList := next.get(path[len(n.path):], paramsCount)
				if node != nil {
					return node, paramsList
				}
			}
		}
		return nil, nil
	}
}

func (n *node) handle(method string, handler http.Handler) {
	if n.handlers == nil {
		n.handlers = newHandlerArray()
	}
	n.handlers.add(method, handler)
}<|endoftext|>


<commit_before>package nvml

/*
// #cgo CPPFLAGS: -I/path/to/install
// #cgo LDFLAGS: -L/path/to/install -l nvidia-ml

#include <nvml.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

// Not every function can be genericized in this way because of all the custom structs,
// but there are several nvmlGet functions we want that take a nvmlDevice_t, *char, and
// a length as arguments. These are trivial to pass as function pointers along with their,
// arguments, so we might as well save some effort.
//
typedef int (*gettextProperty) (nvmlDevice_t device , char *buf, uint length);
int bridge_get_text_property(gettextProperty f,
                             nvmlDevice_t device,
                             char *buf,
                             uint length)
{
    nvmlReturn_t ret;

    ret = f(device, buf, length);

    if (ret == NVML_SUCCESS) {
        return(EXIT_SUCCESS);
    } else {
        return(EXIT_FAILURE);
    }
}

// Same as above, but for integer properties
typedef int (*getintProperty) (nvmlDevice_t device , uint *property);
int bridge_get_int_property(getintProperty f,
                             nvmlDevice_t device,
                             uint *property)
{
    nvmlReturn_t ret;

    ret = f(device, property);

    if (ret == NVML_SUCCESS) {
        return(EXIT_SUCCESS);
    } else {
        return(EXIT_FAILURE);
    }
}

*/
import "C"

import (
	"errors"
	"log"
	"unsafe"
)

type Device struct {
	nvmldevice C.nvmlDevice_t
	pcibus     string
	name       string
	uuid       string
}

// NewDevice is a contstructor function for Device structs. Given an nvmlDevice_t
// object as input, it populates some static property fields and returns a Device
func NewDevice(cdevice C.nvmlDevice_t) (*Device, error) {
	device := Device{
		nvmldevice: cdevice,
	}

	uuid, err := device.UUID()
	if err != nil {
		return nil, errors.New("Cannot retrieve UUID property")
	}
	device.uuid = uuid

	name, err := device.Name()
	if err != nil {
		return nil, errors.New("Cannot retrieve Name property")
	}
	device.name = name

	return &device, nil
}

func (gpu *Device) PowerState() (int, error) {
	var pstate C.nvmlPstates_t
	var result C.nvmlReturn_t

	result = C.nvmlDeviceGetPowerState(gpu.nvmldevice, &pstate)
	if result != C.NVML_SUCCESS {
		return -1, errors.New("GetPowerState returned error")
	}

	return int(pstate), nil
}

// Temp returns the current temperature of the card in degrees Celsius
func (gpu *Device) Temp() (uint, error) {
	var result C.nvmlReturn_t
	var ctemp C.uint

	result = C.nvmlDeviceGetTemperature(gpu.nvmldevice, C.NVML_TEMPERATURE_GPU, &ctemp)
	if result != C.NVML_SUCCESS {
		return 0, errors.New("GetPowerState returned error")
	}

	return uint(ctemp), nil
}

type CIntPropFunc struct {
	f C.getintProperty
}

var intpropfunctions = map[string]*CIntPropFunc{
	"Index":                        {C.getintProperty(C.nvmlDeviceGetIndex)},
	"MinorNumber":                  {C.getintProperty(C.nvmlDeviceGetMinorNumber)},
	"InforomConfigurationChecksum": {C.getintProperty(C.nvmlDeviceGetInforomConfigurationChecksum)},
	"MaxPcieLinkGeneration":        {C.getintProperty(C.nvmlDeviceGetMaxPcieLinkGeneration)},
	"MaxPcieLinkWidth":             {C.getintProperty(C.nvmlDeviceGetMaxPcieLinkWidth)},
	"CurrPcieLinkGeneration":       {C.getintProperty(C.nvmlDeviceGetCurrPcieLinkGeneration)},
	"CurrPcieLinkWidth":            {C.getintProperty(C.nvmlDeviceGetCurrPcieLinkWidth)},
	"PcieReplayCounter":            {C.getintProperty(C.nvmlDeviceGetPcieReplayCounter)},
	"FanSpeed":                     {C.getintProperty(C.nvmlDeviceGetFanSpeed)},
	"PowerManagementLimit":         {C.getintProperty(C.nvmlDeviceGetPowerManagementLimit)},
	"PowerManagementDefaultLimit":  {C.getintProperty(C.nvmlDeviceGetPowerManagementDefaultLimit)},
	"PowerUsage":                   {C.getintProperty(C.nvmlDeviceGetPowerUsage)},
	"EnforcedPowerLimit":           {C.getintProperty(C.nvmlDeviceGetEnforcedPowerLimit)},
	"BoardId":                      {C.getintProperty(C.nvmlDeviceGetBoardId)},
	"MultiGpuBoard":                {C.getintProperty(C.nvmlDeviceGetMultiGpuBoard)},
	"AccountingBufferSize":         {C.getintProperty(C.nvmlDeviceGetAccountingBufferSize)},
}

func (gpu *Device) intProperty(property string) (uint, error) {
	var cuintproperty C.uint

	ipf, ok := intpropfunctions[property]
	if ok == false {
		return 0, errors.New("property not found")
	}

	result := C.bridge_get_int_property(ipf.f, gpu.nvmldevice, &cuintproperty)
	if result != C.EXIT_SUCCESS {
		return 0, errors.New("getintProperty bridge returned error")
	}

	return uint(cuintproperty), nil
}

func (gpu *Device) Index() (uint, error) {
	return gpu.intProperty("Index")
}

func (gpu *Device) MinorNumber() (uint, error) {
	return gpu.intProperty("MinorNumber")
}

func (gpu *Device) InforomConfigurationChecksum() (uint, error) {
	return gpu.intProperty("InforomConfigurationChecksum")
}

func (gpu *Device) MaxPcieLinkGeneration() (uint, error) {
	return gpu.intProperty("MaxPcieLinkGeneration")
}

func (gpu *Device) MaxPcieLinkWidth() (uint, error) {
	return gpu.intProperty("MaxPcieLinkWidth")
}

func (gpu *Device) CurrPcieLinkGeneration() (uint, error) {
	return gpu.intProperty("CurrPcieLinkGeneration")
}

func (gpu *Device) CurrPcieLinkWidth() (uint, error) {
	return gpu.intProperty("CurrPcieLinkWidth")
}

func (gpu *Device) PcieReplayCounter() (uint, error) {
	return gpu.intProperty("PcieReplayCounter")
}

func (gpu *Device) FanSpeed() (uint, error) {
	return gpu.intProperty("FanSpeed")
}

func (gpu *Device) PowerManagementLimit() (uint, error) {
	return gpu.intProperty("PowerManagementLimit")
}

func (gpu *Device) PowerManagementDefaultLimit() (uint, error) {
	return gpu.intProperty("PowerManagementDefaultLimit")
}

func (gpu *Device) PowerUsage() (uint, error) {
	return gpu.intProperty("PowerUsage")
}

func (gpu *Device) EnforcedPowerLimit() (uint, error) {
	return gpu.intProperty("EnforcedPowerLimit")
}

func (gpu *Device) BoardId() (uint, error) {
	return gpu.intProperty("BoardId")
}

func (gpu *Device) MultiGpuBoard() (uint, error) {
	return gpu.intProperty("MultiGpuBoard")
}

func (gpu *Device) AccountingBufferSize() (uint, error) {
	return gpu.intProperty("AccountingBufferSize")
}

type CTextPropFunc struct {
	f      C.gettextProperty
	length C.uint
}

var textpropfunctions = map[string]*CTextPropFunc{
	"Name":                {C.gettextProperty(C.nvmlDeviceGetName), C.NVML_DEVICE_NAME_BUFFER_SIZE},
	"Serial":              {C.gettextProperty(C.nvmlDeviceGetSerial), C.NVML_DEVICE_SERIAL_BUFFER_SIZE},
	"UUID":                {C.gettextProperty(C.nvmlDeviceGetUUID), C.NVML_DEVICE_UUID_BUFFER_SIZE},
	"InforomImageVersion": {C.gettextProperty(C.nvmlDeviceGetInforomImageVersion), C.NVML_DEVICE_INFOROM_VERSION_BUFFER_SIZE},
	"VbiosVersion":        {C.gettextProperty(C.nvmlDeviceGetVbiosVersion), C.NVML_DEVICE_VBIOS_VERSION_BUFFER_SIZE},
}

// textProperty takes a propertyname as input and then runs the corresponding
// function in the textpropfunctions map, returning the result as a Go string.
//
// textProperty takes care of allocating (and freeing) the text buffers of
// proper size.
func (gpu *Device) textProperty(property string) (string, error) {
	var propvalue string

	// If there isn't a valid entry for this property in the map, there's no reason
	// to process any further
	tpf, ok := textpropfunctions[property]
	if ok == false {
		return "", errors.New("property not found")
	}

	var buf *C.char = genCStringBuffer(uint(tpf.length))
	defer C.free(unsafe.Pointer(buf))

	result := C.bridge_get_text_property(tpf.f, gpu.nvmldevice, buf, tpf.length)
	if result != C.EXIT_SUCCESS {
		return propvalue, errors.New("gettextProperty bridge returned error")
	}

	propvalue = strndup(buf, int(tpf.length))
	if len(propvalue) > 0 {
		return propvalue, nil
	} else {
		return "", errors.New("textProperty returned empty string")
	}
}

func (gpu *Device) InforomImageVersion() (string, error) {
	return gpu.textProperty("InforomImageVersion")
}

func (gpu *Device) VbiosVersion() (string, error) {
	return gpu.textProperty("VbiosVersion")
}

// Return the product name of the device, e.g. "Tesla K40m"
func (gpu *Device) Name() (string, error) {
	return gpu.textProperty("Name")
}

// Return the UUID of the device
func (gpu *Device) UUID() (string, error) {
	return gpu.textProperty("UUID")
}

// Return the serial number of the device
func (gpu *Device) Serial() (string, error) {
	return gpu.textProperty("Serial")
}

// Go correspondent of the C.nvmlMemory_t struct. Memory in bytes
type NvmlMemory struct {
	Free  uint64
	Total uint64
	Used  uint64
}

// MemoryInfo returns a NvmlMemory struct populated with the amount of memory used,
// free, and in total on the device, in bytes.
func (gpu *Device) MemoryInfo() (NvmlMemory, error) {
	var result C.nvmlReturn_t
	var cmeminfo C.nvmlMemory_t
	var meminfo NvmlMemory

	result = C.nvmlDeviceGetMemoryInfo(gpu.nvmldevice, &cmeminfo)
	if result != C.NVML_SUCCESS {
		return meminfo, errors.New("GetPowerState returned error")
	}

	meminfo.Free = uint64(cmeminfo.free)
	meminfo.Total = uint64(cmeminfo.total)
	meminfo.Used = uint64(cmeminfo.used)

	return meminfo, nil
}

// Return a proper golang error of representation of the nvmlReturn_t error
func (gpu *Device) Error(cerror C.nvmlReturn_t) error {
	var cerrorstring *C.char

	// No need to process anything further if the nvml call succeeded
	if cerror == C.NVML_SUCCESS {
		return nil
	}

	cerrorstring = C.nvmlErrorString(cerror)
	if cerrorstring == nil {
		// I'm not sure how this could happen, but it's easy to check for
		return errors.New("Error not found in nvml.h")
	}

	return errors.New(C.GoString(cerrorstring))
}

// Initialize the NVML session
func nvmlInit() error {
	var result C.nvmlReturn_t

	result = C.nvmlInit()
	if result != C.NVML_SUCCESS {
		return errors.New("nvmlInit returned error")
	}

	return nil
}

func nvmlDeviceGetCount() (int, error) {
	var count C.uint

	result := C.nvmlDeviceGetCount(&count)
	if result != C.NVML_SUCCESS {
		return -1, errors.New("nvmlDeviceGetCount failed")
	}

	return int(count), nil
}

func GetAllGPUs() ([]Device, error) {
	var devices []Device
	cdevices, err := getAllDevices()
	if err != nil {
		return devices, err
	}

	for _, cdevice := range cdevices {
		device, err := NewDevice(cdevice)
		if err != nil {
			break
		}

		devices = append(devices, *device)
	}

	return devices, nil
}

// getAllDevices returns an array of nvmlDevice_t structs representing all GPU
// devices in the system.
func getAllDevices() ([]C.nvmlDevice_t, error) {
	var devices []C.nvmlDevice_t

	device_count, err := nvmlDeviceGetCount()
	if err != nil {
		log.Fatal(err)
	}

	for i := 0; i < device_count; i++ {
		var device C.nvmlDevice_t
		result := C.nvmlDeviceGetHandleByIndex(C.uint(i), &device)
		if result != C.NVML_SUCCESS {
			return devices, errors.New("nvmlDeviceGetHandleByIndex returns error")
		}

		devices = append(devices, device)
	}

	if len(devices) == 0 {
		return devices, errors.New("No devices found")
	}

	return devices, nil
}

// lots of the nvml functions require an allocated *char into which to place
// strings. genCStringBuffer() allocates this buffer and returns it.
//
// IMPORTANT: These buffers need to be freed! It is strongly advised to put:
//
//            defer C.free(unsafe.Pointer(buffer))
//
// immediately after the allocation of this buffer!
//
func genCStringBuffer(size uint) *C.char {
	buf := make([]byte, size)
	return C.CString(string(buf))
}

// strndup replicates the functionality of strndup from string.h in go to
// convert *C.char to string, respecting null-termination in the original.
// C.GoStringN doesn't stop at null characters.
//
// h/t: https://utcc.utoronto.ca/~cks/space/blog/programming/GoCGoStringFunctions
//
func strndup(cstring *C.char, length int) string {
	clength := C.int(C.strnlen(cstring, C.size_t(length)))
	gostring := C.GoStringN(cstring, clength)
	return gostring
}

func init() {
	err := nvmlInit()
	if err != nil {
		log.Fatal("Could not initialize NVML interface: %s\n", err)
	}
}
<commit_msg>unexport the Go/C function translation structs<commit_after>package nvml

/*
// #cgo CPPFLAGS: -I/path/to/install
// #cgo LDFLAGS: -L/path/to/install -l nvidia-ml

#include <nvml.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

// Not every function can be genericized in this way because of all the custom structs,
// but there are several nvmlGet functions we want that take a nvmlDevice_t, *char, and
// a length as arguments. These are trivial to pass as function pointers along with their,
// arguments, so we might as well save some effort.
//
typedef int (*gettextProperty) (nvmlDevice_t device , char *buf, uint length);
int bridge_get_text_property(gettextProperty f,
                             nvmlDevice_t device,
                             char *buf,
                             uint length)
{
    nvmlReturn_t ret;

    ret = f(device, buf, length);

    if (ret == NVML_SUCCESS) {
        return(EXIT_SUCCESS);
    } else {
        return(EXIT_FAILURE);
    }
}

// Same as above, but for integer properties
typedef int (*getintProperty) (nvmlDevice_t device , uint *property);
int bridge_get_int_property(getintProperty f,
                             nvmlDevice_t device,
                             uint *property)
{
    nvmlReturn_t ret;

    ret = f(device, property);

    if (ret == NVML_SUCCESS) {
        return(EXIT_SUCCESS);
    } else {
        return(EXIT_FAILURE);
    }
}

*/
import "C"

import (
	"errors"
	"log"
	"unsafe"
)

type Device struct {
	nvmldevice C.nvmlDevice_t
	pcibus     string
	name       string
	uuid       string
}

// NewDevice is a contstructor function for Device structs. Given an nvmlDevice_t
// object as input, it populates some static property fields and returns a Device
func NewDevice(cdevice C.nvmlDevice_t) (*Device, error) {
	device := Device{
		nvmldevice: cdevice,
	}

	uuid, err := device.UUID()
	if err != nil {
		return nil, errors.New("Cannot retrieve UUID property")
	}
	device.uuid = uuid

	name, err := device.Name()
	if err != nil {
		return nil, errors.New("Cannot retrieve Name property")
	}
	device.name = name

	return &device, nil
}

func (gpu *Device) PowerState() (int, error) {
	var pstate C.nvmlPstates_t
	var result C.nvmlReturn_t

	result = C.nvmlDeviceGetPowerState(gpu.nvmldevice, &pstate)
	if result != C.NVML_SUCCESS {
		return -1, errors.New("GetPowerState returned error")
	}

	return int(pstate), nil
}

// Temp returns the current temperature of the card in degrees Celsius
func (gpu *Device) Temp() (uint, error) {
	var result C.nvmlReturn_t
	var ctemp C.uint

	result = C.nvmlDeviceGetTemperature(gpu.nvmldevice, C.NVML_TEMPERATURE_GPU, &ctemp)
	if result != C.NVML_SUCCESS {
		return 0, errors.New("GetPowerState returned error")
	}

	return uint(ctemp), nil
}

type cIntPropFunc struct {
	f C.getintProperty
}

var intpropfunctions = map[string]*cIntPropFunc{
	"Index":                        {C.getintProperty(C.nvmlDeviceGetIndex)},
	"MinorNumber":                  {C.getintProperty(C.nvmlDeviceGetMinorNumber)},
	"InforomConfigurationChecksum": {C.getintProperty(C.nvmlDeviceGetInforomConfigurationChecksum)},
	"MaxPcieLinkGeneration":        {C.getintProperty(C.nvmlDeviceGetMaxPcieLinkGeneration)},
	"MaxPcieLinkWidth":             {C.getintProperty(C.nvmlDeviceGetMaxPcieLinkWidth)},
	"CurrPcieLinkGeneration":       {C.getintProperty(C.nvmlDeviceGetCurrPcieLinkGeneration)},
	"CurrPcieLinkWidth":            {C.getintProperty(C.nvmlDeviceGetCurrPcieLinkWidth)},
	"PcieReplayCounter":            {C.getintProperty(C.nvmlDeviceGetPcieReplayCounter)},
	"FanSpeed":                     {C.getintProperty(C.nvmlDeviceGetFanSpeed)},
	"PowerManagementLimit":         {C.getintProperty(C.nvmlDeviceGetPowerManagementLimit)},
	"PowerManagementDefaultLimit":  {C.getintProperty(C.nvmlDeviceGetPowerManagementDefaultLimit)},
	"PowerUsage":                   {C.getintProperty(C.nvmlDeviceGetPowerUsage)},
	"EnforcedPowerLimit":           {C.getintProperty(C.nvmlDeviceGetEnforcedPowerLimit)},
	"BoardId":                      {C.getintProperty(C.nvmlDeviceGetBoardId)},
	"MultiGpuBoard":                {C.getintProperty(C.nvmlDeviceGetMultiGpuBoard)},
	"AccountingBufferSize":         {C.getintProperty(C.nvmlDeviceGetAccountingBufferSize)},
}

func (gpu *Device) intProperty(property string) (uint, error) {
	var cuintproperty C.uint

	ipf, ok := intpropfunctions[property]
	if ok == false {
		return 0, errors.New("property not found")
	}

	result := C.bridge_get_int_property(ipf.f, gpu.nvmldevice, &cuintproperty)
	if result != C.EXIT_SUCCESS {
		return 0, errors.New("getintProperty bridge returned error")
	}

	return uint(cuintproperty), nil
}

func (gpu *Device) Index() (uint, error) {
	return gpu.intProperty("Index")
}

func (gpu *Device) MinorNumber() (uint, error) {
	return gpu.intProperty("MinorNumber")
}

func (gpu *Device) InforomConfigurationChecksum() (uint, error) {
	return gpu.intProperty("InforomConfigurationChecksum")
}

func (gpu *Device) MaxPcieLinkGeneration() (uint, error) {
	return gpu.intProperty("MaxPcieLinkGeneration")
}

func (gpu *Device) MaxPcieLinkWidth() (uint, error) {
	return gpu.intProperty("MaxPcieLinkWidth")
}

func (gpu *Device) CurrPcieLinkGeneration() (uint, error) {
	return gpu.intProperty("CurrPcieLinkGeneration")
}

func (gpu *Device) CurrPcieLinkWidth() (uint, error) {
	return gpu.intProperty("CurrPcieLinkWidth")
}

func (gpu *Device) PcieReplayCounter() (uint, error) {
	return gpu.intProperty("PcieReplayCounter")
}

func (gpu *Device) FanSpeed() (uint, error) {
	return gpu.intProperty("FanSpeed")
}

func (gpu *Device) PowerManagementLimit() (uint, error) {
	return gpu.intProperty("PowerManagementLimit")
}

func (gpu *Device) PowerManagementDefaultLimit() (uint, error) {
	return gpu.intProperty("PowerManagementDefaultLimit")
}

func (gpu *Device) PowerUsage() (uint, error) {
	return gpu.intProperty("PowerUsage")
}

func (gpu *Device) EnforcedPowerLimit() (uint, error) {
	return gpu.intProperty("EnforcedPowerLimit")
}

func (gpu *Device) BoardId() (uint, error) {
	return gpu.intProperty("BoardId")
}

func (gpu *Device) MultiGpuBoard() (uint, error) {
	return gpu.intProperty("MultiGpuBoard")
}

func (gpu *Device) AccountingBufferSize() (uint, error) {
	return gpu.intProperty("AccountingBufferSize")
}

type cTextPropFunc struct {
	f      C.gettextProperty
	length C.uint
}

var textpropfunctions = map[string]*cTextPropFunc{
	"Name":                {C.gettextProperty(C.nvmlDeviceGetName), C.NVML_DEVICE_NAME_BUFFER_SIZE},
	"Serial":              {C.gettextProperty(C.nvmlDeviceGetSerial), C.NVML_DEVICE_SERIAL_BUFFER_SIZE},
	"UUID":                {C.gettextProperty(C.nvmlDeviceGetUUID), C.NVML_DEVICE_UUID_BUFFER_SIZE},
	"InforomImageVersion": {C.gettextProperty(C.nvmlDeviceGetInforomImageVersion), C.NVML_DEVICE_INFOROM_VERSION_BUFFER_SIZE},
	"VbiosVersion":        {C.gettextProperty(C.nvmlDeviceGetVbiosVersion), C.NVML_DEVICE_VBIOS_VERSION_BUFFER_SIZE},
}

// textProperty takes a propertyname as input and then runs the corresponding
// function in the textpropfunctions map, returning the result as a Go string.
//
// textProperty takes care of allocating (and freeing) the text buffers of
// proper size.
func (gpu *Device) textProperty(property string) (string, error) {
	var propvalue string

	// If there isn't a valid entry for this property in the map, there's no reason
	// to process any further
	tpf, ok := textpropfunctions[property]
	if ok == false {
		return "", errors.New("property not found")
	}

	var buf *C.char = genCStringBuffer(uint(tpf.length))
	defer C.free(unsafe.Pointer(buf))

	result := C.bridge_get_text_property(tpf.f, gpu.nvmldevice, buf, tpf.length)
	if result != C.EXIT_SUCCESS {
		return propvalue, errors.New("gettextProperty bridge returned error")
	}

	propvalue = strndup(buf, int(tpf.length))
	if len(propvalue) > 0 {
		return propvalue, nil
	} else {
		return "", errors.New("textProperty returned empty string")
	}
}

func (gpu *Device) InforomImageVersion() (string, error) {
	return gpu.textProperty("InforomImageVersion")
}

func (gpu *Device) VbiosVersion() (string, error) {
	return gpu.textProperty("VbiosVersion")
}

// Return the product name of the device, e.g. "Tesla K40m"
func (gpu *Device) Name() (string, error) {
	return gpu.textProperty("Name")
}

// Return the UUID of the device
func (gpu *Device) UUID() (string, error) {
	return gpu.textProperty("UUID")
}

// Return the serial number of the device
func (gpu *Device) Serial() (string, error) {
	return gpu.textProperty("Serial")
}

// Go correspondent of the C.nvmlMemory_t struct. Memory in bytes
type NvmlMemory struct {
	Free  uint64
	Total uint64
	Used  uint64
}

// MemoryInfo returns a NvmlMemory struct populated with the amount of memory used,
// free, and in total on the device, in bytes.
func (gpu *Device) MemoryInfo() (NvmlMemory, error) {
	var result C.nvmlReturn_t
	var cmeminfo C.nvmlMemory_t
	var meminfo NvmlMemory

	result = C.nvmlDeviceGetMemoryInfo(gpu.nvmldevice, &cmeminfo)
	if result != C.NVML_SUCCESS {
		return meminfo, errors.New("GetPowerState returned error")
	}

	meminfo.Free = uint64(cmeminfo.free)
	meminfo.Total = uint64(cmeminfo.total)
	meminfo.Used = uint64(cmeminfo.used)

	return meminfo, nil
}

// Return a proper golang error of representation of the nvmlReturn_t error
func (gpu *Device) Error(cerror C.nvmlReturn_t) error {
	var cerrorstring *C.char

	// No need to process anything further if the nvml call succeeded
	if cerror == C.NVML_SUCCESS {
		return nil
	}

	cerrorstring = C.nvmlErrorString(cerror)
	if cerrorstring == nil {
		// I'm not sure how this could happen, but it's easy to check for
		return errors.New("Error not found in nvml.h")
	}

	return errors.New(C.GoString(cerrorstring))
}

// Initialize the NVML session
func nvmlInit() error {
	var result C.nvmlReturn_t

	result = C.nvmlInit()
	if result != C.NVML_SUCCESS {
		return errors.New("nvmlInit returned error")
	}

	return nil
}

func nvmlDeviceGetCount() (int, error) {
	var count C.uint

	result := C.nvmlDeviceGetCount(&count)
	if result != C.NVML_SUCCESS {
		return -1, errors.New("nvmlDeviceGetCount failed")
	}

	return int(count), nil
}

func GetAllGPUs() ([]Device, error) {
	var devices []Device
	cdevices, err := getAllDevices()
	if err != nil {
		return devices, err
	}

	for _, cdevice := range cdevices {
		device, err := NewDevice(cdevice)
		if err != nil {
			break
		}

		devices = append(devices, *device)
	}

	return devices, nil
}

// getAllDevices returns an array of nvmlDevice_t structs representing all GPU
// devices in the system.
func getAllDevices() ([]C.nvmlDevice_t, error) {
	var devices []C.nvmlDevice_t

	device_count, err := nvmlDeviceGetCount()
	if err != nil {
		log.Fatal(err)
	}

	for i := 0; i < device_count; i++ {
		var device C.nvmlDevice_t
		result := C.nvmlDeviceGetHandleByIndex(C.uint(i), &device)
		if result != C.NVML_SUCCESS {
			return devices, errors.New("nvmlDeviceGetHandleByIndex returns error")
		}

		devices = append(devices, device)
	}

	if len(devices) == 0 {
		return devices, errors.New("No devices found")
	}

	return devices, nil
}

// lots of the nvml functions require an allocated *char into which to place
// strings. genCStringBuffer() allocates this buffer and returns it.
//
// IMPORTANT: These buffers need to be freed! It is strongly advised to put:
//
//            defer C.free(unsafe.Pointer(buffer))
//
// immediately after the allocation of this buffer!
//
func genCStringBuffer(size uint) *C.char {
	buf := make([]byte, size)
	return C.CString(string(buf))
}

// strndup replicates the functionality of strndup from string.h in go to
// convert *C.char to string, respecting null-termination in the original.
// C.GoStringN doesn't stop at null characters.
//
// h/t: https://utcc.utoronto.ca/~cks/space/blog/programming/GoCGoStringFunctions
//
func strndup(cstring *C.char, length int) string {
	clength := C.int(C.strnlen(cstring, C.size_t(length)))
	gostring := C.GoStringN(cstring, clength)
	return gostring
}

func init() {
	err := nvmlInit()
	if err != nil {
		log.Fatal("Could not initialize NVML interface: %s\n", err)
	}
}
<|endoftext|>


<commit_before>package oci8

/*
#include <oci.h>
#include <stdlib.h>
#include <string.h>

#cgo pkg-config: oci8
*/
import "C"
import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"
	"io"
	"strings"
	"unsafe"
)

func init() {
	sql.Register("oci8", &OCI8Driver{})
}

type OCI8Driver struct {
}

type OCI8Conn struct {
	svc unsafe.Pointer
	env unsafe.Pointer
	err unsafe.Pointer
}

type OCI8Tx struct {
	c *OCI8Conn
}

func (tx *OCI8Tx) Commit() error {
	if err := tx.c.exec("COMMIT"); err != nil {
		return err
	}
	return nil
}

func (tx *OCI8Tx) Rollback() error {
	if err := tx.c.exec("ROLLBACK"); err != nil {
		return err
	}
	return nil
}

func (c *OCI8Conn) exec(cmd string) error {
	stmt, err := c.Prepare(cmd)
	if err == nil {
		defer stmt.Close()
		_, err = stmt.Exec(nil)
	}
	return err
}

func (c *OCI8Conn) Begin() (driver.Tx, error) {
	if err := c.exec("BEGIN"); err != nil {
		return nil, err
	}
	return &OCI8Tx{c}, nil
}

func (d *OCI8Driver) Open(dsn string) (driver.Conn, error) {
	var conn OCI8Conn
	token := strings.SplitN(dsn, "@", 2)
	userpass := strings.SplitN(token[0], "/", 2)

	rv := C.OCIInitialize(
		C.OCI_DEFAULT,
		nil,
		nil,
		nil,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	rv = C.OCIEnvInit(
		(**C.OCIEnv)(unsafe.Pointer(&conn.env)),
		C.OCI_DEFAULT,
		0,
		nil)

	rv = C.OCIHandleAlloc(
		conn.env,
		&conn.err,
		C.OCI_HTYPE_ERROR,
		0,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	var phost *C.char
	phostlen := C.size_t(0)
	if len(token) > 1 {
		phost = C.CString(token[1])
		defer C.free(unsafe.Pointer(phost))
		phostlen = C.strlen(phost)
	}
	puser := C.CString(userpass[0])
	defer C.free(unsafe.Pointer(puser))
	ppass := C.CString(userpass[1])
	defer C.free(unsafe.Pointer(ppass))

	rv = C.OCILogon(
		(*C.OCIEnv)(conn.env),
		(*C.OCIError)(conn.err),
		(**C.OCIServer)(unsafe.Pointer(&conn.svc)),
		(*C.OraText)(unsafe.Pointer(puser)),
		C.ub4(C.strlen(puser)),
		(*C.OraText)(unsafe.Pointer(ppass)),
		C.ub4(C.strlen(ppass)),
		(*C.OraText)(unsafe.Pointer(phost)),
		C.ub4(phostlen))
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	return &conn, nil
}

func (c *OCI8Conn) Close() error {
	rv := C.OCILogoff(
		(*C.OCIServer)(c.svc),
		(*C.OCIError)(c.err))
	if rv == C.OCI_ERROR {
		return ociGetError(c.err)
	}

	C.OCIHandleFree(
		c.env,
		C.OCI_HTYPE_ENV)

	c.svc = nil
	c.env = nil
	c.err = nil
	return nil
}

type OCI8Stmt struct {
	c      *OCI8Conn
	s      unsafe.Pointer
	closed bool
}

func (c *OCI8Conn) Prepare(query string) (driver.Stmt, error) {
	pquery := C.CString(query)
	defer C.free(unsafe.Pointer(pquery))
	var s unsafe.Pointer

	rv := C.OCIHandleAlloc(
		c.env,
		&s,
		C.OCI_HTYPE_STMT,
		0,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(c.err)
	}

	rv = C.OCIStmtPrepare(
		(*C.OCIStmt)(s),
		(*C.OCIError)(c.err),
		(*C.OraText)(unsafe.Pointer(pquery)),
		C.ub4(C.strlen(pquery)),
		C.ub4(C.OCI_NTV_SYNTAX),
		C.ub4(C.OCI_DEFAULT))
	if rv == C.OCI_ERROR {
		return nil, ociGetError(c.err)
	}

	return &OCI8Stmt{c: c, s: s}, nil
}

func (s *OCI8Stmt) Close() error {
	if s.closed {
		return nil
	}
	s.closed = true

	C.OCIHandleFree(
		s.s,
		C.OCI_HTYPE_STMT)
	s.s = nil

	return nil
}

func (s *OCI8Stmt) NumInput() int {
	var num C.int
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&num),
		nil,
		C.OCI_ATTR_BIND_COUNT,
		(*C.OCIError)(s.c.err))
	return int(num)
}

func (s *OCI8Stmt) bind(args []driver.Value) error {
	if args == nil {
		return nil
	}

	var bp *C.OCIBind
	for i, v := range args {
		b := []byte(fmt.Sprintf("%v", v))
		b = append(b, 0)
		rv := C.OCIBindByPos(
			(*C.OCIStmt)(s.s),
			&bp,
			(*C.OCIError)(s.c.err),
			C.ub4(i+1),
			unsafe.Pointer(&b[0]),
			C.sb4(len(b)),
			C.SQLT_STR,
			nil,
			nil,
			nil,
			0,
			nil,
			C.OCI_DEFAULT)

		if rv == C.OCI_ERROR {
			return ociGetError(s.c.err)
		}
	}
	return nil
}

func (s *OCI8Stmt) Query(args []driver.Value) (driver.Rows, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}

	var t C.int
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_STMT_TYPE,
		(*C.OCIError)(s.c.err))
	iter := C.ub4(1)
	if t == C.OCI_STMT_SELECT {
		iter = 0
	}

	rv := C.OCIStmtExecute(
		(*C.OCIServer)(s.c.svc),
		(*C.OCIStmt)(s.s),
		(*C.OCIError)(s.c.err),
		iter,
		0,
		nil,
		nil,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(s.c.err)
	}

	var rc C.ub2
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&rc),
		nil,
		C.OCI_ATTR_PARAM_COUNT,
		(*C.OCIError)(s.c.err))

	oci8cols := make([]oci8col, int(rc))
	for i := 0; i < int(rc); i++ {
		var p unsafe.Pointer
		var np *C.char
		var ns C.ub4
		var tp C.ub2
		var lp C.ub2
		C.OCIParamGet(
			s.s,
			C.OCI_HTYPE_STMT,
			(*C.OCIError)(s.c.err),
			(*unsafe.Pointer)(unsafe.Pointer(&p)),
			C.ub4(i+1))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&tp),
			nil,
			C.OCI_ATTR_DATA_TYPE,
			(*C.OCIError)(s.c.err))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&np),
			&ns,
			C.OCI_ATTR_NAME,
			(*C.OCIError)(s.c.err))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&lp),
			nil,
			C.OCI_ATTR_DATA_SIZE,
			(*C.OCIError)(s.c.err))
		oci8cols[i].name = string((*[1 << 30]byte)(unsafe.Pointer(np))[0:int(ns)])
		oci8cols[i].kind = int(tp)
		oci8cols[i].size = int(lp)
		oci8cols[i].pbuf = make([]byte, int(lp)+1)

		var defp *C.OCIDefine
		rv = C.OCIDefineByPos(
			(*C.OCIStmt)(s.s),
			&defp,
			(*C.OCIError)(s.c.err),
			C.ub4(i+1),
			unsafe.Pointer(&oci8cols[i].pbuf[0]),
			C.sb4(lp+1),
			C.SQLT_CHR,
			unsafe.Pointer(&oci8cols[i].ind),
			&oci8cols[i].rlen,
			nil,
			C.OCI_DEFAULT)
		if rv == C.OCI_ERROR {
			return nil, ociGetError(s.c.err)
		}
	}
	return &OCI8Rows{s, oci8cols, false}, nil
}

type OCI8Result struct {
	s *OCI8Stmt
}

func (r *OCI8Result) LastInsertId() (int64, error) {
	var t C.ub4
	rv := C.OCIAttrGet(
		r.s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_ROWID,
		(*C.OCIError)(r.s.c.err))
	if rv == C.OCI_ERROR {
		return 0, ociGetError(r.s.c.err)
	}
	return int64(t), nil
}

func (r *OCI8Result) RowsAffected() (int64, error) {
	var t C.ub4
	rv := C.OCIAttrGet(
		r.s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_ROW_COUNT,
		(*C.OCIError)(r.s.c.err))
	if rv == C.OCI_ERROR {
		return 0, ociGetError(r.s.c.err)
	}
	return int64(t), nil
}

func (s *OCI8Stmt) Exec(args []driver.Value) (driver.Result, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}

	rv := C.OCIStmtExecute(
		(*C.OCIServer)(s.c.svc),
		(*C.OCIStmt)(s.s),
		(*C.OCIError)(s.c.err),
		1,
		0,
		nil,
		nil,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(s.c.err)
	}
	return &OCI8Result{s}, nil
}

type oci8col struct {
	name string
	kind int
	size int
	ind  C.sb2
	rlen C.ub2
	pbuf []byte
}

type OCI8Rows struct {
	s    *OCI8Stmt
	cols []oci8col
	e    bool
}

func (rc *OCI8Rows) Close() error {
	return rc.s.Close()
}

func (rc *OCI8Rows) Columns() []string {
	cols := make([]string, len(rc.cols))
	for i, col := range rc.cols {
		cols[i] = col.name
	}
	return cols
}

func (rc *OCI8Rows) Next(dest []driver.Value) error {
	rv := C.OCIStmtFetch(
		(*C.OCIStmt)(rc.s.s),
		(*C.OCIError)(rc.s.c.err),
		1,
		C.OCI_FETCH_NEXT,
		C.OCI_DEFAULT)

	if rv == C.OCI_ERROR {
		return ociGetError(rc.s.c.err)
	}

	if rv == C.OCI_NO_DATA {
		return io.EOF
	}
	for i := range dest {
		switch {
		case rc.cols[i].ind == -1: //Null
			dest[i] = nil
		case rc.cols[i].ind == 0: //Normal
			dest[i] = string(rc.cols[i].pbuf)[0:rc.cols[i].rlen]
		case rc.cols[i].ind == -2 || //Field longer than type (truncated)
			rc.cols[i].ind > 0: //Field longer than type (truncated). Value is original length.
			dest[i] = string(rc.cols[i].pbuf)
		default:
			return errors.New(fmt.Sprintf("Unknown column indicator: %d", rc.cols[i].ind))
		}
	}

	return nil
}

func ociGetError(err unsafe.Pointer) error {
	var errcode C.sb4
	var errbuff [512]C.char
	C.OCIErrorGet(
		err,
		1,
		nil,
		&errcode,
		(*C.OraText)(unsafe.Pointer(&errbuff[0])),
		512,
		C.OCI_HTYPE_ERROR)
	s := C.GoString(&errbuff[0])
	//println(s)
	return errors.New(s)
}
<commit_msg>Added handling of Date column type<commit_after>package oci8

/*
#include <oci.h>
#include <stdlib.h>
#include <string.h>

#cgo pkg-config: oci8
*/
import "C"
import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"
	"io"
	"strings"
	"time"
	"unsafe"
)

func init() {
	sql.Register("oci8", &OCI8Driver{})
}

type OCI8Driver struct {
}

type OCI8Conn struct {
	svc unsafe.Pointer
	env unsafe.Pointer
	err unsafe.Pointer
}

type OCI8Tx struct {
	c *OCI8Conn
}

func (tx *OCI8Tx) Commit() error {
	if err := tx.c.exec("COMMIT"); err != nil {
		return err
	}
	return nil
}

func (tx *OCI8Tx) Rollback() error {
	if err := tx.c.exec("ROLLBACK"); err != nil {
		return err
	}
	return nil
}

func (c *OCI8Conn) exec(cmd string) error {
	stmt, err := c.Prepare(cmd)
	if err == nil {
		defer stmt.Close()
		_, err = stmt.Exec(nil)
	}
	return err
}

func (c *OCI8Conn) Begin() (driver.Tx, error) {
	if err := c.exec("BEGIN"); err != nil {
		return nil, err
	}
	return &OCI8Tx{c}, nil
}

func (d *OCI8Driver) Open(dsn string) (driver.Conn, error) {
	var conn OCI8Conn
	token := strings.SplitN(dsn, "@", 2)
	userpass := strings.SplitN(token[0], "/", 2)

	rv := C.OCIInitialize(
		C.OCI_DEFAULT,
		nil,
		nil,
		nil,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	rv = C.OCIEnvInit(
		(**C.OCIEnv)(unsafe.Pointer(&conn.env)),
		C.OCI_DEFAULT,
		0,
		nil)

	rv = C.OCIHandleAlloc(
		conn.env,
		&conn.err,
		C.OCI_HTYPE_ERROR,
		0,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	var phost *C.char
	phostlen := C.size_t(0)
	if len(token) > 1 {
		phost = C.CString(token[1])
		defer C.free(unsafe.Pointer(phost))
		phostlen = C.strlen(phost)
	}
	puser := C.CString(userpass[0])
	defer C.free(unsafe.Pointer(puser))
	ppass := C.CString(userpass[1])
	defer C.free(unsafe.Pointer(ppass))

	rv = C.OCILogon(
		(*C.OCIEnv)(conn.env),
		(*C.OCIError)(conn.err),
		(**C.OCIServer)(unsafe.Pointer(&conn.svc)),
		(*C.OraText)(unsafe.Pointer(puser)),
		C.ub4(C.strlen(puser)),
		(*C.OraText)(unsafe.Pointer(ppass)),
		C.ub4(C.strlen(ppass)),
		(*C.OraText)(unsafe.Pointer(phost)),
		C.ub4(phostlen))
	if rv == C.OCI_ERROR {
		return nil, ociGetError(conn.err)
	}

	return &conn, nil
}

func (c *OCI8Conn) Close() error {
	rv := C.OCILogoff(
		(*C.OCIServer)(c.svc),
		(*C.OCIError)(c.err))
	if rv == C.OCI_ERROR {
		return ociGetError(c.err)
	}

	C.OCIHandleFree(
		c.env,
		C.OCI_HTYPE_ENV)

	c.svc = nil
	c.env = nil
	c.err = nil
	return nil
}

type OCI8Stmt struct {
	c      *OCI8Conn
	s      unsafe.Pointer
	closed bool
}

func (c *OCI8Conn) Prepare(query string) (driver.Stmt, error) {
	pquery := C.CString(query)
	defer C.free(unsafe.Pointer(pquery))
	var s unsafe.Pointer

	rv := C.OCIHandleAlloc(
		c.env,
		&s,
		C.OCI_HTYPE_STMT,
		0,
		nil)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(c.err)
	}

	rv = C.OCIStmtPrepare(
		(*C.OCIStmt)(s),
		(*C.OCIError)(c.err),
		(*C.OraText)(unsafe.Pointer(pquery)),
		C.ub4(C.strlen(pquery)),
		C.ub4(C.OCI_NTV_SYNTAX),
		C.ub4(C.OCI_DEFAULT))
	if rv == C.OCI_ERROR {
		return nil, ociGetError(c.err)
	}

	return &OCI8Stmt{c: c, s: s}, nil
}

func (s *OCI8Stmt) Close() error {
	if s.closed {
		return nil
	}
	s.closed = true

	C.OCIHandleFree(
		s.s,
		C.OCI_HTYPE_STMT)
	s.s = nil

	return nil
}

func (s *OCI8Stmt) NumInput() int {
	var num C.int
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&num),
		nil,
		C.OCI_ATTR_BIND_COUNT,
		(*C.OCIError)(s.c.err))
	return int(num)
}

func (s *OCI8Stmt) bind(args []driver.Value) error {
	if args == nil {
		return nil
	}

	var bp *C.OCIBind
	for i, v := range args {
		b := []byte(fmt.Sprintf("%v", v))
		b = append(b, 0)
		rv := C.OCIBindByPos(
			(*C.OCIStmt)(s.s),
			&bp,
			(*C.OCIError)(s.c.err),
			C.ub4(i+1),
			unsafe.Pointer(&b[0]),
			C.sb4(len(b)),
			C.SQLT_STR,
			nil,
			nil,
			nil,
			0,
			nil,
			C.OCI_DEFAULT)

		if rv == C.OCI_ERROR {
			return ociGetError(s.c.err)
		}
	}
	return nil
}

func (s *OCI8Stmt) Query(args []driver.Value) (driver.Rows, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}

	var t C.int
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_STMT_TYPE,
		(*C.OCIError)(s.c.err))
	iter := C.ub4(1)
	if t == C.OCI_STMT_SELECT {
		iter = 0
	}

	rv := C.OCIStmtExecute(
		(*C.OCIServer)(s.c.svc),
		(*C.OCIStmt)(s.s),
		(*C.OCIError)(s.c.err),
		iter,
		0,
		nil,
		nil,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(s.c.err)
	}

	var rc C.ub2
	C.OCIAttrGet(
		s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&rc),
		nil,
		C.OCI_ATTR_PARAM_COUNT,
		(*C.OCIError)(s.c.err))

	oci8cols := make([]oci8col, int(rc))
	for i := 0; i < int(rc); i++ {
		var p unsafe.Pointer
		var np *C.char
		var ns C.ub4
		var tp C.ub2
		var lp C.ub2
		C.OCIParamGet(
			s.s,
			C.OCI_HTYPE_STMT,
			(*C.OCIError)(s.c.err),
			(*unsafe.Pointer)(unsafe.Pointer(&p)),
			C.ub4(i+1))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&tp),
			nil,
			C.OCI_ATTR_DATA_TYPE,
			(*C.OCIError)(s.c.err))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&np),
			&ns,
			C.OCI_ATTR_NAME,
			(*C.OCIError)(s.c.err))
		C.OCIAttrGet(
			p,
			C.OCI_DTYPE_PARAM,
			unsafe.Pointer(&lp),
			nil,
			C.OCI_ATTR_DATA_SIZE,
			(*C.OCIError)(s.c.err))
		oci8cols[i].name = string((*[1 << 30]byte)(unsafe.Pointer(np))[0:int(ns)])
		oci8cols[i].kind = tp
		oci8cols[i].size = int(lp)
		oci8cols[i].pbuf = make([]byte, int(lp)+1)

		var defp *C.OCIDefine
		rv = C.OCIDefineByPos(
			(*C.OCIStmt)(s.s),
			&defp,
			(*C.OCIError)(s.c.err),
			C.ub4(i+1),
			unsafe.Pointer(&oci8cols[i].pbuf[0]),
			C.sb4(lp+1),
			oci8cols[i].kind,
			unsafe.Pointer(&oci8cols[i].ind),
			&oci8cols[i].rlen,
			nil,
			C.OCI_DEFAULT)
		if rv == C.OCI_ERROR {
			return nil, ociGetError(s.c.err)
		}
	}
	return &OCI8Rows{s, oci8cols, false}, nil
}

type OCI8Result struct {
	s *OCI8Stmt
}

func (r *OCI8Result) LastInsertId() (int64, error) {
	var t C.ub4
	rv := C.OCIAttrGet(
		r.s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_ROWID,
		(*C.OCIError)(r.s.c.err))
	if rv == C.OCI_ERROR {
		return 0, ociGetError(r.s.c.err)
	}
	return int64(t), nil
}

func (r *OCI8Result) RowsAffected() (int64, error) {
	var t C.ub4
	rv := C.OCIAttrGet(
		r.s.s,
		C.OCI_HTYPE_STMT,
		unsafe.Pointer(&t),
		nil,
		C.OCI_ATTR_ROW_COUNT,
		(*C.OCIError)(r.s.c.err))
	if rv == C.OCI_ERROR {
		return 0, ociGetError(r.s.c.err)
	}
	return int64(t), nil
}

func (s *OCI8Stmt) Exec(args []driver.Value) (driver.Result, error) {
	if err := s.bind(args); err != nil {
		return nil, err
	}

	rv := C.OCIStmtExecute(
		(*C.OCIServer)(s.c.svc),
		(*C.OCIStmt)(s.s),
		(*C.OCIError)(s.c.err),
		1,
		0,
		nil,
		nil,
		C.OCI_DEFAULT)
	if rv == C.OCI_ERROR {
		return nil, ociGetError(s.c.err)
	}
	return &OCI8Result{s}, nil
}

type oci8col struct {
	name string
	kind C.ub2
	size int
	ind  C.sb2
	rlen C.ub2
	pbuf []byte
}

type OCI8Rows struct {
	s    *OCI8Stmt
	cols []oci8col
	e    bool
}

func (rc *OCI8Rows) Close() error {
	return rc.s.Close()
}

func (rc *OCI8Rows) Columns() []string {
	cols := make([]string, len(rc.cols))
	for i, col := range rc.cols {
		cols[i] = col.name
	}
	return cols
}

func (rc *OCI8Rows) Next(dest []driver.Value) error {
	rv := C.OCIStmtFetch(
		(*C.OCIStmt)(rc.s.s),
		(*C.OCIError)(rc.s.c.err),
		1,
		C.OCI_FETCH_NEXT,
		C.OCI_DEFAULT)

	if rv == C.OCI_ERROR {
		return ociGetError(rc.s.c.err)
	}

	if rv == C.OCI_NO_DATA {
		return io.EOF
	}
	for i := range dest {
		if rc.cols[i].ind == -1 { //Null
			dest[i] = nil
			continue
		}

		buf := rc.cols[i].pbuf
		switch rc.cols[i].kind {
		case C.SQLT_DAT:
			//TODO Handle BCE dates (http://docs.oracle.com/cd/B12037_01/appdev.101/b10779/oci03typ.htm#438305)
			//TODO Handle timezones (http://docs.oracle.com/cd/B12037_01/appdev.101/b10779/oci03typ.htm#443601)
			dest[i] = time.Date(((int(buf[0])-100)*100)+(int(buf[1])-100), time.Month(int(buf[2])), int(buf[3]), int(buf[4])-1, int(buf[5])-1, int(buf[6])-1, 0, time.UTC)
		case C.SQLT_CHR:
			switch {
			case rc.cols[i].ind == 0: //Normal
				dest[i] = string(buf)[0:rc.cols[i].rlen]
			case rc.cols[i].ind == -2 || //Field longer than type (truncated)
				rc.cols[i].ind > 0: //Field longer than type (truncated). Value is original length.
				dest[i] = string(buf)
			default:
				return errors.New(fmt.Sprintf("Unknown column indicator: %d", rc.cols[i].ind))
			}
		default:
			return errors.New(fmt.Sprintf("Unhandled column type: %d", rc.cols[i].kind))
		}
	}

	return nil
}

func ociGetError(err unsafe.Pointer) error {
	var errcode C.sb4
	var errbuff [512]C.char
	C.OCIErrorGet(
		err,
		1,
		nil,
		&errcode,
		(*C.OraText)(unsafe.Pointer(&errbuff[0])),
		512,
		C.OCI_HTYPE_ERROR)
	s := C.GoString(&errbuff[0])
	//println(s)
	return errors.New(s)
}
<|endoftext|>


<commit_before>/*

OpenImageIO bindings

https://sites.google.com/site/openimageio/home

OpenImageIO is a library for reading and writing images, and a bunch of related classes,
utilities, and applications.  There is a particular emphasis on formats and functionality
used in professional, large-scale animation and visual effects work for film.
OpenImageIO is used extensively in animation and VFX studios all over the world, and is
also incorporated into several commercial products.

*/
package oiio

/*
#cgo CPPFLAGS: -I./cpp
#cgo CPPFLAGS: -I/usr/local/include
#cgo LDFLAGS: -lstdc++
#cgo LDFLAGS: -L/usr/local/lib -lOpenImageIO

#include "stdlib.h"

#include "cpp/oiio.h"

extern bool image_progress_callback(void* goCallback, float done);

*/
import "C"

import (
	"unsafe"
)

// Various representation formats for image data
type TypeDesc int

const (
	TypeUnknown TypeDesc = C.TYPE_UNKNOWN
	TypeUint8   TypeDesc = C.TYPE_UINT8
	TypeInt8    TypeDesc = C.TYPE_INT8
	TypeUint16  TypeDesc = C.TYPE_UINT16
	TypeInt16   TypeDesc = C.TYPE_INT16
	TypeUint    TypeDesc = C.TYPE_UINT
	TypeInt     TypeDesc = C.TYPE_INT
	TypeUint64  TypeDesc = C.TYPE_UINT64
	TypeInt64   TypeDesc = C.TYPE_INT64
	TypeHalf    TypeDesc = C.TYPE_HALF
	TypeFloat   TypeDesc = C.TYPE_FLOAT
	TypeDouble  TypeDesc = C.TYPE_DOUBLE
)

// For image reading functions that accept a callback to monitor progress.
// A function that will be passed a float value indicating the progress
// percentage of the current operation. If the functon returns true, then
// the process should be aborted. Return false to allow processing to continue.
type ProgressCallback func(done float32) bool

//export image_progress_callback
func image_progress_callback(goCallback unsafe.Pointer, done C.float) C.bool {
	if goCallback == nil {
		return C.bool(false)
	}

	fn := *(*ProgressCallback)(goCallback)
	cancel := fn(float32(done))
	return C.bool(cancel)
}
<commit_msg>ProgressCallback doc typo<commit_after>/*

OpenImageIO bindings

https://sites.google.com/site/openimageio/home

OpenImageIO is a library for reading and writing images, and a bunch of related classes,
utilities, and applications.  There is a particular emphasis on formats and functionality
used in professional, large-scale animation and visual effects work for film.
OpenImageIO is used extensively in animation and VFX studios all over the world, and is
also incorporated into several commercial products.

*/
package oiio

/*
#cgo CPPFLAGS: -I./cpp
#cgo CPPFLAGS: -I/usr/local/include
#cgo LDFLAGS: -lstdc++
#cgo LDFLAGS: -L/usr/local/lib -lOpenImageIO

#include "stdlib.h"

#include "cpp/oiio.h"

extern bool image_progress_callback(void* goCallback, float done);

*/
import "C"

import (
	"unsafe"
)

// Various representation formats for image data
type TypeDesc int

const (
	TypeUnknown TypeDesc = C.TYPE_UNKNOWN
	TypeUint8   TypeDesc = C.TYPE_UINT8
	TypeInt8    TypeDesc = C.TYPE_INT8
	TypeUint16  TypeDesc = C.TYPE_UINT16
	TypeInt16   TypeDesc = C.TYPE_INT16
	TypeUint    TypeDesc = C.TYPE_UINT
	TypeInt     TypeDesc = C.TYPE_INT
	TypeUint64  TypeDesc = C.TYPE_UINT64
	TypeInt64   TypeDesc = C.TYPE_INT64
	TypeHalf    TypeDesc = C.TYPE_HALF
	TypeFloat   TypeDesc = C.TYPE_FLOAT
	TypeDouble  TypeDesc = C.TYPE_DOUBLE
)

// For image processing functions that accept a callback to monitor progress.
// A function that will be passed a float value indicating the progress
// percentage of the current operation. If the functon returns true, then
// the process should be aborted. Return false to allow processing to continue.
type ProgressCallback func(done float32) bool

//export image_progress_callback
func image_progress_callback(goCallback unsafe.Pointer, done C.float) C.bool {
	if goCallback == nil {
		return C.bool(false)
	}

	fn := *(*ProgressCallback)(goCallback)
	cancel := fn(float32(done))
	return C.bool(cancel)
}
<|endoftext|>