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

// +build example

package main

import (
	"log"
	"strconv"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
	"github.com/hajimehoshi/ebiten/examples/keyboard/keyboard"
)

const (
	screenWidth  = 320
	screenHeight = 240
)

var keyboardImage *ebiten.Image

func init() {
	var err error
	keyboardImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "keyboard", "keyboard.png"), ebiten.FilterNearest)
	if err != nil {
		log.Fatal(err)
	}
}

var keyNames = map[ebiten.Key]string{
	ebiten.KeyBackspace:    "BS",
	ebiten.KeyComma:        ",",
	ebiten.KeyEnter:        "Enter",
	ebiten.KeyEscape:       "Esc",
	ebiten.KeyPeriod:       ".",
	ebiten.KeySpace:        "Space",
	ebiten.KeyTab:          "Tab",
	ebiten.KeyMinus:        "-",
	ebiten.KeyEqual:        "=",
	ebiten.KeyBackslash:    "\\",
	ebiten.KeyGraveAccent:  "`",
	ebiten.KeyLeftBracket:  "[",
	ebiten.KeyRightBracket: "]",
	ebiten.KeySemicolon:    ";",
	ebiten.KeyApostrophe:   "'",
	ebiten.KeySlash:        "/",

	// Arrows
	ebiten.KeyDown:  "Down",
	ebiten.KeyLeft:  "Left",
	ebiten.KeyRight: "Right",
	ebiten.KeyUp:    "Up",

	// Mods
	ebiten.KeyShift:   "Shift",
	ebiten.KeyControl: "Ctrl",
	ebiten.KeyAlt:     "Alt",
}

func update(screen *ebiten.Image) error {
	if ebiten.IsRunningSlowly() {
		return nil
	}
	const offsetX, offsetY = 24, 40
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(offsetX, offsetY)
	op.ColorM.Scale(0.5, 0.5, 0.5, 1)
	screen.DrawImage(keyboardImage, op)

	pressed := []string{}
	for i := 0; i <= 9; i++ {
		if ebiten.IsKeyPressed(ebiten.Key(i) + ebiten.Key0) {
			pressed = append(pressed, string(i+'0'))
		}
	}
	for c := 'A'; c <= 'Z'; c++ {
		if ebiten.IsKeyPressed(ebiten.Key(c) - 'A' + ebiten.KeyA) {
			pressed = append(pressed, string(c))
		}
	}
	for i := 1; i <= 12; i++ {
		if ebiten.IsKeyPressed(ebiten.Key(i) + ebiten.KeyF1 - 1) {
			pressed = append(pressed, "F"+strconv.Itoa(i))
		}
	}
	for key, name := range keyNames {
		if ebiten.IsKeyPressed(key) {
			pressed = append(pressed, name)
		}
	}

	op = &ebiten.DrawImageOptions{}
	for _, p := range pressed {
		op.GeoM.Reset()
		r, ok := keyboard.KeyRect(p)
		if !ok {
			continue
		}
		op.GeoM.Translate(float64(r.Min.X), float64(r.Min.Y))
		op.GeoM.Translate(offsetX, offsetY)
		op.SourceRect = &r
		screen.DrawImage(keyboardImage, op)
	}

	return nil
}

func main() {
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Keyboard (Ebiten Demo)"); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>examples/keyboard: Add comments<commit_after>// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build example

package main

import (
	"log"
	"strconv"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
	"github.com/hajimehoshi/ebiten/examples/keyboard/keyboard"
)

const (
	screenWidth  = 320
	screenHeight = 240
)

var keyboardImage *ebiten.Image

func init() {
	var err error
	keyboardImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "keyboard", "keyboard.png"), ebiten.FilterNearest)
	if err != nil {
		log.Fatal(err)
	}
}

var keyNames = map[ebiten.Key]string{
	ebiten.KeyBackspace:    "BS",
	ebiten.KeyComma:        ",",
	ebiten.KeyEnter:        "Enter",
	ebiten.KeyEscape:       "Esc",
	ebiten.KeyPeriod:       ".",
	ebiten.KeySpace:        "Space",
	ebiten.KeyTab:          "Tab",
	ebiten.KeyMinus:        "-",
	ebiten.KeyEqual:        "=",
	ebiten.KeyBackslash:    "\\",
	ebiten.KeyGraveAccent:  "`",
	ebiten.KeyLeftBracket:  "[",
	ebiten.KeyRightBracket: "]",
	ebiten.KeySemicolon:    ";",
	ebiten.KeyApostrophe:   "'",
	ebiten.KeySlash:        "/",

	// Arrows
	ebiten.KeyDown:  "Down",
	ebiten.KeyLeft:  "Left",
	ebiten.KeyRight: "Right",
	ebiten.KeyUp:    "Up",

	// Mods
	ebiten.KeyShift:   "Shift",
	ebiten.KeyControl: "Ctrl",
	ebiten.KeyAlt:     "Alt",
}

func update(screen *ebiten.Image) error {
	// Collect pressed keys' names.
	pressed := []string{}
	for i := 0; i <= 9; i++ {
		if ebiten.IsKeyPressed(ebiten.Key(i) + ebiten.Key0) {
			pressed = append(pressed, string(i+'0'))
		}
	}
	for c := 'A'; c <= 'Z'; c++ {
		if ebiten.IsKeyPressed(ebiten.KeyA + ebiten.Key(c-'A')) {
			pressed = append(pressed, string(c))
		}
	}
	for i := 1; i <= 12; i++ {
		if ebiten.IsKeyPressed(ebiten.KeyF1 + ebiten.Key(i-1)) {
			pressed = append(pressed, "F"+strconv.Itoa(i))
		}
	}
	for key, name := range keyNames {
		if ebiten.IsKeyPressed(key) {
			pressed = append(pressed, name)
		}
	}

	if ebiten.IsRunningSlowly() {
		return nil
	}

	const (
		offsetX = 24
		offsetY = 40
	)

	// Draw the base (grayed) keyboard image.
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(offsetX, offsetY)
	op.ColorM.Scale(0.5, 0.5, 0.5, 1)
	screen.DrawImage(keyboardImage, op)

	// Draw the highlighted keys.
	op = &ebiten.DrawImageOptions{}
	for _, p := range pressed {
		op.GeoM.Reset()
		r, ok := keyboard.KeyRect(p)
		if !ok {
			continue
		}
		op.GeoM.Translate(float64(r.Min.X), float64(r.Min.Y))
		op.GeoM.Translate(offsetX, offsetY)
		op.SourceRect = &r
		screen.DrawImage(keyboardImage, op)
	}

	return nil
}

