<commit_before>// Copyright (c) 2016 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package run_test

import (
	"bufio"
	"bytes"
	"context"
	"io"
	"os"
	"path"
	"sync"
	"testing"

	"github.com/tsavola/gate/internal/runtest"
	"github.com/tsavola/gate/packet"
	"github.com/tsavola/gate/run"
	"github.com/tsavola/wag"
	"github.com/tsavola/wag/dewag"
	"github.com/tsavola/wag/sections"
	"github.com/tsavola/wag/wasm"
)

type readWriter struct {
	io.Reader
	io.Writer
}

func openProgram(testName string) (f *os.File) {
	f, err := os.Open(path.Join(os.Getenv("GATE_TEST_DIR"), testName, "prog.wasm"))
	if err != nil {
		panic(err)
	}
	return
}

const (
	dumpText = false
)

func TestAlloc(t *testing.T) {
	output := testRun(t, "alloc")
	t.Log(string(output.Bytes()))
}

func TestHello(t *testing.T) {
	output := testRun(t, "hello")
	if s := string(output.Bytes()); s != "hello world\n" {
		t.Fatalf("output: %#v", s)
	}
}

func TestServices(t *testing.T) {
	testRun(t, "services")
}

func testRun(t *testing.T, testName string) (output bytes.Buffer) {
	const (
		memorySizeLimit = 24 * wasm.Page
		stackSize       = 24 * 4096
	)

	rt := runtest.NewRuntime(nil)
	defer rt.Close()

	wasm := openProgram(testName)
	defer wasm.Close()

	var nameSection sections.NameSection

	m := wag.Module{
		UnknownSectionLoader: sections.UnknownLoaders{"name": nameSection.Load}.Load,
	}

	err := run.Load(&m, bufio.NewReader(wasm), rt.Runtime, new(bytes.Buffer), nil, nil)
	if err != nil {
		t.Fatalf("load error: %v", err)
	}

	if dumpText && testing.Verbose() {
		dewag.PrintTo(os.Stdout, m.Text(), m.FunctionMap(), nil)
	}

	_, memorySize := m.MemoryLimits()
	if memorySize > memorySizeLimit {
		memorySize = memorySizeLimit
	}

	var (
		image run.Image
		proc  run.Process
	)
	defer image.Release(rt.Runtime)
	defer proc.Kill(rt.Runtime)

	err = image.Init(context.Background(), rt.Runtime)
	if err != nil {
		t.Fatalf("image error: %v", err)
	}

	err = proc.Init(context.Background(), rt.Runtime, &image, os.Stdout)
	if err != nil {
		return
	}

	err = image.Populate(&m, memorySize, stackSize)
	if err != nil {
		t.Fatalf("image error: %v", err)
	}

	if false {
		var buf bytes.Buffer

		if err := dewag.PrintTo(&buf, m.Text(), m.FunctionMap(), &nameSection); err == nil {
			t.Logf("disassembly:\n%s", string(buf.Bytes()))
		} else {
			t.Errorf("disassembly error: %v", err)
		}
	}

	stacktrace := true

	exit, trap, err := run.Run(context.Background(), rt.Runtime, &proc, &image, &testServiceRegistry{origin: &output})
	if err != nil {
		t.Errorf("run error: %v", err)
	} else if trap != 0 {
		t.Errorf("run trap: %s", trap)
	} else if exit != 0 {
		t.Errorf("run exit: %d", exit)
	} else {
		stacktrace = false
	}

	if stacktrace {
		var buf bytes.Buffer

		if err := image.DumpStacktrace(&buf, &m, &nameSection); err == nil {
			t.Logf("stacktrace:\n%s", string(buf.Bytes()))
		} else {
			t.Errorf("stacktrace error: %v", err)
		}
	}

	if name := os.Getenv("GATE_TEST_DUMP"); name != "" {
		f, err := os.Create(name)
		if err != nil {
			panic(err)
		}
		defer f.Close()

		if err := image.DumpGlobalsMemoryStack(f); err != nil {
			t.Errorf("dump error: %v", err)
		}
	}

	return
}

type testServiceRegistry struct {
	origin io.Writer
}

func (services *testServiceRegistry) StartServing(ctx context.Context, ops <-chan packet.Buf, evs chan<- packet.Buf, maxContentSize int,
) run.ServiceDiscoverer {
	d := new(testServiceDiscoverer)

	go func() {
		defer close(evs)

		for op := range ops {
			i := op.Code().Int16()

			d.nameLock.Lock()
			name := d.names[i]
			d.nameLock.Unlock()

			switch name {
			case "origin":
				if _, err := services.origin.Write(op.Content()); err != nil {
					panic(err)
				}
			}
		}
	}()

	return d
}

type testServiceDiscoverer struct {
	services []run.Service
	nameLock sync.Mutex
	names    []string
}

func (d *testServiceDiscoverer) Discover(names []string) []run.Service {
	for _, name := range names {
		var s run.Service

		switch name {
		case "origin":
			s.SetAvailable(0)

		case "test1":
			s.SetAvailable(1337)

		case "test2":
			s.SetAvailable(12765)
		}

		d.services = append(d.services, s)

		d.nameLock.Lock()
		d.names = append(d.names, name)
		d.nameLock.Unlock()
	}

	return d.services
}

func (d *testServiceDiscoverer) NumServices() int {
	return len(d.services)
}
<commit_msg>run: test: generate instruction map<commit_after>// Copyright (c) 2016 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package run_test

import (
	"bufio"
	"bytes"
	"context"
	"io"
	"os"
	"path"
	"sync"
	"testing"

	"github.com/tsavola/gate/internal/runtest"
	"github.com/tsavola/gate/packet"
	"github.com/tsavola/gate/run"
	"github.com/tsavola/wag"
	"github.com/tsavola/wag/dewag"
	"github.com/tsavola/wag/insnmap"
	"github.com/tsavola/wag/sections"
	"github.com/tsavola/wag/wasm"
)

type readWriter struct {
	io.Reader
	io.Writer
}

func openProgram(testName string) (f *os.File) {
	f, err := os.Open(path.Join(os.Getenv("GATE_TEST_DIR"), testName, "prog.wasm"))
	if err != nil {
		panic(err)
	}
	return
}

const (
	dumpText = false
)

func TestAlloc(t *testing.T) {
	output := testRun(t, "alloc")
	t.Log(string(output.Bytes()))
}

func TestHello(t *testing.T) {
	output := testRun(t, "hello")
	if s := string(output.Bytes()); s != "hello world\n" {
		t.Fatalf("output: %#v", s)
	}
}

func TestServices(t *testing.T) {
	testRun(t, "services")
}

func testRun(t *testing.T, testName string) (output bytes.Buffer) {
	const (
		memorySizeLimit = 24 * wasm.Page
		stackSize       = 24 * 4096
	)

	rt := runtest.NewRuntime(nil)
	defer rt.Close()

	wasm := openProgram(testName)
	defer wasm.Close()

	var (
		nameSection sections.NameSection
		insnMap     insnmap.InsnMap
	)

	m := wag.Module{
		UnknownSectionLoader: sections.UnknownLoaders{"name": nameSection.Load}.Load,
		InsnMap:              &insnMap,
	}

	err := run.Load(&m, bufio.NewReader(wasm), rt.Runtime, new(bytes.Buffer), nil, nil)
	if err != nil {
		t.Fatalf("load error: %v", err)
	}

	if dumpText && testing.Verbose() {
		dewag.PrintTo(os.Stdout, m.Text(), m.FunctionMap(), nil)
	}

	_, memorySize := m.MemoryLimits()
	if memorySize > memorySizeLimit {
		memorySize = memorySizeLimit
	}

	var (
		image run.Image
		proc  run.Process
	)
	defer image.Release(rt.Runtime)
	defer proc.Kill(rt.Runtime)

	err = image.Init(context.Background(), rt.Runtime)
	if err != nil {
		t.Fatalf("image error: %v", err)
	}

	err = proc.Init(context.Background(), rt.Runtime, &image, os.Stdout)
	if err != nil {
		return
	}

	err = image.Populate(&m, memorySize, stackSize)
	if err != nil {
		t.Fatalf("image error: %v", err)
	}

	if false {
		var buf bytes.Buffer

		if err := dewag.PrintTo(&buf, m.Text(), m.FunctionMap(), &nameSection); err == nil {
			t.Logf("disassembly:\n%s", string(buf.Bytes()))
		} else {
			t.Errorf("disassembly error: %v", err)
		}
	}

	stacktrace := true

	exit, trap, err := run.Run(context.Background(), rt.Runtime, &proc, &image, &testServiceRegistry{origin: &output})
	if err != nil {
		t.Errorf("run error: %v", err)
	} else if trap != 0 {
		t.Errorf("run trap: %s", trap)
	} else if exit != 0 {
		t.Errorf("run exit: %d", exit)
	} else {
		stacktrace = false
	}

	if stacktrace {
		var buf bytes.Buffer

		if err := image.DumpStacktrace(&buf, &m, &nameSection); err == nil {
			t.Logf("stacktrace:\n%s", string(buf.Bytes()))
		} else {
			t.Errorf("stacktrace error: %v", err)
		}
	}

	if name := os.Getenv("GATE_TEST_DUMP"); name != "" {
		f, err := os.Create(name)
		if err != nil {
			panic(err)
		}
		defer f.Close()

		if err := image.DumpGlobalsMemoryStack(f); err != nil {
			t.Errorf("dump error: %v", err)
		}
	}

	return
}

type testServiceRegistry struct {
	origin io.Writer
}

func (services *testServiceRegistry) StartServing(ctx context.Context, ops <-chan packet.Buf, evs chan<- packet.Buf, maxContentSize int,
) run.ServiceDiscoverer {
	d := new(testServiceDiscoverer)

	go func() {
		defer close(evs)

		for op := range ops {
			i := op.Code().Int16()

			d.nameLock.Lock()
			name := d.names[i]
			d.nameLock.Unlock()

			switch name {
			case "origin":
				if _, err := services.origin.Write(op.Content()); err != nil {
					panic(err)
				}
			}
		}
	}()

	return d
}

type testServiceDiscoverer struct {
	services []run.Service
	nameLock sync.Mutex
	names    []string
}

func (d *testServiceDiscoverer) Discover(names []string) []run.Service {
	for _, name := range names {
		var s run.Service

		switch name {
		case "origin":
			s.SetAvailable(0)

		case "test1":
			s.SetAvailable(1337)

		case "test2":
			s.SetAvailable(12765)
		}

		d.services = append(d.services, s)

		d.nameLock.Lock()
		d.names = append(d.names, name)
		d.nameLock.Unlock()
	}

	return d.services
}

func (d *testServiceDiscoverer) NumServices() int {
	return len(d.services)
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp

import (
	"net/http"
	"os"
	"sort"
	"strings"

	"github.com/gogf/gf/text/gstr"

	"github.com/gogf/gf/errors/gerror"

	"github.com/gogf/gf/os/gres"

	"github.com/gogf/gf/encoding/ghtml"
	"github.com/gogf/gf/os/gfile"
	"github.com/gogf/gf/os/gspath"
	"github.com/gogf/gf/os/gtime"
)

// ServeHTTP is the default handler for http request.
// It should not create new goroutine handling the request as
// it's called by am already created new goroutine from http.Server.
//
// This function also make serve implementing the interface of http.Handler.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Max body size limit.
	if s.config.ClientMaxBodySize > 0 {
		r.Body = http.MaxBytesReader(w, r.Body, s.config.ClientMaxBodySize)
	}

	// Rewrite feature checks.
	if len(s.config.Rewrites) > 0 {
		if rewrite, ok := s.config.Rewrites[r.URL.Path]; ok {
			r.URL.Path = rewrite
		}
	}

	// Remove char '/' in the tail of URI.
	if r.URL.Path != "/" {
		for len(r.URL.Path) > 0 && r.URL.Path[len(r.URL.Path)-1] == '/' {
			r.URL.Path = r.URL.Path[:len(r.URL.Path)-1]
		}
	}

	// Default URI value if it's empty.
	if r.URL.Path == "" {
		r.URL.Path = "/"
	}

	// Create a new request object.
	request := newRequest(s, r, w)

	defer func() {
		request.LeaveTime = gtime.TimestampMilli()
		// error log handling.
		if request.error != nil {
			s.handleErrorLog(request.error, request)
		} else {
			if exception := recover(); exception != nil {
				request.Response.WriteStatus(http.StatusInternalServerError)
				s.handleErrorLog(gerror.Newf("%v", exception), request)
			}
		}
		// access log handling.
		s.handleAccessLog(request)
		// Close the session, which automatically update the TTL
		// of the session if it exists.
		request.Session.Close()
	}()

	// ============================================================
	// Priority:
	// Static File > Dynamic Service > Static Directory
	// ============================================================

	// Search the static file with most high priority,
	// which also handle the index files feature.
	if s.config.FileServerEnabled {
		request.StaticFile = s.searchStaticFile(r.URL.Path)
		if request.StaticFile != nil {
			request.isFileRequest = true
		}
	}

	// Search the dynamic service handler.
	request.handlers, request.hasHookHandler, request.hasServeHandler = s.getHandlersWithCache(request)

	// Check the service type static or dynamic for current request.
	if request.StaticFile != nil && request.StaticFile.IsDir && request.hasServeHandler {
		request.isFileRequest = false
	}

	// HOOK - BeforeServe
	s.callHookHandler(HOOK_BEFORE_SERVE, request)

	// Core serving handling.
	if !request.IsExited() {
		if request.isFileRequest {
			// Static file service.
			s.serveFile(request, request.StaticFile)
		} else {
			if len(request.handlers) > 0 {
				// Dynamic service.
				request.Middleware.Next()
			} else {
				if request.StaticFile != nil && request.StaticFile.IsDir {
					// Serve the directory.
					s.serveFile(request, request.StaticFile)
				} else {
					if len(request.Response.Header()) == 0 &&
						request.Response.Status == 0 &&
						request.Response.BufferLength() == 0 {
						request.Response.WriteHeader(http.StatusNotFound)
					}
				}
			}
		}
	}

	// HOOK - AfterServe
	if !request.IsExited() {
		s.callHookHandler(HOOK_AFTER_SERVE, request)
	}

	// HOOK - BeforeOutput
	if !request.IsExited() {
		s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
	}

	// HTTP status checking.
	if request.Response.Status == 0 {
		if request.StaticFile != nil || request.Middleware.served || request.Response.buffer.Len() > 0 {
			request.Response.WriteHeader(http.StatusOK)
		} else {
			request.Response.WriteHeader(http.StatusNotFound)
		}
	}
	// HTTP status handler.
	if request.Response.Status != http.StatusOK {
		if f := s.getStatusHandler(request.Response.Status, request); f != nil {
			// Call custom status handler.
			niceCallFunc(func() {
				f(request)
			})
		}
	}

	// Automatically set the session id to cookie
	// if it creates a new session id in this request.
	if request.Session.IsDirty() && request.Session.Id() != request.GetSessionId() {
		request.Cookie.SetSessionId(request.Session.Id())
	}
	// Output the cookie content to client.
	request.Cookie.Flush()
	// Output the buffer content to client.
	request.Response.Flush()
	// HOOK - AfterOutput
	if !request.IsExited() {
		s.callHookHandler(HOOK_AFTER_OUTPUT, request)
	}
}

