<commit_before>package unsplash

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/ayoisaiah/stellar-photos-server/config"
	"github.com/ayoisaiah/stellar-photos-server/utils"
)

// UnsplashAPILocation represents the base URL for requests to Unsplash's API
const UnsplashAPILocation = "https://api.unsplash.com"

// download represents the result from triggering a download on a photo
type download struct {
	URL string `json:"url,omitempty"`
}

// searchResult represents the result for a search for photos.
type searchResult struct {
	Total      int           `json:"total,omitempty"`
	TotalPages int           `json:"total_pages,omitempty"`
	Results    []interface{} `json:"results,omitempty"`
}

// collection respresents a single Unsplash collection ID
type collection struct {
	ID string `json:"id,omitempty"`
}

// randomPhoto represents the result from fetching a random photo from Unsplash
type randomPhoto struct {
	ID             string `json:"id"`
	CreatedAt      string `json:"created_at"`
	UpdatedAt      string `json:"updated_at"`
	PromotedAt     string `json:"promoted_at"`
	Width          int    `json:"width"`
	Height         int    `json:"height"`
	Color          string `json:"color"`
	BlurHash       string `json:"blur_hash"`
	Description    string `json:"description"`
	AltDescription string `json:"alt_description"`
	Urls           struct {
		Raw     string `json:"raw"`
		Full    string `json:"full"`
		Regular string `json:"regular"`
		Small   string `json:"small"`
		Thumb   string `json:"thumb"`
		Custom  string `json:"custom"`
	} `json:"urls"`
	Links struct {
		Self             string `json:"self"`
		HTML             string `json:"html"`
		Download         string `json:"download"`
		DownloadLocation string `json:"download_location"`
	} `json:"links"`
	Categories             []interface{} `json:"categories"`
	Likes                  int           `json:"likes"`
	LikedByUser            bool          `json:"liked_by_user"`
	CurrentUserCollections []interface{} `json:"current_user_collections"`
	User                   struct {
		ID              string      `json:"id"`
		UpdatedAt       string      `json:"updated_at"`
		Username        string      `json:"username"`
		Name            string      `json:"name"`
		FirstName       string      `json:"first_name"`
		LastName        string      `json:"last_name"`
		TwitterUsername interface{} `json:"twitter_username"`
		PortfolioURL    string      `json:"portfolio_url"`
		Bio             string      `json:"bio"`
		Location        interface{} `json:"location"`
		Links           struct {
			Self      string `json:"self"`
			HTML      string `json:"html"`
			Photos    string `json:"photos"`
			Likes     string `json:"likes"`
			Portfolio string `json:"portfolio"`
			Following string `json:"following"`
			Followers string `json:"followers"`
		} `json:"links"`
		ProfileImage struct {
			Small  string `json:"small"`
			Medium string `json:"medium"`
			Large  string `json:"large"`
		} `json:"profile_image"`
		InstagramUsername string `json:"instagram_username"`
		TotalCollections  int    `json:"total_collections"`
		TotalLikes        int    `json:"total_likes"`
		TotalPhotos       int    `json:"total_photos"`
		AcceptedTos       bool   `json:"accepted_tos"`
	} `json:"user"`
	Exif struct {
		Make         string `json:"make"`
		Model        string `json:"model"`
		ExposureTime string `json:"exposure_time"`
		Aperture     string `json:"aperture"`
		FocalLength  string `json:"focal_length"`
		Iso          int    `json:"iso"`
	} `json:"exif"`
	Location struct {
		Title    string `json:"title"`
		Name     string `json:"name"`
		City     string `json:"city"`
		Country  string `json:"country"`
		Position struct {
			Latitude  float64 `json:"latitude"`
			Longitude float64 `json:"longitude"`
		} `json:"position"`
	} `json:"location"`
	Views     int `json:"views"`
	Downloads int `json:"downloads"`
}

// randomPhotoWithBase64 respresents the base64 encoding of randomPhoto
type randomPhotoBase64 struct {
	*randomPhoto
	Base64 string `json:"base64,omitempty"`
}

// unsplashResponse the entire range of responses that can be expected from
// Unsplash
type unsplashResponse struct {
	Errors []interface{} `json:"errors,omitempty"`
	download
	randomPhoto
	randomPhotoBase64
	searchResult
}

// DownloadPhoto is triggered each time a download is attempted
func DownloadPhoto(w http.ResponseWriter, r *http.Request) error {
	values, err := utils.GetURLQueryParams(r.URL.String())
	if err != nil {
		return err
	}

	id := values.Get("id")
	if id == "" {
		return utils.NewHTTPError(nil, http.StatusBadRequest, "Photo ID must not be empty")
	}

	_, err = TrackPhotoDownload(id)
	if err != nil {
		return err
	}

	w.WriteHeader(http.StatusOK)
	return nil
}

// TrackPhotoDownload is used to increment the number of downloads
// for the specified photo
func TrackPhotoDownload(id string) ([]byte, error) {
	unsplashAccessKey := config.Conf.Unsplash.AccessKey
	url := fmt.Sprintf("%s/photos/%s/download?client_id=%s", UnsplashAPILocation, id, unsplashAccessKey)

	return utils.SendGETRequest(url, &download{})
}

// SearchUnsplash triggers a photo search and sends a single page of photo
// results for a query.
func SearchUnsplash(w http.ResponseWriter, r *http.Request) error {
	values, err := utils.GetURLQueryParams(r.URL.String())
	if err != nil {
		return err
	}

	key := values.Get("key")
	page := values.Get("page")

	unsplashAccessKey := config.Conf.Unsplash.AccessKey
	url := fmt.Sprintf("%s/search/photos?page=%s&query=%s&per_page=%s&client_id=%s", UnsplashAPILocation, page, key, "28", unsplashAccessKey)

	s := &searchResult{}

	bytes, err := utils.SendGETRequest(url, s)
	if err != nil {
		return err
	}

	return utils.JsonResponse(w, bytes)
}

// GetRandomPhoto retrives a single random photo using the provided collection
// IDs to narrow the pool of photos from which a random one will be chosen.
// If no collection IDs are present, it defaults to 998309 which is the ID of
// the official Stellar Photos collection
func GetRandomPhoto(w http.ResponseWriter, r *http.Request) error {
	values, err := utils.GetURLQueryParams(r.URL.String())
	if err != nil {
		return err
	}

	collections := values.Get("collections")
	if collections == "" {
		collections = "998309"
	}

	width := 2000

	unsplashAccessKey := config.Conf.Unsplash.AccessKey
	url := fmt.Sprintf("%s/photos/random?collections=%s&w=%d&client_id=%s", UnsplashAPILocation, collections, width, unsplashAccessKey)

	res := &randomPhoto{}

	_, err = utils.SendGETRequest(url, res)
	if err != nil {
		return err
	}

	imageURL := res.Urls.Custom

	base64, err := utils.ImageURLToBase64(imageURL)
	if err != nil {
		return err
	}

	data := randomPhotoBase64{
		res,
		base64,
	}

	bytes, err := json.Marshal(data)
	if err != nil {
		return err
	}

	return utils.JsonResponse(w, bytes)
}

// ValidateCollections ensures that all the custom collection IDs that are added
// to the extension are valid
func ValidateCollections(w http.ResponseWriter, r *http.Request) error {
	values, err := utils.GetURLQueryParams(r.URL.String())
	if err != nil {
		return err
	}

	collections := strings.Split(values.Get("collections"), ",")

	unsplashAccessKey := config.Conf.Unsplash.AccessKey

	for _, value := range collections {
		valueToNum, err := strconv.Atoi(value)
		if err != nil {
			return utils.NewHTTPError(err, http.StatusBadRequest, "Collection ID must be a number")
		}

		url := fmt.Sprintf("%s/collections/%d/?client_id=%s", UnsplashAPILocation, valueToNum, unsplashAccessKey)
		_, err = utils.SendGETRequest(url, &collection{})
		if err != nil {
			return err
		}
	}

	w.WriteHeader(http.StatusOK)
	return nil
}
<commit_msg>Fix image URL<commit_after>package unsplash

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/ayoisaiah/stellar-photos-server/config"
	"github.com/ayoisaiah/stellar-photos-server/utils"
)

// UnsplashAPILocation represents the base URL for requests to Unsplash's API
const UnsplashAPILocation = "https://api.unsplash.com"

// download represents the result from triggering a download on a photo
type download struct {
	URL string `json:"url,omitempty"`
}

// searchResult represents the result for a search for photos.
type searchResult struct {
	Total      int           `json:"total,omitempty"`
	TotalPages int           `json:"total_pages,omitempty"`
	Results    []interface{} `json:"results,omitempty"`
}

// collection respresents a single Unsplash collection ID
type collection struct {
	ID string `json:"id,omitempty"`
}

// randomPhoto represents the result from fetching a random photo from Unsplash
type randomPhoto struct {
	ID             string `json:"id"`
	CreatedAt      string `json:"created_at"`
	UpdatedAt      string `json:"updated_at"`
	PromotedAt     string `json:"promoted_at"`
	Width          int    `json:"width"`
	Height         int    `json:"height"`
	Color          string `json:"color"`
	BlurHash       string `json:"blur_hash"`
	Description    string `json:"description"`
	AltDescription string `json:"alt_description"`
	Urls           struct {
		Raw     string `json:"raw"`
		Full    string `json:"full"`
		Regular string `json:"regular"`
		Small   string `json:"small"`
		Thumb   string `json:"thumb"`
		Custom  string `json:"custom"`
	} `json:"urls"`
	Links struct {
		Self             string `json:"self"`
		HTML             string `json:"html"`
		Download         string `json:"download"`
		DownloadLocation string `json:"download_location"`
	} `json:"links"`
	Categories             []interface{} `json:"categories"`
	Likes                  int           `json:"likes"`
	LikedByUser            bool          `json:"liked_by_user"`
	CurrentUserCollections []interface{} `json:"current_user_collections"`
	User                   struct {
		ID              string      `json:"id"`
		UpdatedAt       string      `json:"updated_at"`
		Username        string      `json:"username"`
		Name            string      `json:"name"`
		FirstName       string      `json:"first_name"`
		LastName        string      `json:"last_name"`
		TwitterUsername interface{} `json:"twitter_username"`
		PortfolioURL    string      `json:"portfolio_url"`
		Bio             string      `json:"bio"`
		Location        interface{} `json:"location"`
		Links           struct {
			Self      string `json:"self"`
			HTML      string `json:"html"`
			Photos    string `json:"photos"`
			Likes     string `json:"likes"`
			Portfolio string `json:"portfolio"`
			Following string `json:"following"`
			Followers string `json:"followers"`
		} `json:"links"`
		ProfileImage struct {
			Small  string `json:"small"`
			Medium string `json:"medium"`
			Large  string `json:"large"`
		} `json:"profile_image"`
		InstagramUsername string `json:"instagram_username"`
		TotalCollections  int    `json:"total_collections"`
		TotalLikes        int    `json:"total_likes"`
		TotalPhotos       int    `json:"total_photos"`
		AcceptedTos       bool   `json:"accepted_tos"`
	} `json:"user"`
	Exif struct {
		Make         string `json:"make"`
		Model        string `json:"model"`
		ExposureTime string `json:"exposure_time"`
		Aperture     string `json:"aperture"`
		FocalLength  string `json:"focal_length"`
		Iso          int    `json:"iso"`
	} `json:"exif"`
	Location struct {
		Title    string `json:"title"`
		Name     string `json:"name"`
		City     string `json:"city"`
		Country  string `json:"country"`
		Position struct {
			Latitude  float64 `json:"latitude"`
			Longitude float64 `json:"longitude"`
		} `json:"position"`
	} `json:"location"`
	Views     int `json:"views"`
	Downloads int `json:"downloads"`
}

// randomPhotoWithBase64 respresents the base64 encoding of randomPhoto
type randomPhotoBase64 struct {
	*randomPhoto
	Base64 string `json:"base64,omitempty"`
}

// unsplashResponse the entire range of responses that can be expected from
// Unsplash
type unsplashResponse struct {
	Errors []interface{} `json:"errors,omitempty"`
	download
	randomPhoto
	randomPhotoBase64
	searchResult
}

// DownloadPhoto is triggered each time a download is attempted
func DownloadPhoto(w http.ResponseWriter, r *http.Request) error {
	values, err := utils.GetURLQueryParams(r.URL.String())
	if err != nil {
		return err
	}

	id := values.Get("id")
	if id == "" {
		return utils.NewHTTPError(nil, http.StatusBadRequest, "Photo ID must not be empty")
	}

	_, err = TrackPhotoDownload(id)
	if err != nil {
		return err
	}

	w.WriteHeader(http.StatusOK)
	return nil
}

// TrackPhotoDownload is used to increment the number of downloads
// for the specified photo
func TrackPhotoDownload(id string) ([]byte, error) {
	unsplashAccessKey := config.Conf.Unsplash.AccessKey
	url := fmt.Sprintf("%s/photos/%s/download?client_id=%s", UnsplashAPILocation, id, unsplashAccessKey)

	return utils.SendGETRequest(url, &download{})
}

