<commit_before>package multiplex

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	mpool "github.com/jbenet/go-msgio/mpool"
)

var MaxMessageSize = 1 << 20

const (
	NewStream = iota
	Receiver
	Initiator
	Unknown
	Close
)

type Stream struct {
	id     uint64
	name   string
	header uint64
	dataIn chan []byte
	mp     *Multiplex

	extra []byte

	// exbuf is for holding the reference to the beginning of the extra slice
	// for later memory pool freeing
	exbuf []byte

	wDeadline time.Time
	rDeadline time.Time

	clLock sync.Mutex
	closed bool
	clCh   chan struct{}
}

func (mp *Multiplex) newStream(id uint64, name string, initiator bool) *Stream {
	var hfn uint64
	if initiator {
		hfn = 2
	} else {
		hfn = 1
	}
	return &Stream{
		id:     id,
		name:   name,
		header: (id << 3) | hfn,
		dataIn: make(chan []byte, 8),
		clCh:   make(chan struct{}),
		mp:     mp,
	}
}

func (s *Stream) Name() string {
	return s.name
}

func (s *Stream) receive(b []byte) {
	select {
	case s.dataIn <- b:
	case <-s.clCh:
	}
}

func (m *Multiplex) Accept() (*Stream, error) {
	select {
	case s, ok := <-m.nstreams:
		if !ok {
			return nil, errors.New("multiplex closed")
		}
		return s, nil
	case err := <-m.errs:
		return nil, err
	case <-m.closed:
		return nil, errors.New("multiplex closed")
	}
}

func (s *Stream) waitForData(ctx context.Context) error {
	if !s.rDeadline.IsZero() {
		dctx, cancel := context.WithDeadline(ctx, s.rDeadline)
		defer cancel()
		ctx = dctx
	}
	select {
	case read, ok := <-s.dataIn:
		if !ok {
			return io.EOF
		}
		s.extra = read
		s.exbuf = read
		return nil
	case <-s.clCh:
		return io.EOF
	case <-ctx.Done():
		return ctx.Err()
	}
}

func (s *Stream) Read(b []byte) (int, error) {
	if s.extra == nil {
		err := s.waitForData(context.Background())
		if err != nil {
			return 0, err
		}
	}
	n := copy(b, s.extra)
	if n < len(s.extra) {
		s.extra = s.extra[n:]
	} else {
		if s.exbuf != nil {
			mpool.ByteSlicePool.Put(uint32(cap(s.exbuf)), s.exbuf)
		}
		s.extra = nil
		s.exbuf = nil
	}
	return n, nil
}

func (s *Stream) Write(b []byte) (int, error) {
	if s.isClosed() {
		return 0, fmt.Errorf("cannot write to closed stream")
	}

	err := s.mp.sendMsg(s.header, b, s.wDeadline)
	if err != nil {
		return 0, err
	}

	return len(b), nil
}

func (s *Stream) isClosed() bool {
	s.clLock.Lock()
	defer s.clLock.Unlock()
	return s.closed
}

func (s *Stream) Close() error {
	s.clLock.Lock()
	defer s.clLock.Unlock()
	if s.closed {
		return nil
	}

	s.closed = true
	close(s.clCh)
	return s.mp.sendMsg((s.id<<3)|Close, nil, time.Time{})
}

func (s *Stream) SetDeadline(t time.Time) error {
	s.rDeadline = t
	s.wDeadline = t
	return nil
}

func (s *Stream) SetReadDeadline(t time.Time) error {
	s.rDeadline = t
	return nil
}

func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.wDeadline = t
	return nil
}

type Multiplex struct {
	con       net.Conn
	buf       *bufio.Reader
	nextID    uint64
	closed    chan struct{}
	initiator bool

	wrLock sync.Mutex

	nstreams chan *Stream
	errs     chan error

	hdrBuf []byte

	channels map[uint64]*Stream
	chLock   sync.Mutex
}

func NewMultiplex(con net.Conn, initiator bool) *Multiplex {
	mp := &Multiplex{
		con:       con,
		initiator: initiator,
		buf:       bufio.NewReader(con),
		channels:  make(map[uint64]*Stream),
		closed:    make(chan struct{}),
		nstreams:  make(chan *Stream, 16),
		errs:      make(chan error),
		hdrBuf:    make([]byte, 20),
	}

	go mp.handleIncoming()

	return mp
}