func main() {
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Keyboard (Ebiten Demo)"); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package web

import (
	"log"
	"net/http"
	"regexp"
	"strings"
	"sync"

	"code.google.com/p/go.net/context"
)

type method int

type methodSet int

const (
	mCONNECT method = 1 << iota
	mDELETE
	mGET
	mHEAD
	mOPTIONS
	mPATCH
	mPOST
	mPUT
	mTRACE
	// We only natively support the methods above, but we pass through other
	// methods. This constant pretty much only exists for the sake of mALL.
	mIDK

	mALL method = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS | mPATCH |
		mPOST | mPUT | mTRACE | mIDK
)

var validMethodsMap = map[string]method{
	"CONNECT": mCONNECT,
	"DELETE":  mDELETE,
	"GET":     mGET,
	"HEAD":    mHEAD,
	"OPTIONS": mOPTIONS,
	"PATCH":   mPATCH,
	"POST":    mPOST,
	"PUT":     mPUT,
	"TRACE":   mTRACE,
}

type route struct {
	// Theory: most real world routes have a string prefix which is both
	// cheap(-ish) to test against and pretty selective. And, conveniently,
	// both regexes and string patterns give us this out-of-box.
	prefix  string
	method  method
	pattern Pattern
	handler Handler
}

type router struct {
	lock     sync.Mutex
	routes   []route
	notFound Handler
	machine  *routeMachine
}

// A Pattern determines whether or not a given request matches some criteria.
// They are often used in routes, which are essentially (pattern, methodSet,
// handler) tuples. If the method and pattern match, the given handler is used.
//
// Built-in implementations of this interface are used to implement regular
// expression and string matching.
type Pattern interface {
	// In practice, most real-world routes have a string prefix that can be
	// used to quickly determine if a pattern is an eligible match. The
	// router uses the result of this function to optimize away calls to the
	// full Match function, which is likely much more expensive to compute.
	// If your Pattern does not support prefixes, this function should
	// return the empty string.
	Prefix() string
	// Returns true if the request satisfies the pattern. This function is
	// free to examine both the request and the context to make this
	// decision. Match should not modify either argument, and since it will
	// potentially be called several times over the course of matching a
	// request, it should be reasonably efficient.
	Match(r *http.Request, ctx context.Context) bool
	// Run the pattern on the request and context. The context is extended as
	// necessary to bind URL parameters or other parsed state.
	// The new context is returned by run
	Run(r *http.Request, ctx context.Context) context.Context
}

func parsePattern(p interface{}) Pattern {
	switch p.(type) {
	case Pattern:
		return p.(Pattern)
	case *regexp.Regexp:
		return parseRegexpPattern(p.(*regexp.Regexp))
	case string:
		return parseStringPattern(p.(string))
	default:
		log.Fatalf("Unknown pattern type %v. Expected a web.Pattern, "+
			"regexp.Regexp, or a string.", p)
	}
	panic("log.Fatalf does not return")
}

type netHTTPWrap struct {
	http.Handler
}

func (h netHTTPWrap) ServeHTTPC(c context.Context, w http.ResponseWriter, r *http.Request) {
	h.Handler.ServeHTTP(w, r)
}

func parseHandler(h interface{}) Handler {
	switch f := h.(type) {
	case Handler:
		return f
	case http.Handler:
		return netHTTPWrap{f}
	case func(c context.Context, w http.ResponseWriter, r *http.Request):
		return HandlerFunc(f)
	case func(w http.ResponseWriter, r *http.Request):
		return netHTTPWrap{http.HandlerFunc(f)}
	default:
		log.Panicf("Unknown handler type %T. Expected a web.Handler, "+
			"a http.Handler, or a function with signature func(context.Context, "+
			"http.ResponseWriter, *http.Request) or "+
			"func(http.ResponseWriter, *http.Request)", h)
	}
	panic("log.Fatalf does not return")
}

func httpMethod(mname string) method {
	if method, ok := validMethodsMap[mname]; ok {
		return method
	}
	return mIDK
}

type routeMachine struct {
	sm     stateMachine
	routes []route
}

func matchRoute(route route, m method, ms *methodSet, r *http.Request, c context.Context) (context.Context, bool) {
	if !route.pattern.Match(r, c) {
		return c, false
	}

	if m == mOPTIONS {
		*ms |= methodSet(route.method)
	}

	if route.method&m != 0 {
		c = route.pattern.Run(r, c)
		return c, true
	}
	return c, false
}

func (rm routeMachine) route(c context.Context, w http.ResponseWriter, r *http.Request) (methodSet, bool) {
	m := httpMethod(r.Method)
	var methods methodSet
	p := r.URL.Path

	if len(rm.sm) == 0 {
		return methods, false
	}

	var i int
	for {
		sm := rm.sm[i].mode
		if sm&smSetCursor != 0 {
			si := rm.sm[i].i
			p = r.URL.Path[si:]
			i++
			continue
		}

		length := int(sm & smLengthMask)
		match := false
		if length <= len(p) {
			bs := rm.sm[i].bs
			switch length {
			case 3:
				if p[2] != bs[2] {
					break
				}
				fallthrough
			case 2:
				if p[1] != bs[1] {
					break
				}
				fallthrough
			case 1:
				if p[0] != bs[0] {
					break
				}
				fallthrough
			case 0:
				p = p[length:]
				match = true
			}
		}

		if match && sm&smRoute != 0 {
			si := rm.sm[i].i
			if c, ok := matchRoute(rm.routes[si], m, &methods, r, c); ok {
				rm.routes[si].handler.ServeHTTPC(c, w, r)
				return 0, true
			}
			i++
		} else if (match && sm&smJumpOnMatch != 0) ||
			(!match && sm&smJumpOnMatch == 0) {

			if sm&smFail != 0 {
				return methods, false
			}
			i = int(rm.sm[i].i)
		} else {
			i++
		}
	}

	return methods, false
}

// Compile the list of routes into bytecode. This only needs to be done once
// after all the routes have been added, and will be called automatically for
// you (at some performance cost on the first request) if you do not call it
// explicitly.
func (rt *router) Compile() *routeMachine {
	rt.lock.Lock()
	defer rt.lock.Unlock()
	sm := routeMachine{
		sm:     compile(rt.routes),
		routes: rt.routes,
	}
	rt.setMachine(&sm)
	return &sm
}

func (rt *router) route(c context.Context, w http.ResponseWriter, r *http.Request) {
	rm := rt.getMachine()
	if rm == nil {
		rm = rt.Compile()
	}

	ms, ok := rm.route(c, w, r)
	if ok {
		return
	}

	if ms != 0 {
		c = context.WithValue(c, validMethodsKey, ms)
	}

	rt.notFound.ServeHTTPC(c, w, r)
}

func (rt *router) handleUntyped(p interface{}, m method, h interface{}) {
	pat := parsePattern(p)
	rt.handle(pat, m, parseHandler(h))
}

func (rt *router) handle(p Pattern, m method, h Handler) {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	// Calculate the sorted insertion point, because there's no reason to do
	// swapping hijinks if we're already making a copy. We need to use
	// bubble sort because we can only compare adjacent elements.
	pp := p.Prefix()
	var i int
	for i = len(rt.routes); i > 0; i-- {
		rip := rt.routes[i-1].prefix
		if rip <= pp || strings.HasPrefix(rip, pp) {
			break
		}
	}

	newRoutes := make([]route, len(rt.routes)+1)
	copy(newRoutes, rt.routes[:i])
	newRoutes[i] = route{
		prefix:  pp,
		method:  m,
		pattern: p,
		handler: h,
	}
	copy(newRoutes[i+1:], rt.routes[i:])

	rt.setMachine(nil)
	rt.routes = newRoutes
}

// This is a bit silly, but I've renamed the method receivers in the public
// functions here "m" instead of the standard "rt", since they will eventually
// be shown on the documentation for the Mux that they are included in.

/*
Dispatch to the given handler when the pattern matches, regardless of HTTP
method. See the documentation for type Mux for a description of what types are
accepted for pattern and handler.

This method is commonly used to implement sub-routing: an admin application, for
instance, can expose a single handler that is attached to the main Mux by
calling Handle("/admin*", adminHandler) or similar. Note that this function
doesn't strip this prefix from the path before forwarding it on (e.g., the
handler will see the full path, including the "/admin" part), but this
functionality can easily be performed by an extra middleware layer.
*/
func (rt *router) Handle(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mALL, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// CONNECT. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Connect(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mCONNECT, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// DELETE. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Delete(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mDELETE, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// GET. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
//
// All GET handlers also transparently serve HEAD requests, since net/http will
// take care of all the fiddly bits for you. If you wish to provide an alternate
// implementation of HEAD, you should add a handler explicitly and place it
// above your GET handler.
func (rt *router) Get(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mGET|mHEAD, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// HEAD. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Head(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mHEAD, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// OPTIONS. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Options(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mOPTIONS, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// PATCH. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Patch(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPATCH, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// POST. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Post(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPOST, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// PUT. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Put(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPUT, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// TRACE. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Trace(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mTRACE, handler)
}

// Set the fallback (i.e., 404) handler for this mux. See the documentation for
// type Mux for a description of what types are accepted for handler.
//
// As a convenience, the context environment variable "goji.web.validMethods"
// (also available as the constant ValidMethodsKey) will be set to the list of
// HTTP methods that could have been routed had they been provided on an
// otherwise identical request.
func (rt *router) NotFound(handler interface{}) {
	rt.notFound = parseHandler(handler)
}
<commit_msg>Slight simplification of netHTTPWrap<commit_after>package web

import (
	"log"
	"net/http"
	"regexp"
	"strings"
	"sync"

	"code.google.com/p/go.net/context"
)

type method int

type methodSet int

const (
	mCONNECT method = 1 << iota
	mDELETE
	mGET
	mHEAD
	mOPTIONS
	mPATCH
	mPOST
	mPUT
	mTRACE
	// We only natively support the methods above, but we pass through other
	// methods. This constant pretty much only exists for the sake of mALL.
	mIDK

	mALL method = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS | mPATCH |
		mPOST | mPUT | mTRACE | mIDK
)

var validMethodsMap = map[string]method{
	"CONNECT": mCONNECT,
	"DELETE":  mDELETE,
	"GET":     mGET,
	"HEAD":    mHEAD,
	"OPTIONS": mOPTIONS,
	"PATCH":   mPATCH,
	"POST":    mPOST,
	"PUT":     mPUT,
	"TRACE":   mTRACE,
}

type route struct {
	// Theory: most real world routes have a string prefix which is both
	// cheap(-ish) to test against and pretty selective. And, conveniently,
	// both regexes and string patterns give us this out-of-box.
	prefix  string
	method  method
	pattern Pattern
	handler Handler
}

type router struct {
	lock     sync.Mutex
	routes   []route
	notFound Handler
	machine  *routeMachine
}

// A Pattern determines whether or not a given request matches some criteria.
// They are often used in routes, which are essentially (pattern, methodSet,
// handler) tuples. If the method and pattern match, the given handler is used.
//
// Built-in implementations of this interface are used to implement regular
// expression and string matching.
type Pattern interface {
	// In practice, most real-world routes have a string prefix that can be
	// used to quickly determine if a pattern is an eligible match. The
	// router uses the result of this function to optimize away calls to the
	// full Match function, which is likely much more expensive to compute.
	// If your Pattern does not support prefixes, this function should
	// return the empty string.
	Prefix() string
	// Returns true if the request satisfies the pattern. This function is
	// free to examine both the request and the context to make this
	// decision. Match should not modify either argument, and since it will
	// potentially be called several times over the course of matching a
	// request, it should be reasonably efficient.
	Match(r *http.Request, ctx context.Context) bool
	// Run the pattern on the request and context. The context is extended as
	// necessary to bind URL parameters or other parsed state.
	// The new context is returned by run
	Run(r *http.Request, ctx context.Context) context.Context
}

func parsePattern(p interface{}) Pattern {
	switch p.(type) {
	case Pattern:
		return p.(Pattern)
	case *regexp.Regexp:
		return parseRegexpPattern(p.(*regexp.Regexp))
	case string:
		return parseStringPattern(p.(string))
	default:
		log.Fatalf("Unknown pattern type %v. Expected a web.Pattern, "+
			"regexp.Regexp, or a string.", p)
	}
	panic("log.Fatalf does not return")
}

type netHTTPWrap func(w http.ResponseWriter, r *http.Request)

func (h netHTTPWrap) ServeHTTPC(c context.Context, w http.ResponseWriter, r *http.Request) {
	h(w, r)
}

func parseHandler(h interface{}) Handler {
	switch f := h.(type) {
	case Handler:
		return f
	case http.Handler:
		return netHTTPWrap(f.ServeHTTP)
	case func(c context.Context, w http.ResponseWriter, r *http.Request):
		return HandlerFunc(f)
	case func(w http.ResponseWriter, r *http.Request):
		return netHTTPWrap(f)
	default:
		log.Panicf("Unknown handler type %T. Expected a web.Handler, "+
			"a http.Handler, or a function with signature func(context.Context, "+
			"http.ResponseWriter, *http.Request) or "+
			"func(http.ResponseWriter, *http.Request)", h)
	}
	panic("log.Fatalf does not return")
}

func httpMethod(mname string) method {
	if method, ok := validMethodsMap[mname]; ok {
		return method
	}
	return mIDK
}

type routeMachine struct {
	sm     stateMachine
	routes []route
}

func matchRoute(route route, m method, ms *methodSet, r *http.Request, c context.Context) (context.Context, bool) {
	if !route.pattern.Match(r, c) {
		return c, false
	}

	if m == mOPTIONS {
		*ms |= methodSet(route.method)
	}

	if route.method&m != 0 {
		c = route.pattern.Run(r, c)
		return c, true
	}
	return c, false
}

func (rm routeMachine) route(c context.Context, w http.ResponseWriter, r *http.Request) (methodSet, bool) {
	m := httpMethod(r.Method)
	var methods methodSet
	p := r.URL.Path

	if len(rm.sm) == 0 {
		return methods, false
	}

	var i int
	for {
		sm := rm.sm[i].mode
		if sm&smSetCursor != 0 {
			si := rm.sm[i].i
			p = r.URL.Path[si:]
			i++
			continue
		}

		length := int(sm & smLengthMask)
		match := false
		if length <= len(p) {
			bs := rm.sm[i].bs
			switch length {
			case 3:
				if p[2] != bs[2] {
					break
				}
				fallthrough
			case 2:
				if p[1] != bs[1] {
					break
				}
				fallthrough
			case 1:
				if p[0] != bs[0] {
					break
				}
				fallthrough
			case 0:
				p = p[length:]
				match = true
			}
		}

		if match && sm&smRoute != 0 {
			si := rm.sm[i].i
			if c, ok := matchRoute(rm.routes[si], m, &methods, r, c); ok {
				rm.routes[si].handler.ServeHTTPC(c, w, r)
				return 0, true
			}
			i++
		} else if (match && sm&smJumpOnMatch != 0) ||
			(!match && sm&smJumpOnMatch == 0) {

			if sm&smFail != 0 {
				return methods, false
			}
			i = int(rm.sm[i].i)
		} else {
			i++
		}
	}

	return methods, false
}

// Compile the list of routes into bytecode. This only needs to be done once
// after all the routes have been added, and will be called automatically for
// you (at some performance cost on the first request) if you do not call it
// explicitly.
func (rt *router) Compile() *routeMachine {
	rt.lock.Lock()
	defer rt.lock.Unlock()
	sm := routeMachine{
		sm:     compile(rt.routes),
		routes: rt.routes,
	}
	rt.setMachine(&sm)
	return &sm
}

func (rt *router) route(c context.Context, w http.ResponseWriter, r *http.Request) {
	rm := rt.getMachine()
	if rm == nil {
		rm = rt.Compile()
	}

	ms, ok := rm.route(c, w, r)
	if ok {
		return
	}

	if ms != 0 {
		c = context.WithValue(c, validMethodsKey, ms)
	}

	rt.notFound.ServeHTTPC(c, w, r)
}

func (rt *router) handleUntyped(p interface{}, m method, h interface{}) {
	pat := parsePattern(p)
	rt.handle(pat, m, parseHandler(h))
}

func (rt *router) handle(p Pattern, m method, h Handler) {
	rt.lock.Lock()
	defer rt.lock.Unlock()

	// Calculate the sorted insertion point, because there's no reason to do
	// swapping hijinks if we're already making a copy. We need to use
	// bubble sort because we can only compare adjacent elements.
	pp := p.Prefix()
	var i int
	for i = len(rt.routes); i > 0; i-- {
		rip := rt.routes[i-1].prefix
		if rip <= pp || strings.HasPrefix(rip, pp) {
			break
		}
	}

	newRoutes := make([]route, len(rt.routes)+1)
	copy(newRoutes, rt.routes[:i])
	newRoutes[i] = route{
		prefix:  pp,
		method:  m,
		pattern: p,
		handler: h,
	}
	copy(newRoutes[i+1:], rt.routes[i:])

	rt.setMachine(nil)
	rt.routes = newRoutes
}

// This is a bit silly, but I've renamed the method receivers in the public
// functions here "m" instead of the standard "rt", since they will eventually
// be shown on the documentation for the Mux that they are included in.

/*
Dispatch to the given handler when the pattern matches, regardless of HTTP
method. See the documentation for type Mux for a description of what types are
accepted for pattern and handler.

This method is commonly used to implement sub-routing: an admin application, for
instance, can expose a single handler that is attached to the main Mux by
calling Handle("/admin*", adminHandler) or similar. Note that this function
doesn't strip this prefix from the path before forwarding it on (e.g., the
handler will see the full path, including the "/admin" part), but this
functionality can easily be performed by an extra middleware layer.
*/
func (rt *router) Handle(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mALL, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// CONNECT. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Connect(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mCONNECT, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// DELETE. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Delete(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mDELETE, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// GET. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
//
// All GET handlers also transparently serve HEAD requests, since net/http will
// take care of all the fiddly bits for you. If you wish to provide an alternate
// implementation of HEAD, you should add a handler explicitly and place it
// above your GET handler.
func (rt *router) Get(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mGET|mHEAD, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// HEAD. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Head(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mHEAD, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// OPTIONS. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (rt *router) Options(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mOPTIONS, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// PATCH. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Patch(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPATCH, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// POST. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Post(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPOST, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// PUT. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Put(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mPUT, handler)
}

// Dispatch to the given handler when the pattern matches and the HTTP method is
// TRACE. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (rt *router) Trace(pattern interface{}, handler interface{}) {
	rt.handleUntyped(pattern, mTRACE, handler)
}

// Set the fallback (i.e., 404) handler for this mux. See the documentation for
// type Mux for a description of what types are accepted for handler.
//
// As a convenience, the context environment variable "goji.web.validMethods"
// (also available as the constant ValidMethodsKey) will be set to the list of
// HTTP methods that could have been routed had they been provided on an
// otherwise identical request.
func (rt *router) NotFound(handler interface{}) {
	rt.notFound = parseHandler(handler)
}
<|endoftext|>


<commit_before>package web

import (
	"net/http"

	"github.com/gocraft/web"
)

func mapUserUrls(router *web.Router) {
	router.
		Get("/orek/v0/users", (*Context).GetAllUsers).
		Get("/orek/v0/users/:userName", (*Context).GetUser).
		Post("/orek/v0/users", (*Context).CreateUser).
		Delete("/orek/v0/users/:userName", (*Context).DeleteUser)
}

func mapSourceUrls(router *web.Router) {
	router.
		Get("/orek/v0/sources", (*Context).GetAllSources).
		Get("/orek/v0/sources/:sourceOwner/:sourceName", (*Context).GetSource).
		Get("/orek/v0/sources/:sourceId", (*Context).GetSourceWithId).
		Post("/orek/v0/sources", (*Context).CreateOrUpdateSource).
		Delete("/orek/v0/sources/:sourceId", (*Context).DeleteSource)
}

func mapVariableUrls(router *web.Router) {
	router.
		Get("/orek/v0/variables", (*Context).GetAllVariables).
		Get("/orek/v0/variables/:sourceId/:variableName", (*Context).GetVariable).
		Get("/orek/v0/variables/:variableId", (*Context).GetVariableWithId).
		Post("/orek/v0/variables", (*Context).CreateOrUpdateVariable).
		Delete("/orek/v0/variables/:variableId", (*Context).DeleteVariable)
}

func def(router *web.Router) {
	router.Middleware(web.LoggerMiddleware).
		Get("/*", (*Context).Default)
}

func Serve() {
	router := web.New(Context{})
	def(router)
	mapUserUrls(router)
	mapSourceUrls(router)
	mapVariableUrls(router)
	http.ListenAndServe("localhost:3000", router)
}
<commit_msg>Small addition in mapping<commit_after>package web

import (
	"net/http"

	"github.com/gocraft/web"
)

func mapUserUrls(router *web.Router) {
	router.
		Get("/orek/v0/users", (*Context).GetAllUsers).
		Get("/orek/v0/users/:userName", (*Context).GetUser).
		Post("/orek/v0/users", (*Context).CreateUser).
		Delete("/orek/v0/users/:userName", (*Context).DeleteUser)
}

func mapSourceUrls(router *web.Router) {
	router.
		Get("/orek/v0/sources", (*Context).GetAllSources).
		Get("/orek/v0/sources/:sourceOwner/:sourceName", (*Context).GetSource).
		Get("/orek/v0/sources/:sourceId", (*Context).GetSourceWithId).
		Post("/orek/v0/sources", (*Context).CreateOrUpdateSource).
		Delete("/orek/v0/sources/:sourceId", (*Context).DeleteSource)
}

func mapVariableUrls(router *web.Router) {
	router.
		Get("/orek/v0/variables", (*Context).GetAllVariables).
		Get("/orek/v0/variables/:sourceId/:variableName", (*Context).GetVariable).
		Get("/orek/v0/variables/:variableId", (*Context).GetVariableWithId).
		Post("/orek/v0/variables", (*Context).CreateOrUpdateVariable).
		Delete("/orek/v0/variables/:variableId", (*Context).DeleteVariable)
		//Also add variable value related functions
}

func mapUserGroupUrls(router *web.Router) {

}

func mapVariableGroupUrls(router *web.Router) {

}

func def(router *web.Router) {
	router.Middleware(web.LoggerMiddleware).
		Get("/*", (*Context).Default)
}

func Serve() {
	router := web.New(Context{})
	def(router)
	mapUserUrls(router)
	mapSourceUrls(router)
	mapVariableUrls(router)
	http.ListenAndServe("localhost:3000", router)
}
<|endoftext|>


<commit_before>package agent

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"syscall"
	"time"

	"code.google.com/p/go-shlex"

	"github.com/tutumcloud/tutum-agent/utils"
)

func DownloadDocker(url, dockerBinPath string) {
	if utils.FileExist(dockerBinPath) {
		Logger.Printf("Found docker locally(%s), skip downloading\n", dockerBinPath)
	} else {
		Logger.Println("No docker binary is found locally. Starting to download docker...")
		downloadFile(url, dockerBinPath, "docker")
	}
	createDockerSymlink(dockerBinPath, DockerSymbolicLink)
}

func StartDocker(dockerBinPath, keyFilePath, certFilePath, caFilePath string) {
	var command *exec.Cmd

	cmdstring := fmt.Sprintf("-d -H %s -H %s --tlscert %s --tlskey %s --tlscacert %s --tlsverify",
		Conf.DockerHost, DockerDefaultHost, certFilePath, keyFilePath, caFilePath)

	if *FlagStandalone && !utils.FileExist(caFilePath) {
		cmdstring = fmt.Sprintf("-d -H %s -H %s --tlscert %s --tlskey %s --tls",
			Conf.DockerHost, DockerDefaultHost, certFilePath, keyFilePath)
		fmt.Fprintln(os.Stderr, "WARNING: standalone mode activated but no CA certificate found - client authentication disabled")
	}

	if *FlagDockerOpts != "" {
		cmdstring = cmdstring + " " + *FlagDockerOpts
	}

	cmdslice, err := shlex.Split(cmdstring)
	if err != nil {
		cmdslice = strings.Split(cmdstring, " ")
	}

	command = exec.Command(dockerBinPath, cmdslice...)

	go runDocker(command)
}

func StopDocker() {
	if DockerProcess != nil {
		DockerProcess.Signal(syscall.SIGTERM)
		for {
			if DockerProcess != nil {
				time.Sleep(500 * time.Millisecond)
			} else {
				break
			}
		}
	}
}

func UpdateDocker(dockerBinPath, dockerNewBinPath, dockerNewBinSigPath, keyFilePath, certFilePath, caFilePath string) {
	if utils.FileExist(dockerNewBinPath) {
		Logger.Printf("New Docker binary(%s) found\n", dockerNewBinPath)
		Logger.Println("Updating docker...")
		if verifyDockerSig(dockerNewBinPath, dockerNewBinSigPath) {
			Logger.Println("Stopping docker daemon")
			ScheduleToTerminateDocker = true
			StopDocker()
			Logger.Println("Removing old docker binary")
			if err := os.RemoveAll(dockerBinPath); err != nil {
				Logger.Println("Cannot remove old docker binary:", err)
			}
			Logger.Println("Renaming new docker binary")
			if err := os.Rename(dockerNewBinPath, dockerBinPath); err != nil {
				Logger.Println("Cannot rename docker binary:", err)
			}
			Logger.Println("Removing the signature file ", dockerNewBinSigPath)
			if err := os.RemoveAll(dockerNewBinSigPath); err != nil {
				Logger.Println(err.Error())
			}
			createDockerSymlink(dockerBinPath, DockerSymbolicLink)
			ScheduleToTerminateDocker = false
			StartDocker(dockerBinPath, keyFilePath, certFilePath, caFilePath)
			Logger.Println("Succeeded to update docker binary")
		} else {
			Logger.Println("New docker binary signature cannot be verified. Update is rejected!")
			Logger.Println("Removing the invalid docker binary ", dockerNewBinPath)
			if err := os.RemoveAll(dockerNewBinPath); err != nil {
				Logger.Println(err.Error())
			}
			Logger.Println("Removing the invalid signature file ", dockerNewBinSigPath)
			if err := os.RemoveAll(dockerNewBinSigPath); err != nil {
				Logger.Println(err.Error())
			}
			Logger.Println("Failed to update docker binary")
		}
	}
}

func verifyDockerSig(dockerNewBinPath, dockerNewBinSigPath string) bool {
	cmd := exec.Command("gpg", "--verify", dockerNewBinSigPath, dockerNewBinPath)
	err := cmd.Run()
	if err != nil {
		Logger.Println("gpg verfication failed:", err.Error())
		return false
	}
	Logger.Println("gpg verfication passed")
	return true
}

func createDockerSymlink(dockerBinPath, dockerSymbolicLink string) {
	Logger.Println("Removing the docker symbolic from ", dockerSymbolicLink)
	if err := os.RemoveAll(DockerSymbolicLink); err != nil {
		Logger.Println(err.Error())
	}
	Logger.Println("Creating the docker symbolic to ", dockerSymbolicLink)
	if err := os.Symlink(dockerBinPath, DockerSymbolicLink); err != nil {
		Logger.Println(err.Error())
	}
}

func runDocker(cmd *exec.Cmd) {
	//open file to log docker logs
	dockerLog := path.Join(LogDir, DockerLogFileName)
	Logger.Println("Set docker log to", dockerLog)
	f, err := os.OpenFile(dockerLog, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		Logger.Println(err)
		Logger.Println("Cannot set docker log to", dockerLog)
	} else {
		defer f.Close()
		cmd.Stdout = f
		cmd.Stderr = f
	}

	Logger.Println("Starting docker daemon:", cmd.Args)

	if err := cmd.Start(); err != nil {
		Logger.Println("Cannot start docker daemon:", err)
	}
	DockerProcess = cmd.Process
	Logger.Printf("Docker daemon (PID:%d) has been started\n", DockerProcess.Pid)

	Logger.Printf("Renicing docker daemon to priority %d\n", RenicePriority)
	syscall.Setpriority(syscall.PRIO_PROCESS, DockerProcess.Pid, RenicePriority)

	exit_renice := make(chan int)

	go decreaseDockerChildProcessPriority(exit_renice)

	if err := cmd.Wait(); err != nil {
		Logger.Println("Docker daemon died with error:", err)
	}
	exit_renice <- 1
	Logger.Println("Docker daemon died")
	DockerProcess = nil
}

func decreaseDockerChildProcessPriority(exit_renice chan int) {
	Logger.Println("Starting lower the priority of docker child processes")
	for {
		select {
		case <-exit_renice:
			Logger.Println("Exiting lower the priority of docker child processes")
			return
		default:
			out, err := exec.Command("ps", "axo", "pid,ppid,ni").Output()
			if err != nil {
				Logger.Println(err)
				continue
			}
			lines := strings.Split(string(out), "\n")
			ppids := []int{DockerProcess.Pid}
			for _, line := range lines {
				items := strings.Fields(line)
				if len(items) != 3 {
					continue
				}
				pid, err := strconv.Atoi(items[0])
				if err != nil {
					continue
				}
				ppid, err := strconv.Atoi(items[1])
				if err != nil {
					continue
				}
				ni, err := strconv.Atoi(items[2])
				if err != nil {
					continue
				}
				if ni != RenicePriority {
					continue
				}
				if pid == DockerProcess.Pid {
					continue
				}
				for _, _ppid := range ppids {
					if ppid == _ppid {
						syscall.Setpriority(syscall.PRIO_PROCESS, pid, 0)
						ppids = append(ppids, pid)
						break
					}
				}
			}
			time.Sleep(5 * time.Second)
		}
	}
}
<commit_msg>run docker daemon in debug mode if tutum agent runs in debug mode<commit_after>package agent

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"syscall"
	"time"

	"code.google.com/p/go-shlex"

	"github.com/tutumcloud/tutum-agent/utils"
)