// SearchUnsplash triggers a photo search and sends a single page of photo
// results for a query.
func SearchUnsplash(w http.ResponseWriter, r *http.Request) error {
	values, err := utils.GetURLQueryParams(r.URL.String())
	if err != nil {
		return err
	}

	key := values.Get("key")
	page := values.Get("page")

	unsplashAccessKey := config.Conf.Unsplash.AccessKey
	url := fmt.Sprintf("%s/search/photos?page=%s&query=%s&per_page=%s&client_id=%s", UnsplashAPILocation, page, key, "28", unsplashAccessKey)

	s := &searchResult{}

	bytes, err := utils.SendGETRequest(url, s)
	if err != nil {
		return err
	}

	return utils.JsonResponse(w, bytes)
}

// GetRandomPhoto retrives a single random photo using the provided collection
// IDs to narrow the pool of photos from which a random one will be chosen.
// If no collection IDs are present, it defaults to 998309 which is the ID of
// the official Stellar Photos collection
func GetRandomPhoto(w http.ResponseWriter, r *http.Request) error {
	values, err := utils.GetURLQueryParams(r.URL.String())
	if err != nil {
		return err
	}

	collections := values.Get("collections")
	if collections == "" {
		collections = "998309"
	}

	unsplashAccessKey := config.Conf.Unsplash.AccessKey
	url := fmt.Sprintf("%s/photos/random?collections=%s&client_id=%s", UnsplashAPILocation, collections, unsplashAccessKey)

	res := &randomPhoto{}

	_, err = utils.SendGETRequest(url, res)
	if err != nil {
		return err
	}

	imageURL := res.Urls.Raw + "&w=2000"

	base64, err := utils.ImageURLToBase64(imageURL)
	if err != nil {
		return err
	}

	data := randomPhotoBase64{
		res,
		base64,
	}

	bytes, err := json.Marshal(data)
	if err != nil {
		return err
	}

	return utils.JsonResponse(w, bytes)
}

// ValidateCollections ensures that all the custom collection IDs that are added
// to the extension are valid
func ValidateCollections(w http.ResponseWriter, r *http.Request) error {
	values, err := utils.GetURLQueryParams(r.URL.String())
	if err != nil {
		return err
	}

	collections := strings.Split(values.Get("collections"), ",")

	unsplashAccessKey := config.Conf.Unsplash.AccessKey

	for _, value := range collections {
		valueToNum, err := strconv.Atoi(value)
		if err != nil {
			return utils.NewHTTPError(err, http.StatusBadRequest, "Collection ID must be a number")
		}

		url := fmt.Sprintf("%s/collections/%d/?client_id=%s", UnsplashAPILocation, valueToNum, unsplashAccessKey)
		_, err = utils.SendGETRequest(url, &collection{})
		if err != nil {
			return err
		}
	}

	w.WriteHeader(http.StatusOK)
	return nil
}
<|endoftext|>


<commit_before>// +build darwin dragonfly freebsd linux netbsd openbsd solaris

package user

import (
	"io"
	"os"

	"golang.org/x/sys/unix"
)

// Unix-specific path to the passwd and group formatted files.
const (
	unixPasswdPath = "/etc/passwd"
	unixGroupPath  = "/etc/group"
)

func lookupUser(username string) (User, error) {
	return lookupUserFunc(func(u User) bool {
		return u.Name == username
	})
}

func lookupUid(uid int) (User, error) {
	return lookupUserFunc(func(u User) bool {
		return u.Uid == uid
	})
}

func lookupUserFunc(filter func(u User) bool) (User, error) {
	// Get operating system-specific passwd reader-closer.
	passwd, err := GetPasswd()
	if err != nil {
		return User{}, err
	}
	defer passwd.Close()

	// Get the users.
	users, err := ParsePasswdFilter(passwd, filter)
	if err != nil {
		return User{}, err
	}

	// No user entries found.
	if len(users) == 0 {
		return User{}, ErrNoPasswdEntries
	}

	// Assume the first entry is the "correct" one.
	return users[0], nil
}

func lookupGroup(groupname string) (Group, error) {
	return lookupGroupFunc(func(g Group) bool {
		return g.Name == groupname
	})
}

func lookupGid(gid int) (Group, error) {
	return lookupGroupFunc(func(g Group) bool {
		return g.Gid == gid
	})
}

func lookupGroupFunc(filter func(g Group) bool) (Group, error) {
	// Get operating system-specific group reader-closer.
	group, err := GetGroup()
	if err != nil {
		return Group{}, err
	}
	defer group.Close()

	// Get the users.
	groups, err := ParseGroupFilter(group, filter)
	if err != nil {
		return Group{}, err
	}

	// No user entries found.
	if len(groups) == 0 {
		return Group{}, ErrNoGroupEntries
	}

	// Assume the first entry is the "correct" one.
	return groups[0], nil
}

func GetPasswdPath() (string, error) {
	return unixPasswdPath, nil
}

func GetPasswd() (io.ReadCloser, error) {
	return os.Open(unixPasswdPath)
}

func GetGroupPath() (string, error) {
	return unixGroupPath, nil
}

func GetGroup() (io.ReadCloser, error) {
	return os.Open(unixGroupPath)
}

// CurrentUser looks up the current user by their user id in /etc/passwd. If the
// user cannot be found (or there is no /etc/passwd file on the filesystem),
// then CurrentUser returns an error.
func CurrentUser() (User, error) {
	return LookupUid(unix.Getuid())
}

// CurrentGroup looks up the current user's group by their primary group id's
// entry in /etc/passwd. If the group cannot be found (or there is no
// /etc/group file on the filesystem), then CurrentGroup returns an error.
func CurrentGroup() (Group, error) {
	return LookupGid(unix.Getgid())
}

func CurrentUserSubUIDs() ([]SubID, error) {
	u, err := CurrentUser()
	if err != nil {
		return nil, err
	}
	return ParseSubIDFileFilter("/etc/subuid",
		func(entry SubID) bool { return entry.Name == u.Name })
}

func CurrentGroupSubGIDs() ([]SubID, error) {
	g, err := CurrentGroup()
	if err != nil {
		return nil, err
	}
	return ParseSubIDFileFilter("/etc/subgid",
		func(entry SubID) bool { return entry.Name == g.Name })
}

func CurrentProcessUIDMap() ([]IDMap, error) {
	return ParseIDMapFile("/proc/self/uid_map")
}

func CurrentProcessGIDMap() ([]IDMap, error) {
	return ParseIDMapFile("/proc/self/gid_map")
}
<commit_msg>libcontainer: CurrentGroupSubGIDs -> CurrentUserSubGIDs<commit_after>// +build darwin dragonfly freebsd linux netbsd openbsd solaris

package user

import (
	"io"
	"os"
	"strconv"

	"golang.org/x/sys/unix"
)

// Unix-specific path to the passwd and group formatted files.
const (
	unixPasswdPath = "/etc/passwd"
	unixGroupPath  = "/etc/group"
)

func lookupUser(username string) (User, error) {
	return lookupUserFunc(func(u User) bool {
		return u.Name == username
	})
}

func lookupUid(uid int) (User, error) {
	return lookupUserFunc(func(u User) bool {
		return u.Uid == uid
	})
}

func lookupUserFunc(filter func(u User) bool) (User, error) {
	// Get operating system-specific passwd reader-closer.
	passwd, err := GetPasswd()
	if err != nil {
		return User{}, err
	}
	defer passwd.Close()

	// Get the users.
	users, err := ParsePasswdFilter(passwd, filter)
	if err != nil {
		return User{}, err
	}

	// No user entries found.
	if len(users) == 0 {
		return User{}, ErrNoPasswdEntries
	}

	// Assume the first entry is the "correct" one.
	return users[0], nil
}

func lookupGroup(groupname string) (Group, error) {
	return lookupGroupFunc(func(g Group) bool {
		return g.Name == groupname
	})
}

func lookupGid(gid int) (Group, error) {
	return lookupGroupFunc(func(g Group) bool {
		return g.Gid == gid
	})
}

func lookupGroupFunc(filter func(g Group) bool) (Group, error) {
	// Get operating system-specific group reader-closer.
	group, err := GetGroup()
	if err != nil {
		return Group{}, err
	}
	defer group.Close()

	// Get the users.
	groups, err := ParseGroupFilter(group, filter)
	if err != nil {
		return Group{}, err
	}

	// No user entries found.
	if len(groups) == 0 {
		return Group{}, ErrNoGroupEntries
	}

	// Assume the first entry is the "correct" one.
	return groups[0], nil
}

func GetPasswdPath() (string, error) {
	return unixPasswdPath, nil
}

func GetPasswd() (io.ReadCloser, error) {
	return os.Open(unixPasswdPath)
}

func GetGroupPath() (string, error) {
	return unixGroupPath, nil
}

func GetGroup() (io.ReadCloser, error) {
	return os.Open(unixGroupPath)
}

// CurrentUser looks up the current user by their user id in /etc/passwd. If the
// user cannot be found (or there is no /etc/passwd file on the filesystem),
// then CurrentUser returns an error.
func CurrentUser() (User, error) {
	return LookupUid(unix.Getuid())
}

// CurrentGroup looks up the current user's group by their primary group id's
// entry in /etc/passwd. If the group cannot be found (or there is no
// /etc/group file on the filesystem), then CurrentGroup returns an error.
func CurrentGroup() (Group, error) {
	return LookupGid(unix.Getgid())
}

func currentUserSubIDs(fileName string) ([]SubID, error) {
	u, err := CurrentUser()
	if err != nil {
		return nil, err
	}
	filter := func(entry SubID) bool {
		return entry.Name == u.Name || entry.Name == strconv.Itoa(u.Uid)
	}
	return ParseSubIDFileFilter(fileName, filter)
}

func CurrentUserSubUIDs() ([]SubID, error) {
	return currentUserSubIDs("/etc/subuid")
}

func CurrentUserSubGIDs() ([]SubID, error) {
	return currentUserSubIDs("/etc/subgid")
}

func CurrentProcessUIDMap() ([]IDMap, error) {
	return ParseIDMapFile("/proc/self/uid_map")
}

func CurrentProcessGIDMap() ([]IDMap, error) {
	return ParseIDMapFile("/proc/self/gid_map")
}
<|endoftext|>


<commit_before>package lexer

import (
	"bufio"
	"code.google.com/p/go.text/transform"
	"io"
)

// Alphabet in Latin-1 order
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÅÖ"

// Normalization translation table
var normalize table

type table map[byte]byte

func (t *table) Transform(dst, src []byte, atEOF bool) (nd, ns int, err error) {
	for i, b := range src {
		dst[i] = t[b]
		nd = i
		ns = i
	}
	return
}

func init() {
	var ch rune
	runes := make(map[rune]rune)
	table = make(map[byte]byte)

	// Början på översatt kod från tokenizer.c

	/*
	 * tokenizer.c delar upp en text i sina ord och matar ut orden
	 * omvandlade till små bokstäver, ett ord per rad följt av ordets
	 * teckenposition i texten. Ett ord definieras som en eller flera
	 * på varandra följande bokstäver i det svenska alfabetet.
	 * Copyright Viggo Kann, viggo@nada.kth.se, 1999
	 *
	 * Lätt omhackad av Jakob Nordström 2003 för att klara tecken med accenter
	 * (t.ex. á, è. ü osv.).
	 */

	for i := 0; i < 256; i++ {
		u2l[i] = 0
	}

	for _, s := range ALPHABET {
		ch = s + 'a' - 'A'
		runes[s] = ch
		runes[ch] = ch
	}

	/*
	 * Nedan följer speciallösning för att klara accenterade tecken
	 *
	 */

	for ch = 224; ch <= 227; ch++ { /* a med accent (utom å och ä) */
		runes[ch] = 'a'
		runes[ch+-'a'+'A'] = 'a'
	}

	ch = 230 /* ae till ä */
	runes[ch] = 'ä'
	runes[ch+-'a'+'A'] = 'ä'

	ch = 231 /* c med cedilj till c*/
	runes[ch] = 'c'
	runes[ch+-'a'+'A'] = 'c'

	for ch = 232; ch <= 235; ch++ { /* e med accent (även é) */
		runes[ch] = 'e'
		runes[ch+-'a'+'A'] = 'e'
	}

	for ch = 236; ch <= 239; ch++ { /* i med accent */
		runes[ch] = 'i'
		runes[ch+-'a'+'A'] = 'i'
	}

	ch = 241 /* n med ~ rill n */
	runes[ch] = 'n'
	runes[ch+-'a'+'A'] = 'n'

	for ch = 242; ch <= 245; ch++ { /* o med accent (förutom ö) */
		runes[ch] = 'o'
		runes[ch+-'a'+'A'] = 'o'
	}

	ch = 248 /* o genomskuret till ö */
	runes[ch] = 'ö'
	runes[ch+-'a'+'A'] = 'ö'

	for ch = 249; ch <= 252; ch++ { /* u med accent */
		runes[ch] = 'u'
		runes[ch+-'a'+'A'] = 'u'
	}

	ch = 253 /* y med accent */
	runes[ch] = 'y'
	runes[ch+-'a'+'A'] = 'y'
	ch = 255
	runes[ch] = 'y'
	runes[ch+-'a'+'A'] = 'y'

	// Slut på översatt kod från tokenizer.c

	for from, to := range runes {
		table[byte(from)] = byte(to)
	}
}

type tokenizer struct {
	input    *bufio.Reader
	position uint64
}

