<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"sync"
	"syscall"
	"time"
)

type PackageInfo struct {
	packageName    string
	packageVersion string
	repositoryUrl  string
	repositoryType string
}

// Flags
var (
	satisPath  string
	configPath string
	repoPath   string

	listen string
)

// Operation vars
var (
	shouldGenerateConfig bool
	shouldGenerateRepo   bool
	runningGoroutines    = 0

	pendingUpdates map[string]*PackageInfo = make(map[string]*PackageInfo)

	updateMutex sync.Mutex
	configMutex sync.RWMutex
)

func init() {
	flag.StringVar(&satisPath, "satis", "", "The path to the satis binary (required)")
	flag.StringVar(&configPath, "config", "", "The path to the satis repo configuration file (required)")
	flag.StringVar(&repoPath, "repo", "", "The path to the satis repository (required)")

	flag.StringVar(&listen, "listen", ":8080", "The address to listen on")
}

func printHelp() {
	flag.PrintDefaults()
}

// Writes the satis configuration file upon receiving a signal
func configGenerator(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	var config map[string]interface{}
	for {
		// check if we're shutting down
		select {
		case <-abortChan:
			return
		default:
			break
		}

		// Have we been flagged for a config rebuild?
		if !shouldGenerateConfig {
			time.Sleep(time.Second * 1)
			continue
		}

		// Read the existing config
		configMutex.RLock()
		data, err := ioutil.ReadFile(configPath)
		if err != nil {
			configMutex.RUnlock()
			fmt.Fprintf(os.Stderr, "Failed to load satis config file: %s", err)
			time.Sleep(time.Second * 5)
			continue
		}
		configMutex.RUnlock()
		// Decode the config
		err = json.Unmarshal(data, &config)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to decode satis config file: %s", err)
			time.Sleep(time.Second * 5)
			continue
		}

		var repositories []map[string]interface{}
		var packages map[string]interface{}

		// Create the keys if they don't exist,
		// but assume they're the correct type if they do exist
		if tmp, ok := config["repositories"]; !ok {
			repositories = make([]map[string]interface{}, 0, 1)
		} else {
			tmp2 := tmp.([]interface{})
			repositories = make([]map[string]interface{}, len(tmp2))
			for k, repo := range tmp2 {
				repositories[k] = repo.(map[string]interface{})
			}
		}
		if tmp, ok := config["require"]; !ok {
			packages = make(map[string]interface{})
		} else {
			packages = tmp.(map[string]interface{})
		}

		// Update the config
		updateMutex.Lock()
		for _, packageInfo := range pendingUpdates {
			var repoExists = false

			// Update the repo if it already exists
			for _, repo := range repositories {
				if repo["url"] == packageInfo.repositoryUrl {
					repo["type"] = packageInfo.repositoryType
					repoExists = true
					break
				}
			}

			// Create the repo if it doesn't exist
			if !repoExists {
				repositories = append(repositories, map[string]interface{}{
					"url":  packageInfo.repositoryUrl,
					"type": packageInfo.repositoryType,
				})
			}

			// Update the package version
			packages[packageInfo.packageName] = packageInfo.packageVersion
		}
		updateMutex.Unlock()

		// Write the config changes
		config["repositories"] = repositories
		config["require"] = packages

		data, err = json.MarshalIndent(config, "", "    ")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to encode satis config file: %s", err)
			os.Exit(1)
		}

		configMutex.Lock()
		err = ioutil.WriteFile(configPath, data, 0644)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to write satis config file: %s", err)
			continue
		}
		configMutex.Unlock()

		log.Println("Generated config file for", len(pendingUpdates), "updates")
		pendingUpdates = make(map[string]*PackageInfo)

		// Update the worker flags to trigger a build
		shouldGenerateConfig = false
		shouldGenerateRepo = true
	}
}

// Generates the satis repositroy upon receiving a signal
func repoGenerator(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	for {
		// check if we're shutting down
		select {
		case <-abortChan:
			return
		default:
			break
		}

		// Check if we should generate the repo
		if !shouldGenerateRepo {
			time.Sleep(time.Second * 1)
			continue
		}

		// Lock the config writer for an arbitrary amount of time on the off chance we want to write
		// to just as we launch satis
		configMutex.RLock()
		go func() {
			time.Sleep(time.Second * 5)
			configMutex.RUnlock()
		}()

		command := exec.Command(satisPath, "build", configPath, repoPath)
		command.Stdout = os.Stdout
		command.Stderr = os.Stderr
		command.Stdin = os.Stdin
		err := command.Run()

		if err != nil {
			log.Fatalln("failed to execute satis: %s", err)
		}

		shouldGenerateRepo = false
	}
}

func serveHttp(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	// Serve the repo config
	http.HandleFunc("/config.json", func(w http.ResponseWriter, r *http.Request) {
		configMutex.RLock()
		defer configMutex.RUnlock()
		http.ServeFile(w, r, configPath)
	})

	// Endpoint to force regeneration
	http.HandleFunc("/generate", func(w http.ResponseWriter, r *http.Request) {
		log.Println("HTTP triggered repo generation")

		shouldGenerateRepo = true

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(200)
		w.Write([]byte("{\"success\": true}"))
	})

	// Endpoint to register a repo update
	http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Content-Type", "application/json")

		params := r.URL.Query()
		var update = &PackageInfo{
			repositoryUrl:  params.Get("repo"),
			repositoryType: params.Get("repoType"),
			packageName:    params.Get("package"),
			packageVersion: params.Get("version"),
		}

		// Basic sanity checking
		if update.packageName == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing package\"}"))
			return
		}

		if update.repositoryUrl == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing repo\"}"))
			return
		}

		if update.repositoryType == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing repoType\"}"))
			return
		}

		if update.packageVersion == "" {
			update.packageVersion = "*"
		}

		updateMutex.Lock()
		pendingUpdates[update.packageName] = update
		updateMutex.Unlock()

		shouldGenerateRepo = true
		w.WriteHeader(200)
		w.Write([]byte("{\"success\": true}"))
	})

	// Serve the repo
	fs := http.FileServer(http.Dir(repoPath))
	http.Handle("/", fs)

	var server = http.Server{Addr: listen}
	var errorChan = make(chan error)

	go func() {
		fmt.Println("listening on", listen)
		errorChan <- server.ListenAndServe()
	}()

	select {
	case err := <-errorChan:
		log.Fatalln("HTTP listener error:", err)
		break
	case <-abortChan:
		server.Close()
		break
	}

}

func main() {
	fmt.Println("satisd - dynamic satis repository generator daemon")
	flag.Parse()

	if satisPath == "" || configPath == "" || repoPath == "" {
		printHelp()
		return
	}

	// Check that the files exist
	if _, err := os.Stat(satisPath); os.IsNotExist(err) {
		log.Fatalln("satis binary not found at", satisPath)
	}
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		log.Fatalln("satis configuration not found at", configPath)
	}

	// Perform a basic sanity check on the config
	var config map[string]interface{}
	data, err := ioutil.ReadFile(configPath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load satis config file: %s", err)
		os.Exit(1)
	}
	err = json.Unmarshal(data, &config)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to decode satis config file: %s", err)
		os.Exit(1)
	}

	// Spin up the worker goroutines
	shutdownChannel := make(chan bool)
	go configGenerator(shutdownChannel)
	go repoGenerator(shutdownChannel)
	go serveHttp(shutdownChannel)

	// Catch signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// We've got a kill signal? - do a clean shutdown
	sig := <-sigs
	fmt.Println("Received signal", sig)
	close(shutdownChannel)

	// Wait for the goroutines to exit
	for {
		if runningGoroutines == 0 {
			return
		}
		runtime.Gosched()
	}
}
<commit_msg>Fixed a bug where we were triggering the wrong update thread<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"sync"
	"syscall"
	"time"
)

type PackageInfo struct {
	packageName    string
	packageVersion string
	repositoryUrl  string
	repositoryType string
}

// Flags
var (
	satisPath  string
	configPath string
	repoPath   string

	listen string
)

// Operation vars
var (
	shouldGenerateConfig bool
	shouldGenerateRepo   bool
	runningGoroutines    = 0

	pendingUpdates map[string]*PackageInfo = make(map[string]*PackageInfo)

	updateMutex sync.Mutex
	configMutex sync.RWMutex
)

func init() {
	flag.StringVar(&satisPath, "satis", "", "The path to the satis binary (required)")
	flag.StringVar(&configPath, "config", "", "The path to the satis repo configuration file (required)")
	flag.StringVar(&repoPath, "repo", "", "The path to the satis repository (required)")

	flag.StringVar(&listen, "listen", ":8080", "The address to listen on")
}

func printHelp() {
	flag.PrintDefaults()
}

// Writes the satis configuration file upon receiving a signal
func configGenerator(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	var config map[string]interface{}
	for {
		// check if we're shutting down
		select {
		case <-abortChan:
			return
		default:
			break
		}

		// Have we been flagged for a config rebuild?
		if !shouldGenerateConfig {
			time.Sleep(time.Second * 1)
			continue
		}

		// Read the existing config
		configMutex.RLock()
		data, err := ioutil.ReadFile(configPath)
		if err != nil {
			configMutex.RUnlock()
			fmt.Fprintf(os.Stderr, "Failed to load satis config file: %s", err)
			time.Sleep(time.Second * 5)
			continue
		}
		configMutex.RUnlock()
		// Decode the config
		err = json.Unmarshal(data, &config)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to decode satis config file: %s", err)
			time.Sleep(time.Second * 5)
			continue
		}

		var repositories []map[string]interface{}
		var packages map[string]interface{}

		// Create the keys if they don't exist,
		// but assume they're the correct type if they do exist
		if tmp, ok := config["repositories"]; !ok {
			repositories = make([]map[string]interface{}, 0, 1)
		} else {
			tmp2 := tmp.([]interface{})
			repositories = make([]map[string]interface{}, len(tmp2))
			for k, repo := range tmp2 {
				repositories[k] = repo.(map[string]interface{})
			}
		}
		if tmp, ok := config["require"]; !ok {
			packages = make(map[string]interface{})
		} else {
			packages = tmp.(map[string]interface{})
		}

		// Update the config
		updateMutex.Lock()
		for _, packageInfo := range pendingUpdates {
			var repoExists = false

			// Update the repo if it already exists
			for _, repo := range repositories {
				if repo["url"] == packageInfo.repositoryUrl {
					repo["type"] = packageInfo.repositoryType
					repoExists = true
					break
				}
			}

			// Create the repo if it doesn't exist
			if !repoExists {
				repositories = append(repositories, map[string]interface{}{
					"url":  packageInfo.repositoryUrl,
					"type": packageInfo.repositoryType,
				})
			}

			// Update the package version
			packages[packageInfo.packageName] = packageInfo.packageVersion
		}
		updateMutex.Unlock()

		// Write the config changes
		config["repositories"] = repositories
		config["require"] = packages

		data, err = json.MarshalIndent(config, "", "    ")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to encode satis config file: %s", err)
			os.Exit(1)
		}

		configMutex.Lock()
		err = ioutil.WriteFile(configPath, data, 0644)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to write satis config file: %s", err)
			continue
		}
		configMutex.Unlock()

		log.Println("Generated config file for", len(pendingUpdates), "updates")
		pendingUpdates = make(map[string]*PackageInfo)

		// Update the worker flags to trigger a build
		shouldGenerateConfig = false
		shouldGenerateRepo = true
	}
}

