<commit_before>package chunk

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"os"
	"path/filepath"
	"sync"

	"time"

	. "github.com/claudetech/loggo/default"
)

// ErrTimeout is a timeout error
var ErrTimeout = errors.New("timeout")

// Storage is a chunk storage
type Storage struct {
	ChunkPath  string
	ChunkSize  int64
	MaxChunks  int
	queue      chan *Item
	chunks     map[string][]byte
	chunksLock sync.Mutex
	toc        map[string]error
	tocLock    sync.Mutex
	stack      *Stack
}

// Item represents a chunk in RAM
type Item struct {
	id    string
	bytes []byte
}

// NewStorage creates a new storage
func NewStorage(chunkPath string, chunkSize int64, maxChunks int) *Storage {
	storage := Storage{
		ChunkPath: chunkPath,
		ChunkSize: chunkSize,
		MaxChunks: maxChunks,
		queue:     make(chan *Item, 100),
		chunks:    make(map[string][]byte),
		toc:       make(map[string]error),
		stack:     NewStack(),
	}

	go storage.thread()

	return &storage
}

// Clear removes all old chunks on disk (will be called on each program start)
func (s *Storage) Clear() error {
	if err := os.RemoveAll(s.ChunkPath); nil != err {
		return fmt.Errorf("Could not clear old chunks from disk")
	}
	return nil
}

// ExistsOrCreate check if an item already exists, otherwise it will create a placeholder
func (s *Storage) ExistsOrCreate(id string) bool {
	s.tocLock.Lock()
	if _, exists := s.toc[id]; exists {
		s.tocLock.Unlock()
		return true
	}
	s.toc[id] = nil
	s.tocLock.Unlock()
	return false
}

// Store stores a chunk in the RAM and adds it to the disk storage queue
func (s *Storage) Store(id string, bytes []byte) error {
	s.chunksLock.Lock()
	s.chunks[id] = bytes
	s.chunksLock.Unlock()

	s.queue <- &Item{
		id:    id,
		bytes: bytes,
	}

	return nil
}

// Error is called to remove an item from the index if there has been an issue downloading the chunk
func (s *Storage) Error(id string, err error) {
	s.tocLock.Lock()
	s.toc[id] = err
	s.tocLock.Unlock()
}

// Get gets a chunk content (blocking)
func (s *Storage) Get(id string, offset, size int64, timeout time.Duration) ([]byte, error) {
	// TODO: use timeout

	for {
		s.tocLock.Lock()
		err, exists := s.toc[id]
		s.tocLock.Unlock()
		if nil == err && exists {
			bytes, exists := s.loadFromRAM(id, offset, size)
			if exists {
				return bytes, nil
			}

			bytes, exists = s.loadFromDisk(id, offset, size)
			if exists {
				return bytes, nil
			}
		} else if nil != err {
			s.deleteFromToc(id)
			return nil, err
		}
	}
}

func (s *Storage) thread() {
	for {
		item := <-s.queue
		if err := s.storeToDisk(item.id, item.bytes); nil != err {
			Log.Warningf("%v", err)
		}
	}
}

func (s *Storage) deleteFromToc(id string) {
	s.tocLock.Lock()
	delete(s.toc, id)
	s.tocLock.Unlock()
}

func (s *Storage) loadFromRAM(id string, offset, size int64) ([]byte, bool) {
	s.chunksLock.Lock()
	bytes, exists := s.chunks[id]
	s.chunksLock.Unlock()
	if !exists {
		return nil, false
	}

	sOffset := int64(math.Min(float64(len(bytes)), float64(offset)))
	eOffset := int64(math.Min(float64(len(bytes)), float64(offset+size)))
	return bytes[sOffset:eOffset], true
}

func (s *Storage) loadFromDisk(id string, offset, size int64) ([]byte, bool) {
	filename := filepath.Join(s.ChunkPath, id)

	f, err := os.Open(filename)
	if nil != err {
		Log.Tracef("%v", err)
		return nil, false
	}
	defer f.Close()

	buf := make([]byte, size)
	n, err := f.ReadAt(buf, offset)
	if n > 0 && (nil == err || io.EOF == err || io.ErrUnexpectedEOF == err) {
		s.stack.Touch(id)

		eOffset := int64(math.Min(float64(size), float64(len(buf))))
		return buf[:eOffset], true
	}

	Log.Tracef("%v", err)
	return nil, false
}

func (s *Storage) storeToDisk(id string, bytes []byte) error {
	filename := filepath.Join(s.ChunkPath, id)

	if s.stack.Len() >= s.MaxChunks {
		deleteID := s.stack.Pop()

		if "" != deleteID {
			filename := filepath.Join(s.ChunkPath, deleteID)

			Log.Debugf("Deleting chunk %v", filename)
			if err := os.Remove(filename); nil != err {
				Log.Debugf("%v", err)
				Log.Warningf("Could not delete chunk %v", filename)
			}

			s.tocLock.Lock()
			delete(s.toc, deleteID)
			s.tocLock.Unlock()
		}
	}

	if _, err := os.Stat(s.ChunkPath); os.IsNotExist(err) {
		if err := os.MkdirAll(s.ChunkPath, 0777); nil != err {
			Log.Debugf("%v", err)
			return fmt.Errorf("Could not create chunk temp path %v", s.ChunkPath)
		}
	}

	if _, err := os.Stat(filename); os.IsNotExist(err) {
		if err := ioutil.WriteFile(filename, bytes, 0777); nil != err {
			Log.Debugf("%v", err)
			return fmt.Errorf("Could not write chunk temp file %v", filename)
		}

		s.stack.Push(id)
	}

	s.chunksLock.Lock()
	delete(s.chunks, id)
	s.chunksLock.Unlock()

	return nil
}
<commit_msg>Update storage.go<commit_after>package chunk

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"os"
	"path/filepath"
	"sync"

	"time"

	. "github.com/claudetech/loggo/default"
)

// ErrTimeout is a timeout error
var ErrTimeout = errors.New("timeout")

// Storage is a chunk storage
type Storage struct {
	ChunkPath  string
	ChunkSize  int64
	MaxChunks  int
	queue      chan *Item
	chunks     map[string][]byte
	chunksLock sync.Mutex
	toc        map[string]error
	tocLock    sync.Mutex
	stack      *Stack
}

// Item represents a chunk in RAM
type Item struct {
	id    string
	bytes []byte
}

// NewStorage creates a new storage
func NewStorage(chunkPath string, chunkSize int64, maxChunks int) *Storage {
	storage := Storage{
		ChunkPath: chunkPath,
		ChunkSize: chunkSize,
		MaxChunks: maxChunks,
		queue:     make(chan *Item, 100),
		chunks:    make(map[string][]byte),
		toc:       make(map[string]error),
		stack:     NewStack(),
	}

	go storage.thread()

	return &storage
}

// Clear removes all old chunks on disk (will be called on each program start)
func (s *Storage) Clear() error {
	if err := os.RemoveAll(s.ChunkPath); nil != err {
		return fmt.Errorf("Could not clear old chunks from disk")
	}
	return nil
}

// ExistsOrCreate check if an item already exists, otherwise it will create a placeholder
func (s *Storage) ExistsOrCreate(id string) bool {
	s.tocLock.Lock()
	if _, exists := s.toc[id]; exists {
		s.tocLock.Unlock()
		return true
	}
	s.toc[id] = nil
	s.tocLock.Unlock()
	return false
}

// Store stores a chunk in the RAM and adds it to the disk storage queue
func (s *Storage) Store(id string, bytes []byte) error {
	s.chunksLock.Lock()
	s.chunks[id] = bytes
	s.chunksLock.Unlock()

	s.queue <- &Item{
		id:    id,
		bytes: bytes,
	}

	return nil
}

// Error is called to remove an item from the index if there has been an issue downloading the chunk
func (s *Storage) Error(id string, err error) {
	s.tocLock.Lock()
	s.toc[id] = err
	s.tocLock.Unlock()
}

// Get gets a chunk content (blocking)
func (s *Storage) Get(id string, offset, size int64, timeout time.Duration) ([]byte, error) {
	// TODO: use timeout

	for {
		s.tocLock.Lock()
		err, exists := s.toc[id]
		s.tocLock.Unlock()
		if nil == err && exists {
			bytes, exists := s.loadFromRAM(id, offset, size)
			if exists {
				return bytes, nil
			}

			bytes, exists = s.loadFromDisk(id, offset, size)
			if exists {
				return bytes, nil
			}
		} else if nil != err {
			s.deleteFromToc(id)
			return nil, err
		}
	}
}

func (s *Storage) thread() {
	for {
		item := <-s.queue
		if err := s.storeToDisk(item.id, item.bytes); nil != err {
			Log.Warningf("%v", err)
		}
	}
}

func (s *Storage) deleteFromToc(id string) {
	s.tocLock.Lock()
	delete(s.toc, id)
	s.tocLock.Unlock()
}

func (s *Storage) loadFromRAM(id string, offset, size int64) ([]byte, bool) {
	s.chunksLock.Lock()
	bytes, exists := s.chunks[id]
	s.chunksLock.Unlock()
	if !exists {
		return nil, false
	}

	sOffset := int64(math.Min(float64(len(bytes)), float64(offset)))
	eOffset := int64(math.Min(float64(len(bytes)), float64(offset+size)))
	return bytes[sOffset:eOffset], true
}

func (s *Storage) loadFromDisk(id string, offset, size int64) ([]byte, bool) {
	filename := filepath.Join(s.ChunkPath, id)

	f, err := os.Open(filename)
	if nil != err {
		Log.Tracef("%v", err)
		return nil, false
	}
	defer f.Close()

	buf := make([]byte, size)
	n, err := f.ReadAt(buf, offset)
	if n > 0 && (nil == err || io.EOF == err || io.ErrUnexpectedEOF == err) {
		s.stack.Touch(id)

		eOffset := int64(math.Min(float64(size), float64(n)))
		return buf[:eOffset], true
	}

	Log.Tracef("%v", err)
	return nil, false
}

func (s *Storage) storeToDisk(id string, bytes []byte) error {
	filename := filepath.Join(s.ChunkPath, id)

	if s.stack.Len() >= s.MaxChunks {
		deleteID := s.stack.Pop()

		if "" != deleteID {
			filename := filepath.Join(s.ChunkPath, deleteID)

			Log.Debugf("Deleting chunk %v", filename)
			if err := os.Remove(filename); nil != err {
				Log.Debugf("%v", err)
				Log.Warningf("Could not delete chunk %v", filename)
			}

			s.tocLock.Lock()
			delete(s.toc, deleteID)
			s.tocLock.Unlock()
		}
	}

	if _, err := os.Stat(s.ChunkPath); os.IsNotExist(err) {
		if err := os.MkdirAll(s.ChunkPath, 0777); nil != err {
			Log.Debugf("%v", err)
			return fmt.Errorf("Could not create chunk temp path %v", s.ChunkPath)
		}
	}

	if _, err := os.Stat(filename); os.IsNotExist(err) {
		if err := ioutil.WriteFile(filename, bytes, 0777); nil != err {
			Log.Debugf("%v", err)
			return fmt.Errorf("Could not write chunk temp file %v", filename)
		}

		s.stack.Push(id)
	}

	s.chunksLock.Lock()
	delete(s.chunks, id)
	s.chunksLock.Unlock()

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Tom Thorogood. All rights reserved.
// Use of this source code is governed by a Modified
// BSD License that can be found in the LICENSE file.

package id3v2

//go:generate go run generate_ids.go

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

// This is an implementation of v2.4.0 of the ID3v2 tagging format,
// defined in: http://id3.org/id3v2.4.0-structure, and v2.3.0 of
// the ID3v2 tagging format, defined in: http://id3.org/id3v2.3.0.

var errInvalidID3 = errors.New("id3: invalid tag data")

const (
	flagUnsynchronisation = 1 << (7 - iota)
	flagExtendedHeader
	flagExperimental
	flagFooter
)

type FrameID uint32

const syncsafeInvalid = ^uint32(0)

func syncsafe(data []byte) uint32 {
	_ = data[3]

	if data[0]&0x80 != 0 || data[1]&0x80 != 0 ||
		data[2]&0x80 != 0 || data[3]&0x80 != 0 {
		return syncsafeInvalid
	}

	return uint32(data[0])<<21 | uint32(data[1])<<14 |
		uint32(data[2])<<7 | uint32(data[3])
}

func beUint32(data []byte) uint32 {
	_ = data[3]
	return uint32(data[0])<<24 | uint32(data[1])<<16 |
		uint32(data[2])<<8 | uint32(data[3])
}

func id3Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	i := bytes.Index(data, []byte("ID3"))
	if i == -1 {
		if len(data) < 2 {
			return 0, nil, nil
		}

		return len(data) - 2, nil, nil
	}

	data = data[i:]
	if len(data) < 10 {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	size := syncsafe(data[6:])

	if data[3] == 0xff || data[4] == 0xff || size == syncsafeInvalid {
		// Skipping when we find the string "ID3" in the file but
		// the remaining header is invalid is consistent with the
		// detection logic in §3.1. This also reduces the
		// likelihood of errors being caused by the byte sequence
		// "ID3" (49 44 33) occuring in the audio, but does not
		// eliminate the possibility of errors in this case.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   An ID3v2 tag can be detected with the following pattern:
		//     $49 44 33 yy yy xx zz zz zz zz
		//   Where yy is less than $FF, xx is the 'flags' byte and zz
		//   is less than $80.
		return i + 3, nil, nil
	}

	if data[3] > 0x05 {
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If software with ID3v2.4.0 and below support should
		//   encounter version five or higher it should simply
		//   ignore the whole tag.
		return i + 3, nil, nil
	}

	if data[5]&flagFooter == flagFooter {
		size += 10
	}

	if len(data) < 10+int(size) {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	return i + 10 + int(size), data[:10+size], nil
}

const invalidFrameID = ^FrameID(0)

func validIDByte(b byte) bool {
	return (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
}

func frameID(data []byte) FrameID {
	_ = data[3]

	if validIDByte(data[0]) && validIDByte(data[1]) &&
		validIDByte(data[2]) && validIDByte(data[3]) {
		return FrameID(data[0])<<24 | FrameID(data[1])<<16 |
			FrameID(data[2])<<8 | FrameID(data[3])
	}

	for _, v := range data {
		if v != 0 {
			return invalidFrameID
		}
	}

	// This is probably the begging of padding.
	return 0
}

var bufPool = &sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 4<<10)
		return &buf
	},
}