// searchStaticFile searches the file with given URI.
// It returns a file struct specifying the file information.
func (s *Server) searchStaticFile(uri string) *StaticFile {
	var file *gres.File
	var path string
	var dir bool
	// Firstly search the StaticPaths mapping.
	if len(s.config.StaticPaths) > 0 {
		for _, item := range s.config.StaticPaths {
			if len(uri) >= len(item.prefix) && strings.EqualFold(item.prefix, uri[0:len(item.prefix)]) {
				// To avoid case like: /static/style -> /static/style.css
				if len(uri) > len(item.prefix) && uri[len(item.prefix)] != '/' {
					continue
				}
				file = gres.GetWithIndex(item.path+uri[len(item.prefix):], s.config.IndexFiles)
				if file != nil {
					return &StaticFile{
						File:  file,
						IsDir: file.FileInfo().IsDir(),
					}
				}
				path, dir = gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...)
				if path != "" {
					return &StaticFile{
						Path:  path,
						IsDir: dir,
					}
				}

			}
		}
	}
	// Secondly search the root and searching paths.
	if len(s.config.SearchPaths) > 0 {
		for _, p := range s.config.SearchPaths {
			file = gres.GetWithIndex(p+uri, s.config.IndexFiles)
			if file != nil {
				return &StaticFile{
					File:  file,
					IsDir: file.FileInfo().IsDir(),
				}
			}
			if path, dir = gspath.Search(p, uri, s.config.IndexFiles...); path != "" {
				return &StaticFile{
					Path:  path,
					IsDir: dir,
				}
			}
		}
	}
	// Lastly search the resource manager.
	if len(s.config.StaticPaths) == 0 && len(s.config.SearchPaths) == 0 {
		if file = gres.GetWithIndex(uri, s.config.IndexFiles); file != nil {
			return &StaticFile{
				File:  file,
				IsDir: file.FileInfo().IsDir(),
			}
		}
	}
	return nil
}

// serveFile serves the static file for client.
// The optional parameter <allowIndex> specifies if allowing directory listing if <f> is directory.
func (s *Server) serveFile(r *Request, f *StaticFile, allowIndex ...bool) {
	// Use resource file from memory.
	if f.File != nil {
		if f.IsDir {
			if s.config.IndexFolder || (len(allowIndex) > 0 && allowIndex[0]) {
				s.listDir(r, f.File)
			} else {
				r.Response.WriteStatus(http.StatusForbidden)
			}
		} else {
			info := f.File.FileInfo()
			r.Response.wroteHeader = true
			http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), f.File)
		}
		return
	}
	// Use file from dist.
	file, err := os.Open(f.Path)
	if err != nil {
		r.Response.WriteStatus(http.StatusForbidden)
		return
	}
	defer file.Close()

	// Clear the response buffer before file serving.
	// It ignores all custom buffer content and uses the file content.
	r.Response.ClearBuffer()

	info, _ := file.Stat()
	if info.IsDir() {
		if s.config.IndexFolder || (len(allowIndex) > 0 && allowIndex[0]) {
			s.listDir(r, file)
		} else {
			r.Response.WriteStatus(http.StatusForbidden)
		}
	} else {
		r.Response.wroteHeader = true
		http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), file)
	}
}

// listDir lists the sub files of specified directory as HTML content to client.
func (s *Server) listDir(r *Request, f http.File) {
	files, err := f.Readdir(-1)
	if err != nil {
		r.Response.WriteStatus(http.StatusInternalServerError, "Error reading directory")
		return
	}
	// The folder type has the most priority than file.
	sort.Slice(files, func(i, j int) bool {
		if files[i].IsDir() && !files[j].IsDir() {
			return true
		}
		if !files[i].IsDir() && files[j].IsDir() {
			return false
		}
		return files[i].Name() < files[j].Name()
	})
	if r.Response.Header().Get("Content-Type") == "" {
		r.Response.Header().Set("Content-Type", "text/html; charset=utf-8")
	}
	r.Response.Write(`<html>`)
	r.Response.Write(`<head>`)
	r.Response.Write(`<style>`)
	r.Response.Write(`body {font-family:Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;}`)
	r.Response.Write(`</style>`)
	r.Response.Write(`</head>`)
	r.Response.Write(`<body>`)
	r.Response.Writef(`<h1>Index of %s</h1>`, r.URL.Path)
	r.Response.Writef(`<hr />`)
	r.Response.Write(`<table>`)
	if r.URL.Path != "/" {
		r.Response.Write(`<tr>`)
		r.Response.Writef(`<td><a href="%s">..</a></td>`, gfile.Dir(r.URL.Path))
		r.Response.Write(`</tr>`)
	}
	name := ""
	size := ""
	prefix := gstr.TrimRight(r.URL.Path, "/")
	for _, file := range files {
		name = file.Name()
		size = gfile.FormatSize(file.Size())
		if file.IsDir() {
			name += "/"
			size = "-"
		}
		r.Response.Write(`<tr>`)
		r.Response.Writef(`<td><a href="%s/%s">%s</a></td>`, prefix, name, ghtml.SpecialChars(name))
		r.Response.Writef(`<td style="width:300px;text-align:center;">%s</td>`, gtime.New(file.ModTime()).ISO8601())
		r.Response.Writef(`<td style="width:80px;text-align:right;">%s</td>`, size)
		r.Response.Write(`</tr>`)
	}
	r.Response.Write(`</table>`)
	r.Response.Write(`</body>`)
	r.Response.Write(`</html>`)
}
<commit_msg>improve ghttp.Server by closing the request and response body to release the file descriptor in time<commit_after>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp

import (
	"net/http"
	"os"
	"sort"
	"strings"

	"github.com/gogf/gf/text/gstr"

	"github.com/gogf/gf/errors/gerror"

	"github.com/gogf/gf/os/gres"

	"github.com/gogf/gf/encoding/ghtml"
	"github.com/gogf/gf/os/gfile"
	"github.com/gogf/gf/os/gspath"
	"github.com/gogf/gf/os/gtime"
)

// ServeHTTP is the default handler for http request.
// It should not create new goroutine handling the request as
// it's called by am already created new goroutine from http.Server.
//
// This function also make serve implementing the interface of http.Handler.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Max body size limit.
	if s.config.ClientMaxBodySize > 0 {
		r.Body = http.MaxBytesReader(w, r.Body, s.config.ClientMaxBodySize)
	}

	// Rewrite feature checks.
	if len(s.config.Rewrites) > 0 {
		if rewrite, ok := s.config.Rewrites[r.URL.Path]; ok {
			r.URL.Path = rewrite
		}
	}

	// Remove char '/' in the tail of URI.
	if r.URL.Path != "/" {
		for len(r.URL.Path) > 0 && r.URL.Path[len(r.URL.Path)-1] == '/' {
			r.URL.Path = r.URL.Path[:len(r.URL.Path)-1]
		}
	}

	// Default URI value if it's empty.
	if r.URL.Path == "" {
		r.URL.Path = "/"
	}

	// Create a new request object.
	request := newRequest(s, r, w)

	defer func() {
		request.LeaveTime = gtime.TimestampMilli()
		// error log handling.
		if request.error != nil {
			s.handleErrorLog(request.error, request)
		} else {
			if exception := recover(); exception != nil {
				request.Response.WriteStatus(http.StatusInternalServerError)
				s.handleErrorLog(gerror.Newf("%v", exception), request)
			}
		}
		// access log handling.
		s.handleAccessLog(request)
		// Close the session, which automatically update the TTL
		// of the session if it exists.
		request.Session.Close()

		// Close the request and response body
		// to release the file descriptor in time.
		request.Request.Body.Close()
		if request.Request.Response != nil {
			request.Request.Response.Body.Close()
		}
	}()

	// ============================================================
	// Priority:
	// Static File > Dynamic Service > Static Directory
	// ============================================================

	// Search the static file with most high priority,
	// which also handle the index files feature.
	if s.config.FileServerEnabled {
		request.StaticFile = s.searchStaticFile(r.URL.Path)
		if request.StaticFile != nil {
			request.isFileRequest = true
		}
	}

	// Search the dynamic service handler.
	request.handlers, request.hasHookHandler, request.hasServeHandler = s.getHandlersWithCache(request)

	// Check the service type static or dynamic for current request.
	if request.StaticFile != nil && request.StaticFile.IsDir && request.hasServeHandler {
		request.isFileRequest = false
	}

	// HOOK - BeforeServe
	s.callHookHandler(HOOK_BEFORE_SERVE, request)

	// Core serving handling.
	if !request.IsExited() {
		if request.isFileRequest {
			// Static file service.
			s.serveFile(request, request.StaticFile)
		} else {
			if len(request.handlers) > 0 {
				// Dynamic service.
				request.Middleware.Next()
			} else {
				if request.StaticFile != nil && request.StaticFile.IsDir {
					// Serve the directory.
					s.serveFile(request, request.StaticFile)
				} else {
					if len(request.Response.Header()) == 0 &&
						request.Response.Status == 0 &&
						request.Response.BufferLength() == 0 {
						request.Response.WriteHeader(http.StatusNotFound)
					}
				}
			}
		}
	}

	// HOOK - AfterServe
	if !request.IsExited() {
		s.callHookHandler(HOOK_AFTER_SERVE, request)
	}

	// HOOK - BeforeOutput
	if !request.IsExited() {
		s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
	}

	// HTTP status checking.
	if request.Response.Status == 0 {
		if request.StaticFile != nil || request.Middleware.served || request.Response.buffer.Len() > 0 {
			request.Response.WriteHeader(http.StatusOK)
		} else {
			request.Response.WriteHeader(http.StatusNotFound)
		}
	}
	// HTTP status handler.
	if request.Response.Status != http.StatusOK {
		if f := s.getStatusHandler(request.Response.Status, request); f != nil {
			// Call custom status handler.
			niceCallFunc(func() {
				f(request)
			})
		}
	}

	// Automatically set the session id to cookie
	// if it creates a new session id in this request.
	if request.Session.IsDirty() && request.Session.Id() != request.GetSessionId() {
		request.Cookie.SetSessionId(request.Session.Id())
	}
	// Output the cookie content to client.
	request.Cookie.Flush()
	// Output the buffer content to client.
	request.Response.Flush()
	// HOOK - AfterOutput
	if !request.IsExited() {
		s.callHookHandler(HOOK_AFTER_OUTPUT, request)
	}
}

// searchStaticFile searches the file with given URI.
// It returns a file struct specifying the file information.
func (s *Server) searchStaticFile(uri string) *StaticFile {
	var file *gres.File
	var path string
	var dir bool
	// Firstly search the StaticPaths mapping.
	if len(s.config.StaticPaths) > 0 {
		for _, item := range s.config.StaticPaths {
			if len(uri) >= len(item.prefix) && strings.EqualFold(item.prefix, uri[0:len(item.prefix)]) {
				// To avoid case like: /static/style -> /static/style.css
				if len(uri) > len(item.prefix) && uri[len(item.prefix)] != '/' {
					continue
				}
				file = gres.GetWithIndex(item.path+uri[len(item.prefix):], s.config.IndexFiles)
				if file != nil {
					return &StaticFile{
						File:  file,
						IsDir: file.FileInfo().IsDir(),
					}
				}
				path, dir = gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...)
				if path != "" {
					return &StaticFile{
						Path:  path,
						IsDir: dir,
					}
				}

			}
		}
	}
	// Secondly search the root and searching paths.
	if len(s.config.SearchPaths) > 0 {
		for _, p := range s.config.SearchPaths {
			file = gres.GetWithIndex(p+uri, s.config.IndexFiles)
			if file != nil {
				return &StaticFile{
					File:  file,
					IsDir: file.FileInfo().IsDir(),
				}
			}
			if path, dir = gspath.Search(p, uri, s.config.IndexFiles...); path != "" {
				return &StaticFile{
					Path:  path,
					IsDir: dir,
				}
			}
		}
	}
	// Lastly search the resource manager.
	if len(s.config.StaticPaths) == 0 && len(s.config.SearchPaths) == 0 {
		if file = gres.GetWithIndex(uri, s.config.IndexFiles); file != nil {
			return &StaticFile{
				File:  file,
				IsDir: file.FileInfo().IsDir(),
			}
		}
	}
	return nil
}