func New(reader io.Reader) {
	return &tokenizer{bufio.NewReader(transform.NewReader(reader, normalize))}
}

func (t *tokenizer) ReadToken() (advance uint64, token string, err error) {
	// Consume non-alphabet characters
	for ch := 0; ch == 0; ch, err = t.input.ReadByte() {
		advance++
		if err != nil {
			return
		}
	}

	err = t.input.UnreadByte()
	if err != nil {
		return
	}
	advance--

	token, err = t.input.ReadString(0)
	if len(token) > 0 {
		// Remove the trailing zero byte
		advance += len(token)
		token = token[:len(token)-1]
	}
	return
}
<commit_msg>Clean up lexer.Tranform<commit_after>package lexer

import (
	"bufio"
	"code.google.com/p/go.text/transform"
	"io"
)

// Alphabet in Latin-1 order
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÅÖ"

// Normalization translation table
var normalize table

type table map[byte]byte

func (t *table) Transform(dst, src []byte, atEOF bool) (int, int, error) {
	for i, b := range src {
		dst[i] = t[b]
	}
	return len(src), len(src), nil
}

func init() {
	var ch rune
	runes := make(map[rune]rune)
	table = make(map[byte]byte)

	// Början på översatt kod från tokenizer.c

	/*
	 * tokenizer.c delar upp en text i sina ord och matar ut orden
	 * omvandlade till små bokstäver, ett ord per rad följt av ordets
	 * teckenposition i texten. Ett ord definieras som en eller flera
	 * på varandra följande bokstäver i det svenska alfabetet.
	 * Copyright Viggo Kann, viggo@nada.kth.se, 1999
	 *
	 * Lätt omhackad av Jakob Nordström 2003 för att klara tecken med accenter
	 * (t.ex. á, è. ü osv.).
	 */

	for i := 0; i < 256; i++ {
		u2l[i] = 0
	}

	for _, s := range ALPHABET {
		ch = s + 'a' - 'A'
		runes[s] = ch
		runes[ch] = ch
	}

	/*
	 * Nedan följer speciallösning för att klara accenterade tecken
	 *
	 */

	for ch = 224; ch <= 227; ch++ { /* a med accent (utom å och ä) */
		runes[ch] = 'a'
		runes[ch+-'a'+'A'] = 'a'
	}

	ch = 230 /* ae till ä */
	runes[ch] = 'ä'
	runes[ch+-'a'+'A'] = 'ä'

	ch = 231 /* c med cedilj till c*/
	runes[ch] = 'c'
	runes[ch+-'a'+'A'] = 'c'

	for ch = 232; ch <= 235; ch++ { /* e med accent (även é) */
		runes[ch] = 'e'
		runes[ch+-'a'+'A'] = 'e'
	}

	for ch = 236; ch <= 239; ch++ { /* i med accent */
		runes[ch] = 'i'
		runes[ch+-'a'+'A'] = 'i'
	}

	ch = 241 /* n med ~ rill n */
	runes[ch] = 'n'
	runes[ch+-'a'+'A'] = 'n'

	for ch = 242; ch <= 245; ch++ { /* o med accent (förutom ö) */
		runes[ch] = 'o'
		runes[ch+-'a'+'A'] = 'o'
	}

	ch = 248 /* o genomskuret till ö */
	runes[ch] = 'ö'
	runes[ch+-'a'+'A'] = 'ö'

	for ch = 249; ch <= 252; ch++ { /* u med accent */
		runes[ch] = 'u'
		runes[ch+-'a'+'A'] = 'u'
	}

	ch = 253 /* y med accent */
	runes[ch] = 'y'
	runes[ch+-'a'+'A'] = 'y'
	ch = 255
	runes[ch] = 'y'
	runes[ch+-'a'+'A'] = 'y'

	// Slut på översatt kod från tokenizer.c

	for from, to := range runes {
		table[byte(from)] = byte(to)
	}
}

type tokenizer struct {
	input    *bufio.Reader
	position uint64
}

func New(reader io.Reader) {
	return &tokenizer{bufio.NewReader(transform.NewReader(reader, normalize))}
}