// Generates the satis repositroy upon receiving a signal
func repoGenerator(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	for {
		// check if we're shutting down
		select {
		case <-abortChan:
			return
		default:
			break
		}

		// Check if we should generate the repo
		if !shouldGenerateRepo {
			time.Sleep(time.Second * 1)
			continue
		}

		// Lock the config writer for an arbitrary amount of time on the off chance we want to write
		// to just as we launch satis
		configMutex.RLock()
		go func() {
			time.Sleep(time.Second * 5)
			configMutex.RUnlock()
		}()

		command := exec.Command(satisPath, "build", configPath, repoPath)
		command.Stdout = os.Stdout
		command.Stderr = os.Stderr
		command.Stdin = os.Stdin
		err := command.Run()

		if err != nil {
			log.Fatalln("failed to execute satis: %s", err)
		}

		shouldGenerateRepo = false
	}
}

func serveHttp(abortChan chan bool) {
	runningGoroutines += 1
	defer func() {
		runningGoroutines -= 1
	}()

	// Serve the repo config
	http.HandleFunc("/config.json", func(w http.ResponseWriter, r *http.Request) {
		configMutex.RLock()
		defer configMutex.RUnlock()
		http.ServeFile(w, r, configPath)
	})

	// Endpoint to force regeneration
	http.HandleFunc("/generate", func(w http.ResponseWriter, r *http.Request) {
		log.Println("HTTP triggered repo generation")

		shouldGenerateRepo = true

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(200)
		w.Write([]byte("{\"success\": true}"))
	})

	// Endpoint to register a repo update
	http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Content-Type", "application/json")

		params := r.URL.Query()
		var update = &PackageInfo{
			repositoryUrl:  params.Get("repo"),
			repositoryType: params.Get("repoType"),
			packageName:    params.Get("package"),
			packageVersion: params.Get("version"),
		}

		// Basic sanity checking
		if update.packageName == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing package\"}"))
			return
		}

		if update.repositoryUrl == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing repo\"}"))
			return
		}

		if update.repositoryType == "" {
			w.WriteHeader(400)
			w.Write([]byte("{\"error\": \"missing repoType\"}"))
			return
		}

		if update.packageVersion == "" {
			update.packageVersion = "*"
		}

		updateMutex.Lock()
		pendingUpdates[update.packageName] = update
		updateMutex.Unlock()

		shouldGenerateConfig = true
		w.WriteHeader(200)
		w.Write([]byte("{\"success\": true}"))
	})

	// Serve the repo
	fs := http.FileServer(http.Dir(repoPath))
	http.Handle("/", fs)

	var server = http.Server{Addr: listen}
	var errorChan = make(chan error)

	go func() {
		fmt.Println("listening on", listen)
		errorChan <- server.ListenAndServe()
	}()

	select {
	case err := <-errorChan:
		log.Fatalln("HTTP listener error:", err)
		break
	case <-abortChan:
		server.Close()
		break
	}

}

func main() {
	fmt.Println("satisd - dynamic satis repository generator daemon")
	flag.Parse()

	if satisPath == "" || configPath == "" || repoPath == "" {
		printHelp()
		return
	}

	// Check that the files exist
	if _, err := os.Stat(satisPath); os.IsNotExist(err) {
		log.Fatalln("satis binary not found at", satisPath)
	}
	if _, err := os.Stat(configPath); os.IsNotExist(err) {
		log.Fatalln("satis configuration not found at", configPath)
	}

	// Perform a basic sanity check on the config
	var config map[string]interface{}
	data, err := ioutil.ReadFile(configPath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load satis config file: %s", err)
		os.Exit(1)
	}
	err = json.Unmarshal(data, &config)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to decode satis config file: %s", err)
		os.Exit(1)
	}

	// Spin up the worker goroutines
	shutdownChannel := make(chan bool)
	go configGenerator(shutdownChannel)
	go repoGenerator(shutdownChannel)
	go serveHttp(shutdownChannel)

	// Catch signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// We've got a kill signal? - do a clean shutdown
	sig := <-sigs
	fmt.Println("Received signal", sig)
	close(shutdownChannel)

	// Wait for the goroutines to exit
	for {
		if runningGoroutines == 0 {
			return
		}
		runtime.Gosched()
	}
}
<|endoftext|>


<commit_before>// saving.go
package main

import (
	"compress/gzip"
	"encoding/csv"
	"encoding/json"
	"io"
	"log"
	"os"
	"sort"
	"strconv"
)

// SaveToCsvWriter saves data to writerOut
// if fullExport saves log for each key else only sum for keys
func SaveToCsvWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {

	numKeysInt := make([]int, 0)
	for key := range keyMap {
		numKeysInt = append(numKeysInt, int(key))
	}
	sort.Ints(numKeysInt)
	numKeys := make([]uint8, 0)
	for _, key := range numKeysInt {
		numKeys = append(numKeys, uint8(key))
	}

	titleLine := make([]string, 0)
	titleLine = append(titleLine, "Time")
	if fullExport {
		for _, key := range numKeys {
			titleLine = append(titleLine, keyMap[key])
		}
	}
	titleLine = append(titleLine, "Sum")

	table := make([][]string, 0)
	table = append(table, titleLine)
	for _, rec := range data {
		line := make([]string, 0)
		line = append(line, strconv.Itoa(int(rec.time)))
		var sum int
		for _, key := range numKeys {
			sum += rec.keys[key]
			if fullExport {
				line = append(line, strconv.Itoa(rec.keys[key]))
			}
		}
		line = append(line, strconv.Itoa(sum))
		table = append(table, line)
	}

	writer := csv.NewWriter(writerOut)
	writer.WriteAll(table)
}

// SaveToCsvFile saves data to path
// if fullExport saves log for each key else only sum for keys
func SaveToCsvFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	csvfile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
		return
	}
	defer csvfile.Close()

	SaveToCsvWriter(data, keyMap, csvfile, fullExport)
}

// SaveToJSONWriter saves data to writerOut
// if fullExport saves log for each key else only sum for keys
// Save in one Json array
func SaveToJSONWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	type JSONStatForTime struct {
		Time int64
		Keys map[string]int
	}

	table := make([]JSONStatForTime, len(data))
	for i, stat := range data {
		table[i].Keys = make(map[string]int)

		table[i].Time = stat.time
		var sum int
		for numKey, key := range keyMap {
			if fullExport {
				table[i].Keys[key] = stat.keys[numKey]
			}
			sum += stat.keys[numKey]
		}
		table[i].Keys["sum"] = sum
	}

	outString, err := json.Marshal(table)
	if err != nil {
		log.Fatal(err)
	}

	writerOut.Write(outString)
}

// SaveToJSONFile saves data to path
// if fullExport saves log for each key else only sum for keys
// Save in one Json array
func SaveToJSONFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jsonFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jsonFile.Close()

	SaveToJSONWriter(data, keyMap, jsonFile, fullExport)
}

// SaveToJSONWriter saves data to writerOut
// if fullExport saves log for each key else only sum for keys
// Each record on new line
func SaveToJSLWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	type JSLStatForTime struct {
		Time int64
		Keys map[string]int
	}

	table := make([]JSLStatForTime, len(data))
	for i, stat := range data {
		table[i].Keys = make(map[string]int)

		table[i].Time = stat.time
		var sum int
		for numKey, key := range keyMap {
			if fullExport {
				table[i].Keys[key] = stat.keys[numKey]
			}
			sum += stat.keys[numKey]
		}
		table[i].Keys["sum"] = sum
	}

	for ind, line := range table {
		lineBytes, err := json.Marshal(line)
		if err != nil {
			log.Fatal(err)
		}
		writerOut.Write(lineBytes)
		if ind != len(table)-1 {
			writerOut.Write([]byte("\n"))
		}
	}
}

// SaveToJSONWriter saves data to path
// if fullExport saves log for each key else only sum for keys
// Each record on new line
func SaveToJSLFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jslFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jslFile.Close()

	SaveToJSLWriter(data, keyMap, jslFile, fullExport)
}

// SaveToCsvGzWriter same as SaveToCsvWriter but gunzip file before saving
func SaveToCsvGzWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	gzipWriter := gzip.NewWriter(writerOut)
	defer gzipWriter.Close()

	SaveToCsvWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToCsvGzFile same as SaveToCsvFile but gunzip file before saving
func SaveToCsvGzFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jsonFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jsonFile.Close()

	gzipWriter := gzip.NewWriter(jsonFile)
	defer gzipWriter.Close()

	SaveToCsvWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToJSONGzWriter same as SaveToJSONWriter but gunzip file before saving
func SaveToJSONGzWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	gzipWriter := gzip.NewWriter(writerOut)
	defer gzipWriter.Close()

	SaveToJSONWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToJSONGzFile same as SaveToJSONFile but gunzip file before saving
func SaveToJSONGzFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jsonFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jsonFile.Close()

	gzipWriter := gzip.NewWriter(jsonFile)
	defer gzipWriter.Close()

	SaveToJSONWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToJSLGzWriter same as SaveToJSLWriter but gunzip file before saving
func SaveToJSLGzWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	gzipWriter := gzip.NewWriter(writerOut)
	defer gzipWriter.Close()

	SaveToJSLWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToJSLFile same as SaveToJSLFile but gunzip file before saving
func SaveToJSLGzFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jslFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jslFile.Close()

	SaveToJSLGzWriter(data, keyMap, jslFile, fullExport)
}
<commit_msg>Fixed some documentation misprints<commit_after>// saving.go
package main

import (
	"compress/gzip"
	"encoding/csv"
	"encoding/json"
	"io"
	"log"
	"os"
	"sort"
	"strconv"
)

// SaveToCsvWriter saves data to writerOut
// if fullExport saves log for each key else only sum for keys
func SaveToCsvWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {

	numKeysInt := make([]int, 0)
	for key := range keyMap {
		numKeysInt = append(numKeysInt, int(key))
	}
	sort.Ints(numKeysInt)
	numKeys := make([]uint8, 0)
	for _, key := range numKeysInt {
		numKeys = append(numKeys, uint8(key))
	}

	titleLine := make([]string, 0)
	titleLine = append(titleLine, "Time")
	if fullExport {
		for _, key := range numKeys {
			titleLine = append(titleLine, keyMap[key])
		}
	}
	titleLine = append(titleLine, "Sum")

	table := make([][]string, 0)
	table = append(table, titleLine)
	for _, rec := range data {
		line := make([]string, 0)
		line = append(line, strconv.Itoa(int(rec.time)))
		var sum int
		for _, key := range numKeys {
			sum += rec.keys[key]
			if fullExport {
				line = append(line, strconv.Itoa(rec.keys[key]))
			}
		}
		line = append(line, strconv.Itoa(sum))
		table = append(table, line)
	}

	writer := csv.NewWriter(writerOut)
	writer.WriteAll(table)
}

// SaveToCsvFile saves data to path
// if fullExport saves log for each key else only sum for keys
func SaveToCsvFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	csvfile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
		return
	}
	defer csvfile.Close()

	SaveToCsvWriter(data, keyMap, csvfile, fullExport)
}