func Scan(r io.Reader) (ID3Frames, error) {
	buf := bufPool.Get()
	defer bufPool.Put(buf)

	s := bufio.NewScanner(r)
	s.Buffer(*buf.(*[]byte), bufio.MaxScanTokenSize)
	s.Split(id3Split)

	var frames ID3Frames

scan:
	for s.Scan() {
		data := s.Bytes()

		header := data[:10]
		data = data[10:]

		if string(header[:3]) != "ID3" {
			panic("id3: bufio.Scanner failed")
		}

		version := header[3]
		switch version {
		case 0x04, 0x03:
		default:
			continue scan
		}

		flags := header[5]

		if flags&flagFooter == flagFooter {
			footer := data[len(data)-10:]
			data = data[:len(data)-10]

			if string(footer[:3]) != "3DI" ||
				!bytes.Equal(header[3:], footer[3:]) {
				return nil, errInvalidID3
			}
		}

		if flags&flagExtendedHeader == flagExtendedHeader {
			size := syncsafe(data)
			if size == syncsafeInvalid || len(data) < int(size) {
				return nil, errInvalidID3
			}

			extendedHeader := data[:size]
			data = data[size:]

			_ = extendedHeader
		}

		// TODO: expose unsynchronisation flag

	frames:
		for len(data) > 10 {
			_ = data[9]

			id := frameID(data)
			switch id {
			case 0:
				// We've probably hit padding, the padding
				// validity check below will handle this.
				break frames
			case invalidFrameID:
				return nil, errInvalidID3
			}

			var size uint32
			switch version {
			case 0x04:
				size = syncsafe(data[4:])
				if size == syncsafeInvalid {
					return nil, errInvalidID3
				}
			case 0x03:
				size = beUint32(data[4:])
			default:
				panic("unhandled version")
			}

			if len(data) < 10+int(size) {
				return nil, errInvalidID3
			}

			frames = append(frames, &ID3Frame{
				ID:    id,
				Flags: uint16(data[8])<<8 | uint16(data[9]),
				Data:  append([]byte(nil), data[10:10+size]...),
			})

			data = data[10+size:]
		}

		if flags&flagFooter == flagFooter && len(data) != 0 {
			return nil, errInvalidID3
		}

		for _, v := range data {
			if v != 0 {
				return nil, errInvalidID3
			}
		}
	}

	if s.Err() != nil {
		return nil, s.Err()
	}

	return frames, nil
}

type ID3Frames []*ID3Frame

func (frames ID3Frames) Lookup(id FrameID) *ID3Frame {
	for _, frame := range frames {
		if frame.ID == id {
			return frame
		}
	}

	return nil
}

type ID3Frame struct {
	ID    FrameID
	Flags uint16
	Data  []byte
}

func (f *ID3Frame) String() string {
	data, terminus := f.Data, ""
	if len(data) > 128 {
		data, terminus = data[:128], "..."
	}

	return fmt.Sprintf("&ID3Frame{ID: %s, Flags: 0x%04x, Data: %q%s}",
		f.ID.String(), f.Flags, data, terminus)
}
<commit_msg>Add method to ID3Frame to return text content<commit_after>// Copyright 2017 Tom Thorogood. All rights reserved.
// Use of this source code is governed by a Modified
// BSD License that can be found in the LICENSE file.

package id3v2

//go:generate go run generate_ids.go

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

// This is an implementation of v2.4.0 of the ID3v2 tagging format,
// defined in: http://id3.org/id3v2.4.0-structure, and v2.3.0 of
// the ID3v2 tagging format, defined in: http://id3.org/id3v2.3.0.

var errInvalidID3 = errors.New("id3: invalid tag data")

const (
	flagUnsynchronisation = 1 << (7 - iota)
	flagExtendedHeader
	flagExperimental
	flagFooter
)

type FrameID uint32

const syncsafeInvalid = ^uint32(0)

func syncsafe(data []byte) uint32 {
	_ = data[3]

	if data[0]&0x80 != 0 || data[1]&0x80 != 0 ||
		data[2]&0x80 != 0 || data[3]&0x80 != 0 {
		return syncsafeInvalid
	}

	return uint32(data[0])<<21 | uint32(data[1])<<14 |
		uint32(data[2])<<7 | uint32(data[3])
}

func beUint32(data []byte) uint32 {
	_ = data[3]
	return uint32(data[0])<<24 | uint32(data[1])<<16 |
		uint32(data[2])<<8 | uint32(data[3])
}

func id3Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	i := bytes.Index(data, []byte("ID3"))
	if i == -1 {
		if len(data) < 2 {
			return 0, nil, nil
		}

		return len(data) - 2, nil, nil
	}

	data = data[i:]
	if len(data) < 10 {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	size := syncsafe(data[6:])

	if data[3] == 0xff || data[4] == 0xff || size == syncsafeInvalid {
		// Skipping when we find the string "ID3" in the file but
		// the remaining header is invalid is consistent with the
		// detection logic in §3.1. This also reduces the
		// likelihood of errors being caused by the byte sequence
		// "ID3" (49 44 33) occuring in the audio, but does not
		// eliminate the possibility of errors in this case.
		//
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   An ID3v2 tag can be detected with the following pattern:
		//     $49 44 33 yy yy xx zz zz zz zz
		//   Where yy is less than $FF, xx is the 'flags' byte and zz
		//   is less than $80.
		return i + 3, nil, nil
	}

	if data[3] > 0x05 {
		// Quoting from §3.1 of id3v2.4.0-structure.txt:
		//   If software with ID3v2.4.0 and below support should
		//   encounter version five or higher it should simply
		//   ignore the whole tag.
		return i + 3, nil, nil
	}

	if data[5]&flagFooter == flagFooter {
		size += 10
	}

	if len(data) < 10+int(size) {
		if atEOF {
			return 0, nil, io.ErrUnexpectedEOF
		}

		return i, nil, nil
	}

	return i + 10 + int(size), data[:10+size], nil
}

const invalidFrameID = ^FrameID(0)

func validIDByte(b byte) bool {
	return (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')
}

func frameID(data []byte) FrameID {
	_ = data[3]

	if validIDByte(data[0]) && validIDByte(data[1]) &&
		validIDByte(data[2]) && validIDByte(data[3]) {
		return FrameID(data[0])<<24 | FrameID(data[1])<<16 |
			FrameID(data[2])<<8 | FrameID(data[3])
	}

	for _, v := range data {
		if v != 0 {
			return invalidFrameID
		}
	}

	// This is probably the begging of padding.
	return 0
}

var bufPool = &sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 4<<10)
		return &buf
	},
}

func Scan(r io.Reader) (ID3Frames, error) {
	buf := bufPool.Get()
	defer bufPool.Put(buf)

	s := bufio.NewScanner(r)
	s.Buffer(*buf.(*[]byte), bufio.MaxScanTokenSize)
	s.Split(id3Split)

	var frames ID3Frames

scan:
	for s.Scan() {
		data := s.Bytes()

		header := data[:10]
		data = data[10:]

		if string(header[:3]) != "ID3" {
			panic("id3: bufio.Scanner failed")
		}

		version := header[3]
		switch version {
		case 0x04, 0x03:
		default:
			continue scan
		}

		flags := header[5]

		if flags&flagFooter == flagFooter {
			footer := data[len(data)-10:]
			data = data[:len(data)-10]

			if string(footer[:3]) != "3DI" ||
				!bytes.Equal(header[3:], footer[3:]) {
				return nil, errInvalidID3
			}
		}

		if flags&flagExtendedHeader == flagExtendedHeader {
			size := syncsafe(data)
			if size == syncsafeInvalid || len(data) < int(size) {
				return nil, errInvalidID3
			}

			extendedHeader := data[:size]
			data = data[size:]

			_ = extendedHeader
		}

		// TODO: expose unsynchronisation flag

	frames:
		for len(data) > 10 {
			_ = data[9]

			id := frameID(data)
			switch id {
			case 0:
				// We've probably hit padding, the padding
				// validity check below will handle this.
				break frames
			case invalidFrameID:
				return nil, errInvalidID3
			}

			var size uint32
			switch version {
			case 0x04:
				size = syncsafe(data[4:])
				if size == syncsafeInvalid {
					return nil, errInvalidID3
				}
			case 0x03:
				size = beUint32(data[4:])
			default:
				panic("unhandled version")
			}

			if len(data) < 10+int(size) {
				return nil, errInvalidID3
			}

			frames = append(frames, &ID3Frame{
				ID:    id,
				Flags: uint16(data[8])<<8 | uint16(data[9]),
				Data:  append([]byte(nil), data[10:10+size]...),
			})

			data = data[10+size:]
		}

		if flags&flagFooter == flagFooter && len(data) != 0 {
			return nil, errInvalidID3
		}

		for _, v := range data {
			if v != 0 {
				return nil, errInvalidID3
			}
		}
	}

	if s.Err() != nil {
		return nil, s.Err()
	}

	return frames, nil
}

type ID3Frames []*ID3Frame

func (frames ID3Frames) Lookup(id FrameID) *ID3Frame {
	for _, frame := range frames {
		if frame.ID == id {
			return frame
		}
	}

	return nil
}

type ID3Frame struct {
	ID    FrameID
	Flags uint16
	Data  []byte
}

func (f *ID3Frame) String() string {
	data, terminus := f.Data, ""
	if len(data) > 128 {
		data, terminus = data[:128], "..."
	}

	return fmt.Sprintf("&ID3Frame{ID: %s, Flags: 0x%04x, Data: %q%s}",
		f.ID.String(), f.Flags, data, terminus)
}

func (f *ID3Frame) Text() (string, error) {
	m := len(f.Data)
	if m < 2 {
		return "", errors.New("id3: frame data is invalid")
	}

	switch f.Data[0] {
	case 0x00:
		for _, v := range f.Data {
			if v&0x80 == 0 {
				continue
			}

			runes := make([]rune, len(f.Data))
			for i, v := range f.Data {
				runes[i] = rune(v)
			}

			return string(runes), nil
		}

		fallthrough
	case 0x03:
		if f.Data[m-1] == 0x00 {
			// The specification requires that the string be
			// terminated with 0x00, but not all implementations
			// do this.
			m--
		}

		return string(f.Data[1:m]), nil
	default:
		return "", errors.New("id3: frame uses unsupported encoding")
	}
}
<|endoftext|>