func (mp *Multiplex) Close() error {
	if mp.IsClosed() {
		return nil
	}
	close(mp.closed)
	mp.chLock.Lock()
	defer mp.chLock.Unlock()
	for _, s := range mp.channels {
		err := s.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

func (mp *Multiplex) IsClosed() bool {
	select {
	case <-mp.closed:
		return true
	default:
		return false
	}
}

func (mp *Multiplex) sendMsg(header uint64, data []byte, dl time.Time) error {
	mp.wrLock.Lock()
	defer mp.wrLock.Unlock()
	if !dl.IsZero() {
		if err := mp.con.SetWriteDeadline(dl); err != nil {
			return err
		}
	}
	n := EncodeVarint(mp.hdrBuf, header)
	n += EncodeVarint(mp.hdrBuf[n:], uint64(len(data)))
	_, err := mp.con.Write(mp.hdrBuf[:n])
	if err != nil {
		return err
	}

	if len(data) != 0 {
		_, err = mp.con.Write(data)
		if err != nil {
			return err
		}
	}
	if !dl.IsZero() {
		if err := mp.con.SetWriteDeadline(time.Time{}); err != nil {
			return err
		}
	}

	return nil
}

func (mp *Multiplex) nextChanID() (out uint64) {
	if mp.initiator {
		out = mp.nextID + 1
	} else {
		out = mp.nextID
	}
	mp.nextID += 2
	return
}

func (mp *Multiplex) NewStream() (*Stream, error) {
	return mp.NewNamedStream("")
}

func (mp *Multiplex) NewNamedStream(name string) (*Stream, error) {
	mp.chLock.Lock()
	sid := mp.nextChanID()
	header := (sid << 3) | NewStream

	if name == "" {
		name = fmt.Sprint(sid)
	}
	s := mp.newStream(sid, name, true)
	mp.channels[sid] = s
	mp.chLock.Unlock()

	err := mp.sendMsg(header, []byte(name), time.Time{})
	if err != nil {
		return nil, err
	}

	return s, nil
}

func (mp *Multiplex) sendErr(err error) {
	select {
	case mp.errs <- err:
	case <-mp.closed:
	}
}

func (mp *Multiplex) handleIncoming() {
	defer mp.shutdown()
	for {
		ch, tag, err := mp.readNextHeader()
		if err != nil {
			mp.sendErr(err)
			return
		}

		b, err := mp.readNext()
		if err != nil {
			mp.sendErr(err)
			return
		}

		mp.chLock.Lock()
		msch, ok := mp.channels[ch]
		mp.chLock.Unlock()
		if !ok {
			var name string
			if tag == NewStream {
				name = string(b)
			}
			msch = mp.newStream(ch, name, false)
			mp.channels[ch] = msch
			select {
			case mp.nstreams <- msch:
			case <-mp.closed:
				return
			}
			if tag == NewStream {
				continue
			}
		}

		if tag == Close {
			close(msch.dataIn)
			mp.chLock.Lock()
			delete(mp.channels, ch)
			mp.chLock.Unlock()
			continue
		}

		msch.receive(b)
	}
}

func (mp *Multiplex) shutdown() {
	mp.chLock.Lock()
	defer mp.chLock.Unlock()
	for _, s := range mp.channels {
		s.Close()
	}
}

func (mp *Multiplex) readNextHeader() (uint64, uint64, error) {
	h, _, err := DecodeVarint(mp.buf)
	if err != nil {
		return 0, 0, err
	}

	// get channel ID
	ch := h >> 3

	rem := h & 7

	return ch, rem, nil
}

func (mp *Multiplex) readNext() ([]byte, error) {
	// get length
	l, _, err := DecodeVarint(mp.buf)
	if err != nil {
		return nil, err
	}

	if l > uint64(MaxMessageSize) {
		return nil, fmt.Errorf("message size too large!")
	}

	if l == 0 {
		return nil, nil
	}

	buf := mpool.ByteSlicePool.Get(uint32(l)).([]byte)[:l]
	n, err := io.ReadFull(mp.buf, buf)
	if err != nil {
		return nil, err
	}

	return buf[:n], nil
}

func EncodeVarint(buf []byte, x uint64) int {
	var n int
	for n = 0; x > 127; n++ {
		buf[n] = 0x80 | uint8(x&0x7F)
		x >>= 7
	}
	buf[n] = uint8(x)
	n++
	return n
}

func DecodeVarint(r *bufio.Reader) (x uint64, n int, err error) {
	// x, n already 0
	for shift := uint(0); shift < 64; shift += 7 {
		val, err := r.ReadByte()
		if err != nil {
			return 0, 0, err
		}

		b := uint64(val)
		n++
		x |= (b & 0x7F) << shift
		if (b & 0x80) == 0 {
			return x, n, nil
		}
	}

	// The number is too large to represent in a 64-bit value.
	return 0, 0, errors.New("Too large of a number!")
}
<commit_msg>use varint from stdlib<commit_after>package multiplex

import (
	"bufio"
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"time"

	mpool "github.com/jbenet/go-msgio/mpool"
)

var MaxMessageSize = 1 << 20

const (
	NewStream = iota
	Receiver
	Initiator
	Unknown
	Close
)

type Stream struct {
	id     uint64
	name   string
	header uint64
	dataIn chan []byte
	mp     *Multiplex

	extra []byte

	// exbuf is for holding the reference to the beginning of the extra slice
	// for later memory pool freeing
	exbuf []byte

	wDeadline time.Time
	rDeadline time.Time

	clLock sync.Mutex
	closed bool
	clCh   chan struct{}
}

func (mp *Multiplex) newStream(id uint64, name string, initiator bool) *Stream {
	var hfn uint64
	if initiator {
		hfn = 2
	} else {
		hfn = 1
	}
	return &Stream{
		id:     id,
		name:   name,
		header: (id << 3) | hfn,
		dataIn: make(chan []byte, 8),
		clCh:   make(chan struct{}),
		mp:     mp,
	}
}

func (s *Stream) Name() string {
	return s.name
}

func (s *Stream) receive(b []byte) {
	select {
	case s.dataIn <- b:
	case <-s.clCh:
	}
}

func (m *Multiplex) Accept() (*Stream, error) {
	select {
	case s, ok := <-m.nstreams:
		if !ok {
			return nil, errors.New("multiplex closed")
		}
		return s, nil
	case err := <-m.errs:
		return nil, err
	case <-m.closed:
		return nil, errors.New("multiplex closed")
	}
}

func (s *Stream) waitForData(ctx context.Context) error {
	if !s.rDeadline.IsZero() {
		dctx, cancel := context.WithDeadline(ctx, s.rDeadline)
		defer cancel()
		ctx = dctx
	}
	select {
	case read, ok := <-s.dataIn:
		if !ok {
			return io.EOF
		}
		s.extra = read
		s.exbuf = read
		return nil
	case <-s.clCh:
		return io.EOF
	case <-ctx.Done():
		return ctx.Err()
	}
}

func (s *Stream) Read(b []byte) (int, error) {
	if s.extra == nil {
		err := s.waitForData(context.Background())
		if err != nil {
			return 0, err
		}
	}
	n := copy(b, s.extra)
	if n < len(s.extra) {
		s.extra = s.extra[n:]
	} else {
		if s.exbuf != nil {
			mpool.ByteSlicePool.Put(uint32(cap(s.exbuf)), s.exbuf)
		}
		s.extra = nil
		s.exbuf = nil
	}
	return n, nil
}

func (s *Stream) Write(b []byte) (int, error) {
	if s.isClosed() {
		return 0, fmt.Errorf("cannot write to closed stream")
	}

	err := s.mp.sendMsg(s.header, b, s.wDeadline)
	if err != nil {
		return 0, err
	}

	return len(b), nil
}

func (s *Stream) isClosed() bool {
	s.clLock.Lock()
	defer s.clLock.Unlock()
	return s.closed
}

func (s *Stream) Close() error {
	s.clLock.Lock()
	defer s.clLock.Unlock()
	if s.closed {
		return nil
	}

	s.closed = true
	close(s.clCh)
	return s.mp.sendMsg((s.id<<3)|Close, nil, time.Time{})
}

func (s *Stream) SetDeadline(t time.Time) error {
	s.rDeadline = t
	s.wDeadline = t
	return nil
}

func (s *Stream) SetReadDeadline(t time.Time) error {
	s.rDeadline = t
	return nil
}

func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.wDeadline = t
	return nil
}

type Multiplex struct {
	con       net.Conn
	buf       *bufio.Reader
	nextID    uint64
	closed    chan struct{}
	initiator bool

	wrLock sync.Mutex

	nstreams chan *Stream
	errs     chan error

	hdrBuf []byte

	channels map[uint64]*Stream
	chLock   sync.Mutex
}

func NewMultiplex(con net.Conn, initiator bool) *Multiplex {
	mp := &Multiplex{
		con:       con,
		initiator: initiator,
		buf:       bufio.NewReader(con),
		channels:  make(map[uint64]*Stream),
		closed:    make(chan struct{}),
		nstreams:  make(chan *Stream, 16),
		errs:      make(chan error),
		hdrBuf:    make([]byte, 20),
	}

	go mp.handleIncoming()

	return mp
}

func (mp *Multiplex) Close() error {
	if mp.IsClosed() {
		return nil
	}
	close(mp.closed)
	mp.chLock.Lock()
	defer mp.chLock.Unlock()
	for _, s := range mp.channels {
		err := s.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

func (mp *Multiplex) IsClosed() bool {
	select {
	case <-mp.closed:
		return true
	default:
		return false
	}
}

func (mp *Multiplex) sendMsg(header uint64, data []byte, dl time.Time) error {
	mp.wrLock.Lock()
	defer mp.wrLock.Unlock()
	if !dl.IsZero() {
		if err := mp.con.SetWriteDeadline(dl); err != nil {
			return err
		}
	}
	n := binary.PutUvarint(mp.hdrBuf, header)
	n += binary.PutUvarint(mp.hdrBuf[n:], uint64(len(data)))
	_, err := mp.con.Write(mp.hdrBuf[:n])
	if err != nil {
		return err
	}

	if len(data) != 0 {
		_, err = mp.con.Write(data)
		if err != nil {
			return err
		}
	}
	if !dl.IsZero() {
		if err := mp.con.SetWriteDeadline(time.Time{}); err != nil {
			return err
		}
	}

	return nil
}

func (mp *Multiplex) nextChanID() (out uint64) {
	if mp.initiator {
		out = mp.nextID + 1
	} else {
		out = mp.nextID
	}
	mp.nextID += 2
	return
}

func (mp *Multiplex) NewStream() (*Stream, error) {
	return mp.NewNamedStream("")
}

func (mp *Multiplex) NewNamedStream(name string) (*Stream, error) {
	mp.chLock.Lock()
	sid := mp.nextChanID()
	header := (sid << 3) | NewStream

	if name == "" {
		name = fmt.Sprint(sid)
	}
	s := mp.newStream(sid, name, true)
	mp.channels[sid] = s
	mp.chLock.Unlock()

	err := mp.sendMsg(header, []byte(name), time.Time{})
	if err != nil {
		return nil, err
	}

	return s, nil
}

func (mp *Multiplex) sendErr(err error) {
	select {
	case mp.errs <- err:
	case <-mp.closed:
	}
}

func (mp *Multiplex) handleIncoming() {
	defer mp.shutdown()
	for {
		ch, tag, err := mp.readNextHeader()
		if err != nil {
			mp.sendErr(err)
			return
		}

		b, err := mp.readNext()
		if err != nil {
			mp.sendErr(err)
			return
		}

		mp.chLock.Lock()
		msch, ok := mp.channels[ch]
		mp.chLock.Unlock()
		if !ok {
			var name string
			if tag == NewStream {
				name = string(b)
			}
			msch = mp.newStream(ch, name, false)
			mp.channels[ch] = msch
			select {
			case mp.nstreams <- msch:
			case <-mp.closed:
				return
			}
			if tag == NewStream {
				continue
			}
		}

		if tag == Close {
			close(msch.dataIn)
			mp.chLock.Lock()
			delete(mp.channels, ch)
			mp.chLock.Unlock()
			continue
		}

		msch.receive(b)
	}
}

func (mp *Multiplex) shutdown() {
	mp.chLock.Lock()
	defer mp.chLock.Unlock()
	for _, s := range mp.channels {
		s.Close()
	}
}

func (mp *Multiplex) readNextHeader() (uint64, uint64, error) {
	h, err := binary.ReadUvarint(mp.buf)
	if err != nil {
		return 0, 0, err
	}

	// get channel ID
	ch := h >> 3

	rem := h & 7

	return ch, rem, nil
}

func (mp *Multiplex) readNext() ([]byte, error) {
	// get length
	l, err := binary.ReadUvarint(mp.buf)
	if err != nil {
		return nil, err
	}

	if l > uint64(MaxMessageSize) {
		return nil, fmt.Errorf("message size too large!")
	}

	if l == 0 {
		return nil, nil
	}

	buf := mpool.ByteSlicePool.Get(uint32(l)).([]byte)[:l]
	n, err := io.ReadFull(mp.buf, buf)
	if err != nil {
		return nil, err
	}

	return buf[:n], nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/nlopes/slack"
	"os"
	"time"
)

var apiKey string = ""

func sendMessage(api slack.Client, channel string, message string) error {
	params := slack.PostMessageParameters{}
	_, _, err := api.PostMessage(channel, message, params)
	return err
}

func annoyUser(api slack.Client, user string) error {
	message := "You have been identied as a user that does not have 2 Factor Auth (2FA).\n\n" +
		"To you Slack you must have 2FA enabled as per the requirements and terms of use.  You will be reminded every 24 hours until you enabled 2FA.\n\n" +
		"Every so often we will disable accounts that do not have 2FA turned on.  To avoid this please turn on 2FA now.  Instructions for 2FA can be found at https://slack.zendesk.com/hc/en-us/articles/204509068-Enabling-two-factor-authentication."

	err := sendMessage(api, "@"+user, message)
	return err
}

func getUsers(api slack.Client) error {
	users, err := api.GetUsers()
	if err != nil {
		return err
	}

	for _, member := range users {
		if member.Has2FA == false && member.Deleted == false && member.IsBot == false {
			if err := annoyUser(api, member.Name); err != nil {
				return err
			}
			fmt.Printf("%s\n", member.Name)
		}
	}
	return nil
}

func main() {

	apiKey = os.Getenv("SLACK_API_KEY")
	if apiKey == "" {
		println("You must set the enviroment variable SLACK_API_KEY")
		os.Exit(-1)
	}

	api := slack.New(apiKey)

	for {
		if err := getUsers(*api); err != nil {
			fmt.Printf("Error getting users and annoying them %s\n", err)
		}
		fmt.Printf("Running again in 24 hours...")
		time.Sleep(time.Hour * 24)
	}

}
<commit_msg>adds public shaming<commit_after>package main

import (
	"fmt"
	"github.com/nlopes/slack"
	"os"
	"time"
)

var apiKey string = ""

var slackPolicy = "To you Slack you must have 2FA enabled as per the requirements and terms of use.  You will be reminded every 24 hours until you enabled 2FA.\n\n" +
	"Every so often we will disable accounts that do not have 2FA turned on.  To avoid this please turn on 2FA now.  Instructions for 2FA can be found at https://slack.zendesk.com/hc/en-us/articles/204509068-Enabling-two-factor-authentication."

func sendMessage(api slack.Client, channel string, message string) error {
	params := slack.PostMessageParameters{}
	params.LinkNames = 1
	_, _, err := api.PostMessage(channel, message, params)
	return err
}

func annoyUser(api slack.Client, user string) error {
	message := "You have been identied as a user that does not have 2 Factor Auth (2FA).\n\n" + slackPolicy

	err := sendMessage(api, "@"+user, message)
	return err
}

func shameUsers(api slack.Client, userString string) error {
	message := "Hello everyone...\n\n" +
		"The following users have not enabled 2 Factor auth " + userString + "\n\n" +
		slackPolicy

	err := sendMessage(api, "#general", message)
	return err
}

func getUsers(api slack.Client) error {
	users, err := api.GetUsers()
	if err != nil {
		return err
	}

	var userList = ""

	for _, member := range users {
		if member.Has2FA == false && member.Deleted == false && member.IsBot == false {
			if err := annoyUser(api, member.Name); err != nil {
				return err
			}
			userList += "@" + member.Name + " "
			fmt.Printf("%s\n", member.Name)
		}
	}

	if err := shameUsers(api, userList); err != nil {
		return err
	}

	return nil
}

func main() {

	apiKey = os.Getenv("SLACK_API_KEY")
	if apiKey == "" {
		println("You must set the enviroment variable SLACK_API_KEY")
		os.Exit(-1)
	}

	api := slack.New(apiKey)

	for {
		if err := getUsers(*api); err != nil {
			fmt.Printf("Error getting users and annoying them %s\n", err)
		}
		fmt.Printf("Running again in 24 hours...")
		time.Sleep(time.Hour * 24)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"log"

	"github.com/robfig/cron"
)

var (
	metricsToSave = []Metric{&Cloudwatch{"NetworkOut"}}
)

func main() {
	c := cron.New()

	// queue to get metrics at top of every hour; uses redis set to queue
	// the usernames so multiple workers don't queue the same usernames.
	c.AddFunc("@hourly", func() {
		err := queueUsernamesForMetricGet()
		if err != nil {
			log.Fatal(err)
		}
	})

	// get and save metrics at 5th and 35th minutes of every hour; the
	// reason for 5th minute, is to not queue and pop at the same time.
	c.AddFunc("0 5-59/30 * * * *", func() {
		err := getAndSaveQueueMachineMetrics()
		if err != nil {
			log.Fatal(err)
		}
	})

	// stop machines overlimit at 10th and 40th minutes of every hour;
	// there's no reason for running it at a certain point of the hour,
	// there's no overlap for the logs to not overlap.
	c.AddFunc("0 10-59/35 * * * *", func() {
		err := stopMachinesOverLimit()
		if err != nil {
			log.Fatal(err)
		}
	})

	c.Start()

	// run forever
	select {}
}
<commit_msg>vmwatcher: fix cron schedules<commit_after>package main

import (
	"log"

	"github.com/robfig/cron"
)

var (
	metricsToSave = []Metric{&Cloudwatch{"NetworkOut"}}
)

func main() {
	c := cron.New()

	// queue to get metrics at top of every hour; uses redis set to queue
	// the usernames so multiple workers don't queue the same usernames.
	c.AddFunc("@hourly", func() {
		err := queueUsernamesForMetricGet()
		if err != nil {
			log.Fatal(err)
		}
	})

	// get and save metrics at 5th and 35th minutes of every hour; the
	// reason for 5th minute, is to not queue and pop at the same time.
	c.AddFunc("0 15 * * * *", func() {
		err := getAndSaveQueueMachineMetrics()
		if err != nil {
			log.Fatal(err)
		}
	})

	// stop machines overlimit at 10th and 40th minutes of every hour;
	// there's no reason for running it at a certain point of the hour,
	// there's no overlap for the logs to not overlap.
	c.AddFunc("0 0,30 * * * *", func() {
		err := stopMachinesOverLimit()
		if err != nil {
			log.Fatal(err)
		}
	})

	c.Start()

	// run forever
	select {}
}
<|endoftext|>


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

package snetutil

import (
	"net"
	"time"
	"fmt"
	"bytes"
	"io/ioutil"
	"encoding/binary"
	"strings"
	"errors"
	"net/http"
)

func GetInterIp() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		//fmt.Printf("Inter %v\n", addr)
		ip := addr.String()
		if "10." == ip[:3] {
			return strings.Split(ip, "/")[0], nil
		} else if "172." == ip[:4] {
			return strings.Split(ip, "/")[0], nil
		} else if "196." == ip[:4] {
			return strings.Split(ip, "/")[0], nil
		} else if "192." == ip[:4] {
			return strings.Split(ip, "/")[0], nil
		}

	}

	return "", errors.New("no inter ip")
}

// 获取首个外网ip v4
func GetExterIp() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		//fmt.Printf("Inter %v\n", addr)
		ips := addr.String()
		idx := strings.LastIndex(ips, "/")
		if idx == -1 {
                        continue
		}
		ipv := net.ParseIP(ips[:idx])
		if ipv == nil {
                        continue
		}

		ipv4 := ipv.To4()
		if ipv4 == nil {
                        // ipv6
                        continue
		}
		ip := ipv4.String()

		if "10." != ip[:3] && "172." != ip[:4] && "196." != ip[:4] && "127." != ip[:4] {
			return ip, nil
		}

	}

	return "", errors.New("no exter ip")
}

// 不指定host使用内网host
// 指定了就使用指定的，不管指定的是0.0.0.0还是内网或者外网
func GetListenAddr(a string) (string, error) {

	addrTcp, err := net.ResolveTCPAddr("tcp", a)
	if err != nil {
		return "", err
	}

	addr := addrTcp.String()
	host, _, err := net.SplitHostPort(addr)
	if err != nil {
		return "", err
	}

	if len(host) == 0 {
		return GetServAddr(addrTcp)
	}

	return addr, nil

}