// serveFile serves the static file for client.
// The optional parameter <allowIndex> specifies if allowing directory listing if <f> is directory.
func (s *Server) serveFile(r *Request, f *StaticFile, allowIndex ...bool) {
	// Use resource file from memory.
	if f.File != nil {
		if f.IsDir {
			if s.config.IndexFolder || (len(allowIndex) > 0 && allowIndex[0]) {
				s.listDir(r, f.File)
			} else {
				r.Response.WriteStatus(http.StatusForbidden)
			}
		} else {
			info := f.File.FileInfo()
			r.Response.wroteHeader = true
			http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), f.File)
		}
		return
	}
	// Use file from dist.
	file, err := os.Open(f.Path)
	if err != nil {
		r.Response.WriteStatus(http.StatusForbidden)
		return
	}
	defer file.Close()

	// Clear the response buffer before file serving.
	// It ignores all custom buffer content and uses the file content.
	r.Response.ClearBuffer()

	info, _ := file.Stat()
	if info.IsDir() {
		if s.config.IndexFolder || (len(allowIndex) > 0 && allowIndex[0]) {
			s.listDir(r, file)
		} else {
			r.Response.WriteStatus(http.StatusForbidden)
		}
	} else {
		r.Response.wroteHeader = true
		http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), file)
	}
}

// listDir lists the sub files of specified directory as HTML content to client.
func (s *Server) listDir(r *Request, f http.File) {
	files, err := f.Readdir(-1)
	if err != nil {
		r.Response.WriteStatus(http.StatusInternalServerError, "Error reading directory")
		return
	}
	// The folder type has the most priority than file.
	sort.Slice(files, func(i, j int) bool {
		if files[i].IsDir() && !files[j].IsDir() {
			return true
		}
		if !files[i].IsDir() && files[j].IsDir() {
			return false
		}
		return files[i].Name() < files[j].Name()
	})
	if r.Response.Header().Get("Content-Type") == "" {
		r.Response.Header().Set("Content-Type", "text/html; charset=utf-8")
	}
	r.Response.Write(`<html>`)
	r.Response.Write(`<head>`)
	r.Response.Write(`<style>`)
	r.Response.Write(`body {font-family:Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;}`)
	r.Response.Write(`</style>`)
	r.Response.Write(`</head>`)
	r.Response.Write(`<body>`)
	r.Response.Writef(`<h1>Index of %s</h1>`, r.URL.Path)
	r.Response.Writef(`<hr />`)
	r.Response.Write(`<table>`)
	if r.URL.Path != "/" {
		r.Response.Write(`<tr>`)
		r.Response.Writef(`<td><a href="%s">..</a></td>`, gfile.Dir(r.URL.Path))
		r.Response.Write(`</tr>`)
	}
	name := ""
	size := ""
	prefix := gstr.TrimRight(r.URL.Path, "/")
	for _, file := range files {
		name = file.Name()
		size = gfile.FormatSize(file.Size())
		if file.IsDir() {
			name += "/"
			size = "-"
		}
		r.Response.Write(`<tr>`)
		r.Response.Writef(`<td><a href="%s/%s">%s</a></td>`, prefix, name, ghtml.SpecialChars(name))
		r.Response.Writef(`<td style="width:300px;text-align:center;">%s</td>`, gtime.New(file.ModTime()).ISO8601())
		r.Response.Writef(`<td style="width:80px;text-align:right;">%s</td>`, size)
		r.Response.Write(`</tr>`)
	}
	r.Response.Write(`</table>`)
	r.Response.Write(`</body>`)
	r.Response.Write(`</html>`)
}
<|endoftext|>


<commit_before>package sack

import (
	"fmt"
	"io/ioutil"
	"path"
	"strings"
)

func checkState() {}

func splitLine(s string) []string {
	arr := strings.SplitN(s, ":", 3)
	return arr
}

func check(e error) {
	if e != nil {
		fmt.Printf("\n----\nError: %#v\n----\n", e)
		panic(e)
	}
}

func content() []string {
	filePath := path.Join(home, shortcutFilename)
	dat, err := ioutil.ReadFile(filePath)
	check(err)
	lines := strings.Split(string(dat), "\n")
	return lines[0 : len(lines)-1]
}
<commit_msg>Improve error message for no shortcut file<commit_after>package sack

import (
	"fmt"
	"io/ioutil"
	"path"
	"strings"
)

func checkState() {}

func splitLine(s string) []string {
	arr := strings.SplitN(s, ":", 3)
	return arr
}

func check(e error) {
	if e != nil {
		fmt.Printf("\n----\nError: %#v\n----\n", e)
		panic(e)
	}
}

func content() []string {
	filePath := path.Join(home, shortcutFilename)
	dat, err := ioutil.ReadFile(filePath)
	if err != nil {
		fmt.Println("Unable to open shortcut file. Try doing a search.")
		panic(1)
	}
	lines := strings.Split(string(dat), "\n")
	return lines[0 : len(lines)-1]
}
<|endoftext|>


<commit_before>package sack

func Version() string {
	return "0.2.5"
}
<commit_msg>Version bump<commit_after>package sack

func Version() string {
	return "0.2.6"
}
<|endoftext|>


<commit_before>package sql

import "github.com/jen20/riviera/azure"

type CreateOrUpdateDatabaseResponse struct {
	ID                             *string            `mapstructure:"id"`
	Name                           *string            `mapstructure:"name"`
	Location                       *string            `mapstructure:"location"`
	Tags                           *map[string]string `mapstructure:"tags"`
	DatabaseID                     *string            `mapstructure:"databaseId"`
	DatabaseName                   *string            `mapstructure:"databaseName"`
	Edition                        *string            `mapstructure:"edition"`
	ServiceLevelObjective          *string            `mapstructure:"serviceLevelObjective"`
	MaxSizeInBytes                 *string            `mapstructure:"maxSizeInBytes"`
	CreationDate                   *string            `mapstructure:"creationDate"`
	CurrentServiceLevelObjectiveID *string            `mapstructure:"currentServiceLevelObjectiveId"`
	RequestedServiceObjectiveID    *string            `mapstructure:"requestedServiceObjectiveId"`
	DefaultSecondaryLocation       *string            `mapstructure:"defaultSecondaryLocation"`
	Encryption                     *string            `mapstructure:"encryption"`
}

type CreateOrUpdateDatabase struct {
	Name                          string             `json:"-"`
	ResourceGroupName             string             `json:"-"`
	ServerName                    string             `json:"-"`
	Location                      string             `json:"-" riviera:"location"`
	Tags                          map[string]*string `json:"-" riviera:"tags"`
	Edition                       *string            `json:"edition,omitempty"`
	Collation                     *string            `json:"collation,omitempty"`
	MaxSizeBytes                  *string            `json:"maxSizeBytes,omitempty"`
	RequestedServiceObjectiveName *string            `json:"requestedServiceObjectiveName,omitempty"`
	RequestedServiceObjectiveId   *string            `json:"requestedServiceObjectiveId,omitempty"`
	CreateMode                    *string            `json:"createMode,omitempty"`
	SourceDatabaseID              *string            `json:"sourceDatabaseId,omitempty"`
	SourceDatabaseDeletionDate    *string            `json:"sourceDatabaseDeletionDate,omitempty"`
	RestorePointInTime            *string            `json:"restorePointInTime,omitempty"`
	ElasticPoolName               *string            `json:"elasticPoolName,omitempty"`
}

func (s CreateOrUpdateDatabase) APIInfo() azure.APIInfo {
	return azure.APIInfo{
		APIVersion:  apiVersion,
		Method:      "PUT",
		URLPathFunc: sqlDatabaseDefaultURLPath(s.ResourceGroupName, s.ServerName, s.Name),
		ResponseTypeFunc: func() interface{} {
			return &CreateOrUpdateDatabaseResponse{}
		},
	}
}
<commit_msg>sql: ObjectiveId => ObjectiveID for idiomatic Go<commit_after>package sql

import "github.com/jen20/riviera/azure"

type CreateOrUpdateDatabaseResponse struct {
	ID                             *string            `mapstructure:"id"`
	Name                           *string            `mapstructure:"name"`
	Location                       *string            `mapstructure:"location"`
	Tags                           *map[string]string `mapstructure:"tags"`
	DatabaseID                     *string            `mapstructure:"databaseId"`
	DatabaseName                   *string            `mapstructure:"databaseName"`
	Edition                        *string            `mapstructure:"edition"`
	ServiceLevelObjective          *string            `mapstructure:"serviceLevelObjective"`
	MaxSizeInBytes                 *string            `mapstructure:"maxSizeInBytes"`
	CreationDate                   *string            `mapstructure:"creationDate"`
	CurrentServiceLevelObjectiveID *string            `mapstructure:"currentServiceLevelObjectiveId"`
	RequestedServiceObjectiveID    *string            `mapstructure:"requestedServiceObjectiveId"`
	DefaultSecondaryLocation       *string            `mapstructure:"defaultSecondaryLocation"`
	Encryption                     *string            `mapstructure:"encryption"`
}

type CreateOrUpdateDatabase struct {
	Name                          string             `json:"-"`
	ResourceGroupName             string             `json:"-"`
	ServerName                    string             `json:"-"`
	Location                      string             `json:"-" riviera:"location"`
	Tags                          map[string]*string `json:"-" riviera:"tags"`
	Edition                       *string            `json:"edition,omitempty"`
	Collation                     *string            `json:"collation,omitempty"`
	MaxSizeBytes                  *string            `json:"maxSizeBytes,omitempty"`
	RequestedServiceObjectiveName *string            `json:"requestedServiceObjectiveName,omitempty"`
	RequestedServiceObjectiveID   *string            `json:"requestedServiceObjectiveId,omitempty"`
	CreateMode                    *string            `json:"createMode,omitempty"`
	SourceDatabaseID              *string            `json:"sourceDatabaseId,omitempty"`
	SourceDatabaseDeletionDate    *string            `json:"sourceDatabaseDeletionDate,omitempty"`
	RestorePointInTime            *string            `json:"restorePointInTime,omitempty"`
	ElasticPoolName               *string            `json:"elasticPoolName,omitempty"`
}

func (s CreateOrUpdateDatabase) APIInfo() azure.APIInfo {
	return azure.APIInfo{
		APIVersion:  apiVersion,
		Method:      "PUT",
		URLPathFunc: sqlDatabaseDefaultURLPath(s.ResourceGroupName, s.ServerName, s.Name),
		ResponseTypeFunc: func() interface{} {
			return &CreateOrUpdateDatabaseResponse{}
		},
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 The go-gl 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 gltext

import (
	"fmt"
	"github.com/go-gl/glow/gl-core/3.3/gl"
	"github.com/go-gl/mathgl/mgl32"
)

type Text struct {
	// The desired color of the text
	color mgl32.Vec4

	// scaling the text
	Scale       float32
	ScaleMax    float32
	scaleMatrix mgl32.Mat4

	vao           uint32
	vbo           uint32
	ebo           uint32
	windowWidth   float32
	windowHeight  float32
	vboData       []float32
	vboIndexCount int
	eboData       []int32
	eboIndexCount int

	// X1, X2: the lower left and upper right points of a box that bounds the text
	X1 Point
	X2 Point
}

func LoadText(f *Font) (t *Text, err error) {
	t = new(Text)

	// text hover values - implicit ScaleMin of 1.0
	t.ScaleMax = 1.1
	t.SetScale(1)

	// size of glfloat
	glfloat_size := int32(4)

	// stride of the buffered data
	xy_count := int32(2)
	stride := xy_count + int32(2)

	gl.GenVertexArrays(1, &t.vao)
	gl.GenBuffers(1, &t.vbo)
	gl.GenBuffers(1, &t.ebo)

	// vao
	gl.BindVertexArray(t.vao)

	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, f.textureID)

	// vbo
	// specify the buffer for which the VertexAttribPointer calls apply
	gl.BindBuffer(gl.ARRAY_BUFFER, t.vbo)

	gl.EnableVertexAttribArray(f.position)
	gl.VertexAttribPointer(
		f.position,
		2,
		gl.FLOAT,
		false,
		glfloat_size*stride,
		gl.PtrOffset(0),
	)

	gl.EnableVertexAttribArray(f.uv)
	gl.VertexAttribPointer(
		f.uv,
		2,
		gl.FLOAT,
		false,
		glfloat_size*stride,
		gl.PtrOffset(int(glfloat_size*xy_count)),
	)

	// ebo
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, t.ebo)

	// i am guessing that order is important here
	gl.BindVertexArray(0)
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)

	return t, nil
}