<commit_before>package clw11

/*
#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
#ifdef __APPLE__
#include "OpenCL/opencl.h"
#else
#include "CL/opencl.h"
#endif
*/
import "C"
import "unsafe"

type (
	MemObjectType C.cl_mem_object_type
	ImageFormat   C.cl_image_format
	ImageInfo     C.cl_image_info
	ChannelOrder  C.cl_channel_order
	ChannelType   C.cl_channel_type
)

const (
	MemObjectBuffer  MemObjectType = C.CL_MEM_OBJECT_BUFFER
	MemObjectImage2D MemObjectType = C.CL_MEM_OBJECT_IMAGE2D
	MemObjectImage3D MemObjectType = C.CL_MEM_OBJECT_IMAGE3D
)

const (
	R         ChannelOrder = C.CL_R
	A         ChannelOrder = C.CL_A
	RG        ChannelOrder = C.CL_RG
	RA        ChannelOrder = C.CL_RA
	RGB       ChannelOrder = C.CL_RGB
	RGBA      ChannelOrder = C.CL_RGBA
	BGRA      ChannelOrder = C.CL_BGRA
	ARGB      ChannelOrder = C.CL_ARGB
	Intensity ChannelOrder = C.CL_INTENSITY
	Luminance ChannelOrder = C.CL_LUMINANCE
	Rx        ChannelOrder = C.CL_Rx
	RGx       ChannelOrder = C.CL_RGx
	RGBx      ChannelOrder = C.CL_RGBx
)

const (
	SnormInt8      ChannelType = C.CL_SNORM_INT8
	SnormInt16     ChannelType = C.CL_SNORM_INT16
	UnormInt8      ChannelType = C.CL_UNORM_INT8
	UnormInt16     ChannelType = C.CL_UNORM_INT16
	UnormShort565  ChannelType = C.CL_UNORM_SHORT_565
	UnormShort555  ChannelType = C.CL_UNORM_SHORT_555
	UnormInt101010 ChannelType = C.CL_UNORM_INT_101010
	SignedInt8     ChannelType = C.CL_SIGNED_INT8
	SignedInt16    ChannelType = C.CL_SIGNED_INT16
	SignedInt32    ChannelType = C.CL_SIGNED_INT32
	UnsignedInt8   ChannelType = C.CL_UNSIGNED_INT8
	UnsignedInt16  ChannelType = C.CL_UNSIGNED_INT16
	UnsignedInt32  ChannelType = C.CL_UNSIGNED_INT32
	HalfFloat      ChannelType = C.CL_HALF_FLOAT
	Float          ChannelType = C.CL_FLOAT
)

const (
	ImageFormatInfo  ImageInfo = C.CL_IMAGE_FORMAT // Appended "Info" due to conflict with type.
	ImageElementSize ImageInfo = C.CL_IMAGE_ELEMENT_SIZE
	ImageRowPitch    ImageInfo = C.CL_IMAGE_ROW_PITCH
	ImageSlicePitch  ImageInfo = C.CL_IMAGE_SLICE_PITCH
	ImageWidth       ImageInfo = C.CL_IMAGE_WIDTH
	ImageHeight      ImageInfo = C.CL_IMAGE_HEIGHT
	ImageDepth       ImageInfo = C.CL_IMAGE_DEPTH
)

// Create an image format descriptor.
func CreateImageFormat(co ChannelOrder, ct ChannelType) ImageFormat {
	return ImageFormat{C.cl_channel_order(co), C.cl_channel_type(ct)}
}

// Retruns the number of channels and the channel layout.
func (f *ImageFormat) ChannelOrder() ChannelOrder {
	return ChannelOrder(f.image_channel_order)
}

// Returns the size of the channel data type.
func (f *ImageFormat) ChannelType() ChannelType {
	return ChannelType(f.image_channel_data_type)
}

// Creates a 2D image object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateImage2D.html
func CreateImage2D(context Context, flags MemFlags, image_format ImageFormat, image_width, image_height,
	image_row_pitch Size, host_ptr unsafe.Pointer) (Mem, error) {

	var err C.cl_int
	mem := C.clCreateImage2D(context, C.cl_mem_flags(flags), (*C.cl_image_format)(&image_format), C.size_t(image_width),
		C.size_t(image_height), C.size_t(image_row_pitch), host_ptr, &err)

	return Mem(mem), toError(err)
}

// Creates a 3D image object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateImage3D.html
func CreateImage3D(context Context, flags MemFlags, image_format ImageFormat, image_width, image_height, image_depth,
	image_row_pitch, image_slice_pitch Size, host_ptr unsafe.Pointer) (Mem, error) {

	var err C.cl_int
	mem := C.clCreateImage3D(context, C.cl_mem_flags(flags), (*C.cl_image_format)(&image_format), C.size_t(image_width),
		C.size_t(image_height), C.size_t(image_depth), C.size_t(image_row_pitch), C.size_t(image_slice_pitch), host_ptr,
		&err)

	return Mem(mem), toError(err)
}

// Get information specific to an image object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clGetImageInfo.html
func GetImageInfo(image Mem, param_name ImageInfo, param_value_size Size, param_value unsafe.Pointer,
	param_value_size_ret *Size) error {

	return toError(C.clGetImageInfo(image, C.cl_image_info(param_name), C.size_t(param_value_size), param_value,
		(*C.size_t)(param_value_size_ret)))
}

// Get the list of image formats supported by an OpenCL implementation.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clGetSupportedImageFormats.html
func GetSupportedImageFormats(context Context, flags MemFlags, image_type MemObjectType, num_entries Uint,
	image_formats *ImageFormat, num_image_formats *Uint) error {

	return toError(C.clGetSupportedImageFormats(context, C.cl_mem_flags(flags), C.cl_mem_object_type(image_type),
		C.cl_uint(num_entries), (*C.cl_image_format)(image_formats), (*C.cl_uint)(num_image_formats)))
}
<commit_msg>Added remaining image functions to wrap.<commit_after>package clw11

/*
#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
#ifdef __APPLE__
#include "OpenCL/opencl.h"
#else
#include "CL/opencl.h"
#endif
*/
import "C"
import "unsafe"

type (
	MemObjectType C.cl_mem_object_type
	ImageFormat   C.cl_image_format
	ImageInfo     C.cl_image_info
	ChannelOrder  C.cl_channel_order
	ChannelType   C.cl_channel_type
)

const (
	MemObjectBuffer  MemObjectType = C.CL_MEM_OBJECT_BUFFER
	MemObjectImage2D MemObjectType = C.CL_MEM_OBJECT_IMAGE2D
	MemObjectImage3D MemObjectType = C.CL_MEM_OBJECT_IMAGE3D
)

const (
	R         ChannelOrder = C.CL_R
	A         ChannelOrder = C.CL_A
	RG        ChannelOrder = C.CL_RG
	RA        ChannelOrder = C.CL_RA
	RGB       ChannelOrder = C.CL_RGB
	RGBA      ChannelOrder = C.CL_RGBA
	BGRA      ChannelOrder = C.CL_BGRA
	ARGB      ChannelOrder = C.CL_ARGB
	Intensity ChannelOrder = C.CL_INTENSITY
	Luminance ChannelOrder = C.CL_LUMINANCE
	Rx        ChannelOrder = C.CL_Rx
	RGx       ChannelOrder = C.CL_RGx
	RGBx      ChannelOrder = C.CL_RGBx
)

const (
	SnormInt8      ChannelType = C.CL_SNORM_INT8
	SnormInt16     ChannelType = C.CL_SNORM_INT16
	UnormInt8      ChannelType = C.CL_UNORM_INT8
	UnormInt16     ChannelType = C.CL_UNORM_INT16
	UnormShort565  ChannelType = C.CL_UNORM_SHORT_565
	UnormShort555  ChannelType = C.CL_UNORM_SHORT_555
	UnormInt101010 ChannelType = C.CL_UNORM_INT_101010
	SignedInt8     ChannelType = C.CL_SIGNED_INT8
	SignedInt16    ChannelType = C.CL_SIGNED_INT16
	SignedInt32    ChannelType = C.CL_SIGNED_INT32
	UnsignedInt8   ChannelType = C.CL_UNSIGNED_INT8
	UnsignedInt16  ChannelType = C.CL_UNSIGNED_INT16
	UnsignedInt32  ChannelType = C.CL_UNSIGNED_INT32
	HalfFloat      ChannelType = C.CL_HALF_FLOAT
	Float          ChannelType = C.CL_FLOAT
)

const (
	ImageFormatInfo  ImageInfo = C.CL_IMAGE_FORMAT // Appended "Info" due to conflict with type.
	ImageElementSize ImageInfo = C.CL_IMAGE_ELEMENT_SIZE
	ImageRowPitch    ImageInfo = C.CL_IMAGE_ROW_PITCH
	ImageSlicePitch  ImageInfo = C.CL_IMAGE_SLICE_PITCH
	ImageWidth       ImageInfo = C.CL_IMAGE_WIDTH
	ImageHeight      ImageInfo = C.CL_IMAGE_HEIGHT
	ImageDepth       ImageInfo = C.CL_IMAGE_DEPTH
)

// Create an image format descriptor.
func CreateImageFormat(co ChannelOrder, ct ChannelType) ImageFormat {
	return ImageFormat{C.cl_channel_order(co), C.cl_channel_type(ct)}
}

// Retruns the number of channels and the channel layout.
func (f *ImageFormat) ChannelOrder() ChannelOrder {
	return ChannelOrder(f.image_channel_order)
}

// Returns the size of the channel data type.
func (f *ImageFormat) ChannelType() ChannelType {
	return ChannelType(f.image_channel_data_type)
}

// Creates a 2D image object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateImage2D.html
func CreateImage2D(context Context, flags MemFlags, image_format ImageFormat, image_width, image_height,
	image_row_pitch Size, host_ptr unsafe.Pointer) (Mem, error) {

	var err C.cl_int
	mem := C.clCreateImage2D(context, C.cl_mem_flags(flags), (*C.cl_image_format)(&image_format), C.size_t(image_width),
		C.size_t(image_height), C.size_t(image_row_pitch), host_ptr, &err)

	return Mem(mem), toError(err)
}

// Creates a 3D image object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateImage3D.html
func CreateImage3D(context Context, flags MemFlags, image_format ImageFormat, image_width, image_height, image_depth,
	image_row_pitch, image_slice_pitch Size, host_ptr unsafe.Pointer) (Mem, error) {

	var err C.cl_int
	mem := C.clCreateImage3D(context, C.cl_mem_flags(flags), (*C.cl_image_format)(&image_format), C.size_t(image_width),
		C.size_t(image_height), C.size_t(image_depth), C.size_t(image_row_pitch), C.size_t(image_slice_pitch), host_ptr,
		&err)

	return Mem(mem), toError(err)
}

// Get the list of image formats supported by an OpenCL implementation.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clGetSupportedImageFormats.html
func GetSupportedImageFormats(context Context, flags MemFlags, image_type MemObjectType, num_entries Uint,
	image_formats *ImageFormat, num_image_formats *Uint) error {

	return toError(C.clGetSupportedImageFormats(context, C.cl_mem_flags(flags), C.cl_mem_object_type(image_type),
		C.cl_uint(num_entries), (*C.cl_image_format)(image_formats), (*C.cl_uint)(num_image_formats)))
}

func EnqueueReadImage() {

}

func EnqueueWriteImage() {

}

func EnqueueCopyImage() {

}

func EnqueueCopyImageToBuffer() {

}

func EnqueueCopyBufferToImage() {

}

func EnqueueMapImage() {

}

// Get information specific to an image object.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clGetImageInfo.html
func GetImageInfo(image Mem, param_name ImageInfo, param_value_size Size, param_value unsafe.Pointer,
	param_value_size_ret *Size) error {

	return toError(C.clGetImageInfo(image, C.cl_image_info(param_name), C.size_t(param_value_size), param_value,
		(*C.size_t)(param_value_size_ret)))
}
<|endoftext|>


<commit_before>package influxdb

import (
	"regexp"
	"sort"
	"strings"
	"sync"
)