func (t *tokenizer) ReadToken() (advance uint64, token string, err error) {
	// Consume non-alphabet characters
	for ch := 0; ch == 0; ch, err = t.input.ReadByte() {
		advance++
		if err != nil {
			return
		}
	}

	err = t.input.UnreadByte()
	if err != nil {
		return
	}
	advance--

	token, err = t.input.ReadString(0)
	if len(token) > 0 {
		// Remove the trailing zero byte
		advance += len(token)
		token = token[:len(token)-1]
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"

	"github.com/AdRoll/goamz/aws"
	"github.com/AdRoll/goamz/sns"
	"github.com/AdRoll/goamz/sqs"
	_ "github.com/lib/pq"
)

var SQS *sqs.SQS
var bufferCount = 2000
var sem = make(chan bool, bufferCount)

func main() {
	n := runtime.NumCPU()
	log.Println("Num CPUS:", n)
	runtime.GOMAXPROCS(n)
	for k, v := range os.Args {
		log.Println(k, v)
	}
	log.Println("-----")
	if len(os.Args) > 1 {
		rawData := os.Args[1]

		var kpayload KinesisPayload
		err := json.Unmarshal([]byte(rawData), &kpayload)
		if err != nil {
			log.Println("Error Kinesisis Payload:", err)
		}
		for _, v := range kpayload.Records {
			log.Println("Record:", v)
			var tpm topicPageMessage
			sDec, errDec := base64.StdEncoding.DecodeString(v.Kinesis.Data)
			if errDec != nil {
				log.Println("Error:", errDec)
			} else {
				log.Println(string(sDec))
				errJSON := json.Unmarshal(sDec, &tpm)
				if errJSON != nil {
					log.Println("Error:", errJSON)
					return
				}
				arns := getDevicesArnsByTopicIDPage(tpm.TopicID, tpm.PageNum, 10000)
				//This should return the arn & the lang for the user
				//we'd then pull the correct iten out of the message map
				msgSlice := make([]sqs.Message, 0, 10)
				msgAll := [][]sqs.Message{}
				log.Printf("Started page %v\n", tpm.PageNum)
				for _, v := range arns {
					tempData := fmt.Sprintf("arn:%v|%v", v, tpm.Message)
					msg := sqs.Message{Body: base64.StdEncoding.EncodeToString([]byte(tempData))}
					msgSlice = append(msgSlice, msg)
					if len(msgSlice) == 10 {
						msgAll = append(msgAll, msgSlice)
						msgSlice = []sqs.Message{}
					}
				}
				for _, s := range msgAll {
					s := s //It's idomatic go I swear! http://golang.org/doc/effective_go.html#channels

					//Using the Semaphore
					sem <- true
					go func(sl10 []sqs.Message) {
						proxySNS(sl10)
						defer func() { <-sem }()
					}(s)

				}
				for i := 0; i < cap(sem); i++ {
					sem <- true
				}
				log.Println("All done!")
			}
			if tpm.PageNum == 1 {
				publishPageComplete(1)
			}
			if tpm.LastPage {
				publishPageComplete(tpm.PageNum)
			}
			log.Printf("Completed page %v\n", tpm.PageNum)

		}
		return

	}
	log.Println("Error: os.Args was 1 length.")
}
func publishPageComplete(pagenum int) error {
	topicarn, topicErr := getTopicArn()
	if topicErr != nil {
		log.Println(topicErr)
		return topicErr
	}
	p, s, _ := getSettings()
	auth := aws.Auth{AccessKey: p, SecretKey: s}
	region := aws.Region{}
	region.Name = "us-east-1"
	region.SNSEndpoint = "http://sns.us-east-1.amazonaws.com"
	awssns, _ := sns.New(auth, region)
	if awssns == nil {
		return fmt.Errorf("Can't get sns reference for %v %v", auth, region)
	}
	msg := fmt.Sprintf("Page %v complete.", pagenum)
	opt := sns.PublishOptions{}
	opt.TopicArn = topicarn
	opt.Message = msg
	opt.Subject = msg
	_, pubErr := awssns.Publish(&opt)
	if pubErr != nil {
		return pubErr
	}
	return nil
}

func getDevicesArnsByTopicIDPage(topicID, pagenum, pagesize int) []string {
	var arns []string
	info := getDBSettings()
	db, errCon := sql.Open("postgres", fmt.Sprintf("host=%v user=%v password=%v dbname=%v sslmode=require", info.Host, info.Username, info.Password, info.Database))
	defer db.Close()
	if errCon != nil {
		log.Fatal(errCon)
	}
	rows, err := db.Query(`
		select
			u.endpointarn
		from
			subscription s ,userdevices u
		where
			s.topicid= $1 and
			s.userID=u.userid
			order by u.userid
			limit $2 offset $3
;`, topicID, pagesize, (pagenum-1)*pagesize)
	if err != nil {
		panic(err)
	}
	for rows.Next() {
		var arn string
		errScan := rows.Scan(&arn)
		if errScan != nil {
			panic(errScan)
		}
		arns = append(arns, arn)
	}
	return arns
}

type topicPageMessage struct {
	TopicID  int    `json:"topic_id"`
	Message  string `json:"message"`
	PageNum  int    `json:"page_num"`
	LastPage bool   `json:"last_page"`
}

type KinesisPayload struct {
	Records []struct {
		AwsRegion         string `json:"awsRegion"`
		EventID           string `json:"eventID"`
		EventName         string `json:"eventName"`
		EventSource       string `json:"eventSource"`
		EventSourceARN    string `json:"eventSourceARN"`
		EventVersion      string `json:"eventVersion"`
		InvokeIdentityArn string `json:"invokeIdentityArn"`
		Kinesis           struct {
			Data                 string `json:"data"`
			KinesisSchemaVersion string `json:"kinesisSchemaVersion"`
			PartitionKey         string `json:"partitionKey"`
			SequenceNumber       string `json:"sequenceNumber"`
		} `json:"kinesis"`
	} `json:"Records"`
}

func getTopicArn() (string, error) {
	file, err := ioutil.ReadFile("./settings.json")
	if err != nil {
		return "", err
	}
	settingsMap := make(map[string]string)
	json.Unmarshal(file, &settingsMap)
	return settingsMap["Topicarn"], nil
}

func getSettings() (string, string, error) {
	file, err := ioutil.ReadFile("./settings.json")
	if err != nil {
		return "", "", nil
	}
	settingsMap := make(map[string]string)
	json.Unmarshal(file, &settingsMap)
	return settingsMap["Access"], settingsMap["Secret"], nil
}

func getDBSettings() *dbInfo {
	file, err := ioutil.ReadFile("./settings.json")
	if err != nil {
		log.Println("Error:", err)
		return nil
	}
	db := dbInfo{}
	err2 := json.Unmarshal(file, &db)
	if err2 != nil {
		log.Println("Error:", err2)
		return nil
	}
	return &db
}

type dbInfo struct {
	Host     string
	Database string
	Username string
	Password string
}

func proxySNS(msgs []sqs.Message) {
	pub, sec, _ := getSettings()
	sqs, err := getQueue("sns-prox", pub, sec)
	if err != nil {
		panic(err)
	}
	_, respErr := sqs.SendMessageBatch(msgs)
	if respErr != nil {
		log.Println("ERROR:", respErr)
	}
}

func getQueue(name, public, secret string) (*sqs.Queue, error) {
	auth := aws.Auth{AccessKey: public, SecretKey: secret}
	region := aws.Region{}
	region.Name = "us-east-1"
	region.SQSEndpoint = "http://sqs.us-east-1.amazonaws.com"
	awssqs := sqs.New(auth, region)
	if awssqs == nil {
		return nil, fmt.Errorf("Can't get sqs reference for %v %v", auth, region)
	}
	return awssqs.GetQueue(name)
}
<commit_msg>update notifications to only fire once since we upped the batch count from kinesis<commit_after>package main

import (
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"runtime"

	"github.com/AdRoll/goamz/aws"
	"github.com/AdRoll/goamz/sns"
	"github.com/AdRoll/goamz/sqs"
	_ "github.com/lib/pq"
)

var SQS *sqs.SQS
var bufferCount = 2000
var sem = make(chan bool, bufferCount)

func main() {
	n := runtime.NumCPU()
	log.Println("Num CPUS:", n)
	runtime.GOMAXPROCS(n)
	for k, v := range os.Args {
		log.Println(k, v)
	}
	log.Println("-----")
	if len(os.Args) > 1 {
		rawData := os.Args[1]

		var kpayload KinesisPayload
		err := json.Unmarshal([]byte(rawData), &kpayload)
		if err != nil {
			log.Println("Error Kinesisis Payload:", err)
		}
		recrun := false
		lastrun := false
		lastpage := 0
		for _, v := range kpayload.Records {
			log.Println("Record:", v)
			var tpm topicPageMessage
			sDec, errDec := base64.StdEncoding.DecodeString(v.Kinesis.Data)
			if errDec != nil {
				log.Println("Error:", errDec)
			} else {
				log.Println(string(sDec))
				errJSON := json.Unmarshal(sDec, &tpm)
				if errJSON != nil {
					log.Println("Error:", errJSON)
					return
				}
				arns := getDevicesArnsByTopicIDPage(tpm.TopicID, tpm.PageNum, 10000)
				//This should return the arn & the lang for the user
				//we'd then pull the correct iten out of the message map
				msgSlice := make([]sqs.Message, 0, 10)
				msgAll := [][]sqs.Message{}
				log.Printf("Started page %v\n", tpm.PageNum)
				for _, v := range arns {
					tempData := fmt.Sprintf("arn:%v|%v", v, tpm.Message)
					msg := sqs.Message{Body: base64.StdEncoding.EncodeToString([]byte(tempData))}
					msgSlice = append(msgSlice, msg)
					if len(msgSlice) == 10 {
						msgAll = append(msgAll, msgSlice)
						msgSlice = []sqs.Message{}
					}
				}
				for _, s := range msgAll {
					s := s //It's idomatic go I swear! http://golang.org/doc/effective_go.html#channels

					//Using the Semaphore
					sem <- true
					go func(sl10 []sqs.Message) {
						proxySNS(sl10)
						defer func() { <-sem }()
					}(s)

				}
				for i := 0; i < cap(sem); i++ {
					sem <- true
				}
				log.Println("All done!")
			}
			if tpm.PageNum == 1 && !recrun {
				publishPageComplete(1)
			}
			recrun = true
			if tpm.LastPage {
				lastrun = true
				lastpage = tpm.PageNum
				publishPageComplete(tpm.PageNum)
			}
			log.Printf("Completed page %v\n", tpm.PageNum)

		}
		if lastrun {
			publishPageComplete(lastpage)
		}
		return

	}
	log.Println("Error: os.Args was 1 length.")
}
func publishPageComplete(pagenum int) error {
	topicarn, topicErr := getTopicArn()
	if topicErr != nil {
		log.Println(topicErr)
		return topicErr
	}
	p, s, _ := getSettings()
	auth := aws.Auth{AccessKey: p, SecretKey: s}
	region := aws.Region{}
	region.Name = "us-east-1"
	region.SNSEndpoint = "http://sns.us-east-1.amazonaws.com"
	awssns, _ := sns.New(auth, region)
	if awssns == nil {
		return fmt.Errorf("Can't get sns reference for %v %v", auth, region)
	}
	msg := fmt.Sprintf("Page %v complete.", pagenum)
	opt := sns.PublishOptions{}
	opt.TopicArn = topicarn
	opt.Message = msg
	opt.Subject = msg
	_, pubErr := awssns.Publish(&opt)
	if pubErr != nil {
		return pubErr
	}
	return nil
}

func getDevicesArnsByTopicIDPage(topicID, pagenum, pagesize int) []string {
	var arns []string
	info := getDBSettings()
	db, errCon := sql.Open("postgres", fmt.Sprintf("host=%v user=%v password=%v dbname=%v sslmode=require", info.Host, info.Username, info.Password, info.Database))
	defer db.Close()
	if errCon != nil {
		log.Fatal(errCon)
	}
	rows, err := db.Query(`
		select
			u.endpointarn
		from
			subscription s ,userdevices u
		where
			s.topicid= $1 and
			s.userID=u.userid
			order by u.userid
			limit $2 offset $3
;`, topicID, pagesize, (pagenum-1)*pagesize)
	if err != nil {
		panic(err)
	}
	for rows.Next() {
		var arn string
		errScan := rows.Scan(&arn)
		if errScan != nil {
			panic(errScan)
		}
		arns = append(arns, arn)
	}
	return arns
}

type topicPageMessage struct {
	TopicID  int    `json:"topic_id"`
	Message  string `json:"message"`
	PageNum  int    `json:"page_num"`
	LastPage bool   `json:"last_page"`
}

type KinesisPayload struct {
	Records []struct {
		AwsRegion         string `json:"awsRegion"`
		EventID           string `json:"eventID"`
		EventName         string `json:"eventName"`
		EventSource       string `json:"eventSource"`
		EventSourceARN    string `json:"eventSourceARN"`
		EventVersion      string `json:"eventVersion"`
		InvokeIdentityArn string `json:"invokeIdentityArn"`
		Kinesis           struct {
			Data                 string `json:"data"`
			KinesisSchemaVersion string `json:"kinesisSchemaVersion"`
			PartitionKey         string `json:"partitionKey"`
			SequenceNumber       string `json:"sequenceNumber"`
		} `json:"kinesis"`
	} `json:"Records"`
}

func getTopicArn() (string, error) {
	file, err := ioutil.ReadFile("./settings.json")
	if err != nil {
		return "", err
	}
	settingsMap := make(map[string]string)
	json.Unmarshal(file, &settingsMap)
	return settingsMap["Topicarn"], nil
}

func getSettings() (string, string, error) {
	file, err := ioutil.ReadFile("./settings.json")
	if err != nil {
		return "", "", nil
	}
	settingsMap := make(map[string]string)
	json.Unmarshal(file, &settingsMap)
	return settingsMap["Access"], settingsMap["Secret"], nil
}

func getDBSettings() *dbInfo {
	file, err := ioutil.ReadFile("./settings.json")
	if err != nil {
		log.Println("Error:", err)
		return nil
	}
	db := dbInfo{}
	err2 := json.Unmarshal(file, &db)
	if err2 != nil {
		log.Println("Error:", err2)
		return nil
	}
	return &db
}

type dbInfo struct {
	Host     string
	Database string
	Username string
	Password string
}

func proxySNS(msgs []sqs.Message) {
	pub, sec, _ := getSettings()
	sqs, err := getQueue("sns-prox", pub, sec)
	if err != nil {
		panic(err)
	}
	_, respErr := sqs.SendMessageBatch(msgs)
	if respErr != nil {
		log.Println("ERROR:", respErr)
	}
}

func getQueue(name, public, secret string) (*sqs.Queue, error) {
	auth := aws.Auth{AccessKey: public, SecretKey: secret}
	region := aws.Region{}
	region.Name = "us-east-1"
	region.SQSEndpoint = "http://sqs.us-east-1.amazonaws.com"
	awssqs := sqs.New(auth, region)
	if awssqs == nil {
		return nil, fmt.Errorf("Can't get sqs reference for %v %v", auth, region)
	}
	return awssqs.GetQueue(name)
}
<|endoftext|>


<commit_before>package db

import (
	"math"
	"strings"
	"sync"
	"time"

	sgbucket "github.com/couchbase/sg-bucket"
	"github.com/couchbase/sync_gateway/auth"
	"github.com/couchbase/sync_gateway/base"
	"github.com/couchbase/sync_gateway/channels"
)

// A wrapper around a Bucket's TapFeed that allows any number of client goroutines to wait for
// changes.
type changeListener struct {
	bucket                base.Bucket
	bucketName            string                  // Used for logging
	tapFeed               base.TapFeed            // Observes changes to bucket
	tapNotifier           *sync.Cond              // Posts notifications when documents are updated
	FeedArgs              sgbucket.FeedArguments  // The Tap Args (backfill, etc)
	counter               uint64                  // Event counter; increments on every doc update
	terminateCheckCounter uint64                  // Termination Event counter; increments on every notifyCheckForTermination
	keyCounts             map[string]uint64       // Latest count at which each doc key was updated
	DocChannel            chan sgbucket.FeedEvent // Passthru channel for doc mutations
	OnDocChanged          DocChangedFunc          // Called when change arrives on feed
	trackDocs             bool                    // Whether events should be routed to DocChannel passthru
	terminator            chan bool               // Signal to cause cbdatasource bucketdatasource.Close() to be called, which removes dcp receiver
}

type DocChangedFunc func(event sgbucket.FeedEvent)

func (listener *changeListener) Init(name string) {
	listener.bucketName = name
	listener.counter = 1
	listener.terminateCheckCounter = 0
	listener.keyCounts = map[string]uint64{}
	listener.tapNotifier = sync.NewCond(&sync.Mutex{})
}

// Starts a changeListener on a given Bucket.

func (listener *changeListener) Start(bucket base.Bucket, trackDocs bool, backfillMode uint64, bucketStateNotify sgbucket.BucketNotifyFn) error {

	listener.terminator = make(chan bool)
	listener.bucket = bucket
	listener.bucketName = bucket.GetName()
	listener.FeedArgs = sgbucket.FeedArguments{
		Backfill:   backfillMode,
		Notify:     bucketStateNotify,
		Terminator: listener.terminator,
	}

	if trackDocs {
		listener.DocChannel = make(chan sgbucket.FeedEvent, 100)
		listener.trackDocs = true
	}

	return listener.StartMutationFeed(bucket)
}

func (listener *changeListener) StartMutationFeed(bucket base.Bucket) error {

	// Uses DCP by default, unless TAP is explicitly specified
	feedType := base.GetFeedType(bucket)
	switch feedType {
	case base.TapFeedType:
		// TAP Feed
		//    TAP feed is a go-channel of Tap events served by the bucket.  Start the feed, then
		//    start a goroutine to work the event channel, calling ProcessEvent for each event
		var err error
		listener.tapFeed, err = bucket.StartTapFeed(listener.FeedArgs)
		if err != nil {
			return err
		}
		go func() {
			defer func() {
				listener.notifyStopping()
				if listener.DocChannel != nil {
					close(listener.DocChannel)
				}
			}()
			for event := range listener.tapFeed.Events() {
				event.TimeReceived = time.Now()
				listener.ProcessFeedEvent(event)
			}
		}()
		return nil
	default:
		// DCP Feed
		//    DCP receiver isn't go-channel based - DCPReceiver calls ProcessEvent directly.
		base.Infof(base.KeyDCP, "Using DCP feed for bucket: %q (based on feed_type specified in config file)", base.UD(bucket.GetName()))
		return bucket.StartDCPFeed(listener.FeedArgs, listener.ProcessFeedEvent)
	}
}

// ProcessFeedEvent is invoked for each mutate or delete event seen on the server's mutation feed (TAP or DCP).  Uses document
// key to determine handling, based on whether the incoming mutation is an internal Sync Gateway document.
func (listener *changeListener) ProcessFeedEvent(event sgbucket.FeedEvent) bool {
	requiresCheckpointPersistence := true
	if event.Opcode == sgbucket.FeedOpMutation || event.Opcode == sgbucket.FeedOpDeletion {
		key := string(event.Key)
		if strings.HasPrefix(key, base.UserPrefix) ||
			strings.HasPrefix(key, base.RolePrefix) { // SG users and roles
			if listener.OnDocChanged != nil && event.Opcode == sgbucket.FeedOpMutation {
				listener.OnDocChanged(event)
			}
			listener.Notify(base.SetOf(key))
		} else if strings.HasPrefix(key, base.UnusedSeqPrefix) || strings.HasPrefix(key, base.UnusedSeqRangePrefix) { // SG unused sequence marker docs
			if listener.OnDocChanged != nil {
				listener.OnDocChanged(event)
			}
		} else if strings.HasPrefix(key, base.DCPCheckpointPrefix) { // SG DCP checkpoint docs
			// Do not require checkpoint persistence when DCP checkpoint docs come back over DCP - otherwise
			// we'll end up in a feedback loop for their vbucket
			requiresCheckpointPersistence = false
		} else if !strings.HasPrefix(key, base.SyncPrefix) && !strings.HasPrefix(key, base.KIndexPrefix) { // Non-SG docs
			if listener.OnDocChanged != nil {
				listener.OnDocChanged(event)
			}
			if listener.trackDocs {
				listener.DocChannel <- event
			}

		}
	}
	return requiresCheckpointPersistence
}

// Stops a changeListener. Any pending Wait() calls will immediately return false.
func (listener *changeListener) Stop() {

	base.Debugf(base.KeyChanges, "changeListener.Stop() called")

	if listener.terminator != nil {
		close(listener.terminator)
	}

	if listener.tapNotifier != nil {
		// Unblock any change listeners blocked on tapNotifier.Wait()
		listener.tapNotifier.Broadcast()
	}

	if listener.tapFeed != nil {
		listener.tapFeed.Close()
	}
}

func (listener changeListener) TapFeed() base.TapFeed {
	return listener.tapFeed
}

//////// NOTIFICATIONS:

// Changes the counter, notifying waiting clients.
func (listener *changeListener) Notify(keys base.Set) {
	if len(keys) == 0 {
		return
	}
	listener.tapNotifier.L.Lock()
	listener.counter++
	for key := range keys {
		listener.keyCounts[key] = listener.counter
	}
	base.Debugf(base.KeyChanges, "Notifying that %q changed (keys=%q) count=%d",
		base.MD(listener.bucketName), base.UD(keys), listener.counter)
	listener.tapNotifier.Broadcast()
	listener.tapNotifier.L.Unlock()
}

// Changes the counter, notifying waiting clients.
func (listener *changeListener) NotifyCheckForTermination(keys base.Set) {
	if len(keys) == 0 {
		return
	}
	listener.tapNotifier.L.Lock()

	//Increment terminateCheckCounter, but loop back to zero
	//if we have reached maximum value for uint64 type
	if listener.terminateCheckCounter < math.MaxUint64 {
		listener.terminateCheckCounter++
	} else {
		listener.terminateCheckCounter = 0
	}

	base.Debugf(base.KeyChanges, "Notifying to check for _changes feed termination")
	listener.tapNotifier.Broadcast()
	listener.tapNotifier.L.Unlock()
}

func (listener *changeListener) notifyStopping() {
	listener.tapNotifier.L.Lock()
	listener.counter = 0
	listener.keyCounts = map[string]uint64{}
	base.Debugf(base.KeyChanges, "Notifying that changeListener is stopping")
	listener.tapNotifier.Broadcast()
	listener.tapNotifier.L.Unlock()
}

// Waits until either the counter, or terminateCheckCounter exceeds the given value. Returns the new counters.
func (listener *changeListener) Wait(keys []string, counter uint64, terminateCheckCounter uint64) (uint64, uint64) {
	listener.tapNotifier.L.Lock()
	defer listener.tapNotifier.L.Unlock()
	base.Debugf(base.KeyChanges, "No new changes to send to change listener.  Waiting for %q's count to pass %d",
		base.MD(listener.bucketName), counter)

	for {
		curCounter := listener._currentCount(keys)

		if curCounter != counter || listener.terminateCheckCounter != terminateCheckCounter {
			return curCounter, listener.terminateCheckCounter
		}

		listener.tapNotifier.Wait()

		// Don't go back through the for loop if this changeListener was terminated
		select {
		case <-listener.terminator:
			return 0, 0
		default:
			// do nothing
		}

	}
}

// Returns the max value of the counter for all the given keys
func (listener *changeListener) CurrentCount(keys []string) uint64 {
	listener.tapNotifier.L.Lock()
	defer listener.tapNotifier.L.Unlock()
	return listener._currentCount(keys)
}

func (listener *changeListener) _currentCount(keys []string) uint64 {
	var max uint64 = 0
	for _, key := range keys {
		if count := listener.keyCounts[key]; count > max {
			max = count
		}
	}
	return max
}

//////// CHANGE WAITER

// Helper for waiting on a changeListener. Every call to wait() will wait for the
// listener's counter to increment from the value at the last call.
type changeWaiter struct {
	listener                  *changeListener
	keys                      []string
	userKeys                  []string
	lastCounter               uint64
	lastTerminateCheckCounter uint64
	lastUserCount             uint64
}

// Creates a new changeWaiter that will wait for changes for the given document keys.
func (listener *changeListener) NewWaiter(keys []string) *changeWaiter {
	return &changeWaiter{
		listener:                  listener,
		keys:                      keys,
		lastCounter:               listener.CurrentCount(keys),
		lastTerminateCheckCounter: listener.terminateCheckCounter,
	}
}

func (listener *changeListener) NewWaiterWithChannels(chans base.Set, user auth.User) *changeWaiter {
	waitKeys := make([]string, 0, 5)
	for channel := range chans {
		waitKeys = append(waitKeys, channel)
	}
	var userKeys []string
	if user != nil {
		userKeys = []string{base.UserPrefix + user.Name()}
		for role := range user.RoleNames() {
			userKeys = append(userKeys, base.RolePrefix+role)
		}
		waitKeys = append(waitKeys, userKeys...)
	}
	waiter := listener.NewWaiter(waitKeys)
	waiter.userKeys = userKeys
	if userKeys != nil {
		waiter.lastUserCount = listener.CurrentCount(userKeys)
	}
	return waiter
}

// Waits for the changeListener's counter to change from the last time Wait() was called.
func (waiter *changeWaiter) Wait() uint32 {

	lastTerminateCheckCounter := waiter.lastTerminateCheckCounter
	lastCounter := waiter.lastCounter
	waiter.lastCounter, waiter.lastTerminateCheckCounter = waiter.listener.Wait(waiter.keys, waiter.lastCounter, waiter.lastTerminateCheckCounter)
	if waiter.userKeys != nil {
		waiter.lastUserCount = waiter.listener.CurrentCount(waiter.userKeys)
	}
	countChanged := waiter.lastCounter > lastCounter

	//Uses != to compare as value can cycle back through 0
	terminateCheckCountChanged := waiter.lastTerminateCheckCounter != lastTerminateCheckCounter

	if countChanged {
		return WaiterHasChanges
	} else if terminateCheckCountChanged {
		return WaiterCheckTerminated
	} else {
		return WaiterClosed
	}
}

// Returns the current counter value for the waiter's user (and roles).
// If this value changes, it means the user or roles have been updated.
func (waiter *changeWaiter) CurrentUserCount() uint64 {
	return waiter.lastUserCount
}

// Updates the set of channel keys in the ChangeWaiter (maintains the existing set of user keys)
func (waiter *changeWaiter) UpdateChannels(chans channels.TimedSet) {
	initialCapacity := len(chans) + len(waiter.userKeys)
	updatedKeys := make([]string, 0, initialCapacity)
	for channel := range chans {
		updatedKeys = append(updatedKeys, channel)
	}
	if len(waiter.userKeys) > 0 {
		updatedKeys = append(updatedKeys, waiter.userKeys...)
	}
	waiter.keys = updatedKeys

}

// Returns the set of user keys for this ChangeWaiter
func (waiter *changeWaiter) GetUserKeys() (result []string) {
	if len(waiter.userKeys) == 0 {
		return result
	}
	result = make([]string, len(waiter.userKeys))
	copy(result, waiter.userKeys)
	return result
}
<commit_msg>CBG-408 - Don't reprocess unused sequence docs on expiry (#4168)<commit_after>package db

import (
	"math"
	"strings"
	"sync"
	"time"

	sgbucket "github.com/couchbase/sg-bucket"
	"github.com/couchbase/sync_gateway/auth"
	"github.com/couchbase/sync_gateway/base"
	"github.com/couchbase/sync_gateway/channels"
)

// A wrapper around a Bucket's TapFeed that allows any number of client goroutines to wait for
// changes.
type changeListener struct {
	bucket                base.Bucket
	bucketName            string                  // Used for logging
	tapFeed               base.TapFeed            // Observes changes to bucket
	tapNotifier           *sync.Cond              // Posts notifications when documents are updated
	FeedArgs              sgbucket.FeedArguments  // The Tap Args (backfill, etc)
	counter               uint64                  // Event counter; increments on every doc update
	terminateCheckCounter uint64                  // Termination Event counter; increments on every notifyCheckForTermination
	keyCounts             map[string]uint64       // Latest count at which each doc key was updated
	DocChannel            chan sgbucket.FeedEvent // Passthru channel for doc mutations
	OnDocChanged          DocChangedFunc          // Called when change arrives on feed
	trackDocs             bool                    // Whether events should be routed to DocChannel passthru
	terminator            chan bool               // Signal to cause cbdatasource bucketdatasource.Close() to be called, which removes dcp receiver
}

type DocChangedFunc func(event sgbucket.FeedEvent)

func (listener *changeListener) Init(name string) {
	listener.bucketName = name
	listener.counter = 1
	listener.terminateCheckCounter = 0
	listener.keyCounts = map[string]uint64{}
	listener.tapNotifier = sync.NewCond(&sync.Mutex{})
}

// Starts a changeListener on a given Bucket.

func (listener *changeListener) Start(bucket base.Bucket, trackDocs bool, backfillMode uint64, bucketStateNotify sgbucket.BucketNotifyFn) error {

	listener.terminator = make(chan bool)
	listener.bucket = bucket
	listener.bucketName = bucket.GetName()
	listener.FeedArgs = sgbucket.FeedArguments{
		Backfill:   backfillMode,
		Notify:     bucketStateNotify,
		Terminator: listener.terminator,
	}

	if trackDocs {
		listener.DocChannel = make(chan sgbucket.FeedEvent, 100)
		listener.trackDocs = true
	}

	return listener.StartMutationFeed(bucket)
}

func (listener *changeListener) StartMutationFeed(bucket base.Bucket) error {

	// Uses DCP by default, unless TAP is explicitly specified
	feedType := base.GetFeedType(bucket)
	switch feedType {
	case base.TapFeedType:
		// TAP Feed
		//    TAP feed is a go-channel of Tap events served by the bucket.  Start the feed, then
		//    start a goroutine to work the event channel, calling ProcessEvent for each event
		var err error
		listener.tapFeed, err = bucket.StartTapFeed(listener.FeedArgs)
		if err != nil {
			return err
		}
		go func() {
			defer func() {
				listener.notifyStopping()
				if listener.DocChannel != nil {
					close(listener.DocChannel)
				}
			}()
			for event := range listener.tapFeed.Events() {
				event.TimeReceived = time.Now()
				listener.ProcessFeedEvent(event)
			}
		}()
		return nil
	default:
		// DCP Feed
		//    DCP receiver isn't go-channel based - DCPReceiver calls ProcessEvent directly.
		base.Infof(base.KeyDCP, "Using DCP feed for bucket: %q (based on feed_type specified in config file)", base.UD(bucket.GetName()))
		return bucket.StartDCPFeed(listener.FeedArgs, listener.ProcessFeedEvent)
	}
}

// ProcessFeedEvent is invoked for each mutate or delete event seen on the server's mutation feed (TAP or DCP).  Uses document
// key to determine handling, based on whether the incoming mutation is an internal Sync Gateway document.
func (listener *changeListener) ProcessFeedEvent(event sgbucket.FeedEvent) bool {
	requiresCheckpointPersistence := true
	if event.Opcode == sgbucket.FeedOpMutation || event.Opcode == sgbucket.FeedOpDeletion {
		key := string(event.Key)
		if strings.HasPrefix(key, base.UserPrefix) ||
			strings.HasPrefix(key, base.RolePrefix) { // SG users and roles
			if listener.OnDocChanged != nil && event.Opcode == sgbucket.FeedOpMutation {
				listener.OnDocChanged(event)
			}
			listener.Notify(base.SetOf(key))
		} else if strings.HasPrefix(key, base.UnusedSeqPrefix) || strings.HasPrefix(key, base.UnusedSeqRangePrefix) { // SG unused sequence marker docs
			if listener.OnDocChanged != nil && event.Opcode == sgbucket.FeedOpMutation {
				listener.OnDocChanged(event)
			}
		} else if strings.HasPrefix(key, base.DCPCheckpointPrefix) { // SG DCP checkpoint docs
			// Do not require checkpoint persistence when DCP checkpoint docs come back over DCP - otherwise
			// we'll end up in a feedback loop for their vbucket
			requiresCheckpointPersistence = false
		} else if !strings.HasPrefix(key, base.SyncPrefix) && !strings.HasPrefix(key, base.KIndexPrefix) { // Non-SG docs
			if listener.OnDocChanged != nil {
				listener.OnDocChanged(event)
			}
			if listener.trackDocs {
				listener.DocChannel <- event
			}

		}
	}
	return requiresCheckpointPersistence
}

// Stops a changeListener. Any pending Wait() calls will immediately return false.
func (listener *changeListener) Stop() {

	base.Debugf(base.KeyChanges, "changeListener.Stop() called")

	if listener.terminator != nil {
		close(listener.terminator)
	}

	if listener.tapNotifier != nil {
		// Unblock any change listeners blocked on tapNotifier.Wait()
		listener.tapNotifier.Broadcast()
	}

	if listener.tapFeed != nil {
		listener.tapFeed.Close()
	}
}

func (listener changeListener) TapFeed() base.TapFeed {
	return listener.tapFeed
}

//////// NOTIFICATIONS:

// Changes the counter, notifying waiting clients.
func (listener *changeListener) Notify(keys base.Set) {
	if len(keys) == 0 {
		return
	}
	listener.tapNotifier.L.Lock()
	listener.counter++
	for key := range keys {
		listener.keyCounts[key] = listener.counter
	}
	base.Debugf(base.KeyChanges, "Notifying that %q changed (keys=%q) count=%d",
		base.MD(listener.bucketName), base.UD(keys), listener.counter)
	listener.tapNotifier.Broadcast()
	listener.tapNotifier.L.Unlock()
}

// Changes the counter, notifying waiting clients.
func (listener *changeListener) NotifyCheckForTermination(keys base.Set) {
	if len(keys) == 0 {
		return
	}
	listener.tapNotifier.L.Lock()

	//Increment terminateCheckCounter, but loop back to zero
	//if we have reached maximum value for uint64 type
	if listener.terminateCheckCounter < math.MaxUint64 {
		listener.terminateCheckCounter++
	} else {
		listener.terminateCheckCounter = 0
	}

	base.Debugf(base.KeyChanges, "Notifying to check for _changes feed termination")
	listener.tapNotifier.Broadcast()
	listener.tapNotifier.L.Unlock()
}

func (listener *changeListener) notifyStopping() {
	listener.tapNotifier.L.Lock()
	listener.counter = 0
	listener.keyCounts = map[string]uint64{}
	base.Debugf(base.KeyChanges, "Notifying that changeListener is stopping")
	listener.tapNotifier.Broadcast()
	listener.tapNotifier.L.Unlock()
}

// Waits until either the counter, or terminateCheckCounter exceeds the given value. Returns the new counters.
func (listener *changeListener) Wait(keys []string, counter uint64, terminateCheckCounter uint64) (uint64, uint64) {
	listener.tapNotifier.L.Lock()
	defer listener.tapNotifier.L.Unlock()
	base.Debugf(base.KeyChanges, "No new changes to send to change listener.  Waiting for %q's count to pass %d",
		base.MD(listener.bucketName), counter)

	for {
		curCounter := listener._currentCount(keys)

		if curCounter != counter || listener.terminateCheckCounter != terminateCheckCounter {
			return curCounter, listener.terminateCheckCounter
		}

		listener.tapNotifier.Wait()

		// Don't go back through the for loop if this changeListener was terminated
		select {
		case <-listener.terminator:
			return 0, 0
		default:
			// do nothing
		}

	}
}

// Returns the max value of the counter for all the given keys
func (listener *changeListener) CurrentCount(keys []string) uint64 {
	listener.tapNotifier.L.Lock()
	defer listener.tapNotifier.L.Unlock()
	return listener._currentCount(keys)
}

func (listener *changeListener) _currentCount(keys []string) uint64 {
	var max uint64 = 0
	for _, key := range keys {
		if count := listener.keyCounts[key]; count > max {
			max = count
		}
	}
	return max
}

//////// CHANGE WAITER

// Helper for waiting on a changeListener. Every call to wait() will wait for the
// listener's counter to increment from the value at the last call.
type changeWaiter struct {
	listener                  *changeListener
	keys                      []string
	userKeys                  []string
	lastCounter               uint64
	lastTerminateCheckCounter uint64
	lastUserCount             uint64
}

// Creates a new changeWaiter that will wait for changes for the given document keys.
func (listener *changeListener) NewWaiter(keys []string) *changeWaiter {
	return &changeWaiter{
		listener:                  listener,
		keys:                      keys,
		lastCounter:               listener.CurrentCount(keys),
		lastTerminateCheckCounter: listener.terminateCheckCounter,
	}
}

func (listener *changeListener) NewWaiterWithChannels(chans base.Set, user auth.User) *changeWaiter {
	waitKeys := make([]string, 0, 5)
	for channel := range chans {
		waitKeys = append(waitKeys, channel)
	}
	var userKeys []string
	if user != nil {
		userKeys = []string{base.UserPrefix + user.Name()}
		for role := range user.RoleNames() {
			userKeys = append(userKeys, base.RolePrefix+role)
		}
		waitKeys = append(waitKeys, userKeys...)
	}
	waiter := listener.NewWaiter(waitKeys)
	waiter.userKeys = userKeys
	if userKeys != nil {
		waiter.lastUserCount = listener.CurrentCount(userKeys)
	}
	return waiter
}

// Waits for the changeListener's counter to change from the last time Wait() was called.
func (waiter *changeWaiter) Wait() uint32 {

	lastTerminateCheckCounter := waiter.lastTerminateCheckCounter
	lastCounter := waiter.lastCounter
	waiter.lastCounter, waiter.lastTerminateCheckCounter = waiter.listener.Wait(waiter.keys, waiter.lastCounter, waiter.lastTerminateCheckCounter)
	if waiter.userKeys != nil {
		waiter.lastUserCount = waiter.listener.CurrentCount(waiter.userKeys)
	}
	countChanged := waiter.lastCounter > lastCounter

	//Uses != to compare as value can cycle back through 0
	terminateCheckCountChanged := waiter.lastTerminateCheckCounter != lastTerminateCheckCounter

	if countChanged {
		return WaiterHasChanges
	} else if terminateCheckCountChanged {
		return WaiterCheckTerminated
	} else {
		return WaiterClosed
	}
}

// Returns the current counter value for the waiter's user (and roles).
// If this value changes, it means the user or roles have been updated.
func (waiter *changeWaiter) CurrentUserCount() uint64 {
	return waiter.lastUserCount
}

// Updates the set of channel keys in the ChangeWaiter (maintains the existing set of user keys)
func (waiter *changeWaiter) UpdateChannels(chans channels.TimedSet) {
	initialCapacity := len(chans) + len(waiter.userKeys)
	updatedKeys := make([]string, 0, initialCapacity)
	for channel := range chans {
		updatedKeys = append(updatedKeys, channel)
	}
	if len(waiter.userKeys) > 0 {
		updatedKeys = append(updatedKeys, waiter.userKeys...)
	}
	waiter.keys = updatedKeys

}

// Returns the set of user keys for this ChangeWaiter
func (waiter *changeWaiter) GetUserKeys() (result []string) {
	if len(waiter.userKeys) == 0 {
		return result
	}
	result = make([]string, len(waiter.userKeys))
	copy(result, waiter.userKeys)
	return result
}
<|endoftext|>


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

// +build !windows,!nacl,!plan9

package syslog

import (
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"strings"
	"sync"
	"time"
)

// The Priority is a combination of the syslog facility and
// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
// message from the FTP facility. The default severity is LOG_EMERG;
// the default facility is LOG_KERN.
type Priority int

const severityMask = 0x07
const facilityMask = 0xf8

const (
	// Severity.

	// From /usr/include/sys/syslog.h.
	// These are the same on Linux, BSD, and OS X.
	LOG_EMERG Priority = iota
	LOG_ALERT
	LOG_CRIT
	LOG_ERR
	LOG_WARNING
	LOG_NOTICE
	LOG_INFO
	LOG_DEBUG
)

const (
	// Facility.

	// From /usr/include/sys/syslog.h.
	// These are the same up to LOG_FTP on Linux, BSD, and OS X.
	LOG_KERN Priority = iota << 3
	LOG_USER
	LOG_MAIL
	LOG_DAEMON
	LOG_AUTH
	LOG_SYSLOG
	LOG_LPR
	LOG_NEWS
	LOG_UUCP
	LOG_CRON
	LOG_AUTHPRIV
	LOG_FTP
	_ // unused
	_ // unused
	_ // unused
	_ // unused
	LOG_LOCAL0
	LOG_LOCAL1
	LOG_LOCAL2
	LOG_LOCAL3
	LOG_LOCAL4
	LOG_LOCAL5
	LOG_LOCAL6
	LOG_LOCAL7
)

// A Writer is a connection to a syslog server.
type Writer struct {
	priority Priority
	tag      string
	hostname string
	network  string
	raddr    string

	mu   sync.Mutex // guards conn
	conn serverConn
}

// This interface and the separate syslog_unix.go file exist for
// Solaris support as implemented by gccgo. On Solaris you cannot
// simply open a TCP connection to the syslog daemon. The gccgo
// sources have a syslog_solaris.go file that implements unixSyslog to
// return a type that satisfies this interface and simply calls the C
// library syslog function.
type serverConn interface {
	writeString(p Priority, hostname, tag, s, nl string) error
	close() error
}

type netConn struct {
	local bool
	conn  net.Conn
}

// New establishes a new connection to the system log daemon. Each
// write to the returned writer sends a log message with the given
// priority and prefix.
func New(priority Priority, tag string) (*Writer, error) {
	return Dial("", "", priority, tag)
}

// Dial establishes a connection to a log daemon by connecting to
// address raddr on the specified network. Each write to the returned
// writer sends a log message with the given facility, severity and
// tag.
// If network is empty, Dial will connect to the local syslog server.
// Otherwise, see the documentation for net.Dial for valid values
// of network and raddr.
func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
	if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
		return nil, errors.New("log/syslog: invalid priority")
	}

	if tag == "" {
		tag = os.Args[0]
	}
	hostname, _ := os.Hostname()

	w := &Writer{
		priority: priority,
		tag:      tag,
		hostname: hostname,
		network:  network,
		raddr:    raddr,
	}

	w.mu.Lock()
	defer w.mu.Unlock()

	err := w.connect()
	if err != nil {
		return nil, err
	}
	return w, err
}