func GetServAddr(a net.Addr) (string, error) {
	addr := a.String()
	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		return "", err
	}
	if len(host) == 0 {
		host = "0.0.0.0"
	}

	ip := net.ParseIP(host)

	if ip == nil {
		return "", fmt.Errorf("ParseIP error:%s", host)
	}
	/*
	fmt.Println("ADDR TYPE", ip,
		"IsGlobalUnicast",
		ip.IsGlobalUnicast(),
		"IsInterfaceLocalMulticast",
		ip.IsInterfaceLocalMulticast(),
		"IsLinkLocalMulticast",
		ip.IsLinkLocalMulticast(),
		"IsLinkLocalUnicast",
		ip.IsLinkLocalUnicast(),
		"IsLoopback",
		ip.IsLoopback(),
		"IsMulticast",
		ip.IsMulticast(),
		"IsUnspecified",
		ip.IsUnspecified(),
	)
    */

	raddr := addr
	if ip.IsUnspecified() {
		// 没有指定ip的情况下，使用内网地址
		inerip, err := GetInterIp()
		if err != nil {
			return "", err
		}

		raddr = net.JoinHostPort(inerip, port)
	}

	//slog.Tracef("ServAddr --> addr:[%s] ip:[%s] host:[%s] port:[%s] raddr[%s]", addr, ip, host, port, raddr)

	return raddr, nil
}

// Request.RemoteAddress contains port, which we want to remove i.e.:
// "[::1]:58292" => "[::1]"
func IpAddrFromRemoteAddr(s string) string {
	idx := strings.LastIndex(s, ":")
	if idx == -1 {
		return s
	}
	return s[:idx]
}

func IpAddrPort(s string) string {
	idx := strings.LastIndex(s, ":")
	if idx == -1 {
		return ""
	}
	return s[idx+1:]
}

// 获取http请求的client的地址
func IpAddressHttpClient(r *http.Request) string {
	hdr := r.Header
	hdrRealIp := hdr.Get("X-Real-Ip")
	hdrForwardedFor := hdr.Get("X-Forwarded-For")

	if hdrRealIp == "" && hdrForwardedFor == "" {
		return IpAddrFromRemoteAddr(r.RemoteAddr)
	}

	if hdrForwardedFor != "" {
		// X-Forwarded-For is potentially a list of addresses separated with ","
		parts := strings.Split(hdrForwardedFor, ",")
		for i, p := range parts {
			parts[i] = strings.TrimSpace(p)
		}
		// TODO: should return first non-local address
		for _, ip := range(parts) {
			if len(ip) > 5 && "10." != ip[:3] && "172." != ip[:4] && "196." != ip[:4] && "127." != ip[:4] {
				return ip
			}
		}

	}

	return hdrRealIp
}

func PackdataPad(data []byte, pad byte) []byte {
	sendbuff := make([]byte, 0)
	// no pad
	var pacLen uint64 = uint64(len(data))
	buff := make([]byte, 20)
	rv := binary.PutUvarint(buff, pacLen)

	sendbuff = append(sendbuff, buff[:rv]...) // len
	sendbuff = append(sendbuff, data...) //data
	sendbuff = append(sendbuff, pad) //pad

	return sendbuff

}

func Packdata(data []byte) []byte {
	return PackdataPad(data, 0)
}

// 最小的消息长度、最大消息长度，数据流，包回调
// 正常返回解析剩余的数据，nil
// 否则返回错误
func UnPackdata(lenmin uint, lenmax uint, packBuff []byte, readCall func([]byte)) ([]byte, error) {
	for {

		// n == 0: buf too small
		// n  < 0: value larger than 64 bits (overflow)
        //     and -n is the number of bytes read
		pacLen, sz := binary.Uvarint(packBuff)
		if sz < 0 {
			return packBuff, errors.New("package head error")
		} else if sz == 0 {
			return packBuff, nil
		}

		// sz > 0

		// must < lenmax
		if pacLen > uint64(lenmax) {
			return packBuff, errors.New("package too long")
		} else if pacLen < uint64(lenmin) {
			return packBuff, errors.New("package too short")
		}

		apacLen := uint64(sz)+pacLen+1
		if uint64(len(packBuff)) >= apacLen {
			pad := packBuff[apacLen-1]
			if pad != 0 {
				return packBuff, errors.New("package pad error")
			}

			readCall(packBuff[sz:apacLen-1])
			packBuff = packBuff[apacLen:]
		} else {
			return packBuff, nil
		}

	}

	return nil, errors.New("unknown err")

}

func HttpReqGetOk(url string, timeout time.Duration) ([]byte, error) {

	client := &http.Client{Timeout: timeout}
	response, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

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

	if response.StatusCode != 200 {
		return nil, fmt.Errorf("statuscode:%d body:%s", response.StatusCode, body)

	} else {
		return body, nil
	}

}

func HttpReqPostOk(url string, data []byte, timeout time.Duration) ([]byte, error) {
	body, status, err := HttpReqPost(url, data, timeout)
	if err != nil {
		return nil, err
	}
	if status != 200 {
		return nil, errors.New(fmt.Sprintf("status:%d err:%s", status, body))

	} else {
		return body, nil
	}

}

func HttpReqPost(url string, data []byte, timeout time.Duration) ([]byte, int, error) {
	client := &http.Client{Timeout: timeout}

	reqest, err := http.NewRequest("POST", url, bytes.NewReader(data))
	if err != nil {
		return nil, 0, err
	}
	reqest.Header.Set("Connection","Keep-Alive")

	response, err := client.Do(reqest)
	if err != nil {
		return nil, 0, err
	}

	defer response.Body.Close()

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

	return body, response.StatusCode, nil

}

func PackageSplit(conn net.Conn, readtimeout time.Duration, readCall func([]byte)) (bool, []byte, error) {
	buffer := make([]byte, 2048)
	packBuff := make([]byte, 0)

	for {
		conn.SetReadDeadline(time.Now().Add(readtimeout))
		bytesRead, err := conn.Read(buffer)
		if err != nil {
			return true, nil, err
		}

		packBuff = append(packBuff, buffer[:bytesRead]...)

		packBuff, err = UnPackdata(1, 1024*5, packBuff, readCall)

		if err != nil {
			return false, packBuff, err
		}

	}

	return false, nil, errors.New("fuck err")

}
<commit_msg>snetutil<commit_after>// Copyright 2014 The sutil Author. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package snetutil

import (
	"net"
	"time"
	"fmt"
	"bytes"
	"io/ioutil"
	"encoding/binary"
	"strings"
	"errors"
	"net/http"
)

func GetInterIp() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		//fmt.Printf("Inter %v\n", addr)
		ip := addr.String()
		if "10." == ip[:3] {
			return strings.Split(ip, "/")[0], nil
		} else if "172." == ip[:4] {
			return strings.Split(ip, "/")[0], nil
		} else if "196." == ip[:4] {
			return strings.Split(ip, "/")[0], nil
		} else if "192." == ip[:4] {
			return strings.Split(ip, "/")[0], nil
		}

	}

	return "", errors.New("no inter ip")
}

// 获取首个外网ip v4
func GetExterIp() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		//fmt.Printf("Inter %v\n", addr)
		ips := addr.String()
		idx := strings.LastIndex(ips, "/")
		if idx == -1 {
                        continue
		}
		ipv := net.ParseIP(ips[:idx])
		if ipv == nil {
                        continue
		}

		ipv4 := ipv.To4()
		if ipv4 == nil {
                        // ipv6
                        continue
		}
		ip := ipv4.String()

		if "10." != ip[:3] && "172." != ip[:4] && "196." != ip[:4] && "127." != ip[:4] {
			return ip, nil
		}

	}

	return "", errors.New("no exter ip")
}

// 不指定host使用内网host
// 指定了就使用指定的，不管指定的是0.0.0.0还是内网或者外网
func GetListenAddr(a string) (string, error) {

	addrTcp, err := net.ResolveTCPAddr("tcp", a)
	if err != nil {
		return "", err
	}

	addr := addrTcp.String()
	host, _, err := net.SplitHostPort(addr)
	if err != nil {
		return "", err
	}

	if len(host) == 0 {
		return GetServAddr(addrTcp)
	}

	return addr, nil

}

func GetServAddr(a net.Addr) (string, error) {
	addr := a.String()
	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		return "", err
	}
	if len(host) == 0 {
		host = "0.0.0.0"
	}

	ip := net.ParseIP(host)

	if ip == nil {
		return "", fmt.Errorf("ParseIP error:%s", host)
	}
	/*
	fmt.Println("ADDR TYPE", ip,
		"IsGlobalUnicast",
		ip.IsGlobalUnicast(),
		"IsInterfaceLocalMulticast",
		ip.IsInterfaceLocalMulticast(),
		"IsLinkLocalMulticast",
		ip.IsLinkLocalMulticast(),
		"IsLinkLocalUnicast",
		ip.IsLinkLocalUnicast(),
		"IsLoopback",
		ip.IsLoopback(),
		"IsMulticast",
		ip.IsMulticast(),
		"IsUnspecified",
		ip.IsUnspecified(),
	)
    */

	raddr := addr
	if ip.IsUnspecified() {
		// 没有指定ip的情况下，使用内网地址
		inerip, err := GetInterIp()
		if err != nil {
			return "", err
		}

		raddr = net.JoinHostPort(inerip, port)
	}

	//slog.Tracef("ServAddr --> addr:[%s] ip:[%s] host:[%s] port:[%s] raddr[%s]", addr, ip, host, port, raddr)

	return raddr, nil
}

// Request.RemoteAddress contains port, which we want to remove i.e.:
// "[::1]:58292" => "[::1]"
func IpAddrFromRemoteAddr(s string) string {
	idx := strings.LastIndex(s, ":")
	if idx == -1 {
		return s
	}
	return s[:idx]
}

func IpAddrPort(s string) string {
	idx := strings.LastIndex(s, ":")
	if idx == -1 {
		return ""
	}
	return s[idx+1:]
}

// 获取http请求的client的地址
func IpAddressHttpClient(r *http.Request) string {
	hdr := r.Header
	hdrRealIp := hdr.Get("X-Real-Ip")
	hdrForwardedFor := hdr.Get("X-Forwarded-For")

	if hdrRealIp == "" && hdrForwardedFor == "" {
		return IpAddrFromRemoteAddr(r.RemoteAddr)
	}

	if hdrForwardedFor != "" {
		// X-Forwarded-For is potentially a list of addresses separated with ","
		parts := strings.Split(hdrForwardedFor, ",")
		for i, p := range parts {
			parts[i] = strings.TrimSpace(p)
		}
		// TODO: should return first non-local address
		for _, ip := range(parts) {
			if len(ip) > 5 && "10." != ip[:3] && "172." != ip[:4] && "196." != ip[:4] && "127." != ip[:4] {
				return ip
			}
		}

	}

	return hdrRealIp
}

func PackdataPad(data []byte, pad byte) []byte {
	sendbuff := make([]byte, 0)
	// no pad
	var pacLen uint64 = uint64(len(data))
	buff := make([]byte, 20)
	rv := binary.PutUvarint(buff, pacLen)

	sendbuff = append(sendbuff, buff[:rv]...) // len
	sendbuff = append(sendbuff, data...) //data
	sendbuff = append(sendbuff, pad) //pad

	return sendbuff

}

func Packdata(data []byte) []byte {
	return PackdataPad(data, 0)
}

// 最小的消息长度、最大消息长度，数据流，包回调
// 正常返回解析剩余的数据，nil
// 否则返回错误
func UnPackdata(lenmin uint, lenmax uint, packBuff []byte, readCall func([]byte)) ([]byte, error) {
	for {

		// n == 0: buf too small
		// n  < 0: value larger than 64 bits (overflow)
        //     and -n is the number of bytes read
		pacLen, sz := binary.Uvarint(packBuff)
		if sz < 0 {
			return packBuff, errors.New("package head error")
		} else if sz == 0 {
			return packBuff, nil
		}

		// sz > 0

		// must < lenmax
		if pacLen > uint64(lenmax) {
			return packBuff, errors.New("package too long")
		} else if pacLen < uint64(lenmin) {
			return packBuff, errors.New("package too short")
		}

		apacLen := uint64(sz)+pacLen+1
		if uint64(len(packBuff)) >= apacLen {
			pad := packBuff[apacLen-1]
			if pad != 0 {
				return packBuff, errors.New("package pad error")
			}

			readCall(packBuff[sz:apacLen-1])
			packBuff = packBuff[apacLen:]
		} else {
			return packBuff, nil
		}

	}

	return nil, errors.New("unknown err")

}

func HttpReqGetOk(url string, timeout time.Duration) ([]byte, error) {

	client := &http.Client{Timeout: timeout}
	response, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

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

	if response.StatusCode != 200 {
		return nil, fmt.Errorf("statuscode:%d body:%s", response.StatusCode, body)

	} else {
		return body, nil
	}

}

func HttpReqPostOk(url string, data []byte, timeout time.Duration) ([]byte, error) {
	return HttpReqOk(url, "POST", data, timeout)
}

func HttpReqOk(url, method string, data []byte, timeout time.Duration) ([]byte, error) {
	body, status, err := HttpReq(url, method, data, timeout)
	if err != nil {
		return nil, err
	}
	if status != 200 {
		return nil, errors.New(fmt.Sprintf("status:%d err:%s", status, body))

	} else {
		return body, nil
	}

}