// Index is the in memory index structure for answering queries about measurements, tags
// and series within a database.
type Index struct {
	mu                  sync.RWMutex
	measurementIndex    map[string]*measurementIndex // map measurement name to its tag index
	seriesToMeasurement map[uint32]*Measurement      // map series id to its measurement
	series              map[uint32]*Series           // map series id to the Series object
}

func NewIndex() *Index {
	return &Index{
		measurementIndex:    make(map[string]*measurementIndex),
		seriesToMeasurement: make(map[uint32]*Measurement),
		series:              make(map[uint32]*Series),
	}
}

// Filter represents a tag filter when looking up other tags or measurements.
type Filter struct {
	Not   bool
	Key   string
	Value string
	Regex regexp.Regexp
}

type Filters []*Filter

func (f Filters) String() string {
	return string(mustMarshalJSON(f))
}

// SeriesIDs is a convenience type for sorting, checking equality, and doing union and
// intersection of collections of series ids.
type SeriesIDs []uint32

func (p SeriesIDs) Len() int           { return len(p) }
func (p SeriesIDs) Less(i, j int) bool { return p[i] < p[j] }
func (p SeriesIDs) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p SeriesIDs) Sort()              { sort.Sort(p) }

// Equals assumes that both are sorted. This is by design, no touchy!
func (p SeriesIDs) Equals(s SeriesIDs) bool {
	if len(p) != len(s) {
		return false
	}
	for i, pp := range p {
		if s[i] != pp {
			return false
		}
	}
	return true
}

// Intersect returns a new collection of series ids in sorted order that is the intersection of the two.
// The two collections must already be sorted.
func (s SeriesIDs) Intersect(a SeriesIDs) SeriesIDs {
	l := s
	r := a

	// we want to iterate through the shortest one and stop
	if len(a) < len(s) {
		l = a
		r = s
	}

	// they're in sorted order so advance the counter as needed.
	// That is, don't run comparisons against lower values that we've already passed
	var i, j int

	ids := make([]uint32, 0, len(l))
	for i < len(l) {
		if l[i] == r[j] {
			ids = append(ids, l[i])
			i += 1
			j += 1
		} else if l[i] < r[j] {
			i += 1
		} else {
			j += 1
		}
	}

	return SeriesIDs(ids)
}

// Union returns a new collection of series ids in sorted order that is the union of the two.
// The two collections must already be sorted.
func (s SeriesIDs) Union(a SeriesIDs) SeriesIDs {
	l := s
	r := a

	ids := make([]uint32, 0, len(l)+len(r))
	var i, j int
	for i < len(l) && j < len(r) {
		if l[i] == r[j] {
			ids = append(ids, l[i])
			i += 1
			j += 1
		} else if l[i] < r[j] {
			ids = append(ids, l[i])
			i += 1
		} else {
			ids = append(ids, r[j])
			j += 1
		}
	}

	// now append the remainder
	if i < len(l) {
		ids = append(ids, l[i:]...)
	} else if j < len(r) {
		ids = append(ids, r[j:]...)
	}

	return ids
}

// Reject returns a new collection of series ids in sorted order with the passed in set removed from the original. This is useful for the NOT operator.
// The two collections must already be sorted.
func (l SeriesIDs) Reject(r SeriesIDs) SeriesIDs {
	var i, j int

	ids := make([]uint32, 0, len(l))
	for i < len(l) && j < len(r) {
		if l[i] == r[j] {
			i += 1
			j += 1
		} else if l[i] < r[j] {
			ids = append(ids, l[i])
			i += 1
		} else {
			j += 1
		}
	}

	// append the remainder
	if i < len(l) {
		ids = append(ids, l[i:]...)
	}

	return SeriesIDs(ids)
}

// Convenience method to output something during tests
func (s SeriesIDs) String() string {
	return string(mustMarshalJSON(s))
}

// Keeps a mapping of the series in a measurement
type measurementIndex struct {
	series       map[string]*Series // sorted tag string to the series object
	measurement  *Measurement
	tagsToSeries map[string]map[string]SeriesIDs // map from tag key to value to sorted set of series ids
	ids          SeriesIDs                       // sorted list of series IDs in this measurement
}

// addSeries will add a series to the measurementIndex. Returns false if already present
func (m measurementIndex) addSeries(s *Series) bool {
	id := string(tagsToBytes(s.Tags))
	if _, ok := m.series[id]; ok {
		warn("already added: ", s)
		return false
	}
	m.series[id] = s
	m.ids = append(m.ids, s.ID)
	warn("add: ", m.ids)
	// the series ID should always be higher than all others because it's a new
	// series. So don't do the sort if we don't have to.
	if len(m.ids) > 1 && m.ids[len(m.ids)-1] < m.ids[len(m.ids)-2] {
		warn("sort")
		m.ids.Sort()
	}

	// add this series id to the tag index on the measurement
	for k, v := range s.Tags {
		valueMap := m.tagsToSeries[k]
		if valueMap == nil {
			valueMap = make(map[string]SeriesIDs)
			m.tagsToSeries[k] = valueMap
		}
		ids := valueMap[v]
		if ids == nil {
			ids = make([]uint32, 0)
		}
		ids = append(ids, s.ID)

		// most of the time the series ID will be higher than all others because it's a new
		// series. So don't do the sort if we don't have to.
		if len(ids) > 1 && ids[len(ids)-1] < ids[len(ids)-2] {
			ids.Sort()
		}
		valueMap[v] = ids
	}

	warn("end of add: ", m.ids)
	return true
}

// seriesByTags returns the Series that matches the given tagset.
func (m measurementIndex) seriesByTags(tags map[string]string) *Series {
	return m.series[string(tagsToBytes(tags))]
}

// sereisIDs returns the series ids for a given filter
func (m measurementIndex) seriesIDs(filter *Filter) (ids SeriesIDs) {
	values := m.tagsToSeries[filter.Key]
	if values != nil {
		ids = SeriesIDs(values[filter.Value])
		warn(ids)
		if filter.Not {
			warn("IDS:", m.ids)
			ids = m.ids.Reject(ids)
			warn("NOT:", ids)
		}
	}
	return
}

// AddSeries adds the series for the given measurement to the index. Returns false if already present
func (t *Index) AddSeries(name string, s *Series) bool {
	t.mu.Lock()
	defer t.mu.Unlock()

	// if there is a measurement for this id, it's already been added
	if t.seriesToMeasurement[s.ID] != nil {
		return false
	}

	// get or create the measurement index
	idx := t.measurementIndex[name]
	if idx == nil {
		warn("NEW: ", name)
		idx = &measurementIndex{
			series:       make(map[string]*Series),
			measurement:  NewMeasurement(name),
			tagsToSeries: make(map[string]map[string]SeriesIDs),
			ids:          SeriesIDs(make([]uint32, 0)),
		}
		t.measurementIndex[name] = idx
	}
	idx.measurement.Series = append(idx.measurement.Series, s)
	t.seriesToMeasurement[s.ID] = idx.measurement
	t.series[s.ID] = s

	// TODO: add this series to the global tag index

	warn("begin addSeries")
	b := idx.addSeries(s)
	warn("AddSeries: ", idx.ids)
	return b
}

// AddField adds a field to the measurement name. Returns false if already present
func (t *Index) AddField(name string, f *Field) bool {
	panic("not implemented")
	return false
}

// SeriesIDs returns an array of series ids for the given measurements and filters to be applied to all.
// Filters are equivalent to and AND operation. If you want to do an OR, get the series IDs for one set,
// then get the series IDs for another set and use the SeriesIDs.Union to combine the two.
func (t *Index) SeriesIDs(names []string, filters Filters) SeriesIDs {
	t.mu.RLock()
	defer t.mu.RUnlock()

	// they want all ids if no filters are specified
	if len(filters) == 0 {
		ids := SeriesIDs(make([]uint32, 0))
		for _, idx := range t.measurementIndex {
			for _, s := range idx.series {
				ids = append(ids, s.ID)
			}
		}
		ids.Sort()
		return ids
	}

	ids := SeriesIDs(make([]uint32, 0))
	for _, n := range names {
		ids = append(ids, t.seriesIDsForName(n, filters)...)
	}

	// if it's just one name, they'll already be sorted so don't waste the time
	if len(names) > 1 {
		ids.Sort()
	}

	return ids
}

//seriesIDsForName is the same as SeriesIDs, but for a specific measurement.
func (t *Index) seriesIDsForName(name string, filters Filters) SeriesIDs {
	idx := t.measurementIndex[name]
	if idx == nil {
		return nil
	}

	// process the filters one at a time to get the list of ids they return
	idsPerFilter := make([]SeriesIDs, len(filters), len(filters))
	for i, filter := range filters {
		idsPerFilter[i] = idx.seriesIDs(filter)
	}

	// collapse the set of ids
	allIDs := idsPerFilter[0]
	for i := 1; i < len(filters); i++ {
		allIDs = allIDs.Intersect(idsPerFilter[i])
	}

	return allIDs
}

// MeasurementBySeriesID returns the Measurement that is the parent of the given series id.
func (t *Index) MeasurementBySeriesID(id uint32) *Measurement {
	t.mu.RLock()
	defer t.mu.RUnlock()
	return t.seriesToMeasurement[id]
}

// MeasurementAndSeries returns the Measurement and the Series for a given measurement name and tag set.
func (t *Index) MeasurementAndSeries(name string, tags map[string]string) (*Measurement, *Series) {
	t.mu.RLock()
	defer t.mu.RUnlock()
	idx := t.measurementIndex[name]
	if idx == nil {
		return nil, nil
	}
	return idx.measurement, idx.seriesByTags(tags)
}

// SereiesByID returns the Series that has the given id.
func (t *Index) SeriesByID(id uint32) *Series {
	return t.series[id]
}

// Measurements returns all measurements that match the given filters.
func (t *Index) Measurements(filters []*Filter) []*Measurement {
	measurements := make([]*Measurement, 0, len(t.measurementIndex))
	for _, idx := range t.measurementIndex {
		measurements = append(measurements, idx.measurement)
	}
	return measurements
}

// DropSeries will clear the index of all references to a series.
func (t *Index) DropSeries(id uint32) {
	panic("not implemented")
}

// DropMeasurement will clear the index of all references to a measurement and its child series.
func (t *Index) DropMeasurement(name string) {
	panic("not implemented")
}

// used to convert the tag set to bytes for use as a lookup key
func tagsToBytes(tags map[string]string) []byte {
	s := make([]string, 0, len(tags))
	// pull out keys to sort
	for k := range tags {
		s = append(s, k)
	}
	sort.Strings(s)

	// now append on the key values in key sorted order
	for _, k := range s {
		s = append(s, tags[k])
	}
	return []byte(strings.Join(s, "|"))
}
<commit_msg>Fix stupid bug on measurementIndex.addSeries. Thanks @benbjohnson!<commit_after>package influxdb

import (
	"regexp"
	"sort"
	"strings"
	"sync"
)

// Index is the in memory index structure for answering queries about measurements, tags
// and series within a database.
type Index struct {
	mu                  sync.RWMutex
	measurementIndex    map[string]*measurementIndex // map measurement name to its tag index
	seriesToMeasurement map[uint32]*Measurement      // map series id to its measurement
	series              map[uint32]*Series           // map series id to the Series object
}

func NewIndex() *Index {
	return &Index{
		measurementIndex:    make(map[string]*measurementIndex),
		seriesToMeasurement: make(map[uint32]*Measurement),
		series:              make(map[uint32]*Series),
	}
}

// Filter represents a tag filter when looking up other tags or measurements.
type Filter struct {
	Not   bool
	Key   string
	Value string
	Regex regexp.Regexp
}

type Filters []*Filter

func (f Filters) String() string {
	return string(mustMarshalJSON(f))
}

// SeriesIDs is a convenience type for sorting, checking equality, and doing union and
// intersection of collections of series ids.
type SeriesIDs []uint32

func (p SeriesIDs) Len() int           { return len(p) }
func (p SeriesIDs) Less(i, j int) bool { return p[i] < p[j] }
func (p SeriesIDs) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p SeriesIDs) Sort()              { sort.Sort(p) }

// Equals assumes that both are sorted. This is by design, no touchy!
func (p SeriesIDs) Equals(s SeriesIDs) bool {
	if len(p) != len(s) {
		return false
	}
	for i, pp := range p {
		if s[i] != pp {
			return false
		}
	}
	return true
}