// SaveToJSONWriter saves data to writerOut
// if fullExport saves log for each key else only sum for keys
// Save in one Json array
func SaveToJSONWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	type JSONStatForTime struct {
		Time int64
		Keys map[string]int
	}

	table := make([]JSONStatForTime, len(data))
	for i, stat := range data {
		table[i].Keys = make(map[string]int)

		table[i].Time = stat.time
		var sum int
		for numKey, key := range keyMap {
			if fullExport {
				table[i].Keys[key] = stat.keys[numKey]
			}
			sum += stat.keys[numKey]
		}
		table[i].Keys["sum"] = sum
	}

	outString, err := json.Marshal(table)
	if err != nil {
		log.Fatal(err)
	}

	writerOut.Write(outString)
}

// SaveToJSONFile saves data to path
// if fullExport saves log for each key else only sum for keys
// Save in one Json array
func SaveToJSONFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jsonFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jsonFile.Close()

	SaveToJSONWriter(data, keyMap, jsonFile, fullExport)
}

// SaveToJSLWriter saves data to writerOut
// if fullExport saves log for each key else only sum for keys
// Each record on new line
func SaveToJSLWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	type JSLStatForTime struct {
		Time int64
		Keys map[string]int
	}

	table := make([]JSLStatForTime, len(data))
	for i, stat := range data {
		table[i].Keys = make(map[string]int)

		table[i].Time = stat.time
		var sum int
		for numKey, key := range keyMap {
			if fullExport {
				table[i].Keys[key] = stat.keys[numKey]
			}
			sum += stat.keys[numKey]
		}
		table[i].Keys["sum"] = sum
	}

	for ind, line := range table {
		lineBytes, err := json.Marshal(line)
		if err != nil {
			log.Fatal(err)
		}
		writerOut.Write(lineBytes)
		if ind != len(table)-1 {
			writerOut.Write([]byte("\n"))
		}
	}
}

// SaveToJSLFile saves data to path
// if fullExport saves log for each key else only sum for keys
// Each record on new line
func SaveToJSLFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jslFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jslFile.Close()

	SaveToJSLWriter(data, keyMap, jslFile, fullExport)
}

// SaveToCsvGzWriter same as SaveToCsvWriter but gunzip file before saving
func SaveToCsvGzWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	gzipWriter := gzip.NewWriter(writerOut)
	defer gzipWriter.Close()

	SaveToCsvWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToCsvGzFile same as SaveToCsvFile but gunzip file before saving
func SaveToCsvGzFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jsonFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jsonFile.Close()

	gzipWriter := gzip.NewWriter(jsonFile)
	defer gzipWriter.Close()

	SaveToCsvWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToJSONGzWriter same as SaveToJSONWriter but gunzip file before saving
func SaveToJSONGzWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	gzipWriter := gzip.NewWriter(writerOut)
	defer gzipWriter.Close()

	SaveToJSONWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToJSONGzFile same as SaveToJSONFile but gunzip file before saving
func SaveToJSONGzFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jsonFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jsonFile.Close()

	gzipWriter := gzip.NewWriter(jsonFile)
	defer gzipWriter.Close()

	SaveToJSONWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToJSLGzWriter same as SaveToJSLWriter but gunzip file before saving
func SaveToJSLGzWriter(data []StatForTime, keyMap map[uint8]string, writerOut io.Writer, fullExport bool) {
	gzipWriter := gzip.NewWriter(writerOut)
	defer gzipWriter.Close()

	SaveToJSLWriter(data, keyMap, gzipWriter, fullExport)
}

// SaveToJSLGzFile same as SaveToJSLFile but gunzip file before saving
func SaveToJSLGzFile(data []StatForTime, keyMap map[uint8]string, path string, fullExport bool) {
	jslFile, err := os.Create(path)
	if err != nil {
		log.Fatal(err)
	}
	defer jslFile.Close()

	SaveToJSLGzWriter(data, keyMap, jslFile, fullExport)
}
<|endoftext|>


<commit_before>// Copyright 2019 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.

package ristretto255

import (
	"github.com/gtank/ristretto255/internal/scalar"
)

// A Scalar is an element of the ristretto255 scalar field, as specified in
// draft-hdevalence-cfrg-ristretto-01, Section 3.4. That is, an integer modulo
//
//     l = 2^252 + 27742317777372353535851937790883648493
type Scalar struct {
	s scalar.Scalar
}

// NewScalar returns a Scalar set to the value 0.
func NewScalar() *Scalar {
	return (&Scalar{}).Zero()
}

// Add sets s = x + y mod l and returns s.
func (s *Scalar) Add(x, y *Scalar) *Scalar {
	s.s.Add(&x.s, &y.s)
	return s
}

// Subtract sets s = x - y mod l and returns s.
func (s *Scalar) Subtract(x, y *Scalar) *Scalar {
	s.s.Sub(&x.s, &y.s)
	return s
}

// Negate sets s = -x mod l and returns s.
func (s *Scalar) Negate(x *Scalar) *Scalar {
	s.s.Neg(&x.s)
	return s
}

// Multiply sets s = x * y mod l and returns s.
func (s *Scalar) Multiply(x, y *Scalar) *Scalar {
	s.s.Mul(&x.s, &y.s)
	return s
}

// FromUniformBytes sets s to an uniformly distributed value given 64 uniformly
// distributed random bytes.
func (s *Scalar) FromUniformBytes(x []byte) *Scalar {
	s.s.FromUniformBytes(x)
	return s
}

// Decode sets s = x, where x is a 32 bytes little-endian encoding of s. If x is
// not a canonical encoding of s, Decode returns an error and the receiver is
// unchanged.
func (s *Scalar) Decode(x []byte) error {
	return s.s.FromCanonicalBytes(x)
}

// Encode appends a 32 bytes little-endian encoding of s to b.
func (s *Scalar) Encode(b []byte) []byte {
	return s.s.Bytes(b)
}

// Equal returns 1 if v and u are equal, and 0 otherwise.
func (s *Scalar) Equal(u *Scalar) int {
	return s.s.Equal(&u.s)
}

// Zero sets s = 0 and returns s.
func (s *Scalar) Zero() *Scalar {
	s.s = scalar.Scalar{}
	return s
}
<commit_msg>ristretto255: add Scalar.Invert<commit_after>// Copyright 2019 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.

package ristretto255

import (
	"github.com/gtank/ristretto255/internal/scalar"
)

// A Scalar is an element of the ristretto255 scalar field, as specified in
// draft-hdevalence-cfrg-ristretto-01, Section 3.4. That is, an integer modulo
//
//     l = 2^252 + 27742317777372353535851937790883648493
type Scalar struct {
	s scalar.Scalar
}

// NewScalar returns a Scalar set to the value 0.
func NewScalar() *Scalar {
	return (&Scalar{}).Zero()
}

// Add sets s = x + y mod l and returns s.
func (s *Scalar) Add(x, y *Scalar) *Scalar {
	s.s.Add(&x.s, &y.s)
	return s
}

// Subtract sets s = x - y mod l and returns s.
func (s *Scalar) Subtract(x, y *Scalar) *Scalar {
	s.s.Sub(&x.s, &y.s)
	return s
}

// Negate sets s = -x mod l and returns s.
func (s *Scalar) Negate(x *Scalar) *Scalar {
	s.s.Neg(&x.s)
	return s
}

// Multiply sets s = x * y mod l and returns s.
func (s *Scalar) Multiply(x, y *Scalar) *Scalar {
	s.s.Mul(&x.s, &y.s)
	return s
}

// Invert sets s = 1 / x such that s * x = 1 mod l and returns s.
//
// If x is 0, the result is undefined.
func (s *Scalar) Invert(x *Scalar) *Scalar {
	s.s.Inv(&x.s)
	return s
}

// FromUniformBytes sets s to an uniformly distributed value given 64 uniformly
// distributed random bytes.
func (s *Scalar) FromUniformBytes(x []byte) *Scalar {
	s.s.FromUniformBytes(x)
	return s
}

// Decode sets s = x, where x is a 32 bytes little-endian encoding of s. If x is
// not a canonical encoding of s, Decode returns an error and the receiver is
// unchanged.
func (s *Scalar) Decode(x []byte) error {
	return s.s.FromCanonicalBytes(x)
}

// Encode appends a 32 bytes little-endian encoding of s to b.
func (s *Scalar) Encode(b []byte) []byte {
	return s.s.Bytes(b)
}

// Equal returns 1 if v and u are equal, and 0 otherwise.
func (s *Scalar) Equal(u *Scalar) int {
	return s.s.Equal(&u.s)
}

// Zero sets s = 0 and returns s.
func (s *Scalar) Zero() *Scalar {
	s.s = scalar.Scalar{}
	return s
}
<|endoftext|>


<commit_before>package buffalo