// connect makes a connection to the syslog server.
// It must be called with w.mu held.
func (w *Writer) connect() (err error) {
	if w.conn != nil {
		// ignore err from close, it makes sense to continue anyway
		w.conn.close()
		w.conn = nil
	}

	if w.network == "" {
		w.conn, err = unixSyslog()
		if w.hostname == "" {
			w.hostname = "localhost"
		}
	} else {
		var c net.Conn
		c, err = net.Dial(w.network, w.raddr)
		if err == nil {
			w.conn = &netConn{conn: c}
			if w.hostname == "" {
				w.hostname = c.LocalAddr().String()
			}
		}
	}
	return
}

// Write sends a log message to the syslog daemon.
func (w *Writer) Write(b []byte) (int, error) {
	return w.writeAndRetry(w.priority, string(b))
}

// Close closes a connection to the syslog daemon.
func (w *Writer) Close() error {
	w.mu.Lock()
	defer w.mu.Unlock()

	if w.conn != nil {
		err := w.conn.close()
		w.conn = nil
		return err
	}
	return nil
}

// Emerg logs a message with severity LOG_EMERG, ignoring the severity
// passed to New.
func (w *Writer) Emerg(m string) error {
	_, err := w.writeAndRetry(LOG_EMERG, m)
	return err
}