// Intersect returns a new collection of series ids in sorted order that is the intersection of the two.
// The two collections must already be sorted.
func (s SeriesIDs) Intersect(a SeriesIDs) SeriesIDs {
	l := s
	r := a

	// we want to iterate through the shortest one and stop
	if len(a) < len(s) {
		l = a
		r = s
	}

	// they're in sorted order so advance the counter as needed.
	// That is, don't run comparisons against lower values that we've already passed
	var i, j int

	ids := make([]uint32, 0, len(l))
	for i < len(l) {
		if l[i] == r[j] {
			ids = append(ids, l[i])
			i += 1
			j += 1
		} else if l[i] < r[j] {
			i += 1
		} else {
			j += 1
		}
	}

	return SeriesIDs(ids)
}

// Union returns a new collection of series ids in sorted order that is the union of the two.
// The two collections must already be sorted.
func (s SeriesIDs) Union(a SeriesIDs) SeriesIDs {
	l := s
	r := a

	ids := make([]uint32, 0, len(l)+len(r))
	var i, j int
	for i < len(l) && j < len(r) {
		if l[i] == r[j] {
			ids = append(ids, l[i])
			i += 1
			j += 1
		} else if l[i] < r[j] {
			ids = append(ids, l[i])
			i += 1
		} else {
			ids = append(ids, r[j])
			j += 1
		}
	}

	// now append the remainder
	if i < len(l) {
		ids = append(ids, l[i:]...)
	} else if j < len(r) {
		ids = append(ids, r[j:]...)
	}

	return ids
}

// Reject returns a new collection of series ids in sorted order with the passed in set removed from the original. This is useful for the NOT operator.
// The two collections must already be sorted.
func (l SeriesIDs) Reject(r SeriesIDs) SeriesIDs {
	var i, j int

	ids := make([]uint32, 0, len(l))
	for i < len(l) && j < len(r) {
		if l[i] == r[j] {
			i += 1
			j += 1
		} else if l[i] < r[j] {
			ids = append(ids, l[i])
			i += 1
		} else {
			j += 1
		}
	}

	// append the remainder
	if i < len(l) {
		ids = append(ids, l[i:]...)
	}

	return SeriesIDs(ids)
}

// Convenience method to output something during tests
func (s SeriesIDs) String() string {
	return string(mustMarshalJSON(s))
}

// Keeps a mapping of the series in a measurement
type measurementIndex struct {
	series       map[string]*Series // sorted tag string to the series object
	measurement  *Measurement
	tagsToSeries map[string]map[string]SeriesIDs // map from tag key to value to sorted set of series ids
	ids          SeriesIDs                       // sorted list of series IDs in this measurement
}

// addSeries will add a series to the measurementIndex. Returns false if already present
func (m *measurementIndex) addSeries(s *Series) bool {
	id := string(tagsToBytes(s.Tags))
	if _, ok := m.series[id]; ok {
		return false
	}
	m.series[id] = s
	m.ids = append(m.ids, s.ID)
	// the series ID should always be higher than all others because it's a new
	// series. So don't do the sort if we don't have to.
	if len(m.ids) > 1 && m.ids[len(m.ids)-1] < m.ids[len(m.ids)-2] {
		m.ids.Sort()
	}

	// add this series id to the tag index on the measurement
	for k, v := range s.Tags {
		valueMap := m.tagsToSeries[k]
		if valueMap == nil {
			valueMap = make(map[string]SeriesIDs)
			m.tagsToSeries[k] = valueMap
		}
		ids := valueMap[v]
		if ids == nil {
			ids = make([]uint32, 0)
		}
		ids = append(ids, s.ID)

		// most of the time the series ID will be higher than all others because it's a new
		// series. So don't do the sort if we don't have to.
		if len(ids) > 1 && ids[len(ids)-1] < ids[len(ids)-2] {
			ids.Sort()
		}
		valueMap[v] = ids
	}

	return true
}

// seriesByTags returns the Series that matches the given tagset.
func (m measurementIndex) seriesByTags(tags map[string]string) *Series {
	return m.series[string(tagsToBytes(tags))]
}

// sereisIDs returns the series ids for a given filter
func (m measurementIndex) seriesIDs(filter *Filter) (ids SeriesIDs) {
	values := m.tagsToSeries[filter.Key]
	if values != nil {
		ids = SeriesIDs(values[filter.Value])
		if filter.Not {
			ids = m.ids.Reject(ids)
		}
	}
	return
}

// AddSeries adds the series for the given measurement to the index. Returns false if already present
func (t *Index) AddSeries(name string, s *Series) bool {
	t.mu.Lock()
	defer t.mu.Unlock()

	// if there is a measurement for this id, it's already been added
	if t.seriesToMeasurement[s.ID] != nil {
		return false
	}

	// get or create the measurement index
	idx := t.measurementIndex[name]
	if idx == nil {
		idx = &measurementIndex{
			series:       make(map[string]*Series),
			measurement:  NewMeasurement(name),
			tagsToSeries: make(map[string]map[string]SeriesIDs),
			ids:          SeriesIDs(make([]uint32, 0)),
		}
		t.measurementIndex[name] = idx
	}
	idx.measurement.Series = append(idx.measurement.Series, s)
	t.seriesToMeasurement[s.ID] = idx.measurement
	t.series[s.ID] = s

	// TODO: add this series to the global tag index

	b := idx.addSeries(s)
	return b
}

// AddField adds a field to the measurement name. Returns false if already present
func (t *Index) AddField(name string, f *Field) bool {
	panic("not implemented")
	return false
}

// SeriesIDs returns an array of series ids for the given measurements and filters to be applied to all.
// Filters are equivalent to and AND operation. If you want to do an OR, get the series IDs for one set,
// then get the series IDs for another set and use the SeriesIDs.Union to combine the two.
func (t *Index) SeriesIDs(names []string, filters Filters) SeriesIDs {
	t.mu.RLock()
	defer t.mu.RUnlock()

	// they want all ids if no filters are specified
	if len(filters) == 0 {
		ids := SeriesIDs(make([]uint32, 0))
		for _, idx := range t.measurementIndex {
			ids = append(ids, idx.ids...)
		}
		ids.Sort()
		return ids
	}

	ids := SeriesIDs(make([]uint32, 0))
	for _, n := range names {
		ids = append(ids, t.seriesIDsForName(n, filters)...)
	}

	// if it's just one name, they'll already be sorted so don't waste the time
	if len(names) > 1 {
		ids.Sort()
	}

	return ids
}

//seriesIDsForName is the same as SeriesIDs, but for a specific measurement.
func (t *Index) seriesIDsForName(name string, filters Filters) SeriesIDs {
	idx := t.measurementIndex[name]
	if idx == nil {
		return nil
	}

	// process the filters one at a time to get the list of ids they return
	idsPerFilter := make([]SeriesIDs, len(filters), len(filters))
	for i, filter := range filters {
		idsPerFilter[i] = idx.seriesIDs(filter)
	}

	// collapse the set of ids
	allIDs := idsPerFilter[0]
	for i := 1; i < len(filters); i++ {
		allIDs = allIDs.Intersect(idsPerFilter[i])
	}

	return allIDs
}

// MeasurementBySeriesID returns the Measurement that is the parent of the given series id.
func (t *Index) MeasurementBySeriesID(id uint32) *Measurement {
	t.mu.RLock()
	defer t.mu.RUnlock()
	return t.seriesToMeasurement[id]
}

// MeasurementAndSeries returns the Measurement and the Series for a given measurement name and tag set.
func (t *Index) MeasurementAndSeries(name string, tags map[string]string) (*Measurement, *Series) {
	t.mu.RLock()
	defer t.mu.RUnlock()
	idx := t.measurementIndex[name]
	if idx == nil {
		return nil, nil
	}
	return idx.measurement, idx.seriesByTags(tags)
}

// SereiesByID returns the Series that has the given id.
func (t *Index) SeriesByID(id uint32) *Series {
	return t.series[id]
}

// Measurements returns all measurements that match the given filters.
func (t *Index) Measurements(filters []*Filter) []*Measurement {
	measurements := make([]*Measurement, 0, len(t.measurementIndex))
	for _, idx := range t.measurementIndex {
		measurements = append(measurements, idx.measurement)
	}
	return measurements
}

// DropSeries will clear the index of all references to a series.
func (t *Index) DropSeries(id uint32) {
	panic("not implemented")
}

// DropMeasurement will clear the index of all references to a measurement and its child series.
func (t *Index) DropMeasurement(name string) {
	panic("not implemented")
}

// used to convert the tag set to bytes for use as a lookup key
func tagsToBytes(tags map[string]string) []byte {
	s := make([]string, 0, len(tags))
	// pull out keys to sort
	for k := range tags {
		s = append(s, k)
	}
	sort.Strings(s)

	// now append on the key values in key sorted order
	for _, k := range s {
		s = append(s, tags[k])
	}
	return []byte(strings.Join(s, "|"))
}
<|endoftext|>


<commit_before>package client

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/armon/mdns"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
)

var log = logger.GetLogger("Client")

type client struct {
}

func Start() {

	log.Infof("Starting client on Node: %s", config.Serial())
	client := &client{}

	if !client.isPaired() {
		log.Infof("Client is unpaired. Attempting to pair.")
		if err := client.pair(); err != nil {
			log.Infof("An error occurred while pairing. Restarting. error: %s", err)
			os.Exit(1)
		}

		log.Infof("Pairing was successful.")
		// We reload the config so the creds can be picked up
		config.MustRefresh()

		if !client.isPaired() {
			log.Infof("Pairing appeared successful, but we did not get the credentials. Restarting.")
			os.Exit(1)
		}
	}

	log.Infof("Client is paired. Site: %s User: %s", config.MustString("siteId"), config.MustString("userId"))

	masterId := config.String(config.Serial(), "masterNodeId")

	if masterId == config.Serial() {
		log.Infof("We are the master")
	} else {
		log.Infof("We are a slave. The master is %s", masterId)
	}

	// Make a channel for results and start listening
	entriesCh := make(chan *mdns.ServiceEntry, 4)
	go func() {
		for entry := range entriesCh {

			info := parseMdnsInfo(entry.Info)

			id, ok := info["ninja.sphere.node_id"]

			if !ok {
				log.Warningf("Found a node, but couldn't get it's node id. %v", entry)
				continue
			}

			if id == masterId {
				log.Infof("Found the master node (%s) - %s", id, entry.Addr)
			} else {

				user, ok := info["ninja.sphere.user_id"]

				if !ok {
					log.Warningf("Found a node, but couldn't get it's user id. %v", entry)
					continue
				}

				if user == config.MustString("userId") {
					log.Infof("Found a sibling node (%s) - %s", id, entry.Addr)
				} else {
					log.Infof("Found a node owned by another user (%s) (%s) - %s", user, id, entry.Addr)
				}

			}
		}
	}()

	// Start the lookup
	mdns.Lookup("_ninja-homecloud-mqtt._tcp", entriesCh)
	close(entriesCh)

}

func (c *client) isPaired() bool {
	return config.HasString("siteId") && config.HasString("token") && config.HasString("userId") && config.HasString("nodeId")
}

func (c *client) pair() error {

	localIP, err := ninja.GetNetAddress()

	if err != nil {
		log.Fatalf("Could not find local IP: %s", err)
	}

	var boardType string
	if config.HasString("boardType") {
		boardType = config.MustString("boardType")
	} else {
		boardType = fmt.Sprintf("custom-%s-%s", runtime.GOOS, runtime.GOARCH)
	}

	log.Debugf("Board type: %s", boardType)
	log.Debugf("Local IP: %s", localIP)

	url := fmt.Sprintf(config.MustString("cloud", "activation"), config.Serial(), localIP, boardType)

	log.Infof("Activating at URL: %s", url)

	client := &http.Client{}

	if config.Bool(false, "cloud", "allowSelfSigned") {
		log.Warningf("Allowing self-signed cerificate (should only be used to connect to development cloud)")
		client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}

	var creds *credentials

	for {
		creds, err = activate(client, url)

		if err != nil {
			log.Warningf("Activation error : %s", err)
			log.Warningf("Sleeping for 10sec")
			time.Sleep(time.Second * 10)
		} else if creds != nil {
			break
		}
	}

	log.Infof("Got credentials. Joining site: %s user: %s", creds.SiteID, creds.UserID)

	if creds.MasterNodeID == "" {
		log.Warningf("Cloud did not give us the master node id. So setting it to ourself (%s)", config.Serial())
		creds.MasterNodeID = config.Serial()
	}

	credsFile := config.String("/etc/opt/ninja/credentials.json", "credentialFile")

	log.Infof("Saving credentials to %s", credsFile)

	credsJSON, err := json.Marshal(creds)
	if err != nil {
		return fmt.Errorf("Failed to marshal credentials: %s", err)
	}

	return ioutil.WriteFile(credsFile, credsJSON, 0700)
}