// Release releases font resources.
// A font can no longer be used for rendering after this call completes.
func (t *Text) Release() {
	gl.DeleteBuffers(1, &t.vbo)
	gl.DeleteBuffers(1, &t.ebo)
	gl.DeleteBuffers(1, &t.vao)
}

func (t *Text) SetScale(s float32) (changed bool) {
	if s > t.ScaleMax || s < 1.0 {
		return
	}
	changed = true
	t.Scale = s
	t.scaleMatrix = mgl32.Scale3D(s, s, s)
	return
}

func (t *Text) AddScale(s float32) (changed bool) {
	if s < 0 && t.Scale <= 1.0 {
		return
	}
	if s > 0 && t.Scale >= t.ScaleMax {
		return
	}
	changed = true
	t.Scale += s
	t.scaleMatrix = mgl32.Scale3D(t.Scale, t.Scale, t.Scale)
	return
}

func (t *Text) SetColor(r, g, b, a float32) {
	t.color = mgl32.Vec4{r, g, b, a}
}

func (t *Text) SetString(f *Font, fs string, argv ...interface{}) (Point, Point) {
	indices := []rune(fmt.Sprintf(fs, argv...))
	if len(indices) == 0 {
		return Point{}, Point{}
	}
	// ebo, vbo data
	t.vboIndexCount = len(indices) * 4 * 2 * 2 // 4 indexes per rune (containing 2 position + 2 texture)
	t.eboIndexCount = len(indices) * 6         // each rune requires 6 triangle indices for a quad
	t.vboData = make([]float32, t.vboIndexCount, t.vboIndexCount)
	t.eboData = make([]int32, t.eboIndexCount, t.eboIndexCount)
	t.makeBufferData(f, indices)
	return t.X1, t.X2
}

func (t *Text) SetPosition(x, y float32) {
	t.setDataPosition(x, y)
	if debug {
		fmt.Printf("vbo data\n%v\n", t.vboData)
		fmt.Printf("ebo data\n%v\n", t.eboData)
	}
	glfloat_size := int32(4)

	// setup context
	gl.BindVertexArray(t.vao)
	gl.BindBuffer(gl.ARRAY_BUFFER, t.vbo)
	gl.BufferData(
		gl.ARRAY_BUFFER, int(glfloat_size)*t.vboIndexCount, gl.Ptr(t.vboData), gl.DYNAMIC_DRAW)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, t.ebo)
	gl.BufferData(
		gl.ELEMENT_ARRAY_BUFFER, int(glfloat_size)*t.eboIndexCount, gl.Ptr(t.eboData), gl.DYNAMIC_DRAW)
	gl.BindVertexArray(0)

	// not necesssary, but i just want to better understand using vertex arrays
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
	return
}

func (t *Text) Draw(f *Font) {
	gl.UseProgram(f.program)

	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, f.textureID)

	// uniforms
	gl.Uniform1i(f.fragmentTextureUniform, 0)
	gl.Uniform1f(f.textLowerBoundUniform, f.textLowerBound)
	gl.Uniform4fv(f.colorUniform, 1, &t.color[0])
	gl.UniformMatrix4fv(f.orthographicMatrixUniform, 1, false, &f.orthographicMatrix[0])
	gl.UniformMatrix4fv(f.scaleMatrixUniform, 1, false, &t.scaleMatrix[0])

	// draw
	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	gl.BindVertexArray(t.vao)
	gl.DrawElements(gl.TRIANGLES, int32(t.eboIndexCount), gl.UNSIGNED_INT, nil)
	gl.BindVertexArray(0)
	gl.Disable(gl.BLEND)
}

func (t *Text) getBoundingBox(vboIndex int) {
	// index -4: x, index -3: y, index -2: uv's x, index -1 uv's y
	x := t.vboData[vboIndex-4]
	y := t.vboData[vboIndex-3]

	if vboIndex-4 == 0 {
		t.X1.X = x
		t.X1.Y = y
	} else {
		if x < t.X1.X {
			t.X1.X = x
		}
		if y < t.X1.Y {
			t.X1.Y = y
		}
		if x > t.X2.X {
			t.X2.X = x
		}
		if y > t.X2.Y {
			t.X2.Y = y
		}
	}
}

// all text originally sits at point (0,0) which is the
// lower left hand corner of the screen.
func (t *Text) setDataPosition(x, y float32) {
	length := len(t.vboData)
	for index := 0; index < length; {
		// index (0,0)
		t.vboData[index] += x
		index++
		t.vboData[index] += y
		index += 3 // skip texture data

		// index (1,0)
		t.vboData[index] += x
		index++
		t.vboData[index] += y
		index += 3

		// index (1,1)
		t.vboData[index] += x
		index++
		t.vboData[index] += y
		index += 3

		// index (0,1)
		t.vboData[index] += x
		index++
		t.vboData[index] += y
		index += 3
	}
	// update screen position
	t.X1.X += x
	t.X2.X += x
	t.X1.Y += y
	t.X2.Y += y
}

// currently only supports left to right text flow
func (t *Text) makeBufferData(f *Font, indices []rune) {
	glyphs := f.config.Glyphs
	low := f.config.Low

	vboIndex := 0
	eboIndex := 0
	lineX := float32(0)
	eboOffset := int32(0)
	for _, r := range indices {
		r -= low
		if r >= 0 && int(r) < len(glyphs) {
			vw := float32(glyphs[r].Width)
			vh := float32(glyphs[r].Height)
			tP1, tP2 := glyphs[r].GetIndices(f)

			// counter-clockwise quad

			// index (0,0)
			t.vboData[vboIndex] = lineX // position
			vboIndex++
			t.vboData[vboIndex] = 0
			vboIndex++
			t.vboData[vboIndex] = tP1.X // texture uv
			vboIndex++
			t.vboData[vboIndex] = tP2.Y
			vboIndex++
			t.getBoundingBox(vboIndex)

			// index (1,0)
			t.vboData[vboIndex] = lineX + vw
			vboIndex++
			t.vboData[vboIndex] = 0
			vboIndex++
			t.vboData[vboIndex] = tP2.X
			vboIndex++
			t.vboData[vboIndex] = tP2.Y
			vboIndex++
			t.getBoundingBox(vboIndex)

			// index (1,1)
			t.vboData[vboIndex] = lineX + vw
			vboIndex++
			t.vboData[vboIndex] = vh
			vboIndex++
			t.vboData[vboIndex] = tP2.X
			vboIndex++
			t.vboData[vboIndex] = tP1.Y
			vboIndex++
			t.getBoundingBox(vboIndex)

			// index (0,1)
			t.vboData[vboIndex] = lineX
			vboIndex++
			t.vboData[vboIndex] = vh
			vboIndex++
			t.vboData[vboIndex] = tP1.X
			vboIndex++
			t.vboData[vboIndex] = tP1.Y
			vboIndex++
			t.getBoundingBox(vboIndex)

			advance := float32(glyphs[r].Advance)
			lineX += advance

			// ebo data
			t.eboData[eboIndex] = 0 + eboOffset
			eboIndex++
			t.eboData[eboIndex] = 1 + eboOffset
			eboIndex++
			t.eboData[eboIndex] = 2 + eboOffset
			eboIndex++

			t.eboData[eboIndex] = 0 + eboOffset
			eboIndex++
			t.eboData[eboIndex] = 2 + eboOffset
			eboIndex++
			t.eboData[eboIndex] = 3 + eboOffset
			eboIndex++
			eboOffset += 4
		}
	}
	return
}
<commit_msg>Keep a pointer to the font object in every text object.<commit_after>// Copyright 2012 The go-gl 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 gltext

import (
	"fmt"
	"github.com/go-gl/glow/gl-core/3.3/gl"
	"github.com/go-gl/mathgl/mgl32"
)

type Text struct {
	font *Font

	// The desired color of the text
	color mgl32.Vec4

	// scaling the text
	Scale       float32
	ScaleMax    float32
	scaleMatrix mgl32.Mat4

	vao           uint32
	vbo           uint32
	ebo           uint32
	windowWidth   float32
	windowHeight  float32
	vboData       []float32
	vboIndexCount int
	eboData       []int32
	eboIndexCount int

	// X1, X2: the lower left and upper right points of a box that bounds the text
	X1 Point
	X2 Point
}

func LoadText(f *Font) (t *Text, err error) {
	t = new(Text)
	t.font = f

	// text hover values - implicit ScaleMin of 1.0
	t.ScaleMax = 1.1
	t.SetScale(1)

	// size of glfloat
	glfloat_size := int32(4)

	// stride of the buffered data
	xy_count := int32(2)
	stride := xy_count + int32(2)

	gl.GenVertexArrays(1, &t.vao)
	gl.GenBuffers(1, &t.vbo)
	gl.GenBuffers(1, &t.ebo)

	// vao
	gl.BindVertexArray(t.vao)

	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, t.font.textureID)

	// vbo
	// specify the buffer for which the VertexAttribPointer calls apply
	gl.BindBuffer(gl.ARRAY_BUFFER, t.vbo)

	gl.EnableVertexAttribArray(t.font.position)
	gl.VertexAttribPointer(
		t.font.position,
		2,
		gl.FLOAT,
		false,
		glfloat_size*stride,
		gl.PtrOffset(0),
	)

	gl.EnableVertexAttribArray(t.font.uv)
	gl.VertexAttribPointer(
		t.font.uv,
		2,
		gl.FLOAT,
		false,
		glfloat_size*stride,
		gl.PtrOffset(int(glfloat_size*xy_count)),
	)

	// ebo
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, t.ebo)

	// i am guessing that order is important here
	gl.BindVertexArray(0)
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)

	return t, nil
}

// Release releases font resources.
// A font can no longer be used for rendering after this call completes.
func (t *Text) Release() {
	gl.DeleteBuffers(1, &t.vbo)
	gl.DeleteBuffers(1, &t.ebo)
	gl.DeleteBuffers(1, &t.vao)
}

func (t *Text) SetScale(s float32) (changed bool) {
	if s > t.ScaleMax || s < 1.0 {
		return
	}
	changed = true
	t.Scale = s
	t.scaleMatrix = mgl32.Scale3D(s, s, s)
	return
}

func (t *Text) AddScale(s float32) (changed bool) {
	if s < 0 && t.Scale <= 1.0 {
		return
	}
	if s > 0 && t.Scale >= t.ScaleMax {
		return
	}
	changed = true
	t.Scale += s
	t.scaleMatrix = mgl32.Scale3D(t.Scale, t.Scale, t.Scale)
	return
}

func (t *Text) SetColor(r, g, b, a float32) {
	t.color = mgl32.Vec4{r, g, b, a}
}

func (t *Text) SetString(f *Font, fs string, argv ...interface{}) (Point, Point) {
	indices := []rune(fmt.Sprintf(fs, argv...))
	if len(indices) == 0 {
		return Point{}, Point{}
	}
	// ebo, vbo data
	t.vboIndexCount = len(indices) * 4 * 2 * 2 // 4 indexes per rune (containing 2 position + 2 texture)
	t.eboIndexCount = len(indices) * 6         // each rune requires 6 triangle indices for a quad
	t.vboData = make([]float32, t.vboIndexCount, t.vboIndexCount)
	t.eboData = make([]int32, t.eboIndexCount, t.eboIndexCount)
	t.makeBufferData(indices)
	return t.X1, t.X2
}

func (t *Text) SetPosition(x, y float32) {
	t.setDataPosition(x, y)
	if debug {
		fmt.Printf("vbo data\n%v\n", t.vboData)
		fmt.Printf("ebo data\n%v\n", t.eboData)
	}
	glfloat_size := int32(4)

	// setup context
	gl.BindVertexArray(t.vao)
	gl.BindBuffer(gl.ARRAY_BUFFER, t.vbo)
	gl.BufferData(
		gl.ARRAY_BUFFER, int(glfloat_size)*t.vboIndexCount, gl.Ptr(t.vboData), gl.DYNAMIC_DRAW)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, t.ebo)
	gl.BufferData(
		gl.ELEMENT_ARRAY_BUFFER, int(glfloat_size)*t.eboIndexCount, gl.Ptr(t.eboData), gl.DYNAMIC_DRAW)
	gl.BindVertexArray(0)

	// not necesssary, but i just want to better understand using vertex arrays
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
	return
}

func (t *Text) Draw() {
	gl.UseProgram(t.font.program)

	gl.ActiveTexture(gl.TEXTURE0)
	gl.BindTexture(gl.TEXTURE_2D, t.font.textureID)

	// uniforms
	gl.Uniform1i(t.font.fragmentTextureUniform, 0)
	gl.Uniform1f(t.font.textLowerBoundUniform, t.font.textLowerBound)
	gl.Uniform4fv(t.font.colorUniform, 1, &t.color[0])
	gl.UniformMatrix4fv(t.font.orthographicMatrixUniform, 1, false, &t.font.orthographicMatrix[0])
	gl.UniformMatrix4fv(t.font.scaleMatrixUniform, 1, false, &t.scaleMatrix[0])

	// draw
	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	gl.BindVertexArray(t.vao)
	gl.DrawElements(gl.TRIANGLES, int32(t.eboIndexCount), gl.UNSIGNED_INT, nil)
	gl.BindVertexArray(0)
	gl.Disable(gl.BLEND)
}