func DownloadDocker(url, dockerBinPath string) {
	if utils.FileExist(dockerBinPath) {
		Logger.Printf("Found docker locally(%s), skip downloading\n", dockerBinPath)
	} else {
		Logger.Println("No docker binary is found locally. Starting to download docker...")
		downloadFile(url, dockerBinPath, "docker")
	}
	createDockerSymlink(dockerBinPath, DockerSymbolicLink)
}

func StartDocker(dockerBinPath, keyFilePath, certFilePath, caFilePath string) {
	var command *exec.Cmd
	var cmdstring string

	if *FlagDebugMode {
		cmdstring = fmt.Sprintf("-d -D -H %s -H %s --tlscert %s --tlskey %s --tlscacert %s --tlsverify",
			Conf.DockerHost, DockerDefaultHost, certFilePath, keyFilePath, caFilePath)
	} else {
		cmdstring = fmt.Sprintf("-d -H %s -H %s --tlscert %s --tlskey %s --tlscacert %s --tlsverify",
			Conf.DockerHost, DockerDefaultHost, certFilePath, keyFilePath, caFilePath)
	}
	if *FlagStandalone && !utils.FileExist(caFilePath) {
		if *FlagDebugMode {
			cmdstring = fmt.Sprintf("-d -D -H %s -H %s --tlscert %s --tlskey %s --tls",
				Conf.DockerHost, DockerDefaultHost, certFilePath, keyFilePath)
		} else {
			cmdstring = fmt.Sprintf("-d -H %s -H %s --tlscert %s --tlskey %s --tls",
				Conf.DockerHost, DockerDefaultHost, certFilePath, keyFilePath)
		}

		fmt.Fprintln(os.Stderr, "WARNING: standalone mode activated but no CA certificate found - client authentication disabled")
	}

	if *FlagDockerOpts != "" {
		cmdstring = cmdstring + " " + *FlagDockerOpts
	}

	cmdslice, err := shlex.Split(cmdstring)
	if err != nil {
		cmdslice = strings.Split(cmdstring, " ")
	}

	command = exec.Command(dockerBinPath, cmdslice...)

	go runDocker(command)
}