type nodeClaimResponse struct {
	Type string              `json:"type"`
	Data responseCredentials `json:"data"`
}

type responseCredentials struct {
	UserID       string `json:"user_id"`
	SiteID       string `json:"site_id"`
	NodeID       string `json:"node_id"`
	Token        string `json:"token"`
	MasterNodeID string `json:"master_node_id"`
}

type credentials struct {
	UserID       string `json:"userId"`
	SiteID       string `json:"siteId"`
	NodeID       string `json:"nodeId"`
	Token        string `json:"token"`
	MasterNodeID string `json:"masterNodeId"`
}

func activate(client *http.Client, url string) (*credentials, error) {

	log.Debugf("Requesting url: %s", url)

	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode == http.StatusRequestTimeout {
		return nil, nil
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Failed to activate: %s - %s", resp.Status, body)
	}

	log.Debugf("Got response: %s", body)

	var response nodeClaimResponse
	err = json.Unmarshal(body, &response)

	return &credentials{
		UserID:       response.Data.UserID,
		SiteID:       response.Data.SiteID,
		NodeID:       response.Data.NodeID,
		Token:        response.Data.Token,
		MasterNodeID: response.Data.MasterNodeID,
	}, err
}

func parseMdnsInfo(field string) map[string]string {
	vals := make(map[string]string)

	for _, part := range strings.Split(field, "|") {
		chunks := strings.Split(part, "=")
		vals[chunks[0]] = chunks[1]
	}
	return vals
}
<commit_msg>Set up the bridge when we're the master, update the lights on dev kits<commit_after>package client

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/armon/mdns"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
)

var log = logger.GetLogger("Client")

type client struct {
	conn   *ninja.Connection
	master bool
}

type bridgeStatus struct {
	Connected  bool `json:"connected"`
	Configured bool `json:"connected"`
}

func Start() {
	log.Infof("Starting client on Node: %s", config.Serial())

	conn, err := ninja.Connect("client")
	if err != nil {
		log.Fatalf("Failed to connect to sphere: %s", err)
	}

	client := &client{conn, false}

	client.updatePairingLight("black", false)

	conn.Subscribe("$sphere/bridge/status", client.onBridgeStatus)

	client.start()
}

func (c *client) start() {

	if !c.isPaired() {
		log.Infof("Client is unpaired. Attempting to pair.")
		if err := c.pair(); err != nil {
			log.Infof("An error occurred while pairing. Restarting. error: %s", err)
			os.Exit(1)
		}

		log.Infof("Pairing was successful.")
		// We reload the config so the creds can be picked up
		config.MustRefresh()

		if !c.isPaired() {
			log.Infof("Pairing appeared successful, but we did not get the credentials. Restarting.")
			os.Exit(1)
		}
	}

	log.Infof("Client is paired. Site: %s User: %s", config.MustString("siteId"), config.MustString("userId"))

	masterID := config.String(config.Serial(), "masterNodeId")

	if masterID == config.Serial() {
		log.Infof("We are the master.")

	} else {
		log.Infof("We are a slave. The master is %s", masterID)
	}

	// Make a channel for results and start listening
	entriesCh := make(chan *mdns.ServiceEntry, 4)
	go func() {
		for entry := range entriesCh {

			info := parseMdnsInfo(entry.Info)

			id, ok := info["ninja.sphere.node_id"]

			if !ok {
				log.Warningf("Found a node, but couldn't get it's node id. %v", entry)
				continue
			}

			if id == masterID {
				log.Infof("Found the master node (%s) - %s", id, entry.Addr)
			} else {

				user, ok := info["ninja.sphere.user_id"]

				if !ok {
					log.Warningf("Found a node, but couldn't get it's user id. %v", entry)
					continue
				}

				if user == config.MustString("userId") {
					log.Infof("Found a sibling node (%s) - %s", id, entry.Addr)
				} else {
					log.Infof("Found a node owned by another user (%s) (%s) - %s", user, id, entry.Addr)
				}

			}
		}
	}()

	// Start the lookup
	mdns.Lookup("_ninja-homecloud-mqtt._tcp", entriesCh)
	close(entriesCh)

}

func (c *client) isPaired() bool {
	return config.HasString("siteId") && config.HasString("token") && config.HasString("userId") && config.HasString("nodeId")
}

func (c *client) onBridgeStatus(status *bridgeStatus) bool {
	log.Debugf("Got bridge status: %v", status)

	if status.Connected {
		c.updatePairingLight("green", false)
	} else {
		c.updatePairingLight("red", true)
	}

	if !status.Configured && c.master {
		log.Infof("Configuring bridge")

		c.conn.SendNotification("$sphere/bridge/connect", map[string]string{
			"url":   config.MustString("cloud", "url"),
			"token": config.MustString("token"),
		})
	}

	return true
}

func (c *client) updatePairingLight(color string, flash bool) {
	c.conn.SendNotification("$hardware/status/pairing", map[string]interface{}{
		"color": color,
		"flash": flash,
	})
}

func (c *client) pair() error {

	localIP, err := ninja.GetNetAddress()

	if err != nil {
		log.Fatalf("Could not find local IP: %s", err)
	}

	var boardType string
	if config.HasString("boardType") {
		boardType = config.MustString("boardType")
	} else {
		boardType = fmt.Sprintf("custom-%s-%s", runtime.GOOS, runtime.GOARCH)
	}

	log.Debugf("Board type: %s", boardType)
	log.Debugf("Local IP: %s", localIP)

	url := fmt.Sprintf(config.MustString("cloud", "activation"), config.Serial(), localIP, boardType)

	log.Infof("Activating at URL: %s", url)

	client := &http.Client{}

	if config.Bool(false, "cloud", "allowSelfSigned") {
		log.Warningf("Allowing self-signed cerificate (should only be used to connect to development cloud)")
		client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}

	var creds *credentials

	for {
		creds, err = activate(client, url)

		if err != nil {
			log.Warningf("Activation error : %s", err)
			log.Warningf("Sleeping for 10sec")
			time.Sleep(time.Second * 10)
		} else if creds != nil {
			break
		}
	}

	log.Infof("Got credentials. Joining site: %s user: %s", creds.SiteID, creds.UserID)

	if creds.MasterNodeID == "" {
		log.Warningf("Cloud did not give us the master node id. So setting it to ourself (%s)", config.Serial())
		creds.MasterNodeID = config.Serial()
	}

	credsFile := config.String("/etc/opt/ninja/credentials.json", "credentialFile")

	log.Infof("Saving credentials to %s", credsFile)

	credsJSON, err := json.Marshal(creds)
	if err != nil {
		return fmt.Errorf("Failed to marshal credentials: %s", err)
	}

	return ioutil.WriteFile(credsFile, credsJSON, 0700)
}

type nodeClaimResponse struct {
	Type string              `json:"type"`
	Data responseCredentials `json:"data"`
}

type responseCredentials struct {
	UserID       string `json:"user_id"`
	SiteID       string `json:"site_id"`
	NodeID       string `json:"node_id"`
	Token        string `json:"token"`
	MasterNodeID string `json:"master_node_id"`
}

type credentials struct {
	UserID       string `json:"userId"`
	SiteID       string `json:"siteId"`
	NodeID       string `json:"nodeId"`
	Token        string `json:"token"`
	MasterNodeID string `json:"masterNodeId"`
}

func activate(client *http.Client, url string) (*credentials, error) {

	log.Debugf("Requesting url: %s", url)

	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode == http.StatusRequestTimeout {
		return nil, nil
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Failed to activate: %s - %s", resp.Status, body)
	}

	log.Debugf("Got response: %s", body)

	var response nodeClaimResponse
	err = json.Unmarshal(body, &response)

	return &credentials{
		UserID:       response.Data.UserID,
		SiteID:       response.Data.SiteID,
		NodeID:       response.Data.NodeID,
		Token:        response.Data.Token,
		MasterNodeID: response.Data.MasterNodeID,
	}, err
}

func parseMdnsInfo(field string) map[string]string {
	vals := make(map[string]string)

	for _, part := range strings.Split(field, "|") {
		chunks := strings.Split(part, "=")
		vals[chunks[0]] = chunks[1]
	}
	return vals
}
<|endoftext|>


<commit_before>package client

import (
	"crypto/tls"
	"net"
	"net/http"
	"net/url"
	"time"

	"github.com/libopenstorage/openstorage/config"
	"github.com/libopenstorage/openstorage/volume"
)

type Client struct {
	base       *url.URL
	version    string
	httpClient *http.Client
}

func (c *Client) VolumeDriver() volume.VolumeDriver {
	return newVolumeClient(c)
}

func (c *Client) Status() (*Status, error) {
	var status Status
	err := c.Get().UsePath("/status").Do().Unmarshal(&status)
	return &status, err
}

func (c *Client) Get() *Request {
	return NewRequest(c.httpClient, c.base, "GET", c.version)
}

func (c *Client) Post() *Request {
	return NewRequest(c.httpClient, c.base, "POST", c.version)
}

func (c *Client) Put() *Request {
	return NewRequest(c.httpClient, c.base, "PUT", c.version)
}

func (c *Client) Delete() *Request {
	return NewRequest(c.httpClient, c.base, "DELETE", c.version)
}

func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration) *http.Client {

	httpTransport := &http.Transport{
		TLSClientConfig: tlsConfig,
	}

	switch u.Scheme {
	default:
		httpTransport.Dial = func(proto, addr string) (net.Conn, error) {
			return net.DialTimeout(proto, addr, timeout)
		}
	case "unix":
		socketPath := u.Path
		unixDial := func(proto, addr string) (net.Conn, error) {
			ret, err := net.DialTimeout("unix", socketPath, timeout)
			return ret, err
		}
		httpTransport.Dial = unixDial
		// Override the main URL object so the HTTP lib won't complain
		u.Scheme = "http"
		u.Host = "unix.sock"
		u.Path = ""
	}
	return &http.Client{Transport: httpTransport}
}

func NewClient(host string, version string) (*Client, error) {

	baseURL, err := url.Parse(host)
	if err != nil {
		return nil, err
	}
	if baseURL.Path == "" {
		baseURL.Path = "/"
	}
	httpClient := newHTTPClient(baseURL, nil, 10*time.Second)
	c := &Client{
		base:       baseURL,
		version:    version,
		httpClient: httpClient,
	}
	return c, nil
}

func NewDriverClient(driverName string) (*Client, error) {
	sockPath := "unix://" + config.DriverAPIBase + driverName + ".sock"
	return NewClient(sockPath, config.Version)
}
<commit_msg>address golint complaints<commit_after>package client

import (
	"crypto/tls"
	"net"
	"net/http"
	"net/url"
	"time"

	"github.com/libopenstorage/openstorage/config"
	"github.com/libopenstorage/openstorage/volume"
)

// Client is an HTTP REST wrapper. Use one of Get/Porst/Put/Delete to get a request
// object.
type Client struct {
	base       *url.URL
	version    string
	httpClient *http.Client
}

// VolumeDriver returns REST wrapper for the VolumeDriver interface.
func (c *Client) VolumeDriver() volume.VolumeDriver {
	return newVolumeClient(c)
}

// Status sends a Status request at the /status REST endpoint.
func (c *Client) Status() (*Status, error) {
	var status Status
	err := c.Get().UsePath("/status").Do().Unmarshal(&status)
	return &status, err
}

// Get returns a Request object setup for GET call.
func (c *Client) Get() *Request {
	return NewRequest(c.httpClient, c.base, "GET", c.version)
}

// Post returns a Request object setup for POST call.
func (c *Client) Post() *Request {
	return NewRequest(c.httpClient, c.base, "POST", c.version)
}

// Put returns a Request object setup for PUT call.
func (c *Client) Put() *Request {
	return NewRequest(c.httpClient, c.base, "PUT", c.version)
}