func HttpReqPost(url string, data []byte, timeout time.Duration) ([]byte, int, error) {
	return HttpReq(url, "POST", data, timeout)
}

func HttpReq(url, method string, data []byte, timeout time.Duration) ([]byte, int, error) {
	client := &http.Client{Timeout: timeout}

	reqest, err := http.NewRequest("POST", url, bytes.NewReader(data))
	if err != nil {
		return nil, 0, err
	}
	reqest.Header.Set("Connection","Keep-Alive")

	response, err := client.Do(reqest)
	if err != nil {
		return nil, 0, err
	}

	defer response.Body.Close()

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

	return body, response.StatusCode, nil

}

func PackageSplit(conn net.Conn, readtimeout time.Duration, readCall func([]byte)) (bool, []byte, error) {
	buffer := make([]byte, 2048)
	packBuff := make([]byte, 0)

	for {
		conn.SetReadDeadline(time.Now().Add(readtimeout))
		bytesRead, err := conn.Read(buffer)
		if err != nil {
			return true, nil, err
		}

		packBuff = append(packBuff, buffer[:bytesRead]...)

		packBuff, err = UnPackdata(1, 1024*5, packBuff, readCall)

		if err != nil {
			return false, packBuff, err
		}

	}

	return false, nil, errors.New("fuck err")

}
<|endoftext|>


<commit_before>package topk

import (
	"bufio"
	"os"
	"sort"
	"testing"
)

type freqs struct {
	keys   []string
	counts map[string]int
}

func (f freqs) Len() int { return len(f.keys) }

// Actually 'Greater', since we want decreasing
func (f *freqs) Less(i, j int) bool {
	return f.counts[f.keys[i]] >= f.counts[f.keys[j]] || f.counts[f.keys[i]] == f.counts[f.keys[j]] && f.keys[i] < f.keys[j]
}

func (f *freqs) Swap(i, j int) { f.keys[i], f.keys[j] = f.keys[j], f.keys[i] }

func TestTopK(t *testing.T) {

	f, err := os.Open("testdata/domains.txt")

	if err != nil {
		t.Fatal(err)
	}

	scanner := bufio.NewScanner(f)

	tk := New(100)
	exact := make(map[string]int)

	for scanner.Scan() {

		item := scanner.Text()

		exact[item]++
		tk.Insert(item, 1)
	}

	var keys []string

	for k, _ := range exact {
		keys = append(keys, k)
	}

	freq := &freqs{keys: keys, counts: exact}

	sort.Sort(freq)

	top := tk.Keys()

	// at least the top 25 must be in order
	for i := 0; i < 25; i++ {
		if top[i].Key != freq.keys[i] {
			t.Errorf("key mismatch: idx=%d top=%s (%d) exact=%s (%d)", i, top[i].Key, top[i].Count, freq.keys[i], freq.counts[freq.keys[i]])
		}
	}
}
<commit_msg>Add missing error check for scanner<commit_after>package topk

import (
	"bufio"
	"log"
	"os"
	"sort"
	"testing"
)

type freqs struct {
	keys   []string
	counts map[string]int
}

func (f freqs) Len() int { return len(f.keys) }

// Actually 'Greater', since we want decreasing
func (f *freqs) Less(i, j int) bool {
	return f.counts[f.keys[i]] >= f.counts[f.keys[j]] || f.counts[f.keys[i]] == f.counts[f.keys[j]] && f.keys[i] < f.keys[j]
}

func (f *freqs) Swap(i, j int) { f.keys[i], f.keys[j] = f.keys[j], f.keys[i] }