import (
	"github.com/stretchr/testify/require"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func TestCookies_Get(t *testing.T) {
	r := require.New(t)
	req := httptest.NewRequest("POST", "/", nil)
	req.Header.Set("Cookie", "name=Arthur Dent; answer=42")

	c := Cookies{req, nil}

	v, err := c.Get("name")
	r.NoError(err)
	r.Equal("Arthur Dent", v)

	v, err = c.Get("answer")
	r.NoError(err)
	r.Equal("42", v)

	_, err = c.Get("unknown")
	r.EqualError(err, http.ErrNoCookie.Error())
}

func TestCookies_Set(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	c.Set("name", "Rob Pike", time.Hour*24)

	h := res.Header().Get("Set-Cookie")
	r.Equal("name=Rob Pike; Max-Age=86400", h)
}

func TestCookies_SetWithExpirationTime(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	e := time.Date(2017, 7, 29, 19, 28, 45, 0, time.UTC)
	c.SetWithExpirationTime("name", "Rob Pike", e)

	h := res.Header().Get("Set-Cookie")
	r.Equal("name=Rob Pike; Expires=Sat, 29 Jul 2017 19:28:45 GMT", h)
}

func TestCookies_Delete(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	c.Delete("remove-me")

	h := res.Header().Get("Set-Cookie")
	r.Equal("remove-me=v; Expires=Thu, 01 Jan 1970 00:00:00 GMT", h)
}
<commit_msg>fixed broken test for 1.9<commit_after>package buffalo

import (
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestCookies_Get(t *testing.T) {
	r := require.New(t)
	req := httptest.NewRequest("POST", "/", nil)
	req.Header.Set("Cookie", "name=Arthur Dent; answer=42")

	c := Cookies{req, nil}

	v, err := c.Get("name")
	r.NoError(err)
	r.Equal("Arthur Dent", v)

	v, err = c.Get("answer")
	r.NoError(err)
	r.Equal("42", v)

	_, err = c.Get("unknown")
	r.EqualError(err, http.ErrNoCookie.Error())
}

func TestCookies_Set(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	c.Set("name", "Rob Pike", time.Hour*24)

	h := res.Header().Get("Set-Cookie")
	r.Equal("name=\"Rob Pike\"; Max-Age=86400", h)
}

func TestCookies_SetWithExpirationTime(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	e := time.Date(2017, 7, 29, 19, 28, 45, 0, time.UTC)
	c.SetWithExpirationTime("name", "Rob Pike", e)

	h := res.Header().Get("Set-Cookie")
	r.Equal("name=\"Rob Pike\"; Expires=Sat, 29 Jul 2017 19:28:45 GMT", h)
}

func TestCookies_Delete(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	c.Delete("remove-me")

	h := res.Header().Get("Set-Cookie")
	r.Equal("remove-me=v; Expires=Thu, 01 Jan 1970 00:00:00 GMT", h)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Camlistore Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Command zip-source packs the Camlistore source in a zip file, for a release.
// It should be run in a docker container.
package main

import (
	"archive/tar"
	"archive/zip"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
)

var (
	flagRev     = flag.String("rev", "", "Camlistore revision to ship (tag or commit hash). For development purposes, you can instead specify the path to a local Camlistore source tree from which to build, with the form \"WIP:/path/to/dir\".")
	flagVersion = flag.String("version", "", "The version number that is used in the zip file name, and in the VERSION file, e.g. 0.10")
	flagOutDir  = flag.String("outdir", "/OUT/", "Directory where to write the zip file.")
	flagSanity  = flag.Bool("sanity", true, "Check before making the zip that its contents pass the \"go run make.go\" test.")
)

const tmpSource = "/tmp/camlistore.org"

var (
	// Everything that should be included in the release.
	// maps filename to whether it's a directory.
	rootNames = map[string]bool{
		"app":          true,
		"AUTHORS":      false,
		"bin":          true,
		"BUILDING":     false,
		"clients":      true,
		"cmd":          true,
		"config":       true,
		"CONTRIBUTORS": false,
		"COPYING":      false,
		"depcheck":     true,
		"dev":          true,
		"doc":          true,
		"Dockerfile":   false,
		"HACKING":      false,
		"internal":     true,
		"lib":          true,
		"Makefile":     false,
		"make.go":      false,
		"misc":         true,
		"old":          true,
		"pkg":          true,
		"README":       false,
		"server":       true,
		"TESTS":        false,
		"third_party":  true,
		"TODO":         false,
		"vendor":       true,
		"website":      true,
	}
	tarballSrc = path.Join(*flagOutDir, "camlistore.org")
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage:\n")
	flag.PrintDefaults()
	example()
	os.Exit(1)
}

func example() {
	fmt.Fprintf(os.Stderr, "Examples:\n")
	fmt.Fprintf(os.Stderr, "\tdocker run --rm --volume /tmp/camlirelease:/OUT --volume $GOPATH/src/camlistore.org/misc/docker/release/cut-source.go:/usr/local/bin/cut-source.go:ro --volume $GOPATH/src/camlistore.org:/IN:ro camlistore/go /usr/local/go/bin/go run /usr/local/bin/zip-source.go --rev WIP:/IN\n")
	fmt.Fprintf(os.Stderr, "\tdocker run --rm --volume /tmp/camlirelease:/OUT --volume $GOPATH/src/camlistore.org/misc/docker/release/zip-source.go:/usr/local/bin/cut-source.go:ro camlistore/go /usr/local/go/bin/go run /usr/local/bin/cut-source.go --rev=4e8413c5012c\n")
}

func isWIP() bool {
	return strings.HasPrefix(*flagRev, "WIP")
}

// localCamliSource returns the path to the local Camlistore source tree
// that should be specified in *flagRev if *flagRev starts with "WIP:",
// empty string otherwise.
func localCamliSource() string {
	if !isWIP() {
		return ""
	}
	return strings.TrimPrefix(*flagRev, "WIP:")
}

func rev() string {
	if isWIP() {
		return "WORKINPROGRESS"
	}
	return *flagRev
}

func version() string {
	if *flagVersion != "" {
		return fmt.Sprintf("%v (git rev %v)", *flagVersion, rev())
	}
	return rev()
}

func getCamliSrc() {
	// TODO(mpl): we could filter right within mirrorCamliSrc and
	// fetchCamliSrc so we end up directly only with what we want as source.
	// Instead, I'm doing it in two passes, which is a bit more wasteful, but
	// simpler. Maybe reconsider.
	if localCamliSource() != "" {
		mirrorCamliSrc(localCamliSource())
	} else {
		fetchCamliSrc()
	}
}

func mirrorCamliSrc(srcDir string) {
	cmd := exec.Command("cp", "-a", srcDir, tmpSource)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		log.Fatalf("Error mirroring camlistore source from %v: %v", srcDir, err)
	}
}

func fetchCamliSrc() {
	check(os.MkdirAll(tmpSource, 0777))
	check(os.Chdir(tmpSource))

	res, err := http.Get("https://camlistore.googlesource.com/camlistore/+archive/" + *flagRev + ".tar.gz")
	check(err)
	defer res.Body.Close()
	gz, err := gzip.NewReader(res.Body)
	check(err)
	defer gz.Close()
	tr := tar.NewReader(gz)
	for {
		h, err := tr.Next()
		if err == io.EOF {
			break
		}
		check(err)
		if h.Typeflag == tar.TypeDir {
			check(os.MkdirAll(h.Name, os.FileMode(h.Mode)))
			continue
		}
		f, err := os.Create(h.Name)
		check(err)
		n, err := io.Copy(f, tr)
		if err != nil && err != io.EOF {
			log.Fatal(err)
		}
		if n != h.Size {
			log.Fatalf("Error when creating %v: wanted %v bytes, got %v bytes", h.Name, h.Size, n)
		}
		check(f.Close())
	}
}

func cpFile(dst, src string) error {
	sfi, err := os.Stat(src)
	if err != nil {
		return err
	}
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	// ok to defer because we're in main loop, and not many iterations.
	defer sf.Close()
	df, err := os.Create(dst)
	if err != nil {
		return err
	}
	n, err := io.Copy(df, sf)
	if err == nil && n != sfi.Size() {
		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
	}
	cerr := df.Close()
	if err == nil {
		err = cerr
	}
	if err != nil {
		return err
	}
	if err := os.Chmod(dst, sfi.Mode()); err != nil {
		return err
	}
	if err := os.Chtimes(dst, sfi.ModTime(), sfi.ModTime()); err != nil {
		return err
	}
	return nil
}

func filter() {
	destDir := tarballSrc
	check(os.MkdirAll(destDir, 0777))

	d, err := os.Open(tmpSource)
	check(err)
	names, err := d.Readdirnames(-1)
	d.Close()
	check(err)

	found := make(map[string]struct{})
	for _, name := range names {
		isDir, ok := rootNames[name]
		if !ok {
			continue
		}
		found[name] = struct{}{}
		srcPath := path.Join(tmpSource, name)
		dstPath := path.Join(destDir, name)
		if isDir {
			cmd := exec.Command("cp", "-a", srcPath, dstPath)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			if err := cmd.Run(); err != nil {
				log.Fatalf("could not cp dir %v into %v: %v", name, destDir, err)
			}
			continue
		}
		check(cpFile(dstPath, srcPath))
	}
	for name, _ := range rootNames {
		if _, ok := found[name]; !ok {
			log.Fatalf("file (or directory) %v should be included in release, but not found in source", name)
		}
	}
	// we insert the version in the VERSION file, so make.go does no need git
	// in the container to detect the Camlistore version.
	check(os.Chdir(destDir))
	check(ioutil.WriteFile("VERSION", []byte(version()), 0777))
}

func checkBuild() {
	if !*flagSanity {
		return
	}
	check(os.Chdir(tarballSrc))
	check(os.Setenv("PATH", os.Getenv("PATH")+":/usr/local/go/bin/"))
	cmd := exec.Command("go", "run", "make.go")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		log.Fatalf("could not build Camlistore from tarball contents: %v", err)
	}
	// cleanup
	check(os.RemoveAll(path.Join(tarballSrc, "tmp")))
	binDir := path.Join(tarballSrc, "bin")
	check(os.Rename(path.Join(binDir, "README"), "README.bin"))
	check(os.RemoveAll(binDir))
	check(os.MkdirAll(binDir, 0755))
	check(os.Rename("README.bin", path.Join(binDir, "README")))
}

func pack() {
	zipFile := path.Join(*flagOutDir, "camlistore-src.zip")
	check(os.Chdir(*flagOutDir))
	fw, err := os.Create(zipFile)
	check(err)
	w := zip.NewWriter(fw)

	check(filepath.Walk("camlistore.org", func(filePath string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if fi.IsDir() {
			return nil
		}
		b, err := ioutil.ReadFile(filePath)
		if err != nil {
			return err
		}
		fh := &zip.FileHeader{
			Name:   filePath,
			Method: zip.Deflate,
		}
		fh.SetModTime(fi.ModTime())
		fh.SetMode(fi.Mode())
		f, err := w.CreateHeader(fh)
		if err != nil {
			return err
		}
		if _, err = f.Write(b); err != nil {
			return err
		}
		return nil
	}))
	check(w.Close())
	check(fw.Close())
	fmt.Printf("Camlistore source successfully packed in %v\n", zipFile)
}

func checkArgs() {
	if flag.NArg() != 0 {
		usage()
	}
	if *flagRev == "" {
		fmt.Fprintf(os.Stderr, "Usage error: --rev is required.\n")
		usage()
	}
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if _, err := os.Stat("/.dockerinit"); err != nil {
		fmt.Fprintf(os.Stderr, "Usage error: this program should be run within a docker container, and is meant to be called from misc/docker/dock.go\n")
		usage()
	}
	checkArgs()

	getCamliSrc()
	filter()
	checkBuild()
	pack()
}

func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>misc/docker: rm third_party in zip-source<commit_after>/*
Copyright 2016 The Camlistore Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Command zip-source packs the Camlistore source in a zip file, for a release.
// It should be run in a docker container.
package main

import (
	"archive/tar"
	"archive/zip"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
)

var (
	flagRev     = flag.String("rev", "", "Camlistore revision to ship (tag or commit hash). For development purposes, you can instead specify the path to a local Camlistore source tree from which to build, with the form \"WIP:/path/to/dir\".")
	flagVersion = flag.String("version", "", "The version number that is used in the zip file name, and in the VERSION file, e.g. 0.10")
	flagOutDir  = flag.String("outdir", "/OUT/", "Directory where to write the zip file.")
	flagSanity  = flag.Bool("sanity", true, "Check before making the zip that its contents pass the \"go run make.go\" test.")
)

const tmpSource = "/tmp/camlistore.org"

var (
	// Everything that should be included in the release.
	// maps filename to whether it's a directory.
	rootNames = map[string]bool{
		"app":          true,
		"AUTHORS":      false,
		"bin":          true,
		"BUILDING":     false,
		"clients":      true,
		"cmd":          true,
		"config":       true,
		"CONTRIBUTORS": false,
		"COPYING":      false,
		"depcheck":     true,
		"dev":          true,
		"doc":          true,
		"Dockerfile":   false,
		"HACKING":      false,
		"internal":     true,
		"lib":          true,
		"Makefile":     false,
		"make.go":      false,
		"misc":         true,
		"old":          true,
		"pkg":          true,
		"README":       false,
		"server":       true,
		"TESTS":        false,
		"TODO":         false,
		"vendor":       true,
		"website":      true,
	}
	tarballSrc = path.Join(*flagOutDir, "camlistore.org")
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage:\n")
	flag.PrintDefaults()
	example()
	os.Exit(1)
}

func example() {
	fmt.Fprintf(os.Stderr, "Examples:\n")
	fmt.Fprintf(os.Stderr, "\tdocker run --rm --volume /tmp/camlirelease:/OUT --volume $GOPATH/src/camlistore.org/misc/docker/release/cut-source.go:/usr/local/bin/cut-source.go:ro --volume $GOPATH/src/camlistore.org:/IN:ro camlistore/go /usr/local/go/bin/go run /usr/local/bin/zip-source.go --rev WIP:/IN\n")
	fmt.Fprintf(os.Stderr, "\tdocker run --rm --volume /tmp/camlirelease:/OUT --volume $GOPATH/src/camlistore.org/misc/docker/release/zip-source.go:/usr/local/bin/cut-source.go:ro camlistore/go /usr/local/go/bin/go run /usr/local/bin/cut-source.go --rev=4e8413c5012c\n")
}

func isWIP() bool {
	return strings.HasPrefix(*flagRev, "WIP")
}

// localCamliSource returns the path to the local Camlistore source tree
// that should be specified in *flagRev if *flagRev starts with "WIP:",
// empty string otherwise.
func localCamliSource() string {
	if !isWIP() {
		return ""
	}
	return strings.TrimPrefix(*flagRev, "WIP:")
}

func rev() string {
	if isWIP() {
		return "WORKINPROGRESS"
	}
	return *flagRev
}

func version() string {
	if *flagVersion != "" {
		return fmt.Sprintf("%v (git rev %v)", *flagVersion, rev())
	}
	return rev()
}

func getCamliSrc() {
	// TODO(mpl): we could filter right within mirrorCamliSrc and
	// fetchCamliSrc so we end up directly only with what we want as source.
	// Instead, I'm doing it in two passes, which is a bit more wasteful, but
	// simpler. Maybe reconsider.
	if localCamliSource() != "" {
		mirrorCamliSrc(localCamliSource())
	} else {
		fetchCamliSrc()
	}
}

func mirrorCamliSrc(srcDir string) {
	cmd := exec.Command("cp", "-a", srcDir, tmpSource)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		log.Fatalf("Error mirroring camlistore source from %v: %v", srcDir, err)
	}
}

func fetchCamliSrc() {
	check(os.MkdirAll(tmpSource, 0777))
	check(os.Chdir(tmpSource))

	res, err := http.Get("https://camlistore.googlesource.com/camlistore/+archive/" + *flagRev + ".tar.gz")
	check(err)
	defer res.Body.Close()
	gz, err := gzip.NewReader(res.Body)
	check(err)
	defer gz.Close()
	tr := tar.NewReader(gz)
	for {
		h, err := tr.Next()
		if err == io.EOF {
			break
		}
		check(err)
		if h.Typeflag == tar.TypeDir {
			check(os.MkdirAll(h.Name, os.FileMode(h.Mode)))
			continue
		}
		f, err := os.Create(h.Name)
		check(err)
		n, err := io.Copy(f, tr)
		if err != nil && err != io.EOF {
			log.Fatal(err)
		}
		if n != h.Size {
			log.Fatalf("Error when creating %v: wanted %v bytes, got %v bytes", h.Name, h.Size, n)
		}
		check(f.Close())
	}
}

func cpFile(dst, src string) error {
	sfi, err := os.Stat(src)
	if err != nil {
		return err
	}
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	// ok to defer because we're in main loop, and not many iterations.
	defer sf.Close()
	df, err := os.Create(dst)
	if err != nil {
		return err
	}
	n, err := io.Copy(df, sf)
	if err == nil && n != sfi.Size() {
		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
	}
	cerr := df.Close()
	if err == nil {
		err = cerr
	}
	if err != nil {
		return err
	}
	if err := os.Chmod(dst, sfi.Mode()); err != nil {
		return err
	}
	if err := os.Chtimes(dst, sfi.ModTime(), sfi.ModTime()); err != nil {
		return err
	}
	return nil
}

func filter() {
	destDir := tarballSrc
	check(os.MkdirAll(destDir, 0777))

	d, err := os.Open(tmpSource)
	check(err)
	names, err := d.Readdirnames(-1)
	d.Close()
	check(err)

	found := make(map[string]struct{})
	for _, name := range names {
		isDir, ok := rootNames[name]
		if !ok {
			continue
		}
		found[name] = struct{}{}
		srcPath := path.Join(tmpSource, name)
		dstPath := path.Join(destDir, name)
		if isDir {
			cmd := exec.Command("cp", "-a", srcPath, dstPath)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			if err := cmd.Run(); err != nil {
				log.Fatalf("could not cp dir %v into %v: %v", name, destDir, err)
			}
			continue
		}
		check(cpFile(dstPath, srcPath))
	}
	for name, _ := range rootNames {
		if _, ok := found[name]; !ok {
			log.Fatalf("file (or directory) %v should be included in release, but not found in source", name)
		}
	}
	// we insert the version in the VERSION file, so make.go does no need git
	// in the container to detect the Camlistore version.
	check(os.Chdir(destDir))
	check(ioutil.WriteFile("VERSION", []byte(version()), 0777))
}

func checkBuild() {
	if !*flagSanity {
		return
	}
	check(os.Chdir(tarballSrc))
	check(os.Setenv("PATH", os.Getenv("PATH")+":/usr/local/go/bin/"))
	cmd := exec.Command("go", "run", "make.go")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		log.Fatalf("could not build Camlistore from tarball contents: %v", err)
	}
	// cleanup
	check(os.RemoveAll(path.Join(tarballSrc, "tmp")))
	binDir := path.Join(tarballSrc, "bin")
	check(os.Rename(path.Join(binDir, "README"), "README.bin"))
	check(os.RemoveAll(binDir))
	check(os.MkdirAll(binDir, 0755))
	check(os.Rename("README.bin", path.Join(binDir, "README")))
}

func pack() {
	zipFile := path.Join(*flagOutDir, "camlistore-src.zip")
	check(os.Chdir(*flagOutDir))
	fw, err := os.Create(zipFile)
	check(err)
	w := zip.NewWriter(fw)

	check(filepath.Walk("camlistore.org", func(filePath string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if fi.IsDir() {
			return nil
		}
		b, err := ioutil.ReadFile(filePath)
		if err != nil {
			return err
		}
		fh := &zip.FileHeader{
			Name:   filePath,
			Method: zip.Deflate,
		}
		fh.SetModTime(fi.ModTime())
		fh.SetMode(fi.Mode())
		f, err := w.CreateHeader(fh)
		if err != nil {
			return err
		}
		if _, err = f.Write(b); err != nil {
			return err
		}
		return nil
	}))
	check(w.Close())
	check(fw.Close())
	fmt.Printf("Camlistore source successfully packed in %v\n", zipFile)
}

func checkArgs() {
	if flag.NArg() != 0 {
		usage()
	}
	if *flagRev == "" {
		fmt.Fprintf(os.Stderr, "Usage error: --rev is required.\n")
		usage()
	}
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if _, err := os.Stat("/.dockerinit"); err != nil {
		fmt.Fprintf(os.Stderr, "Usage error: this program should be run within a docker container, and is meant to be called from misc/docker/dock.go\n")
		usage()
	}
	checkArgs()

	getCamliSrc()
	filter()
	checkBuild()
	pack()
}

func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

var (
	tokenFile   = flag.String("token_file", "", "File containing Auth Token.")
	owner       = flag.String("owner", "istio", "Github Owner or org.")
	repo        = flag.String("repo", "", "Github repo within the org.")
	base        = flag.String("base", "stable", "The base branch used for PR.")
	head        = flag.String("head", "master", "The head branch used for PR.")
	pr          = flag.Int("pr", 0, "The Pull request to use.")
	fastForward = flag.Bool("fast_forward", false, "Creates a PR updating Base to Head.")
	verify      = flag.Bool("verify", false, "Verifies PR on Base and push them if success.")
	comment     = flag.String("comment", "", "The comment to send to the Pull Request.")
	GH          = newGhConst()
)

type ghConst struct {
	success string
	failure string
	pending string
	closed  string
	all     string
	commit  string
}

// Simple Github Helper
type helper struct {
	Owner  string
	Repo   string
	Base   string
	Head   string
	Pr     int
	Client *github.Client
}

// Get token from tokenFile is set, otherwise is anonymous.
func getToken() (*http.Client, error) {
	if *tokenFile == "" {
		return nil, nil
	}
	b, err := ioutil.ReadFile(*tokenFile)
	if err != nil {
		return nil, err
	}
	token := strings.TrimSpace(string(b[:]))
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: string(token[:])})
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	return tc, nil
}

// Creates a new ghConst
func newGhConst() *ghConst {
	return &ghConst{
		success: "success",
		failure: "failure",
		pending: "pending",
		closed:  "closed",
		all:     "all",
		commit:  "commit",
	}
}

// Creates a new Github Helper from provided
func newHelper() (*helper, error) {
	if tc, err := getToken(); err == nil {
		if *repo == "" {
			return nil, errors.New("repo flag must be set!")
		}
		client := github.NewClient(tc)
		return &helper{
			Owner:  *owner,
			Repo:   *repo,
			Base:   *base,
			Head:   *head,
			Pr:     *pr,
			Client: client,
		}, nil
	} else {
		return nil, err
	}
}

// Creates a pull request from Base branch
func (h helper) createPullRequestToBase(commit *string) error {
	if commit == nil {
		return errors.New("commit cannot be nil.")
	}
	title := fmt.Sprintf(
		"DO NOT MERGE! Fast Forward %s to %s.", h.Base, *commit)
	body := "This PR will be merged automatically once checks are successful."
	req := github.NewPullRequest{
		Head:  &h.Head,
		Base:  &h.Base,
		Title: &title,
		Body:  &body,
	}
	log.Printf("Creating a PR with Title: \"%s\"", title)
	if pr, _, err := h.Client.PullRequests.Create(h.Owner, h.Repo, &req); err == nil {
		log.Printf("Created new PR at %s", *pr.HTMLURL)
	} else {
		return err
	}
	return nil
}

// Gets the last commit from Head branch.
func (h helper) getLastCommitFromHead() (*string, error) {
	comp, _, err := h.Client.Repositories.CompareCommits(h.Owner, h.Repo, h.Head, h.Base)
	if err == nil {
		if *comp.BehindBy > 0 {
			commit := comp.BaseCommit.SHA
			log.Printf(
				"%s is %d commits ahead from %s, and HEAD commit is %s",
				h.Head, *comp.BehindBy, h.Base, *commit)
			return commit, nil
		}
	}
	return nil, err
}

// Fast forward Base branch to the last commit of Head branch.
func (h helper) fastForwardBase() error {
	commit, err := h.getLastCommitFromHead()
	if err != nil {
		return err
	}
	if commit != nil {
		options := github.PullRequestListOptions{
			Head:  h.Head,
			Base:  h.Base,
			State: GH.all,
		}

		prs, _, err := h.Client.PullRequests.List(h.Owner, h.Repo, &options)
		if err == nil {
			for _, pr := range prs {
				if strings.Contains(*pr.Title, *commit) {
					log.Printf("A PR already exist for %s", *commit)
					return nil
				}
			}
		}
		return h.createPullRequestToBase(commit)
	}
	log.Printf("Branches %s and %s are in sync.", h.Base, h.Head)
	return nil
}

// Close an existing PR
func (h helper) closePullRequest(pr *github.PullRequest) error {
	log.Printf("Closing PR %d", *pr.ID)
	*pr.State = GH.closed
	_, _, err := h.Client.PullRequests.Edit(h.Owner, h.Repo, *pr.ID, pr)
	return err
}

// Create an annotated stable tag from the given commit.
func (h helper) createStableTag(commit *string) error {
	if commit == nil {
		return errors.New("commit cannot be nil.")
	}
	sha := *commit
	tag := fmt.Sprintf("stable-%s", sha[0:7])
	message := "Stable build"
	log.Printf("Creating tag %s on %s for commit %s", tag, h.Base, *commit)
	gho := github.GitObject{
		SHA:  commit,
		Type: &GH.commit,
	}
	gt := github.Tag{
		Object:  &gho,
		Message: &message,
		Tag:     &tag,
	}
	t, resp, err := h.Client.Git.CreateTag(h.Owner, h.Repo, &gt)
	if err != nil {
		return err
	}
	log.Printf("Creating ref tag %s on %s for commit %s", tag, h.Base, *commit)
	ref := fmt.Sprintf("refs/tags/%s", tag)
	r := github.Reference{
		Ref:    &ref,
		Object: t.Object,
	}
	_, resp, err = h.Client.Git.CreateRef(h.Owner, h.Repo, &r)
	// Already exists
	if resp.StatusCode != 422 {
		return err
	}
	return nil
}

// Update the Base branch reference to a given commit.
func (h helper) updateBaseReference(commit *string) error {
	if commit == nil {
		return errors.New("commit cannot be nil")
	}
	ref := fmt.Sprintf("refs/heads/%s", h.Base)
	log.Printf("Updating ref %s to commit %s", ref, *commit)
	gho := github.GitObject{
		SHA:  commit,
		Type: &GH.commit,
	}
	r := github.Reference{
		Ref:    &ref,
		Object: &gho,
	}
	r.Ref = new(string)
	*r.Ref = ref

	_, _, err := h.Client.Git.UpdateRef(h.Owner, h.Repo, &r, false)
	return err
}

// Checks if a PR is ready to be pushed. Create a stable tag and
// fast forward Base to the PR's head commit.
func (h helper) updatePullRequest(pr *github.PullRequest, s *github.CombinedStatus) error {
	switch *s.State {
	case GH.success:
		if err := h.createStableTag(s.SHA); err == nil {
			if err := h.updateBaseReference(s.SHA); err != nil {
				log.Printf("Could not update %s reference to %s.\n%v", h.Base, *s.SHA, err)
				return nil
			}
			// Note there is no need to close the PR here.
			// It will be done automatically once Base ref is updated
		} else {
			return err
		}
	case GH.failure:
		log.Printf("Closing PR %d", *pr.ID)
		return h.closePullRequest(pr)
	case GH.pending:
		log.Printf("Pull Request %d is still being checked", pr.ID)
	}
	return nil
}

// Checks all the PR on Base and calls updatePullRequest on each.
func (h helper) verifyPullRequestStatus() error {
	options := github.PullRequestListOptions{
		Head:  h.Head,
		Base:  h.Base,
		State: "open",
	}
	prs, _, err := h.Client.PullRequests.List(h.Owner, h.Repo, &options)
	if err != nil {
		return err
	}
	for _, pr := range prs {
		statuses, _, err := h.Client.Repositories.GetCombinedStatus(
			h.Owner, h.Repo, *pr.Head.SHA, new(github.ListOptions))
		if err == nil {
			err = h.updatePullRequest(pr, statuses)
		}
		if err != nil {
			log.Fatalf("Could not update PR %d. \n%v", *pr.ID, err)
		}
	}
	log.Printf("No more PR to verify for branch %s.", h.Base)
	return nil
}

// Creates a comment on a Pull Request
func (h helper) createComment(comment *string) error {
	if h.Pr <= 0 {
		return errors.New("PR number needs to be greather than 0")
	}
	c := github.IssueComment{
		Body: comment,
	}
	log.Printf("Commenting \"%s\" on PR %d for %s/%s", *comment, h.Pr, h.Owner, h.Repo)
	_, _, err := h.Client.Issues.CreateComment(h.Owner, h.Repo, h.Pr, &c)
	return err
}

func main() {
	flag.Parse()
	h, err := newHelper()
	if err != nil {
		log.Fatalf("Could not instantiate a github client %v", err)
	}
	if *verify {
		if err = h.verifyPullRequestStatus(); err != nil {
			log.Fatalf("Unable to verify PR from %s.\n%v", h.Base, err)
		}
	}
	if *fastForward {
		if err = h.fastForwardBase(); err != nil {
			log.Fatalf("Unable to fast forward %s.\n%v", h.Base, err)
		}
	}
	if *comment != "" {
		if err := h.createComment(comment); err != nil {
			log.Fatalf("Unable to create a comment on PR %d.\n%v", h.Pr, err)
		}
	}
}
<commit_msg>Using PR number instead of PR id (#47)<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

var (
	tokenFile   = flag.String("token_file", "", "File containing Auth Token.")
	owner       = flag.String("owner", "istio", "Github Owner or org.")
	repo        = flag.String("repo", "", "Github repo within the org.")
	base        = flag.String("base", "stable", "The base branch used for PR.")
	head        = flag.String("head", "master", "The head branch used for PR.")
	pullRequest = flag.Int("pr", 0, "The Pull request to use.")
	fastForward = flag.Bool("fast_forward", false, "Creates a PR updating Base to Head.")
	verify      = flag.Bool("verify", false, "Verifies PR on Base and push them if success.")
	comment     = flag.String("comment", "", "The comment to send to the Pull Request.")
	GH          = newGhConst()
)

type ghConst struct {
	success string
	failure string
	pending string
	closed  string
	all     string
	commit  string
}

// Simple Github Helper
type helper struct {
	Owner  string
	Repo   string
	Base   string
	Head   string
	Pr     int
	Client *github.Client
}

// Get token from tokenFile is set, otherwise is anonymous.
func getToken() (*http.Client, error) {
	if *tokenFile == "" {
		return nil, nil
	}
	b, err := ioutil.ReadFile(*tokenFile)
	if err != nil {
		return nil, err
	}
	token := strings.TrimSpace(string(b[:]))
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: string(token[:])})
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	return tc, nil
}

// Creates a new ghConst
func newGhConst() *ghConst {
	return &ghConst{
		success: "success",
		failure: "failure",
		pending: "pending",
		closed:  "closed",
		all:     "all",
		commit:  "commit",
	}
}

// Creates a new Github Helper from provided
func newHelper() (*helper, error) {
	if tc, err := getToken(); err == nil {
		if *repo == "" {
			return nil, errors.New("repo flag must be set!")
		}
		client := github.NewClient(tc)
		return &helper{
			Owner:  *owner,
			Repo:   *repo,
			Base:   *base,
			Head:   *head,
			Pr:     *pullRequest,
			Client: client,
		}, nil
	} else {
		return nil, err
	}
}

// Creates a pull request from Base branch
func (h helper) createPullRequestToBase(commit *string) error {
	if commit == nil {
		return errors.New("commit cannot be nil.")
	}
	title := fmt.Sprintf(
		"DO NOT MERGE! Fast Forward %s to %s.", h.Base, *commit)
	body := "This PR will be merged automatically once checks are successful."
	req := github.NewPullRequest{
		Head:  &h.Head,
		Base:  &h.Base,
		Title: &title,
		Body:  &body,
	}
	log.Printf("Creating a PR with Title: \"%s\"", title)
	if pr, _, err := h.Client.PullRequests.Create(h.Owner, h.Repo, &req); err == nil {
		log.Printf("Created new PR at %s", *pr.HTMLURL)
	} else {
		return err
	}
	return nil
}

// Gets the last commit from Head branch.
func (h helper) getLastCommitFromHead() (*string, error) {
	comp, _, err := h.Client.Repositories.CompareCommits(h.Owner, h.Repo, h.Head, h.Base)
	if err == nil {
		if *comp.BehindBy > 0 {
			commit := comp.BaseCommit.SHA
			log.Printf(
				"%s is %d commits ahead from %s, and HEAD commit is %s",
				h.Head, *comp.BehindBy, h.Base, *commit)
			return commit, nil
		}
	}
	return nil, err
}

// Fast forward Base branch to the last commit of Head branch.
func (h helper) fastForwardBase() error {
	commit, err := h.getLastCommitFromHead()
	if err != nil {
		return err
	}
	if commit != nil {
		options := github.PullRequestListOptions{
			Head:  h.Head,
			Base:  h.Base,
			State: GH.all,
		}

		prs, _, err := h.Client.PullRequests.List(h.Owner, h.Repo, &options)
		if err == nil {
			for _, pr := range prs {
				if strings.Contains(*pr.Title, *commit) {
					log.Printf("A PR already exist for %s", *commit)
					return nil
				}
			}
		}
		return h.createPullRequestToBase(commit)
	}
	log.Printf("Branches %s and %s are in sync.", h.Base, h.Head)
	return nil
}

// Close an existing PR
func (h helper) closePullRequest(pr *github.PullRequest) error {
	log.Printf("Closing PR %d", *pr.Number)
	*pr.State = GH.closed
	_, _, err := h.Client.PullRequests.Edit(h.Owner, h.Repo, *pr.Number, pr)
	return err
}

// Create an annotated stable tag from the given commit.
func (h helper) createStableTag(commit *string) error {
	if commit == nil {
		return errors.New("commit cannot be nil.")
	}
	sha := *commit
	tag := fmt.Sprintf("stable-%s", sha[0:7])
	message := "Stable build"
	log.Printf("Creating tag %s on %s for commit %s", tag, h.Base, *commit)
	gho := github.GitObject{
		SHA:  commit,
		Type: &GH.commit,
	}
	gt := github.Tag{
		Object:  &gho,
		Message: &message,
		Tag:     &tag,
	}
	t, resp, err := h.Client.Git.CreateTag(h.Owner, h.Repo, &gt)
	if err != nil {
		return err
	}
	log.Printf("Creating ref tag %s on %s for commit %s", tag, h.Base, *commit)
	ref := fmt.Sprintf("refs/tags/%s", tag)
	r := github.Reference{
		Ref:    &ref,
		Object: t.Object,
	}
	_, resp, err = h.Client.Git.CreateRef(h.Owner, h.Repo, &r)
	// Already exists
	if resp.StatusCode != 422 {
		return err
	}
	return nil
}

// Update the Base branch reference to a given commit.
func (h helper) updateBaseReference(commit *string) error {
	if commit == nil {
		return errors.New("commit cannot be nil")
	}
	ref := fmt.Sprintf("refs/heads/%s", h.Base)
	log.Printf("Updating ref %s to commit %s", ref, *commit)
	gho := github.GitObject{
		SHA:  commit,
		Type: &GH.commit,
	}
	r := github.Reference{
		Ref:    &ref,
		Object: &gho,
	}
	r.Ref = new(string)
	*r.Ref = ref

	_, _, err := h.Client.Git.UpdateRef(h.Owner, h.Repo, &r, false)
	return err
}

// Checks if a PR is ready to be pushed. Create a stable tag and
// fast forward Base to the PR's head commit.
func (h helper) updatePullRequest(pr *github.PullRequest, s *github.CombinedStatus) error {
	switch *s.State {
	case GH.success:
		if err := h.createStableTag(s.SHA); err == nil {
			if err := h.updateBaseReference(s.SHA); err != nil {
				log.Printf("Could not update %s reference to %s.\n%v", h.Base, *s.SHA, err)
				return nil
			}
			// Note there is no need to close the PR here.
			// It will be done automatically once Base ref is updated
		} else {
			return err
		}
	case GH.failure:
		return h.closePullRequest(pr)
	case GH.pending:
		log.Printf("Pull Request %d is still being checked", pr.Number)
	}
	return nil
}

// Checks all the PR on Base and calls updatePullRequest on each.
func (h helper) verifyPullRequestStatus() error {
	options := github.PullRequestListOptions{
		Head:  h.Head,
		Base:  h.Base,
		State: "open",
	}
	prs, _, err := h.Client.PullRequests.List(h.Owner, h.Repo, &options)
	if err != nil {
		return err
	}
	for _, pr := range prs {
		statuses, _, err := h.Client.Repositories.GetCombinedStatus(
			h.Owner, h.Repo, *pr.Head.SHA, new(github.ListOptions))
		if err == nil {
			err = h.updatePullRequest(pr, statuses)
		}
		if err != nil {
			log.Fatalf("Could not update PR %d. \n%v", *pr.Number, err)
		}
	}
	log.Printf("No more PR to verify for branch %s.", h.Base)
	return nil
}

// Creates a comment on a Pull Request
func (h helper) createComment(comment *string) error {
	if h.Pr <= 0 {
		return errors.New("PR number needs to be greather than 0")
	}
	c := github.IssueComment{
		Body: comment,
	}
	log.Printf("Commenting \"%s\" on PR %d for %s/%s", *comment, h.Pr, h.Owner, h.Repo)
	_, _, err := h.Client.Issues.CreateComment(h.Owner, h.Repo, h.Pr, &c)
	return err
}

func main() {
	flag.Parse()
	h, err := newHelper()
	if err != nil {
		log.Fatalf("Could not instantiate a github client %v", err)
	}
	if *verify {
		if err = h.verifyPullRequestStatus(); err != nil {
			log.Fatalf("Unable to verify PR from %s.\n%v", h.Base, err)
		}
	}
	if *fastForward {
		if err = h.fastForwardBase(); err != nil {
			log.Fatalf("Unable to fast forward %s.\n%v", h.Base, err)
		}
	}
	if *comment != "" {
		if err := h.createComment(comment); err != nil {
			log.Fatalf("Unable to create a comment on PR %d.\n%v", h.Pr, err)
		}
	}
}
<|endoftext|>


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

package compiler

import (
	"fmt"

	"github.com/google/gapid/core/codegen"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/gapil/compiler/mangling"
	"github.com/google/gapid/gapil/semantic"
)

type refRel struct {
	name      string
	reference *codegen.Function // void T_reference(T)
	release   *codegen.Function // void T_release(T)
}

func (f *refRel) declare(c *C, name, ref, rel string, ty codegen.Type) {
	f.reference = c.M.Function(c.T.Void, ref, ty).LinkOnceODR().Inline()
	f.release = c.M.Function(c.T.Void, rel, ty).LinkOnceODR().Inline()
	f.name = name
}

func (f *refRel) delegate(c *C, to refRel) {
	c.Delegate(f.reference, to.reference)
	c.Delegate(f.release, to.release)
}

func (f *refRel) build(
	c *C,
	isNull func(s *S, val *codegen.Value) *codegen.Value,
	getRefPtr func(s *S, val *codegen.Value) *codegen.Value,
	del func(s *S, val *codegen.Value),
) {
	c.Build(f.reference, func(s *S) {
		val := s.Parameter(0)
		s.If(isNull(s, val), func(s *S) {
			s.Return(nil)
		})
		refPtr := getRefPtr(s, val)
		oldCount := refPtr.Load()
		s.If(s.Equal(oldCount, s.Scalar(uint32(0))), func(s *S) {
			c.Log(s, log.Fatal, "Attempting to reference released "+f.name)
		})
		newCount := s.Add(oldCount, s.Scalar(uint32(1)))
		if debugRefCounts {
			c.LogI(s, f.name+" %p ref_count: %d -> %d", refPtr, oldCount, newCount)
		}
		refPtr.Store(newCount)
	})

	c.Build(f.release, func(s *S) {
		val := s.Parameter(0)
		s.If(isNull(s, val), func(s *S) {
			s.Return(nil)
		})
		refPtr := getRefPtr(s, val)
		oldCount := refPtr.Load()
		s.If(s.Equal(oldCount, s.Scalar(uint32(0))), func(s *S) {
			c.Log(s, log.Fatal, "Attempting to release "+f.name+" with no remaining references!")
		})
		newCount := s.Sub(oldCount, s.Scalar(uint32(1)))
		if debugRefCounts {
			c.LogI(s, f.name+" %p ref_count: %d -> %d", refPtr, oldCount, newCount)
		}
		refPtr.Store(newCount)
		s.If(s.Equal(newCount, s.Scalar(uint32(0))), func(s *S) {
			del(s, val)
		})
	})
}

type refRels struct {
	tys   map[semantic.Type]refRel // Delegate on to impls
	impls map[semantic.Type]refRel // Implementations of lowered map types
}

var slicePrototype = &semantic.Slice{}

// declareRefRels declares all the reference type's reference() and release()
// functions.
func (c *C) declareRefRels() {
	c.refRels = refRels{
		tys:   map[semantic.Type]refRel{},
		impls: map[semantic.Type]refRel{},
	}

	sli := refRel{}
	sli.declare(c, "slice", "gapil_slice_reference", "gapil_slice_release", c.T.Sli)
	c.refRels.tys[slicePrototype] = sli
	c.refRels.impls[slicePrototype] = sli

	str := refRel{}
	str.declare(c, "string", "gapil_string_reference", "gapil_string_release", c.T.StrPtr)
	c.refRels.tys[semantic.StringType] = str
	c.refRels.impls[semantic.StringType] = str

	var isRefTy func(ty semantic.Type) bool
	isRefTy = func(ty semantic.Type) bool {
		ty = semantic.Underlying(ty)
		if ty == semantic.StringType {
			return true
		}
		switch ty := ty.(type) {
		case *semantic.Slice, *semantic.Reference, *semantic.Map:
			return true
		case *semantic.Class:
			for _, f := range ty.Fields {
				if isRefTy(f.Type) {
					return true
				}
			}
		}
		return false
	}

	// Forward declare all the reference types.

	// impls is a map of type mangled type name to the public reference and
	// release functions.
	// This is used to deduplicate types that have the same underlying key and
	// value LLVM types when lowered.
	impls := map[string]refRel{}

	for _, api := range c.APIs {
		declare := func(apiTy semantic.Type) {
			cgTy := c.T.Target(apiTy)
			apiTy = semantic.Underlying(apiTy)
			switch apiTy {
			case semantic.StringType:
				// Already implemented
			default:
				switch apiTy := apiTy.(type) {
				case *semantic.Slice:
					c.refRels.tys[apiTy] = sli

				default:
					if isRefTy(apiTy) {
						name := fmt.Sprintf("%v_%v", api.Name(), apiTy.Name())

						// Use the mangled name of the type to determine whether
						// the reference and release functions have already been
						// declared for the lowered type.
						m := c.Mangle(cgTy)
						mangled := c.Mangler(m)
						impl, seen := impls[mangled]
						if !seen {
							// First instance of this lowered type. Declare it.
							ref := c.Mangler(&mangling.Function{
								Name:       "reference",
								Parent:     m.(mangling.Scope),
								Parameters: []mangling.Type{m},
							})
							rel := c.Mangler(&mangling.Function{
								Name:       "release",
								Parent:     m.(mangling.Scope),
								Parameters: []mangling.Type{m},
							})
							impl.declare(c, name, ref, rel, cgTy)
							impls[mangled] = impl
							c.refRels.impls[apiTy] = impl
						}

						// Delegate the reference and release functions of this type
						// on to the common implementation.
						funcs := refRel{}
						funcs.declare(c, name, name+"_reference", name+"_release", cgTy)
						funcs.delegate(c, impl)
						c.refRels.tys[apiTy] = funcs
					}
				}
			}
		}
		for _, ty := range api.Slices {
			declare(ty)
		}
		for _, ty := range api.Maps {
			declare(ty)
		}
		for _, ty := range api.References {
			declare(ty)
		}
		for _, ty := range api.Classes {
			declare(ty)
		}
	}
}

// buildRefRels implements all the reference type's reference() and release()
// functions.
func (c *C) buildRefRels() {
	r := c.refRels.impls

	sli := r[slicePrototype]
	sli.build(c,
		func(s *S, sli *codegen.Value) *codegen.Value {
			poolPtr := sli.Extract(SlicePool)
			return s.Equal(poolPtr, s.Zero(poolPtr.Type()))
		},
		func(s *S, sli *codegen.Value) *codegen.Value {
			poolPtr := sli.Extract(SlicePool)
			return poolPtr.Index(0, PoolRefCount)
		},
		func(s *S, sli *codegen.Value) {
			poolPtr := sli.Extract(SlicePool)
			s.Call(c.callbacks.freePool, poolPtr)
		})

	str := r[semantic.StringType]
	str.build(c,
		func(s *S, strPtr *codegen.Value) *codegen.Value {
			return s.Equal(strPtr, s.Zero(c.T.StrPtr))
		},
		func(s *S, strPtr *codegen.Value) *codegen.Value {
			return strPtr.Index(0, StringRefCount)
		},
		func(s *S, strPtr *codegen.Value) {
			s.Call(c.callbacks.freeString, strPtr)
		})

	for apiTy, funcs := range r {
		switch apiTy {
		case semantic.StringType:
			// Already implemented

		default:
			switch apiTy := apiTy.(type) {
			case *semantic.Slice:
				// Already implemented

			case *semantic.Reference:
				funcs.build(c,
					func(s *S, refPtr *codegen.Value) *codegen.Value {
						return refPtr.IsNull()
					},
					func(s *S, refPtr *codegen.Value) *codegen.Value {
						return refPtr.Index(0, RefRefCount)
					},
					func(s *S, refPtr *codegen.Value) {
						s.Arena = refPtr.Index(0, RefArena).Load().SetName("arena")
						c.release(s, refPtr.Index(0, RefValue).Load(), apiTy.To)
						c.Free(s, refPtr)
					})

			case *semantic.Map:
				funcs.build(c,
					func(s *S, mapPtr *codegen.Value) *codegen.Value {
						return mapPtr.IsNull()
					},
					func(s *S, mapPtr *codegen.Value) *codegen.Value {
						return mapPtr.Index(0, MapRefCount)
					},
					func(s *S, mapPtr *codegen.Value) {
						s.Arena = mapPtr.Index(0, MapArena).Load().SetName("arena")
						s.Call(c.T.Maps[apiTy].Clear, mapPtr)
						c.Free(s, mapPtr)
					})

			case *semantic.Class:
				refFields := []*semantic.Field{}
				for _, f := range apiTy.Fields {
					ty := semantic.Underlying(f.Type)
					if _, ok := c.refRels.tys[ty]; ok {
						refFields = append(refFields, f)
					}
				}

				c.Build(funcs.reference, func(s *S) {
					val := s.Parameter(0)
					for _, f := range refFields {
						c.reference(s, val.Extract(f.Name()), f.Type)
					}
				})
				c.Build(funcs.release, func(s *S) {
					val := s.Parameter(0)
					for _, f := range refFields {
						c.release(s, val.Extract(f.Name()), f.Type)
					}
				})
			default:
				fail("Unhandled reference type %T", apiTy)
			}
		}
	}
}

func (c *C) reference(s *S, val *codegen.Value, ty semantic.Type) {
	if f, ok := c.refRels.tys[semantic.Underlying(ty)]; ok {
		s.Call(f.reference, val)
	}
}

func (c *C) release(s *S, val *codegen.Value, ty semantic.Type) {
	if f, ok := c.refRels.tys[semantic.Underlying(ty)]; ok {
		s.Call(f.release, val)
	}
}

func (c *C) deferRelease(s *S, val *codegen.Value, ty semantic.Type) {
	if debugRefCounts {
		c.LogI(s, "deferRelease("+fmt.Sprintf("%T", ty)+": %p)", val)
	}
	s.onExit(func() {
		if s.IsBlockTerminated() {
			// The last instruction written to the current block was a
			// terminator instruction. This should only happen if we've emitted
			// a return statement and the scopes around this statement are
			// closing. The logic in Scope.Return() will have already exited
			// all the contexts, so we can safely return here.
			//
			// TODO: This is really icky - more time should be spent thinking
			// of ways to avoid special casing return statements like this.
			return
		}
		c.release(s, val, ty)
	})
}

func (c *C) isRefCounted(ty semantic.Type) bool {
	_, ok := c.refRels.tys[semantic.Underlying(ty)]
	return ok
}
<commit_msg>gapil/compiler: Include object pointers in refcount errors<commit_after>// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package compiler

import (
	"fmt"

	"github.com/google/gapid/core/codegen"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/gapil/compiler/mangling"
	"github.com/google/gapid/gapil/semantic"
)

type refRel struct {
	name      string
	reference *codegen.Function // void T_reference(T)
	release   *codegen.Function // void T_release(T)
}

func (f *refRel) declare(c *C, name, ref, rel string, ty codegen.Type) {
	f.reference = c.M.Function(c.T.Void, ref, ty).LinkOnceODR().Inline()
	f.release = c.M.Function(c.T.Void, rel, ty).LinkOnceODR().Inline()
	f.name = name
}

func (f *refRel) delegate(c *C, to refRel) {
	c.Delegate(f.reference, to.reference)
	c.Delegate(f.release, to.release)
}

func (f *refRel) build(
	c *C,
	isNull func(s *S, val *codegen.Value) *codegen.Value,
	getRefPtr func(s *S, val *codegen.Value) *codegen.Value,
	del func(s *S, val *codegen.Value),
) {
	c.Build(f.reference, func(s *S) {
		val := s.Parameter(0)
		s.If(isNull(s, val), func(s *S) {
			s.Return(nil)
		})
		refPtr := getRefPtr(s, val)
		oldCount := refPtr.Load()
		s.If(s.Equal(oldCount, s.Scalar(uint32(0))), func(s *S) {
			c.Log(s, log.Fatal, "Attempting to reference released "+f.name+" (%p)", refPtr)
		})
		newCount := s.Add(oldCount, s.Scalar(uint32(1)))
		if debugRefCounts {
			c.LogI(s, f.name+" %p ref_count: %d -> %d", refPtr, oldCount, newCount)
		}
		refPtr.Store(newCount)
	})

	c.Build(f.release, func(s *S) {
		val := s.Parameter(0)
		s.If(isNull(s, val), func(s *S) {
			s.Return(nil)
		})
		refPtr := getRefPtr(s, val)
		oldCount := refPtr.Load()
		s.If(s.Equal(oldCount, s.Scalar(uint32(0))), func(s *S) {
			c.Log(s, log.Fatal, "Attempting to release "+f.name+" with no remaining references! (%p)", refPtr)
		})
		newCount := s.Sub(oldCount, s.Scalar(uint32(1)))
		if debugRefCounts {
			c.LogI(s, f.name+" %p ref_count: %d -> %d", refPtr, oldCount, newCount)
		}
		refPtr.Store(newCount)
		s.If(s.Equal(newCount, s.Scalar(uint32(0))), func(s *S) {
			del(s, val)
		})
	})
}

type refRels struct {
	tys   map[semantic.Type]refRel // Delegate on to impls
	impls map[semantic.Type]refRel // Implementations of lowered map types
}

var slicePrototype = &semantic.Slice{}

// declareRefRels declares all the reference type's reference() and release()
// functions.
func (c *C) declareRefRels() {
	c.refRels = refRels{
		tys:   map[semantic.Type]refRel{},
		impls: map[semantic.Type]refRel{},
	}

	sli := refRel{}
	sli.declare(c, "slice", "gapil_slice_reference", "gapil_slice_release", c.T.Sli)
	c.refRels.tys[slicePrototype] = sli
	c.refRels.impls[slicePrototype] = sli

	str := refRel{}
	str.declare(c, "string", "gapil_string_reference", "gapil_string_release", c.T.StrPtr)
	c.refRels.tys[semantic.StringType] = str
	c.refRels.impls[semantic.StringType] = str

	var isRefTy func(ty semantic.Type) bool
	isRefTy = func(ty semantic.Type) bool {
		ty = semantic.Underlying(ty)
		if ty == semantic.StringType {
			return true
		}
		switch ty := ty.(type) {
		case *semantic.Slice, *semantic.Reference, *semantic.Map:
			return true
		case *semantic.Class:
			for _, f := range ty.Fields {
				if isRefTy(f.Type) {
					return true
				}
			}
		}
		return false
	}

	// Forward declare all the reference types.

	// impls is a map of type mangled type name to the public reference and
	// release functions.
	// This is used to deduplicate types that have the same underlying key and
	// value LLVM types when lowered.
	impls := map[string]refRel{}

	for _, api := range c.APIs {
		declare := func(apiTy semantic.Type) {
			cgTy := c.T.Target(apiTy)
			apiTy = semantic.Underlying(apiTy)
			switch apiTy {
			case semantic.StringType:
				// Already implemented
			default:
				switch apiTy := apiTy.(type) {
				case *semantic.Slice:
					c.refRels.tys[apiTy] = sli

				default:
					if isRefTy(apiTy) {
						name := fmt.Sprintf("%v_%v", api.Name(), apiTy.Name())

						// Use the mangled name of the type to determine whether
						// the reference and release functions have already been
						// declared for the lowered type.
						m := c.Mangle(cgTy)
						mangled := c.Mangler(m)
						impl, seen := impls[mangled]
						if !seen {
							// First instance of this lowered type. Declare it.
							ref := c.Mangler(&mangling.Function{
								Name:       "reference",
								Parent:     m.(mangling.Scope),
								Parameters: []mangling.Type{m},
							})
							rel := c.Mangler(&mangling.Function{
								Name:       "release",
								Parent:     m.(mangling.Scope),
								Parameters: []mangling.Type{m},
							})
							impl.declare(c, name, ref, rel, cgTy)
							impls[mangled] = impl
							c.refRels.impls[apiTy] = impl
						}

						// Delegate the reference and release functions of this type
						// on to the common implementation.
						funcs := refRel{}
						funcs.declare(c, name, name+"_reference", name+"_release", cgTy)
						funcs.delegate(c, impl)
						c.refRels.tys[apiTy] = funcs
					}
				}
			}
		}
		for _, ty := range api.Slices {
			declare(ty)
		}
		for _, ty := range api.Maps {
			declare(ty)
		}
		for _, ty := range api.References {
			declare(ty)
		}
		for _, ty := range api.Classes {
			declare(ty)
		}
	}
}

// buildRefRels implements all the reference type's reference() and release()
// functions.
func (c *C) buildRefRels() {
	r := c.refRels.impls

	sli := r[slicePrototype]
	sli.build(c,
		func(s *S, sli *codegen.Value) *codegen.Value {
			poolPtr := sli.Extract(SlicePool)
			return s.Equal(poolPtr, s.Zero(poolPtr.Type()))
		},
		func(s *S, sli *codegen.Value) *codegen.Value {
			poolPtr := sli.Extract(SlicePool)
			return poolPtr.Index(0, PoolRefCount)
		},
		func(s *S, sli *codegen.Value) {
			poolPtr := sli.Extract(SlicePool)
			s.Call(c.callbacks.freePool, poolPtr)
		})

	str := r[semantic.StringType]
	str.build(c,
		func(s *S, strPtr *codegen.Value) *codegen.Value {
			return s.Equal(strPtr, s.Zero(c.T.StrPtr))
		},
		func(s *S, strPtr *codegen.Value) *codegen.Value {
			return strPtr.Index(0, StringRefCount)
		},
		func(s *S, strPtr *codegen.Value) {
			s.Call(c.callbacks.freeString, strPtr)
		})

	for apiTy, funcs := range r {
		switch apiTy {
		case semantic.StringType:
			// Already implemented

		default:
			switch apiTy := apiTy.(type) {
			case *semantic.Slice:
				// Already implemented

			case *semantic.Reference:
				funcs.build(c,
					func(s *S, refPtr *codegen.Value) *codegen.Value {
						return refPtr.IsNull()
					},
					func(s *S, refPtr *codegen.Value) *codegen.Value {
						return refPtr.Index(0, RefRefCount)
					},
					func(s *S, refPtr *codegen.Value) {
						s.Arena = refPtr.Index(0, RefArena).Load().SetName("arena")
						c.release(s, refPtr.Index(0, RefValue).Load(), apiTy.To)
						c.Free(s, refPtr)
					})

			case *semantic.Map:
				funcs.build(c,
					func(s *S, mapPtr *codegen.Value) *codegen.Value {
						return mapPtr.IsNull()
					},
					func(s *S, mapPtr *codegen.Value) *codegen.Value {
						return mapPtr.Index(0, MapRefCount)
					},
					func(s *S, mapPtr *codegen.Value) {
						s.Arena = mapPtr.Index(0, MapArena).Load().SetName("arena")
						s.Call(c.T.Maps[apiTy].Clear, mapPtr)
						c.Free(s, mapPtr)
					})

			case *semantic.Class:
				refFields := []*semantic.Field{}
				for _, f := range apiTy.Fields {
					ty := semantic.Underlying(f.Type)
					if _, ok := c.refRels.tys[ty]; ok {
						refFields = append(refFields, f)
					}
				}

				c.Build(funcs.reference, func(s *S) {
					val := s.Parameter(0)
					for _, f := range refFields {
						c.reference(s, val.Extract(f.Name()), f.Type)
					}
				})
				c.Build(funcs.release, func(s *S) {
					val := s.Parameter(0)
					for _, f := range refFields {
						c.release(s, val.Extract(f.Name()), f.Type)
					}
				})
			default:
				fail("Unhandled reference type %T", apiTy)
			}
		}
	}
}

func (c *C) reference(s *S, val *codegen.Value, ty semantic.Type) {
	if f, ok := c.refRels.tys[semantic.Underlying(ty)]; ok {
		s.Call(f.reference, val)
	}
}

func (c *C) release(s *S, val *codegen.Value, ty semantic.Type) {
	if f, ok := c.refRels.tys[semantic.Underlying(ty)]; ok {
		s.Call(f.release, val)
	}
}

func (c *C) deferRelease(s *S, val *codegen.Value, ty semantic.Type) {
	if debugRefCounts {
		c.LogI(s, "deferRelease("+fmt.Sprintf("%T", ty)+": %p)", val)
	}
	s.onExit(func() {
		if s.IsBlockTerminated() {
			// The last instruction written to the current block was a
			// terminator instruction. This should only happen if we've emitted
			// a return statement and the scopes around this statement are
			// closing. The logic in Scope.Return() will have already exited
			// all the contexts, so we can safely return here.
			//
			// TODO: This is really icky - more time should be spent thinking
			// of ways to avoid special casing return statements like this.
			return
		}
		c.release(s, val, ty)
	})
}

func (c *C) isRefCounted(ty semantic.Type) bool {
	_, ok := c.refRels.tys[semantic.Underlying(ty)]
	return ok
}
<|endoftext|>