// Put returns a Request object setup for DELETE call.
func (c *Client) Delete() *Request {
	return NewRequest(c.httpClient, c.base, "DELETE", c.version)
}

func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration) *http.Client {

	httpTransport := &http.Transport{
		TLSClientConfig: tlsConfig,
	}

	switch u.Scheme {
	default:
		httpTransport.Dial = func(proto, addr string) (net.Conn, error) {
			return net.DialTimeout(proto, addr, timeout)
		}
	case "unix":
		socketPath := u.Path
		unixDial := func(proto, addr string) (net.Conn, error) {
			ret, err := net.DialTimeout("unix", socketPath, timeout)
			return ret, err
		}
		httpTransport.Dial = unixDial
		// Override the main URL object so the HTTP lib won't complain
		u.Scheme = "http"
		u.Host = "unix.sock"
		u.Path = ""
	}
	return &http.Client{Transport: httpTransport}
}

// NewClient returns a new REST client for specified server.
func NewClient(host string, version string) (*Client, error) {

	baseURL, err := url.Parse(host)
	if err != nil {
		return nil, err
	}
	if baseURL.Path == "" {
		baseURL.Path = "/"
	}
	httpClient := newHTTPClient(baseURL, nil, 10*time.Second)
	c := &Client{
		base:       baseURL,
		version:    version,
		httpClient: httpClient,
	}
	return c, nil
}

// NewDriver returns a new REST client for specified driver.
func NewDriverClient(driverName string) (*Client, error) {
	sockPath := "unix://" + config.DriverAPIBase + driverName + ".sock"
	return NewClient(sockPath, config.Version)
}
<|endoftext|>


<commit_before>// written by Daniel Oaks <daniel@danieloaks.net>
// released under the ISC license

package gircclient

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/goshuirc/eventmgr"
	"github.com/goshuirc/irc-go/ircmap"
	"github.com/goshuirc/irc-go/ircmsg"
)

// ServerConnection is a connection to a single server.
type ServerConnection struct {
	Name        string
	Connected   bool
	Registered  bool
	Casemapping ircmap.MappingType

	// internal stuff
	RawConnection  net.Conn
	eventsIn       eventmgr.EventManager
	eventsOut      eventmgr.EventManager
	channelsToJoin []channel

	// data we keep track of
	Features ServerFeatures
	Caps     ClientCapabilities

	// details users must supply before connection
	Nick              string
	InitialNick       string
	FallbackNicks     []string
	fallbackNickIndex int
	InitialUser       string
	InitialRealName   string
	ConnectionPass    string

	// options
	SimplifyEvents bool
}

// newServerConnection returns an initialised ServerConnection, for internal
// use.
func newServerConnection(name string) *ServerConnection {
	var sc ServerConnection

	sc.Name = name
	sc.Caps = NewClientCapabilities()
	sc.Features = make(ServerFeatures)

	sc.Caps.AddWantedCaps("account-notify", "away-notify", "extended-join", "multi-prefix", "sasl")
	sc.Caps.AddWantedCaps("account-tag", "cap-notify", "chghost", "invite-notify", "server-time", "userhost-in-names")

	sc.Features.Parse("CHANTYPES=#", "LINELEN=512", "PREFIX=(ov)@+")

	sc.SimplifyEvents = true

	return &sc
}

// Connect connects to the given address.
func (sc *ServerConnection) Connect(address string, ssl bool, tlsconfig *tls.Config) error {
	// check the required attributes
	if sc.InitialNick == "" || sc.InitialUser == "" {
		return errors.New("InitialNick and InitialUser must be set before connecting")
	}

	// connect
	var conn net.Conn
	var err error

	if ssl {
		conn, err = tls.Dial("tcp", address, tlsconfig)
	} else {
		conn, err = net.Dial("tcp", address)
	}

	if err != nil {
		return err
	}

	sc.RawConnection = conn
	sc.Connected = true

	sc.Send(nil, "", "CAP", "LS", "302")

	return nil
}

// JoinChannel joins a channel, or marks the channel as to be joined after registration.
func (sc *ServerConnection) JoinChannel(name string, key string, useKey bool) {
	if sc.Registered {
		params := []string{name}
		if useKey {
			params = []string{name, key}
		}
		sc.Send(nil, "", "JOIN", params...)
	} else {
		sc.channelsToJoin = append(sc.channelsToJoin, channel{
			Name:   name,
			Key:    key,
			UseKey: useKey,
		})
	}
}

// WaitForConnection waits for the serverConnection to become available.
// This is used when writing a custom event loop.
func (sc *ServerConnection) WaitForConnection() {
	waitTime := 10 * time.Millisecond
	for sc.RawConnection == nil {
		time.Sleep(waitTime)
	}
}

// ProcessIncomingLine processes the incoming IRC line.
// This is used when writing a custom event loop.
func (sc *ServerConnection) ProcessIncomingLine(line string) {
	line = strings.Trim(line, "\r\n")

	// ignore empty lines
	if len(line) < 1 {
		return
	}

	// dispatch raw
	rawInfo := eventmgr.NewInfoMap()
	rawInfo["server"] = sc
	rawInfo["direction"] = "in"
	rawInfo["data"] = line

	sc.dispatchRawIn(rawInfo)

	// dispatch events
	message, err := ircmsg.ParseLine(line)

	// convert numerics to names
	cmd := message.Command
	num, err := strconv.Atoi(cmd)
	if err == nil {
		name, exists := Numerics[num]
		if exists {
			cmd = name
		}
	}

	info := eventmgr.NewInfoMap()
	info["server"] = sc
	info["direction"] = "in"
	info["tags"] = message.AllTags()
	info["prefix"] = message.Prefix
	info["command"] = cmd
	info["params"] = message.Params

	// simplify event
	if sc.SimplifyEvents {
		err = SimplifyEvent(info)

		if err != nil {
			fmt.Println("Could not simplify incoming IRC message, skipping line.")
			fmt.Println("line:", line)
			fmt.Println("error:", err)
			fmt.Println("info:", info)
			return
		}
	}

	// IRC commands are case-insensitive
	sc.dispatchIn(strings.ToUpper(cmd), info)
}

// Disconnect closes the IRC socket.
// It is used when writing your own event loop.
func (sc *ServerConnection) Disconnect() {
	sc.Connected = false
	sc.RawConnection.Close()
	info := eventmgr.NewInfoMap()
	info["server"] = sc
	sc.dispatchOut("server disconnected", info)
}

// ReceiveLoop runs a loop of receiving and dispatching new messages.
func (sc *ServerConnection) ReceiveLoop() {
	// wait for the connection to become available
	sc.WaitForConnection()

	reader := bufio.NewReader(sc.RawConnection)

	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			break
		}

		sc.ProcessIncomingLine(line)
	}

	sc.Disconnect()
}

// RegisterEvent registers a new handler for the given event.
//
// The standard directions are "in" and "out".
//
// 'name' can either be the name of an event, "all", or "raw". Note that "all"
// will not catch "raw" events, but will catch all others.
func (sc *ServerConnection) RegisterEvent(direction string, name string, handler eventmgr.HandlerFn, priority int) {
	if direction == "in" || direction == "both" {
		sc.eventsIn.Attach(name, handler, priority)
	}
	if direction == "out" || direction == "both" {
		sc.eventsOut.Attach(name, handler, priority)
	}
}

// Shutdown closes the connection to the server.
func (sc *ServerConnection) Shutdown(message string) {
	sc.Send(nil, "", "QUIT", message)
	sc.Connected = false
	sc.RawConnection.Close()
}

// Casefold folds the given string using the server's casemapping.
func (sc *ServerConnection) Casefold(message string) (string, error) {
	return ircmap.Casefold(sc.Casemapping, message)
}

// Send sends an IRC message to the server. If the message cannot be converted
// to a raw IRC line, an error is returned.
func (sc *ServerConnection) Send(tags map[string]string, prefix string, command string, params ...string) error {
	msg := ircmsg.MakeMessage(tags, prefix, command, params...)
	line, err := msg.Line()
	if err != nil {
		return err
	}
	fmt.Fprintf(sc.RawConnection, line)

	// dispatch raw event
	info := eventmgr.NewInfoMap()
	info["server"] = sc
	info["direction"] = "out"
	info["data"] = line
	sc.dispatchRawOut(info)

	var outTags map[string]string
	if tags == nil {
		outTags = map[string]string{}
	} else {
		outTags = tags
	}

	// dispatch real event
	info = eventmgr.NewInfoMap()
	info["server"] = sc
	info["direction"] = "out"
	info["tags"] = outTags
	info["prefix"] = prefix
	info["command"] = command
	info["params"] = params

	// IRC commands are case-insensitive
	sc.dispatchOut(strings.ToUpper(command), info)

	return nil
}

// dispatchRawIn dispatches raw inbound messages.
func (sc *ServerConnection) dispatchRawIn(info eventmgr.InfoMap) {
	sc.eventsIn.Dispatch("raw", info)
}

// dispatchIn dispatches inbound messages.
func (sc *ServerConnection) dispatchIn(name string, info eventmgr.InfoMap) {
	sc.eventsIn.Dispatch(name, info)
	sc.eventsIn.Dispatch("all", info)
}

// dispatchRawOut dispatches raw outbound messages.
func (sc *ServerConnection) dispatchRawOut(info eventmgr.InfoMap) {
	sc.eventsOut.Dispatch("raw", info)
}

// dispatchOut dispatches outbound messages.
func (sc *ServerConnection) dispatchOut(name string, info eventmgr.InfoMap) {
	sc.eventsOut.Dispatch(name, info)
	sc.eventsOut.Dispatch("all", info)
}

// IsChannel returns true if the given target is a channel.
func (sc *ServerConnection) IsChannel(target string) bool {
	channelChars := sc.Features["CHANTYPES"].(string)
	return strings.ContainsAny(string(target[0]), channelChars)
}
<commit_msg>Add basic command handler<commit_after>// written by Daniel Oaks <daniel@danieloaks.net>
// released under the ISC license

package gircclient

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/goshuirc/eventmgr"
	"github.com/goshuirc/irc-go/ircmap"
	"github.com/goshuirc/irc-go/ircmsg"
)

var basicCmdPrefixes = []byte{'!', '@', '~', '.', '$'}

// ServerConnection is a connection to a single server.
type ServerConnection struct {
	Name        string
	Connected   bool
	Registered  bool
	Casemapping ircmap.MappingType

	// internal stuff
	RawConnection  net.Conn
	eventsIn       eventmgr.EventManager
	eventsOut      eventmgr.EventManager
	channelsToJoin []channel

	// data we keep track of
	Features ServerFeatures
	Caps     ClientCapabilities

	// details users must supply before connection
	Nick              string
	InitialNick       string
	FallbackNicks     []string
	fallbackNickIndex int
	InitialUser       string
	InitialRealName   string
	ConnectionPass    string

	// options
	SimplifyEvents bool
}

// newServerConnection returns an initialised ServerConnection, for internal
// use.
func newServerConnection(name string) *ServerConnection {
	var sc ServerConnection

	sc.Name = name
	sc.Caps = NewClientCapabilities()
	sc.Features = make(ServerFeatures)

	sc.Caps.AddWantedCaps("account-notify", "away-notify", "extended-join", "multi-prefix", "sasl")
	sc.Caps.AddWantedCaps("account-tag", "cap-notify", "chghost", "invite-notify", "server-time", "userhost-in-names")

	sc.Features.Parse("CHANTYPES=#", "LINELEN=512", "PREFIX=(ov)@+")

	sc.SimplifyEvents = true

	return &sc
}

// Connect connects to the given address.
func (sc *ServerConnection) Connect(address string, ssl bool, tlsconfig *tls.Config) error {
	// check the required attributes
	if sc.InitialNick == "" || sc.InitialUser == "" {
		return errors.New("InitialNick and InitialUser must be set before connecting")
	}

	// connect
	var conn net.Conn
	var err error

	if ssl {
		conn, err = tls.Dial("tcp", address, tlsconfig)
	} else {
		conn, err = net.Dial("tcp", address)
	}

	if err != nil {
		return err
	}

	sc.RawConnection = conn
	sc.Connected = true

	sc.Send(nil, "", "CAP", "LS", "302")

	return nil
}