func StopDocker() {
	if DockerProcess != nil {
		DockerProcess.Signal(syscall.SIGTERM)
		for {
			if DockerProcess != nil {
				time.Sleep(500 * time.Millisecond)
			} else {
				break
			}
		}
	}
}

func UpdateDocker(dockerBinPath, dockerNewBinPath, dockerNewBinSigPath, keyFilePath, certFilePath, caFilePath string) {
	if utils.FileExist(dockerNewBinPath) {
		Logger.Printf("New Docker binary(%s) found\n", dockerNewBinPath)
		Logger.Println("Updating docker...")
		if verifyDockerSig(dockerNewBinPath, dockerNewBinSigPath) {
			Logger.Println("Stopping docker daemon")
			ScheduleToTerminateDocker = true
			StopDocker()
			Logger.Println("Removing old docker binary")
			if err := os.RemoveAll(dockerBinPath); err != nil {
				Logger.Println("Cannot remove old docker binary:", err)
			}
			Logger.Println("Renaming new docker binary")
			if err := os.Rename(dockerNewBinPath, dockerBinPath); err != nil {
				Logger.Println("Cannot rename docker binary:", err)
			}
			Logger.Println("Removing the signature file ", dockerNewBinSigPath)
			if err := os.RemoveAll(dockerNewBinSigPath); err != nil {
				Logger.Println(err.Error())
			}
			createDockerSymlink(dockerBinPath, DockerSymbolicLink)
			ScheduleToTerminateDocker = false
			StartDocker(dockerBinPath, keyFilePath, certFilePath, caFilePath)
			Logger.Println("Succeeded to update docker binary")
		} else {
			Logger.Println("New docker binary signature cannot be verified. Update is rejected!")
			Logger.Println("Removing the invalid docker binary ", dockerNewBinPath)
			if err := os.RemoveAll(dockerNewBinPath); err != nil {
				Logger.Println(err.Error())
			}
			Logger.Println("Removing the invalid signature file ", dockerNewBinSigPath)
			if err := os.RemoveAll(dockerNewBinSigPath); err != nil {
				Logger.Println(err.Error())
			}
			Logger.Println("Failed to update docker binary")
		}
	}
}

func verifyDockerSig(dockerNewBinPath, dockerNewBinSigPath string) bool {
	cmd := exec.Command("gpg", "--verify", dockerNewBinSigPath, dockerNewBinPath)
	err := cmd.Run()
	if err != nil {
		Logger.Println("gpg verfication failed:", err.Error())
		return false
	}
	Logger.Println("gpg verfication passed")
	return true
}

func createDockerSymlink(dockerBinPath, dockerSymbolicLink string) {
	Logger.Println("Removing the docker symbolic from ", dockerSymbolicLink)
	if err := os.RemoveAll(DockerSymbolicLink); err != nil {
		Logger.Println(err.Error())
	}
	Logger.Println("Creating the docker symbolic to ", dockerSymbolicLink)
	if err := os.Symlink(dockerBinPath, DockerSymbolicLink); err != nil {
		Logger.Println(err.Error())
	}
}

func runDocker(cmd *exec.Cmd) {
	//open file to log docker logs
	dockerLog := path.Join(LogDir, DockerLogFileName)
	Logger.Println("Set docker log to", dockerLog)
	f, err := os.OpenFile(dockerLog, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		Logger.Println(err)
		Logger.Println("Cannot set docker log to", dockerLog)
	} else {
		defer f.Close()
		cmd.Stdout = f
		cmd.Stderr = f
	}

	Logger.Println("Starting docker daemon:", cmd.Args)

	if err := cmd.Start(); err != nil {
		Logger.Println("Cannot start docker daemon:", err)
	}
	DockerProcess = cmd.Process
	Logger.Printf("Docker daemon (PID:%d) has been started\n", DockerProcess.Pid)

	Logger.Printf("Renicing docker daemon to priority %d\n", RenicePriority)
	syscall.Setpriority(syscall.PRIO_PROCESS, DockerProcess.Pid, RenicePriority)

	exit_renice := make(chan int)

	go decreaseDockerChildProcessPriority(exit_renice)

	if err := cmd.Wait(); err != nil {
		Logger.Println("Docker daemon died with error:", err)
	}
	exit_renice <- 1
	Logger.Println("Docker daemon died")
	DockerProcess = nil
}

func decreaseDockerChildProcessPriority(exit_renice chan int) {
	Logger.Println("Starting lower the priority of docker child processes")
	for {
		select {
		case <-exit_renice:
			Logger.Println("Exiting lower the priority of docker child processes")
			return
		default:
			out, err := exec.Command("ps", "axo", "pid,ppid,ni").Output()
			if err != nil {
				Logger.Println(err)
				continue
			}
			lines := strings.Split(string(out), "\n")
			ppids := []int{DockerProcess.Pid}
			for _, line := range lines {
				items := strings.Fields(line)
				if len(items) != 3 {
					continue
				}
				pid, err := strconv.Atoi(items[0])
				if err != nil {
					continue
				}
				ppid, err := strconv.Atoi(items[1])
				if err != nil {
					continue
				}
				ni, err := strconv.Atoi(items[2])
				if err != nil {
					continue
				}
				if ni != RenicePriority {
					continue
				}
				if pid == DockerProcess.Pid {
					continue
				}
				for _, _ppid := range ppids {
					if ppid == _ppid {
						syscall.Setpriority(syscall.PRIO_PROCESS, pid, 0)
						ppids = append(ppids, pid)
						break
					}
				}
			}
			time.Sleep(5 * time.Second)
		}
	}
}
<|endoftext|>


<commit_before>package kafka

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"testing"
	"time"
)

const (
	testCorrelationID = 1
	testClientID      = "localhost"
	testTopic         = "topic"
	testPartition     = 42
)

type WriteVarIntTestCase struct {
	v  []byte
	tc int64
}

func TestWriteVarInt(t *testing.T) {
	testCases := []*WriteVarIntTestCase{
		&WriteVarIntTestCase{v: []byte{0}, tc: 0},
		&WriteVarIntTestCase{v: []byte{2}, tc: 1},
		&WriteVarIntTestCase{v: []byte{1}, tc: -1},
		&WriteVarIntTestCase{v: []byte{3}, tc: -2},
		&WriteVarIntTestCase{v: []byte{128, 2}, tc: 128},
		&WriteVarIntTestCase{v: []byte{254, 1}, tc: 127},
		&WriteVarIntTestCase{v: []byte{142, 6}, tc: 391},
		&WriteVarIntTestCase{v: []byte{142, 134, 6}, tc: 49543},
	}

	for _, tc := range testCases {
		buf := &bytes.Buffer{}
		bufWriter := bufio.NewWriter(buf)
		writeVarInt(bufWriter, tc.tc)
		bufWriter.Flush()
		if !bytes.Equal(buf.Bytes(), tc.v) {
			t.Errorf("Expected %v; got %v", tc.v, buf.Bytes())
		}
	}
}

func TestWriteOptimizations(t *testing.T) {
	t.Parallel()
	t.Run("writeFetchRequestV2", testWriteFetchRequestV2)
	t.Run("writeListOffsetRequestV1", testWriteListOffsetRequestV1)
	t.Run("writeProduceRequestV2", testWriteProduceRequestV2)
}

func testWriteFetchRequestV2(t *testing.T) {
	const offset = 42
	const minBytes = 10
	const maxBytes = 1000
	const maxWait = 100 * time.Millisecond
	testWriteOptimization(t,
		requestHeader{
			ApiKey:        int16(fetchRequest),
			ApiVersion:    int16(v2),
			CorrelationID: testCorrelationID,
			ClientID:      testClientID,
		},
		fetchRequestV2{
			ReplicaID:   -1,
			MaxWaitTime: milliseconds(maxWait),
			MinBytes:    minBytes,
			Topics: []fetchRequestTopicV2{{
				TopicName: testTopic,
				Partitions: []fetchRequestPartitionV2{{
					Partition:   testPartition,
					FetchOffset: offset,
					MaxBytes:    maxBytes,
				}},
			}},
		},
		func(w *bufio.Writer) {
			writeFetchRequestV2(w, testCorrelationID, testClientID, testTopic, testPartition, offset, minBytes, maxBytes, maxWait)
		},
	)
}