// Alert logs a message with severity LOG_ALERT, ignoring the severity
// passed to New.
func (w *Writer) Alert(m string) error {
	_, err := w.writeAndRetry(LOG_ALERT, m)
	return err
}

// Crit logs a message with severity LOG_CRIT, ignoring the severity
// passed to New.
func (w *Writer) Crit(m string) error {
	_, err := w.writeAndRetry(LOG_CRIT, m)
	return err
}

// Err logs a message with severity LOG_ERR, ignoring the severity
// passed to New.
func (w *Writer) Err(m string) error {
	_, err := w.writeAndRetry(LOG_ERR, m)
	return err
}

// Warning logs a message with severity LOG_WARNING, ignoring the
// severity passed to New.
func (w *Writer) Warning(m string) error {
	_, err := w.writeAndRetry(LOG_WARNING, m)
	return err
}

// Notice logs a message with severity LOG_NOTICE, ignoring the
// severity passed to New.
func (w *Writer) Notice(m string) error {
	_, err := w.writeAndRetry(LOG_NOTICE, m)
	return err
}

// Info logs a message with severity LOG_INFO, ignoring the severity
// passed to New.
func (w *Writer) Info(m string) error {
	_, err := w.writeAndRetry(LOG_INFO, m)
	return err
}

// Debug logs a message with severity LOG_DEBUG, ignoring the severity
// passed to New.
func (w *Writer) Debug(m string) error {
	_, err := w.writeAndRetry(LOG_DEBUG, m)
	return err
}

func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
	pr := (w.priority & facilityMask) | (p & severityMask)

	w.mu.Lock()
	defer w.mu.Unlock()

	if w.conn != nil {
		if n, err := w.write(pr, s); err == nil {
			return n, err
		}
	}
	if err := w.connect(); err != nil {
		return 0, err
	}
	return w.write(pr, s)
}

// write generates and writes a syslog formatted string. The
// format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
func (w *Writer) write(p Priority, msg string) (int, error) {
	// ensure it ends in a \n
	nl := ""
	if !strings.HasSuffix(msg, "\n") {
		nl = "\n"
	}

	err := w.conn.writeString(p, w.hostname, w.tag, msg, nl)
	if err != nil {
		return 0, err
	}
	// Note: return the length of the input, not the number of
	// bytes printed by Fprintf, because this must behave like
	// an io.Writer.
	return len(msg), nil
}

func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error {
	if n.local {
		// Compared to the network form below, the changes are:
		//	1. Use time.Stamp instead of time.RFC3339.
		//	2. Drop the hostname field from the Fprintf.
		timestamp := time.Now().Format(time.Stamp)
		_, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s",
			p, timestamp,
			tag, os.Getpid(), msg, nl)
		return err
	}
	timestamp := time.Now().Format(time.RFC3339)
	_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s",
		p, timestamp, hostname,
		tag, os.Getpid(), msg, nl)
	return err
}

func (n *netConn) close() error {
	return n.conn.Close()
}

// NewLogger creates a log.Logger whose output is written to
// the system log service with the specified priority. The logFlag
// argument is the flag set passed through to log.New to create
// the Logger.
func NewLogger(p Priority, logFlag int) (*log.Logger, error) {
	s, err := New(p, "")
	if err != nil {
		return nil, err
	}
	return log.New(s, "", logFlag), nil
}
<commit_msg>log/syslog: expand docs a bit to reduce confusion<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !windows,!nacl,!plan9

package syslog

import (
	"errors"
	"fmt"
	"log"
	"net"
	"os"
	"strings"
	"sync"
	"time"
)

// The Priority is a combination of the syslog facility and
// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
// message from the FTP facility. The default severity is LOG_EMERG;
// the default facility is LOG_KERN.
type Priority int

const severityMask = 0x07
const facilityMask = 0xf8

const (
	// Severity.

	// From /usr/include/sys/syslog.h.
	// These are the same on Linux, BSD, and OS X.
	LOG_EMERG Priority = iota
	LOG_ALERT
	LOG_CRIT
	LOG_ERR
	LOG_WARNING
	LOG_NOTICE
	LOG_INFO
	LOG_DEBUG
)

const (
	// Facility.

	// From /usr/include/sys/syslog.h.
	// These are the same up to LOG_FTP on Linux, BSD, and OS X.
	LOG_KERN Priority = iota << 3
	LOG_USER
	LOG_MAIL
	LOG_DAEMON
	LOG_AUTH
	LOG_SYSLOG
	LOG_LPR
	LOG_NEWS
	LOG_UUCP
	LOG_CRON
	LOG_AUTHPRIV
	LOG_FTP
	_ // unused
	_ // unused
	_ // unused
	_ // unused
	LOG_LOCAL0
	LOG_LOCAL1
	LOG_LOCAL2
	LOG_LOCAL3
	LOG_LOCAL4
	LOG_LOCAL5
	LOG_LOCAL6
	LOG_LOCAL7
)