func (t *Text) getBoundingBox(vboIndex int) {
	// index -4: x, index -3: y, index -2: uv's x, index -1 uv's y
	x := t.vboData[vboIndex-4]
	y := t.vboData[vboIndex-3]

	if vboIndex-4 == 0 {
		t.X1.X = x
		t.X1.Y = y
	} else {
		if x < t.X1.X {
			t.X1.X = x
		}
		if y < t.X1.Y {
			t.X1.Y = y
		}
		if x > t.X2.X {
			t.X2.X = x
		}
		if y > t.X2.Y {
			t.X2.Y = y
		}
	}
}

// all text originally sits at point (0,0) which is the
// lower left hand corner of the screen.
func (t *Text) setDataPosition(x, y float32) {
	length := len(t.vboData)
	for index := 0; index < length; {
		// index (0,0)
		t.vboData[index] += x
		index++
		t.vboData[index] += y
		index += 3 // skip texture data

		// index (1,0)
		t.vboData[index] += x
		index++
		t.vboData[index] += y
		index += 3

		// index (1,1)
		t.vboData[index] += x
		index++
		t.vboData[index] += y
		index += 3

		// index (0,1)
		t.vboData[index] += x
		index++
		t.vboData[index] += y
		index += 3
	}
	// update screen position
	t.X1.X += x
	t.X2.X += x
	t.X1.Y += y
	t.X2.Y += y
}

// currently only supports left to right text flow
func (t *Text) makeBufferData(indices []rune) {
	glyphs := t.font.config.Glyphs
	low := t.font.config.Low

	vboIndex := 0
	eboIndex := 0
	lineX := float32(0)
	eboOffset := int32(0)
	for _, r := range indices {
		r -= low
		if r >= 0 && int(r) < len(glyphs) {
			vw := float32(glyphs[r].Width)
			vh := float32(glyphs[r].Height)
			tP1, tP2 := glyphs[r].GetIndices(t.font)

			// counter-clockwise quad

			// index (0,0)
			t.vboData[vboIndex] = lineX // position
			vboIndex++
			t.vboData[vboIndex] = 0
			vboIndex++
			t.vboData[vboIndex] = tP1.X // texture uv
			vboIndex++
			t.vboData[vboIndex] = tP2.Y
			vboIndex++
			t.getBoundingBox(vboIndex)

			// index (1,0)
			t.vboData[vboIndex] = lineX + vw
			vboIndex++
			t.vboData[vboIndex] = 0
			vboIndex++
			t.vboData[vboIndex] = tP2.X
			vboIndex++
			t.vboData[vboIndex] = tP2.Y
			vboIndex++
			t.getBoundingBox(vboIndex)

			// index (1,1)
			t.vboData[vboIndex] = lineX + vw
			vboIndex++
			t.vboData[vboIndex] = vh
			vboIndex++
			t.vboData[vboIndex] = tP2.X
			vboIndex++
			t.vboData[vboIndex] = tP1.Y
			vboIndex++
			t.getBoundingBox(vboIndex)

			// index (0,1)
			t.vboData[vboIndex] = lineX
			vboIndex++
			t.vboData[vboIndex] = vh
			vboIndex++
			t.vboData[vboIndex] = tP1.X
			vboIndex++
			t.vboData[vboIndex] = tP1.Y
			vboIndex++
			t.getBoundingBox(vboIndex)

			advance := float32(glyphs[r].Advance)
			lineX += advance

			// ebo data
			t.eboData[eboIndex] = 0 + eboOffset
			eboIndex++
			t.eboData[eboIndex] = 1 + eboOffset
			eboIndex++
			t.eboData[eboIndex] = 2 + eboOffset
			eboIndex++

			t.eboData[eboIndex] = 0 + eboOffset
			eboIndex++
			t.eboData[eboIndex] = 2 + eboOffset
			eboIndex++
			t.eboData[eboIndex] = 3 + eboOffset
			eboIndex++
			eboOffset += 4
		}
	}
	return
}
<|endoftext|>


<commit_before>package conv

import (
	"math"
	"math/cmplx"
	"reflect"
	"strconv"
	"time"
)

func (c Conv) convStrToDuration(v string) (time.Duration, bool) {
	if parsed, err := time.ParseDuration(v); err == nil {
		return parsed, true
	}
	if parsed, err := strconv.ParseInt(v, 10, 0); err == nil {
		// @TODO This feels more natural but maybe add a option to disable since
		// it breaks Duration() -> String() -> Duration()
		return time.Duration(parsed) * time.Second, true
	}
	if parsed, err := strconv.ParseFloat(v, 64); err == nil {
		return time.Duration(1e9 * parsed), true
	}
	return 0, false
}

func (c Conv) convNumToDuration(
	k reflect.Kind, v reflect.Value) (time.Duration, bool) {
	switch {
	case isKindInt(k):
		return time.Duration(v.Int()), true
	case isKindUint(k):
		T := v.Uint()
		if T > math.MaxInt64 {
			T = math.MaxInt64
		}
		return time.Duration(T), true
	case isKindFloat(k):
		T := v.Float()
		if math.IsNaN(T) || math.IsInf(T, 0) {
			return 0, true
		}
		return time.Duration(1e9 * T), true
	case isKindComplex(k):
		T := v.Complex()
		if cmplx.IsNaN(T) || cmplx.IsInf(T) {
			return 0, true
		}
		return time.Duration(1e9 * real(T)), true
	}
	return 0, false
}

// Duration attempts to convert the given value to time.Duration, returns the
// zero value and an error on failure.
func (c Conv) Duration(from interface{}) (time.Duration, error) {
	if T, ok := from.(time.Duration); ok {
		return T, nil
	}
	if c, ok := from.(interface {
		Duration() (time.Duration, error)
	}); ok {
		return c.Duration()
	}

	value := reflect.ValueOf(indirect(from))
	kind := value.Kind()
	switch {
	case reflect.String == kind:
		if parsed, ok := c.convStrToDuration(value.String()); ok {
			return parsed, nil
		}
	case isKindNumeric(kind):
		if parsed, ok := c.convNumToDuration(kind, value); ok {
			return parsed, nil
		}
	case reflect.Struct == kind && value.CanInterface():
		v := value.Interface()
		if T, ok := v.(time.Time); ok {
			// @TODO I find this useful but is it WTF?
			return time.Since(T), nil
		}
	}
	return 0, newConvErr(from, "time.Duration")
}

// Time attempts to convert the given value to time.Time, returns the zero value
// of time.Time and an error on failure.
func (c Conv) Time(from interface{}) (time.Time, error) {
	if T, ok := from.(time.Time); ok {
		return T, nil
	} else if T, ok := from.(*time.Time); ok {
		return *T, nil
	} else if c, ok := from.(interface {
		Time() (time.Time, error)
	}); ok {
		return c.Time()
	}

	value := reflect.ValueOf(indirect(from))
	kind := value.Kind()
	switch {
	case reflect.String == kind:
		if T, ok := timeFromString(value.String()); ok {
			return T, nil
		}
	case reflect.Struct == kind:
		if value.Type().ConvertibleTo(typeOfTime) {
			valueConv := value.Convert(typeOfTime)
			if valueConv.CanInterface() {
				return valueConv.Interface().(time.Time), nil
			}
		}
		field := value.FieldByName("Time")
		if field.IsValid() && field.CanInterface() {
			return c.Time(field.Interface())
		}
	}
	return emptyTime, newConvErr(from, "time.Time")
}

type formatInfo struct {
	format string
	needed string
}

var formats = []formatInfo{
	{time.RFC3339Nano, ""},
	{time.RFC3339, ""},
	{time.RFC850, ""},
	{time.RFC1123, ""},
	{time.RFC1123Z, ""},
	{"02 Jan 06 15:04:05", ""},
	{"02 Jan 06 15:04:05 +-0700", ""},
	{"02 Jan 06 15:4:5 MST", ""},
	{"02 Jan 2006 15:04:05", ""},
	{"2 Jan 2006 15:04:05", ""},
	{"2 Jan 2006 15:04:05 MST", ""},
	{"2 Jan 2006 15:04:05 -0700", ""},
	{"2 Jan 2006 15:04:05 -0700 (MST)", ""},
	{"02 January 2006 15:04", ""},
	{"02 Jan 2006 15:04 MST", ""},
	{"02 Jan 2006 15:04:05 MST", ""},
	{"02 Jan 2006 15:04:05 -0700", ""},
	{"02 Jan 2006 15:04:05 -0700 (MST)", ""},
	{"Mon, 2 Jan  15:04:05 MST 2006", ""},
	{"Mon, 2 Jan 15:04:05 MST 2006", ""},
	{"Mon, 02 Jan 2006 15:04:05", ""},
	{"Mon, 02 Jan 2006 15:04:05 (MST)", ""},
	{"Mon, 2 Jan 2006 15:04:05", ""},
	{"Mon, 2 Jan 2006 15:04:05 MST", ""},
	{"Mon, 2 Jan 2006 15:04:05 -0700", ""},
	{"Mon, 2 Jan 2006 15:04:05 -0700 (MST)", ""},
	{"Mon, 02 Jan 06 15:04:05 MST", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700 MST", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700 (MST)", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700 (MST-07:00)", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700 (MST MST)", ""},
	{"Mon, 02 Jan 2006 15:04 -0700", ""},
	{"Mon, 02 Jan 2006 15:04 -0700 (MST)", ""},
	{"Mon Jan 02 15:05:05 2006 MST", ""},
	{"Monday, 02 Jan 2006 15:04 -0700", ""},
	{"Monday, 02 Jan 2006 15:04:05 -0700", ""},
	{time.UnixDate, ""},
	{time.RubyDate, ""},
	{time.RFC822, ""},
	{time.RFC822Z, ""},
}

// Quick google yields no date parsing libraries, first thing that came to mind
// was trying all the formats in time package. This is reasonable enough until
// I can find a decent lexer or polish up my "timey" Go lib. I am using the
// table of dates politely released into public domain by github.com/tomarus:
//   https://github.com/tomarus/parsedate/blob/master/parsedate.go
func timeFromString(s string) (time.Time, bool) {
	if len(s) == 0 {
		return time.Time{}, false
	}
	for _, f := range formats {
		t, err := time.Parse(f.format, s)
		if err != nil {
			continue
		}
		if t, err = time.Parse(
			f.format+f.needed, s+time.Now().Format(f.needed)); err == nil {
			return t, true
		}
	}
	return time.Time{}, false
}
<commit_msg>Oops.<commit_after>package conv

import (
	"math"
	"math/cmplx"
	"reflect"
	"strconv"
	"time"
)

func (c Conv) convStrToDuration(v string) (time.Duration, bool) {
	if parsed, err := time.ParseDuration(v); err == nil {
		return parsed, true
	}
	if parsed, err := strconv.ParseInt(v, 10, 0); err == nil {
		// @TODO This feels more natural but maybe add a option to disable since
		// it breaks Duration() -> String() -> Duration()
		return time.Duration(parsed) * time.Second, true
	}
	if parsed, err := strconv.ParseFloat(v, 64); err == nil {
		return time.Duration(1e9 * parsed), true
	}
	return 0, false
}

func (c Conv) convNumToDuration(
	k reflect.Kind, v reflect.Value) (time.Duration, bool) {
	switch {
	case isKindInt(k):
		return time.Duration(v.Int()), true
	case isKindUint(k):
		T := v.Uint()
		if T > math.MaxInt64 {
			T = math.MaxInt64
		}
		return time.Duration(T), true
	case isKindFloat(k):
		T := v.Float()
		if math.IsNaN(T) || math.IsInf(T, 0) {
			return 0, true
		}
		return time.Duration(1e9 * T), true
	case isKindComplex(k):
		T := v.Complex()
		if cmplx.IsNaN(T) || cmplx.IsInf(T) {
			return 0, true
		}
		return time.Duration(1e9 * real(T)), true
	}
	return 0, false
}

// Duration attempts to convert the given value to time.Duration, returns the
// zero value and an error on failure.
func (c Conv) Duration(from interface{}) (time.Duration, error) {
	if T, ok := from.(time.Duration); ok {
		return T, nil
	}
	if c, ok := from.(interface {
		Duration() (time.Duration, error)
	}); ok {
		return c.Duration()
	}

	value := reflect.ValueOf(indirect(from))
	kind := value.Kind()
	switch {
	case reflect.String == kind:
		if parsed, ok := c.convStrToDuration(value.String()); ok {
			return parsed, nil
		}
	case isKindNumeric(kind):
		if parsed, ok := c.convNumToDuration(kind, value); ok {
			return parsed, nil
		}
	case reflect.Struct == kind && value.CanInterface():
		v := value.Interface()
		if T, ok := v.(time.Time); ok {
			// @TODO I find this useful but is it WTF?
			return time.Since(T), nil
		}
	}
	return 0, newConvErr(from, "time.Duration")
}

// Time attempts to convert the given value to time.Time, returns the zero value
// of time.Time and an error on failure.
func (c Conv) Time(from interface{}) (time.Time, error) {
	if T, ok := from.(time.Time); ok {
		return T, nil
	} else if T, ok := from.(*time.Time); ok {
		return *T, nil
	} else if c, ok := from.(interface {
		Time() (time.Time, error)
	}); ok {
		return c.Time()
	}

	value := reflect.ValueOf(indirect(from))
	kind := value.Kind()
	switch {
	case reflect.String == kind:
		if T, ok := timeFromString(value.String()); ok {
			return T, nil
		}
	case reflect.Struct == kind:
		if value.Type().ConvertibleTo(typeOfTime) {
			valueConv := value.Convert(typeOfTime)
			if valueConv.CanInterface() {
				return valueConv.Interface().(time.Time), nil
			}
		}
		field := value.FieldByName("Time")
		if field.IsValid() && field.CanInterface() {
			return c.Time(field.Interface())
		}
	}
	return emptyTime, newConvErr(from, "time.Time")
}