func testWriteListOffsetRequestV1(t *testing.T) {
	const time = -1
	testWriteOptimization(t,
		requestHeader{
			ApiKey:        int16(listOffsetRequest),
			ApiVersion:    int16(v1),
			CorrelationID: testCorrelationID,
			ClientID:      testClientID,
		},
		listOffsetRequestV1{
			ReplicaID: -1,
			Topics: []listOffsetRequestTopicV1{{
				TopicName: testTopic,
				Partitions: []listOffsetRequestPartitionV1{{
					Partition: testPartition,
					Time:      time,
				}},
			}},
		},
		func(w *bufio.Writer) {
			writeListOffsetRequestV1(w, testCorrelationID, testClientID, testTopic, testPartition, time)
		},
	)
}

func testWriteProduceRequestV2(t *testing.T) {
	key := []byte(nil)
	val := []byte("Hello World!")

	msg := messageSetItem{
		Offset: 10,
		Message: message{
			MagicByte:  1,
			Attributes: 0,
			Key:        key,
			Value:      val,
		},
	}
	msg.MessageSize = msg.Message.size()
	msg.Message.CRC = msg.Message.crc32()

	const timeout = 100
	testWriteOptimization(t,
		requestHeader{
			ApiKey:        int16(produceRequest),
			ApiVersion:    int16(v2),
			CorrelationID: testCorrelationID,
			ClientID:      testClientID,
		},
		produceRequestV2{
			RequiredAcks: -1,
			Timeout:      timeout,
			Topics: []produceRequestTopicV2{{
				TopicName: testTopic,
				Partitions: []produceRequestPartitionV2{{
					Partition:      testPartition,
					MessageSetSize: msg.size(), MessageSet: messageSet{msg},
				}},
			}},
		},
		func(w *bufio.Writer) {
			writeProduceRequestV2(w, nil, testCorrelationID, testClientID, testTopic, testPartition, timeout*time.Millisecond, -1, Message{
				Offset: 10,
				Key:    key,
				Value:  val,
			})
		},
	)
}

func testWriteOptimization(t *testing.T, h requestHeader, r request, f func(*bufio.Writer)) {
	b1 := &bytes.Buffer{}
	w1 := bufio.NewWriter(b1)

	b2 := &bytes.Buffer{}
	w2 := bufio.NewWriter(b2)

	h.Size = (h.size() + r.size()) - 4
	h.writeTo(w1)
	r.writeTo(w1)
	w1.Flush()

	f(w2)
	w2.Flush()

	c1 := b1.Bytes()
	c2 := b2.Bytes()

	if !bytes.Equal(c1, c2) {
		t.Error("content differs")

		n1 := len(c1)
		n2 := len(c2)

		if n1 != n2 {
			t.Log("content length 1 =", n1)
			t.Log("content length 2 =", n2)
		} else {
			for i := 0; i != n1; i++ {
				if c1[i] != c2[i] {
					t.Logf("byte at offset %d/%d: %#x != %#x", i, n1, c1[i], c2[i])
					break
				}
			}
		}
	}
}

func TestWriteV2RecordBatch(t *testing.T) {

	if !KafkaIsAtLeast("0.11.0") {
		t.Skip("RecordBatch was added in kafka 0.11.0")
		return
	}

	topic := CreateTopic(t, 1)
	msgs := make([]Message, 15)
	for i := range msgs {
		value := fmt.Sprintf("Sample message content: %d!", i)
		msgs[i] = Message{Key: []byte("Key"), Value: []byte(value), Headers: []Header{Header{Key: "hk", Value: []byte("hv")}}}
	}
	w := NewWriter(WriterConfig{
		Brokers:      []string{"localhost:9092"},
		Topic:        topic,
		BatchTimeout: 100 * time.Millisecond,
		BatchSize:    5,
	})

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	if err := w.WriteMessages(ctx, msgs...); err != nil {
		t.Errorf("Failed to write v2 messages to kafka: %v", err)
		return
	}
	w.Close()

	r := NewReader(ReaderConfig{
		Brokers: []string{"localhost:9092"},
		Topic:   topic,
		MaxWait: 100 * time.Millisecond,
	})
	defer r.Close()

	msg, err := r.ReadMessage(context.Background())
	if err != nil {
		t.Error("Failed to read message")
		return
	}

	if string(msg.Key) != "Key" {
		t.Error("Received message's key doesn't match")
		return
	}
	if msg.Headers[0].Key != "hk" {
		t.Error("Received message header's key doesn't match")
		return
	}
}
<commit_msg>Fixed outdated call to KafkaIsAtLeast (#239)<commit_after>package kafka

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"testing"
	"time"

	ktesting "github.com/segmentio/kafka-go/testing"
)

const (
	testCorrelationID = 1
	testClientID      = "localhost"
	testTopic         = "topic"
	testPartition     = 42
)

type WriteVarIntTestCase struct {
	v  []byte
	tc int64
}

func TestWriteVarInt(t *testing.T) {
	testCases := []*WriteVarIntTestCase{
		&WriteVarIntTestCase{v: []byte{0}, tc: 0},
		&WriteVarIntTestCase{v: []byte{2}, tc: 1},
		&WriteVarIntTestCase{v: []byte{1}, tc: -1},
		&WriteVarIntTestCase{v: []byte{3}, tc: -2},
		&WriteVarIntTestCase{v: []byte{128, 2}, tc: 128},
		&WriteVarIntTestCase{v: []byte{254, 1}, tc: 127},
		&WriteVarIntTestCase{v: []byte{142, 6}, tc: 391},
		&WriteVarIntTestCase{v: []byte{142, 134, 6}, tc: 49543},
	}

	for _, tc := range testCases {
		buf := &bytes.Buffer{}
		bufWriter := bufio.NewWriter(buf)
		writeVarInt(bufWriter, tc.tc)
		bufWriter.Flush()
		if !bytes.Equal(buf.Bytes(), tc.v) {
			t.Errorf("Expected %v; got %v", tc.v, buf.Bytes())
		}
	}
}

func TestWriteOptimizations(t *testing.T) {
	t.Parallel()
	t.Run("writeFetchRequestV2", testWriteFetchRequestV2)
	t.Run("writeListOffsetRequestV1", testWriteListOffsetRequestV1)
	t.Run("writeProduceRequestV2", testWriteProduceRequestV2)
}

func testWriteFetchRequestV2(t *testing.T) {
	const offset = 42
	const minBytes = 10
	const maxBytes = 1000
	const maxWait = 100 * time.Millisecond
	testWriteOptimization(t,
		requestHeader{
			ApiKey:        int16(fetchRequest),
			ApiVersion:    int16(v2),
			CorrelationID: testCorrelationID,
			ClientID:      testClientID,
		},
		fetchRequestV2{
			ReplicaID:   -1,
			MaxWaitTime: milliseconds(maxWait),
			MinBytes:    minBytes,
			Topics: []fetchRequestTopicV2{{
				TopicName: testTopic,
				Partitions: []fetchRequestPartitionV2{{
					Partition:   testPartition,
					FetchOffset: offset,
					MaxBytes:    maxBytes,
				}},
			}},
		},
		func(w *bufio.Writer) {
			writeFetchRequestV2(w, testCorrelationID, testClientID, testTopic, testPartition, offset, minBytes, maxBytes, maxWait)
		},
	)
}

func testWriteListOffsetRequestV1(t *testing.T) {
	const time = -1
	testWriteOptimization(t,
		requestHeader{
			ApiKey:        int16(listOffsetRequest),
			ApiVersion:    int16(v1),
			CorrelationID: testCorrelationID,
			ClientID:      testClientID,
		},
		listOffsetRequestV1{
			ReplicaID: -1,
			Topics: []listOffsetRequestTopicV1{{
				TopicName: testTopic,
				Partitions: []listOffsetRequestPartitionV1{{
					Partition: testPartition,
					Time:      time,
				}},
			}},
		},
		func(w *bufio.Writer) {
			writeListOffsetRequestV1(w, testCorrelationID, testClientID, testTopic, testPartition, time)
		},
	)
}

func testWriteProduceRequestV2(t *testing.T) {
	key := []byte(nil)
	val := []byte("Hello World!")

	msg := messageSetItem{
		Offset: 10,
		Message: message{
			MagicByte:  1,
			Attributes: 0,
			Key:        key,
			Value:      val,
		},
	}
	msg.MessageSize = msg.Message.size()
	msg.Message.CRC = msg.Message.crc32()

	const timeout = 100
	testWriteOptimization(t,
		requestHeader{
			ApiKey:        int16(produceRequest),
			ApiVersion:    int16(v2),
			CorrelationID: testCorrelationID,
			ClientID:      testClientID,
		},
		produceRequestV2{
			RequiredAcks: -1,
			Timeout:      timeout,
			Topics: []produceRequestTopicV2{{
				TopicName: testTopic,
				Partitions: []produceRequestPartitionV2{{
					Partition:      testPartition,
					MessageSetSize: msg.size(), MessageSet: messageSet{msg},
				}},
			}},
		},
		func(w *bufio.Writer) {
			writeProduceRequestV2(w, nil, testCorrelationID, testClientID, testTopic, testPartition, timeout*time.Millisecond, -1, Message{
				Offset: 10,
				Key:    key,
				Value:  val,
			})
		},
	)
}

func testWriteOptimization(t *testing.T, h requestHeader, r request, f func(*bufio.Writer)) {
	b1 := &bytes.Buffer{}
	w1 := bufio.NewWriter(b1)

	b2 := &bytes.Buffer{}
	w2 := bufio.NewWriter(b2)

	h.Size = (h.size() + r.size()) - 4
	h.writeTo(w1)
	r.writeTo(w1)
	w1.Flush()

	f(w2)
	w2.Flush()

	c1 := b1.Bytes()
	c2 := b2.Bytes()

	if !bytes.Equal(c1, c2) {
		t.Error("content differs")

		n1 := len(c1)
		n2 := len(c2)

		if n1 != n2 {
			t.Log("content length 1 =", n1)
			t.Log("content length 2 =", n2)
		} else {
			for i := 0; i != n1; i++ {
				if c1[i] != c2[i] {
					t.Logf("byte at offset %d/%d: %#x != %#x", i, n1, c1[i], c2[i])
					break
				}
			}
		}
	}
}

func TestWriteV2RecordBatch(t *testing.T) {

	if !ktesting.KafkaIsAtLeast("0.11.0") {
		t.Skip("RecordBatch was added in kafka 0.11.0")
		return
	}

	topic := CreateTopic(t, 1)
	msgs := make([]Message, 15)
	for i := range msgs {
		value := fmt.Sprintf("Sample message content: %d!", i)
		msgs[i] = Message{Key: []byte("Key"), Value: []byte(value), Headers: []Header{Header{Key: "hk", Value: []byte("hv")}}}
	}
	w := NewWriter(WriterConfig{
		Brokers:      []string{"localhost:9092"},
		Topic:        topic,
		BatchTimeout: 100 * time.Millisecond,
		BatchSize:    5,
	})

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	if err := w.WriteMessages(ctx, msgs...); err != nil {
		t.Errorf("Failed to write v2 messages to kafka: %v", err)
		return
	}
	w.Close()

	r := NewReader(ReaderConfig{
		Brokers: []string{"localhost:9092"},
		Topic:   topic,
		MaxWait: 100 * time.Millisecond,
	})
	defer r.Close()

	msg, err := r.ReadMessage(context.Background())
	if err != nil {
		t.Error("Failed to read message")
		return
	}

	if string(msg.Key) != "Key" {
		t.Error("Received message's key doesn't match")
		return
	}
	if msg.Headers[0].Key != "hk" {
		t.Error("Received message header's key doesn't match")
		return
	}
}
<|endoftext|>


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