// A Writer is a connection to a syslog server.
type Writer struct {
	priority Priority
	tag      string
	hostname string
	network  string
	raddr    string

	mu   sync.Mutex // guards conn
	conn serverConn
}

// This interface and the separate syslog_unix.go file exist for
// Solaris support as implemented by gccgo. On Solaris you cannot
// simply open a TCP connection to the syslog daemon. The gccgo
// sources have a syslog_solaris.go file that implements unixSyslog to
// return a type that satisfies this interface and simply calls the C
// library syslog function.
type serverConn interface {
	writeString(p Priority, hostname, tag, s, nl string) error
	close() error
}

type netConn struct {
	local bool
	conn  net.Conn
}

// New establishes a new connection to the system log daemon. Each
// write to the returned writer sends a log message with the given
// priority (a combination of the syslog facility and severity) and
// prefix tag. If tag is empty, the os.Args[0] is used.
func New(priority Priority, tag string) (*Writer, error) {
	return Dial("", "", priority, tag)
}

// Dial establishes a connection to a log daemon by connecting to
// address raddr on the specified network. Each write to the returned
// writer sends a log message with the facility and severity
// (from priority) and tag. If tag is empty, the os.Args[0] is used.
// If network is empty, Dial will connect to the local syslog server.
// Otherwise, see the documentation for net.Dial for valid values
// of network and raddr.
func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
	if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
		return nil, errors.New("log/syslog: invalid priority")
	}

	if tag == "" {
		tag = os.Args[0]
	}
	hostname, _ := os.Hostname()

	w := &Writer{
		priority: priority,
		tag:      tag,
		hostname: hostname,
		network:  network,
		raddr:    raddr,
	}

	w.mu.Lock()
	defer w.mu.Unlock()

	err := w.connect()
	if err != nil {
		return nil, err
	}
	return w, err
}

// connect makes a connection to the syslog server.
// It must be called with w.mu held.
func (w *Writer) connect() (err error) {
	if w.conn != nil {
		// ignore err from close, it makes sense to continue anyway
		w.conn.close()
		w.conn = nil
	}

	if w.network == "" {
		w.conn, err = unixSyslog()
		if w.hostname == "" {
			w.hostname = "localhost"
		}
	} else {
		var c net.Conn
		c, err = net.Dial(w.network, w.raddr)
		if err == nil {
			w.conn = &netConn{conn: c}
			if w.hostname == "" {
				w.hostname = c.LocalAddr().String()
			}
		}
	}
	return
}

// Write sends a log message to the syslog daemon.
func (w *Writer) Write(b []byte) (int, error) {
	return w.writeAndRetry(w.priority, string(b))
}

// Close closes a connection to the syslog daemon.
func (w *Writer) Close() error {
	w.mu.Lock()
	defer w.mu.Unlock()

	if w.conn != nil {
		err := w.conn.close()
		w.conn = nil
		return err
	}
	return nil
}

// Emerg logs a message with severity LOG_EMERG, ignoring the severity
// passed to New.
func (w *Writer) Emerg(m string) error {
	_, err := w.writeAndRetry(LOG_EMERG, m)
	return err
}

// Alert logs a message with severity LOG_ALERT, ignoring the severity
// passed to New.
func (w *Writer) Alert(m string) error {
	_, err := w.writeAndRetry(LOG_ALERT, m)
	return err
}

// Crit logs a message with severity LOG_CRIT, ignoring the severity
// passed to New.
func (w *Writer) Crit(m string) error {
	_, err := w.writeAndRetry(LOG_CRIT, m)
	return err
}

// Err logs a message with severity LOG_ERR, ignoring the severity
// passed to New.
func (w *Writer) Err(m string) error {
	_, err := w.writeAndRetry(LOG_ERR, m)
	return err
}

// Warning logs a message with severity LOG_WARNING, ignoring the
// severity passed to New.
func (w *Writer) Warning(m string) error {
	_, err := w.writeAndRetry(LOG_WARNING, m)
	return err
}

// Notice logs a message with severity LOG_NOTICE, ignoring the
// severity passed to New.
func (w *Writer) Notice(m string) error {
	_, err := w.writeAndRetry(LOG_NOTICE, m)
	return err
}

// Info logs a message with severity LOG_INFO, ignoring the severity
// passed to New.
func (w *Writer) Info(m string) error {
	_, err := w.writeAndRetry(LOG_INFO, m)
	return err
}

// Debug logs a message with severity LOG_DEBUG, ignoring the severity
// passed to New.
func (w *Writer) Debug(m string) error {
	_, err := w.writeAndRetry(LOG_DEBUG, m)
	return err
}

func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
	pr := (w.priority & facilityMask) | (p & severityMask)

	w.mu.Lock()
	defer w.mu.Unlock()

	if w.conn != nil {
		if n, err := w.write(pr, s); err == nil {
			return n, err
		}
	}
	if err := w.connect(); err != nil {
		return 0, err
	}
	return w.write(pr, s)
}

// write generates and writes a syslog formatted string. The
// format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
func (w *Writer) write(p Priority, msg string) (int, error) {
	// ensure it ends in a \n
	nl := ""
	if !strings.HasSuffix(msg, "\n") {
		nl = "\n"
	}

	err := w.conn.writeString(p, w.hostname, w.tag, msg, nl)
	if err != nil {
		return 0, err
	}
	// Note: return the length of the input, not the number of
	// bytes printed by Fprintf, because this must behave like
	// an io.Writer.
	return len(msg), nil
}

func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error {
	if n.local {
		// Compared to the network form below, the changes are:
		//	1. Use time.Stamp instead of time.RFC3339.
		//	2. Drop the hostname field from the Fprintf.
		timestamp := time.Now().Format(time.Stamp)
		_, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s",
			p, timestamp,
			tag, os.Getpid(), msg, nl)
		return err
	}
	timestamp := time.Now().Format(time.RFC3339)
	_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s",
		p, timestamp, hostname,
		tag, os.Getpid(), msg, nl)
	return err
}

func (n *netConn) close() error {
	return n.conn.Close()
}

// NewLogger creates a log.Logger whose output is written to the
// system log service with the specified priority, a combination of
// the syslog facility and severity. The logFlag argument is the flag
// set passed through to log.New to create the Logger.
func NewLogger(p Priority, logFlag int) (*log.Logger, error) {
	s, err := New(p, "")
	if err != nil {
		return nil, err
	}
	return log.New(s, "", logFlag), nil
}
<|endoftext|>


<commit_before>//
// Face-meltingly fast, thread-safe, marshalable, unionable, probability- and optimal-size-calculating Bloom filter in go
//
// https://github.com/steakknife/bloomfilter
//
// Copyright © 2014, 2015 Barry Allard
//
// MIT license
//
package bloomfilter

import (
	"bytes"
	"encoding/binary"
)

// conforms to encoding.BinaryUnmarshaler

func (f *Filter) UnmarshalBinary(data []byte) (err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	var k uint32

	buf := bytes.NewBuffer(data)
	if err = binary.Read(buf, binary.LittleEndian, &k); err != nil {
		return err
	}

	if err = binary.Read(buf, binary.LittleEndian, &(f.n)); err != nil {
		return err
	}

	if err = binary.Read(buf, binary.LittleEndian, &(f.m)); err != nil {
		return err
	}

	debug("read bf k=%d n=%d m=%d\n", k, f.n, f.m)

	f.keys = make([]uint64, k)
	if err = binary.Read(buf, binary.LittleEndian, f.keys); err != nil {
		return err
	}

	f.bits = make([]uint64, f.n)
	if err = binary.Read(buf, binary.LittleEndian, f.bits); err != nil {
		return err
	}

	return nil
}
<commit_msg>fix UnmarshalBinary<commit_after>//
// Face-meltingly fast, thread-safe, marshalable, unionable, probability- and optimal-size-calculating Bloom filter in go
//
// https://github.com/steakknife/bloomfilter
//
// Copyright © 2014, 2015 Barry Allard
//
// MIT license
//
package bloomfilter

import (
	"bytes"
	"encoding/binary"
)

// conforms to encoding.BinaryUnmarshaler

func (f *Filter) UnmarshalBinary(data []byte) (err error) {
	f.lock.Lock()
	defer f.lock.Unlock()

	var k uint64

	buf := bytes.NewBuffer(data)
	if err = binary.Read(buf, binary.LittleEndian, &k); err != nil {
		return err
	}

	if err = binary.Read(buf, binary.LittleEndian, &(f.n)); err != nil {
		return err
	}

	if err = binary.Read(buf, binary.LittleEndian, &(f.m)); err != nil {
		return err
	}

	debug("read bf k=%d n=%d m=%d\n", k, f.n, f.m)

	f.keys = make([]uint64, k)
	if err = binary.Read(buf, binary.LittleEndian, f.keys); err != nil {
		return err
	}

	f.bits = make([]uint64, f.n)
	if err = binary.Read(buf, binary.LittleEndian, f.bits); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package imageserver

import (
	"bytes"
	"image"
	"image/color"
	"image/png"
	"math/rand"
	"testing"
)

func TestImage(t *testing.T) {
	i1 := CreateBaseImage(500, 400)
	buffer1 := new(bytes.Buffer)
	png.Encode(buffer1, i1)
	im1 := &Image{
		Type: "png",
		Data: buffer1.Bytes(),
	}
	data, err := im1.Marshal()
	if err != nil {
		t.Fatal(err)
	}

	im2 := new(Image)
	err = im2.Unmarshal(data)
	if err != nil {
		t.Fatal(err)
	}
	if im2.Type != im1.Type {
		t.Fatalf("Types not equals: %s %s", im1.Type, im2.Type)
	}
	if len(im2.Data) != len(im1.Data) {
		t.Fatalf("Data not the same size %d %d", len(im1.Data), len(im2.Data))
	}
	for i, b := range im2.Data {
		if b != im1.Data[i] {
			t.Fatalf("Data not equals at index %d: %d %d", i, im1.Data[i], b)
		}
	}
	buffer2 := bytes.NewBuffer(im2.Data)
	i2, err := png.Decode(buffer2)
	if err != nil {
		t.Fatal(err)
	}
	if !i2.Bounds().Eq(i1.Bounds()) {
		t.Fatal("Image bounds not equals %s %s", i1.Bounds(), i2.Bounds())
	}
	for y, height := 0, i2.Bounds().Dy(); y < height; y++ {
		for x, width := 0, i2.Bounds().Dx(); x < width; x++ {
			c1 := i1.At(x, y)
			r1, g1, b1, a1 := c1.RGBA()
			c2 := i2.At(x, y)
			r2, g2, b2, a2 := i2.At(x, y).RGBA()
			if r1 != r2 || g1 != g2 || b1 != b2 || a1 != a2 {
				t.Fatalf("Colors not equals at %d,%d: %s %s", x, y, c1, c2)
			}
		}
	}
}

func CreateBaseImage(width, height int) *image.NRGBA {
	i := image.NewNRGBA(image.Rect(0, 0, width, height))
	for y, height := 0, i.Bounds().Dy(); y < height; y++ {
		for x, width := 0, i.Bounds().Dx(); x < width; x++ {
			i.Set(x, y, randColor())
		}
	}
	return i
}

func randComponent() uint8 {
	return uint8(rand.Int31n(256))
}

func randColor() color.RGBA {
	return color.RGBA{
		R: randComponent(),
		G: randComponent(),
		B: randComponent(),
		A: randComponent(),
	}
}
<commit_msg>Move rename test function<commit_after>package imageserver

import (
	"bytes"
	"image"
	"image/color"
	"image/png"
	"math/rand"
	"testing"
)

func TestImage(t *testing.T) {
	i1 := CreateBaseImage(500, 400)
	buffer1 := new(bytes.Buffer)
	png.Encode(buffer1, i1)
	im1 := &Image{
		Type: "png",
		Data: buffer1.Bytes(),
	}
	data, err := im1.Marshal()
	if err != nil {
		t.Fatal(err)
	}

	im2 := new(Image)
	err = im2.Unmarshal(data)
	if err != nil {
		t.Fatal(err)
	}
	if im2.Type != im1.Type {
		t.Fatalf("Types not equals: %s %s", im1.Type, im2.Type)
	}
	if len(im2.Data) != len(im1.Data) {
		t.Fatalf("Data not the same size %d %d", len(im1.Data), len(im2.Data))
	}
	for i, b := range im2.Data {
		if b != im1.Data[i] {
			t.Fatalf("Data not equals at index %d: %d %d", i, im1.Data[i], b)
		}
	}
	buffer2 := bytes.NewBuffer(im2.Data)
	i2, err := png.Decode(buffer2)
	if err != nil {
		t.Fatal(err)
	}
	if !i2.Bounds().Eq(i1.Bounds()) {
		t.Fatal("Image bounds not equals %s %s", i1.Bounds(), i2.Bounds())
	}
	for y, height := 0, i2.Bounds().Dy(); y < height; y++ {
		for x, width := 0, i2.Bounds().Dx(); x < width; x++ {
			c1 := i1.At(x, y)
			r1, g1, b1, a1 := c1.RGBA()
			c2 := i2.At(x, y)
			r2, g2, b2, a2 := i2.At(x, y).RGBA()
			if r1 != r2 || g1 != g2 || b1 != b2 || a1 != a2 {
				t.Fatalf("Colors not equals at %d,%d: %s %s", x, y, c1, c2)
			}
		}
	}
}

func CreateBaseImage(width, height int) *image.NRGBA {
	i := image.NewNRGBA(image.Rect(0, 0, width, height))
	for y, height := 0, i.Bounds().Dy(); y < height; y++ {
		for x, width := 0, i.Bounds().Dx(); x < width; x++ {
			i.Set(x, y, randColor())
		}
	}
	return i
}

func randColor() color.RGBA {
	return color.RGBA{
		R: randColorComponent(),
		G: randColorComponent(),
		B: randColorComponent(),
		A: randColorComponent(),
	}
}

func randColorComponent() uint8 {
	return uint8(rand.Int31n(256))
}
<|endoftext|>


<commit_before>package gummyimage

import (
	"bytes"
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"io/ioutil"
	"math"
	"math/rand"
	"os"
	"strconv"
	"time"

	"code.google.com/p/freetype-go/freetype"
	"code.google.com/p/freetype-go/freetype/truetype"
)

type Gummy struct {
	Img   *image.RGBA
	Color *color.Color
	Font  *truetype.Font
}

// Color in HEX format: FAFAFA
// If hexColor = "" then random color
func NewDefaultGummy(w, h int, hexColor string) (*Gummy, error) {
	var bgColor color.Color
	if hexColor == "" {
		bgColor = randColor(255)

	} else {
		cr, _ := strconv.ParseUint(string(hexColor[:2]), 16, 64)
		cg, _ := strconv.ParseUint(string(hexColor[2:4]), 16, 64)
		cb, _ := strconv.ParseUint(string(hexColor[4:]), 16, 64)
		bgColor = color.RGBA{R: uint8(cr), G: uint8(cg), B: uint8(cb), A: 255}
	}

	return NewGummy(0, 0, w, h, bgColor)
}

func NewGummy(x, y, w, h int, gummyColor color.Color) (*Gummy, error) {

	img, err := createImg(x, y, w, h, gummyColor)

	if err != nil {
		return nil, err
	}

	return &Gummy{
		Img:   img,
		Color: &gummyColor,
		Font:  nil,
	}, nil
}

/*
Gets the image in the specified format (JPEG, GIF or PNG) in the specified writer
*/
func (g *Gummy) get(format string, r io.Writer) error {

	switch format {
	case "jpeg", "JPEG":
		jpeg.Encode(r, g.Img, nil)
	case "png", "PNG":
		png.Encode(r, g.Img)
	case "gif", "GIF":
		gif.Encode(r, g.Img, nil)
	default:
		return errors.New("Wrong format")
	}

	return nil
}

func (g *Gummy) SaveJpeg(path string) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	return g.get("jpeg", file)
}