// JoinChannel joins a channel, or marks the channel as to be joined after registration.
func (sc *ServerConnection) JoinChannel(name string, key string, useKey bool) {
	if sc.Registered {
		params := []string{name}
		if useKey {
			params = []string{name, key}
		}
		sc.Send(nil, "", "JOIN", params...)
	} else {
		sc.channelsToJoin = append(sc.channelsToJoin, channel{
			Name:   name,
			Key:    key,
			UseKey: useKey,
		})
	}
}

// WaitForConnection waits for the serverConnection to become available.
// This is used when writing a custom event loop.
func (sc *ServerConnection) WaitForConnection() {
	waitTime := 10 * time.Millisecond
	for sc.RawConnection == nil {
		time.Sleep(waitTime)
	}
}

// ProcessIncomingLine processes the incoming IRC line.
// This is used when writing a custom event loop.
func (sc *ServerConnection) ProcessIncomingLine(line string) {
	line = strings.Trim(line, "\r\n")

	// ignore empty lines
	if len(line) < 1 {
		return
	}

	// dispatch raw
	rawInfo := eventmgr.NewInfoMap()
	rawInfo["server"] = sc
	rawInfo["direction"] = "in"
	rawInfo["data"] = line

	sc.dispatchRawIn(rawInfo)

	// dispatch events
	message, err := ircmsg.ParseLine(line)

	// convert numerics to names
	cmd := message.Command
	num, err := strconv.Atoi(cmd)
	if err == nil {
		name, exists := Numerics[num]
		if exists {
			cmd = name
		}
	}

	info := eventmgr.NewInfoMap()
	info["server"] = sc
	info["direction"] = "in"
	info["tags"] = message.AllTags()
	info["prefix"] = message.Prefix
	info["command"] = cmd
	info["params"] = message.Params

	// simplify event
	if sc.SimplifyEvents {
		err = SimplifyEvent(info)

		if err != nil {
			fmt.Println("Could not simplify incoming IRC message, skipping line.")
			fmt.Println("line:", line)
			fmt.Println("error:", err)
			fmt.Println("info:", info)
			return
		}
	}

	// IRC commands are case-insensitive
	sc.dispatchIn(strings.ToUpper(cmd), info)
        if strings.ToUpper(cmd) == "PRIVMSG" {
            sc.dispatchBasicCmd(info)
        }

}

// Disconnect closes the IRC socket.
// It is used when writing your own event loop.
func (sc *ServerConnection) Disconnect() {
	sc.Connected = false
	sc.RawConnection.Close()
	info := eventmgr.NewInfoMap()
	info["server"] = sc
	sc.dispatchOut("server disconnected", info)
}

// ReceiveLoop runs a loop of receiving and dispatching new messages.
func (sc *ServerConnection) ReceiveLoop() {
	// wait for the connection to become available
	sc.WaitForConnection()

	reader := bufio.NewReader(sc.RawConnection)

	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			break
		}

		sc.ProcessIncomingLine(line)
	}

	sc.Disconnect()
}

// RegisterEvent registers a new handler for the given event.
//
// The standard directions are "in" and "out".
//
// 'name' can either be the name of an event, "all", or "raw". Note that "all"
// will not catch "raw" events, but will catch all others.
func (sc *ServerConnection) RegisterEvent(direction string, name string, handler eventmgr.HandlerFn, priority int) {
	if direction == "in" || direction == "both" {
		sc.eventsIn.Attach(name, handler, priority)
	}
	if direction == "out" || direction == "both" {
		sc.eventsOut.Attach(name, handler, priority)
	}
}

// Shutdown closes the connection to the server.
func (sc *ServerConnection) Shutdown(message string) {
	sc.Send(nil, "", "QUIT", message)
	sc.Connected = false
	sc.RawConnection.Close()
}

// Casefold folds the given string using the server's casemapping.
func (sc *ServerConnection) Casefold(message string) (string, error) {
	return ircmap.Casefold(sc.Casemapping, message)
}

// Send sends an IRC message to the server. If the message cannot be converted
// to a raw IRC line, an error is returned.
func (sc *ServerConnection) Send(tags map[string]string, prefix string, command string, params ...string) error {
	msg := ircmsg.MakeMessage(tags, prefix, command, params...)
	line, err := msg.Line()
	if err != nil {
		return err
	}
	fmt.Fprintf(sc.RawConnection, line)

	// dispatch raw event
	info := eventmgr.NewInfoMap()
	info["server"] = sc
	info["direction"] = "out"
	info["data"] = line
	sc.dispatchRawOut(info)

	var outTags map[string]string
	if tags == nil {
		outTags = map[string]string{}
	} else {
		outTags = tags
	}

	// dispatch real event
	info = eventmgr.NewInfoMap()
	info["server"] = sc
	info["direction"] = "out"
	info["tags"] = outTags
	info["prefix"] = prefix
	info["command"] = command
	info["params"] = params

	// IRC commands are case-insensitive
	sc.dispatchOut(strings.ToUpper(command), info)

	return nil
}

// dispatchCommand dispatches an event based on simple commands (e.g !help)
func (sc *ServerConnection) dispatchBasicCmd(info eventmgr.InfoMap) {
    cmd := info["params"].([]string)
    pfx := cmd[1][0]

    for _, p := range basicCmdPrefixes {
        if pfx == p {
            sc.eventsIn.Dispatch("cmd"+cmd[1], info)
        }
    }
}

// dispatchRawIn dispatches raw inbound messages.
func (sc *ServerConnection) dispatchRawIn(info eventmgr.InfoMap) {
	sc.eventsIn.Dispatch("raw", info)
}

// dispatchIn dispatches inbound messages.
func (sc *ServerConnection) dispatchIn(name string, info eventmgr.InfoMap) {
	sc.eventsIn.Dispatch(name, info)
	sc.eventsIn.Dispatch("all", info)
}

// dispatchRawOut dispatches raw outbound messages.
func (sc *ServerConnection) dispatchRawOut(info eventmgr.InfoMap) {
	sc.eventsOut.Dispatch("raw", info)
}

// dispatchOut dispatches outbound messages.
func (sc *ServerConnection) dispatchOut(name string, info eventmgr.InfoMap) {
	sc.eventsOut.Dispatch(name, info)
	sc.eventsOut.Dispatch("all", info)
}

// IsChannel returns true if the given target is a channel.
func (sc *ServerConnection) IsChannel(target string) bool {
	channelChars := sc.Features["CHANTYPES"].(string)
	return strings.ContainsAny(string(target[0]), channelChars)
}
<|endoftext|>


<commit_before>package client

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"strconv"
)

type Client struct {
	conn net.Conn
}

// NewClient will connect to the server and create a client.
func NewClient(o *Options) (*Client, error) {
	conn, err := o.getDialer()()
	return &Client{
		conn: conn,
	}, err
}

// Cmd will send command, receive data from server and build reply.
func (c *Client) Cmd(args ...interface{}) (r *Reply) {
	r = &Reply{
		State: ReplyError,
	}

	if c.conn == nil {
		r.State = ReplyFail
		return
	}

	if err := c.send(args); err != nil {
		r.State = ReplyFail
		return
	}
	r = c.recv()

	return
}

// Close socks.
func (c *Client) Close() {
	if c.conn != nil {
		c.conn.Close()
	}
}

func (c *Client) send(args []interface{}) (err error) {
	var buf []byte

	buf, err = c.sendBuf(args)
	if err == nil {
		_, err = c.conn.Write(buf)
	}
	return
}

func (c *Client) recv() (r *Reply) {
	var line []byte
	reader := bufio.NewReader(c.conn)

	// *<number of arguments>CRLF
	line, err = reader.ReadBytes('\n')
	if err != nil {
		return
	}

	if line[0] == '+' || line[0] == '-' {
		return &Reply{
			State: string(line[1 : len(line)-2]),
		}
	}
	if line[0] == ':' {
		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[1 : len(line)-2]},
		}
	}

	if line[0] == '$' {
		var dLen int
		dLen, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		line, _ = reader.ReadBytes('\n')

		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[:dLen]},
		}
	}

	var data [][]byte
	if line[0] == '*' {
		var count int
		count, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		for i := 0; i < count; i++ {
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			dLen, _ := strconv.Atoi(string(line[1 : len(line)-2]))
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			data = append(data, line[:dLen])
		}
	}

	r = &Reply{
		State: ReplyOK,
		Data:  data,
	}

	return
}

func (c *Client) sendBuf(args []interface{}) (b []byte, err error) {
	var buf bytes.Buffer
	var s, size string
	buf.WriteString(fmt.Sprintf("*%d\r\n", len(args)))

	for _, arg := range args {
		switch arg.(type) {
		case string:
			s = arg.(string)
			size = fmt.Sprintf("$%d\r\n", len(s))
			buf.WriteString(size)
			buf.WriteString(s + "\r\n")
			continue
		case int, int8, int16, int32, int64,
			uint, uint8, uint16, uint32, uint64:
			s = fmt.Sprintf("%d", arg)
			size = fmt.Sprintf("$%d\r\n", len(s))
			buf.WriteString(size)
			buf.WriteString(s + "\r\n")

		case float32:
			s = strconv.FormatFloat(float64(arg.(float32)), 'f', -1, 32)
			size = fmt.Sprintf("$%d\r\n", len(s))
			buf.WriteString(size)
			buf.WriteString(s + "\r\n")
		case float64:
			s = strconv.FormatFloat(arg.(float64), 'f', -1, 64)
			size = fmt.Sprintf("$%d\r\n", len(s))
			buf.WriteString(size)
			buf.WriteString(s + "\r\n")

		}
	}

	b = buf.Bytes()

	return
}
<commit_msg>Clean code.<commit_after>package client

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"strconv"
)

type Client struct {
	conn net.Conn
}

// NewClient will connect to the server and create a client.
func NewClient(o *Options) (*Client, error) {
	conn, err := o.getDialer()()
	return &Client{
		conn: conn,
	}, err
}

// Cmd will send command, receive data from server and build reply.
func (c *Client) Cmd(args ...interface{}) (r *Reply) {
	r = &Reply{
		State: ReplyError,
	}

	if c.conn == nil {
		r.State = ReplyFail
		return
	}

	if err := c.send(args); err != nil {
		r.State = ReplyFail
		return
	}
	r = c.recv()

	return
}

// Close socks.
func (c *Client) Close() {
	if c.conn != nil {
		c.conn.Close()
	}
}

func (c *Client) send(args []interface{}) (err error) {
	var buf []byte

	buf, err = c.sendBuf(args)
	if err == nil {
		_, err = c.conn.Write(buf)
	}
	return
}

func (c *Client) recv() (r *Reply) {
	var line []byte
	reader := bufio.NewReader(c.conn)

	// *<number of arguments>CRLF
	line, err = reader.ReadBytes('\n')
	if err != nil {
		return
	}

	if line[0] == '+' || line[0] == '-' {
		return &Reply{
			State: string(line[1 : len(line)-2]),
		}
	}
	if line[0] == ':' {
		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[1 : len(line)-2]},
		}
	}

	if line[0] == '$' {
		var dLen int
		dLen, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		line, _ = reader.ReadBytes('\n')

		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[:dLen]},
		}
	}

	var data [][]byte
	if line[0] == '*' {
		var count int
		count, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		for i := 0; i < count; i++ {
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			dLen, _ := strconv.Atoi(string(line[1 : len(line)-2]))
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			data = append(data, line[:dLen])
		}
	}

	r = &Reply{
		State: ReplyOK,
		Data:  data,
	}

	return
}

func (c *Client) sendBuf(args []interface{}) (b []byte, err error) {
	var buf bytes.Buffer
	var s, size string
	buf.WriteString(fmt.Sprintf("*%d\r\n", len(args)))

	for _, arg := range args {
		switch arg.(type) {
		case string:
			s = arg.(string)

		case int, int8, int16, int32, int64,
			uint, uint8, uint16, uint32, uint64:
			s = fmt.Sprintf("%d", arg)

		case float32:
			s = strconv.FormatFloat(float64(arg.(float32)), 'f', -1, 32)

		case float64:
			s = strconv.FormatFloat(arg.(float64), 'f', -1, 64)

		case bool:
		case nil:
		case []byte:
		case [][]byte:
		case []string:

		}

		size = fmt.Sprintf("$%d\r\n", len(s))
		buf.WriteString(size)
		buf.WriteString(s + "\r\n")

	}

	b = buf.Bytes()

	return
}
<|endoftext|>