package json2

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"testing"

	"github.com/gorilla/rpc/v2"
)

// ResponseRecorder is an implementation of http.ResponseWriter that
// records its mutations for later inspection in tests.
type ResponseRecorder struct {
	Code      int           // the HTTP response code from WriteHeader
	HeaderMap http.Header   // the HTTP response headers
	Body      *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
	Flushed   bool
}

// NewRecorder returns an initialized ResponseRecorder.
func NewRecorder() *ResponseRecorder {
	return &ResponseRecorder{
		HeaderMap: make(http.Header),
		Body:      new(bytes.Buffer),
	}
}

// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
const DefaultRemoteAddr = "1.2.3.4"

// Header returns the response headers.
func (rw *ResponseRecorder) Header() http.Header {
	return rw.HeaderMap
}

// Write always succeeds and writes to rw.Body, if not nil.
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
	if rw.Body != nil {
		rw.Body.Write(buf)
	}
	if rw.Code == 0 {
		rw.Code = http.StatusOK
	}
	return len(buf), nil
}

// WriteHeader sets rw.Code.
func (rw *ResponseRecorder) WriteHeader(code int) {
	rw.Code = code
}

// Flush sets rw.Flushed to true.
func (rw *ResponseRecorder) Flush() {
	rw.Flushed = true
}

// ----------------------------------------------------------------------------

var ErrResponseError = errors.New("response error")

type Service1Request struct {
	A int
	B int
}

type Service1BadRequest struct {
	V  string `json:"jsonrpc"`
	M  string `json:"method"`
	ID uint64 `json:"id"`
}

type Service1Response struct {
	Result int
}

type Service1 struct {
}

func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error {
	res.Result = req.A * req.B
	return nil
}

func (t *Service1) ResponseError(r *http.Request, req *Service1Request, res *Service1Response) error {
	return ErrResponseError
}

func execute(t *testing.T, s *rpc.Server, method string, req, res interface{}) error {
	if !s.HasMethod(method) {
		t.Fatal("Expected to be registered:", method)
	}

	buf, _ := EncodeClientRequest(method, req)
	body := bytes.NewBuffer(buf)
	r, _ := http.NewRequest("POST", "http://localhost:8080/", body)
	r.Header.Set("Content-Type", "application/json")

	w := NewRecorder()
	s.ServeHTTP(w, r)

	return DecodeClientResponse(w.Body, res)
}

func executeRaw(t *testing.T, s *rpc.Server, req interface{}, res interface{}) error {
	j, _ := json.Marshal(req)
	r, _ := http.NewRequest("POST", "http://localhost:8080/", bytes.NewBuffer(j))
	r.Header.Set("Content-Type", "application/json")

	w := NewRecorder()
	s.ServeHTTP(w, r)

	return DecodeClientResponse(w.Body, res)
}

func TestService(t *testing.T) {
	s := rpc.NewServer()
	s.RegisterCodec(NewCodec(), "application/json")
	s.RegisterService(new(Service1), "")

	var res Service1Response
	if err := execute(t, s, "Service1.Multiply", &Service1Request{4, 2}, &res); err != nil {
		t.Error("Expected err to be nil, but got:", err)
	}
	if res.Result != 8 {
		t.Errorf("Wrong response: %v.", res.Result)
	}

	if err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err == nil {
		t.Errorf("Expected to get %q, but got nil", ErrResponseError)
	} else if err.Error() != ErrResponseError.Error() {
		t.Errorf("Expected to get %q, but got %q", ErrResponseError, err)
	}

	if err := executeRaw(t, s, &Service1BadRequest{"2.0", "Service1.Multiply", 1}, &res); err == nil {
		t.Errorf("Expected error but error in nil")
	}
}
<commit_msg>Add tests to verify jsonrpc2 null results<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package json2

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"testing"

	"github.com/gorilla/rpc/v2"
)

// ResponseRecorder is an implementation of http.ResponseWriter that
// records its mutations for later inspection in tests.
type ResponseRecorder struct {
	Code      int           // the HTTP response code from WriteHeader
	HeaderMap http.Header   // the HTTP response headers
	Body      *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
	Flushed   bool
}

// NewRecorder returns an initialized ResponseRecorder.
func NewRecorder() *ResponseRecorder {
	return &ResponseRecorder{
		HeaderMap: make(http.Header),
		Body:      new(bytes.Buffer),
	}
}

// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
const DefaultRemoteAddr = "1.2.3.4"

// Header returns the response headers.
func (rw *ResponseRecorder) Header() http.Header {
	return rw.HeaderMap
}

// Write always succeeds and writes to rw.Body, if not nil.
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
	if rw.Body != nil {
		rw.Body.Write(buf)
	}
	if rw.Code == 0 {
		rw.Code = http.StatusOK
	}
	return len(buf), nil
}

// WriteHeader sets rw.Code.
func (rw *ResponseRecorder) WriteHeader(code int) {
	rw.Code = code
}

// Flush sets rw.Flushed to true.
func (rw *ResponseRecorder) Flush() {
	rw.Flushed = true
}

// ----------------------------------------------------------------------------

var ErrResponseError = errors.New("response error")

type Service1Request struct {
	A int
	B int
}

type Service1BadRequest struct {
	V  string `json:"jsonrpc"`
	M  string `json:"method"`
	ID uint64 `json:"id"`
}

type Service1Response struct {
	Result int
}

type Service1 struct {
}

func (t *Service1) Multiply(r *http.Request, req *Service1Request, res *Service1Response) error {
	res.Result = req.A * req.B
	return nil
}

func (t *Service1) ResponseError(r *http.Request, req *Service1Request, res *Service1Response) error {
	return ErrResponseError
}

func execute(t *testing.T, s *rpc.Server, method string, req, res interface{}) error {
	if !s.HasMethod(method) {
		t.Fatal("Expected to be registered:", method)
	}

	buf, _ := EncodeClientRequest(method, req)
	body := bytes.NewBuffer(buf)
	r, _ := http.NewRequest("POST", "http://localhost:8080/", body)
	r.Header.Set("Content-Type", "application/json")

	w := NewRecorder()
	s.ServeHTTP(w, r)

	return DecodeClientResponse(w.Body, res)
}

func executeRaw(t *testing.T, s *rpc.Server, req interface{}, res interface{}) error {
	j, _ := json.Marshal(req)
	r, _ := http.NewRequest("POST", "http://localhost:8080/", bytes.NewBuffer(j))
	r.Header.Set("Content-Type", "application/json")

	w := NewRecorder()
	s.ServeHTTP(w, r)

	return DecodeClientResponse(w.Body, res)
}

func TestService(t *testing.T) {
	s := rpc.NewServer()
	s.RegisterCodec(NewCodec(), "application/json")
	s.RegisterService(new(Service1), "")

	var res Service1Response
	if err := execute(t, s, "Service1.Multiply", &Service1Request{4, 2}, &res); err != nil {
		t.Error("Expected err to be nil, but got:", err)
	}
	if res.Result != 8 {
		t.Errorf("Wrong response: %v.", res.Result)
	}

	if err := execute(t, s, "Service1.ResponseError", &Service1Request{4, 2}, &res); err == nil {
		t.Errorf("Expected to get %q, but got nil", ErrResponseError)
	} else if err.Error() != ErrResponseError.Error() {
		t.Errorf("Expected to get %q, but got %q", ErrResponseError, err)
	}

	if err := executeRaw(t, s, &Service1BadRequest{"2.0", "Service1.Multiply", 1}, &res); err == nil {
		t.Errorf("Expected error but error in nil")
	}
}

func TestDecodeNullResult(t *testing.T) {
	data := `{"jsonrpc": "2.0", "id": 12345, "result": null}`
	reader := bytes.NewReader([]byte(data))
	var result interface{}

	err := DecodeClientResponse(reader, &result)

	if err != nil {
		t.Error("Expected err no be nil, but got:", err)
	}

	if result != nil {
		t.Error("Expected result to be nil, but got:", result)
	}
}
<|endoftext|>


<commit_before>package yeelight

import (
	"bufio"
	"fmt"
	"log"
	"math"
	"net"
	"strconv"
	"strings"

	"errors"
	"time"
)

// timeout value for TCP and UDP commands
const timeout = time.Second * 2

type Hub struct {
	IP       string
	LightIDs []string // array of just the IDs of all lights the hub controls -- MAYBE? Maybe best in device, not here...
}

type Light struct {
	ID                                        string
	Type, Online, LQI, R, G, B, Level, Effect int
	/*
	   id = HEX code for light ID
	   type =  0 or 1 (always 1)
	   online = 0 or 1 (1 is online)
	   lqi = LED ZigBee signal, 0-100  *I think
	   r, g, b = 0-255...
	   level = 0-100 brightness
	   effect = reserved/not implemented by Yeelight yet
	*/
}

// GetLights queries the Yeelight hub for current status of all lights and
// returns an array of Light structs with the values
func GetLights(ip string) ([]Light, error) {
	response, err := SendCommand("GL\r\n", ip)
	if err != nil {
		log.Println("Error is:", err)
		return []Light{}, err
	} else {
		lights := getLightsFromString(response)
		return lights, err
	}

}

// SendCommand sends a single named command to the Yeelight hub via TCP
func SendCommand(cmd string, ip string) (string, error) {
	success := make(chan string, 1)
	response := ""
	var err error

	//	log.Printf("Sending command %s to IP %s\n", cmd, ip)
	go func() {
		conn, err := net.Dial("tcp", ip+":10003")

		if err != nil {
			log.Printf("Failed to connect: %s\n", err)
			return
		}
		// send command to net connection, read it
		fmt.Fprintf(conn, cmd)
		response, err = bufio.NewReader(conn).ReadString('\n')
		conn.Close()
		success <- response
	}()
	select {
	case <- success:
		return response, err
	case <- time.After(timeout):
		return "", fmt.Errorf("Timed out sending TCP command %v to Yeelight hub at %v\n", cmd, ip)
	}
	return response, err
}

// TurnOffAllLights turns off all Yeelight bulbs on hub
func TurnOffAllLights(ip string) error {
	_, err := SendCommand("C G000,,,0,0,0\r\n", ip)
	return err
}

// SetLight sets the useful values (R, G, B, Brightness - ints) for a given light based on its ID (string)
func SetLight(id string, r, g, b, brightness int, ip string) error {
	// format string like command: "C 50F5,200,100,255,90,0\r\n"
	cmd := fmt.Sprintf("C %s,%d,%d,%d,%d,0\r\n", id, r, g, b, brightness)
	_, err := SendCommand(cmd, ip)
	return err
}

// SetOnOff sets the light to on (full brightness) when state is true, off when it is false
func SetOnOff(id string, state bool, ip string) error {
	var cmd string
	if state {
		cmd = fmt.Sprintf("C %s,,,,100,\r\n", id)
	} else {
		cmd = fmt.Sprintf("C %s,,,,0,\r\n", id)
	}
	//	fmt.Printf("%#v", cmd)
	_, err := SendCommand(cmd, ip)
	return err
}

// ToggleOnOff determines on/off state of chosen light (0 is off, anything > 0 is on)
// then calls SetOnOff to set opposite on/off state
func ToggleOnOff(id string, ip string) error {
	lights, err := GetLights(ip)
	if err != nil {
		return err
	}
	for i := 0; i < len(lights); i++ {
		if lights[i].ID == id {
			if lights[i].Level != 0 {
				err = SetOnOff(id, false, ip)
			} else {
				err = SetOnOff(id, true, ip)
			}
			break
		}
	}
	return err
}