type formatInfo struct {
	format string
	needed string
}

var formats = []formatInfo{
	{time.RFC3339Nano, ""},
	{time.RFC3339, ""},
	{time.RFC850, ""},
	{time.RFC1123, ""},
	{time.RFC1123Z, ""},
	{"02 Jan 06 15:04:05", ""},
	{"02 Jan 06 15:04:05 +-0700", ""},
	{"02 Jan 06 15:4:5 MST", ""},
	{"02 Jan 2006 15:04:05", ""},
	{"2 Jan 2006 15:04:05", ""},
	{"2 Jan 2006 15:04:05 MST", ""},
	{"2 Jan 2006 15:04:05 -0700", ""},
	{"2 Jan 2006 15:04:05 -0700 (MST)", ""},
	{"02 January 2006 15:04", ""},
	{"02 Jan 2006 15:04 MST", ""},
	{"02 Jan 2006 15:04:05 MST", ""},
	{"02 Jan 2006 15:04:05 -0700", ""},
	{"02 Jan 2006 15:04:05 -0700 (MST)", ""},
	{"Mon, 2 Jan  15:04:05 MST 2006", ""},
	{"Mon, 2 Jan 15:04:05 MST 2006", ""},
	{"Mon, 02 Jan 2006 15:04:05", ""},
	{"Mon, 02 Jan 2006 15:04:05 (MST)", ""},
	{"Mon, 2 Jan 2006 15:04:05", ""},
	{"Mon, 2 Jan 2006 15:04:05 MST", ""},
	{"Mon, 2 Jan 2006 15:04:05 -0700", ""},
	{"Mon, 2 Jan 2006 15:04:05 -0700 (MST)", ""},
	{"Mon, 02 Jan 06 15:04:05 MST", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700 MST", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700 (MST)", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700 (MST-07:00)", ""},
	{"Mon, 02 Jan 2006 15:04:05 -0700 (MST MST)", ""},
	{"Mon, 02 Jan 2006 15:04 -0700", ""},
	{"Mon, 02 Jan 2006 15:04 -0700 (MST)", ""},
	{"Mon Jan 02 15:05:05 2006 MST", ""},
	{"Monday, 02 Jan 2006 15:04 -0700", ""},
	{"Monday, 02 Jan 2006 15:04:05 -0700", ""},
	{time.UnixDate, ""},
	{time.RubyDate, ""},
	{time.RFC822, ""},
	{time.RFC822Z, ""},
}

// Quick google yields no date parsing libraries, first thing that came to mind
// was trying all the formats in time package. This is reasonable enough until
// I can find a decent lexer or polish up my "timey" Go lib. I am using the
// table of dates politely released into public domain by github.com/tomarus:
//   https://github.com/tomarus/parsedate/blob/master/parsedate.go
func timeFromString(s string) (time.Time, bool) {
	if len(s) == 0 {
		return time.Time{}, false
	}
	for _, f := range formats {
		_, err := time.Parse(f.format, s)
		if err != nil {
			continue
		}
		if t, err := time.Parse(
			f.format+f.needed, s+time.Now().Format(f.needed)); err == nil {
			return t, true
		}
	}
	return time.Time{}, false
}
<|endoftext|>


<commit_before>package gofinch

import (
    "fmt"
)

func main() {
    fmt.Println("世界你好!")
}
<commit_msg>Blah blah<commit_after>package gofinch

import (
    "fmt"
    "errors"
    "strings"
    "net/http"
)

const finchBaseUrl = "http://f.exosite.com"

type Dataport struct {
    Id string
}
func (dp *Dataport) Write(val string) error {
    resp, err := post(dp.Id, val)
    if resp.StatusCode >= 300 {
        return errors.New(fmt.Sprintf("Received %d response when writing data to '%s'",
                                      resp.StatusCode, dp.Id))
    }
    return err
}
func NewDataport(dataportId string) *Dataport {
    return &Dataport{
        Id: dataportId,
    }
}

func post(uri, val string) (*http.Response, error) {
    url := fmt.Sprintf("%s%s", finchBaseUrl, uri)
    return http.Post(url, "text/plain", strings.NewReader(val))
}
<|endoftext|>


<commit_before>package CloudForest

import ()

/*
GradBoostTarget wraps a numerical feature as a target for us in Gradiant Boosting Trees
*/
type GradBoostTarget struct {
	NumFeature
	LearnRate float64
}

//BUG(ryan) does GradBoostingTarget need seperate residuals and values?
func (f *GradBoostTarget) Boost(leaves *[][]int) (weight float64) {
	for _, cases := range *leaves {
		f.Update(&cases)
	}
	return f.LearnRate

}

//Update updates the underlying numeric data by subtracting the mean*weight of the
//specified cases from the value for those cases.
func (f *GradBoostTarget) Update(cases *[]int) {
	m := f.Predicted(cases)
	for _, i := range *cases {
		if !f.IsMissing(i) {
			f.Put(i, f.Get(i)-f.LearnRate*m)
		}
	}
}
<commit_msg>fix to boosting<commit_after>package CloudForest

import ()

/*
GradBoostTarget wraps a numerical feature as a target for us in Gradiant Boosting Trees
*/
type GradBoostTarget struct {
	NumFeature
	LearnRate float64
	Mean      float64
}

func NewGradBoostTarget(f NumFeature, learnrate float64) (gbc *GradBoostTarget) {

	//res := NumFeature.(*DenseNumFeature).Copy().(*DenseNumFeature)
	sum := 0.0
	for i := 0; i < f.Length(); i++ {
		sum += f.Get(i)
	}

	// Set intial residual to
	prior := sum / float64(f.Length())

	for i := 0; i < f.Length(); i++ {
		v := f.Get(i) - prior
		f.Put(i, v)
	}

	//fmt.Println(res.Copy().(*DenseNumFeature).NumData)

	gbc = &GradBoostTarget{f, learnrate, prior}
	return

}

func (f *GradBoostTarget) Intercept() float64 {
	return f.Mean
}

//BUG(ryan) does GradBoostingTarget need seperate residuals and values?
func (f *GradBoostTarget) Boost(leaves *[][]int, preds *[]string) (weight float64) {
	for i, cases := range *leaves {
		f.Update(&cases, ParseFloat((*preds)[i]))
	}
	return f.LearnRate

}

//Update updates the underlying numeric data by subtracting the mean*weight of the
//specified cases from the value for those cases.
func (f *GradBoostTarget) Update(cases *[]int, predicted float64) {
	for _, i := range *cases {
		if !f.IsMissing(i) {
			f.Put(i, f.Get(i)-f.LearnRate*predicted)
		}
	}
}

//Impurity returns Gini impurity or mean squared error vs the mean for a set of cases
//depending on weather the feature is categorical or numerical
func (target *GradBoostTarget) Impurity(cases *[]int, counter *[]int) (e float64) {
	e = target.NumFeature.Impurity(cases, counter)
	if e <= minImp {
		return e
	}
	e = -1.0
	return e

}

func (target *GradBoostTarget) Sum(cases *[]int) (sum float64) {
	for _, i := range *cases {
		x := target.Get(i)
		sum += x
	}
	return
}

func FriedmanScore(allocs *BestSplitAllocs, l, r *[]int) (impurityDecrease float64) {
	nl := float64(len(*l))
	nr := float64(len(*r))
	diff := (allocs.Lsum / nl) - (allocs.Rsum / nr)
	impurityDecrease = (diff * diff * nl * nr) / (nl + nr)

	// if impurityDecrease <= 10e-6 {
	// 	impurityDecrease = 0.0
	// }
	return

}

// Friedman MSE slit improvment score from from equation 35 in "Greedy Function Approximation: A Gradiet Boosting Machine"
// Todo...what should the parent impurity be
func (target *GradBoostTarget) SplitImpurity(l *[]int, r *[]int, m *[]int, allocs *BestSplitAllocs) (impurityDecrease float64) {

	allocs.Lsum = target.Sum(l)
	allocs.Rsum = target.Sum(r)

	impurityDecrease = FriedmanScore(allocs, l, r)
	return
}

func (target *GradBoostTarget) UpdateSImpFromAllocs(l *[]int, r *[]int, m *[]int, allocs *BestSplitAllocs, movedRtoL *[]int) (impurityDecrease float64) {

	MVsum := target.Sum(movedRtoL)

	allocs.Lsum += MVsum
	allocs.Rsum -= MVsum

	impurityDecrease = FriedmanScore(allocs, l, r)
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of the copyright holder nor the names of its
//       contributors may be used to endorse or promote products derived from
//       this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// The tomb package helps with clean goroutine termination.
//
// See the Tomb type for details.
package tomb

import (
	"errors"
	"fmt"
	"sync"
)

// A Tomb tracks the lifecycle of a goroutine as alive, dying or dead,
// and the reason for its death.
//
// The zero value of a Tomb assumes that a goroutine is about to be
// created or already alive. Once Kill or Killf is called with an
// argument that informs the reason for death, the goroutine is in
// a dying state and is expected to terminate soon. Right before the
// goroutine function or method returns, Done must be called to inform
// that the goroutine is indeed dead and about to stop running.
//
// A Tomb exposes Dying and Dead channels. These channels are closed
// when the Tomb state changes in the respective way. They enable
// explicit blocking until the state changes, and also to selectively
// unblock select statements accordingly.
//
// When the tomb state changes to dying and there's still logic going
// on within the goroutine, nested functions and methods may choose to
// return ErrDying as their error value, as this error won't alter the
// tomb state if provied to the Kill method. This is a convenient way to
// follow standard Go practices in the context of a dying tomb.
//
// For background and a detailed example, see the following blog post:
//
//   http://blog.labix.org/2011/10/09/death-of-goroutines-under-control
//
// For a more complex code snippet demonstrating the use of multiple
// goroutines with a single Tomb, see:
//
//   http://play.golang.org/p/Xh7qWsDPZP
//
type Tomb struct {
	m      sync.Mutex
	dying  chan struct{}
	dead   chan struct{}
	reason error
}

var (
	ErrStillAlive = errors.New("tomb: still alive")
	ErrDying = errors.New("tomb: dying")
)

func (t *Tomb) init() {
	t.m.Lock()
	if t.dead == nil {
		t.dead = make(chan struct{})
		t.dying = make(chan struct{})
		t.reason = ErrStillAlive
	}
	t.m.Unlock()
}

// Dead returns the channel that can be used to wait
// until t.Done has been called.
func (t *Tomb) Dead() <-chan struct{} {
	t.init()
	return t.dead
}

// Dying returns the channel that can be used to wait
// until t.Kill or t.Done has been called.
func (t *Tomb) Dying() <-chan struct{} {
	t.init()
	return t.dying
}

// Wait blocks until the goroutine is in a dead state and returns the
// reason for its death.
func (t *Tomb) Wait() error {
	t.init()
	<-t.dead
	t.m.Lock()
	reason := t.reason
	t.m.Unlock()
	return reason
}

// Done flags the goroutine as dead, and should be called a single time
// right before the goroutine function or method returns.
// If the goroutine was not already in a dying state before Done is
// called, it will be flagged as dying and dead at once with no
// error.
func (t *Tomb) Done() {
	t.Kill(nil)
	close(t.dead)
}

// Kill flags the goroutine as dying for the given reason.
// Kill may be called multiple times, but only the first
// non-nil error is recorded as the reason for termination.
//
// If reason is ErrDying, the previous reason isn't replaced
// even if it is nil. It's a runtime error to call Kill with
// ErrDying if t is not in a dying state.
func (t *Tomb) Kill(reason error) {
	t.init()
	t.m.Lock()
	defer t.m.Unlock()
	if reason == ErrDying {
		if t.reason == ErrStillAlive {
			panic("tomb: Kill with ErrDying while still alive")
		}
		return
	}
	if t.reason == nil || t.reason == ErrStillAlive {
		t.reason = reason
	}
	// If the receive on t.dying succeeds, then
	// it can only be because we have already closed it.
	// If it blocks, then we know that it needs to be closed.
	select {
	case <-t.dying:
	default:
		close(t.dying)
	}
}

// Killf works like Kill, but builds the reason providing the received
// arguments to fmt.Errorf. The generated error is also returned.
func (t *Tomb) Killf(f string, a ...interface{}) error {
	err := fmt.Errorf(f, a...)
	t.Kill(err)
	return err
}

// Err returns the reason for the goroutine death provided via Kill
// or Killf, or ErrStillAlive when the goroutine is still alive.
func (t *Tomb) Err() (reason error) {
	t.init()
	t.m.Lock()
	reason = t.reason
	t.m.Unlock()
	return
}
<commit_msg>Move the main docs to the package documentation.<commit_after>// Copyright (c) 2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of the copyright holder nor the names of its
//       contributors may be used to endorse or promote products derived from
//       this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// The tomb package offers a conventional API for clean goroutine termination.
//
// A Tomb tracks the lifecycle of a goroutine as alive, dying or dead,
// and the reason for its death.
//
// The zero value of a Tomb assumes that a goroutine is about to be
// created or already alive. Once Kill or Killf is called with an
// argument that informs the reason for death, the goroutine is in
// a dying state and is expected to terminate soon. Right before the
// goroutine function or method returns, Done must be called to inform
// that the goroutine is indeed dead and about to stop running.
//
// A Tomb exposes Dying and Dead channels. These channels are closed
// when the Tomb state changes in the respective way. They enable
// explicit blocking until the state changes, and also to selectively
// unblock select statements accordingly.
//
// When the tomb state changes to dying and there's still logic going
// on within the goroutine, nested functions and methods may choose to
// return ErrDying as their error value, as this error won't alter the
// tomb state if provied to the Kill method. This is a convenient way to
// follow standard Go practices in the context of a dying tomb.
//
// For background and a detailed example, see the following blog post:
//
//   http://blog.labix.org/2011/10/09/death-of-goroutines-under-control
//
// For a more complex code snippet demonstrating the use of multiple
// goroutines with a single Tomb, see:
//
//   http://play.golang.org/p/Xh7qWsDPZP
//
package tomb

import (
	"errors"
	"fmt"
	"sync"
)

// A Tomb tracks the lifecycle of a goroutine as alive, dying or dead,
// and the reason for its death.
//
// See the package documentation for details.
type Tomb struct {
	m      sync.Mutex
	dying  chan struct{}
	dead   chan struct{}
	reason error
}

var (
	ErrStillAlive = errors.New("tomb: still alive")
	ErrDying = errors.New("tomb: dying")
)

func (t *Tomb) init() {
	t.m.Lock()
	if t.dead == nil {
		t.dead = make(chan struct{})
		t.dying = make(chan struct{})
		t.reason = ErrStillAlive
	}
	t.m.Unlock()
}

// Dead returns the channel that can be used to wait
// until t.Done has been called.
func (t *Tomb) Dead() <-chan struct{} {
	t.init()
	return t.dead
}

// Dying returns the channel that can be used to wait
// until t.Kill or t.Done has been called.
func (t *Tomb) Dying() <-chan struct{} {
	t.init()
	return t.dying
}

// Wait blocks until the goroutine is in a dead state and returns the
// reason for its death.
func (t *Tomb) Wait() error {
	t.init()
	<-t.dead
	t.m.Lock()
	reason := t.reason
	t.m.Unlock()
	return reason
}

// Done flags the goroutine as dead, and should be called a single time
// right before the goroutine function or method returns.
// If the goroutine was not already in a dying state before Done is
// called, it will be flagged as dying and dead at once with no
// error.
func (t *Tomb) Done() {
	t.Kill(nil)
	close(t.dead)
}

// Kill flags the goroutine as dying for the given reason.
// Kill may be called multiple times, but only the first
// non-nil error is recorded as the reason for termination.
//
// If reason is ErrDying, the previous reason isn't replaced
// even if it is nil. It's a runtime error to call Kill with
// ErrDying if t is not in a dying state.
func (t *Tomb) Kill(reason error) {
	t.init()
	t.m.Lock()
	defer t.m.Unlock()
	if reason == ErrDying {
		if t.reason == ErrStillAlive {
			panic("tomb: Kill with ErrDying while still alive")
		}
		return
	}
	if t.reason == nil || t.reason == ErrStillAlive {
		t.reason = reason
	}
	// If the receive on t.dying succeeds, then
	// it can only be because we have already closed it.
	// If it blocks, then we know that it needs to be closed.
	select {
	case <-t.dying:
	default:
		close(t.dying)
	}
}

// Killf works like Kill, but builds the reason providing the received
// arguments to fmt.Errorf. The generated error is also returned.
func (t *Tomb) Killf(f string, a ...interface{}) error {
	err := fmt.Errorf(f, a...)
	t.Kill(err)
	return err
}

// Err returns the reason for the goroutine death provided via Kill
// or Killf, or ErrStillAlive when the goroutine is still alive.
func (t *Tomb) Err() (reason error) {
	t.init()
	t.m.Lock()
	reason = t.reason
	t.m.Unlock()
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"runtime"

	"gopkg.in/yaml.v2"
)

// yaml special keys
const (
	DEFAULT_KEY = "default"
	SOURCE_KEY  = "source"
	TARGET_KEY  = "target"
)

// temporary definitions for dev
const (
	SETTINGS_FILE_NAME = "dotorrc.sample.yml"
)

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Fprintln(os.Stderr, r)
		}
	}()

	settings, err := ReadSettings(SETTINGS_FILE_NAME)
	if err != nil {
		panic(err)
	}

	rules, err := BuildRules(settings)
	if err != nil {
		panic(err)
	}

	fmt.Println(rules)
}