func (g *Gummy) SaveGif(path string) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	return g.get("gif", file)
}

func (g *Gummy) SavePng(path string) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	return g.get("png", file)
}

func (g *Gummy) GetJpeg() ([]byte, error) {
	b := new(bytes.Buffer)
	err := g.get("jpeg", b)
	return b.Bytes(), err
}

func (g *Gummy) GetGif() ([]byte, error) {
	b := new(bytes.Buffer)
	err := g.get("gif", b)
	return b.Bytes(), err
}

func (g *Gummy) GetPng() ([]byte, error) {
	b := new(bytes.Buffer)
	err := g.get("png", b)
	return b.Bytes(), err
}

// Color in HEX format: FAFAFA
func (g *Gummy) DrawText(text, textColor string, fontSize, xPosition, yPosition int) error {

	fc := freetype.NewContext()
	fc.SetDst(g.Img)
	fc.SetFont(g.Font)
	fc.SetClip(g.Img.Bounds())

	// Color parsing
	cr, _ := strconv.ParseUint(string(textColor[:2]), 16, 64)
	cg, _ := strconv.ParseUint(string(textColor[2:4]), 16, 64)
	cb, _ := strconv.ParseUint(string(textColor[4:]), 16, 64)
	c := image.NewUniform(color.RGBA{R: uint8(cr), G: uint8(cg), B: uint8(cb), A: 255})

	fc.SetSrc(c)
	fc.SetFontSize(float64(fontSize))

	_, err := fc.DrawString(text, freetype.Pt(xPosition, yPosition))

	return err
}

// Color in HEX format: FAFAFA
// If "" the color of the text is black or white depending on the brightness of the bg
func (g *Gummy) DrawTextSize(textColor string) error {

	// Get black or white depending on the background
	if textColor == "" {
		c := (*g.Color).(color.RGBA)
		if blackWithBackground(float64(c.R), float64(c.G), float64(c.B)) {
			textColor = "000000"
		} else {
			textColor = "FFFFFF"
		}
	}

	text := fmt.Sprintf("%dx%d", g.Img.Rect.Max.X, g.Img.Rect.Max.Y)

	// I can't get the text final size so more or less center the text with this
	// manual awful stuff :/
	size := g.Img.Rect.Max.Y

	if g.Img.Rect.Max.X < g.Img.Rect.Max.Y {
		size = g.Img.Rect.Max.X
	}

	textSize := (size - (size / 10 * 2))
	fontSize := textSize / len(text) * 2

	x := g.Img.Rect.Max.X/2 - textSize/2 - fontSize/8
	y := g.Img.Rect.Max.Y/2 + textSize/10 + fontSize/16

	return g.DrawText(
		text,
		textColor,
		fontSize,
		x,
		y,
	)
}

func LoadFont(path string) (*truetype.Font, error) {
	bs, err := ioutil.ReadFile(path)

	// quick debug
	if err != nil {
		fmt.Println(err)
	}
	f, err := truetype.Parse(bs)
	return f, err
}

func (g *Gummy) SetFont(path string) error {
	f, err := LoadFont(path)
	g.Font = f
	return err
}

func createImg(x, y, w, h int, gummyColor color.Color) (*image.RGBA, error) {
	img := image.NewRGBA(image.Rect(x, y, w, h))

	// Colorize!
	for y := img.Rect.Min.Y; y < img.Rect.Max.Y; y++ {
		for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
			img.Set(x, y, gummyColor)
		}
	}

	return img, nil

}

func randColor(alpha int) color.Color {

	random := func(min, max int) int {
		rand.Seed(time.Now().UnixNano())
		return rand.Intn(max-min) + min
	}

	r := uint8(random(0, 255))
	g := uint8(random(0, 255))
	b := uint8(random(0, 255))

	return color.RGBA{r, g, b, uint8(alpha)}

}

func inverseColor(r, g, b int) (rr, rg, rb int) {
	rr = 255 - r
	rg = 255 - g
	rb = 255 - b

	return
}

// Returns false if white text with that background
// Rrturns true if black text with that background
// Calculates based on the brightness
// Source: http://stackoverflow.com/a/2241471
func blackWithBackground(r, g, b float64) bool {

	perceivedBrightness := func(r, g, b float64) int {
		return int(math.Sqrt(r*r*0.241 + g*g*0.691 + b*b*0.068))
	}

	return perceivedBrightness(r, g, b) > 130
}
<commit_msg>Minor changes<commit_after>package gummyimage

import (
	"bytes"
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"io/ioutil"
	"math"
	"math/rand"
	"os"
	"strconv"
	"time"

	"code.google.com/p/freetype-go/freetype"
	"code.google.com/p/freetype-go/freetype/truetype"
)

type Gummy struct {
	Img   *image.RGBA
	Color *color.Color
	Font  *truetype.Font
}

// Color in HEX format: FAFAFA
// If hexColor = "" then random color
func NewDefaultGummy(w, h int, hexColor string) (*Gummy, error) {
	var bgColor color.Color
	if hexColor == "" {
		bgColor = randColor(255)

	} else {
		cr, _ := strconv.ParseUint(string(hexColor[:2]), 16, 64)
		cg, _ := strconv.ParseUint(string(hexColor[2:4]), 16, 64)
		cb, _ := strconv.ParseUint(string(hexColor[4:]), 16, 64)
		bgColor = color.RGBA{R: uint8(cr), G: uint8(cg), B: uint8(cb), A: 255}
	}

	return NewGummy(0, 0, w, h, bgColor)
}

func NewGummy(x, y, w, h int, gummyColor color.Color) (*Gummy, error) {

	img, err := createImg(x, y, w, h, gummyColor)

	if err != nil {
		return nil, err
	}

	return &Gummy{
		Img:   img,
		Color: &gummyColor,
		Font:  nil,
	}, nil
}

/*
Gets the image in the specified format (JPEG, GIF or PNG) in the specified writer
*/
func (g *Gummy) Get(format string, r io.Writer) error {

	switch format {
	case "jpeg", "JPEG", "jpg", "JPG":
		jpeg.Encode(r, g.Img, nil)
	case "png", "PNG":
		png.Encode(r, g.Img)
	case "gif", "GIF":
		gif.Encode(r, g.Img, nil)
	default:
		return errors.New("Wrong format")
	}

	return nil
}

func (g *Gummy) SaveJpeg(path string) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	return g.Get("jpeg", file)
}

func (g *Gummy) SaveGif(path string) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	return g.Get("gif", file)
}

func (g *Gummy) SavePng(path string) error {
	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	return g.Get("png", file)
}

func (g *Gummy) GetJpeg() ([]byte, error) {
	b := new(bytes.Buffer)
	err := g.Get("jpeg", b)
	return b.Bytes(), err
}

func (g *Gummy) GetGif() ([]byte, error) {
	b := new(bytes.Buffer)
	err := g.Get("gif", b)
	return b.Bytes(), err
}

func (g *Gummy) GetPng() ([]byte, error) {
	b := new(bytes.Buffer)
	err := g.Get("png", b)
	return b.Bytes(), err
}

// Color in HEX format: FAFAFA
func (g *Gummy) DrawText(text, textColor string, fontSize, xPosition, yPosition int) error {

	fc := freetype.NewContext()
	fc.SetDst(g.Img)
	fc.SetFont(g.Font)
	fc.SetClip(g.Img.Bounds())

	// Color parsing
	cr, _ := strconv.ParseUint(string(textColor[:2]), 16, 64)
	cg, _ := strconv.ParseUint(string(textColor[2:4]), 16, 64)
	cb, _ := strconv.ParseUint(string(textColor[4:]), 16, 64)
	c := image.NewUniform(color.RGBA{R: uint8(cr), G: uint8(cg), B: uint8(cb), A: 255})

	fc.SetSrc(c)
	fc.SetFontSize(float64(fontSize))

	_, err := fc.DrawString(text, freetype.Pt(xPosition, yPosition))

	return err
}

// Color in HEX format: FAFAFA
// If "" the color of the text is black or white depending on the brightness of the bg
func (g *Gummy) DrawTextSize(textColor string) error {

	// Get black or white depending on the background
	if textColor == "" {
		c := (*g.Color).(color.RGBA)
		if blackWithBackground(float64(c.R), float64(c.G), float64(c.B)) {
			textColor = "000000"
		} else {
			textColor = "FFFFFF"
		}
	}

	text := fmt.Sprintf("%dx%d", g.Img.Rect.Max.X, g.Img.Rect.Max.Y)

	// I can't get the text final size so more or less center the text with this
	// manual awful stuff :/
	size := g.Img.Rect.Max.Y

	if g.Img.Rect.Max.X < g.Img.Rect.Max.Y {
		size = g.Img.Rect.Max.X
	}

	textSize := (size - (size / 10 * 2))
	fontSize := textSize / len(text) * 2

	x := g.Img.Rect.Max.X/2 - textSize/2 - fontSize/8
	y := g.Img.Rect.Max.Y/2 + textSize/10 + fontSize/16

	return g.DrawText(
		text,
		textColor,
		fontSize,
		x,
		y,
	)
}

func LoadFont(path string) (*truetype.Font, error) {
	bs, err := ioutil.ReadFile(path)

	// quick debug
	if err != nil {
		fmt.Println(err)
	}
	f, err := truetype.Parse(bs)
	return f, err
}

func (g *Gummy) SetFont(path string) error {
	f, err := LoadFont(path)
	g.Font = f
	return err
}

func createImg(x, y, w, h int, gummyColor color.Color) (*image.RGBA, error) {
	img := image.NewRGBA(image.Rect(x, y, w, h))

	// Colorize!
	for y := img.Rect.Min.Y; y < img.Rect.Max.Y; y++ {
		for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
			img.Set(x, y, gummyColor)
		}
	}

	return img, nil

}

func randColor(alpha int) color.Color {

	random := func(min, max int) int {
		rand.Seed(time.Now().UnixNano())
		return rand.Intn(max-min) + min
	}

	r := uint8(random(0, 255))
	g := uint8(random(0, 255))
	b := uint8(random(0, 255))

	return color.RGBA{r, g, b, uint8(alpha)}

}

func inverseColor(r, g, b int) (rr, rg, rb int) {
	rr = 255 - r
	rg = 255 - g
	rb = 255 - b

	return
}

// Returns false if white text with that background
// Rrturns true if black text with that background
// Calculates based on the brightness
// Source: http://stackoverflow.com/a/2241471
func blackWithBackground(r, g, b float64) bool {

	perceivedBrightness := func(r, g, b float64) int {
		return int(math.Sqrt(r*r*0.241 + g*g*0.691 + b*b*0.068))
	}

	return perceivedBrightness(r, g, b) > 130
}
<|endoftext|>