// SetBrightness takes a float level (0-1) and sets the brightness of a light (0-100)
func SetBrightness(id string, level float64, ip string) error {
	// convert level fraction to int 0-100
	brightness := int(level * 100)
	cmd := fmt.Sprintf("C %s,,,,%d,\r\n", id, brightness)
	_, err := SendCommand(cmd, ip)
	return err
}

// SetColor takes r, g, b values (0-255) and sets the colour, leaving brightness unchanged
func SetColor(id string, r, g, b uint8, ip string) error {
	cmd := fmt.Sprintf("C %s,%d,%d,%d,,\r\n", id, r, g, b)
	_, err := SendCommand(cmd, ip)
	return err
}

// Heartbeat pings the Yeelight hub to see if it's alive, returns either nil error if it's responsive
// or error if the ack is not received from the hub.
func Heartbeat(ip string) error {
	response, err := SendCommand("HB\r\n", ip)
	if err != nil {
		return err
	}
	if response != "HACK\r\n" {
		return errors.New("Error. Hub not responding")
	}
	return nil
}

func IsOn(id, ip string) (bool, error) {
	lights, err := GetLights(ip)
	for _, light := range lights {
		if light.ID == id {
			if light.Level > 0 {
				return true, err
			} else {
				return false, err
			}
		}
	}
	// unknown light
	return false, fmt.Errorf("Unknown light ID %v\n", id)
}

// DiscoverHub uses SSDP (UDP) to find and return the IP address of the Yeelight hub
// returns an empty string if not found
// ref for UDP code: https://groups.google.com/forum/#!topic/golang-nuts/Llfb0wMY9WI
func DiscoverHub() (string, error) {
	success := make(chan string, 1)
	searchString := "M-SEARCH * HTTP/1.1\r\n HOST:239.255.255.250:1900\r\n MAN:\"ssdp:discover\"\r\n ST:yeelink:yeebox\r\n MAC:00000001\r\n MX:3\r\n\n\r\n"
	ip := ""
	var err error
	ssdp, _ := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
	c, _ := net.ListenPacket("udp4", ":0")
	socket := c.(*net.UDPConn)
	message := []byte(searchString)
	go func() {
		socket.WriteToUDP(message, ssdp)
		answerBytes := make([]byte, 1024)
		// stores result in answerBytes (pass-by-reference)
		_, _, err = socket.ReadFromUDP(answerBytes)
		if err == nil {
			response := string(answerBytes)
			// extract IP address from full response
			startIndex := strings.Index(response, "LOCATION: ") + 10
			endIndex := strings.Index(response, "MAC: ") - 2
			ip = response[startIndex:endIndex]
			success <- ip
		}
	}()

	select {
	case result := <-success:
		return result, nil
	case <-time.After(timeout):
		return "", fmt.Errorf("Timed out searching for hub\n")
	}

	return ip, err
}

// getLightsFromString converts string response from Yeelight GL command
// into an array of Light structs with all the data for each light
func getLightsFromString(response string) []Light {
	var lights []Light
	lights = make([]Light, 0)
	if response == "" {
		fmt.Println(fmt.Errorf("Error, string is empty"))
	} else {
		response = strings.TrimLeft(response, "GLB ")
		// remove last ';\r\n' so we don't get an empty light
		response = strings.TrimRight(response, ";\r\n")
		lightStrings := strings.Split(response, ";")
		for _, lightString := range lightStrings {
			parts := strings.Split(lightString, ",")
			address := parts[0]
			// convert strings to ints for data values
			var values [8]int
			for i := 1; i < len(parts); i++ {
				values[i-1], _ = strconv.Atoi(parts[i])
			}
			newLight := Light{address, values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7]}
			lights = append(lights, newLight)
		}
	}
	return lights
}

// HSVToRGB converts an HSV triple to a RGB triple.
// from https://godoc.org/code.google.com/p/sadbox/color
// Ported from http://goo.gl/Vg1h9
func HSVToRGB(h, s, v float64) (r, g, b uint8) {
	var fR, fG, fB float64
	i := math.Floor(h * 6)
	f := h*6 - i
	p := v * (1.0 - s)
	q := v * (1.0 - f*s)
	t := v * (1.0 - (1.0-f)*s)
	switch int(i) % 6 {
	case 0:
		fR, fG, fB = v, t, p
	case 1:
		fR, fG, fB = q, v, p
	case 2:
		fR, fG, fB = p, v, t
	case 3:
		fR, fG, fB = p, q, v
	case 4:
		fR, fG, fB = t, p, v
	case 5:
		fR, fG, fB = v, p, q
	}
	r, g, b = float64ToUint8(fR), float64ToUint8(fG), float64ToUint8(fB)
	return
}

// float64ToUint8 converts a float64 to uint8.
// See: http://code.google.com/p/go/issues/detail?id=3423
func float64ToUint8(x float64) uint8 {
	if x < 0 {
		return 0
	}
	if x > 1 {
		return 255
	}
	return uint8(int(x*255 + 0.5))
}

// TemperatureToRGB converts a colour temperature in kelvins in range [1000, 40000] to RGB
// https://gist.github.com/paulkaplan/5184275
// From http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
func TemperatureToRGB(kelvin float64) (r, g, b uint8) {
	temp := kelvin / 100

	var red, green, blue float64

	if temp <= 66 {

		red = 255

		green = temp
		green = 99.4708025861*math.Log(green) - 161.1195681661

		if temp <= 19 {
			blue = 0
		} else {
			blue = temp - 10
			blue = 138.5177312231*math.Log(blue) - 305.0447927307
		}

	} else {
		red = temp - 60
		red = 329.698727446 * math.Pow(red, -0.1332047592)

		green = temp - 60
		green = 288.1221695283 * math.Pow(green, -0.0755148492)

		blue = 255
	}
	return clamp(red, 0, 255), clamp(green, 0, 255), clamp(blue, 0, 255)
}

func clamp(x, min, max float64) uint8 {

	if x < min {
		return uint8(min)
	}
	if x > max {
		return uint8(max)
	}
	return uint8(x)
}
<commit_msg>Commenting improvements<commit_after>package yeelight

import (
	"bufio"
	"fmt"
	"log"
	"math"
	"net"
	"strconv"
	"strings"

	"errors"
	"time"
)

// timeout value for TCP and UDP commands
const timeout = time.Second * 2

//A Light stores all of the data for a Yeelight, as returned by the GetLights ("GL") command
//id = HEX code for light ID
//type =  0 or 1 (always 1)
//online = 0 or 1 (1 is online)
//lqi = LED ZigBee signal, 0-100  *I think
//r, g, b = 0-255...
//level = 0-100 brightness
//effect = reserved/not implemented by Yeelight yet
type Light struct {
	ID                                        string
	Type, Online, LQI, R, G, B, Level, Effect int
}

// GetLights queries the Yeelight hub for current status of all lights and
// returns an array of Light structs with the values
func GetLights(ip string) ([]Light, error) {
	response, err := SendCommand("GL\r\n", ip)
	if err != nil {
		log.Println("Error is:", err)
		return []Light{}, err
	} else {
		lights := getLightsFromString(response)
		return lights, err
	}

}

// SendCommand sends a single named command to the Yeelight hub via TCP
func SendCommand(cmd string, ip string) (string, error) {
	success := make(chan string, 1)
	response := ""
	var err error

	//	log.Printf("Sending command %s to IP %s\n", cmd, ip)
	go func() {
		conn, err := net.Dial("tcp", ip+":10003")

		if err != nil {
			log.Printf("Failed to connect: %s\n", err)
			return
		}
		// send command to net connection, read it
		fmt.Fprintf(conn, cmd)
		response, err = bufio.NewReader(conn).ReadString('\n')
		conn.Close()
		success <- response
	}()
	select {
	case <- success:
		return response, err
	case <- time.After(timeout):
		return "", fmt.Errorf("Timed out sending TCP command %v to Yeelight hub at %v\n", cmd, ip)
	}
	return response, err
}

// TurnOffAllLights turns off all Yeelight bulbs on hub
func TurnOffAllLights(ip string) error {
	_, err := SendCommand("C G000,,,0,0,0\r\n", ip)
	return err
}

// SetLight sets the useful values (R, G, B, Brightness - ints) for a given light based on its ID (string)
func SetLight(id string, r, g, b, brightness int, ip string) error {
	// format string like command: "C 50F5,200,100,255,90,0\r\n"
	cmd := fmt.Sprintf("C %s,%d,%d,%d,%d,0\r\n", id, r, g, b, brightness)
	_, err := SendCommand(cmd, ip)
	return err
}

// SetOnOff sets the light to on (full brightness) when state is true, off when it is false
func SetOnOff(id string, state bool, ip string) error {
	var cmd string
	if state {
		cmd = fmt.Sprintf("C %s,,,,100,\r\n", id)
	} else {
		cmd = fmt.Sprintf("C %s,,,,0,\r\n", id)
	}
	_, err := SendCommand(cmd, ip)
	return err
}

// ToggleOnOff determines on/off state of chosen light (level/brightness of 0 is off, anything > 0 is on)
// then calls SetOnOff to set opposite on/off state
func ToggleOnOff(id string, ip string) error {
	lights, err := GetLights(ip)
	if err != nil {
		return err
	}
	for i := 0; i < len(lights); i++ {
		if lights[i].ID == id {
			if lights[i].Level != 0 {
				err = SetOnOff(id, false, ip)
			} else {
				err = SetOnOff(id, true, ip)
			}
			break
		}
	}
	return err
}

// SetBrightness takes a float level (0-1) and sets the brightness of a light (0-100)
func SetBrightness(id string, level float64, ip string) error {
	// convert level fraction to int 0-100
	brightness := int(level * 100)
	cmd := fmt.Sprintf("C %s,,,,%d,\r\n", id, brightness)
	_, err := SendCommand(cmd, ip)
	return err
}

// SetColor takes r, g, b values (0-255) and sets the color, leaving brightness unchanged
func SetColor(id string, r, g, b uint8, ip string) error {
	cmd := fmt.Sprintf("C %s,%d,%d,%d,,\r\n", id, r, g, b)
	_, err := SendCommand(cmd, ip)
	return err
}

// Heartbeat pings the Yeelight hub to see if it's alive,
// returns either nil error if it's responsive or error if the ack is not received from the hub.
func Heartbeat(ip string) error {
	response, err := SendCommand("HB\r\n", ip)
	if err != nil {
		return err
	}
	if response != "HACK\r\n" {
		return errors.New("Error. Hub not responding")
	}
	return nil
}

// IsOn determines if a given light is on (has brightness/level above 0)
func IsOn(id, ip string) (bool, error) {
	lights, err := GetLights(ip)
	for _, light := range lights {
		if light.ID == id {
			if light.Level > 0 {
				return true, err
			} else {
				return false, err
			}
		}
	}
	return false, fmt.Errorf("Unknown light ID %v\n", id)
}

// DiscoverHub uses SSDP (UDP) to find and return the IP address of the Yeelight hub
// returns an empty string and error if not found
// ref for UDP code: https://groups.google.com/forum/#!topic/golang-nuts/Llfb0wMY9WI
func DiscoverHub() (string, error) {
	success := make(chan string, 1)
	searchString := "M-SEARCH * HTTP/1.1\r\n HOST:239.255.255.250:1900\r\n MAN:\"ssdp:discover\"\r\n ST:yeelink:yeebox\r\n MAC:00000001\r\n MX:3\r\n\n\r\n"
	ip := ""
	var err error
	ssdp, _ := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
	c, _ := net.ListenPacket("udp4", ":0")
	socket := c.(*net.UDPConn)
	message := []byte(searchString)
	go func() {
		socket.WriteToUDP(message, ssdp)
		answerBytes := make([]byte, 1024)
		// stores result in answerBytes (pass-by-reference)
		_, _, err = socket.ReadFromUDP(answerBytes)
		if err == nil {
			response := string(answerBytes)
			// extract IP address from full response
			startIndex := strings.Index(response, "LOCATION: ") + 10
			endIndex := strings.Index(response, "MAC: ") - 2
			ip = response[startIndex:endIndex]
			success <- ip
		}
	}()

	select {
	case result := <-success:
		return result, nil
	case <-time.After(timeout):
		return "", fmt.Errorf("Timed out searching for hub\n")
	}

	return ip, err
}