func TestTopK(t *testing.T) {

	f, err := os.Open("testdata/domains.txt")

	if err != nil {
		t.Fatal(err)
	}

	scanner := bufio.NewScanner(f)

	tk := New(100)
	exact := make(map[string]int)

	for scanner.Scan() {

		item := scanner.Text()

		exact[item]++
		tk.Insert(item, 1)
	}

	if err := scanner.Err(); err != nil {
		log.Println("error during scan: ", err)
	}

	var keys []string

	for k, _ := range exact {
		keys = append(keys, k)
	}

	freq := &freqs{keys: keys, counts: exact}

	sort.Sort(freq)

	top := tk.Keys()

	// at least the top 25 must be in order
	for i := 0; i < 25; i++ {
		if top[i].Key != freq.keys[i] {
			t.Errorf("key mismatch: idx=%d top=%s (%d) exact=%s (%d)", i, top[i].Key, top[i].Count, freq.keys[i], freq.counts[freq.keys[i]])
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 DG Lab
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

/**
 * Helper functions for the RPC utility
 */
package rpc

import (
	"fmt"
)

func (ul UnspentList) Len() int {
	return len(ul)
}

func (ul UnspentList) Swap(i, j int) {
	ul[i], ul[j] = ul[j], ul[i]
}

func (ul UnspentList) Less(i, j int) bool {
	if (*ul[i]).Amount < (*ul[j]).Amount {
		return true
	}
	if (*ul[i]).Amount > (*ul[j]).Amount {
		return false
	}
	return (*ul[i]).Confirmations < (*ul[j]).Confirmations
}

func (rpc* Rpc) GetNewAddr(confidential bool) (string, error) {
	var validAddr ValidatedAddress

	adr, _, err := rpc.RequestAndCastString("getnewaddress")
	if err != nil {
		return "", err
	}

	if confidential {
		return adr, nil
	}

	_, err = rpc.RequestAndUnmarshalResult(&validAddr, "validateaddress", adr)
	if err != nil {
		return "", err
	}

	if validAddr.Unconfidential == "" {
		return "", fmt.Errorf("unconfidential is empty")
	}

	return validAddr.Unconfidential, nil
}

/**
 * Extract the commitments from a list of UTXOs and return these
 * as an array of hex strings.
 */
func (rpc* Rpc) GetCommitments(utxos UnspentList) ([]string, error) {
	var commitments []string = make([]string, len(utxos))

	for i, u := range utxos {
		commitments[i] = u.AssetCommitment
	}
	return commitments, nil
}
<commit_msg>Moved UnspentList and LockList to rpc/helper.go<commit_after>// Copyright (c) 2017 DG Lab
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

/**
 * Helper functions for the RPC utility
 */
package rpc

import (
	"fmt"
	"time"
)

// Lock lists to keep referenced utxos (potentially to-be-spent)

type LockList map[string]time.Time

var utxoLockDuration time.Duration

func SetUtxoLockDuration(utxoLockDurationIn time.Duration) {
	utxoLockDuration = utxoLockDurationIn
}

func (ul UnspentList) Len() int {
	return len(ul)
}

func (ul UnspentList) Swap(i, j int) {
	ul[i], ul[j] = ul[j], ul[i]
}

func (ul UnspentList) Less(i, j int) bool {
	if (*ul[i]).Amount < (*ul[j]).Amount {
		return true
	}
	if (*ul[i]).Amount > (*ul[j]).Amount {
		return false
	}
	return (*ul[i]).Confirmations < (*ul[j]).Confirmations
}

func getLockingKey(txid string, vout int64) string {
	return fmt.Sprintf("%s:%d", txid, vout)
}

func (ll LockList) Lock(txid string, vout int64) bool {
	key := getLockingKey(txid, vout)
	now := time.Now()
	to := now.Add(utxoLockDuration)

	old, ok := ll[key]
	if !ok {
		// new lock.
		ll[key] = to
		return true
	}
	if old.Sub(now) < 0 {
		// exists but no longer locked. lock again.
		ll[key] = to
		return true
	}

	// already locked.
	return false
}

func (ll LockList) Unlock(txid string, vout int64) {
	key := getLockingKey(txid, vout)
	delete(ll, key)

	return
}

func (ll LockList) Sweep() {
	now := time.Now()
	for k, v := range ll {
		if v.Sub(now) < 0 {
			delete(ll, k)
		}
	}
}

func (ll LockList) UnlockUnspentList(ul UnspentList) {
	for _, u := range ul {
		ll.Unlock(u.Txid, u.Vout)
	}
}

func (rpc* Rpc) GetNewAddr(confidential bool) (string, error) {
	var validAddr ValidatedAddress

	adr, _, err := rpc.RequestAndCastString("getnewaddress")
	if err != nil {
		return "", err
	}

	if confidential {
		return adr, nil
	}

	_, err = rpc.RequestAndUnmarshalResult(&validAddr, "validateaddress", adr)
	if err != nil {
		return "", err
	}

	if validAddr.Unconfidential == "" {
		return "", fmt.Errorf("unconfidential is empty")
	}

	return validAddr.Unconfidential, nil
}

/**
 * Extract the commitments from a list of UTXOs and return these
 * as an array of hex strings.
 */
func (rpc* Rpc) GetCommitments(utxos UnspentList) ([]string, error) {
	var commitments []string = make([]string, len(utxos))

	for i, u := range utxos {
		commitments[i] = u.AssetCommitment
	}
	return commitments, nil
}
<|endoftext|>


<commit_before><commit_msg>examples/core51822/ventilation: Update to use new nrf5/input/* packages.<commit_after><|endoftext|>


<commit_before>package router

import (
	"net/http"
	"net/url"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

func TestAdd(t *testing.T) {
	tree := NewNode()
	path := "/test-add"
	verb := "GET"
	tree.Add(verb, path, handlerA)
}

func TestFind(t *testing.T) {
	tree := NewNode()
	verb := "GET"
	params := url.Values{}
	path := "/test-find"
	tree.Add(verb, path, handlerB)
	leaf, _ := tree.Find(path[1:], params)

	handlers := leaf.handlers[verb]
	if len(handlers) != 1 {
		t.Fatalf("Expected 1 handler, %d found", len(handlers))
		return
	}

	handler_name := FuncName(handlers[0])
	if strings.Compare("handlerB", handler_name) != 0 {
		t.Error("Test")
		t.Error(handler_name)
		t.Errorf("Expected to find handlerB in the handlers list")
	}
}

func handlerA(w http.ResponseWriter, req *http.Request) {
}

func handlerB(w http.ResponseWriter, req *http.Request) {
}

func FuncName(h Handler) string {
	p := runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
	handlers := strings.Split(p, "/")
	long_name := handlers[len(handlers)-1]
	return strings.Split(long_name, ".")[1]
}
<commit_msg>clean up<commit_after>package router

import (
	"net/http"
	"net/url"
	"reflect"
	"runtime"
	"strings"
	"testing"
)

func TestAdd(t *testing.T) {
	tree := NewNode()
	path := "/test-add"
	verb := "GET"
	tree.Add(verb, path, handlerA)
}

func TestFind(t *testing.T) {
	tree := NewNode()
	verb := "GET"
	params := url.Values{}
	path := "/test-find"
	tree.Add(verb, path, handlerB)
	leaf, _ := tree.Find(path[1:], params)

	handlers := leaf.handlers[verb]
	if len(handlers) != 1 {
		t.Fatalf("Expected 1 handler, %d found", len(handlers))
		return
	}

	handler_name := FuncName(handlers[0])
	if strings.Compare("handlerB", handler_name) != 0 {
		t.Fatalf("Expected to find handlerB in the handlers list")
	}
}

func handlerA(w http.ResponseWriter, req *http.Request) {
}

func handlerB(w http.ResponseWriter, req *http.Request) {
}

func FuncName(h Handler) string {
	p := runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
	handlers := strings.Split(p, "/")
	long_name := handlers[len(handlers)-1]
	return strings.Split(long_name, ".")[1]
}
<|endoftext|>


<commit_before>//
// model.go
// template model implementation
//
package srnd

import (
  "fmt"
  "io"
  "path/filepath"
  "strings"
  "time"
)

// base model type
type BaseModel interface {

  // site url prefix
  Prefix() string

  // render to a writer
  RenderTo(wr io.Writer) error

}

// for attachments
type AttachmentModel interface {

  BaseModel

  Thumbnail() string
  Source() string
  Filename() string

}

// for individual posts
type PostModel interface {

  BaseModel

  CSSClass() string

  MessageID() string
  PostHash() string
  ShortHash() string
  PostURL() string
  Frontend() string
  Subject() string
  Name() string
  Date() string
  OP() bool
  Attachments() []AttachmentModel
  Board() string
  Sage() bool
  Pubkey() string
  Reference() string

  RenderBody() string
  RenderPost() string

  IsI2P() bool
  IsTor() bool
  IsClearnet() bool

  // truncate body to a certain size
  // return copy
  Truncate() PostModel

}

// interface for models that have a navbar
type NavbarModel interface {

  Navbar() string

}

// for threads
type ThreadModel interface {

  BaseModel
  NavbarModel

  OP() PostModel
  Replies() []PostModel
  Board() string
  BoardURL() string
  // return a short version of the thread
  // does not include all replies
  Truncate() ThreadModel
  // update the thread's replies
  // return the updated model
  Update(db Database) ThreadModel
}

// board interface
// for 1 page on a board
type BoardModel interface {

  BaseModel
  NavbarModel

  Frontend() string
  Name() string
  Threads() []ThreadModel

  // JUST update this thread
  // if we don't have it already loaded do nothing
  UpdateThread(message_id string, db Database) BoardModel

  // get a thread model with this id
  // returns nil if we don't have it
  GetThread(message_id string) ThreadModel

  // deprecated, use GetThread
  HasThread(message_id string) bool

  // update the board's contents
  // return the updated model
  Update(db Database) BoardModel
}

type LinkModel interface {

  Text() string
  LinkURL() string
}

// newsgroup model
// every page on a newsgroup
type GroupModel []BoardModel

// TODO: optimize using 1 query?
// update every page
// return updated model
func (self GroupModel) UpdateAll(db Database) GroupModel {
  for idx, page := range self {
    self[idx] = page.Update(db)
  }
  return self
}

// update a certain page
// does nothing if out of bounds
func (self GroupModel) Update(page int, db Database) GroupModel {
  if len(self) > page {
    self[page] = self[page].Update(db)
  }
  return self
}

type boardModel struct {
  frontend string
  prefix string
  board string
  page int
  pages int
  threads []ThreadModel
}

func (self boardModel) Navbar() string {
  param := make(map[string]interface{})
  param["name"] = fmt.Sprintf("page %d for %s", self.page, self.board)
  param["frontend"] = self.frontend
  var links []LinkModel
  for i := 0 ; i < self.pages ; i ++ {
    links = append(links, linkModel{
      link: fmt.Sprintf("%s%s-%d.html", self.prefix, self.board, i),
      text: fmt.Sprintf("[ %d ]", i),
    })
  }
  param["prefix"] = self.prefix
  param["links"] = links
  return template.renderTemplate("navbar.mustache", param)
}

func (self boardModel) UpdateThread(messageID string, db Database) BoardModel {

  for idx, th := range self.threads {
    if th.OP().MessageID() == messageID {
      // found it
      self.threads[idx] = th.Update(db)
      break
    }
  }
  return self
}

func (self boardModel) GetThread(messageID string) ThreadModel {
  for _, th := range self.threads {
    if th.OP().MessageID() == messageID {
      return th
    }
  }
  return nil
}

func (self boardModel) HasThread(messageID string) bool {
  return self.GetThread(messageID) != nil
}

func (self boardModel) Frontend() string {
  return self.frontend
}

func (self boardModel) Prefix() string {
  return self.prefix
}

func (self boardModel) Name() string {
  return self.board
}

func (self boardModel) Threads() []ThreadModel {
  return self.threads
}

func (self boardModel) RenderTo(wr io.Writer) error {
  param := make(map[string]interface{})
  param["board"] = self
  param["form"] = renderPostForm(self.Prefix(), self.board, "")
  _, err := io.WriteString(wr, template.renderTemplate("board.mustache", param))
  return err
}

// refetch all threads on this page
func (self boardModel) Update(db Database) BoardModel {
  // ignore error
  perpage, _ := db.GetThreadsPerPage(self.board)
  // refetch all on this page
  model := db.GetGroupForPage(self.prefix, self.frontend, self.board, self.page, int(perpage))
  var threads []ThreadModel
  for _, th := range model.Threads() {
    threads = append(threads, th.Update(db))
  }
  return boardModel{
    frontend: self.frontend,
    prefix: self.prefix,
    board: self.board,
    page: self.page,
    pages: self.pages,
    threads: threads,
  }
}

type post struct {
  prefix string
  board string
  name string
  subject string
  message string
  message_rendered string
  message_id string
  path string
  addr string
  op bool
  posted int64
  parent string
  sage bool
  pubkey string
  reference string
  attachments []AttachmentModel
}

type attachment struct {
  prefix string
  filepath string
  filename string
}

func (self attachment) Prefix() string {
  return self.prefix
}

func (self attachment) RenderTo(wr io.Writer) error {
  // does nothing
  return nil
}

func (self attachment) Thumbnail() string {
  return self.prefix + "thm/" + self.filepath + ".jpg"
}

func (self attachment) Source() string {
  return self.prefix + "img/" + self.filepath
}

func (self attachment) Filename() string {
  return self.filename
}

func PostModelFromMessage(parent, prefix string, nntp NNTPMessage) PostModel {
  p := post{}
  p.name = nntp.Name()
  p.subject = nntp.Subject()
  p.message = nntp.Message()
  p.path = nntp.Path()
  p.message_id = nntp.MessageID()
  p.board = nntp.Newsgroup()
  p.posted = nntp.Posted()
  p.op = nntp.OP()
  p.prefix = prefix
  p.parent = parent
  p.addr = nntp.Addr()
  p.sage = nntp.Sage()
  p.pubkey = nntp.Pubkey()
  for _, att := range nntp.Attachments() {
    p.attachments = append(p.attachments, att.ToModel(prefix))
  }
  return p
}

func (self post) Reference() string {
  return self.parent
}

func (self post) ShortHash() string {
  return ShortHashMessageID(self.message_id)
}

func (self post) Pubkey() string {
  if len(self.pubkey) > 0 {
    return fmt.Sprintf("%s", self.pubkey, makeTripcode(self.pubkey))
  }
  return ""
}

func (self post) Sage() bool {
  return self.sage
}

func (self post) CSSClass() string {
  if self.OP() {
    return "post op"
  } else {
    return "post reply"
  }
}

func (self post) OP() bool {
  return self.parent == self.message_id || len(self.parent) == 0
}

func (self post) Date() string {
  return time.Unix(self.posted, 0).Format(time.ANSIC)
}

func (self post) TemplateDir() string {
  return filepath.Join("contrib", "templates", "default")
}

func (self post) MessageID() string  {
  return self.message_id
}

func (self post) Frontend() string {
  idx := strings.LastIndex(self.path, "!")
  if idx == -1 {
    return self.path
  }
  return self.path[idx+1:]
}

func (self post) Board() string {
  return self.board
}

func (self post) PostHash() string {
  return HashMessageID(self.message_id)
}

func (self post) Name() string {
  return self.name
}

func (self post) Subject() string {
  return self.subject
}

func (self post) Attachments() []AttachmentModel {
  return self.attachments
}

func (self post) PostURL() string {
  return fmt.Sprintf("%sthread-%s.html#%s", self.Prefix(), ShortHashMessageID(self.parent), self.PostHash())
}

func (self post) Prefix() string {
  return self.prefix
}

func (self post) IsClearnet() bool {
  return len(self.addr) == encAddrLen()
}

func (self post) IsI2P() bool {
  return len(self.addr) == i2pDestHashLen()
}

func (self post) IsTor() bool {
  return len(self.addr) == 0
}

func (self post) RenderTo(wr io.Writer) error {
  _, err := io.WriteString(wr, self.RenderPost())
  return err
}

func (self post) RenderPost() string {
  return template.renderTemplate("post.mustache", self)
}

func (self post) Truncate() PostModel {
  if len(self.message) > 500 {
    message := self.message[:500] + "\n...\n[Post Truncated]\n"
    return post{
      message: message,
      prefix: self.prefix,
      board: self.board,
      name: self.name,
      subject: self.subject,
      message_id: self.message_id,
      path: self.path,
      op: self.op,
      posted: self.posted,
      parent: self.parent,
      sage: self.sage,
      pubkey: self.pubkey,
      reference: self.reference,
      attachments: self.attachments,
      addr: self.addr,
    }
  }
  return self
}

func (self post) RenderShortBody() string {
  // TODO: hardcoded limit
  return memeposting(self.message)
}

func (self post) RenderBody() string {
  // :^)
  if len(self.message_rendered) == 0 {
    self.message_rendered = memeposting(self.message)
  }
  return self.message_rendered
}

type thread struct {
  prefix string
  links []LinkModel
  posts []PostModel
}

func (self thread) Prefix() string {
  return self.prefix
}

func (self thread) Navbar() string {
  param := make(map[string]interface{})
  param["name"] = fmt.Sprintf("Thread %s", self.posts[0].ShortHash())
  param["frontend"] = self.Board()
  param["links"] = self.links
  param["prefix"] = self.prefix
  return template.renderTemplate("navbar.mustache", param)
}

func (self thread) Board() string {
  return self.posts[0].Board()
}

func (self thread) BoardURL() string {
  return fmt.Sprintf("%s%s-0.html", self.Prefix(), self.Board())
}

// get our default template dir
func defaultTemplateDir() string {
  return  filepath.Join("contrib", "templates", "default")
}

func (self thread) RenderTo(wr io.Writer) error {
  postform := renderPostForm(self.prefix, self.Board(), self.posts[0].MessageID())
  data := template.renderTemplate("thread.mustache", map[string]interface{} { "thread": self, "form" : postform})
  io.WriteString(wr, data)
  return nil
}

func (self thread) OP() PostModel {
  return self.posts[0]
}

func (self thread) Replies() []PostModel {
  if len(self.posts) > 1 {
    return self.posts[1:]
  }
  return []PostModel{}
}

func (self thread) Truncate() ThreadModel {
  trunc := 5
  if len(self.posts) > trunc {
    return thread{
      links: self.links,
      posts: append([]PostModel{self.posts[0]}, self.posts[len(self.posts)-trunc:]...),
      prefix: self.prefix,
    }
  }
  return self
}

// refetch all replies if anything differs
func (self thread) Update(db Database) ThreadModel {
  root := self.posts[0].MessageID()
  reply_count := db.CountThreadReplies(root)

  if int(reply_count) + 1 != len(self.posts) {

    return thread{
      posts: append([]PostModel{self.posts[0]}, db.GetThreadReplyPostModels(self.prefix, root, 0)...),
      links: self.links,
      prefix: self.prefix,
    }
  }
  return self
}

type linkModel struct {
  text string
  link string
}

func (self linkModel) LinkURL() string {
  return self.link
}

func (self linkModel) Text() string {
  return self.text
}

type boardPageRow struct {
  Board string
  Hour int64
  Day int64
  All int64
}

type boardPageRows []boardPageRow

func (self boardPageRows) Len() int {
  return len(self)
}

func (self boardPageRows) Less(i, j int) bool {
  i_val := self[i]
  j_val := self[j]
  return (i_val.Day + i_val.Hour * 24 ) > ( j_val.Day + j_val.Hour * 24)
}

func (self boardPageRows) Swap(i, j int) {
  self[i] , self[j] = self[j], self[i]
}

type postsGraphRow struct {
  day time.Time
  Num int64
  mag int64
}

func (p *postsGraphRow) GraphRune(r string) (s string) {
  var num int64
  if p.mag > 0 {
    num = p.Num / p.mag
  } else {
    num = p.Num
  }
  for num > 0 {
    s += r
    num --
  }
  return
}

func (p postsGraphRow) Day() (s string) {
  return p.day.String()[5:11]
}

func (p postsGraphRow) RegularGraph() (s string) {
  return p.GraphRune("=")
}

// :0========3 overcock :3 graph of data
func (p postsGraphRow) OvercockGraph() (s string) {
  var num int64
  if p.mag > 0 {
    num = p.Num / p.mag
  } else {
    num = p.Num
  }
  if num > 0 {
    s = ":0"
    num -= 1
    for num > 0 {
      s += "="
      num--
    }
    s += "3"
  } else {
    s = ":3"
  }
  return
}

type postsGraph []postsGraphRow

func (graph postsGraph) Render() string {
  return template.renderTemplate("posts_graph.mustache", map[string]interface{} { "graph": graph})
}

func (self postsGraph) Len() int {
  return len(self)
}

func (self postsGraph) Less(i, j int) bool {
  i_val := self[i]
  j_val := self[j]
  return i_val.day.Day() > j_val.day.Day()
}

func (self postsGraph) Swap(i, j int) {
  self[i] , self[j] = self[j], self[i]
}

func (self postsGraph) Scale() (graph postsGraph) {
  // find max
  max := int64(0)
  for _, p := range self {
    if p.Num > max {
      max = p.Num
    }
  }
  mag := max / 25
  for _, p := range self {
    p.mag = mag
    graph = append(graph, p)
  }
  return
}

type overviewModel []PostModel

func (o overviewModel) Render() {
  template.renderTemplate("overview.mustache", map[string]interface{} { "overview" : o })
}
<commit_msg>hurrrrrrrr<commit_after>//
// model.go
// template model implementation
//
package srnd

import (
  "fmt"
  "io"
  "path/filepath"
  "strings"
  "time"
)

// base model type
type BaseModel interface {

  // site url prefix
  Prefix() string

  // render to a writer
  RenderTo(wr io.Writer) error

}

// for attachments
type AttachmentModel interface {

  BaseModel

  Thumbnail() string
  Source() string
  Filename() string

}

// for individual posts
type PostModel interface {

  BaseModel

  CSSClass() string

  MessageID() string
  PostHash() string
  ShortHash() string
  PostURL() string
  Frontend() string
  Subject() string
  Name() string
  Date() string
  OP() bool
  Attachments() []AttachmentModel
  Board() string
  Sage() bool
  Pubkey() string
  Reference() string

  RenderBody() string
  RenderPost() string

  IsI2P() bool
  IsTor() bool
  IsClearnet() bool

  // truncate body to a certain size
  // return copy
  Truncate() PostModel

}

// interface for models that have a navbar
type NavbarModel interface {

  Navbar() string

}

// for threads
type ThreadModel interface {

  BaseModel
  NavbarModel

  OP() PostModel
  Replies() []PostModel
  Board() string
  BoardURL() string
  // return a short version of the thread
  // does not include all replies
  Truncate() ThreadModel
  // update the thread's replies
  // return the updated model
  Update(db Database) ThreadModel
}

// board interface
// for 1 page on a board
type BoardModel interface {

  BaseModel
  NavbarModel

  Frontend() string
  Name() string
  Threads() []ThreadModel

  // JUST update this thread
  // if we don't have it already loaded do nothing
  UpdateThread(message_id string, db Database) BoardModel

  // get a thread model with this id
  // returns nil if we don't have it
  GetThread(message_id string) ThreadModel

  // deprecated, use GetThread
  HasThread(message_id string) bool

  // update the board's contents
  // return the updated model
  Update(db Database) BoardModel
}

type LinkModel interface {

  Text() string
  LinkURL() string
}

// newsgroup model
// every page on a newsgroup
type GroupModel []BoardModel

// TODO: optimize using 1 query?
// update every page
// return updated model
func (self GroupModel) UpdateAll(db Database) GroupModel {
  for idx, page := range self {
    self[idx] = page.Update(db)
  }
  return self
}

// update a certain page
// does nothing if out of bounds
func (self GroupModel) Update(page int, db Database) GroupModel {
  if len(self) > page {
    self[page] = self[page].Update(db)
  }
  return self
}

type boardModel struct {
  frontend string
  prefix string
  board string
  page int
  pages int
  threads []ThreadModel
}

func (self boardModel) Navbar() string {
  param := make(map[string]interface{})
  param["name"] = fmt.Sprintf("page %d for %s", self.page, self.board)
  param["frontend"] = self.frontend
  var links []LinkModel
  for i := 0 ; i < self.pages ; i ++ {
    links = append(links, linkModel{
      link: fmt.Sprintf("%s%s-%d.html", self.prefix, self.board, i),
      text: fmt.Sprintf("[ %d ]", i),
    })
  }
  param["prefix"] = self.prefix
  param["links"] = links
  return template.renderTemplate("navbar.mustache", param)
}

func (self boardModel) UpdateThread(messageID string, db Database) BoardModel {

  for idx, th := range self.threads {
    if th.OP().MessageID() == messageID {
      // found it
      self.threads[idx] = th.Update(db)
      break
    }
  }
  return self
}

func (self boardModel) GetThread(messageID string) ThreadModel {
  for _, th := range self.threads {
    if th.OP().MessageID() == messageID {
      return th
    }
  }
  return nil
}

func (self boardModel) HasThread(messageID string) bool {
  return self.GetThread(messageID) != nil
}

func (self boardModel) Frontend() string {
  return self.frontend
}

func (self boardModel) Prefix() string {
  return self.prefix
}

func (self boardModel) Name() string {
  return self.board
}

func (self boardModel) Threads() []ThreadModel {
  return self.threads
}

func (self boardModel) RenderTo(wr io.Writer) error {
  param := make(map[string]interface{})
  param["board"] = self
  param["form"] = renderPostForm(self.Prefix(), self.board, "")
  _, err := io.WriteString(wr, template.renderTemplate("board.mustache", param))
  return err
}

// refetch all threads on this page
func (self boardModel) Update(db Database) BoardModel {
  // ignore error
  perpage, _ := db.GetThreadsPerPage(self.board)
  // refetch all on this page
  model := db.GetGroupForPage(self.prefix, self.frontend, self.board, self.page, int(perpage))
  var threads []ThreadModel
  for _, th := range model.Threads() {
    threads = append(threads, th.Update(db))
  }
  return boardModel{
    frontend: self.frontend,
    prefix: self.prefix,
    board: self.board,
    page: self.page,
    pages: self.pages,
    threads: threads,
  }
}

type post struct {
  prefix string
  board string
  name string
  subject string
  message string
  message_rendered string
  message_id string
  path string
  addr string
  op bool
  posted int64
  parent string
  sage bool
  pubkey string
  reference string
  attachments []AttachmentModel
}

type attachment struct {
  prefix string
  filepath string
  filename string
}

func (self attachment) Prefix() string {
  return self.prefix
}

func (self attachment) RenderTo(wr io.Writer) error {
  // does nothing
  return nil
}

func (self attachment) Thumbnail() string {
  return self.prefix + "thm/" + self.filepath + ".jpg"
}

func (self attachment) Source() string {
  return self.prefix + "img/" + self.filepath
}

func (self attachment) Filename() string {
  return self.filename
}

func PostModelFromMessage(parent, prefix string, nntp NNTPMessage) PostModel {
  p := post{}
  p.name = nntp.Name()
  p.subject = nntp.Subject()
  p.message = nntp.Message()
  p.path = nntp.Path()
  p.message_id = nntp.MessageID()
  p.board = nntp.Newsgroup()
  p.posted = nntp.Posted()
  p.op = nntp.OP()
  p.prefix = prefix
  p.parent = parent
  p.addr = nntp.Addr()
  p.sage = nntp.Sage()
  p.pubkey = nntp.Pubkey()
  for _, att := range nntp.Attachments() {
    p.attachments = append(p.attachments, att.ToModel(prefix))
  }
  return p
}

func (self post) Reference() string {
  return self.parent
}

func (self post) ShortHash() string {
  return ShortHashMessageID(self.message_id)
}

func (self post) Pubkey() string {
  if len(self.pubkey) > 0 {
    return fmt.Sprintf("%s", self.pubkey, makeTripcode(self.pubkey))
  }
  return ""
}

func (self post) Sage() bool {
  return self.sage
}

func (self post) CSSClass() string {
  if self.OP() {
    return "post op"
  } else {
    return "post reply"
  }
}

func (self post) OP() bool {
  return self.parent == self.message_id || len(self.parent) == 0
}

func (self post) Date() string {
  return time.Unix(self.posted, 0).Format(time.ANSIC)
}

func (self post) TemplateDir() string {
  return filepath.Join("contrib", "templates", "default")
}

func (self post) MessageID() string  {
  return self.message_id
}

func (self post) Frontend() string {
  idx := strings.LastIndex(self.path, "!")
  if idx == -1 {
    return self.path
  }
  return self.path[idx+1:]
}

func (self post) Board() string {
  return self.board
}

func (self post) PostHash() string {
  return HashMessageID(self.message_id)
}

func (self post) Name() string {
  return self.name
}

func (self post) Subject() string {
  return self.subject
}

func (self post) Attachments() []AttachmentModel {
  return self.attachments
}

func (self post) PostURL() string {
  return fmt.Sprintf("%sthread-%s.html#%s", self.Prefix(), ShortHashMessageID(self.parent), self.PostHash())
}

func (self post) Prefix() string {
  return self.prefix
}

func (self post) IsClearnet() bool {
  return len(self.addr) == encAddrLen()
}

func (self post) IsI2P() bool {
  return len(self.addr) == i2pDestHashLen()
}

func (self post) IsTor() bool {
  return len(self.addr) == 0
}

func (self post) RenderTo(wr io.Writer) error {
  _, err := io.WriteString(wr, self.RenderPost())
  return err
}

func (self post) RenderPost() string {
  return template.renderTemplate("post.mustache", self)
}

func (self post) Truncate() PostModel {
  if len(self.message) > 500 {
    message := self.message[:500] + "\n...\n[Post Truncated]\n"
    return post{
      message: message,
      prefix: self.prefix,
      board: self.board,
      name: self.name,
      subject: self.subject,
      message_id: self.message_id,
      path: self.path,
      op: self.op,
      posted: self.posted,
      parent: self.parent,
      sage: self.sage,
      pubkey: self.pubkey,
      reference: self.reference,
      attachments: self.attachments,
      addr: self.addr,
    }
  }
  return self
}

func (self post) RenderShortBody() string {
  // TODO: hardcoded limit
  return memeposting(self.message)
}

func (self post) RenderBody() string {
  // :^)
  if len(self.message_rendered) == 0 {
    self.message_rendered = memeposting(self.message)
  }
  return self.message_rendered
}

type thread struct {
  prefix string
  links []LinkModel
  posts []PostModel
}

func (self thread) Prefix() string {
  return self.prefix
}

func (self thread) Navbar() string {
  param := make(map[string]interface{})
  param["name"] = fmt.Sprintf("Thread %s", self.posts[0].ShortHash())
  param["frontend"] = self.Board()
  param["links"] = self.links
  param["prefix"] = self.prefix
  return template.renderTemplate("navbar.mustache", param)
}

func (self thread) Board() string {
  return self.posts[0].Board()
}

func (self thread) BoardURL() string {
  return fmt.Sprintf("%s%s-0.html", self.Prefix(), self.Board())
}

// get our default template dir
func defaultTemplateDir() string {
  return  filepath.Join("contrib", "templates", "default")
}

func (self thread) RenderTo(wr io.Writer) error {
  postform := renderPostForm(self.prefix, self.Board(), self.posts[0].MessageID())
  data := template.renderTemplate("thread.mustache", map[string]interface{} { "thread": self, "form" : postform})
  io.WriteString(wr, data)
  return nil
}

func (self thread) OP() PostModel {
  return self.posts[0]
}

func (self thread) Replies() []PostModel {
  if len(self.posts) > 1 {
    return self.posts[1:]
  }
  return []PostModel{}
}

func (self thread) Truncate() ThreadModel {
  trunc := 5
  if len(self.posts) > trunc {
    return thread{
      links: self.links,
      posts: append([]PostModel{self.posts[0]}, self.posts[len(self.posts)-trunc:]...),
      prefix: self.prefix,
    }
  }
  return self
}

// refetch all replies if anything differs
func (self thread) Update(db Database) ThreadModel {
  root := self.posts[0].MessageID()
  reply_count := db.CountThreadReplies(root)

  if int(reply_count) + 1 != len(self.posts) {

    return thread{
      posts: append([]PostModel{self.posts[0]}, db.GetThreadReplyPostModels(self.prefix, root, 0)...),
      links: self.links,
      prefix: self.prefix,
    }
  }
  return self
}

type linkModel struct {
  text string
  link string
}

func (self linkModel) LinkURL() string {
  return self.link
}

func (self linkModel) Text() string {
  return self.text
}

type boardPageRow struct {
  Board string
  Hour int64
  Day int64
  All int64
}

type boardPageRows []boardPageRow

func (self boardPageRows) Len() int {
  return len(self)
}

func (self boardPageRows) Less(i, j int) bool {
  i_val := self[i]
  j_val := self[j]
  return (i_val.Day + i_val.Hour * 24 ) > ( j_val.Day + j_val.Hour * 24)
}

func (self boardPageRows) Swap(i, j int) {
  self[i] , self[j] = self[j], self[i]
}

type postsGraphRow struct {
  day time.Time
  Num int64
  mag int64
}

func (p *postsGraphRow) GraphRune(r string) (s string) {
  var num int64
  if p.mag > 0 {
    num = p.Num / p.mag
  } else {
    num = p.Num
  }
  for num > 0 {
    s += r
    num --
  }
  return
}

func (p postsGraphRow) Day() (s string) {
  return p.day.String()[5:11]
}

func (p postsGraphRow) RegularGraph() (s string) {
  return p.GraphRune("=")
}

// :0========3 overcock :3 graph of data
func (p postsGraphRow) OvercockGraph() (s string) {
  var num int64
  if p.mag > 0 {
    num = p.Num / p.mag
  } else {
    num = p.Num
  }
  if num > 0 {
    s = ":0"
    num -= 1
    for num > 0 {
      s += "="
      num--
    }
    s += "3"
  } else {
    s = ":3"
  }
  return
}

type postsGraph []postsGraphRow

func (graph postsGraph) Render() string {
  return template.renderTemplate("posts_graph.mustache", map[string]interface{} { "graph": graph})
}

func (self postsGraph) Len() int {
  return len(self)
}

func (self postsGraph) Less(i, j int) bool {
  i_val := self[i]
  j_val := self[j]
  return i_val.day.Day() > j_val.day.Day()
}

func (self postsGraph) Swap(i, j int) {
  self[i] , self[j] = self[j], self[i]
}

func (self postsGraph) Scale() (graph postsGraph) {
  // find max
  max := int64(0)
  for _, p := range self {
    if p.Num > max {
      max = p.Num
    }
  }
  mag := max / 25
  for _, p := range self {
    p.mag = mag
    graph = append(graph, p)
  }
  return
}

type overviewModel []PostModel

func (o overviewModel) Render() string {
  return template.renderTemplate("overview.mustache", map[string]interface{} { "overview" : o })
}
<|endoftext|>


<commit_before>package hbn

// Neighbors represents list of Neighbor.
type Neighbors []Neighbor

func (n Neighbors) Len() int {
	return len(n)
}

func (n Neighbors) Swap(i, j int) {
	n[i], n[j] = n[j], n[i]
}

func (n Neighbors) Less(i, j int) bool {
	var ret bool
	if len(n[i].CommonBookmarks) == len(n[j].CommonBookmarks) {
		ret = n[i].User < n[j].User
	} else {
		ret = len(n[i].CommonBookmarks) > len(n[j].CommonBookmarks)
	}
	return ret
}

func (n Neighbors) Exclude(users []string) Neighbors {
	excluded := Neighbors{}
	for _, neighbor := range n {
		if !neighbor.included(users) {
			excluded = append(excluded, neighbor)
		}
	}
	return excluded
}
<commit_msg>add Neighbors Top<commit_after>package hbn

// Neighbors represents list of Neighbor.
type Neighbors []Neighbor

func (n Neighbors) Len() int {
	return len(n)
}

func (n Neighbors) Swap(i, j int) {
	n[i], n[j] = n[j], n[i]
}

func (n Neighbors) Less(i, j int) bool {
	var ret bool
	if len(n[i].CommonBookmarks) == len(n[j].CommonBookmarks) {
		ret = n[i].User < n[j].User
	} else {
		ret = len(n[i].CommonBookmarks) > len(n[j].CommonBookmarks)
	}
	return ret
}

func (n Neighbors) Exclude(users []string) Neighbors {
	excluded := Neighbors{}
	for _, neighbor := range n {
		if !neighbor.included(users) {
			excluded = append(excluded, neighbor)
		}
	}
	return excluded
}

func (n Neighbors) Top(max int) Neighbors {
	return n[0:max]
}
<|endoftext|>


<commit_before>package encoding

import (
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

type (
	// QueryCodec is a Query implementation
	// of parcel.Decoder
	QueryCodec struct{}

	// QueryTypeError is used to indicate
	// an error during the decoding process of
	// a Query
	QueryTypeError struct {
		FromType string
		ToType   string
		Path     string
	}
)

// Query returns a new QueryCodec
func Query() *QueryCodec {
	return new(QueryCodec)
}

// set converts a string value into a target value's kind
func (q *QueryCodec) set(kind reflect.Kind, qs string, field reflect.Value) error {
	switch kind {
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
		v, e := strconv.ParseInt(qs, 10, 64)

		if e != nil {
			return &QueryTypeError{FromType: "string", ToType: "integer"}
		}

		field.SetInt(v)
	case reflect.Float32, reflect.Float64:
		v, e := strconv.ParseFloat(qs, 10)

		if e != nil {
			return &QueryTypeError{FromType: "string", ToType: "floating point"}
		}

		field.SetFloat(v)
	case reflect.String:
		field.SetString(qs)
	case reflect.Bool:
		v := strings.ToLower(qs)

		if v == "true" {
			field.SetBool(true)
		} else {
			field.SetBool(false)
		}
	default:
		return &QueryTypeError{FromType: "string", ToType: field.Type().Name()}
	}

	return nil
}

// Decode will convert query string values into appropriate type
// values
func (q *QueryCodec) Decode(r *http.Request, candidate interface{}) error {
	queries := r.URL.Query()

	// Shortcut for queries strings
	if len(queries) == 0 {
		return nil
	}

	// Value and type of candidate
	value := reflect.ValueOf(candidate).Elem()
	ty := value.Type()

	// Iterate candidate fields
	for i := 0; i < ty.NumField(); i++ {
		field := ty.Field(i)
		tag := field.Tag.Get("query")

		// tag does not exists
		if tag == "" {
			continue
		}

		qs := queries.Get(tag)

		// stringer value does not exist
		if qs == "" {
			continue
		}

		// value can not be set
		if !value.Field(i).CanSet() {
			continue
		}

		// Do conversion

		kind := field.Type.Kind()

		switch kind {
		case reflect.Slice:
			arr := queries[tag]
			arrLen := len(arr)

			sl := reflect.MakeSlice(field.Type, arrLen, arrLen)

			for a, entry := range arr {
				if err := q.set(field.Type.Elem().Kind(), entry, sl.Index(a)); err != nil {
					return err
				}
			}

			value.Field(i).Set(sl)
		default:
			if err := q.set(kind, qs, value.Field(i)); err != nil {
				return err
			}
		}
	}

	return nil
}

// Error provides the error implementation for
// a QueryTypeError
func (e *QueryTypeError) Error() string {
	return "QueryTypeError: Can not convert type " + e.FromType + " to type " + e.ToType
}
<commit_msg>Removed unused path variable<commit_after>package encoding

import (
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

type (
	// QueryCodec is a Query implementation
	// of parcel.Decoder
	QueryCodec struct{}

	// QueryTypeError is used to indicate
	// an error during the decoding process of
	// a Query
	QueryTypeError struct {
		FromType string
		ToType   string
	}
)

// Query returns a new QueryCodec
func Query() *QueryCodec {
	return new(QueryCodec)
}

// set converts a string value into a target value's kind
func (q *QueryCodec) set(kind reflect.Kind, qs string, field reflect.Value) error {
	switch kind {
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
		v, e := strconv.ParseInt(qs, 10, 64)

		if e != nil {
			return &QueryTypeError{FromType: "string", ToType: "integer"}
		}

		field.SetInt(v)
	case reflect.Float32, reflect.Float64:
		v, e := strconv.ParseFloat(qs, 10)

		if e != nil {
			return &QueryTypeError{FromType: "string", ToType: "floating point"}
		}

		field.SetFloat(v)
	case reflect.String:
		field.SetString(qs)
	case reflect.Bool:
		v := strings.ToLower(qs)

		if v == "true" {
			field.SetBool(true)
		} else {
			field.SetBool(false)
		}
	default:
		return &QueryTypeError{FromType: "string", ToType: field.Type().Name()}
	}

	return nil
}

// Decode will convert query string values into appropriate type
// values
func (q *QueryCodec) Decode(r *http.Request, candidate interface{}) error {
	queries := r.URL.Query()

	// Shortcut for queries strings
	if len(queries) == 0 {
		return nil
	}

	// Value and type of candidate
	value := reflect.ValueOf(candidate).Elem()
	ty := value.Type()

	// Iterate candidate fields
	for i := 0; i < ty.NumField(); i++ {
		field := ty.Field(i)
		tag := field.Tag.Get("query")

		// tag does not exists
		if tag == "" {
			continue
		}

		qs := queries.Get(tag)

		// stringer value does not exist
		if qs == "" {
			continue
		}

		// value can not be set
		if !value.Field(i).CanSet() {
			continue
		}

		// Do conversion

		kind := field.Type.Kind()

		switch kind {
		case reflect.Slice:
			arr := queries[tag]
			arrLen := len(arr)

			sl := reflect.MakeSlice(field.Type, arrLen, arrLen)

			for a, entry := range arr {
				if err := q.set(field.Type.Elem().Kind(), entry, sl.Index(a)); err != nil {
					return err
				}
			}

			value.Field(i).Set(sl)
		default:
			if err := q.set(kind, qs, value.Field(i)); err != nil {
				return err
			}
		}
	}

	return nil
}

// Error provides the error implementation for
// a QueryTypeError
func (e *QueryTypeError) Error() string {
	return "QueryTypeError: Can not convert type " + e.FromType + " to type " + e.ToType
}
<|endoftext|>


<commit_before>package entity

import (
	"os"
	"io"
	"fmt"
	"encoding/json"
)

var userlist []User
var meetinglist []Meeting

type uFilter func (*User) bool
type uSwitcher func (*User)
type mFilter func (*Meeting) bool
type mSwitcher func (*Meeting)

func readFromFile()  {
	//读user
	file1, err1 := os.Open("UserInfo");
	if err1 != nil {
		fmt.Fprintf(os.Stderr, "Fail to open UserInfo")
	}
	dec1 := json.NewDecoder(file1)
	for {
		err1 := dec1.Decode(&userlist)
		if err1 == io.EOF {
			fmt.Println("User读取完成")
			break
		} else if err1 != nil {
			fmt.Fprintf(os.Stderr, "Fail to Decode")
		}
	}
	file1.Close()

	//读Meeting
	file2, err2 := os.Open("MeetingInfo");
	if err2 != nil {
		fmt.Fprintf(os.Stderr, "Fail to open MeetingInfo")
	}
	dec2 := json.NewDecoder(file2)
	for {
		err2 := dec2.Decode(&meetinglist)
		if err2 == io.EOF {
			fmt.Println("Meeting读取完成")
			break
		} else if err2 != nil {
			fmt.Fprintf(os.Stderr, "Fail to Decode")
		}
	}
	file2.Close()
}

func writeToFile()  {
	//写User
	file1, err1 := os.Create("UserInfo");
	if err1 != nil {
		fmt.Fprintf(os.Stderr, "Fail to create UserInfo")
	}
	enc1 := json.NewEncoder(file1)
	if err1 := enc1.Encode(&userlist); err1 != nil {
		fmt.Fprintf(os.Stderr, "Fail to encode")
	}
	file1.Close()
	//写Meeting
	file2, err2 := os.Create("MeetingInfo");
	if err2 != nil {
		fmt.Fprintf(os.Stderr, "Fail to create MeetingInfo")
	}
	enc2 := json.NewEncoder(file2)
	if err2 := enc2.Encode(&meetinglist); err2 != nil {
		fmt.Fprintf(os.Stderr, "Fail to encode")
	}
	file2.Close()
}

func createUser(t_user User) {
	userlist = append(userlist,t_user)
}
func createMeeting(t_meeting Meeting) {
	meetinglist = append(meetinglist,t_meeting)
}

func queryUser(filter uFilter) []User {
	var dy []User;
	for _, u := range userlist {
		if filter(&u) {
			dy = append(dy, u)
		}
	}
	return dy
}

func updateUser(filter uFilter, switcher uSwitcher) int {
	n := 0
	for _, u := range userlist {
		if filter(&u) {
			switcher(&u)
			n++
		}
	}
	return n
}

func deleteUser(filter uFilter) int {
	n := 0
	for i, u := range userlist {
		if filter(&u) {
			userlist[i] = userlist[len(userlist) - 1]
			userlist = userlist[:len(userlist) - 1]
			n++
		}
	}
	return n
}
func queryMeeting(filter mFilter) []Meeting {
	var dy []Meeting;
	for _, m := range meetinglist {
		if filter(&m) {
			dy = append(dy, m)
		}
	}
	return dy
}

func updateMeeting(filter mFilter, switcher mSwitcher) int {
	n := 0
	for _, m := range meetinglist {
		if filter(&m) {
			switcher(&m)
			n++
		}
	}
	return n
}

func deleteMeeting(filter mFilter) int {
	n := 0
	for i, m := range meetinglist {
		if filter(&m) {
			meetinglist[i] = meetinglist[len(meetinglist) - 1]
			meetinglist = meetinglist[:len(meetinglist) - 1]
			n++
		}
	}
	return n
}

<commit_msg>新增保存当前登录用户信息<commit_after>package entity

import (
	"os"
	"io"
	"fmt"
	"encoding/json"
)

var userlist []User
var meetinglist []Meeting
var CurrentUser User

type uFilter func (*User) bool
type uSwitcher func (*User)
type mFilter func (*Meeting) bool
type mSwitcher func (*Meeting)

func readCurrentUser()  {
	file1, err1 := os.Open("CurUser");
	if err1 != nil {
		fmt.Fprintf(os.Stderr, "Fail to open CurUser")
	}
	dec1 := json.NewDecoder(file1)
	for {
		err1 := dec1.Decode(&CurrentUser)
		if err1 == io.EOF {
			fmt.Println("CurrentUser读取完成")
			break
		} else if err1 != nil {
			fmt.Fprintf(os.Stderr, "Fail to Decode")
		}
	}
	file1.Close()
}
func readFromFile()  {
	//读user
	file1, err1 := os.Open("UserInfo");
	if err1 != nil {
		fmt.Fprintf(os.Stderr, "Fail to open UserInfo")
	}
	dec1 := json.NewDecoder(file1)
	for {
		err1 := dec1.Decode(&userlist)
		if err1 == io.EOF {
			fmt.Println("User读取完成")
			break
		} else if err1 != nil {
			fmt.Fprintf(os.Stderr, "Fail to Decode")
		}
	}
	file1.Close()

	//读Meeting
	file2, err2 := os.Open("MeetingInfo");
	if err2 != nil {
		fmt.Fprintf(os.Stderr, "Fail to open MeetingInfo")
	}
	dec2 := json.NewDecoder(file2)
	for {
		err2 := dec2.Decode(&meetinglist)
		if err2 == io.EOF {
			fmt.Println("Meeting读取完成")
			break
		} else if err2 != nil {
			fmt.Fprintf(os.Stderr, "Fail to Decode")
		}
	}
	file2.Close()
}

func writeToFile()  {
	//写User
	file1, err1 := os.Create("UserInfo");
	if err1 != nil {
		fmt.Fprintf(os.Stderr, "Fail to create UserInfo")
	}
	enc1 := json.NewEncoder(file1)
	if err1 := enc1.Encode(&userlist); err1 != nil {
		fmt.Fprintf(os.Stderr, "Fail to encode")
	}
	file1.Close()
	//写Meeting
	file2, err2 := os.Create("MeetingInfo");
	if err2 != nil {
		fmt.Fprintf(os.Stderr, "Fail to create MeetingInfo")
	}
	enc2 := json.NewEncoder(file2)
	if err2 := enc2.Encode(&meetinglist); err2 != nil {
		fmt.Fprintf(os.Stderr, "Fail to encode")
	}
	file2.Close()
}
func writeCurrentUser()  {
	file1, err1 := os.Create("CurUser");
	if err1 != nil {
		fmt.Fprintf(os.Stderr, "Fail to create CurUser")
	}
	enc1 := json.NewEncoder(file1)
	if err1 := enc1.Encode(&CurrentUser); err1 != nil {
		fmt.Fprintf(os.Stderr, "Fail to encode")
	}
	file1.Close()
}
func createUser(t_user User) {
	userlist = append(userlist,t_user)
}
func createMeeting(t_meeting Meeting) {
	meetinglist = append(meetinglist,t_meeting)
}

func queryUser(filter uFilter) []User {
	var dy []User;
	for _, u := range userlist {
		if filter(&u) {
			dy = append(dy, u)
		}
	}
	return dy
}

func updateUser(filter uFilter, switcher uSwitcher) int {
	n := 0
	for _, u := range userlist {
		if filter(&u) {
			switcher(&u)
			n++
		}
	}
	return n
}

func deleteUser(filter uFilter) int {
	n := 0
	for i, u := range userlist {
		if filter(&u) {
			userlist[i] = userlist[len(userlist) - 1]
			userlist = userlist[:len(userlist) - 1]
			n++
		}
	}
	return n
}
func queryMeeting(filter mFilter) []Meeting {
	var dy []Meeting;
	for _, m := range meetinglist {
		if filter(&m) {
			dy = append(dy, m)
		}
	}
	return dy
}

func updateMeeting(filter mFilter, switcher mSwitcher) int {
	n := 0
	for _, m := range meetinglist {
		if filter(&m) {
			switcher(&m)
			n++
		}
	}
	return n
}

func deleteMeeting(filter mFilter) int {
	n := 0
	for i, m := range meetinglist {
		if filter(&m) {
			meetinglist[i] = meetinglist[len(meetinglist) - 1]
			meetinglist = meetinglist[:len(meetinglist) - 1]
			n++
		}
	}
	return n
}

<|endoftext|>


<commit_before>// +build  !lambdabinary

package sparta

import "syscall"

// Support Windows development, by only requiring `syscall` in the compiled
// linux binary.
func platformKill(parentProcessPID int) {
	syscall.Kill(parentProcessPID, syscall.SIGUSR2)
}
<commit_msg>Do not reference `syscall.Kill`<commit_after>// +build  !lambdabinary

package sparta

// Support Windows development, by only requiring `syscall` in the compiled
// linux binary.
func platformKill(parentProcessPID int) {
	// NOP
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

// Package docs docs 内容管理
package docs

import (
	"net/http"
	"os"
	"path"
	"path/filepath"
)

const indexPage = "index.xml"

// Handler 将 docs 当作一个网站进行管理
func Handler(docs string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		pp := r.URL.Path

		if len(pp) > 0 && path.Base(pp) == "CNAME" {
			errStatus(w, http.StatusNotFound)
			return
		}

		if len(pp) == 0 || pp[0] != '/' {
			pp = "/" + pp
			r.URL.Path = pp
		}

		pp = filepath.Clean(filepath.Join(docs, pp))
		info, err := os.Stat(pp)
		if err != nil {
			if os.IsNotExist(err) {
				errStatus(w, http.StatusNotFound)
				return
			}
			if os.IsPermission(err) {
				errStatus(w, http.StatusForbidden)
				return
			}

			errStatus(w, http.StatusInternalServerError)
			return
		}
		if info.IsDir() {
			pp = filepath.Clean(filepath.Join(pp, indexPage))
		}

		http.ServeFile(w, r, pp)
	})
}

func errStatus(w http.ResponseWriter, status int) {
	http.Error(w, http.StatusText(status), status)
}
<commit_msg>调整 Site 的过滤方式<commit_after>// SPDX-License-Identifier: MIT

// Package docs docs 内容管理
package docs

import (
	"net/http"
	"os"
	"path"
	"path/filepath"
)

const indexPage = "index.xml"

var filterFiles = []string{
	"CNAME",
	"README.md",
}

// Handler 将 docs 当作一个网站进行管理
func Handler(docs string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		pp := r.URL.Path

		if len(pp) > 0 && isFilterFile(path.Base(pp)) {
			errStatus(w, http.StatusNotFound)
			return
		}

		if len(pp) == 0 || pp[0] != '/' {
			pp = "/" + pp
			r.URL.Path = pp
		}

		pp = filepath.Clean(filepath.Join(docs, pp))
		info, err := os.Stat(pp)
		if err != nil {
			if os.IsNotExist(err) {
				errStatus(w, http.StatusNotFound)
				return
			}
			if os.IsPermission(err) {
				errStatus(w, http.StatusForbidden)
				return
			}

			errStatus(w, http.StatusInternalServerError)
			return
		}
		if info.IsDir() {
			pp = filepath.Clean(filepath.Join(pp, indexPage))
		}

		http.ServeFile(w, r, pp)
	})
}

func errStatus(w http.ResponseWriter, status int) {
	http.Error(w, http.StatusText(status), status)
}

func isFilterFile(filename string) bool {
	for _, file := range filterFiles {
		if file == filename {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package node

import (
	"fmt"
)

type Type int

const (
	InternalType Type = iota
	LeafType
)

type Node struct {
	Type Type
	Internal
	Leaf
}

type Internal struct {
	// Ply is an index into the vocabular assigning this node to
	// the corresponding variable.
	Ply   int
	True  *Node
	False *Node
}

type Leaf struct {
	Value bool
}

func (n Node) String(v []string) string {
	switch n.Type {
	case InternalType:
		return n.Internal.String(v)
	case LeafType:
		return n.Leaf.String()
	default:
		return fmt.Sprintf("Garbage Node type: %v", n.Type)
	}
}

func (n Leaf) String() string {
	if n.Value {
		return "T"
	} else {
		return "F"
	}
}

func (n Internal) String(v []string) string {
	return fmt.Sprintf(
		"(%v/T: %v, %v/F: %v)",
		v[n.Ply], n.True.String(v), v[n.Ply], n.False.String(v),
	)

}

func Uniform(depth int, v bool) *Node {
	return uniformHelper(0, depth, v)
}

func uniformHelper(ply int, totalPlies int, v bool) *Node {
	if ply == totalPlies {
		return &Node{
			Type: LeafType,
			Leaf: Leaf{v},
		}
	}
	return &Node{
		Type: InternalType,
		Internal: Internal{
			Ply:   ply,
			True:  uniformHelper(ply+1, totalPlies, v),
			False: uniformHelper(ply+1, totalPlies, v),
		},
	}
}

// Reduce converts the ROBDD rooted at n to the canonical reduced form.
// Reduce is not part of the operations package because it is a transitional operation.
// Eventually, all ROBDDs should be constructed reduced and stay in reduced form during the operations.
func Reduce(n *Node) (*Node, error) {
	t, f, e := findTrueFalse(n, true, true)
	if e != nil {
		return nil, e
	}
	return reduceHelper(n, t, f)
}

func reduceHelper(n *Node, t *Node, f *Node) (*Node, error) {
	switch n.Type {
	case LeafType:
		if n.Value {
			return t, nil
		} else {
			return f, nil
		}
	case InternalType:
		var e error
		n.True, e = reduceHelper(n.True, t, f)
		if e != nil {
			return nil, e
		}
		n.False, e = reduceHelper(n.False, t, f)
		if e != nil {
			return nil, e
		}
		if n.True == n.False {
			return n.True, nil
		} else {
			return n, nil
		}
	default:
		return nil, fmt.Errorf("Unexpected node type: %v", n)
	}
}

// findTrueFalse finds a True and False leaf node to be used to replace all True/False leaves.
func findTrueFalse(n *Node, findTrue bool, findFalse bool) (*Node, *Node, error) {
	switch n.Type {
	case LeafType:
		if n.Value {
			return n, nil, nil
		} else {
			return nil, n, nil
		}
	case InternalType:
		tt, ft, et := findTrueFalse(n.True, findTrue, findFalse)
		if et != nil {
			return nil, nil, et
		}
		findTrue = findTrue && (tt == nil)
		findFalse = findFalse && (ft == nil)
		if !findTrue && !findFalse {
			return tt, ft, nil
		}

		tf, ff, ef := findTrueFalse(n.False, findTrue, findFalse)
		if ef != nil {
			return nil, nil, ef
		}
		if tt == nil {
			tt = tf
		}
		if ft == nil {
			ft = ff
		}
		return tt, ft, nil
	default:
		return nil, nil, fmt.Errorf("Unexpected node type: %v", n)
	}
}
<commit_msg>Add pretty String() for Node without a vocabulary.<commit_after>package node

import (
	"fmt"
)

type Type int

const (
	InternalType Type = iota
	LeafType
)

type Node struct {
	Type Type
	Internal
	Leaf
}

type Internal struct {
	// Ply is an index into the vocabular assigning this node to
	// the corresponding variable.
	Ply   int
	True  *Node
	False *Node
}

type Leaf struct {
	Value bool
}

func (n Node) String(v ...[]string) string {
	switch n.Type {
	case InternalType:
		return n.Internal.String(v...)
	case LeafType:
		return n.Leaf.String()
	default:
		return fmt.Sprintf("Garbage Node type: %v", n.Type)
	}
}

func (n Leaf) String() string {
	if n.Value {
		return "T"
	} else {
		return "F"
	}
}

func (n Internal) String(v ...[]string) string {
	switch len(v) {
	case 0:
		return fmt.Sprintf(
			"(%v/T: %v, %v/F: %v)",
			n.Ply, n.True.String(v...), n.Ply, n.False.String(v...),
		)
	case 1:
		return fmt.Sprintf(
			"(%v/T: %v, %v/F: %v)",
			v[0][n.Ply], n.True.String(v...), v[0][n.Ply], n.False.String(v...),
		)
	default:
		return fmt.Sprintf("Unepxected vocabulary: %v", v)
	}
}

func Uniform(depth int, v bool) *Node {
	return uniformHelper(0, depth, v)
}

func uniformHelper(ply int, totalPlies int, v bool) *Node {
	if ply == totalPlies {
		return &Node{
			Type: LeafType,
			Leaf: Leaf{v},
		}
	}
	return &Node{
		Type: InternalType,
		Internal: Internal{
			Ply:   ply,
			True:  uniformHelper(ply+1, totalPlies, v),
			False: uniformHelper(ply+1, totalPlies, v),
		},
	}
}

// Reduce converts the ROBDD rooted at n to the canonical reduced form.
// Reduce is not part of the operations package because it is a transitional operation.
// Eventually, all ROBDDs should be constructed reduced and stay in reduced form during the operations.
func Reduce(n *Node) (*Node, error) {
	t, f, e := findTrueFalse(n, true, true)
	if e != nil {
		return nil, e
	}
	return reduceHelper(n, t, f)
}

func reduceHelper(n *Node, t *Node, f *Node) (*Node, error) {
	switch n.Type {
	case LeafType:
		if n.Value {
			return t, nil
		} else {
			return f, nil
		}
	case InternalType:
		var e error
		n.True, e = reduceHelper(n.True, t, f)
		if e != nil {
			return nil, e
		}
		n.False, e = reduceHelper(n.False, t, f)
		if e != nil {
			return nil, e
		}
		if n.True == n.False {
			return n.True, nil
		} else {
			return n, nil
		}
	default:
		return nil, fmt.Errorf("Unexpected node type: %v", n)
	}
}

// findTrueFalse finds a True and False leaf node to be used to replace all True/False leaves.
func findTrueFalse(n *Node, findTrue bool, findFalse bool) (*Node, *Node, error) {
	switch n.Type {
	case LeafType:
		if n.Value {
			return n, nil, nil
		} else {
			return nil, n, nil
		}
	case InternalType:
		tt, ft, et := findTrueFalse(n.True, findTrue, findFalse)
		if et != nil {
			return nil, nil, et
		}
		findTrue = findTrue && (tt == nil)
		findFalse = findFalse && (ft == nil)
		if !findTrue && !findFalse {
			return tt, ft, nil
		}

		tf, ff, ef := findTrueFalse(n.False, findTrue, findFalse)
		if ef != nil {
			return nil, nil, ef
		}
		if tt == nil {
			tt = tf
		}
		if ft == nil {
			ft = ff
		}
		return tt, ft, nil
	default:
		return nil, nil, fmt.Errorf("Unexpected node type: %v", n)
	}
}
<|endoftext|>