func ReadSettings(filepath string) (map[interface{}]interface{}, error) {
	file, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}

	settings := make(map[interface{}]interface{})
	if err := yaml.Unmarshal(file, &settings); err != nil {
		return nil, err
	}

	return settings, nil
}

func BuildRules(settings map[interface{}]interface{}) (map[string]string, error) {
	defaultRules, err := BuildSpecificOsRules(DEFAULT_KEY, settings)
	if err != nil {
		return nil, err
	}

	osRules, err := BuildSpecificOsRules(runtime.GOOS, settings)
	if err != nil {
		return nil, err
	}

	rules := Extend(defaultRules, osRules)
	return rules, err
}

func BuildSpecificOsRules(os string, settings map[interface{}]interface{}) (map[string]string, error) {
	rules := make(map[string]string)
	if _, hasKey := settings[os]; !hasKey {
		return rules, nil
	}

	for _, value := range settings[os].([]interface{}) {
		switch value.(type) {
		case string:
			rules[value.(string)] = value.(string)
		case map[interface{}]interface{}:
			setting := value.(map[interface{}]interface{})
			_, hasSource := setting[SOURCE_KEY]
			_, hasTarget := setting[TARGET_KEY]
			if !(hasSource && hasTarget) {
				return nil, fmt.Errorf("specify key-values in format of \"%s:<source file name>\" and \"%s:<target file name>\" to change sysmbolic link names", SOURCE_KEY, TARGET_KEY)
			}
			source := setting[SOURCE_KEY].(string)
			target := setting[TARGET_KEY].(string)
			rules[source] = target
		}
	}

	return rules, nil
}

func Extend(m1, m2 map[string]string) map[string]string {
	result := map[string]string{}

	for v, k := range m1 {
		result[k] = v
	}
	for v, k := range m2 {
		result[k] = v
	}
	return (result)
}
<commit_msg>[WIP] finish impl to operate paths<commit_after>package main

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

	"gopkg.in/yaml.v2"
)

// yaml special keys
const (
	DEFAULT_KEY = "default"
	SOURCE_KEY  = "source"
	TARGET_KEY  = "target"
)

// environment variable names
const (
	HOME_DIRECTORY = "HOME"
)

// temporary definitions for dev
const (
	SETTINGS_FILE_NAME = "dotorrc.sample.yml"
	SOURCE_PATH        = "/Users/janus/work/dev/github/dotfiles"
)

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Fprintln(os.Stderr, r)
		}
	}()

	settings, err := ReadSettings(SETTINGS_FILE_NAME)
	if err != nil {
		panic(err)
	}

	rules, err := BuildRules(settings)
	if err != nil {
		panic(err)
	}

	err = CreateSymbolicLinks(rules, SOURCE_PATH)
	if err != nil {
		panic(err)
	}
}

func ReadSettings(filepath string) (map[interface{}]interface{}, error) {
	file, err := ioutil.ReadFile(filepath)
	if err != nil {
		return nil, err
	}

	settings := make(map[interface{}]interface{})
	if err := yaml.Unmarshal(file, &settings); err != nil {
		return nil, err
	}

	return settings, nil
}

func BuildRules(settings map[interface{}]interface{}) (map[string]string, error) {
	defaultRules, err := BuildSpecificOsRules(DEFAULT_KEY, settings)
	if err != nil {
		return nil, err
	}

	osRules, err := BuildSpecificOsRules(runtime.GOOS, settings)
	if err != nil {
		return nil, err
	}

	rules := Extend(defaultRules, osRules)
	return rules, err
}

func BuildSpecificOsRules(os string, settings map[interface{}]interface{}) (map[string]string, error) {
	rules := make(map[string]string)
	if _, hasKey := settings[os]; !hasKey {
		return rules, nil
	}

	for _, value := range settings[os].([]interface{}) {
		switch value.(type) {
		case string:
			rules[value.(string)] = value.(string)
		case map[interface{}]interface{}:
			setting := value.(map[interface{}]interface{})
			_, hasSource := setting[SOURCE_KEY]
			_, hasTarget := setting[TARGET_KEY]
			if !(hasSource && hasTarget) {
				return nil, fmt.Errorf("specify key-values in format of \"%s:<source file name>\" and \"%s:<target file name>\" to change sysmbolic link names", SOURCE_KEY, TARGET_KEY)
			}
			source := setting[SOURCE_KEY].(string)
			target := setting[TARGET_KEY].(string)
			rules[source] = target
		}
	}

	return rules, nil
}

func Extend(m1, m2 map[string]string) map[string]string {
	result := map[string]string{}

	for v, k := range m1 {
		result[k] = v
	}
	for v, k := range m2 {
		result[k] = v
	}
	return (result)
}

func CreateSymbolicLinks(rules map[string]string, sourceDirectoryPath string) error {
	targetDirectoryAbsolutePath, err := GetHomeDirectory()
	if err != nil {
		return err
	}
	sourceDirectoryAbsolutePath, err := filepath.Abs(sourceDirectoryPath)
	if err != nil {
		return err
	}

	fmt.Printf("%s => %s\n", sourceDirectoryAbsolutePath, targetDirectoryAbsolutePath)

	for source, target := range rules {
		sourceAbsolutePath := filepath.Join(sourceDirectoryAbsolutePath, source)
		targetAbsolutePath := filepath.Join(targetDirectoryAbsolutePath, target)
		if !ExistsPath(sourceAbsolutePath) {
			fmt.Printf("source file \"%s\" is not exists. skipping.\n", targetAbsolutePath)
			continue
		}
		if ExistsPath(targetAbsolutePath) {
			fmt.Printf("target file \"%s\" is already exists. skipping.\n", targetAbsolutePath)
			continue
		}
		fmt.Printf("creating symbolic link: %s => %s\n", sourceAbsolutePath, targetAbsolutePath)
		// TODO: use os.Symlink()
	}

	return nil
}

func GetHomeDirectory() (dir string, err error) {
	environmentVariables := GetEnvironmentVariables()
	if _, ok := environmentVariables[HOME_DIRECTORY]; !ok {
		return "", fmt.Errorf("Define the environment variable \"%s\"", HOME_DIRECTORY)
	}
	return environmentVariables[HOME_DIRECTORY], nil
}

func GetEnvironmentVariables() map[string]string {
	environmentVariables := make(map[string]string)
	for _, item := range os.Environ() {
		splited := strings.Split(item, "=")
		environmentVariables[splited[0]] = splited[1]
	}
	return environmentVariables
}

func ExistsPath(path string) bool {
	_, err := os.Stat(path)
	return !os.IsNotExist(err)
}
<|endoftext|>


<commit_before>package backend

import (
	"strconv"
	"math"

	"gopkg.in/mgo.v2/bson"
	"github.com/mindcastio/podcast-feed"

	"github.com/mindcastio/mindcastio/backend/datastore"
	"github.com/mindcastio/mindcastio/backend/logger"
	"github.com/mindcastio/mindcastio/backend/metrics"
	"github.com/mindcastio/mindcastio/backend/util"
)

func SubmitPodcastFeed(feed string) error {

	logger.Log("submit_podcast_feed", feed)

	// check if the podcast is already in the index
	uid := util.UID(feed)
	idx := IndexLookup(uid)

	if idx == nil {
		err := IndexAdd(uid, feed)
		if err != nil {

			logger.Error("submit_podcast_feed.error", err, feed)
			metrics.Error("submit_podcast_feed.error", err.Error(), []string{feed})

			return err
		}
	} else {
		logger.Warn("submit_podcast_feed.duplicate", uid, feed)
		metrics.Warning("submit_podcast_feed.duplicate", "", []string{feed})
	}

	logger.Log("submit_podcast_feed.done", uid, feed)
	return nil
}

func BulkSubmitPodcastFeed(urls []string) error {

	logger.Log("bulk_submit_podcast_feed")

	count := 0
	feed := ""

	for i := 0; i < len(urls); i++ {
		feed = urls[i]

		// check if the podcast is already in the index
		uid := util.UID(feed)
		idx := IndexLookup(uid)

		if idx == nil {
			err := IndexAdd(uid, feed)
			if err != nil {

				logger.Error("bulk_submit_podcast_feed.error", err, feed)
				metrics.Error("bulk_submit_podcast_feed.error", err.Error(), []string{feed})

				return err
			} else {
				count++
			}
		}
	}

	logger.Log("bulk_submit_podcast_feed.done", strconv.FormatInt((int64)(count), 10))
	return nil
}

func IndexLookup(uid string) *PodcastIndex {

	ds := datastore.GetDataStore()
	defer ds.Close()

	main_index := ds.Collection(datastore.META_COL)

	i := PodcastIndex{}
	main_index.Find(bson.M{"uid": uid}).One(&i)

	if i.Feed == "" {
		return nil
	} else {
		return &i
	}
}

func IndexAdd(uid string, url string) error {
	ds := datastore.GetDataStore()
	defer ds.Close()

	main_index := ds.Collection(datastore.META_COL)

	// add some random element to the first update point in time
	next := util.IncT(util.Timestamp(), 10+util.Random(DEFAULT_UPDATE_RATE))

	i := PodcastIndex{uid, url, DEFAULT_UPDATE_RATE, next, 0, 0, util.Timestamp(), 0}
	return main_index.Insert(&i)
}