// getLightsFromString converts string response from Yeelight GL command
// into an array of Light structs with all the data for each light
func getLightsFromString(response string) []Light {
	var lights []Light
	lights = make([]Light, 0)
	if response == "" {
		fmt.Println(fmt.Errorf("Error, string is empty"))
	} else {
		response = strings.TrimLeft(response, "GLB ")
		// remove last ';\r\n' so we don't get an empty light
		response = strings.TrimRight(response, ";\r\n")
		lightStrings := strings.Split(response, ";")
		for _, lightString := range lightStrings {
			parts := strings.Split(lightString, ",")
			address := parts[0]
			// convert strings to ints for data values
			var values [8]int
			for i := 1; i < len(parts); i++ {
				values[i-1], _ = strconv.Atoi(parts[i])
			}
			newLight := Light{address, values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7]}
			lights = append(lights, newLight)
		}
	}
	return lights
}

// HSVToRGB converts an HSV triple to a RGB triple.
// from https://godoc.org/code.google.com/p/sadbox/color
// Ported from http://goo.gl/Vg1h9
func HSVToRGB(h, s, v float64) (r, g, b uint8) {
	var fR, fG, fB float64
	i := math.Floor(h * 6)
	f := h*6 - i
	p := v * (1.0 - s)
	q := v * (1.0 - f*s)
	t := v * (1.0 - (1.0-f)*s)
	switch int(i) % 6 {
	case 0:
		fR, fG, fB = v, t, p
	case 1:
		fR, fG, fB = q, v, p
	case 2:
		fR, fG, fB = p, v, t
	case 3:
		fR, fG, fB = p, q, v
	case 4:
		fR, fG, fB = t, p, v
	case 5:
		fR, fG, fB = v, p, q
	}
	r, g, b = float64ToUint8(fR), float64ToUint8(fG), float64ToUint8(fB)
	return
}

// float64ToUint8 converts a float64 to uint8.
// See: http://code.google.com/p/go/issues/detail?id=3423
func float64ToUint8(x float64) uint8 {
	if x < 0 {
		return 0
	}
	if x > 1 {
		return 255
	}
	return uint8(int(x*255 + 0.5))
}

// TemperatureToRGB converts a colour temperature in kelvins in range [1000, 40000] to RGB
// https://gist.github.com/paulkaplan/5184275
// From http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
func TemperatureToRGB(kelvin float64) (r, g, b uint8) {
	temp := kelvin / 100

	var red, green, blue float64

	if temp <= 66 {

		red = 255

		green = temp
		green = 99.4708025861*math.Log(green) - 161.1195681661

		if temp <= 19 {
			blue = 0
		} else {
			blue = temp - 10
			blue = 138.5177312231*math.Log(blue) - 305.0447927307
		}

	} else {
		red = temp - 60
		red = 329.698727446 * math.Pow(red, -0.1332047592)

		green = temp - 60
		green = 288.1221695283 * math.Pow(green, -0.0755148492)

		blue = 255
	}
	return clamp(red, 0, 255), clamp(green, 0, 255), clamp(blue, 0, 255)
}

func clamp(x, min, max float64) uint8 {

	if x < min {
		return uint8(min)
	}
	if x > max {
		return uint8(max)
	}
	return uint8(x)
}
<|endoftext|>


<commit_before>package redlot

func encodeHashKey(name, key []byte) (buf []byte) {
	buf = append(buf, typeHASH)
	buf = append(buf, uint32ToBytes(uint32(len(name)))...)
	buf = append(buf, name...)
	buf = append(buf, uint32ToBytes(uint32(len(key)))...)
	buf = append(buf, key...)
	return
}

func decodeHashKey(b []byte) (name, key []byte) {
	nameLen := bytesToUint32(b[1:5])
	name = b[5 : 5+nameLen]
	key = b[9+nameLen:]
	return
}

func encodeHsizeKey(name []byte) (buf []byte) {
	buf = append(buf, typeHSIZE)
	buf = append(buf, uint32ToBytes(uint32(len(name)))...)
	buf = append(buf, name...)
	return
}

func decodeHsizeKey(b []byte) (key []byte) {
	return b[5:]
}

func hashSizeIncr(name, key []byte) {
	hsize := encodeHsizeKey(name)
	hash := encodeHashKey(name, key)

	var size uint32
	if b, err := db.Get(hsize, nil); err == nil {
		size = bytesToUint32(b)
	}

	if exists, _ := db.Has(hash, nil); !exists {
		size++
		db.Put(hsize, uint32ToBytes(size), nil)
	}
}

// Hset will set a hashmap value by the key.
// Args: name string, key string, value any
func Hset(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	hashSizeIncr(args[0], args[1])
	err = db.Put(encodeHashKey(args[0], args[1]), args[2], nil)
	if err != nil {
		return nil, err
	}

	return
}

// Hset will return a hashmap value by the key.
// Args: name string, key string
func Hget(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}
	var b []byte
	b, err = db.Get(encodeHashKey(args[0], args[1]), nil)
	return string(b), err
}

// Hdel will delete a hashmap value by the key.
// Args: name string, key string
func Hdel(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// Hdel will incr a hashmap value by the key.
// Args: name string, key string, value int
func Hincr(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

// Hexists will check the hashmap key is exists.
// Args: name string, key string
func Hexists(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// Hsize will return the hashmap size.
// Args: name string
func Hsize(args [][]byte) (r interface{}, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	var size int64
	if b, e := db.Get(encodeHsizeKey(args[0]), nil); e != nil {
		size = -1
	} else {
		size = int64(bytesToUint32(b))
	}

	return size, nil
}

// Hlist will list all hashmap in the range.
// Args: start string, end string, limit int
func Hlist(args [][]byte) (r []string, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

// Hrlist will reverse list all hashmap in the range.
// Args: start string, end string, limit int
func Hrlist(args [][]byte) (r []string, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

// Hkeys will list the hashmap keys in the range.
// Args: name string, start string, end string, limit int
func Hkeys(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	return
}

// Hgetall will list all keys/value in the hashmap.
// Args: name string
func Hgetall(args [][]byte) (r []string, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	return
}

// Hscan will list keys/value of the hashmap in the range.
// Args: name string, start string, end string, limit int
func Hscan(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	return
}

// Hrscan will reverse list keys/value of the hashmap in the range.
// Args: name string, start string, end string, limit int
func Hrscan(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	return
}

// Hclear will remove all value in the hashmap.
// Args: name string
func Hclear(args [][]byte) (r interface{}, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	return
}

// MultiHget will return multi hashmap value by keys.
func MultiHget(args [][]byte) (r []string, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// MultiHset will set multi hashmap value by keys.
func MultiHset(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 && len(args)%2 == 0 {
		return nil, errNosArgs
	}

	return
}

// MultiHdel will delete multi hashmap value by keys.
func MultiHdel(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}
<commit_msg>Implement Hgetall method.<commit_after>package redlot

import "github.com/syndtr/goleveldb/leveldb/util"

func encodeHashKey(name, key []byte) (buf []byte) {
	buf = append(buf, typeHASH)
	buf = append(buf, uint32ToBytes(uint32(len(name)))...)
	buf = append(buf, name...)
	buf = append(buf, uint32ToBytes(uint32(len(key)))...)
	buf = append(buf, key...)
	return
}

func decodeHashKey(b []byte) (name, key []byte) {
	nameLen := bytesToUint32(b[1:5])
	name = b[5 : 5+nameLen]
	key = b[9+nameLen:]
	return
}

func encodeHsizeKey(name []byte) (buf []byte) {
	buf = append(buf, typeHSIZE)
	buf = append(buf, uint32ToBytes(uint32(len(name)))...)
	buf = append(buf, name...)
	return
}

func decodeHsizeKey(b []byte) (key []byte) {
	return b[5:]
}

func hashSizeIncr(name, key []byte) {
	hsize := encodeHsizeKey(name)
	hash := encodeHashKey(name, key)

	var size uint32
	if b, err := db.Get(hsize, nil); err == nil {
		size = bytesToUint32(b)
	}

	if exists, _ := db.Has(hash, nil); !exists {
		size++
		db.Put(hsize, uint32ToBytes(size), nil)
	}
}

// Hset will set a hashmap value by the key.
// Args: name string, key string, value any
func Hset(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	hashSizeIncr(args[0], args[1])
	err = db.Put(encodeHashKey(args[0], args[1]), args[2], nil)
	if err != nil {
		return nil, err
	}

	return
}

// Hset will return a hashmap value by the key.
// Args: name string, key string
func Hget(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}
	var b []byte
	b, err = db.Get(encodeHashKey(args[0], args[1]), nil)
	return string(b), err
}

// Hdel will delete a hashmap value by the key.
// Args: name string, key string
func Hdel(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// Hdel will incr a hashmap value by the key.
// Args: name string, key string, value int
func Hincr(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

// Hexists will check the hashmap key is exists.
// Args: name string, key string
func Hexists(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// Hsize will return the hashmap size.
// Args: name string
func Hsize(args [][]byte) (r interface{}, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	var size int64
	if b, e := db.Get(encodeHsizeKey(args[0]), nil); e != nil {
		size = -1
	} else {
		size = int64(bytesToUint32(b))
	}

	return size, nil
}

// Hlist will list all hashmap in the range.
// Args: start string, end string, limit int
func Hlist(args [][]byte) (r []string, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

// Hrlist will reverse list all hashmap in the range.
// Args: start string, end string, limit int
func Hrlist(args [][]byte) (r []string, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

// Hkeys will list the hashmap keys in the range.
// Args: name string, start string, end string, limit int
func Hkeys(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	return
}

// Hgetall will list all keys/value in the hashmap.
// Args: name string
func Hgetall(args [][]byte) (r []string, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	if _, err = db.Get(encodeHsizeKey(args[0]), nil); err != nil {
		return
	}

	var buf []byte
	buf = append(buf, typeHASH)
	buf = append(buf, uint32ToBytes(uint32(len(args[0])))...)
	buf = append(buf, args[0]...)
	ke := append(buf, []byte{0xff}...)

	iter := db.NewIterator(&util.Range{Start: buf, Limit: ke}, nil)
	for iter.Next() {
		_, key := decodeHashKey(iter.Key())
		r = append(r, string(key))
		r = append(r, string(iter.Value()))
	}
	iter.Release()
	err = iter.Error()
	return
}

// Hscan will list keys/value of the hashmap in the range.
// Args: name string, start string, end string, limit int
func Hscan(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	return
}

// Hrscan will reverse list keys/value of the hashmap in the range.
// Args: name string, start string, end string, limit int
func Hrscan(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	return
}

// Hclear will remove all value in the hashmap.
// Args: name string
func Hclear(args [][]byte) (r interface{}, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	return
}

// MultiHget will return multi hashmap value by keys.
func MultiHget(args [][]byte) (r []string, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// MultiHset will set multi hashmap value by keys.
func MultiHset(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 && len(args)%2 == 0 {
		return nil, errNosArgs
	}

	return
}

// MultiHdel will delete multi hashmap value by keys.
func MultiHdel(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}
<|endoftext|>