func IndexUpdate(uid string) error {

	ds := datastore.GetDataStore()
	defer ds.Close()

	main_index := ds.Collection(datastore.META_COL)

	i := PodcastIndex{}
	err := main_index.Find(bson.M{"uid": uid}).One(&i)

	if i.Feed == "" || err != nil {
		return err
	} else {
		i.Updated = util.Timestamp()
		i.Next = util.IncT(i.Next, i.UpdateRate+util.RandomPlusMinus(15))
		i.Errors = 0 // reset in case there was an erro

		// update the DB
		err = main_index.Update(bson.M{"uid": uid}, &i)
	}

	return err
}

func IndexBackoff(uid string) (bool, error) {

	ds := datastore.GetDataStore()
	defer ds.Close()

	suspended := false
	main_index := ds.Collection(datastore.META_COL)

	i := PodcastIndex{}
	err := main_index.Find(bson.M{"uid": uid}).One(&i)

	if i.Feed == "" || err != nil {
		return suspended, err
	} else {
		i.Updated = util.Timestamp()
		i.Errors++

		if i.Errors > MAX_ERRORS {
			// just disable the UID by using a LAAAARGE next time
			i.Next = math.MaxInt64
			suspended = true
		} else {
			// + 10, 100, 1000, 10000 min ...
			i.Next = util.IncT(i.Updated, (int)(math.Pow(10, (float64)(i.Errors))))
		}

		// update the DB
		err = main_index.Update(bson.M{"uid": uid}, &i)
	}

	return suspended, err
}

func PodcastLookup(uid string) *PodcastMetadata {

	ds := datastore.GetDataStore()
	defer ds.Close()

	podcast_metadata := ds.Collection(datastore.PODCASTS_COL)

	p := PodcastMetadata{}
	podcast_metadata.Find(bson.M{"uid": uid}).One(&p)

	if p.Uid == "" {
		return nil
	} else {
		return &p
	}
}

func PodcastAdd(podcast *podcast.PodcastDetails) (bool, error) {
	p := PodcastLookup(podcast.Uid)
	if p != nil {
		return false, nil
	}

	ds := datastore.GetDataStore()
	defer ds.Close()

	podcast_metadata := ds.Collection(datastore.PODCASTS_COL)

	meta := podcastDetailsToMetadata(podcast)

	// fix the published timestamp
	now := util.Timestamp()
	if podcast.Published > now {
		meta.Published = now // prevents dates in the future
	}

	err := podcast_metadata.Insert(&meta)

	if err != nil {
		return false, err
	} else {
		return true, nil
	}
}

func PodcastUpdateTimestamp(podcast *podcast.PodcastDetails) (bool, error) {

	ds := datastore.GetDataStore()
	defer ds.Close()

	podcast_metadata := ds.Collection(datastore.PODCASTS_COL)

	p := PodcastMetadata{}
	podcast_metadata.Find(bson.M{"uid": podcast.Uid}).One(&p)

	if p.Uid == "" {
		return false, nil
	} else {
		now := util.Timestamp()
		p.Updated = now
		if podcast.Published > now {
			p.Published = now // prevents dates in the future
		} else {
			p.Published = podcast.Published
		}

		// update the DB
		err := podcast_metadata.Update(bson.M{"uid": podcast.Uid}, &p)
		if err != nil {
			return false, err
		}
	}

	return true, nil
}

func EpisodeLookup(uid string) *EpisodeMetadata {

	ds := datastore.GetDataStore()
	defer ds.Close()

	episodes_metadata := ds.Collection(datastore.EPISODES_COL)

	e := EpisodeMetadata{}
	episodes_metadata.Find(bson.M{"uid": uid}).One(&e)

	if e.Uid == "" {
		return nil
	} else {
		return &e
	}
}

func EpisodeAdd(episode *podcast.EpisodeDetails, puid string) (bool, error) {
	e := EpisodeLookup(episode.Uid)
	if e != nil {
		return false, nil
	}

	ds := datastore.GetDataStore()
	defer ds.Close()

	episodes_metadata := ds.Collection(datastore.EPISODES_COL)

	meta := episodeDetailsToMetadata(episode, puid)
	err := episodes_metadata.Insert(&meta)

	if err != nil {
		return false, err
	} else {
		return true, nil
	}
}

func EpisodesAddAll(podcast *podcast.PodcastDetails) (int, error) {
	count := 0

	for i := 0; i < len(podcast.Episodes); i++ {
		added, err := EpisodeAdd(&podcast.Episodes[i], podcast.Uid)
		if err != nil {
			return 0, err
		}
		if added {
			count++
		}
	}
	return count, nil
}

/*
func latestUpdatedPodcasts(limit int, page int) (*PodcastCollection, error) {

	ds := datastore.GetDataStore()
	defer ds.Close()

	podcast_metadata := ds.Collection(datastore.PODCASTS_COL)

	results := []PodcastMetadata{}
	err := podcast_metadata.Find(nil).Limit(limit).Sort("-published").All(&results)
	if err != nil {
		return nil, err
	}

	podcasts := make([]PodcastSummary, len(results))
	for i := 0; i < len(results); i++ {
		podcasts[i] = podcastMetadataToSummary(&results[i])
	}

	podcastCollection := PodcastCollection{
		len(results),
		podcasts,
	}

	return &podcastCollection, nil
}
*/
<commit_msg>refactored crawler parts<commit_after>package backend

import (
	"math"
	"strconv"

	"github.com/mindcastio/podcast-feed"
	"gopkg.in/mgo.v2/bson"

	"github.com/mindcastio/mindcastio/backend/datastore"
	"github.com/mindcastio/mindcastio/backend/logger"
	"github.com/mindcastio/mindcastio/backend/metrics"
	"github.com/mindcastio/mindcastio/backend/util"
)

func SubmitPodcastFeed(feed string) error {

	logger.Log("submit_podcast_feed", feed)

	// check if the podcast is already in the index
	uid := util.UID(feed)
	idx := IndexLookup(uid)

	if idx == nil {
		err := IndexAdd(uid, feed)
		if err != nil {

			logger.Error("submit_podcast_feed.error", err, feed)
			metrics.Error("submit_podcast_feed.error", err.Error(), []string{feed})

			return err
		}
	} else {
		logger.Warn("submit_podcast_feed.duplicate", uid, feed)
		metrics.Warning("submit_podcast_feed.duplicate", "", []string{feed})
	}

	logger.Log("submit_podcast_feed.done", uid, feed)
	return nil
}

func BulkSubmitPodcastFeed(urls []string) error {

	logger.Log("bulk_submit_podcast_feed")

	count := 0
	feed := ""

	for i := 0; i < len(urls); i++ {
		feed = urls[i]

		// check if the podcast is already in the index
		uid := util.UID(feed)
		idx := IndexLookup(uid)

		if idx == nil {
			err := IndexAdd(uid, feed)
			if err != nil {

				logger.Error("bulk_submit_podcast_feed.error", err, feed)
				metrics.Error("bulk_submit_podcast_feed.error", err.Error(), []string{feed})

				return err
			} else {
				count++
			}
		}
	}

	logger.Log("bulk_submit_podcast_feed.done", strconv.FormatInt((int64)(count), 10))
	return nil
}

func IndexLookup(uid string) *PodcastIndex {

	ds := datastore.GetDataStore()
	defer ds.Close()

	main_index := ds.Collection(datastore.META_COL)

	i := PodcastIndex{}
	main_index.Find(bson.M{"uid": uid}).One(&i)

	if i.Feed == "" {
		return nil
	} else {
		return &i
	}
}

func IndexAdd(uid string, url string) error {
	ds := datastore.GetDataStore()
	defer ds.Close()

	main_index := ds.Collection(datastore.META_COL)

	// add some random element to the first update point in time
	next := util.IncT(util.Timestamp(), 10+util.Random(DEFAULT_UPDATE_RATE))

	i := PodcastIndex{uid, url, DEFAULT_UPDATE_RATE, next, 0, 0, util.Timestamp(), 0}
	return main_index.Insert(&i)
}

func IndexUpdate(uid string) error {

	ds := datastore.GetDataStore()
	defer ds.Close()

	main_index := ds.Collection(datastore.META_COL)

	i := PodcastIndex{}
	err := main_index.Find(bson.M{"uid": uid}).One(&i)

	if i.Feed == "" || err != nil {
		return err
	} else {
		i.Updated = util.Timestamp()
		i.Next = util.IncT(i.Next, i.UpdateRate+util.RandomPlusMinus(15))
		i.Errors = 0 // reset in case there was an erro

		// update the DB
		err = main_index.Update(bson.M{"uid": uid}, &i)
	}

	return err
}

func IndexBackoff(uid string) (bool, error) {

	ds := datastore.GetDataStore()
	defer ds.Close()

	suspended := false
	main_index := ds.Collection(datastore.META_COL)

	i := PodcastIndex{}
	err := main_index.Find(bson.M{"uid": uid}).One(&i)

	if i.Feed == "" || err != nil {
		return suspended, err
	} else {
		i.Updated = util.Timestamp()
		i.Errors++

		if i.Errors > MAX_ERRORS {
			// just disable the UID by using a LAAAARGE next time
			i.Next = math.MaxInt64
			suspended = true
		} else {
			// + 10, 100, 1000, 10000 min ...
			i.Next = util.IncT(i.Updated, (int)(math.Pow(10, (float64)(i.Errors))))
		}

		// update the DB
		err = main_index.Update(bson.M{"uid": uid}, &i)
	}

	return suspended, err
}

func PodcastLookup(uid string) *PodcastMetadata {

	ds := datastore.GetDataStore()
	defer ds.Close()

	podcast_metadata := ds.Collection(datastore.PODCASTS_COL)

	p := PodcastMetadata{}
	podcast_metadata.Find(bson.M{"uid": uid}).One(&p)

	if p.Uid == "" {
		return nil
	} else {
		return &p
	}
}

func PodcastAdd(podcast *podcast.PodcastDetails) (bool, error) {
	p := PodcastLookup(podcast.Uid)
	if p != nil {
		return false, nil
	}

	ds := datastore.GetDataStore()
	defer ds.Close()

	podcast_metadata := ds.Collection(datastore.PODCASTS_COL)

	meta := podcastDetailsToMetadata(podcast)

	// fix the published timestamp
	now := util.Timestamp()
	if podcast.Published > now {
		meta.Published = now // prevents dates in the future
	}

	err := podcast_metadata.Insert(&meta)

	if err != nil {
		return false, err
	} else {
		return true, nil
	}
}

func PodcastUpdateTimestamp(podcast *podcast.PodcastDetails) (bool, error) {

	ds := datastore.GetDataStore()
	defer ds.Close()

	podcast_metadata := ds.Collection(datastore.PODCASTS_COL)

	p := PodcastMetadata{}
	podcast_metadata.Find(bson.M{"uid": podcast.Uid}).One(&p)

	if p.Uid == "" {
		return false, nil
	} else {
		now := util.Timestamp()
		p.Updated = now
		if podcast.Published > now {
			p.Published = now // prevents dates in the future
		} else {
			p.Published = podcast.Published
		}

		// update the DB
		err := podcast_metadata.Update(bson.M{"uid": podcast.Uid}, &p)
		if err != nil {
			return false, err
		}
	}

	return true, nil
}

func EpisodeLookup(uid string) *EpisodeMetadata {

	ds := datastore.GetDataStore()
	defer ds.Close()

	episodes_metadata := ds.Collection(datastore.EPISODES_COL)

	e := EpisodeMetadata{}
	episodes_metadata.Find(bson.M{"uid": uid}).One(&e)

	if e.Uid == "" {
		return nil
	} else {
		return &e
	}
}

func EpisodeAdd(episode *podcast.EpisodeDetails, puid string) (bool, error) {
	e := EpisodeLookup(episode.Uid)
	if e != nil {
		return false, nil
	}

	ds := datastore.GetDataStore()
	defer ds.Close()

	episodes_metadata := ds.Collection(datastore.EPISODES_COL)

	meta := episodeDetailsToMetadata(episode, puid)
	err := episodes_metadata.Insert(&meta)

	if err != nil {
		return false, err
	} else {
		return true, nil
	}
}

func EpisodesAddAll(podcast *podcast.PodcastDetails) (int, error) {
	count := 0

	for i := 0; i < len(podcast.Episodes); i++ {
		added, err := EpisodeAdd(&podcast.Episodes[i], podcast.Uid)
		if err != nil {
			return 0, err
		}
		if added {
			count++
		}
	}
	return count, nil
}

/*
func latestUpdatedPodcasts(limit int, page int) (*PodcastCollection, error) {

	ds := datastore.GetDataStore()
	defer ds.Close()

	podcast_metadata := ds.Collection(datastore.PODCASTS_COL)

	results := []PodcastMetadata{}
	err := podcast_metadata.Find(nil).Limit(limit).Sort("-published").All(&results)
	if err != nil {
		return nil, err
	}

	podcasts := make([]PodcastSummary, len(results))
	for i := 0; i < len(results); i++ {
		podcasts[i] = podcastMetadataToSummary(&results[i])
	}

	podcastCollection := PodcastCollection{
		len(results),
		podcasts,
	}

	return &podcastCollection, nil
}
*/
<|endoftext|>